Reviewed-on: https://git.openi.org.cn/OpenI/aiforge/pulls/768 Reviewed-by: ychao_1983 <ychao_1983@sina.com>tags/v1.21.12.1
| @@ -353,6 +353,7 @@ type FlavorInfos struct { | |||||
| type FlavorInfo struct { | type FlavorInfo struct { | ||||
| Id int `json:"id"` | Id int `json:"id"` | ||||
| Value string `json:"value"` | Value string `json:"value"` | ||||
| Desc string `json:"desc"` | |||||
| } | } | ||||
| type PoolInfos struct { | type PoolInfos struct { | ||||
| @@ -19,6 +19,7 @@ type CreateModelArtsNotebookForm struct { | |||||
| JobName string `form:"job_name" binding:"Required"` | JobName string `form:"job_name" binding:"Required"` | ||||
| Attachment string `form:"attachment"` | Attachment string `form:"attachment"` | ||||
| Description string `form:"description"` | Description string `form:"description"` | ||||
| Flavor string `form:"flavor"` | |||||
| } | } | ||||
| func (f *CreateModelArtsNotebookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | func (f *CreateModelArtsNotebookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | ||||
| @@ -99,7 +99,7 @@ type ResourcePool struct { | |||||
| } `json:"resource_pool"` | } `json:"resource_pool"` | ||||
| } | } | ||||
| func GenerateTask(ctx *context.Context, jobName, uuid, description string) error { | |||||
| func GenerateTask(ctx *context.Context, jobName, uuid, description, flavor string) error { | |||||
| var dataActualPath string | var dataActualPath string | ||||
| if uuid != "" { | if uuid != "" { | ||||
| dataActualPath = setting.Bucket + "/" + setting.BasePath + path.Join(uuid[0:1], uuid[1:2]) + "/" + uuid + "/" | dataActualPath = setting.Bucket + "/" + setting.BasePath + path.Join(uuid[0:1], uuid[1:2]) + "/" + uuid + "/" | ||||
| @@ -128,7 +128,7 @@ func GenerateTask(ctx *context.Context, jobName, uuid, description string) error | |||||
| JobName: jobName, | JobName: jobName, | ||||
| Description: description, | Description: description, | ||||
| ProfileID: setting.ProfileID, | ProfileID: setting.ProfileID, | ||||
| Flavor: setting.Flavor, | |||||
| Flavor: flavor, | |||||
| Pool: models.Pool{ | Pool: models.Pool{ | ||||
| ID: poolInfos.PoolInfo[0].PoolId, | ID: poolInfos.PoolInfo[0].PoolId, | ||||
| Name: poolInfos.PoolInfo[0].PoolName, | Name: poolInfos.PoolInfo[0].PoolName, | ||||
| @@ -204,7 +204,7 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) { | |||||
| resourceSpecId := form.ResourceSpecId | resourceSpecId := form.ResourceSpecId | ||||
| if !jobNamePattern.MatchString(jobName) { | if !jobNamePattern.MatchString(jobName) { | ||||
| ctx.RenderWithErr(ctx.Tr("repo.cloudbrain_jobname_err"), tplModelArtsNew, &form) | |||||
| ctx.RenderWithErr(ctx.Tr("repo.cloudbrain_jobname_err"), tplCloudBrainNew, &form) | |||||
| return | return | ||||
| } | } | ||||
| @@ -27,15 +27,10 @@ import ( | |||||
| ) | ) | ||||
| const ( | const ( | ||||
| // tplModelArtsNotebookIndex base.TplName = "repo/modelarts/notebook/index" | |||||
| tplModelArtsNotebookIndex base.TplName = "repo/modelarts/notebook/index" | tplModelArtsNotebookIndex base.TplName = "repo/modelarts/notebook/index" | ||||
| tplModelArtsNotebookNew base.TplName = "repo/modelarts/notebook/new" | tplModelArtsNotebookNew base.TplName = "repo/modelarts/notebook/new" | ||||
| tplModelArtsNotebookShow base.TplName = "repo/modelarts/notebook/show" | tplModelArtsNotebookShow base.TplName = "repo/modelarts/notebook/show" | ||||
| tplModelArtsIndex base.TplName = "repo/modelarts/index" | |||||
| tplModelArtsNew base.TplName = "repo/modelarts/new" | |||||
| tplModelArtsShow base.TplName = "repo/modelarts/show" | |||||
| tplModelArtsTrainJobIndex base.TplName = "repo/modelarts/trainjob/index" | tplModelArtsTrainJobIndex base.TplName = "repo/modelarts/trainjob/index" | ||||
| tplModelArtsTrainJobNew base.TplName = "repo/modelarts/trainjob/new" | tplModelArtsTrainJobNew base.TplName = "repo/modelarts/trainjob/new" | ||||
| tplModelArtsTrainJobShow base.TplName = "repo/modelarts/trainjob/show" | tplModelArtsTrainJobShow base.TplName = "repo/modelarts/trainjob/show" | ||||
| @@ -50,229 +45,6 @@ func MustEnableModelArts(ctx *context.Context) { | |||||
| } | } | ||||
| } | } | ||||
| func ModelArtsIndex(ctx *context.Context) { | |||||
| MustEnableModelArts(ctx) | |||||
| repo := ctx.Repo.Repository | |||||
| page := ctx.QueryInt("page") | |||||
| if page <= 0 { | |||||
| page = 1 | |||||
| } | |||||
| ciTasks, count, err := models.Cloudbrains(&models.CloudbrainsOptions{ | |||||
| ListOptions: models.ListOptions{ | |||||
| Page: page, | |||||
| PageSize: setting.UI.IssuePagingNum, | |||||
| }, | |||||
| RepoID: repo.ID, | |||||
| Type: models.TypeCloudBrainTwo, | |||||
| }) | |||||
| if err != nil { | |||||
| ctx.ServerError("Cloudbrain", err) | |||||
| return | |||||
| } | |||||
| for i, task := range ciTasks { | |||||
| if task.Status == string(models.JobRunning) { | |||||
| ciTasks[i].CanDebug = true | |||||
| } else { | |||||
| ciTasks[i].CanDebug = false | |||||
| } | |||||
| ciTasks[i].CanDel = models.CanDelJob(ctx.IsSigned, ctx.User, task) | |||||
| } | |||||
| pager := context.NewPagination(int(count), setting.UI.IssuePagingNum, page, 5) | |||||
| pager.SetDefaultParams(ctx) | |||||
| ctx.Data["Page"] = pager | |||||
| ctx.Data["PageIsCloudBrain"] = true | |||||
| ctx.Data["Tasks"] = ciTasks | |||||
| ctx.HTML(200, tplModelArtsIndex) | |||||
| } | |||||
| func ModelArtsNew(ctx *context.Context) { | |||||
| ctx.Data["PageIsCloudBrain"] = true | |||||
| t := time.Now() | |||||
| var jobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:] | |||||
| ctx.Data["job_name"] = jobName | |||||
| attachs, err := models.GetModelArtsUserAttachments(ctx.User.ID) | |||||
| if err != nil { | |||||
| ctx.ServerError("GetAllUserAttachments failed:", err) | |||||
| return | |||||
| } | |||||
| ctx.Data["attachments"] = attachs | |||||
| ctx.Data["dataset_path"] = modelarts.DataSetMountPath | |||||
| ctx.Data["env"] = modelarts.NotebookEnv | |||||
| ctx.Data["notebook_type"] = modelarts.NotebookType | |||||
| if modelarts.FlavorInfos == nil { | |||||
| json.Unmarshal([]byte(setting.FlavorInfos), &modelarts.FlavorInfos) | |||||
| } | |||||
| ctx.Data["flavors"] = modelarts.FlavorInfos.FlavorInfo | |||||
| ctx.HTML(200, tplModelArtsNew) | |||||
| } | |||||
| func ModelArtsCreate(ctx *context.Context, form auth.CreateModelArtsForm) { | |||||
| ctx.Data["PageIsCloudBrain"] = true | |||||
| jobName := form.JobName | |||||
| uuid := form.Attachment | |||||
| description := form.Description | |||||
| //repo := ctx.Repo.Repository | |||||
| if !jobNamePattern.MatchString(jobName) { | |||||
| ctx.RenderWithErr(ctx.Tr("repo.cloudbrain_jobname_err"), tplModelArtsNew, &form) | |||||
| return | |||||
| } | |||||
| err := modelarts.GenerateTask(ctx, jobName, uuid, description) | |||||
| if err != nil { | |||||
| ctx.RenderWithErr(err.Error(), tplModelArtsNew, &form) | |||||
| return | |||||
| } | |||||
| ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts") | |||||
| } | |||||
| func ModelArtsShow(ctx *context.Context) { | |||||
| ctx.Data["PageIsCloudBrain"] = true | |||||
| var jobID = ctx.Params(":jobid") | |||||
| task, err := models.GetCloudbrainByJobID(jobID) | |||||
| if err != nil { | |||||
| ctx.Data["error"] = err.Error() | |||||
| ctx.RenderWithErr(err.Error(), tplModelArtsIndex, nil) | |||||
| return | |||||
| } | |||||
| result, err := modelarts.GetJob(jobID) | |||||
| if err != nil { | |||||
| ctx.Data["error"] = err.Error() | |||||
| ctx.RenderWithErr(err.Error(), tplModelArtsIndex, nil) | |||||
| return | |||||
| } | |||||
| if result != nil { | |||||
| task.Status = result.Status | |||||
| err = models.UpdateJob(task) | |||||
| if err != nil { | |||||
| ctx.Data["error"] = err.Error() | |||||
| ctx.RenderWithErr(err.Error(), tplModelArtsIndex, nil) | |||||
| return | |||||
| } | |||||
| createTime, _ := com.StrTo(result.CreationTimestamp).Int64() | |||||
| result.CreateTime = time.Unix(int64(createTime/1000), 0).Format("2006-01-02 15:04:05") | |||||
| endTime, _ := com.StrTo(result.LatestUpdateTimestamp).Int64() | |||||
| result.LatestUpdateTime = time.Unix(int64(endTime/1000), 0).Format("2006-01-02 15:04:05") | |||||
| result.QueuingInfo.BeginTime = time.Unix(int64(result.QueuingInfo.BeginTimestamp/1000), 0).Format("2006-01-02 15:04:05") | |||||
| result.QueuingInfo.EndTime = time.Unix(int64(result.QueuingInfo.EndTimestamp/1000), 0).Format("2006-01-02 15:04:05") | |||||
| } | |||||
| ctx.Data["task"] = task | |||||
| ctx.Data["jobID"] = jobID | |||||
| ctx.Data["result"] = result | |||||
| ctx.HTML(200, tplModelArtsShow) | |||||
| } | |||||
| func ModelArtsDebug(ctx *context.Context) { | |||||
| var jobID = ctx.Params(":jobid") | |||||
| _, err := models.GetCloudbrainByJobID(jobID) | |||||
| if err != nil { | |||||
| ctx.ServerError("GetCloudbrainByJobID failed", err) | |||||
| return | |||||
| } | |||||
| result, err := modelarts.GetJob(jobID) | |||||
| if err != nil { | |||||
| ctx.RenderWithErr(err.Error(), tplModelArtsIndex, nil) | |||||
| return | |||||
| } | |||||
| res, err := modelarts.GetJobToken(jobID) | |||||
| if err != nil { | |||||
| ctx.RenderWithErr(err.Error(), tplModelArtsIndex, nil) | |||||
| return | |||||
| } | |||||
| urls := strings.Split(result.Spec.Annotations.Url, "/") | |||||
| urlPrefix := result.Spec.Annotations.TargetDomain | |||||
| for i, url := range urls { | |||||
| if i > 2 { | |||||
| urlPrefix += "/" + url | |||||
| } | |||||
| } | |||||
| //urlPrefix := result.Spec.Annotations.TargetDomain + "/modelarts/internal/hub/notebook/user/" + task.JobID | |||||
| log.Info(urlPrefix) | |||||
| debugUrl := urlPrefix + "?token=" + res.Token | |||||
| ctx.Redirect(debugUrl) | |||||
| } | |||||
| func ModelArtsStop(ctx *context.Context) { | |||||
| var jobID = ctx.Params(":jobid") | |||||
| log.Info(jobID) | |||||
| task, err := models.GetCloudbrainByJobID(jobID) | |||||
| if err != nil { | |||||
| ctx.ServerError("GetCloudbrainByJobID failed", err) | |||||
| return | |||||
| } | |||||
| if task.Status != string(models.JobRunning) { | |||||
| log.Error("the job(%s) is not running", task.JobName) | |||||
| ctx.ServerError("the job is not running", errors.New("the job is not running")) | |||||
| return | |||||
| } | |||||
| param := models.NotebookAction{ | |||||
| Action: models.ActionStop, | |||||
| } | |||||
| res, err := modelarts.StopJob(jobID, param) | |||||
| if err != nil { | |||||
| log.Error("StopJob(%s) failed:%v", task.JobName, err.Error()) | |||||
| ctx.ServerError("StopJob failed", err) | |||||
| return | |||||
| } | |||||
| task.Status = res.CurrentStatus | |||||
| err = models.UpdateJob(task) | |||||
| if err != nil { | |||||
| ctx.ServerError("UpdateJob failed", err) | |||||
| return | |||||
| } | |||||
| ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts") | |||||
| } | |||||
| func ModelArtsDel(ctx *context.Context) { | |||||
| var jobID = ctx.Params(":jobid") | |||||
| task, err := models.GetCloudbrainByJobID(jobID) | |||||
| if err != nil { | |||||
| ctx.ServerError("GetCloudbrainByJobID failed", err) | |||||
| return | |||||
| } | |||||
| if task.Status != string(models.ModelArtsCreateFailed) && task.Status != string(models.ModelArtsStartFailed) && task.Status != string(models.ModelArtsStopped) { | |||||
| log.Error("the job(%s) has not been stopped", task.JobName) | |||||
| ctx.ServerError("the job has not been stopped", errors.New("the job has not been stopped")) | |||||
| return | |||||
| } | |||||
| _, err = modelarts.DelJob(jobID) | |||||
| if err != nil { | |||||
| log.Error("DelJob(%s) failed:%v", task.JobName, err.Error()) | |||||
| ctx.ServerError("DelJob failed", err) | |||||
| return | |||||
| } | |||||
| err = models.DeleteJob(task) | |||||
| if err != nil { | |||||
| ctx.ServerError("DeleteJob failed", err) | |||||
| return | |||||
| } | |||||
| ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts") | |||||
| } | |||||
| func NotebookIndex(ctx *context.Context) { | func NotebookIndex(ctx *context.Context) { | ||||
| MustEnableModelArts(ctx) | MustEnableModelArts(ctx) | ||||
| repo := ctx.Repo.Repository | repo := ctx.Repo.Repository | ||||
| @@ -342,8 +114,9 @@ func NotebookCreate(ctx *context.Context, form auth.CreateModelArtsNotebookForm) | |||||
| jobName := form.JobName | jobName := form.JobName | ||||
| uuid := form.Attachment | uuid := form.Attachment | ||||
| description := form.Description | description := form.Description | ||||
| flavor := form.Flavor | |||||
| err := modelarts.GenerateTask(ctx, jobName, uuid, description) | |||||
| err := modelarts.GenerateTask(ctx, jobName, uuid, description, flavor) | |||||
| if err != nil { | if err != nil { | ||||
| ctx.RenderWithErr(err.Error(), tplModelArtsNotebookNew, &form) | ctx.RenderWithErr(err.Error(), tplModelArtsNotebookNew, &form) | ||||
| return | return | ||||
| @@ -1,485 +0,0 @@ | |||||
| <!-- 头部导航栏 --> | |||||
| {{template "base/head" .}} | |||||
| <style> | |||||
| .selectcloudbrain .active.item{ | |||||
| color: #0087f5 !important; | |||||
| border: 1px solid #0087f5; | |||||
| margin: -1px; | |||||
| background: #FFF !important; | |||||
| } | |||||
| #deletemodel { | |||||
| width: 100%; | |||||
| height: 100%; | |||||
| } | |||||
| /* 弹窗 */ | |||||
| #mask { | |||||
| position: fixed; | |||||
| top: 0px; | |||||
| left: 0px; | |||||
| right: 0px; | |||||
| bottom: 0px; | |||||
| filter: alpha(opacity=60); | |||||
| background-color: #777; | |||||
| z-index: 1000; | |||||
| display: none; | |||||
| opacity: 0.8; | |||||
| -moz-opacity: 0.5; | |||||
| padding-top: 100px; | |||||
| color: #000000 | |||||
| } | |||||
| #loadingPage { | |||||
| margin: 200px auto; | |||||
| width: 50px; | |||||
| height: 40px; | |||||
| text-align: center; | |||||
| font-size: 10px; | |||||
| display: block; | |||||
| } | |||||
| #loadingPage>div { | |||||
| background-color: green; | |||||
| height: 100%; | |||||
| width: 6px; | |||||
| display: inline-block; | |||||
| -webkit-animation: sk-stretchdelay 1.2s infinite ease-in-out; | |||||
| animation: sk-stretchdelay 1.2s infinite ease-in-out; | |||||
| } | |||||
| #loadingPage .rect2 { | |||||
| -webkit-animation-delay: -1.1s; | |||||
| animation-delay: -1.1s; | |||||
| } | |||||
| #loadingPage .rect3 { | |||||
| -webkit-animation-delay: -1.0s; | |||||
| animation-delay: -1.0s; | |||||
| } | |||||
| #loadingPage .rect4 { | |||||
| -webkit-animation-delay: -0.9s; | |||||
| animation-delay: -0.9s; | |||||
| } | |||||
| #loadingPage .rect5 { | |||||
| -webkit-animation-delay: -0.8s; | |||||
| animation-delay: -0.8s; | |||||
| } | |||||
| @-webkit-keyframes sk-stretchdelay { | |||||
| 0%, | |||||
| 40%, | |||||
| 100% { | |||||
| -webkit-transform: scaleY(0.4) | |||||
| } | |||||
| 20% { | |||||
| -webkit-transform: scaleY(1.0) | |||||
| } | |||||
| } | |||||
| @keyframes sk-stretchdelay { | |||||
| 0%, | |||||
| 40%, | |||||
| 100% { | |||||
| transform: scaleY(0.4); | |||||
| -webkit-transform: scaleY(0.4); | |||||
| } | |||||
| 20% { | |||||
| transform: scaleY(1.0); | |||||
| -webkit-transform: scaleY(1.0); | |||||
| } | |||||
| } | |||||
| /* 消息框 */ | |||||
| .alert { | |||||
| display: none; | |||||
| position: fixed; | |||||
| width: 100%; | |||||
| z-index: 1001; | |||||
| padding: 15px; | |||||
| border: 1px solid transparent; | |||||
| border-radius: 4px; | |||||
| text-align: center; | |||||
| font-weight: bold; | |||||
| } | |||||
| .alert-success { | |||||
| color: #3c763d; | |||||
| background-color: #dff0d8; | |||||
| border-color: #d6e9c6; | |||||
| } | |||||
| .alert-info { | |||||
| color: #31708f; | |||||
| background-color: #d9edf7; | |||||
| border-color: #bce8f1; | |||||
| } | |||||
| .alert-warning { | |||||
| color: #8a6d3b; | |||||
| background-color: #fcf8e3; | |||||
| border-color: #faebcc; | |||||
| } | |||||
| .alert-danger { | |||||
| color: #a94442; | |||||
| background-color: #f2dede; | |||||
| border-color: #ebccd1; | |||||
| } | |||||
| .pusher { | |||||
| width: calc(100% - 260px); | |||||
| box-sizing: border-box; | |||||
| } | |||||
| /* 弹窗 (background) */ | |||||
| #imageModal { | |||||
| display: none; | |||||
| position: fixed; | |||||
| z-index: 1; | |||||
| left: 0; | |||||
| top: 0; | |||||
| width: 100%; | |||||
| height: 100%; | |||||
| overflow: auto; | |||||
| background-color: rgb(0, 0, 0); | |||||
| background-color: rgba(0, 0, 0, 0.4); | |||||
| } | |||||
| /* 弹窗内容 */ | |||||
| .modal-content { | |||||
| background-color: #fefefe; | |||||
| margin: 15% auto; | |||||
| padding: 20px; | |||||
| border: 1px solid #888; | |||||
| width: 30%; | |||||
| } | |||||
| /* 关闭按钮 */ | |||||
| .close { | |||||
| color: #aaa; | |||||
| float: right; | |||||
| font-size: 28px; | |||||
| font-weight: bold; | |||||
| } | |||||
| .close:hover, | |||||
| .close:focus { | |||||
| color: black; | |||||
| text-decoration: none; | |||||
| cursor: pointer; | |||||
| } | |||||
| .dis { | |||||
| margin-bottom: 20px; | |||||
| } | |||||
| .disabled { | |||||
| cursor: pointer; | |||||
| pointer-events: none; | |||||
| } | |||||
| </style> | |||||
| <!-- 弹窗 --> | |||||
| <div id="mask"> | |||||
| <div id="loadingPage"> | |||||
| <div class="rect1"></div> | |||||
| <div class="rect2"></div> | |||||
| <div class="rect3"></div> | |||||
| <div class="rect4"></div> | |||||
| <div class="rect5"></div> | |||||
| </div> | |||||
| </div> | |||||
| <!-- 提示框 --> | |||||
| <div class="alert"></div> | |||||
| <div class="repository release dataset-list view"> | |||||
| {{template "repo/header" .}} | |||||
| <!-- 列表容器 --> | |||||
| <div class="ui container"> | |||||
| <!-- 中间云脑和新建任务按钮 --> | |||||
| <div class="ui two column stackable grid "> | |||||
| <div class="column"> | |||||
| <div class="ui blue small menu compact selectcloudbrain"> | |||||
| <a class="active item" href="{{.RepoLink}}/modelarts/notebook">调试任务</a> | |||||
| <a class="item" href="{{.RepoLink}}/modelarts/train-job">训练任务</a> | |||||
| </div> | |||||
| </div> | |||||
| <div class="column right aligned"> | |||||
| <div class="ui selection dropdown" style="min-width: 10em;min-height:2.6em;border-radius: .28571429rem;margin-right: 1em;padding: .67em 3.2em .7em 1em;"> | |||||
| {{svg "octicon-server" 16}} | |||||
| <div class="default text" style="color: rgba(0,0,0,.87);"> Ascend NPU</div> | |||||
| <i class="dropdown icon"></i> | |||||
| <div class="menu"> | |||||
| <a class="item" href="{{.RepoLink}}/cloudbrain" data-value="11">CPU / GPU</a> | |||||
| <a class="item" href="{{.RepoLink}}/modelarts" data-value="22">Ascend NPU</a> | |||||
| </div> | |||||
| </div> | |||||
| <a class="ui green button" href="{{.RepoLink}}/modelarts/create">新建调试任务</a> | |||||
| </div> | |||||
| </div> | |||||
| <!-- 中下列表展示区 --> | |||||
| <div class="ui grid"> | |||||
| <div class="row"> | |||||
| <div class="ui sixteen wide column"> | |||||
| <!-- 排序区 --> | |||||
| <!-- <div class="ui sixteen wide column"> | |||||
| <div class="ui two column stackable grid"> | |||||
| <div class="column"> | |||||
| </div> | |||||
| <div class="column right aligned"> | |||||
| <div class="ui right dropdown type jump item"> | |||||
| <span class="text"> | |||||
| {{.i18n.Tr "repo.issues.filter_sort"}}<i class="dropdown icon"></i> | |||||
| </span> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> --> | |||||
| <!-- 任务展示 --> | |||||
| <div class="dataset list"> | |||||
| <!-- 表头 --> | |||||
| <div class="ui grid stackable" style="background: #f0f0f0;;"> | |||||
| <div class="row"> | |||||
| <div class="five wide column"> | |||||
| <span style="margin:0 6px">{{$.i18n.Tr "repo.cloudbrain_task"}}</span> | |||||
| </div> | |||||
| <div class="three wide column"> | |||||
| <span>{{$.i18n.Tr "repo.cloudbrain_status_createtime"}}</span> | |||||
| </div> | |||||
| <div class="one wide column"> | |||||
| <span>{{$.i18n.Tr "repo.cloudbrain_creator"}}</span> | |||||
| </div> | |||||
| <div class="seven wide column text center"> | |||||
| <span style="margin-left: 10rem;">{{$.i18n.Tr "repo.cloudbrain_operate"}}</span> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| {{range .Tasks}} | |||||
| <div class="ui grid stackable item"> | |||||
| <div class="row"> | |||||
| <!-- 任务名 --> | |||||
| <div class="five wide column"> | |||||
| <a class="title" href="{{$.Link}}/{{.JobID}}" title="{{.JobName}}" style="font-size: 15px;"> | |||||
| <span class="fitted" style="vertical-align: middle;">{{svg "octicon-tasklist" 16}}</span> | |||||
| <span class="fitted" style="width: 90%;vertical-align: middle;margin-left: 0.4rem;">{{.JobName}}</span> | |||||
| </a> | |||||
| </div> | |||||
| <div class="three wide column"> | |||||
| <!--任务状态 --> | |||||
| <!-- <span class="ui compact button job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}"> | |||||
| {{.Status}} | |||||
| </span> --> | |||||
| <span class="job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}"> | |||||
| <span><i style="vertical-align: middle;" class="{{.Status}}"></i><span style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span> | |||||
| </span> | |||||
| <!-- 任务创建时间 --> | |||||
| <span style="font-size: 12px;margin-left: 0.4rem;" class="">{{TimeSinceUnix .Cloudbrain.CreatedUnix $.Lang}}</span> | |||||
| </div> | |||||
| <div class="one wide column"> | |||||
| {{if .User.Name}} | |||||
| <a href="{{AppSubUrl}}/{{.User.Name}}" title="{{.User.Name}}"><img class="ui avatar image" src="{{.User.RelAvatarLink}}"></a> | |||||
| {{else}} | |||||
| <a title="Ghost"><img class="ui avatar image" src="{{AppSubUrl}}/user/avatar/Ghost/-1"></a> | |||||
| {{end}} | |||||
| </div> | |||||
| <div class="seven wide column text right"> | |||||
| <div class="ui compact buttons" style="margin-right:10px;"> | |||||
| <a class="ui basic blue button" href="{{$.Link}}/{{.JobID}}"> | |||||
| 查看 | |||||
| </a> | |||||
| <a class="ui basic {{if not .CanDebug}}disabled {{else}}blue {{end}}button" href="{{$.Link}}/{{.JobID}}/debug" target="_blank"> | |||||
| 调试 | |||||
| </a> | |||||
| <form id="stopForm-{{.JobID}}" action="{{if ne .Status "RUNNING"}}javascript:void(0){{else}}{{$.Link}}/{{.JobID}}/stop{{end}}" method="post" style="margin-left:-1px;"> | |||||
| {{$.CsrfTokenHtml}} | |||||
| <a class="ui basic {{if ne .Status "RUNNING"}}disabled {{else}}blue {{end}}button" onclick="document.getElementById('stopForm-{{.JobID}}').submit();"> | |||||
| 停止 | |||||
| </a> | |||||
| </form> | |||||
| </div> | |||||
| <!-- 删除任务 --> | |||||
| <form class="ui compact buttons" id="delForm-{{.JobID}}" action="{{if not .CanDel}}javascript:void(0){{else}}{{$.Link}}/{{.JobID}}/del{{end}}" method="post"> | |||||
| {{$.CsrfTokenHtml}} | |||||
| <a class="ui compact {{if not .CanDel}}disabled {{else}}red {{end}}button" onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | |||||
| 删除 | |||||
| </a> | |||||
| </form> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| {{end}} {{template "base/paginate" .}} | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <!-- 确认模态框 --> | |||||
| <div id="deletemodel"> | |||||
| <div class="ui basic modal"> | |||||
| <div class="ui icon header"> | |||||
| <i class="trash icon"></i> 删除任务 | |||||
| </div> | |||||
| <div class="content"> | |||||
| <p>你确认删除该任务么?此任务一旦删除不可恢复。</p> | |||||
| </div> | |||||
| <div class="actions"> | |||||
| <div class="ui red basic inverted cancel button"> | |||||
| <i class="remove icon"></i> 取消操作 | |||||
| </div> | |||||
| <div class="ui green basic inverted ok button"> | |||||
| <i class="checkmark icon"></i> 确定操作 | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| {{template "base/footer" .}} | |||||
| <script> | |||||
| // 调试和评分新开窗口 | |||||
| function stop(obj) { | |||||
| if (obj.style.color != "rgb(204, 204, 204)") { | |||||
| obj.target = '_blank' | |||||
| } else { | |||||
| return | |||||
| } | |||||
| } | |||||
| // 删除时用户确认 | |||||
| function assertDelete(obj) { | |||||
| if (obj.style.color == "rgb(204, 204, 204)") { | |||||
| return | |||||
| } else { | |||||
| var delId = obj.parentNode.id | |||||
| flag = 1; | |||||
| $('.ui.basic.modal') | |||||
| .modal({ | |||||
| onDeny: function() { | |||||
| flag = false | |||||
| }, | |||||
| onApprove: function() { | |||||
| document.getElementById(delId).submit() | |||||
| flag = true | |||||
| }, | |||||
| onHidden: function() { | |||||
| if (flag == false) { | |||||
| $('.alert').html('您已取消操作').removeClass('alert-success').addClass('alert-danger').show().delay(1500).fadeOut(); | |||||
| } | |||||
| } | |||||
| }) | |||||
| .modal('show') | |||||
| } | |||||
| } | |||||
| // 加载任务状态 | |||||
| var timeid = window.setInterval(loadJobStatus, 15000); | |||||
| $(document).ready(loadJobStatus); | |||||
| function loadJobStatus() { | |||||
| $(".job-status").each((index, job) => { | |||||
| const jobID = job.dataset.jobid; | |||||
| const repoPath = job.dataset.repopath; | |||||
| if (job.textContent.trim() == 'STOPPED' || job.textContent.trim() == 'START_FAILED' || job.textContent.trim() == 'CREATE_FAILED') { | |||||
| return | |||||
| } | |||||
| $.get(`/api/v1/repos/${repoPath}/modelarts/${jobID}`, (data) => { | |||||
| const jobID = data.JobID | |||||
| const status = data.JobStatus | |||||
| if (status != job.textContent.trim()) { | |||||
| //$('#' + jobID).text(status) | |||||
| //if (status == 'STOPPED') { | |||||
| window.location.reload() | |||||
| //} | |||||
| } | |||||
| }).fail(function(err) { | |||||
| console.log(err); | |||||
| }); | |||||
| }); | |||||
| }; | |||||
| // 获取弹窗 | |||||
| var modal = document.getElementById('imageModal'); | |||||
| // 打开弹窗的按钮对象 | |||||
| var btns = document.getElementsByClassName("imageBtn"); | |||||
| // 获取 <span> 元素,用于关闭弹窗 | |||||
| var spans = document.getElementsByClassName('close'); | |||||
| // 点击按钮打开弹窗 | |||||
| for (i = 0; i < btns.length; i++) { | |||||
| btns[i].onclick = function() { | |||||
| modal.style.display = "block"; | |||||
| } | |||||
| } | |||||
| // 点击 <span> (x), 关闭弹窗 | |||||
| for (i = 0; i < spans.length; i++) { | |||||
| spans[i].onclick = function() { | |||||
| modal.style.display = "none"; | |||||
| } | |||||
| } | |||||
| // 在用户点击其他地方时,关闭弹窗 | |||||
| window.onclick = function(event) { | |||||
| if (event.target == modal) { | |||||
| modal.style.display = "none"; | |||||
| } | |||||
| } | |||||
| // 显示弹窗,弹出相应的信息 | |||||
| function showmask() { | |||||
| $('#imageModal').css('display', 'none') | |||||
| $('#mask').css('display', 'block') | |||||
| $("iframe[name=iframeContent]").on("load", function() { | |||||
| var responseText = $("iframe")[0].contentDocument.body.getElementsByTagName("pre")[0].innerHTML; | |||||
| var json1 = JSON.parse(responseText) | |||||
| $('#mask').css('display', 'none') | |||||
| parent.location.href | |||||
| if (json1.result_code === "0") { | |||||
| $('.alert').html('操作成功!').removeClass('alert-danger').addClass('alert-success').show().delay(1500).fadeOut(); | |||||
| } else { | |||||
| $('.alert').html(json1.error_msg).removeClass('alert-success').addClass('alert-danger').show().delay(5000).fadeOut(); | |||||
| } | |||||
| }) | |||||
| } | |||||
| </script> | |||||
| @@ -1,240 +0,0 @@ | |||||
| {{template "base/head" .}} | |||||
| <style> | |||||
| /* 遮罩层css效果图 */ | |||||
| #mask { | |||||
| position: fixed; | |||||
| top: 0px; | |||||
| left: 0px; | |||||
| right: 0px; | |||||
| bottom: 0px; | |||||
| filter: alpha(opacity=60); | |||||
| background-color: #777; | |||||
| z-index: 1000; | |||||
| display: none; | |||||
| opacity: 0.8; | |||||
| -moz-opacity: 0.5; | |||||
| padding-top: 100px; | |||||
| color: #000000 | |||||
| } | |||||
| /* 加载圈css效果图 */ | |||||
| #loadingPage { | |||||
| margin: 200px auto; | |||||
| width: 50px; | |||||
| height: 40px; | |||||
| text-align: center; | |||||
| font-size: 10px; | |||||
| display: block; | |||||
| } | |||||
| #loadingPage>div { | |||||
| background-color: green; | |||||
| height: 100%; | |||||
| width: 6px; | |||||
| display: inline-block; | |||||
| -webkit-animation: sk-stretchdelay 1.2s infinite ease-in-out; | |||||
| animation: sk-stretchdelay 1.2s infinite ease-in-out; | |||||
| } | |||||
| #loadingPage .rect2 { | |||||
| -webkit-animation-delay: -1.1s; | |||||
| animation-delay: -1.1s; | |||||
| } | |||||
| #loadingPage .rect3 { | |||||
| -webkit-animation-delay: -1.0s; | |||||
| animation-delay: -1.0s; | |||||
| } | |||||
| #loadingPage .rect4 { | |||||
| -webkit-animation-delay: -0.9s; | |||||
| animation-delay: -0.9s; | |||||
| } | |||||
| #loadingPage .rect5 { | |||||
| -webkit-animation-delay: -0.8s; | |||||
| animation-delay: -0.8s; | |||||
| } | |||||
| @-webkit-keyframes sk-stretchdelay { | |||||
| 0%, | |||||
| 40%, | |||||
| 100% { | |||||
| -webkit-transform: scaleY(0.4) | |||||
| } | |||||
| 20% { | |||||
| -webkit-transform: scaleY(1.0) | |||||
| } | |||||
| } | |||||
| @keyframes sk-stretchdelay { | |||||
| 0%, | |||||
| 40%, | |||||
| 100% { | |||||
| transform: scaleY(0.4); | |||||
| -webkit-transform: scaleY(0.4); | |||||
| } | |||||
| 20% { | |||||
| transform: scaleY(1.0); | |||||
| -webkit-transform: scaleY(1.0); | |||||
| } | |||||
| } | |||||
| .inline.required.field.cloudbrain_benchmark { | |||||
| display: none; | |||||
| } | |||||
| </style> | |||||
| <div id="mask"> | |||||
| <div id="loadingPage"> | |||||
| <div class="rect1"></div> | |||||
| <div class="rect2"></div> | |||||
| <div class="rect3"></div> | |||||
| <div class="rect4"></div> | |||||
| <div class="rect5"></div> | |||||
| </div> | |||||
| </div> | |||||
| <div class="repository"> | |||||
| {{template "repo/header" .}} | |||||
| <div class="repository new repo ui middle very relaxed page grid"> | |||||
| <div class="column"> | |||||
| {{template "base/alert" .}} | |||||
| <div class="ui negative message" id="messageInfo"> | |||||
| <p></p> | |||||
| </div> | |||||
| <form class="ui form" id="form_id" action="{{.Link}}" method="post"> | |||||
| {{.CsrfTokenHtml}} | |||||
| <h3 class="ui top attached header"> | |||||
| {{.i18n.Tr "repo.cloudbrain.new"}} | |||||
| </h3> | |||||
| <div class="ui attached segment"> | |||||
| <!-- <br> --> | |||||
| <div class="inline required field"> | |||||
| <label>任务名称</label> | |||||
| <input name="job_name" id="cloudbrain_job_name" placeholder="任务名称" value="{{.job_name}}" tabindex="3" autofocus required maxlength="255"> | |||||
| </div> | |||||
| <div class="inline field"> | |||||
| <label>数据集</label> | |||||
| <input type="text" list="cloudbrain_dataset" placeholder="选择数据集" name="" id="answerInput" autofocus maxlength="36"> | |||||
| <datalist id="cloudbrain_dataset" class="ui search" style='width:385px' name="attachment"> | |||||
| {{range .attachments}} | |||||
| <option name="attachment" data-value="{{.UUID}}">{{.Attachment.Name}}</option> | |||||
| {{end}} | |||||
| </datalist> | |||||
| <input type="hidden" name="attachment" id="answerInput-hidden"> | |||||
| </div> | |||||
| <div class="inline required field"> | |||||
| <label>工作环境</label> | |||||
| <input name="de" id="cloudbrain_de" value="{{.env}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | |||||
| </div> | |||||
| <div class="inline required field"> | |||||
| <label>类型</label> | |||||
| <input name="job_type" id="cloudbrain_job_type" value="{{.notebook_type}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | |||||
| </div> | |||||
| <div class="inline required field"> | |||||
| <label>规格</label> | |||||
| <select id="cloudbrain_flavor" class="ui search dropdown" placeholder="选择规格" style='width:385px' name="flavor"> | |||||
| {{range .flavors}} | |||||
| <option name="flavor" value="{{.Value}}">{{.Value}}</option> | |||||
| {{end}} | |||||
| </select> | |||||
| </div> | |||||
| <div class="inline required field"> | |||||
| <label>数据集存放路径</label> | |||||
| <input name="dataset_path" id="cloudbrain_dataset_path" value="{{.dataset_path}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | |||||
| </div> | |||||
| <div class="inline field"> | |||||
| <label>描述</label> | |||||
| <input name="description" id="cloudbrain_description" tabindex="3" autofocus maxlength="255"> | |||||
| </div> | |||||
| <div class="inline field"> | |||||
| <label></label> | |||||
| <button class="ui green button"> | |||||
| {{.i18n.Tr "repo.cloudbrain.new"}} | |||||
| </button> | |||||
| <a class="ui button" href="/">{{.i18n.Tr "repo.cloudbrain.cancel"}}</a> | |||||
| </div> | |||||
| </div> | |||||
| </form> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| {{template "base/footer" .}} | |||||
| <script> | |||||
| // 取消创建跳转 | |||||
| let url_href = window.location.pathname.split('create')[0] | |||||
| $(".ui.button").attr('href',url_href) | |||||
| // 判断必填选项是否填写正确 | |||||
| let form = document.getElementById('form_id'); | |||||
| $('#messageInfo').css('display','none') | |||||
| form.onsubmit = function(e){ | |||||
| let value_task = $("input[name='job_name']").val() | |||||
| let re = /^[a-z0-9][a-z0-9-_]{1,34}[a-z0-9-]$/ | |||||
| let flag = re.test(value_task) | |||||
| if(!flag){ | |||||
| $('#messageInfo').css('display','block') | |||||
| let str = '只能以小写字母或数字开头且只包含小写字母、数字、_和-,不能以_结尾,最长36个字符。' | |||||
| $('#messageInfo p').text(str) | |||||
| return false | |||||
| } | |||||
| let min_value_task = value_task.toLowerCase() | |||||
| $("input[name='job_name']").attr("value",min_value_task) | |||||
| document.getElementById("mask").style.display = "block" | |||||
| } | |||||
| // 点击按钮后遮罩层显示 | |||||
| // function showmask() { | |||||
| // document.getElementById("mask").style.display = "block" | |||||
| // } | |||||
| // 页面加载完毕后遮罩层隐藏 | |||||
| document.onreadystatechange = function() { | |||||
| if (document.readyState === "complete") { | |||||
| document.getElementById("mask").style.display = "none" | |||||
| } | |||||
| } | |||||
| $('select.dropdown') | |||||
| .dropdown(); | |||||
| $(function() { | |||||
| $("#cloudbrain_job_type").change(function() { | |||||
| if ($(this).val() == 'BENCHMARK') { | |||||
| $(".cloudbrain_benchmark").show(); | |||||
| } else { | |||||
| $(".cloudbrain_benchmark").hide(); | |||||
| } | |||||
| }) | |||||
| }) | |||||
| document.querySelector('input[list]').addEventListener('input',function(e){ | |||||
| var input = e.target, | |||||
| list = input.getAttribute('list'), | |||||
| options = document.querySelectorAll('#'+list+' option'), | |||||
| hiddenInput = document.getElementById(input.getAttribute('id')+'-hidden'), | |||||
| inputValue = input.value; | |||||
| hiddenInput.value = inputValue; | |||||
| for (let i=0;i<options.length;i++){ | |||||
| var option = options[i] | |||||
| if(option.innerText===inputValue){ | |||||
| hiddenInput.value = option.getAttribute('data-value'); | |||||
| break | |||||
| } | |||||
| } | |||||
| }) | |||||
| </script> | |||||
| @@ -138,7 +138,7 @@ | |||||
| <label>规格</label> | <label>规格</label> | ||||
| <select id="cloudbrain_flavor" class="ui search dropdown" placeholder="选择规格" style='width:385px' name="flavor"> | <select id="cloudbrain_flavor" class="ui search dropdown" placeholder="选择规格" style='width:385px' name="flavor"> | ||||
| {{range .flavors}} | {{range .flavors}} | ||||
| <option name="flavor" value="{{.Value}}">{{.Value}}</option> | |||||
| <option name="flavor" value="{{.Value}}">{{.Desc}}</option> | |||||
| {{end}} | {{end}} | ||||
| </select> | </select> | ||||
| @@ -1,122 +0,0 @@ | |||||
| {{template "base/head" .}} | |||||
| <div class="repository"> | |||||
| {{template "repo/header" .}} | |||||
| <div class="repository new repo ui middle very relaxed page grid"> | |||||
| <div class="column"> | |||||
| {{template "base/alert" .}} | |||||
| <h4 class="ui header" id="vertical-segment"> | |||||
| <a href="javascript:window.history.back();"><i class="arrow left icon"></i>返回</a> | |||||
| </h4> | |||||
| <div> | |||||
| <div class="ui yellow segment"> | |||||
| {{with .task}} | |||||
| <p>任务名称: {{.JobName}}</p> | |||||
| {{end}} | |||||
| </div> | |||||
| <div class="ui green segment"> | |||||
| <p>任务结果:</p> | |||||
| {{with .result}} | |||||
| <table class="ui celled striped table"> | |||||
| <tbody> | |||||
| <tr> | |||||
| <td class="four wide"> 状态 </td> | |||||
| <td> {{.Status}} </td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td> 开始时间 </td> | |||||
| <td>{{.CreateTime}}</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td> 最后更新时间 </td> | |||||
| <td>{{.LatestUpdateTime}}</td> | |||||
| </tr> | |||||
| </tbody> | |||||
| </table> | |||||
| {{end}} | |||||
| </div> | |||||
| <div class="ui blue segment"> | |||||
| {{with .result}} | |||||
| <table class="ui celled striped table"> | |||||
| <thead> | |||||
| <tr> <th colspan="2"> 配置信息 </th> </tr> | |||||
| </thead> | |||||
| <tbody> | |||||
| <tr> | |||||
| <td class="four wide"> 开发环境类型 </td> | |||||
| <td>{{.Profile.DeType}}</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td> 硬件类型 </td> | |||||
| <td>{{.Profile.FlavorType}}</td> | |||||
| </tr> | |||||
| </tbody> | |||||
| </table> | |||||
| <table class="ui celled striped table"> | |||||
| <thead> | |||||
| <tr> <th colspan="2"> 机器规格详情 </th> </tr> | |||||
| </thead> | |||||
| <tbody> | |||||
| <tr> | |||||
| <td class="four wide"> 机器规格 </td> | |||||
| <td> {{.Flavor}} </td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td> 规格名称 </td> | |||||
| <td>{{.FlavorDetails.Name}}</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td> 规格销售状态 </td> | |||||
| <td>{{.FlavorDetails.Status}}</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td> 排队个数 </td> | |||||
| <td>{{.FlavorDetails.QueuingNum}}</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td> 排到队的剩余时间(秒) </td> | |||||
| <td>{{.FlavorDetails.QueueLeftTime}}</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td> 自动停止时间(秒) </td> | |||||
| <td>{{.FlavorDetails.Duration}}</td> | |||||
| </tr> | |||||
| </tbody> | |||||
| </table> | |||||
| <table class="ui celled striped table" {{if eq .QueuingInfo.RemainTime 0}}hidden{{end}}> | |||||
| <thead> | |||||
| <tr> <th colspan="2"> 排队信息 </th> </tr> | |||||
| </thead> | |||||
| <tbody> | |||||
| <tr> | |||||
| <td> 实例状态 </td> | |||||
| <td>{{.QueuingInfo.Status}}</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td> 实例排队的开始时间 </td> | |||||
| <td>{{.QueuingInfo.BeginTime}}</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td> 排到队的剩余时间(秒) </td> | |||||
| <td>{{.QueuingInfo.RemainTime}}</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td> 实例排队的预计停止时间 </td> | |||||
| <td>{{.QueuingInfo.EndTime}}</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td> 实例在队列中的排位 </td> | |||||
| <td>{{.QueuingInfo.Rank}}</td> | |||||
| </tr> | |||||
| </tbody> | |||||
| </table> | |||||
| {{end}} | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| {{template "base/footer" .}} | |||||