pull-requests: added awaiting my review filter for users pull-requests.
- additionally the awaiting my review now properly filters pull requests that have no review votes on them
This commit is contained in:
parent
0031dcc28b
commit
aded27dc31
11 changed files with 347 additions and 131 deletions
|
|
@ -76,7 +76,11 @@ class TestMyAccountEdit(TestController):
|
|||
'requests requiring your participation.')
|
||||
|
||||
@pytest.mark.backends("git", "hg")
|
||||
def test_my_account_my_pullrequests_data(self, pr_util, xhr_header):
|
||||
@pytest.mark.parametrize('params, expected_title', [
|
||||
({'closed': 1}, 'Closed'),
|
||||
({'awaiting_my_review': 1}, 'Awaiting my review'),
|
||||
])
|
||||
def test_my_account_my_pullrequests_data(self, pr_util, xhr_header, params, expected_title):
|
||||
self.log_user()
|
||||
response = self.app.get(route_path('my_account_pullrequests_data'),
|
||||
extra_environ=xhr_header)
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ from rhodecode.model.comment import CommentsModel
|
|||
from rhodecode.model.db import (
|
||||
IntegrityError, or_, in_filter_generator,
|
||||
Repository, UserEmailMap, UserApiKeys, UserFollowing,
|
||||
PullRequest, UserBookmark, RepoGroup)
|
||||
PullRequest, UserBookmark, RepoGroup, ChangesetStatus)
|
||||
from rhodecode.model.meta import Session
|
||||
from rhodecode.model.pull_request import PullRequestModel
|
||||
from rhodecode.model.user import UserModel
|
||||
|
|
@ -654,21 +654,31 @@ class MyAccountView(BaseAppView, DataGridAppView):
|
|||
Session().commit()
|
||||
return user.user_data['notification_status']
|
||||
|
||||
def _get_pull_requests_list(self, statuses):
|
||||
def _get_pull_requests_list(self, statuses, filter_type=None):
|
||||
draw, start, limit = self._extract_chunk(self.request)
|
||||
search_q, order_by, order_dir = self._extract_ordering(self.request)
|
||||
|
||||
_render = self.request.get_partial_renderer(
|
||||
'rhodecode:templates/data_table/_dt_elements.mako')
|
||||
|
||||
pull_requests = PullRequestModel().get_im_participating_in(
|
||||
user_id=self._rhodecode_user.user_id,
|
||||
statuses=statuses, query=search_q,
|
||||
offset=start, length=limit, order_by=order_by,
|
||||
order_dir=order_dir)
|
||||
if filter_type == 'awaiting_my_review':
|
||||
pull_requests = PullRequestModel().get_im_participating_in_for_review(
|
||||
user_id=self._rhodecode_user.user_id,
|
||||
statuses=statuses, query=search_q,
|
||||
offset=start, length=limit, order_by=order_by,
|
||||
order_dir=order_dir)
|
||||
|
||||
pull_requests_total_count = PullRequestModel().count_im_participating_in(
|
||||
user_id=self._rhodecode_user.user_id, statuses=statuses, query=search_q)
|
||||
pull_requests_total_count = PullRequestModel().count_im_participating_in_for_review(
|
||||
user_id=self._rhodecode_user.user_id, statuses=statuses, query=search_q)
|
||||
else:
|
||||
pull_requests = PullRequestModel().get_im_participating_in(
|
||||
user_id=self._rhodecode_user.user_id,
|
||||
statuses=statuses, query=search_q,
|
||||
offset=start, length=limit, order_by=order_by,
|
||||
order_dir=order_dir)
|
||||
|
||||
pull_requests_total_count = PullRequestModel().count_im_participating_in(
|
||||
user_id=self._rhodecode_user.user_id, statuses=statuses, query=search_q)
|
||||
|
||||
data = []
|
||||
comments_model = CommentsModel()
|
||||
|
|
@ -678,6 +688,12 @@ class MyAccountView(BaseAppView, DataGridAppView):
|
|||
repo_id, pull_request=pr, include_drafts=False, count_only=True)
|
||||
owned = pr.user_id == self._rhodecode_user.user_id
|
||||
|
||||
review_statuses = pr.reviewers_statuses(user=self._rhodecode_db_user)
|
||||
my_review_status = ChangesetStatus.STATUS_NOT_REVIEWED
|
||||
if review_statuses and review_statuses[4]:
|
||||
_review_obj, _user, _reasons, _mandatory, statuses = review_statuses
|
||||
my_review_status = statuses[0][1].status
|
||||
|
||||
data.append({
|
||||
'target_repo': _render('pullrequest_target_repo',
|
||||
pr.target_repo.repo_name),
|
||||
|
|
@ -688,6 +704,8 @@ class MyAccountView(BaseAppView, DataGridAppView):
|
|||
'name_raw': pr.pull_request_id,
|
||||
'status': _render('pullrequest_status',
|
||||
pr.calculated_review_status()),
|
||||
'my_status': _render('pullrequest_status',
|
||||
my_review_status),
|
||||
'title': _render('pullrequest_title', pr.title, pr.description),
|
||||
'description': h.escape(pr.description),
|
||||
'updated_on': _render('pullrequest_updated_on',
|
||||
|
|
@ -723,7 +741,14 @@ class MyAccountView(BaseAppView, DataGridAppView):
|
|||
c.active = 'pullrequests'
|
||||
req_get = self.request.GET
|
||||
|
||||
c.closed = str2bool(req_get.get('pr_show_closed'))
|
||||
c.closed = str2bool(req_get.get('closed'))
|
||||
c.awaiting_my_review = str2bool(req_get.get('awaiting_my_review'))
|
||||
|
||||
c.selected_filter = 'all'
|
||||
if c.closed:
|
||||
c.selected_filter = 'all_closed'
|
||||
if c.awaiting_my_review:
|
||||
c.selected_filter = 'awaiting_my_review'
|
||||
|
||||
return self._get_template_context(c)
|
||||
|
||||
|
|
@ -732,13 +757,19 @@ class MyAccountView(BaseAppView, DataGridAppView):
|
|||
def my_account_pullrequests_data(self):
|
||||
self.load_default_context()
|
||||
req_get = self.request.GET
|
||||
|
||||
awaiting_my_review = str2bool(req_get.get('awaiting_my_review'))
|
||||
closed = str2bool(req_get.get('closed'))
|
||||
|
||||
statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN]
|
||||
if closed:
|
||||
statuses += [PullRequest.STATUS_CLOSED]
|
||||
|
||||
data = self._get_pull_requests_list(statuses=statuses)
|
||||
filter_type = \
|
||||
'awaiting_my_review' if awaiting_my_review \
|
||||
else None
|
||||
|
||||
data = self._get_pull_requests_list(statuses=statuses, filter_type=filter_type)
|
||||
return data
|
||||
|
||||
@LoginRequired()
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ class TestPullRequestList(object):
|
|||
|
||||
@pytest.mark.parametrize('params, expected_title', [
|
||||
({'source': 0, 'closed': 1}, 'Closed'),
|
||||
({'source': 0, 'my': 1}, 'Opened by me'),
|
||||
({'source': 0, 'my': 1}, 'Created by me'),
|
||||
({'source': 0, 'awaiting_review': 1}, 'Awaiting review'),
|
||||
({'source': 0, 'awaiting_my_review': 1}, 'Awaiting my review'),
|
||||
({'source': 1}, 'From this repo'),
|
||||
|
|
|
|||
|
|
@ -79,21 +79,20 @@ class RepoPullRequestsView(RepoAppView, DataGridAppView):
|
|||
|
||||
if filter_type == 'awaiting_review':
|
||||
pull_requests = PullRequestModel().get_awaiting_review(
|
||||
repo_name, search_q=search_q, source=source, opened_by=opened_by,
|
||||
statuses=statuses, offset=start, length=limit,
|
||||
order_by=order_by, order_dir=order_dir)
|
||||
repo_name,
|
||||
search_q=search_q, statuses=statuses,
|
||||
offset=start, length=limit, order_by=order_by, order_dir=order_dir)
|
||||
pull_requests_total_count = PullRequestModel().count_awaiting_review(
|
||||
repo_name, search_q=search_q, source=source, statuses=statuses,
|
||||
opened_by=opened_by)
|
||||
repo_name,
|
||||
search_q=search_q, statuses=statuses)
|
||||
elif filter_type == 'awaiting_my_review':
|
||||
pull_requests = PullRequestModel().get_awaiting_my_review(
|
||||
repo_name, search_q=search_q, source=source, opened_by=opened_by,
|
||||
user_id=self._rhodecode_user.user_id, statuses=statuses,
|
||||
offset=start, length=limit, order_by=order_by,
|
||||
order_dir=order_dir)
|
||||
repo_name, self._rhodecode_user.user_id,
|
||||
search_q=search_q, statuses=statuses,
|
||||
offset=start, length=limit, order_by=order_by, order_dir=order_dir)
|
||||
pull_requests_total_count = PullRequestModel().count_awaiting_my_review(
|
||||
repo_name, search_q=search_q, source=source, user_id=self._rhodecode_user.user_id,
|
||||
statuses=statuses, opened_by=opened_by)
|
||||
repo_name, self._rhodecode_user.user_id,
|
||||
search_q=search_q, statuses=statuses)
|
||||
else:
|
||||
pull_requests = PullRequestModel().get_all(
|
||||
repo_name, search_q=search_q, source=source, opened_by=opened_by,
|
||||
|
|
@ -110,6 +109,12 @@ class RepoPullRequestsView(RepoAppView, DataGridAppView):
|
|||
self.db_repo.repo_id, pull_request=pr,
|
||||
include_drafts=False, count_only=True)
|
||||
|
||||
review_statuses = pr.reviewers_statuses(user=self._rhodecode_db_user)
|
||||
my_review_status = ChangesetStatus.STATUS_NOT_REVIEWED
|
||||
if review_statuses and review_statuses[4]:
|
||||
_review_obj, _user, _reasons, _mandatory, statuses = review_statuses
|
||||
my_review_status = statuses[0][1].status
|
||||
|
||||
data.append({
|
||||
'name': _render('pullrequest_name',
|
||||
pr.pull_request_id, pr.pull_request_state,
|
||||
|
|
@ -118,6 +123,8 @@ class RepoPullRequestsView(RepoAppView, DataGridAppView):
|
|||
'name_raw': pr.pull_request_id,
|
||||
'status': _render('pullrequest_status',
|
||||
pr.calculated_review_status()),
|
||||
'my_status': _render('pullrequest_status',
|
||||
my_review_status),
|
||||
'title': _render('pullrequest_title', pr.title, pr.description),
|
||||
'description': h.escape(pr.description),
|
||||
'updated_on': _render('pullrequest_updated_on',
|
||||
|
|
|
|||
|
|
@ -354,7 +354,7 @@ class ChangesetStatusModel(BaseModel):
|
|||
Session().add(new_status)
|
||||
return new_statuses
|
||||
|
||||
def aggregate_votes_by_user(self, commit_statuses, reviewers_data):
|
||||
def aggregate_votes_by_user(self, commit_statuses, reviewers_data, user=None):
|
||||
|
||||
commit_statuses_map = collections.defaultdict(list)
|
||||
for st in commit_statuses:
|
||||
|
|
@ -368,6 +368,10 @@ class ChangesetStatusModel(BaseModel):
|
|||
for obj in reviewers_data:
|
||||
if not obj.user:
|
||||
continue
|
||||
if user and obj.user.username != user.username:
|
||||
# single user filter
|
||||
continue
|
||||
|
||||
statuses = commit_statuses_map.get(obj.user.username, None)
|
||||
if statuses:
|
||||
status_groups = itertools.groupby(
|
||||
|
|
@ -376,16 +380,19 @@ class ChangesetStatusModel(BaseModel):
|
|||
|
||||
reviewers.append((obj, obj.user, obj.reasons, obj.mandatory, statuses))
|
||||
|
||||
return reviewers
|
||||
if user:
|
||||
return reviewers[0] if reviewers else reviewers
|
||||
else:
|
||||
return reviewers
|
||||
|
||||
def reviewers_statuses(self, pull_request):
|
||||
def reviewers_statuses(self, pull_request, user=None):
|
||||
_commit_statuses = self.get_statuses(
|
||||
pull_request.source_repo,
|
||||
pull_request=pull_request,
|
||||
with_revisions=True)
|
||||
reviewers = pull_request.get_pull_request_reviewers(
|
||||
role=PullRequestReviewers.ROLE_REVIEWER)
|
||||
return self.aggregate_votes_by_user(_commit_statuses, reviewers)
|
||||
return self.aggregate_votes_by_user(_commit_statuses, reviewers, user=user)
|
||||
|
||||
def calculated_review_status(self, pull_request):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -41,10 +41,10 @@ from sqlalchemy import (
|
|||
Index, Sequence, UniqueConstraint, ForeignKey, CheckConstraint, Column,
|
||||
Boolean, String, Unicode, UnicodeText, DateTime, Integer, LargeBinary,
|
||||
Text, Float, PickleType, BigInteger)
|
||||
from sqlalchemy.sql.expression import true, false, case
|
||||
from sqlalchemy.sql.expression import true, false, case, null
|
||||
from sqlalchemy.sql.functions import coalesce, count # pragma: no cover
|
||||
from sqlalchemy.orm import (
|
||||
relationship, joinedload, class_mapper, validates, aliased)
|
||||
relationship, lazyload, joinedload, class_mapper, validates, aliased)
|
||||
from sqlalchemy.ext.declarative import declared_attr
|
||||
from sqlalchemy.ext.hybrid import hybrid_property
|
||||
from sqlalchemy.exc import IntegrityError # pragma: no cover
|
||||
|
|
@ -4479,9 +4479,9 @@ class PullRequest(Base, _PullRequestBase):
|
|||
from rhodecode.model.changeset_status import ChangesetStatusModel
|
||||
return ChangesetStatusModel().calculated_review_status(self)
|
||||
|
||||
def reviewers_statuses(self):
|
||||
def reviewers_statuses(self, user=None):
|
||||
from rhodecode.model.changeset_status import ChangesetStatusModel
|
||||
return ChangesetStatusModel().reviewers_statuses(self)
|
||||
return ChangesetStatusModel().reviewers_statuses(self, user=user)
|
||||
|
||||
def get_pull_request_reviewers(self, role=None):
|
||||
qry = PullRequestReviewers.query()\
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ from rhodecode.model import BaseModel
|
|||
from rhodecode.model.changeset_status import ChangesetStatusModel
|
||||
from rhodecode.model.comment import CommentsModel
|
||||
from rhodecode.model.db import (
|
||||
or_, String, cast, PullRequest, PullRequestReviewers, ChangesetStatus,
|
||||
aliased, null, lazyload, and_, or_, func, String, cast, PullRequest, PullRequestReviewers, ChangesetStatus,
|
||||
PullRequestVersion, ChangesetComment, Repository, RepoReviewRule, User)
|
||||
from rhodecode.model.meta import Session
|
||||
from rhodecode.model.notification import NotificationModel, \
|
||||
|
|
@ -319,7 +319,7 @@ class PullRequestModel(BaseModel):
|
|||
|
||||
if search_q:
|
||||
like_expression = u'%{}%'.format(safe_unicode(search_q))
|
||||
q = q.join(User)
|
||||
q = q.join(User, User.user_id == PullRequest.user_id)
|
||||
q = q.filter(or_(
|
||||
cast(PullRequest.pull_request_id, String).ilike(like_expression),
|
||||
User.username.ilike(like_expression),
|
||||
|
|
@ -405,36 +405,30 @@ class PullRequestModel(BaseModel):
|
|||
|
||||
return pull_requests
|
||||
|
||||
def count_awaiting_review(self, repo_name, search_q=None, source=False, statuses=None,
|
||||
opened_by=None):
|
||||
def count_awaiting_review(self, repo_name, search_q=None, statuses=None):
|
||||
"""
|
||||
Count the number of pull requests for a specific repository that are
|
||||
awaiting review.
|
||||
|
||||
:param repo_name: target or source repo
|
||||
:param search_q: filter by text
|
||||
:param source: boolean flag to specify if repo_name refers to source
|
||||
:param statuses: list of pull request statuses
|
||||
:param opened_by: author user of the pull request
|
||||
:returns: int number of pull requests
|
||||
"""
|
||||
pull_requests = self.get_awaiting_review(
|
||||
repo_name, search_q=search_q, source=source, statuses=statuses, opened_by=opened_by)
|
||||
repo_name, search_q=search_q, statuses=statuses)
|
||||
|
||||
return len(pull_requests)
|
||||
|
||||
def get_awaiting_review(self, repo_name, search_q=None, source=False, statuses=None,
|
||||
opened_by=None, offset=0, length=None,
|
||||
order_by=None, order_dir='desc'):
|
||||
def get_awaiting_review(self, repo_name, search_q=None, statuses=None,
|
||||
offset=0, length=None, order_by=None, order_dir='desc'):
|
||||
"""
|
||||
Get all pull requests for a specific repository that are awaiting
|
||||
review.
|
||||
|
||||
:param repo_name: target or source repo
|
||||
:param search_q: filter by text
|
||||
:param source: boolean flag to specify if repo_name refers to source
|
||||
:param statuses: list of pull request statuses
|
||||
:param opened_by: author user of the pull request
|
||||
:param offset: pagination offset
|
||||
:param length: length of returned list
|
||||
:param order_by: order of the returned list
|
||||
|
|
@ -442,8 +436,8 @@ class PullRequestModel(BaseModel):
|
|||
:returns: list of pull requests
|
||||
"""
|
||||
pull_requests = self.get_all(
|
||||
repo_name, search_q=search_q, source=source, statuses=statuses,
|
||||
opened_by=opened_by, order_by=order_by, order_dir=order_dir)
|
||||
repo_name, search_q=search_q, statuses=statuses,
|
||||
order_by=order_by, order_dir=order_dir)
|
||||
|
||||
_filtered_pull_requests = []
|
||||
for pr in pull_requests:
|
||||
|
|
@ -456,68 +450,117 @@ class PullRequestModel(BaseModel):
|
|||
else:
|
||||
return _filtered_pull_requests
|
||||
|
||||
def count_awaiting_my_review(self, repo_name, search_q=None, source=False, statuses=None,
|
||||
opened_by=None, user_id=None):
|
||||
def _prepare_awaiting_my_review_review_query(
|
||||
self, repo_name, user_id, search_q=None, statuses=None,
|
||||
order_by=None, order_dir='desc'):
|
||||
|
||||
for_review_statuses = [
|
||||
ChangesetStatus.STATUS_UNDER_REVIEW, ChangesetStatus.STATUS_NOT_REVIEWED
|
||||
]
|
||||
|
||||
pull_request_alias = aliased(PullRequest)
|
||||
status_alias = aliased(ChangesetStatus)
|
||||
reviewers_alias = aliased(PullRequestReviewers)
|
||||
repo_alias = aliased(Repository)
|
||||
|
||||
last_ver_subq = Session()\
|
||||
.query(func.min(ChangesetStatus.version)) \
|
||||
.filter(ChangesetStatus.pull_request_id == reviewers_alias.pull_request_id)\
|
||||
.filter(ChangesetStatus.user_id == reviewers_alias.user_id) \
|
||||
.subquery()
|
||||
|
||||
q = Session().query(pull_request_alias) \
|
||||
.options(lazyload(pull_request_alias.author)) \
|
||||
.join(reviewers_alias,
|
||||
reviewers_alias.pull_request_id == pull_request_alias.pull_request_id) \
|
||||
.join(repo_alias,
|
||||
repo_alias.repo_id == pull_request_alias.target_repo_id) \
|
||||
.outerjoin(status_alias,
|
||||
and_(status_alias.user_id == reviewers_alias.user_id,
|
||||
status_alias.pull_request_id == reviewers_alias.pull_request_id)) \
|
||||
.filter(or_(status_alias.version == null(),
|
||||
status_alias.version == last_ver_subq)) \
|
||||
.filter(reviewers_alias.user_id == user_id) \
|
||||
.filter(repo_alias.repo_name == repo_name) \
|
||||
.filter(or_(status_alias.status == null(), status_alias.status.in_(for_review_statuses))) \
|
||||
.group_by(pull_request_alias)
|
||||
|
||||
# closed,opened
|
||||
if statuses:
|
||||
q = q.filter(pull_request_alias.status.in_(statuses))
|
||||
|
||||
if search_q:
|
||||
like_expression = u'%{}%'.format(safe_unicode(search_q))
|
||||
q = q.join(User, User.user_id == pull_request_alias.user_id)
|
||||
q = q.filter(or_(
|
||||
cast(pull_request_alias.pull_request_id, String).ilike(like_expression),
|
||||
User.username.ilike(like_expression),
|
||||
pull_request_alias.title.ilike(like_expression),
|
||||
pull_request_alias.description.ilike(like_expression),
|
||||
))
|
||||
|
||||
if order_by:
|
||||
order_map = {
|
||||
'name_raw': pull_request_alias.pull_request_id,
|
||||
'title': pull_request_alias.title,
|
||||
'updated_on_raw': pull_request_alias.updated_on,
|
||||
'target_repo': pull_request_alias.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_awaiting_my_review(self, repo_name, user_id, search_q=None, statuses=None):
|
||||
"""
|
||||
Count the number of pull requests for a specific repository that are
|
||||
awaiting review from a specific user.
|
||||
|
||||
:param repo_name: target or source repo
|
||||
:param search_q: filter by text
|
||||
:param source: boolean flag to specify if repo_name refers to source
|
||||
:param statuses: list of pull request statuses
|
||||
:param opened_by: author user of the pull request
|
||||
:param user_id: reviewer user of the pull request
|
||||
:param search_q: filter by text
|
||||
:param statuses: list of pull request statuses
|
||||
:returns: int number of pull requests
|
||||
"""
|
||||
pull_requests = self.get_awaiting_my_review(
|
||||
repo_name, search_q=search_q, source=source, statuses=statuses,
|
||||
opened_by=opened_by, user_id=user_id)
|
||||
q = self._prepare_awaiting_my_review_review_query(
|
||||
repo_name, user_id, search_q=search_q, statuses=statuses)
|
||||
return q.count()
|
||||
|
||||
return len(pull_requests)
|
||||
|
||||
def get_awaiting_my_review(self, repo_name, search_q=None, source=False, statuses=None,
|
||||
opened_by=None, user_id=None, offset=0,
|
||||
length=None, order_by=None, order_dir='desc'):
|
||||
def get_awaiting_my_review(self, repo_name, user_id, search_q=None, statuses=None,
|
||||
offset=0, length=None, order_by=None, order_dir='desc'):
|
||||
"""
|
||||
Get all pull requests for a specific repository that are awaiting
|
||||
review from a specific user.
|
||||
|
||||
:param repo_name: target or source repo
|
||||
:param search_q: filter by text
|
||||
:param source: boolean flag to specify if repo_name refers to source
|
||||
:param statuses: list of pull request statuses
|
||||
:param opened_by: author user of the pull request
|
||||
:param user_id: reviewer user of the pull request
|
||||
:param search_q: filter by text
|
||||
:param statuses: list of pull request statuses
|
||||
:param offset: pagination offset
|
||||
:param length: length of returned list
|
||||
:param order_by: order of the returned list
|
||||
:param order_dir: 'asc' or 'desc' ordering direction
|
||||
:returns: list of pull requests
|
||||
"""
|
||||
pull_requests = self.get_all(
|
||||
repo_name, search_q=search_q, source=source, statuses=statuses,
|
||||
opened_by=opened_by, order_by=order_by, order_dir=order_dir)
|
||||
|
||||
_my = PullRequestModel().get_not_reviewed(user_id)
|
||||
my_participation = []
|
||||
for pr in pull_requests:
|
||||
if pr in _my:
|
||||
my_participation.append(pr)
|
||||
_filtered_pull_requests = my_participation
|
||||
q = self._prepare_awaiting_my_review_review_query(
|
||||
repo_name, user_id, search_q=search_q, statuses=statuses,
|
||||
order_by=order_by, order_dir=order_dir)
|
||||
|
||||
if length:
|
||||
return _filtered_pull_requests[offset:offset+length]
|
||||
pull_requests = q.limit(length).offset(offset).all()
|
||||
else:
|
||||
return _filtered_pull_requests
|
||||
pull_requests = q.all()
|
||||
|
||||
def get_not_reviewed(self, user_id):
|
||||
return [
|
||||
x.pull_request for x in PullRequestReviewers.query().filter(
|
||||
PullRequestReviewers.user_id == user_id).all()
|
||||
]
|
||||
return pull_requests
|
||||
|
||||
def _prepare_participating_query(self, user_id=None, statuses=None, query='',
|
||||
order_by=None, order_dir='desc'):
|
||||
def _prepare_im_participating_query(self, user_id=None, statuses=None, query='',
|
||||
order_by=None, order_dir='desc'):
|
||||
"""
|
||||
return a query of pull-requests user is an creator, or he's added as a reviewer
|
||||
"""
|
||||
q = PullRequest.query()
|
||||
if user_id:
|
||||
reviewers_subquery = Session().query(
|
||||
|
|
@ -535,7 +578,7 @@ class PullRequestModel(BaseModel):
|
|||
|
||||
if query:
|
||||
like_expression = u'%{}%'.format(safe_unicode(query))
|
||||
q = q.join(User)
|
||||
q = q.join(User, User.user_id == PullRequest.user_id)
|
||||
q = q.filter(or_(
|
||||
cast(PullRequest.pull_request_id, String).ilike(like_expression),
|
||||
User.username.ilike(like_expression),
|
||||
|
|
@ -557,17 +600,97 @@ class PullRequestModel(BaseModel):
|
|||
return q
|
||||
|
||||
def count_im_participating_in(self, user_id=None, statuses=None, query=''):
|
||||
q = self._prepare_participating_query(user_id, statuses=statuses, query=query)
|
||||
q = self._prepare_im_participating_query(user_id, statuses=statuses, query=query)
|
||||
return q.count()
|
||||
|
||||
def get_im_participating_in(
|
||||
self, user_id=None, statuses=None, query='', offset=0,
|
||||
length=None, order_by=None, order_dir='desc'):
|
||||
"""
|
||||
Get all Pull requests that i'm participating in, or i have opened
|
||||
Get all Pull requests that i'm participating in as a reviewer, or i have opened
|
||||
"""
|
||||
|
||||
q = self._prepare_participating_query(
|
||||
q = self._prepare_im_participating_query(
|
||||
user_id, statuses=statuses, query=query, 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 _prepare_participating_in_for_review_query(
|
||||
self, user_id, statuses=None, query='', order_by=None, order_dir='desc'):
|
||||
|
||||
for_review_statuses = [
|
||||
ChangesetStatus.STATUS_UNDER_REVIEW, ChangesetStatus.STATUS_NOT_REVIEWED
|
||||
]
|
||||
|
||||
pull_request_alias = aliased(PullRequest)
|
||||
status_alias = aliased(ChangesetStatus)
|
||||
reviewers_alias = aliased(PullRequestReviewers)
|
||||
|
||||
last_ver_subq = Session()\
|
||||
.query(func.min(ChangesetStatus.version)) \
|
||||
.filter(ChangesetStatus.pull_request_id == reviewers_alias.pull_request_id)\
|
||||
.filter(ChangesetStatus.user_id == reviewers_alias.user_id) \
|
||||
.subquery()
|
||||
|
||||
q = Session().query(pull_request_alias) \
|
||||
.options(lazyload(pull_request_alias.author)) \
|
||||
.join(reviewers_alias,
|
||||
reviewers_alias.pull_request_id == pull_request_alias.pull_request_id) \
|
||||
.outerjoin(status_alias,
|
||||
and_(status_alias.user_id == reviewers_alias.user_id,
|
||||
status_alias.pull_request_id == reviewers_alias.pull_request_id)) \
|
||||
.filter(or_(status_alias.version == null(),
|
||||
status_alias.version == last_ver_subq)) \
|
||||
.filter(reviewers_alias.user_id == user_id) \
|
||||
.filter(or_(status_alias.status == null(), status_alias.status.in_(for_review_statuses))) \
|
||||
.group_by(pull_request_alias)
|
||||
|
||||
# closed,opened
|
||||
if statuses:
|
||||
q = q.filter(pull_request_alias.status.in_(statuses))
|
||||
|
||||
if query:
|
||||
like_expression = u'%{}%'.format(safe_unicode(query))
|
||||
q = q.join(User, User.user_id == pull_request_alias.user_id)
|
||||
q = q.filter(or_(
|
||||
cast(pull_request_alias.pull_request_id, String).ilike(like_expression),
|
||||
User.username.ilike(like_expression),
|
||||
pull_request_alias.title.ilike(like_expression),
|
||||
pull_request_alias.description.ilike(like_expression),
|
||||
))
|
||||
|
||||
if order_by:
|
||||
order_map = {
|
||||
'name_raw': pull_request_alias.pull_request_id,
|
||||
'title': pull_request_alias.title,
|
||||
'updated_on_raw': pull_request_alias.updated_on,
|
||||
'target_repo': pull_request_alias.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_for_review(self, user_id, statuses=None, query=''):
|
||||
q = self._prepare_participating_in_for_review_query(user_id, statuses=statuses, query=query)
|
||||
return q.count()
|
||||
|
||||
def get_im_participating_in_for_review(
|
||||
self, user_id, statuses=None, query='', offset=0,
|
||||
length=None, order_by=None, order_dir='desc'):
|
||||
"""
|
||||
Get all Pull requests that needs user approval or rejection
|
||||
"""
|
||||
|
||||
q = self._prepare_participating_in_for_review_query(
|
||||
user_id, statuses=statuses, query=query, order_by=order_by,
|
||||
order_dir=order_dir)
|
||||
|
||||
|
|
|
|||
|
|
@ -618,13 +618,13 @@ input[type="reset"] {
|
|||
text-align: right;
|
||||
|
||||
li {
|
||||
|
||||
|
||||
list-style: none;
|
||||
text-align: right;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
li.active {
|
||||
background-color: @grey6;
|
||||
.border ( @border-thickness, @grey4 );
|
||||
a.active {
|
||||
border: 2px solid @rcblue;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
padding: @panel-padding;
|
||||
|
||||
&.panel-body-min-height {
|
||||
min-height: 150px
|
||||
min-height: 200px
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,17 +1,29 @@
|
|||
<%namespace name="base" file="/base/base.mako"/>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<div style="height: 35px">
|
||||
<%
|
||||
selected_filter = 'all'
|
||||
if c.closed:
|
||||
selected_filter = 'all_closed'
|
||||
%>
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">${_('Pull Requests You Participate In')}</h3>
|
||||
</div>
|
||||
|
||||
<div class="panel-body panel-body-min-height">
|
||||
<div class="title">
|
||||
<ul class="button-links">
|
||||
<li class="btn ${h.is_active('all', selected_filter)}"><a href="${h.route_path('my_account_pullrequests')}">${_('All')}</a></li>
|
||||
<li class="btn ${h.is_active('all_closed', selected_filter)}"><a href="${h.route_path('my_account_pullrequests', _query={'pr_show_closed':1})}">${_('All + Closed')}</a></li>
|
||||
<li><a class="btn ${h.is_active('all', c.selected_filter)}"
|
||||
href="${h.route_path('my_account_pullrequests', _query={})}">
|
||||
${_('Open')}
|
||||
</a>
|
||||
</li>
|
||||
<li><a class="btn ${h.is_active('all_closed', c.selected_filter)}"
|
||||
href="${h.route_path('my_account_pullrequests', _query={'closed':1})}">
|
||||
${_('All + Closed')}
|
||||
</a>
|
||||
</li>
|
||||
<li><a class="btn ${h.is_active('awaiting_my_review', c.selected_filter)}"
|
||||
href="${h.route_path('my_account_pullrequests', _query={'awaiting_my_review':1})}">
|
||||
|
||||
${_('Awaiting my review')}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="grid-quick-filter">
|
||||
|
|
@ -20,19 +32,13 @@
|
|||
<i class="icon-search"></i>
|
||||
</li>
|
||||
<li class="grid-filter-box-input">
|
||||
<input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
|
||||
<input class="q_filter_box" id="q_filter" size="15" type="text" name="filter"
|
||||
placeholder="${_('quick filter...')}" value=""/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">${_('Pull Requests You Participate In')}</h3>
|
||||
</div>
|
||||
<div class="panel-body panel-body-min-height">
|
||||
<table id="pull_request_list_table" class="rctable table-bordered"></table>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -52,6 +58,7 @@
|
|||
"url": "${h.route_path('my_account_pullrequests_data')}",
|
||||
"data": function (d) {
|
||||
d.closed = "${c.closed}";
|
||||
d.awaiting_my_review = "${c.awaiting_my_review}";
|
||||
},
|
||||
"dataSrc": function (json) {
|
||||
return json.data;
|
||||
|
|
@ -60,13 +67,19 @@
|
|||
|
||||
dom: 'rtp',
|
||||
pageLength: ${c.visual.dashboard_items},
|
||||
order: [[1, "desc"]],
|
||||
order: [[2, "desc"]],
|
||||
columns: [
|
||||
{
|
||||
data: {
|
||||
"_": "status",
|
||||
"sort": "status"
|
||||
}, title: "", className: "td-status", orderable: false
|
||||
}, title: "PR", className: "td-status", orderable: false
|
||||
},
|
||||
{
|
||||
data: {
|
||||
"_": "my_status",
|
||||
"sort": "status"
|
||||
}, title: "You", className: "td-status", orderable: false
|
||||
},
|
||||
{
|
||||
data: {
|
||||
|
|
|
|||
|
|
@ -23,13 +23,14 @@
|
|||
|
||||
<div class="box">
|
||||
<div class="title">
|
||||
|
||||
<ul class="button-links">
|
||||
<li class="btn ${h.is_active('open', c.active)}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0})}">${_('Opened')}</a></li>
|
||||
<li class="btn ${h.is_active('my', c.active)}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0,'my':1})}">${_('Opened by me')}</a></li>
|
||||
<li class="btn ${h.is_active('awaiting', c.active)}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0,'awaiting_review':1})}">${_('Awaiting review')}</a></li>
|
||||
<li class="btn ${h.is_active('awaiting_my', c.active)}"><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>
|
||||
<li class="btn ${h.is_active('closed', c.active)}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0,'closed':1})}">${_('Closed')}</a></li>
|
||||
<li class="btn ${h.is_active('source', c.active)}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':1})}">${_('From this repo')}</a></li>
|
||||
<li><a class="btn ${h.is_active('open', c.active)}" href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0,'open':1})}">${_('Open')}</a></li>
|
||||
<li><a class="btn ${h.is_active('my', c.active)}" href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0,'my':1})}">${_('Created by me')}</a></li>
|
||||
<li><a class="btn ${h.is_active('awaiting', c.active)}" href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0,'awaiting_review':1})}">${_('Awaiting review')}</a></li>
|
||||
<li><a class="btn ${h.is_active('awaiting_my', c.active)}" href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0,'awaiting_my_review':1})}">${_('Awaiting my review')}</a></li>
|
||||
<li><a class="btn ${h.is_active('closed', c.active)}" href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0,'closed':1})}">${_('Closed')}</a></li>
|
||||
<li><a class="btn ${h.is_active('source', c.active)}" href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':1})}">${_('From this repo')}</a></li>
|
||||
</ul>
|
||||
|
||||
<ul class="links">
|
||||
|
|
@ -88,20 +89,50 @@ $(document).ready(function() {
|
|||
},
|
||||
dom: 'rtp',
|
||||
pageLength: ${c.visual.dashboard_items},
|
||||
order: [[ 1, "desc" ]],
|
||||
order: [[ 2, "desc" ]],
|
||||
columns: [
|
||||
{ data: {"_": "status",
|
||||
"sort": "status"}, title: "", className: "td-status", orderable: false},
|
||||
{ data: {"_": "name",
|
||||
"sort": "name_raw"}, title: "${_('Id')}", className: "td-componentname", "type": "num" },
|
||||
{ data: {"_": "title",
|
||||
"sort": "title"}, title: "${_('Title')}", className: "td-description" },
|
||||
{ data: {"_": "author",
|
||||
"sort": "author_raw"}, title: "${_('Author')}", className: "td-user", orderable: false },
|
||||
{ data: {"_": "comments",
|
||||
"sort": "comments_raw"}, title: "", className: "td-comments", orderable: false},
|
||||
{ data: {"_": "updated_on",
|
||||
"sort": "updated_on_raw"}, title: "${_('Last Update')}", className: "td-time" }
|
||||
{
|
||||
data: {
|
||||
"_": "status",
|
||||
"sort": "status"
|
||||
}, title: "PR", className: "td-status", orderable: false
|
||||
},
|
||||
{
|
||||
data: {
|
||||
"_": "my_status",
|
||||
"sort": "status"
|
||||
}, title: "You", className: "td-status", orderable: false
|
||||
},
|
||||
{
|
||||
data: {
|
||||
"_": "name",
|
||||
"sort": "name_raw"
|
||||
}, title: "${_('Id')}", className: "td-componentname", "type": "num"
|
||||
},
|
||||
{
|
||||
data: {
|
||||
"_": "title",
|
||||
"sort": "title"
|
||||
}, title: "${_('Title')}", className: "td-description"
|
||||
},
|
||||
{
|
||||
data: {
|
||||
"_": "author",
|
||||
"sort": "author_raw"
|
||||
}, title: "${_('Author')}", className: "td-user", orderable: false
|
||||
},
|
||||
{
|
||||
data: {
|
||||
"_": "comments",
|
||||
"sort": "comments_raw"
|
||||
}, title: "", className: "td-comments", orderable: false
|
||||
},
|
||||
{
|
||||
data: {
|
||||
"_": "updated_on",
|
||||
"sort": "updated_on_raw"
|
||||
}, title: "${_('Last Update')}", className: "td-time"
|
||||
}
|
||||
],
|
||||
language: {
|
||||
paginate: DEFAULT_GRID_PAGINATION,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue