##// END OF EJS Templates
- pull request generates overview based on it's params...
marcink -
r2440:1bc579bc codereview
parent child Browse files
Show More
@@ -0,0 +1,31 b''
1 <%inherit file="/base/base.html"/>
2
3 <%def name="title()">
4 ${c.repo_name} ${_('All pull requests')}
5 </%def>
6
7 <%def name="breadcrumbs_links()">
8 ${h.link_to(u'Home',h.url('/'))}
9 &raquo;
10 ${h.link_to(c.repo_name,h.url('changelog_home',repo_name=c.repo_name))}
11 &raquo;
12 ${_('All pull requests')}
13 </%def>
14
15 <%def name="main()">
16
17 <div class="box">
18 <!-- box / title -->
19 <div class="title">
20 ${self.breadcrumbs()}
21 </div>
22
23 %for pr in c.pull_requests:
24 <a href="${h.url('pullrequest_show',repo_name=c.repo_name,pull_request_id=pr.pull_request_id)}">#${pr.pull_request_id}</a>
25 %endfor
26
27 </div>
28
29 <script type="text/javascript"></script>
30
31 </%def>
@@ -451,6 +451,12 b' def make_map(config):'
451 451 action='show', conditions=dict(function=check_repo,
452 452 method=["GET"]))
453 453
454 rmap.connect('pullrequest_show_all',
455 '/{repo_name:.*}/pull-request',
456 controller='pullrequests',
457 action='show_all', conditions=dict(function=check_repo,
458 method=["GET"]))
459
454 460 rmap.connect('summary_home', '/{repo_name:.*}/summary',
455 461 controller='summary', conditions=dict(function=check_repo))
456 462
@@ -295,7 +295,7 b' class ChangesetController(BaseRepoContro'
295 295 )
296 296
297 297 # count inline comments
298 for _, lines in c.inline_comments:
298 for __, lines in c.inline_comments:
299 299 for comments in lines.values():
300 300 c.inline_cnt += len(comments)
301 301
@@ -24,6 +24,9 b''
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25 import logging
26 26 import traceback
27 import binascii
28
29 from webob.exc import HTTPNotFound
27 30
28 31 from pylons import request, response, session, tmpl_context as c, url
29 32 from pylons.controllers.util import abort, redirect
@@ -32,9 +35,13 b' from pylons.i18n.translation import _'
32 35 from rhodecode.lib.base import BaseRepoController, render
33 36 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
34 37 from rhodecode.lib import helpers as h
35 from rhodecode.model.db import User, PullRequest
38 from rhodecode.lib import diffs
39 from rhodecode.model.db import User, PullRequest, Repository, ChangesetStatus
36 40 from rhodecode.model.pull_request import PullRequestModel
37 41 from rhodecode.model.meta import Session
42 from rhodecode.model.repo import RepoModel
43 from rhodecode.model.comment import ChangesetCommentsModel
44 from rhodecode.model.changeset_status import ChangesetStatusModel
38 45
39 46 log = logging.getLogger(__name__)
40 47
@@ -50,12 +57,12 b' class PullrequestsController(BaseRepoCon'
50 57 def _get_repo_refs(self, repo):
51 58 hist_l = []
52 59
53 branches_group = ([('branch:' + k, k) for k in repo.branches.keys()],
54 _("Branches"))
55 bookmarks_group = ([('book:' + k, k) for k in repo.bookmarks.keys()],
56 _("Bookmarks"))
57 tags_group = ([('tag:' + k, k) for k in repo.tags.keys()],
58 _("Tags"))
60 branches_group = ([('branch:%s:%s' % (k, v), k) for
61 k, v in repo.branches.iteritems()], _("Branches"))
62 bookmarks_group = ([('book:%s:%s' % (k, v), k) for
63 k, v in repo.bookmarks.iteritems()], _("Bookmarks"))
64 tags_group = ([('tag:%s:%s' % (k, v), k) for
65 k, v in repo.tags.iteritems()], _("Tags"))
59 66
60 67 hist_l.append(bookmarks_group)
61 68 hist_l.append(branches_group)
@@ -63,6 +70,11 b' class PullrequestsController(BaseRepoCon'
63 70
64 71 return hist_l
65 72
73 def show_all(self, repo_name):
74 c.pull_requests = PullRequestModel().get_all(repo_name)
75 c.repo_name = repo_name
76 return render('/pullrequests/pullrequest_show_all.html')
77
66 78 def index(self):
67 79 org_repo = c.rhodecode_db_repo
68 80 c.org_refs = self._get_repo_refs(c.rhodecode_repo)
@@ -128,6 +140,118 b' class PullrequestsController(BaseRepoCon'
128 140
129 141 return redirect(url('changelog_home', repo_name=repo_name))
130 142
143 def _get_changesets(self, org_repo, org_ref, other_repo, other_ref, tmp):
144 changesets = []
145 #case two independent repos
146 if org_repo != other_repo:
147 common, incoming, rheads = tmp
148
149 if not incoming:
150 revs = []
151 else:
152 revs = org_repo._repo.changelog.findmissing(common, rheads)
153
154 for cs in reversed(map(binascii.hexlify, revs)):
155 changesets.append(org_repo.get_changeset(cs))
156 else:
157 revs = ['ancestors(%s) and not ancestors(%s)' % (org_ref[1],
158 other_ref[1])]
159 from mercurial import scmutil
160 out = scmutil.revrange(org_repo._repo, revs)
161 for cs in reversed(out):
162 changesets.append(org_repo.get_changeset(cs))
163
164 return changesets
165
166 def _get_discovery(self, org_repo, org_ref, other_repo, other_ref):
167 from mercurial import discovery
168 other = org_repo._repo
169 repo = other_repo._repo
170 tip = other[org_ref[1]]
171 log.debug('Doing discovery for %s@%s vs %s@%s' % (
172 org_repo, org_ref, other_repo, other_ref)
173 )
174 log.debug('Filter heads are %s[%s]' % (tip, org_ref[1]))
175 tmp = discovery.findcommonincoming(
176 repo=repo, # other_repo we check for incoming
177 remote=other, # org_repo source for incoming
178 heads=[tip.node()],
179 force=False
180 )
181 return tmp
182
183 def _compare(self, pull_request):
184
185 org_repo = pull_request.org_repo
186 org_ref_type, org_ref_, org_ref = pull_request.org_ref.split(':')
187 other_repo = pull_request.other_repo
188 other_ref_type, other_ref, other_ref_ = pull_request.other_ref.split(':')
189
190 org_ref = (org_ref_type, org_ref)
191 other_ref = (other_ref_type, other_ref)
192
193 c.org_repo = org_repo
194 c.other_repo = other_repo
195
196 discovery_data = self._get_discovery(org_repo.scm_instance,
197 org_ref,
198 other_repo.scm_instance,
199 other_ref)
200 c.cs_ranges = self._get_changesets(org_repo.scm_instance,
201 org_ref,
202 other_repo.scm_instance,
203 other_ref,
204 discovery_data)
205
206 c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in
207 c.cs_ranges])
208 # defines that we need hidden inputs with changesets
209 c.as_form = request.GET.get('as_form', False)
210 if request.environ.get('HTTP_X_PARTIAL_XHR'):
211 return render('compare/compare_cs.html')
212
213 c.org_ref = org_ref[1]
214 c.other_ref = other_ref[1]
215 # diff needs to have swapped org with other to generate proper diff
216 _diff = diffs.differ(other_repo, other_ref, org_repo, org_ref,
217 discovery_data)
218 diff_processor = diffs.DiffProcessor(_diff, format='gitdiff')
219 _parsed = diff_processor.prepare()
220
221 c.files = []
222 c.changes = {}
223
224 for f in _parsed:
225 fid = h.FID('', f['filename'])
226 c.files.append([fid, f['operation'], f['filename'], f['stats']])
227 diff = diff_processor.as_html(enable_comments=False, diff_lines=[f])
228 c.changes[fid] = [f['operation'], f['filename'], diff]
229
131 230 def show(self, repo_name, pull_request_id):
231 repo_model = RepoModel()
232 c.users_array = repo_model.get_users_js()
233 c.users_groups_array = repo_model.get_users_groups_js()
132 234 c.pull_request = PullRequest.get(pull_request_id)
235 ##TODO: need more generic solution
236 self._compare(c.pull_request)
237
238 # inline comments
239 c.inline_cnt = 0
240 c.inline_comments = ChangesetCommentsModel()\
241 .get_inline_comments(c.rhodecode_db_repo.repo_id,
242 pull_request=pull_request_id)
243 # count inline comments
244 for __, lines in c.inline_comments:
245 for comments in lines.values():
246 c.inline_cnt += len(comments)
247 # comments
248 c.comments = ChangesetCommentsModel()\
249 .get_comments(c.rhodecode_db_repo.repo_id,
250 pull_request=pull_request_id)
251
252 # changeset(pull-request) statuse
253 c.current_changeset_status = ChangesetStatusModel()\
254 .get_status(c.rhodecode_db_repo.repo_id,
255 pull_request=pull_request_id)
256 c.changeset_statuses = ChangesetStatus.STATUSES
133 257 return render('/pullrequests/pullrequest_show.html')
@@ -204,7 +204,7 b' class BaseRepoController(BaseController)'
204 204 super(BaseRepoController, self).__before__()
205 205 if c.repo_name:
206 206
207 c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name)
207 dbr = c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name)
208 208 c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
209 209
210 210 if c.rhodecode_repo is None:
@@ -213,5 +213,7 b' class BaseRepoController(BaseController)'
213 213
214 214 redirect(url('home'))
215 215
216 c.repository_followers = self.scm_model.get_followers(c.repo_name)
217 c.repository_forks = self.scm_model.get_forks(c.repo_name)
216 # some globals counter for menu
217 c.repository_followers = self.scm_model.get_followers(dbr)
218 c.repository_forks = self.scm_model.get_forks(dbr)
219 c.repository_pull_requests = self.scm_model.get_pull_requests(dbr) No newline at end of file
@@ -26,7 +26,7 b''
26 26 import logging
27 27
28 28 from rhodecode.model import BaseModel
29 from rhodecode.model.db import ChangesetStatus
29 from rhodecode.model.db import ChangesetStatus, PullRequest
30 30
31 31 log = logging.getLogger(__name__)
32 32
@@ -36,23 +36,37 b' class ChangesetStatusModel(BaseModel):'
36 36 def __get_changeset_status(self, changeset_status):
37 37 return self._get_instance(ChangesetStatus, changeset_status)
38 38
39 def get_status(self, repo, revision):
39 def __get_pull_request(self, pull_request):
40 return self._get_instance(PullRequest, pull_request)
41
42 def get_status(self, repo, revision=None, pull_request=None):
40 43 """
41 Returns status of changeset for given revision and version 0
42 versioning makes a history of statuses, and version == 0 is always the
43 current one
44 Returns latest status of changeset for given revision or for given
45 pull request. Statuses are versioned inside a table itself and
46 version == 0 is always the current one
44 47
45 48 :param repo:
46 49 :type repo:
47 :param revision: 40char hash
50 :param revision: 40char hash or None
48 51 :type revision: str
52 :param pull_request: pull_request reference
53 :type:
49 54 """
50 55 repo = self._get_repo(repo)
51 56
52 status = ChangesetStatus.query()\
57 q = ChangesetStatus.query()\
53 58 .filter(ChangesetStatus.repo == repo)\
54 .filter(ChangesetStatus.revision == revision)\
55 .filter(ChangesetStatus.version == 0).scalar()
59 .filter(ChangesetStatus.version == 0)
60
61 if revision:
62 q = q.filter(ChangesetStatus.revision == revision)
63 elif pull_request:
64 pull_request = self.__get_pull_request(pull_request)
65 q = q.filter(ChangesetStatus.pull_request == pull_request)
66 else:
67 raise Exception('Please specify revision or pull_request')
68
69 status = q.scalar()
56 70 status = status.status if status else status
57 71 st = status or ChangesetStatus.DEFAULT
58 72 return str(st)
@@ -32,7 +32,8 b' from sqlalchemy.util.compat import defau'
32 32 from rhodecode.lib.utils2 import extract_mentioned_users, safe_unicode
33 33 from rhodecode.lib import helpers as h
34 34 from rhodecode.model import BaseModel
35 from rhodecode.model.db import ChangesetComment, User, Repository, Notification
35 from rhodecode.model.db import ChangesetComment, User, Repository, \
36 Notification, PullRequest
36 37 from rhodecode.model.notification import NotificationModel
37 38
38 39 log = logging.getLogger(__name__)
@@ -43,6 +44,9 b' class ChangesetCommentsModel(BaseModel):'
43 44 def __get_changeset_comment(self, changeset_comment):
44 45 return self._get_instance(ChangesetComment, changeset_comment)
45 46
47 def __get_pull_request(self, pull_request):
48 return self._get_instance(PullRequest, pull_request)
49
46 50 def _extract_mentions(self, s):
47 51 user_objects = []
48 52 for username in extract_mentioned_users(s):
@@ -135,7 +139,7 b' class ChangesetCommentsModel(BaseModel):'
135 139
136 140 return comment
137 141
138 def get_comments(self, repo_id, revision=None, pull_request_id=None):
142 def get_comments(self, repo_id, revision=None, pull_request=None):
139 143 """
140 144 Get's main comments based on revision or pull_request_id
141 145
@@ -143,22 +147,24 b' class ChangesetCommentsModel(BaseModel):'
143 147 :type repo_id:
144 148 :param revision:
145 149 :type revision:
146 :param pull_request_id:
147 :type pull_request_id:
150 :param pull_request:
151 :type pull_request:
148 152 """
153
149 154 q = ChangesetComment.query()\
150 155 .filter(ChangesetComment.repo_id == repo_id)\
151 156 .filter(ChangesetComment.line_no == None)\
152 157 .filter(ChangesetComment.f_path == None)
153 158 if revision:
154 159 q = q.filter(ChangesetComment.revision == revision)
155 elif pull_request_id:
156 q = q.filter(ChangesetComment.pull_request_id == pull_request_id)
160 elif pull_request:
161 pull_request = self.__get_pull_request(pull_request)
162 q = q.filter(ChangesetComment.pull_request == pull_request)
157 163 else:
158 raise Exception('Please specify revision or pull_request_id')
164 raise Exception('Please specify revision or pull_request')
159 165 return q.all()
160 166
161 def get_inline_comments(self, repo_id, revision=None, pull_request_id=None):
167 def get_inline_comments(self, repo_id, revision=None, pull_request=None):
162 168 q = self.sa.query(ChangesetComment)\
163 169 .filter(ChangesetComment.repo_id == repo_id)\
164 170 .filter(ChangesetComment.line_no != None)\
@@ -167,8 +173,9 b' class ChangesetCommentsModel(BaseModel):'
167 173
168 174 if revision:
169 175 q = q.filter(ChangesetComment.revision == revision)
170 elif pull_request_id:
171 q = q.filter(ChangesetComment.pull_request_id == pull_request_id)
176 elif pull_request:
177 pull_request = self.__get_pull_request(pull_request)
178 q = q.filter(ChangesetComment.pull_request == pull_request)
172 179 else:
173 180 raise Exception('Please specify revision or pull_request_id')
174 181
@@ -1322,7 +1322,8 b' class ChangesetComment(Base, BaseModel):'
1322 1322 )
1323 1323 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1324 1324 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1325 revision = Column('revision', String(40), nullable=False)
1325 revision = Column('revision', String(40), nullable=True)
1326 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1326 1327 line_no = Column('line_no', Unicode(10), nullable=True)
1327 1328 f_path = Column('f_path', Unicode(1000), nullable=True)
1328 1329 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
@@ -1332,6 +1333,7 b' class ChangesetComment(Base, BaseModel):'
1332 1333 author = relationship('User', lazy='joined')
1333 1334 repo = relationship('Repository')
1334 1335 status_change = relationship('ChangesetStatus', uselist=False)
1336 pull_request = relationship('PullRequest', lazy='joined')
1335 1337
1336 1338 @classmethod
1337 1339 def get_users(cls, revision):
@@ -1397,6 +1399,8 b' class PullRequest(Base, BaseModel):'
1397 1399 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1398 1400 title = Column('title', Unicode(256), nullable=True)
1399 1401 description = Column('description', Unicode(10240), nullable=True)
1402 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1403 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1400 1404 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1401 1405 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1402 1406 org_ref = Column('org_ref', Unicode(256), nullable=False)
@@ -1411,6 +1415,7 b' class PullRequest(Base, BaseModel):'
1411 1415 def revisions(self, val):
1412 1416 self._revisions = ':'.join(val)
1413 1417
1418 author = relationship('User', lazy='joined')
1414 1419 reviewers = relationship('PullRequestReviewers')
1415 1420 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1416 1421 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
@@ -37,8 +37,13 b' log = logging.getLogger(__name__)'
37 37
38 38 class PullRequestModel(BaseModel):
39 39
40 def get_all(self, repo):
41 repo = self._get_repo(repo)
42 return PullRequest.query().filter(PullRequest.other_repo == repo).all()
43
40 44 def create(self, created_by, org_repo, org_ref, other_repo,
41 45 other_ref, revisions, reviewers, title, description=None):
46 created_by_user = self._get_user(created_by)
42 47
43 48 new = PullRequest()
44 49 new.org_repo = self._get_repo(org_repo)
@@ -48,7 +53,7 b' class PullRequestModel(BaseModel):'
48 53 new.revisions = revisions
49 54 new.title = title
50 55 new.description = description
51
56 new.author = created_by_user
52 57 self.sa.add(new)
53 58
54 59 #members
@@ -59,7 +64,7 b' class PullRequestModel(BaseModel):'
59 64
60 65 #notification to reviewers
61 66 notif = NotificationModel()
62 created_by_user = self._get_user(created_by)
67
63 68 subject = safe_unicode(
64 69 h.link_to(
65 70 _('%(user)s wants you to review pull request #%(pr_id)s') % \
@@ -43,7 +43,7 b' from rhodecode.lib.utils import get_repo'
43 43 action_logger, EmptyChangeset, REMOVED_REPO_PAT
44 44 from rhodecode.model import BaseModel
45 45 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
46 UserFollowing, UserLog, User, RepoGroup
46 UserFollowing, UserLog, User, RepoGroup, PullRequest
47 47
48 48 log = logging.getLogger(__name__)
49 49
@@ -320,19 +320,21 b' class ScmModel(BaseModel):'
320 320
321 321 return f is not None
322 322
323 def get_followers(self, repo_id):
324 if not isinstance(repo_id, int):
325 repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
323 def get_followers(self, repo):
324 repo = self._get_repo(repo)
326 325
327 326 return self.sa.query(UserFollowing)\
328 .filter(UserFollowing.follows_repo_id == repo_id).count()
327 .filter(UserFollowing.follows_repository == repo).count()
329 328
330 def get_forks(self, repo_id):
331 if not isinstance(repo_id, int):
332 repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
329 def get_forks(self, repo):
330 repo = self._get_repo(repo)
331 return self.sa.query(Repository)\
332 .filter(Repository.fork == repo).count()
333 333
334 return self.sa.query(Repository)\
335 .filter(Repository.fork_id == repo_id).count()
334 def get_pull_requests(self, repo):
335 repo = self._get_repo(repo)
336 return self.sa.query(PullRequest)\
337 .filter(PullRequest.other_repo == repo).count()
336 338
337 339 def mark_as_fork(self, repo, fork, user):
338 340 repo = self.__get_repo(repo)
@@ -247,6 +247,14 b''
247 247 <span class="short">${c.repository_forks}</span>
248 248 </a>
249 249 </li>
250 <li>
251 <a class="menu_link" title="${_('Pull requests')}" href="${h.url('pullrequest_show_all',repo_name=c.repo_name)}">
252 <span class="icon_short">
253 <img src="${h.url('/images/icons/arrow_join.png')}" alt="${_('Pull requests')}" />
254 </span>
255 <span class="short">${c.repository_pull_requests}</span>
256 </a>
257 </li>
250 258 ${usermenu()}
251 259 </ul>
252 260 <script type="text/javascript">
@@ -20,8 +20,59 b''
20 20 ${self.breadcrumbs()}
21 21 </div>
22 22
23 pull request ${c.pull_request} overview...
23 <h3>${_('Title')}: ${c.pull_request.title}</h3>
24 <div class="changeset-status-container" style="float:left;padding:0px 20px 20px 20px">
25 %if c.current_changeset_status:
26 <div title="${_('Changeset status')}" class="changeset-status-lbl">[${h.changeset_status_lbl(c.current_changeset_status)}]</div>
27 <div class="changeset-status-ico"><img src="${h.url('/images/icons/flag_status_%s.png' % c.current_changeset_status)}" /></div>
28 %endif
29 </div>
30 <div style="padding:4px">
31 <div>${h.fmt_date(c.pull_request.created_on)}</div>
32 </div>
33
34 ##DIFF
35
36 <div class="table">
37 <div id="body" class="diffblock">
38 <div style="white-space:pre-wrap;padding:5px">${h.literal(c.pull_request.description)}</div>
39 </div>
40 <div id="changeset_compare_view_content">
41 ##CS
42 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Incoming changesets')}</div>
43 <%include file="/compare/compare_cs.html" />
24 44
45 ## FILES
46 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Files affected')}</div>
47 <div class="cs_files">
48 %for fid, change, f, stat in c.files:
49 <div class="cs_${change}">
50 <div class="node">${h.link_to(h.safe_unicode(f),h.url.current(anchor=fid))}</div>
51 <div class="changes">${h.fancy_file_stats(stat)}</div>
52 </div>
53 %endfor
54 </div>
55 </div>
56 </div>
57 <script>
58 var _USERS_AC_DATA = ${c.users_array|n};
59 var _GROUPS_AC_DATA = ${c.users_groups_array|n};
60 </script>
61
62 ## diff block
63 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
64 %for fid, change, f, stat in c.files:
65 ${diff_block.diff_block_simple([c.changes[fid]])}
66 %endfor
67
68 ## template for inline comment form
69 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
70 ##${comment.comment_inline_form(c.changeset)}
71
72 ## render comments main comments form and it status
73 ##${comment.comments(h.url('pull_request_comment', repo_name=c.repo_name, pull_request_id=c.pull_request.pull_request_id),
74 ## c.current_changeset_status)}
75
25 76 </div>
26 77
27 78 <script type="text/javascript">
General Comments 0
You need to be logged in to leave comments. Login now