##// END OF EJS Templates
pull-requests: added awaiting my review filter for users pull-requests....
super-admin -
r4690:2e951f8d stable
parent child Browse files
Show More
@@ -76,7 +76,11 b' class TestMyAccountEdit(TestController):'
76 76 'requests requiring your participation.')
77 77
78 78 @pytest.mark.backends("git", "hg")
79 def test_my_account_my_pullrequests_data(self, pr_util, xhr_header):
79 @pytest.mark.parametrize('params, expected_title', [
80 ({'closed': 1}, 'Closed'),
81 ({'awaiting_my_review': 1}, 'Awaiting my review'),
82 ])
83 def test_my_account_my_pullrequests_data(self, pr_util, xhr_header, params, expected_title):
80 84 self.log_user()
81 85 response = self.app.get(route_path('my_account_pullrequests_data'),
82 86 extra_environ=xhr_header)
@@ -43,7 +43,7 b' from rhodecode.model.comment import Comm'
43 43 from rhodecode.model.db import (
44 44 IntegrityError, or_, in_filter_generator,
45 45 Repository, UserEmailMap, UserApiKeys, UserFollowing,
46 PullRequest, UserBookmark, RepoGroup)
46 PullRequest, UserBookmark, RepoGroup, ChangesetStatus)
47 47 from rhodecode.model.meta import Session
48 48 from rhodecode.model.pull_request import PullRequestModel
49 49 from rhodecode.model.user import UserModel
@@ -654,13 +654,23 b' class MyAccountView(BaseAppView, DataGri'
654 654 Session().commit()
655 655 return user.user_data['notification_status']
656 656
657 def _get_pull_requests_list(self, statuses):
657 def _get_pull_requests_list(self, statuses, filter_type=None):
658 658 draw, start, limit = self._extract_chunk(self.request)
659 659 search_q, order_by, order_dir = self._extract_ordering(self.request)
660 660
661 661 _render = self.request.get_partial_renderer(
662 662 'rhodecode:templates/data_table/_dt_elements.mako')
663 663
664 if filter_type == 'awaiting_my_review':
665 pull_requests = PullRequestModel().get_im_participating_in_for_review(
666 user_id=self._rhodecode_user.user_id,
667 statuses=statuses, query=search_q,
668 offset=start, length=limit, order_by=order_by,
669 order_dir=order_dir)
670
671 pull_requests_total_count = PullRequestModel().count_im_participating_in_for_review(
672 user_id=self._rhodecode_user.user_id, statuses=statuses, query=search_q)
673 else:
664 674 pull_requests = PullRequestModel().get_im_participating_in(
665 675 user_id=self._rhodecode_user.user_id,
666 676 statuses=statuses, query=search_q,
@@ -678,6 +688,12 b' class MyAccountView(BaseAppView, DataGri'
678 688 repo_id, pull_request=pr, include_drafts=False, count_only=True)
679 689 owned = pr.user_id == self._rhodecode_user.user_id
680 690
691 review_statuses = pr.reviewers_statuses(user=self._rhodecode_db_user)
692 my_review_status = ChangesetStatus.STATUS_NOT_REVIEWED
693 if review_statuses and review_statuses[4]:
694 _review_obj, _user, _reasons, _mandatory, statuses = review_statuses
695 my_review_status = statuses[0][1].status
696
681 697 data.append({
682 698 'target_repo': _render('pullrequest_target_repo',
683 699 pr.target_repo.repo_name),
@@ -688,6 +704,8 b' class MyAccountView(BaseAppView, DataGri'
688 704 'name_raw': pr.pull_request_id,
689 705 'status': _render('pullrequest_status',
690 706 pr.calculated_review_status()),
707 'my_status': _render('pullrequest_status',
708 my_review_status),
691 709 'title': _render('pullrequest_title', pr.title, pr.description),
692 710 'description': h.escape(pr.description),
693 711 'updated_on': _render('pullrequest_updated_on',
@@ -723,7 +741,14 b' class MyAccountView(BaseAppView, DataGri'
723 741 c.active = 'pullrequests'
724 742 req_get = self.request.GET
725 743
726 c.closed = str2bool(req_get.get('pr_show_closed'))
744 c.closed = str2bool(req_get.get('closed'))
745 c.awaiting_my_review = str2bool(req_get.get('awaiting_my_review'))
746
747 c.selected_filter = 'all'
748 if c.closed:
749 c.selected_filter = 'all_closed'
750 if c.awaiting_my_review:
751 c.selected_filter = 'awaiting_my_review'
727 752
728 753 return self._get_template_context(c)
729 754
@@ -732,13 +757,19 b' class MyAccountView(BaseAppView, DataGri'
732 757 def my_account_pullrequests_data(self):
733 758 self.load_default_context()
734 759 req_get = self.request.GET
760
761 awaiting_my_review = str2bool(req_get.get('awaiting_my_review'))
735 762 closed = str2bool(req_get.get('closed'))
736 763
737 764 statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN]
738 765 if closed:
739 766 statuses += [PullRequest.STATUS_CLOSED]
740 767
741 data = self._get_pull_requests_list(statuses=statuses)
768 filter_type = \
769 'awaiting_my_review' if awaiting_my_review \
770 else None
771
772 data = self._get_pull_requests_list(statuses=statuses, filter_type=filter_type)
742 773 return data
743 774
744 775 @LoginRequired()
@@ -41,7 +41,7 b' class TestPullRequestList(object):'
41 41
42 42 @pytest.mark.parametrize('params, expected_title', [
43 43 ({'source': 0, 'closed': 1}, 'Closed'),
44 ({'source': 0, 'my': 1}, 'Opened by me'),
44 ({'source': 0, 'my': 1}, 'Created by me'),
45 45 ({'source': 0, 'awaiting_review': 1}, 'Awaiting review'),
46 46 ({'source': 0, 'awaiting_my_review': 1}, 'Awaiting my review'),
47 47 ({'source': 1}, 'From this repo'),
@@ -79,21 +79,20 b' class RepoPullRequestsView(RepoAppView, '
79 79
80 80 if filter_type == 'awaiting_review':
81 81 pull_requests = PullRequestModel().get_awaiting_review(
82 repo_name, search_q=search_q, source=source, opened_by=opened_by,
83 statuses=statuses, offset=start, length=limit,
84 order_by=order_by, order_dir=order_dir)
82 repo_name,
83 search_q=search_q, statuses=statuses,
84 offset=start, length=limit, order_by=order_by, order_dir=order_dir)
85 85 pull_requests_total_count = PullRequestModel().count_awaiting_review(
86 repo_name, search_q=search_q, source=source, statuses=statuses,
87 opened_by=opened_by)
86 repo_name,
87 search_q=search_q, statuses=statuses)
88 88 elif filter_type == 'awaiting_my_review':
89 89 pull_requests = PullRequestModel().get_awaiting_my_review(
90 repo_name, search_q=search_q, source=source, opened_by=opened_by,
91 user_id=self._rhodecode_user.user_id, statuses=statuses,
92 offset=start, length=limit, order_by=order_by,
93 order_dir=order_dir)
90 repo_name, self._rhodecode_user.user_id,
91 search_q=search_q, statuses=statuses,
92 offset=start, length=limit, order_by=order_by, order_dir=order_dir)
94 93 pull_requests_total_count = PullRequestModel().count_awaiting_my_review(
95 repo_name, search_q=search_q, source=source, user_id=self._rhodecode_user.user_id,
96 statuses=statuses, opened_by=opened_by)
94 repo_name, self._rhodecode_user.user_id,
95 search_q=search_q, statuses=statuses)
97 96 else:
98 97 pull_requests = PullRequestModel().get_all(
99 98 repo_name, search_q=search_q, source=source, opened_by=opened_by,
@@ -110,6 +109,12 b' class RepoPullRequestsView(RepoAppView, '
110 109 self.db_repo.repo_id, pull_request=pr,
111 110 include_drafts=False, count_only=True)
112 111
112 review_statuses = pr.reviewers_statuses(user=self._rhodecode_db_user)
113 my_review_status = ChangesetStatus.STATUS_NOT_REVIEWED
114 if review_statuses and review_statuses[4]:
115 _review_obj, _user, _reasons, _mandatory, statuses = review_statuses
116 my_review_status = statuses[0][1].status
117
113 118 data.append({
114 119 'name': _render('pullrequest_name',
115 120 pr.pull_request_id, pr.pull_request_state,
@@ -118,6 +123,8 b' class RepoPullRequestsView(RepoAppView, '
118 123 'name_raw': pr.pull_request_id,
119 124 'status': _render('pullrequest_status',
120 125 pr.calculated_review_status()),
126 'my_status': _render('pullrequest_status',
127 my_review_status),
121 128 'title': _render('pullrequest_title', pr.title, pr.description),
122 129 'description': h.escape(pr.description),
123 130 'updated_on': _render('pullrequest_updated_on',
@@ -354,7 +354,7 b' class ChangesetStatusModel(BaseModel):'
354 354 Session().add(new_status)
355 355 return new_statuses
356 356
357 def aggregate_votes_by_user(self, commit_statuses, reviewers_data):
357 def aggregate_votes_by_user(self, commit_statuses, reviewers_data, user=None):
358 358
359 359 commit_statuses_map = collections.defaultdict(list)
360 360 for st in commit_statuses:
@@ -368,6 +368,10 b' class ChangesetStatusModel(BaseModel):'
368 368 for obj in reviewers_data:
369 369 if not obj.user:
370 370 continue
371 if user and obj.user.username != user.username:
372 # single user filter
373 continue
374
371 375 statuses = commit_statuses_map.get(obj.user.username, None)
372 376 if statuses:
373 377 status_groups = itertools.groupby(
@@ -376,16 +380,19 b' class ChangesetStatusModel(BaseModel):'
376 380
377 381 reviewers.append((obj, obj.user, obj.reasons, obj.mandatory, statuses))
378 382
383 if user:
384 return reviewers[0] if reviewers else reviewers
385 else:
379 386 return reviewers
380 387
381 def reviewers_statuses(self, pull_request):
388 def reviewers_statuses(self, pull_request, user=None):
382 389 _commit_statuses = self.get_statuses(
383 390 pull_request.source_repo,
384 391 pull_request=pull_request,
385 392 with_revisions=True)
386 393 reviewers = pull_request.get_pull_request_reviewers(
387 394 role=PullRequestReviewers.ROLE_REVIEWER)
388 return self.aggregate_votes_by_user(_commit_statuses, reviewers)
395 return self.aggregate_votes_by_user(_commit_statuses, reviewers, user=user)
389 396
390 397 def calculated_review_status(self, pull_request):
391 398 """
@@ -41,10 +41,10 b' from sqlalchemy import ('
41 41 Index, Sequence, UniqueConstraint, ForeignKey, CheckConstraint, Column,
42 42 Boolean, String, Unicode, UnicodeText, DateTime, Integer, LargeBinary,
43 43 Text, Float, PickleType, BigInteger)
44 from sqlalchemy.sql.expression import true, false, case
44 from sqlalchemy.sql.expression import true, false, case, null
45 45 from sqlalchemy.sql.functions import coalesce, count # pragma: no cover
46 46 from sqlalchemy.orm import (
47 relationship, joinedload, class_mapper, validates, aliased)
47 relationship, lazyload, joinedload, class_mapper, validates, aliased)
48 48 from sqlalchemy.ext.declarative import declared_attr
49 49 from sqlalchemy.ext.hybrid import hybrid_property
50 50 from sqlalchemy.exc import IntegrityError # pragma: no cover
@@ -4479,9 +4479,9 b' class PullRequest(Base, _PullRequestBase'
4479 4479 from rhodecode.model.changeset_status import ChangesetStatusModel
4480 4480 return ChangesetStatusModel().calculated_review_status(self)
4481 4481
4482 def reviewers_statuses(self):
4482 def reviewers_statuses(self, user=None):
4483 4483 from rhodecode.model.changeset_status import ChangesetStatusModel
4484 return ChangesetStatusModel().reviewers_statuses(self)
4484 return ChangesetStatusModel().reviewers_statuses(self, user=user)
4485 4485
4486 4486 def get_pull_request_reviewers(self, role=None):
4487 4487 qry = PullRequestReviewers.query()\
@@ -56,7 +56,7 b' from rhodecode.model import BaseModel'
56 56 from rhodecode.model.changeset_status import ChangesetStatusModel
57 57 from rhodecode.model.comment import CommentsModel
58 58 from rhodecode.model.db import (
59 or_, String, cast, PullRequest, PullRequestReviewers, ChangesetStatus,
59 aliased, null, lazyload, and_, or_, func, String, cast, PullRequest, PullRequestReviewers, ChangesetStatus,
60 60 PullRequestVersion, ChangesetComment, Repository, RepoReviewRule, User)
61 61 from rhodecode.model.meta import Session
62 62 from rhodecode.model.notification import NotificationModel, \
@@ -319,7 +319,7 b' class PullRequestModel(BaseModel):'
319 319
320 320 if search_q:
321 321 like_expression = u'%{}%'.format(safe_unicode(search_q))
322 q = q.join(User)
322 q = q.join(User, User.user_id == PullRequest.user_id)
323 323 q = q.filter(or_(
324 324 cast(PullRequest.pull_request_id, String).ilike(like_expression),
325 325 User.username.ilike(like_expression),
@@ -405,36 +405,30 b' class PullRequestModel(BaseModel):'
405 405
406 406 return pull_requests
407 407
408 def count_awaiting_review(self, repo_name, search_q=None, source=False, statuses=None,
409 opened_by=None):
408 def count_awaiting_review(self, repo_name, search_q=None, statuses=None):
410 409 """
411 410 Count the number of pull requests for a specific repository that are
412 411 awaiting review.
413 412
414 413 :param repo_name: target or source repo
415 414 :param search_q: filter by text
416 :param source: boolean flag to specify if repo_name refers to source
417 415 :param statuses: list of pull request statuses
418 :param opened_by: author user of the pull request
419 416 :returns: int number of pull requests
420 417 """
421 418 pull_requests = self.get_awaiting_review(
422 repo_name, search_q=search_q, source=source, statuses=statuses, opened_by=opened_by)
419 repo_name, search_q=search_q, statuses=statuses)
423 420
424 421 return len(pull_requests)
425 422
426 def get_awaiting_review(self, repo_name, search_q=None, source=False, statuses=None,
427 opened_by=None, offset=0, length=None,
428 order_by=None, order_dir='desc'):
423 def get_awaiting_review(self, repo_name, search_q=None, statuses=None,
424 offset=0, length=None, order_by=None, order_dir='desc'):
429 425 """
430 426 Get all pull requests for a specific repository that are awaiting
431 427 review.
432 428
433 429 :param repo_name: target or source repo
434 430 :param search_q: filter by text
435 :param source: boolean flag to specify if repo_name refers to source
436 431 :param statuses: list of pull request statuses
437 :param opened_by: author user of the pull request
438 432 :param offset: pagination offset
439 433 :param length: length of returned list
440 434 :param order_by: order of the returned list
@@ -442,8 +436,8 b' class PullRequestModel(BaseModel):'
442 436 :returns: list of pull requests
443 437 """
444 438 pull_requests = self.get_all(
445 repo_name, search_q=search_q, source=source, statuses=statuses,
446 opened_by=opened_by, order_by=order_by, order_dir=order_dir)
439 repo_name, search_q=search_q, statuses=statuses,
440 order_by=order_by, order_dir=order_dir)
447 441
448 442 _filtered_pull_requests = []
449 443 for pr in pull_requests:
@@ -456,68 +450,117 b' class PullRequestModel(BaseModel):'
456 450 else:
457 451 return _filtered_pull_requests
458 452
459 def count_awaiting_my_review(self, repo_name, search_q=None, source=False, statuses=None,
460 opened_by=None, user_id=None):
453 def _prepare_awaiting_my_review_review_query(
454 self, repo_name, user_id, search_q=None, statuses=None,
455 order_by=None, order_dir='desc'):
456
457 for_review_statuses = [
458 ChangesetStatus.STATUS_UNDER_REVIEW, ChangesetStatus.STATUS_NOT_REVIEWED
459 ]
460
461 pull_request_alias = aliased(PullRequest)
462 status_alias = aliased(ChangesetStatus)
463 reviewers_alias = aliased(PullRequestReviewers)
464 repo_alias = aliased(Repository)
465
466 last_ver_subq = Session()\
467 .query(func.min(ChangesetStatus.version)) \
468 .filter(ChangesetStatus.pull_request_id == reviewers_alias.pull_request_id)\
469 .filter(ChangesetStatus.user_id == reviewers_alias.user_id) \
470 .subquery()
471
472 q = Session().query(pull_request_alias) \
473 .options(lazyload(pull_request_alias.author)) \
474 .join(reviewers_alias,
475 reviewers_alias.pull_request_id == pull_request_alias.pull_request_id) \
476 .join(repo_alias,
477 repo_alias.repo_id == pull_request_alias.target_repo_id) \
478 .outerjoin(status_alias,
479 and_(status_alias.user_id == reviewers_alias.user_id,
480 status_alias.pull_request_id == reviewers_alias.pull_request_id)) \
481 .filter(or_(status_alias.version == null(),
482 status_alias.version == last_ver_subq)) \
483 .filter(reviewers_alias.user_id == user_id) \
484 .filter(repo_alias.repo_name == repo_name) \
485 .filter(or_(status_alias.status == null(), status_alias.status.in_(for_review_statuses))) \
486 .group_by(pull_request_alias)
487
488 # closed,opened
489 if statuses:
490 q = q.filter(pull_request_alias.status.in_(statuses))
491
492 if search_q:
493 like_expression = u'%{}%'.format(safe_unicode(search_q))
494 q = q.join(User, User.user_id == pull_request_alias.user_id)
495 q = q.filter(or_(
496 cast(pull_request_alias.pull_request_id, String).ilike(like_expression),
497 User.username.ilike(like_expression),
498 pull_request_alias.title.ilike(like_expression),
499 pull_request_alias.description.ilike(like_expression),
500 ))
501
502 if order_by:
503 order_map = {
504 'name_raw': pull_request_alias.pull_request_id,
505 'title': pull_request_alias.title,
506 'updated_on_raw': pull_request_alias.updated_on,
507 'target_repo': pull_request_alias.target_repo_id
508 }
509 if order_dir == 'asc':
510 q = q.order_by(order_map[order_by].asc())
511 else:
512 q = q.order_by(order_map[order_by].desc())
513
514 return q
515
516 def count_awaiting_my_review(self, repo_name, user_id, search_q=None, statuses=None):
461 517 """
462 518 Count the number of pull requests for a specific repository that are
463 519 awaiting review from a specific user.
464 520
465 521 :param repo_name: target or source repo
522 :param user_id: reviewer user of the pull request
466 523 :param search_q: filter by text
467 :param source: boolean flag to specify if repo_name refers to source
468 524 :param statuses: list of pull request statuses
469 :param opened_by: author user of the pull request
470 :param user_id: reviewer user of the pull request
471 525 :returns: int number of pull requests
472 526 """
473 pull_requests = self.get_awaiting_my_review(
474 repo_name, search_q=search_q, source=source, statuses=statuses,
475 opened_by=opened_by, user_id=user_id)
527 q = self._prepare_awaiting_my_review_review_query(
528 repo_name, user_id, search_q=search_q, statuses=statuses)
529 return q.count()
476 530
477 return len(pull_requests)
478
479 def get_awaiting_my_review(self, repo_name, search_q=None, source=False, statuses=None,
480 opened_by=None, user_id=None, offset=0,
481 length=None, order_by=None, order_dir='desc'):
531 def get_awaiting_my_review(self, repo_name, user_id, search_q=None, statuses=None,
532 offset=0, length=None, order_by=None, order_dir='desc'):
482 533 """
483 534 Get all pull requests for a specific repository that are awaiting
484 535 review from a specific user.
485 536
486 537 :param repo_name: target or source repo
538 :param user_id: reviewer user of the pull request
487 539 :param search_q: filter by text
488 :param source: boolean flag to specify if repo_name refers to source
489 540 :param statuses: list of pull request statuses
490 :param opened_by: author user of the pull request
491 :param user_id: reviewer user of the pull request
492 541 :param offset: pagination offset
493 542 :param length: length of returned list
494 543 :param order_by: order of the returned list
495 544 :param order_dir: 'asc' or 'desc' ordering direction
496 545 :returns: list of pull requests
497 546 """
498 pull_requests = self.get_all(
499 repo_name, search_q=search_q, source=source, statuses=statuses,
500 opened_by=opened_by, order_by=order_by, order_dir=order_dir)
501 547
502 _my = PullRequestModel().get_not_reviewed(user_id)
503 my_participation = []
504 for pr in pull_requests:
505 if pr in _my:
506 my_participation.append(pr)
507 _filtered_pull_requests = my_participation
548 q = self._prepare_awaiting_my_review_review_query(
549 repo_name, user_id, search_q=search_q, statuses=statuses,
550 order_by=order_by, order_dir=order_dir)
551
508 552 if length:
509 return _filtered_pull_requests[offset:offset+length]
553 pull_requests = q.limit(length).offset(offset).all()
510 554 else:
511 return _filtered_pull_requests
555 pull_requests = q.all()
556
557 return pull_requests
512 558
513 def get_not_reviewed(self, user_id):
514 return [
515 x.pull_request for x in PullRequestReviewers.query().filter(
516 PullRequestReviewers.user_id == user_id).all()
517 ]
518
519 def _prepare_participating_query(self, user_id=None, statuses=None, query='',
559 def _prepare_im_participating_query(self, user_id=None, statuses=None, query='',
520 560 order_by=None, order_dir='desc'):
561 """
562 return a query of pull-requests user is an creator, or he's added as a reviewer
563 """
521 564 q = PullRequest.query()
522 565 if user_id:
523 566 reviewers_subquery = Session().query(
@@ -535,7 +578,7 b' class PullRequestModel(BaseModel):'
535 578
536 579 if query:
537 580 like_expression = u'%{}%'.format(safe_unicode(query))
538 q = q.join(User)
581 q = q.join(User, User.user_id == PullRequest.user_id)
539 582 q = q.filter(or_(
540 583 cast(PullRequest.pull_request_id, String).ilike(like_expression),
541 584 User.username.ilike(like_expression),
@@ -557,17 +600,97 b' class PullRequestModel(BaseModel):'
557 600 return q
558 601
559 602 def count_im_participating_in(self, user_id=None, statuses=None, query=''):
560 q = self._prepare_participating_query(user_id, statuses=statuses, query=query)
603 q = self._prepare_im_participating_query(user_id, statuses=statuses, query=query)
561 604 return q.count()
562 605
563 606 def get_im_participating_in(
564 607 self, user_id=None, statuses=None, query='', offset=0,
565 608 length=None, order_by=None, order_dir='desc'):
566 609 """
567 Get all Pull requests that i'm participating in, or i have opened
610 Get all Pull requests that i'm participating in as a reviewer, or i have opened
568 611 """
569 612
570 q = self._prepare_participating_query(
613 q = self._prepare_im_participating_query(
614 user_id, statuses=statuses, query=query, order_by=order_by,
615 order_dir=order_dir)
616
617 if length:
618 pull_requests = q.limit(length).offset(offset).all()
619 else:
620 pull_requests = q.all()
621
622 return pull_requests
623
624 def _prepare_participating_in_for_review_query(
625 self, user_id, statuses=None, query='', order_by=None, order_dir='desc'):
626
627 for_review_statuses = [
628 ChangesetStatus.STATUS_UNDER_REVIEW, ChangesetStatus.STATUS_NOT_REVIEWED
629 ]
630
631 pull_request_alias = aliased(PullRequest)
632 status_alias = aliased(ChangesetStatus)
633 reviewers_alias = aliased(PullRequestReviewers)
634
635 last_ver_subq = Session()\
636 .query(func.min(ChangesetStatus.version)) \
637 .filter(ChangesetStatus.pull_request_id == reviewers_alias.pull_request_id)\
638 .filter(ChangesetStatus.user_id == reviewers_alias.user_id) \
639 .subquery()
640
641 q = Session().query(pull_request_alias) \
642 .options(lazyload(pull_request_alias.author)) \
643 .join(reviewers_alias,
644 reviewers_alias.pull_request_id == pull_request_alias.pull_request_id) \
645 .outerjoin(status_alias,
646 and_(status_alias.user_id == reviewers_alias.user_id,
647 status_alias.pull_request_id == reviewers_alias.pull_request_id)) \
648 .filter(or_(status_alias.version == null(),
649 status_alias.version == last_ver_subq)) \
650 .filter(reviewers_alias.user_id == user_id) \
651 .filter(or_(status_alias.status == null(), status_alias.status.in_(for_review_statuses))) \
652 .group_by(pull_request_alias)
653
654 # closed,opened
655 if statuses:
656 q = q.filter(pull_request_alias.status.in_(statuses))
657
658 if query:
659 like_expression = u'%{}%'.format(safe_unicode(query))
660 q = q.join(User, User.user_id == pull_request_alias.user_id)
661 q = q.filter(or_(
662 cast(pull_request_alias.pull_request_id, String).ilike(like_expression),
663 User.username.ilike(like_expression),
664 pull_request_alias.title.ilike(like_expression),
665 pull_request_alias.description.ilike(like_expression),
666 ))
667
668 if order_by:
669 order_map = {
670 'name_raw': pull_request_alias.pull_request_id,
671 'title': pull_request_alias.title,
672 'updated_on_raw': pull_request_alias.updated_on,
673 'target_repo': pull_request_alias.target_repo_id
674 }
675 if order_dir == 'asc':
676 q = q.order_by(order_map[order_by].asc())
677 else:
678 q = q.order_by(order_map[order_by].desc())
679
680 return q
681
682 def count_im_participating_in_for_review(self, user_id, statuses=None, query=''):
683 q = self._prepare_participating_in_for_review_query(user_id, statuses=statuses, query=query)
684 return q.count()
685
686 def get_im_participating_in_for_review(
687 self, user_id, statuses=None, query='', offset=0,
688 length=None, order_by=None, order_dir='desc'):
689 """
690 Get all Pull requests that needs user approval or rejection
691 """
692
693 q = self._prepare_participating_in_for_review_query(
571 694 user_id, statuses=statuses, query=query, order_by=order_by,
572 695 order_dir=order_dir)
573 696
@@ -618,13 +618,13 b' input[type="reset"] {'
618 618 text-align: right;
619 619
620 620 li {
621
622
621 list-style: none;
622 text-align: right;
623 display: inline-block;
623 624 }
624 625
625 li.active {
626 background-color: @grey6;
627 .border ( @border-thickness, @grey4 );
626 a.active {
627 border: 2px solid @rcblue;
628 628 }
629 629
630 630 }
@@ -44,7 +44,7 b''
44 44 padding: @panel-padding;
45 45
46 46 &.panel-body-min-height {
47 min-height: 150px
47 min-height: 200px
48 48 }
49 49 }
50 50
@@ -1,17 +1,29 b''
1 1 <%namespace name="base" file="/base/base.mako"/>
2 2
3 3 <div class="panel panel-default">
4 <div class="panel-body">
5 <div style="height: 35px">
6 <%
7 selected_filter = 'all'
8 if c.closed:
9 selected_filter = 'all_closed'
10 %>
4 <div class="panel-heading">
5 <h3 class="panel-title">${_('Pull Requests You Participate In')}</h3>
6 </div>
11 7
8 <div class="panel-body panel-body-min-height">
9 <div class="title">
12 10 <ul class="button-links">
13 <li class="btn ${h.is_active('all', selected_filter)}"><a href="${h.route_path('my_account_pullrequests')}">${_('All')}</a></li>
14 <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>
11 <li><a class="btn ${h.is_active('all', c.selected_filter)}"
12 href="${h.route_path('my_account_pullrequests', _query={})}">
13 ${_('Open')}
14 </a>
15 </li>
16 <li><a class="btn ${h.is_active('all_closed', c.selected_filter)}"
17 href="${h.route_path('my_account_pullrequests', _query={'closed':1})}">
18 ${_('All + Closed')}
19 </a>
20 </li>
21 <li><a class="btn ${h.is_active('awaiting_my_review', c.selected_filter)}"
22 href="${h.route_path('my_account_pullrequests', _query={'awaiting_my_review':1})}">
23
24 ${_('Awaiting my review')}
25 </a>
26 </li>
15 27 </ul>
16 28
17 29 <div class="grid-quick-filter">
@@ -20,19 +32,13 b''
20 32 <i class="icon-search"></i>
21 33 </li>
22 34 <li class="grid-filter-box-input">
23 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
35 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter"
36 placeholder="${_('quick filter...')}" value=""/>
24 37 </li>
25 38 </ul>
26 39 </div>
27 40 </div>
28 </div>
29 </div>
30 41
31 <div class="panel panel-default">
32 <div class="panel-heading">
33 <h3 class="panel-title">${_('Pull Requests You Participate In')}</h3>
34 </div>
35 <div class="panel-body panel-body-min-height">
36 42 <table id="pull_request_list_table" class="rctable table-bordered"></table>
37 43 </div>
38 44 </div>
@@ -52,6 +58,7 b''
52 58 "url": "${h.route_path('my_account_pullrequests_data')}",
53 59 "data": function (d) {
54 60 d.closed = "${c.closed}";
61 d.awaiting_my_review = "${c.awaiting_my_review}";
55 62 },
56 63 "dataSrc": function (json) {
57 64 return json.data;
@@ -60,13 +67,19 b''
60 67
61 68 dom: 'rtp',
62 69 pageLength: ${c.visual.dashboard_items},
63 order: [[1, "desc"]],
70 order: [[2, "desc"]],
64 71 columns: [
65 72 {
66 73 data: {
67 74 "_": "status",
68 75 "sort": "status"
69 }, title: "", className: "td-status", orderable: false
76 }, title: "PR", className: "td-status", orderable: false
77 },
78 {
79 data: {
80 "_": "my_status",
81 "sort": "status"
82 }, title: "You", className: "td-status", orderable: false
70 83 },
71 84 {
72 85 data: {
@@ -23,13 +23,14 b''
23 23
24 24 <div class="box">
25 25 <div class="title">
26
26 27 <ul class="button-links">
27 <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>
28 <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>
29 <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>
30 <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>
31 <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>
32 <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>
28 <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>
29 <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>
30 <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>
31 <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>
32 <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>
33 <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>
33 34 </ul>
34 35
35 36 <ul class="links">
@@ -88,20 +89,50 b''
88 89 },
89 90 dom: 'rtp',
90 91 pageLength: ${c.visual.dashboard_items},
91 order: [[ 1, "desc" ]],
92 order: [[ 2, "desc" ]],
92 93 columns: [
93 { data: {"_": "status",
94 "sort": "status"}, title: "", className: "td-status", orderable: false},
95 { data: {"_": "name",
96 "sort": "name_raw"}, title: "${_('Id')}", className: "td-componentname", "type": "num" },
97 { data: {"_": "title",
98 "sort": "title"}, title: "${_('Title')}", className: "td-description" },
99 { data: {"_": "author",
100 "sort": "author_raw"}, title: "${_('Author')}", className: "td-user", orderable: false },
101 { data: {"_": "comments",
102 "sort": "comments_raw"}, title: "", className: "td-comments", orderable: false},
103 { data: {"_": "updated_on",
104 "sort": "updated_on_raw"}, title: "${_('Last Update')}", className: "td-time" }
94 {
95 data: {
96 "_": "status",
97 "sort": "status"
98 }, title: "PR", className: "td-status", orderable: false
99 },
100 {
101 data: {
102 "_": "my_status",
103 "sort": "status"
104 }, title: "You", className: "td-status", orderable: false
105 },
106 {
107 data: {
108 "_": "name",
109 "sort": "name_raw"
110 }, title: "${_('Id')}", className: "td-componentname", "type": "num"
111 },
112 {
113 data: {
114 "_": "title",
115 "sort": "title"
116 }, title: "${_('Title')}", className: "td-description"
117 },
118 {
119 data: {
120 "_": "author",
121 "sort": "author_raw"
122 }, title: "${_('Author')}", className: "td-user", orderable: false
123 },
124 {
125 data: {
126 "_": "comments",
127 "sort": "comments_raw"
128 }, title: "", className: "td-comments", orderable: false
129 },
130 {
131 data: {
132 "_": "updated_on",
133 "sort": "updated_on_raw"
134 }, title: "${_('Last Update')}", className: "td-time"
135 }
105 136 ],
106 137 language: {
107 138 paginate: DEFAULT_GRID_PAGINATION,
General Comments 1
Under Review
author

Auto status change to "Under Review"

You need to be logged in to leave comments. Login now