Browse Source

Merge pull request 'file-notebook' (#3272) from file-notebook into V20221130

Reviewed-on: https://openi.pcl.ac.cn/OpenI/aiforge/pulls/3272
Reviewed-by: zouap <zouap@pcl.ac.cn>
tags/v1.22.11.3^2
zouap 3 years ago
parent
commit
909c2da753
34 changed files with 1773 additions and 378 deletions
  1. +9
    -0
      models/resource_specification.go
  2. +1
    -1
      models/user.go
  3. +17
    -4
      modules/cloudbrain/cloudbrain.go
  4. +94
    -8
      modules/modelarts/modelarts.go
  5. +1
    -1
      modules/modelarts/resty.go
  6. +8
    -45
      modules/modelarts_cd/modelarts.go
  7. +29
    -0
      modules/setting/setting.go
  8. +8
    -0
      modules/structs/cloudbrain.go
  9. +3
    -0
      modules/templates/helper.go
  10. +8
    -1
      options/locale/locale_en-US.ini
  11. +7
    -0
      options/locale/locale_zh-CN.ini
  12. +6
    -0
      routers/api/v1/api.go
  13. +72
    -2
      routers/api/v1/repo/cloudbrain.go
  14. +5
    -1
      routers/api/v1/repo/modelarts.go
  15. +4
    -5
      routers/repo/aisafety.go
  16. +12
    -53
      routers/repo/cloudbrain.go
  17. +1
    -3
      routers/repo/grampus.go
  18. +35
    -10
      routers/repo/modelarts.go
  19. +362
    -0
      services/cloudbrain/cloudbrainTask/notebook.go
  20. +16
    -7
      services/cloudbrain/cloudbrainTask/sync_status.go
  21. +12
    -0
      services/cloudbrain/cloudbrainTask/train.go
  22. +26
    -0
      services/cloudbrain/util.go
  23. +2
    -0
      templates/admin/cloudbrain/list.tmpl
  24. +8
    -2
      templates/repo/debugjob/index.tmpl
  25. +1
    -1
      templates/repo/header.tmpl
  26. +29
    -29
      templates/repo/home.tmpl
  27. +6
    -4
      templates/user/dashboard/cloudbrains.tmpl
  28. +23
    -12
      web_src/js/features/cloudrbanin.js
  29. +47
    -0
      web_src/vuepages/apis/modules/notobook.js
  30. +0
    -1
      web_src/vuepages/apis/service.js
  31. +30
    -2
      web_src/vuepages/langs/config/en-US.js
  32. +217
    -186
      web_src/vuepages/langs/config/zh-CN.js
  33. +662
    -0
      web_src/vuepages/pages/notebook/debug/index.vue
  34. +12
    -0
      web_src/vuepages/pages/notebook/debug/vp-notebook-debug.js

+ 9
- 0
models/resource_specification.go View File

@@ -298,6 +298,15 @@ func ResourceSpecOffShelf(id int64) (int64, error) {
return n, err return n, err
} }


func GetResourceSpecificationByIds(ids []int64) ([]*Specification, error) {
r := make([]*Specification, 0)
err := x.In("resource_specification.id", ids).
Join("INNER", "resource_queue", "resource_queue.id = resource_specification.queue_id").
Find(&r)
return r, err

}

func GetResourceSpecification(r *ResourceSpecification) (*ResourceSpecification, error) { func GetResourceSpecification(r *ResourceSpecification) (*ResourceSpecification, error) {
has, err := x.Get(r) has, err := x.Get(r)
if err != nil { if err != nil {


+ 1
- 1
models/user.go View File

@@ -346,7 +346,7 @@ func (u *User) DashboardLink() string {
if u.IsOrganization() { if u.IsOrganization() {
return setting.AppSubURL + "/org/" + u.Name + "/dashboard/" return setting.AppSubURL + "/org/" + u.Name + "/dashboard/"
} }
return setting.AppSubURL + "/"
return setting.AppSubURL + "/dashboard"
} }


// HomeLink returns the user or organization home page link. // HomeLink returns the user or organization home page link.


+ 17
- 4
modules/cloudbrain/cloudbrain.go View File

@@ -145,7 +145,7 @@ func isAdminOrImageCreater(ctx *context.Context, image *models.Image, err error)
func AdminOrOwnerOrJobCreaterRight(ctx *context.Context) { func AdminOrOwnerOrJobCreaterRight(ctx *context.Context) {


var id = ctx.Params(":id") var id = ctx.Params(":id")
job, err := models.GetCloudbrainByID(id)
job, err := GetCloudBrainByIdOrJobId(id)
if err != nil { if err != nil {
log.Error("GetCloudbrainByID failed:%v", err.Error()) log.Error("GetCloudbrainByID failed:%v", err.Error())
ctx.NotFound(ctx.Req.URL.RequestURI(), nil) ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
@@ -161,7 +161,7 @@ func AdminOrOwnerOrJobCreaterRight(ctx *context.Context) {
func AdminOrJobCreaterRight(ctx *context.Context) { func AdminOrJobCreaterRight(ctx *context.Context) {


var id = ctx.Params(":id") var id = ctx.Params(":id")
job, err := models.GetCloudbrainByID(id)
job, err := GetCloudBrainByIdOrJobId(id)
if err != nil { if err != nil {
log.Error("GetCloudbrainByID failed:%v", err.Error()) log.Error("GetCloudbrainByID failed:%v", err.Error())
ctx.NotFound(ctx.Req.URL.RequestURI(), nil) ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
@@ -177,7 +177,7 @@ func AdminOrJobCreaterRight(ctx *context.Context) {
func AdminOrOwnerOrJobCreaterRightForTrain(ctx *context.Context) { func AdminOrOwnerOrJobCreaterRightForTrain(ctx *context.Context) {


var jobID = ctx.Params(":jobid") var jobID = ctx.Params(":jobid")
job, err := models.GetCloudbrainByJobID(jobID)
job, err := GetCloudBrainByIdOrJobId(jobID)
if err != nil { if err != nil {
log.Error("GetCloudbrainByJobID failed:%v", err.Error()) log.Error("GetCloudbrainByJobID failed:%v", err.Error())
ctx.NotFound(ctx.Req.URL.RequestURI(), nil) ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
@@ -193,7 +193,7 @@ func AdminOrOwnerOrJobCreaterRightForTrain(ctx *context.Context) {
func AdminOrJobCreaterRightForTrain(ctx *context.Context) { func AdminOrJobCreaterRightForTrain(ctx *context.Context) {


var jobID = ctx.Params(":jobid") var jobID = ctx.Params(":jobid")
job, err := models.GetCloudbrainByJobID(jobID)
job, err := GetCloudBrainByIdOrJobId(jobID)
if err != nil { if err != nil {
log.Error("GetCloudbrainByJobID failed:%v", err.Error()) log.Error("GetCloudbrainByJobID failed:%v", err.Error())
ctx.NotFound(ctx.Req.URL.RequestURI(), nil) ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
@@ -652,3 +652,16 @@ func IsElementExist(s []string, str string) bool {
} }
return false return false
} }

func GetCloudBrainByIdOrJobId(id string) (*models.Cloudbrain,error) {
_, err := strconv.ParseInt(id, 10, 64)
var job *models.Cloudbrain
if err != nil {

job, err = models.GetCloudbrainByJobID(id)
} else {
job, err = models.GetCloudbrainByID(id)

}
return job,err
}

+ 94
- 8
modules/modelarts/modelarts.go View File

@@ -1,13 +1,18 @@
package modelarts package modelarts


import ( import (
"encoding/base64"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"io/ioutil"
"net/http"
"path" "path"
"strconv" "strconv"
"strings" "strings"


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

"code.gitea.io/gitea/modules/modelarts_cd" "code.gitea.io/gitea/modules/modelarts_cd"


"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
@@ -23,7 +28,7 @@ const (
//notebook //notebook
storageTypeOBS = "obs" storageTypeOBS = "obs"
autoStopDuration = 4 * 60 * 60 autoStopDuration = 4 * 60 * 60
autoStopDurationMs = 4 * 60 * 60 * 1000
AutoStopDurationMs = 4 * 60 * 60 * 1000
MORDELART_USER_IMAGE_ENGINE_ID = -1 MORDELART_USER_IMAGE_ENGINE_ID = -1
DataSetMountPath = "/home/ma-user/work" DataSetMountPath = "/home/ma-user/work"
NotebookEnv = "Python3" NotebookEnv = "Python3"
@@ -276,7 +281,7 @@ func GenerateTask(ctx *context.Context, jobName, uuid, description, flavor strin
return nil return nil
} }


func GenerateNotebook2(ctx *context.Context, displayJobName, jobName, uuid, description, imageId string, spec *models.Specification) error {
func GenerateNotebook2(ctx *context.Context, displayJobName, jobName, uuid, description, imageId string, spec *models.Specification, bootFile string,autoStopDurationInMs int64) (string, error) {
if poolInfos == nil { if poolInfos == nil {
json.Unmarshal([]byte(setting.PoolInfos), &poolInfos) json.Unmarshal([]byte(setting.PoolInfos), &poolInfos)
} }
@@ -284,14 +289,14 @@ func GenerateNotebook2(ctx *context.Context, displayJobName, jobName, uuid, desc
imageName, err := GetNotebookImageName(imageId) imageName, err := GetNotebookImageName(imageId)
if err != nil { if err != nil {
log.Error("GetNotebookImageName failed: %v", err.Error()) log.Error("GetNotebookImageName failed: %v", err.Error())
return err
return "", err
} }
createTime := timeutil.TimeStampNow() createTime := timeutil.TimeStampNow()
jobResult, err := createNotebook2(models.CreateNotebook2Params{ jobResult, err := createNotebook2(models.CreateNotebook2Params{
JobName: jobName, JobName: jobName,
Description: description, Description: description,
Flavor: spec.SourceSpecId, Flavor: spec.SourceSpecId,
Duration: autoStopDurationMs,
Duration: autoStopDurationInMs,
ImageID: imageId, ImageID: imageId,
PoolID: poolInfos.PoolInfo[0].PoolId, PoolID: poolInfos.PoolInfo[0].PoolId,
Feature: models.NotebookFeature, Feature: models.NotebookFeature,
@@ -316,10 +321,10 @@ func GenerateNotebook2(ctx *context.Context, displayJobName, jobName, uuid, desc
}) })
if errTemp != nil { if errTemp != nil {
log.Error("InsertCloudbrainTemp failed: %v", errTemp.Error()) log.Error("InsertCloudbrainTemp failed: %v", errTemp.Error())
return errTemp
return "", errTemp
} }
} }
return err
return "", err
} }
task := &models.Cloudbrain{ task := &models.Cloudbrain{
Status: jobResult.Status, Status: jobResult.Status,
@@ -334,6 +339,7 @@ func GenerateNotebook2(ctx *context.Context, displayJobName, jobName, uuid, desc
Uuid: uuid, Uuid: uuid,
ComputeResource: models.NPUResource, ComputeResource: models.NPUResource,
Image: imageName, Image: imageName,
BootFile: bootFile,
Description: description, Description: description,
CreatedUnix: createTime, CreatedUnix: createTime,
UpdatedUnix: createTime, UpdatedUnix: createTime,
@@ -342,12 +348,12 @@ func GenerateNotebook2(ctx *context.Context, displayJobName, jobName, uuid, desc


err = models.CreateCloudbrain(task) err = models.CreateCloudbrain(task)
if err != nil { if err != nil {
return err
return "", err
} }


stringId := strconv.FormatInt(task.ID, 10) stringId := strconv.FormatInt(task.ID, 10)
notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, stringId, displayJobName, models.ActionCreateDebugNPUTask) notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, stringId, displayJobName, models.ActionCreateDebugNPUTask)
return nil
return jobResult.ID, nil
} }


func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (jobId string, err error) { func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (jobId string, err error) {
@@ -907,6 +913,11 @@ func HandleNotebookInfo(task *models.Cloudbrain) error {
if task.FlavorCode == "" { if task.FlavorCode == "" {
task.FlavorCode = result.Flavor task.FlavorCode = result.Flavor
} }

if oldStatus != task.Status && task.Status == string(models.ModelArtsRunning) && task.BootFile != "" {
uploadNoteBookFile(task, result)

}
err = models.UpdateJob(task) err = models.UpdateJob(task)
if err != nil { if err != nil {
log.Error("UpdateJob(%s) failed:%v", task.DisplayJobName, err) log.Error("UpdateJob(%s) failed:%v", task.DisplayJobName, err)
@@ -917,6 +928,81 @@ func HandleNotebookInfo(task *models.Cloudbrain) error {
return nil return nil
} }


func uploadNoteBookFile(task *models.Cloudbrain, result *models.GetNotebook2Result) {
jupyterUrl := result.Url + "?token=" + result.Token

cookies, xsrf := getCookiesAndCsrf(jupyterUrl)
if xsrf == "" {
log.Error("browser jupyterUrl failed:%v", task.DisplayJobName)
} else {

codePath := setting.JobPath + task.JobName + cloudbrain.CodeMountPath
fileContents, err := ioutil.ReadFile(codePath + "/" + task.BootFile)
if err != nil {
log.Error("read jupyter file failed:%v", task.DisplayJobName, err)
}

base64Content := base64.StdEncoding.EncodeToString(fileContents)
client := getRestyClient()
uploadUrl := getJupyterBaseUrl(result.Url) + "api/contents/" + path.Base(task.BootFile)
res, err := client.R().
SetCookies(cookies).
SetHeader("X-XSRFToken", xsrf).
SetBody(map[string]interface{}{
"type": "file",
"format": "base64",
"name": path.Base(task.BootFile),
"path": path.Base(task.BootFile),
"content": base64Content}).
Put(uploadUrl)
if err != nil {
log.Error("upload jupyter file failed:%v", task.DisplayJobName, err)
} else if res.StatusCode() != http.StatusCreated {
log.Error("upload jupyter file failed:%v", task.DisplayJobName, err)
}

}

}

func getJupyterBaseUrl(url string) string {
jupyterUrlLength := len(url)
baseUrl := url[0 : jupyterUrlLength-len(path.Base(url))]
return baseUrl
}

func getCookiesAndCsrf(jupyterUrl string) ([]*http.Cookie, string) {
log.Info("jupyter url:"+jupyterUrl)
var cookies []*http.Cookie
const retryTimes = 10
for i := 0; i < retryTimes; i++ {
res, err := http.Get(jupyterUrl)
if err != nil {
log.Error("browser jupyterUrl failed.",err)
if i==retryTimes-1{
return cookies, ""
}

} else {
cookies = res.Cookies()
xsrf := ""
for _, cookie := range cookies {
if cookie.Name == "_xsrf" {
xsrf = cookie.Value
break
}

}
if xsrf != "" {
return cookies, xsrf
}

}
}
return cookies, ""

}

func SyncTempStatusJob() { func SyncTempStatusJob() {
jobs, err := models.GetCloudBrainTempJobs() jobs, err := models.GetCloudBrainTempJobs()
if err != nil { if err != nil {


+ 1
- 1
modules/modelarts/resty.go View File

@@ -280,7 +280,7 @@ sendjob:
SetHeader("Content-Type", "application/json"). SetHeader("Content-Type", "application/json").
SetAuthToken(TOKEN). SetAuthToken(TOKEN).
SetResult(&result). SetResult(&result).
Post(HOST + "/v1/" + setting.ProjectID + urlNotebook2 + "/" + jobID + "/" + param.Action + "?duration=" + strconv.Itoa(autoStopDurationMs))
Post(HOST + "/v1/" + setting.ProjectID + urlNotebook2 + "/" + jobID + "/" + param.Action + "?duration=" + strconv.Itoa(AutoStopDurationMs))


if err != nil { if err != nil {
return &result, fmt.Errorf("resty ManageNotebook2: %v", err) return &result, fmt.Errorf("resty ManageNotebook2: %v", err)


+ 8
- 45
modules/modelarts_cd/modelarts.go View File

@@ -88,18 +88,18 @@ type Parameters struct {
} `json:"parameter"` } `json:"parameter"`
} }


func GenerateNotebook(ctx *context.Context, displayJobName, jobName, uuid, description, imageId string, spec *models.Specification) error {
func GenerateNotebook(ctx *context.Context, displayJobName, jobName, uuid, description, imageId string, spec *models.Specification, bootFile string,autoStopDurationInMs int64) (string, error) {
imageName, err := GetNotebookImageName(imageId) imageName, err := GetNotebookImageName(imageId)
if err != nil { if err != nil {
log.Error("GetNotebookImageName failed: %v", err.Error()) log.Error("GetNotebookImageName failed: %v", err.Error())
return err
return "", err
} }
createTime := timeutil.TimeStampNow() createTime := timeutil.TimeStampNow()
jobResult, err := createNotebook(models.CreateNotebookWithoutPoolParams{ jobResult, err := createNotebook(models.CreateNotebookWithoutPoolParams{
JobName: jobName, JobName: jobName,
Description: description, Description: description,
Flavor: spec.SourceSpecId, Flavor: spec.SourceSpecId,
Duration: autoStopDurationMs,
Duration: autoStopDurationInMs,
ImageID: imageId, ImageID: imageId,
Feature: models.NotebookFeature, Feature: models.NotebookFeature,
Volume: models.VolumeReq{ Volume: models.VolumeReq{
@@ -123,10 +123,10 @@ func GenerateNotebook(ctx *context.Context, displayJobName, jobName, uuid, descr
}) })
if errTemp != nil { if errTemp != nil {
log.Error("InsertCloudbrainTemp failed: %v", errTemp.Error()) log.Error("InsertCloudbrainTemp failed: %v", errTemp.Error())
return errTemp
return "", errTemp
} }
} }
return err
return "", err
} }
task := &models.Cloudbrain{ task := &models.Cloudbrain{
Status: jobResult.Status, Status: jobResult.Status,
@@ -145,16 +145,17 @@ func GenerateNotebook(ctx *context.Context, displayJobName, jobName, uuid, descr
CreatedUnix: createTime, CreatedUnix: createTime,
UpdatedUnix: createTime, UpdatedUnix: createTime,
Spec: spec, Spec: spec,
BootFile: bootFile,
} }


err = models.CreateCloudbrain(task) err = models.CreateCloudbrain(task)
if err != nil { if err != nil {
return err
return "", err
} }


stringId := strconv.FormatInt(task.ID, 10) stringId := strconv.FormatInt(task.ID, 10)
notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, stringId, displayJobName, models.ActionCreateDebugNPUTask) notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, stringId, displayJobName, models.ActionCreateDebugNPUTask)
return nil
return jobResult.ID, nil
} }


func GetNotebookImageName(imageId string) (string, error) { func GetNotebookImageName(imageId string) (string, error) {
@@ -175,41 +176,3 @@ func GetNotebookImageName(imageId string) (string, error) {


return imageName, nil return imageName, nil
} }

/*
func HandleNotebookInfo(task *models.Cloudbrain) error {

result, err := GetNotebook(task.JobID)
if err != nil {
log.Error("GetNotebook2(%s) failed:%v", task.DisplayJobName, err)
return err
}

if result != nil {
oldStatus := task.Status
task.Status = result.Status
if task.StartTime == 0 && result.Lease.UpdateTime > 0 {
task.StartTime = timeutil.TimeStamp(result.Lease.UpdateTime / 1000)
}
if task.EndTime == 0 && models.IsModelArtsDebugJobTerminal(task.Status) {
task.EndTime = timeutil.TimeStampNow()
}
task.CorrectCreateUnix()
task.ComputeAndSetDuration()
if oldStatus != task.Status {
notification.NotifyChangeCloudbrainStatus(task, oldStatus)
}
if task.FlavorCode == "" {
task.FlavorCode = result.Flavor
}
err = models.UpdateJob(task)
if err != nil {
log.Error("UpdateJob(%s) failed:%v", task.DisplayJobName, err)
return err
}
}

return nil
}

*/

+ 29
- 0
modules/setting/setting.go View File

@@ -715,6 +715,21 @@ var (
TeamName string TeamName string
}{} }{}


FileNoteBook = struct {
ProjectName string
ImageGPU string
SpecIdGPU int64
SpecIdCPU int64
ImageIdNPU string
SpecIdNPU int64
ImageIdNPUCD string
SpecIdNPUCD int64
ImageCPUDescription string
ImageGPUDescription string
ImageNPUDescription string
ImageNPUCDDescription string
}{}

ModelConvert = struct { ModelConvert = struct {
GPU_PYTORCH_IMAGE string GPU_PYTORCH_IMAGE string
GpuQueue string GpuQueue string
@@ -1580,6 +1595,20 @@ func NewContext() {
Course.OrgName = sec.Key("org_name").MustString("") Course.OrgName = sec.Key("org_name").MustString("")
Course.TeamName = sec.Key("team_name").MustString("") Course.TeamName = sec.Key("team_name").MustString("")


sec = Cfg.Section("file_notebook")
FileNoteBook.ProjectName = sec.Key("project_name").MustString("openi-notebook")
FileNoteBook.ImageIdNPU = sec.Key("imageid_npu").MustString("")
FileNoteBook.ImageGPU = sec.Key("image_gpu").MustString("")
FileNoteBook.SpecIdCPU = sec.Key("specid_cpu").MustInt64(-1)
FileNoteBook.SpecIdGPU = sec.Key("specid_gpu").MustInt64(-1)
FileNoteBook.SpecIdNPU = sec.Key("specid_npu").MustInt64(-1)
FileNoteBook.ImageIdNPUCD = sec.Key("imageid_npu_cd").MustString("")
FileNoteBook.SpecIdNPUCD = sec.Key("specid_npu_cd").MustInt64(-1)
FileNoteBook.ImageCPUDescription = sec.Key("image_cpu_desc").MustString("")
FileNoteBook.ImageGPUDescription = sec.Key("image_gpu_desc").MustString("")
FileNoteBook.ImageNPUDescription = sec.Key("image_npu_desc").MustString("")
FileNoteBook.ImageNPUCDDescription = sec.Key("image_npu_cd_desc").MustString("")

getGrampusConfig() getGrampusConfig()
getModelartsCDConfig() getModelartsCDConfig()
getModelConvertConfig() getModelConvertConfig()


+ 8
- 0
modules/structs/cloudbrain.go View File

@@ -41,6 +41,14 @@ type CreateTrainJobOption struct {
SpecId int64 `json:"spec_id" binding:"Required"` SpecId int64 `json:"spec_id" binding:"Required"`
} }


type CreateFileNotebookJobOption struct {
Type int `json:"type"` //0 CPU 1 GPU 2 NPU
File string `json:"file" binding:"Required"`
BranchName string `json:"branch_name" binding:"Required"`
OwnerName string `json:"owner_name" binding:"Required"`
ProjectName string `json:"project_name" binding:"Required"`
}

type Cloudbrain struct { type Cloudbrain struct {
ID int64 `json:"id"` ID int64 `json:"id"`
JobID string `json:"job_id"` JobID string `json:"job_id"`


+ 3
- 0
modules/templates/helper.go View File

@@ -151,6 +151,9 @@ func NewFuncMap() []template.FuncMap {
"EscapePound": func(str string) string { "EscapePound": func(str string) string {
return strings.NewReplacer("%", "%25", "#", "%23", " ", "%20", "?", "%3F").Replace(str) return strings.NewReplacer("%", "%25", "#", "%23", " ", "%20", "?", "%3F").Replace(str)
}, },
"IpynbBool":func(str string) bool{
return strings.Contains(str, ".ipynb")
},
"nl2br": func(text string) template.HTML { "nl2br": func(text string) template.HTML {
return template.HTML(strings.Replace(template.HTMLEscapeString(text), "\n", "<br>", -1)) return template.HTML(strings.Replace(template.HTMLEscapeString(text), "\n", "<br>", -1))
}, },


+ 8
- 1
options/locale/locale_en-US.ini View File

@@ -1010,6 +1010,8 @@ readme = README
readme_helper = Select a README file template. readme_helper = Select a README file template.
auto_init = Initialize Repository (Adds .gitignore, License and README) auto_init = Initialize Repository (Adds .gitignore, License and README)
create_repo = Create Repository create_repo = Create Repository
failed_to_create_repo=Failed to create repository, please try again later.
failed_to_create_notebook_repo=Failed to create %s repository, please check whether you have the same name project, if yes please update or delete it first.
create_course = Publish Course create_course = Publish Course
failed_to_create_course=Failed to publish course, please try again later. failed_to_create_course=Failed to publish course, please try again later.
default_branch = Default Branch default_branch = Default Branch
@@ -1044,6 +1046,10 @@ model_experience = Model Experience
model_noright=You have no right to do the operation. model_noright=You have no right to do the operation.
model_rename=Duplicate model name, please modify model name. model_rename=Duplicate model name, please modify model name.


notebook_file_not_exist=Notebook file does not exist.
notebook_select_wrong=Please select a Notebook(.ipynb) file first.
notebook_file_no_right=You have no right to access the Notebook(.ipynb) file.

date=Date date=Date
repo_add=Project Increment repo_add=Project Increment
repo_total=Project Total repo_total=Project Total
@@ -1223,7 +1229,7 @@ cloudbrain.benchmark.evaluate_test=Test Script
cloudbrain.benchmark.types={"type":[{"id":1,"rank_link":"https://git.openi.org.cn/benchmark/?username=admin&algType=detection","first":"Target detection","second":[{"id":1,"value":"None","attachment":"84cf39c4-d8bc-41aa-aaa3-182ce289b105","owner":"yangzhx","repo_name":"detection_benchmark_script"}]},{"id":2,"rank_link":"https://git.openi.org.cn/benchmark/?username=admin&algType=reid","first":"Target re-identification","second":[{"id":1,"value":"Vehicle re-identification","attachment":"84cf39c4-d8bc-41aa-aaa3-182ce289b105","owner":"JiahongXu","repo_name":"benchmark_reID_script"},{"id":2,"value":"Image-based person re-identification","attachment":"84cf39c4-d8bc-41aa-aaa3-182ce289b105","owner":"JiahongXu","repo_name":"benchmark_reID_script"}]},{"id":3,"rank_link":"https://git.openi.org.cn/benchmark/?username=admin&algType=tracking","first":"Multi-target tracking","second":[{"id":1,"value":"None","attachment":"84cf39c4-d8bc-41aa-aaa3-182ce289b105","owner":"lix07","repo_name":"MOT_benchmark_script"}]}]} cloudbrain.benchmark.types={"type":[{"id":1,"rank_link":"https://git.openi.org.cn/benchmark/?username=admin&algType=detection","first":"Target detection","second":[{"id":1,"value":"None","attachment":"84cf39c4-d8bc-41aa-aaa3-182ce289b105","owner":"yangzhx","repo_name":"detection_benchmark_script"}]},{"id":2,"rank_link":"https://git.openi.org.cn/benchmark/?username=admin&algType=reid","first":"Target re-identification","second":[{"id":1,"value":"Vehicle re-identification","attachment":"84cf39c4-d8bc-41aa-aaa3-182ce289b105","owner":"JiahongXu","repo_name":"benchmark_reID_script"},{"id":2,"value":"Image-based person re-identification","attachment":"84cf39c4-d8bc-41aa-aaa3-182ce289b105","owner":"JiahongXu","repo_name":"benchmark_reID_script"}]},{"id":3,"rank_link":"https://git.openi.org.cn/benchmark/?username=admin&algType=tracking","first":"Multi-target tracking","second":[{"id":1,"value":"None","attachment":"84cf39c4-d8bc-41aa-aaa3-182ce289b105","owner":"lix07","repo_name":"MOT_benchmark_script"}]}]}
cloudbrain.morethanonejob=You already have a running or waiting task, create it after that task is over. cloudbrain.morethanonejob=You already have a running or waiting task, create it after that task is over.
cloudbrain.morethanonejob1=You have created an <span style="color:rgba(242, 113, 28, 1);"> equivalent task </span> that is waiting or running, please wait for the task to finish before creating it. cloudbrain.morethanonejob1=You have created an <span style="color:rgba(242, 113, 28, 1);"> equivalent task </span> that is waiting or running, please wait for the task to finish before creating it.
cloudbrain.morethanonejob2=You can view all your Cloud Brain tasks in <a href="/cloudbrains" target="_blank"> Home > Cloudbrain Task </a>.
cloudbrain.morethanonejob2=You can view all your Cloud Brain tasks in <a href="/cloudbrains" target="_blank"> Home > Cloudbrain Task </a>.


modelarts.infer_job_model = Model modelarts.infer_job_model = Model
modelarts.infer_job_model_file = Model File modelarts.infer_job_model_file = Model File
@@ -1443,6 +1449,7 @@ blame = Blame
normal_view = Normal View normal_view = Normal View
line = line line = line
lines = lines lines = lines
notebook_open = Open in Notebook


editor.new_file = New File editor.new_file = New File
editor.upload_file = Upload File editor.upload_file = Upload File


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

@@ -1016,6 +1016,8 @@ readme=自述
readme_helper=选择自述文件模板。 readme_helper=选择自述文件模板。
auto_init=初始化存储库 (添加. gitignore、许可证和自述文件) auto_init=初始化存储库 (添加. gitignore、许可证和自述文件)
create_repo=创建项目 create_repo=创建项目
failed_to_create_repo=创建项目失败,请稍后再试。
failed_to_create_notebook_repo=创建项目%s失败,请检查您是否有同名的项目,如果有请先手工修改或删除后重试。
create_course=发布课程 create_course=发布课程
failed_to_create_course=发布课程失败,请稍后再试。 failed_to_create_course=发布课程失败,请稍后再试。
default_branch=默认分支 default_branch=默认分支
@@ -1044,6 +1046,9 @@ model_experience = 模型体验
model_noright=您没有操作权限。 model_noright=您没有操作权限。
model_rename=模型名称重复,请修改模型名称 model_rename=模型名称重复,请修改模型名称


notebook_file_not_exist=Notebook文件不存在。
notebook_select_wrong=请先选择Notebook(.ipynb)文件。
notebook_file_no_right=您没有这个Notebook文件的读权限。


date=日期 date=日期
repo_add=新增项目 repo_add=新增项目
@@ -1462,6 +1467,8 @@ normal_view=普通视图
line=行 line=行
lines=行 lines=行


notebook_open = 在Notebook中打开

editor.new_file=新建文件 editor.new_file=新建文件
editor.upload_file=上传文件 editor.upload_file=上传文件
editor.edit_file=编辑文件 editor.edit_file=编辑文件


+ 6
- 0
routers/api/v1/api.go View File

@@ -737,6 +737,12 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/my_favorite", repo.MyFavoriteDatasetMultiple) m.Get("/my_favorite", repo.MyFavoriteDatasetMultiple)
}, reqToken(), repoAssignment()) }, reqToken(), repoAssignment())


m.Group("/file_notebook", func() {
m.Get("", reqToken(), repo.GetFileNoteBookInfo)
m.Post("/create", reqToken(), reqWeChat(), bind(api.CreateFileNotebookJobOption{}), repo.CreateFileNoteBook)

})

m.Group("/repos", func() { m.Group("/repos", func() {
m.Get("/search", repo.Search) m.Get("/search", repo.Search)




+ 72
- 2
routers/api/v1/repo/cloudbrain.go View File

@@ -78,6 +78,75 @@ func CloudBrainShow(ctx *context.APIContext) {


ctx.JSON(http.StatusOK, models.BaseMessageWithDataApi{Code: 0, Message: "", Data: convert.ToCloudBrain(task)}) ctx.JSON(http.StatusOK, models.BaseMessageWithDataApi{Code: 0, Message: "", Data: convert.ToCloudBrain(task)})


}
func CreateFileNoteBook(ctx *context.APIContext, option api.CreateFileNotebookJobOption) {
cloudbrainTask.FileNotebookCreate(ctx.Context, option)
}

func GetFileNoteBookInfo(ctx *context.APIContext) {
//image description spec description waiting count

specs, err := models.GetResourceSpecificationByIds([]int64{setting.FileNoteBook.SpecIdCPU, setting.FileNoteBook.SpecIdGPU, setting.FileNoteBook.SpecIdNPU, setting.FileNoteBook.SpecIdNPUCD})
if err != nil {
log.Error("Fail to query specifications", err)
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_query_fail")))
return
}

var specCPU, specGpu, specNPU, specNPUCD *api.SpecificationShow
var specGpuQueueCode string
for _, spec := range specs {
if spec.ID == setting.FileNoteBook.SpecIdCPU {
specCPU = convert.ToSpecification(spec)
} else if spec.ID == setting.FileNoteBook.SpecIdGPU {
specGpu = convert.ToSpecification(spec)
specGpuQueueCode = spec.QueueCode
} else if spec.ID == setting.FileNoteBook.SpecIdNPU {
specNPU = convert.ToSpecification(spec)
} else if spec.ID == setting.FileNoteBook.SpecIdNPUCD {
specNPUCD = convert.ToSpecification(spec)
}
}

waitCountNpu := cloudbrain.GetWaitingCloudbrainCount(models.TypeCloudBrainTwo, "")

queuesMap, err := cloudbrain.GetQueuesDetail()
if err != nil {
log.Error("Fail to query gpu queues waiting count", err)
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_query_fail")))
return
}
waitCountGPU := (*queuesMap)[specGpuQueueCode]
if !setting.ModelartsCD.Enabled{
ctx.JSON(http.StatusOK, map[string]interface{}{
"code": 0,
"projectName":setting.FileNoteBook.ProjectName,
"specCpu": specCPU,
"specGpu": specGpu,
"specNpu": specNPU,
"waitCountGpu": waitCountGPU,
"waitCountNpu": waitCountNpu,
"imageCpuDescription": setting.FileNoteBook.ImageCPUDescription,
"imageGpuDescription": setting.FileNoteBook.ImageGPUDescription,
"imageNpuDescription": setting.FileNoteBook.ImageNPUDescription,

})
} else{
ctx.JSON(http.StatusOK, map[string]interface{}{
"code": 0,
"projectName":setting.FileNoteBook.ProjectName,
"specCpu": specCPU,
"specGpu": specGpu,
"specNpu": specNPUCD,
"waitCountGpu": waitCountGPU,
"waitCountNpu": waitCountNpu,
"imageCpuDescription": setting.FileNoteBook.ImageCPUDescription,
"imageGpuDescription": setting.FileNoteBook.ImageGPUDescription,
"imageNpuDescription": setting.FileNoteBook.ImageNPUCDDescription,
})

}

} }


func CreateCloudBrain(ctx *context.APIContext, option api.CreateTrainJobOption) { func CreateCloudBrain(ctx *context.APIContext, option api.CreateTrainJobOption) {
@@ -141,10 +210,11 @@ func GetCloudbrainTask(ctx *context.APIContext) {
) )


ID := ctx.Params(":id") ID := ctx.Params(":id")
job, err := models.GetCloudbrainByID(ID)

job,err := cloudbrain.GetCloudBrainByIdOrJobId(ID)

if err != nil { if err != nil {
ctx.NotFound(err) ctx.NotFound(err)
log.Error("GetCloudbrainByID failed:", err)
return return
} }
if job.JobType == string(models.JobTypeModelSafety) { if job.JobType == string(models.JobTypeModelSafety) {


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

@@ -6,6 +6,7 @@
package repo package repo


import ( import (
"code.gitea.io/gitea/modules/cloudbrain"
"encoding/json" "encoding/json"
"net/http" "net/http"
"path" "path"
@@ -37,11 +38,14 @@ func GetModelArtsNotebook2(ctx *context.APIContext) {
) )


ID := ctx.Params(":id") ID := ctx.Params(":id")
job, err := models.GetCloudbrainByID(ID)

job,err := cloudbrain.GetCloudBrainByIdOrJobId(ID)

if err != nil { if err != nil {
ctx.NotFound(err) ctx.NotFound(err)
return return
} }

err = modelarts.HandleNotebookInfo(job) err = modelarts.HandleNotebookInfo(job)
if err != nil { if err != nil {
ctx.NotFound(err) ctx.NotFound(err)


+ 4
- 5
routers/repo/aisafety.go View File

@@ -11,7 +11,8 @@ import (
"os" "os"
"strconv" "strconv"
"strings" "strings"
"time"

cloudbrainService "code.gitea.io/gitea/services/cloudbrain"


"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/aisafety" "code.gitea.io/gitea/modules/aisafety"
@@ -483,7 +484,6 @@ func isTaskNotFinished(status string) bool {
} }


func AiSafetyCreateForGetGPU(ctx *context.Context) { func AiSafetyCreateForGetGPU(ctx *context.Context) {
t := time.Now()
ctx.Data["PageIsCloudBrain"] = true ctx.Data["PageIsCloudBrain"] = true
ctx.Data["IsCreate"] = true ctx.Data["IsCreate"] = true
ctx.Data["type"] = models.TypeCloudBrainOne ctx.Data["type"] = models.TypeCloudBrainOne
@@ -497,7 +497,7 @@ func AiSafetyCreateForGetGPU(ctx *context.Context) {
log.Info("GPUBaseDataSetUUID=" + setting.ModelSafetyTest.GPUBaseDataSetUUID) log.Info("GPUBaseDataSetUUID=" + setting.ModelSafetyTest.GPUBaseDataSetUUID)
log.Info("GPUCombatDataSetName=" + setting.ModelSafetyTest.GPUCombatDataSetName) log.Info("GPUCombatDataSetName=" + setting.ModelSafetyTest.GPUCombatDataSetName)
log.Info("GPUCombatDataSetUUID=" + setting.ModelSafetyTest.GPUCombatDataSetUUID) log.Info("GPUCombatDataSetUUID=" + setting.ModelSafetyTest.GPUCombatDataSetUUID)
var displayJobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:]
var displayJobName = cloudbrainService.GetDisplayJobName(ctx.User.Name)
ctx.Data["display_job_name"] = displayJobName ctx.Data["display_job_name"] = displayJobName
prepareCloudbrainOneSpecs(ctx) prepareCloudbrainOneSpecs(ctx)
queuesDetail, _ := cloudbrain.GetQueuesDetail() queuesDetail, _ := cloudbrain.GetQueuesDetail()
@@ -514,12 +514,11 @@ func AiSafetyCreateForGetGPU(ctx *context.Context) {
} }


func AiSafetyCreateForGetNPU(ctx *context.Context) { func AiSafetyCreateForGetNPU(ctx *context.Context) {
t := time.Now()
ctx.Data["PageIsCloudBrain"] = true ctx.Data["PageIsCloudBrain"] = true
ctx.Data["IsCreate"] = true ctx.Data["IsCreate"] = true
ctx.Data["type"] = models.TypeCloudBrainTwo ctx.Data["type"] = models.TypeCloudBrainTwo
ctx.Data["compute_resource"] = models.NPUResource ctx.Data["compute_resource"] = models.NPUResource
var displayJobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:]
var displayJobName = cloudbrainService.GetDisplayJobName(ctx.User.Name)
ctx.Data["display_job_name"] = displayJobName ctx.Data["display_job_name"] = displayJobName
ctx.Data["datasetType"] = models.TypeCloudBrainTwo ctx.Data["datasetType"] = models.TypeCloudBrainTwo
ctx.Data["BaseDataSetName"] = setting.ModelSafetyTest.NPUBaseDataSetName ctx.Data["BaseDataSetName"] = setting.ModelSafetyTest.NPUBaseDataSetName


+ 12
- 53
routers/repo/cloudbrain.go View File

@@ -15,6 +15,8 @@ import (
"time" "time"
"unicode/utf8" "unicode/utf8"


cloudbrainService "code.gitea.io/gitea/services/cloudbrain"

"code.gitea.io/gitea/modules/urfs_client/urchin" "code.gitea.io/gitea/modules/urfs_client/urchin"


"code.gitea.io/gitea/modules/dataset" "code.gitea.io/gitea/modules/dataset"
@@ -92,28 +94,9 @@ func MustEnableCloudbrain(ctx *context.Context) {
} }
} }


func cutString(str string, lens int) string {
if len(str) < lens {
return str
}
return str[:lens]
}

func jobNamePrefixValid(s string) string {
lowStr := strings.ToLower(s)
re := regexp.MustCompile(`[^a-z0-9_\\-]+`)

removeSpecial := re.ReplaceAllString(lowStr, "")

re = regexp.MustCompile(`^[_\\-]+`)
return re.ReplaceAllString(removeSpecial, "")

}

func cloudBrainNewDataPrepare(ctx *context.Context, jobType string) error { func cloudBrainNewDataPrepare(ctx *context.Context, jobType string) error {
ctx.Data["PageIsCloudBrain"] = true ctx.Data["PageIsCloudBrain"] = true
t := time.Now()
var displayJobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:]
var displayJobName = cloudbrainService.GetDisplayJobName(ctx.User.Name)
ctx.Data["display_job_name"] = displayJobName ctx.Data["display_job_name"] = displayJobName


ctx.Data["command"] = cloudbrain.GetCloudbrainDebugCommand() ctx.Data["command"] = cloudbrain.GetCloudbrainDebugCommand()
@@ -696,7 +679,7 @@ func CloudBrainRestart(ctx *context.Context) {
} else { } else {
if count >= 1 { if count >= 1 {
log.Error("the user already has running or waiting task", ctx.Data["MsgID"]) log.Error("the user already has running or waiting task", ctx.Data["MsgID"])
resultCode = "-1"
resultCode = "2"
errorMsg = ctx.Tr("repo.cloudbrain.morethanonejob") errorMsg = ctx.Tr("repo.cloudbrain.morethanonejob")
break break
} }
@@ -759,43 +742,13 @@ func cloudBrainShow(ctx *context.Context, tpName base.TplName, jobType models.Jo
return return
} }
if task.Status == string(models.JobWaiting) || task.Status == string(models.JobRunning) { if task.Status == string(models.JobWaiting) || task.Status == string(models.JobRunning) {
result, err := cloudbrain.GetJob(task.JobID)
task, err = cloudbrainTask.SyncCloudBrainOneStatus(task)
if err != nil { if err != nil {
log.Info("error:" + err.Error()) log.Info("error:" + err.Error())
ctx.NotFound(ctx.Req.URL.RequestURI(), nil) ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
return return
} }


if result != nil {
jobRes, _ := models.ConvertToJobResultPayload(result.Payload)
taskRoles := jobRes.TaskRoles
taskRes, _ := models.ConvertToTaskPod(taskRoles[cloudbrain.SubTaskName].(map[string]interface{}))
ctx.Data["taskRes"] = taskRes
ctx.Data["ExitDiagnostics"] = taskRes.TaskStatuses[0].ExitDiagnostics
oldStatus := task.Status
task.Status = taskRes.TaskStatuses[0].State
task.ContainerIp = ""
task.ContainerID = taskRes.TaskStatuses[0].ContainerID
models.ParseAndSetDurationFromCloudBrainOne(jobRes, task)

if task.DeletedAt.IsZero() { //normal record
if oldStatus != task.Status {
notification.NotifyChangeCloudbrainStatus(task, oldStatus)
}
err = models.UpdateJob(task)
if err != nil {
ctx.Data["error"] = err.Error()
return
}
} else { //deleted record

}

ctx.Data["result"] = jobRes
} else {
log.Info("error:" + err.Error())
return
}
} }


user, err := models.GetUserByID(task.UserID) user, err := models.GetUserByID(task.UserID)
@@ -889,7 +842,13 @@ func cloudBrainShow(ctx *context.Context, tpName base.TplName, jobType models.Jo
func CloudBrainDebug(ctx *context.Context) { func CloudBrainDebug(ctx *context.Context) {
task := ctx.Cloudbrain task := ctx.Cloudbrain
debugUrl := setting.DebugServerHost + "jpylab_" + task.JobID + "_" + task.SubTaskName debugUrl := setting.DebugServerHost + "jpylab_" + task.JobID + "_" + task.SubTaskName
ctx.Redirect(debugUrl)
if task.BootFile!=""{
ctx.Redirect(getFileUrl(debugUrl,task.BootFile))

}else{
ctx.Redirect(debugUrl)
}

} }


func prepareSpec4Show(ctx *context.Context, task *models.Cloudbrain) { func prepareSpec4Show(ctx *context.Context, task *models.Cloudbrain) {


+ 1
- 3
routers/repo/grampus.go View File

@@ -10,7 +10,6 @@ import (
"path" "path"
"strconv" "strconv"
"strings" "strings"
"time"


"code.gitea.io/gitea/modules/urfs_client/urchin" "code.gitea.io/gitea/modules/urfs_client/urchin"
"code.gitea.io/gitea/routers/response" "code.gitea.io/gitea/routers/response"
@@ -77,8 +76,7 @@ func GrampusTrainJobNPUNew(ctx *context.Context) {
func grampusTrainJobNewDataPrepare(ctx *context.Context, processType string) error { func grampusTrainJobNewDataPrepare(ctx *context.Context, processType string) error {
ctx.Data["PageIsCloudBrain"] = true ctx.Data["PageIsCloudBrain"] = true


t := time.Now()
var displayJobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:]
var displayJobName = cloudbrainService.GetDisplayJobName(ctx.User.Name)
ctx.Data["display_job_name"] = displayJobName ctx.Data["display_job_name"] = displayJobName


//get valid images //get valid images


+ 35
- 10
routers/repo/modelarts.go View File

@@ -15,6 +15,8 @@ import (
"time" "time"
"unicode/utf8" "unicode/utf8"


cloudbrainService "code.gitea.io/gitea/services/cloudbrain"

"code.gitea.io/gitea/services/cloudbrain/cloudbrainTask" "code.gitea.io/gitea/services/cloudbrain/cloudbrainTask"


"code.gitea.io/gitea/modules/dataset" "code.gitea.io/gitea/modules/dataset"
@@ -128,8 +130,7 @@ func NotebookNew(ctx *context.Context) {


func notebookNewDataPrepare(ctx *context.Context) error { func notebookNewDataPrepare(ctx *context.Context) error {
ctx.Data["PageIsCloudBrain"] = true ctx.Data["PageIsCloudBrain"] = true
t := time.Now()
var displayJobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:]
var displayJobName = cloudbrainService.GetDisplayJobName(ctx.User.Name)
ctx.Data["display_job_name"] = displayJobName ctx.Data["display_job_name"] = displayJobName


attachs, err := models.GetModelArtsUserAttachments(ctx.User.ID) attachs, err := models.GetModelArtsUserAttachments(ctx.User.ID)
@@ -239,9 +240,9 @@ func Notebook2Create(ctx *context.Context, form auth.CreateModelArtsNotebookForm
} }


if setting.ModelartsCD.Enabled { if setting.ModelartsCD.Enabled {
err = modelarts_cd.GenerateNotebook(ctx, displayJobName, jobName, uuid, description, imageId, spec)
_, err = modelarts_cd.GenerateNotebook(ctx, displayJobName, jobName, uuid, description, imageId, spec, "",modelarts.AutoStopDurationMs)
} else { } else {
err = modelarts.GenerateNotebook2(ctx, displayJobName, jobName, uuid, description, imageId, spec)
_, err = modelarts.GenerateNotebook2(ctx, displayJobName, jobName, uuid, description, imageId, spec, "",modelarts.AutoStopDurationMs)
} }


if err != nil { if err != nil {
@@ -387,8 +388,33 @@ func NotebookDebug2(ctx *context.Context) {
ctx.RenderWithErr(err.Error(), tplModelArtsNotebookIndex, nil) ctx.RenderWithErr(err.Error(), tplModelArtsNotebookIndex, nil)
return return
} }
if task.BootFile!=""{
ctx.Redirect(getFileUrl(result.Url,task.BootFile) + "?token="+ result.Token)
}else{
ctx.Redirect(result.Url + "?token=" + result.Token)
}



ctx.Redirect(result.Url + "?token=" + result.Token)
}

func getFileUrl(url string,filename string) string{
middle:=""
if url[len(url)-3:]=="lab" || url[len(url)-4:]=="lab/" {
if url[len(url)-1] == '/' {
middle="tree/"
} else {
middle= "/tree/"
}
}else{
if url[len(url)-1] == '/' {
middle = "lab/tree/"
} else {
middle= "/lab/tree/"
}
}


return url+middle+path.Base(filename)
} }


func NotebookRestart(ctx *context.Context) { func NotebookRestart(ctx *context.Context) {
@@ -420,7 +446,8 @@ func NotebookRestart(ctx *context.Context) {
} else { } else {
if count >= 1 { if count >= 1 {
log.Error("the user already has running or waiting task", ctx.Data["MsgID"]) log.Error("the user already has running or waiting task", ctx.Data["MsgID"])
errorMsg = "you have already a running or waiting task, can not create more"
resultCode="2"
errorMsg = ctx.Tr("repo.cloudbrain.morethanonejob")
break break
} }
} }
@@ -714,8 +741,7 @@ func trainJobNewDataPrepare(ctx *context.Context) error {
// return // return
//} //}


t := time.Now()
var displayJobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:]
var displayJobName = cloudbrainService.GetDisplayJobName(ctx.User.Name)
ctx.Data["display_job_name"] = displayJobName ctx.Data["display_job_name"] = displayJobName


attachs, err := models.GetModelArtsTrainAttachments(ctx.User.ID) attachs, err := models.GetModelArtsTrainAttachments(ctx.User.ID)
@@ -2351,8 +2377,7 @@ func inferenceJobNewDataPrepare(ctx *context.Context) error {
ctx.Data["PageIsCloudBrain"] = true ctx.Data["PageIsCloudBrain"] = true
ctx.Data["newInference"] = true ctx.Data["newInference"] = true


t := time.Now()
var displayJobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:]
var displayJobName = cloudbrainService.GetDisplayJobName(ctx.User.Name)
ctx.Data["display_job_name"] = displayJobName ctx.Data["display_job_name"] = displayJobName


attachs, err := models.GetModelArtsTrainAttachments(ctx.User.ID) attachs, err := models.GetModelArtsTrainAttachments(ctx.User.ID)


+ 362
- 0
services/cloudbrain/cloudbrainTask/notebook.go View File

@@ -0,0 +1,362 @@
package cloudbrainTask

import (
"fmt"
"net/http"
"path"

"code.gitea.io/gitea/modules/modelarts"
"code.gitea.io/gitea/modules/modelarts_cd"

"code.gitea.io/gitea/modules/git"

"code.gitea.io/gitea/modules/cloudbrain"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/redis/redis_key"
"code.gitea.io/gitea/modules/redis/redis_lock"
"code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/services/cloudbrain/resource"
"code.gitea.io/gitea/services/reward/point/account"

"code.gitea.io/gitea/modules/setting"
cloudbrainService "code.gitea.io/gitea/services/cloudbrain"
repo_service "code.gitea.io/gitea/services/repository"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
)

const NoteBookExtension = ".ipynb"

func FileNotebookCreate(ctx *context.Context, option api.CreateFileNotebookJobOption) {

if ctx.Written() {
return
}

if path.Ext(option.File) != NoteBookExtension {
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_select_wrong")))
return
}

isNotebookFileExist, _ := isNoteBookFileExist(ctx, option)
if !isNotebookFileExist {
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_file_not_exist")))
return
}

sourceRepo, err := models.GetRepositoryByOwnerAndName(option.OwnerName, option.ProjectName)
if err != nil {
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_file_not_exist")))
return
}

permission, err := models.GetUserRepoPermission(sourceRepo, ctx.User)
if err != nil {
log.Error("Get permission failed", err)
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_file_no_right")))
return
}

if !permission.CanRead(models.UnitTypeCode) {
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_file_no_right")))
return
}

//create repo if not exist
repo, err := models.GetRepositoryByName(ctx.User.ID, setting.FileNoteBook.ProjectName)
if repo == nil {
repo, err = repo_service.CreateRepository(ctx.User, ctx.User, models.CreateRepoOptions{
Name: setting.FileNoteBook.ProjectName,
Alias: "",
Description: "",
IssueLabels: "",
Gitignores: "",
License: "",
Readme: "Default",
IsPrivate: false,
AutoInit: true,
DefaultBranch: "master",
})
}
if err != nil {
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.failed_to_create_notebook_repo",setting.FileNoteBook.ProjectName)))
return
}
if option.Type <= 1 {
cloudBrainFileNoteBookCreate(ctx, option, repo, sourceRepo)
} else {
modelartsFileNoteBookCreate(ctx, option, repo, sourceRepo)
}

}

func cloudBrainFileNoteBookCreate(ctx *context.Context, option api.CreateFileNotebookJobOption, repo *models.Repository, sourceRepo *models.Repository) {

displayJobName := cloudbrainService.GetDisplayJobName(ctx.User.Name)
jobName := util.ConvertDisplayJobNameToJobName(displayJobName)
jobType := string(models.JobTypeDebug)

lock := redis_lock.NewDistributeLock(redis_key.CloudbrainBindingJobNameKey(fmt.Sprint(repo.ID), jobType, displayJobName))
defer lock.UnLock()
isOk, err := lock.Lock(models.CloudbrainKeyDuration)
if !isOk {
log.Error("lock processed failed:%v", err, ctx.Data["MsgID"])
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.cloudbrain_samejob_err")))
return
}

tasks, err := models.GetCloudbrainsByDisplayJobName(repo.ID, jobType, displayJobName)
if err == nil {
if len(tasks) != 0 {
log.Error("the job name did already exist", ctx.Data["MsgID"])
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.cloudbrain_samejob_err")))
return
}
} else {
if !models.IsErrJobNotExist(err) {
log.Error("system error, %v", err, ctx.Data["MsgID"])
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi("system error."))
return
}
}

count, err := GetNotFinalStatusTaskCount(ctx.User.ID, models.TypeCloudBrainOne, jobType)
if err != nil {
log.Error("GetCloudbrainCountByUserID failed:%v", err, ctx.Data["MsgID"])
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi("system error."))
return
} else {
if count >= 1 {
log.Error("the user already has running or waiting task", ctx.Data["MsgID"])
ctx.JSON(http.StatusOK,models.BaseMessageApi{
Code: 2,
Message: ctx.Tr("repo.cloudbrain.morethanonejob"),
})
return
}
}

errStr := uploadCodeFile(sourceRepo, getCodePath(jobName), option.BranchName, option.File, jobName)
if errStr != "" {
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_file_not_exist")))
return
}
command := cloudbrain.GetCloudbrainDebugCommand()
specId := setting.FileNoteBook.SpecIdGPU
if option.Type == 0 {
specId = setting.FileNoteBook.SpecIdCPU
}
spec, err := resource.GetAndCheckSpec(ctx.User.ID, specId, models.FindSpecsOptions{
JobType: models.JobType(jobType),
ComputeResource: models.GPU,
Cluster: models.OpenICluster,
AiCenterCode: models.AICenterOfCloudBrainOne})
if err != nil || spec == nil {
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("cloudbrain.wrong_specification")))
return
}

if !account.IsPointBalanceEnough(ctx.User.ID, spec.UnitPrice) {
log.Error("point balance is not enough,userId=%d specId=%d", ctx.User.ID, spec.ID)
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("points.insufficient_points_balance")))
return
}
ctx.Repo = &context.Repository{
Repository: repo,
}

req := cloudbrain.GenerateCloudBrainTaskReq{
Ctx: ctx,
DisplayJobName: displayJobName,
JobName: jobName,
Image: setting.FileNoteBook.ImageGPU,
Command: command,
Uuids: "",
DatasetNames: "",
DatasetInfos: nil,
CodePath: storage.GetMinioPath(jobName, cloudbrain.CodeMountPath+"/"),
ModelPath: storage.GetMinioPath(jobName, cloudbrain.ModelMountPath+"/"),
BenchmarkPath: storage.GetMinioPath(jobName, cloudbrain.BenchMarkMountPath+"/"),
Snn4ImageNetPath: storage.GetMinioPath(jobName, cloudbrain.Snn4imagenetMountPath+"/"),
BrainScorePath: storage.GetMinioPath(jobName, cloudbrain.BrainScoreMountPath+"/"),
JobType: jobType,
Description: getDescription(option),
BranchName: option.BranchName,
BootFile: option.File,
Params: "{\"parameter\":[]}",
CommitID: "",
BenchmarkTypeID: 0,
BenchmarkChildTypeID: 0,
ResultPath: storage.GetMinioPath(jobName, cloudbrain.ResultPath+"/"),
Spec: spec,
}

jobId, err := cloudbrain.GenerateTask(req)
if err != nil {
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(err.Error()))
return
}
ctx.JSON(http.StatusOK, models.BaseMessageApi{
Code: 0,
Message: jobId,
})

}

func getCodePath(jobName string) string {
return setting.JobPath + jobName + cloudbrain.CodeMountPath
}

func getDescription(option api.CreateFileNotebookJobOption) string {
return option.OwnerName + "/" + option.ProjectName + "/" + option.File
}

func modelartsFileNoteBookCreate(ctx *context.Context, option api.CreateFileNotebookJobOption, repo *models.Repository, sourceRepo *models.Repository) {
displayJobName := cloudbrainService.GetDisplayJobName(ctx.User.Name)
jobName := util.ConvertDisplayJobNameToJobName(displayJobName)

lock := redis_lock.NewDistributeLock(redis_key.CloudbrainBindingJobNameKey(fmt.Sprint(repo.ID), string(models.JobTypeDebug), displayJobName))
isOk, err := lock.Lock(models.CloudbrainKeyDuration)
if !isOk {
log.Error("lock processed failed:%v", err, ctx.Data["MsgID"])
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.cloudbrain_samejob_err")))
return
}
defer lock.UnLock()

count, err := GetNotFinalStatusTaskCount(ctx.User.ID, models.TypeCloudBrainTwo, string(models.JobTypeDebug))

if err != nil {
log.Error("GetCloudbrainNotebookCountByUserID failed:%v", err, ctx.Data["MsgID"])

ctx.JSON(http.StatusOK, models.BaseErrorMessageApi("system error."))
return
} else {
if count >= 1 {
log.Error("the user already has running or waiting task", ctx.Data["MsgID"])
ctx.JSON(http.StatusOK,models.BaseMessageApi{
Code: 2,
Message: ctx.Tr("repo.cloudbrain.morethanonejob"),
})
return
}
}

tasks, err := models.GetCloudbrainsByDisplayJobName(repo.ID, string(models.JobTypeDebug), displayJobName)
if err == nil {
if len(tasks) != 0 {
log.Error("the job name did already exist", ctx.Data["MsgID"])
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.cloudbrain_samejob_err")))
return
}
} else {
if !models.IsErrJobNotExist(err) {
log.Error("system error, %v", err, ctx.Data["MsgID"])
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi("system error."))
return
}
}

err = downloadCode(sourceRepo, getCodePath(jobName), option.BranchName)
if err != nil {
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("cloudbrain.load_code_failed")))
return
}

var aiCenterCode = models.AICenterOfCloudBrainTwo
var specId = setting.FileNoteBook.SpecIdNPU
if setting.ModelartsCD.Enabled {
aiCenterCode = models.AICenterOfChengdu
specId = setting.FileNoteBook.SpecIdNPUCD
}
spec, err := resource.GetAndCheckSpec(ctx.User.ID, specId, models.FindSpecsOptions{
JobType: models.JobTypeDebug,
ComputeResource: models.NPU,
Cluster: models.OpenICluster,
AiCenterCode: aiCenterCode})
if err != nil || spec == nil {
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("cloudbrain.wrong_specification")))
return
}
if !account.IsPointBalanceEnough(ctx.User.ID, spec.UnitPrice) {
log.Error("point balance is not enough,userId=%d specId=%d ", ctx.User.ID, spec.ID)
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("points.insufficient_points_balance")))
return
}
ctx.Repo = &context.Repository{
Repository: repo,
}

var jobId string
if setting.ModelartsCD.Enabled {
jobId, err = modelarts_cd.GenerateNotebook(ctx, displayJobName, jobName, "", getDescription(option), setting.FileNoteBook.ImageIdNPUCD, spec, option.File,modelarts.AutoStopDurationMs/4)
} else {
jobId, err = modelarts.GenerateNotebook2(ctx, displayJobName, jobName, "", getDescription(option), setting.FileNoteBook.ImageIdNPU, spec, option.File,modelarts.AutoStopDurationMs/4)
}

if err != nil {
log.Error("GenerateNotebook2 failed, %v", err, ctx.Data["MsgID"])

ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(err.Error()))

return
}

ctx.JSON(http.StatusOK, models.BaseMessageApi{
Code: 0,
Message: jobId,
})

}

func isNoteBookFileExist(ctx *context.Context, option api.CreateFileNotebookJobOption) (bool, error) {
repoPathOfNoteBook := models.RepoPath(option.OwnerName, option.ProjectName)

gitRepoOfNoteBook, err := git.OpenRepository(repoPathOfNoteBook)
if err != nil {
log.Error("RepoRef Invalid repo "+repoPathOfNoteBook, err.Error())
return false, err
}
// We opened it, we should close it
defer func() {
// If it's been set to nil then assume someone else has closed it.
if gitRepoOfNoteBook != nil {
gitRepoOfNoteBook.Close()
}
}()
fileExist, err := fileExists(gitRepoOfNoteBook, option.File, option.BranchName)
if err != nil || !fileExist {
log.Error("Get file error:", err, ctx.Data["MsgID"])

return false, err
}
return true, nil
}

func uploadCodeFile(repo *models.Repository, codePath string, branchName string, filePath string, jobName string) string {
err := downloadCode(repo, codePath, branchName)
if err != nil {
return "cloudbrain.load_code_failed"
}

err = uploadOneFileToMinio(codePath, filePath, jobName, cloudbrain.CodeMountPath+"/")
if err != nil {
return "cloudbrain.load_code_failed"
}
return ""
}

func fileExists(gitRepo *git.Repository, path string, branch string) (bool, error) {

commit, err := gitRepo.GetBranchCommit(branch)
if err != nil {
return false, err
}
if _, err := commit.GetTreeEntryByPath(path); err != nil {
return false, err
}
return true, nil
}

+ 16
- 7
services/cloudbrain/cloudbrainTask/sync_status.go View File

@@ -1,20 +1,21 @@
package cloudbrainTask package cloudbrainTask


import ( import (
"net/http"

"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/cloudbrain" "code.gitea.io/gitea/modules/cloudbrain"
"code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification" "code.gitea.io/gitea/modules/notification"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"net/http"
"strconv"
) )


var noteBookOKMap = make(map[int64]int, 20) var noteBookOKMap = make(map[int64]int, 20)
var noteBookFailMap = make(map[int64]int, 20)


//if a task notebook url can get two times, the notebook can browser.
//if a task notebook url can get successfulCount times, the notebook can browser.
const successfulCount = 3 const successfulCount = 3
const maxSuccessfulCount=10


func SyncCloudBrainOneStatus(task *models.Cloudbrain) (*models.Cloudbrain, error) { func SyncCloudBrainOneStatus(task *models.Cloudbrain) (*models.Cloudbrain, error) {
jobResult, err := cloudbrain.GetJob(task.JobID) jobResult, err := cloudbrain.GetJob(task.JobID)
@@ -62,21 +63,29 @@ func isNoteBookReady(task *models.Cloudbrain) bool {
return true return true
} }
noteBookUrl := setting.DebugServerHost + "jpylab_" + task.JobID + "_" + task.SubTaskName noteBookUrl := setting.DebugServerHost + "jpylab_" + task.JobID + "_" + task.SubTaskName
r := httplib.Get(noteBookUrl)
res, err := r.Response()
res,err := http.Get(noteBookUrl)
if err != nil { if err != nil {
return false return false
} }
log.Info("notebook success count:"+strconv.Itoa(noteBookOKMap[task.ID])+",fail count:"+strconv.Itoa(noteBookFailMap[task.ID]))
if res.StatusCode == http.StatusOK { if res.StatusCode == http.StatusOK {
count := noteBookOKMap[task.ID] count := noteBookOKMap[task.ID]
if count < successfulCount-1 {
if count==0{ //如果是第一次成功,把失败数重置为0
noteBookFailMap[task.ID]=0
}

if count < successfulCount-1 || (noteBookFailMap[task.ID]==0 && count < maxSuccessfulCount-1) {
noteBookOKMap[task.ID] = count + 1 noteBookOKMap[task.ID] = count + 1
return false return false
} else { } else {
log.Info("notebook success count:"+strconv.Itoa(count)+",fail count:"+strconv.Itoa(noteBookFailMap[task.ID]))
delete(noteBookOKMap, task.ID) delete(noteBookOKMap, task.ID)
delete(noteBookFailMap, task.ID)
return true return true
} }


}else{
noteBookFailMap[task.ID]+=1
} }
return false return false




+ 12
- 0
services/cloudbrain/cloudbrainTask/train.go View File

@@ -810,6 +810,18 @@ func uploadCodeToMinio(codePath, jobName, parentDir string) error {
return nil return nil
} }


func uploadOneFileToMinio(codePath, filePath, jobName, parentDir string) error {
destObject := setting.CBCodePathPrefix + jobName + parentDir + path.Base(filePath)
sourceFile := codePath + "/" + filePath
err := storage.Attachments.UploadObject(destObject, sourceFile)
if err != nil {
log.Error("UploadObject(%s) failed: %s", filePath, err.Error())
return err
}
return nil

}

func readDir(dirname string) ([]os.FileInfo, error) { func readDir(dirname string) ([]os.FileInfo, error) {
f, err := os.Open(dirname) f, err := os.Open(dirname)
if err != nil { if err != nil {


+ 26
- 0
services/cloudbrain/util.go View File

@@ -1,7 +1,11 @@
package cloudbrain package cloudbrain


import ( import (
"regexp"
"strconv"
"strings" "strings"
"time"



"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
@@ -33,6 +37,28 @@ func GetAiCenterShow(aiCenter string, ctx *context.Context) string {


} }


func GetDisplayJobName(username string) string {
t := time.Now()
return jobNamePrefixValid(cutString(username, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:]
}

func cutString(str string, lens int) string {
if len(str) < lens {
return str
}
return str[:lens]
}

func jobNamePrefixValid(s string) string {
lowStr := strings.ToLower(s)
re := regexp.MustCompile(`[^a-z0-9_\\-]+`)

removeSpecial := re.ReplaceAllString(lowStr, "")

re = regexp.MustCompile(`^[_\\-]+`)
return re.ReplaceAllString(removeSpecial, "")
}

func GetAiCenterInfoByCenterCode(aiCenterCode string) *setting.C2NetSequenceInfo { func GetAiCenterInfoByCenterCode(aiCenterCode string) *setting.C2NetSequenceInfo {
if setting.AiCenterCodeAndNameMapInfo != nil { if setting.AiCenterCodeAndNameMapInfo != nil {
if info, ok := setting.AiCenterCodeAndNameMapInfo[aiCenterCode]; ok { if info, ok := setting.AiCenterCodeAndNameMapInfo[aiCenterCode]; ok {


+ 2
- 0
templates/admin/cloudbrain/list.tmpl View File

@@ -238,6 +238,7 @@
{{$.i18n.Tr "repo.debug"}} {{$.i18n.Tr "repo.debug"}}
</a> </a>
{{else}} {{else}}
{{if not .BootFile}}
<a id="ai-debug-{{$JobID}}" <a id="ai-debug-{{$JobID}}"
class='ui basic ai_debug {{if eq .Status "CREATING" "STOPPING" "WAITING" "STARTING"}} disabled {{else}}blue {{end}}button' class='ui basic ai_debug {{if eq .Status "CREATING" "STOPPING" "WAITING" "STARTING"}} disabled {{else}}blue {{end}}button'
data-jobid="{{$JobID}}" data-jobid="{{$JobID}}"
@@ -245,6 +246,7 @@
{{$.i18n.Tr "repo.debug_again"}} {{$.i18n.Tr "repo.debug_again"}}
</a> </a>
{{end}} {{end}}
{{end}}
</form> </form>
</div> </div>
{{end}} {{end}}


+ 8
- 2
templates/repo/debugjob/index.tmpl View File

@@ -221,7 +221,7 @@
<!--任务状态 --> <!--任务状态 -->
<span class="job-status" id="{{.Cloudbrain.ID}}" <span class="job-status" id="{{.Cloudbrain.ID}}"
data-repopath="{{$.RepoRelPath}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}" data-repopath="{{$.RepoRelPath}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}"
data-jobid="{{.Cloudbrain.ID}}" data-resource="{{.ComputeResource}}">
data-jobid="{{.Cloudbrain.ID}}" data-resource="{{.ComputeResource}}" data-bootfile="{{.BootFile}}">
<span><i id="{{.Cloudbrain.ID}}-icon" style="vertical-align: middle;" <span><i id="{{.Cloudbrain.ID}}-icon" style="vertical-align: middle;"
class="{{.Status}}"></i><span id="{{.Cloudbrain.ID}}-text" class="{{.Status}}"></i><span id="{{.Cloudbrain.ID}}-text"
style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span> style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span>
@@ -266,6 +266,7 @@
{{$.i18n.Tr "repo.debug"}} {{$.i18n.Tr "repo.debug"}}
</a> </a>
{{else}} {{else}}
{{if not .BootFile}}
<a id="ai-debug-{{.Cloudbrain.ID}}" <a id="ai-debug-{{.Cloudbrain.ID}}"
class='ui basic ai_debug {{if eq .Status "CREATING" "STOPPING" "WAITING" "STARTING"}} disabled {{else}}blue {{end}}button' class='ui basic ai_debug {{if eq .Status "CREATING" "STOPPING" "WAITING" "STARTING"}} disabled {{else}}blue {{end}}button'
data-jobid="{{.Cloudbrain.ID}}" data-jobid="{{.Cloudbrain.ID}}"
@@ -274,6 +275,7 @@
{{$.i18n.Tr "repo.debug_again"}} {{$.i18n.Tr "repo.debug_again"}}
</a> </a>
{{end}} {{end}}
{{end}}
{{else}} {{else}}
{{if eq .Status "RUNNING" "WAITING" "CREATING" "STARTING"}} {{if eq .Status "RUNNING" "WAITING" "CREATING" "STARTING"}}
<a class="ui basic disabled button"> <a class="ui basic disabled button">
@@ -294,7 +296,9 @@
<a id="ai-stop-{{.Cloudbrain.ID}}" <a id="ai-stop-{{.Cloudbrain.ID}}"
class='ui basic ai_stop {{if eq .Status "STOPPED" "FAILED" "START_FAILED" "STOPPING" "CREATING" "STARTING" "SUCCEEDED" "CREATE_FAILED" "DELETED"}}disabled {{else}}blue {{end}}button' class='ui basic ai_stop {{if eq .Status "STOPPED" "FAILED" "START_FAILED" "STOPPING" "CREATING" "STARTING" "SUCCEEDED" "CREATE_FAILED" "DELETED"}}disabled {{else}}blue {{end}}button'
data-repopath="{{$.RepoLink}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}/{{.Cloudbrain.ID}}/stop" data-repopath="{{$.RepoLink}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}/{{.Cloudbrain.ID}}/stop"
data-jobid="{{.Cloudbrain.ID}}">
data-jobid="{{.Cloudbrain.ID}}"
{{if .BootFile}}data-bootfile="{{.BootFile}}"{{end}}>
{{$.i18n.Tr "repo.stop"}} {{$.i18n.Tr "repo.stop"}}
</a> </a>
{{else}} {{else}}
@@ -322,6 +326,7 @@
{{end}} {{end}}
</form> </form>
</div> </div>
{{if not .BootFile}}
<div class="ui compact buttons" <div class="ui compact buttons"
style="{{if eq .ComputeResource "CPU/GPU"}} visibility: visible {{else}} visibility: hidden{{end}}"> style="{{if eq .ComputeResource "CPU/GPU"}} visibility: visible {{else}} visibility: hidden{{end}}">
<div class="ui dropdown" id="model_more" <div class="ui dropdown" id="model_more"
@@ -363,6 +368,7 @@
</div> </div>
</div> </div>
</div> </div>
{{end}}
</div> </div>
<!-- 镜像列表弹窗 --> <!-- 镜像列表弹窗 -->




+ 1
- 1
templates/repo/header.tmpl View File

@@ -133,7 +133,7 @@
{{end}} {{end}}
<div class="ui tabs container"> <div class="ui tabs container">
{{if not .Repository.IsBeingCreated}} {{if not .Repository.IsBeingCreated}}
<div class="ui tabular menu navbar">
<div class="ui tabular menu navbar" style="overflow-x:auto;overflow-y:hidden">
{{if .Permission.CanRead $.UnitTypeCode}} {{if .Permission.CanRead $.UnitTypeCode}}
<div class="dropdown-menu"> <div class="dropdown-menu">
<a class="{{if or .PageIsViewCode .PageIsReleaseList .PageIsWiki .PageIsActivity .PageIsViewCode}}active{{end}} item hover_active" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/{{.BranchNameSubURL | EscapePound}}{{end}}"> <a class="{{if or .PageIsViewCode .PageIsReleaseList .PageIsWiki .PageIsActivity .PageIsViewCode}}active{{end}} item hover_active" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/{{.BranchNameSubURL | EscapePound}}{{end}}">


+ 29
- 29
templates/repo/home.tmpl View File

@@ -99,6 +99,9 @@
animation-fill-mode: both; animation-fill-mode: both;
} }
</style> </style>
{{if and (.IsViewFile) (IpynbBool .TreePath)}}
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/vp-notebook-debug.css?v={{MD5 AppVer}}" />
{{end}}
<div class="repository file list"> <div class="repository file list">
{{template "repo/header" .}} {{template "repo/header" .}}
<div class="ui container"> <div class="ui container">
@@ -154,7 +157,9 @@
{{.i18n.Tr "repo.archive.title"}} {{.i18n.Tr "repo.archive.title"}}
</div> </div>
{{end}} {{end}}
{{if not .IsViewFile}}
{{template "repo/sub_menu" .}} {{template "repo/sub_menu" .}}
{{end}}
<div class="ui stackable secondary menu mobile--margin-between-items mobile--no-negative-margins"> <div class="ui stackable secondary menu mobile--margin-between-items mobile--no-negative-margins">
{{template "repo/branch_dropdown" .}} {{template "repo/branch_dropdown" .}}
{{ $n := len .TreeNames}} {{ $n := len .TreeNames}}
@@ -200,6 +205,7 @@
class="section"><a href="{{EscapePound $.BranchLink}}/{{EscapePound $p}}" class="section"><a href="{{EscapePound $.BranchLink}}/{{EscapePound $p}}"
title="{{$v}}">{{EllipsisString $v 30}}</a></span>{{end}}{{end}}</span></div> title="{{$v}}">{{EllipsisString $v 30}}</a></span>{{end}}{{end}}</span></div>
{{end}} {{end}}
<div class="right fitted item" id="file-buttons"> <div class="right fitted item" id="file-buttons">
<div class="ui tiny blue buttons"> <div class="ui tiny blue buttons">
{{if .Repository.CanEnableEditor}} {{if .Repository.CanEnableEditor}}
@@ -223,7 +229,6 @@
</a> </a>
{{end}} {{end}}
</div> </div>

</div> </div>
<div class="fitted item"> <div class="fitted item">
{{if eq $n 0}} {{if eq $n 0}}
@@ -237,7 +242,6 @@
{{end}} {{end}}
</div> </div>
<div class="fitted item"> <div class="fitted item">

<!-- Only show clone panel in repository home page --> <!-- Only show clone panel in repository home page -->
{{if eq $n 0}} {{if eq $n 0}}
<div class="ui action tiny input" id="clone-panel"> <div class="ui action tiny input" id="clone-panel">
@@ -279,10 +283,22 @@
</div> </div>
{{end}} {{end}}
</div> </div>
{{if and (.IsViewFile) (IpynbBool .TreePath)}}
<div class="right fitted item">
<button class="ui green button tiny" id="notebook-debug" style="display: flex;align-items: center;">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="none" d="M0 0h24v24H0z"/><path d="M3 3h18a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1zm1 2v14h16V5H4zm8 10h6v2h-6v-2zm-3.333-3L5.838 9.172l1.415-1.415L11.495 12l-4.242 4.243-1.415-1.415L8.667 12z" fill="rgba(255,255,255,1)"/></svg>
<span style="margin-left:0.3rem;font-size: 14px;">{{.i18n.Tr "repo.notebook_open"}}</span>
</button>
</div>
{{end}}
</div> </div>
<div class="ui container"> <div class="ui container">
<div class="ui mobile reversed stackable grid"> <div class="ui mobile reversed stackable grid">
{{if not .IsViewFile}}
<div class="ui ten wide tablet twelve wide computer column"> <div class="ui ten wide tablet twelve wide computer column">
{{else}}
<div class="ui sixteen wide tablet sixteen wide computer column">
{{end}}
{{if .IsViewFile}} {{if .IsViewFile}}
{{template "repo/view_file" .}} {{template "repo/view_file" .}}
{{else if .IsBlame}} {{else if .IsBlame}}
@@ -291,6 +307,7 @@
{{template "repo/view_list" .}} {{template "repo/view_list" .}}
{{end}} {{end}}
</div> </div>
{{if not .IsViewFile}}
<div class="ui six wide tablet four wide computer column"> <div class="ui six wide tablet four wide computer column">
<div id="repo-desc" data-IsAdmin="{{.Permission.IsAdmin}}" <div id="repo-desc" data-IsAdmin="{{.Permission.IsAdmin}}"
data-IsArchived="{{.Repository.IsArchived}}"> data-IsArchived="{{.Repository.IsArchived}}">
@@ -303,25 +320,17 @@
{{else}} {{else}}
<span class="no-description text-italic">{{.i18n.Tr "repo.no_desc"}}</span> <span class="no-description text-italic">{{.i18n.Tr "repo.no_desc"}}</span>
{{end}} {{end}}

</p> </p>

</div> </div>



{{if .Repository.Website}} {{if .Repository.Website}}
<p class="ui"> <p class="ui">
<i class="gray linkify icon"></i> <i class="gray linkify icon"></i>
<a class="link edit-link" target="_blank" title="{{.Repository.Website}}" <a class="link edit-link" target="_blank" title="{{.Repository.Website}}"
href="{{.Repository.Website}}">{{.Repository.Website}}</a> href="{{.Repository.Website}}">{{.Repository.Website}}</a>
</p> </p>

{{end}} {{end}}

<div class="ui" id="repo-topics" style="display: flex;position: relative;margin-bottom: 1.0rem;"> <div class="ui" id="repo-topics" style="display: flex;position: relative;margin-bottom: 1.0rem;">
<i class="grey bookmark icon"></i> <i class="grey bookmark icon"></i>

<div id="repo-topics1" style="flex: 1;"> <div id="repo-topics1" style="flex: 1;">
{{range .Topics}} {{range .Topics}}
<a class="ui repo-topic small label topic" <a class="ui repo-topic small label topic"
@@ -334,32 +343,22 @@
</div> </div>
<div id="topic_edit" class="vue_menu" style="display:none"> <div id="topic_edit" class="vue_menu" style="display:none">
<div id="topic_edit1"> <div id="topic_edit1">

</div> </div>
</div> </div>

</div> </div>


<p class="ui"> <p class="ui">
<i class="grey code icon"></i> <i class="grey code icon"></i>
{{range .LanguageStats}} {{range .LanguageStats}}
{{.Language}} {{.Language}}
{{end}} {{end}}
</p> </p>



{{if .LICENSE}} {{if .LICENSE}}
<p class="ui"> <p class="ui">
<i class="grey clone icon"></i> <i class="grey clone icon"></i>
{{.LICENSE}} {{.LICENSE}}
</p> </p>

{{end}} {{end}}

<div class="ui divider"></div> <div class="ui divider"></div>

<div> <div>
<h4 class="ui header"> <h4 class="ui header">
{{$lenCon := len .ContributorInfo}} {{$lenCon := len .ContributorInfo}}
@@ -387,19 +386,20 @@
{{end}} {{end}}
</div> </div>
</div> </div>

</div> </div>
{{end}}
</div> </div>
</div> </div>


</div> </div>
{{if and (.IsViewFile) (IpynbBool .TreePath)}}
<div id="__vue-root"></div>
<div id="__vue-self-data" data-branch="{{.BranchName}}" data-owner="{{.Repository.OwnerName}}" data-name="{{.SignedUser.Name}}" data-project="{{.Repository.Name}}"
data-file="{{.TreePath}}">
</div>
{{end}}
</div> </div>

<script type="text/javascript">
// $(document).ready(function(){
// $(".membersmore").click(function(){
// $("#contributorInfo > a:nth-child(n+25)").show();
// });
// });
</script>
{{if and (.IsViewFile) (IpynbBool .TreePath)}}
<script src="{{StaticUrlPrefix}}/js/vp-notebook-debug.js?v={{MD5 AppVer}}"></script>
{{end}}
{{template "base/footer" .}} {{template "base/footer" .}}

+ 6
- 4
templates/user/dashboard/cloudbrains.tmpl View File

@@ -124,7 +124,8 @@
style="width: 8% !important;"> style="width: 8% !important;">
<span class="job-status" id="{{$JobID}}" <span class="job-status" id="{{$JobID}}"
data-repopath='{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .JobType "DEBUG"}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}{{else if eq .JobType "INFERENCE"}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts{{end}}/inference-job{{else if eq .JobType "TRAIN"}}{{if eq .ComputeResource "NPU"}}/modelarts/train-job{{else}}/cloudbrain/train-job{{end}}{{else if eq .JobType "BENCHMARK" "MODELSAFETY"}}/cloudbrain{{end}}' data-repopath='{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .JobType "DEBUG"}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}{{else if eq .JobType "INFERENCE"}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts{{end}}/inference-job{{else if eq .JobType "TRAIN"}}{{if eq .ComputeResource "NPU"}}/modelarts/train-job{{else}}/cloudbrain/train-job{{end}}{{else if eq .JobType "BENCHMARK" "MODELSAFETY"}}/cloudbrain{{end}}'
data-jobid="{{$JobID}}" data-version="{{.VersionName}}">
data-jobid="{{$JobID}}" data-version="{{.VersionName}}"
data-bootfile="{{.BootFile}}">
<span><i id="{{$JobID}}-icon" style="vertical-align: middle;" <span><i id="{{$JobID}}-icon" style="vertical-align: middle;"
class="{{.Status}}"></i><span id="{{$JobID}}-text" class="{{.Status}}"></i><span id="{{$JobID}}-text"
style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span> style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span>
@@ -185,7 +186,6 @@
<a href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}" <a href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}"
title="{{.Repo.OwnerName}}/{{.Repo.Alias}}">{{.Repo.OwnerName}}/{{.Repo.Alias}}</a> title="{{.Repo.OwnerName}}/{{.Repo.Alias}}">{{.Repo.OwnerName}}/{{.Repo.Alias}}</a>
</div> </div>

<div class="three wide column text center nowrap" style="width: 15%!important;"> <div class="three wide column text center nowrap" style="width: 15%!important;">
{{if eq .JobType "DEBUG"}} {{if eq .JobType "DEBUG"}}
<div class="ui compact buttons"> <div class="ui compact buttons">
@@ -199,6 +199,7 @@
{{$.i18n.Tr "repo.debug"}} {{$.i18n.Tr "repo.debug"}}
</a> </a>
{{else}} {{else}}
{{if not .BootFile}}
<a id="ai-debug-{{$JobID}}" <a id="ai-debug-{{$JobID}}"
class='ui basic ai_debug {{if eq .Status "CREATING" "STOPPING" "WAITING" "STARTING"}} disabled {{else}}blue {{end}}button' class='ui basic ai_debug {{if eq .Status "CREATING" "STOPPING" "WAITING" "STARTING"}} disabled {{else}}blue {{end}}button'
data-jobid="{{$JobID}}" data-jobid="{{$JobID}}"
@@ -206,6 +207,7 @@
{{$.i18n.Tr "repo.debug_again"}} {{$.i18n.Tr "repo.debug_again"}}
</a> </a>
{{end}} {{end}}
{{end}}
</form> </form>
</div> </div>
{{end}} {{end}}
@@ -228,7 +230,7 @@
<a style="padding: 0.5rem 1rem;" id="ai-stop-{{$JobID}}" <a style="padding: 0.5rem 1rem;" id="ai-stop-{{$JobID}}"
class='ui basic ai_stop {{if eq .Status "KILLED" "FAILED" "START_FAILED" "KILLING" "COMPLETED" "SUCCEEDED" "STOPPED" "STOPPING" "CREATE_FAILED"}}disabled {{else}} blue {{end}}button' class='ui basic ai_stop {{if eq .Status "KILLED" "FAILED" "START_FAILED" "KILLING" "COMPLETED" "SUCCEEDED" "STOPPED" "STOPPING" "CREATE_FAILED"}}disabled {{else}} blue {{end}}button'
data-repopath='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else if eq .JobType "BENCHMARK" }}/cloudbrain/benchmark{{else if eq .ComputeResource "NPU" }}/modelarts/notebook{{end}}/{{$JobID}}/stop' data-repopath='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else if eq .JobType "BENCHMARK" }}/cloudbrain/benchmark{{else if eq .ComputeResource "NPU" }}/modelarts/notebook{{end}}/{{$JobID}}/stop'
data-jobid="{{$JobID}}">
data-jobid="{{$JobID}}" data-bootfile="{{.BootFile}}">
{{$.i18n.Tr "repo.stop"}} {{$.i18n.Tr "repo.stop"}}
</a> </a>
</form> </form>
@@ -236,7 +238,7 @@
<a style="padding: 0.5rem 1rem;" id="ai-stop-{{$JobID}}" <a style="padding: 0.5rem 1rem;" id="ai-stop-{{$JobID}}"
class='ui basic ai_stop_version {{if eq .Status "KILLED" "FAILED" "START_FAILED" "KILLING" "COMPLETED" "STOPPED" "SUCCEEDED" "CREATE_FAILED"}}disabled {{else}} blue {{end}}button' class='ui basic ai_stop_version {{if eq .Status "KILLED" "FAILED" "START_FAILED" "KILLING" "COMPLETED" "STOPPED" "SUCCEEDED" "CREATE_FAILED"}}disabled {{else}} blue {{end}}button'
data-repopath='{{.Repo.OwnerName}}/{{.Repo.Name}}/{{if eq .JobType "INFERENCE"}}{{if eq .Cloudbrain.Type 1}}modelarts/inference-job{{else}}cloudbrain/train-job{{end}}{{else if eq .JobType "TRAIN"}}{{if eq .Cloudbrain.Type 1}}modelarts/train-job{{else if eq .Cloudbrain.Type 0}}cloudbrain/train-job{{else if eq .Cloudbrain.Type 2}}grampus/train-job{{end}}{{end}}' data-repopath='{{.Repo.OwnerName}}/{{.Repo.Name}}/{{if eq .JobType "INFERENCE"}}{{if eq .Cloudbrain.Type 1}}modelarts/inference-job{{else}}cloudbrain/train-job{{end}}{{else if eq .JobType "TRAIN"}}{{if eq .Cloudbrain.Type 1}}modelarts/train-job{{else if eq .Cloudbrain.Type 0}}cloudbrain/train-job{{else if eq .Cloudbrain.Type 2}}grampus/train-job{{end}}{{end}}'
data-jobid="{{$JobID}}" data-version="{{.VersionName}}">
data-jobid="{{$JobID}}" data-version="{{.VersionName}}" data-bootfile="{{.BootFile}}">
{{$.i18n.Tr "repo.stop"}} {{$.i18n.Tr "repo.stop"}}
</a> </a>
{{end}} {{end}}


+ 23
- 12
web_src/js/features/cloudrbanin.js View File

@@ -24,6 +24,7 @@ export default async function initCloudrain() {
const repoPath = job.dataset.repopath; const repoPath = job.dataset.repopath;
// const computeResource = job.dataset.resource // const computeResource = job.dataset.resource
const versionname = job.dataset.version; const versionname = job.dataset.version;
const bootfile = job.dataset.bootfile;
const status_text = $(`#${ID}-text`).text(); const status_text = $(`#${ID}-text`).text();
const finalState = [ const finalState = [
"STOPPED", "STOPPED",
@@ -99,11 +100,16 @@ export default async function initCloudrain() {
"SUCCEEDED", "SUCCEEDED",
].includes(status) ].includes(status)
) { ) {
$("#ai-debug-" + ID)
.removeClass("disabled")
.addClass("blue")
.text(debug_again_button)
.css("margin", "0");
if (!bootfile) {
$("#ai-debug-" + ID)
.removeClass("disabled")
.addClass("blue")
.text(debug_again_button)
.css("margin", "0");
} else {
$("#ai-debug-" + ID).remove()
}
} }
if (["RUNNING", "WAITING"].includes(status)) { if (["RUNNING", "WAITING"].includes(status)) {
$("#ai-stop-" + ID) $("#ai-stop-" + ID)
@@ -289,7 +295,7 @@ export default async function initCloudrain() {
assertDelete(this); assertDelete(this);
} }
}); });
function stopDebug(ID, stopUrl) {
function stopDebug(ID, stopUrl,bootFile) {
$.ajax({ $.ajax({
type: "POST", type: "POST",
url: stopUrl, url: stopUrl,
@@ -301,11 +307,15 @@ export default async function initCloudrain() {
.addClass(res.status); .addClass(res.status);
$("#" + ID + "-text").text(res.status); $("#" + ID + "-text").text(res.status);
if (res.status === "STOPPED") { if (res.status === "STOPPED") {
$("#ai-debug-" + ID)
.removeClass("disabled")
.addClass("blue")
.text(debug_again_button)
.css("margin", "0");
if (!bootFile) {
$("#ai-debug-" + ID)
.removeClass("disabled")
.addClass("blue")
.text(debug_again_button)
.css("margin", "0");
} else {
$("#ai-debug-" + ID).remove()
}
$("#ai-image-" + ID) $("#ai-image-" + ID)
.removeClass("blue") .removeClass("blue")
.addClass("disabled"); .addClass("disabled");
@@ -344,7 +354,8 @@ export default async function initCloudrain() {
$(".ui.basic.ai_stop").click(function () { $(".ui.basic.ai_stop").click(function () {
const ID = this.dataset.jobid; const ID = this.dataset.jobid;
const repoPath = this.dataset.repopath; const repoPath = this.dataset.repopath;
stopDebug(ID, repoPath);
const bootFile = this.dataset.bootfile
stopDebug(ID, repoPath,bootFile);
}); });


function stopVersion(version_name, ID, repoPath) { function stopVersion(version_name, ID, repoPath) {


+ 47
- 0
web_src/vuepages/apis/modules/notobook.js View File

@@ -0,0 +1,47 @@
import service from "../service";

// Notebook新建页面需要的信息
export const getFileNotebook = () => {
return service({
url: "/api/v1/file_notebook",
method: "get",
params: {},
});
};

// Notebook新建调试任务
// type, file, branch_name, owner_name, project_name
export const createNotebook = (data) => {
return service({
url: "/api/v1/file_notebook/create",
method: "post",
data,
params: {},
});
};

// Notebook获取云脑I调试任务状态
export const getCb1Notebook = (path,jobid) => {
return service({
url: `/api/v1/${path}/cloudbrain/${jobid}`,
method: "get",
params: {},
});
};

// Notebook获取云脑I调试任务状态
export const getCb2Notebook = (path,jobid) => {
return service({
url: `/api/v1/${path}/modelarts/notebook/${jobid}`,
method: "get",
params: {},
});
};

export const stopNotebook = (url) => {
return service({
url: url,
method: "post",
params: {},
});
};

+ 0
- 1
web_src/vuepages/apis/service.js View File

@@ -2,7 +2,6 @@ import axios from 'axios';


const service = axios.create({ const service = axios.create({
baseURL: '/', baseURL: '/',
timeout: 20000,
}); });


service.interceptors.request.use((config) => { service.interceptors.request.use((config) => {


+ 30
- 2
web_src/vuepages/langs/config/en-US.js View File

@@ -157,7 +157,7 @@ const en = {
computeCluster: 'Compute Cluster', computeCluster: 'Compute Cluster',
resourceSpecification: 'Resource Specification', resourceSpecification: 'Resource Specification',
lastUpdateTime: 'Last Update Time', lastUpdateTime: 'Last Update Time',
resSceneDeleteConfirm: 'Are you sure to delete the current Resource Scene?',
resSceneDeleteConfirm: 'Are you sure to delete the current Resource Scene?',
resourceSpecificationIsAvailable: 'Specification Is Available', resourceSpecificationIsAvailable: 'Specification Is Available',
resourceSpecificationIsAvailableAll: 'Specification Is Available(All)', resourceSpecificationIsAvailableAll: 'Specification Is Available(All)',
available: 'Available', available: 'Available',
@@ -192,7 +192,35 @@ const en = {
dataDesensitizationModelExperience:'Data desensitization model experience', dataDesensitizationModelExperience:'Data desensitization model experience',
dataDesensitizationModelDesc:'Use AI technology to desensitize the face and license plate number in the picture. For more information about this model, please visit the project', dataDesensitizationModelDesc:'Use AI technology to desensitize the face and license plate number in the picture. For more information about this model, please visit the project',
limitFilesUpload:'Only jpg/jpeg/png files can be uploaded', limitFilesUpload:'Only jpg/jpeg/png files can be uploaded',
limitSizeUpload:'The size of the uploaded file cannot exceed 20M!',
limitSizeUpload: 'The size of the uploaded file cannot exceed 20M!',
notebook: {
createNewNotebook: "Create new notebook debug task",
sameTaskTips1: "You have created an",
sameTaskTips2: "equivalent task",
sameTaskTips3: "that is waiting or running, please wait for the task to finish before creating it.",
sameTaskTips4: "You can view all your Cloudbrain tasks in",
sameTaskTips5: "Home",
sameTaskTips6: "Cloudbrain Task",
sameTaskTips7: "",
cpuEnv: "CPU Environment",
gpuEnv: "GPU Environment",
npuEnv: "NPU environment",
newTask: "Create new task",
noQuene: "The current resources are sufficient and no tasks are queued",
queneTips1: "There are currently",
queneTips2: "tasks queued",
watiResource: "Waiting for resources to be allocated, please be patient",
debug: "Debug",
stop: "Stop",
stopping: "Stopping",
notebookRunning: "The debugging environment has been started, and it will automatically close after 4 hours ",
stopSuccess: "Stop task succeeded",
specification: "Specification",
graphicMemory: "Graphic Memory",
memory: "Memory",
sharedMemory: "Shared Memory",
tips: 'The newly created debugging task will be placed in the project openi-notebook under your name. If there is no such project, the system will automatically create a new one.'
},
modelManage: { modelManage: {
modelManage: 'Model management', modelManage: 'Model management',
modelName: 'Model name', modelName: 'Model name',


+ 217
- 186
web_src/vuepages/langs/config/zh-CN.js View File

@@ -1,198 +1,227 @@
const zh = { const zh = {
loading: '加载中...',
noData: '暂无数据',
date: '日期',
loading: "加载中...",
noData: "暂无数据",
date: "日期",


confirm: '确定',
cancel: '取消',
confirm1: '确认',
pleaseCompleteTheInformationFirst: '请先完善信息!',
submittedSuccessfully: '提交成功!',
submittedFailed: '提交失败!',
operation: '操作',
edit: '修改',
delete: '删除',
tips: '提示',
confirm: "确定",
cancel: "取消",
confirm1: "确认",
pleaseCompleteTheInformationFirst: "请先完善信息!",
submittedSuccessfully: "提交成功!",
submittedFailed: "提交失败!",
operation: "操作",
edit: "修改",
delete: "删除",
tips: "提示",


accomplishTask: '积分任务',
adminOperate: '管理员操作',
runCloudBrainTask: '运行云脑任务',
operating: '消耗中',
succeeded: '已完成',
debugTask: '调试任务',
trainTask: '训练任务',
inferenceTask: '推理任务',
benchmarkTask: '评测任务',
createPublicProject: '创建公开项目',
dailyPutforwardTasks: '每日提出任务',
dailyPR: '每日提出PR',
comment: '发表评论',
uploadDatasetFile: '上传数据集文件',
importNewModel: '导入新模型',
completeWechatCodeScanningVerification: '完成微信扫码验证',
dailyRunCloudbrainTasks: '每日运行云脑任务',
datasetRecommendedByThePlatform: '数据集被平台推荐',
submitNewPublicImage: '提交新公开镜像',
imageRecommendedByThePlatform: '镜像被平台推荐',
firstChangeofAvatar: '首次更换头像',
dailyCommit: '每日commit',
calcPointDetails: '算力积分明细',
calcPointAcquisitionInstructions: '积分获取说明',
CurrAvailableCalcPoints: '当前可用算力积分(分)',
totalGainCalcPoints: '总获取算力积分(分)',
totalConsumeCalcPoints: '总消耗算力积分(分)',
gainDetail: '获取明细',
consumeDetail: '消耗明细',
serialNumber: '流水号',
time: '时间',
scene: '场景',
behaviorOfPoint: '积分行为',
explanation: '说明',
points: '积分',
status: '状态',
runTime: '运行时长',
taskName: '任务名称',
accomplishTask: "积分任务",
adminOperate: "管理员操作",
runCloudBrainTask: "运行云脑任务",
operating: "消耗中",
succeeded: "已完成",
debugTask: "调试任务",
trainTask: "训练任务",
inferenceTask: "推理任务",
benchmarkTask: "评测任务",
createPublicProject: "创建公开项目",
dailyPutforwardTasks: "每日提出任务",
dailyPR: "每日提出PR",
comment: "发表评论",
uploadDatasetFile: "上传数据集文件",
importNewModel: "导入新模型",
completeWechatCodeScanningVerification: "完成微信扫码验证",
dailyRunCloudbrainTasks: "每日运行云脑任务",
datasetRecommendedByThePlatform: "数据集被平台推荐",
submitNewPublicImage: "提交新公开镜像",
imageRecommendedByThePlatform: "镜像被平台推荐",
firstChangeofAvatar: "首次更换头像",
dailyCommit: "每日commit",
calcPointDetails: "算力积分明细",
calcPointAcquisitionInstructions: "积分获取说明",
CurrAvailableCalcPoints: "当前可用算力积分(分)",
totalGainCalcPoints: "总获取算力积分(分)",
totalConsumeCalcPoints: "总消耗算力积分(分)",
gainDetail: "获取明细",
consumeDetail: "消耗明细",
serialNumber: "流水号",
time: "时间",
scene: "场景",
behaviorOfPoint: "积分行为",
explanation: "说明",
points: "积分",
status: "状态",
runTime: "运行时长",
taskName: "任务名称",


createdRepository: '创建了项目',
repositoryWasDel: '项目已删除',
openedIssue: '创建了任务',
createdPullRequest: '创建了合并请求',
commentedOnIssue: '评论了任务',
uploadDataset: '上传了数据集文件',
createdNewModel: '导入了新模型',
firstBindingWechatRewards: '首次绑定微信奖励',
created: '创建了',
type: '类型',
dataset: '数据集',
setAsRecommendedDataset: '被设置为推荐数据集',
committedImage: '提交了镜像',
image: '镜像',
setAsRecommendedImage: '被设置为推荐镜像',
updatedAvatar: '更新了头像',
pushedBranch: '推送了{branch}分支代码到',
deleteBranch: '从{repo}删除分支{branch}',
pushedTag: '推送了标签{tag}到',
deleteTag: '从{repo}删除了标签{tag}',
dailyMaxTips: '达到每日上限积分,不能拿满分',
memory: '内存',
sharedMemory: '共享内存',
';': ';',
createdRepository: "创建了项目",
repositoryWasDel: "项目已删除",
openedIssue: "创建了任务",
createdPullRequest: "创建了合并请求",
commentedOnIssue: "评论了任务",
uploadDataset: "上传了数据集文件",
createdNewModel: "导入了新模型",
firstBindingWechatRewards: "首次绑定微信奖励",
created: "创建了",
type: "类型",
dataset: "数据集",
setAsRecommendedDataset: "被设置为推荐数据集",
committedImage: "提交了镜像",
image: "镜像",
setAsRecommendedImage: "被设置为推荐镜像",
updatedAvatar: "更新了头像",
pushedBranch: "推送了{branch}分支代码到",
deleteBranch: "从{repo}删除分支{branch}",
pushedTag: "推送了标签{tag}到",
deleteTag: "从{repo}删除了标签{tag}",
dailyMaxTips: "达到每日上限积分,不能拿满分",
memory: "内存",
sharedMemory: "共享内存",
";": ";",


noPointGainRecord: '还没有积分获取记录',
noPointConsumeRecord: '还没有积分消耗记录',
noPointGainRecord: "还没有积分获取记录",
noPointConsumeRecord: "还没有积分消耗记录",


resourcesManagement: { resourcesManagement: {
OpenI: '启智集群',
C2Net: '智算集群',
OpenIOne: '云脑一',
OpenITwo: '云脑二',
OpenIChengdu: '启智成都智算',
chengduCenter: '成都智算',
pclcci: '鹏城云计算所',
hefeiCenter: '合肥类脑类脑智能开放平台',
xuchangCenter: '中原人工智能计算中心',
willOnShelf: '待上架',
onShelf: '已上架',
offShelf: '已下架',
toOnShelf: '上架',
toOffShelf: '下架',
toSetPriceAndOnShelf: '定价上架',
status: '状态',
allStatus: '全部状态',
syncAiNetwork: '同步智算网络',
resQueue: '资源池(队列)',
allResQueue: '全部资源池(队列)',
addResQueue: '新建资源池(队列)',
addResQueueBtn: '新增资源池',
editResQueue: '修改资源池(队列)',
resQueueName: '资源池(队列)名称',
whichCluster: '所属集群',
allCluster: '全部集群',
aiCenter: '智算中心',
aiCenterID: '智算中心ID',
allAiCenter: '全部智算中心',
computeResource: '计算资源',
allComputeResource: '全部计算资源',
accCardType: '卡类型',
allAccCardType: '全部卡类型',
cardsTotalNum: '卡数',
accCardsNum: '卡数',
remark: '备注',
pleaseEnterRemark: '请输入备注(最大长度不超过255)',
pleaseEnterPositiveIntegerCardsTotalNum: '请输入正整数的卡数!',
addResSpecificationAndPriceInfo: '新增资源规格和单价信息',
addResSpecificationBtn: '新增资源规格',
editResSpecificationAndPriceInfo: '修改资源规格和单价信息',
resSpecificationAndPriceManagement: '资源规格单价管理',
sourceSpecCode: '对应资源编码',
sourceSpecCodeTips: '云脑II需要填写对应的资源编码',
sourceSpecId: '智算网络资源规格ID',
cpuNum: 'CPU数',
gpuMem: '显存',
mem: '内存',
shareMem: '共享内存',
unitPrice: '单价',
point_hr: '积分/时',
node: '节点',
free: '免费',
onShelfConfirm: '请确认上架该规格?',
offShelfConfirm: '请确认下架该规格?',
onShelfCode1001: '上架失败,资源池(队列)不可用。',
onShelfCode1003: '上架失败,资源规格不可用。',
offShelfDlgTip1: '当前资源规格已在以下场景中使用:',
offShelfDlgTip2: '请确认进行下架操作?',
resSceneManagement: '算力资源应用场景管理',
addResScene: '新建算力资源应用场景',
addResSceneBtn: '新增应用场景',
editResScene: '修改算力资源应用场景',
resSceneName: '应用场景名称',
jobType: '任务类型',
allJobType: '全部任务类型',
isExclusive: '是否专属',
allExclusiveAndCommonUse: '全部专属和通用',
exclusive: '专属',
commonUse: '通用',
exclusiveOrg: '专属组织',
exclusiveOrgTips: '多个组织名之间用英文分号隔开',
computeCluster: '算力集群',
resourceSpecification: '资源规格',
lastUpdateTime: '最后更新时间',
resSceneDeleteConfirm: '是否确认删除当前应用场景?',
resourceSpecificationIsAvailable: '资源规格是否可用',
resourceSpecificationIsAvailableAll: '资源规格是否可用(全部)',
available: '可用',
notAvailable: '不可用',
OpenI: "启智集群",
C2Net: "智算集群",
OpenIOne: "云脑一",
OpenITwo: "云脑二",
OpenIChengdu: "启智成都智算",
chengduCenter: "成都智算",
pclcci: "鹏城云计算所",
hefeiCenter: "合肥类脑类脑智能开放平台",
xuchangCenter: "中原人工智能计算中心",
willOnShelf: "待上架",
onShelf: "已上架",
offShelf: "已下架",
toOnShelf: "上架",
toOffShelf: "下架",
toSetPriceAndOnShelf: "定价上架",
status: "状态",
allStatus: "全部状态",
syncAiNetwork: "同步智算网络",
resQueue: "资源池(队列)",
allResQueue: "全部资源池(队列)",
addResQueue: "新建资源池(队列)",
addResQueueBtn: "新增资源池",
editResQueue: "修改资源池(队列)",
resQueueName: "资源池(队列)名称",
whichCluster: "所属集群",
allCluster: "全部集群",
aiCenter: "智算中心",
aiCenterID: "智算中心ID",
allAiCenter: "全部智算中心",
computeResource: "计算资源",
allComputeResource: "全部计算资源",
accCardType: "卡类型",
allAccCardType: "全部卡类型",
cardsTotalNum: "卡数",
accCardsNum: "卡数",
remark: "备注",
pleaseEnterRemark: "请输入备注(最大长度不超过255)",
pleaseEnterPositiveIntegerCardsTotalNum: "请输入正整数的卡数!",
addResSpecificationAndPriceInfo: "新增资源规格和单价信息",
addResSpecificationBtn: "新增资源规格",
editResSpecificationAndPriceInfo: "修改资源规格和单价信息",
resSpecificationAndPriceManagement: "资源规格单价管理",
sourceSpecCode: "对应资源编码",
sourceSpecCodeTips: "云脑II需要填写对应的资源编码",
sourceSpecId: "智算网络资源规格ID",
cpuNum: "CPU数",
gpuMem: "显存",
mem: "内存",
shareMem: "共享内存",
unitPrice: "单价",
point_hr: "积分/时",
node: "节点",
free: "免费",
onShelfConfirm: "请确认上架该规格?",
offShelfConfirm: "请确认下架该规格?",
onShelfCode1001: "上架失败,资源池(队列)不可用。",
onShelfCode1003: "上架失败,资源规格不可用。",
offShelfDlgTip1: "当前资源规格已在以下场景中使用:",
offShelfDlgTip2: "请确认进行下架操作?",
resSceneManagement: "算力资源应用场景管理",
addResScene: "新建算力资源应用场景",
addResSceneBtn: "新增应用场景",
editResScene: "修改算力资源应用场景",
resSceneName: "应用场景名称",
jobType: "任务类型",
allJobType: "全部任务类型",
isExclusive: "是否专属",
allExclusiveAndCommonUse: "全部专属和通用",
exclusive: "专属",
commonUse: "通用",
exclusiveOrg: "专属组织",
exclusiveOrgTips: "多个组织名之间用英文分号隔开",
computeCluster: "算力集群",
resourceSpecification: "资源规格",
lastUpdateTime: "最后更新时间",
resSceneDeleteConfirm: "是否确认删除当前应用场景?",
resourceSpecificationIsAvailable: "资源规格是否可用",
resourceSpecificationIsAvailableAll: "资源规格是否可用(全部)",
available: "可用",
notAvailable: "不可用",
}, },
user: { user: {
inviteFriends: '邀请好友',
inviteFriendsTips: '复制二维码或者注册邀请链接分享给好友',
clickToViewTheEventDetails: '点击查看活动详情',
copyRegistrationInvitationLink: '复制注册邀请链接',
registrationAdress: '注册地址:',
recommender: '推荐人:',
invitedFriends: '已邀请好友',
registrationTime: '注册时间',
theSharedContentHasBeenCopiedToTheClipboard: '分享内容已复制到剪切板',
copyError: '复制错误',
Activated: '已激活',
notActive: '未激活',
inviteFriends: "邀请好友",
inviteFriendsTips: "复制二维码或者注册邀请链接分享给好友",
clickToViewTheEventDetails: "点击查看活动详情",
copyRegistrationInvitationLink: "复制注册邀请链接",
registrationAdress: "注册地址:",
recommender: "推荐人:",
invitedFriends: "已邀请好友",
registrationTime: "注册时间",
theSharedContentHasBeenCopiedToTheClipboard: "分享内容已复制到剪切板",
copyError: "复制错误",
Activated: "已激活",
notActive: "未激活",
},
tranformImageFailed: "图片脱敏失败",
originPicture: "原始图片",
desensitizationPicture: "脱敏图片",
desensitizationObject: "脱敏对象",
example: "示例",
startDesensitization: "开始处理",
all: "全部",
onlyFace: "仅人脸",
onlyLicensePlate: "仅车牌",
dragThePictureHere: "拖动图片到这里",
or: "或",
clickUpload: "点击上传",
dataDesensitizationModelExperience: "数据脱敏模型体验",
dataDesensitizationModelDesc:
"利用人工智能AI技术,把图片中的人脸、车牌号码进行脱敏处理。该模型更多信息请访问项目",
limitFilesUpload: "只能上传 jpg/jpeg/png 格式的文件",
limitSizeUpload: "上传文件大小不能超过 20M !",
notebook: {
createNewNotebook: "新建Notebook调试任务",
sameTaskTips1: "您已经有",
sameTaskTips2: "同类任务",
sameTaskTips3: "正在等待或运行中,请等待任务结束再创建",
sameTaskTips4: "可以在",
sameTaskTips5: "个人中心",
sameTaskTips6: "云脑任务",
sameTaskTips7: "查看您所有的云脑任务。",
cpuEnv: "CPU 环境",
gpuEnv: "GPU 环境",
npuEnv: "NPU 环境",
newTask: "新建任务",
noQuene: "当前资源充足,没有任务排队",
queneTips1: "当前有",
queneTips2: "个任务正在排队",
watiResource: "正在等待分配资源,请耐心等待",
debug: "调试",
stop: "停止",
stopping: "停止中",
notebookRunning: "调试环境已启动,单次连接 4 小时后自动关闭",
stopSuccess: "停止任务成功",
specification: "规格",
graphicMemory: "显存",
memory: "内存",
sharedMemory: "共享内存",
tips:'本次新建的调试任务会放在您名下项目openi-notebook中,如果没有该项目系统会自动新建一个。'
}, },
tranformImageFailed:'图片脱敏失败',
originPicture:'原始图片',
desensitizationPicture:'脱敏图片',
desensitizationObject:'脱敏对象',
example:'示例',
startDesensitization:'开始处理',
all:'全部',
onlyFace:'仅人脸',
onlyLicensePlate:'仅车牌',
dragThePictureHere:'拖动图片到这里',
or:'或',
clickUpload:'点击上传',
dataDesensitizationModelExperience:'数据脱敏模型体验',
dataDesensitizationModelDesc:'利用人工智能AI技术,把图片中的人脸、车牌号码进行脱敏处理。该模型更多信息请访问项目',
limitFilesUpload:'只能上传 jpg/jpeg/png 格式的文件',
limitSizeUpload:'上传文件大小不能超过 20M !',
modelManage: { modelManage: {
modelManage: '模型管理', modelManage: '模型管理',
modelName: '模型名称', modelName: '模型名称',
@@ -254,6 +283,8 @@ const zh = {
deleteModelFileConfirmTips: '请确认是否删除当前模型文件?', deleteModelFileConfirmTips: '请确认是否删除当前模型文件?',
modelFileDeleteFailed: '模型文件删除失败', modelFileDeleteFailed: '模型文件删除失败',
}, },
}
};




export default zh; export default zh;

+ 662
- 0
web_src/vuepages/pages/notebook/debug/index.vue View File

@@ -0,0 +1,662 @@
<template>
<div>
<el-dialog
:title="$t('notebook.createNewNotebook')"
:visible.sync="dialogVisible"
width="50%"
:close-on-click-modal="false"
@closed="handleClose"
>
<div class="wrapper" v-loading="loading" element-loading-spinner="el-icon-loading">
<div style="text-align: center;padding-bottom: 12px;">
<span class="text-tip">{{$t('notebook.tips')}}</span>
</div>
<div v-show="alertCb" class="ui message alert-info">
<div style="display: flex;align-items: center;">
<i class="ri-information-line" style="font-size: 35px;color: rgba(242, 113, 28, 1);;"></i>
<div style="text-align: left;margin-left: 1rem;">
<div style="font-weight: 600;line-height: 2;">{{$t('notebook.sameTaskTips1')}} <span style="color:rgba(242, 113, 28, 1);">{{$t('notebook.sameTaskTips2')}}</span> {{$t('notebook.sameTaskTips3')}}</div>
<div style="color:#939393">{{$t('notebook.sameTaskTips4')}} “<a href="/cloudbrains" target="_blank">{{$t('notebook.sameTaskTips5')}} &gt; {{$t('notebook.sameTaskTips6')}}</a>” {{$t('notebook.sameTaskTips7')}}</div>
</div>
</div>
</div>
<div
class="three-resource-type"
:class="{ active: selectIndex == 0 }"
@click="selectResource(0)"
>
<div class="resource-child-node">
<div class="resource-type-icon background-C">
<span class="text">C</span>
</div>
<div class="resource-type-detail">
<div class="detail-title"><span>{{$t('notebook.cpuEnv')}}</span></div>
<div class="detail-spec">
<span>{{cpuSpec}}</span>
</div>
<div class="detail-spec">
<span>{{$t('image')}}:{{notebookInfo.imageCpuDescription}}</span>
</div>
</div>
<div class="resource-select">
<i v-if="selectIndex===0" :class="{'slide-in-bottom': !slideActive && !initSelect }" class="ri-checkbox-circle-line green"></i>
<i
class="ri-checkbox-blank-circle-line gray"
:class="{'fade-out':selectIndex===0}"
></i>
</div>
</div>
</div>
<div
class="three-resource-type"
:class="{ active: selectIndex == 2 }"
@click="selectResource(2)"
>
<div class="resource-child-node">
<div class="resource-type-icon background-N">
<span class="text">N</span>
</div>
<div class="resource-type-detail">
<div class="detail-title"><span>{{$t('notebook.npuEnv')}}</span></div>
<div class="detail-spec">
<span>{{npuSpec}}</span>
</div>
<div class="detail-spec">
<span>{{$t('image')}}:{{notebookInfo.imageNpuDescription}}</span>
</div>
</div>
<div class="resource-select">
<i v-if="selectIndex===2" :class="[slideActive && !initSelect ?'slide-in-top':'slide-in-bottom']" class="ri-checkbox-circle-line green"></i>
<i
class="ri-checkbox-blank-circle-line gray"
:class="{'fade-out':selectIndex===2}"
></i>
</div>
</div>
</div>
<div
class="three-resource-type"
:class="{ active: selectIndex == 1 }"
@click="selectResource(1)"
>
<div class="resource-child-node">
<div class="resource-type-icon background-G">
<span class="text">G</span>
</div>
<div class="resource-type-detail">
<div class="detail-title"><span>{{$t('notebook.gpuEnv')}}</span></div>
<div class="detail-spec">
<span>{{gpuSpec}}</span>
</div>
<div class="detail-spec">
<span>{{$t('image')}}:{{notebookInfo.imageGpuDescription}}</span>
</div>
</div>
<div class="resource-select">
<i v-if="selectIndex===1 && !initSelect" :class="{'slide-in-top': slideActive && !initSelect}" class="ri-checkbox-circle-line green"></i>
<i v-if="selectIndex===1 && initSelect" class="ri-checkbox-circle-line green"></i>
<i
class="ri-checkbox-blank-circle-line gray"
:class="{'fade-out':selectIndex===1}"
></i>
</div>
</div>
</div>
<div class="resource-footer">
<div class="resource-operate" v-if="selectIndex==0">
<div v-if="btnStatus[0]===0">
<button class="ui green small button" @click="createTask(0)"></i>{{$t('notebook.newTask')}}</button>
<span v-if="notebookInfo.waitCountGpu==0" class="text">{{$t('notebook.noQuene')}}</span>
<span v-else class="text">{{$t('notebook.queneTips1')}} <span style="color: red;">{{notebookInfo.waitCountGpu}}</span> {{$t('notebook.queneTips2')}}</span>
</div>
<div v-else-if="btnStatus[0]===1">
<button class="ui disabled small button" style="background-color: #888;"><i class="loading spinner icon" style="color: #fff;"></i>{{$t('notebook.newTask')}}</button>
<span class="text">{{$t('notebook.watiResource')}}</span>
</div>
<div v-else-if="btnStatus[0]===2">
<button class="ui small button" style="background-color: #1684fc;">
<a style="color:#fff" :href="deubgUrlGpu" target="_blank">{{$t('notebook.debug')}}</a>
</button>
<button class="ui small button" @click="stopDebug(0)">{{$t('notebook.stop')}}</button>
<span class="text">{{$t('notebook.notebookRunning')}}</span>
</div>
<div v-else>
<button class="ui disabled small button" style="background-color: #888;"><i class="loading spinner icon" style="color: #fff;"></i>{{$t('notebook.stopping')}}</button>
</div>
</div>
<div class="resource-operate" v-if="selectIndex==2">
<div v-if="btnStatus[2]===0">
<button class="ui green small button" @click="createTask(2)"></i>{{$t('notebook.newTask')}}</button>
<span v-if="notebookInfo.waitCountNpu==0" class="text">{{$t('notebook.noQuene')}}</span>
<span v-else class="text">{{$t('notebook.queneTips1')}} <span style="color: red;">{{notebookInfo.waitCountNpu}}</span> {{$t('notebook.queneTips2')}}</span>
</div>
<div v-else-if="btnStatus[2]===1">
<button class="ui disabled small button" style="background-color: #888;"><i class="loading spinner icon" style="color: #fff;"></i>{{$t('notebook.newTask')}}</button>
<span class="text">{{$t('notebook.watiResource')}}</span>
</div>
<div v-else-if="btnStatus[2]===2">
<button class="ui small button" style="background-color: #1684fc;">
<a style="color:#fff" :href="deubgUrlNpu" target="_blank">{{$t('notebook.debug')}}</a>
</button>
<button class="ui small button" @click="stopDebug(2)">{{$t('notebook.stop')}}</button>
<span class="text">{{$t('notebook.notebookRunning')}}</span>
</div>
<div v-else>
<button class="ui disabled small button" style="background-color: #888;"><i class="loading spinner icon" style="color: #fff;"></i>{{$t('notebook.stopping')}}</button>
</div>
</div>
<div class="resource-operate" v-if="selectIndex==1">
<div v-if="btnStatus[1]===0">
<button class="ui green small button" @click="createTask(1)"></i>{{$t('notebook.newTask')}}</button>
<span v-if="notebookInfo.waitCountGpu==0" class="text">{{$t('notebook.noQuene')}}</span>
<span v-else class="text">{{$t('notebook.queneTips1')}} <span style="color: red;">{{notebookInfo.waitCountGpu}}</span> {{$t('notebook.queneTips2')}}</span>
</div>
<div v-else-if="btnStatus[1]===1">
<button class="ui disabled small button" style="background-color: #888;"><i class="loading spinner icon" style="color: #fff;"></i>{{$t('notebook.newTask')}}</button>
<span class="text">{{$t('notebook.watiResource')}}</span>
</div>
<div v-else-if="btnStatus[1]===2">
<button class="ui small button" style="background-color: #1684fc;">
<a style="color:#fff" :href="deubgUrlGpu" target="_blank">{{$t('notebook.debug')}}</a>
</button>
<button class="ui small button" @click="stopDebug(1)">{{$t('notebook.stop')}}</button>
<span class="text">{{$t('notebook.notebookRunning')}}</span>
</div>
<div v-else>
<button class="ui disabled small button" style="background-color: #888;"><i class="loading spinner icon" style="color: #fff;"></i>{{$t('notebook.stopping')}}</button>
</div>
</div>
</div>
</div>
</el-dialog>
</div>
</template>
<script>
import { getFileNotebook,createNotebook,getCb1Notebook,getCb2Notebook,stopNotebook } from "~/apis/modules/notobook";
import { Message } from "element-ui";
let timerCb1,timerCb2
let {AppSubUrl} = window.config
const finalState = [
"STOPPED",
"CREATE_FAILED",
"UNAVAILABLE",
"DELETED",
"RESIZE_FAILED",
"SUCCEEDED",
"IMAGE_FAILED",
"SUBMIT_FAILED",
"DELETE_FAILED",
"KILLED",
"COMPLETED",
"FAILED",
"CANCELED",
"LOST",
"START_FAILED",
"SUBMIT_MODEL_FAILED",
"DEPLOY_SERVICE_FAILED",
"CHECK_FAILED",
"STOPPING"
];
export default {
data() {
return {
dialogVisible: false,
selectIndex: 0,
slideActive:true,
initSelect:true,
notebookInfo:{specCpu:{acc_cards_num:0},specGpu:{acc_cards_num:0},specNpu:{acc_cards_num:0}},
fileInfo:{
file:'',
branch_name:'',
owner_name:'',
project_name:'',
sign_name:''
},
btnStatus:{0:0,1:0,2:0},
alertCb:false,
deubgUrlNpu:'',
deubgUrlGpu:'',
deubgUrlNpuStop:'',
deubgUrlGpuStop:'',
loading:false,
activeLoadFirst:true
};
},
methods: {
handleClose(done) {
this.initSelect = true
this.alertCb = false
},
selectResource(index) {
this.getNotebookInfo()
if(index==this.selectIndex){
return
}
if(index>this.selectIndex && this.selectIndex!==1){
this.slideActive = true
}else if(index<this.selectIndex && index==1){
this.slideActive = true
}else{
this.slideActive = false
}
this.selectIndex = index;
this.initSelect = false
this.alertCb = false
},
getNotebookInfo(){
if(this.activeLoadFirst){
this.loading = true
}
getFileNotebook().then((res)=>{
if(res.data.code==0){
this.notebookInfo = res.data
}else{
Message.error(res.data.message)
}
this.loading = false
this.activeLoadFirst = false
}).catch((err)=>{
Message.error(err)
this.loading = false
this.activeLoadFirst = false
})
},
getCb1NotebookInfo(path,id,index){
getCb1Notebook(path,id).then((res)=>{
if(res.status===200){
if(res.data.JobStatus==="RUNNING"){
this.btnStatus[index]=2
this.deubgUrlGpu = `${AppSubUrl}/${this.fileInfo.sign_name}/${this.notebookInfo.projectName}/cloudbrain/${id}/debug`
this.deubgUrlGpuStop = `${AppSubUrl}/${this.fileInfo.sign_name}/${this.notebookInfo.projectName}/cloudbrain/${id}/stop`
clearInterval(timerCb1)
}
if(finalState.includes(res.data.JobStatus)){
this.btnStatus[index] = 0
clearInterval(timerCb1)
}
}
})
},
getCb2NotebookInfo(path,id){
getCb2Notebook(path,id).then((res)=>{
if(res.status===200){
if(res.data.JobStatus==="RUNNING"){
this.btnStatus[2]=2
this.deubgUrlNpu = `${AppSubUrl}/${this.fileInfo.sign_name}/${this.notebookInfo.projectName}/modelarts/notebook/${id}/debug`
this.deubgUrlNpuStop = `${AppSubUrl}/${this.fileInfo.sign_name}/${this.notebookInfo.projectName}/modelarts/notebook/${id}/stop`
clearInterval(timerCb2)
}
if(finalState.includes(res.data.JobStatus)){
this.btnStatus[2] = 0
clearInterval(timerCb2)
}
}
})
},
stopDebug(index){
this.btnStatus[index]=3
let url = index===2 ? this.deubgUrlNpuStop :this.deubgUrlGpuStop
stopNotebook(url).then((res)=>{
if(res.data.result_code==='0'){
this.btnStatus[index]=0
Message.success(this.$t("notebook.stopSuccess"))
}else{
this.btnStatus[index]=0
Message.error(res.data.error_msg)
}
}).catch((err)=>{
this.btnStatus[index]=0
Message.error(err)
})
},
createTask(index){
this.btnStatus[index]=1
const data = {type:index,...this.fileInfo}
let repoPath = `repos/${this.fileInfo.owner_name}/${this.fileInfo.project_name}`
createNotebook(data).then((res)=>{
if(res.data.code===0 && res.status===200){
if(index===2){
timerCb2 = setInterval(() => {
setTimeout(this.getCb2NotebookInfo(repoPath,res.data.message), 0)
}, 10000)
}else{
timerCb1 = setInterval(() => {
setTimeout(this.getCb1NotebookInfo(repoPath,res.data.message,index), 0)
}, 10000)
}
this.alertCb = false
}else if(res.data.code==2){
this.btnStatus[index]=0
this.alertCb = true
}else{
this.btnStatus[index]=0
Message.error(res.data.message)
}
}).catch((err)=>{
if(err.response.status===403 && err.response.data.code===1 ){
location.href=`${AppSubUrl}/authentication/wechat/bind`
}
this.btnStatus[index]=0
this.alertCb = false
Message.error(err)
})
}
},
computed: {
cpuSpec(){
let cpu_spec = `${this.$t("notebook.specification")}:GPU: ${this.notebookInfo.specCpu.acc_cards_num}*${this.notebookInfo.specCpu.acc_card_type}, CPU: ${this.notebookInfo.specCpu.cpu_cores}`
if(this.notebookInfo.specCpu.gpu_mem_gi_b!==0){
cpu_spec += `, ${this.$t("notebook.graphicMemory")}: ${this.notebookInfo.specCpu.gpu_mem_gi_b}GB`
}
if(this.notebookInfo.specCpu.mem_gi_b!==0){
cpu_spec += `, ${this.$t("notebook.memory")}: ${this.notebookInfo.specCpu.mem_gi_b}GB`
}
if(this.notebookInfo.specCpu.share_mem_gi_b!==0){
cpu_spec += `, ${this.$t("notebook.sharedMemory")}: ${this.notebookInfo.specCpu.share_mem_gi_b}GB`
}
return cpu_spec
},
npuSpec(){
let acc_card_type = ''
if(this.notebookInfo.specNpu.acc_card_type==="ASCEND910"){
acc_card_type = "Ascend 910"
}
let npu_spec = `${this.$t("notebook.specification")}:NPU: ${this.notebookInfo.specNpu.acc_cards_num}*${acc_card_type}, CPU: ${this.notebookInfo.specNpu.cpu_cores}`
if(this.notebookInfo.specNpu.gpu_mem_gi_b!==0){
npu_spec += `, ${this.$t("notebook.graphicMemory")}: ${this.notebookInfo.specNpu.gpu_mem_gi_b}GB`
}
if(this.notebookInfo.specNpu.mem_gi_b!==0){
npu_spec += `, ${this.$t("notebook.memory")}: ${this.notebookInfo.specNpu.mem_gi_b}GB`
}
if(this.notebookInfo.specNpu.share_mem_gi_b!==0){
npu_spec += `, ${this.$t("notebook.sharedMemory")}: ${this.notebookInfo.specNpu.share_mem_gi_b}GB`
}
return npu_spec
},
gpuSpec(){
let gpu_spec = `${this.$t("notebook.specification")}:GPU: ${this.notebookInfo.specGpu.acc_cards_num}*${this.notebookInfo.specGpu.acc_card_type}, CPU: ${this.notebookInfo.specGpu.cpu_cores}`
if(this.notebookInfo.specGpu.gpu_mem_gi_b!==0){
gpu_spec += `, ${this.$t("notebook.graphicMemory")}: ${this.notebookInfo.specGpu.gpu_mem_gi_b}GB`
}
if(this.notebookInfo.specGpu.mem_gi_b!==0){
gpu_spec += `, ${this.$t("notebook.memory")}: ${this.notebookInfo.specGpu.mem_gi_b}GB`
}
if(this.notebookInfo.specGpu.share_mem_gi_b!==0){
gpu_spec += `, ${this.$t("notebook.sharedMemory")}: ${this.notebookInfo.specGpu.share_mem_gi_b}GB`
}
return gpu_spec
}
},
beforeDestroy() {
clearInterval(timerCb1)
clearInterval(timerCb2)
},
mounted() {
const selfData = document.querySelector('#__vue-self-data')
this.fileInfo.file = selfData.getAttribute('data-file')
this.fileInfo.branch_name = selfData.getAttribute('data-branch')
this.fileInfo.owner_name = selfData.getAttribute('data-owner')
this.fileInfo.project_name = selfData.getAttribute('data-project')
this.fileInfo.sign_name = selfData.getAttribute('data-name')
let that = this;
document
.querySelector("#notebook-debug")
.addEventListener("click", function () {
that.getNotebookInfo()
that.dialogVisible = true;
});
},
};
</script>
<style scoped lang="less">
/deep/ .el-dialog__header {
text-align: left;
height: 45px;
background: rgb(240, 240, 240);
border-radius: 5px 5px 0px 0px;
border-bottom: 1px solid rgb(212, 212, 213);
padding: 0 15px;
display: flex;
align-items: center;
font-weight: 500;
font-size: 16px;
color: rgb(16, 16, 16);
font-family: Roboto;

.el-dialog__title {
font-weight: 600;
font-size: 15px;
color: rgb(16, 16, 16);
}

.el-dialog__headerbtn {
top: 15px;
right: 15px;
}
}

/deep/ .el-dialog__body {
padding: 55px 15px 0 15px;
}
.wrapper {
width: 100%;
.active {
background: linear-gradient(
269.2deg,
rgba(183, 247, 255, 0.5) 0%,
rgba(233, 233, 255, 0) 78.67%
);
border-radius: 5px 5px 0px 0px;
}
.text-tip{
color: #888;
font-size: 12px;
}
.text-tip::before{
content: '*';
color: #f2711c;
}
.alert-info{
width: 70%;
background-color: rgba(242, 113, 28, 0.05);
border: 1px solid rgb(242, 113, 28);
border-radius: 5px;
margin: 0 auto;
padding-bottom: 10px;
}
& >.three-resource-type:nth-child(2){
border:none;
}
.three-resource-type {
width: 70%;
margin: 0 auto;
display: flex;
border-top: 1px solid rgba(16, 16, 16, 0.1);
cursor: pointer;
.resource-child-node {
display: flex;
align-items: center;
width: 100%;
height: 115px;
.resource-type-icon {
width: 50px;
height: 50px;
line-height: 20px;
border-radius: 25px;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
.text {
font-size: 26px;
color: rgba(251, 251, 251, 1);
font-family: ZKXiaoWeiLogo-regular;
}
}
.background-C {
background: linear-gradient(
134.2deg,
rgba(130, 209, 246, 1) 0%,
rgba(41, 182, 244, 1) 51.94%,
rgba(0, 137, 205, 1) 102.83%
);
}
.background-N {
background: linear-gradient(
151.47deg,
rgba(123, 50, 178, 1) 20.02%,
rgba(64, 26, 93, 1) 100%
);
}
.background-G {
background: linear-gradient(
-25.25deg,
rgba(254, 86, 77, 1) 9.3%,
rgba(251, 155, 54, 1) 38.86%,
rgba(249, 202, 38, 1) 67.95%
);
}
}
.resource-type-detail {
margin-left: 23px;
.detail-title {
font-family: SourceHanSansSC;
font-weight: 500;
font-size: 16px;
color: rgb(16, 16, 16);
font-style: normal;
letter-spacing: 0px;
line-height: 32px;
text-decoration: none;
}
.detail-spec {
font-family: SourceHanSansSC;
font-weight: 400;
font-size: 14px;
color: rgba(136, 136, 136, 1);
font-style: normal;
letter-spacing: 0px;
line-height: 24px;
text-decoration: none;
}
}
.resource-select {
margin-left: auto;
margin-right: 20px;
font-size: 20px;
height: 100%;
display: flex;
align-items: center;
.green {
color: green;
}
.gray {
color: rgba(16, 16, 16, 0.1);
}
}
}
.resource-footer {
margin-top: 40px;
border-top: 1px solid rgba(16, 16, 16, 0.1);
height: 71px;
display: flex;
align-items: center;
.resource-operate {
display: flex;
align-items: center;
.text{
color: #101010;
margin-left: 20px;
}
}
}
.slide-in-top {
-webkit-animation: slide-in-top 1s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
animation: slide-in-top 1s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
}
.slide-in-bottom {
-webkit-animation: slide-in-bottom 1s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
animation: slide-in-bottom 1s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
}
.fade-out {
-webkit-animation: fade-in 1.2s cubic-bezier(0.39, 0.575, 0.565, 1) both;
animation: fade-in 1.2s cubic-bezier(0.39, 0.575, 0.565, 1) both;
position: absolute
}
@-webkit-keyframes slide-in-top {
0% {
-webkit-transform: translateY(-50px);
transform: translateY(-50px);
opacity: 0;
}
100% {
-webkit-transform: translateY(0);
transform: translateY(0);
opacity: 1;
}
}
@keyframes slide-in-top {
0% {
-webkit-transform: translateY(-50px);
transform: translateY(-50px);
opacity: 0;
}
100% {
-webkit-transform: translateY(0);
transform: translateY(0);
opacity: 1;
}
}
@-webkit-keyframes slide-in-bottom {
0% {
-webkit-transform: translateY(50px);
transform: translateY(50px);
opacity: 0;
}
100% {
-webkit-transform: translateY(0);
transform: translateY(0);
opacity: 1;
}
}
@keyframes slide-in-bottom {
0% {
-webkit-transform: translateY(50px);
transform: translateY(50px);
opacity: 0;
}
100% {
-webkit-transform: translateY(0);
transform: translateY(0);
opacity: 1;
}
}
@-webkit-keyframes fade-in {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@keyframes fade-in {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
}
</style>

+ 12
- 0
web_src/vuepages/pages/notebook/debug/vp-notebook-debug.js View File

@@ -0,0 +1,12 @@
import Vue from 'vue';
import {Dialog,Loading} from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import { i18n, lang } from '~/langs';
import App from './index.vue';

Vue.use(Dialog)
Vue.use(Loading.directive)
new Vue({
i18n,
render: (h) => h(App),
}).$mount('#__vue-root');

Loading…
Cancel
Save