diff --git a/models/dbsql/dataset_foreigntable_for_es.sql b/models/dbsql/dataset_foreigntable_for_es.sql index 815b89d02..02e5f0ddf 100644 --- a/models/dbsql/dataset_foreigntable_for_es.sql +++ b/models/dbsql/dataset_foreigntable_for_es.sql @@ -1,4 +1,17 @@ +DELETE FROM public.dataset_es; DROP FOREIGN TABLE public.dataset_es; +DROP TRIGGER IF EXISTS es_insert_dataset on public.dataset; +DROP FUNCTION public.insert_dataset_data(); +DROP TRIGGER IF EXISTS es_udpate_dataset_file_name on public.attachment; +DROP FUNCTION public.udpate_dataset_file_name; + +DROP TRIGGER IF EXISTS es_update_dataset on public.dataset; +DROP FUNCTION public.update_dataset; + +DROP TRIGGER IF EXISTS es_delete_dataset on public.dataset; +DROP FUNCTION public.delete_dataset; + + CREATE FOREIGN TABLE public.dataset_es ( id bigint NOT NULL, diff --git a/models/dbsql/issue_foreigntable_for_es.sql b/models/dbsql/issue_foreigntable_for_es.sql index bb5c1634e..30fa01550 100644 --- a/models/dbsql/issue_foreigntable_for_es.sql +++ b/models/dbsql/issue_foreigntable_for_es.sql @@ -1,4 +1,15 @@ +delete from public.issue_es; DROP FOREIGN TABLE public.issue_es; +DROP TRIGGER IF EXISTS es_insert_issue on public.issue; +DROP FUNCTION public.insert_issue_data; +DROP TRIGGER IF EXISTS es_udpate_issue_comment on public.comment; +DROP FUNCTION udpate_issue_comment; +DROP TRIGGER IF EXISTS es_update_issue on public.issue; +DROP FUNCTION public.update_issue; +DROP TRIGGER IF EXISTS es_delete_issue on public.issue; +DROP FUNCTION public.delete_issue; + + CREATE FOREIGN TABLE public.issue_es ( id bigint NOT NULL, diff --git a/models/dbsql/repo_foreigntable_for_es.sql b/models/dbsql/repo_foreigntable_for_es.sql index f51155ccf..7e06fd99e 100644 --- a/models/dbsql/repo_foreigntable_for_es.sql +++ b/models/dbsql/repo_foreigntable_for_es.sql @@ -1,5 +1,18 @@ -- 要处理项目从私有变为公有,并且从公有变成私有的情况 +DELETE FROM public.repository_es; DROP FOREIGN table if exists public.repository_es; +DROP TRIGGER IF EXISTS es_insert_repository on public.repository; +DROP FUNCTION public.insert_repository_data; +DROP TRIGGER IF EXISTS es_update_repository on public.repository; +DROP FUNCTION public.update_repository; + +DROP TRIGGER IF EXISTS es_delete_repository on public.repository; +DROP FUNCTION public.delete_repository; + +DROP TRIGGER IF EXISTS es_udpate_repository_lang on public.language_stat; +DROP FUNCTION public.udpate_repository_lang; + + CREATE FOREIGN TABLE public.repository_es ( id bigint NOT NULL, owner_id bigint, diff --git a/models/dbsql/user_foreigntable_for_es.sql b/models/dbsql/user_foreigntable_for_es.sql index c3d21b92a..5d77757f0 100644 --- a/models/dbsql/user_foreigntable_for_es.sql +++ b/models/dbsql/user_foreigntable_for_es.sql @@ -1,4 +1,13 @@ +DELETE FROM public.user_es; DROP FOREIGN table if exists public.user_es; +DROP TRIGGER IF EXISTS es_insert_user on public.user; +DROP FUNCTION public.insert_user_data; +DROP TRIGGER IF EXISTS es_update_user on public.user; +DROP FUNCTION public.update_user; + +DROP TRIGGER IF EXISTS es_delete_user on public.user; +DROP FUNCTION public.delete_user; + CREATE FOREIGN TABLE public.user_es ( id bigint NOT NULL , diff --git a/models/models.go b/models/models.go index e8839572d..2ec61941d 100755 --- a/models/models.go +++ b/models/models.go @@ -138,6 +138,7 @@ func init() { new(OfficialTag), new(OfficialTagRepos), new(WechatBindLog), + new(OrgStatistic), new(SearchRecord), ) diff --git a/models/org.go b/models/org.go index 85fb157ae..8b3e60ef8 100755 --- a/models/org.go +++ b/models/org.go @@ -8,6 +8,7 @@ package models import ( "fmt" "os" + "strconv" "strings" "code.gitea.io/gitea/modules/log" @@ -19,6 +20,17 @@ import ( "xorm.io/xorm" ) +type OrgStatistic struct { + ID int64 `xorm:"pk autoincr"` + OrgID int64 `xorm:"UNIQUE"` + NumScore int `xorm:"INDEX NOT NULL DEFAULT 0"` +} + +type OrgScore struct { + *User + Score string +} + // IsOwnedBy returns true if given user is in the owner team. func (org *User) IsOwnedBy(uid int64) (bool, error) { return IsOrganizationOwner(org.ID, uid) @@ -135,6 +147,93 @@ func (org *User) RemoveOrgRepo(repoID int64) error { return org.removeOrgRepo(x, repoID) } +func UpdateOrgStatistics() { + ids, err := GetOrganizationsId() + if err != nil { + return + } + for _, id := range ids { + org := User{ID: id} + orgStat := &OrgStatistic{OrgID: id} + numScore, err := org.getOrgStatistics() + if err == nil { + has, _ := x.Get(orgStat) + + orgStat.NumScore = numScore + if has { + x.ID(orgStat.ID).Cols("num_score").Update(&orgStat) + } else { + x.Insert(orgStat) + } + + } + } + +} + +func (org *User) getOrgStatistics() (int, error) { + count, err := getRepositoryCount(x, org) + if err != nil { + return 0, err + } + + err = org.GetRepositories(ListOptions{int(count), 1}) + + if err != nil { + return 0, err + } + var numScore = 0 + for _, repo := range org.Repos { + + numScore += int(getOpenIByRepoId(repo.ID)) + } + + return numScore, nil + +} + +func FindTopNStarsOrgs(n int) ([]*OrgScore, error) { + sql := "select a.id,sum(b.num_stars) score from \"user\" a ,repository b where a.id=b.owner_id and a.type=1 group by a.id order by score desc limit " + strconv.Itoa(n) + + return findTopNOrgs(sql) +} +func FindTopNMembersOrgs(n int) ([]*OrgScore, error) { + sql := "select id, count(user_id) score from" + + " (select org_id as id, uid as user_id from org_user " + + "union select a.id,b.user_id from \"user\" a,collaboration b,repository c " + + "where a.type=1 and a.id=c.owner_id and b.repo_id=c.id) d " + + "group by id order by score desc limit " + strconv.Itoa(n) + + return findTopNOrgs(sql) +} + +func FindTopNOpenIOrgs(n int) ([]*OrgScore, error) { + sql := "select org_id id,num_score score from org_statistic order by num_score desc limit 10" + strconv.Itoa(n) + + return findTopNOrgs(sql) +} + +func findTopNOrgs(sql string) ([]*OrgScore, error) { + resutls, err := x.QueryString(sql) + + if err != nil { + return nil, err + } + var orgScore []*OrgScore + for _, record := range resutls { + id, _ := strconv.ParseInt(record["id"], 10, 64) + user, err := getUserByID(x, id) + if err != nil { + continue + } + orgScore = append(orgScore, &OrgScore{user, record["score"]}) + + } + + return orgScore, nil + +} + // CreateOrganization creates record of a new organization. func CreateOrganization(org, owner *User) (err error) { if !owner.CanCreateOrganization() { diff --git a/models/repo_statistic.go b/models/repo_statistic.go index a9e9593af..4f8f13ed7 100755 --- a/models/repo_statistic.go +++ b/models/repo_statistic.go @@ -73,6 +73,16 @@ func (repo *RepoStatistic) DisplayName() string { return repo.Alias } +func getOpenIByRepoId(repoId int64) float64 { + repoStatistic := new(RepoStatistic) + has, err := xStatistic.Cols("radar_total").Where("repo_id=?", repoId).Desc("id").Limit(1).Get(repoStatistic) + if !has || err != nil { + return 0 + } + return repoStatistic.RadarTotal + +} + func DeleteRepoStatDaily(date string) error { sess := xStatistic.NewSession() defer sess.Close() diff --git a/models/user.go b/models/user.go index f72462051..71885aeb1 100755 --- a/models/user.go +++ b/models/user.go @@ -2104,6 +2104,12 @@ func GetOrganizationsCount() (int64, error) { } +func GetOrganizationsId() ([]int64, error) { + var ids []int64 + err := x.Table("user").Where("type=1").Cols("id").Find(&ids) + return ids, err +} + func GetBlockChainUnSuccessUsers() ([]*User, error) { users := make([]*User, 0, 10) err := x.Where("public_key = ''"). diff --git a/modules/cron/tasks_basic.go b/modules/cron/tasks_basic.go index b9838e66f..b3a6c02a1 100755 --- a/modules/cron/tasks_basic.go +++ b/modules/cron/tasks_basic.go @@ -185,6 +185,17 @@ func registerHandleSummaryStatistic() { }) } +func registerHandleOrgStatistic() { + RegisterTaskFatal("handle_org_statistic", &BaseConfig{ + Enabled: true, + RunAtStart: false, + Schedule: "0 0 2 * * ?", + }, func(ctx context.Context, _ *models.User, _ Config) error { + models.UpdateOrgStatistics() + return nil + }) +} + func registerSyncCloudbrainStatus() { RegisterTaskFatal("sync_cloudbrain_status", &BaseConfig{ Enabled: true, @@ -215,4 +226,5 @@ func initBasicTasks() { registerHandleSummaryStatistic() registerSyncCloudbrainStatus() + registerHandleOrgStatistic() } diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 00cc4e273..58168d1e2 100755 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2198,6 +2198,16 @@ customize = Customize selected_project=Selected Projects fold = Fold unfold = Unfold +org_member = Member +org_members = Members +org_team = Team +org_teams = Teams +org_repository = Repository +org_repositories = Repositories + +star = Star Top10 +member = Members Top10 +active = Active Top10 form.name_reserved = The organization name '%s' is reserved. form.name_pattern_not_allowed = The pattern '%s' is not allowed in an organization name. diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index b410aa52f..28041428f 100755 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -2203,6 +2203,16 @@ customize = 自定义 selected_project=精选项目 fold = 收起 unfold = 展开 +org_member = 成员 +org_members = 成员 +org_team = 团队 +org_teams = 团队 +org_repository = 项目 +org_repositories = 项目 + +star = 点赞榜 +member = 成员榜 +active = 活跃榜 form.name_reserved=组织名称 '%s' 是被保留的。 form.name_pattern_not_allowed=组织名称中不允许使用 "%s"。 diff --git a/public/home/search.js b/public/home/search.js index 70b5d4ef9..e23d27549 100644 --- a/public/home/search.js +++ b/public/home/search.js @@ -108,8 +108,9 @@ function searchItem(type,sortType){ currentSearchSortBy = sortBy[sortType]; currentSearchAscending = sortAscending[sortType]; OnlySearchLabel =false; - page(currentPage); + }else{ + emptySearch(); } } @@ -121,49 +122,31 @@ function search(){ if(!isEmpty(currentSearchKeyword)){ currentSearchKeyword = currentSearchKeyword.trim(); } - $('#searchForm').addClass("hiddenSearch"); - initPageInfo(); if(!isEmpty(currentSearchKeyword)){ - document.getElementById("find_id").innerHTML=getLabel(isZh,"search_finded"); - currentSearchSortBy = sortBy[10]; - currentSearchAscending = "false"; - OnlySearchLabel =false; - page(currentPage); - if(currentSearchTableName != "repository"){ - doSearch("repository",currentSearchKeyword,1,pageSize,true,"",false); - } - if(currentSearchTableName != "issue"){ - doSearch("issue",currentSearchKeyword,1,pageSize,true,"",false); - } - if(currentSearchTableName != "user"){ - doSearch("user",currentSearchKeyword,1,pageSize,true,"",false); - } - if(currentSearchTableName != "org"){ - doSearch("org",currentSearchKeyword,1,pageSize,true,"",false); - } - if(currentSearchTableName != "dataset"){ - doSearch("dataset",currentSearchKeyword,1,pageSize,true,"",false); - } - if(currentSearchTableName != "pr"){ - doSearch("pr",currentSearchKeyword,1,pageSize,true,"",false); - } + doSpcifySearch(currentSearchTableName,currentSearchKeyword,sortBy[10],"false"); }else{ - initDiv(false); - document.getElementById("find_id").innerHTML=getLabel(isZh,"search_empty"); - $('#find_title').html(""); - document.getElementById("sort_type").innerHTML=""; - document.getElementById("child_search_item").innerHTML=""; - document.getElementById("page_menu").innerHTML=""; - $('#repo_total').text(""); - $('#pr_total').text(""); - $('#issue_total').text(""); - $('#dataset_total').text(""); - $('#user_total').text(""); - $('#org_total').text(""); - setActivate(null); + emptySearch(); } } +function emptySearch(){ + initDiv(false); + initPageInfo(); + $('#searchForm').addClass("hiddenSearch"); + document.getElementById("find_id").innerHTML=getLabel(isZh,"search_empty"); + $('#find_title').html(""); + document.getElementById("sort_type").innerHTML=""; + document.getElementById("child_search_item").innerHTML=""; + document.getElementById("page_menu").innerHTML=""; + $('#repo_total').text(""); + $('#pr_total').text(""); + $('#issue_total').text(""); + $('#dataset_total').text(""); + $('#user_total').text(""); + $('#org_total').text(""); + setActivate(null); +} + function initDiv(isSearchLabel=false){ if(isSearchLabel){ document.getElementById("search_div").style.display="none"; @@ -174,7 +157,6 @@ function initDiv(isSearchLabel=false){ document.getElementById("user_item").style.display="none"; document.getElementById("org_item").style.display="none"; document.getElementById("find_id").innerHTML=""; - }else{ document.getElementById("search_div").style.display="block"; document.getElementById("search_label_div").style.display="none"; @@ -187,6 +169,39 @@ function initDiv(isSearchLabel=false){ } } +function doSpcifySearch(tableName,keyword,sortBy="",ascending="false"){ + initDiv(false); + $('#searchForm').addClass("hiddenSearch"); + document.getElementById("find_id").innerHTML=getLabel(isZh,"search_finded"); + currentSearchKeyword = keyword; + initPageInfo(); + currentSearchTableName = tableName; + currentSearchSortBy = sortBy; + currentSearchAscending = ascending; + OnlySearchLabel =false; + + page(currentPage); + + if(currentSearchTableName != "repository"){ + doSearch("repository",currentSearchKeyword,1,pageSize,true,"",false); + } + if(currentSearchTableName != "issue"){ + doSearch("issue",currentSearchKeyword,1,pageSize,true,"",false); + } + if(currentSearchTableName != "user"){ + doSearch("user",currentSearchKeyword,1,pageSize,true,"",false); + } + if(currentSearchTableName != "org"){ + doSearch("org",currentSearchKeyword,1,pageSize,true,"",false); + } + if(currentSearchTableName != "dataset"){ + doSearch("dataset",currentSearchKeyword,1,pageSize,true,"",false); + } + if(currentSearchTableName != "pr"){ + doSearch("pr",currentSearchKeyword,1,pageSize,true,"",false); + } +} + function doSearchLabel(tableName,keyword,sortBy="",ascending="false"){ initDiv(true); //document.getElementById("search_div").style.display="none"; @@ -1272,8 +1287,17 @@ var zhCN={ sessionStorage.removeItem("searchLabel"); doSearchLabel(sessionStorage.getItem("tableName"),sessionStorage.getItem("keyword"),sessionStorage.getItem("sortBy"),sessionStorage.getItem("ascending")); }else{ - console.log("normal search...."); - search(); + var specifySearch = sessionStorage.getItem("specifySearch"); + if(specifySearch){ + sessionStorage.removeItem("specifySearch"); + console.log("search sepcial keyword=...." + sessionStorage.getItem("keyword")); + document.getElementById("keyword_input").value = sessionStorage.getItem("keyword"); + doSpcifySearch(sessionStorage.getItem("tableName"),sessionStorage.getItem("keyword"),sessionStorage.getItem("sortBy"),sessionStorage.getItem("ascending")); + }else{ + console.log("normal search...."); + search(); + } + } } } diff --git a/routers/home.go b/routers/home.go index c33d7a049..324bb1032 100755 --- a/routers/home.go +++ b/routers/home.go @@ -49,7 +49,7 @@ func Home(ctx *context.Context) { ctx.HTML(200, tplHome) } -func setRecommendURL(ctx *context.Context) { +func setRecommendURLOnly(ctx *context.Context) { addr := setting.RecommentRepoAddr[10:] start := strings.Index(addr, "/") end := strings.Index(addr, "raw") @@ -58,7 +58,10 @@ func setRecommendURL(ctx *context.Context) { } else { ctx.Data["RecommendURL"] = setting.RecommentRepoAddr } +} +func setRecommendURL(ctx *context.Context) { + setRecommendURLOnly(ctx) ctx.Data["page_title"] = ctx.Tr("home.page_title") ctx.Data["page_small_title"] = ctx.Tr("home.page_small_title") ctx.Data["page_description"] = ctx.Tr("home.page_description") @@ -441,17 +444,39 @@ func ExploreOrganizations(ctx *context.Context) { ctx.Data["PageIsExploreOrganizations"] = true ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled - visibleTypes := []structs.VisibleType{structs.VisibleTypePublic} - if ctx.User != nil { - visibleTypes = append(visibleTypes, structs.VisibleTypeLimited, structs.VisibleTypePrivate) + N := 10 + starInfo, err := models.FindTopNStarsOrgs(N) + if err != nil { + log.Error("GetStarOrgInfos failed:%v", err.Error(), ctx.Data["MsgID"]) + ctx.ServerError("GetStarOrgInfos", err) + return + } + memberInfo, err := models.FindTopNMembersOrgs(N) + if err != nil { + log.Error("GetMemberOrgInfos failed:%v", err.Error(), ctx.Data["MsgID"]) + ctx.ServerError("GetMemberOrgInfos", err) + return + } + openIInfo, err := models.FindTopNOpenIOrgs(N) + if err != nil { + log.Error("GetOpenIOrgInfos failed:%v", err.Error(), ctx.Data["MsgID"]) + ctx.ServerError("GetOpenIOrgInfos", err) + return } - RenderUserSearch(ctx, &models.SearchUserOptions{ - Actor: ctx.User, - Type: models.UserTypeOrganization, - ListOptions: models.ListOptions{PageSize: setting.UI.ExplorePagingNum}, - Visible: visibleTypes, - }, tplExploreOrganizations) + recommendOrgs, err := GetRecommendOrg() + if err != nil { + log.Error("GetRecommendOrgInfos failed:%v", err.Error(), ctx.Data["MsgID"]) + ctx.ServerError("GetRecommendOrgInfos", err) + return + } + setRecommendURLOnly(ctx) + ctx.Data["RecommendOrgs"] = recommendOrgs + ctx.Data["StarOrgs"] = starInfo + ctx.Data["MemberOrgs"] = memberInfo + ctx.Data["ActiveOrgs"] = openIInfo + + ctx.HTML(http.StatusOK, tplExploreOrganizations) } // ExploreCode render explore code page @@ -583,12 +608,12 @@ func NotFound(ctx *context.Context) { ctx.NotFound("home.NotFound", nil) } -func RecommendOrgFromPromote(ctx *context.Context) { +func GetRecommendOrg() ([]map[string]interface{}, error) { url := setting.RecommentRepoAddr + "organizations" result, err := repository.RecommendFromPromote(url) + if err != nil { - ctx.ServerError("500", err) - return + return nil, err } resultOrg := make([]map[string]interface{}, 0) for _, userName := range result { @@ -598,6 +623,7 @@ func RecommendOrgFromPromote(ctx *context.Context) { userMap["Name"] = user.Name userMap["Description"] = user.Description userMap["FullName"] = user.FullName + userMap["HomeLink"] = user.HomeLink() userMap["ID"] = user.ID userMap["Avatar"] = user.RelAvatarLink() userMap["NumRepos"] = user.NumRepos @@ -608,7 +634,15 @@ func RecommendOrgFromPromote(ctx *context.Context) { log.Info("query user error," + err.Error()) } } + return resultOrg, nil +} +func RecommendOrgFromPromote(ctx *context.Context) { + resultOrg, err := GetRecommendOrg() + if err != nil { + ctx.ServerError("500", err) + return + } ctx.JSON(200, resultOrg) } diff --git a/routers/private/internal.go b/routers/private/internal.go index d80a706cc..ace25c809 100755 --- a/routers/private/internal.go +++ b/routers/private/internal.go @@ -45,6 +45,8 @@ func RegisterRoutes(m *macaron.Macaron) { m.Post("/manager/flush-queues", bind(private.FlushOptions{}), FlushQueues) m.Post("/tool/update_all_repo_commit_cnt", UpdateAllRepoCommitCnt) m.Post("/tool/repo_stat/:date", RepoStatisticManually) + + m.Get("/tool/org_stat", OrgStatisticManually) m.Post("/tool/update_repo_visit/:date", UpdateRepoVisit) m.Post("/task/history_handle/duration", repo.HandleTaskWithNoDuration) diff --git a/routers/private/tool.go b/routers/private/tool.go index d01c5b2ab..122a41afe 100755 --- a/routers/private/tool.go +++ b/routers/private/tool.go @@ -45,6 +45,10 @@ func RepoStatisticManually(ctx *macaron.Context) { repo.TimingCountDataByDate(date) } +func OrgStatisticManually() { + models.UpdateOrgStatistics() +} + func UpdateRepoVisit(ctx *macaron.Context) { date := ctx.Params("date") log.Info("date(%s)", date) diff --git a/templates/explore/organizations.tmpl b/templates/explore/organizations.tmpl index 1151c5a94..58615a8e5 100644 --- a/templates/explore/organizations.tmpl +++ b/templates/explore/organizations.tmpl @@ -1,68 +1,251 @@ + + + + + + {{template "base/head" .}}