# HG changeset patch # User Marcin Kuzminski # Date 2016-11-01 07:58:57 # Node ID 84545e70b68976ea3b04b3a6971e18af3716a7bc # Parent 1492beda4d437044c8b0dab063dc7f0909323d24 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 diff --git a/rhodecode/controllers/admin/my_account.py b/rhodecode/controllers/admin/my_account.py --- a/rhodecode/controllers/admin/my_account.py +++ b/rhodecode/controllers/admin/my_account.py @@ -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') - - 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.show_closed = str2bool(request.GET.get('pr_show_closed')) - 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' diff --git a/rhodecode/model/db.py b/rhodecode/model/db.py --- a/rhodecode/model/db.py +++ b/rhodecode/model/db.py @@ -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) diff --git a/rhodecode/model/pull_request.py b/rhodecode/model/pull_request.py --- a/rhodecode/model/pull_request.py +++ b/rhodecode/model/pull_request.py @@ -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 diff --git a/rhodecode/templates/admin/my_account/my_account_pullrequests.html b/rhodecode/templates/admin/my_account/my_account_pullrequests.html --- a/rhodecode/templates/admin/my_account/my_account_pullrequests.html +++ b/rhodecode/templates/admin/my_account/my_account_pullrequests.html @@ -11,123 +11,12 @@
-
-

${_('Pull Requests You Opened')}: ${len(c.my_pull_requests)}

-
-
-
- %if c.my_pull_requests: - - - - - - - - - - - %for pull_request in c.my_pull_requests: - - - - - - - - - - %endfor -
${_('Target Repo')}${_('Author')}${_('Title')}${_('Last Update')}
-
-
-
- ${h.link_to(pull_request.target_repo.repo_name,h.url('summary_home',repo_name=pull_request.target_repo.repo_name))} -
-
- ${base.gravatar_with_user(pull_request.author.email, 16)} - -
-   -
-
-
-
#${pull_request.pull_request_id}: ${pull_request.title}\ - %if pull_request.is_closed(): -  (${_('Closed')})\ - %endif -
${pull_request.description}
-
-
- ${h.age_component(pull_request.updated_on)} - - ${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()} -
- %else: -

${_('You currently have no open pull requests.')}

- %endif +
+

${_('Pull Requests You Participate In')}: ${c.records_total_participate}

-
-
- -
-
-

${_('Pull Requests You Participate In')}: ${len(c.participate_in_pull_requests)}

-
- -
-
- %if c.participate_in_pull_requests: - - - - - - - - - - %for pull_request in c.participate_in_pull_requests: - - - - - - - - - %endfor -
${_('Target Repo')}${_('Author')}${_('Title')}${_('Last Update')}
-
-
-
- ${h.link_to(pull_request.target_repo.repo_name,h.url('summary_home',repo_name=pull_request.target_repo.repo_name))} -
-
- ${base.gravatar_with_user(pull_request.author.email, 16)} - -
-   -
-
-
-
#${pull_request.pull_request_id}: ${pull_request.title}\ - %if pull_request.is_closed(): -  (${_('Closed')})\ - %endif -
${pull_request.description}
-
-
- ${h.age_component(pull_request.updated_on)} -
- %else: -

${_('There are currently no open pull requests requiring your participation.')}

- %endif +
+
-
diff --git a/rhodecode/templates/data_table/_dt_elements.html b/rhodecode/templates/data_table/_dt_elements.html --- a/rhodecode/templates/data_table/_dt_elements.html +++ b/rhodecode/templates/data_table/_dt_elements.html @@ -271,6 +271,12 @@ ## PULL REQUESTS GRID RENDERERS + +<%def name="pullrequest_target_repo(repo_name)"> +
+ ${h.link_to(repo_name,h.url('summary_home',repo_name=repo_name))} +
+ <%def name="pullrequest_status(status)">
@@ -284,9 +290,13 @@ ${comments_nr} -<%def name="pullrequest_name(pull_request_id, target_repo_name)"> +<%def name="pullrequest_name(pull_request_id, target_repo_name, short=False)"> - ${_('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 diff --git a/rhodecode/tests/functional/test_admin_my_account.py b/rhodecode/tests/functional/test_admin_my_account.py --- a/rhodecode/tests/functional/test_admin_my_account.py +++ b/rhodecode/tests/functional/test_admin_my_account.py @@ -85,14 +85,13 @@ class TestMyAccountController(TestContro 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()