diff --git a/README.md b/README.md index afb2da5d9..1cb142db6 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@

logoAiForge - 启智AI开发协作平台

-[![release](https://img.shields.io/badge/release-1.21.10.1-blue)](https://git.openi.org.cn/OpenI/aiforge/releases/latest) +[![release](https://img.shields.io/badge/release-1.21.9.2-blue)](https://git.openi.org.cn/OpenI/aiforge/releases/latest) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) diff --git a/go.mod b/go.mod old mode 100644 new mode 100755 index c663ab2ff..9f93281c3 --- a/go.mod +++ b/go.mod @@ -52,6 +52,7 @@ require ( github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28 github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14 github.com/golang/protobuf v1.4.1 // indirect + github.com/gomodule/redigo v2.0.0+incompatible github.com/google/go-github/v24 v24.0.1 github.com/gorilla/context v1.1.1 github.com/hashicorp/go-retryablehttp v0.6.6 // indirect diff --git a/models/cloudbrain.go b/models/cloudbrain.go index 5be07f741..c26f8a2b6 100755 --- a/models/cloudbrain.go +++ b/models/cloudbrain.go @@ -5,11 +5,12 @@ import ( "fmt" "strings" "time" + "xorm.io/builder" "xorm.io/xorm" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" - "xorm.io/builder" ) type CloudbrainStatus string @@ -59,12 +60,18 @@ type Cloudbrain struct { UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` DeletedAt time.Time `xorm:"deleted"` CanDebug bool `xorm:"-"` + CanDel bool `xorm:"-"` Type int `xorm:"INDEX DEFAULT 0"` User *User `xorm:"-"` Repo *Repository `xorm:"-"` } +type CloudbrainInfo struct { + Cloudbrain `xorm:"extends"` + User `xorm:"extends"` +} + type CloudBrainLoginResult struct { Code string Msg string @@ -523,7 +530,7 @@ type NotebookDelResult struct { InstanceID string `json:"instance_id"` } -func Cloudbrains(opts *CloudbrainsOptions) ([]*Cloudbrain, int64, error) { +func Cloudbrains(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int64, error) { sess := x.NewSession() defer sess.Close() @@ -583,8 +590,10 @@ func Cloudbrains(opts *CloudbrainsOptions) ([]*Cloudbrain, int64, error) { } sess.OrderBy("cloudbrain.created_unix DESC") - cloudbrains := make([]*Cloudbrain, 0, setting.UI.IssuePagingNum) - if err := sess.Where(cond).Find(&cloudbrains); err != nil { + cloudbrains := make([]*CloudbrainInfo, 0, setting.UI.IssuePagingNum) + if err := sess.Table(&Cloudbrain{}).Where(cond). + Join("left", "`user`", "cloudbrain.user_id = `user`.id"). + Find(&cloudbrains); err != nil { return nil, 0, fmt.Errorf("Find: %v", err) } sess.Close() @@ -622,13 +631,13 @@ func GetCloudbrainByJobID(jobID string) (*Cloudbrain, error) { func GetCloudbrainsNeededStopByUserID(userID int64) ([]*Cloudbrain, error) { cloudBrains := make([]*Cloudbrain, 0) - err := x.Cols("job_id", "status", "type").Where("user_id=? AND (status =? OR status=?)", userID, string(JobRunning), string(JobWaiting)).Find(&cloudBrains) + err := x.Cols("job_id", "status", "type").Where("user_id=? AND status !=?", userID, string(JobStopped)).Find(&cloudBrains) return cloudBrains, err } func GetCloudbrainsNeededStopByRepoID(repoID int64) ([]*Cloudbrain, error) { cloudBrains := make([]*Cloudbrain, 0) - err := x.Cols("job_id", "status", "type").Where("repo_id=? AND (status =? OR status=?)", repoID, string(JobRunning), string(JobWaiting)).Find(&cloudBrains) + err := x.Cols("job_id", "status", "type").Where("repo_id=? AND status !=?", repoID, string(JobStopped)).Find(&cloudBrains) return cloudBrains, err } @@ -662,3 +671,24 @@ func GetCloudbrainByName(jobName string) (*Cloudbrain, error) { cb := &Cloudbrain{JobName: jobName} return getRepoCloudBrain(cb) } + +func CanDelJob(isSigned bool, user *User, job *CloudbrainInfo) bool { + if !isSigned || job.Status != string(JobStopped) { + return false + } + repo, err := GetRepositoryByID(job.RepoID) + if err != nil { + log.Error("GetRepositoryByID failed:%v", err.Error()) + return false + } + permission, _ := GetUserRepoPermission(repo, user) + if err != nil { + log.Error("GetUserRepoPermission failed:%v", err.Error()) + return false + } + + if user.ID == job.UserID || user.IsAdmin || permission.AccessMode >= AccessModeAdmin { + return true + } + return false +} diff --git a/models/repo_activity_custom.go b/models/repo_activity_custom.go index 44d20abb3..f6cbf0331 100644 --- a/models/repo_activity_custom.go +++ b/models/repo_activity_custom.go @@ -3,7 +3,11 @@ package models import "code.gitea.io/gitea/modules/git" func GetRepoKPIStats(repo *Repository) (*git.RepoKPIStats, error) { - return git.GetRepoKPIStats(repo.RepoPath()) + wikiPath := "" + if repo.HasWiki() { + wikiPath = repo.WikiPath() + } + return git.GetRepoKPIStats(repo.RepoPath(), wikiPath) } func GetAllUserKPIStats() (map[string]*git.UserKPIStats, error) { diff --git a/models/repo_list.go b/models/repo_list.go index 57b6ebbd6..928e1f953 100755 --- a/models/repo_list.go +++ b/models/repo_list.go @@ -166,6 +166,8 @@ type SearchRepoOptions struct { Archived util.OptionalBool // only search topic name TopicOnly bool + //search by Specific TopicName + TopicName string // include description in keyword search IncludeDescription bool // None -> include has milestones AND has no milestone @@ -327,6 +329,18 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond { } cond = cond.And(keywordCond) } + if opts.TopicName != "" { + var subQueryCond = builder.NewCond() + subQueryCond = subQueryCond.Or(builder.Eq{"topic.name": opts.TopicName}) + subQuery := builder.Select("repo_topic.repo_id").From("repo_topic"). + Join("INNER", "topic", "topic.id = repo_topic.topic_id"). + Where(subQueryCond). + GroupBy("repo_topic.repo_id") + + var topicNameCond = builder.In("id", subQuery) + cond = cond.And(topicNameCond) + + } if opts.Fork != util.OptionalBoolNone { cond = cond.And(builder.Eq{"is_fork": opts.Fork == util.OptionalBoolTrue}) diff --git a/models/user.go b/models/user.go index 38f699740..78ab4627a 100755 --- a/models/user.go +++ b/models/user.go @@ -1556,6 +1556,18 @@ func GetUserByActivateEmail(email string) (*User, error) { if len(users) >= 1 { return &users[0],nil }else { + // Finally, if email address is the protected email address:用户邮件地址设置为隐藏电子邮件地址 + if strings.HasSuffix(email, fmt.Sprintf("@%s", setting.Service.NoReplyAddress)) { + username := strings.TrimSuffix(email, fmt.Sprintf("@%s", setting.Service.NoReplyAddress)) + user := &User{LowerName: username} + has, err := ctx.e.Get(user) + if err != nil { + return nil, err + } + if has { + return user, nil + } + } return nil, errors.New("cannot find user by email") } } diff --git a/modules/git/repo_stats_custom.go b/modules/git/repo_stats_custom.go index 95f1086ad..f7556d5c2 100644 --- a/modules/git/repo_stats_custom.go +++ b/modules/git/repo_stats_custom.go @@ -4,18 +4,23 @@ import ( "bufio" "bytes" "fmt" + "net/url" "sort" "strconv" "strings" "time" + + Log "code.gitea.io/gitea/modules/log" ) type RepoKPIStats struct { Contributors int64 KeyContributors int64 + DevelopAge int64 ContributorsAdded int64 CommitsAdded int64 CommitLinesModified int64 + WikiPages int64 Authors []*UserKPITypeStats } @@ -30,7 +35,7 @@ type UserKPITypeStats struct { isNewContributor bool //是否是4个月内的新增贡献者 } -func GetRepoKPIStats(repoPath string) (*RepoKPIStats, error) { +func GetRepoKPIStats(repoPath string, wikiPath string) (*RepoKPIStats, error) { stats := &RepoKPIStats{} contributors, err := GetContributors(repoPath) @@ -66,15 +71,42 @@ func GetRepoKPIStats(repoPath string) (*RepoKPIStats, error) { } + err = setDevelopAge(repoPath, stats) + if err != nil { + return nil, fmt.Errorf("FillFromGit: %v", err) + } err = setRepoKPIStats(repoPath, fourMonthAgo, stats, newContributersDict) if err != nil { return nil, fmt.Errorf("FillFromGit: %v", err) } + + setWikiPages(wikiPath, stats) return stats, nil } +func setDevelopAge(repoPath string, stats *RepoKPIStats) error { + args := []string{"log", "--no-merges", "--branches=*", "--format=%cd", "--date=short"} + stdout, err := NewCommand(args...).RunInDirBytes(repoPath) + if err != nil { + return err + } + scanner := bufio.NewScanner(bytes.NewReader(stdout)) + scanner.Split(bufio.ScanLines) + developMonth := make(map[string]struct{}) + for scanner.Scan() { + l := strings.TrimSpace(scanner.Text()) + month := l[0:strings.LastIndex(l, "-")] + if _, ok := developMonth[month]; !ok { + developMonth[month] = struct{}{} + } + } + + stats.DevelopAge = int64(len(developMonth)) + return nil +} + //获取一天内的用户贡献指标 func GetUserKPIStats(repoPath string) (map[string]*UserKPIStats, error) { timeUntil := time.Now() @@ -256,3 +288,82 @@ func getContributors(repoPath string, fromTime time.Time) ([]Contributor, error) } return nil, nil } + +func setWikiPages(wikiPath string, stats *RepoKPIStats) { + wikiPages := 0 + + if wikiPath == "" { + stats.WikiPages = int64(wikiPages) + return + } + + wikiRepo, commit, err := findWikiRepoCommit(wikiPath) + if err != nil { + if !IsErrNotExist(err) { + Log.Warn("GetBranchCommit", err) + } + stats.WikiPages = int64(wikiPages) + return + } + + // Get page list. + entries, err := commit.ListEntries() + if err != nil { + if wikiRepo != nil { + wikiRepo.Close() + } + Log.Warn("GetBranchCommit", err) + stats.WikiPages = int64(wikiPages) + return + + } + + for _, entry := range entries { + if !entry.IsRegular() { + continue + } + + wikiName, err := filenameToName(entry.Name()) + if err != nil || wikiName == "_Sidebar" || wikiName == "_Footer" { + continue + } + + wikiPages += 1 + + } + //确保wikiRepo用完被关闭 + defer func() { + if wikiRepo != nil { + wikiRepo.Close() + } + }() + stats.WikiPages = int64(wikiPages) + return + +} + +func filenameToName(filename string) (string, error) { + if !strings.HasSuffix(filename, ".md") { + return "", fmt.Errorf("invalid file") + } + basename := filename[:len(filename)-3] + unescaped, err := url.QueryUnescape(basename) + if err != nil { + return "", err + } + return strings.Replace(unescaped, "-", " ", -1), nil +} + +func findWikiRepoCommit(wikiPath string) (*Repository, *Commit, error) { + wikiRepo, err := OpenRepository(wikiPath) + if err != nil { + + return nil, nil, err + } + + commit, err := wikiRepo.GetBranchCommit("master") + if err != nil { + return wikiRepo, nil, err + } + return wikiRepo, commit, nil +} diff --git a/modules/repository/elk_pagedata.go b/modules/repository/elk_pagedata.go new file mode 100644 index 000000000..bb027726d --- /dev/null +++ b/modules/repository/elk_pagedata.go @@ -0,0 +1,312 @@ +package repository + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + + "code.gitea.io/gitea/modules/setting" +) + +//输入elk的json结构begin +type InputInfo struct { + Batch []Batch `json:"batch"` +} +type Fields struct { + Field string `json:"field"` + Format string `json:"format"` +} +type MatchPhrase struct { + Message string `json:"message"` +} +type Should struct { + MatchPhrase MatchPhrase `json:"match_phrase"` +} +type Bool struct { + Should []Should `json:"should"` + MinimumShouldMatch int `json:"minimum_should_match"` +} +type Timestamptest struct { + // Gte time.Time `json:"gte"` + Gte string `json:"gte"` + Lte string `json:"lte"` + Format string `json:"format"` +} +type Range struct { + Timestamptest Timestamptest `json:"@timestamptest"` +} + +type FilterMatchPhrase struct { + UserName string `json:"userName.keyword,omitempty"` + ProjectName string `json:"projectName.keyword,omitempty"` + TagName string `json:"tagName.keyword,omitempty"` +} + +type Filter struct { + Bool *Bool `json:"bool,omitempty"` + Range *Range `json:"range,omitempty"` + FilterMatchPhrase *FilterMatchPhrase `json:"match_phrase,omitempty"` +} +type MustNotMatchPhrase struct { + ProjectName string `json:"projectName"` +} +type MustNot struct { + MustNotMatchPhrase MustNotMatchPhrase `json:"match_phrase"` +} +type BoolIn struct { + Filter []Filter `json:"filter"` + MustNot []MustNot `json:"must_not"` +} +type Query struct { + BoolIn BoolIn `json:"bool"` +} +type Body struct { + Size int `json:"size"` + Fields []Fields `json:"fields"` + Query Query `json:"query"` +} +type Params struct { + Index string `json:"index"` + Body Body `json:"body"` +} +type Request struct { + Params Params `json:"params"` +} +type Batch struct { + Request Request `json:"request"` +} + +//输入elk的json结构end + +//elk输出的json结构begin +type Hits struct { + Total int `json:"total"` +} +type RawResponse struct { + Hits Hits `json:"hits"` +} +type Result struct { + RawResponse RawResponse `json:"rawResponse"` + Loaded int `json:"loaded"` +} +type ResultInfo struct { + Id int `json:"id"` + Result Result `json:"result"` +} + +//elk输出的json结构end + +//发送post请求到elk +func SendReqToElk(jsonStr []byte) (content string) { + ElkBase64Init := setting.ElkUser + ":" + setting.ElkPassword + ElkBase64 := base64.StdEncoding.EncodeToString([]byte(ElkBase64Init)) + BasicElkBase64 := "Basic" + " " + ElkBase64 + url := setting.ElkUrl + req, _ := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr)) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("kbn-version", "7.13.2") + req.Header.Set("Authorization", BasicElkBase64) + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + panic(err) + } + defer resp.Body.Close() + body, _ := ioutil.ReadAll(resp.Body) + return string(body) +} + +//处理返回的elk数据,只保留totalView,即访问量;loaded是分片载入次数,用来判断返回的数据是否准确 +func GetResultFromElk(resultinfo ResultInfo, jobResult string) (loaded int, totalView int) { + var resultTest ResultInfo + errs := json.Unmarshal([]byte(jobResult), &resultTest) + fmt.Println(errs) + return resultTest.Result.Loaded, resultTest.Result.RawResponse.Hits.Total +} + +//初始化传给elk的数据结构,给定用户名和项目名,查询的起止时间,返回初始化后的结构 +func ProjectViewInit(User string, Project string, Gte string, Lte string) (projectViewInit InputInfo) { + var inputStruct InputInfo + inputStruct.Batch = make([]Batch, 1) + inputStruct.Batch[0].Request.Params.Index = setting.Index + inputStruct.Batch[0].Request.Params.Body.Size = 0 + inputStruct.Batch[0].Request.Params.Body.Fields = make([]Fields, 1) + inputStruct.Batch[0].Request.Params.Body.Fields[0].Field = setting.TimeField + inputStruct.Batch[0].Request.Params.Body.Fields[0].Format = setting.ElkTimeFormat + inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.Filter = make([]Filter, 3) + //限定查询时间 + var timeRange Range + timeRange.Timestamptest.Gte = Gte + timeRange.Timestamptest.Lte = Lte + inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.Filter[0].Range = &timeRange + //限定用户 + var userName FilterMatchPhrase + userName.UserName = User + inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.Filter[1].FilterMatchPhrase = &userName + //限定项目 + var projectName FilterMatchPhrase + projectName.ProjectName = Project + inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.Filter[2].FilterMatchPhrase = &projectName + return inputStruct +} + +//初始化传给elk的数据结构,给定查询信息和非项目名,查询的起止时间,返回初始化后的结构 +func AllProjectViewInit(MessageInfo string, NotProject string, Gte string, Lte string) (allProjectViewInit InputInfo) { + var inputStruct InputInfo + inputStruct.Batch = make([]Batch, 1) + inputStruct.Batch[0].Request.Params.Index = setting.Index + inputStruct.Batch[0].Request.Params.Body.Size = 0 + inputStruct.Batch[0].Request.Params.Body.Fields = make([]Fields, 1) + inputStruct.Batch[0].Request.Params.Body.Fields[0].Field = setting.TimeField + inputStruct.Batch[0].Request.Params.Body.Fields[0].Format = setting.ElkTimeFormat + inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.Filter = make([]Filter, 2) + //限定message + var bool Bool + bool.Should = make([]Should, 1) + bool.Should[0].MatchPhrase.Message = MessageInfo + bool.MinimumShouldMatch = 1 + inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.Filter[0].Bool = &bool + //限定查询时间 + var timeRange Range + timeRange.Timestamptest.Gte = Gte + timeRange.Timestamptest.Lte = Lte + inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.Filter[1].Range = &timeRange + //限定非项目 + // var boolIn BoolIn + inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.MustNot = make([]MustNot, 1) + inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.MustNot[0].MustNotMatchPhrase.ProjectName = NotProject + return inputStruct +} + +//初始化传给elk的数据结构,给定查询信息和tagName,查询的起止时间,返回初始化后的结构 +func TagNameInit(MessageInfo string, Tagname string, Gte string, Lte string) (projectViewInit InputInfo) { + var inputStruct InputInfo + inputStruct.Batch = make([]Batch, 1) + inputStruct.Batch[0].Request.Params.Index = setting.Index + inputStruct.Batch[0].Request.Params.Body.Size = 0 + inputStruct.Batch[0].Request.Params.Body.Fields = make([]Fields, 1) + inputStruct.Batch[0].Request.Params.Body.Fields[0].Field = setting.TimeField + inputStruct.Batch[0].Request.Params.Body.Fields[0].Format = setting.ElkTimeFormat + inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.Filter = make([]Filter, 3) + //限定message + var bool Bool + bool.Should = make([]Should, 1) + bool.Should[0].MatchPhrase.Message = MessageInfo + bool.MinimumShouldMatch = 1 + inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.Filter[0].Bool = &bool + //限定tagName + var tagName FilterMatchPhrase + tagName.TagName = Tagname + inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.Filter[1].FilterMatchPhrase = &tagName + //限定查询时间 + var timeRange Range + timeRange.Timestamptest.Gte = Gte + timeRange.Timestamptest.Lte = Lte + inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.Filter[2].Range = &timeRange + return inputStruct +} + +//向elk发送请求,将获取的结果只保留访问量,输入是初始化后的数据结构,返回访问量 +func ViewInfo(viewInfo InputInfo) (totalView int) { + jsons, errs := json.Marshal(viewInfo) + if errs != nil { + fmt.Println("errs:", errs.Error()) + } + // fmt.Println("viewInfoInit:",string(jsons)) + var jsonStr = []byte(jsons) + var resultInfo ResultInfo + loaded, totalView := GetResultFromElk(resultInfo, SendReqToElk(jsonStr)) + time := 0 + for { + if loaded == 0 { + loaded_next, totalView := GetResultFromElk(resultInfo, SendReqToElk(jsonStr)) + time++ + if loaded_next != 0 && time < 100 { + fmt.Println("totalView:", totalView) + return totalView + } + if time > 100 { + break + } + } else { + break + } + } + fmt.Println("loaded:", loaded) + return totalView +} + +// @title ProjectView +// @description 获取指定用户和项目的访问量 +// @param User string "用户名" +// @param Project string "项目名" +// @param Gte string "起始时间" 如time.Now().AddDate(0, 0, -1).Format(time.RFC3339) +// @param Lte string "结束时间" 如time.Now().Format(time.RFC3339) +// @return totalView int "访问量" +func AppointProjectView(User string, Project string, Gte string, Lte string) (totalView int) { + InitInfo := ProjectViewInit(User, Project, Gte, Lte) + return ViewInfo(InitInfo) +} + +//统计项目相关页面的访问量 +type ProjectInfo struct { + /* 统计所有项目中该页面的浏览情况,不需要区分项目。以aiforge项目为例 */ + //地址:https://git.openi.org.cn/OpenI/aiforge/datasets?type=0 + Project_dataset_type_0 int + //地址:https://git.openi.org.cn/OpenI/aiforge/datasets?type=1 + Project_dataset_type_1 int + //地址:https://git.openi.org.cn/OpenI/aiforge/issues + Project_issues int + //地址:https://git.openi.org.cn/OpenI/aiforge/labels + Project_labels int + //地址:https://git.openi.org.cn/OpenI/aiforge/milestones + Project_milestones int + //地址:https://git.openi.org.cn/OpenI/aiforge/pulls + Project_pulls int + //地址:https://git.openi.org.cn/OpenI/aiforge/release + Project_release int + //地址:https://git.openi.org.cn/OpenI/aiforge/wiki + Project_wiki int + //地址:https://git.openi.org.cn/OpenI/aiforge/activity + Project_activity int + //地址:https://git.openi.org.cn/OpenI/aiforge/cloudbrain + Project_cloudbrain int + //地址:https://git.openi.org.cn/OpenI/aiforge/modelarts + Project_modelarts int + //地址:https://git.openi.org.cn/OpenI/aiforge/blockchain + Project_blockchain int + //地址:https://git.openi.org.cn/OpenI/aiforge/watchers + Project_watchers int + //地址:https://git.openi.org.cn/OpenI/aiforge/stars + Project_stars int + //地址:https://git.openi.org.cn/OpenI/aiforge/forks + Project_forks int +} + +// @title AllProjectView +// @description 获取指定用户和项目的访问量 +// @param Gte string "起始时间" 如time.Now().AddDate(0, 0, -1).Format(time.RFC3339) +// @param Lte string "结束时间" +// @return projectInfo ProjectInfo "统计所有项目中页面的浏览情况,不需要区分项目" +func AllProjectView(Gte string, Lte string) (projectInfo ProjectInfo) { + projectInfo.Project_dataset_type_0 = ViewInfo(AllProjectViewInit("/datasets?type=0", "%{[request][2]}", Gte, Lte)) + projectInfo.Project_dataset_type_1 = ViewInfo(AllProjectViewInit("/datasets?type=1", "%{[request][2]}", Gte, Lte)) + projectInfo.Project_issues = ViewInfo(AllProjectViewInit("/issues HTTP/2.0", "%{[request][2]}", Gte, Lte)) + projectInfo.Project_labels = ViewInfo(TagNameInit("/labels HTTP/2.0", "labels", Gte, Lte)) + projectInfo.Project_milestones = ViewInfo(AllProjectViewInit("/milestones HTTP/2.0", "%{[request][2]}", Gte, Lte)) + projectInfo.Project_pulls = ViewInfo(AllProjectViewInit("/pulls HTTP/2.0", "%{[request][2]}", Gte, Lte)) + projectInfo.Project_release = ViewInfo(AllProjectViewInit("/release HTTP/2.0", "%{[request][2]}", Gte, Lte)) + projectInfo.Project_wiki = ViewInfo(AllProjectViewInit("/wiki HTTP/2.0", "%{[request][2]}", Gte, Lte)) + projectInfo.Project_activity = ViewInfo(AllProjectViewInit("/activity HTTP/2.0", "%{[request][2]}", Gte, Lte)) + projectInfo.Project_cloudbrain = ViewInfo(AllProjectViewInit("/cloudbrain HTTP/2.0", "%{[request][2]}", Gte, Lte)) + projectInfo.Project_modelarts = ViewInfo(AllProjectViewInit("/modelarts HTTP/2.0", "%{[request][2]}", Gte, Lte)) + projectInfo.Project_blockchain = ViewInfo(AllProjectViewInit("/blockchain HTTP/2.0", "%{[request][2]}", Gte, Lte)) + projectInfo.Project_watchers = ViewInfo(AllProjectViewInit("/watchers HTTP/2.0", "%{[request][2]}", Gte, Lte)) + projectInfo.Project_stars = ViewInfo(AllProjectViewInit("/stars HTTP/2.0", "%{[request][2]}", Gte, Lte)) + projectInfo.Project_forks = ViewInfo(AllProjectViewInit("/forks HTTP/2.0", "%{[request][2]}", Gte, Lte)) + return projectInfo +} diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 0fbc9f909..e1e7b7902 100755 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -482,6 +482,14 @@ var ( PoolInfos string Flavor string FlavorInfos string + + //elk config + ElkUrl string + ElkUser string + ElkPassword string + Index string + TimeField string + ElkTimeFormat string ) // DateLang transforms standard language locale name to corresponding value in datetime plugin. @@ -1201,6 +1209,14 @@ func NewContext() { PoolInfos = sec.Key("POOL_INFOS").MustString("") Flavor = sec.Key("FLAVOR").MustString("") FlavorInfos = sec.Key("FLAVOR_INFOS").MustString("") + + sec = Cfg.Section("elk") + ElkUrl = sec.Key("ELKURL").MustString("http://192.168.207.35:5601/internal/bsearch") + ElkUser = sec.Key("ELKUSER").MustString("Qizhi") + ElkPassword = sec.Key("ELKPASSWORD").MustString("Pcl2020") + Index = sec.Key("INDEX").MustString("filebeat-7.3.2*") + TimeField = sec.Key("TIMEFIELD").MustString(" @timestamptest") + ElkTimeFormat = sec.Key("ELKTIMEFORMAT").MustString("date_time") } func loadInternalToken(sec *ini.Section) string { diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 50e85ba27..746e46463 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -87,7 +87,7 @@ write = Write preview = Preview loading = Loading… -error404_index = Request forbidden by administrative rules +error404_index = Request forbidden by administrative rules error500_index = Internal Server Error error404 = The page you are trying to reach either does not exist or you are not authorized to view it. error500= Sorry, the site has encountered some problems, we are trying to fix the page, please try again later. @@ -573,7 +573,7 @@ authorized_oauth2_applications_description = You've granted access to your perso revoke_key = Revoke revoke_oauth2_grant = Revoke Access revoke_oauth2_grant_description = Revoking access for this third party application will prevent this application from accessing your data. Are you sure? -revoke_oauth2_grant_success = You've revoked access successfully. +revoke_oauth2_grant_success = You have revoked access successfully. twofa_desc = Two-factor authentication enhances the security of your account. twofa_is_enrolled = Your account is currently enrolled in two-factor authentication. @@ -770,6 +770,10 @@ cloudbrain_selection = select cloudbrain cloudbrain_platform_selection = Select the cloudbrain platform you want to use: confirm_choice = confirm cloudbran1_tips = Only data in zip format can create cloudbrain tasks +cloudbrain_creator=Creator +cloudbrain_task = Task Name +cloudbrain_operate = Operate +cloudbrain_status_createtime = Status/Createtime template.items = Template Items template.git_content = Git Content (Default Branch) @@ -831,6 +835,7 @@ fork = Fork download_archive = Download Repository no_desc = No Description +no_label = No labels quick_guide = Quick Guide clone_this_repo = Clone this repository create_new_repo_command = Creating a new repository on the command line @@ -845,6 +850,7 @@ filter_branch_and_tag = Filter branch or tag branches = Branches tags = Tags issues = Issues +issues_detail = Detail pulls = Pull Requests labels = Labels org_labels_desc = Organization level labels that can be used with all repositories under this organization @@ -1241,6 +1247,11 @@ pulls.reject_count_1 = "%d change request" pulls.reject_count_n = "%d change requests" pulls.waiting_count_1 = "%d waiting review" pulls.waiting_count_n = "%d waiting reviews" +pulls.commits_count_1=This branch is %d commit behind the upstream. +pulls.commits_count_n=This branch is %d commit behind the upstream. +pulls.fetch_upstream=Fetch upstream +pulls.upstream_up_to_date=No new commits to fetch +pulls.upstream_error=Cannot get upstream info pulls.no_merge_desc = This pull request cannot be merged because all repository merge options are disabled. pulls.no_merge_helper = Enable merge options in the repository settings or merge the pull request manually. diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 89a6282e5..6dc0d410c 100755 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -772,6 +772,10 @@ cloudbrain_selection=云脑选择 cloudbrain_platform_selection=选择您准备使用的云脑平台: confirm_choice=确定 cloudbran1_tips=只有zip格式的数据集才能发起云脑任务 +cloudbrain_creator=创建者 +cloudbrain_task=任务名称 +cloudbrain_operate=操作 +cloudbrain_status_createtime=状态/创建时间 template.items=模板选项 template.git_content=Git数据(默认分支) @@ -833,6 +837,7 @@ fork=派生 download_archive=下载此项目 no_desc=暂无描述 +no_label = 暂无标签 quick_guide=快速帮助 clone_this_repo=克隆当前项目 create_new_repo_command=从命令行创建一个新的项目 @@ -847,6 +852,7 @@ filter_branch_and_tag=过滤分支或标签 branches=分支列表 tags=标签列表 issues=任务 +issues_detail=详情 pulls=合并请求 labels=标签 org_labels_desc=组织级别的标签,可以被本组织下的 所有项目 使用 @@ -1243,6 +1249,11 @@ pulls.reject_count_1=%d 变更请求 pulls.reject_count_n=%d 变更请求 pulls.waiting_count_1=%d 个正在等待审核 pulls.waiting_count_n=%d 个正在等待审核 +pulls.commits_count_1=当前分支落后上游分支 %d 个提交 +pulls.commits_count_n=当前分支落后上游分支 %d 个提交 +pulls.fetch_upstream=拉取上游更新 +pulls.upstream_up_to_date=上游分支没有新的更新 +pulls.upstream_error=获取上游分支信息错误 pulls.no_merge_desc=由于未启用合并选项,此合并请求无法被合并。 pulls.no_merge_helper=在项目设置中启用合并选项或者手工合并请求。 diff --git a/public/img/icons.svg b/public/img/icons.svg new file mode 100644 index 000000000..3a83f8cdf --- /dev/null +++ b/public/img/icons.svg @@ -0,0 +1,1653 @@ + + + + 切图 + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 存量 + + + + + + + + + + + + + + + + + + + + + + + + R + + + + + M + + + + + A + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Microsof + t + + + + + G + + + o + + + o + + + g + + + l + + + e + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 托管 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + tts.wav + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CSB + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 共享 + + + + + + 默认 + + + + + + GPU + + + + + + NEW + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/img/loading.gif b/public/img/loading.gif new file mode 100644 index 000000000..2c45c7bca Binary files /dev/null and b/public/img/loading.gif differ diff --git a/routers/home.go b/routers/home.go index a1c07e317..70fea437c 100755 --- a/routers/home.go +++ b/routers/home.go @@ -149,8 +149,7 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) { //todo:support other topics keyword := strings.Trim(ctx.Query("q"), " ") - topicOnly := ctx.QueryBool("topic") - ctx.Data["TopicOnly"] = topicOnly + topic := strings.Trim(ctx.Query("topic"), " ") repos, count, err = models.SearchRepository(&models.SearchRepoOptions{ ListOptions: models.ListOptions{ @@ -164,7 +163,7 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) { OwnerID: opts.OwnerID, AllPublic: true, AllLimited: true, - TopicOnly: topicOnly, + TopicName: topic, IncludeDescription: setting.UI.SearchRepoDescription, }) if err != nil { @@ -177,6 +176,8 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) { repo.Active = int64(repo.NumIssues) + int64(repo.NumPulls) + int64(repo.NumCommit) } ctx.Data["Keyword"] = keyword + ctx.Data["Topic"] = topic + ctx.Data["Total"] = count ctx.Data["Repos"] = repos ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled diff --git a/routers/repo/cloudbrain.go b/routers/repo/cloudbrain.go index c30906af4..648061171 100755 --- a/routers/repo/cloudbrain.go +++ b/routers/repo/cloudbrain.go @@ -4,6 +4,7 @@ import ( "bufio" "encoding/json" "errors" + "fmt" "io" "net/http" "os" @@ -69,11 +70,13 @@ func CloudBrainIndex(ctx *context.Context) { timestamp := time.Now().Unix() for i, task := range ciTasks { - if task.Status == string(models.JobRunning) && (timestamp-int64(task.CreatedUnix) > 10) { + if task.Status == string(models.JobRunning) && (timestamp-int64(task.Cloudbrain.CreatedUnix) > 10) { ciTasks[i].CanDebug = true } else { ciTasks[i].CanDebug = false } + + ciTasks[i].CanDel = models.CanDelJob(ctx.IsSigned, ctx.User, task) } pager := context.NewPagination(int(count), setting.UI.IssuePagingNum, page, 5) @@ -390,19 +393,39 @@ func StopJobs(cloudBrains []*models.Cloudbrain) { for _, taskInfo := range cloudBrains { if taskInfo.Type == models.TypeCloudBrainOne { - err := cloudbrain.StopJob(taskInfo.JobID) + err := retry(3, time.Second*30, func() error { + return cloudbrain.StopJob(taskInfo.JobID) + }) + logErrorAndUpdateJobStatus(err, taskInfo) } else { param := models.NotebookAction{ Action: models.ActionStop, } - _, err := modelarts.StopJob(taskInfo.JobID, param) + err := retry(3, time.Second*30, func() error { + _, err := modelarts.StopJob(taskInfo.JobID, param) + return err + }) logErrorAndUpdateJobStatus(err, taskInfo) } } } +func retry(attempts int, sleep time.Duration, f func() error) (err error) { + for i := 0; i < attempts; i++ { + if i > 0 { + log.Warn("retrying after error:", err) + time.Sleep(sleep) + } + err = f() + if err == nil { + return nil + } + } + return fmt.Errorf("after %d attempts, last error: %s", attempts, err) +} + func logErrorAndUpdateJobStatus(err error, taskInfo *models.Cloudbrain) { if err != nil { log.Warn("Failed to stop cloudBrain job:"+taskInfo.JobID, err) diff --git a/routers/repo/modelarts.go b/routers/repo/modelarts.go index 2dc327d33..080c36377 100755 --- a/routers/repo/modelarts.go +++ b/routers/repo/modelarts.go @@ -57,6 +57,8 @@ func ModelArtsIndex(ctx *context.Context) { } else { ciTasks[i].CanDebug = false } + + ciTasks[i].CanDel = models.CanDelJob(ctx.IsSigned, ctx.User, task) } pager := context.NewPagination(int(count), setting.UI.IssuePagingNum, page, 5) diff --git a/routers/repo/view.go b/routers/repo/view.go index 1546f53b7..9477b27dd 100644 --- a/routers/repo/view.go +++ b/routers/repo/view.go @@ -790,6 +790,46 @@ func renderCode(ctx *context.Context) { } } + //如果是fork的仓库 + if ctx.Repo.Repository.IsFork { + //获得fetchUpstream对应的分支参数 + /* + // 1. /{:baseOwner}/{:baseRepoName}/compare/{:baseBranch}...{:headBranch} + // 2. /{:baseOwner}/{:baseRepoName}/compare/{:baseBranch}...{:headOwner}:{:headBranch} + // 3. /{:baseOwner}/{:baseRepoName}/compare/{:baseBranch}...{:headOwner}/{:headRepoName}:{:headBranch} + */ + baseGitRepo, err := git.OpenRepository(ctx.Repo.Repository.BaseRepo.RepoPath()) + defer baseGitRepo.Close() + if err != nil { + log.Error("error open baseRepo:%s",ctx.Repo.Repository.BaseRepo.RepoPath()) + ctx.Data["FetchUpstreamCnt"] = -1 // minus value indicates error + }else{ + if _,error:= baseGitRepo.GetBranch(ctx.Repo.BranchName);error==nil{ + //base repo has the same branch, then compare between current repo branch and base repo's branch + compareUrl := ctx.Repo.BranchName + "..." + ctx.Repo.Repository.BaseRepo.OwnerName + "/" + ctx.Repo.Repository.BaseRepo.Name + ":" + ctx.Repo.BranchName + ctx.SetParams("*",compareUrl) + ctx.Data["UpstreamSameBranchName"] = true + }else{ + //else, compare between current repo branch and base repo's default branch + compareUrl := ctx.Repo.BranchName + "..." + ctx.Repo.Repository.BaseRepo.OwnerName + "/" + ctx.Repo.Repository.BaseRepo.Name + ":" + ctx.Repo.Repository.BaseRepo.DefaultBranch + ctx.SetParams("*",compareUrl) + ctx.Data["UpstreamSameBranchName"] = false + } + _, _, headGitRepo, compareInfo, _, _ := ParseCompareInfo(ctx) + defer headGitRepo.Close() + if compareInfo!= nil { + if compareInfo.Commits!=nil { + log.Info("compareInfoCommits数量:%d",compareInfo.Commits.Len()) + ctx.Data["FetchUpstreamCnt"] = compareInfo.Commits.Len() + }else{ + log.Info("compareInfo nothing different") + ctx.Data["FetchUpstreamCnt"] = 0 + } + }else{ + ctx.Data["FetchUpstreamCnt"] = -1 // minus value indicates error + } + } + } ctx.Data["Paths"] = paths ctx.Data["TreeLink"] = treeLink ctx.Data["TreeNames"] = treeNames diff --git a/routers/secure/user.go b/routers/secure/user.go index 8567dc9e6..d5b303d5e 100755 --- a/routers/secure/user.go +++ b/routers/secure/user.go @@ -7,6 +7,7 @@ package secure import ( "net/http" + "net/mail" "strings" "code.gitea.io/gitea/models" @@ -63,6 +64,14 @@ func CreateUser(ctx *context.Context, form api.CreateUserOption) { // "422": // "$ref": "#/responses/validationError" + _, err1 := mail.ParseAddress(form.Email) + if err1 != nil { + ctx.JSON(http.StatusBadRequest, map[string]string{ + "error_msg": "Email format is wrong.", + }) + return + } + u := &models.User{ Name: form.Username, FullName: form.FullName, diff --git a/templates/base/head_navbar.tmpl b/templates/base/head_navbar.tmpl index 0dac7824c..c22cb9fa7 100755 --- a/templates/base/head_navbar.tmpl +++ b/templates/base/head_navbar.tmpl @@ -154,7 +154,7 @@ {{svg "octicon-person" 16}} {{.i18n.Tr "register"}} {{end}} - + {{svg "octicon-sign-in" 16}} {{.i18n.Tr "sign_in"}} diff --git a/templates/base/head_navbar_home.tmpl b/templates/base/head_navbar_home.tmpl index 2379c1782..e6729da62 100644 --- a/templates/base/head_navbar_home.tmpl +++ b/templates/base/head_navbar_home.tmpl @@ -154,7 +154,7 @@ {{svg "octicon-person" 16}} {{.i18n.Tr "register"}} {{end}} - + {{svg "octicon-sign-in" 16}} {{.i18n.Tr "sign_in"}} diff --git a/templates/explore/dataset_search.tmpl b/templates/explore/dataset_search.tmpl index 6c973026a..aad8f5083 100644 --- a/templates/explore/dataset_search.tmpl +++ b/templates/explore/dataset_search.tmpl @@ -1,12 +1,12 @@
-
+
- +
diff --git a/templates/explore/repo_left.tmpl b/templates/explore/repo_left.tmpl index af32851c0..ca6a3b3dd 100755 --- a/templates/explore/repo_left.tmpl +++ b/templates/explore/repo_left.tmpl @@ -6,67 +6,67 @@ 全部领域 - + 大模型 - + AI开发工具 - + 计算机视觉 - + 自然语言处理 - + 机器学习 - + 神经网络 - + 自动驾驶 - + 机器人 - + 联邦学习 - + 数据挖掘 - + diff --git a/templates/explore/repo_list.tmpl b/templates/explore/repo_list.tmpl index a9eaaaccf..b05b0e469 100755 --- a/templates/explore/repo_list.tmpl +++ b/templates/explore/repo_list.tmpl @@ -40,20 +40,20 @@
@@ -143,7 +143,7 @@ {{if .Topics }}
{{range .Topics}} - {{if ne . "" }}
{{.}}
{{end}} + {{if ne . "" }}
{{.}}
{{end}} {{end}}
{{end}} diff --git a/templates/explore/repo_search.tmpl b/templates/explore/repo_search.tmpl index 7ed853fc8..d30ad5625 100644 --- a/templates/explore/repo_search.tmpl +++ b/templates/explore/repo_search.tmpl @@ -9,7 +9,7 @@
- +
diff --git a/templates/explore/search.tmpl b/templates/explore/search.tmpl index 11e89d50a..4353f7302 100644 --- a/templates/explore/search.tmpl +++ b/templates/explore/search.tmpl @@ -1,11 +1,11 @@
- +
- +
diff --git a/templates/repo/cloudbrain/index.tmpl b/templates/repo/cloudbrain/index.tmpl index 532a8f4ff..0626f57c6 100755 --- a/templates/repo/cloudbrain/index.tmpl +++ b/templates/repo/cloudbrain/index.tmpl @@ -187,6 +187,12 @@ cursor: pointer; pointer-events: none; } + .time-show{ + font-size: 10px; + margin-top: 0.4rem; + display: inline-block; + } + @@ -235,45 +241,87 @@
-
+ +
-
+
--> + +
+
+
+
+ {{$.i18n.Tr "repo.cloudbrain_task"}} +
+
+ {{$.i18n.Tr "repo.cloudbrain_status_createtime"}} +
+
+ {{$.i18n.Tr "repo.cloudbrain_creator"}} +
+
+ {{$.i18n.Tr "repo.cloudbrain_operate"}} +
+ +
+ +
{{range .Tasks}}
-
- - {{svg "octicon-tasklist" 16}} - {{.JobName}} +
- - {{.Status}} + + + + {{.Status}} + + - {{TimeSinceUnix .CreatedUnix $.Lang}} + {{TimeSinceUnix .Cloudbrain.CreatedUnix $.Lang}}
+
+ +
-
+
{{if and (ne .Status "WAITING") (ne .JobType "DEBUG")}} 评分 @@ -304,10 +352,10 @@
- -
+ + {{$.CsrfTokenHtml}} - + 删除
@@ -436,15 +484,18 @@ $(document).ready(loadJobStatus); function loadJobStatus() { $(".job-status").each((index, job) => { + console.log("---------",index,job) const jobID = job.dataset.jobid; const repoPath = job.dataset.repopath; if (job.textContent.trim() == 'STOPPED') { + return } $.get(`/api/v1/repos/${repoPath}/cloudbrain/${jobID}`, (data) => { const jobID = data.JobID const status = data.JobStatus + console.log("status",status) if (status != job.textContent.trim()) { //$('#' + jobID).text(status) //if (status == 'STOPPED') { diff --git a/templates/repo/datasets/dataset_list.tmpl b/templates/repo/datasets/dataset_list.tmpl index cf6c47926..47716418a 100755 --- a/templates/repo/datasets/dataset_list.tmpl +++ b/templates/repo/datasets/dataset_list.tmpl @@ -1,63 +1,52 @@ + {{if .Attachments}} {{range .Attachments}}
-
+ -
- {{.Size | FileSize}} -
-
- {{svg "octicon-flame" 16}} {{(.DownloadCount | PrettyNumber)}} -
- -
- {{svg "octicon-file" 16}} -
- -
- {{svg "octicon-file-binary" 16}} -
- - - {{if $.IsSigned}} -
- +
+
+ {{svg "octicon-flame" 16}} {{(.DownloadCount | PrettyNumber)}} + + {{svg "octicon-file-binary" 16}}
- {{end}} - {{if not .CanDel}} -
- {{if .IsPrivate}} {{$.i18n.Tr "dataset.private"}} {{else}} {{$.i18n.Tr "dataset.public"}} {{end}} + {{if ne .DecompressState 0}} + - {{else}} - {{if $.Permission.CanWrite $.UnitTypeDatasets}} + {{end}} + {{if not .CanDel}} + {{$.i18n.Tr "dataset.delete"}} + {{if .IsPrivate}} {{$.i18n.Tr "dataset.private"}} {{else}} {{$.i18n.Tr "dataset.public"}} {{end}} + {{else}} + {{if $.Permission.CanWrite $.UnitTypeDatasets}} + {{$.i18n.Tr "dataset.delete"}} {{if $.Repository.IsPrivate}} - - {{ else }} -
- + {{end}} + {{else}} + {{$.i18n.Tr "dataset.delete"}} + {{if .IsPrivate}} {{$.i18n.Tr "dataset.private"}} {{else}} {{$.i18n.Tr "dataset.public"}} {{end}} {{end}} - - {{else}} - {{end}} - {{end}} +
+
{{end}} diff --git a/templates/repo/datasets/index.tmpl b/templates/repo/datasets/index.tmpl index 6fa6ccb69..167b1ef44 100755 --- a/templates/repo/datasets/index.tmpl +++ b/templates/repo/datasets/index.tmpl @@ -37,7 +37,7 @@
{{if .Permission.CanWrite $.UnitTypeDatasets}} @@ -66,7 +66,7 @@
{{.i18n.Tr "cancel"}} - +
@@ -80,7 +80,7 @@
-

{{if eq .Type 0}}{{.i18n.Tr "repo.cloudbrain1"}}{{else}}{{.i18n.Tr "repo.cloudbrain2"}}{{end}}-{{.i18n.Tr "datasets"}}

+ {{if eq .Type 0}}{{.i18n.Tr "repo.cloudbrain1"}}{{else}}{{.i18n.Tr "repo.cloudbrain2"}}{{end}}-{{.i18n.Tr "datasets"}}
+
- +
+ + diff --git a/web_src/js/components/MinioUploader.vue b/web_src/js/components/MinioUploader.vue index 71aa3b2ce..8006b0c91 100755 --- a/web_src/js/components/MinioUploader.vue +++ b/web_src/js/components/MinioUploader.vue @@ -1,17 +1,16 @@ diff --git a/web_src/js/components/ObsUploader.vue b/web_src/js/components/ObsUploader.vue index 1f1697a78..381cb7f95 100755 --- a/web_src/js/components/ObsUploader.vue +++ b/web_src/js/components/ObsUploader.vue @@ -6,9 +6,12 @@ />

{{ file_status_text }} - {{ status }} + {{ status }} +

+

说明:
+ - 只有zip格式的数据集才能发起云脑任务;
+ - 云脑1提供 CPU / GPU 资源,云脑2提供 Ascend NPU 资源;调试使用的数据集也需要上传到对应的环境。

-

云脑1提供 CPU / GPU 资源,云脑2提供 Ascend NPU 资源;调试使用的数据集也需要上传到对应的环境。

diff --git a/web_src/js/index.js b/web_src/js/index.js index 2cc622cc0..5c6ff188a 100755 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -36,6 +36,7 @@ import MinioUploader from './components/MinioUploader.vue'; import ObsUploader from './components/ObsUploader.vue'; import EditAboutInfo from './components/EditAboutInfo.vue'; import Images from './components/Images.vue' +import EditTopics from './components/EditTopics.vue' Vue.use(ElementUI); Vue.prototype.$axios = axios; @@ -2967,11 +2968,13 @@ $(document).ready(async () => { initVueUploader(); initObsUploader(); initVueEditAbout(); + initVueEditTopic(); initVueImages(); initTeamSettings(); initCtrlEnterSubmit(); initNavbarContentToggle(); - initTopicbar(); + // initTopicbar(); + // closeTopicbar(); initU2FAuth(); initU2FRegister(); initIssueList(); @@ -3666,7 +3669,19 @@ function initVueEditAbout() { }); } - + +function initVueEditTopic() { + const el = document.getElementById('topic_edit1'); + + if (!el) { + return; + } + + new Vue({ + el:'#topic_edit1', + render:h=>h(EditTopics) + }) +} function initVueImages() { const el = document.getElementById('images'); console.log("el",el) @@ -3677,6 +3692,7 @@ function initVueImages() { new Vue({ el: '#images', + render: h => h(Images) }); } @@ -3932,218 +3948,243 @@ function initNavbarContentToggle() { }); } -function initTopicbar() { - const mgrBtn = $('#manage_topic'); - const editDiv = $('#topic_edit'); - const viewDiv = $('#repo-topics'); - const saveBtn = $('#save_topic'); - const topicDropdown = $('#topic_edit .dropdown'); - const topicForm = $('#topic_edit.ui.form'); - const topicPrompts = getPrompts(); - - mgrBtn.on('click', () => { - viewDiv.hide(); - editDiv.css('display', ''); // show Semantic UI Grid - }); - - function getPrompts() { - const hidePrompt = $('div.hide#validate_prompt'); - const prompts = { - countPrompt: hidePrompt.children('#count_prompt').text(), - formatPrompt: hidePrompt.children('#format_prompt').text() - }; - hidePrompt.remove(); - return prompts; - } - - saveBtn.on('click', () => { - const topics = $('input[name=topics]').val(); - - $.post( - saveBtn.data('link'), - { - _csrf: csrf, - topics - }, - (_data, _textStatus, xhr) => { - if (xhr.responseJSON.status === 'ok') { - viewDiv.children('.topic').remove(); - if (topics.length) { - const topicArray = topics.split(','); - - const last = viewDiv.children('a').last(); - for (let i = 0; i < topicArray.length; i++) { - const link = $(''); - link.attr( - 'href', - `${AppSubUrl}/explore/repos?q=${encodeURIComponent( - topicArray[i] - )}&topic=1` - ); - link.text(topicArray[i]); - link.insertBefore(last); - } - } - editDiv.css('display', 'none'); - viewDiv.show(); - } - } - ) - .fail((xhr) => { - if (xhr.status === 422) { - if (xhr.responseJSON.invalidTopics.length > 0) { - topicPrompts.formatPrompt = xhr.responseJSON.message; - - const {invalidTopics} = xhr.responseJSON; - const topicLables = topicDropdown.children('a.ui.label'); - - topics.split(',').forEach((value, index) => { - for (let i = 0; i < invalidTopics.length; i++) { - if (invalidTopics[i] === value) { - topicLables - .eq(index) - .removeClass('green') - .addClass('red'); - } - } - }); - } else { - topicPrompts.countPrompt = xhr.responseJSON.message; - } - } - }) - .always(() => { - topicForm.form('validate form'); - }); - }); - - topicDropdown.dropdown({ - allowAdditions: true, - forceSelection: false, - fields: {name: 'description', value: 'data-value'}, - saveRemoteData: false, - label: { - transition: 'horizontal flip', - duration: 200, - variation: false, - blue: true, - basic: true - }, - className: { - label: 'ui small label' - }, - apiSettings: { - url: `${AppSubUrl}/api/v1/topics/search?q={query}`, - throttle: 500, - cache: false, - onResponse(res) { - const formattedResponse = { - success: false, - results: [] - }; - const stripTags = function (text) { - return text.replace(/<[^>]*>?/gm, ''); - }; - - const query = stripTags(this.urlData.query.trim()); - let found_query = false; - const current_topics = []; - topicDropdown - .find('div.label.visible.topic,a.label.visible') - .each((_, e) => { - current_topics.push(e.dataset.value); - }); - - if (res.topics) { - let found = false; - for (let i = 0; i < res.topics.length; i++) { - // skip currently added tags - if (current_topics.includes(res.topics[i].topic_name)) { - continue; - } - - if ( - res.topics[i].topic_name.toLowerCase() === query.toLowerCase() - ) { - found_query = true; - } - formattedResponse.results.push({ - description: res.topics[i].topic_name, - 'data-value': res.topics[i].topic_name - }); - found = true; - } - formattedResponse.success = found; - } - - if (query.length > 0 && !found_query) { - formattedResponse.success = true; - formattedResponse.results.unshift({ - description: query, - 'data-value': query - }); - } else if (query.length > 0 && found_query) { - formattedResponse.results.sort((a, b) => { - if (a.description.toLowerCase() === query.toLowerCase()) return -1; - if (b.description.toLowerCase() === query.toLowerCase()) return 1; - if (a.description > b.description) return -1; - if (a.description < b.description) return 1; - return 0; - }); - } - - return formattedResponse; - } - }, - onLabelCreate(value) { - value = value.toLowerCase().trim(); - this.attr('data-value', value) - .contents() - .first() - .replaceWith(value); - return $(this); - }, - onAdd(addedValue, _addedText, $addedChoice) { - addedValue = addedValue.toLowerCase().trim(); - $($addedChoice).attr('data-value', addedValue); - $($addedChoice).attr('data-text', addedValue); - } - }); - - $.fn.form.settings.rules.validateTopic = function (_values, regExp) { - const topics = topicDropdown.children('a.ui.label'); - const status = - topics.length === 0 || (topics.last().attr('data-value').match(regExp) !== null && topics.last().attr('data-value').length <= 35); - if (!status) { - topics - .last() - .removeClass('green') - .addClass('red'); - } - return status && topicDropdown.children('a.ui.label.red').length === 0; - }; - - topicForm.form({ - on: 'change', - inline: true, - fields: { - topics: { - identifier: 'topics', - rules: [ - { - type: 'validateTopic', - value: /^[\u4e00-\u9fa5a-z0-9][\u4e00-\u9fa5a-z0-9-]{0,105}$/, - prompt: topicPrompts.formatPrompt - }, - { - type: 'maxCount[25]', - prompt: topicPrompts.countPrompt - } - ] - } - } - }); -} +// function initTopicbar() { +// const mgrBtn = $('#manage_topic'); +// const editDiv = $('#topic_edit'); +// const viewDiv = $('#repo-topics'); +// const saveBtn = $('#save_topic'); +// const topicDropdown = $('#topic_edit .dropdown'); +// const topicForm = $('#topic_edit.ui.form'); +// const topicInput = $("#topics_input") +// const topicPrompts = getPrompts(); + + +// mgrBtn.on('click', (e) => { +// // viewDiv.hide(); +// editDiv.css('display', ''); // show Semantic UI Grid +// topicInput.val('') +// console.log("-----------------asdasd",$("#topics_input"),$("#topics_input").val()) +// stopPropagation(e); + +// }); +// $(document).bind('click',function(){ +// editDiv.css('display','none'); + +// }) +// editDiv.click(function(e){ +// stopPropagation(e); +// }) + +// function getPrompts() { +// const hidePrompt = $('div.hide#validate_prompt'); +// const prompts = { +// countPrompt: hidePrompt.children('#count_prompt').text(), +// formatPrompt: hidePrompt.children('#format_prompt').text() +// }; +// hidePrompt.remove(); +// return prompts; +// } + +// function stopPropagation(e) { +// var ev = e || window.event; +// if (ev.stopPropagation) { +// ev.stopPropagation(); +// } +// else if (window.event) { +// window.event.cancelBubble = true;//兼容IE +// } +// } + + +// saveBtn.on('click', () => { +// const topics = $('input[name=topics]').val(); + +// $.post( +// saveBtn.data('link'), +// { +// _csrf: csrf, +// topics +// }, +// (_data, _textStatus, xhr) => { +// if (xhr.responseJSON.status === 'ok') { +// console.log("--------saveBtn------------") +// viewDiv.children('.topic').remove(); +// if (topics.length) { +// const topicArray = topics.split(','); + +// const last = viewDiv.children('a').last(); +// for (let i = 0; i < topicArray.length; i++) { +// const link = $(''); +// link.attr( +// 'href', +// `${AppSubUrl}/explore/repos?q=${encodeURIComponent( +// topicArray[i] +// )}&topic=1` +// ); +// link.text(topicArray[i]); +// link.insertBefore(last); +// } +// } +// editDiv.css('display', 'none'); +// viewDiv.show(); +// } +// } +// ) +// .fail((xhr) => { +// if (xhr.status === 422) { +// if (xhr.responseJSON.invalidTopics.length > 0) { +// topicPrompts.formatPrompt = xhr.responseJSON.message; + +// const {invalidTopics} = xhr.responseJSON; +// const topicLables = topicDropdown.children('a.ui.label'); + +// topics.split(',').forEach((value, index) => { +// for (let i = 0; i < invalidTopics.length; i++) { +// if (invalidTopics[i] === value) { +// topicLables +// .eq(index) +// .removeClass('green') +// .addClass('red'); +// } +// } +// }); +// } else { +// topicPrompts.countPrompt = xhr.responseJSON.message; +// } +// } +// }) +// .always(() => { +// topicForm.form('validate form'); +// }); +// }); + +// topicDropdown.dropdown({ +// allowAdditions: true, +// forceSelection: false, +// fields: {name: 'description', value: 'data-value'}, +// saveRemoteData: false, +// label: { +// transition: 'horizontal flip', +// duration: 200, +// variation: false, +// blue: true, +// basic: true +// }, +// className: { +// label: 'ui small label' +// }, +// apiSettings: { +// url: `${AppSubUrl}/api/v1/topics/search?q={query}`, +// throttle: 500, +// cache: false, +// onResponse(res) { +// const formattedResponse = { +// success: false, +// results: [] +// }; +// const stripTags = function (text) { +// return text.replace(/<[^>]*>?/gm, ''); +// }; + +// const query = stripTags(this.urlData.query.trim()); +// let found_query = false; +// const current_topics = []; +// topicDropdown +// .find('div.label.visible.topic,a.label.visible') +// .each((_, e) => { +// current_topics.push(e.dataset.value); +// }); + +// if (res.topics) { +// let found = false; +// for (let i = 0; i < res.topics.length; i++) { +// // skip currently added tags +// if (current_topics.includes(res.topics[i].topic_name)) { +// continue; +// } + +// if ( +// res.topics[i].topic_name.toLowerCase() === query.toLowerCase() +// ) { +// found_query = true; +// } +// formattedResponse.results.push({ +// description: res.topics[i].topic_name, +// 'data-value': res.topics[i].topic_name +// }); +// found = true; +// } +// formattedResponse.success = found; +// } + +// if (query.length > 0 && !found_query) { +// formattedResponse.success = true; +// formattedResponse.results.unshift({ +// description: query, +// 'data-value': query +// }); +// } else if (query.length > 0 && found_query) { +// formattedResponse.results.sort((a, b) => { +// if (a.description.toLowerCase() === query.toLowerCase()) return -1; +// if (b.description.toLowerCase() === query.toLowerCase()) return 1; +// if (a.description > b.description) return -1; +// if (a.description < b.description) return 1; +// return 0; +// }); +// } + +// return formattedResponse; +// } +// }, +// onLabelCreate(value) { +// value = value.toLowerCase().trim(); +// this.attr('data-value', value) +// .contents() +// .first() +// .replaceWith(value); +// return $(this); +// }, +// onAdd(addedValue, _addedText, $addedChoice) { +// addedValue = addedValue.toLowerCase().trim(); +// $($addedChoice).attr('data-value', addedValue); +// $($addedChoice).attr('data-text', addedValue); +// } +// }); + +// $.fn.form.settings.rules.validateTopic = function (_values, regExp) { +// const topics = topicDropdown.children('a.ui.label'); +// const status = +// topics.length === 0 || (topics.last().attr('data-value').match(regExp) !== null && topics.last().attr('data-value').length <= 35); +// if (!status) { +// topics +// .last() +// .removeClass('green') +// .addClass('red'); +// } +// return status && topicDropdown.children('a.ui.label.red').length === 0; +// }; + +// topicForm.form({ +// on: 'change', +// inline: true, +// fields: { +// topics: { +// identifier: 'topics', +// rules: [ +// { +// type: 'validateTopic', +// value: /^[\u4e00-\u9fa5a-z0-9][\u4e00-\u9fa5a-z0-9-]{0,105}$/, +// prompt: topicPrompts.formatPrompt +// }, +// { +// type: 'maxCount[25]', +// prompt: topicPrompts.countPrompt +// } +// ] +// } +// } +// }); +// } window.toggleDeadlineForm = function () { $('#deadlineForm').fadeToggle(150); diff --git a/web_src/less/_dataset.less b/web_src/less/_dataset.less index 79acbf524..3bc19ef63 100644 --- a/web_src/less/_dataset.less +++ b/web_src/less/_dataset.less @@ -140,19 +140,32 @@ border: 1px solid #ffffff; } } - } + } } + .item { border-bottom: 1px solid rgba(34,36,38,.15); + .ui.buttons { + .button { + box-shadow: none !important; + } + } + } .ui.grid > .row { align-items: center; } + .title { font-size: 16px; font-weight: bold; - margin: 0 6px; + margin: 0 6px; + overflow: hidden; + padding-right: 15px; + white-space: nowrap; + text-overflow: ellipsis; + display: block; } .directory-seperator { padding: 0 4px; diff --git a/web_src/less/openi.less b/web_src/less/openi.less index 3c82606e9..cf8ca6d27 100644 --- a/web_src/less/openi.less +++ b/web_src/less/openi.less @@ -220,3 +220,25 @@ footer .column{margin-bottom:0!important; padding-bottom:0!important;} .ui.vertical.menu .dropdown.item .menu { left: 50%; } + +// icon cloudbrain +.i-round{display:inline-block;width:18px;height:18px;background:url("/img/icons.svg");background-position: -496px -52px;} +.i-bg-organ{background-position: -496px -52px;} +.STOPPED{display:inline-block;width:18px;height:18px;background:url("/img/icons.svg");background-position: -496px -52px;background-position: -459px -52px;} +.RUNNING{display:inline-block;width:18px;height:18px;background:url("/img/icons.svg");background-position: -496px -52px;background-position: -478px -52px;} +.i-bg-orange{background-position: -495px -51px;} +.FAILED{display:inline-block;width:18px;height:18px;background:url("/img/icons.svg");background-position: -496px -52px;background-position: -532px -52px;} +.i-bg-green{background-position: -441px -52px;} +.i-bg-used{background-position: -514px -52px;} +.icon-bind{background-position: -550px -52px;} +.icon-unbind{background-position: -568px -52px;} +.CREATING, .STOPPING, .DELETING, .STARTING, .WAITING{display:inline-block;background-image:url('/img/loading.gif');background-repeat:no-repeat;width:16px;height:16px;background-size:16px 16px;margin-right:5px;} +.text_over{ + overflow: hidden; + text-overflow: ellipsis; + -o-text-overflow: ellipsis; + white-space: nowrap; + display: inline-block; + width: 100%; +} +