##// END OF EJS Templates
pull-requests: moved the listing of pull requests for repo into pyramid....
marcink -
r1766:d8048d6f default
parent child Browse files
Show More
@@ -0,0 +1,184 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import logging
22
23 from pyramid.view import view_config
24
25 from rhodecode.apps._base import RepoAppView, DataGridAppView
26 from rhodecode.lib import helpers as h
27 from rhodecode.lib import audit_logger
28 from rhodecode.lib.auth import (
29 LoginRequired, HasRepoPermissionAnyDecorator)
30 from rhodecode.lib.utils import PartialRenderer
31 from rhodecode.lib.utils2 import str2bool
32 from rhodecode.model.comment import CommentsModel
33 from rhodecode.model.db import PullRequest
34 from rhodecode.model.pull_request import PullRequestModel
35
36 log = logging.getLogger(__name__)
37
38
39 class RepoPullRequestsView(RepoAppView, DataGridAppView):
40
41 def load_default_context(self):
42 c = self._get_local_tmpl_context()
43
44 # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead
45 c.repo_info = self.db_repo
46
47 self._register_global_c(c)
48 return c
49
50 def _get_pull_requests_list(
51 self, repo_name, source, filter_type, opened_by, statuses):
52
53 draw, start, limit = self._extract_chunk(self.request)
54 search_q, order_by, order_dir = self._extract_ordering(self.request)
55 _render = PartialRenderer('data_table/_dt_elements.mako')
56
57 # pagination
58
59 if filter_type == 'awaiting_review':
60 pull_requests = PullRequestModel().get_awaiting_review(
61 repo_name, source=source, opened_by=opened_by,
62 statuses=statuses, offset=start, length=limit,
63 order_by=order_by, order_dir=order_dir)
64 pull_requests_total_count = PullRequestModel().count_awaiting_review(
65 repo_name, source=source, statuses=statuses,
66 opened_by=opened_by)
67 elif filter_type == 'awaiting_my_review':
68 pull_requests = PullRequestModel().get_awaiting_my_review(
69 repo_name, source=source, opened_by=opened_by,
70 user_id=self._rhodecode_user.user_id, statuses=statuses,
71 offset=start, length=limit, order_by=order_by,
72 order_dir=order_dir)
73 pull_requests_total_count = PullRequestModel().count_awaiting_my_review(
74 repo_name, source=source, user_id=self._rhodecode_user.user_id,
75 statuses=statuses, opened_by=opened_by)
76 else:
77 pull_requests = PullRequestModel().get_all(
78 repo_name, source=source, opened_by=opened_by,
79 statuses=statuses, offset=start, length=limit,
80 order_by=order_by, order_dir=order_dir)
81 pull_requests_total_count = PullRequestModel().count_all(
82 repo_name, source=source, statuses=statuses,
83 opened_by=opened_by)
84
85 data = []
86 comments_model = CommentsModel()
87 for pr in pull_requests:
88 comments = comments_model.get_all_comments(
89 self.db_repo.repo_id, pull_request=pr)
90
91 data.append({
92 'name': _render('pullrequest_name',
93 pr.pull_request_id, pr.target_repo.repo_name),
94 'name_raw': pr.pull_request_id,
95 'status': _render('pullrequest_status',
96 pr.calculated_review_status()),
97 'title': _render(
98 'pullrequest_title', pr.title, pr.description),
99 'description': h.escape(pr.description),
100 'updated_on': _render('pullrequest_updated_on',
101 h.datetime_to_time(pr.updated_on)),
102 'updated_on_raw': h.datetime_to_time(pr.updated_on),
103 'created_on': _render('pullrequest_updated_on',
104 h.datetime_to_time(pr.created_on)),
105 'created_on_raw': h.datetime_to_time(pr.created_on),
106 'author': _render('pullrequest_author',
107 pr.author.full_contact, ),
108 'author_raw': pr.author.full_name,
109 'comments': _render('pullrequest_comments', len(comments)),
110 'comments_raw': len(comments),
111 'closed': pr.is_closed(),
112 })
113
114 data = ({
115 'draw': draw,
116 'data': data,
117 'recordsTotal': pull_requests_total_count,
118 'recordsFiltered': pull_requests_total_count,
119 })
120 return data
121
122 @LoginRequired()
123 @HasRepoPermissionAnyDecorator(
124 'repository.read', 'repository.write', 'repository.admin')
125 @view_config(
126 route_name='pullrequest_show_all', request_method='GET',
127 renderer='rhodecode:templates/pullrequests/pullrequests.mako')
128 def pull_request_list(self):
129 c = self.load_default_context()
130
131 req_get = self.request.GET
132 c.source = str2bool(req_get.get('source'))
133 c.closed = str2bool(req_get.get('closed'))
134 c.my = str2bool(req_get.get('my'))
135 c.awaiting_review = str2bool(req_get.get('awaiting_review'))
136 c.awaiting_my_review = str2bool(req_get.get('awaiting_my_review'))
137
138 c.active = 'open'
139 if c.my:
140 c.active = 'my'
141 if c.closed:
142 c.active = 'closed'
143 if c.awaiting_review and not c.source:
144 c.active = 'awaiting'
145 if c.source and not c.awaiting_review:
146 c.active = 'source'
147 if c.awaiting_my_review:
148 c.active = 'awaiting_my'
149
150 return self._get_template_context(c)
151
152 @LoginRequired()
153 @HasRepoPermissionAnyDecorator(
154 'repository.read', 'repository.write', 'repository.admin')
155 @view_config(
156 route_name='pullrequest_show_all_data', request_method='GET',
157 renderer='json_ext', xhr=True)
158 def pull_request_list_data(self):
159
160 # additional filters
161 req_get = self.request.GET
162 source = str2bool(req_get.get('source'))
163 closed = str2bool(req_get.get('closed'))
164 my = str2bool(req_get.get('my'))
165 awaiting_review = str2bool(req_get.get('awaiting_review'))
166 awaiting_my_review = str2bool(req_get.get('awaiting_my_review'))
167
168 filter_type = 'awaiting_review' if awaiting_review \
169 else 'awaiting_my_review' if awaiting_my_review \
170 else None
171
172 opened_by = None
173 if my:
174 opened_by = [self._rhodecode_user.user_id]
175
176 statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN]
177 if closed:
178 statuses = [PullRequest.STATUS_CLOSED]
179
180 data = self._get_pull_requests_list(
181 repo_name=self.db_repo_name, source=source,
182 filter_type=filter_type, opened_by=opened_by, statuses=statuses)
183
184 return data
@@ -268,6 +268,34 b' class RepoRoutePredicate(object):'
268 268 return False
269 269
270 270
271 class RepoTypeRoutePredicate(object):
272 def __init__(self, val, config):
273 self.val = val or ['hg', 'git', 'svn']
274
275 def text(self):
276 return 'repo_accepted_type = %s' % self.val
277
278 phash = text
279
280 def __call__(self, info, request):
281
282 rhodecode_db_repo = request.db_repo
283
284 log.debug(
285 '%s checking repo type for %s in %s',
286 self.__class__.__name__, rhodecode_db_repo.repo_type, self.val)
287
288 if rhodecode_db_repo.repo_type in self.val:
289 return True
290 else:
291 log.warning('Current view is not supported for repo type:%s',
292 rhodecode_db_repo.repo_type)
293 return False
294
295
296
271 297 def includeme(config):
272 298 config.add_route_predicate(
273 299 'repo_route', RepoRoutePredicate)
300 config.add_route_predicate(
301 'repo_accepted_types', RepoTypeRoutePredicate) No newline at end of file
@@ -47,6 +47,16 b' def includeme(config):'
47 47 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id}',
48 48 repo_route=True)
49 49
50 config.add_route(
51 name='pullrequest_show_all',
52 pattern='/{repo_name:.*?[^/]}/pull-request',
53 repo_route=True, repo_accepted_types=['hg', 'git'])
54
55 config.add_route(
56 name='pullrequest_show_all_data',
57 pattern='/{repo_name:.*?[^/]}/pull-request-data',
58 repo_route=True, repo_accepted_types=['hg', 'git'])
59
50 60 # Settings
51 61 config.add_route(
52 62 name='edit_repo',
@@ -827,13 +827,6 b' def make_map(config):'
827 827 'method': ['DELETE']},
828 828 requirements=URL_NAME_REQUIREMENTS)
829 829
830 rmap.connect('pullrequest_show_all',
831 '/{repo_name}/pull-request',
832 controller='pullrequests',
833 action='show_all', conditions={'function': check_repo,
834 'method': ['GET']},
835 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
836
837 830 rmap.connect('pullrequest_comment',
838 831 '/{repo_name}/pull-request-comment/{pull_request_id}',
839 832 controller='pullrequests',
@@ -72,124 +72,6 b' class PullrequestsController(BaseRepoCon'
72 72 c.REVIEW_STATUS_APPROVED = ChangesetStatus.STATUS_APPROVED
73 73 c.REVIEW_STATUS_REJECTED = ChangesetStatus.STATUS_REJECTED
74 74
75 def _extract_ordering(self, request):
76 column_index = safe_int(request.GET.get('order[0][column]'))
77 order_dir = request.GET.get('order[0][dir]', 'desc')
78 order_by = request.GET.get(
79 'columns[%s][data][sort]' % column_index, 'name_raw')
80 return order_by, order_dir
81
82 @LoginRequired()
83 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
84 'repository.admin')
85 @HasAcceptedRepoType('git', 'hg')
86 def show_all(self, repo_name):
87 # filter types
88 c.active = 'open'
89 c.source = str2bool(request.GET.get('source'))
90 c.closed = str2bool(request.GET.get('closed'))
91 c.my = str2bool(request.GET.get('my'))
92 c.awaiting_review = str2bool(request.GET.get('awaiting_review'))
93 c.awaiting_my_review = str2bool(request.GET.get('awaiting_my_review'))
94 c.repo_name = repo_name
95
96 opened_by = None
97 if c.my:
98 c.active = 'my'
99 opened_by = [c.rhodecode_user.user_id]
100
101 statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN]
102 if c.closed:
103 c.active = 'closed'
104 statuses = [PullRequest.STATUS_CLOSED]
105
106 if c.awaiting_review and not c.source:
107 c.active = 'awaiting'
108 if c.source and not c.awaiting_review:
109 c.active = 'source'
110 if c.awaiting_my_review:
111 c.active = 'awaiting_my'
112
113 data = self._get_pull_requests_list(
114 repo_name=repo_name, opened_by=opened_by, statuses=statuses)
115 if not request.is_xhr:
116 c.data = json.dumps(data['data'])
117 c.records_total = data['recordsTotal']
118 return render('/pullrequests/pullrequests.mako')
119 else:
120 return json.dumps(data)
121
122 def _get_pull_requests_list(self, repo_name, opened_by, statuses):
123 # pagination
124 start = safe_int(request.GET.get('start'), 0)
125 length = safe_int(request.GET.get('length'), c.visual.dashboard_items)
126 order_by, order_dir = self._extract_ordering(request)
127
128 if c.awaiting_review:
129 pull_requests = PullRequestModel().get_awaiting_review(
130 repo_name, source=c.source, opened_by=opened_by,
131 statuses=statuses, offset=start, length=length,
132 order_by=order_by, order_dir=order_dir)
133 pull_requests_total_count = PullRequestModel(
134 ).count_awaiting_review(
135 repo_name, source=c.source, statuses=statuses,
136 opened_by=opened_by)
137 elif c.awaiting_my_review:
138 pull_requests = PullRequestModel().get_awaiting_my_review(
139 repo_name, source=c.source, opened_by=opened_by,
140 user_id=c.rhodecode_user.user_id, statuses=statuses,
141 offset=start, length=length, order_by=order_by,
142 order_dir=order_dir)
143 pull_requests_total_count = PullRequestModel(
144 ).count_awaiting_my_review(
145 repo_name, source=c.source, user_id=c.rhodecode_user.user_id,
146 statuses=statuses, opened_by=opened_by)
147 else:
148 pull_requests = PullRequestModel().get_all(
149 repo_name, source=c.source, opened_by=opened_by,
150 statuses=statuses, offset=start, length=length,
151 order_by=order_by, order_dir=order_dir)
152 pull_requests_total_count = PullRequestModel().count_all(
153 repo_name, source=c.source, statuses=statuses,
154 opened_by=opened_by)
155
156 from rhodecode.lib.utils import PartialRenderer
157 _render = PartialRenderer('data_table/_dt_elements.mako')
158 data = []
159 for pr in pull_requests:
160 comments = CommentsModel().get_all_comments(
161 c.rhodecode_db_repo.repo_id, pull_request=pr)
162
163 data.append({
164 'name': _render('pullrequest_name',
165 pr.pull_request_id, pr.target_repo.repo_name),
166 'name_raw': pr.pull_request_id,
167 'status': _render('pullrequest_status',
168 pr.calculated_review_status()),
169 'title': _render(
170 'pullrequest_title', pr.title, pr.description),
171 'description': h.escape(pr.description),
172 'updated_on': _render('pullrequest_updated_on',
173 h.datetime_to_time(pr.updated_on)),
174 'updated_on_raw': h.datetime_to_time(pr.updated_on),
175 'created_on': _render('pullrequest_updated_on',
176 h.datetime_to_time(pr.created_on)),
177 'created_on_raw': h.datetime_to_time(pr.created_on),
178 'author': _render('pullrequest_author',
179 pr.author.full_contact, ),
180 'author_raw': pr.author.full_name,
181 'comments': _render('pullrequest_comments', len(comments)),
182 'comments_raw': len(comments),
183 'closed': pr.is_closed(),
184 })
185 # json used to render the grid
186 data = ({
187 'data': data,
188 'recordsTotal': pull_requests_total_count,
189 'recordsFiltered': pull_requests_total_count,
190 })
191 return data
192
193 75 @LoginRequired()
194 76 @NotAnonymous()
195 77 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
@@ -42,6 +42,10 b''
42 42
43 43 .panel-body {
44 44 padding: @panel-padding;
45
46 &.panel-body-min-height {
47 min-height: 150px
48 }
45 49 }
46 50
47 51 .panel-footer {
@@ -34,7 +34,6 b' function registerRCRoutes() {'
34 34 pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']);
35 35 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
36 36 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
37 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
38 37 pyroutes.register('pullrequest_comment', '/%(repo_name)s/pull-request-comment/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
39 38 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request-comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
40 39 pyroutes.register('changelog_home', '/%(repo_name)s/changelog', ['repo_name']);
@@ -106,6 +105,8 b' function registerRCRoutes() {'
106 105 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
107 106 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
108 107 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
108 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
109 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
109 110 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
110 111 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
111 112 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
@@ -234,7 +234,7 b''
234 234 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
235 235 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
236 236 <li class="${is_active('showpullrequest')}">
237 <a class="menulink" href="${h.url('pullrequest_show_all',repo_name=c.repo_name)}" title="${_('Show Pull Requests for %s') % c.repo_name}">
237 <a class="menulink" href="${h.route_path('pullrequest_show_all', repo_name=c.repo_name)}" title="${_('Show Pull Requests for %s') % c.repo_name}">
238 238 %if c.repository_pull_requests:
239 239 <span class="pr_notifications">${c.repository_pull_requests}</span>
240 240 %endif
@@ -45,12 +45,12 b''
45 45 ##main
46 46 <div class="sidebar">
47 47 <ul class="nav nav-pills nav-stacked">
48 <li class="${'active' if c.active=='open' else ''}"><a href="${h.url('pullrequest_show_all',repo_name=c.repo_name,source=0)}">${_('Opened')}</a></li>
49 <li class="${'active' if c.active=='my' else ''}"><a href="${h.url('pullrequest_show_all',repo_name=c.repo_name,source=0,my=1)}">${_('Opened by me')}</a></li>
50 <li class="${'active' if c.active=='awaiting' else ''}"><a href="${h.url('pullrequest_show_all',repo_name=c.repo_name,source=0,awaiting_review=1)}">${_('Awaiting review')}</a></li>
51 <li class="${'active' if c.active=='awaiting_my' else ''}"><a href="${h.url('pullrequest_show_all',repo_name=c.repo_name,source=0,awaiting_my_review=1)}">${_('Awaiting my review')}</a></li>
52 <li class="${'active' if c.active=='closed' else ''}"><a href="${h.url('pullrequest_show_all',repo_name=c.repo_name,source=0,closed=1)}">${_('Closed')}</a></li>
53 <li class="${'active' if c.active=='source' else ''}"><a href="${h.url('pullrequest_show_all',repo_name=c.repo_name,source=1)}">${_('From this repo')}</a></li>
48 <li class="${'active' if c.active=='open' else ''}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0})}">${_('Opened')}</a></li>
49 <li class="${'active' if c.active=='my' else ''}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0,'my':1})}">${_('Opened by me')}</a></li>
50 <li class="${'active' if c.active=='awaiting' else ''}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0,'awaiting_review':1})}">${_('Awaiting review')}</a></li>
51 <li class="${'active' if c.active=='awaiting_my' else ''}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0,'awaiting_my_review':1})}">${_('Awaiting my review')}</a></li>
52 <li class="${'active' if c.active=='closed' else ''}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0,'closed':1})}">${_('Closed')}</a></li>
53 <li class="${'active' if c.active=='source' else ''}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':1})}">${_('From this repo')}</a></li>
54 54 </ul>
55 55 </div>
56 56
@@ -73,7 +73,7 b''
73 73 %endif
74 74 </h3>
75 75 </div>
76 <div class="panel-body">
76 <div class="panel-body panel-body-min-height">
77 77 <table id="pull_request_list_table" class="display"></table>
78 78 </div>
79 79 </div>
@@ -83,14 +83,24 b''
83 83
84 84 <script type="text/javascript">
85 85 $(document).ready(function() {
86
87 var $pullRequestListTable = $('#pull_request_list_table');
88
86 89 // object list
87 $('#pull_request_list_table').DataTable({
88 data: ${c.data|n},
89 processing: false,
90 $pullRequestListTable.DataTable({
91 processing: true,
90 92 serverSide: true,
91 deferLoading: ${c.records_total},
92 ajax: "",
93 dom: 'tp',
93 ajax: {
94 "url": "${h.route_path('pullrequest_show_all_data', repo_name=c.repo_name)}",
95 "data": function (d) {
96 d.source = "${c.source}";
97 d.closed = "${c.closed}";
98 d.my = "${c.my}";
99 d.awaiting_review = "${c.awaiting_review}";
100 d.awaiting_my_review = "${c.awaiting_my_review}";
101 }
102 },
103 dom: 'rtp',
94 104 pageLength: ${c.visual.dashboard_items},
95 105 order: [[ 1, "desc" ]],
96 106 columns: [
@@ -109,6 +119,7 b''
109 119 ],
110 120 language: {
111 121 paginate: DEFAULT_GRID_PAGINATION,
122 sProcessing: _gettext('loading...'),
112 123 emptyTable: _gettext("No pull requests available yet.")
113 124 },
114 125 "drawCallback": function( settings, json ) {
@@ -120,13 +131,16 b''
120 131 }
121 132 }
122 133 });
123 });
124 $('#pull_request_list_table').on('xhr.dt', function(e, settings, json, xhr){
125 $('#pull_request_list_table').css('opacity', 1);
134
135 $pullRequestListTable.on('xhr.dt', function(e, settings, json, xhr){
136 $pullRequestListTable.css('opacity', 1);
126 137 });
127 138
128 $('#pull_request_list_table').on('preXhr.dt', function(e, settings, data){
129 $('#pull_request_list_table').css('opacity', 0.3);
139 $pullRequestListTable.on('preXhr.dt', function(e, settings, data){
140 $pullRequestListTable.css('opacity', 0.3);
130 141 });
142
143 });
144
131 145 </script>
132 146 </%def>
General Comments 0
You need to be logged in to leave comments. Login now