##// END OF EJS Templates
pull requeset: move stuff around, preparing for next change...
Mads Kiilerich -
r3301:3c91ec44 beta
parent child Browse files
Show More
@@ -1,260 +1,264
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.pull_request
3 rhodecode.model.pull_request
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 pull request model for RhodeCode
6 pull request model for RhodeCode
7
7
8 :created_on: Jun 6, 2012
8 :created_on: Jun 6, 2012
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2012-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2012-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 binascii
27 import binascii
28 import datetime
28 import datetime
29 import re
29 import re
30
30
31 from pylons.i18n.translation import _
31 from pylons.i18n.translation import _
32
32
33 from rhodecode.model.meta import Session
33 from rhodecode.model.meta import Session
34 from rhodecode.lib import helpers as h
34 from rhodecode.lib import helpers as h
35 from rhodecode.model import BaseModel
35 from rhodecode.model import BaseModel
36 from rhodecode.model.db import PullRequest, PullRequestReviewers, Notification,\
36 from rhodecode.model.db import PullRequest, PullRequestReviewers, Notification,\
37 ChangesetStatus
37 ChangesetStatus
38 from rhodecode.model.notification import NotificationModel
38 from rhodecode.model.notification import NotificationModel
39 from rhodecode.lib.utils2 import safe_unicode
39 from rhodecode.lib.utils2 import safe_unicode
40
40
41 from rhodecode.lib.vcs.utils.hgcompat import discovery, localrepo, scmutil, \
41 from rhodecode.lib.vcs.utils.hgcompat import discovery, localrepo, scmutil, \
42 findcommonoutgoing
42 findcommonoutgoing
43 from rhodecode.lib.vcs.utils import safe_str
43
44
44 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
45
46
46
47
47 class PullRequestModel(BaseModel):
48 class PullRequestModel(BaseModel):
48
49
49 cls = PullRequest
50 cls = PullRequest
50
51
51 def __get_pull_request(self, pull_request):
52 def __get_pull_request(self, pull_request):
52 return self._get_instance(PullRequest, pull_request)
53 return self._get_instance(PullRequest, pull_request)
53
54
54 def get_all(self, repo):
55 def get_all(self, repo):
55 repo = self._get_repo(repo)
56 repo = self._get_repo(repo)
56 return PullRequest.query()\
57 return PullRequest.query()\
57 .filter(PullRequest.other_repo == repo)\
58 .filter(PullRequest.other_repo == repo)\
58 .order_by(PullRequest.created_on)\
59 .order_by(PullRequest.created_on)\
59 .all()
60 .all()
60
61
61 def create(self, created_by, org_repo, org_ref, other_repo, other_ref,
62 def create(self, created_by, org_repo, org_ref, other_repo, other_ref,
62 revisions, reviewers, title, description=None):
63 revisions, reviewers, title, description=None):
63 from rhodecode.model.changeset_status import ChangesetStatusModel
64 from rhodecode.model.changeset_status import ChangesetStatusModel
64
65
65 created_by_user = self._get_user(created_by)
66 created_by_user = self._get_user(created_by)
66 org_repo = self._get_repo(org_repo)
67 org_repo = self._get_repo(org_repo)
67 other_repo = self._get_repo(other_repo)
68 other_repo = self._get_repo(other_repo)
68
69
69 new = PullRequest()
70 new = PullRequest()
70 new.org_repo = org_repo
71 new.org_repo = org_repo
71 new.org_ref = org_ref
72 new.org_ref = org_ref
72 new.other_repo = other_repo
73 new.other_repo = other_repo
73 new.other_ref = other_ref
74 new.other_ref = other_ref
74 new.revisions = revisions
75 new.revisions = revisions
75 new.title = title
76 new.title = title
76 new.description = description
77 new.description = description
77 new.author = created_by_user
78 new.author = created_by_user
78 self.sa.add(new)
79 self.sa.add(new)
79 Session().flush()
80 Session().flush()
80 #members
81 #members
81 for member in reviewers:
82 for member in reviewers:
82 _usr = self._get_user(member)
83 _usr = self._get_user(member)
83 reviewer = PullRequestReviewers(_usr, new)
84 reviewer = PullRequestReviewers(_usr, new)
84 self.sa.add(reviewer)
85 self.sa.add(reviewer)
85
86
86 #reset state to under-review
87 #reset state to under-review
87 ChangesetStatusModel().set_status(
88 ChangesetStatusModel().set_status(
88 repo=org_repo,
89 repo=org_repo,
89 status=ChangesetStatus.STATUS_UNDER_REVIEW,
90 status=ChangesetStatus.STATUS_UNDER_REVIEW,
90 user=created_by_user,
91 user=created_by_user,
91 pull_request=new
92 pull_request=new
92 )
93 )
93
94
94 #notification to reviewers
95 #notification to reviewers
95 notif = NotificationModel()
96 notif = NotificationModel()
96
97
97 pr_url = h.url('pullrequest_show', repo_name=other_repo.repo_name,
98 pr_url = h.url('pullrequest_show', repo_name=other_repo.repo_name,
98 pull_request_id=new.pull_request_id,
99 pull_request_id=new.pull_request_id,
99 qualified=True,
100 qualified=True,
100 )
101 )
101 subject = safe_unicode(
102 subject = safe_unicode(
102 h.link_to(
103 h.link_to(
103 _('%(user)s wants you to review pull request #%(pr_id)s: %(pr_title)s') % \
104 _('%(user)s wants you to review pull request #%(pr_id)s: %(pr_title)s') % \
104 {'user': created_by_user.username,
105 {'user': created_by_user.username,
105 'pr_title': new.title,
106 'pr_title': new.title,
106 'pr_id': new.pull_request_id},
107 'pr_id': new.pull_request_id},
107 pr_url
108 pr_url
108 )
109 )
109 )
110 )
110 body = description
111 body = description
111 kwargs = {
112 kwargs = {
112 'pr_title': title,
113 'pr_title': title,
113 'pr_user_created': h.person(created_by_user.email),
114 'pr_user_created': h.person(created_by_user.email),
114 'pr_repo_url': h.url('summary_home', repo_name=other_repo.repo_name,
115 'pr_repo_url': h.url('summary_home', repo_name=other_repo.repo_name,
115 qualified=True,),
116 qualified=True,),
116 'pr_url': pr_url,
117 'pr_url': pr_url,
117 'pr_revisions': revisions
118 'pr_revisions': revisions
118 }
119 }
119 notif.create(created_by=created_by_user, subject=subject, body=body,
120 notif.create(created_by=created_by_user, subject=subject, body=body,
120 recipients=reviewers,
121 recipients=reviewers,
121 type_=Notification.TYPE_PULL_REQUEST, email_kwargs=kwargs)
122 type_=Notification.TYPE_PULL_REQUEST, email_kwargs=kwargs)
122 return new
123 return new
123
124
124 def update_reviewers(self, pull_request, reviewers_ids):
125 def update_reviewers(self, pull_request, reviewers_ids):
125 reviewers_ids = set(reviewers_ids)
126 reviewers_ids = set(reviewers_ids)
126 pull_request = self.__get_pull_request(pull_request)
127 pull_request = self.__get_pull_request(pull_request)
127 current_reviewers = PullRequestReviewers.query()\
128 current_reviewers = PullRequestReviewers.query()\
128 .filter(PullRequestReviewers.pull_request==
129 .filter(PullRequestReviewers.pull_request==
129 pull_request)\
130 pull_request)\
130 .all()
131 .all()
131 current_reviewers_ids = set([x.user.user_id for x in current_reviewers])
132 current_reviewers_ids = set([x.user.user_id for x in current_reviewers])
132
133
133 to_add = reviewers_ids.difference(current_reviewers_ids)
134 to_add = reviewers_ids.difference(current_reviewers_ids)
134 to_remove = current_reviewers_ids.difference(reviewers_ids)
135 to_remove = current_reviewers_ids.difference(reviewers_ids)
135
136
136 log.debug("Adding %s reviewers" % to_add)
137 log.debug("Adding %s reviewers" % to_add)
137 log.debug("Removing %s reviewers" % to_remove)
138 log.debug("Removing %s reviewers" % to_remove)
138
139
139 for uid in to_add:
140 for uid in to_add:
140 _usr = self._get_user(uid)
141 _usr = self._get_user(uid)
141 reviewer = PullRequestReviewers(_usr, pull_request)
142 reviewer = PullRequestReviewers(_usr, pull_request)
142 self.sa.add(reviewer)
143 self.sa.add(reviewer)
143
144
144 for uid in to_remove:
145 for uid in to_remove:
145 reviewer = PullRequestReviewers.query()\
146 reviewer = PullRequestReviewers.query()\
146 .filter(PullRequestReviewers.user_id==uid,
147 .filter(PullRequestReviewers.user_id==uid,
147 PullRequestReviewers.pull_request==pull_request)\
148 PullRequestReviewers.pull_request==pull_request)\
148 .scalar()
149 .scalar()
149 if reviewer:
150 if reviewer:
150 self.sa.delete(reviewer)
151 self.sa.delete(reviewer)
151
152
152 def delete(self, pull_request):
153 def delete(self, pull_request):
153 pull_request = self.__get_pull_request(pull_request)
154 pull_request = self.__get_pull_request(pull_request)
154 Session().delete(pull_request)
155 Session().delete(pull_request)
155
156
156 def close_pull_request(self, pull_request):
157 def close_pull_request(self, pull_request):
157 pull_request = self.__get_pull_request(pull_request)
158 pull_request = self.__get_pull_request(pull_request)
158 pull_request.status = PullRequest.STATUS_CLOSED
159 pull_request.status = PullRequest.STATUS_CLOSED
159 pull_request.updated_on = datetime.datetime.now()
160 pull_request.updated_on = datetime.datetime.now()
160 self.sa.add(pull_request)
161 self.sa.add(pull_request)
161
162
162 def _get_changesets(self, alias, org_repo, org_ref, other_repo, other_ref):
163 def _get_changesets(self, alias, org_repo, org_ref, other_repo, other_ref):
163 """
164 """
164 Returns a list of changesets that are incoming from org_repo@org_ref
165 Returns a list of changesets that are incoming from org_repo@org_ref
165 to other_repo@other_ref
166 to other_repo@other_ref
166
167
167 :param org_repo:
168 :param org_repo:
168 :param org_ref:
169 :param org_ref:
169 :param other_repo:
170 :param other_repo:
170 :param other_ref:
171 :param other_ref:
171 :param tmp:
172 :param tmp:
172 """
173 """
173
174
174 changesets = []
175 changesets = []
175
176
176 if alias == 'hg':
177 if alias == 'hg':
178 # lookup up the exact node id
179 _revset_predicates = {
180 'branch': 'branch',
181 'book': 'bookmark',
182 'tag': 'tag',
183 'rev': 'id',
184 }
185 org_rev_spec = "%s('%s')" % (_revset_predicates[org_ref[0]],
186 safe_str(org_ref[1]))
187 org_rev = scmutil.revsingle(org_repo._repo,
188 org_rev_spec)
189 other_rev_spec = "%s('%s')" % (_revset_predicates[other_ref[0]],
190 safe_str(other_ref[1]))
191 other_rev = scmutil.revsingle(other_repo._repo, other_rev_spec)
177
192
178 #case two independent repos
193 #case two independent repos
179 if org_repo != other_repo:
194 if org_repo != other_repo:
180 revs = [
195 revs = [
181 org_repo._repo.lookup(org_ref[1]),
196 org_repo._repo.lookup(org_ref[1]),
182 org_repo._repo.lookup(other_ref[1]),
197 org_repo._repo.lookup(other_ref[1]), # lookup up in the wrong repo!
183 ]
198 ]
184
199
185 obj = findcommonoutgoing(org_repo._repo,
200 obj = findcommonoutgoing(org_repo._repo,
186 localrepo.locallegacypeer(other_repo._repo.local()),
201 localrepo.locallegacypeer(other_repo._repo.local()),
187 revs,
202 revs,
188 force=True)
203 force=True)
189 revs = obj.missing
204 revs = obj.missing
190
205
191 for cs in map(binascii.hexlify, revs):
206 for cs in map(binascii.hexlify, revs):
192 _cs = org_repo.get_changeset(cs)
207 _cs = org_repo.get_changeset(cs)
193 changesets.append(_cs)
208 changesets.append(_cs)
194 # in case we have revisions filter out the ones not in given range
209 # in case we have revisions filter out the ones not in given range
195 if org_ref[0] == 'rev' and other_ref[0] == 'rev':
210 if org_ref[0] == 'rev' and other_ref[0] == 'rev':
196 revs = [x.raw_id for x in changesets]
211 revs = [x.raw_id for x in changesets]
197 start = org_ref[1]
212 start = org_ref[1]
198 stop = other_ref[1]
213 stop = other_ref[1]
199 changesets = changesets[revs.index(start):revs.index(stop) + 1]
214 changesets = changesets[revs.index(start):revs.index(stop) + 1]
200
215
201 #no remote compare do it on the same repository
216 #no remote compare do it on the same repository
202 else:
217 else:
203 _revset_predicates = {
218 revs = ["ancestors(id('%s')) and not ancestors(id('%s'))" %
204 'branch': 'branch',
219 (other_rev, org_rev)]
205 'book': 'bookmark',
220
206 'tag': 'tag',
207 'rev': 'id',
208 }
209
210 revs = [
211 "ancestors(%s('%s')) and not ancestors(%s('%s'))" % (
212 _revset_predicates[other_ref[0]], other_ref[1],
213 _revset_predicates[org_ref[0]], org_ref[1],
214 )
215 ]
216
217 out = scmutil.revrange(org_repo._repo, revs)
221 out = scmutil.revrange(org_repo._repo, revs)
218 for cs in (out):
222 for cs in (out):
219 changesets.append(org_repo.get_changeset(cs))
223 changesets.append(org_repo.get_changeset(cs))
220
224
221 elif alias == 'git':
225 elif alias == 'git':
222 assert org_repo == other_repo, (org_repo, other_repo) # no git support for different repos
226 assert org_repo == other_repo, (org_repo, other_repo) # no git support for different repos
223 so, se = org_repo.run_git_command(
227 so, se = org_repo.run_git_command(
224 'log --reverse --pretty="format: %%H" -s -p %s..%s' % (org_ref[1],
228 'log --reverse --pretty="format: %%H" -s -p %s..%s' % (org_ref[1],
225 other_ref[1])
229 other_ref[1])
226 )
230 )
227 ids = re.findall(r'[0-9a-fA-F]{40}', so)
231 ids = re.findall(r'[0-9a-fA-F]{40}', so)
228 for cs in (ids):
232 for cs in (ids):
229 changesets.append(org_repo.get_changeset(cs))
233 changesets.append(org_repo.get_changeset(cs))
230
234
231 return changesets
235 return changesets
232
236
233 def get_compare_data(self, org_repo, org_ref, other_repo, other_ref):
237 def get_compare_data(self, org_repo, org_ref, other_repo, other_ref):
234 """
238 """
235 Returns incomming changesets for mercurial repositories
239 Returns incoming changesets for mercurial repositories
236
240
237 :param org_repo:
241 :param org_repo:
238 :type org_repo:
242 :type org_repo:
239 :param org_ref:
243 :param org_ref:
240 :type org_ref:
244 :type org_ref:
241 :param other_repo:
245 :param other_repo:
242 :type other_repo:
246 :type other_repo:
243 :param other_ref:
247 :param other_ref:
244 :type other_ref:
248 :type other_ref:
245 """
249 """
246
250
247 if len(org_ref) != 2 or not isinstance(org_ref, (list, tuple)):
251 if len(org_ref) != 2 or not isinstance(org_ref, (list, tuple)):
248 raise Exception('org_ref must be a two element list/tuple')
252 raise Exception('org_ref must be a two element list/tuple')
249
253
250 if len(other_ref) != 2 or not isinstance(org_ref, (list, tuple)):
254 if len(other_ref) != 2 or not isinstance(org_ref, (list, tuple)):
251 raise Exception('other_ref must be a two element list/tuple')
255 raise Exception('other_ref must be a two element list/tuple')
252
256
253 org_repo_scm = org_repo.scm_instance
257 org_repo_scm = org_repo.scm_instance
254 other_repo_scm = other_repo.scm_instance
258 other_repo_scm = other_repo.scm_instance
255
259
256 alias = org_repo.scm_instance.alias
260 alias = org_repo.scm_instance.alias
257 cs_ranges = self._get_changesets(alias,
261 cs_ranges = self._get_changesets(alias,
258 org_repo_scm, org_ref,
262 org_repo_scm, org_ref,
259 other_repo_scm, other_ref)
263 other_repo_scm, other_ref)
260 return cs_ranges
264 return cs_ranges
General Comments 0
You need to be logged in to leave comments. Login now