Browse Source

提交代码。

Signed-off-by: zouap <zouap@pcl.ac.cn>
tags/v1.21.12.1^2
zouap 4 years ago
parent
commit
e28a1f3d58
25 changed files with 1207 additions and 1061 deletions
  1. +76
    -159
      models/cloudbrain.go
  2. +0
    -1
      models/models.go
  3. +12
    -11
      models/repo_activity_custom.go
  4. +70
    -77
      modules/modelarts/modelarts.go
  5. +61
    -1
      modules/modelarts/resty.go
  6. +0
    -56
      modules/storage/obs.go
  7. +2
    -0
      modules/templates/helper.go
  8. +5
    -0
      modules/timeutil/since.go
  9. +6
    -1
      options/locale/locale_en-US.ini
  10. +15
    -5
      options/locale/locale_zh-CN.ini
  11. +4
    -5
      routers/api/v1/api.go
  12. +205
    -21
      routers/api/v1/repo/modelarts.go
  13. +1
    -1
      routers/api/v1/repo/repo_dashbord.go
  14. +88
    -283
      routers/repo/modelarts.go
  15. +1
    -2
      routers/repo/user_data_analysis.go
  16. +0
    -11
      routers/routes/routes.go
  17. +13
    -1
      templates/repo/cloudbrain/show.tmpl
  18. +13
    -1
      templates/repo/modelarts/notebook/show.tmpl
  19. +15
    -12
      templates/repo/modelarts/trainjob/index.tmpl
  20. +4
    -2
      templates/repo/modelarts/trainjob/new.tmpl
  21. +501
    -94
      templates/repo/modelarts/trainjob/show.tmpl
  22. +45
    -7
      templates/repo/modelarts/trainjob/version_new.tmpl
  23. +35
    -9
      web_src/js/components/ProAnalysis.vue
  24. +35
    -3
      web_src/js/components/UserAnalysis.vue
  25. +0
    -298
      web_src/js/index.js

+ 76
- 159
models/cloudbrain.go View File

@@ -30,7 +30,6 @@ const (
JobTypeSnn4imagenet JobType = "SNN4IMAGENET"
JobTypeBrainScore JobType = "BRAINSCORE"
JobTypeTrain JobType = "TRAIN"
JobVersionName JobType = "V0001"

ModelArtsCreateQueue ModelArtsJobStatus = "CREATE_QUEUING" //免费资源创建排队中
ModelArtsCreating ModelArtsJobStatus = "CREATING" //创建中
@@ -53,64 +52,46 @@ type Cloudbrain struct {
ID int64 `xorm:"pk autoincr"`
JobID string `xorm:"INDEX NOT NULL"`
JobType string `xorm:"INDEX NOT NULL DEFAULT 'DEBUG'"`
JobName string `xorm:"INDEX"`
Status string `xorm:"INDEX"`
UserID int64 `xorm:"INDEX"`
RepoID int64 `xorm:"INDEX"`
SubTaskName string `xorm:"INDEX"`
JobName string
Status string
UserID int64
RepoID int64
SubTaskName string
ContainerID string
ContainerIp string
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
Duration int64 `xorm:"INDEX duration"`
Duration int64
TrainJobDuration string
DeletedAt time.Time `xorm:"deleted"`
CanDebug bool `xorm:"-"`
CanDel bool `xorm:"-"`
Type int `xorm:"INDEX DEFAULT 0"`

VersionID int64 `xorm:"INDEX DEFAULT 0"`
VersionName string `xorm:"INDEX"`
Uuid string
DatasetName string
VersionCount int `xorm:"INDEX DEFAULT 1"`
IsLatestVersion string
CommitID string
FatherVersionName string
ComputeResource string
EngineID int64

TrainUrl string
BranchName string
Parameters string
BootFile string
DataUrl string
LogUrl string
PreVersionId int64
FlavorCode string
Description string
WorkServerNumber int
FlavorName string
EngineName string

User *User `xorm:"-"`
Repo *Repository `xorm:"-"`
}
Type int

type TrainjobConfigDetail struct {
ID int64 `xorm:"pk autoincr"`
JobID string `xorm:"INDEX"`
JobName string `xorm:"INDEX"`
ResourcePools string `xorm:"INDEX"`
EngineVersions int `xorm:"INDEX"`
FlavorInfos string `xorm:"INDEX"`
TrainUrl string `xorm:"INDEX"`
BootFile string `xorm:"INDEX"`
Uuid string `xorm:"INDEX"`
DatasetName string `xorm:"INDEX"`
Params string `xorm:"INDEX"`
BranchName string `xorm:"INDEX"`
VersionName string `xorm:"INDEX"`
VersionID int64 //版本id
VersionName string `xorm:"INDEX"` //当前版本
Uuid string //数据集id
DatasetName string
VersionCount int //任务的当前版本数量,不包括删除的
IsLatestVersion string //是否是最新版本,1是,0否
CommitID string //提交的仓库代码id
PreVersionName string //父版本名称
ComputeResource string //计算资源,例如npu
EngineID int64 //引擎id

TrainUrl string //输出的obs路径
BranchName string //分支名称
Parameters string //传给modelarts的param参数
BootFile string //启动文件
DataUrl string //数据集的obs路径
LogUrl string //日志输出的obs路径
PreVersionId int64 //父版本的版本id
FlavorCode string //modelarts上的规格id
Description string //描述
WorkServerNumber int //节点数
FlavorName string //规格名称
EngineName string //引擎名称
TotalVersionCount int //任务的所有版本数量,包括删除的

User *User `xorm:"-"`
Repo *Repository `xorm:"-"`
@@ -621,20 +602,14 @@ type Config struct {
BootFileUrl string `json:"boot_file_url"` //训练作业的代码启动文件,需要在代码目录下
Parameter []Parameter `json:"parameter"`
DataUrl string `json:"data_url"` //训练作业需要的数据集OBS路径URL
//DatasetID string `json:"dataset_id"`
//DataVersionID string `json:"dataset_version_id"`
//DataSource []DataSource `json:"data_source"`
//SpecID int64 `json:"spec_id"`
EngineID int64 `json:"engine_id"`
//ModelID int64 `json:"model_id"`
TrainUrl string `json:"train_url"` //训练作业的输出文件OBS路径URL
LogUrl string `json:"log_url"`
EngineID int64 `json:"engine_id"`
TrainUrl string `json:"train_url"` //训练作业的输出文件OBS路径URL
LogUrl string `json:"log_url"`
//UserImageUrl string `json:"user_image_url"`
//UserCommand string `json:"user_command"`
CreateVersion bool `json:"create_version"`
//Volumes []Volumes `json:"volumes"`
Flavor Flavor `json:"flavor"`
PoolID string `json:"pool_id"`
CreateVersion bool `json:"create_version"`
Flavor Flavor `json:"flavor"`
PoolID string `json:"pool_id"`
}

type CreateTrainJobVersionParams struct {
@@ -648,20 +623,12 @@ type TrainJobVersionConfig struct {
BootFileUrl string `json:"boot_file_url"` //训练作业的代码启动文件,需要在代码目录下
Parameter []Parameter `json:"parameter"`
DataUrl string `json:"data_url"` //训练作业需要的数据集OBS路径URL
//DatasetID string `json:"dataset_id"`
//DataVersionID string `json:"dataset_version_id"`
//DataSource []DataSource `json:"data_source"`
//SpecID int64 `json:"spec_id"`
EngineID int64 `json:"engine_id"`
//ModelID int64 `json:"model_id"`
TrainUrl string `json:"train_url"` //训练作业的输出文件OBS路径URL
LogUrl string `json:"log_url"`
//UserImageUrl string `json:"user_image_url"`
//UserCommand string `json:"user_command"`
//Volumes []Volumes `json:"volumes"`
Flavor Flavor `json:"flavor"`
PoolID string `json:"pool_id"`
PreVersionId int64 `json:"pre_version_id"`
EngineID int64 `json:"engine_id"`
TrainUrl string `json:"train_url"` //训练作业的输出文件OBS路径URL
LogUrl string `json:"log_url"`
Flavor Flavor `json:"flavor"`
PoolID string `json:"pool_id"`
PreVersionId int64 `json:"pre_version_id"`
}

type CreateConfigParams struct {
@@ -672,20 +639,11 @@ type CreateConfigParams struct {
BootFileUrl string `json:"boot_file_url"` //训练作业的代码启动文件,需要在代码目录下
Parameter []Parameter `json:"parameter"`
DataUrl string `json:"data_url"` //训练作业需要的数据集OBS路径URL
//DatasetID string `json:"dataset_id"`
//DataVersionID string `json:"dataset_version_id"`
//DataSource []DataSource `json:"data_source"`
//SpecID int64 `json:"spec_id"`
EngineID int64 `json:"engine_id"`
//ModelID int64 `json:"model_id"`
TrainUrl string `json:"train_url"` //训练作业的输出文件OBS路径URL
LogUrl string `json:"log_url"`
//UserImageUrl string `json:"user_image_url"`
//UserCommand string `json:"user_command"`
//CreateVersion bool `json:"create_version"`
//Volumes []Volumes `json:"volumes"`
Flavor Flavor `json:"flavor"`
PoolID string `json:"pool_id"`
EngineID int64 `json:"engine_id"`
TrainUrl string `json:"train_url"` //训练作业的输出文件OBS路径URL
LogUrl string `json:"log_url"`
Flavor Flavor `json:"flavor"`
PoolID string `json:"pool_id"`
}

type Parameter struct {
@@ -799,18 +757,10 @@ type GetConfigResult struct {
BootFileUrl string `json:"boot_file_url"` //训练作业的代码启动文件,需要在代码目录下
Parameter []Parameter `json:"parameter"`
DataUrl string `json:"data_url"` //训练作业需要的数据集OBS路径URL
//DatasetID string `json:"dataset_id"`
//DataVersionID string `json:"dataset_version_id"`
//DataSource []DataSource `json:"data_source"`
//SpecID int64 `json:"spec_id"`
EngineID int64 `json:"engine_id"`
//ModelID int64 `json:"model_id"`
TrainUrl string `json:"train_url"` //训练作业的输出文件OBS路径URL
LogUrl string `json:"log_url"`
//UserImageUrl string `json:"user_image_url"`
//UserCommand string `json:"user_command"`
//CreateVersion bool `json:"create_version"`
//Volumes []Volumes `json:"volumes"`
EngineID int64 `json:"engine_id"`
TrainUrl string `json:"train_url"` //训练作业的输出文件OBS路径URL
LogUrl string `json:"log_url"`

Flavor Flavor `json:"flavor"`
PoolID string `json:"pool_id"`
}
@@ -841,26 +791,18 @@ type GetTrainJobResult struct {
BootFileUrl string `json:"boot_file_url"` //训练作业的代码启动文件,需要在代码目录下
Parameter []Parameter `json:"parameter"`
DataUrl string `json:"data_url"` //训练作业需要的数据集OBS路径URL
//DatasetID string `json:"dataset_id"`
//DataVersionID string `json:"dataset_version_id"`
//DataSource []DataSource `json:"data_source"`
//SpecID int64 `json:"spec_id"`
EngineID int64 `json:"engine_id"`
EngineName string `json:"engine_name"`
EngineVersion string `json:"engine_version"`
//ModelID int64 `json:"model_id"`
TrainUrl string `json:"train_url"` //训练作业的输出文件OBS路径URL
LogUrl string `json:"log_url"`
//UserImageUrl string `json:"user_image_url"`
//UserCommand string `json:"user_command"`
//Volumes []Volumes `json:"volumes"`
Flavor Flavor `json:"flavor"`
PoolID string `json:"pool_id"`
PoolName string `json:"pool_name"`
NasMountPath string `json:"nas_mount_path"`
NasShareAddr string `json:"nas_share_addr"`
DatasetName string
ModelMetricList string `json:"model_metric_list"` //列表里包含f1_score,recall,precision,accuracy,若有的话
EngineID int64 `json:"engine_id"`
EngineName string `json:"engine_name"`
EngineVersion string `json:"engine_version"`
TrainUrl string `json:"train_url"` //训练作业的输出文件OBS路径URL
LogUrl string `json:"log_url"`
Flavor Flavor `json:"flavor"`
PoolID string `json:"pool_id"`
PoolName string `json:"pool_name"`
NasMountPath string `json:"nas_mount_path"`
NasShareAddr string `json:"nas_share_addr"`
DatasetName string
ModelMetricList string `json:"model_metric_list"` //列表里包含f1_score,recall,precision,accuracy,若有的话
}

type GetTrainJobLogResult struct {
@@ -931,17 +873,6 @@ func Cloudbrains(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int64, error) {
)
}

// switch opts.JobStatus {
// case JobWaiting:
// cond.And(builder.Eq{"cloudbrain.status": int(JobWaiting)})
// case JobFailed:
// cond.And(builder.Eq{"cloudbrain.status": int(JobFailed)})
// case JobStopped:
// cond.And(builder.Eq{"cloudbrain.status": int(JobStopped)})
// case JobSucceeded:
// cond.And(builder.Eq{"cloudbrain.status": int(JobSucceeded)})
// }

if len(opts.CloudbrainIDs) > 0 {
cond = cond.And(builder.In("cloudbrain.id", opts.CloudbrainIDs))
}
@@ -968,7 +899,6 @@ func Cloudbrains(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int64, error) {
Find(&cloudbrains); err != nil {
return nil, 0, fmt.Errorf("Find: %v", err)
}
sess.Close()

return cloudbrains, count, nil
}
@@ -1034,7 +964,6 @@ func CloudbrainsVersionList(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int, e
Find(&cloudbrains); err != nil {
return nil, 0, fmt.Errorf("Find: %v", err)
}
sess.Close()

return cloudbrains, int(count), nil
}
@@ -1046,13 +975,6 @@ func CreateCloudbrain(cloudbrain *Cloudbrain) (err error) {
return nil
}

func CreateTrainjobConfigDetail(trainjobConfigDetail *TrainjobConfigDetail) (err error) {
if _, err = x.Insert(trainjobConfigDetail); err != nil {
return err
}
return nil
}

func getRepoCloudBrain(cb *Cloudbrain) (*Cloudbrain, error) {
has, err := x.Get(cb)
if err != nil {
@@ -1068,11 +990,6 @@ func GetRepoCloudBrainByJobID(repoID int64, jobID string) (*Cloudbrain, error) {
return getRepoCloudBrain(cb)
}

func GetRepoCloudBrainByJobIDAndVersionName(repoID int64, jobID string, versionName string) (*Cloudbrain, error) {
cb := &Cloudbrain{JobID: jobID, RepoID: repoID, VersionName: versionName}
return getRepoCloudBrain(cb)
}

func GetCloudbrainByJobID(jobID string) (*Cloudbrain, error) {
cb := &Cloudbrain{JobID: jobID}
return getRepoCloudBrain(cb)
@@ -1112,9 +1029,9 @@ func SetTrainJobStatusByJobID(jobID string, status string, duration int64, train
return
}

func SetVersionCountAndLatestVersionByJobIDAndVersionName(jobID string, versionName string, versionCount int, isLatestVersion string) (err error) {
cb := &Cloudbrain{JobID: jobID, VersionName: versionName, VersionCount: versionCount, IsLatestVersion: isLatestVersion}
_, err = x.Cols("version_Count", "is_latest_version").Where("cloudbrain.job_id=? AND cloudbrain.version_name=?", jobID, versionName).Update(cb)
func SetVersionCountAndLatestVersion(jobID string, versionName string, versionCount int, isLatestVersion string, totalVersionCount int) (err error) {
cb := &Cloudbrain{JobID: jobID, VersionName: versionName, VersionCount: versionCount, IsLatestVersion: isLatestVersion, TotalVersionCount: totalVersionCount}
_, err = x.Cols("version_Count", "is_latest_version", "total_version_count").Where("cloudbrain.job_id=? AND cloudbrain.version_name=?", jobID, versionName).Update(cb)
return
}

@@ -1129,16 +1046,16 @@ func updateJob(e Engine, job *Cloudbrain) error {
return err
}

// func UpdateTrainJob(job *CloudbrainInfo) error {
// return updateTrainJob(x, job)
// }
func UpdateTrainJobVersion(job *Cloudbrain) error {
return updateJobTrainVersion(x, job)
}

// func updateTrainJob(e Engine, job *CloudbrainInfo) error {
// var sess *xorm.Session
// sess = e.Where("job_id = ?", job.Cloudbrain.JobID)
// _, err := sess.Cols("status", "container_id", "container_ip").Update(job)
// return err
// }
func updateJobTrainVersion(e Engine, job *Cloudbrain) error {
var sess *xorm.Session
sess = e.Where("job_id = ? AND version_name=?", job.JobID, job.VersionName)
_, err := sess.Cols("status", "train_job_duration").Update(job)
return err
}

func DeleteJob(job *Cloudbrain) error {
return deleteJob(x, job)


+ 0
- 1
models/models.go View File

@@ -134,7 +134,6 @@ func init() {
new(BlockChain),
new(RecommendOrg),
new(AiModelManage),
new(TrainjobConfigDetail),
)

tablesStatistic = append(tablesStatistic,


+ 12
- 11
models/repo_activity_custom.go View File

@@ -14,7 +14,6 @@ type ContributorWithUserId struct {
UserId int64
IsAdmin bool
RelAvatarLink string
Email string
}

func GetRepoKPIStats(repo *Repository) (*git.RepoKPIStats, error) {
@@ -127,12 +126,12 @@ func getRepoKPIStats(repoPath string, wikiPath string) (*git.RepoKPIStats, error

}

func GetTop10Contributor(repoPath string) ([]ContributorWithUserId, error) {
func GetTop10Contributor(repoPath string) ([]*ContributorWithUserId, error) {
contributors, err := git.GetContributors(repoPath)
if err != nil {
return make([]ContributorWithUserId, 0), err
return make([]*ContributorWithUserId, 0), err
}
contributorDistinctDict := make(map[string]ContributorWithUserId, 0)
contributorDistinctDict := make(map[string]*ContributorWithUserId, 0)
if contributors != nil {
for _, contributor := range contributors {
if strings.Compare(contributor.Email, "") == 0 {
@@ -144,12 +143,15 @@ func GetTop10Contributor(repoPath string) ([]ContributorWithUserId, error) {

value, ok := contributorDistinctDict[user.Email]
if !ok {
contributorDistinctDict[user.Email] = ContributorWithUserId{
contributor,
contributorDistinctDict[user.Email] = &ContributorWithUserId{
git.Contributor{
contributor.CommitCnt,
user.Name,
user.Email,
},
user.ID,
user.IsAdmin,
user.RelAvatarLink(),
user.Email,
}
} else {

@@ -159,12 +161,11 @@ func GetTop10Contributor(repoPath string) ([]ContributorWithUserId, error) {
} else {
value, ok := contributorDistinctDict[contributor.Email]
if !ok {
contributorDistinctDict[contributor.Email] = ContributorWithUserId{
contributorDistinctDict[contributor.Email] = &ContributorWithUserId{
contributor,
-1,
false,
"",
contributor.Email,
}
} else {
value.CommitCnt += contributor.CommitCnt
@@ -173,7 +174,7 @@ func GetTop10Contributor(repoPath string) ([]ContributorWithUserId, error) {
}

}
v := make([]ContributorWithUserId, 0, len(contributorDistinctDict))
v := make([]*ContributorWithUserId, 0, len(contributorDistinctDict))
for _, value := range contributorDistinctDict {
v = append(v, value)
}
@@ -188,7 +189,7 @@ func GetTop10Contributor(repoPath string) ([]ContributorWithUserId, error) {
return v[0:10], nil
}
}
return make([]ContributorWithUserId, 0), nil
return make([]*ContributorWithUserId, 0), nil
}

func setKeyContributerDict(contributorDistinctDict map[string]int, email string, keyContributorsDict map[string]struct{}) {


+ 70
- 77
modules/modelarts/modelarts.go View File

@@ -2,6 +2,7 @@ package modelarts

import (
"encoding/json"
"fmt"
"path"
"strconv"

@@ -35,24 +36,24 @@ const (
// "{\"code\":\"modelarts.bm.910.arm.public.4\",\"value\":\"Ascend : 4 * Ascend 910 CPU:96 核 1024GiB\"}," +
// "{\"code\":\"modelarts.bm.910.arm.public.1\",\"value\":\"Ascend : 1 * Ascend 910 CPU:24 核 256GiB\"}" +
// "]}"
CodePath = "/code/"
OutputPath = "/output/"
LogPath = "/log/"
JobPath = "/job/"
OrderDesc = "desc" //向下查询
OrderAsc = "asc" //向上查询
Lines = 20
TrainUrl = "train_url"
DataUrl = "data_url"
PerPage = 10
IsLatestVersion = "1"
NotLatestVersion = "0"
ComputeResource = "NPU"
InitFatherVersionName = "V0001"
VersionCount = 1
SortByCreateTime = "create_time"
ConfigTypeCustom = "custom"
CodePath = "/code/"
OutputPath = "/output/"
LogPath = "/log/"
JobPath = "/job/"
OrderDesc = "desc" //向下查询
OrderAsc = "asc" //向上查询
Lines = 500
TrainUrl = "train_url"
DataUrl = "data_url"
PerPage = 10
IsLatestVersion = "1"
NotLatestVersion = "0"
ComputeResource = "NPU"
VersionCount = 1
SortByCreateTime = "create_time"
ConfigTypeCustom = "custom"
TotalVersionCount = 1
)

var (
@@ -79,33 +80,37 @@ type GenerateTrainJobReq struct {
IsLatestVersion string
Params string
BranchName string
FatherVersionName string
PreVersionId int64
PreVersionName string
FlavorName string
VersionCount int
EngineName string
TotalVersionCount int
}

type GenerateTrainJobVersionReq struct {
JobName string
Uuid string
Description string
CodeObsPath string
BootFile string
BootFileUrl string
DataUrl string
TrainUrl string
FlavorCode string
LogUrl string
PoolID string
WorkServerNumber int
EngineID int64
Parameters []models.Parameter
Params string
PreVersionId int64
CommitID string
BranchName string
FlavorName string
EngineName string
JobName string
Uuid string
Description string
CodeObsPath string
BootFile string
BootFileUrl string
DataUrl string
TrainUrl string
FlavorCode string
LogUrl string
PoolID string
WorkServerNumber int
EngineID int64
Parameters []models.Parameter
Params string
PreVersionId int64
CommitID string
BranchName string
FlavorName string
EngineName string
PreVersionName string
TotalVersionCount int
}

type VersionInfo struct {
@@ -270,7 +275,6 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error
IsLatestVersion: req.IsLatestVersion,
ComputeResource: ComputeResource,
EngineID: req.EngineID,
FatherVersionName: req.FatherVersionName,
TrainUrl: req.TrainUrl,
BranchName: req.BranchName,
Parameters: req.Params,
@@ -283,6 +287,7 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error
FlavorName: req.FlavorName,
EngineName: req.EngineName,
VersionCount: req.VersionCount,
TotalVersionCount: req.TotalVersionCount,
})

if err != nil {
@@ -293,7 +298,7 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error
return nil
}

func GenerateTrainJobVersion(ctx *context.Context, req *GenerateTrainJobVersionReq, jobId string, fatherVersionName string) (err error) {
func GenerateTrainJobVersion(ctx *context.Context, req *GenerateTrainJobReq, jobId string) (err error) {
jobResult, err := createTrainJobVersion(models.CreateTrainJobVersionParams{
Description: req.Description,
Config: models.TrainJobVersionConfig{
@@ -323,6 +328,19 @@ func GenerateTrainJobVersion(ctx *context.Context, req *GenerateTrainJobVersionR
return err
}

repo := ctx.Repo.Repository
VersionTaskList, VersionListCount, err := models.CloudbrainsVersionList(&models.CloudbrainsOptions{
RepoID: repo.ID,
Type: models.TypeCloudBrainTwo,
JobType: string(models.JobTypeTrain),
JobID: strconv.FormatInt(jobResult.JobID, 10),
})
if err != nil {
ctx.ServerError("Cloudbrain", err)
return err
}
//将当前版本的isLatestVersion设置为"1"和任务数量更新,任务数量包括当前版本数VersionCount和历史创建的总版本数TotalVersionCount

err = models.CreateCloudbrain(&models.Cloudbrain{
Status: TransTrainJobStatus(jobResult.Status),
UserID: ctx.User.ID,
@@ -336,7 +354,8 @@ func GenerateTrainJobVersion(ctx *context.Context, req *GenerateTrainJobVersionR
Uuid: req.Uuid,
DatasetName: attach.Name,
CommitID: req.CommitID,
FatherVersionName: fatherVersionName,
IsLatestVersion: req.IsLatestVersion,
PreVersionName: req.PreVersionName,
ComputeResource: ComputeResource,
EngineID: req.EngineID,
TrainUrl: req.TrainUrl,
@@ -351,48 +370,18 @@ func GenerateTrainJobVersion(ctx *context.Context, req *GenerateTrainJobVersionR
WorkServerNumber: req.WorkServerNumber,
FlavorName: req.FlavorName,
EngineName: req.EngineName,
TotalVersionCount: VersionTaskList[0].TotalVersionCount + 1,
VersionCount: VersionListCount + 1,
})
if err != nil {
log.Error("CreateCloudbrain(%s) failed:%v", req.JobName, err.Error())
return err
}

repo := ctx.Repo.Repository
page := ctx.QueryInt("page")
if page <= 0 {
page = 1
}
_, VersionListCount, err := models.CloudbrainsVersionList(&models.CloudbrainsOptions{
ListOptions: models.ListOptions{
Page: page,
PageSize: setting.UI.IssuePagingNum,
},
RepoID: repo.ID,
Type: models.TypeCloudBrainTwo,
JobType: string(models.JobTypeTrain),
JobID: strconv.FormatInt(jobResult.JobID, 10),
})
if err != nil {
ctx.ServerError("Cloudbrain", err)
return err
}

//将训练任务的上一版本的isLatestVersion设置为"0"
latestTask, err := models.GetCloudbrainByJobIDAndIsLatestVersion(strconv.FormatInt(jobResult.JobID, 10), IsLatestVersion)
err = models.SetVersionCountAndLatestVersion(strconv.FormatInt(jobResult.JobID, 10), VersionTaskList[0].VersionName, VersionCount, NotLatestVersion, TotalVersionCount)
if err != nil {
ctx.ServerError("GetCloudbrainByJobIDAndIsLatestVersion faild:", err)
return err
}
err = models.SetVersionCountAndLatestVersionByJobIDAndVersionName(strconv.FormatInt(jobResult.JobID, 10), latestTask.VersionName, VersionListCount, NotLatestVersion)
if err != nil {
ctx.ServerError("UpdateJobVersionCount failed", err)
return err
}

//将当前版本的isLatestVersion设置为"1"和任务数量更新
err = models.SetVersionCountAndLatestVersionByJobIDAndVersionName(strconv.FormatInt(jobResult.JobID, 10), jobResult.VersionName, VersionListCount, IsLatestVersion)
if err != nil {
ctx.ServerError("UpdateJobVersionCount failed", err)
ctx.ServerError("Update IsLatestVersion failed", err)
return err
}

@@ -449,6 +438,10 @@ func TransTrainJobStatus(status int) string {
default:
return strconv.Itoa(status)
}
}

return ""
func GetVersionOutputPathByTotalVersionCount(TotalVersionCount int) (VersionOutputPath string) {
talVersionCountToString := fmt.Sprintf("%04d", TotalVersionCount)
VersionOutputPath = "V" + talVersionCountToString
return VersionOutputPath
}

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

@@ -366,6 +366,16 @@ sendjob:
return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error())
}
log.Error("createTrainJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
BootFileErrorMsg := "Invalid OBS path '" + createJobParams.Config.BootFileUrl + "'."
DataSetErrorMsg := "Invalid OBS path '" + createJobParams.Config.DataUrl + "'."
if temp.ErrorMsg == BootFileErrorMsg {
log.Error("启动文件错误!createTrainJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
return &result, fmt.Errorf("启动文件错误!")
}
if temp.ErrorMsg == DataSetErrorMsg {
log.Error("数据集错误!createTrainJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
return &result, fmt.Errorf("数据集错误!")
}
return &result, fmt.Errorf("createTrainJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
}

@@ -411,7 +421,16 @@ sendjob:
log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error())
return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error())
}
log.Error("createTrainJobVersion failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
BootFileErrorMsg := "Invalid OBS path '" + createJobVersionParams.Config.BootFileUrl + "'."
DataSetErrorMsg := "Invalid OBS path '" + createJobVersionParams.Config.DataUrl + "'."
if temp.ErrorMsg == BootFileErrorMsg {
log.Error("启动文件错误!createTrainJobVersion failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
return &result, fmt.Errorf("启动文件错误!")
}
if temp.ErrorMsg == DataSetErrorMsg {
log.Error("数据集错误!createTrainJobVersion failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
return &result, fmt.Errorf("数据集错误!")
}
return &result, fmt.Errorf("createTrainJobVersion failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
}

@@ -814,3 +833,44 @@ sendjob:

return &result, nil
}

func DelTrainJobVersion(jobID string, versionID string) (*models.TrainJobResult, error) {
checkSetting()
client := getRestyClient()
var result models.TrainJobResult

retry := 0

sendjob:
res, err := client.R().
SetAuthToken(TOKEN).
SetResult(&result).
Delete(HOST + "/v1/" + setting.ProjectID + urlTrainJob + "/" + jobID + "/versions/" + versionID)

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

if res.StatusCode() == http.StatusUnauthorized && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

if res.StatusCode() != http.StatusOK {
var temp models.ErrorResult
if err = json.Unmarshal([]byte(res.String()), &temp); err != nil {
log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error())
return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error())
}
log.Error("DelTrainJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
return &result, fmt.Errorf("删除训练作业版本失败(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
}

if !result.IsSuccess {
log.Error("DelTrainJob(%s) failed", jobID)
return &result, fmt.Errorf("删除训练作业版本失败:%s", result.ErrorMsg)
}

return &result, nil
}

+ 0
- 56
modules/storage/obs.go View File

@@ -431,62 +431,6 @@ func GetObsListObject(jobName, parentDir string) ([]FileInfo, error) {
}
}

func GetVersionObsListObject(jobName, parentDir string) ([]FileInfo, error) {
input := &obs.ListObjectsInput{}
input.Bucket = setting.Bucket
input.Prefix = strings.TrimPrefix(path.Join(setting.TrainJobModelPath, jobName, setting.OutPutPath, parentDir), "/")
strPrefix := strings.Split(input.Prefix, "/")
output, err := ObsCli.ListObjects(input)
fileInfos := make([]FileInfo, 0)
if err == nil {
for _, val := range output.Contents {
str1 := strings.Split(val.Key, "/")
var isDir bool
var fileName, nextParentDir string
if strings.HasSuffix(val.Key, "/") {
//dirs in next level dir
if len(str1)-len(strPrefix) > 2 {
continue
}
fileName = str1[len(str1)-2]
isDir = true
if parentDir == "" {
nextParentDir = fileName
} else {
nextParentDir = parentDir + "/" + fileName
}

if fileName == strPrefix[len(strPrefix)-1] || (fileName+"/") == setting.OutPutPath {
continue
}
} else {
//files in next level dir
if len(str1)-len(strPrefix) > 1 {
continue
}
fileName = str1[len(str1)-1]
isDir = false
nextParentDir = parentDir
}

fileInfo := FileInfo{
ModTime: val.LastModified.Local().Format("2006-01-02 15:04:05"),
FileName: fileName,
Size: val.Size,
IsDir: isDir,
ParenDir: nextParentDir,
}
fileInfos = append(fileInfos, fileInfo)
}
return fileInfos, err
} else {
if obsError, ok := err.(obs.ObsError); ok {
log.Error("Code:%s, Message:%s", obsError.Code, obsError.Message)
}
return nil, err
}
}

func ObsGenMultiPartSignedUrl(uuid string, uploadId string, partNumber int, fileName string) (string, error) {

input := &obs.CreateSignedUrlInput{}


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

@@ -92,6 +92,7 @@ func NewFuncMap() []template.FuncMap {
"Str2html": Str2html,
"TimeSince": timeutil.TimeSince,
"TimeSinceUnix": timeutil.TimeSinceUnix,
"TimeSinceUnix1": timeutil.TimeSinceUnix1,
"RawTimeSince": timeutil.RawTimeSince,
"FileSize": base.FileSize,
"PrettyNumber": base.PrettyNumber,
@@ -340,6 +341,7 @@ func NewTextFuncMap() []texttmpl.FuncMap {
},
"TimeSince": timeutil.TimeSince,
"TimeSinceUnix": timeutil.TimeSinceUnix,
"TimeSinceUnix1": timeutil.TimeSinceUnix1,
"RawTimeSince": timeutil.RawTimeSince,
"DateFmtLong": func(t time.Time) string {
return t.Format(time.RFC1123Z)


+ 5
- 0
modules/timeutil/since.go View File

@@ -162,3 +162,8 @@ func htmlTimeSinceUnix(then, now TimeStamp, lang string) template.HTML {
then.FormatInLocation(GetTimeFormat(lang), setting.DefaultUILocation),
timeSinceUnix(int64(then), int64(now), lang)))
}
func TimeSinceUnix1(then TimeStamp) string {
format := time.Unix(int64(then), 0).Format("2006-01-02 15:04:05")
return format
}

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

@@ -823,7 +823,11 @@ modelarts.train_job.new_train=New Train Task
modelarts.train_job.config=Configuration information
modelarts.train_job.new=New train Task
modelarts.train_job.new_place=The description should not exceed 256 characters

modelarts.modify=Modify
modelarts.current_version=Current version
modelarts.parent_version=Parent Version
modelarts.run_version=Run Version
modelarts.train_job.compute_node=Compute Node


modelarts.train_job.basic_info=Basic Info
@@ -845,6 +849,7 @@ modelarts.train_job.start_file=Start File
modelarts.train_job.boot_file_helper=The startup file is the entry file that your program executes, and it must be a file ending in .py
modelarts.train_job.dataset=Dataset
modelarts.code_version = Code Version
modelarts.parents_version = Parents Version
modelarts.train_job.run_parameter=Run Parameter
modelarts.train_job.add_run_parameter=Add Run Parameter
modelarts.train_job.parameter_name=Parameter Name


+ 15
- 5
options/locale/locale_zh-CN.ini View File

@@ -816,9 +816,11 @@ total_count_get_error=查询总页数失败。
last_update_time_error=查询最新更新时间失败。
get_repo_stat_error=查询当前仓库的统计信息失败。
get_repo_info_error=查询当前仓库信息失败。
generate_statistic_file_error=生成文件失败。
repo_stat_inspect=项目分析
all=所有

modelarts.status=状态
modelarts.createtime=创建时间
modelarts.version_nums=版本数
modelarts.computing_resources=计算资源
modelarts.notebook=调试任务
modelarts.train_job=训练任务
modelarts.train_job.new_debug=新建调试任务
@@ -826,6 +828,10 @@ modelarts.train_job.new_train=新建训练任务
modelarts.train_job.config=配置信息
modelarts.train_job.new=新建训练任务
modelarts.train_job.new_place=描述字数不超过256个字符
modelarts.modify=修改
modelarts.current_version=当前版本
modelarts.parent_version=父版本
modelarts.run_version=运行版本



@@ -845,10 +851,14 @@ modelarts.train_job.frames=常用框架
modelarts.train_job.algorithm_origin=算法来源
modelarts.train_job.AI_driver=AI引擎
modelarts.train_job.start_file=启动文件
modelarts.train_job.boot_file_helper=启动文件是您程序执行的入口文件,必须是以.py结尾的文件。
modelarts.train_job.boot_file_helper=启动文件是您程序执行的入口文件,必须是以.py结尾的文件。比如train.py、main.py、example/train.py、case/main.py。
modelarts.train_job.boot_file_place=填写启动文件路径,默认为train.py
modelarts.train_job.dataset=数据集
modelarts.code_version=代码版本
modelarts.code_version=代码分支
modelarts.parents_version=基于版本
modelarts.train_job.compute_node=计算节点
modelarts.train_job.train_dataset=训练数据集

modelarts.train_job.run_parameter=运行参数
modelarts.train_job.add_run_parameter=增加运行参数
modelarts.train_job.parameter_name=参数名


+ 4
- 5
routers/api/v1/api.go View File

@@ -874,13 +874,12 @@ func RegisterRoutes(m *macaron.Macaron) {
})
m.Group("/train-job", func() {
m.Group("/:jobid", func() {
// m.Get("", repo.GetModelArtsTrainJob)
m.Get("", repo.GetModelArtsTrainJobVersion)
// m.Get("/log", repo.TrainJobGetLog)
m.Get("/log", repo.TrainJobGetLog)
// m.Group("/:version-name", func() {
// m.Get("", repo.GetModelArtsTrainJobVersion)
// })
m.Post("/del_version", repo.DelTrainJobVersion)
m.Post("/stop_version", repo.StopTrainJobVersion)
m.Get("/model_list", repo.ModelList)
m.Get("/model_download", repo.ModelDownload)
})
})
}, reqRepoReader(models.UnitTypeCloudBrain))


+ 205
- 21
routers/api/v1/repo/modelarts.go View File

@@ -8,11 +8,13 @@ package repo
import (
"net/http"
"strconv"
"strings"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/modelarts"
"code.gitea.io/gitea/modules/storage"
)

func GetModelArtsNotebook(ctx *context.APIContext) {
@@ -87,8 +89,7 @@ func GetModelArtsTrainJobVersion(ctx *context.APIContext) {

jobID := ctx.Params(":jobid")
versionName := ctx.Query("version_name")
repoID := ctx.Repo.Repository.ID
job, err := models.GetRepoCloudBrainByJobIDAndVersionName(repoID, jobID, versionName)
job, err := models.GetCloudbrainByJobIDAndVersionName(jobID, versionName)
if err != nil {
ctx.NotFound(err)
return
@@ -102,7 +103,15 @@ func GetModelArtsTrainJobVersion(ctx *context.APIContext) {
job.Status = modelarts.TransTrainJobStatus(result.IntStatus)
job.Duration = result.Duration
job.TrainJobDuration = result.TrainJobDuration
err = models.UpdateJob(job)

if result.Duration != 0 {
job.TrainJobDuration = addZero(result.Duration/3600000) + ":" + addZero(result.Duration%3600000/60000) + ":" + addZero(result.Duration%60000/1000)

} else {
job.TrainJobDuration = "00:00:00"
}

err = models.UpdateTrainJobVersion(job)
if err != nil {
log.Error("UpdateJob failed:", err)
}
@@ -110,23 +119,35 @@ func GetModelArtsTrainJobVersion(ctx *context.APIContext) {
ctx.JSON(http.StatusOK, map[string]interface{}{
"JobID": jobID,
"JobStatus": job.Status,
"JobDuration": job.Duration,
"JobDuration": job.TrainJobDuration,
})

}

func addZero(t int64) (m string) {
if t < 10 {
m = "0" + strconv.FormatInt(t, 10)
return m
} else {
return strconv.FormatInt(t, 10)
}
}

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

log.Info("test")

var jobID = ctx.Params(":jobid")
var versionName = ctx.Query("version_name")
var logFileName = ctx.Query("file_name")
// var logFileName = ctx.Query("file_name")
var baseLine = ctx.Query("base_line")
var order = ctx.Query("order")
var lines = ctx.Query("lines")
lines_int, err := strconv.Atoi(lines)
if err != nil {
log.Error("change lines(%d) string to int failed", lines_int)
}

if order != modelarts.OrderDesc && order != modelarts.OrderAsc {
log.Error("order(%s) check failed", order)
@@ -136,29 +157,192 @@ func TrainJobGetLog(ctx *context.APIContext) {
return
}

task, err := models.GetCloudbrainByJobIDAndVersionName(jobID, versionName)
resultLogFile, result, err := trainJobGetLogContent(jobID, versionName, baseLine, order, lines_int)
if err != nil {
log.Error("GetCloudbrainByJobIDAndVersionName(%s) failed:%v", jobID, err.Error())
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
"err_msg": "GetCloudbrainByJobIDAndVersionName failed",
})
log.Error("trainJobGetLog(%s) failed:%v", jobID, err.Error())
// ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobShow, nil)
return
}

result, err := modelarts.GetTrainJobLog(jobID, strconv.FormatInt(task.VersionID, 10), baseLine, logFileName, order, modelarts.Lines)
ctx.Data["log_file_name"] = resultLogFile.LogFileList[0]

ctx.JSON(http.StatusOK, map[string]interface{}{
"JobID": jobID,
"LogFileName": resultLogFile.LogFileList[0],
"StartLine": result.StartLine,
"EndLine": result.EndLine,
"Content": result.Content,
"Lines": result.Lines,
})
}

func trainJobGetLogContent(jobID string, versionName string, baseLine string, order string, lines int) (*models.GetTrainJobLogFileNamesResult, *models.GetTrainJobLogResult, error) {
task, err := models.GetCloudbrainByJobIDAndVersionName(jobID, versionName)
if err != nil {
log.Error("GetCloudbrainByJobID(%s) failed:%v", jobID, err.Error())
return nil, nil, err
}

resultLogFile, err := modelarts.GetTrainJobLogFileNames(jobID, strconv.FormatInt(task.VersionID, 10))
if err != nil {
log.Error("GetTrainJobLogFileNames(%s) failed:%v", jobID, err.Error())
return nil, nil, err
}

result, err := modelarts.GetTrainJobLog(jobID, strconv.FormatInt(task.VersionID, 10), baseLine, resultLogFile.LogFileList[0], order, lines)
if err != nil {
log.Error("GetTrainJobLog(%s) failed:%v", jobID, err.Error())
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
"err_msg": "GetTrainJobLog failed",
})
return nil, nil, err
}

return resultLogFile, result, err
}

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

var jobID = ctx.Params(":jobid")
var versionName = ctx.Query("version_name")
task, err := models.GetCloudbrainByJobIDAndVersionName(jobID, versionName)
if err != nil {
log.Error("GetCloudbrainByJobID(%s) failed:%v", task.JobName, err.Error())
ctx.NotFound(err)
return
}

//删除modelarts上的记录
_, err = modelarts.DelTrainJobVersion(jobID, strconv.FormatInt(task.VersionID, 10))
if err != nil {
log.Error("DelTrainJobVersion(%s) failed:%v", task.JobName, err.Error())
ctx.NotFound(err)
return
}

//删除数据库记录
err = models.DeleteJob(task)
if err != nil {
ctx.ServerError("DeleteJob failed", err)
ctx.NotFound(err)
return
}

//获取删除后的版本数量
repo := ctx.Repo.Repository
VersionTaskList, VersionListCount, err := models.CloudbrainsVersionList(&models.CloudbrainsOptions{
RepoID: repo.ID,
Type: models.TypeCloudBrainTwo,
JobType: string(models.JobTypeTrain),
JobID: jobID,
})
if err != nil {
ctx.ServerError("get VersionListCount faild", err)
return
}

// 判断当前删掉的任务是否是最新版本,若是,将排序后的TotalVersionCount置为删掉的最新版本的TotalVersionCount,若不是,按时间排序后的版本列表的第一个版本设置为最新版本,TotalVersionCount不变
if task.IsLatestVersion == modelarts.IsLatestVersion {
err = models.SetVersionCountAndLatestVersion(jobID, VersionTaskList[0].Cloudbrain.VersionName, VersionListCount, modelarts.IsLatestVersion, task.TotalVersionCount)
if err != nil {
ctx.ServerError("UpdateJobVersionCount failed", err)
return
}
} else {
err = models.SetVersionCountAndLatestVersion(jobID, VersionTaskList[0].VersionName, VersionListCount, modelarts.IsLatestVersion, VersionTaskList[0].Cloudbrain.TotalVersionCount)
if err != nil {
ctx.ServerError("UpdateJobVersionCount failed", err)
return
}
}

ctx.JSON(http.StatusOK, map[string]interface{}{
"JobID": jobID,
"StartLine": result.StartLine,
"EndLine": result.EndLine,
"Content": result.Content,
"Lines": result.Lines,
"JobID": jobID,
"VersionName": versionName,
"StatusOK": 0,
})
}

func StopTrainJobVersion(ctx *context.APIContext) {
var (
err error
)
var jobID = ctx.Params(":jobid")
var versionName = ctx.Query("version_name")
task, err := models.GetCloudbrainByJobIDAndVersionName(jobID, versionName)
if err != nil {
log.Error("GetCloudbrainByJobID(%s) failed:%v", task.JobName, err.Error())
return
}

_, err = modelarts.StopTrainJob(jobID, strconv.FormatInt(task.VersionID, 10))
if err != nil {
log.Error("StopTrainJob(%s) failed:%v", task.JobName, err.Error())
return
}

ctx.JSON(http.StatusOK, map[string]interface{}{
"JobID": jobID,
"VersionName": versionName,
"StatusOK": 0,
})
}

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

var jobID = ctx.Params(":jobid")
var versionName = ctx.Query("version_name")
parentDir := ctx.Query("parentDir")
dirArray := strings.Split(parentDir, "/")
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
models, err := storage.GetObsListObject(task.JobName, parentDir)
if err != nil {
log.Info("get TrainJobListModel failed:", err)
ctx.ServerError("GetObsListObject:", err)
return
}

ctx.JSON(http.StatusOK, map[string]interface{}{
"JobID": jobID,
"VersionName": versionName,
"StatusOK": 0,
"Path": dirArray,
"Dirs": models,
"task": task,
"PageIsCloudBrain": true,
})
}

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

var jobID = ctx.Params(":jobid")
versionName := ctx.Query("version_name")
parentDir := ctx.Query("parent_dir")
fileName := ctx.Query("file_name")
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
}
http.Redirect(ctx.Resp, ctx.Req.Request, url, http.StatusMovedPermanently)
}

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

@@ -362,7 +362,7 @@ func generateRadarSql(beginTime time.Time, endTime time.Time, repoId int64) stri
}

func generateTargetSql(beginTime time.Time, endTime time.Time, repoId int64) string {
sql := "SELECT date, num_visits,num_downloads,num_commits FROM repo_statistic" +
sql := "SELECT date, num_visits,num_downloads_added as num_downloads,num_commits_added as num_commits FROM repo_statistic" +
" where repo_id=" + strconv.FormatInt(repoId, 10) + " and created_unix >=" + strconv.FormatInt(beginTime.Unix(), 10) +
" and created_unix<" + strconv.FormatInt(endTime.Unix(), 10) + " order by created_unix desc"



+ 88
- 283
routers/repo/modelarts.go View File

@@ -34,7 +34,6 @@ const (
tplModelArtsTrainJobIndex base.TplName = "repo/modelarts/trainjob/index"
tplModelArtsTrainJobNew base.TplName = "repo/modelarts/trainjob/new"
tplModelArtsTrainJobShow base.TplName = "repo/modelarts/trainjob/show"
tplModelArtsTrainJobShowModels base.TplName = "repo/modelarts/trainjob/models/index"
tplModelArtsTrainJobVersionNew base.TplName = "repo/modelarts/trainjob/version_new"
)

@@ -463,7 +462,7 @@ func trainJobNewVersionDataPrepare(ctx *context.Context) error {
ctx.Data["dataset_name"] = task.DatasetName
ctx.Data["work_server_number"] = task.WorkServerNumber
ctx.Data["flavor_name"] = task.FlavorName
ctx.Data["engine_name"] = task.FlavorName
ctx.Data["engine_name"] = task.EngineName
ctx.Data["uuid"] = task.Uuid
ctx.Data["flavor_code"] = task.FlavorCode
ctx.Data["engine_id"] = task.EngineID
@@ -480,6 +479,7 @@ func trainJobNewVersionDataPrepare(ctx *context.Context) error {

func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm) {
ctx.Data["PageIsTrainJob"] = true
VersionOutputPath := modelarts.GetVersionOutputPathByTotalVersionCount(modelarts.TotalVersionCount)
jobName := form.JobName
uuid := form.Attachment
description := form.Description
@@ -493,8 +493,8 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm)
repo := ctx.Repo.Repository
codeLocalPath := setting.JobPath + jobName + modelarts.CodePath
codeObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.CodePath
outputObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.OutputPath
logObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.LogPath
outputObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.OutputPath + VersionOutputPath + "/"
logObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.LogPath + VersionOutputPath + "/"
dataPath := "/" + setting.Bucket + "/" + setting.BasePath + path.Join(uuid[0:1], uuid[1:2]) + "/" + uuid + uuid + "/"
branch_name := form.BranchName
isLatestVersion := modelarts.IsLatestVersion
@@ -527,7 +527,7 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm)
if err := git.Clone(repo.RepoPath(), codeLocalPath, git.CloneRepoOptions{
Branch: branch_name,
}); err != nil {
log.Error("创建任务失败,任务名称已存在!: %s (%v)", repo.FullName(), err)
log.Error("创建任务失败,服务器超时!: %s (%v)", repo.FullName(), err)
trainJobNewDataPrepare(ctx)

ctx.Data["bootFile"] = form.BootFile
@@ -536,21 +536,19 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm)
ctx.Data["params"] = form.Params
ctx.Data["branch_name"] = branch_name
trainJobNewDataPrepare(ctx)
// ctx.RenderWithErr("Failed to clone repository", tplModelArtsTrainJobNew, &form)
ctx.RenderWithErr("创建任务失败,任务名称已存在!", tplModelArtsTrainJobNew, &form)
// ctx.RenderWithErr(err, tplModelArtsTrainJobNew, &form)
ctx.RenderWithErr("创建任务失败,服务器超时!", tplModelArtsTrainJobNew, &form)
return
}

//todo: upload code (send to file_server todo this work?)
if err := obsMkdir(setting.CodePathPrefix + jobName + modelarts.OutputPath); err != nil {
if err := obsMkdir(setting.CodePathPrefix + jobName + modelarts.OutputPath + VersionOutputPath + "/"); err != nil {
log.Error("Failed to obsMkdir_output: %s (%v)", repo.FullName(), err)
trainJobNewDataPrepare(ctx)
ctx.RenderWithErr("Failed to obsMkdir_output", tplModelArtsTrainJobNew, &form)
return
}

if err := obsMkdir(setting.CodePathPrefix + jobName + modelarts.LogPath); err != nil {
if err := obsMkdir(setting.CodePathPrefix + jobName + modelarts.LogPath + VersionOutputPath + "/"); err != nil {
log.Error("Failed to obsMkdir_log: %s (%v)", repo.FullName(), err)
trainJobNewDataPrepare(ctx)
ctx.RenderWithErr("Failed to obsMkdir_log", tplModelArtsTrainJobNew, &form)
@@ -647,10 +645,10 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm)
IsLatestVersion: isLatestVersion,
BranchName: branch_name,
Params: form.Params,
FatherVersionName: modelarts.InitFatherVersionName,
FlavorName: FlavorName,
EngineName: EngineName,
VersionCount: VersionCount,
TotalVersionCount: modelarts.TotalVersionCount,
}

err = modelarts.GenerateTrainJob(ctx, req)
@@ -665,42 +663,19 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm)
ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobNew, &form)
return
}
// // 保存openi创建训练任务界面的参数
// err = models.CreateTrainjobConfigDetail(&models.TrainjobConfigDetail{

// JobName: req.JobName,
// JobID: strconv.FormatInt(jobResult.JobID, 10),
// VersionName: jobResult.VersionName,
// ResourcePools: form.PoolID,
// EngineVersions: form.EngineID,
// FlavorInfos: form.Flavor,
// TrainUrl: outputObsPath,
// BootFile: form.BootFile,
// Uuid: form.Attachment,
// DatasetName: attach.Name,
// Params: form.Params,
// BranchName: branch_name,
// })

// if err != nil {
// log.Error("CreateTrainjobConfigDetail failed:%v", err.Error())
// trainJobNewVersionDataPrepare(ctx)
// ctx.Data["bootFile"] = form.BootFile
// ctx.Data["uuid"] = form.Attachment
// ctx.Data["datasetName"] = attach.Name
// ctx.Data["params"] = form.Params
// ctx.Data["branch_name"] = branch_name
// ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobVersionNew, &form)
// return
// }
ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/train-job")
}

func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJobForm) {
ctx.Data["PageIsTrainJob"] = true
var jobID = ctx.Params(":jobid")
// var versionName = ctx.Params(":version-name")
var versionName = ctx.Query("version_name")

latestTask, err := models.GetCloudbrainByJobIDAndIsLatestVersion(jobID, modelarts.IsLatestVersion)
if err != nil {
ctx.ServerError("GetCloudbrainByJobIDAndIsLatestVersion faild:", err)
return
}
VersionOutputPath := modelarts.GetVersionOutputPathByTotalVersionCount(latestTask.TotalVersionCount + 1)

jobName := form.JobName
uuid := form.Attachment
@@ -715,13 +690,14 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ
repo := ctx.Repo.Repository
codeLocalPath := setting.JobPath + jobName + modelarts.CodePath
codeObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.CodePath
outputObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.OutputPath
logObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.LogPath
outputObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.OutputPath + VersionOutputPath + "/"
logObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.LogPath + VersionOutputPath + "/"
dataPath := "/" + setting.Bucket + "/" + setting.BasePath + path.Join(uuid[0:1], uuid[1:2]) + "/" + uuid + uuid + "/"
branch_name := form.BranchName
fatherVersionName := versionName
PreVersionName := form.VersionName
FlavorName := form.FlavorName
EngineName := form.EngineName
isLatestVersion := modelarts.IsLatestVersion

if err := paramCheckCreateTrainJob(form); err != nil {
log.Error("paramCheckCreateTrainJob failed:(%v)", err)
@@ -755,21 +731,19 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ
ctx.Data["datasetName"] = attach.Name
ctx.Data["params"] = form.Params
ctx.Data["branch_name"] = branch_name
// ctx.RenderWithErr("Failed to clone repository", tplModelArtsTrainJobNew, &form)
ctx.RenderWithErr("创建任务失败,任务名称已存在!", tplModelArtsTrainJobVersionNew, &form)
// ctx.RenderWithErr(err, tplModelArtsTrainJobNew, &form)
return
}

//todo: upload code (send to file_server todo this work?)
if err := obsMkdir(setting.CodePathPrefix + jobName + modelarts.OutputPath); err != nil {
if err := obsMkdir(setting.CodePathPrefix + jobName + modelarts.OutputPath + VersionOutputPath + "/"); err != nil {
log.Error("Failed to obsMkdir_output: %s (%v)", repo.FullName(), err)
trainJobNewVersionDataPrepare(ctx)
ctx.RenderWithErr("Failed to obsMkdir_output", tplModelArtsTrainJobVersionNew, &form)
return
}

if err := obsMkdir(setting.CodePathPrefix + jobName + modelarts.LogPath); err != nil {
if err := obsMkdir(setting.CodePathPrefix + jobName + modelarts.LogPath + VersionOutputPath + "/"); err != nil {
log.Error("Failed to obsMkdir_log: %s (%v)", repo.FullName(), err)
trainJobNewVersionDataPrepare(ctx)
ctx.RenderWithErr("Failed to obsMkdir_log", tplModelArtsTrainJobVersionNew, &form)
@@ -853,34 +827,39 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ
return
}

task, err := models.GetCloudbrainByJobIDAndVersionName(jobID, fatherVersionName)
task, err := models.GetCloudbrainByJobIDAndVersionName(jobID, PreVersionName)
if err != nil {
log.Error("GetCloudbrainByJobIDAndVersionName(%s) failed:%v", jobID, err.Error())
ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobVersionNew, &form)
return
}
req := &modelarts.GenerateTrainJobVersionReq{
JobName: task.JobName,
DataUrl: dataPath,
Description: description,
CodeObsPath: codeObsPath,
BootFileUrl: codeObsPath + bootFile,
BootFile: bootFile,
TrainUrl: outputObsPath,
FlavorCode: flavorCode,
WorkServerNumber: workServerNumber,
EngineID: int64(engineID),
LogUrl: logObsPath,
PoolID: poolID,
Uuid: uuid,
Params: form.Params,
PreVersionId: task.VersionID,
CommitID: commitID,
BranchName: branch_name,
FlavorName: FlavorName,
EngineName: EngineName,
}
err = modelarts.GenerateTrainJobVersion(ctx, req, jobID, fatherVersionName)
req := &modelarts.GenerateTrainJobReq{
JobName: task.JobName,
DataUrl: dataPath,
Description: description,
CodeObsPath: codeObsPath,
BootFileUrl: codeObsPath + bootFile,
BootFile: bootFile,
TrainUrl: outputObsPath,
FlavorCode: flavorCode,
WorkServerNumber: workServerNumber,
IsLatestVersion: isLatestVersion,
EngineID: int64(engineID),
LogUrl: logObsPath,
PoolID: poolID,
Uuid: uuid,
Params: form.Params,
Parameters: parameters.Parameter,
PreVersionId: task.VersionID,
CommitID: commitID,
BranchName: branch_name,
FlavorName: FlavorName,
EngineName: EngineName,
PreVersionName: PreVersionName,
TotalVersionCount: latestTask.TotalVersionCount + 1,
}

err = modelarts.GenerateTrainJobVersion(ctx, req, jobID)
if err != nil {
log.Error("GenerateTrainJob failed:%v", err.Error())
trainJobNewVersionDataPrepare(ctx)
@@ -891,36 +870,8 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ
ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobVersionNew, &form)
return
}
// 保存openi创建训练任务界面的参数
// err = models.CreateTrainjobConfigDetail(&models.TrainjobConfigDetail{

// JobName: req.JobName,
// JobID: strconv.FormatInt(jobResult.JobID, 10),
// VersionName: jobResult.VersionName,
// ResourcePools: form.PoolID,
// EngineVersions: form.EngineID,
// FlavorInfos: form.Flavor,
// TrainUrl: outputObsPath,
// BootFile: form.BootFile,
// Uuid: form.Attachment,
// DatasetName: attach.Name,
// Params: form.Params,
// BranchName: branch_name,
// })

// if err != nil {
// log.Error("CreateTrainjobConfigDetail failed:%v", err.Error())
// trainJobNewVersionDataPrepare(ctx)
// ctx.Data["bootFile"] = form.BootFile
// ctx.Data["uuid"] = form.Attachment
// ctx.Data["datasetName"] = attach.Name
// ctx.Data["params"] = form.Params
// ctx.Data["branch_name"] = branch_name
// ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobVersionNew, &form)
// return
// }
// ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/train-job")
ctx.HTML(http.StatusOK, tplModelArtsTrainJobShow)
ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/train-job")
// ctx.HTML(http.StatusOK, tplModelArtsTrainJobShow)
}

// readDir reads the directory named by dirname and returns
@@ -1014,11 +965,6 @@ func TrainJobShow(ctx *context.Context) {
ctx.Data["PageIsCloudBrain"] = true

var jobID = ctx.Params(":jobid")
task, err := models.GetCloudbrainByJobID(jobID)
if err != nil {
ctx.ServerError("GetCloudbrainByJobID faild", err)
return
}

repo := ctx.Repo.Repository
page := ctx.QueryInt("page")
@@ -1035,75 +981,43 @@ func TrainJobShow(ctx *context.Context) {
JobType: string(models.JobTypeTrain),
JobID: jobID,
})
if err != nil {
ctx.ServerError("Cloudbrain", err)
return
}

if err != nil {
log.Error("GetCloudbrainByJobID(%s) failed:%v", jobID, err.Error())
ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobShow, nil)
return
}

// attach, err := models.GetAttachmentByUUID(task.Uuid)
// if err != nil {
// log.Error("GetAttachmentByUUID(%s) failed:%v", jobID, err.Error())
// ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobShow, nil)
// return
// }

result, err := modelarts.GetTrainJob(jobID, strconv.FormatInt(task.VersionID, 10))
if err != nil {
log.Error("GetJob(%s) failed:%v", jobID, err.Error())
log.Error("GetVersionListTasks(%s) failed:%v", jobID, err.Error())
ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobShow, nil)
return
}
//将运行参数转化为epoch_size = 3, device_target = Ascend的格式
for i, _ := range VersionListTasks {

if result != nil {
result.CreateTime = time.Unix(int64(result.LongCreateTime/1000), 0).Format("2006-01-02 15:04:05")
if result.Duration != 0 {
result.TrainJobDuration = addZero(result.Duration/3600000) + ":" + addZero(result.Duration%3600000/60000) + ":" + addZero(result.Duration%60000/1000)
var parameters models.Parameters

} else {
result.TrainJobDuration = "00:00:00"
}
result.Status = modelarts.TransTrainJobStatus(result.IntStatus)
err = models.SetTrainJobStatusByJobID(jobID, result.Status, result.Duration, string(result.TrainJobDuration))
err := json.Unmarshal([]byte(VersionListTasks[i].Parameters), &parameters)
if err != nil {
ctx.ServerError("UpdateJob failed", err)
log.Error("Failed to Unmarshal Parameters: %s (%v)", VersionListTasks[i].Parameters, err)
trainJobNewDataPrepare(ctx)
return
}

result.DatasetName = task.DatasetName
}

resultLogFile, resultLog, err := trainJobGetLog(jobID)
if err != nil {
log.Error("trainJobGetLog(%s) failed:%v", jobID, err.Error())
ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobShow, nil)
return
if len(parameters.Parameter) > 0 {
paramTemp := ""
for _, Parameter := range parameters.Parameter {
param := Parameter.Label + " = " + Parameter.Value + ", "
paramTemp = paramTemp + param
}
VersionListTasks[i].Parameters = paramTemp[:len(paramTemp)-2]
} else {
VersionListTasks[i].Parameters = ""
}
}

ctx.Data["log_file_name"] = resultLogFile.LogFileList[0]
ctx.Data["log"] = resultLog
ctx.Data["task"] = task
ctx.Data["jobID"] = jobID
ctx.Data["result"] = result
ctx.Data["jobName"] = VersionListTasks[0].JobName
ctx.Data["version_list_task"] = VersionListTasks
ctx.Data["version_list_count"] = VersionListCount
ctx.HTML(http.StatusOK, tplModelArtsTrainJobShow)
}

func addZero(t int64) (m string) {
if t < 10 {
m = "0" + strconv.FormatInt(t, 10)
return m
} else {
return strconv.FormatInt(t, 10)
}
}

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

@@ -1160,24 +1074,34 @@ func trainJobGetLog(jobID string) (*models.GetTrainJobLogFileNamesResult, *model

func TrainJobDel(ctx *context.Context) {
var jobID = ctx.Params(":jobid")
task, err := models.GetCloudbrainByJobID(jobID)
repo := ctx.Repo.Repository

VersionListTasks, _, err := models.CloudbrainsVersionList(&models.CloudbrainsOptions{
RepoID: repo.ID,
Type: models.TypeCloudBrainTwo,
JobType: string(models.JobTypeTrain),
JobID: jobID,
})
if err != nil {
log.Error("GetCloudbrainByJobID(%s) failed:%v", task.JobName, err.Error())
ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobIndex, nil)
ctx.ServerError("get VersionListTasks failed", err)
return
}

//删除modelarts上的任务记录
_, err = modelarts.DelTrainJob(jobID)
if err != nil {
log.Error("DelTrainJob(%s) failed:%v", task.JobName, err.Error())
log.Error("DelTrainJob(%s) failed:%v", jobID, err.Error())
ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobIndex, nil)
return
}

err = models.DeleteJob(task)
if err != nil {
ctx.ServerError("DeleteJob failed", err)
return
//删除数据库Cloudbrain表的记录
for _, task := range VersionListTasks {
err = models.DeleteJob(&task.Cloudbrain)
if err != nil {
ctx.ServerError("DeleteJob failed", err)
return
}
}

ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/train-job")
@@ -1202,54 +1126,6 @@ func TrainJobStop(ctx *context.Context) {
ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/train-job")
}

func TrainJobVersionDel(ctx *context.Context) {
var jobID = ctx.Params(":jobid")
var versionName = ctx.Query(":versionName")
task, err := models.GetCloudbrainByJobIDAndVersionName(jobID, versionName)
if err != nil {
log.Error("GetCloudbrainByJobID(%s) failed:%v", task.JobName, err.Error())
ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobShow, nil)
return
}

_, err = modelarts.DelTrainJob(jobID)
if err != nil {
log.Error("DelTrainJob(%s) failed:%v", task.JobName, err.Error())
ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobShow, nil)
return
}

err = models.DeleteJob(task)
if err != nil {
ctx.ServerError("DeleteJob failed", err)
return
}

// ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/train-job")
ctx.HTML(http.StatusOK, tplModelArtsTrainJobShow)
}

func TrainJobVersionStop(ctx *context.Context) {
var jobID = ctx.Params(":jobid")
var versionName = ctx.Query(":versionName")
task, err := models.GetCloudbrainByJobIDAndVersionName(jobID, versionName)
if err != nil {
log.Error("GetCloudbrainByJobID(%s) failed:%v", task.JobName, err.Error())
ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobIndex, nil)
return
}

_, err = modelarts.StopTrainJob(jobID, strconv.FormatInt(task.VersionID, 10))
if err != nil {
log.Error("StopTrainJob(%s) failed:%v", task.JobName, err.Error())
ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobIndex, nil)
return
}

// ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/train-job")
ctx.HTML(http.StatusOK, tplModelArtsTrainJobShow)
}

func canUserCreateTrainJob(uid int64) (bool, error) {
org, err := models.GetOrgByName(setting.AllowedOrg)
if err != nil {
@@ -1313,74 +1189,3 @@ func getConfigList(perPage, page int, sortBy, order, searchContent, configType s

return list, nil
}

func TrainJobShowModels(ctx *context.Context) {
ctx.Data["PageIsCloudBrain"] = true

jobID := ctx.Params(":jobid")
parentDir := ctx.Query("parentDir")

log.Info("parentDir=" + parentDir)

dirArray := strings.Split(parentDir, "/")
task, err := models.GetCloudbrainByJobID(jobID)
if err != nil {
log.Error("no such job!", ctx.Data["msgID"])
ctx.ServerError("no such job:", err)
return
}

models, err := storage.GetObsListObject(task.JobName, parentDir)
if err != nil {
log.Info("get TrainJobListModel failed:", err)
ctx.ServerError("GetObsListObject:", err)
return
}

ctx.Data["Path"] = dirArray
ctx.Data["Dirs"] = models
ctx.Data["task"] = task
ctx.Data["JobID"] = jobID
ctx.HTML(200, tplModelArtsTrainJobShowModels)
}

func TrainJobVersionShowModels(ctx *context.Context) {
ctx.Data["PageIsCloudBrain"] = true

jobID := ctx.Params(":jobid")
parentDir := ctx.Query("parentDir")
versionName := ctx.Query("version_name")
dirArray := strings.Split(parentDir, "/")
task, err := models.GetCloudbrainByJobIDAndVersionName(jobID, versionName)
if err != nil {
log.Error("no such job!", ctx.Data["msgID"])
ctx.ServerError("no such job:", err)
return
}
parentDir = versionName
models, err := storage.GetVersionObsListObject(task.JobName, parentDir)
if err != nil {
log.Info("get TrainJobListModel failed:", err)
ctx.ServerError("GetVersionObsListObject:", err)
return
}

ctx.Data["Path"] = dirArray
ctx.Data["Dirs"] = models
ctx.Data["task"] = task
ctx.Data["JobID"] = jobID
ctx.HTML(200, tplModelArtsTrainJobShowModels)
}

func TrainJobDownloadModel(ctx *context.Context) {
parentDir := ctx.Query("parentDir")
fileName := ctx.Query("fileName")
jobName := ctx.Query("jobName")
url, err := storage.GetObsCreateSignedUrl(jobName, parentDir, fileName)
if err != nil {
log.Error("GetObsCreateSignedUrl failed: %v", err.Error(), ctx.Data["msgID"])
ctx.ServerError("GetObsCreateSignedUrl", err)
return
}
http.Redirect(ctx.Resp, ctx.Req.Request, url, http.StatusMovedPermanently)
}

+ 1
- 2
routers/repo/user_data_analysis.go View File

@@ -83,10 +83,9 @@ func QueryUserStaticDataPage(ctx *context.Context) {
if IsReturnFile {
//writer exec file.
xlsx := excelize.NewFile()
xlsx.DeleteSheet("Sheet1")
sheetName := ctx.Tr("user.static.sheetname")
index := xlsx.NewSheet(sheetName)
xlsx.DeleteSheet("Sheet1")
dataHeader := map[string]string{
"A1": ctx.Tr("user.static.id"),
"B1": ctx.Tr("user.static.name"),


+ 0
- 11
routers/routes/routes.go View File

@@ -1000,23 +1000,12 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("", reqRepoCloudBrainReader, repo.TrainJobShow)
m.Post("/stop", reqRepoCloudBrainWriter, repo.TrainJobStop)
m.Post("/del", reqRepoCloudBrainWriter, repo.TrainJobDel)
m.Get("/log", reqRepoCloudBrainReader, repo.TrainJobGetLog)
m.Get("/models", reqRepoCloudBrainReader, repo.TrainJobShowModels)
m.Get("/download_model", reqRepoCloudBrainReader, repo.TrainJobDownloadModel)
m.Get("/version_models", reqRepoCloudBrainReader, repo.TrainJobVersionShowModels)
// m.Group("/:version-name", func() {
m.Get("/create_version", reqRepoCloudBrainReader, repo.TrainJobNewVersion)
m.Post("/create_version", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateModelArtsTrainJobForm{}), repo.TrainJobCreateVersion)
// })
m.Post("/stop_version", reqRepoCloudBrainWriter, repo.TrainJobVersionStop)
m.Post("/del_version", reqRepoCloudBrainWriter, repo.TrainJobVersionDel)
})
m.Get("/create", reqRepoCloudBrainReader, repo.TrainJobNew)
m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateModelArtsTrainJobForm{}), repo.TrainJobCreate)

// m.Get("/create", reqRepoCloudBrainReader, repo.TrainJobNewVersion)
// m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateModelArtsTrainJobForm{}), repo.TrainJobCreateVersion)

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


+ 13
- 1
templates/repo/cloudbrain/show.tmpl View File

@@ -6,7 +6,19 @@
{{template "base/alert" .}}

<h4 class="ui header" id="vertical-segment">
<a href="javascript:window.history.back();"><i class="arrow left icon"></i>返回</a>
<div class="ui breadcrumb">
<a class="section" href="{{.RepoLink}}/cloudbrain">
{{.i18n.Tr "repo.cloudbrain"}}
</a>
<div class="divider"> / </div>
<a class="section" href="{{.RepoLink}}/modelarts/notebook">
{{$.i18n.Tr "repo.modelarts.notebook"}}
</a>
<div class="divider"> / </div>
{{with .task}}
<div class="active section">{{.JobName}}</div>
{{end}}
</div>
</h4>
<div>
<div class="ui yellow segment">


+ 13
- 1
templates/repo/modelarts/notebook/show.tmpl View File

@@ -6,7 +6,19 @@
{{template "base/alert" .}}

<h4 class="ui header" id="vertical-segment">
<a href="javascript:window.history.back();"><i class="arrow left icon"></i>返回</a>
<div class="ui breadcrumb">
<a class="section" href="{{.RepoLink}}/cloudbrain">
{{.i18n.Tr "repo.cloudbrain"}}
</a>
<div class="divider"> / </div>
<a class="section" href="{{.RepoLink}}/cloudbrain">
{{$.i18n.Tr "repo.modelarts.notebook"}}
</a>
<div class="divider"> / </div>
{{with .task}}
<div class="active section">{{.JobName}}</div>
{{end}}
</div>
</h4>
<div>
<div class="ui yellow segment">


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

@@ -333,7 +333,7 @@
</div>
<!-- 任务状态 -->
<div class="two wide column padding0" style="padding-left: 2.2rem !important;">
<span class="job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}">
<span class="job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}" data-version="{{.VersionName}}">
<span><i id="{{.JobID}}-icon" style="vertical-align: middle;" class="{{.Status}}"></i><span id="{{.JobID}}-text" style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span>
</span>
</div>
@@ -381,12 +381,12 @@
{{end}}
</form>
</div>
<div class="ui compact buttons">
<!-- 模型下载 -->
<!-- 模型下载 -->
<!-- <div class="ui compact buttons">
<a style="padding: 0.5rem;" class="ui basic blue button" href="{{$.Link}}/{{.JobID}}/models" target="_blank">
{{$.i18n.Tr "repo.model_download"}}
</a>
</div>
</div> -->
<!-- 删除任务 -->
<form class="ui compact buttons" id="delForm-{{.JobID}}" action="{{$.Link}}/{{.JobID}}/del" method="post">
{{$.CsrfTokenHtml}}
@@ -442,6 +442,8 @@
{{template "base/footer" .}}

<script>

console.log({{.Tasks}})
// 调试和评分新开窗口
function stop(obj) {
if (obj.style.color != "rgb(204, 204, 204)") {
@@ -491,11 +493,12 @@
$(".job-status").each((index, job) => {
const jobID = job.dataset.jobid;
const repoPath = job.dataset.repopath;
$.get(`/api/v1/repos/${repoPath}/modelarts/train-job/${jobID}`, (data) => {
const versionname = job.dataset.version
$.get(`/api/v1/repos/${repoPath}/modelarts/train-job/${jobID}?version_name=${versionname}`, (data) => {
console.log(data)
const duration = data.JobDuration
const jobID = data.JobID
let train_duration = runtime(duration)
$('#duration-'+jobID).text(train_duration)
$('#duration-'+jobID).text(duration)
})
})
@@ -508,17 +511,18 @@
$(".job-status").each((index, job) => {
const jobID = job.dataset.jobid;
const repoPath = job.dataset.repopath;
const versionname = job.dataset.version
if (job.textContent.trim() == 'IMAGE_FAILED' || job.textContent.trim() == 'SUBMIT_FAILED' || job.textContent.trim() == 'DELETE_FAILED'
|| job.textContent.trim() == 'KILLED' || job.textContent.trim() == 'COMPLETED' || job.textContent.trim() == 'FAILED'
|| job.textContent.trim() == 'CANCELED' || job.textContent.trim() == 'LOST') {
return
}

$.get(`/api/v1/repos/${repoPath}/modelarts/train-job/${jobID}`, (data) => {
$.get(`/api/v1/repos/${repoPath}/modelarts/train-job/${jobID}?version_name=${versionname}`, (data) => {
const jobID = data.JobID
const status = data.JobStatus
const duration = data.JobDuration
$('#duration-'+jobID).text(duration)
if (status != job.textContent.trim()) {
$('#' + jobID+'-icon').removeClass().addClass(status)
$('#' + jobID+ '-text').text(status)
@@ -527,8 +531,7 @@
if(status==="RUNNING"){
$('#model-debug-'+jobID).removeClass('disabled')
$('#model-debug-'+jobID).addClass('blue')
let train_duration = runtime(duration)
$('#duration-'+jobID).text(train_duration)
// $('#duration-'+jobID).text(duration)

}
if(status!=="RUNNING"){
@@ -542,7 +545,7 @@
$('#model-delete-'+jobID).removeClass('red')
$('#model-delete-'+jobID).addClass('disabled')
}
if(status=="KILLED" || status=="FAILED" || status=="KILLING"){
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')


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

@@ -103,7 +103,9 @@
-webkit-animation-delay: -0.8s;
animation-delay: -0.8s;
}
.left2{
margin-left: -2px;
}
@-webkit-keyframes sk-stretchdelay {
0%,
40%,
@@ -172,7 +174,7 @@

<div class="required unite min_title inline field">
<label>{{.i18n.Tr "repo.modelarts.code_version"}}</label>
<select class="ui dropdown width80" id="code_version" name="branch_name">
<select class="ui dropdown width80 left2" id="code_version" name="branch_name">
{{range $k, $v :=.Branches}}
<option name="branch_name" value="{{$v}}">{{$v}}</option>
{{end}}


+ 501
- 94
templates/repo/modelarts/trainjob/show.tmpl View File

@@ -22,6 +22,7 @@
vertical-align: middle;
display: inline-block;
width: calc(100% - 32px);
cursor: default;
}
.acc-margin-bottom {
margin-bottom: 5px;
@@ -55,7 +56,7 @@
margin:10px 5px ;
}
.tab_2_content {
min-height: 260px;
min-height: 360px;
margin-left: 10px;
}
.ac-grid {
@@ -83,6 +84,7 @@
.ti-text-form-label {
padding-bottom: 20px;
padding-right: 20px;
color: #8a8e99;
font-size: 12px;
white-space: nowrap;
@@ -105,38 +107,117 @@ td, th {
text-overflow: ellipsis;
white-space: nowrap;
}

.redo-color{
color: #3291F8;
}
.ti-action-menu-item:not(:last-child){
margin-right: 10px;
padding-right: 11px;
text-decoration: none!important;
color: #526ecc;
cursor: pointer;
display: inline-block;
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
-khtml-user-select: none;
user-select: none;
position: relative;
}
.ti-action-menu-item:not(:last-child):after {
content: "";
display: inline-block;
position: absolute;
height: 12px;
right: 0;
top: 50%;
-webkit-transform: translateY(-6px);
-ms-transform: translateY(-6px);
-o-transform: translateY(-6px);
transform: translateY(-6px);
border-right: 1px solid #dfe1e6;
}
.text-width80{
width: 100px;
line-height: 30px;
}
.border-according{
border: 1px solid #dfe1e6;
}
.disabled {
cursor: default;
pointer-events: none;
color: rgba(0,0,0,.6) !important;
opacity: .45 !important;
}
.pad20{
border:0px !important;
}
.model_file_bread{
margin-bottom: -0.5rem !important;
padding-left: 1rem;
padding-top: 0.5rem ;
}
</style>
<div class="repository">
{{template "repo/header" .}}
<div class="ui container">
<h4 class="ui header" id="vertical-segment">
<a href="javascript:window.history.back();"><i class="arrow left icon"></i>返回</a>
<!-- <a href="javascript:window.history.back();"><i class="arrow left icon"></i>返回</a> -->
<div class="ui breadcrumb">
<a class="section" href="{{.RepoLink}}/cloudbrain">
{{.i18n.Tr "repo.cloudbrain"}}
</a>
<div class="divider"> / </div>
<a class="section" href="{{$.RepoLink}}/modelarts/train-job">
{{$.i18n.Tr "repo.modelarts.train_job"}}
</a>
<div class="divider"> / </div>
<div class="active section">{{.jobName}}</div>
</div>
</h4>
{{range .version_list_task}}
<div class="ui accordion">
<div class="title padding0">
{{range $k ,$v := .version_list_task}}
<div class="ui accordion border-according" id="accordion{{.VersionName}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}" data-version="{{.VersionName}}">
<div class="{{if eq $k 0}}active{{end}} title padding0">
<div class="according-panel-heading">
<div class="accordion-panel-title">
<i class="dropdown icon"></i>
<span class="accordion-panel-title-content">
<span>
<div style="float: right;">
<button>创建模型</button>
<a href="{{$.RepoLink}}/modelarts/train-job/{{.JobID}}/create_version?version_name={{.VersionName}}">修改</a>
<button>停止</button>
<button>删除</button>
<!-- <a class="ti-action-menu-item {{if ne .Status "COMPLETED"}}disabled {{end}}">创建模型</a> -->
{{$.CsrfTokenHtml}}
{{if $.Permission.CanWrite $.UnitTypeCloudBrain}}
<a class="ti-action-menu-item" href="{{$.RepoLink}}/modelarts/train-job/{{.JobID}}/create_version?version_name={{.VersionName}}">{{$.i18n.Tr "repo.modelarts.modify"}}</a>
{{else}}
<a class="ti-action-menu-item disabled" href="{{$.RepoLink}}/modelarts/train-job/{{.JobID}}/create_version?version_name={{.VersionName}}">{{$.i18n.Tr "repo.modelarts.modify"}}</a>
{{end}}
{{$.CsrfTokenHtml}}
{{if $.Permission.CanWrite $.UnitTypeCloudBrain}}
<a class="ti-action-menu-item {{if eq .Status "KILLED" "FAILED" "START_FAILED" "KILLING" "COMPLETED"}}disabled {{end}}" id="{{.VersionName}}-stop" onclick="stopVersion({{.VersionName}})">{{$.i18n.Tr "repo.stop"}}</a>
{{else}}
<a class="ti-action-menu-item disabled" id="{{.VersionName}}-stop" onclick="stopVersion({{.VersionName}})">{{$.i18n.Tr "repo.stop"}}</a>
{{end}}
{{$.CsrfTokenHtml}}
{{if $.Permission.CanWrite $.UnitTypeCloudBrain}}
<a class="ti-action-menu-item" onclick="deleteVersion({{.VersionName}})" style="color: #FF4D4F;">{{$.i18n.Tr "repo.delete"}}</a>
{{else}}
<a class="ti-action-menu-item disabled" onclick="deleteVersion({{.VersionName}})" style="color: #FF4D4F;">{{$.i18n.Tr "repo.delete"}}</a>
{{end}}
</div>
<div class="ac-display-inblock title_text acc-margin-bottom">
<span class="cti-mgRight-sm">2021/11/08 19:35:19</span>
<span class="cti-mgRight-sm"> 当前版本:{{.VersionName}}</span>
<span class="cti-mgRight-sm"> 父版本:{{.FatherVersionName}}</span>
<span class="cti-mgRight-sm ac-text-normal title_text">状态
<span><i id="icon" style="vertical-align: middle;" class=""></i><span id="text" style="margin-left: 0.4em;font-size: 12px;">运行成功</span></span>
<span class="cti-mgRight-sm">{{TimeSinceUnix1 .Cloudbrain.CreatedUnix}}</span>
<span class="cti-mgRight-sm"> {{$.i18n.Tr "repo.modelarts.current_version"}}:{{.VersionName}}</span>
<span class="cti-mgRight-sm"> {{$.i18n.Tr "repo.modelarts.parent_version"}}:{{.PreVersionName}}</span>
<span class="cti-mgRight-sm ac-text-normal title_text">{{$.i18n.Tr "repo.modelarts.status"}}:
<span id="{{.VersionName}}-status-span"><i id="icon" style="vertical-align: middle;" class="{{.Status}}"></i><span id="text" style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span>
</span>
<span class="ac-text-normal title_text">运行时间:</span>
<span class="cti-mgRight-sm uc-accordionTitle-black">01:09:50</span>
<span data-tooltip="刷新" data-inverted=""><i class="redo icon"></i></span>
<span class="ac-text-normal title_text">{{$.i18n.Tr "repo.modelarts.train_job.dura_time"}}:</span>
<span class="cti-mgRight-sm uc-accordionTitle-black" id="{{.VersionName}}-duration-span">{{.TrainJobDuration}}</span>
<span data-tooltip="刷新" style="cursor: pointer;" data-inverted="" onclick="refreshStatus({{.VersionName}})"><i class="redo icon redo-color"></i></span>

</div>
</span>
@@ -144,14 +225,15 @@ td, th {
</div>
</div>
</div>
<div class="content accordion-border">
<div class="{{if eq $k 0}}active{{end}} content accordion-border">
<div class="content-pad">
<div class="ui pointing secondary menu">
<a class="active item" data-tab="first">配置信息</a>
<a class="item" data-tab="second">日志文件</a>
<a class="item" data-tab="third">模型下载</a>
<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>
<a class="item" data-tab="third{{$k}}" onclick="loadModelFile({{.VersionName}},'','','init')">{{$.i18n.Tr "repo.model_download"}}</a>
</div>
<div class="ui tab" data-tab="first">
<div class="ui tab active" data-tab="first{{$k}}">
<div style="padding-top: 10px;">
<div class="tab_2_content">
<div class="ac-grid ac-grid-col2">
@@ -159,8 +241,8 @@ td, th {
<table class="ti-form">
<tbody class="ti-text-form">
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label">
任务名称
<td class="ti-no-ng-animate ti-text-form-label text-width80">
{{$.i18n.Tr "repo.cloudbrain_task"}}
</td>

<td class="ti-text-form-content">
@@ -170,24 +252,68 @@ td, th {
</td>
</tr>
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label">
状态
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w">
{{.Status}}
</div>
</td>
<td class="ti-no-ng-animate ti-text-form-label text-width80">
{{$.i18n.Tr "repo.modelarts.status"}}
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w" id="{{.VersionName}}-status">
{{.Status}}
</div>
</td>
</tr>
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label">
开始时间
<td class="ti-no-ng-animate ti-text-form-label text-width80">
{{$.i18n.Tr "repo.modelarts.run_version"}}
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w">
aaa
{{.VersionName}}
</div>
</td>
</tr>
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
{{$.i18n.Tr "repo.modelarts.train_job.start_time"}}
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w">
<span style="font-size: 12px;" class="">{{TimeSinceUnix1 .Cloudbrain.CreatedUnix}}</span>
</div>
</td>
</tr>
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
{{$.i18n.Tr "repo.modelarts.train_job.dura_time"}}
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w" id="{{.VersionName}}-duration">
{{.TrainJobDuration}}
</div>
</td>
</tr>
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
{{$.i18n.Tr "repo.modelarts.train_job.standard"}}
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w">
{{.FlavorName}}
</div>
</td>
</tr>
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
{{$.i18n.Tr "repo.modelarts.train_job.compute_node"}}
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w">
{{.WorkServerNumber}}
</div>
</td>
</tr>
@@ -198,35 +324,79 @@ td, th {
<table class="ti-form">
<tbody class="ti-text-form">
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label">
作业名称
<td class="ti-no-ng-animate ti-text-form-label text-width80">
{{$.i18n.Tr "repo.modelarts.train_job.AI_driver"}}
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w">
trainjob-d672 | job15b681bc
{{.EngineName}}
</div>
</td>
</tr>
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label">
作业名称
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w">
trainjob-d672 | job15b681bc
</div>
</td>
<td class="ti-no-ng-animate ti-text-form-label text-width80">
{{$.i18n.Tr "repo.modelarts.code_version"}}
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w">
{{.BranchName}}
</div>
</td>
</tr>
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label">
作业名称
<td class="ti-no-ng-animate ti-text-form-label text-width80">
{{$.i18n.Tr "repo.modelarts.train_job.start_file"}}
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w">
trainjob-d672 | job15b681bc
{{.BootFile}}
</div>
</td>
</tr>
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
{{$.i18n.Tr "repo.modelarts.train_job.train_dataset"}}
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w">
{{.DatasetName}}
</div>
</td>
</tr>
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
{{$.i18n.Tr "repo.modelarts.train_job.run_parameter"}}
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w">
{{.Parameters}}
</div>
</td>
</tr>
<!-- <tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
训练输出位置
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w">
{{.TrainUrl}}
</div>
</td>
</tr> -->
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
{{$.i18n.Tr "repo.modelarts.train_job.description"}}
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w">
{{.Cloudbrain.Description}}
</div>
</td>
</tr>
@@ -238,30 +408,41 @@ td, th {
</div>
</div>
<div class="ui tab" data-tab="second">
<div class="ui tab" data-tab="second{{$k}}">
<div>
<div class="ui message" style="display: none;">
<div class="header"></div>
<div class="ui message{{.VersionName}}" style="display: none;">
<div id="header"></div>
</div>
<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>
<!-- <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" style="height: 300px !important; overflow: auto;">
<pre></pre>
<div class="ui attached segment 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>
<pre id="log_file{{.VersionName}}"></pre>
</div>
</div>
</div>
<div class="ui tab" data-tab="third">
<div class="content-pad">
asdasd
<div class="ui tab" data-tab="third{{$k}}">
<input type="hidden" name="model{{.VersionName}}" value="-1">
<input type="hidden" name="modelback{{.VersionName}}" value="-1">
<div class='ui breadcrumb model_file_bread' id='file_breadcrumb{{.VersionName}}'>
<div class="active section">{{.VersionName}}</div>
<div class="divider"> / </div>

</div>
<div id="dir_list{{.VersionName}}">
</div>
</div>

@@ -270,72 +451,298 @@ td, th {
</div>
{{end}}
</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>
console.log({{.version_list_task}})
$('.menu .item').tab()
// $('.ui.style.accordion').accordion();

// $(document).ready(function(){
// $('.ui.accordion').accordion({selector:{trigger:'.icon'}});
// });
$(document).ready(function(){
$('.ui.accordion').accordion({selector:{trigger:'.icon'}});
});
$(document).ready(function(){
$('.secondary.menu .item').tab();
});
var userName
var repoPath
var jobID
let userName
let repoPath
let jobID
$(document).ready(function(){
var url = window.location.href;
var urlArr = url.split('/')
let url = window.location.href;
let urlArr = url.split('/')
userName = urlArr.slice(-5)[0]
repoPath = urlArr.slice(-4)[0]
jobID = urlArr.slice(-1)[0]
})
function stopBubbling(e) {
e = window.event || e;
if (e.stopPropagation) {
e.stopPropagation(); //阻止事件 冒泡传播
} else {
e.cancelBubble = true; //ie兼容
}
}
// let timeid = window.setInterval(refreshStatus(version_name), 30000);
// document.ready(refreshStatus(version_name))
let timeid = window.setInterval(loadJobStatus, 30000);
$(document).ready(loadJobStatus);
$(".log").scroll(function () {
var scrollTop = $(this)[0].scrollTop; // 滚动距离
var scrollHeight = $(this)[0].scrollHeight; // 文档高度
var divHeight = $(this).height(); // 可视区高度
var file_name = $('input[name=file_name]').val()
function renderSize(value){
if(null==value||value==''){
return "0 Bytes";
}
var unitArr = new Array("Bytes","KB","MB","GB","TB","PB","EB","ZB","YB");
var index=0;
var srcsize = parseFloat(value);
index=Math.floor(Math.log(srcsize)/Math.log(1024));
var size =srcsize/Math.pow(1024,index);
size=size.toFixed(2);//保留的小数位数
return size+unitArr[index];
}
function loadJobStatus() {
$(".ui.accordion.border-according").each((index, job) => {
const jobID = job.dataset.jobid;
const repoPath = job.dataset.repopath;
const versionname = job.dataset.version
if (job.textContent.trim() == 'IMAGE_FAILED' || job.textContent.trim() == 'SUBMIT_FAILED' || job.textContent.trim() == 'DELETE_FAILED'
|| job.textContent.trim() == 'KILLED' || job.textContent.trim() == 'COMPLETED' || job.textContent.trim() == 'FAILED'
|| job.textContent.trim() == 'CANCELED' || job.textContent.trim() == 'LOST') {
return
}
let stopArray=["KILLED","FAILED","START_FAILED","KILLING","COMPLETED"]
$.get(`/api/v1/repos/${repoPath}/modelarts/train-job/${jobID}?version_name=${versionname}`, (data) => {
$(`#${versionname}-duration-span`).text(data.JobDuration)
$(`#${versionname}-status-span span`).text(data.JobStatus)
$(`#${versionname}-status-span i`).attr("class",data.JobStatus)
// detail status and duration
$('#'+versionname+'-duration').text(data.JobDuration)
$('#'+versionname+'-status').text(data.JobStatus)
if(stopArray.includes(data.JobStatus)){
$('#'+versionname+'-stop').addClass('disabled')
}
}).fail(function(err) {
console.log(err);
});
});
};

if(parseInt(scrollTop) + divHeight + 29 == scrollHeight){
var end_line = $('input[name=end_line]').val()
$.get(`/api/v1/repos/${userName}/${repoPath}/modelarts/train-job/${jobID}/log?file_name=${file_name}&base_line=${end_line}&order=desc`, (data) => {
if (data.lines == 0){
$('.header').text('您已翻阅至日志底部')
$('.message').css('display', 'block')
function refreshStatus(version_name){
$.get(`/api/v1/repos/${userName}/${repoPath}/modelarts/train-job/${jobID}?version_name=${version_name}`,(data)=>{
console.log(data)
// header status and duration
$(`#${version_name}-duration-span`).text(data.JobDuration)
$(`#${version_name}-status-span span`).text(data.JobStatus)
$(`#${version_name}-status-span i`).attr("class",data.JobStatus)
// detail status and duration
$('#'+version_name+'-duration').text(data.JobDuration)
$('#'+version_name+'-status').text(data.JobStatus)


}).fail(function(err) {
console.log(err);
});
stopBubbling(arguments.callee.caller.arguments[0])
}
function deleteVersion(version_name){
stopBubbling(arguments.callee.caller.arguments[0])
let flag = 1;
$('.ui.basic.modal').modal({
onDeny: function() {
flag = false
},
onApprove: function() {
$.post(`/api/v1/repos/${userName}/${repoPath}/modelarts/train-job/${jobID}/del_version`,{version_name:version_name},(data)=>{
$('#accordion'+version_name).remove()
}).fail(function(err) {
console.log(err);
});
flag = true
},
onHidden: function() {
if (flag == false) {
$('.alert').html('您已取消操作').removeClass('alert-success').addClass('alert-danger').show().delay(1500).fadeOut();
}
}
})
.modal('show')
}
function stopVersion(version_name){
stopBubbling(arguments.callee.caller.arguments[0])
$.post(`/api/v1/repos/${userName}/${repoPath}/modelarts/train-job/${jobID}/stop_version`,{version_name:version_name},(data)=>{
if(data.StatusOK===0){
$('#'+version_name+'-stop').addClass('disabled')
refreshStatus(version_name)
}
}).fail(function(err) {
console.log(err);
});
}
function loadLog(version_name){
$.get(`/api/v1/repos/${userName}/${repoPath}/modelarts/train-job/${jobID}/log?version_name=${version_name}&lines=20&order=asc`, (data) => {
$('input[name=end_line]').val(data.EndLine)
$('input[name=start_line]').val(data.StartLine)
$(`#log_file${version_name}`).text(data.Content)
}).fail(function(err) {
console.log(err);
});
}
function loadModelFile(version_name,parents,filename,init){
parents = parents || ''
filename = filename || ''
init = init || ''
$.get(`/api/v1/repos/${userName}/${repoPath}/modelarts/train-job/${jobID}/model_list?version_name=${version_name}&parentDir=${parents}`, (data) => {
$(`#dir_list${version_name}`).empty()
renderDir(data,version_name)
if(init==="init"){
$(`input[name=model${version_name}]`).val("")
$(`input[name=modelback${version_name}]`).val(version_name)
$(`#file_breadcrumb${version_name}`).empty()
let htmlBread = ""
htmlBread += `<div class='active section'>${version_name}</div>`
htmlBread += "<div class='divider'> / </div>"
$(`#file_breadcrumb${version_name}`).append(htmlBread)
}else{
renderBrend(version_name,parents,filename,init)
}
}).fail(function(err) {
console.log(err,version_name);
});
}
function renderBrend(version_name,parents,filename,init){
if(init=="folder"){
let htmlBrend = ""
let sectionName=$(`#file_breadcrumb${version_name} .active.section`).text()
let parents1 = $(`input[name=model${version_name}]`).val()
let filename1 = $(`input[name=modelback${version_name}]`).val()
if(parents1===""){
$(`#file_breadcrumb${version_name} .active.section`).replaceWith(`<a class='section' onclick="loadModelFile('${version_name}','${parents1}','','init')">${sectionName}</a>`)
}else{
$(`#file_breadcrumb${version_name} .active.section`).replaceWith(`<a class='section' onclick="loadModelFile('${version_name}','${parents1}','${filename1}')">${sectionName}</a>`)
}
htmlBrend += `<div class='active section'>${filename}</div>`
htmlBrend += "<div class='divider'> / </div>"
$(`#file_breadcrumb${version_name}`).append(htmlBrend)
$(`input[name=model${version_name}]`).val(parents)
$(`input[name=modelback${version_name}]`).val(filename)
}else{
$(`input[name=model${version_name}]`).val(parents)
$(`input[name=modelback${version_name}]`).val(filename)
$(`#file_breadcrumb${version_name} a.section:contains(${filename})`).nextAll().remove()
$(`#file_breadcrumb${version_name} a.section:contains(${filename})`).replaceWith(`<div class='active section'>${filename}</div>`)
$(`#file_breadcrumb${version_name} div.section:contains(${filename})`).append("<div class='divider'> / </div>")
}
}
function renderDir(data,version_name){
let html=""
html += "<div class='ui grid' style='margin:0;'>"
html += "<div class='row' style='padding: 0;'>"
html += "<div class='ui sixteen wide column' style='padding:1rem;'>"
html += "<div class='dir list'>"
html += "<table id='repo-files-table' class='ui single line table pad20'>"
html += '<tbody>'
// html += "</tbody>"
for(let i=0;i<data.Dirs.length;i++){
let dirs_size = renderSize(data.Dirs[i].Size)
html += "<tr>"
html += "<td class='name six wid'>"
html += "<span class='truncate'>"
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 += "<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 += "<span class='fitted'><i class='file icon' width='16' height='16' aria-hidden='true'></i>" + data.Dirs[i].FileName + "</span>"
}
html += '</a>'
html += "</span>"
html += "</td>"
html += "<td class='message seven wide'>"
html += "<span class='truncate has-emoji'>"+ `${dirs_size}` + "</span>"
html += "</td>"

html += "<td class='text right age three wide'>"
html += "<span class='truncate has-emoji'>" + data.Dirs[i].ModTime + "</span>"
html += "</td>"
html += "</tr>"
}
html += "</tbody>"
html += "</table>"
html += "</div>"
html += "</div>"
html += "</div>"
html += "</div>"
$(`#dir_list${version_name}`).append(html)
}
// $(`.log{}`).scroll()
function logScroll(version_name) {
let scrollTop = $(`#log${version_name}`)[0].scrollTop; // 滚动距离
let scrollHeight = $(`#log${version_name}`)[0].scrollHeight; // 文档高度
let divHeight = $(`#log${version_name}`).height(); // 可视区高度
// let version_name=$(this).find('input[name=version_name]').val()
console.log("scrollTo,scrollHeight,divHeight",scrollTop,scrollHeight,divHeight)
if(parseInt(scrollTop) + divHeight + 18 == scrollHeight){
let end_line = $(`#log${version_name} input[name=end_line]`).val()
$.get(`/api/v1/repos/${userName}/${repoPath}/modelarts/train-job/${jobID}/log?version_name=${version_name}&base_line=${end_line}&order=desc`, (data) => {
if (data.Lines == 0){
$(`.message${version_name} #header`).text('您已翻阅至日志底部')
$(`.message${version_name}`).css('display', 'block')
setTimeout(function(){
$('.message').css('display', 'none')
$(`.message${version_name}`).css('display', 'none')
}, 1000)
}else{
$('input[name=end_line]').val(data.EndLine)
$('.log').append('<pre>' + data.Content)
$(`#log${version_name} input[name=end_line]`).val(data.EndLine)
$(`#log${version_name}`).append('<pre>' + data.Content)
}
}).fail(function(err) {
console.log(err);
});
}
if(scrollTop == 0){
var start_line = $('input[name=start_line]').val()
$.get(`/api/v1/repos/${userName}/${repoPath}/modelarts/train-job/${jobID}/log?file_name=${file_name}&base_line=${start_line}&order=asc`, (data) => {
if (data.lines == 0){
$('.header').text('您已翻阅至日志顶部')
$('.message').css('display', 'block')
let start_line = $(`#log${version_name} input[name=start_line]`).val()
$.get(`/api/v1/repos/${userName}/${repoPath}/modelarts/train-job/${jobID}/log?version_name=${version_name}&base_line=${start_line}&order=asc`, (data) => {
if (data.Lines == 0){
$(`.message${version_name} #header`).text('您已翻阅至日志顶部')
$(`.message${version_name}`).css('display', 'block')
setTimeout(function(){
$('.message').css('display', 'none')
$(`.message${version_name}`).css('display', 'none')
}, 1000)
}else{
$('input[name=start_line]').val(data.StartLine) //如果变动就改变所对应的值
$(".log").prepend('<pre>' + data.Content)
$(`#log${version_name} input[name=start_line]`).val(data.StartLine) //如果变动就改变所对应的值
$(`#log${version_name}`).prepend('<pre>' + data.Content)
}
}).fail(function(err) {
console.log(err);
});
}
})
}
</script>

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

@@ -156,15 +156,24 @@
<form class="ui form" action="{{$.RepoLink}}/modelarts/train-job/{{.JobID}}/create_version" method="post">
{{.CsrfTokenHtml}}
<input type="hidden" name="action" value="update">
<input type="hidden" name="version_name" value="">
<input type="hidden" id="ai_engine_name" name="engine_names" value="">
<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>
<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>
<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>
<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)"></textarea>
<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>

@@ -198,7 +207,7 @@
</select>
</div>

<div class="field" style="flex: 2;">
<div class="field" style="flex: 2;" id="engine_name">
<select class="ui dropdown width" id="trainjob_engine_versions" style='width: 100%;' name="engine_id">
{{if .engine_id}}
<option name="engine_id" value="{{.engine_id}}">{{.engine_name}}</option>
@@ -290,7 +299,7 @@
</div>
</div>

<div class="required unite min_title inline field">
<div class="required unite min_title inline field" id="flaver_name">
<label>{{.i18n.Tr "repo.modelarts.train_job.standard"}}</label>
<select class="ui dropdown width81" id="trainjob-flavor" style='width:385px' name="flavor">
{{if .flavor_name}}
@@ -331,9 +340,12 @@

<script>
let url_href = {{.RepoLink}}+'/modelarts/train-job'
let url_post = window.location.pathname.split('?version_name=V0001')[0]
let url_post = location.pathname
let version_name = location.search.split('?version_name=')[1]
$("#parents_version").val(version_name)
$(".ui.button").attr('href',url_href)
$(".ui.form").attr('action',url_post)
$("input[name=version_name]").attr('value',version_name)
$('select.dropdown')
.dropdown();

@@ -397,7 +409,6 @@
})
});
console.log(parameters)
$('.ui.parameter.modal')
.modal('hide');
for(var i = 2; i < parameters.length; i++){
@@ -505,7 +516,16 @@
prompt : '计算节点需要在1-25之间,请您键入正确的值'
}
]
}
},
run_para_list:{
identifier : 'run_para_list',
rules: [
{
type: 'maxLength[256]',
prompt : '所有字符最长不超过256个字符。'
}
]
},
},
})

@@ -553,7 +573,16 @@
prompt : '计算节点需要在1-25之间,请您键入正确的值'
}
]
}
},
run_para_list:{
identifier : 'run_para_list',
rules: [
{
type: 'maxLength[256]',
prompt : '所有字符最长不超过256个字符。'
}
]
},
},
onSuccess: function(){
// $('.ui.page.dimmer').dimmer('show')
@@ -582,8 +611,17 @@
msg = JSON.stringify(msg)
$('#store_run_para').val(msg)
}
function get_name(){
let name1=$("#engine_name .text").text()
let name2=$("#flaver_name .text").text()
console.log(name1,name2)
$("input#ai_engine_name").val(name1)
$("input#ai_flaver_name").val(name2)

}

$('.ui.create_train_job.green.button').click(function(e) {
get_name()
send_run_para()
validate()
})

+ 35
- 9
web_src/js/components/ProAnalysis.vue View File

@@ -8,13 +8,13 @@
<bar-label :width="'95%'" :height="'500px'"></bar-label>
<div style="margin-top: 20px;">
<span class="sta_iterm">统计周期:</span>
<button type="button" class='btn' id ="yesterday" v-bind:class="{colorChange:1==dynamic}" @click="resetPage(),getAllProList('yesterday',1)">昨天</button>
<button type="button" class='btnFirst' id ="yesterday" v-bind:class="{colorChange:1==dynamic}" @click="resetPage(),getAllProList('yesterday',1)">昨天</button>
<button type="button" class='btn' id = "current_week" v-bind:class="{colorChange:2==dynamic}" @click="resetPage(),getAllProList('current_week',2)">本周</button>
<button type="button" class='btn' id = "current_month" v-bind:class="{colorChange:3==dynamic}" @click="resetPage(),getAllProList('current_month',3)">本月</button>
<button type="button" class='btn' id = "last_month" v-bind:class="{colorChange:4==dynamic}" @click="resetPage(),getAllProList('last_month',4)">上月</button>
<button type="button" class='btn' id = "monthly" v-bind:class="{colorChange:5==dynamic}" @click="resetPage(),getAllProList('monthly',5)">近30天</button>
<button type="button" class='btn' id = "current_year" v-bind:class="{colorChange:6==dynamic}" @click="resetPage(),getAllProList('current_year',6)">今年</button>
<button type="button" class='btn' id = "all" v-bind:class="{colorChange:7==dynamic}" @click="resetPage(),getAllProList('all',7)">所有</button>
<button type="button" class='btnLast' id = "all" v-bind:class="{colorChange:7==dynamic}" @click="resetPage(),getAllProList('all',7)">所有</button>
<span style="margin-left: 20px;">
<el-date-picker
v-model="value_time"
@@ -160,7 +160,7 @@
</div>
<div id ="pro_detail" style="display:none;width: 100%;">
<div style="margin-top: 10px;">
<b class="pro_item">{{this.ownerName}}&nbsp/&nbsp{{this.pro_name}}</b> <span class="update_time">数据更新时间:</span><span style="font-size: 12px;">{{tableDataIDTotal.lastUpdatedTime}}&nbsp/&nbsp从{{tableDataIDTotal.recordBeginTime}}开始统计</span>
<a class="pro_item" :href="'../../../'+this.ownerName+'/'+this.pro_name">{{this.ownerName}}&nbsp/&nbsp{{this.pro_name}}</a> <span class="update_time">数据更新时间:</span><span style="font-size: 12px;">{{tableDataIDTotal.lastUpdatedTime}}&nbsp/&nbsp从{{tableDataIDTotal.recordBeginTime}}开始统计</span>
</div>
<div style="margin-top: 10px;">
项目描述:{{tableDataIDTotal.description | discriptionFun}}
@@ -207,7 +207,7 @@
</el-col>
<el-col :span=6 >
<div class="item_r">
<div style="font-size:14px;color:rgb(0,0,0);margin:20px 5px;">贡献者TOP10</div>
<div style="font-size:14px;color:rgb(0,0,0);margin:20px 0px">贡献者TOP10</div>
<div>
<el-table
:data="tableDataContTop10"
@@ -220,7 +220,7 @@
align="left"
prop="user">
<template slot-scope="scope">
<a v-if="scope.row.mode!=-1" :href="AppSubUrl +'../../../'+ scope.row.user"><img class="ui avatar s16 image js-popover-card" :src="scope.row.relAvatarLink">{{scope.row.user}} </a>
<a v-if="scope.row.relAvatarLink!=''" :href="AppSubUrl +'../../../'+ scope.row.user"><img class="ui avatar s16 image js-popover-card" :src="scope.row.relAvatarLink">{{scope.row.user}} </a>
<a v-else :href="'mailto:'+ scope.row.email "> <img class="ui avatar s16 image js-popover-card" :avatar="scope.row.email"> {{scope.row.user}}</a>
</template>
</el-table-column>
@@ -252,13 +252,13 @@
</div>
<div style="margin-top: 20px;">
<span class="sta_iterm">统计周期:</span>
<button type="button" class='btn' id ="yesterday_pro" v-bind:class="{colorChange:1==dynamic_pro}" @click="resetCurrentPage(),getOneProList(pro_id,'yesterday',false,1)">昨天</button>
<button type="button" class='btnFirst' id ="yesterday_pro" v-bind:class="{colorChange:1==dynamic_pro}" @click="resetCurrentPage(),getOneProList(pro_id,'yesterday',false,1)">昨天</button>
<button type="button" class='btn' id = "current_week_pro" v-bind:class="{colorChange:2==dynamic_pro}" @click="resetCurrentPage(),getOneProList(pro_id,'current_week',false,2)">本周</button>
<button type="button" class='btn' id = "current_month_pro" v-bind:class="{colorChange:3==dynamic_pro}" @click="resetCurrentPage(),getOneProList(pro_id,'current_month',false,3)">本月</button>
<button type="button" class='btn' id = "last_month_pro" v-bind:class="{colorChange:4==dynamic_pro}" @click="resetCurrentPage(),getOneProList(pro_id,'last_month',false,4)">上月</button>
<button type="button" class='btn' id = "monthly_pro" v-bind:class="{colorChange:5==dynamic_pro}" @click="resetCurrentPage(),getOneProList(pro_id,'monthly',false,5)">近30天</button>
<button type="button" class='btn' id = "current_year_pro" v-bind:class="{colorChange:6==dynamic_pro}" @click="resetCurrentPage(),getOneProList(pro_id,'current_year',false,6)">今年</button>
<button type="button" class='btn' id = "all_pro" v-bind:class="{colorChange:7==dynamic_pro}" @click="resetCurrentPage(),getOneProList(pro_id,'all',false,7)">所有</button>
<button type="button" class='btnLast' id = "all_pro" v-bind:class="{colorChange:7==dynamic_pro}" @click="resetCurrentPage(),getOneProList(pro_id,'all',false,7)">所有</button>
<span style="margin-left: 20px;">
<el-date-picker
v-model="create_time_pro"
@@ -788,6 +788,10 @@
},
slpitNumber:5,
center: ['50%', '50%'],
splitArea: { // 坐标轴在 grid 区域中的分隔区域
show: false,
},

indicator: [{
name: '社区影响力',
max: 100
@@ -1170,15 +1174,37 @@
color:rgba(187, 187, 187, 100);
margin-left: 10px;
}
.btnFirst{
line-height: 1.5;
margin: -3.5px;
border: 1px solid #409eff;
border-right: none;
background: #FFFF;
color: #409eff;
width: 60px;
height: 30px;
border-radius:4px 0px 0px 4px;
}
.btn{
line-height: 1.5;
margin: -3px;
margin: -3.5px;
border: 1px solid #409eff;
border-right: none;
background: #FFFF;
color: #409eff;
width: 60px;
height: 30px;
}
.btnLast{
line-height: 1.5;
margin: -3.5px;
border: 1px solid #409eff;
/* border-right: none; */
background: #FFFF;
color: #409eff;
width: 60px;
height: 30px;
border-radius:4px ;
border-radius:0px 4px 4px 0px;
}
/*
.btn:focus,


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

@@ -5,13 +5,13 @@
</div>
<div style="margin-top: 20px;">
<span class="sta_iterm">统计周期:</span>
<button type="button" class='btn' id ="yesterday_usr" v-bind:class="{colorChange:1==dynamic}" @click="resetPage(),getUserList('yesterday_usr',1)">昨天</button>
<button type="button" class='btnFirst' id ="yesterday_usr" v-bind:class="{colorChange:1==dynamic}" @click="resetPage(),getUserList('yesterday_usr',1)">昨天</button>
<button type="button" class='btn' id = "current_week_usr" v-bind:class="{colorChange:2==dynamic}" @click="resetPage(),getUserList('current_week_usr',2)">本周</button>
<button type="button" class='btn' id = "current_month_usr" v-bind:class="{colorChange:3==dynamic}" @click="resetPage(),getUserList('current_month_usr',3)">本月</button>
<button type="button" class='btn' id = "last_month_usr" v-bind:class="{colorChange:4==dynamic}" @click="resetPage(),getUserList('last_month_usr',4)">上月</button>
<button type="button" class='btn' id = "monthly_usr" v-bind:class="{colorChange:5==dynamic}" @click="resetPage(),getUserList('monthly_usr',5)">近30天</button>
<button type="button" class='btn' id = "current_year_usr" v-bind:class="{colorChange:6==dynamic}" @click="resetPage(),getUserList('current_year_usr',6)">今年</button>
<button type="button" class='btn' id = "all_usr" v-bind:class="{colorChange:7==dynamic}" @click="resetPage(),getUserList('all_usr',7)">所有</button>
<button type="button" class='btnLast' id = "all_usr" v-bind:class="{colorChange:7==dynamic}" @click="resetPage(),getUserList('all_usr',7)">所有</button>
<span style="margin-left: 20px;">
<el-date-picker
v-model="value_time"
@@ -462,7 +462,7 @@
color:rgba(187, 187, 187, 100);
margin-left: 10px;
}
.btn{
/* .btn{
line-height: 1.5;
margin: -3px;
border: 1px solid #409effd6;
@@ -471,6 +471,38 @@
width: 60px;
height: 30px;
border-radius:4px ;
} */
.btnFirst{
line-height: 1.5;
margin: -3.5px;
border: 1px solid #409eff;
border-right: none;
background: #FFFF;
color: #409eff;
width: 60px;
height: 30px;
border-radius:4px 0px 0px 4px;
}
.btn{
line-height: 1.5;
margin: -3.5px;
border: 1px solid #409eff;
border-right: none;
background: #FFFF;
color: #409eff;
width: 60px;
height: 30px;
}
.btnLast{
line-height: 1.5;
margin: -3.5px;
border: 1px solid #409eff;
/* border-right: none; */
background: #FFFF;
color: #409eff;
width: 60px;
height: 30px;
border-radius:0px 4px 4px 0px;
}

/* .btn:focus,


+ 0
- 298
web_src/js/index.js View File

@@ -2785,67 +2785,6 @@ $(document).ready(async () => {
}
});
}

// dataset Dropzone
// const $dataset = $('#dataset');
// if ($dataset.length > 0) {
// const filenameDict = {};
// let previewTemplate = '';
// previewTemplate += '<div class="dz-preview dz-file-preview">\n ';
// previewTemplate += ' <div class="dz-details">\n ';
// previewTemplate += ' <div class="dz-filename">';
// previewTemplate += ' <span data-dz-name data-dz-thumbnail></span>';
// previewTemplate += ' </div>\n ';
// previewTemplate += ' <div class="dz-size" data-dz-size></div>\n ';
// previewTemplate += ' </div>\n ';
// previewTemplate += ' <div class="dz-progress ui active progress">';
// previewTemplate += ' <div class="dz-upload bar" data-dz-uploadprogress><div class="progress"></div></div>\n ';
// previewTemplate += ' </div>\n ';
// previewTemplate += ' <div class="dz-success-mark">';
// previewTemplate += ' <span>上传成功</span>';
// previewTemplate += ' </div>\n ';
// previewTemplate += ' <div class="dz-error-mark">';
// previewTemplate += ' <span>上传失败</span>';
// previewTemplate += ' </div>\n ';
// previewTemplate += ' <div class="dz-error-message">';
// previewTemplate += ' <span data-dz-errormessage></span>';
// previewTemplate += ' </div>\n';
// previewTemplate += '</div>';

// await createDropzone('#dataset', {
// url: $dataset.data('upload-url'),
// headers: {'X-Csrf-Token': csrf},
// maxFiles: $dataset.data('max-file'),
// maxFilesize: $dataset.data('max-size'),
// acceptedFiles: ($dataset.data('accepts') === '*/*') ? null : $dataset.data('accepts'),
// addRemoveLinks: true,
// timeout: 0,
// dictDefaultMessage: $dataset.data('default-message'),
// dictInvalidFileType: $dataset.data('invalid-input-type'),
// dictFileTooBig: $dataset.data('file-too-big'),
// dictRemoveFile: $dataset.data('remove-file'),
// previewTemplate,
// init() {
// this.on('success', (file, data) => {
// filenameDict[file.name] = data.uuid;
// const input = $(`<input id="${data.uuid}" name="files" type="hidden">`).val(data.uuid);
// $('.files').append(input);
// });
// this.on('removedfile', (file) => {
// if (file.name in filenameDict) {
// $(`#${filenameDict[file.name]}`).remove();
// }
// if ($dataset.data('remove-url') && $dataset.data('csrf')) {
// $.post($dataset.data('remove-url'), {
// file: filenameDict[file.name],
// _csrf: $dataset.data('csrf')
// });
// }
// });
// },
// });
// }

// Helpers.
$('.delete-button').on('click', showDeletePopup);
$('.add-all-button').on('click', showAddAllPopup);
@@ -3984,243 +3923,6 @@ function initNavbarContentToggle() {
});
}

// function initTopicbar() {
// const mgrBtn = $('#manage_topic');
// const editDiv = $('#topic_edit');
// const viewDiv = $('#repo-topics');
// const saveBtn = $('#save_topic');
// const topicDropdown = $('#topic_edit .dropdown');
// const topicForm = $('#topic_edit.ui.form');
// const topicInput = $("#topics_input")
// const topicPrompts = getPrompts();
// mgrBtn.on('click', (e) => {
// // viewDiv.hide();
// editDiv.css('display', ''); // show Semantic UI Grid
// topicInput.val('')
// console.log("-----------------asdasd",$("#topics_input"),$("#topics_input").val())
// stopPropagation(e);
// });
// $(document).bind('click',function(){
// editDiv.css('display','none');

// })
// editDiv.click(function(e){
// stopPropagation(e);
// })

// function getPrompts() {
// const hidePrompt = $('div.hide#validate_prompt');
// const prompts = {
// countPrompt: hidePrompt.children('#count_prompt').text(),
// formatPrompt: hidePrompt.children('#format_prompt').text()
// };
// hidePrompt.remove();
// return prompts;
// }

// function stopPropagation(e) {
// var ev = e || window.event;
// if (ev.stopPropagation) {
// ev.stopPropagation();
// }
// else if (window.event) {
// window.event.cancelBubble = true;//兼容IE
// }
// }


// saveBtn.on('click', () => {
// const topics = $('input[name=topics]').val();

// $.post(
// saveBtn.data('link'),
// {
// _csrf: csrf,
// topics
// },
// (_data, _textStatus, xhr) => {
// if (xhr.responseJSON.status === 'ok') {
// console.log("--------saveBtn------------")
// viewDiv.children('.topic').remove();
// if (topics.length) {
// const topicArray = topics.split(',');

// const last = viewDiv.children('a').last();
// for (let i = 0; i < topicArray.length; i++) {
// const link = $('<a class="ui repo-topic small label topic"></a>');
// link.attr(
// 'href',
// `${AppSubUrl}/explore/repos?q=${encodeURIComponent(
// topicArray[i]
// )}&topic=1`
// );
// link.text(topicArray[i]);
// link.insertBefore(last);
// }
// }
// editDiv.css('display', 'none');
// viewDiv.show();
// }
// }
// )
// .fail((xhr) => {
// if (xhr.status === 422) {
// if (xhr.responseJSON.invalidTopics.length > 0) {
// topicPrompts.formatPrompt = xhr.responseJSON.message;

// const {invalidTopics} = xhr.responseJSON;
// const topicLables = topicDropdown.children('a.ui.label');

// topics.split(',').forEach((value, index) => {
// for (let i = 0; i < invalidTopics.length; i++) {
// if (invalidTopics[i] === value) {
// topicLables
// .eq(index)
// .removeClass('green')
// .addClass('red');
// }
// }
// });
// } else {
// topicPrompts.countPrompt = xhr.responseJSON.message;
// }
// }
// })
// .always(() => {
// topicForm.form('validate form');
// });
// });

// topicDropdown.dropdown({
// allowAdditions: true,
// forceSelection: false,
// fields: {name: 'description', value: 'data-value'},
// saveRemoteData: false,
// label: {
// transition: 'horizontal flip',
// duration: 200,
// variation: false,
// blue: true,
// basic: true
// },
// className: {
// label: 'ui small label'
// },
// apiSettings: {
// url: `${AppSubUrl}/api/v1/topics/search?q={query}`,
// throttle: 500,
// cache: false,
// onResponse(res) {
// const formattedResponse = {
// success: false,
// results: []
// };
// const stripTags = function (text) {
// return text.replace(/<[^>]*>?/gm, '');
// };

// const query = stripTags(this.urlData.query.trim());
// let found_query = false;
// const current_topics = [];
// topicDropdown
// .find('div.label.visible.topic,a.label.visible')
// .each((_, e) => {
// current_topics.push(e.dataset.value);
// });

// if (res.topics) {
// let found = false;
// for (let i = 0; i < res.topics.length; i++) {
// // skip currently added tags
// if (current_topics.includes(res.topics[i].topic_name)) {
// continue;
// }

// if (
// res.topics[i].topic_name.toLowerCase() === query.toLowerCase()
// ) {
// found_query = true;
// }
// formattedResponse.results.push({
// description: res.topics[i].topic_name,
// 'data-value': res.topics[i].topic_name
// });
// found = true;
// }
// formattedResponse.success = found;
// }

// if (query.length > 0 && !found_query) {
// formattedResponse.success = true;
// formattedResponse.results.unshift({
// description: query,
// 'data-value': query
// });
// } else if (query.length > 0 && found_query) {
// formattedResponse.results.sort((a, b) => {
// if (a.description.toLowerCase() === query.toLowerCase()) return -1;
// if (b.description.toLowerCase() === query.toLowerCase()) return 1;
// if (a.description > b.description) return -1;
// if (a.description < b.description) return 1;
// return 0;
// });
// }

// return formattedResponse;
// }
// },
// onLabelCreate(value) {
// value = value.toLowerCase().trim();
// this.attr('data-value', value)
// .contents()
// .first()
// .replaceWith(value);
// return $(this);
// },
// onAdd(addedValue, _addedText, $addedChoice) {
// addedValue = addedValue.toLowerCase().trim();
// $($addedChoice).attr('data-value', addedValue);
// $($addedChoice).attr('data-text', addedValue);
// }
// });

// $.fn.form.settings.rules.validateTopic = function (_values, regExp) {
// const topics = topicDropdown.children('a.ui.label');
// const status =
// topics.length === 0 || (topics.last().attr('data-value').match(regExp) !== null && topics.last().attr('data-value').length <= 35);
// if (!status) {
// topics
// .last()
// .removeClass('green')
// .addClass('red');
// }
// return status && topicDropdown.children('a.ui.label.red').length === 0;
// };

// topicForm.form({
// on: 'change',
// inline: true,
// fields: {
// topics: {
// identifier: 'topics',
// rules: [
// {
// type: 'validateTopic',
// value: /^[\u4e00-\u9fa5a-z0-9][\u4e00-\u9fa5a-z0-9-]{0,105}$/,
// prompt: topicPrompts.formatPrompt
// },
// {
// type: 'maxCount[25]',
// prompt: topicPrompts.countPrompt
// }
// ]
// }
// }
// });
// }

window.toggleDeadlineForm = function () {
$('#deadlineForm').fadeToggle(150);


Loading…
Cancel
Save