pull-requests: redo my account pull request page with datagrid. Fixes #4297
- consistent ui with per-repo pull-request page - faster loading due to pagination - enabled sorting of columns
This commit is contained in:
parent
e0af9069d6
commit
753d0ff478
6 changed files with 207 additions and 159 deletions
|
|
@ -39,19 +39,20 @@ from rhodecode.lib.auth import (
|
|||
LoginRequired, NotAnonymous, AuthUser, generate_auth_token)
|
||||
from rhodecode.lib.base import BaseController, render
|
||||
from rhodecode.lib.utils import jsonify
|
||||
from rhodecode.lib.utils2 import safe_int, md5
|
||||
from rhodecode.lib.utils2 import safe_int, md5, str2bool
|
||||
from rhodecode.lib.ext_json import json
|
||||
|
||||
from rhodecode.model.validation_schema.schemas import user_schema
|
||||
from rhodecode.model.db import (
|
||||
Repository, PullRequest, PullRequestReviewers, UserEmailMap, User,
|
||||
UserFollowing)
|
||||
Repository, PullRequest, UserEmailMap, User, UserFollowing)
|
||||
from rhodecode.model.forms import UserForm
|
||||
from rhodecode.model.scm import RepoList
|
||||
from rhodecode.model.user import UserModel
|
||||
from rhodecode.model.repo import RepoModel
|
||||
from rhodecode.model.auth_token import AuthTokenModel
|
||||
from rhodecode.model.meta import Session
|
||||
from rhodecode.model.pull_request import PullRequestModel
|
||||
from rhodecode.model.comment import ChangesetCommentsModel
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -289,25 +290,85 @@ class MyAccountController(BaseController):
|
|||
category='success')
|
||||
return redirect(url('my_account_emails'))
|
||||
|
||||
def _extract_ordering(self, request):
|
||||
column_index = safe_int(request.GET.get('order[0][column]'))
|
||||
order_dir = request.GET.get('order[0][dir]', 'desc')
|
||||
order_by = request.GET.get(
|
||||
'columns[%s][data][sort]' % column_index, 'name_raw')
|
||||
return order_by, order_dir
|
||||
|
||||
def _get_pull_requests_list(self, statuses):
|
||||
start = safe_int(request.GET.get('start'), 0)
|
||||
length = safe_int(request.GET.get('length'), c.visual.dashboard_items)
|
||||
order_by, order_dir = self._extract_ordering(request)
|
||||
|
||||
pull_requests = PullRequestModel().get_im_participating_in(
|
||||
user_id=c.rhodecode_user.user_id,
|
||||
statuses=statuses,
|
||||
offset=start, length=length, order_by=order_by,
|
||||
order_dir=order_dir)
|
||||
|
||||
pull_requests_total_count = PullRequestModel().count_im_participating_in(
|
||||
user_id=c.rhodecode_user.user_id, statuses=statuses)
|
||||
|
||||
from rhodecode.lib.utils import PartialRenderer
|
||||
_render = PartialRenderer('data_table/_dt_elements.html')
|
||||
data = []
|
||||
for pr in pull_requests:
|
||||
repo_id = pr.target_repo_id
|
||||
comments = ChangesetCommentsModel().get_all_comments(
|
||||
repo_id, pull_request=pr)
|
||||
owned = pr.user_id == c.rhodecode_user.user_id
|
||||
status = pr.calculated_review_status()
|
||||
|
||||
data.append({
|
||||
'target_repo': _render('pullrequest_target_repo',
|
||||
pr.target_repo.repo_name),
|
||||
'name': _render('pullrequest_name',
|
||||
pr.pull_request_id, pr.target_repo.repo_name,
|
||||
short=True),
|
||||
'name_raw': pr.pull_request_id,
|
||||
'status': _render('pullrequest_status', status),
|
||||
'title': _render(
|
||||
'pullrequest_title', pr.title, pr.description),
|
||||
'description': h.escape(pr.description),
|
||||
'updated_on': _render('pullrequest_updated_on',
|
||||
h.datetime_to_time(pr.updated_on)),
|
||||
'updated_on_raw': h.datetime_to_time(pr.updated_on),
|
||||
'created_on': _render('pullrequest_updated_on',
|
||||
h.datetime_to_time(pr.created_on)),
|
||||
'created_on_raw': h.datetime_to_time(pr.created_on),
|
||||
'author': _render('pullrequest_author',
|
||||
pr.author.full_contact, ),
|
||||
'author_raw': pr.author.full_name,
|
||||
'comments': _render('pullrequest_comments', len(comments)),
|
||||
'comments_raw': len(comments),
|
||||
'closed': pr.is_closed(),
|
||||
'owned': owned
|
||||
})
|
||||
# json used to render the grid
|
||||
data = ({
|
||||
'data': data,
|
||||
'recordsTotal': pull_requests_total_count,
|
||||
'recordsFiltered': pull_requests_total_count,
|
||||
})
|
||||
return data
|
||||
|
||||
def my_account_pullrequests(self):
|
||||
c.active = 'pullrequests'
|
||||
self.__load_data()
|
||||
c.show_closed = request.GET.get('pr_show_closed')
|
||||
c.show_closed = str2bool(request.GET.get('pr_show_closed'))
|
||||
|
||||
def _filter(pr):
|
||||
s = sorted(pr, key=lambda o: o.created_on, reverse=True)
|
||||
if not c.show_closed:
|
||||
s = filter(lambda p: p.status != PullRequest.STATUS_CLOSED, s)
|
||||
return s
|
||||
|
||||
c.my_pull_requests = _filter(
|
||||
PullRequest.query().filter(
|
||||
PullRequest.user_id == c.rhodecode_user.user_id).all())
|
||||
my_prs = [
|
||||
x.pull_request for x in PullRequestReviewers.query().filter(
|
||||
PullRequestReviewers.user_id == c.rhodecode_user.user_id).all()]
|
||||
c.participate_in_pull_requests = _filter(my_prs)
|
||||
return render('admin/my_account/my_account.html')
|
||||
statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN]
|
||||
if c.show_closed:
|
||||
statuses += [PullRequest.STATUS_CLOSED]
|
||||
data = self._get_pull_requests_list(statuses)
|
||||
if not request.is_xhr:
|
||||
c.data_participate = json.dumps(data['data'])
|
||||
c.records_total_participate = data['recordsTotal']
|
||||
return render('admin/my_account/my_account.html')
|
||||
else:
|
||||
return json.dumps(data)
|
||||
|
||||
def my_account_auth_tokens(self):
|
||||
c.active = 'auth_tokens'
|
||||
|
|
|
|||
|
|
@ -3203,14 +3203,10 @@ class PullRequest(Base, _PullRequestBase):
|
|||
}
|
||||
|
||||
def calculated_review_status(self):
|
||||
# TODO: anderson: 13.05.15 Used only on templates/my_account_pullrequests.html
|
||||
# because it's tricky on how to use ChangesetStatusModel from there
|
||||
warnings.warn("Use calculated_review_status from ChangesetStatusModel", DeprecationWarning)
|
||||
from rhodecode.model.changeset_status import ChangesetStatusModel
|
||||
return ChangesetStatusModel().calculated_review_status(self)
|
||||
|
||||
def reviewers_statuses(self):
|
||||
warnings.warn("Use reviewers_statuses from ChangesetStatusModel", DeprecationWarning)
|
||||
from rhodecode.model.changeset_status import ChangesetStatusModel
|
||||
return ChangesetStatusModel().reviewers_statuses(self)
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import urllib
|
|||
|
||||
from pylons.i18n.translation import _
|
||||
from pylons.i18n.translation import lazy_ugettext
|
||||
from sqlalchemy import or_
|
||||
|
||||
from rhodecode.lib import helpers as h, hooks_utils, diffs
|
||||
from rhodecode.lib.compat import OrderedDict
|
||||
|
|
@ -159,12 +160,16 @@ class PullRequestModel(BaseModel):
|
|||
def _prepare_get_all_query(self, repo_name, source=False, statuses=None,
|
||||
opened_by=None, order_by=None,
|
||||
order_dir='desc'):
|
||||
repo = self._get_repo(repo_name)
|
||||
repo = None
|
||||
if repo_name:
|
||||
repo = self._get_repo(repo_name)
|
||||
|
||||
q = PullRequest.query()
|
||||
|
||||
# source or target
|
||||
if source:
|
||||
if repo and source:
|
||||
q = q.filter(PullRequest.source_repo == repo)
|
||||
else:
|
||||
elif repo:
|
||||
q = q.filter(PullRequest.target_repo == repo)
|
||||
|
||||
# closed,opened
|
||||
|
|
@ -179,7 +184,8 @@ class PullRequestModel(BaseModel):
|
|||
order_map = {
|
||||
'name_raw': PullRequest.pull_request_id,
|
||||
'title': PullRequest.title,
|
||||
'updated_on_raw': PullRequest.updated_on
|
||||
'updated_on_raw': PullRequest.updated_on,
|
||||
'target_repo': PullRequest.target_repo_id
|
||||
}
|
||||
if order_dir == 'asc':
|
||||
q = q.order_by(order_map[order_by].asc())
|
||||
|
|
@ -337,6 +343,59 @@ class PullRequestModel(BaseModel):
|
|||
PullRequestReviewers.user_id == user_id).all()
|
||||
]
|
||||
|
||||
def _prepare_participating_query(self, user_id=None, statuses=None,
|
||||
order_by=None, order_dir='desc'):
|
||||
q = PullRequest.query()
|
||||
if user_id:
|
||||
reviewers_subquery = Session().query(
|
||||
PullRequestReviewers.pull_request_id).filter(
|
||||
PullRequestReviewers.user_id == user_id).subquery()
|
||||
user_filter= or_(
|
||||
PullRequest.user_id == user_id,
|
||||
PullRequest.pull_request_id.in_(reviewers_subquery)
|
||||
)
|
||||
q = PullRequest.query().filter(user_filter)
|
||||
|
||||
# closed,opened
|
||||
if statuses:
|
||||
q = q.filter(PullRequest.status.in_(statuses))
|
||||
|
||||
if order_by:
|
||||
order_map = {
|
||||
'name_raw': PullRequest.pull_request_id,
|
||||
'title': PullRequest.title,
|
||||
'updated_on_raw': PullRequest.updated_on,
|
||||
'target_repo': PullRequest.target_repo_id
|
||||
}
|
||||
if order_dir == 'asc':
|
||||
q = q.order_by(order_map[order_by].asc())
|
||||
else:
|
||||
q = q.order_by(order_map[order_by].desc())
|
||||
|
||||
return q
|
||||
|
||||
def count_im_participating_in(self, user_id=None, statuses=None):
|
||||
q = self._prepare_participating_query(user_id, statuses=statuses)
|
||||
return q.count()
|
||||
|
||||
def get_im_participating_in(
|
||||
self, user_id=None, statuses=None, offset=0,
|
||||
length=None, order_by=None, order_dir='desc'):
|
||||
"""
|
||||
Get all Pull requests that i'm participating in, or i have opened
|
||||
"""
|
||||
|
||||
q = self._prepare_participating_query(
|
||||
user_id, statuses=statuses, order_by=order_by,
|
||||
order_dir=order_dir)
|
||||
|
||||
if length:
|
||||
pull_requests = q.limit(length).offset(offset).all()
|
||||
else:
|
||||
pull_requests = q.all()
|
||||
|
||||
return pull_requests
|
||||
|
||||
def get_versions(self, pull_request):
|
||||
"""
|
||||
returns version of pull request sorted by ID descending
|
||||
|
|
|
|||
|
|
@ -11,123 +11,12 @@
|
|||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">${_('Pull Requests You Opened')}: ${len(c.my_pull_requests)}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="pullrequestlist">
|
||||
%if c.my_pull_requests:
|
||||
<table class="rctable">
|
||||
<thead>
|
||||
<th class="td-status"></th>
|
||||
<th>${_('Target Repo')}</th>
|
||||
<th>${_('Author')}</th>
|
||||
<th></th>
|
||||
<th>${_('Title')}</th>
|
||||
<th class="td-time">${_('Last Update')}</th>
|
||||
<th></th>
|
||||
</thead>
|
||||
%for pull_request in c.my_pull_requests:
|
||||
<tr class="${'closed' if pull_request.is_closed() else ''} prwrapper">
|
||||
<td class="td-status">
|
||||
<div class="${'flag_status %s' % pull_request.calculated_review_status()} pull-left"></div>
|
||||
</td>
|
||||
<td class="truncate-wrap td-componentname">
|
||||
<div class="truncate">
|
||||
${h.link_to(pull_request.target_repo.repo_name,h.url('summary_home',repo_name=pull_request.target_repo.repo_name))}
|
||||
</div>
|
||||
</td>
|
||||
<td class="user">
|
||||
${base.gravatar_with_user(pull_request.author.email, 16)}
|
||||
</td>
|
||||
<td class="td-message expand_commit" data-pr-id="m${pull_request.pull_request_id}" title="${_('Expand commit message')}">
|
||||
<div class="show_more_col">
|
||||
<i class="show_more"></i>
|
||||
</div>
|
||||
</td>
|
||||
<td class="mid td-description">
|
||||
<div class="log-container truncate-wrap">
|
||||
<div class="message truncate" id="c-m${pull_request.pull_request_id}"><a href="${h.url('pullrequest_show',repo_name=pull_request.target_repo.repo_name,pull_request_id=pull_request.pull_request_id)}">#${pull_request.pull_request_id}: ${pull_request.title}</a>\
|
||||
%if pull_request.is_closed():
|
||||
(${_('Closed')})\
|
||||
%endif
|
||||
<br/>${pull_request.description}</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="td-time">
|
||||
${h.age_component(pull_request.updated_on)}
|
||||
</td>
|
||||
<td class="td-action repolist_actions">
|
||||
${h.secure_form(url('pullrequest_delete', repo_name=pull_request.target_repo.repo_name, pull_request_id=pull_request.pull_request_id),method='delete')}
|
||||
${h.submit('remove_%s' % pull_request.pull_request_id, _('Delete'),
|
||||
class_="btn btn-link btn-danger",onclick="return confirm('"+_('Confirm to delete this pull request')+"');")}
|
||||
${h.end_form()}
|
||||
</td>
|
||||
</tr>
|
||||
%endfor
|
||||
</table>
|
||||
%else:
|
||||
<h2><span class="empty_data">${_('You currently have no open pull requests.')}</span></h2>
|
||||
%endif
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">${_('Pull Requests You Participate In')}: ${c.records_total_participate}</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">${_('Pull Requests You Participate In')}: ${len(c.participate_in_pull_requests)}</h3>
|
||||
</div>
|
||||
|
||||
<div class="panel-body">
|
||||
<div class="pullrequestlist">
|
||||
%if c.participate_in_pull_requests:
|
||||
<table class="rctable">
|
||||
<thead>
|
||||
<th class="td-status"></th>
|
||||
<th>${_('Target Repo')}</th>
|
||||
<th>${_('Author')}</th>
|
||||
<th></th>
|
||||
<th>${_('Title')}</th>
|
||||
<th class="td-time">${_('Last Update')}</th>
|
||||
</thead>
|
||||
%for pull_request in c.participate_in_pull_requests:
|
||||
<tr class="${'closed' if pull_request.is_closed() else ''} prwrapper">
|
||||
<td class="td-status">
|
||||
<div class="${'flag_status %s' % pull_request.calculated_review_status()} pull-left"></div>
|
||||
</td>
|
||||
<td class="truncate-wrap td-componentname">
|
||||
<div class="truncate">
|
||||
${h.link_to(pull_request.target_repo.repo_name,h.url('summary_home',repo_name=pull_request.target_repo.repo_name))}
|
||||
</div>
|
||||
</td>
|
||||
<td class="user">
|
||||
${base.gravatar_with_user(pull_request.author.email, 16)}
|
||||
</td>
|
||||
<td class="td-message expand_commit" data-pr-id="p${pull_request.pull_request_id}" title="${_('Expand commit message')}">
|
||||
<div class="show_more_col">
|
||||
<i class="show_more"></i>
|
||||
</div>
|
||||
</td>
|
||||
<td class="mid td-description">
|
||||
<div class="log-container truncate-wrap">
|
||||
<div class="message truncate" id="c-p${pull_request.pull_request_id}"><a href="${h.url('pullrequest_show',repo_name=pull_request.target_repo.repo_name,pull_request_id=pull_request.pull_request_id)}">#${pull_request.pull_request_id}: ${pull_request.title}</a>\
|
||||
%if pull_request.is_closed():
|
||||
(${_('Closed')})\
|
||||
%endif
|
||||
<br/>${pull_request.description}</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="td-time">
|
||||
${h.age_component(pull_request.updated_on)}
|
||||
</td>
|
||||
</tr>
|
||||
%endfor
|
||||
</table>
|
||||
%else:
|
||||
<h2 class="empty_data">${_('There are currently no open pull requests requiring your participation.')}</h2>
|
||||
%endif
|
||||
<div class="panel-body">
|
||||
<table id="pull_request_list_table_participate" class="display"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
|
@ -139,17 +28,51 @@
|
|||
window.location = "${h.url('my_account_pullrequests')}";
|
||||
}
|
||||
});
|
||||
$('.expand_commit').on('click',function(e){
|
||||
var target_expand = $(this);
|
||||
var cid = target_expand.data('prId');
|
||||
$(document).ready(function() {
|
||||
|
||||
if (target_expand.hasClass('open')){
|
||||
$('#c-'+cid).css({'height': '2.75em', 'text-overflow': 'ellipsis', 'overflow':'hidden'});
|
||||
target_expand.removeClass('open');
|
||||
}
|
||||
else {
|
||||
$('#c-'+cid).css({'height': 'auto', 'text-overflow': 'initial', 'overflow':'visible'});
|
||||
target_expand.addClass('open');
|
||||
}
|
||||
var columnsDefs = [
|
||||
{ data: {"_": "status",
|
||||
"sort": "status"}, title: "", className: "td-status", orderable: false},
|
||||
{ data: {"_": "target_repo",
|
||||
"sort": "target_repo"}, title: "${_('Target Repo')}", className: "td-targetrepo", orderable: false},
|
||||
{ data: {"_": "name",
|
||||
"sort": "name_raw"}, title: "${_('Name')}", className: "td-componentname", "type": "num" },
|
||||
{ data: {"_": "author",
|
||||
"sort": "author_raw"}, title: "${_('Author')}", className: "td-user", orderable: false },
|
||||
{ data: {"_": "title",
|
||||
"sort": "title"}, title: "${_('Title')}", className: "td-description" },
|
||||
{ data: {"_": "comments",
|
||||
"sort": "comments_raw"}, title: "", className: "td-comments", orderable: false},
|
||||
{ data: {"_": "updated_on",
|
||||
"sort": "updated_on_raw"}, title: "${_('Last Update')}", className: "td-time" }
|
||||
];
|
||||
|
||||
// participating object list
|
||||
$('#pull_request_list_table_participate').DataTable({
|
||||
data: ${c.data_participate|n},
|
||||
processing: true,
|
||||
serverSide: true,
|
||||
deferLoading: ${c.records_total_participate},
|
||||
ajax: "",
|
||||
dom: 'tp',
|
||||
pageLength: ${c.visual.dashboard_items},
|
||||
order: [[ 2, "desc" ]],
|
||||
columns: columnsDefs,
|
||||
language: {
|
||||
paginate: DEFAULT_GRID_PAGINATION,
|
||||
emptyTable: _gettext("There are currently no open pull requests requiring your participation.")
|
||||
},
|
||||
"drawCallback": function( settings, json ) {
|
||||
timeagoActivate();
|
||||
},
|
||||
"createdRow": function ( row, data, index ) {
|
||||
if (data['closed']) {
|
||||
$(row).addClass('closed');
|
||||
}
|
||||
if (data['owned']) {
|
||||
$(row).addClass('owned');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -271,6 +271,12 @@
|
|||
|
||||
|
||||
## PULL REQUESTS GRID RENDERERS
|
||||
|
||||
<%def name="pullrequest_target_repo(repo_name)">
|
||||
<div class="truncate">
|
||||
${h.link_to(repo_name,h.url('summary_home',repo_name=repo_name))}
|
||||
</div>
|
||||
</%def>
|
||||
<%def name="pullrequest_status(status)">
|
||||
<div class="${'flag_status %s' % status} pull-left"></div>
|
||||
</%def>
|
||||
|
|
@ -284,9 +290,13 @@
|
|||
<i class="icon-comment icon-comment-colored"></i> ${comments_nr}
|
||||
</%def>
|
||||
|
||||
<%def name="pullrequest_name(pull_request_id, target_repo_name)">
|
||||
<%def name="pullrequest_name(pull_request_id, target_repo_name, short=False)">
|
||||
<a href="${h.url('pullrequest_show',repo_name=target_repo_name,pull_request_id=pull_request_id)}">
|
||||
${_('Pull request #%(pr_number)s') % {'pr_number': pull_request_id,}}
|
||||
% if short:
|
||||
#${pull_request_id}
|
||||
% else:
|
||||
${_('Pull request #%(pr_number)s') % {'pr_number': pull_request_id,}}
|
||||
% endif
|
||||
</a>
|
||||
</%def>
|
||||
|
||||
|
|
|
|||
|
|
@ -85,14 +85,13 @@ class TestMyAccountController(TestController):
|
|||
def test_my_account_my_pullrequests(self, pr_util):
|
||||
self.log_user()
|
||||
response = self.app.get(url('my_account_pullrequests'))
|
||||
response.mustcontain('You currently have no open pull requests.')
|
||||
response.mustcontain('There are currently no open pull '
|
||||
'requests requiring your participation.')
|
||||
|
||||
pr = pr_util.create_pull_request(title='TestMyAccountPR')
|
||||
response = self.app.get(url('my_account_pullrequests'))
|
||||
response.mustcontain('There are currently no open pull requests '
|
||||
'requiring your participation')
|
||||
|
||||
response.mustcontain('#%s: TestMyAccountPR' % pr.pull_request_id)
|
||||
response.mustcontain('"name_raw": %s' % pr.pull_request_id)
|
||||
response.mustcontain('TestMyAccountPR')
|
||||
|
||||
def test_my_account_my_emails(self):
|
||||
self.log_user()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue