Show More
@@ -1,276 +1,277 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | # This program is free software: you can redistribute it and/or modify |
|
3 | 3 | # it under the terms of the GNU General Public License as published by |
|
4 | 4 | # the Free Software Foundation, either version 3 of the License, or |
|
5 | 5 | # (at your option) any later version. |
|
6 | 6 | # |
|
7 | 7 | # This program is distributed in the hope that it will be useful, |
|
8 | 8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
9 | 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
10 | 10 | # GNU General Public License for more details. |
|
11 | 11 | # |
|
12 | 12 | # You should have received a copy of the GNU General Public License |
|
13 | 13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
14 | 14 | """ |
|
15 | 15 | kallithea.model.comment |
|
16 | 16 | ~~~~~~~~~~~~~~~~~~~~~~~ |
|
17 | 17 | |
|
18 | 18 | comments model for Kallithea |
|
19 | 19 | |
|
20 | 20 | This file was forked by the Kallithea project in July 2014. |
|
21 | 21 | Original author and date, and relevant copyright and licensing information is below: |
|
22 | 22 | :created_on: Nov 11, 2011 |
|
23 | 23 | :author: marcink |
|
24 | 24 | :copyright: (c) 2013 RhodeCode GmbH, and others. |
|
25 | 25 | :license: GPLv3, see LICENSE.md for more details. |
|
26 | 26 | """ |
|
27 | 27 | |
|
28 | 28 | import logging |
|
29 | 29 | |
|
30 | 30 | from pylons.i18n.translation import _ |
|
31 | 31 | from collections import defaultdict |
|
32 | 32 | |
|
33 | 33 | from kallithea.lib.utils2 import extract_mentioned_users, safe_unicode |
|
34 | 34 | from kallithea.lib import helpers as h |
|
35 | 35 | from kallithea.model import BaseModel |
|
36 | 36 | from kallithea.model.db import ChangesetComment, User, \ |
|
37 | 37 | Notification, PullRequest |
|
38 | 38 | from kallithea.model.notification import NotificationModel |
|
39 | 39 | from kallithea.model.meta import Session |
|
40 | 40 | |
|
41 | 41 | log = logging.getLogger(__name__) |
|
42 | 42 | |
|
43 | 43 | |
|
44 | 44 | class ChangesetCommentsModel(BaseModel): |
|
45 | 45 | |
|
46 | 46 | cls = ChangesetComment |
|
47 | 47 | |
|
48 | 48 | def __get_changeset_comment(self, changeset_comment): |
|
49 | 49 | return self._get_instance(ChangesetComment, changeset_comment) |
|
50 | 50 | |
|
51 | 51 | def __get_pull_request(self, pull_request): |
|
52 | 52 | return self._get_instance(PullRequest, pull_request) |
|
53 | 53 | |
|
54 | 54 | def _get_notification_data(self, repo, comment, user, comment_text, |
|
55 | 55 | line_no=None, revision=None, pull_request=None, |
|
56 | 56 | status_change=None, closing_pr=False): |
|
57 | 57 | """ |
|
58 | 58 | :returns: tuple (subj,body,recipients,notification_type,email_kwargs) |
|
59 | 59 | """ |
|
60 | 60 | # make notification |
|
61 | 61 | body = comment_text # text of the comment |
|
62 | 62 | line = '' |
|
63 | 63 | if line_no: |
|
64 | 64 | line = _('on line %s') % line_no |
|
65 | 65 | |
|
66 | 66 | #changeset |
|
67 | 67 | if revision: |
|
68 | 68 | notification_type = Notification.TYPE_CHANGESET_COMMENT |
|
69 | 69 | cs = repo.scm_instance.get_changeset(revision) |
|
70 | 70 | desc = cs.short_id |
|
71 | 71 | |
|
72 | 72 | threading = ['%s-rev-%s@%s' % (repo.repo_name, revision, h.canonical_hostname())] |
|
73 | 73 | if line_no: # TODO: url to file _and_ line number |
|
74 | 74 | threading.append('%s-rev-%s-line-%s@%s' % (repo.repo_name, revision, line_no, |
|
75 | 75 | h.canonical_hostname())) |
|
76 | 76 | comment_url = h.canonical_url('changeset_home', |
|
77 | 77 | repo_name=repo.repo_name, |
|
78 | 78 | revision=revision, |
|
79 | 79 | anchor='comment-%s' % comment.comment_id) |
|
80 | 80 | subj = safe_unicode( |
|
81 | 81 | h.link_to('Re changeset: %(desc)s %(line)s' % \ |
|
82 | 82 | {'desc': desc, 'line': line}, |
|
83 | 83 | comment_url) |
|
84 | 84 | ) |
|
85 | 85 | # get the current participants of this changeset |
|
86 | 86 | recipients = ChangesetComment.get_users(revision=revision) |
|
87 | 87 | # add changeset author if it's known locally |
|
88 | 88 | cs_author = User.get_from_cs_author(cs.author) |
|
89 | 89 | if not cs_author: |
|
90 | 90 | #use repo owner if we cannot extract the author correctly |
|
91 | 91 | # FIXME: just use committer name even if not a user |
|
92 | 92 | cs_author = repo.user |
|
93 | 93 | recipients += [cs_author] |
|
94 | 94 | email_kwargs = { |
|
95 | 95 | 'status_change': status_change, |
|
96 | 96 | 'cs_comment_user': user.full_name_and_username, |
|
97 | 97 | 'cs_target_repo': h.canonical_url('summary_home', repo_name=repo.repo_name), |
|
98 | 98 | 'cs_comment_url': comment_url, |
|
99 | 99 | 'raw_id': revision, |
|
100 | 100 | 'message': cs.message, |
|
101 | 101 | 'cs_author': cs_author, |
|
102 | 102 | 'repo_name': repo.repo_name, |
|
103 | 103 | 'short_id': h.short_id(revision), |
|
104 | 104 | 'branch': cs.branch, |
|
105 | 105 | 'comment_username': user.username, |
|
106 | 106 | 'threading': threading, |
|
107 | 107 | } |
|
108 | 108 | #pull request |
|
109 | 109 | elif pull_request: |
|
110 | 110 | notification_type = Notification.TYPE_PULL_REQUEST_COMMENT |
|
111 | 111 | desc = comment.pull_request.title |
|
112 | 112 | _org_ref_type, org_ref_name, _org_rev = comment.pull_request.org_ref.split(':') |
|
113 | 113 | _other_ref_type, other_ref_name, _other_rev = comment.pull_request.other_ref.split(':') |
|
114 | 114 | threading = ['%s-pr-%s@%s' % (pull_request.other_repo.repo_name, |
|
115 | 115 | pull_request.pull_request_id, |
|
116 | 116 | h.canonical_hostname())] |
|
117 | 117 | if line_no: # TODO: url to file _and_ line number |
|
118 | 118 | threading.append('%s-pr-%s-line-%s@%s' % (pull_request.other_repo.repo_name, |
|
119 | 119 | pull_request.pull_request_id, line_no, |
|
120 | 120 | h.canonical_hostname())) |
|
121 | 121 | comment_url = pull_request.url(canonical=True, |
|
122 | 122 | anchor='comment-%s' % comment.comment_id) |
|
123 | 123 | subj = safe_unicode( |
|
124 | 124 | h.link_to('Re pull request %(pr_nice_id)s: %(desc)s %(line)s' % \ |
|
125 | 125 | {'desc': desc, |
|
126 | 126 | 'pr_nice_id': comment.pull_request.nice_id(), |
|
127 | 127 | 'line': line}, |
|
128 | 128 | comment_url) |
|
129 | 129 | ) |
|
130 | 130 | # get the current participants of this pull request |
|
131 | 131 | recipients = ChangesetComment.get_users(pull_request_id= |
|
132 | 132 | pull_request.pull_request_id) |
|
133 | 133 | # add pull request author |
|
134 | 134 | recipients += [pull_request.owner] |
|
135 | 135 | |
|
136 | 136 | # add the reviewers to notification |
|
137 | 137 | recipients += pull_request.get_reviewer_users() |
|
138 | 138 | |
|
139 | 139 | #set some variables for email notification |
|
140 | 140 | email_kwargs = { |
|
141 | 141 | 'pr_title': pull_request.title, |
|
142 | 142 | 'pr_nice_id': pull_request.nice_id(), |
|
143 | 143 | 'status_change': status_change, |
|
144 | 144 | 'closing_pr': closing_pr, |
|
145 | 145 | 'pr_comment_url': comment_url, |
|
146 | 146 | 'pr_comment_user': user.full_name_and_username, |
|
147 | 147 | 'pr_target_repo': h.canonical_url('summary_home', |
|
148 | 148 | repo_name=pull_request.other_repo.repo_name), |
|
149 | 149 | 'pr_target_branch': other_ref_name, |
|
150 | 150 | 'pr_source_repo': h.canonical_url('summary_home', |
|
151 | 151 | repo_name=pull_request.org_repo.repo_name), |
|
152 | 152 | 'pr_source_branch': org_ref_name, |
|
153 | 153 | 'pr_owner': pull_request.owner, |
|
154 | 154 | 'repo_name': pull_request.other_repo.repo_name, |
|
155 | 155 | 'comment_username': user.username, |
|
156 | 156 | 'threading': threading, |
|
157 | 157 | } |
|
158 | 158 | |
|
159 | 159 | return subj, body, recipients, notification_type, email_kwargs |
|
160 | 160 | |
|
161 | 161 | def create(self, text, repo, user, revision=None, pull_request=None, |
|
162 | 162 | f_path=None, line_no=None, status_change=None, closing_pr=False, |
|
163 | 163 | send_email=True): |
|
164 | 164 | """ |
|
165 | 165 | Creates a new comment for either a changeset or a pull request. |
|
166 | 166 | status_change and closing_pr is only for the optional email. |
|
167 | 167 | |
|
168 | 168 | Returns the created comment. |
|
169 | 169 | """ |
|
170 | 170 | if not status_change and not text: |
|
171 | 171 | log.warning('Missing text for comment, skipping...') |
|
172 | 172 | return None |
|
173 | 173 | |
|
174 | 174 | repo = self._get_repo(repo) |
|
175 | 175 | user = self._get_user(user) |
|
176 | 176 | comment = ChangesetComment() |
|
177 | 177 | comment.repo = repo |
|
178 | 178 | comment.author = user |
|
179 | 179 | comment.text = text |
|
180 | 180 | comment.f_path = f_path |
|
181 | 181 | comment.line_no = line_no |
|
182 | 182 | |
|
183 | 183 | if revision is not None: |
|
184 | 184 | comment.revision = revision |
|
185 | 185 | elif pull_request is not None: |
|
186 | 186 | pull_request = self.__get_pull_request(pull_request) |
|
187 | 187 | comment.pull_request = pull_request |
|
188 | 188 | else: |
|
189 | 189 | raise Exception('Please specify revision or pull_request_id') |
|
190 | 190 | |
|
191 | 191 | Session().add(comment) |
|
192 | 192 | Session().flush() |
|
193 | 193 | |
|
194 | 194 | if send_email: |
|
195 | 195 | (subj, body, recipients, notification_type, |
|
196 | 196 | email_kwargs) = self._get_notification_data( |
|
197 | 197 | repo, comment, user, |
|
198 | 198 | comment_text=text, |
|
199 | 199 | line_no=line_no, |
|
200 | 200 | revision=revision, |
|
201 | 201 | pull_request=pull_request, |
|
202 | 202 | status_change=status_change, |
|
203 | 203 | closing_pr=closing_pr) |
|
204 | 204 | email_kwargs['is_mention'] = False |
|
205 | 205 | # create notification objects, and emails |
|
206 | 206 | NotificationModel().create( |
|
207 | 207 | created_by=user, subject=subj, body=body, |
|
208 | 208 | recipients=recipients, type_=notification_type, |
|
209 | 209 | email_kwargs=email_kwargs, |
|
210 | 210 | ) |
|
211 | 211 | |
|
212 | 212 | mention_recipients = extract_mentioned_users(body).difference(recipients) |
|
213 | 213 | if mention_recipients: |
|
214 | 214 | email_kwargs['is_mention'] = True |
|
215 | 215 | subj = _('[Mention]') + ' ' + subj |
|
216 | # FIXME: this subject is wrong and unused! | |
|
216 | 217 | NotificationModel().create( |
|
217 | 218 | created_by=user, subject=subj, body=body, |
|
218 | 219 | recipients=mention_recipients, |
|
219 | 220 | type_=notification_type, |
|
220 | 221 | email_kwargs=email_kwargs |
|
221 | 222 | ) |
|
222 | 223 | |
|
223 | 224 | return comment |
|
224 | 225 | |
|
225 | 226 | def delete(self, comment): |
|
226 | 227 | comment = self.__get_changeset_comment(comment) |
|
227 | 228 | Session().delete(comment) |
|
228 | 229 | |
|
229 | 230 | return comment |
|
230 | 231 | |
|
231 | 232 | def get_comments(self, repo_id, revision=None, pull_request=None): |
|
232 | 233 | """ |
|
233 | 234 | Gets general comments for either revision or pull_request. |
|
234 | 235 | |
|
235 | 236 | Returns a list, ordered by creation date. |
|
236 | 237 | """ |
|
237 | 238 | return self._get_comments(repo_id, revision=revision, pull_request=pull_request, |
|
238 | 239 | inline=False) |
|
239 | 240 | |
|
240 | 241 | def get_inline_comments(self, repo_id, revision=None, pull_request=None): |
|
241 | 242 | """ |
|
242 | 243 | Gets inline comments for either revision or pull_request. |
|
243 | 244 | |
|
244 | 245 | Returns a list of tuples with file path and list of comments per line number. |
|
245 | 246 | """ |
|
246 | 247 | comments = self._get_comments(repo_id, revision=revision, pull_request=pull_request, |
|
247 | 248 | inline=True) |
|
248 | 249 | |
|
249 | 250 | paths = defaultdict(lambda: defaultdict(list)) |
|
250 | 251 | for co in comments: |
|
251 | 252 | paths[co.f_path][co.line_no].append(co) |
|
252 | 253 | return paths.items() |
|
253 | 254 | |
|
254 | 255 | def _get_comments(self, repo_id, revision=None, pull_request=None, inline=False): |
|
255 | 256 | """ |
|
256 | 257 | Gets comments for either revision or pull_request_id, either inline or general. |
|
257 | 258 | """ |
|
258 | 259 | q = Session().query(ChangesetComment) |
|
259 | 260 | |
|
260 | 261 | if inline: |
|
261 | 262 | q = q.filter(ChangesetComment.line_no != None) \ |
|
262 | 263 | .filter(ChangesetComment.f_path != None) |
|
263 | 264 | else: |
|
264 | 265 | q = q.filter(ChangesetComment.line_no == None) \ |
|
265 | 266 | .filter(ChangesetComment.f_path == None) |
|
266 | 267 | |
|
267 | 268 | if revision is not None: |
|
268 | 269 | q = q.filter(ChangesetComment.revision == revision) \ |
|
269 | 270 | .filter(ChangesetComment.repo_id == repo_id) |
|
270 | 271 | elif pull_request is not None: |
|
271 | 272 | pull_request = self.__get_pull_request(pull_request) |
|
272 | 273 | q = q.filter(ChangesetComment.pull_request == pull_request) |
|
273 | 274 | else: |
|
274 | 275 | raise Exception('Please specify either revision or pull_request') |
|
275 | 276 | |
|
276 | 277 | return q.order_by(ChangesetComment.created_on).all() |
@@ -1,232 +1,233 b'' | |||
|
1 | 1 | # -*- coding: utf-8 -*- |
|
2 | 2 | # This program is free software: you can redistribute it and/or modify |
|
3 | 3 | # it under the terms of the GNU General Public License as published by |
|
4 | 4 | # the Free Software Foundation, either version 3 of the License, or |
|
5 | 5 | # (at your option) any later version. |
|
6 | 6 | # |
|
7 | 7 | # This program is distributed in the hope that it will be useful, |
|
8 | 8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
9 | 9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
10 | 10 | # GNU General Public License for more details. |
|
11 | 11 | # |
|
12 | 12 | # You should have received a copy of the GNU General Public License |
|
13 | 13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
14 | 14 | """ |
|
15 | 15 | kallithea.model.pull_request |
|
16 | 16 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
17 | 17 | |
|
18 | 18 | pull request model for Kallithea |
|
19 | 19 | |
|
20 | 20 | This file was forked by the Kallithea project in July 2014. |
|
21 | 21 | Original author and date, and relevant copyright and licensing information is below: |
|
22 | 22 | :created_on: Jun 6, 2012 |
|
23 | 23 | :author: marcink |
|
24 | 24 | :copyright: (c) 2013 RhodeCode GmbH, and others. |
|
25 | 25 | :license: GPLv3, see LICENSE.md for more details. |
|
26 | 26 | """ |
|
27 | 27 | |
|
28 | 28 | import logging |
|
29 | 29 | import datetime |
|
30 | 30 | |
|
31 | 31 | from pylons.i18n.translation import _ |
|
32 | 32 | |
|
33 | 33 | from sqlalchemy.orm import joinedload |
|
34 | 34 | |
|
35 | 35 | from kallithea.model.meta import Session |
|
36 | 36 | from kallithea.lib import helpers as h |
|
37 | 37 | from kallithea.lib.exceptions import UserInvalidException |
|
38 | 38 | from kallithea.model import BaseModel |
|
39 | 39 | from kallithea.model.db import PullRequest, PullRequestReviewers, Notification, \ |
|
40 | 40 | ChangesetStatus, User |
|
41 | 41 | from kallithea.model.notification import NotificationModel |
|
42 | 42 | from kallithea.lib.utils2 import extract_mentioned_users, safe_unicode |
|
43 | 43 | |
|
44 | 44 | |
|
45 | 45 | log = logging.getLogger(__name__) |
|
46 | 46 | |
|
47 | 47 | |
|
48 | 48 | class PullRequestModel(BaseModel): |
|
49 | 49 | |
|
50 | 50 | cls = PullRequest |
|
51 | 51 | |
|
52 | 52 | def __get_pull_request(self, pull_request): |
|
53 | 53 | return self._get_instance(PullRequest, pull_request) |
|
54 | 54 | |
|
55 | 55 | def get_pullrequest_cnt_for_user(self, user): |
|
56 | 56 | return PullRequest.query() \ |
|
57 | 57 | .join(PullRequestReviewers) \ |
|
58 | 58 | .filter(PullRequestReviewers.user_id == user) \ |
|
59 | 59 | .filter(PullRequest.status != PullRequest.STATUS_CLOSED) \ |
|
60 | 60 | .count() |
|
61 | 61 | |
|
62 | 62 | def get_all(self, repo_name, from_=False, closed=False): |
|
63 | 63 | """Get all PRs for repo. |
|
64 | 64 | Default is all PRs to the repo, PRs from the repo if from_. |
|
65 | 65 | Closed PRs are only included if closed is true.""" |
|
66 | 66 | repo = self._get_repo(repo_name) |
|
67 | 67 | q = PullRequest.query() |
|
68 | 68 | if from_: |
|
69 | 69 | q = q.filter(PullRequest.org_repo == repo) |
|
70 | 70 | else: |
|
71 | 71 | q = q.filter(PullRequest.other_repo == repo) |
|
72 | 72 | if not closed: |
|
73 | 73 | q = q.filter(PullRequest.status != PullRequest.STATUS_CLOSED) |
|
74 | 74 | return q.order_by(PullRequest.created_on.desc()).all() |
|
75 | 75 | |
|
76 | 76 | def _get_valid_reviewers(self, seq): |
|
77 | 77 | """ Generate User objects from a sequence of user IDs, usernames or |
|
78 | 78 | User objects. Raises UserInvalidException if the DEFAULT user is |
|
79 | 79 | specified, or if a given ID or username does not match any user. |
|
80 | 80 | """ |
|
81 | 81 | for user_spec in seq: |
|
82 | 82 | user = self._get_user(user_spec) |
|
83 | 83 | if user is None or user.username == User.DEFAULT_USER: |
|
84 | 84 | raise UserInvalidException(user_spec) |
|
85 | 85 | yield user |
|
86 | 86 | |
|
87 | 87 | def create(self, created_by, org_repo, org_ref, other_repo, other_ref, |
|
88 | 88 | revisions, reviewers, title, description=None): |
|
89 | 89 | from kallithea.model.changeset_status import ChangesetStatusModel |
|
90 | 90 | |
|
91 | 91 | created_by_user = self._get_user(created_by) |
|
92 | 92 | org_repo = self._get_repo(org_repo) |
|
93 | 93 | other_repo = self._get_repo(other_repo) |
|
94 | 94 | |
|
95 | 95 | new = PullRequest() |
|
96 | 96 | new.org_repo = org_repo |
|
97 | 97 | new.org_ref = org_ref |
|
98 | 98 | new.other_repo = other_repo |
|
99 | 99 | new.other_ref = other_ref |
|
100 | 100 | new.revisions = revisions |
|
101 | 101 | new.title = title |
|
102 | 102 | new.description = description |
|
103 | 103 | new.owner = created_by_user |
|
104 | 104 | Session().add(new) |
|
105 | 105 | Session().flush() |
|
106 | 106 | |
|
107 | 107 | #reset state to under-review |
|
108 | 108 | from kallithea.model.comment import ChangesetCommentsModel |
|
109 | 109 | comment = ChangesetCommentsModel().create( |
|
110 | 110 | text=u'', |
|
111 | 111 | repo=org_repo, |
|
112 | 112 | user=new.owner, |
|
113 | 113 | pull_request=new, |
|
114 | 114 | send_email=False, |
|
115 | 115 | status_change=ChangesetStatus.STATUS_UNDER_REVIEW, |
|
116 | 116 | ) |
|
117 | 117 | ChangesetStatusModel().set_status( |
|
118 | 118 | org_repo, |
|
119 | 119 | ChangesetStatus.STATUS_UNDER_REVIEW, |
|
120 | 120 | new.owner, |
|
121 | 121 | comment, |
|
122 | 122 | pull_request=new |
|
123 | 123 | ) |
|
124 | 124 | |
|
125 | 125 | reviewers = set(self._get_valid_reviewers(reviewers)) |
|
126 | 126 | mention_recipients = extract_mentioned_users(new.description) |
|
127 | 127 | self.__add_reviewers(created_by_user, new, reviewers, mention_recipients) |
|
128 | 128 | |
|
129 | 129 | return new |
|
130 | 130 | |
|
131 | 131 | def __add_reviewers(self, user, pr, reviewers, mention_recipients): |
|
132 | 132 | # reviewers and mention_recipients should be sets of User objects. |
|
133 | 133 | #members |
|
134 | 134 | for reviewer in reviewers: |
|
135 | 135 | reviewer = PullRequestReviewers(reviewer, pr) |
|
136 | 136 | Session().add(reviewer) |
|
137 | 137 | |
|
138 | 138 | revision_data = [(x.raw_id, x.message) |
|
139 | 139 | for x in map(pr.org_repo.get_changeset, pr.revisions)] |
|
140 | 140 | |
|
141 | 141 | #notification to reviewers |
|
142 | 142 | pr_url = pr.url(canonical=True) |
|
143 | 143 | threading = ['%s-pr-%s@%s' % (pr.other_repo.repo_name, |
|
144 | 144 | pr.pull_request_id, |
|
145 | 145 | h.canonical_hostname())] |
|
146 | 146 | subject = safe_unicode( |
|
147 | 147 | h.link_to( |
|
148 | 148 | _('%(user)s wants you to review pull request %(pr_nice_id)s: %(pr_title)s') % \ |
|
149 | 149 | {'user': user.username, |
|
150 | 150 | 'pr_title': pr.title, |
|
151 | 151 | 'pr_nice_id': pr.nice_id()}, |
|
152 | 152 | pr_url) |
|
153 | 153 | ) |
|
154 | 154 | body = pr.description |
|
155 | 155 | _org_ref_type, org_ref_name, _org_rev = pr.org_ref.split(':') |
|
156 | 156 | _other_ref_type, other_ref_name, _other_rev = pr.other_ref.split(':') |
|
157 | 157 | email_kwargs = { |
|
158 | 158 | 'pr_title': pr.title, |
|
159 | 159 | 'pr_user_created': user.full_name_and_username, |
|
160 | 160 | 'pr_repo_url': h.canonical_url('summary_home', repo_name=pr.other_repo.repo_name), |
|
161 | 161 | 'pr_url': pr_url, |
|
162 | 162 | 'pr_revisions': revision_data, |
|
163 | 163 | 'repo_name': pr.other_repo.repo_name, |
|
164 | 164 | 'org_repo_name': pr.org_repo.repo_name, |
|
165 | 165 | 'pr_nice_id': pr.nice_id(), |
|
166 | 166 | 'pr_target_repo': h.canonical_url('summary_home', |
|
167 | 167 | repo_name=pr.other_repo.repo_name), |
|
168 | 168 | 'pr_target_branch': other_ref_name, |
|
169 | 169 | 'pr_source_repo': h.canonical_url('summary_home', |
|
170 | 170 | repo_name=pr.org_repo.repo_name), |
|
171 | 171 | 'pr_source_branch': org_ref_name, |
|
172 | 172 | 'pr_owner': pr.owner, |
|
173 | 173 | 'pr_username': user.username, |
|
174 | 174 | 'threading': threading, |
|
175 | 175 | 'is_mention': False, |
|
176 | 176 | } |
|
177 | 177 | if reviewers: |
|
178 | 178 | NotificationModel().create(created_by=user, subject=subject, body=body, |
|
179 | 179 | recipients=reviewers, |
|
180 | 180 | type_=Notification.TYPE_PULL_REQUEST, |
|
181 | 181 | email_kwargs=email_kwargs) |
|
182 | 182 | |
|
183 | 183 | if mention_recipients: |
|
184 | 184 | mention_recipients.difference_update(reviewers) |
|
185 | 185 | if mention_recipients: |
|
186 | 186 | email_kwargs['is_mention'] = True |
|
187 | 187 | subject = _('[Mention]') + ' ' + subject |
|
188 | # FIXME: this subject is wrong and unused! | |
|
188 | 189 | NotificationModel().create(created_by=user, subject=subject, body=body, |
|
189 | 190 | recipients=mention_recipients, |
|
190 | 191 | type_=Notification.TYPE_PULL_REQUEST, |
|
191 | 192 | email_kwargs=email_kwargs) |
|
192 | 193 | |
|
193 | 194 | def mention_from_description(self, user, pr, old_description=''): |
|
194 | 195 | mention_recipients = (extract_mentioned_users(pr.description) - |
|
195 | 196 | extract_mentioned_users(old_description)) |
|
196 | 197 | |
|
197 | 198 | log.debug("Mentioning %s", mention_recipients) |
|
198 | 199 | self.__add_reviewers(user, pr, set(), mention_recipients) |
|
199 | 200 | |
|
200 | 201 | def update_reviewers(self, user, pull_request, reviewers_ids): |
|
201 | 202 | reviewers_ids = set(reviewers_ids) |
|
202 | 203 | pull_request = self.__get_pull_request(pull_request) |
|
203 | 204 | current_reviewers = PullRequestReviewers.query() \ |
|
204 | 205 | .options(joinedload('user')) \ |
|
205 | 206 | .filter_by(pull_request=pull_request) \ |
|
206 | 207 | .all() |
|
207 | 208 | current_reviewer_users = set(x.user for x in current_reviewers) |
|
208 | 209 | new_reviewer_users = set(self._get_valid_reviewers(reviewers_ids)) |
|
209 | 210 | |
|
210 | 211 | to_add = new_reviewer_users - current_reviewer_users |
|
211 | 212 | to_remove = current_reviewer_users - new_reviewer_users |
|
212 | 213 | |
|
213 | 214 | if not to_add and not to_remove: |
|
214 | 215 | return # all done |
|
215 | 216 | |
|
216 | 217 | log.debug("Adding %s reviewers", to_add) |
|
217 | 218 | self.__add_reviewers(user, pull_request, to_add, set()) |
|
218 | 219 | |
|
219 | 220 | log.debug("Removing %s reviewers", to_remove) |
|
220 | 221 | for prr in current_reviewers: |
|
221 | 222 | if prr.user in to_remove: |
|
222 | 223 | Session().delete(prr) |
|
223 | 224 | |
|
224 | 225 | def delete(self, pull_request): |
|
225 | 226 | pull_request = self.__get_pull_request(pull_request) |
|
226 | 227 | Session().delete(pull_request) |
|
227 | 228 | |
|
228 | 229 | def close_pull_request(self, pull_request): |
|
229 | 230 | pull_request = self.__get_pull_request(pull_request) |
|
230 | 231 | pull_request.status = PullRequest.STATUS_CLOSED |
|
231 | 232 | pull_request.updated_on = datetime.datetime.now() |
|
232 | 233 | Session().add(pull_request) |
@@ -1,637 +1,813 b'' | |||
|
1 | 1 | <html><body> |
|
2 | 2 | |
|
3 | 3 | |
|
4 | 4 | <h1>cs_comment, is_mention=False, status_change=None</h1> |
|
5 | 5 | <pre> |
|
6 | 6 | |
|
7 | 7 | From: u1 |
|
8 | 8 | To: u2@example.com |
|
9 | 9 | Subject: [Comment] repo/name changeset cafe1234 on brunch |
|
10 | 10 | |
|
11 | 11 | -------------------- |
|
12 | 12 | |
|
13 | 13 | |
|
14 | 14 | Comment from Opinionated User (jsmith) on repo_target changeset cafe1234c0ff: |
|
15 | 15 | This is the new comment. |
|
16 | 16 | |
|
17 | 17 | - and here it ends indented. |
|
18 | 18 | |
|
19 | 19 | |
|
20 | 20 | URL: http://comment.org |
|
21 | 21 | |
|
22 | 22 | Changeset: cafe1234c0ff |
|
23 | 23 | Description: |
|
24 | 24 | This changeset did something clever which is hard to explain |
|
25 | 25 | |
|
26 | 26 | |
|
27 | 27 | -- |
|
28 | 28 | This is an automatic notification. Don't reply to this mail. |
|
29 | 29 | |
|
30 | 30 | --------------------</pre> |
|
31 | 31 | |
|
32 | 32 | |
|
33 | 33 | |
|
34 | 34 | <p>Comment from Opinionated User (jsmith) on repo_target changeset cafe1234c0ff:</p> |
|
35 | 35 | <p><div class="formatted-fixed">This is the new comment. |
|
36 | 36 | |
|
37 | 37 | - and here it ends indented.</div></p> |
|
38 | 38 | |
|
39 | 39 | |
|
40 | 40 | <p>URL: <a href="http://comment.org">http://comment.org</a></p> |
|
41 | 41 | |
|
42 | 42 | <p>Changeset: cafe1234c0ff</p> |
|
43 | 43 | <p>Description:<br/> |
|
44 | 44 | This changeset did something clever which is hard to explain |
|
45 | 45 | </p> |
|
46 | 46 | |
|
47 | 47 | |
|
48 | 48 | <br/> |
|
49 | 49 | <br/> |
|
50 | 50 | -- <br/> |
|
51 | 51 | This is an automatic notification. Don't reply to this mail. |
|
52 | 52 | |
|
53 | 53 | <pre>--------------------</pre> |
|
54 | 54 | |
|
55 | 55 | |
|
56 | 56 | <h1>cs_comment, is_mention=True, status_change=None</h1> |
|
57 | 57 | <pre> |
|
58 | 58 | |
|
59 | 59 | From: u1 |
|
60 | 60 | To: u2@example.com |
|
61 | 61 | Subject: [Comment] repo/name changeset cafe1234 on brunch |
|
62 | 62 | |
|
63 | 63 | -------------------- |
|
64 | 64 | |
|
65 | 65 | |
|
66 | 66 | Comment from Opinionated User (jsmith) on repo_target changeset cafe1234c0ff mentioned you: |
|
67 | 67 | This is the new comment. |
|
68 | 68 | |
|
69 | 69 | - and here it ends indented. |
|
70 | 70 | |
|
71 | 71 | |
|
72 | 72 | URL: http://comment.org |
|
73 | 73 | |
|
74 | 74 | Changeset: cafe1234c0ff |
|
75 | 75 | Description: |
|
76 | 76 | This changeset did something clever which is hard to explain |
|
77 | 77 | |
|
78 | 78 | |
|
79 | 79 | -- |
|
80 | 80 | This is an automatic notification. Don't reply to this mail. |
|
81 | 81 | |
|
82 | 82 | --------------------</pre> |
|
83 | 83 | |
|
84 | 84 | |
|
85 | 85 | |
|
86 | 86 | <p>Comment from Opinionated User (jsmith) on repo_target changeset cafe1234c0ff mentioned you:</p> |
|
87 | 87 | <p><div class="formatted-fixed">This is the new comment. |
|
88 | 88 | |
|
89 | 89 | - and here it ends indented.</div></p> |
|
90 | 90 | |
|
91 | 91 | |
|
92 | 92 | <p>URL: <a href="http://comment.org">http://comment.org</a></p> |
|
93 | 93 | |
|
94 | 94 | <p>Changeset: cafe1234c0ff</p> |
|
95 | 95 | <p>Description:<br/> |
|
96 | 96 | This changeset did something clever which is hard to explain |
|
97 | 97 | </p> |
|
98 | 98 | |
|
99 | 99 | |
|
100 | 100 | <br/> |
|
101 | 101 | <br/> |
|
102 | 102 | -- <br/> |
|
103 | 103 | This is an automatic notification. Don't reply to this mail. |
|
104 | 104 | |
|
105 | 105 | <pre>--------------------</pre> |
|
106 | 106 | |
|
107 | 107 | |
|
108 | 108 | <h1>cs_comment, is_mention=False, status_change='Approved'</h1> |
|
109 | 109 | <pre> |
|
110 | 110 | |
|
111 | 111 | From: u1 |
|
112 | 112 | To: u2@example.com |
|
113 | 113 | Subject: [Approved: Comment] repo/name changeset cafe1234 on brunch |
|
114 | 114 | |
|
115 | 115 | -------------------- |
|
116 | 116 | |
|
117 | 117 | |
|
118 | 118 | Comment from Opinionated User (jsmith) on repo_target changeset cafe1234c0ff: |
|
119 | 119 | This is the new comment. |
|
120 | 120 | |
|
121 | 121 | - and here it ends indented. |
|
122 | 122 | |
|
123 | 123 | The changeset status was changed to: Approved |
|
124 | 124 | |
|
125 | 125 | URL: http://comment.org |
|
126 | 126 | |
|
127 | 127 | Changeset: cafe1234c0ff |
|
128 | 128 | Description: |
|
129 | 129 | This changeset did something clever which is hard to explain |
|
130 | 130 | |
|
131 | 131 | |
|
132 | 132 | -- |
|
133 | 133 | This is an automatic notification. Don't reply to this mail. |
|
134 | 134 | |
|
135 | 135 | --------------------</pre> |
|
136 | 136 | |
|
137 | 137 | |
|
138 | 138 | |
|
139 | 139 | <p>Comment from Opinionated User (jsmith) on repo_target changeset cafe1234c0ff:</p> |
|
140 | 140 | <p><div class="formatted-fixed">This is the new comment. |
|
141 | 141 | |
|
142 | 142 | - and here it ends indented.</div></p> |
|
143 | 143 | |
|
144 | 144 | <p>The changeset status was changed to: <b>Approved</b></p> |
|
145 | 145 | |
|
146 | 146 | <p>URL: <a href="http://comment.org">http://comment.org</a></p> |
|
147 | 147 | |
|
148 | 148 | <p>Changeset: cafe1234c0ff</p> |
|
149 | 149 | <p>Description:<br/> |
|
150 | 150 | This changeset did something clever which is hard to explain |
|
151 | 151 | </p> |
|
152 | 152 | |
|
153 | 153 | |
|
154 | 154 | <br/> |
|
155 | 155 | <br/> |
|
156 | 156 | -- <br/> |
|
157 | 157 | This is an automatic notification. Don't reply to this mail. |
|
158 | 158 | |
|
159 | 159 | <pre>--------------------</pre> |
|
160 | 160 | |
|
161 | 161 | |
|
162 | 162 | <h1>cs_comment, is_mention=True, status_change='Approved'</h1> |
|
163 | 163 | <pre> |
|
164 | 164 | |
|
165 | 165 | From: u1 |
|
166 | 166 | To: u2@example.com |
|
167 | 167 | Subject: [Approved: Comment] repo/name changeset cafe1234 on brunch |
|
168 | 168 | |
|
169 | 169 | -------------------- |
|
170 | 170 | |
|
171 | 171 | |
|
172 | 172 | Comment from Opinionated User (jsmith) on repo_target changeset cafe1234c0ff mentioned you: |
|
173 | 173 | This is the new comment. |
|
174 | 174 | |
|
175 | 175 | - and here it ends indented. |
|
176 | 176 | |
|
177 | 177 | The changeset status was changed to: Approved |
|
178 | 178 | |
|
179 | 179 | URL: http://comment.org |
|
180 | 180 | |
|
181 | 181 | Changeset: cafe1234c0ff |
|
182 | 182 | Description: |
|
183 | 183 | This changeset did something clever which is hard to explain |
|
184 | 184 | |
|
185 | 185 | |
|
186 | 186 | -- |
|
187 | 187 | This is an automatic notification. Don't reply to this mail. |
|
188 | 188 | |
|
189 | 189 | --------------------</pre> |
|
190 | 190 | |
|
191 | 191 | |
|
192 | 192 | |
|
193 | 193 | <p>Comment from Opinionated User (jsmith) on repo_target changeset cafe1234c0ff mentioned you:</p> |
|
194 | 194 | <p><div class="formatted-fixed">This is the new comment. |
|
195 | 195 | |
|
196 | 196 | - and here it ends indented.</div></p> |
|
197 | 197 | |
|
198 | 198 | <p>The changeset status was changed to: <b>Approved</b></p> |
|
199 | 199 | |
|
200 | 200 | <p>URL: <a href="http://comment.org">http://comment.org</a></p> |
|
201 | 201 | |
|
202 | 202 | <p>Changeset: cafe1234c0ff</p> |
|
203 | 203 | <p>Description:<br/> |
|
204 | 204 | This changeset did something clever which is hard to explain |
|
205 | 205 | </p> |
|
206 | 206 | |
|
207 | 207 | |
|
208 | 208 | <br/> |
|
209 | 209 | <br/> |
|
210 | 210 | -- <br/> |
|
211 | 211 | This is an automatic notification. Don't reply to this mail. |
|
212 | 212 | |
|
213 | 213 | <pre>--------------------</pre> |
|
214 | 214 | |
|
215 | 215 | |
|
216 | 216 | <h1>message</h1> |
|
217 | 217 | <pre> |
|
218 | 218 | |
|
219 | 219 | From: u1 |
|
220 | 220 | To: u2@example.com |
|
221 | 221 | Subject: Test Message |
|
222 | 222 | |
|
223 | 223 | -------------------- |
|
224 | 224 | |
|
225 | 225 | |
|
226 | 226 | This is the body of the test message |
|
227 | 227 | - nothing interesting here except indentation. |
|
228 | 228 | |
|
229 | 229 | |
|
230 | 230 | -- |
|
231 | 231 | This is an automatic notification. Don't reply to this mail. |
|
232 | 232 | |
|
233 | 233 | --------------------</pre> |
|
234 | 234 | |
|
235 | 235 | |
|
236 | 236 | |
|
237 | 237 | <div class="formatted-fixed">This is the body of the test message |
|
238 | 238 | - nothing interesting here except indentation.</div> |
|
239 | 239 | |
|
240 | 240 | |
|
241 | 241 | <br/> |
|
242 | 242 | <br/> |
|
243 | 243 | -- <br/> |
|
244 | 244 | This is an automatic notification. Don't reply to this mail. |
|
245 | 245 | |
|
246 | 246 | <pre>--------------------</pre> |
|
247 | 247 | |
|
248 | 248 | |
|
249 | 249 | <h1>registration</h1> |
|
250 | 250 | <pre> |
|
251 | 251 | |
|
252 | 252 | From: u1 |
|
253 | 253 | To: u2@example.com |
|
254 | 254 | Subject: New user newbie registered |
|
255 | 255 | |
|
256 | 256 | -------------------- |
|
257 | 257 | |
|
258 | 258 | |
|
259 | 259 | Registration body |
|
260 | 260 | |
|
261 | 261 | View this user here: http://newbie.org |
|
262 | 262 | |
|
263 | 263 | |
|
264 | 264 | -- |
|
265 | 265 | This is an automatic notification. Don't reply to this mail. |
|
266 | 266 | |
|
267 | 267 | --------------------</pre> |
|
268 | 268 | |
|
269 | 269 | |
|
270 | 270 | |
|
271 | 271 | <div class="formatted-fixed">Registration body</div> |
|
272 | 272 | |
|
273 | 273 | View this user here: <a href="http://newbie.org">http://newbie.org</a> |
|
274 | 274 | |
|
275 | 275 | |
|
276 | 276 | <br/> |
|
277 | 277 | <br/> |
|
278 | 278 | -- <br/> |
|
279 | 279 | This is an automatic notification. Don't reply to this mail. |
|
280 | 280 | |
|
281 | 281 | <pre>--------------------</pre> |
|
282 | 282 | |
|
283 | 283 | |
|
284 | 284 | <h1>pull_request, is_mention=False</h1> |
|
285 | 285 | <pre> |
|
286 | 286 | |
|
287 | 287 | From: u1 |
|
288 | 288 | To: u2@example.com |
|
289 | 289 | Subject: [Added] repo/name pull request #7 from devbranch |
|
290 | 290 | |
|
291 | 291 | -------------------- |
|
292 | 292 | |
|
293 | 293 | |
|
294 | 294 | Requesting User (root) requested your review of repo/name pull request "The Title" |
|
295 | 295 | |
|
296 | 296 | URL: http://pr.org/7 |
|
297 | 297 | |
|
298 | 298 | Description: |
|
299 | 299 | This PR is awesome because it does stuff |
|
300 | 300 | - please approve indented! |
|
301 | 301 | |
|
302 | 302 | Changesets: |
|
303 | 303 | 123abc123abc: http://changeset_home/?repo_name=repo_org&revision=123abc123abc123abc123abc123abc123abc123abc |
|
304 | 304 | Introduce one and two |
|
305 | 305 | |
|
306 | 306 | and that's it |
|
307 | 307 | |
|
308 | 308 | 567fed567fed: http://changeset_home/?repo_name=repo_org&revision=567fed567fed567fed567fed567fed567fed567fed |
|
309 | 309 | Make one plus two equal tree |
|
310 | 310 | |
|
311 | 311 | |
|
312 | 312 | |
|
313 | 313 | -- |
|
314 | 314 | This is an automatic notification. Don't reply to this mail. |
|
315 | 315 | |
|
316 | 316 | --------------------</pre> |
|
317 | 317 | |
|
318 | 318 | |
|
319 | 319 | |
|
320 | 320 | <p>Requesting User (root) requested your review of repo/name pull request "The Title"</p> |
|
321 | 321 | |
|
322 | 322 | <p>URL: <a href="http://pr.org/7">http://pr.org/7</a></p> |
|
323 | 323 | |
|
324 | 324 | <p>Description:</p> |
|
325 | 325 | <p style="white-space: pre-wrap; font-family: monospace"><div class="formatted-fixed">This PR is awesome because it does stuff |
|
326 | 326 | - please approve indented!</div></p> |
|
327 | 327 | |
|
328 | 328 | <p>Changesets:</p> |
|
329 | 329 | <p style="white-space: pre-wrap"> |
|
330 | 330 | <i><a href="http://changeset_home/?repo_name=repo_org&revision=123abc123abc123abc123abc123abc123abc123abc">123abc123abc</a></i>: |
|
331 | 331 | Introduce one and two |
|
332 | 332 | |
|
333 | 333 | and that's it |
|
334 | 334 | |
|
335 | 335 | <i><a href="http://changeset_home/?repo_name=repo_org&revision=567fed567fed567fed567fed567fed567fed567fed">567fed567fed</a></i>: |
|
336 | 336 | Make one plus two equal tree |
|
337 | 337 | |
|
338 | 338 | </p> |
|
339 | 339 | |
|
340 | 340 | |
|
341 | 341 | <br/> |
|
342 | 342 | <br/> |
|
343 | 343 | -- <br/> |
|
344 | 344 | This is an automatic notification. Don't reply to this mail. |
|
345 | 345 | |
|
346 | 346 | <pre>--------------------</pre> |
|
347 | 347 | |
|
348 | 348 | |
|
349 | 349 | <h1>pull_request, is_mention=True</h1> |
|
350 | 350 | <pre> |
|
351 | 351 | |
|
352 | 352 | From: u1 |
|
353 | 353 | To: u2@example.com |
|
354 | 354 | Subject: [Added] repo/name pull request #7 from devbranch |
|
355 | 355 | |
|
356 | 356 | -------------------- |
|
357 | 357 | |
|
358 | 358 | |
|
359 | 359 | Requesting User (root) mentioned you on repo/name pull request "The Title" |
|
360 | 360 | |
|
361 | 361 | URL: http://pr.org/7 |
|
362 | 362 | |
|
363 | 363 | Description: |
|
364 | 364 | This PR is awesome because it does stuff |
|
365 | 365 | - please approve indented! |
|
366 | 366 | |
|
367 | 367 | Changesets: |
|
368 | 368 | 123abc123abc: http://changeset_home/?repo_name=repo_org&revision=123abc123abc123abc123abc123abc123abc123abc |
|
369 | 369 | Introduce one and two |
|
370 | 370 | |
|
371 | 371 | and that's it |
|
372 | 372 | |
|
373 | 373 | 567fed567fed: http://changeset_home/?repo_name=repo_org&revision=567fed567fed567fed567fed567fed567fed567fed |
|
374 | 374 | Make one plus two equal tree |
|
375 | 375 | |
|
376 | 376 | |
|
377 | 377 | |
|
378 | 378 | -- |
|
379 | 379 | This is an automatic notification. Don't reply to this mail. |
|
380 | 380 | |
|
381 | 381 | --------------------</pre> |
|
382 | 382 | |
|
383 | 383 | |
|
384 | 384 | |
|
385 | 385 | <p>Requesting User (root) mentioned you on repo/name pull request "The Title"</p> |
|
386 | 386 | |
|
387 | 387 | <p>URL: <a href="http://pr.org/7">http://pr.org/7</a></p> |
|
388 | 388 | |
|
389 | 389 | <p>Description:</p> |
|
390 | 390 | <p style="white-space: pre-wrap; font-family: monospace"><div class="formatted-fixed">This PR is awesome because it does stuff |
|
391 | 391 | - please approve indented!</div></p> |
|
392 | 392 | |
|
393 | 393 | <p>Changesets:</p> |
|
394 | 394 | <p style="white-space: pre-wrap"> |
|
395 | 395 | <i><a href="http://changeset_home/?repo_name=repo_org&revision=123abc123abc123abc123abc123abc123abc123abc">123abc123abc</a></i>: |
|
396 | 396 | Introduce one and two |
|
397 | 397 | |
|
398 | 398 | and that's it |
|
399 | 399 | |
|
400 | 400 | <i><a href="http://changeset_home/?repo_name=repo_org&revision=567fed567fed567fed567fed567fed567fed567fed">567fed567fed</a></i>: |
|
401 | 401 | Make one plus two equal tree |
|
402 | 402 | |
|
403 | 403 | </p> |
|
404 | 404 | |
|
405 | 405 | |
|
406 | 406 | <br/> |
|
407 | 407 | <br/> |
|
408 | 408 | -- <br/> |
|
409 | 409 | This is an automatic notification. Don't reply to this mail. |
|
410 | 410 | |
|
411 | 411 | <pre>--------------------</pre> |
|
412 | 412 | |
|
413 | 413 | |
|
414 | <h1>pull_request_comment, status_change=None, closing_pr=False</h1> | |
|
414 | <h1>pull_request_comment, is_mention=False, status_change=None, closing_pr=False</h1> | |
|
415 | 415 | <pre> |
|
416 | 416 | |
|
417 | 417 | From: u1 |
|
418 | 418 | To: u2@example.com |
|
419 | 419 | Subject: [Comment] repo/name pull request #7 from devbranch |
|
420 | 420 | |
|
421 | 421 | -------------------- |
|
422 | 422 | |
|
423 | 423 | |
|
424 | 424 | Comment from Opinionated User (jsmith) on repo/name pull request "The Title": |
|
425 | 425 | Me too! |
|
426 | 426 | |
|
427 | 427 | - and indented on second line |
|
428 | 428 | |
|
429 | 429 | |
|
430 | 430 | URL: http://pr.org/comment |
|
431 | 431 | |
|
432 | 432 | |
|
433 | 433 | -- |
|
434 | 434 | This is an automatic notification. Don't reply to this mail. |
|
435 | 435 | |
|
436 | 436 | --------------------</pre> |
|
437 | 437 | |
|
438 | 438 | |
|
439 | 439 | |
|
440 | 440 | <p>Comment from Opinionated User (jsmith) on repo/name pull request "The Title":</p> |
|
441 | 441 | <p><div class="formatted-fixed">Me too! |
|
442 | 442 | |
|
443 | 443 | - and indented on second line</div></p> |
|
444 | 444 | |
|
445 | 445 | |
|
446 | 446 | <p>URL: <a href="http://pr.org/comment">http://pr.org/comment</a></p> |
|
447 | 447 | |
|
448 | 448 | |
|
449 | 449 | <br/> |
|
450 | 450 | <br/> |
|
451 | 451 | -- <br/> |
|
452 | 452 | This is an automatic notification. Don't reply to this mail. |
|
453 | 453 | |
|
454 | 454 | <pre>--------------------</pre> |
|
455 | 455 | |
|
456 | 456 | |
|
457 |
<h1>pull_request_comment, status_change= |
|
|
457 | <h1>pull_request_comment, is_mention=True, status_change=None, closing_pr=False</h1> | |
|
458 | <pre> | |
|
459 | ||
|
460 | From: u1 | |
|
461 | To: u2@example.com | |
|
462 | Subject: [Comment] repo/name pull request #7 from devbranch | |
|
463 | ||
|
464 | -------------------- | |
|
465 | ||
|
466 | ||
|
467 | Comment from Opinionated User (jsmith) on repo/name pull request "The Title": | |
|
468 | Me too! | |
|
469 | ||
|
470 | - and indented on second line | |
|
471 | ||
|
472 | ||
|
473 | URL: http://pr.org/comment | |
|
474 | ||
|
475 | ||
|
476 | -- | |
|
477 | This is an automatic notification. Don't reply to this mail. | |
|
478 | ||
|
479 | --------------------</pre> | |
|
480 | ||
|
481 | ||
|
482 | ||
|
483 | <p>Comment from Opinionated User (jsmith) on repo/name pull request "The Title":</p> | |
|
484 | <p><div class="formatted-fixed">Me too! | |
|
485 | ||
|
486 | - and indented on second line</div></p> | |
|
487 | ||
|
488 | ||
|
489 | <p>URL: <a href="http://pr.org/comment">http://pr.org/comment</a></p> | |
|
490 | ||
|
491 | ||
|
492 | <br/> | |
|
493 | <br/> | |
|
494 | -- <br/> | |
|
495 | This is an automatic notification. Don't reply to this mail. | |
|
496 | ||
|
497 | <pre>--------------------</pre> | |
|
498 | ||
|
499 | ||
|
500 | <h1>pull_request_comment, is_mention=False, status_change='Under Review', closing_pr=False</h1> | |
|
458 | 501 | <pre> |
|
459 | 502 | |
|
460 | 503 | From: u1 |
|
461 | 504 | To: u2@example.com |
|
462 | 505 | Subject: [Under Review: Comment] repo/name pull request #7 from devbranch |
|
463 | 506 | |
|
464 | 507 | -------------------- |
|
465 | 508 | |
|
466 | 509 | |
|
467 | 510 | Comment from Opinionated User (jsmith) on repo/name pull request "The Title": |
|
468 | 511 | Me too! |
|
469 | 512 | |
|
470 | 513 | - and indented on second line |
|
471 | 514 | |
|
472 | 515 | The comment was made with status: Under Review |
|
473 | 516 | |
|
474 | 517 | URL: http://pr.org/comment |
|
475 | 518 | |
|
476 | 519 | |
|
477 | 520 | -- |
|
478 | 521 | This is an automatic notification. Don't reply to this mail. |
|
479 | 522 | |
|
480 | 523 | --------------------</pre> |
|
481 | 524 | |
|
482 | 525 | |
|
483 | 526 | |
|
484 | 527 | <p>Comment from Opinionated User (jsmith) on repo/name pull request "The Title":</p> |
|
485 | 528 | <p><div class="formatted-fixed">Me too! |
|
486 | 529 | |
|
487 | 530 | - and indented on second line</div></p> |
|
488 | 531 | |
|
489 | 532 | <p>The comment was made with status: <b>Under Review</b></p> |
|
490 | 533 | |
|
491 | 534 | <p>URL: <a href="http://pr.org/comment">http://pr.org/comment</a></p> |
|
492 | 535 | |
|
493 | 536 | |
|
494 | 537 | <br/> |
|
495 | 538 | <br/> |
|
496 | 539 | -- <br/> |
|
497 | 540 | This is an automatic notification. Don't reply to this mail. |
|
498 | 541 | |
|
499 | 542 | <pre>--------------------</pre> |
|
500 | 543 | |
|
501 | 544 | |
|
502 |
<h1>pull_request_comment, status_change= |
|
|
545 | <h1>pull_request_comment, is_mention=True, status_change='Under Review', closing_pr=False</h1> | |
|
546 | <pre> | |
|
547 | ||
|
548 | From: u1 | |
|
549 | To: u2@example.com | |
|
550 | Subject: [Under Review: Comment] repo/name pull request #7 from devbranch | |
|
551 | ||
|
552 | -------------------- | |
|
553 | ||
|
554 | ||
|
555 | Comment from Opinionated User (jsmith) on repo/name pull request "The Title": | |
|
556 | Me too! | |
|
557 | ||
|
558 | - and indented on second line | |
|
559 | ||
|
560 | The comment was made with status: Under Review | |
|
561 | ||
|
562 | URL: http://pr.org/comment | |
|
563 | ||
|
564 | ||
|
565 | -- | |
|
566 | This is an automatic notification. Don't reply to this mail. | |
|
567 | ||
|
568 | --------------------</pre> | |
|
569 | ||
|
570 | ||
|
571 | ||
|
572 | <p>Comment from Opinionated User (jsmith) on repo/name pull request "The Title":</p> | |
|
573 | <p><div class="formatted-fixed">Me too! | |
|
574 | ||
|
575 | - and indented on second line</div></p> | |
|
576 | ||
|
577 | <p>The comment was made with status: <b>Under Review</b></p> | |
|
578 | ||
|
579 | <p>URL: <a href="http://pr.org/comment">http://pr.org/comment</a></p> | |
|
580 | ||
|
581 | ||
|
582 | <br/> | |
|
583 | <br/> | |
|
584 | -- <br/> | |
|
585 | This is an automatic notification. Don't reply to this mail. | |
|
586 | ||
|
587 | <pre>--------------------</pre> | |
|
588 | ||
|
589 | ||
|
590 | <h1>pull_request_comment, is_mention=False, status_change=None, closing_pr=True</h1> | |
|
503 | 591 | <pre> |
|
504 | 592 | |
|
505 | 593 | From: u1 |
|
506 | 594 | To: u2@example.com |
|
507 | 595 | Subject: [Closing: Comment] repo/name pull request #7 from devbranch |
|
508 | 596 | |
|
509 | 597 | -------------------- |
|
510 | 598 | |
|
511 | 599 | |
|
512 | 600 | Comment from Opinionated User (jsmith) on repo/name pull request "The Title": |
|
513 | 601 | Me too! |
|
514 | 602 | |
|
515 | 603 | - and indented on second line |
|
516 | 604 | |
|
517 | 605 | |
|
518 | 606 | URL: http://pr.org/comment |
|
519 | 607 | |
|
520 | 608 | |
|
521 | 609 | -- |
|
522 | 610 | This is an automatic notification. Don't reply to this mail. |
|
523 | 611 | |
|
524 | 612 | --------------------</pre> |
|
525 | 613 | |
|
526 | 614 | |
|
527 | 615 | |
|
528 | 616 | <p>Comment from Opinionated User (jsmith) on repo/name pull request "The Title":</p> |
|
529 | 617 | <p><div class="formatted-fixed">Me too! |
|
530 | 618 | |
|
531 | 619 | - and indented on second line</div></p> |
|
532 | 620 | |
|
533 | 621 | |
|
534 | 622 | <p>URL: <a href="http://pr.org/comment">http://pr.org/comment</a></p> |
|
535 | 623 | |
|
536 | 624 | |
|
537 | 625 | <br/> |
|
538 | 626 | <br/> |
|
539 | 627 | -- <br/> |
|
540 | 628 | This is an automatic notification. Don't reply to this mail. |
|
541 | 629 | |
|
542 | 630 | <pre>--------------------</pre> |
|
543 | 631 | |
|
544 | 632 | |
|
545 |
<h1>pull_request_comment, status_change= |
|
|
633 | <h1>pull_request_comment, is_mention=True, status_change=None, closing_pr=True</h1> | |
|
634 | <pre> | |
|
635 | ||
|
636 | From: u1 | |
|
637 | To: u2@example.com | |
|
638 | Subject: [Closing: Comment] repo/name pull request #7 from devbranch | |
|
639 | ||
|
640 | -------------------- | |
|
641 | ||
|
642 | ||
|
643 | Comment from Opinionated User (jsmith) on repo/name pull request "The Title": | |
|
644 | Me too! | |
|
645 | ||
|
646 | - and indented on second line | |
|
647 | ||
|
648 | ||
|
649 | URL: http://pr.org/comment | |
|
650 | ||
|
651 | ||
|
652 | -- | |
|
653 | This is an automatic notification. Don't reply to this mail. | |
|
654 | ||
|
655 | --------------------</pre> | |
|
656 | ||
|
657 | ||
|
658 | ||
|
659 | <p>Comment from Opinionated User (jsmith) on repo/name pull request "The Title":</p> | |
|
660 | <p><div class="formatted-fixed">Me too! | |
|
661 | ||
|
662 | - and indented on second line</div></p> | |
|
663 | ||
|
664 | ||
|
665 | <p>URL: <a href="http://pr.org/comment">http://pr.org/comment</a></p> | |
|
666 | ||
|
667 | ||
|
668 | <br/> | |
|
669 | <br/> | |
|
670 | -- <br/> | |
|
671 | This is an automatic notification. Don't reply to this mail. | |
|
672 | ||
|
673 | <pre>--------------------</pre> | |
|
674 | ||
|
675 | ||
|
676 | <h1>pull_request_comment, is_mention=False, status_change='Under Review', closing_pr=True</h1> | |
|
677 | <pre> | |
|
678 | ||
|
679 | From: u1 | |
|
680 | To: u2@example.com | |
|
681 | Subject: [Under Review, Closing: Comment] repo/name pull request #7 from devbranch | |
|
682 | ||
|
683 | -------------------- | |
|
684 | ||
|
685 | ||
|
686 | Comment from Opinionated User (jsmith) on repo/name pull request "The Title": | |
|
687 | Me too! | |
|
688 | ||
|
689 | - and indented on second line | |
|
690 | ||
|
691 | The comment closed the pull request with status: Under Review | |
|
692 | ||
|
693 | URL: http://pr.org/comment | |
|
694 | ||
|
695 | ||
|
696 | -- | |
|
697 | This is an automatic notification. Don't reply to this mail. | |
|
698 | ||
|
699 | --------------------</pre> | |
|
700 | ||
|
701 | ||
|
702 | ||
|
703 | <p>Comment from Opinionated User (jsmith) on repo/name pull request "The Title":</p> | |
|
704 | <p><div class="formatted-fixed">Me too! | |
|
705 | ||
|
706 | - and indented on second line</div></p> | |
|
707 | ||
|
708 | <p>The comment closed the pull request with status: <b>Under Review</b></p> | |
|
709 | ||
|
710 | <p>URL: <a href="http://pr.org/comment">http://pr.org/comment</a></p> | |
|
711 | ||
|
712 | ||
|
713 | <br/> | |
|
714 | <br/> | |
|
715 | -- <br/> | |
|
716 | This is an automatic notification. Don't reply to this mail. | |
|
717 | ||
|
718 | <pre>--------------------</pre> | |
|
719 | ||
|
720 | ||
|
721 | <h1>pull_request_comment, is_mention=True, status_change='Under Review', closing_pr=True</h1> | |
|
546 | 722 | <pre> |
|
547 | 723 | |
|
548 | 724 | From: u1 |
|
549 | 725 | To: u2@example.com |
|
550 | 726 | Subject: [Under Review, Closing: Comment] repo/name pull request #7 from devbranch |
|
551 | 727 | |
|
552 | 728 | -------------------- |
|
553 | 729 | |
|
554 | 730 | |
|
555 | 731 | Comment from Opinionated User (jsmith) on repo/name pull request "The Title": |
|
556 | 732 | Me too! |
|
557 | 733 | |
|
558 | 734 | - and indented on second line |
|
559 | 735 | |
|
560 | 736 | The comment closed the pull request with status: Under Review |
|
561 | 737 | |
|
562 | 738 | URL: http://pr.org/comment |
|
563 | 739 | |
|
564 | 740 | |
|
565 | 741 | -- |
|
566 | 742 | This is an automatic notification. Don't reply to this mail. |
|
567 | 743 | |
|
568 | 744 | --------------------</pre> |
|
569 | 745 | |
|
570 | 746 | |
|
571 | 747 | |
|
572 | 748 | <p>Comment from Opinionated User (jsmith) on repo/name pull request "The Title":</p> |
|
573 | 749 | <p><div class="formatted-fixed">Me too! |
|
574 | 750 | |
|
575 | 751 | - and indented on second line</div></p> |
|
576 | 752 | |
|
577 | 753 | <p>The comment closed the pull request with status: <b>Under Review</b></p> |
|
578 | 754 | |
|
579 | 755 | <p>URL: <a href="http://pr.org/comment">http://pr.org/comment</a></p> |
|
580 | 756 | |
|
581 | 757 | |
|
582 | 758 | <br/> |
|
583 | 759 | <br/> |
|
584 | 760 | -- <br/> |
|
585 | 761 | This is an automatic notification. Don't reply to this mail. |
|
586 | 762 | |
|
587 | 763 | <pre>--------------------</pre> |
|
588 | 764 | |
|
589 | 765 | |
|
590 | 766 | <h1>TYPE_PASSWORD_RESET</h1> |
|
591 | 767 | <pre> |
|
592 | 768 | |
|
593 | 769 | From: u1 |
|
594 | 770 | To: john@doe.com |
|
595 | 771 | Subject: Password reset link |
|
596 | 772 | |
|
597 | 773 | -------------------- |
|
598 | 774 | |
|
599 | 775 | |
|
600 | 776 | Hello John Doe |
|
601 | 777 | |
|
602 | 778 | We have received a request to reset the password for your account. |
|
603 | 779 | To set a new password, click the following link: |
|
604 | 780 | |
|
605 | 781 | http://reset.com/decbf64715098db5b0bd23eab44bd792670ab746 |
|
606 | 782 | |
|
607 | 783 | Should you not be able to use the link above, please type the following code into the password reset form: decbf64715098db5b0bd23eab44bd792670ab746 |
|
608 | 784 | |
|
609 | 785 | If it weren't you who requested the password reset, just disregard this message. |
|
610 | 786 | |
|
611 | 787 | |
|
612 | 788 | -- |
|
613 | 789 | This is an automatic notification. Don't reply to this mail. |
|
614 | 790 | |
|
615 | 791 | --------------------</pre> |
|
616 | 792 | |
|
617 | 793 | |
|
618 | 794 | |
|
619 | 795 | <h4>Hello John Doe</h4> |
|
620 | 796 | |
|
621 | 797 | <p>We have received a request to reset the password for your account.</p> |
|
622 | 798 | <p>To set a new password, click the following link:</p> |
|
623 | 799 | <p><a href="http://reset.com/decbf64715098db5b0bd23eab44bd792670ab746">http://reset.com/decbf64715098db5b0bd23eab44bd792670ab746</a></p> |
|
624 | 800 | |
|
625 | 801 | <p>Should you not be able to use the link above, please type the following code into the password reset form: <code>decbf64715098db5b0bd23eab44bd792670ab746</code></p> |
|
626 | 802 | |
|
627 | 803 | <p>If it weren't you who requested the password reset, just disregard this message.</p> |
|
628 | 804 | |
|
629 | 805 | |
|
630 | 806 | <br/> |
|
631 | 807 | <br/> |
|
632 | 808 | -- <br/> |
|
633 | 809 | This is an automatic notification. Don't reply to this mail. |
|
634 | 810 | |
|
635 | 811 | <pre>--------------------</pre> |
|
636 | 812 | |
|
637 | 813 | </body></html> |
@@ -1,277 +1,278 b'' | |||
|
1 | 1 | import os |
|
2 | 2 | |
|
3 | 3 | import mock |
|
4 | 4 | import routes.util |
|
5 | 5 | |
|
6 | 6 | from kallithea.tests import * |
|
7 | 7 | from kallithea.lib import helpers as h |
|
8 | 8 | from kallithea.model.db import User, Notification, UserNotification |
|
9 | 9 | from kallithea.model.user import UserModel |
|
10 | 10 | from kallithea.model.meta import Session |
|
11 | 11 | from kallithea.model.notification import NotificationModel, EmailNotificationModel |
|
12 | 12 | |
|
13 | 13 | import kallithea.lib.celerylib |
|
14 | 14 | import kallithea.lib.celerylib.tasks |
|
15 | 15 | |
|
16 | 16 | |
|
17 | 17 | class TestNotifications(TestController): |
|
18 | 18 | |
|
19 | 19 | def setup_method(self, method): |
|
20 | 20 | Session.remove() |
|
21 | 21 | u1 = UserModel().create_or_update(username=u'u1', |
|
22 | 22 | password=u'qweqwe', |
|
23 | 23 | email=u'u1@example.com', |
|
24 | 24 | firstname=u'u1', lastname=u'u1') |
|
25 | 25 | Session().commit() |
|
26 | 26 | self.u1 = u1.user_id |
|
27 | 27 | |
|
28 | 28 | u2 = UserModel().create_or_update(username=u'u2', |
|
29 | 29 | password=u'qweqwe', |
|
30 | 30 | email=u'u2@example.com', |
|
31 | 31 | firstname=u'u2', lastname=u'u3') |
|
32 | 32 | Session().commit() |
|
33 | 33 | self.u2 = u2.user_id |
|
34 | 34 | |
|
35 | 35 | u3 = UserModel().create_or_update(username=u'u3', |
|
36 | 36 | password=u'qweqwe', |
|
37 | 37 | email=u'u3@example.com', |
|
38 | 38 | firstname=u'u3', lastname=u'u3') |
|
39 | 39 | Session().commit() |
|
40 | 40 | self.u3 = u3.user_id |
|
41 | 41 | |
|
42 | 42 | self.remove_all_notifications() |
|
43 | 43 | assert [] == Notification.query().all() |
|
44 | 44 | assert [] == UserNotification.query().all() |
|
45 | 45 | |
|
46 | 46 | def test_create_notification(self): |
|
47 | 47 | usrs = [self.u1, self.u2] |
|
48 | 48 | def send_email(recipients, subject, body='', html_body='', headers=None, author=None): |
|
49 | 49 | assert recipients == ['u2@example.com'] |
|
50 | 50 | assert subject == 'Test Message' |
|
51 | 51 | assert body == "\n\nhi there\n\n\n-- \nThis is an automatic notification. Don't reply to this mail.\n" |
|
52 | 52 | assert '>hi there<' in html_body |
|
53 | 53 | assert author.username == 'u1' |
|
54 | 54 | with mock.patch.object(kallithea.lib.celerylib.tasks, 'send_email', send_email): |
|
55 | 55 | notification = NotificationModel().create(created_by=self.u1, |
|
56 | 56 | subject=u'subj', body=u'hi there', |
|
57 | 57 | recipients=usrs) |
|
58 | 58 | Session().commit() |
|
59 | 59 | u1 = User.get(self.u1) |
|
60 | 60 | u2 = User.get(self.u2) |
|
61 | 61 | u3 = User.get(self.u3) |
|
62 | 62 | notifications = Notification.query().all() |
|
63 | 63 | assert len(notifications) == 1 |
|
64 | 64 | |
|
65 | 65 | assert notifications[0].recipients == [u1, u2] |
|
66 | 66 | assert notification.notification_id == notifications[0].notification_id |
|
67 | 67 | |
|
68 | 68 | unotification = UserNotification.query() \ |
|
69 | 69 | .filter(UserNotification.notification == notification).all() |
|
70 | 70 | |
|
71 | 71 | assert len(unotification) == len(usrs) |
|
72 | 72 | assert set([x.user.user_id for x in unotification]) == set(usrs) |
|
73 | 73 | |
|
74 | 74 | def test_user_notifications(self): |
|
75 | 75 | notification1 = NotificationModel().create(created_by=self.u1, |
|
76 | 76 | subject=u'subj', body=u'hi there1', |
|
77 | 77 | recipients=[self.u3]) |
|
78 | 78 | Session().commit() |
|
79 | 79 | notification2 = NotificationModel().create(created_by=self.u1, |
|
80 | 80 | subject=u'subj', body=u'hi there2', |
|
81 | 81 | recipients=[self.u3]) |
|
82 | 82 | Session().commit() |
|
83 | 83 | u3 = Session().query(User).get(self.u3) |
|
84 | 84 | |
|
85 | 85 | assert sorted([x.notification for x in u3.notifications]) == sorted([notification2, notification1]) |
|
86 | 86 | |
|
87 | 87 | def test_delete_notifications(self): |
|
88 | 88 | notification = NotificationModel().create(created_by=self.u1, |
|
89 | 89 | subject=u'title', body=u'hi there3', |
|
90 | 90 | recipients=[self.u3, self.u1, self.u2]) |
|
91 | 91 | Session().commit() |
|
92 | 92 | notifications = Notification.query().all() |
|
93 | 93 | assert notification in notifications |
|
94 | 94 | |
|
95 | 95 | Notification.delete(notification.notification_id) |
|
96 | 96 | Session().commit() |
|
97 | 97 | |
|
98 | 98 | notifications = Notification.query().all() |
|
99 | 99 | assert not notification in notifications |
|
100 | 100 | |
|
101 | 101 | un = UserNotification.query().filter(UserNotification.notification |
|
102 | 102 | == notification).all() |
|
103 | 103 | assert un == [] |
|
104 | 104 | |
|
105 | 105 | def test_delete_association(self): |
|
106 | 106 | notification = NotificationModel().create(created_by=self.u1, |
|
107 | 107 | subject=u'title', body=u'hi there3', |
|
108 | 108 | recipients=[self.u3, self.u1, self.u2]) |
|
109 | 109 | Session().commit() |
|
110 | 110 | |
|
111 | 111 | unotification = UserNotification.query() \ |
|
112 | 112 | .filter(UserNotification.notification == |
|
113 | 113 | notification) \ |
|
114 | 114 | .filter(UserNotification.user_id == self.u3) \ |
|
115 | 115 | .scalar() |
|
116 | 116 | |
|
117 | 117 | assert unotification.user_id == self.u3 |
|
118 | 118 | |
|
119 | 119 | NotificationModel().delete(self.u3, |
|
120 | 120 | notification.notification_id) |
|
121 | 121 | Session().commit() |
|
122 | 122 | |
|
123 | 123 | u3notification = UserNotification.query() \ |
|
124 | 124 | .filter(UserNotification.notification == |
|
125 | 125 | notification) \ |
|
126 | 126 | .filter(UserNotification.user_id == self.u3) \ |
|
127 | 127 | .scalar() |
|
128 | 128 | |
|
129 | 129 | assert u3notification == None |
|
130 | 130 | |
|
131 | 131 | # notification object is still there |
|
132 | 132 | assert Notification.query().all() == [notification] |
|
133 | 133 | |
|
134 | 134 | #u1 and u2 still have assignments |
|
135 | 135 | u1notification = UserNotification.query() \ |
|
136 | 136 | .filter(UserNotification.notification == |
|
137 | 137 | notification) \ |
|
138 | 138 | .filter(UserNotification.user_id == self.u1) \ |
|
139 | 139 | .scalar() |
|
140 | 140 | assert u1notification != None |
|
141 | 141 | u2notification = UserNotification.query() \ |
|
142 | 142 | .filter(UserNotification.notification == |
|
143 | 143 | notification) \ |
|
144 | 144 | .filter(UserNotification.user_id == self.u2) \ |
|
145 | 145 | .scalar() |
|
146 | 146 | assert u2notification != None |
|
147 | 147 | |
|
148 | 148 | def test_notification_counter(self): |
|
149 | 149 | NotificationModel().create(created_by=self.u1, |
|
150 | 150 | subject=u'title', body=u'hi there_delete', |
|
151 | 151 | recipients=[self.u3, self.u1]) |
|
152 | 152 | Session().commit() |
|
153 | 153 | |
|
154 | 154 | assert NotificationModel().get_unread_cnt_for_user(self.u1) == 0 |
|
155 | 155 | assert NotificationModel().get_unread_cnt_for_user(self.u2) == 0 |
|
156 | 156 | assert NotificationModel().get_unread_cnt_for_user(self.u3) == 1 |
|
157 | 157 | |
|
158 | 158 | notification = NotificationModel().create(created_by=self.u1, |
|
159 | 159 | subject=u'title', body=u'hi there3', |
|
160 | 160 | recipients=[self.u3, self.u1, self.u2]) |
|
161 | 161 | Session().commit() |
|
162 | 162 | |
|
163 | 163 | assert NotificationModel().get_unread_cnt_for_user(self.u1) == 0 |
|
164 | 164 | assert NotificationModel().get_unread_cnt_for_user(self.u2) == 1 |
|
165 | 165 | assert NotificationModel().get_unread_cnt_for_user(self.u3) == 2 |
|
166 | 166 | |
|
167 | 167 | @mock.patch.object(h, 'canonical_url', (lambda arg, **kwargs: 'http://%s/?%s' % (arg, '&'.join('%s=%s' % (k, v) for (k, v) in sorted(kwargs.items()))))) |
|
168 | 168 | def test_dump_html_mails(self): |
|
169 | 169 | # Exercise all notification types and dump them to one big html file |
|
170 | 170 | l = [] |
|
171 | 171 | |
|
172 | 172 | def send_email(recipients, subject, body='', html_body='', headers=None, author=None): |
|
173 | 173 | l.append('\n\n<h1>%s</h1>\n' % desc) # desc is from outer scope |
|
174 | 174 | l.append('<pre>\n\n') |
|
175 | 175 | l.append('From: %s\n' % author.username) |
|
176 | 176 | l.append('To: %s\n' % ' '.join(recipients)) |
|
177 | 177 | l.append('Subject: %s\n' % subject) |
|
178 | 178 | l.append('\n--------------------\n%s\n--------------------' % body) |
|
179 | 179 | l.append('</pre>\n') |
|
180 | 180 | l.append('\n%s\n' % html_body) |
|
181 | 181 | l.append('<pre>--------------------</pre>\n') |
|
182 | 182 | |
|
183 | 183 | l.append('<html><body>\n') |
|
184 | 184 | with mock.patch.object(kallithea.lib.celerylib.tasks, 'send_email', send_email): |
|
185 | 185 | pr_kwargs = dict( |
|
186 | 186 | pr_nice_id='#7', |
|
187 | 187 | pr_title='The Title', |
|
188 | 188 | pr_url='http://pr.org/7', |
|
189 | 189 | pr_target_repo='http://mainline.com/repo', |
|
190 | 190 | pr_target_branch='trunk', |
|
191 | 191 | pr_source_repo='https://dev.org/repo', |
|
192 | 192 | pr_source_branch='devbranch', |
|
193 | 193 | pr_owner=User.get(self.u2), |
|
194 | 194 | ) |
|
195 | 195 | |
|
196 | 196 | for type_, body, kwargs in [ |
|
197 | 197 | (Notification.TYPE_CHANGESET_COMMENT, |
|
198 | 198 | u'This is the new comment.\n\n - and here it ends indented.', |
|
199 | 199 | dict( |
|
200 | 200 | short_id='cafe1234', |
|
201 | 201 | raw_id='cafe1234c0ffeecafe', |
|
202 | 202 | branch='brunch', |
|
203 | 203 | cs_comment_user='Opinionated User (jsmith)', |
|
204 | 204 | cs_comment_url='http://comment.org', |
|
205 | 205 | is_mention=[False, True], |
|
206 | 206 | message='This changeset did something clever which is hard to explain', |
|
207 | 207 | status_change=[None, 'Approved'], |
|
208 | 208 | cs_target_repo='repo_target', |
|
209 | 209 | cs_url='http://changeset.com', |
|
210 | 210 | cs_author=User.get(self.u2))), |
|
211 | 211 | (Notification.TYPE_MESSAGE, |
|
212 | 212 | u'This is the body of the test message\n - nothing interesting here except indentation.', |
|
213 | 213 | dict()), |
|
214 | 214 | #(Notification.TYPE_MENTION, '$body', None), # not used |
|
215 | 215 | (Notification.TYPE_REGISTRATION, |
|
216 | 216 | u'Registration body', |
|
217 | 217 | dict( |
|
218 | 218 | new_username='newbie', |
|
219 | 219 | registered_user_url='http://newbie.org', |
|
220 | 220 | new_email='new@email.com', |
|
221 | 221 | new_full_name='New Full Name')), |
|
222 | 222 | (Notification.TYPE_PULL_REQUEST, |
|
223 | 223 | u'This PR is awesome because it does stuff\n - please approve indented!', |
|
224 | 224 | dict( |
|
225 | 225 | pr_user_created='Requesting User (root)', # pr_owner should perhaps be used for @mention in description ... |
|
226 | 226 | is_mention=[False, True], |
|
227 | 227 | pr_revisions=[('123abc'*7, "Introduce one and two\n\nand that's it"), ('567fed'*7, 'Make one plus two equal tree')], |
|
228 | 228 | org_repo_name='repo_org', |
|
229 | 229 | **pr_kwargs)), |
|
230 | 230 | (Notification.TYPE_PULL_REQUEST_COMMENT, |
|
231 | 231 | u'Me too!\n\n - and indented on second line', |
|
232 | 232 | dict( |
|
233 | 233 | closing_pr=[False, True], |
|
234 | is_mention=[False, True], | |
|
234 | 235 | pr_comment_user='Opinionated User (jsmith)', |
|
235 | 236 | pr_comment_url='http://pr.org/comment', |
|
236 | 237 | status_change=[None, 'Under Review'], |
|
237 | 238 | **pr_kwargs)), |
|
238 | 239 | ]: |
|
239 | 240 | kwargs['repo_name'] = u'repo/name' |
|
240 | 241 | params = [(type_, type_, body, kwargs)] |
|
241 | 242 | for param_name in ['is_mention', 'status_change', 'closing_pr']: # TODO: inline/general |
|
242 | 243 | if not isinstance(kwargs.get(param_name), list): |
|
243 | 244 | continue |
|
244 | 245 | new_params = [] |
|
245 | 246 | for v in kwargs[param_name]: |
|
246 | 247 | for desc, type_, body, kwargs in params: |
|
247 | 248 | kwargs = dict(kwargs) |
|
248 | 249 | kwargs[param_name] = v |
|
249 | 250 | new_params.append(('%s, %s=%r' % (desc, param_name, v), type_, body, kwargs)) |
|
250 | 251 | params = new_params |
|
251 | 252 | |
|
252 | 253 | for desc, type_, body, kwargs in params: |
|
253 | 254 | # desc is used as "global" variable |
|
254 | 255 | notification = NotificationModel().create(created_by=self.u1, |
|
255 | 256 | subject=u'unused', body=body, email_kwargs=kwargs, |
|
256 | 257 | recipients=[self.u2], type_=type_) |
|
257 | 258 | |
|
258 | 259 | # Email type TYPE_PASSWORD_RESET has no corresponding notification type - test it directly: |
|
259 | 260 | desc = 'TYPE_PASSWORD_RESET' |
|
260 | 261 | kwargs = dict(user='John Doe', reset_token='decbf64715098db5b0bd23eab44bd792670ab746', reset_url='http://reset.com/decbf64715098db5b0bd23eab44bd792670ab746') |
|
261 | 262 | kallithea.lib.celerylib.run_task(kallithea.lib.celerylib.tasks.send_email, ['john@doe.com'], |
|
262 | 263 | "Password reset link", |
|
263 | 264 | EmailNotificationModel().get_email_tmpl(EmailNotificationModel.TYPE_PASSWORD_RESET, 'txt', **kwargs), |
|
264 | 265 | EmailNotificationModel().get_email_tmpl(EmailNotificationModel.TYPE_PASSWORD_RESET, 'html', **kwargs), |
|
265 | 266 | author=User.get(self.u1)) |
|
266 | 267 | |
|
267 | 268 | l.append('\n</body></html>\n') |
|
268 | 269 | out = ''.join(l) |
|
269 | 270 | |
|
270 | 271 | outfn = os.path.join(os.path.dirname(__file__), 'test_dump_html_mails.out.html') |
|
271 | 272 | reffn = os.path.join(os.path.dirname(__file__), 'test_dump_html_mails.ref.html') |
|
272 | 273 | with file(outfn, 'w') as f: |
|
273 | 274 | f.write(out) |
|
274 | 275 | with file(reffn) as f: |
|
275 | 276 | ref = f.read() |
|
276 | 277 | assert ref == out # copy test_dump_html_mails.out.html to test_dump_html_mails.ref.html to update expectations |
|
277 | 278 | os.unlink(outfn) |
General Comments 0
You need to be logged in to leave comments.
Login now