Skip to content

Commit

Permalink
Add "Reviewed by you" filter for pull requests
Browse files Browse the repository at this point in the history
This includes pull requests that you approved, requested changes or commented
on. Currently such pull requests are not visible in any of the filters on
/pulls, while they may need further action like merging, or prodding the
author or reviewers.

Especially when working with a large team on a repository it's helpful to get
a full overview of pull requests that may need your attention, without having
to sift through the complete list.
  • Loading branch information
brechtvl committed Feb 18, 2023
1 parent 007d181 commit a7e905b
Show file tree
Hide file tree
Showing 11 changed files with 115 additions and 7 deletions.
58 changes: 58 additions & 0 deletions models/issues/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -1119,6 +1119,7 @@ type IssuesOptions struct { //nolint
PosterID int64
MentionedID int64
ReviewRequestedID int64
ReviewedID int64
SubscriberID int64
MilestoneIDs []int64
ProjectID int64
Expand Down Expand Up @@ -1233,6 +1234,10 @@ func (opts *IssuesOptions) setupSessionNoLimit(sess *xorm.Session) {
applyReviewRequestedCondition(sess, opts.ReviewRequestedID)
}

if opts.ReviewedID > 0 {
applyReviewedCondition(sess, opts.ReviewedID)
}

if opts.SubscriberID > 0 {
applySubscribedCondition(sess, opts.SubscriberID)
}
Expand Down Expand Up @@ -1403,6 +1408,33 @@ func applyReviewRequestedCondition(sess *xorm.Session, reviewRequestedID int64)
reviewRequestedID, ReviewTypeApprove, ReviewTypeReject, ReviewTypeRequest, reviewRequestedID)
}

func applyReviewedCondition(sess *xorm.Session, reviewedID int64) *xorm.Session {
// Query for pull requests where you are a reviewer or commenter, excluding
// any pull requests already returned by the the review requested filter.
notPoster := builder.Neq{"issue.poster_id": reviewedID}
reviewed := builder.In("issue.id", builder.
Select("issue_id").
From("review").
Where(builder.And(
builder.Neq{"type": ReviewTypeRequest},
builder.Or(
builder.Eq{"reviewer_id": reviewedID},
builder.In("reviewer_team_id", builder.
Select("team_id").
From("team_user").
Where(builder.Eq{"uid": reviewedID}),
),
),
)),
)
comment := builder.In("issue.id", builder.
Select("issue_id").
From("comment").
Where(builder.Eq{"poster_id": reviewedID}),
)
return sess.And(notPoster, reviewed, comment)
}

func applySubscribedCondition(sess *xorm.Session, subscriberID int64) *xorm.Session {
return sess.And(
builder.
Expand Down Expand Up @@ -1557,6 +1589,7 @@ type IssueStats struct {
CreateCount int64
MentionCount int64
ReviewRequestedCount int64
ReviewedCount int64
}

// Filter modes.
Expand All @@ -1566,6 +1599,7 @@ const (
FilterModeCreate
FilterModeMention
FilterModeReviewRequested
FilterModeReviewed
FilterModeYourRepositories
)

Expand All @@ -1579,6 +1613,7 @@ type IssueStatsOptions struct {
MentionedID int64
PosterID int64
ReviewRequestedID int64
ReviewedID int64
IsPull util.OptionalBool
IssueIDs []int64
}
Expand Down Expand Up @@ -1617,6 +1652,7 @@ func GetIssueStats(opts *IssueStatsOptions) (*IssueStats, error) {
accum.CreateCount += stats.CreateCount
accum.OpenCount += stats.MentionCount
accum.ReviewRequestedCount += stats.ReviewRequestedCount
accum.ReviewedCount += stats.ReviewedCount
i = chunk
}
return accum, nil
Expand Down Expand Up @@ -1674,6 +1710,10 @@ func getIssueStatsChunk(opts *IssueStatsOptions, issueIDs []int64) (*IssueStats,
applyReviewRequestedCondition(sess, opts.ReviewRequestedID)
}

if opts.ReviewedID > 0 {
applyReviewedCondition(sess, opts.ReviewedID)
}

switch opts.IsPull {
case util.OptionalBoolTrue:
sess.And("issue.is_pull=?", true)
Expand Down Expand Up @@ -1814,6 +1854,19 @@ func GetUserIssueStats(opts UserIssueStatsOptions) (*IssueStats, error) {
if err != nil {
return nil, err
}
case FilterModeReviewed:
stats.OpenCount, err = applyReviewedCondition(sess(cond), opts.UserID).
And("issue.is_closed = ?", false).
Count(new(Issue))
if err != nil {
return nil, err
}
stats.ClosedCount, err = applyReviewedCondition(sess(cond), opts.UserID).
And("issue.is_closed = ?", true).
Count(new(Issue))
if err != nil {
return nil, err
}
}

cond = cond.And(builder.Eq{"issue.is_closed": opts.IsClosed})
Expand Down Expand Up @@ -1842,6 +1895,11 @@ func GetUserIssueStats(opts UserIssueStatsOptions) (*IssueStats, error) {
return nil, err
}

stats.ReviewedCount, err = applyReviewedCondition(sess(cond), opts.UserID).Count(new(Issue))
if err != nil {
return nil, err
}

return stats, nil
}

Expand Down
2 changes: 2 additions & 0 deletions models/migrations/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,8 @@ var migrations = []Migration{
NewMigration("Add card_type column to project table", v1_19.AddCardTypeToProjectTable),
// v242 -> v243
NewMigration("Alter gpg_key_import content TEXT field to MEDIUMTEXT", v1_19.AlterPublicGPGKeyImportContentFieldToMediumText),
// v243 -> v244
NewMigration("Add exclusive label", v1_19.AddExclusiveLabel),
}

// GetCurrentDBVersion returns the current db version
Expand Down
16 changes: 16 additions & 0 deletions models/migrations/v1_19/v244.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package v1_19 //nolint

import (
"xorm.io/xorm"
)

func AddExclusiveLabel(x *xorm.Engine) error {
type Label struct {
Exclusive bool
}

return x.Sync(new(Label))
}
1 change: 1 addition & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -1322,6 +1322,7 @@ issues.filter_type.assigned_to_you = Assigned to you
issues.filter_type.created_by_you = Created by you
issues.filter_type.mentioning_you = Mentioning you
issues.filter_type.review_requested = Review requested
issues.filter_type.reviewed_by_you = Reviewed by you
issues.filter_sort = Sort
issues.filter_sort.latest = Newest
issues.filter_sort.oldest = Oldest
Expand Down
7 changes: 7 additions & 0 deletions routers/api/v1/repo/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ func SearchIssues(ctx *context.APIContext) {
// in: query
// description: filter pulls requesting your review, default is false
// type: boolean
// - name: reviewed
// in: query
// description: filter pulls reviewed by you, default is false
// type: boolean
// - name: owner
// in: query
// description: filter by owner
Expand Down Expand Up @@ -266,6 +270,9 @@ func SearchIssues(ctx *context.APIContext) {
if ctx.FormBool("review_requested") {
issuesOpt.ReviewRequestedID = ctxUserID
}
if ctx.FormBool("reviewed") {
issuesOpt.ReviewedID = ctxUserID
}

if issues, err = issues_model.Issues(ctx, issuesOpt); err != nil {
ctx.Error(http.StatusInternalServerError, "Issues", err)
Expand Down
10 changes: 9 additions & 1 deletion routers/web/repo/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
var err error
viewType := ctx.FormString("type")
sortType := ctx.FormString("sort")
types := []string{"all", "your_repositories", "assigned", "created_by", "mentioned", "review_requested"}
types := []string{"all", "your_repositories", "assigned", "created_by", "mentioned", "review_requested", "reviewed_by"}
if !util.SliceContainsString(types, viewType, true) {
viewType = "all"
}
Expand All @@ -148,6 +148,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
posterID = ctx.FormInt64("poster")
mentionedID int64
reviewRequestedID int64
reviewedID int64
forceEmpty bool
)

Expand All @@ -161,6 +162,8 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
assigneeID = ctx.Doer.ID
case "review_requested":
reviewRequestedID = ctx.Doer.ID
case "reviewed_by":
reviewedID = ctx.Doer.ID
}
}

Expand Down Expand Up @@ -208,6 +211,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
MentionedID: mentionedID,
PosterID: posterID,
ReviewRequestedID: reviewRequestedID,
ReviewedID: reviewedID,
IsPull: isPullOption,
IssueIDs: issueIDs,
})
Expand Down Expand Up @@ -255,6 +259,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
PosterID: posterID,
MentionedID: mentionedID,
ReviewRequestedID: reviewRequestedID,
ReviewedID: reviewedID,
MilestoneIDs: mileIDs,
ProjectID: projectID,
IsClosed: util.OptionalBoolOf(isShowClosed),
Expand Down Expand Up @@ -2409,6 +2414,9 @@ func SearchIssues(ctx *context.Context) {
if ctx.FormBool("review_requested") {
issuesOpt.ReviewRequestedID = ctxUserID
}
if ctx.FormBool("reviewed") {
issuesOpt.ReviewedID = ctxUserID
}

if issues, err = issues_model.Issues(ctx, issuesOpt); err != nil {
ctx.Error(http.StatusInternalServerError, "Issues", err.Error())
Expand Down
4 changes: 4 additions & 0 deletions routers/web/user/home.go
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,8 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
filterMode = issues_model.FilterModeMention
case "review_requested":
filterMode = issues_model.FilterModeReviewRequested
case "reviewed_by":
filterMode = issues_model.FilterModeReviewed
case "your_repositories":
fallthrough
default:
Expand Down Expand Up @@ -434,6 +436,8 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
opts.MentionedID = ctx.Doer.ID
case issues_model.FilterModeReviewRequested:
opts.ReviewRequestedID = ctx.Doer.ID
case issues_model.FilterModeReviewed:
opts.ReviewedID = ctx.Doer.ID
}

// keyword holds the search term entered into the search field.
Expand Down
3 changes: 2 additions & 1 deletion templates/repo/issue/list.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -165,10 +165,11 @@
<a class="{{if eq .ViewType "all"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=all&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_type.all_issues"}}</a>
<a class="{{if eq .ViewType "assigned"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=assigned&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_type.assigned_to_you"}}</a>
<a class="{{if eq .ViewType "created_by"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=created_by&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_type.created_by_you"}}</a>
<a class="{{if eq .ViewType "mentioned"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=mentioned&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_type.mentioning_you"}}</a>
{{if .PageIsPullList}}
<a class="{{if eq .ViewType "review_requested"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=review_requested&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_type.review_requested"}}</a>
<a class="{{if eq .ViewType "reviewed_by"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=reviewed_by&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_type.reviewed_by_you"}}</a>
{{end}}
<a class="{{if eq .ViewType "mentioned"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=mentioned&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_type.mentioning_you"}}</a>
</div>
</div>
{{end}}
Expand Down
3 changes: 2 additions & 1 deletion templates/repo/issue/milestone_issues.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,9 @@
<a class="{{if eq .ViewType "all"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=all&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_type.all_issues"}}</a>
<a class="{{if eq .ViewType "assigned"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=assigned&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_type.assigned_to_you"}}</a>
<a class="{{if eq .ViewType "created_by"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=created_by&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_type.created_by_you"}}</a>
<a class="{{if eq .ViewType "mentioned"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=mentioned&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_type.mentioning_you"}}</a>
<a class="{{if eq .ViewType "review_requested"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=review_requested&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_type.review_requested"}}</a>
<a class="{{if eq .ViewType "reviewed_by"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=reviewed_by&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_type.reviewed_by_you"}}</a>
<a class="{{if eq .ViewType "mentioned"}}active {{end}}item" href="{{$.Link}}?q={{$.Keyword}}&type=mentioned&sort={{$.SortType}}&state={{$.State}}&labels={{.SelectLabels}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}">{{.locale.Tr "repo.issues.filter_type.mentioning_you"}}</a>
</div>
</div>
{{end}}
Expand Down
6 changes: 6 additions & 0 deletions templates/swagger/v1_json.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -2366,6 +2366,12 @@
"name": "review_requested",
"in": "query"
},
{
"type": "boolean",
"description": "filter pulls reviewed by you, default is false",
"name": "reviewed",
"in": "query"
},
{
"type": "string",
"description": "filter by owner",
Expand Down
12 changes: 8 additions & 4 deletions templates/user/dashboard/issues.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,20 @@
{{.locale.Tr "repo.issues.filter_type.created_by_you"}}
<strong class="ui right">{{CountFmt .IssueStats.CreateCount}}</strong>
</a>
<a class="{{if eq .ViewType "mentioned"}}ui basic primary button{{end}} item" href="{{.Link}}?type=mentioned&repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort={{$.SortType}}&state={{.State}}">
{{.locale.Tr "repo.issues.filter_type.mentioning_you"}}
<strong class="ui right">{{CountFmt .IssueStats.MentionCount}}</strong>
</a>
{{if .PageIsPulls}}
<a class="{{if eq .ViewType "review_requested"}}ui basic primary button{{end}} item" href="{{.Link}}?type=review_requested&repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort={{$.SortType}}&state={{.State}}">
{{.locale.Tr "repo.issues.filter_type.review_requested"}}
<strong class="ui right">{{CountFmt .IssueStats.ReviewRequestedCount}}</strong>
</a>
<a class="{{if eq .ViewType "reviewed_by"}}ui basic primary button{{end}} item" href="{{.Link}}?type=reviewed_by&repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort={{$.SortType}}&state={{.State}}">
{{.locale.Tr "repo.issues.filter_type.reviewed_by_you"}}
<strong class="ui right">{{CountFmt .IssueStats.ReviewedCount}}</strong>
</a>
{{end}}
<a class="{{if eq .ViewType "mentioned"}}ui basic primary button{{end}} item" href="{{.Link}}?type=mentioned&repos=[{{range $.RepoIDs}}{{.}}%2C{{end}}]&sort={{$.SortType}}&state={{.State}}">
{{.locale.Tr "repo.issues.filter_type.mentioning_you"}}
<strong class="ui right">{{CountFmt .IssueStats.MentionCount}}</strong>
</a>
<div class="ui divider"></div>
<a class="{{if not $.RepoIDs}}ui basic primary button{{end}} repo name item" href="{{$.Link}}?type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&q={{$.Keyword}}">
<span class="text truncate">All</span>
Expand Down

0 comments on commit a7e905b

Please sign in to comment.