Browse Source

Merge pull request 'liuzx_trainjob' (#950) from liuzx_trainjob into V20211115

Reviewed-on: https://git.openi.org.cn/OpenI/aiforge/pulls/950
Reviewed-by: lewis <747342561@qq.com>
pull/961/head
lewis 3 years ago
parent
commit
1e24796046
12 changed files with 351 additions and 110 deletions
  1. +45
    -2
      modules/storage/obs.go
  2. +46
    -11
      routers/api/v1/repo/modelarts.go
  3. +117
    -13
      routers/repo/modelarts.go
  4. +6
    -6
      templates/repo/cloudbrain/index.tmpl
  5. +2
    -2
      templates/repo/header.tmpl
  6. +5
    -5
      templates/repo/modelarts/notebook/index.tmpl
  7. +52
    -35
      templates/repo/modelarts/trainjob/index.tmpl
  8. +32
    -12
      templates/repo/modelarts/trainjob/new.tmpl
  9. +1
    -1
      templates/repo/modelarts/trainjob/para_manage.tmpl
  10. +14
    -11
      templates/repo/modelarts/trainjob/show.tmpl
  11. +12
    -12
      templates/repo/modelarts/trainjob/version_new.tmpl
  12. +19
    -0
      web_src/less/openi.less

+ 45
- 2
modules/storage/obs.go View File

@@ -176,10 +176,10 @@ func ObsModelDownload(JobName string, fileName string) (io.ReadCloser, error) {
}
}

func GetObsListObject(jobName, parentDir string, versionOutputPath string) ([]FileInfo, error) {
func GetObsListObject(jobName, parentDir, versionName string) ([]FileInfo, error) {
input := &obs.ListObjectsInput{}
input.Bucket = setting.Bucket
input.Prefix = strings.TrimPrefix(path.Join(setting.TrainJobModelPath, jobName, setting.OutPutPath, versionOutputPath, parentDir), "/")
input.Prefix = strings.TrimPrefix(path.Join(setting.TrainJobModelPath, jobName, setting.OutPutPath, versionName, parentDir), "/")
strPrefix := strings.Split(input.Prefix, "/")
output, err := ObsCli.ListObjects(input)
fileInfos := make([]FileInfo, 0)
@@ -275,8 +275,34 @@ func GetObsCreateSignedUrl(jobName, parentDir, fileName string) (string, error)
log.Error("CreateSignedUrl failed:", err.Error())
return "", err
}
log.Info("SignedUrl:%s", output.SignedUrl)
return output.SignedUrl, nil
}

func GetObsCreateSignedUrlByBucketAndKey(bucket, key string) (string, error) {
input := &obs.CreateSignedUrlInput{}
input.Bucket = bucket
input.Key = key

input.Expires = 60 * 60
input.Method = obs.HttpMethodGet
comma := strings.LastIndex(key, "/")
filename := key
if comma != -1 {
filename = key[comma+1:]
}
reqParams := make(map[string]string)
filename = url.QueryEscape(filename)
reqParams["response-content-disposition"] = "attachment; filename=\"" + filename + "\""
input.QueryParams = reqParams
output, err := ObsCli.CreateSignedUrl(input)
if err != nil {
log.Error("CreateSignedUrl failed:", err.Error())
return "", err
}

return output.SignedUrl, nil

}

func ObsGetPreSignedUrl(uuid, fileName string) (string, error) {
@@ -311,3 +337,20 @@ func ObsCreateObject(path string) error {

return nil
}

func ObsDownloadAFile(bucket string, key string) (io.ReadCloser, error) {
input := &obs.GetObjectInput{}
input.Bucket = bucket
input.Key = key
output, err := ObsCli.GetObject(input)
if err == nil {
log.Info("StorageClass:%s, ETag:%s, ContentType:%s, ContentLength:%d, LastModified:%s\n",
output.StorageClass, output.ETag, output.ContentType, output.ContentLength, output.LastModified)
return output.Body, nil
} else if obsError, ok := err.(obs.ObsError); ok {
log.Error("Code:%s, Message:%s", obsError.Code, obsError.Message)
return nil, obsError
} else {
return nil, err
}
}

+ 46
- 11
routers/api/v1/repo/modelarts.go View File

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

import (
"net/http"
"path"
"strconv"
"strings"

@@ -14,6 +15,7 @@ import (
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/modelarts"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
)

@@ -302,8 +304,7 @@ func ModelList(ctx *context.APIContext) {
log.Error("GetCloudbrainByJobID(%s) failed:%v", task.JobName, err.Error())
return
}
VersionOutputPath := modelarts.GetVersionOutputPathByTotalVersionCount(task.TotalVersionCount)
models, err := storage.GetObsListObject(task.JobName, parentDir, VersionOutputPath)
models, err := storage.GetObsListObject(task.JobName, parentDir, versionName)
if err != nil {
log.Info("get TrainJobListModel failed:", err)
ctx.ServerError("GetObsListObject:", err)
@@ -321,27 +322,61 @@ func ModelList(ctx *context.APIContext) {
})
}

func ModelDownload(ctx *context.APIContext) {
func ModelDownload(ctx *context.Context) {
var (
err error
)

var jobID = ctx.Params(":jobid")
versionName := ctx.Query("version_name")
// versionName := "V0001"
parentDir := ctx.Query("parent_dir")
fileName := ctx.Query("file_name")
log.Info("DownloadSingleModelFile start.")
// id := ctx.Params(":ID")
// path := Model_prefix + models.AttachmentRelativePath(id) + "/" + parentDir + fileName
task, err := models.GetCloudbrainByJobIDAndVersionName(jobID, versionName)
if err != nil {
log.Error("GetCloudbrainByJobID(%s) failed:%v", task.JobName, err.Error())
return
}
VersionOutputPath := modelarts.GetVersionOutputPathByTotalVersionCount(task.TotalVersionCount)
parentDir = VersionOutputPath + "/" + parentDir
url, err := storage.GetObsCreateSignedUrl(task.JobName, parentDir, fileName)
if err != nil {
log.Error("GetObsCreateSignedUrl failed: %v", err.Error(), ctx.Data["msgID"])
ctx.ServerError("GetObsCreateSignedUrl", err)
return

path := strings.TrimPrefix(path.Join(setting.TrainJobModelPath, task.JobName, setting.OutPutPath, versionName, parentDir, fileName), "/")
log.Info("Download path is:%s", path)
if setting.PROXYURL != "" {
body, err := storage.ObsDownloadAFile(setting.Bucket, path)
if err != nil {
log.Info("download error.")
} else {
//count++
// models.ModifyModelDownloadCount(id)
defer body.Close()
ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+fileName)
ctx.Resp.Header().Set("Content-Type", "application/octet-stream")
p := make([]byte, 1024)
var readErr error
var readCount int
// 读取对象内容
for {
readCount, readErr = body.Read(p)
if readCount > 0 {
ctx.Resp.Write(p[:readCount])
//fmt.Printf("%s", p[:readCount])
}
if readErr != nil {
break
}
}
}
} else {
url, err := storage.GetObsCreateSignedUrlByBucketAndKey(setting.Bucket, path)
if err != nil {
log.Error("GetObsCreateSignedUrl failed: %v", err.Error(), ctx.Data["msgID"])
ctx.ServerError("GetObsCreateSignedUrl", err)
return
}
//count++
// models.ModifyModelDownloadCount(id)
http.Redirect(ctx.Resp, ctx.Req.Request, url, http.StatusMovedPermanently)
}
http.Redirect(ctx.Resp, ctx.Req.Request, url, http.StatusMovedPermanently)
}

+ 117
- 13
routers/repo/modelarts.go View File

@@ -369,6 +369,7 @@ func trainJobNewDataPrepare(ctx *context.Context) error {
}
ctx.Data["Branches"] = Branches
ctx.Data["BranchesCount"] = len(Branches)
ctx.Data["params"] = ""

configList, err := getConfigList(modelarts.PerPage, 1, modelarts.SortByCreateTime, "desc", "", modelarts.ConfigTypeCustom)
if err != nil {
@@ -380,7 +381,92 @@ func trainJobNewDataPrepare(ctx *context.Context) error {
return nil
}

func ErrorNewDataPrepare(ctx *context.Context, form auth.CreateModelArtsTrainJobForm) error {
ctx.Data["PageIsCloudBrain"] = true

//can, err := canUserCreateTrainJob(ctx.User.ID)
//if err != nil {
// ctx.ServerError("canUserCreateTrainJob", err)
// return
//}
//
//if !can {
// log.Error("the user can not create train-job")
// ctx.ServerError("the user can not create train-job", fmt.Errorf("the user can not create train-job"))
// return
//}

t := time.Now()
var jobName = 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 err
}
ctx.Data["attachments"] = attachs

var resourcePools modelarts.ResourcePool
if err = json.Unmarshal([]byte(setting.ResourcePools), &resourcePools); err != nil {
ctx.ServerError("json.Unmarshal failed:", err)
return err
}
ctx.Data["resource_pools"] = resourcePools.Info

var engines modelarts.Engine
if err = json.Unmarshal([]byte(setting.Engines), &engines); err != nil {
ctx.ServerError("json.Unmarshal failed:", err)
return err
}
ctx.Data["engines"] = engines.Info

var versionInfos modelarts.VersionInfo
if err = json.Unmarshal([]byte(setting.EngineVersions), &versionInfos); err != nil {
ctx.ServerError("json.Unmarshal failed:", err)
return err
}
ctx.Data["engine_versions"] = versionInfos.Version

var flavorInfos modelarts.Flavor
if err = json.Unmarshal([]byte(setting.TrainJobFLAVORINFOS), &flavorInfos); err != nil {
ctx.ServerError("json.Unmarshal failed:", err)
return err
}
ctx.Data["flavor_infos"] = flavorInfos.Info

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

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

configList, err := getConfigList(modelarts.PerPage, 1, modelarts.SortByCreateTime, "desc", "", modelarts.ConfigTypeCustom)
if err != nil {
ctx.ServerError("getConfigList failed:", err)
return err
}
var Parameters modelarts.Parameters
if err = json.Unmarshal([]byte(form.Params), &Parameters); err != nil {
ctx.ServerError("json.Unmarshal failed:", err)
return err
}
ctx.Data["params"] = Parameters.Parameter
ctx.Data["config_list"] = configList.ParaConfigs
ctx.Data["bootFile"] = form.BootFile
ctx.Data["uuid"] = form.Attachment
ctx.Data["branch_name"] = form.BranchName

return nil
}

func TrainJobNewVersion(ctx *context.Context) {

err := trainJobNewVersionDataPrepare(ctx)
if err != nil {
ctx.ServerError("get new train-job info failed", err)
@@ -394,6 +480,12 @@ func trainJobNewVersionDataPrepare(ctx *context.Context) error {
var jobID = ctx.Params(":jobid")
// var versionName = ctx.Params(":version-name")
var versionName = ctx.Query("version_name")
// canNewJob, err := canUserCreateTrainJobVersion(ctx, jobID)
// if err != nil {
// ctx.ServerError("get can info failed", err)
// return err
// }
// ctx.Data["canNewJob"] = canNewJob

task, err := models.GetCloudbrainByJobIDAndVersionName(jobID, versionName)
if err != nil {
@@ -455,7 +547,8 @@ func trainJobNewVersionDataPrepare(ctx *context.Context) error {
ctx.ServerError("GetBranches error:", err)
return err
}
ctx.Data["branches"] = Branches

ctx.Data["branch"] = Branches
ctx.Data["branch_name"] = task.BranchName
ctx.Data["description"] = task.Description
ctx.Data["boot_file"] = task.BootFile
@@ -477,7 +570,7 @@ func trainJobNewVersionDataPrepare(ctx *context.Context) error {
return nil
}

func ErrorDataPrepare(ctx *context.Context, form auth.CreateModelArtsTrainJobForm) error {
func VersionErrorDataPrepare(ctx *context.Context, form auth.CreateModelArtsTrainJobForm) error {
ctx.Data["PageIsCloudBrain"] = true
var jobID = ctx.Params(":jobid")
// var versionName = ctx.Params(":version-name")
@@ -580,7 +673,6 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm)
isSaveParam := form.IsSaveParam
repo := ctx.Repo.Repository
codeLocalPath := setting.JobPath + jobName + modelarts.CodePath
// codeObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.CodePath + VersionOutputPath + "/"
codeObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.CodePath
outputObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.OutputPath + VersionOutputPath + "/"
logObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.LogPath + VersionOutputPath + "/"
@@ -593,7 +685,7 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm)

if err := paramCheckCreateTrainJob(form); err != nil {
log.Error("paramCheckCreateTrainJob failed:(%v)", err)
trainJobNewDataPrepare(ctx)
ErrorNewDataPrepare(ctx, form)
ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobNew, &form)
return
}
@@ -792,7 +884,7 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ

if err := paramCheckCreateTrainJob(form); err != nil {
log.Error("paramCheckCreateTrainJob failed:(%v)", err)
ErrorDataPrepare(ctx, form)
VersionErrorDataPrepare(ctx, form)
ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobVersionNew, &form)
return
}
@@ -815,7 +907,7 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ
Branch: branch_name,
}); err != nil {
log.Error("创建任务失败,任务名称已存在!: %s (%v)", repo.FullName(), err)
ErrorDataPrepare(ctx, form)
VersionErrorDataPrepare(ctx, form)
ctx.RenderWithErr("创建任务失败,任务名称已存在!", tplModelArtsTrainJobVersionNew, &form)
return
}
@@ -823,14 +915,14 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ
//todo: upload code (send to file_server todo this work?)
if err := obsMkdir(setting.CodePathPrefix + jobName + modelarts.OutputPath + VersionOutputPath + "/"); err != nil {
log.Error("Failed to obsMkdir_output: %s (%v)", repo.FullName(), err)
ErrorDataPrepare(ctx, form)
VersionErrorDataPrepare(ctx, form)
ctx.RenderWithErr("Failed to obsMkdir_output", tplModelArtsTrainJobVersionNew, &form)
return
}

if err := obsMkdir(setting.CodePathPrefix + jobName + modelarts.LogPath + VersionOutputPath + "/"); err != nil {
log.Error("Failed to obsMkdir_log: %s (%v)", repo.FullName(), err)
ErrorDataPrepare(ctx, form)
VersionErrorDataPrepare(ctx, form)
ctx.RenderWithErr("Failed to obsMkdir_log", tplModelArtsTrainJobVersionNew, &form)
return
}
@@ -839,7 +931,7 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ
// if err := uploadCodeToObs(codeLocalPath, jobName, ""); err != nil {
if err := uploadCodeToObs(codeLocalPath, jobName, parentDir); err != nil {
log.Error("Failed to uploadCodeToObs: %s (%v)", repo.FullName(), err)
ErrorDataPrepare(ctx, form)
VersionErrorDataPrepare(ctx, form)
ctx.RenderWithErr("Failed to uploadCodeToObs", tplModelArtsTrainJobVersionNew, &form)
return
}
@@ -859,7 +951,7 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ
err := json.Unmarshal([]byte(params), &parameters)
if err != nil {
log.Error("Failed to Unmarshal params: %s (%v)", params, err)
ErrorDataPrepare(ctx, form)
VersionErrorDataPrepare(ctx, form)
ctx.RenderWithErr("运行参数错误", tplModelArtsTrainJobVersionNew, &form)
return
}
@@ -878,7 +970,7 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ
if isSaveParam == "on" {
if form.ParameterTemplateName == "" {
log.Error("ParameterTemplateName is empty")
ErrorDataPrepare(ctx, form)
VersionErrorDataPrepare(ctx, form)
ctx.RenderWithErr("保存作业参数时,作业参数名称不能为空", tplModelArtsTrainJobVersionNew, &form)
return
}
@@ -902,7 +994,7 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ

if err != nil {
log.Error("Failed to CreateTrainJobConfig: %v", err)
ErrorDataPrepare(ctx, form)
VersionErrorDataPrepare(ctx, form)
ctx.RenderWithErr("保存作业参数失败:"+err.Error(), tplModelArtsTrainJobVersionNew, &form)
return
}
@@ -949,7 +1041,7 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ
err = modelarts.GenerateTrainJobVersion(ctx, req, jobID)
if err != nil {
log.Error("GenerateTrainJob failed:%v", err.Error())
ErrorDataPrepare(ctx, form)
VersionErrorDataPrepare(ctx, form)
ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobVersionNew, &form)
return
}
@@ -1220,6 +1312,18 @@ func canUserCreateTrainJob(uid int64) (bool, error) {

return org.IsOrgMember(uid)
}
func canUserCreateTrainJobVersion(ctx *context.Context, jobID string) (bool, error) {

var versionName = "V0001"
task, err := models.GetCloudbrainByJobIDAndVersionName(jobID, versionName)
if err != nil {
return false, err
}
if ctx.User.ID == task.User.ID {
return true, nil
}
return false, err
}

func TrainJobGetConfigList(ctx *context.Context) {
ctx.Data["PageIsTrainJob"] = true


+ 6
- 6
templates/repo/cloudbrain/index.tmpl View File

@@ -307,9 +307,9 @@

<!-- 任务名 -->
<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 text_over" style="width: 90%;vertical-align: middle;margin-left: 0.4rem;">{{.JobName}}</span>
<a class="title" href="{{$.Link}}/{{.JobID}}" title="{{.JobName}}" style="font-size: 14px;">
<span class="fitted text_over" style="width: 90%;vertical-align: middle;">{{.JobName}}</span>
</a>
</div>

@@ -380,7 +380,7 @@
{{end}}
</form>
</div>
<div class="ui compact buttons" style="margin-right:10px;">
<div class="ui compact buttons">
<!-- 模型下载 -->
<a class="ui basic blue button" href="{{$.Link}}/{{.JobID}}/models" target="_blank">
{{$.i18n.Tr "repo.download"}}
@@ -399,11 +399,11 @@
<form class="ui compact buttons" id="delForm-{{.JobID}}" action="{{$.Link}}/{{.JobID}}/del" method="post">
{{$.CsrfTokenHtml}}
{{if $.Permission.CanWrite $.UnitTypeCloudBrain}}
<a id="model-delete-{{.JobID}}" class="ui compact {{if not .CanDel}}disabled {{else}}red {{end}}button" onclick="assertDelete(this)" style="border-radius: .28571429rem;">
<a id="model-delete-{{.JobID}}" class="ui basic blue button {{if not .CanDel}}disabled {{end}}" onclick="assertDelete(this)" style="border-radius: .28571429rem;">
{{$.i18n.Tr "repo.delete"}}
</a>
{{else}}
<a class="ui compact disabled button" onclick="assertDelete(this)" style="border-radius: .28571429rem;">
<a class="ui basic blue button disabled" onclick="assertDelete(this)" style="border-radius: .28571429rem;">
{{$.i18n.Tr "repo.delete"}}
</a>
{{end}}


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

@@ -93,7 +93,7 @@
<div class="ui tabular stackable menu navbar">
{{if .Permission.CanRead $.UnitTypeCode}}
<div class="dropdown-menu">
<a class="{{if or .PageIsViewCode .PageIsReleaseList .PageIsWiki .PageIsActivity .PageIsViewCode}}active{{end}} item" 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}}">
<span>{{svg "octicon-code" 16}} {{.i18n.Tr "repo.code"}} <i class="dropdown icon"></i></span>
</a>
<div class="dropdown-content">
@@ -243,7 +243,7 @@
})
$('.question.circle.icon').hover(function(){
$(this).popup('show')
$('.ui.popup.mini.top.center').css({"border-color":'rgba(50, 145, 248, 100)',"color":"rgba(3, 102, 214, 100)","border-radius":"5px"})
$('.ui.popup.mini.top.center').css({"border-color":'rgba(50, 145, 248, 100)',"color":"rgba(3, 102, 214, 100)","border-radius":"5px","border-shadow":"none"})
});
</script>

+ 5
- 5
templates/repo/modelarts/notebook/index.tmpl View File

@@ -280,9 +280,9 @@
<!-- 任务名 -->
<div class="six 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 class="title" href="{{$.Link}}/{{.JobID}}" title="{{.JobName}}" style="font-size: 14px;">
<span class="fitted" style="width: 90%;vertical-align: middle;">{{.JobName}}</span>
</a>
</div>

@@ -352,11 +352,11 @@
<form class="ui compact buttons" id="delForm-{{.JobID}}" action="{{$.Link}}/{{.JobID}}/del" method="post">
{{$.CsrfTokenHtml}}
{{if $.Permission.CanWrite $.UnitTypeCloudBrain}}
<a id="model-delete-{{.JobID}}" class="ui compact {{if eq .Status "RUNNING" "CREATING" "WAITING" "STARTING" "STOPPING" }}disabled {{else}}red {{end}}button" onclick="assertDelete(this)" style="border-radius: .28571429rem;">
<a id="model-delete-{{.JobID}}" class="ui basic blue button {{if eq .Status "RUNNING" "CREATING" "WAITING" "STARTING" "STOPPING" }}disabled {{end}}" onclick="assertDelete(this)" style="border-radius: .28571429rem;">
{{$.i18n.Tr "repo.delete"}}
</a>
{{else}}
<a class="ui compact disabled button" onclick="assertDelete(this)" style="border-radius: .28571429rem;">
<a class="ui basic blue button disabled" onclick="assertDelete(this)" style="border-radius: .28571429rem;">
{{$.i18n.Tr "repo.delete"}}
</a>
{{end}}


+ 52
- 35
templates/repo/modelarts/trainjob/index.tmpl View File

@@ -321,9 +321,9 @@
<!-- 任务名 -->
<div class="three wide column padding0">
<a class="title" href="{{$.Link}}/{{.JobID}}" title="{{.JobName}}" style="font-size: 12px;">
<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 class="title" href="{{$.Link}}/{{.JobID}}" title="{{.JobName}}" style="font-size: 14px;">
<span class="fitted" style="width: 90%;vertical-align: middle;">{{.JobName}}</span>
</a>
</div>
<!-- 版本数量 -->
@@ -366,8 +366,9 @@
</div>

<div class="three wide column text center padding0">
<div class="ui compact buttons">
<form id="stopForm-{{.JobID}}" action="{{$.Link}}/{{.JobID}}/stop" method="post">
<!-- <div class="ui compact buttons">
<form id="stopForm-{{.JobID}}" action="/api/v1/repos{{$.Link}}/{{.JobID}}/stop_version" method="post">
<input type="hidden" name="version_name" value="{{.VersionName}}">
{{$.CsrfTokenHtml}}
{{if $.Permission.CanWrite $.UnitTypeCloudBrain}}
<a style="padding: 0.5rem 1rem;" id="stop-model-debug-{{.JobID}}" class="ui basic {{if eq .Status "KILLED" "FAILED" "START_FAILED" "KILLING" "COMPLETED"}}disabled {{else}}blue {{end}}button" onclick="document.getElementById('stopForm-{{.JobID}}').submit();">
@@ -379,7 +380,7 @@
</a>
{{end}}
</form>
</div>
</div> -->
<!-- 模型下载 -->
<!-- <div class="ui compact buttons">
<a style="padding: 0.5rem;" class="ui basic blue button" href="{{$.Link}}/{{.JobID}}/models" target="_blank">
@@ -387,14 +388,27 @@
</a>
</div> -->
<!-- 删除任务 -->
<div class="ui compact buttons">
{{$.CsrfTokenHtml}}
{{if $.Permission.CanWrite $.UnitTypeCloudBrain}}
<a style="padding: 0.5rem 1rem;" id="{{.VersionName}}-stop" class="ui basic {{if eq .Status "KILLED" "FAILED" "START_FAILED" "KILLING" "COMPLETED"}}disabled {{else}} blue {{end}}button" onclick="stopVersion({{.VersionName}},{{.JobID}})">
{{$.i18n.Tr "repo.stop"}}
</a>
{{else}}
<a style="padding: 0.5rem 1rem;" id="{{.VersionName}}-stop" class="ui basic disabled button">
{{$.i18n.Tr "repo.stop"}}
</a>
{{end}}

</div>
<form class="ui compact buttons" id="delForm-{{.JobID}}" action="{{$.Link}}/{{.JobID}}/del" method="post">
{{$.CsrfTokenHtml}}
{{if $.Permission.CanWrite $.UnitTypeCloudBrain}}
<a style="padding: 0.5rem 1rem;margin-left:0.2rem" id="model-delete-{{.JobID}}" class="ui compact {{if or (eq .Status "RUNNING") (eq .Status "INIT") (eq .Status "CREATING") (eq .Status "WAITING") }}disabled {{else}}red {{end}}button" onclick="assertDelete(this)" style="border-radius: .28571429rem;">
<a style="padding: 0.5rem 1rem;margin-left:0.2rem" id="model-delete-{{.JobID}}" class="ui basic blue button" onclick="assertDelete(this)" style="border-radius: .28571429rem;">
{{$.i18n.Tr "repo.delete"}}
</a>
{{else}}
<a style="padding: 0.5rem 1rem;margin-left:0.2rem" class="ui compact disabled button" onclick="assertDelete(this)" style="border-radius: .28571429rem;">
<a style="padding: 0.5rem 1rem;margin-left:0.2rem" class="ui basic button disabled" onclick="assertDelete(this)" style="border-radius: .28571429rem;">
{{$.i18n.Tr "repo.delete"}}
</a>
{{end}}
@@ -527,33 +541,7 @@
$('#' + jobID+ '-text').text(status)

}
if(status==="RUNNING"){
$('#model-debug-'+jobID).removeClass('disabled')
$('#model-debug-'+jobID).addClass('blue')
// $('#duration-'+jobID).text(duration)

}
if(status!=="RUNNING"){
$('#model-debug-'+jobID).removeClass('blue')
$('#model-debug-'+jobID).addClass('disabled')

}
if(status!=="KILLED" || status!=="FAILED"){
$('#stop-model-debug-'+jobID).removeClass('disabled')
$('#stop-model-debug-'+jobID).addClass('blue')
$('#model-delete-'+jobID).removeClass('red')
$('#model-delete-'+jobID).addClass('disabled')
}
if(status=="KILLED" || status=="FAILED" || status=="KILLING" || status=="COMPLETED"){
$('#stop-model-debug-'+jobID).removeClass('blue')
$('#stop-model-debug-'+jobID).addClass('disabled')
$('#model-delete-'+jobID).removeClass('disabled')
$('#model-delete-'+jobID).addClass('red')
}
if(status=="START_FAILED"){
$('#stop-model-debug-'+jobID).removeClass('blue')
$('#stop-model-debug-'+jobID).addClass('disabled')
}
}).fail(function(err) {
console.log(err);
});
@@ -589,7 +577,36 @@
modal.style.display = "none";
}
}
function stopVersion(version_name,jobID){
const url = '/api/v1/repos/{{$.RepoRelPath}}/modelarts/train-job/'+jobID+'/stop_version'
$.post(url,{version_name:version_name},(data)=>{
if(data.StatusOK===0){
$('#'+version_name+'-stop').removeClass('blue')
$('#'+version_name+'-stop').addClass('disabled')
refreshStatus(version_name,jobID)
}
}).fail(function(err) {
console.log(err);
});
}
function refreshStatus(version_name,jobID){

const url = '/api/v1/repos/{{$.RepoRelPath}}/modelarts/train-job/'+jobID+'?version_name='+version_name
$.get(url,(data)=>{
$(`#${jobID}-icon`).attr("class",data.JobStatus)
// detail status and duration
$(`#${jobID}-text`).text(data.JobStatus)


}).fail(function(err) {
console.log(err);
});
}
// 显示弹窗,弹出相应的信息
function showmask() {
$('#imageModal').css('display', 'none')


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

@@ -159,12 +159,12 @@
<input type="hidden" id="ai_flaver_name" name="flaver_names" value="">
<h4 class="unite title ui header ">{{.i18n.Tr "repo.modelarts.train_job.basic_info"}}:</h4>
<div class="required unite min_title inline field">
<label>{{.i18n.Tr "repo.modelarts.train_job.job_name"}}</label>
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.job_name"}}</label>
<input style="width: 60%;" name="job_name" id="trainjob_job_name" placeholder={{.i18n.Tr "repo.modelarts.train_job.job_name"}} value="{{.job_name}}" tabindex="3" autofocus required maxlength="255">
</div>
<div class="unite min_title inline field">
<label for="description">{{.i18n.Tr "repo.modelarts.train_job.description"}}&nbsp;&nbsp;</label>
<label style="font-weight: normal;" for="description">{{.i18n.Tr "repo.modelarts.train_job.description"}}&nbsp;&nbsp;</label>
<textarea style="width: 80%;" id="description" name="description" rows="3" maxlength="255" placeholder={{.i18n.Tr "repo.modelarts.train_job.new_place"}} onchange="this.value=this.value.substring(0, 255)" onkeydown="this.value=this.value.substring(0, 255)" onkeyup="this.value=this.value.substring(0, 256)"></textarea>
</div>
<div class="ui divider"></div>
@@ -173,8 +173,11 @@

<div class="required unite min_title inline field">
<label>{{.i18n.Tr "repo.modelarts.code_version"}}</label>
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.code_version"}}</label>
<select class="ui dropdown width80 left2" id="code_version" name="branch_name">
{{if .branch_name}}
<option name="branch_name" value="{{.branch_name}}">{{.branch_name}}</option>
{{end}}
{{range $k, $v :=.Branches}}
<option name="branch_name" value="{{$v}}">{{$v}}</option>
{{end}}
@@ -184,7 +187,7 @@


<div class="required unite min_title inline fields" style="width: 90%;">
<label>{{.i18n.Tr "repo.modelarts.train_job.AI_driver"}}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</label>
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.AI_driver"}}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</label>
<div class="field" style="flex: 1.5;">
<select class="ui dropdown width" id="trainjob_engines" >
{{range .engines}}
@@ -205,7 +208,7 @@
</div>

<div class="inline unite min_title field required">
<label>{{.i18n.Tr "repo.modelarts.train_job.start_file"}}</label>
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.start_file"}}</label>
{{if .bootFile}}
<input style="width: 33.5%;" name="boot_file" id="trainjob_boot_file" value="{{.bootFile}}" tabindex="3" autofocus required maxlength="255" >
{{else}}
@@ -216,7 +219,7 @@
</span>
</div>
<div class="required unite min_title inline field">
<label>{{.i18n.Tr "repo.modelarts.train_job.dataset"}}</label>
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.dataset"}}</label>
<select class="ui dropdown width80" id="trainjob_datasets" name="attachment" placeholder="选择数据集">
{{if $.uuid}}
<option name="attachment" value="{{$.uuid}}">{{$.datasetName}}</option>
@@ -229,15 +232,32 @@
</div>
<div class="inline unite min_title field">
<label>{{.i18n.Tr "repo.modelarts.train_job.run_parameter"}}</label>
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.run_parameter"}}</label>
<span id="add_run_para" style="margin-left: 0.5rem;cursor:pointer;color: rgba(3, 102, 214, 100);font-size: 14px;line-height: 26px;font-family: SourceHanSansSC-medium;"><i class="plus square outline icon"></i>{{.i18n.Tr "repo.modelarts.train_job.add_run_parameter"}}</span>
<input id="store_run_para" type="hidden" name="run_para_list">
<div class="dynamic field" style="margin-top: 1rem;"></div>
<div class="dynamic field" style="margin-top: 1rem;">
{{if ne 0 (len .params)}}
{{range $k ,$v := .params}}
<div class="two fields width85" id="para{{$k}}">
<div class="field">
<input type="text" name="shipping_first-name" value={{$v.Label}} required>
</div>
<div class="field">
<input type="text" name="shipping_last-name" value={{$v.Value}} required>
</div>
<span>
<i class="trash icon"></i>
</span>

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


<div class="required field " style="display: none;">
<label>{{.i18n.Tr "repo.modelarts.train_job.resource_pool"}}</label>
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.resource_pool"}}</label>
<select class="ui dropdown" id="trainjob_resource_pool" style='width:385px' name="pool_id">
{{range .resource_pools}}
<option value="{{.ID}}">{{.Value}}</option>
@@ -246,7 +266,7 @@
</div>

<div class="required grouped fields" style="display: none;">
<label for="resource_type">{{.i18n.Tr "repo.modelarts.train_job.resource_type"}}</label>
<label style="font-weight: normal;" for="resource_type">{{.i18n.Tr "repo.modelarts.train_job.resource_type"}}</label>
<div class="field">
<div class="ui grid">
<div class="column">
@@ -262,7 +282,7 @@
</div>

<div class="required unite min_title inline field" id="flaver_name">
<label>{{.i18n.Tr "repo.modelarts.train_job.standard"}}</label>
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.standard"}}</label>
<select class="ui dropdown width81" id="trainjob-flavor" style='width:385px' name="flavor">
{{range .flavor_infos}}
<option name="flavor" value="{{.Code}}">{{.Value}}</option>
@@ -270,7 +290,7 @@
</select>
</div>
<div class="inline required unite min_title field">
<label>{{.i18n.Tr "repo.modelarts.train_job.amount_of_compute_node"}}</label>
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.amount_of_compute_node"}}</label>
<div class="ui labeled input" style="width: 5%;">



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

@@ -38,7 +38,7 @@
<!-- 任务名 -->
<div class="five wide column">
<a class="title" href="{{$.Link}}/{{.JobID}}">
<span class="fitted">{{svg "octicon-tasklist" 16}}</span>
<span class="fitted">{{.JobName}}</span>
</a>
</div>


+ 14
- 11
templates/repo/modelarts/trainjob/show.tmpl View File

@@ -226,9 +226,9 @@ td, th {
</div>
</div>
</div>
<div class="{{if eq $k 0}}active{{end}} content accordion-border">
<div class="{{if eq $k 0}}active{{end}} content">
<div class="content-pad">
<div class="ui pointing secondary menu">
<div class="ui pointing secondary menu" style="border-bottom: 1px solid rgba(34,36,38,.15);">
<a class="active item" data-tab="first{{$k}}">{{$.i18n.Tr "repo.modelarts.train_job.config"}}</a>
<a class="item" data-tab="second{{$k}}" onclick="loadLog({{.VersionName}})">{{$.i18n.Tr "repo.modelarts.log"}}</a>
@@ -414,17 +414,14 @@ td, th {
<div class="ui message message{{.VersionName}}" style="display: none;">
<div id="header"></div>
</div>
<div class="ui top attached segment" style="background: #f0f0f0;">
<!-- <div class="ui top attached segment" style="background: #f0f0f0;">
<div class="center aligned">
<label>{{$.i18n.Tr "repo.modelarts.log"}}:</label>
<!-- <span class="fitted file_name">{{.}}</span> -->
<!-- <input type="hidden" name="file_name" value>
<input type="hidden" name="start_line" value>
<input type="hidden" name="end_line" value> -->
</div>
</div>
<div class="ui attached segment log" onscroll="logScroll({{.VersionName}})" id="log{{.VersionName}}" style="height: 300px !important; overflow: auto;">
</div> -->
<div class="ui attached log" onscroll="logScroll({{.VersionName}})" id="log{{.VersionName}}" style="height: 300px !important; overflow: auto;">
<!-- <input type="hidden" class="version_name" name="version_name" value={{.VersionName}}> -->
<input type="hidden" name="end_line" value>
<input type="hidden" name="start_line" value>
@@ -660,6 +657,12 @@ td, th {
}
}
function downloadModelFile(version_name,parentDir,filename){
$.get(`/api/v1/repos/${userName}/${repoPath}/modelarts/train-job/${jobID}/model_download?version_name=${version_name}&file_name=${filename}`, (data) => {
console.log(data)

})
}
function renderDir(data,version_name){
let html=""
html += "<div class='ui grid' style='margin:0;'>"
@@ -677,10 +680,10 @@ td, th {
html += "<span class='octicon octicon-file-directory'>"
html += "</span>"
if(data.Dirs[i].IsDir){
html += `<a onclick="loadModelFile('${version_name}','${data.Dirs[i].ParenDir}','${data.Dirs[i].FileName}','folder')">`
html += `<a onclick="loadModelFile('${version_name}','${data.Dirs[i].ParenDir}','${data.Dirs[i].ParenDir}','folder')">`
html += "<span class='fitted'><i class='folder icon' width='16' height='16' aria-hidden='true'></i>" + data.Dirs[i].FileName + "</span>"
}else{
html += `<a href='${location.href}/download_model?parentDir=&fileName=${data.Dirs[i].FileName}&jobName=${data.task.JobName}'>`
html += `<a onclick="downloadModelFile('${version_name}','${data.Dirs[i].ParenDir}','${data.Dirs[i].ParenDir}')">`
html += "<span class='fitted'><i class='file icon' width='16' height='16' aria-hidden='true'></i>" + data.Dirs[i].FileName + "</span>"
}
html += '</a>'


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

@@ -161,18 +161,18 @@
<input type="hidden" id="ai_flaver_name" name="flaver_names" value="">
<h4 class="unite title ui header ">{{.i18n.Tr "repo.modelarts.train_job.basic_info"}}:</h4>
<div class="required unite min_title inline field">
<label>{{.i18n.Tr "repo.modelarts.train_job.job_name"}}</label>
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.job_name"}}</label>
<input type="hidden" style="width: 60%;" name="job_name" id="trainjob_job_name" value="{{.job_name}}">
<input style="width: 60%;" value="{{.job_name}}" tabindex="3" disabled >
</div>
<div class="required unite min_title inline field">
<label>{{.i18n.Tr "repo.modelarts.parents_version"}}</label>
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.parents_version"}}</label>
<input id="parents_version" style="width: 60%;" value="" tabindex="3" disabled >
</div>
<div class="unite min_title inline field">
<label for="description">{{.i18n.Tr "repo.modelarts.train_job.description"}}&nbsp;&nbsp;</label>
<label style="font-weight: normal;" for="description">{{.i18n.Tr "repo.modelarts.train_job.description"}}&nbsp;&nbsp;</label>
<textarea style="width: 80%;" id="description" value="{{.description}}" name="description" rows="3" maxlength="255" placeholder={{.i18n.Tr "repo.modelarts.train_job.new_place"}} onchange="this.value=this.value.substring(0, 255)" onkeydown="this.value=this.value.substring(0, 255)" onkeyup="this.value=this.value.substring(0, 256)">{{.description}}</textarea>
</div>
<div class="ui divider"></div>
@@ -181,7 +181,7 @@

<div class="required unite min_title inline field">
<label>{{.i18n.Tr "repo.modelarts.code_version"}}</label>
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.code_version"}}</label>
<select class="ui dropdown width80 left2" id="code_version" name="branch_name">
{{if .branch_name}}
<option name="branch_name" value="{{.branch_name}}">{{.branch_name}}</option>
@@ -198,7 +198,7 @@


<div class="required unite min_title inline fields" style="width: 90%;">
<label>{{.i18n.Tr "repo.modelarts.train_job.AI_driver"}}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</label>
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.AI_driver"}}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</label>
<div class="field" style="flex: 1.5;">
<select class="ui dropdown width" id="trainjob_engines" >
{{range .engines}}
@@ -224,7 +224,7 @@
</div>

<div class="inline unite min_title field required">
<label>{{.i18n.Tr "repo.modelarts.train_job.start_file"}}</label>
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.start_file"}}</label>
{{if .boot_file}}
<input style="width: 33.5%;" name="boot_file" id="trainjob_boot_file" value="{{.boot_file}}" tabindex="3" autofocus required maxlength="255" >
{{else}}
@@ -235,7 +235,7 @@
</span>
</div>
<div class="required unite min_title inline field">
<label>{{.i18n.Tr "repo.modelarts.train_job.dataset"}}</label>
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.dataset"}}</label>
<select class="ui dropdown width80" id="trainjob_datasets" name="attachment" placeholder="选择数据集">
{{if .dataset_name}}
<option name="attachment" value="{{.uuid}}">{{.dataset_name}}</option>
@@ -250,7 +250,7 @@
</div>
<div class="inline unite min_title field">
<label>{{.i18n.Tr "repo.modelarts.train_job.run_parameter"}}</label>
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.run_parameter"}}</label>
<span id="add_run_para" style="margin-left: 0.5rem;cursor:pointer;color: rgba(3, 102, 214, 100);font-size: 14px;line-height: 26px;font-family: SourceHanSansSC-medium;"><i class="plus square outline icon"></i>{{.i18n.Tr "repo.modelarts.train_job.add_run_parameter"}}</span>
<input id="store_run_para" type="hidden" name="run_para_list">
<div class="dynamic field" style="margin-top: 1rem;">
@@ -275,7 +275,7 @@


<div class="required field " style="display: none;">
<label>{{.i18n.Tr "repo.modelarts.train_job.resource_pool"}}</label>
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.resource_pool"}}</label>
<select class="ui dropdown" id="trainjob_resource_pool" style='width:385px' name="pool_id">
{{range .resource_pools}}
<option value="{{.ID}}">{{.Value}}</option>
@@ -284,7 +284,7 @@
</div>

<div class="required grouped fields" style="display: none;">
<label for="resource_type">{{.i18n.Tr "repo.modelarts.train_job.resource_type"}}</label>
<label style="font-weight: normal;" for="resource_type">{{.i18n.Tr "repo.modelarts.train_job.resource_type"}}</label>
<div class="field">
<div class="ui grid">
<div class="column">
@@ -300,7 +300,7 @@
</div>

<div class="required unite min_title inline field" id="flaver_name">
<label>{{.i18n.Tr "repo.modelarts.train_job.standard"}}</label>
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.standard"}}</label>
<select class="ui dropdown width81" id="trainjob-flavor" style='width:385px' name="flavor">
{{if .flavor_name}}
<option name="flavor" value="{{.flavor_code}}">{{.flavor_name}}</option>
@@ -313,7 +313,7 @@
</select>
</div>
<div class="inline required unite min_title field">
<label>{{.i18n.Tr "repo.modelarts.train_job.amount_of_compute_node"}}</label>
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.amount_of_compute_node"}}</label>
<div class="ui labeled input" style="width: 5%;">



+ 19
- 0
web_src/less/openi.less View File

@@ -291,9 +291,28 @@ footer .column{margin-bottom:0!important; padding-bottom:0!important;}
display: inline-block;
margin-top: 4px;
}
.hover_active{
}
.dropdown-menu:hover .dropdown-content {
display: block;
}
.dropdown-menu:hover .hover_active {
background: #fff !important;
border-color: #d4d4d5 !important;
font-weight: 700 !important;
// margin-bottom: -1px;
-webkit-box-shadow: none !important;
box-shadow: none !important;
border-radius: .28571429rem .28571429rem 0 0!important;
color: rgba(0,0,0,.95);
border-top-width: 1px;
}

.dropdown-menu:hover .dropdown-content {
display: block;
}
.dropdown-content{
display: none;
position: absolute;


Loading…
Cancel
Save