Browse Source

Merge branch 'V20211213' of https://git.openi.org.cn/OpenI/aiforge into liuzx_trainjob

tags/v1.21.12.1^2
liuzx 3 years ago
parent
commit
4e8394cefd
48 changed files with 822 additions and 243 deletions
  1. +56
    -2
      models/cloudbrain.go
  2. +2
    -6
      models/repo.go
  3. +16
    -5
      models/user.go
  4. +22
    -0
      modules/auth/auth.go
  5. +69
    -2
      modules/cloudbrain/cloudbrain.go
  6. +1
    -2
      modules/context/auth.go
  7. +1
    -0
      modules/context/context.go
  8. +2
    -2
      modules/context/repo.go
  9. +13
    -0
      modules/cron/tasks_basic.go
  10. +95
    -24
      modules/git/repo_branch.go
  11. +28
    -23
      modules/git/repo_tag.go
  12. +8
    -0
      modules/git/utils.go
  13. +56
    -6
      modules/migrations/github.go
  14. +5
    -4
      modules/repository/branch.go
  15. +21
    -3
      modules/repository/repo.go
  16. +21
    -18
      modules/setting/git.go
  17. +1
    -0
      modules/setting/indexer.go
  18. +6
    -0
      modules/setting/migrations.go
  19. +1
    -0
      modules/setting/queue.go
  20. +1
    -0
      modules/setting/webhook.go
  21. +3
    -0
      modules/storage/storage.go
  22. +1
    -0
      options/locale/locale_zh-CN.ini
  23. +1
    -0
      public/img/overview_rgb.svg
  24. +1
    -0
      public/img/pro_rgb.svg
  25. +1
    -0
      public/img/search.svg
  26. +1
    -0
      public/img/user_rgb.svg
  27. +12
    -8
      routers/api/v1/api.go
  28. +1
    -1
      routers/api/v1/repo/branch.go
  29. +4
    -3
      routers/api/v1/repo/repo_dashbord.go
  30. +6
    -0
      routers/init.go
  31. +1
    -1
      routers/repo/branch.go
  32. +110
    -10
      routers/repo/cloudbrain.go
  33. +2
    -2
      routers/repo/compare.go
  34. +2
    -2
      routers/repo/issue.go
  35. +63
    -24
      routers/repo/modelarts.go
  36. +1
    -1
      routers/repo/repo_statistic.go
  37. +18
    -16
      routers/routes/routes.go
  38. +1
    -1
      services/mirror/mirror.go
  39. +1
    -1
      services/pull/pull.go
  40. +54
    -22
      templates/base/head_navbar.tmpl
  41. +50
    -21
      templates/base/head_navbar_fluid.tmpl
  42. +29
    -20
      templates/base/head_navbar_home.tmpl
  43. +2
    -2
      templates/repo/modelarts/trainjob/new.tmpl
  44. +5
    -2
      templates/repo/modelarts/trainjob/show.tmpl
  45. +1
    -1
      templates/repo/modelarts/trainjob/version_new.tmpl
  46. +12
    -4
      web_src/js/components/DataAnalysis.vue
  47. +11
    -1
      web_src/js/components/ProAnalysis.vue
  48. +3
    -3
      web_src/js/components/UserAnalysis.vue

+ 56
- 2
models/cloudbrain.go View File

@@ -31,6 +31,7 @@ const (
JobTypeBrainScore JobType = "BRAINSCORE"
JobTypeTrain JobType = "TRAIN"

//notebook
ModelArtsCreateQueue ModelArtsJobStatus = "CREATE_QUEUING" //免费资源创建排队中
ModelArtsCreating ModelArtsJobStatus = "CREATING" //创建中
ModelArtsCreateFailed ModelArtsJobStatus = "CREATE_FAILED" //创建失败
@@ -46,6 +47,30 @@ const (
ModelArtsDeleted ModelArtsJobStatus = "DELETED" //已删除
ModelArtsResizing ModelArtsJobStatus = "RESIZING" //规格变更中
ModelArtsResizFailed ModelArtsJobStatus = "RESIZE_FAILED" //规格变更失败

//trainjob
ModelArtsTrainJobUnknown ModelArtsJobStatus = "UNKNOWN" //作业状态未知
ModelArtsTrainJobInit ModelArtsJobStatus = "INIT" //作业初始化状态
ModelArtsTrainJobImageCreating ModelArtsJobStatus = "IMAGE_CREATING" //作业镜像正在创建
ModelArtsTrainJobImageFailed ModelArtsJobStatus = "IMAGE_FAILED" //作业镜像创建失败
ModelArtsTrainJobSubmitTrying ModelArtsJobStatus = "SUBMIT_TRYING" //作业正在提交
ModelArtsTrainJobSubmitFailed ModelArtsJobStatus = "SUBMIT_FAILED" //作业提交失败
ModelArtsTrainJobDeleteFailed ModelArtsJobStatus = "DELETE_FAILED" //作业删除失败
ModelArtsTrainJobWaiting ModelArtsJobStatus = "WAITING" //作业正在排队中
ModelArtsTrainJobRunning ModelArtsJobStatus = "RUNNING" //作业正在运行中
ModelArtsTrainJobKilling ModelArtsJobStatus = "KILLING" //作业正在取消
ModelArtsTrainJobCompleted ModelArtsJobStatus = "COMPLETED" //作业已经完成
ModelArtsTrainJobFailed ModelArtsJobStatus = "FAILED" //作业运行失败
ModelArtsTrainJobKilled ModelArtsJobStatus = "KILLED" //作业取消成功
ModelArtsTrainJobCanceled ModelArtsJobStatus = "CANCELED" //作业取消
ModelArtsTrainJobLost ModelArtsJobStatus = "LOST" //作业丢失
ModelArtsTrainJobScaling ModelArtsJobStatus = "SCALING" //作业正在扩容
ModelArtsTrainJobSubmitModelFailed ModelArtsJobStatus = "SUBMIT_MODEL_FAILED" //提交模型失败
ModelArtsTrainJobDeployServiceFailed ModelArtsJobStatus = "DEPLOY_SERVICE_FAILED" //部署服务失败
ModelArtsTrainJobCheckInit ModelArtsJobStatus = "CHECK_INIT" //审核作业初始化
ModelArtsTrainJobCheckRunning ModelArtsJobStatus = "CHECK_RUNNING" //审核作业正在运行中
ModelArtsTrainJobCheckRunningCompleted ModelArtsJobStatus = "CHECK_RUNNING_COMPLETED" //审核作业已经完成
ModelArtsTrainJobCheckFailed ModelArtsJobStatus = "CHECK_FAILED" //审核作业失败
)

type Cloudbrain struct {
@@ -66,6 +91,7 @@ type Cloudbrain struct {
DeletedAt time.Time `xorm:"deleted"`
CanDebug bool `xorm:"-"`
CanDel bool `xorm:"-"`
CanModify bool `xorm:"-"`
Type int

VersionID int64 //版本id
@@ -1007,13 +1033,13 @@ func GetCloudbrainByJobIDAndIsLatestVersion(jobID string, isLatestVersion string

func GetCloudbrainsNeededStopByUserID(userID int64) ([]*Cloudbrain, error) {
cloudBrains := make([]*Cloudbrain, 0)
err := x.Cols("job_id", "status", "type").Where("user_id=? AND status !=?", userID, string(JobStopped)).Find(&cloudBrains)
err := x.Cols("job_id", "status", "type", "job_type", "version_id").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 !=?", repoID, string(JobStopped)).Find(&cloudBrains)
err := x.Cols("job_id", "status", "type", "job_type", "version_id").Where("repo_id=? AND status !=?", repoID, string(JobStopped)).Find(&cloudBrains)
return cloudBrains, err
}

@@ -1091,3 +1117,31 @@ func CanDelJob(isSigned bool, user *User, job *CloudbrainInfo) bool {
}
return false
}

func GetCloudBrainUnStoppedJob() ([]*Cloudbrain, error) {
cloudbrains := make([]*Cloudbrain, 0, 10)
return cloudbrains, x.
NotIn("status",
JobStopped, JobSucceeded, JobFailed, ModelArtsCreateFailed, ModelArtsStartFailed, ModelArtsUnavailable, ModelArtsResizFailed, ModelArtsDeleted,
ModelArtsStopped, ModelArtsTrainJobCanceled, ModelArtsTrainJobCheckFailed, ModelArtsTrainJobCompleted, ModelArtsTrainJobDeleteFailed, ModelArtsTrainJobDeployServiceFailed,
ModelArtsTrainJobFailed, ModelArtsTrainJobImageFailed, ModelArtsTrainJobKilled, ModelArtsTrainJobLost, ModelArtsTrainJobSubmitFailed, ModelArtsTrainJobSubmitModelFailed).
Limit(100).
Find(&cloudbrains)
}

func GetCloudbrainCountByUserID(userID int64) (int, error) {
count, err := x.In("status", JobWaiting, JobRunning).And("job_type = ? and user_id = ? and type = ?", JobTypeDebug, userID, TypeCloudBrainOne).Count(new(Cloudbrain))
return int(count), err
}

func GetCloudbrainNotebookCountByUserID(userID int64) (int, error) {
count, err := x.In("status", ModelArtsCreateQueue, ModelArtsCreating, ModelArtsStarting, ModelArtsReadyToStart, ModelArtsResizing, ModelArtsStartQueuing, ModelArtsRunning, ModelArtsRestarting).
And("job_type = ? and user_id = ? and type = ?", JobTypeDebug, userID, TypeCloudBrainTwo).Count(new(Cloudbrain))
return int(count), err
}

func GetCloudbrainTrainJobCountByUserID(userID int64) (int, error) {
count, err := x.In("status", ModelArtsTrainJobInit, ModelArtsTrainJobImageCreating, ModelArtsTrainJobSubmitTrying, ModelArtsTrainJobWaiting, ModelArtsTrainJobRunning, ModelArtsTrainJobScaling, ModelArtsTrainJobCheckInit, ModelArtsTrainJobCheckRunning, ModelArtsTrainJobCheckRunningCompleted).
And("job_type = ? and user_id = ? and type = ?", JobTypeTrain, userID, TypeCloudBrainTwo).Count(new(Cloudbrain))
return int(count), err
}

+ 2
- 6
models/repo.go View File

@@ -667,15 +667,11 @@ func (repo *Repository) getReviewersPublic(e Engine, doerID, posterID int64) (_
users := make([]*User, 0)

const SQLCmd = "SELECT * FROM `user` WHERE id IN ( " +
"SELECT user_id FROM `access` WHERE repo_id = ? AND mode >= ? AND user_id NOT IN ( ?, ?) " +
"UNION " +
"SELECT user_id FROM `watch` WHERE repo_id = ? AND user_id NOT IN ( ?, ?) AND mode IN (?, ?) " +
") ORDER BY name"
"SELECT user_id FROM `access` WHERE repo_id = ? AND mode >= ? AND user_id NOT IN ( ?, ?) ) ORDER BY name "

if err = e.
SQL(SQLCmd,
repo.ID, AccessModeRead, doerID, posterID,
repo.ID, doerID, posterID, RepoWatchModeNormal, RepoWatchModeAuto).
repo.ID, AccessModeWrite, doerID, posterID).
Find(&users); err != nil {
return nil, err
}


+ 16
- 5
models/user.go View File

@@ -145,6 +145,7 @@ type User struct {
AllowImportLocal bool // Allow migrate repository by local path
AllowCreateOrganization bool `xorm:"DEFAULT true"`
ProhibitLogin bool `xorm:"NOT NULL DEFAULT false"`
IsOperator bool `xorm:"NOT NULL DEFAULT false"` //运营人员

// Avatar
Avatar string `xorm:"VARCHAR(2048) NOT NULL"`
@@ -928,8 +929,17 @@ var (
"template",
"user",
"vendor",
}
reservedUserPatterns = []string{"*.keys", "*.gpg"}
"dashbord",
"operation",
"blockchain",
"avatar",
"swagger.v1.json",
"secure",
"serviceworker.js",
"self",
"repo-avatars",
}
reservedUserPatterns = []string{"*.keys", "*.gpg", "*.png"}
)

// isUsableName checks if name is reserved or pattern of name is not allowed
@@ -1551,11 +1561,11 @@ func GetUserByActivateEmail(email string) (*User, error) {
if err := ctx.e.Join("INNER", "email_address", "email_address.uid = \"user\".id").
Where("email_address.email= ?", email).
Find(&users); err != nil {
return nil,err
return nil, err
}
if len(users) >= 1 {
return &users[0],nil
}else {
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))
@@ -1571,6 +1581,7 @@ func GetUserByActivateEmail(email string) (*User, error) {
return nil, errors.New("cannot find user by email")
}
}

// GetUserByEmail returns the user object by given e-mail if exists.
func GetUserByEmail(email string) (*User, error) {
return GetUserByEmailContext(DefaultDBContext(), email)


+ 22
- 0
modules/auth/auth.go View File

@@ -9,6 +9,9 @@ import (
"reflect"
"strings"

"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/setting"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth/sso"
"code.gitea.io/gitea/modules/validation"
@@ -31,6 +34,8 @@ func SignedInUser(ctx *macaron.Context, sess session.Store) (*models.User, bool)
return nil, false
}

checkAutoLogin(ctx, sess)

// Try to sign in with each of the enabled plugins
for _, ssoMethod := range sso.Methods() {
if !ssoMethod.IsEnabled() {
@@ -46,6 +51,23 @@ func SignedInUser(ctx *macaron.Context, sess session.Store) (*models.User, bool)
return nil, false
}

func checkAutoLogin(ctx *macaron.Context, sess session.Store) {
uid := sess.Get("uid")
if uid == nil {
uname := ctx.GetCookie(setting.CookieUserName)

u, err := models.GetUserByName(uname)
if err == nil {

if val, ok := ctx.GetSuperSecureCookie(
base.EncodeMD5(u.Rands+u.Passwd), setting.CookieRememberName); ok && val == u.Name {
sess.Set("uid", u.ID)
}
}
}

}

// Form form binding interface
type Form interface {
binding.Validator


+ 69
- 2
modules/cloudbrain/cloudbrain.go View File

@@ -1,9 +1,10 @@
package cloudbrain

import (
"code.gitea.io/gitea/modules/setting"
"errors"

"code.gitea.io/gitea/modules/setting"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
@@ -16,7 +17,7 @@ const (
ModelMountPath = "/model"
BenchMarkMountPath = "/benchmark"
Snn4imagenetMountPath = "/snn4imagenet"
BrainScoreMountPath = "/brainscore"
BrainScoreMountPath = "/brainscore"
TaskInfoName = "/taskInfo"

SubTaskName = "task1"
@@ -28,6 +29,72 @@ var (
ResourceSpecs *models.ResourceSpecs
)

func isAdminOrOwnerOrJobCreater(ctx *context.Context, job *models.Cloudbrain, err error) bool {

if err != nil {
return ctx.IsUserRepoOwner() || ctx.IsUserSiteAdmin()
} else {
return ctx.IsUserRepoOwner() || ctx.IsUserSiteAdmin() || ctx.User.ID == job.UserID
}

}

func CanDeleteDebugJob(ctx *context.Context, job *models.Cloudbrain) bool {

if job.Status != string(models.JobStopped) && job.Status != string(models.JobFailed) && job.Status != string(models.ModelArtsStartFailed) && job.Status != string(models.ModelArtsCreateFailed) {
return false
}
return isAdminOrOwnerOrJobCreater(ctx, job, nil)
}

func CanDeleteTrainJob(ctx *context.Context, job *models.Cloudbrain) bool {

return isAdminOrOwnerOrJobCreater(ctx, job, nil)
}

func CanCreateOrDebugJob(ctx *context.Context) bool {
return ctx.Repo.CanWrite(models.UnitTypeCloudBrain)
}

func CanModifyJob(ctx *context.Context, job *models.Cloudbrain) bool {

return isAdminOrJobCreater(ctx, job, nil)
}

func isAdminOrJobCreater(ctx *context.Context, job *models.Cloudbrain, err error) bool {

if err != nil {
return ctx.IsUserSiteAdmin()
} else {
return ctx.IsUserSiteAdmin() || ctx.User.ID == job.UserID
}

}

func AdminOrOwnerOrJobCreaterRight(ctx *context.Context) {

var jobID = ctx.Params(":jobid")

job, err := models.GetCloudbrainByJobID(jobID)

if !isAdminOrOwnerOrJobCreater(ctx, job, err) {

ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
}

}

func AdminOrJobCreaterRight(ctx *context.Context) {

var jobID = ctx.Params(":jobid")
job, err := models.GetCloudbrainByJobID(jobID)
if !isAdminOrJobCreater(ctx, job, err) {

ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
}

}

func GenerateTask(ctx *context.Context, jobName, image, command, uuid, codePath, modelPath, benchmarkPath, snn4imagenetPath, brainScorePath, jobType, gpuQueue string, resourceSpecId int) error {
dataActualPath := setting.Attachment.Minio.RealPath +
setting.Attachment.Minio.Bucket + "/" +


+ 1
- 2
modules/context/auth.go View File

@@ -145,8 +145,7 @@ func Toggle(options *ToggleOptions) macaron.Handler {
}

if options.OperationRequired {
//todo: add isOperator judgement
if !ctx.User.IsAdmin {
if !ctx.User.IsOperator {
ctx.Error(403)
return
}


+ 1
- 0
modules/context/context.go View File

@@ -310,6 +310,7 @@ func Contexter() macaron.Handler {
ctx.Data["SignedUserID"] = ctx.User.ID
ctx.Data["SignedUserName"] = ctx.User.Name
ctx.Data["IsAdmin"] = ctx.User.IsAdmin
ctx.Data["IsOperator"] = ctx.User.IsOperator
c.Data["SignedUserName"] = ctx.User.Name
} else {
ctx.Data["SignedUserID"] = int64(0)


+ 2
- 2
modules/context/repo.go View File

@@ -524,7 +524,7 @@ func RepoAssignment() macaron.Handler {
}
ctx.Data["Tags"] = tags

brs, err := ctx.Repo.GitRepo.GetBranches()
brs, _, err := ctx.Repo.GitRepo.GetBranches(0, 0)
if err != nil {
ctx.ServerError("GetBranches", err)
return
@@ -712,7 +712,7 @@ func RepoRefByType(refType RepoRefType) macaron.Handler {
refName = ctx.Repo.Repository.DefaultBranch
ctx.Repo.BranchName = refName
if !ctx.Repo.GitRepo.IsBranchExist(refName) {
brs, err := ctx.Repo.GitRepo.GetBranches()
brs, _, err := ctx.Repo.GitRepo.GetBranches(0, 0)
if err != nil {
ctx.ServerError("GetBranches", err)
return


+ 13
- 0
modules/cron/tasks_basic.go View File

@@ -185,6 +185,17 @@ func registerHandleSummaryStatistic() {
})
}

func registerSyncCloudbrainStatus() {
RegisterTaskFatal("sync_cloudbrain_status", &BaseConfig{
Enabled: true,
RunAtStart: false,
Schedule: "@every 10m",
}, func(ctx context.Context, _ *models.User, _ Config) error {
repo.SyncCloudbrainStatus()
return nil
})
}

func initBasicTasks() {
registerUpdateMirrorTask()
registerRepoHealthCheck()
@@ -202,4 +213,6 @@ func initBasicTasks() {

registerHandleRepoAndUserStatistic()
registerHandleSummaryStatistic()

registerSyncCloudbrainStatus()
}

+ 95
- 24
modules/git/repo_branch.go View File

@@ -6,7 +6,9 @@
package git

import (
"bufio"
"fmt"
"io"
"strings"

"github.com/go-git/go-git/v5/plumbing"
@@ -74,25 +76,6 @@ func (repo *Repository) SetDefaultBranch(name string) error {
return err
}

// GetBranches returns all branches of the repository.
func (repo *Repository) GetBranches() ([]string, error) {
var branchNames []string

branches, err := repo.gogitRepo.Branches()
if err != nil {
return nil, err
}

_ = branches.ForEach(func(branch *plumbing.Reference) error {
branchNames = append(branchNames, strings.TrimPrefix(branch.Name().String(), BranchPrefix))
return nil
})

// TODO: Sort?

return branchNames, nil
}

// GetBranch returns a branch by it's name
func (repo *Repository) GetBranch(branch string) (*Branch, error) {
if !repo.IsBranchExist(branch) {
@@ -106,16 +89,16 @@ func (repo *Repository) GetBranch(branch string) (*Branch, error) {
}

// GetBranchesByPath returns a branch by it's path
func GetBranchesByPath(path string) ([]*Branch, error) {
func GetBranchesByPath(path string, skip, limit int) ([]*Branch, int, error) {
gitRepo, err := OpenRepository(path)
if err != nil {
return nil, err
return nil, 0, err
}
defer gitRepo.Close()

brs, err := gitRepo.GetBranches()
brs, countAll, err := gitRepo.GetBranches(skip, limit)
if err != nil {
return nil, err
return nil, 0, err
}

branches := make([]*Branch, len(brs))
@@ -127,7 +110,7 @@ func GetBranchesByPath(path string) ([]*Branch, error) {
}
}

return branches, nil
return branches, countAll, nil
}

// DeleteBranchOptions Option(s) for delete branch
@@ -183,3 +166,91 @@ func (repo *Repository) RemoveRemote(name string) error {
func (branch *Branch) GetCommit() (*Commit, error) {
return branch.gitRepo.GetBranchCommit(branch.Name)
}

// GetBranches returns branches from the repository, skipping skip initial branches and
// returning at most limit branches, or all branches if limit is 0.
func (repo *Repository) GetBranches(skip, limit int) ([]string, int, error) {
return callShowRef(repo.Path, BranchPrefix, "--heads", skip, limit)
}

// callShowRef return refs, if limit = 0 it will not limit
func callShowRef(repoPath, prefix, arg string, skip, limit int) (branchNames []string, countAll int, err error) {
stdoutReader, stdoutWriter := io.Pipe()
defer func() {
_ = stdoutReader.Close()
_ = stdoutWriter.Close()
}()

go func() {
stderrBuilder := &strings.Builder{}
err := NewCommand("show-ref", arg).RunInDirPipeline(repoPath, stdoutWriter, stderrBuilder)
if err != nil {
if stderrBuilder.Len() == 0 {
_ = stdoutWriter.Close()
return
}
_ = stdoutWriter.CloseWithError(ConcatenateError(err, stderrBuilder.String()))
} else {
_ = stdoutWriter.Close()
}
}()

i := 0
bufReader := bufio.NewReader(stdoutReader)
for i < skip {
_, isPrefix, err := bufReader.ReadLine()
if err == io.EOF {
return branchNames, i, nil
}
if err != nil {
return nil, 0, err
}
if !isPrefix {
i++
}
}
for limit == 0 || i < skip+limit {
// The output of show-ref is simply a list:
// <sha> SP <ref> LF
_, err := bufReader.ReadSlice(' ')
for err == bufio.ErrBufferFull {
// This shouldn't happen but we'll tolerate it for the sake of peace
_, err = bufReader.ReadSlice(' ')
}
if err == io.EOF {
return branchNames, i, nil
}
if err != nil {
return nil, 0, err
}

branchName, err := bufReader.ReadString('\n')
if err == io.EOF {
// This shouldn't happen... but we'll tolerate it for the sake of peace
return branchNames, i, nil
}
if err != nil {
return nil, i, err
}
branchName = strings.TrimPrefix(branchName, prefix)
if len(branchName) > 0 {
branchName = branchName[:len(branchName)-1]
}
branchNames = append(branchNames, branchName)
i++
}
// count all refs
for limit != 0 {
_, isPrefix, err := bufReader.ReadLine()
if err == io.EOF {
return branchNames, i, nil
}
if err != nil {
return nil, 0, err
}
if !isPrefix {
i++
}
}
return branchNames, i, nil
}

+ 28
- 23
modules/git/repo_tag.go View File

@@ -10,7 +10,6 @@ import (
"strings"

"github.com/go-git/go-git/v5/plumbing"
"github.com/mcuadros/go-version"
)

// TagPrefix tags prefix path on the repository
@@ -225,29 +224,35 @@ func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, error) {
return tags, nil
}

// GetTags returns all tags of the repository.
func (repo *Repository) GetTags() ([]string, error) {
var tagNames []string

tags, err := repo.gogitRepo.Tags()
if err != nil {
return nil, err
}
//// GetTags returns all tags of the repository.
//func (repo *Repository) GetTags() ([]string, error) {
// var tagNames []string
//
// tags, err := repo.gogitRepo.Tags()
// if err != nil {
// return nil, err
// }
//
// _ = tags.ForEach(func(tag *plumbing.Reference) error {
// tagNames = append(tagNames, strings.TrimPrefix(tag.Name().String(), TagPrefix))
// return nil
// })
//
// version.Sort(tagNames)
//
// // Reverse order
// for i := 0; i < len(tagNames)/2; i++ {
// j := len(tagNames) - i - 1
// tagNames[i], tagNames[j] = tagNames[j], tagNames[i]
// }
//
// return tagNames, nil
//}

_ = tags.ForEach(func(tag *plumbing.Reference) error {
tagNames = append(tagNames, strings.TrimPrefix(tag.Name().String(), TagPrefix))
return nil
})

version.Sort(tagNames)

// Reverse order
for i := 0; i < len(tagNames)/2; i++ {
j := len(tagNames) - i - 1
tagNames[i], tagNames[j] = tagNames[j], tagNames[i]
}

return tagNames, nil
// GetTags returns all tags of the repository.
func (repo *Repository) GetTags() (tags []string, err error) {
tags, _, err = callShowRef(repo.Path, TagPrefix, "--tags", 0, 0)
return
}

// GetTagType gets the type of the tag, either commit (simple) or tag (annotated)


+ 8
- 0
modules/git/utils.go View File

@@ -140,3 +140,11 @@ func ParseBool(value string) (result bool, valid bool) {
}
return intValue != 0, true
}

// ConcatenateError concatenats an error with stderr string
func ConcatenateError(err error, stderr string) error {
if len(stderr) == 0 {
return err
}
return fmt.Errorf("%w - %s", err, stderr)
}

+ 56
- 6
modules/migrations/github.go View File

@@ -13,6 +13,9 @@ import (
"strings"
"time"

"code.gitea.io/gitea/modules/setting"
"golang.org/x/net/proxy"

"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/migrations/base"
"code.gitea.io/gitea/modules/structs"
@@ -98,13 +101,41 @@ func NewGithubDownloaderV3(userName, password, repoOwner, repoName string) *Gith
)
client = oauth2.NewClient(downloader.ctx, ts)
} else {
client = &http.Client{
Transport: &http.Transport{
Proxy: func(req *http.Request) (*url.URL, error) {
req.SetBasicAuth(userName, password)
return nil, nil
if setting.Migrations.Proxy != "" {
contextDialer, err := getProxyDialContext()
if err != nil {
log.Warn("Failed to use proxy for Github.", err)
client = &http.Client{
Transport: &http.Transport{

Proxy: func(req *http.Request) (*url.URL, error) {
req.SetBasicAuth(userName, password)
return nil, nil
},
},
}
} else {
client = &http.Client{
Transport: &http.Transport{

Proxy: func(req *http.Request) (*url.URL, error) {
req.SetBasicAuth(userName, password)
return nil, nil
},
DialContext: contextDialer.DialContext,
},
}
}
} else {
client = &http.Client{
Transport: &http.Transport{

Proxy: func(req *http.Request) (*url.URL, error) {
req.SetBasicAuth(userName, password)
return nil, nil
},
},
},
}
}
}
}
@@ -112,6 +143,25 @@ func NewGithubDownloaderV3(userName, password, repoOwner, repoName string) *Gith
return &downloader
}

func getProxyDialContext() (proxy.ContextDialer, error) {
authInfo := &proxy.Auth{
setting.Migrations.Username,
setting.Migrations.Password,
}
dialSocksProxy, err := proxy.SOCKS5("tcp", setting.Migrations.Proxy, authInfo, proxy.Direct)

if err != nil {
return nil, err
}
if contextDialer, ok := dialSocksProxy.(proxy.ContextDialer); ok {
return contextDialer, nil

} else {
return nil, fmt.Errorf("It is not a valiad dialer.")
}

}

// SetContext set context
func (g *GithubDownloaderV3) SetContext(ctx context.Context) {
g.ctx = ctx


+ 5
- 4
modules/repository/branch.go View File

@@ -23,9 +23,10 @@ func GetBranch(repo *models.Repository, branch string) (*git.Branch, error) {
return gitRepo.GetBranch(branch)
}

// GetBranches returns all the branches of a repository
func GetBranches(repo *models.Repository) ([]*git.Branch, error) {
return git.GetBranchesByPath(repo.RepoPath())
// GetBranches returns branches from the repository, skipping skip initial branches and
// returning at most limit branches, or all branches if limit is 0.
func GetBranches(repo *models.Repository, skip, limit int) ([]*git.Branch, int, error) {
return git.GetBranchesByPath(repo.RepoPath(), skip, limit)
}

// checkBranchName validates branch name with existing repository branches
@@ -36,7 +37,7 @@ func checkBranchName(repo *models.Repository, name string) error {
}
defer gitRepo.Close()

branches, err := GetBranches(repo)
branches, _, err := GetBranches(repo, 0, 0)
if err != nil {
return err
}


+ 21
- 3
modules/repository/repo.go View File

@@ -6,6 +6,7 @@ package repository

import (
"fmt"
"net/url"
"os"
"path"
"strings"
@@ -54,29 +55,34 @@ func MigrateRepositoryGitData(doer, u *models.User, repo *models.Repository, opt
repo.NumWatches = 1
}

migrateTimeout := time.Duration(setting.Git.Timeout.Migrate) * time.Second
migrateTimeout := getMigrateTimeout(opts.CloneAddr)

var err error
if err = os.RemoveAll(repoPath); err != nil {
return repo, fmt.Errorf("Failed to remove %s: %v", repoPath, err)
}
log.Info("clone begin:" + opts.CloneAddr)
if err = git.Clone(opts.CloneAddr, repoPath, git.CloneRepoOptions{
Mirror: true,
Quiet: true,
Timeout: migrateTimeout,
}); err != nil {
log.Warn("clone err")
return repo, fmt.Errorf("Clone: %v", err)
}

log.Info("clone end:" + opts.CloneAddr)

if opts.Wiki {
log.Info("test wiki path begin")
wikiPath := models.WikiPath(u.Name, opts.RepoName)
wikiRemotePath := wikiRemoteURL(opts.CloneAddr)
log.Info("test wiki path end")
if len(wikiRemotePath) > 0 {
if err := os.RemoveAll(wikiPath); err != nil {
return repo, fmt.Errorf("Failed to remove %s: %v", wikiPath, err)
}
log.Info("wiki clone begin")
if err = git.Clone(wikiRemotePath, wikiPath, git.CloneRepoOptions{
Mirror: true,
Quiet: true,
@@ -88,6 +94,7 @@ func MigrateRepositoryGitData(doer, u *models.User, repo *models.Repository, opt
return repo, fmt.Errorf("Failed to remove %s: %v", wikiPath, err)
}
}
log.Info("wiki clone end")
}
}

@@ -137,9 +144,20 @@ func MigrateRepositoryGitData(doer, u *models.User, repo *models.Repository, opt
repo, err = CleanUpMigrateInfo(repo)
}

log.Info("clone all end:" + opts.CloneAddr)

return repo, err
}

func getMigrateTimeout(urlClone string) time.Duration {
u, err := url.Parse(urlClone)
if err == nil && strings.EqualFold(u.Host, "github.com") {
return time.Duration(setting.Git.Timeout.GitHubMigrate) * time.Second
}
return time.Duration(setting.Git.Timeout.Migrate) * time.Second

}

// cleanUpMigrateGitConfig removes mirror info which prevents "push --all".
// This also removes possible user credentials.
func cleanUpMigrateGitConfig(configPath string) error {


+ 21
- 18
modules/setting/git.go View File

@@ -27,12 +27,13 @@ var (
EnableAutoGitWireProtocol bool
PullRequestPushMessage bool
Timeout struct {
Default int
Migrate int
Mirror int
Clone int
Pull int
GC int `ini:"GC"`
Default int
Migrate int
GitHubMigrate int
Mirror int
Clone int
Pull int
GC int `ini:"GC"`
} `ini:"git.timeout"`
}{
DisableDiffHighlight: false,
@@ -45,19 +46,21 @@ var (
EnableAutoGitWireProtocol: true,
PullRequestPushMessage: true,
Timeout: struct {
Default int
Migrate int
Mirror int
Clone int
Pull int
GC int `ini:"GC"`
Default int
Migrate int
GitHubMigrate int
Mirror int
Clone int
Pull int
GC int `ini:"GC"`
}{
Default: int(git.DefaultCommandExecutionTimeout / time.Second),
Migrate: 600,
Mirror: 300,
Clone: 300,
Pull: 300,
GC: 60,
Default: int(git.DefaultCommandExecutionTimeout / time.Second),
Migrate: 900,
GitHubMigrate: 1800,
Mirror: 1200,
Clone: 300,
Pull: 300,
GC: 60,
},
}
)


+ 1
- 0
modules/setting/indexer.go View File

@@ -83,6 +83,7 @@ func newIndexerService() {
Indexer.UpdateQueueLength = sec.Key("UPDATE_BUFFER_LEN").MustInt(20)
Indexer.MaxIndexerFileSize = sec.Key("MAX_FILE_SIZE").MustInt64(1024 * 1024)
Indexer.StartupTimeout = sec.Key("STARTUP_TIMEOUT").MustDuration(30 * time.Second)
log.Info("New IndexerService Inited.")
}

// IndexerGlobFromString parses a comma separated list of patterns and returns a glob.Glob slice suited for repo indexing


+ 6
- 0
modules/setting/migrations.go View File

@@ -9,6 +9,9 @@ var (
Migrations = struct {
MaxAttempts int
RetryBackoff int
Proxy string
Username string
Password string
}{
MaxAttempts: 3,
RetryBackoff: 3,
@@ -19,4 +22,7 @@ func newMigrationsService() {
sec := Cfg.Section("migrations")
Migrations.MaxAttempts = sec.Key("MAX_ATTEMPTS").MustInt(Migrations.MaxAttempts)
Migrations.RetryBackoff = sec.Key("RETRY_BACKOFF").MustInt(Migrations.RetryBackoff)
Migrations.Proxy = sec.Key("Proxy").MustString("")
Migrations.Username = sec.Key("Username").MustString("")
Migrations.Password = sec.Key("Password").MustString("")
}

+ 1
- 0
modules/setting/queue.go View File

@@ -160,6 +160,7 @@ func NewQueueService() {
if _, ok := sectionMap["LENGTH"]; !ok {
_, _ = section.NewKey("LENGTH", fmt.Sprintf("%d", Repository.PullRequestQueueLength))
}
log.Info("New QueueService Inited.")
}

// ParseQueueConnStr parses a queue connection string


+ 1
- 0
modules/setting/webhook.go View File

@@ -48,4 +48,5 @@ func newWebhookService() {
}
}
Webhook.ProxyHosts = sec.Key("PROXY_HOSTS").Strings(",")
log.Info("New WebhookService Inited.")
}

+ 3
- 0
modules/storage/storage.go View File

@@ -51,6 +51,7 @@ func Init() error {
switch setting.Attachment.StoreType {
case LocalStorageType:
Attachments, err = NewLocalStorage(setting.Attachment.Path)
log.Info("local storage inited.")
case MinioStorageType:
minio := setting.Attachment.Minio
Attachments, err = NewMinioStorage(
@@ -62,6 +63,7 @@ func Init() error {
minio.BasePath,
minio.UseSSL,
)
log.Info("minio storage inited.")
default:
return fmt.Errorf("Unsupported attachment store type: %s", setting.Attachment.StoreType)
}
@@ -71,6 +73,7 @@ func Init() error {
log.Error("obs.New failed:", err)
return err
}
log.Info("obs cli inited.")

if err != nil {
return err


+ 1
- 0
options/locale/locale_zh-CN.ini View File

@@ -228,6 +228,7 @@ users=用户
organizations=组织
images = 云脑镜像
search=搜索
search_pro=搜项目
code=代码
data_analysis=数字看板(内测)
repo_no_results=未找到匹配的项目。


+ 1
- 0
public/img/overview_rgb.svg View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1638325192035" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2222" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M979.792374 404.577188 574.183101 83.942886c-34.918864-27.694272-89.619352-27.694272-124.538216 0L44.207626 404.577188c-13.933143 11.008903-16.169326 31.134554-5.332437 44.895683s30.618512 16.169326 44.551655 5.332437l12.55703-10.320847 0 387.547791c0 54.872501 57.968755 95.983874 108.712918 95.983874l639.892491 0c50.22812 0 83.254829-38.531161 83.254829-95.983874L927.844112 445.860575l11.69696 8.944734c5.84848 4.644381 13.073072 6.880564 20.125651 6.880564 9.460776 0 18.921552-4.128339 25.286074-12.213002C995.9617 435.711742 993.725517 415.586091 979.792374 404.577188zM479.919368 864.026877 479.919368 686.508315c0-8.77272 15.997312-13.245087 31.994625-13.245087s31.994625 4.472367 31.994625 13.245087l0 177.346548L479.919368 864.026877 479.919368 864.026877zM864.026877 832.032253c0 21.157736-5.84848 31.994625-19.26558 31.994625L608.585923 864.026877c0-0.516042-0.688056-0.860071-0.688056-1.376113L607.897867 686.508315c0-37.155048-29.930455-77.234336-95.983874-77.234336s-95.983874 40.079288-95.983874 77.234336l0 176.142449c0 0.516042 0.860071 0.860071 0.860071 1.376113L204.868806 864.026877c-20.125651 0-44.723669-17.373425-44.723669-31.994625L160.145137 393.740299 488.864102 134.171006c11.868974-9.288762 33.198723-9.288762 44.895683 0l330.095078 261.11742L863.854863 832.032253z" p-id="2223" fill="#1684FC"></path></svg>

+ 1
- 0
public/img/pro_rgb.svg View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1638171446718" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2430" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M512 69.717333l383.018667 221.141334v442.282666L512 954.282667 128.981333 733.141333V290.858667L512 69.717333zM192.96 375.402667v320.768L480 861.888V541.141333l-287.04-165.76z m638.058667 0L544 541.162667V861.866667l287.018667-165.717334V375.424zM512 143.637333L215.722667 314.666667 512 485.717333l296.256-171.050666L512 143.616z" fill="#1684FC" p-id="2431"></path></svg>

+ 1
- 0
public/img/search.svg View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1638433773401" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2884" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M469.333333 768c-166.4 0-298.666667-132.266667-298.666666-298.666667s132.266667-298.666667 298.666666-298.666666 298.666667 132.266667 298.666667 298.666666-132.266667 298.666667-298.666667 298.666667z m0-85.333333c119.466667 0 213.333333-93.866667 213.333334-213.333334s-93.866667-213.333333-213.333334-213.333333-213.333333 93.866667-213.333333 213.333333 93.866667 213.333333 213.333333 213.333334z m251.733334 0l119.466666 119.466666-59.733333 59.733334-119.466667-119.466667 59.733334-59.733333z" fill="#5BB973" p-id="2885"></path></svg>

+ 1
- 0
public/img/user_rgb.svg View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1638171175505" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2087" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M947.924 963.76c0 21.752-17.62 39.372-39.354 39.372-21.75 0-39.37-17.62-39.37-39.371l-0.354-3.655c0-1.554 0.283-3.054 0.442-4.59-24.576-172.792-172.72-299.768-353.104-301.763-1.447 0.036-2.825 0.23-4.29 0.23-1.43 0-2.807-0.194-4.237-0.23C329.34 655.731 182.607 779.9 155.56 949.69c0.9 3.337 1.536 6.78 1.536 10.4l0.37 3.707c0 22.086-17.902 39.989-39.97 39.989s-39.99-17.903-39.99-39.99l-0.035-0.352h-0.76c0.107-1.165 0.336-2.278 0.46-3.425 0-3.302 0.53-6.427 1.27-9.499C98.94 791.217 205.684 662.422 350.968 606.95c-84.586-54.2-139.74-149.963-139.74-259.372 0-169.224 131.478-306.405 300.702-306.405 169.208 0 300.703 137.18 300.703 306.405 0 109.374-55.102 205.118-139.652 259.32 144.278 55.03 250.544 182.325 272.19 340.038 1.465 4.131 2.436 8.528 2.436 13.17l0.318 3.656z m-204.43-616.094c0-127.947-103.706-231.654-231.653-231.654S280.188 219.736 280.188 347.666c0 127.929 103.724 231.636 231.653 231.636s231.654-103.707 231.654-231.636z" fill="#1684FC" p-id="2088"></path></svg>

+ 12
- 8
routers/api/v1/api.go View File

@@ -75,6 +75,7 @@ import (
"code.gitea.io/gitea/routers/api/v1/repo"
_ "code.gitea.io/gitea/routers/api/v1/swagger" // for swagger generation
"code.gitea.io/gitea/routers/api/v1/user"
repo_ext "code.gitea.io/gitea/routers/repo"

"gitea.com/macaron/binding"
"gitea.com/macaron/macaron"
@@ -523,23 +524,26 @@ func RegisterRoutes(m *macaron.Macaron) {
Get(notify.GetThread).
Patch(notify.ReadThread)
}, reqToken())
adminReq := context.Toggle(&context.ToggleOptions{SignInRequired: true, AdminRequired: true})
operationReq := context.Toggle(&context.ToggleOptions{SignInRequired: true, OperationRequired: true})
//Project board
m.Group("/projectboard", func() {

m.Get("/restoreFork", adminReq, repo.RestoreForkNumber)
m.Get("/downloadAll", adminReq, repo.ServeAllProjectsPeriodStatisticsFile)
m.Get("/downloadAllOpenI", adminReq, repo.ServeAllProjectsOpenIStatisticsFile)
m.Get("/restoreFork", repo.RestoreForkNumber)
m.Get("/downloadAll", repo.ServeAllProjectsPeriodStatisticsFile)
m.Get("/downloadAllOpenI", repo.ServeAllProjectsOpenIStatisticsFile)
m.Group("/project", func() {
m.Get("", adminReq, repo.GetAllProjectsPeriodStatistics)
m.Get("", repo.GetAllProjectsPeriodStatistics)

m.Group("/:id", func() {
m.Get("", adminReq, repo.GetProjectLatestStatistics)
m.Get("/period", adminReq, repo.GetProjectPeriodStatistics)
m.Get("", repo.GetProjectLatestStatistics)
m.Get("/period", repo.GetProjectPeriodStatistics)

})
})
})
}, operationReq)

m.Get("/query_user_static_page", operationReq, repo_ext.QueryUserStaticDataPage)

// Users
m.Group("/users", func() {


+ 1
- 1
routers/api/v1/repo/branch.go View File

@@ -204,7 +204,7 @@ func ListBranches(ctx *context.APIContext) {
// "200":
// "$ref": "#/responses/BranchList"

branches, err := repo_module.GetBranches(ctx.Repo.Repository)
branches, _, err := repo_module.GetBranches(ctx.Repo.Repository,0,0)
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetBranches", err)
return


+ 4
- 3
routers/api/v1/repo/repo_dashbord.go View File

@@ -5,6 +5,7 @@ import (
"net/http"
"net/url"
"strconv"
"strings"
"time"

"github.com/360EntSecGroup-Skylar/excelize/v2"
@@ -467,7 +468,7 @@ func generateCountSql(beginTime time.Time, endTime time.Time, latestDate string,
"(SELECT repo_id,name,is_private,radar_total from public.repo_statistic where date='" + latestDate + "') B" +
" where A.repo_id=B.repo_id"
if q != "" {
countSql = countSql + " and B.name like '%" + q + "%'"
countSql = countSql + " and LOWER(B.name) like '%" + strings.ToLower(q) + "%'"
}
return countSql
}
@@ -488,7 +489,7 @@ func generateTypeAllSql(beginTime time.Time, endTime time.Time, latestDate strin
" where A.repo_id=B.repo_id"

if q != "" {
sql = sql + " and name like '%" + q + "%'"
sql = sql + " and LOWER(name) like '%" + strings.ToLower(q) + "%'"
}
sql = sql + " order by " + orderBy + " desc,repo_id" + " limit " + strconv.Itoa(pageSize) + " offset " + strconv.Itoa((page-1)*pageSize)
return sql
@@ -511,7 +512,7 @@ func generatePageSql(beginTime time.Time, endTime time.Time, latestDate string,
"(SELECT repo_id,name,owner_name,is_private,radar_total from public.repo_statistic where date='" + latestDate + "') B" +
" where A.repo_id=B.repo_id"
if q != "" {
sql = sql + " and B.name like '%" + q + "%'"
sql = sql + " and LOWER(B.name) like '%" + strings.ToLower(q) + "%'"
}
sql = sql + " order by " + orderBy + " desc,A.repo_id" + " limit " + strconv.Itoa(pageSize) + " offset " + strconv.Itoa((page-1)*pageSize)
return sql


+ 6
- 0
routers/init.go View File

@@ -60,11 +60,17 @@ func NewServices() {
if err := storage.Init(); err != nil {
log.Fatal("storage init failed: %v", err)
}
log.Info("storage init succeed.")
mailer.NewContext()
log.Info("mailer.NewContext() succeed.")
_ = cache.NewContext()
log.Info("cache.NewContext() succeed.")
notification.NewContext()
log.Info("notification.NewContext() succeed.")
decompression.NewContext()
log.Info("decompression.NewContext() succeed.")
labelmsg.Init()
log.Info("labelmsg.Init() succeed.")
}

// In case of problems connecting to DB, retry connection. Eg, PGSQL in Docker Container on Synology


+ 1
- 1
routers/repo/branch.go View File

@@ -181,7 +181,7 @@ func deleteBranch(ctx *context.Context, branchName string) error {
}

func loadBranches(ctx *context.Context) []*Branch {
rawBranches, err := repo_module.GetBranches(ctx.Repo.Repository)
rawBranches, _, err := repo_module.GetBranches(ctx.Repo.Repository, 0, 0)
if err != nil {
ctx.ServerError("GetBranches", err)
return nil


+ 110
- 10
routers/repo/cloudbrain.go View File

@@ -74,12 +74,13 @@ func CloudBrainIndex(ctx *context.Context) {
timestamp := time.Now().Unix()
for i, task := range ciTasks {
if task.Status == string(models.JobRunning) && (timestamp-int64(task.Cloudbrain.CreatedUnix) > 10) {
ciTasks[i].CanDebug = true
ciTasks[i].CanDebug = cloudbrain.CanCreateOrDebugJob(ctx)
} else {
ciTasks[i].CanDebug = false
}

ciTasks[i].CanDel = models.CanDelJob(ctx.IsSigned, ctx.User, task)
ciTasks[i].CanDel = cloudbrain.CanDeleteDebugJob(ctx, &task.Cloudbrain)

}

pager := context.NewPagination(int(count), setting.UI.IssuePagingNum, page, 5)
@@ -88,6 +89,7 @@ func CloudBrainIndex(ctx *context.Context) {

ctx.Data["PageIsCloudBrain"] = true
ctx.Data["Tasks"] = ciTasks
ctx.Data["CanCreate"] = cloudbrain.CanCreateOrDebugJob(ctx)
ctx.HTML(200, tplCloudBrainIndex)
}

@@ -216,7 +218,22 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) {
return
}

_, err := models.GetCloudbrainByName(jobName)
count, err := models.GetCloudbrainCountByUserID(ctx.User.ID)
if err != nil {
log.Error("GetCloudbrainCountByUserID failed:%v", err, ctx.Data["MsgID"])
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr("system error", tplCloudBrainNew, &form)
return
} else {
if count >= 1 {
log.Error("the user already has running or waiting task", ctx.Data["MsgID"])
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr("you have already a running or waiting task, can not create more", tplCloudBrainNew, &form)
return
}
}

_, err = models.GetCloudbrainByName(jobName)
if err == nil {
log.Error("the job name did already exist", ctx.Data["MsgID"])
cloudBrainNewDataPrepare(ctx)
@@ -437,14 +454,22 @@ func StopJobs(cloudBrains []*models.Cloudbrain) {

logErrorAndUpdateJobStatus(err, taskInfo)
} else {
param := models.NotebookAction{
Action: models.ActionStop,
if taskInfo.JobType == string(models.JobTypeTrain) {
err := retry(3, time.Second*30, func() error {
_, err := modelarts.StopTrainJob(taskInfo.JobID, strconv.FormatInt(taskInfo.VersionID, 10))
return err
})
logErrorAndUpdateJobStatus(err, taskInfo)
} else {
param := models.NotebookAction{
Action: models.ActionStop,
}
err := retry(3, time.Second*30, func() error {
_, err := modelarts.StopJob(taskInfo.JobID, param)
return err
})
logErrorAndUpdateJobStatus(err, taskInfo)
}
err := retry(3, time.Second*30, func() error {
_, err := modelarts.StopJob(taskInfo.JobID, param)
return err
})
logErrorAndUpdateJobStatus(err, taskInfo)
}

}
@@ -715,3 +740,78 @@ func downloadRateCode(repo *models.Repository, taskName, gitPath, codePath, benc

return nil
}

func SyncCloudbrainStatus() {
cloudBrains, err := models.GetCloudBrainUnStoppedJob()
if err != nil {
log.Error("GetCloudBrainUnStoppedJob failed:", err.Error())
return
}

for _, task := range cloudBrains {
if task.Type == models.TypeCloudBrainOne {
result, err := cloudbrain.GetJob(task.JobID)
if err != nil {
log.Error("GetJob(%s) failed:%v", task.JobName, err)
continue
}

if result != nil {
jobRes, _ := models.ConvertToJobResultPayload(result.Payload)
taskRoles := jobRes.TaskRoles
taskRes, _ := models.ConvertToTaskPod(taskRoles[cloudbrain.SubTaskName].(map[string]interface{}))
task.Status = taskRes.TaskStatuses[0].State
if task.Status != string(models.JobWaiting) {
err = models.UpdateJob(task)
if err != nil {
log.Error("UpdateJob(%s) failed:%v", task.JobName, err)
continue
}
}
}
} else if task.Type == models.TypeCloudBrainTwo {
if task.JobType == string(models.JobTypeDebug) {
result, err := modelarts.GetJob(task.JobID)
if err != nil {
log.Error("GetJob(%s) failed:%v", task.JobName, err)
continue
}

if result != nil {
task.Status = result.Status

err = models.UpdateJob(task)
if err != nil {
log.Error("UpdateJob(%s) failed:%v", task.JobName, err)
continue
}
}
} else if task.JobType == string(models.JobTypeTrain) {
result, err := modelarts.GetTrainJob(task.JobID, strconv.FormatInt(task.VersionID, 10))
if err != nil {
log.Error("GetTrainJob(%s) failed:%v", task.JobName, err)
continue
}

if result != nil {
task.Status = modelarts.TransTrainJobStatus(result.IntStatus)
task.Duration = result.Duration
task.TrainJobDuration = result.TrainJobDuration

err = models.UpdateJob(task)
if err != nil {
log.Error("UpdateJob(%s) failed:%v", task.JobName, err)
continue
}
}
} else {
log.Error("task.JobType(%s) is error:%s", task.JobName, task.JobType)
}

} else {
log.Error("task.Type(%s) is error:%d", task.JobName, task.Type)
}
}

return
}

+ 2
- 2
routers/repo/compare.go View File

@@ -507,7 +507,7 @@ func getBranchesForRepo(user *models.User, repo *models.Repository) (bool, []str
}
defer gitRepo.Close()

branches, err := gitRepo.GetBranches()
branches, _, err := gitRepo.GetBranches(0, 0)
if err != nil {
return false, nil, err
}
@@ -528,7 +528,7 @@ func CompareDiff(ctx *context.Context) {
}

if ctx.Data["PageIsComparePull"] == true {
headBranches, err := headGitRepo.GetBranches()
headBranches, _, err := headGitRepo.GetBranches(0,0)
if err != nil {
ctx.ServerError("GetBranches", err)
return


+ 2
- 2
routers/repo/issue.go View File

@@ -424,7 +424,7 @@ func RetrieveRepoMetas(ctx *context.Context, repo *models.Repository, isPull boo
return nil
}

brs, err := ctx.Repo.GitRepo.GetBranches()
brs, _, err := ctx.Repo.GitRepo.GetBranches(0,0)
if err != nil {
ctx.ServerError("GetBranches", err)
return nil
@@ -1390,7 +1390,7 @@ func isLegalReviewRequest(reviewer, doer *models.User, isAdd bool, issue *models

var pemResult bool
if isAdd {
pemResult = permReviewer.CanAccessAny(models.AccessModeRead, models.UnitTypePullRequests)
pemResult = permReviewer.CanAccessAny(models.AccessModeWrite, models.UnitTypePullRequests)
if !pemResult {
return fmt.Errorf("Reviewer can't read [user_id: %d, repo_name: %s]", reviewer.ID, issue.Repo.Name)
}


+ 63
- 24
routers/repo/modelarts.go View File

@@ -11,6 +11,8 @@ import (
"strings"
"time"

"code.gitea.io/gitea/modules/cloudbrain"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth"
"code.gitea.io/gitea/modules/base"
@@ -124,10 +126,11 @@ func NotebookIndex(ctx *context.Context) {

for i, task := range ciTasks {
if task.Status == string(models.JobRunning) {
ciTasks[i].CanDebug = true
ciTasks[i].CanDebug = cloudbrain.CanCreateOrDebugJob(ctx)
} else {
ciTasks[i].CanDebug = false
}
ciTasks[i].CanDel = cloudbrain.CanDeleteDebugJob(ctx, &task.Cloudbrain)
}

pager := context.NewPagination(int(count), setting.UI.IssuePagingNum, page, 5)
@@ -136,6 +139,7 @@ func NotebookIndex(ctx *context.Context) {

ctx.Data["PageIsCloudBrain"] = true
ctx.Data["Tasks"] = ciTasks
ctx.Data["CanCreate"] = cloudbrain.CanCreateOrDebugJob(ctx)
ctx.HTML(200, tplModelArtsNotebookIndex)
}

@@ -171,7 +175,22 @@ func NotebookCreate(ctx *context.Context, form auth.CreateModelArtsNotebookForm)
description := form.Description
flavor := form.Flavor

err := modelarts.GenerateTask(ctx, jobName, uuid, description, flavor)
count, err := models.GetCloudbrainNotebookCountByUserID(ctx.User.ID)
if err != nil {
log.Error("GetCloudbrainNotebookCountByUserID failed:%v", err, ctx.Data["MsgID"])
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr("system error", tplModelArtsNotebookNew, &form)
return
} else {
if count >= 1 {
log.Error("the user already has running or waiting task", ctx.Data["MsgID"])
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr("you have already a running or waiting task, can not create more", tplModelArtsNotebookNew, &form)
return
}
}

err = modelarts.GenerateTask(ctx, jobName, uuid, description, flavor)
if err != nil {
ctx.RenderWithErr(err.Error(), tplModelArtsNotebookNew, &form)
return
@@ -342,12 +361,18 @@ func TrainJobIndex(ctx *context.Context) {
return
}

for i, task := range tasks {
tasks[i].CanDel = cloudbrain.CanDeleteTrainJob(ctx, &task.Cloudbrain)
tasks[i].CanModify = cloudbrain.CanModifyJob(ctx, &task.Cloudbrain)
}

pager := context.NewPagination(int(count), setting.UI.IssuePagingNum, page, 5)
pager.SetDefaultParams(ctx)
ctx.Data["Page"] = pager

ctx.Data["PageIsCloudBrain"] = true
ctx.Data["Tasks"] = tasks
ctx.Data["CanCreate"] = cloudbrain.CanCreateOrDebugJob(ctx)
ctx.HTML(200, tplModelArtsTrainJobIndex)
}

@@ -416,16 +441,8 @@ func trainJobNewDataPrepare(ctx *context.Context) error {

outputObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.OutputPath
ctx.Data["train_url"] = outputObsPath

Branches, err := ctx.Repo.GitRepo.GetBranches()
if err != nil {
ctx.ServerError("GetBranches error:", err)
return err
}
ctx.Data["Branches"] = Branches
ctx.Data["BranchesCount"] = len(Branches)
ctx.Data["params"] = ""
ctx.Data["BranchName"] = ctx.Repo.BranchName
ctx.Data["branchName"] = ctx.Repo.BranchName

configList, err := getConfigList(modelarts.PerPage, 1, modelarts.SortByCreateTime, "desc", "", modelarts.ConfigTypeCustom)
if err != nil {
@@ -494,14 +511,6 @@ func trainJobErrorNewDataPrepare(ctx *context.Context, form auth.CreateModelArts
outputObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.OutputPath
ctx.Data["train_url"] = outputObsPath

Branches, err := ctx.Repo.GitRepo.GetBranches()
if err != nil {
ctx.ServerError("GetBranches error:", err)
return err
}
ctx.Data["Branches"] = Branches
ctx.Data["BranchesCount"] = len(Branches)

configList, err := getConfigList(modelarts.PerPage, 1, modelarts.SortByCreateTime, "desc", "", modelarts.ConfigTypeCustom)
if err != nil {
ctx.ServerError("getConfigList failed:", err)
@@ -597,13 +606,13 @@ func trainJobNewVersionDataPrepare(ctx *context.Context) error {
outputObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.OutputPath
ctx.Data["train_url"] = outputObsPath

Branches, err := ctx.Repo.GitRepo.GetBranches()
branches, _, err := ctx.Repo.GitRepo.GetBranches(0, 0)
if err != nil {
ctx.ServerError("GetBranches error:", err)
return err
}

ctx.Data["branches"] = Branches
ctx.Data["branches"] = branches
ctx.Data["branch_name"] = task.BranchName
ctx.Data["description"] = task.Description
ctx.Data["boot_file"] = task.BootFile
@@ -686,12 +695,12 @@ func versionErrorDataPrepare(ctx *context.Context, form auth.CreateModelArtsTrai
outputObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.OutputPath
ctx.Data["train_url"] = outputObsPath

Branches, err := ctx.Repo.GitRepo.GetBranches()
branches, _, err := ctx.Repo.GitRepo.GetBranches(0, 0)
if err != nil {
ctx.ServerError("GetBranches error:", err)
return err
}
ctx.Data["branches"] = Branches
ctx.Data["branches"] = branches
ctx.Data["description"] = form.Description
ctx.Data["dataset_name"] = task.DatasetName
ctx.Data["work_server_number"] = form.WorkServerNumber
@@ -739,6 +748,21 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm)
VersionCount := modelarts.VersionCount
EngineName := form.EngineName

count, err := models.GetCloudbrainTrainJobCountByUserID(ctx.User.ID)
if err != nil {
log.Error("GetCloudbrainTrainJobCountByUserID failed:%v", err, ctx.Data["MsgID"])
trainJobErrorNewDataPrepare(ctx, form)
ctx.RenderWithErr("system error", tplModelArtsTrainJobNew, &form)
return
} else {
if count >= 1 {
log.Error("the user already has running or waiting task", ctx.Data["MsgID"])
trainJobErrorNewDataPrepare(ctx, form)
ctx.RenderWithErr("you have already a running or waiting task, can not create more", tplModelArtsTrainJobNew, &form)
return
}
}

if err := paramCheckCreateTrainJob(form); err != nil {
log.Error("paramCheckCreateTrainJob failed:(%v)", err)
trainJobErrorNewDataPrepare(ctx, form)
@@ -891,7 +915,7 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm)
return
}

err := modelarts.GenerateTrainJob(ctx, req)
err = modelarts.GenerateTrainJob(ctx, req)
if err != nil {
log.Error("GenerateTrainJob failed:%v", err.Error())
trainJobErrorNewDataPrepare(ctx, form)
@@ -905,6 +929,21 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ
ctx.Data["PageIsTrainJob"] = true
var jobID = ctx.Params(":jobid")

count, err := models.GetCloudbrainTrainJobCountByUserID(ctx.User.ID)
if err != nil {
log.Error("GetCloudbrainTrainJobCountByUserID failed:%v", err, ctx.Data["MsgID"])
versionErrorDataPrepare(ctx, form)
ctx.RenderWithErr("system error", tplModelArtsTrainJobVersionNew, &form)
return
} else {
if count >= 1 {
log.Error("the user already has running or waiting task", ctx.Data["MsgID"])
versionErrorDataPrepare(ctx, form)
ctx.RenderWithErr("you have already a running or waiting task, can not create more", tplModelArtsTrainJobVersionNew, &form)
return
}
}

latestTask, err := models.GetCloudbrainByJobIDAndIsLatestVersion(jobID, modelarts.IsLatestVersion)
if err != nil {
ctx.ServerError("GetCloudbrainByJobIDAndIsLatestVersion faild:", err)


+ 1
- 1
routers/repo/repo_statistic.go View File

@@ -208,7 +208,7 @@ func RepoStatisticDaily(date string) {
maxRepoRadar.Completeness = tempRepoStat.Completeness
}

if tempRepoStat.Liveness < minRepoRadar.Completeness {
if tempRepoStat.Liveness < minRepoRadar.Liveness {
minRepoRadar.Liveness = tempRepoStat.Liveness
}



+ 18
- 16
routers/routes/routes.go View File

@@ -12,6 +12,8 @@ import (
"text/template"
"time"

"code.gitea.io/gitea/modules/cloudbrain"

"code.gitea.io/gitea/routers/operation"
"code.gitea.io/gitea/routers/private"

@@ -792,7 +794,7 @@ func RegisterRoutes(m *macaron.Macaron) {
}, reqSignIn, context.RepoAssignment(), context.UnitTypes(), reqRepoAdmin, context.RepoRef())

m.Post("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), context.UnitTypes(), repo.Action)
m.Get("/tool/query_user_static_page", adminReq, repo.QueryUserStaticDataPage)
// Grouping for those endpoints not requiring authentication
m.Group("/:username/:reponame", func() {
m.Get("/contributors", repo.Contributors)
@@ -957,15 +959,15 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("", reqRepoCloudBrainReader, repo.CloudBrainIndex)
m.Group("/:jobid", func() {
m.Get("", reqRepoCloudBrainReader, repo.CloudBrainShow)
m.Get("/debug", reqRepoCloudBrainReader, repo.CloudBrainDebug)
m.Post("/commit_image", reqRepoCloudBrainWriter, bindIgnErr(auth.CommitImageCloudBrainForm{}), repo.CloudBrainCommitImage)
m.Post("/stop", reqRepoCloudBrainWriter, repo.CloudBrainStop)
m.Post("/del", reqRepoCloudBrainWriter, repo.CloudBrainDel)
m.Get("/debug", reqRepoCloudBrainWriter, repo.CloudBrainDebug)
m.Post("/commit_image", cloudbrain.AdminOrOwnerOrJobCreaterRight, bindIgnErr(auth.CommitImageCloudBrainForm{}), repo.CloudBrainCommitImage)
m.Post("/stop", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.CloudBrainStop)
m.Post("/del", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.CloudBrainDel)
m.Get("/rate", reqRepoCloudBrainReader, repo.GetRate)
m.Get("/models", reqRepoCloudBrainReader, repo.CloudBrainShowModels)
m.Get("/download_model", reqRepoCloudBrainReader, repo.CloudBrainDownloadModel)
m.Get("/download_model", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.CloudBrainDownloadModel)
})
m.Get("/create", reqRepoCloudBrainReader, repo.CloudBrainNew)
m.Get("/create", reqRepoCloudBrainWriter, repo.CloudBrainNew)
m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateCloudBrainForm{}), repo.CloudBrainCreate)
}, context.RepoRef())

@@ -978,9 +980,9 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("", reqRepoCloudBrainReader, repo.NotebookIndex)
m.Group("/:jobid", func() {
m.Get("", reqRepoCloudBrainReader, repo.NotebookShow)
m.Get("/debug", reqRepoCloudBrainReader, repo.NotebookDebug)
m.Post("/stop", reqRepoCloudBrainWriter, repo.NotebookStop)
m.Post("/del", reqRepoCloudBrainWriter, repo.NotebookDel)
m.Get("/debug", reqRepoCloudBrainWriter, repo.NotebookDebug)
m.Post("/stop", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.NotebookStop)
m.Post("/del", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.NotebookDel)
})
m.Get("/create", reqRepoCloudBrainWriter, repo.NotebookNew)
m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateModelArtsNotebookForm{}), repo.NotebookCreate)
@@ -990,13 +992,13 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("", reqRepoCloudBrainReader, repo.TrainJobIndex)
m.Group("/:jobid", func() {
m.Get("", reqRepoCloudBrainReader, repo.TrainJobShow)
m.Post("/stop", reqRepoCloudBrainWriter, repo.TrainJobStop)
m.Post("/del", reqRepoCloudBrainWriter, repo.TrainJobDel)
m.Get("/model_download", reqRepoCloudBrainReader, repo.ModelDownload)
m.Get("/create_version", reqRepoCloudBrainReader, repo.TrainJobNewVersion)
m.Post("/create_version", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateModelArtsTrainJobForm{}), repo.TrainJobCreateVersion)
m.Post("/stop", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.TrainJobStop)
m.Post("/del", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.TrainJobDel)
m.Get("/model_download", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.ModelDownload)
m.Get("/create_version", cloudbrain.AdminOrJobCreaterRight, repo.TrainJobNewVersion)
m.Post("/create_version", cloudbrain.AdminOrJobCreaterRight, bindIgnErr(auth.CreateModelArtsTrainJobForm{}), repo.TrainJobCreateVersion)
})
m.Get("/create", reqRepoCloudBrainReader, repo.TrainJobNew)
m.Get("/create", reqRepoCloudBrainWriter, repo.TrainJobNew)
m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateModelArtsTrainJobForm{}), repo.TrainJobCreate)

m.Get("/para-config-list", reqRepoCloudBrainReader, repo.TrainJobGetConfigList)


+ 1
- 1
services/mirror/mirror.go View File

@@ -252,7 +252,7 @@ func runSync(m *models.Mirror) ([]*mirrorSyncResult, bool) {
}
}

branches, err := repo_module.GetBranches(m.Repo)
branches, _, err := repo_module.GetBranches(m.Repo,0,0)
if err != nil {
log.Error("GetBranches: %v", err)
return nil, false


+ 1
- 1
services/pull/pull.go View File

@@ -452,7 +452,7 @@ func CloseBranchPulls(doer *models.User, repoID int64, branch string) error {

// CloseRepoBranchesPulls close all pull requests which head branches are in the given repository
func CloseRepoBranchesPulls(doer *models.User, repo *models.Repository) error {
branches, err := git.GetBranchesByPath(repo.RepoPath())
branches, _, err := git.GetBranchesByPath(repo.RepoPath(), 0, 0)
if err != nil {
return err
}


+ 54
- 22
templates/base/head_navbar.tmpl View File

@@ -17,47 +17,57 @@

{{if .IsSigned}}
<a class="item {{if .PageIsDashboard}}active{{end}}" href="/dashboard">{{.i18n.Tr "index"}}</a>
<a class="item" href="{{AppSubUrl}}/OpenI">{{.i18n.Tr "custom.head.openi"}}</a>
{{if not .UnitIssuesGlobalDisabled}}
<a class="item {{if .PageIsIssues}}active{{end}}" href="{{AppSubUrl}}/issues">{{.i18n.Tr "issues"}}</a>
{{end}}
{{if not .UnitPullsGlobalDisabled}}
<a class="item {{if .PageIsPulls}}active{{end}}" href="{{AppSubUrl}}/pulls">{{.i18n.Tr "pull_requests"}}</a>
{{end}}
{{if not (and .UnitIssuesGlobalDisabled .UnitPullsGlobalDisabled)}}
{{if .ShowMilestonesDashboardPage}}<a class="item {{if .PageIsMilestonesDashboard}}active{{end}}" href="{{AppSubUrl}}/milestones">{{.i18n.Tr "milestones"}}</a>{{end}}
{{end}}

<div class="ui dropdown item">
{{.i18n.Tr "index"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/issues">{{.i18n.Tr "issues"}}</a>
<a class="item" href="{{AppSubUrl}}/pulls">{{.i18n.Tr "pull_requests"}}</a>
<a class="item" href="{{AppSubUrl}}/milestones">{{.i18n.Tr "milestones"}}</a>
</div>
</div>
<a class="item" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "custom.head.project"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "custom.head.dataset"}}</a>
<div class="ui dropdown item">
{{.i18n.Tr "explore"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "custom.head.project"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "custom.head.dataset"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/images">{{.i18n.Tr "explore.images"}}</a>
{{if .IsAdmin}}
{{if .IsOperator}}
<a class="item" href="{{AppSubUrl}}/explore/data_analysis">{{.i18n.Tr "explore.data_analysis"}}</a>
{{end}}
{{end}}
<a class="item" href="{{AppSubUrl}}/OpenI">{{.i18n.Tr "custom.head.openi"}}</a>
</div>
</div>
</div>
{{else if .IsLandingPageHome}}
<a class="item {{if .PageIsHome}}active{{end}}" href="{{AppSubUrl}}/dashboard">{{.i18n.Tr "home"}}</a>
<a class="item" href="{{AppSubUrl}}/OpenI">{{.i18n.Tr "custom.head.openi"}}</a>


<div class="ui dropdown item">
{{.i18n.Tr "home"}}
<i class="dropdown icon"></i>
<div class="menu">
<!-- 未登录跳转登录界面 -->
<a class="item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "issues"}}</a>
<a class="item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "pull_requests"}}</a>
<a class="item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "milestones"}}</a>
</div>
</div>
<a class="item" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "custom.head.project"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "custom.head.dataset"}}</a>
<div class="ui dropdown item">
{{.i18n.Tr "explore"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "custom.head.project"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "datasets"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/images">{{.i18n.Tr "explore.images"}}</a>
{{if .IsAdmin}}
{{if .IsOperator}}
<a class="item" href="{{AppSubUrl}}/explore/data_analysis">{{.i18n.Tr "explore.data_analysis"}}</a>
{{end}}
<a class="item" href="{{AppSubUrl}}/OpenI">{{.i18n.Tr "custom.head.openi"}}</a>
</div>
</div>
{{else if .IsLandingPageExplore}}
@@ -78,7 +88,18 @@
*/}}

{{if .IsSigned}}
<div class="right stackable menu">
<div class="right stackable menu">
<form class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty" style="margin:auto" action="/explore/repos">
<div class="ui fluid action input" style="background:#363840 ;border-radius: 5px;width: 200px;height:30px;border: #888888 solid 1px;">
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search_pro"}}..."
style="transition: background-color 5000s ease-in-out 0s;-webkit-text-fill-color:#888888;background:#363840 ;color:#888888;border: none;outline: none;">
<input type="hidden" name="tab" value="{{$.TabName}}">
<input type="hidden" name="sort" value="{{$.SortType}}">
<button style="border: none;background-color: #363840;outline: none;border-radius:5px"><img type = "submit" style="width: 25px; height: 25px;margin: auto;" src="/img/search.svg" >
</button>
<!-- <button class="ui green button">{{.i18n.Tr "explore.search"}}</button> -->
</div>
</form>
<a href="{{AppSubUrl}}/notifications" class="item poping up" data-content='{{.i18n.Tr "notifications"}}' data-variation="tiny inverted">
<span class="text">
<span class="fitted">{{svg "octicon-bell" 16}}</span>
@@ -163,6 +184,17 @@

<!--a class="item" target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io">{{.i18n.Tr "help"}}</a-->
<div class="right stackable menu">
<form class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty" style="margin:auto" action="/explore/repos">
<div class="ui fluid action input" style="background:#363840 ;border-radius: 5px;width: 200px;height:30px;border: #888888 solid 1px;">
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search_pro"}}..."
style="transition: background-color 5000s ease-in-out 0s;-webkit-text-fill-color:#888888;background:#363840 ;color:#888888;border: none;outline: none;">
<input type="hidden" name="tab" value="{{$.TabName}}">
<input type="hidden" name="sort" value="{{$.SortType}}">
<button style="border: none;background-color: #363840;outline: none;border-radius:5px"><img type = "submit" style="width: 25px; height: 25px;margin: auto;" src="/img/search.svg" >
</button>
<!-- <button class="ui green button">{{.i18n.Tr "explore.search"}}</button> -->
</div>
</form>
{{if .ShowRegistrationButton}}
<a class="item{{if .PageIsSignUp}} active{{end}}" href="{{AppSubUrl}}/user/sign_up">
{{svg "octicon-person" 16}} {{.i18n.Tr "register"}}


+ 50
- 21
templates/base/head_navbar_fluid.tmpl View File

@@ -17,47 +17,54 @@

{{if .IsSigned}}
<a class="item {{if .PageIsDashboard}}active{{end}}" href="/dashboard">{{.i18n.Tr "index"}}</a>
<a class="item" href="{{AppSubUrl}}/OpenI">{{.i18n.Tr "custom.head.openi"}}</a>
{{if not .UnitIssuesGlobalDisabled}}
<a class="item {{if .PageIsIssues}}active{{end}}" href="{{AppSubUrl}}/issues">{{.i18n.Tr "issues"}}</a>
{{end}}
{{if not .UnitPullsGlobalDisabled}}
<a class="item {{if .PageIsPulls}}active{{end}}" href="{{AppSubUrl}}/pulls">{{.i18n.Tr "pull_requests"}}</a>
{{end}}
{{if not (and .UnitIssuesGlobalDisabled .UnitPullsGlobalDisabled)}}
{{if .ShowMilestonesDashboardPage}}<a class="item {{if .PageIsMilestonesDashboard}}active{{end}}" href="{{AppSubUrl}}/milestones">{{.i18n.Tr "milestones"}}</a>{{end}}
{{end}}
<div class="ui dropdown item">
{{.i18n.Tr "index"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/issues">{{.i18n.Tr "issues"}}</a>
<a class="item" href="{{AppSubUrl}}/pulls">{{.i18n.Tr "pull_requests"}}</a>
<a class="item" href="{{AppSubUrl}}/milestones">{{.i18n.Tr "milestones"}}</a>
</div>
</div>
<a class="item" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "custom.head.project"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "custom.head.dataset"}}</a>
<div class="ui dropdown item">
{{.i18n.Tr "explore"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "custom.head.project"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "custom.head.dataset"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/images">{{.i18n.Tr "explore.images"}}</a>
{{if .IsAdmin}}
{{if .IsOperator}}
<a class="item" href="{{AppSubUrl}}/explore/data_analysis">{{.i18n.Tr "explore.data_analysis"}}</a>
{{end}}
{{end}}
<a class="item" href="{{AppSubUrl}}/OpenI">{{.i18n.Tr "custom.head.openi"}}</a>
</div>
</div>
{{else if .IsLandingPageHome}}
<a class="item {{if .PageIsHome}}active{{end}}" href="{{AppSubUrl}}/dashboard">{{.i18n.Tr "home"}}</a>
<a class="item" href="{{AppSubUrl}}/OpenI">{{.i18n.Tr "custom.head.openi"}}</a>

<div class="ui dropdown item">
{{.i18n.Tr "home"}}
<i class="dropdown icon"></i>
<div class="menu">
<!-- 未登录跳转登录界面 -->
<a class="item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "issues"}}</a>
<a class="item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "pull_requests"}}</a>
<a class="item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "milestones"}}</a>
</div>
</div>
<a class="item" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "custom.head.project"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "custom.head.dataset"}}</a>
<div class="ui dropdown item">
{{.i18n.Tr "explore"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "custom.head.project"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "datasets"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/images">{{.i18n.Tr "explore.images"}}</a>
{{if .IsAdmin}}
{{if .IsOperator}}
<a class="item" href="{{AppSubUrl}}/explore/data_analysis">{{.i18n.Tr "explore.data_analysis"}}</a>
{{end}}
<a class="item" href="{{AppSubUrl}}/OpenI">{{.i18n.Tr "custom.head.openi"}}</a>
</div>
</div>
{{else if .IsLandingPageExplore}}
@@ -79,6 +86,17 @@

{{if .IsSigned}}
<div class="right stackable menu">
<form class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty" style="margin:auto" action="/explore/repos">
<div class="ui fluid action input" style="background:#363840 ;border-radius: 5px;width: 200px;height:30px;border: #888888 solid 1px;">
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search_pro"}}..."
style="transition: background-color 5000s ease-in-out 0s;-webkit-text-fill-color:#888888;background:#363840 ;color:#888888;border: none;outline: none;">
<input type="hidden" name="tab" value="{{$.TabName}}">
<input type="hidden" name="sort" value="{{$.SortType}}">
<button style="border: none;background-color: #363840;outline: none;border-radius:5px"><img type = "submit" style="width: 25px; height: 25px;margin: auto;" src="/img/search.svg" >
</button>
<!-- <button class="ui green button">{{.i18n.Tr "explore.search"}}</button> -->
</div>
</form>
<a href="{{AppSubUrl}}/notifications" class="item poping up" data-content='{{.i18n.Tr "notifications"}}' data-variation="tiny inverted">
<span class="text">
<span class="fitted">{{svg "octicon-bell" 16}}</span>
@@ -163,6 +181,17 @@

<!--a class="item" target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io">{{.i18n.Tr "help"}}</a-->
<div class="right stackable menu">
<form class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty" style="margin:auto" action="/explore/repos">
<div class="ui fluid action input" style="background:#363840 ;border-radius: 5px;width: 200px;height:30px;border: #888888 solid 1px;">
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search_pro"}}..."
style="transition: background-color 5000s ease-in-out 0s;-webkit-text-fill-color:#888888;background:#363840 ;color:#888888;border: none;outline: none;">
<input type="hidden" name="tab" value="{{$.TabName}}">
<input type="hidden" name="sort" value="{{$.SortType}}">
<button style="border: none;background-color: #363840;outline: none;border-radius:5px"><img type = "submit" style="width: 25px; height: 25px;margin: auto;" src="/img/search.svg" >
</button>
<!-- <button class="ui green button">{{.i18n.Tr "explore.search"}}</button> -->
</div>
</form>
{{if .ShowRegistrationButton}}
<a class="item{{if .PageIsSignUp}} active{{end}}" href="{{AppSubUrl}}/user/sign_up">
{{svg "octicon-person" 16}} {{.i18n.Tr "register"}}


+ 29
- 20
templates/base/head_navbar_home.tmpl View File

@@ -9,47 +9,56 @@
</div>

{{if .IsSigned}}
<a class="item {{if .PageIsDashboard}}active{{end}}" href="/dashboard">{{.i18n.Tr "index"}}</a>
<a class="item" href="{{AppSubUrl}}/OpenI">{{.i18n.Tr "custom.head.openi"}}</a>
{{if not .UnitIssuesGlobalDisabled}}
<a class="item {{if .PageIsIssues}}active{{end}}" href="{{AppSubUrl}}/issues">{{.i18n.Tr "issues"}}</a>
{{end}}
{{if not .UnitPullsGlobalDisabled}}
<a class="item {{if .PageIsPulls}}active{{end}}" href="{{AppSubUrl}}/pulls">{{.i18n.Tr "pull_requests"}}</a>
{{end}}
{{if not (and .UnitIssuesGlobalDisabled .UnitPullsGlobalDisabled)}}
{{if .ShowMilestonesDashboardPage}}<a class="item {{if .PageIsMilestonesDashboard}}active{{end}}" href="{{AppSubUrl}}/milestones">{{.i18n.Tr "milestones"}}</a>{{end}}
{{end}}
<div class="ui dropdown item">
{{.i18n.Tr "index"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/issues">{{.i18n.Tr "issues"}}</a>
<a class="item" href="{{AppSubUrl}}/pulls">{{.i18n.Tr "pull_requests"}}</a>
<a class="item" href="{{AppSubUrl}}/milestones">{{.i18n.Tr "milestones"}}</a>
</div>
</div>
<a class="item" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "custom.head.project"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "custom.head.dataset"}}</a>
<div class="ui dropdown item">
{{.i18n.Tr "explore"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "custom.head.project"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "custom.head.dataset"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/images">{{.i18n.Tr "explore.images"}}</a>
{{if .IsAdmin}}
{{if .IsOperator}}
<a class="item" href="{{AppSubUrl}}/explore/data_analysis">{{.i18n.Tr "explore.data_analysis"}}</a>
{{end}}
{{end}}
<a class="item" href="{{AppSubUrl}}/OpenI">{{.i18n.Tr "custom.head.openi"}}</a>
</div>
</div>
{{else if .IsLandingPageHome}}
<a class="item {{if .PageIsHome}}active{{end}}" href="{{AppSubUrl}}/dashboard">{{.i18n.Tr "home"}}</a>
<a class="item" href="{{AppSubUrl}}/OpenI">{{.i18n.Tr "custom.head.openi"}}</a>

<div class="ui dropdown item">
{{.i18n.Tr "home"}}
<i class="dropdown icon"></i>
<div class="menu">
<!-- 未登录跳转登录界面 -->
<a class="item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "issues"}}</a>
<a class="item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "pull_requests"}}</a>
<a class="item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "milestones"}}</a>
</div>
</div>
<a class="item" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "custom.head.project"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "custom.head.dataset"}}</a>
<div class="ui dropdown item">
{{.i18n.Tr "explore"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "custom.head.project"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "datasets"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/images">{{.i18n.Tr "explore.images"}}</a>
{{if .IsAdmin}}
{{if .IsOperator}}
<a class="item" href="{{AppSubUrl}}/explore/data_analysis">{{.i18n.Tr "explore.data_analysis"}}</a>
{{end}}
<a class="item" href="{{AppSubUrl}}/OpenI">{{.i18n.Tr "custom.head.openi"}}</a>
</div>
</div>
{{else if .IsLandingPageExplore}}


+ 2
- 2
templates/repo/modelarts/trainjob/new.tmpl View File

@@ -183,9 +183,9 @@
{{end}}
{{end}}
{{else}}
<option name="branch_name" value="{{.BranchName}}">{{.BranchName}}</option>
<option name="branch_name" value="{{.branchName}}">{{.branchName}}</option>
{{range $k, $v :=.Branches}}
{{ if ne $v $.BranchName }}
{{ if ne $v $.branchName }}
<option name="branch_name" value="{{$v}}">{{$v}}</option>
{{end}}
{{end}}


+ 5
- 2
templates/repo/modelarts/trainjob/show.tmpl View File

@@ -474,6 +474,7 @@ td, th {

<script>
console.log({{.version_list_task}})
console.log({{.}})
$('.menu .item').tab()
// $('.ui.style.accordion').accordion();

@@ -708,11 +709,13 @@ td, th {
}
function logScroll(version_name) {
let container = document.querySelector(`#log${version_name}`)
let scrollTop = container.scrollTop
let scrollHeight = container.scrollHeight
let clientHeight = container.clientHeight
if(parseInt(scrollTop) + clientHeight == scrollHeight || parseInt(scrollTop) + clientHeight +1 == scrollHeight || parseInt(scrollTop) + clientHeight - 1 == scrollHeight){
let scrollLeft = container.scrollLeft
if((parseInt(scrollTop) + clientHeight == scrollHeight || parseInt(scrollTop) + clientHeight +1 == scrollHeight || parseInt(scrollTop) + clientHeight - 1 == scrollHeight) && (scrollLeft===0)){
let end_line = $(`#log${version_name} input[name=end_line]`).val()
$.get(`/api/v1/repos/${userName}/${repoPath}/modelarts/train-job/${jobID}/log?version_name=${version_name}&base_line=${end_line}&lines=50&order=desc`, (data) => {
if (data.Lines == 0){
@@ -735,7 +738,7 @@ td, th {
console.log(err);
});
}
if(scrollTop == 0){
if(scrollTop == 0 && scrollLeft==0){
let start_line = $(`#log${version_name} input[name=start_line]`).val()
$.get(`/api/v1/repos/${userName}/${repoPath}/modelarts/train-job/${jobID}/log?version_name=${version_name}&base_line=${start_line}&lines=50&order=asc`, (data) => {
if (data.Lines == 0){


+ 1
- 1
templates/repo/modelarts/trainjob/version_new.tmpl View File

@@ -346,7 +346,7 @@
{{template "base/footer" .}}

<script>
let url_href = location.pathname.split('create_version')[0]
let url_href = location.pathname.split('/create_version')[0]
let url_post = location.pathname
let version_name = location.search.split('?version_name=')[1]
$("#parents_version").val(version_name)


+ 12
- 4
web_src/js/components/DataAnalysis.vue View File

@@ -3,7 +3,7 @@
<el-tabs tab-position="left" v-model="activeName" style="height:100%" @tab-click="handleClick" >
<el-tab-pane label="概览" name="first" >
<span slot="label">
<el-image style="width: 13px; height: 13px" src="/img/overview.png">
<el-image style="width: 13px; height: 13px" src="/img/overview_rgb.svg">
</el-image>
概览
</span>
@@ -13,14 +13,14 @@
<el-tab-pane label="项目分析" name="second" id="second" >
<ProAnalysis ref='ProAnalysis'id="pro" v-if="isRouterAlive"></ProAnalysis>
<span slot="label">
<el-image style="width: 13px; height: 13px" src="/img/pro_new.svg">
<el-image style="width: 13px; height: 13px" src="/img/pro_rgb.svg">
</el-image>
项目分析
</span>
</el-tab-pane>
<el-tab-pane name="third" id='third' >
<span slot='label'>
<el-image style="width: 13px; height: 13px" src="/img/name.png">
<el-image style="width: 13px; height: 13px" src="/img/user_rgb.svg">
</el-image>
用户分析
</span>
@@ -121,6 +121,14 @@
/deep/ .el-tabs__item {
padding: 0px 20px 0px 20px;
}

/deep/ .el-tabs__item.is-active .el-image{
filter:none
}
/deep/ .el-tabs__item:hover .el-image{
filter:none
}
/deep/ .el-image{
filter:grayscale(100%)
}
</style>

+ 11
- 1
web_src/js/components/ProAnalysis.vue View File

@@ -1085,6 +1085,12 @@
return data[0]+''+data[1]+''+data[2]
}
},
goBack(){
if( $("#pro_detail").is(':visible') ){
document.getElementById("pro_main").style.display = "block";
document.getElementById("pro_detail").style.display = "none";
}
},

},
filters:{
@@ -1125,7 +1131,7 @@
return " <a href=\" mailto:" + value.email + "class=\"circular ui button\">" +value.user+ "</a>"
}
},
},
},
@@ -1140,6 +1146,10 @@
this.radarOpenI = this.$echarts.init(document.getElementById('radar_openi'))
this.echartsOITd = this.$echarts.init(document.getElementById('line_openi'))
this.echartsSelectData = this.$echarts.init(document.getElementById('selectData'))
if (window.history && window.history.pushState) {
history.pushState(null, null, document.URL);
window.addEventListener('popstate', this.goBack, false);
}
// window.onresize=function(){
// this.radarOpenI.resize();
// this.echartsOITd.resize();


+ 3
- 3
web_src/js/components/UserAnalysis.vue View File

@@ -27,10 +27,10 @@
</span>
<span style="float:right; margin-right: 20px;" >
<a style="display:inline-block;margin-left: 20px; " id = 'download'>
<a class="el-icon-download" v-if="tableData!=''" :href= "'../tool/query_user_static_page/?startDate='+this.params.startDate+'&endDate='+this.params.endDate+'&IsReturnFile=true'+'&userName='+this.params.userName" ></a>
<a class="el-icon-download" v-if="tableData!=''" :href= "'../api/v1/query_user_static_page/?startDate='+this.params.startDate+'&endDate='+this.params.endDate+'&IsReturnFile=true'+'&userName='+this.params.userName" ></a>
<i class="el-icon-download" v-else="tableData=''" href="#" style="color:rgba(187, 187, 187, 100);" @click='popMark()'></i>
<span >
<a v-if="tableData!=''" :href= "'../tool/query_user_static_page/?startDate='+this.params.startDate+'&endDate='+this.params.endDate+'&IsReturnFile=true'+'&userName='+this.params.userName" >下载报告</a>
<a v-if="tableData!=''" :href= "'../api/v1/query_user_static_page/?startDate='+this.params.startDate+'&endDate='+this.params.endDate+'&IsReturnFile=true'+'&userName='+this.params.userName" >下载报告</a>
<a v-else="tableData=''" href= "#" style="color:rgba(187, 187, 187, 100);" @click='popMark()'>下载报告</a>
</span>
</a>
@@ -335,7 +335,7 @@
}
};

this.$axios.get('../tool/query_user_static_page',{
this.$axios.get('../api/v1/query_user_static_page',{
params:this.params
}).then((res)=>{
this.tableData = res.data.data


Loading…
Cancel
Save