From e1c0496a3064ad846ff0058c2d0f385ce1e6ab4a Mon Sep 17 00:00:00 2001 From: rogerluo410 Date: Wed, 3 Mar 2021 21:19:44 +0800 Subject: [PATCH 1/7] Feat: Add dashboard milestone search and repo milestone search by name. --- models/issue_milestone.go | 68 +++++++++++++++++++++- routers/repo/milestone.go | 10 +++- routers/user/home.go | 18 +++--- templates/repo/issue/milestones.tmpl | 66 +++++++++++++-------- templates/user/dashboard/milestones.tmpl | 73 ++++++++++++++---------- 5 files changed, 170 insertions(+), 65 deletions(-) diff --git a/models/issue_milestone.go b/models/issue_milestone.go index 5c34834e2a502..b2ff53930d6f4 100644 --- a/models/issue_milestone.go +++ b/models/issue_milestone.go @@ -424,9 +424,12 @@ func GetMilestones(opts GetMilestonesOption) (MilestoneList, error) { } // SearchMilestones search milestones -func SearchMilestones(repoCond builder.Cond, page int, isClosed bool, sortType string) (MilestoneList, error) { +func SearchMilestones(repoCond builder.Cond, page int, isClosed bool, sortType string, keyword string) (MilestoneList, error) { miles := make([]*Milestone, 0, setting.UI.IssuePagingNum) sess := x.Where("is_closed = ?", isClosed) + if len(keyword) > 0 { + sess = sess.And("name = ?", keyword) + } if repoCond.IsValid() { sess.In("repo_id", builder.Select("id").From("repository").Where(repoCond)) } @@ -458,6 +461,7 @@ func GetMilestonesByRepoIDs(repoIDs []int64, page int, isClosed bool, sortType s page, isClosed, sortType, + "", ) } @@ -504,6 +508,38 @@ func GetMilestonesStatsByRepoCond(repoCond builder.Cond) (*MilestonesStats, erro return stats, nil } +// GetMilestonesStatsByRepoCondAndKw returns milestone statistic information for dashboard by given repo conditions and keyword. +func GetMilestonesStatsByRepoCondAndKw(repoCond builder.Cond, keyword string) (*MilestonesStats, error) { + var err error + stats := &MilestonesStats{} + + sess := x.Where("is_closed = ?", false) + if len(keyword) > 0 { + sess = sess.Where("name = ?", keyword) + } + if repoCond.IsValid() { + sess.And(builder.In("repo_id", builder.Select("id").From("repository").Where(repoCond))) + } + stats.OpenCount, err = sess.Count(new(Milestone)) + if err != nil { + return nil, err + } + + sess = x.Where("is_closed = ?", true) + if len(keyword) > 0 { + sess = sess.Where("name = ?", keyword) + } + if repoCond.IsValid() { + sess.And(builder.In("repo_id", builder.Select("id").From("repository").Where(repoCond))) + } + stats.ClosedCount, err = sess.Count(new(Milestone)) + if err != nil { + return nil, err + } + + return stats, nil +} + func countRepoMilestones(e Engine, repoID int64) (int64, error) { return e. Where("repo_id=?", repoID). @@ -546,6 +582,34 @@ func CountMilestonesByRepoCond(repoCond builder.Cond, isClosed bool) (map[int64] return countMap, nil } +// CountMilestonesByRepoCondAndKw map from repo conditions and the keyword of milestones' name to number of milestones matching the options` +func CountMilestonesByRepoCondAndKw(repoCond builder.Cond, keyword string, isClosed bool) (map[int64]int64, error) { + sess := x.Where("is_closed = ?", isClosed) + if len(keyword) > 0 { + sess = sess.Where("name = ?", keyword) + } + if repoCond.IsValid() { + sess.In("repo_id", builder.Select("id").From("repository").Where(repoCond)) + } + + countsSlice := make([]*struct { + RepoID int64 + Count int64 + }, 0, 10) + if err := sess.GroupBy("repo_id"). + Select("repo_id AS repo_id, COUNT(*) AS count"). + Table("milestone"). + Find(&countsSlice); err != nil { + return nil, err + } + + countMap := make(map[int64]int64, len(countsSlice)) + for _, c := range countsSlice { + countMap[c.RepoID] = c.Count + } + return countMap, nil +} + func updateRepoMilestoneNum(e Engine, repoID int64) error { _, err := e.Exec("UPDATE `repository` SET num_milestones=(SELECT count(*) FROM milestone WHERE repo_id=?),num_closed_milestones=(SELECT count(*) FROM milestone WHERE repo_id=? AND is_closed=?) WHERE id=?", repoID, @@ -657,4 +721,4 @@ func (milestones MilestoneList) LoadTotalTrackedTimes() error { // LoadTotalTrackedTime loads the tracked time for the milestone func (m *Milestone) LoadTotalTrackedTime() error { return m.loadTotalTrackedTime(x) -} +} \ No newline at end of file diff --git a/routers/repo/milestone.go b/routers/repo/milestone.go index a9beed75d7c64..9f803411925af 100644 --- a/routers/repo/milestone.go +++ b/routers/repo/milestone.go @@ -6,7 +6,8 @@ package repo import ( "time" - + "strings" + "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" @@ -43,6 +44,9 @@ func Milestones(ctx *context.Context) { ctx.Data["ClosedCount"] = stats.ClosedCount sortType := ctx.Query("sort") + + keyword := strings.Trim(ctx.Query("q"), " ") + page := ctx.QueryInt("page") if page <= 1 { page = 1 @@ -59,13 +63,14 @@ func Milestones(ctx *context.Context) { } miles, err := models.GetMilestones(models.GetMilestonesOption{ - ListOptions: models.ListOptions{ + ListOptions: models.ListOptions{ Page: page, PageSize: setting.UI.IssuePagingNum, }, RepoID: ctx.Repo.Repository.ID, State: state, SortType: sortType, + Name: keyword, }) if err != nil { ctx.ServerError("GetMilestones", err) @@ -89,6 +94,7 @@ func Milestones(ctx *context.Context) { } ctx.Data["SortType"] = sortType + ctx.Data["Keyword"] = keyword ctx.Data["IsShowClosed"] = isShowClosed pager := context.NewPagination(total, setting.UI.IssuePagingNum, page, 5) diff --git a/routers/user/home.go b/routers/user/home.go index 9957ec6b02b4d..2fe81ffe1402e 100644 --- a/routers/user/home.go +++ b/routers/user/home.go @@ -7,6 +7,7 @@ package user import ( "bytes" + "encoding/json" "fmt" "regexp" "sort" @@ -24,7 +25,6 @@ import ( issue_service "code.gitea.io/gitea/services/issue" pull_service "code.gitea.io/gitea/services/pull" - jsoniter "github.com/json-iterator/go" "github.com/keybase/go-crypto/openpgp" "github.com/keybase/go-crypto/openpgp/armor" "xorm.io/builder" @@ -203,6 +203,7 @@ func Milestones(ctx *context.Context) { isShowClosed = ctx.Query("state") == "closed" sortType = ctx.Query("sort") page = ctx.QueryInt("page") + keyword = strings.Trim(ctx.Query("q"), " ") ) if page <= 1 { @@ -235,15 +236,15 @@ func Milestones(ctx *context.Context) { } } - counts, err := models.CountMilestonesByRepoCond(userRepoCond, isShowClosed) + counts, err := models.CountMilestonesByRepoCondAndKw(userRepoCond, keyword, isShowClosed) if err != nil { ctx.ServerError("CountMilestonesByRepoIDs", err) return } - milestones, err := models.SearchMilestones(repoCond, page, isShowClosed, sortType) + milestones, err := models.SearchMilestones(repoCond, page, isShowClosed, sortType, keyword) if err != nil { - ctx.ServerError("GetMilestonesByRepoIDs", err) + ctx.ServerError("SearchMilestones", err) return } @@ -278,7 +279,7 @@ func Milestones(ctx *context.Context) { i++ } - milestoneStats, err := models.GetMilestonesStatsByRepoCond(repoCond) + milestoneStats, err := models.GetMilestonesStatsByRepoCondAndKw(repoCond, keyword) if err != nil { ctx.ServerError("GetMilestoneStats", err) return @@ -288,7 +289,7 @@ func Milestones(ctx *context.Context) { if len(repoIDs) == 0 { totalMilestoneStats = milestoneStats } else { - totalMilestoneStats, err = models.GetMilestonesStatsByRepoCond(userRepoCond) + totalMilestoneStats, err = models.GetMilestonesStatsByRepoCondAndKw(userRepoCond, keyword) if err != nil { ctx.ServerError("GetMilestoneStats", err) return @@ -311,12 +312,14 @@ func Milestones(ctx *context.Context) { ctx.Data["Counts"] = counts ctx.Data["MilestoneStats"] = milestoneStats ctx.Data["SortType"] = sortType + ctx.Data["Keyword"] = keyword if milestoneStats.Total() != totalMilestoneStats.Total() { ctx.Data["RepoIDs"] = repoIDs } ctx.Data["IsShowClosed"] = isShowClosed pager := context.NewPagination(pagerCount, setting.UI.IssuePagingNum, page, 5) + pager.AddParam(ctx, "q", "Keyword") pager.AddParam(ctx, "repos", "RepoIDs") pager.AddParam(ctx, "sort", "SortType") pager.AddParam(ctx, "state", "State") @@ -691,7 +694,6 @@ func buildIssueOverview(ctx *context.Context, unitType models.UnitType) { } // Convert []int64 to string - json := jsoniter.ConfigCompatibleWithStandardLibrary reposParam, _ := json.Marshal(repoIDs) ctx.Data["ReposParam"] = string(reposParam) @@ -902,4 +904,4 @@ func Email2User(ctx *context.Context) { return } ctx.Redirect(setting.AppSubURL + "/user/" + u.Name) -} +} \ No newline at end of file diff --git a/templates/repo/issue/milestones.tmpl b/templates/repo/issue/milestones.tmpl index 4d16b0d0144c1..86ee7e7b4eeac 100644 --- a/templates/repo/issue/milestones.tmpl +++ b/templates/repo/issue/milestones.tmpl @@ -12,34 +12,52 @@
{{template "base/alert" .}} - - -{{template "base/footer" .}} \ No newline at end of file +{{template "base/footer" .}} From 4b15c3570f36d98879e9130e0a46d2a40810d7cb Mon Sep 17 00:00:00 2001 From: rogerluo410 Date: Wed, 3 Mar 2021 21:34:30 +0800 Subject: [PATCH 3/7] Fix: fuzzy search name on dashboard milestone page. --- models/issue_milestone.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/models/issue_milestone.go b/models/issue_milestone.go index b2ff53930d6f4..61f2d5ca714f8 100644 --- a/models/issue_milestone.go +++ b/models/issue_milestone.go @@ -428,7 +428,7 @@ func SearchMilestones(repoCond builder.Cond, page int, isClosed bool, sortType s miles := make([]*Milestone, 0, setting.UI.IssuePagingNum) sess := x.Where("is_closed = ?", isClosed) if len(keyword) > 0 { - sess = sess.And("name = ?", keyword) + sess = sess.And(builder.Like{"name", keyword}) } if repoCond.IsValid() { sess.In("repo_id", builder.Select("id").From("repository").Where(repoCond)) @@ -515,7 +515,7 @@ func GetMilestonesStatsByRepoCondAndKw(repoCond builder.Cond, keyword string) (* sess := x.Where("is_closed = ?", false) if len(keyword) > 0 { - sess = sess.Where("name = ?", keyword) + sess = sess.Where(builder.Like{"name", keyword}) } if repoCond.IsValid() { sess.And(builder.In("repo_id", builder.Select("id").From("repository").Where(repoCond))) @@ -527,7 +527,7 @@ func GetMilestonesStatsByRepoCondAndKw(repoCond builder.Cond, keyword string) (* sess = x.Where("is_closed = ?", true) if len(keyword) > 0 { - sess = sess.Where("name = ?", keyword) + sess = sess.Where(builder.Like{"name", keyword}) } if repoCond.IsValid() { sess.And(builder.In("repo_id", builder.Select("id").From("repository").Where(repoCond))) @@ -721,4 +721,4 @@ func (milestones MilestoneList) LoadTotalTrackedTimes() error { // LoadTotalTrackedTime loads the tracked time for the milestone func (m *Milestone) LoadTotalTrackedTime() error { return m.loadTotalTrackedTime(x) -} \ No newline at end of file +} From afd759651cb6dcc8c0c16940568cf76f6ee98e31 Mon Sep 17 00:00:00 2001 From: rogerluo410 Date: Wed, 3 Mar 2021 21:41:38 +0800 Subject: [PATCH 4/7] Fix: fuzzy query on dashboard milestone page. --- models/issue_milestone.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/models/issue_milestone.go b/models/issue_milestone.go index 61f2d5ca714f8..cd2257a38de40 100644 --- a/models/issue_milestone.go +++ b/models/issue_milestone.go @@ -515,7 +515,7 @@ func GetMilestonesStatsByRepoCondAndKw(repoCond builder.Cond, keyword string) (* sess := x.Where("is_closed = ?", false) if len(keyword) > 0 { - sess = sess.Where(builder.Like{"name", keyword}) + sess = sess.And(builder.Like{"name", keyword}) } if repoCond.IsValid() { sess.And(builder.In("repo_id", builder.Select("id").From("repository").Where(repoCond))) @@ -527,7 +527,7 @@ func GetMilestonesStatsByRepoCondAndKw(repoCond builder.Cond, keyword string) (* sess = x.Where("is_closed = ?", true) if len(keyword) > 0 { - sess = sess.Where(builder.Like{"name", keyword}) + sess = sess.And(builder.Like{"name", keyword}) } if repoCond.IsValid() { sess.And(builder.In("repo_id", builder.Select("id").From("repository").Where(repoCond))) @@ -586,7 +586,7 @@ func CountMilestonesByRepoCond(repoCond builder.Cond, isClosed bool) (map[int64] func CountMilestonesByRepoCondAndKw(repoCond builder.Cond, keyword string, isClosed bool) (map[int64]int64, error) { sess := x.Where("is_closed = ?", isClosed) if len(keyword) > 0 { - sess = sess.Where("name = ?", keyword) + sess = sess.And(builder.Like{"name", keyword}) } if repoCond.IsValid() { sess.In("repo_id", builder.Select("id").From("repository").Where(repoCond)) From 174fb071a65ba21e40b7074bc49790942cf7d9aa Mon Sep 17 00:00:00 2001 From: rogerluo410 Date: Thu, 4 Mar 2021 13:53:56 +0800 Subject: [PATCH 5/7] Fix: case-sensitive and run go fmt. --- models/issue_milestone.go | 8 ++++---- routers/repo/milestone.go | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/models/issue_milestone.go b/models/issue_milestone.go index cd2257a38de40..b538efe22fea5 100644 --- a/models/issue_milestone.go +++ b/models/issue_milestone.go @@ -428,7 +428,7 @@ func SearchMilestones(repoCond builder.Cond, page int, isClosed bool, sortType s miles := make([]*Milestone, 0, setting.UI.IssuePagingNum) sess := x.Where("is_closed = ?", isClosed) if len(keyword) > 0 { - sess = sess.And(builder.Like{"name", keyword}) + sess = sess.And(builder.Like{"UPPER(name)", strings.ToUpper(keyword)}) } if repoCond.IsValid() { sess.In("repo_id", builder.Select("id").From("repository").Where(repoCond)) @@ -515,7 +515,7 @@ func GetMilestonesStatsByRepoCondAndKw(repoCond builder.Cond, keyword string) (* sess := x.Where("is_closed = ?", false) if len(keyword) > 0 { - sess = sess.And(builder.Like{"name", keyword}) + sess = sess.And(builder.Like{"UPPER(name)", strings.ToUpper(keyword)}) } if repoCond.IsValid() { sess.And(builder.In("repo_id", builder.Select("id").From("repository").Where(repoCond))) @@ -527,7 +527,7 @@ func GetMilestonesStatsByRepoCondAndKw(repoCond builder.Cond, keyword string) (* sess = x.Where("is_closed = ?", true) if len(keyword) > 0 { - sess = sess.And(builder.Like{"name", keyword}) + sess = sess.And(builder.Like{"UPPER(name)", strings.ToUpper(keyword)}) } if repoCond.IsValid() { sess.And(builder.In("repo_id", builder.Select("id").From("repository").Where(repoCond))) @@ -586,7 +586,7 @@ func CountMilestonesByRepoCond(repoCond builder.Cond, isClosed bool) (map[int64] func CountMilestonesByRepoCondAndKw(repoCond builder.Cond, keyword string, isClosed bool) (map[int64]int64, error) { sess := x.Where("is_closed = ?", isClosed) if len(keyword) > 0 { - sess = sess.And(builder.Like{"name", keyword}) + sess = sess.And(builder.Like{"UPPER(name)", strings.ToUpper(keyword)}) } if repoCond.IsValid() { sess.In("repo_id", builder.Select("id").From("repository").Where(repoCond)) diff --git a/routers/repo/milestone.go b/routers/repo/milestone.go index d359483214ab1..260eccb2e4542 100644 --- a/routers/repo/milestone.go +++ b/routers/repo/milestone.go @@ -5,8 +5,8 @@ package repo import ( + "strings" "time" - "strings" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/base" @@ -63,7 +63,7 @@ func Milestones(ctx *context.Context) { } miles, err := models.GetMilestones(models.GetMilestonesOption{ - ListOptions: models.ListOptions{ + ListOptions: models.ListOptions{ Page: page, PageSize: setting.UI.IssuePagingNum, }, From 90a321680285d0706557981e2bb00be6ef9d158c Mon Sep 17 00:00:00 2001 From: rogerluo410 Date: Fri, 19 Mar 2021 14:25:45 +0800 Subject: [PATCH 6/7] Build redo. --- models/issue_milestone.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/models/issue_milestone.go b/models/issue_milestone.go index 66ef3722158e3..5aa83ea691d54 100644 --- a/models/issue_milestone.go +++ b/models/issue_milestone.go @@ -510,7 +510,7 @@ func GetMilestonesStatsByRepoCond(repoCond builder.Cond) (*MilestonesStats, erro return stats, nil } -// GetMilestonesStatsByRepoCondAndKw returns milestone statistic information for dashboard by given repo conditions and keyword. +// GetMilestonesStatsByRepoCondAndKw returns milestone statistic information for dashboard by given repo conditions and name keyword. func GetMilestonesStatsByRepoCondAndKw(repoCond builder.Cond, keyword string) (*MilestonesStats, error) { var err error stats := &MilestonesStats{} From 4feafea2f82fce96f317d533de665307e1a93b95 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Thu, 8 Apr 2021 13:03:15 +0200 Subject: [PATCH 7/7] go fmt --- routers/repo/milestone.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routers/repo/milestone.go b/routers/repo/milestone.go index 828dc6b10ee56..5a9d2351bcfb5 100644 --- a/routers/repo/milestone.go +++ b/routers/repo/milestone.go @@ -5,8 +5,8 @@ package repo import ( - "net/http" - "strings" + "net/http" + "strings" "time" "code.gitea.io/gitea/models"