| @@ -631,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 | |||
| } | |||
| @@ -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) { | |||
| @@ -4,10 +4,13 @@ import ( | |||
| "bufio" | |||
| "bytes" | |||
| "fmt" | |||
| "net/url" | |||
| "sort" | |||
| "strconv" | |||
| "strings" | |||
| "time" | |||
| Log "code.gitea.io/gitea/modules/log" | |||
| ) | |||
| type RepoKPIStats struct { | |||
| @@ -17,6 +20,7 @@ type RepoKPIStats struct { | |||
| ContributorsAdded int64 | |||
| CommitsAdded int64 | |||
| CommitLinesModified int64 | |||
| WikiPages int64 | |||
| Authors []*UserKPITypeStats | |||
| } | |||
| @@ -31,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) | |||
| @@ -76,6 +80,8 @@ func GetRepoKPIStats(repoPath string) (*RepoKPIStats, error) { | |||
| if err != nil { | |||
| return nil, fmt.Errorf("FillFromGit: %v", err) | |||
| } | |||
| setWikiPages(wikiPath, stats) | |||
| return stats, nil | |||
| } | |||
| @@ -282,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 | |||
| } | |||
| @@ -4,6 +4,7 @@ import ( | |||
| "bufio" | |||
| "encoding/json" | |||
| "errors" | |||
| "fmt" | |||
| "io" | |||
| "net/http" | |||
| "os" | |||
| @@ -392,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) | |||
| @@ -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) | |||
| @@ -352,7 +352,7 @@ | |||
| </div> | |||
| <!-- 删除镜像 --> | |||
| <!-- 删除任务 --> | |||
| <form class="ui compact buttons" id="delForm-{{.JobID}}" action="{{if not .CanDel}}javascript:void(0){{else}}{{$.Link}}/{{.JobID}}/del{{end}}" method="post"> | |||
| {{$.CsrfTokenHtml}} | |||
| <a class="ui compact {{if not .CanDel}}disabled {{else}}red {{end}}button" onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | |||
| @@ -312,9 +312,9 @@ | |||
| </div> | |||
| <!-- 删除任务 --> | |||
| <form class="ui compact buttons" id="delForm-{{.JobID}}" action="{{if ne .Status "STOPPED"}}javascript:void(0){{else}}{{$.Link}}/{{.JobID}}/del{{end}}" method="post"> | |||
| <form class="ui compact buttons" id="delForm-{{.JobID}}" action="{{if not .CanDel}}javascript:void(0){{else}}{{$.Link}}/{{.JobID}}/del{{end}}" method="post"> | |||
| {{$.CsrfTokenHtml}} | |||
| <a class="ui compact {{if ne .Status "STOPPED"}}disabled {{else}}red {{end}}button" onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | |||
| <a class="ui compact {{if not .CanDel}}disabled {{else}}red {{end}}button" onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | |||
| 删除 | |||
| </a> | |||
| </form> | |||