#4021 #4015 模型广场及模型统一存储相关功能合入

Merged
ychao_1983 merged 181 commits from zouap into V20230410 1 year ago
  1. +265
    -42
      models/ai_model_manage.go
  2. +33
    -3
      models/cloudbrain.go
  3. +2
    -0
      models/models.go
  4. +22
    -1
      models/repo.go
  5. +3
    -1
      modules/auth/cloudbrain.go
  6. +2
    -0
      modules/auth/grampus.go
  7. +4
    -1
      modules/auth/modelarts.go
  8. +11
    -12
      modules/cloudbrain/cloudbrain.go
  9. +4
    -4
      modules/convert/cloudbrain.go
  10. +19
    -10
      modules/grampus/grampus.go
  11. +6
    -0
      modules/modelarts/modelarts.go
  12. +1
    -0
      modules/modelarts_cd/modelarts.go
  13. +3
    -3
      modules/setting/setting.go
  14. +4
    -0
      modules/storage/local.go
  15. +4
    -0
      modules/storage/minio.go
  16. +33
    -1
      modules/storage/obs.go
  17. +4
    -2
      modules/storage/storage.go
  18. +4
    -0
      modules/structs/cloudbrain.go
  19. +6
    -3
      options/locale/locale_en-US.ini
  20. +6
    -3
      options/locale/locale_zh-CN.ini
  21. +1
    -1
      public/home/home.js
  22. +57
    -0
      public/img/empty-box.svg
  23. +4
    -2
      routers/admin/dataset.go
  24. +189
    -0
      routers/admin/modelmanage.go
  25. +14
    -14
      routers/repo/ai_model_convert.go
  26. +282
    -183
      routers/repo/ai_model_manage.go
  27. +515
    -0
      routers/repo/ai_model_square.go
  28. +13
    -6
      routers/repo/aisafety.go
  29. +1
    -1
      routers/repo/attachment_model.go
  30. +77
    -20
      routers/repo/cloudbrain.go
  31. +7
    -3
      routers/repo/grampus.go
  32. +22
    -3
      routers/repo/modelarts.go
  33. +3
    -3
      routers/repo/user_data_analysis.go
  34. +24
    -4
      routers/routes/routes.go
  35. +51
    -3
      services/cloudbrain/cloudbrainTask/inference.go
  36. +5
    -4
      services/cloudbrain/cloudbrainTask/notebook.go
  37. +19
    -3
      services/cloudbrain/cloudbrainTask/train.go
  38. +2
    -2
      services/cloudbrain/modelmanage/model_manage.go
  39. +97
    -0
      templates/admin/model/list.tmpl
  40. +34
    -0
      templates/admin/model/search.tmpl
  41. +3
    -0
      templates/admin/navbar.tmpl
  42. +1
    -1
      templates/base/footer_content.tmpl
  43. +2
    -0
      templates/base/head_navbar.tmpl
  44. +2
    -0
      templates/base/head_navbar_fluid.tmpl
  45. +2
    -0
      templates/base/head_navbar_home.tmpl
  46. +2
    -0
      templates/base/head_navbar_pro.tmpl
  47. +5
    -0
      templates/model/square/index.tmpl
  48. +6
    -1
      templates/repo/cloudbrain/benchmark/new.tmpl
  49. +11
    -5
      templates/repo/cloudbrain/inference/new.tmpl
  50. +27
    -48
      templates/repo/cloudbrain/new.tmpl
  51. +6
    -1
      templates/repo/cloudbrain/trainjob/new.tmpl
  52. +2
    -1
      templates/repo/cloudbrain/trainjob/show.tmpl
  53. +7
    -1
      templates/repo/grampus/notebook/gcu/new.tmpl
  54. +6
    -1
      templates/repo/grampus/notebook/gpu/new.tmpl
  55. +6
    -1
      templates/repo/grampus/notebook/npu/new.tmpl
  56. +1
    -1
      templates/repo/grampus/notebook/show.tmpl
  57. +8
    -1
      templates/repo/grampus/trainjob/gpu/new.tmpl
  58. +7
    -2
      templates/repo/grampus/trainjob/npu/new.tmpl
  59. +2
    -1
      templates/repo/grampus/trainjob/show.tmpl
  60. +14
    -2
      templates/repo/modelarts/inferencejob/index.tmpl
  61. +9
    -2
      templates/repo/modelarts/inferencejob/new.tmpl
  62. +6
    -1
      templates/repo/modelarts/notebook/new.tmpl
  63. +1
    -1
      templates/repo/modelarts/notebook/show.tmpl
  64. +6
    -1
      templates/repo/modelarts/trainjob/new.tmpl
  65. +2
    -1
      templates/repo/modelarts/trainjob/show.tmpl
  66. +6
    -1
      templates/repo/modelarts/trainjob/version_new.tmpl
  67. +2
    -2
      templates/repo/modelmanage/create_local.tmpl
  68. +0
    -11
      templates/repo/modelmanage/create_local_2.tmpl
  69. +7
    -2
      templates/repo/modelmanage/create_online.tmpl
  70. +5
    -0
      templates/repo/modelmanage/evolution_map.tmpl
  71. +5
    -0
      templates/repo/modelmanage/filelist.tmpl
  72. +6
    -0
      templates/repo/modelmanage/fileupload.tmpl
  73. +1
    -1
      templates/repo/modelmanage/index.tmpl
  74. +5
    -0
      templates/repo/modelmanage/readme.tmpl
  75. +6
    -0
      templates/repo/modelmanage/setting.tmpl
  76. +6
    -1
      templates/repo/modelsafety/new.tmpl
  77. +5
    -20
      web_src/js/components/Model.vue
  78. +691
    -0
      web_src/js/components/model/ModelSelect.vue
  79. +17
    -15
      web_src/js/features/cloudrbanin.js
  80. +30
    -0
      web_src/js/features/i18nVue.js
  81. +10
    -0
      web_src/js/index.js
  82. +1
    -0
      web_src/js/standalone/cloudbrainNew.js
  83. +49
    -0
      web_src/vuepages/apis/modules/modelmanage.js
  84. +44
    -0
      web_src/vuepages/apis/modules/modelsquare.js
  85. +22
    -0
      web_src/vuepages/components/NotFound.vue
  86. +563
    -0
      web_src/vuepages/components/cloudbrain/ModelSelect.vue
  87. +50
    -0
      web_src/vuepages/langs/config/en-US.js
  88. +50
    -0
      web_src/vuepages/langs/config/zh-CN.js
  89. +65
    -32
      web_src/vuepages/pages/dataset/square/components/PublicDataset.vue
  90. +28
    -12
      web_src/vuepages/pages/dataset/square/index.vue
  91. +0
    -715
      web_src/vuepages/pages/modelmanage/common/modelmanage-common-detail.vue
  92. +263
    -0
      web_src/vuepages/pages/modelmanage/components/ModelHeader.vue
  93. +424
    -0
      web_src/vuepages/pages/modelmanage/files/index.vue
  94. +1
    -1
      web_src/vuepages/pages/modelmanage/files/vp-model-files.js
  95. +124
    -110
      web_src/vuepages/pages/modelmanage/fileupload/index.vue
  96. +1
    -1
      web_src/vuepages/pages/modelmanage/fileupload/vp-model-fileupload.js
  97. +111
    -0
      web_src/vuepages/pages/modelmanage/graph/index.vue
  98. +290
    -0
      web_src/vuepages/pages/modelmanage/graph/model-graph.css
  99. +496
    -0
      web_src/vuepages/pages/modelmanage/graph/model-graph.js
  100. +1
    -1
      web_src/vuepages/pages/modelmanage/graph/vp-model-graph.js

+ 265
- 42
models/ai_model_manage.go View File

@@ -6,6 +6,7 @@ import (

"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/builder"
"xorm.io/xorm"
@@ -25,6 +26,7 @@ type AiModelManage struct {
Path string `xorm:"varchar(400) NOT NULL" json:"path"`
DownloadCount int `xorm:"NOT NULL DEFAULT 0" json:"downloadCount"`
Engine int64 `xorm:"NOT NULL DEFAULT 0" json:"engine"`
ComputeResource string `json:"computeResource"`
Status int `xorm:"NOT NULL DEFAULT 0" json:"status"`
StatusDesc string `xorm:"varchar(500)" json:"statusDesc"`
Accuracy string `xorm:"varchar(1000)" json:"accuracy"`
@@ -32,16 +34,42 @@ type AiModelManage struct {
RepoId int64 `xorm:"INDEX NULL" json:"repoId"`
CodeBranch string `xorm:"varchar(400) NULL" json:"codeBranch"`
CodeCommitID string `xorm:"NULL" json:"codeCommitID"`
Recommend int `xorm:"NOT NULL DEFAULT 0" json:"recommend"`
UserId int64 `xorm:"NOT NULL" json:"userId"`
IsPrivate bool `xorm:"DEFAULT true" json:"isPrivate"`
UserName string `json:"userName"`
UserRelAvatarLink string `json:"userRelAvatarLink"`
UserName string `xorm:"-" json:"userName"`
UserRelAvatarLink string `xorm:"-" json:"userRelAvatarLink"`
TrainTaskInfo string `xorm:"text NULL" json:"trainTaskInfo"`
CreatedUnix timeutil.TimeStamp `xorm:"created" json:"createdUnix"`
UpdatedUnix timeutil.TimeStamp `xorm:"updated" json:"updatedUnix"`
IsCanOper bool `json:"isCanOper"`
IsCanDelete bool `json:"isCanDelete"`
IsCanDownload bool `json:"isCanDownload"`
IsCanOper bool `xorm:"-" json:"isCanOper"`
IsCanDelete bool `xorm:"-" json:"isCanDelete"`
IsCanDownload bool `xorm:"-" json:"isCanDownload"`
IsCollected bool `xorm:"-" json:"isCollected"`
RepoName string `xorm:"-" json:"repoName"`
RepoDisplayName string `xorm:"-" json:"repoDisplayName"`
RepoOwnerName string `xorm:"-" json:"repoOwnerName"`
ReferenceCount int `xorm:"NOT NULL DEFAULT 0" json:"referenceCount"`
CollectedCount int `xorm:"NOT NULL DEFAULT 0" json:"collectedCount"`
ModelFileList []storage.FileInfo `xorm:"-" json:"modelFileList"`
}

type AiModelFile struct {
ID int64 `xorm:"pk autoincr"`
ModelID string `xorm:"UNIQUE(s)"`
Name string `xorm:"varchar(400) UNIQUE(s)"`
Path string `xorm:"varchar(400) NULL"`
Description string `xorm:"varchar(400) NULL"`
DownloadCount int64 `xorm:"DEFAULT 0"`
Size int64 `xorm:"DEFAULT 0"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
}

type AiModelCollect struct {
ID int64 `xorm:"pk autoincr"`
ModelID string `xorm:"UNIQUE(s)"`
UserId int64 `xorm:"UNIQUE(s)"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
}

type AiModelConvert struct {
@@ -72,10 +100,10 @@ type AiModelConvert struct {
UpdatedUnix timeutil.TimeStamp `xorm:"updated" json:"updatedUnix"`
StartTime timeutil.TimeStamp `json:"startTime"`
EndTime timeutil.TimeStamp `json:"endTime"`
UserName string `json:"userName"`
UserRelAvatarLink string `json:"userRelAvatarLink"`
IsCanOper bool `json:"isCanOper"`
IsCanDelete bool `json:"isCanDelete"`
UserName string `xorm:"-" json:"userName"`
UserRelAvatarLink string `xorm:"-" json:"userRelAvatarLink"`
IsCanOper bool `xorm:"-" json:"isCanOper"`
IsCanDelete bool `xorm:"-" json:"isCanDelete"`
}

type AiModelQueryOptions struct {
@@ -86,10 +114,18 @@ type AiModelQueryOptions struct {
SortType string
New int
// JobStatus CloudbrainStatus
Type int
Status int
IsOnlyThisRepo bool
IsQueryPrivate bool
Type int
Status int
IsOnlyThisRepo bool
IsQueryPrivate bool
IsRecommend bool
IsCollected bool
CollectedUserId int64
Namelike string
LabelFilter string
FrameFilter int
ComputeResourceFilter string
NotNeedEmpty bool
}

func (a *AiModelConvert) IsGpuTrainTask() bool {
@@ -310,6 +346,34 @@ func ModifyModelPrivate(id string, isPrivate bool) error {
return nil
}

func ModifyModelRecommend(id string, recommend int) error {
var sess *xorm.Session
sess = x.ID(id)
defer sess.Close()
re, err := sess.Cols("recommend").Update(&AiModelManage{
Recommend: recommend,
})
if err != nil {
return err
}
log.Info("success to update recommend from db.re=" + fmt.Sprint((re)))
return nil
}

func ModifyModelCollectedNum(id string, collectedNum int) error {
var sess *xorm.Session
sess = x.ID(id)
defer sess.Close()
re, err := sess.Cols("collected_count").Update(&AiModelManage{
CollectedCount: collectedNum,
})
if err != nil {
return err
}
log.Info("success to update collectedNum from db.re=" + fmt.Sprint((re)))
return nil
}

func ModifyLocalModel(id string, name, label, description string, engine int, isPrivate bool) error {
var sess *xorm.Session
sess = x.ID(id)
@@ -394,6 +458,29 @@ func QueryModelByName(name string, repoId int64) []*AiModelManage {
return aiModelManageList
}

func QueryModelByRepoId(repoId int64) []*AiModelManage {
sess := x.NewSession()
defer sess.Close()
sess.Select("*").Table("ai_model_manage").
Where("repo_id=?", repoId)
aiModelManageList := make([]*AiModelManage, 0)
sess.Find(&aiModelManageList)
return aiModelManageList
}

func DeleteModelByRepoId(repoId int64) error {
sess := x.NewSession()
defer sess.Close()
re, err := sess.Delete(&AiModelManage{
RepoId: repoId,
})
if err != nil {
return err
}
log.Info("success to delete DeleteModelByRepoId from db.re=" + fmt.Sprint((re)))
return nil
}

func QueryModelByPath(path string) (*AiModelManage, error) {
modelManage := new(AiModelManage)
has, err := x.Where("path=?", path).Get(modelManage)
@@ -409,51 +496,74 @@ func QueryModelByPath(path string) (*AiModelManage, error) {
func QueryModel(opts *AiModelQueryOptions) ([]*AiModelManage, int64, error) {
sess := x.NewSession()
defer sess.Close()
var cond = builder.NewCond()
var where string
where += " ai_model_manage.user_id > 0 "
if opts.RepoID > 0 {
cond = cond.And(
builder.Eq{"ai_model_manage.repo_id": opts.RepoID},
)
where += " and ai_model_manage.repo_id= " + fmt.Sprint(opts.RepoID)
}

if opts.UserID > 0 {
cond = cond.And(
builder.Eq{"ai_model_manage.user_id": opts.UserID},
)
where += " and ai_model_manage.user_id=" + fmt.Sprint(opts.UserID)
}

if opts.New >= 0 {
cond = cond.And(
builder.Eq{"ai_model_manage.new": opts.New},
)
where += " and ai_model_manage.new=" + fmt.Sprint(opts.New)
}

if len(opts.ModelID) > 0 {
cond = cond.And(
builder.Eq{"ai_model_manage.id": opts.ModelID},
)
where += " and ai_model_manage.id='" + fmt.Sprint(opts.ModelID) + "'"
}

if (opts.Type) >= 0 {
cond = cond.And(
builder.Eq{"ai_model_manage.type": opts.Type},
)
where += " and ai_model_manage.type=" + fmt.Sprint(opts.Type)
}

if (opts.Status) >= 0 {
cond = cond.And(
builder.Eq{"ai_model_manage.status": opts.Status},
)
where += " and ai_model_manage.status=" + fmt.Sprint(opts.Status)
}
if !opts.IsQueryPrivate {
cond = cond.And(
builder.Eq{"ai_model_manage.is_private": false},
)
where += " and ai_model_manage.is_private=false"
}
count, err := sess.Where(cond).Count(new(AiModelManage))
if err != nil {
return nil, 0, fmt.Errorf("Count: %v", err)
if opts.IsRecommend {
where += " and ai_model_manage.recommend=1"
}
if opts.FrameFilter >= 0 {
if opts.FrameFilter == 2 {
where += " and ai_model_manage.engine in (2,121,122)"
} else {
where += " and ai_model_manage.engine=" + fmt.Sprint(opts.FrameFilter)
}
}
if opts.LabelFilter != "" {
where += " and ai_model_manage.label ILIKE '%" + opts.LabelFilter + "%'"
}
if opts.ComputeResourceFilter != "" {
where += " and ai_model_manage.compute_resource ILIKE '%" + opts.ComputeResourceFilter + "%'"
}
if opts.Namelike != "" {
where += " and ( ai_model_manage.name ILIKE '%" + opts.Namelike + "%'"
where += " or ai_model_manage.description ILIKE '%" + opts.Namelike + "%'"
where += " or ai_model_manage.label ILIKE '%" + opts.Namelike + "%')"
}
if opts.NotNeedEmpty {
where += " and ai_model_manage.size > 0 "
}
var count int64
var err error
if opts.IsCollected {
where += " and ai_model_collect.user_id=" + fmt.Sprint(opts.CollectedUserId)

count, err = sess.Join("INNER", "ai_model_collect", "ai_model_manage.id = ai_model_collect.model_id").Where(where).Count(new(AiModelManage))
if err != nil {
log.Info("error=" + err.Error())
return nil, 0, fmt.Errorf("Count: %v", err)
}
} else {
count, err = sess.Where(where).Count(new(AiModelManage))
if err != nil {
log.Info("error=" + err.Error())
return nil, 0, fmt.Errorf("Count: %v", err)
}
}

if opts.Page >= 0 && opts.PageSize > 0 {
@@ -465,11 +575,18 @@ func QueryModel(opts *AiModelQueryOptions) ([]*AiModelManage, int64, error) {
}
sess.Limit(opts.PageSize, start)
}

sess.OrderBy("ai_model_manage.created_unix DESC")
if opts.IsCollected {
sess.Join("INNER", "ai_model_collect", "ai_model_manage.id = ai_model_collect.model_id")
}
orderby := "ai_model_manage.created_unix desc"
if opts.SortType != "" {
orderby = opts.SortType
}
sess.OrderBy(orderby)
aiModelManages := make([]*AiModelManage, 0, setting.UI.IssuePagingNum)
if err := sess.Table("ai_model_manage").Where(cond).
if err := sess.Table("ai_model_manage").Where(where).
Find(&aiModelManages); err != nil {
log.Info("error=" + err.Error())
return nil, 0, fmt.Errorf("Find: %v", err)
}

@@ -551,3 +668,109 @@ func QueryModelConvert(opts *AiModelQueryOptions) ([]*AiModelConvert, int64, err

return aiModelManageConvert, count, nil
}

func SaveModelCollect(modelCollect *AiModelCollect) error {
sess := x.NewSession()
defer sess.Close()
re, err := sess.Insert(modelCollect)
if err != nil {
log.Info("insert AiModelCollect error." + err.Error())
return err
}
log.Info("success to save AiModelCollect db.re=" + fmt.Sprint((re)))
return nil
}

func DeleteModelCollect(modelCollect *AiModelCollect) error {
sess := x.NewSession()
defer sess.Close()
re, err := sess.Delete(modelCollect)
if err != nil {
log.Info("delete AiModelCollect error." + err.Error())
return err
}
log.Info("success to delete AiModelCollect db.re=" + fmt.Sprint((re)))
return nil
}

func QueryModelCollectNum(modelId string) int {
sess := x.NewSession()
defer sess.Close()
modelCollects := make([]*AiModelCollect, 0)
err := sess.Table(new(AiModelCollect)).Where("model_id=?", modelId).Find(&modelCollects)
if err == nil {
return len(modelCollects)
}
return 0
}
func QueryModelCollectByUserId(modelId string, userId int64) []*AiModelCollect {
sess := x.NewSession()
defer sess.Close()
modelCollects := make([]*AiModelCollect, 0)
err := sess.Table(new(AiModelCollect)).Where("model_id=? and user_id=?", modelId, userId).Find(&modelCollects)
if err == nil {
return modelCollects
}
return nil
}

func QueryModelCollectedStatus(modelIds []string, userId int64) map[string]*AiModelCollect {
sess := x.NewSession()
defer sess.Close()
modelCollects := make([]*AiModelCollect, 0)
var cond = builder.NewCond()
cond = cond.And(
builder.In("model_id", modelIds),
)
cond = cond.And(
builder.Eq{"user_id": userId},
)
result := make(map[string]*AiModelCollect, 0)
err := sess.Table(new(AiModelCollect)).Where(cond).Find(&modelCollects)
if err == nil {
for _, v := range modelCollects {
result[v.ModelID] = v
}
}
return result
}

func SaveModelFile(modelFile *AiModelFile) error {
sess := x.NewSession()
defer sess.Close()
re, err := sess.Insert(modelFile)
if err != nil {
log.Info("insert modelFile error." + err.Error())
return err
}
log.Info("success to save modelFile db.re=" + fmt.Sprint((re)))
return nil
}

func DeleteModelFile(modelFile *AiModelFile) error {
sess := x.NewSession()
defer sess.Close()
re, err := sess.Delete(modelFile)
if err != nil {
log.Info("delete modelFile error." + err.Error())
return err
}
log.Info("success to delete modelFile db.re=" + fmt.Sprint((re)))
return nil
}

func QueryModelFileByModelId(modelId string) []*AiModelFile {
sess := x.NewSession()
defer sess.Close()
modelFileList := make([]*AiModelFile, 0)
var cond = builder.NewCond()
cond = cond.And(
builder.Eq{"model_id": modelId},
)
result := make([]*AiModelFile, 0)
err := sess.Table(new(AiModelFile)).Where(cond).Find(&modelFileList)
if err != nil {
log.Info("query AiModelFile failed, err=" + err.Error())
}
return result
}

+ 33
- 3
models/cloudbrain.go View File

@@ -205,6 +205,9 @@ type Cloudbrain struct {
ModelName string //模型名称
ModelVersion string //模型版本
CkptName string //权重文件名称
ModelId string //模型ID
ModelRepoName string `xorm:"-"`
ModelRepoOwnerName string `xorm:"-"`
PreTrainModelUrl string //预训练模型地址
ResultUrl string //推理结果的obs路径
ResultJson string `xorm:"varchar(4000)"`
@@ -2071,12 +2074,28 @@ func CreateCloudbrain(cloudbrain *Cloudbrain) (err error) {
}
}
session.Commit()
increaseModelReference(session, cloudbrain.ModelId)
go IncreaseDatasetUseCount(cloudbrain.Uuid)
go OperateRepoAITaskNum(cloudbrain.RepoID, 1)
//go IncreaseModelRefernceCount(cloudbrain)
return nil
}

func increaseModelReference(session *xorm.Session, modelId string) {
if modelId != "" {
log.Info("increase model count.")
if _, err := session.Exec("UPDATE `ai_model_manage` SET reference_count = reference_count + 1 WHERE id = ?", modelId); err != nil {
log.Info("err=" + err.Error())
}
}
}

// func IncreaseModelRefernceCount(cloudbrain *Cloudbrain) {
// if cloudbrain.ModelId != "" {
// AddModelInferenceCount(cloudbrain.ModelId)
// }
// }

func getRepoCloudBrain(cb *Cloudbrain) (*Cloudbrain, error) {
has, err := x.Get(cb)
if err != nil {
@@ -2490,7 +2509,7 @@ func RestartCloudbrain(old *Cloudbrain, new *Cloudbrain) (err error) {
if err = sess.Commit(); err != nil {
return err
}
increaseModelReference(sess, new.ModelId)
go IncreaseDatasetUseCount(new.Uuid)
return nil
}
@@ -2628,7 +2647,6 @@ func CloudbrainAll(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int64, error) {
Join("left", "`user`", condition).
Join("left", "cloudbrain_spec", "cloudbrain.id = cloudbrain_spec.cloudbrain_id").
Count(new(CloudbrainInfo))

}

if err != nil {
@@ -2982,3 +3000,15 @@ func LoadSpecs4CloudbrainInfo(tasks []*CloudbrainInfo) error {
}
return nil
}

func GetCloudBrainByModelId(modelId string) ([]*Cloudbrain, error) {
cloudBrains := make([]*Cloudbrain, 0)
err := x.AllCols().Where("model_id=?", modelId).OrderBy("created_unix asc").Find(&cloudBrains)
return cloudBrains, err
}

func GetCloudBrainByRepoIdAndModelName(repoId int64, modelName string) ([]*Cloudbrain, error) {
cloudBrains := make([]*Cloudbrain, 0)
err := x.AllCols().Where("model_name=? and repo_id=?", modelName, repoId).OrderBy("created_unix asc").Find(&cloudBrains)
return cloudBrains, err
}

+ 2
- 0
models/models.go View File

@@ -170,6 +170,8 @@ func init() {
new(TechConvergeBaseInfo),
new(RepoConvergeInfo),
new(UserRole),
new(AiModelCollect),
new(AiModelFile),
new(ModelMigrateRecord),
)



+ 22
- 1
models/repo.go View File

@@ -1706,7 +1706,9 @@ func updateRepository(e Engine, repo *Repository, visibilityChanged bool) (err e
if err != nil {
return err
}

_, err = e.Where("repo_id = ?", repo.ID).Cols("is_private").Update(&AiModelManage{
IsPrivate: true,
})
} else {
//If repo has become public, we need set dataset to public
_, err = e.Where("repo_id = ? and status <> 2", repo.ID).Cols("status").Update(&Dataset{
@@ -1870,6 +1872,9 @@ func DeleteRepository(doer *User, uid, repoID int64) error {

// Delete dataset attachment record and remove related files
deleteDatasetAttachmentByRepoId(sess, repoID)

deleteModelByRepoId(repoID)

if err = deleteBeans(sess,
&Access{RepoID: repo.ID},
&Action{RepoID: repo.ID},
@@ -2055,6 +2060,22 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
return nil
}

func deleteModelByRepoId(repoId int64) {
models := QueryModelByRepoId(repoId)
if models != nil {
for _, model := range models {
log.Info("bucket=" + setting.Bucket + " path=" + model.Path)
if len(model.Path) > (len(setting.Bucket) + 1) {
err := storage.ObsRemoveObject(setting.Bucket, model.Path[len(setting.Bucket)+1:])
if err != nil {
log.Info("Failed to delete model. id=" + model.ID)
}
}
}
}
DeleteModelByRepoId(repoId)
}

func deleteDatasetAttachmentByRepoId(sess *xorm.Session, repoId int64) error {
attachments := make([]*Attachment, 0)
if err := sess.Join("INNER", "dataset", "dataset.id = attachment.dataset_id").


+ 3
- 1
modules/auth/cloudbrain.go View File

@@ -26,6 +26,7 @@ type CreateCloudBrainForm struct {
ModelName string `form:"model_name"`
ModelVersion string `form:"model_version"`
CkptName string `form:"ckpt_name"`
ModelId string `form:"model_id"`
LabelName string `form:"label_names"`
PreTrainModelUrl string `form:"pre_train_model_url"`
DatasetName string `form:"dataset_name"`
@@ -68,7 +69,7 @@ type CreateCloudBrainInferencForm struct {
JobType string `form:"job_type" binding:"Required"`
BenchmarkCategory string `form:"get_benchmark_category"`
GpuType string `form:"gpu_type"`
TrainUrl string `form:"train_url"`
PreTrainModelUrl string `form:"pre_train_model_url"`
TestUrl string `form:"test_url"`
Description string `form:"description"`
ResourceSpecId int `form:"resource_spec_id" binding:"Required"`
@@ -79,6 +80,7 @@ type CreateCloudBrainInferencForm struct {
ModelVersion string `form:"model_version" binding:"Required"`
CkptName string `form:"ckpt_name" binding:"Required"`
LabelName string `form:"label_names" binding:"Required"`
ModelId string `form:"model_id" binding:"Required"`
DatasetName string `form:"dataset_name"`
SpecId int64 `form:"spec_id"`
}


+ 2
- 0
modules/auth/grampus.go View File

@@ -21,6 +21,7 @@ type CreateGrampusTrainJobForm struct {
ModelName string `form:"model_name"`
ModelVersion string `form:"model_version"`
CkptName string `form:"ckpt_name"`
ModelId string `form:"model_id"`
LabelName string `form:"label_names"`
PreTrainModelUrl string `form:"pre_train_model_url"`
SpecId int64 `form:"spec_id"`
@@ -44,6 +45,7 @@ type CreateGrampusNotebookForm struct {
ModelName string `form:"model_name"`
ModelVersion string `form:"model_version"`
CkptName string `form:"ckpt_name"`
ModelId string `form:"model_id"`
LabelName string `form:"label_names"`
PreTrainModelUrl string `form:"pre_train_model_url"`
SpecId int64 `form:"spec_id" binding:"Required"`


+ 4
- 1
modules/auth/modelarts.go View File

@@ -25,6 +25,7 @@ type CreateModelArtsNotebookForm struct {
ModelName string `form:"model_name"`
ModelVersion string `form:"model_version"`
CkptName string `form:"ckpt_name"`
ModelId string `form:"model_id"`
LabelName string `form:"label_names"`
PreTrainModelUrl string `form:"pre_train_model_url"`
SpecId int64 `form:"spec_id" binding:"Required"`
@@ -56,6 +57,7 @@ type CreateModelArtsTrainJobForm struct {
EngineName string `form:"engine_names" binding:"Required"`
SpecId int64 `form:"spec_id" binding:"Required"`
ModelName string `form:"model_name"`
ModelId string `form:"model_id"`
ModelVersion string `form:"model_version"`
CkptName string `form:"ckpt_name"`
LabelName string `form:"label_names"`
@@ -79,10 +81,11 @@ type CreateModelArtsInferenceJobForm struct {
FlavorName string `form:"flaver_names" binding:"Required"`
EngineName string `form:"engine_names" binding:"Required"`
LabelName string `form:"label_names" binding:"Required"`
TrainUrl string `form:"train_url" binding:"Required"`
PreTrainModelUrl string `form:"pre_train_model_url" binding:"Required"`
ModelName string `form:"model_name" binding:"Required"`
ModelVersion string `form:"model_version" binding:"Required"`
CkptName string `form:"ckpt_name" binding:"Required"`
ModelId string `form:"model_id"`
SpecId int64 `form:"spec_id" binding:"Required"`
}



+ 11
- 12
modules/cloudbrain/cloudbrain.go View File

@@ -79,6 +79,7 @@ type GenerateCloudBrainTaskReq struct {
ModelName string
ModelVersion string
CkptName string
ModelId string
LabelName string
PreTrainModelPath string
PreTrainModelUrl string
@@ -358,6 +359,7 @@ func GenerateTask(req GenerateCloudBrainTaskReq) (string, error) {
ModelName: req.ModelName,
ModelVersion: req.ModelVersion,
CkptName: req.CkptName,
ModelId: req.ModelId,
ResultUrl: req.ResultPath,
LabelName: req.LabelName,
PreTrainModelUrl: req.PreTrainModelUrl,
@@ -475,18 +477,13 @@ func RestartTask(ctx *context.Context, task *models.Cloudbrain, newID *string) e
}

if task.PreTrainModelUrl != "" { //预训练
_, err := models.QueryModelByPath(task.PreTrainModelUrl)
if err != nil {
log.Warn("The model may be deleted", err)
} else {
volumes = append(volumes, models.Volume{
HostPath: models.StHostPath{
Path: setting.Attachment.Minio.RealPath + task.PreTrainModelUrl,
MountPath: PretrainModelMountPath,
ReadOnly: true,
},
})
}
volumes = append(volumes, models.Volume{
HostPath: models.StHostPath{
Path: setting.Attachment.Minio.RealPath + task.PreTrainModelUrl,
MountPath: PretrainModelMountPath,
ReadOnly: true,
},
})
}

createTime := timeutil.TimeStampNow()
@@ -549,6 +546,7 @@ func RestartTask(ctx *context.Context, task *models.Cloudbrain, newID *string) e
LabelName: task.LabelName,
PreTrainModelUrl: task.PreTrainModelUrl,
CkptName: task.CkptName,
ModelId: task.ModelId,
}

err = models.RestartCloudbrain(task, newTask)
@@ -698,6 +696,7 @@ type GenerateModelArtsNotebookReq struct {
ModelName string
LabelName string
CkptName string
ModelId string
ModelVersion string
PreTrainModelUrl string
}

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

@@ -31,10 +31,10 @@ func ToCloudBrain(task *models.Cloudbrain) *api.Cloudbrain {
VersionName: task.VersionName,
ModelVersion: task.ModelVersion,
CkptName: task.CkptName,
StartTime: int64(task.StartTime),
EndTime: int64(task.EndTime),
Spec: ToSpecification(task.Spec),
ModelId: task.ModelId,
StartTime: int64(task.StartTime),
EndTime: int64(task.EndTime),
Spec: ToSpecification(task.Spec),
}
}
func ToAttachment(attachment *models.Attachment) *api.AttachmentShow {


+ 19
- 10
modules/grampus/grampus.go View File

@@ -1,6 +1,7 @@
package grampus

import (
"encoding/json"
"fmt"
"strconv"
"strings"
@@ -78,6 +79,7 @@ type GenerateTrainJobReq struct {
ModelName string
LabelName string
CkptName string
ModelId string
ModelVersion string
PreTrainModelPath string
PreTrainModelUrl string
@@ -103,6 +105,7 @@ type GenerateNotebookJobReq struct {
ModelName string
LabelName string
CkptName string
ModelId string
ModelVersion string
PreTrainModelPath string
PreTrainModelUrl string
@@ -227,7 +230,7 @@ func GenerateNotebookJob(ctx *context.Context, req *GenerateNotebookJobReq) (job
EndPoint: getEndPoint(),
ReadOnly: true,
ObjectKey: req.PreTrainModelPath,
ContainerPath: cloudbrain.PretrainModelMountPath,
ContainerPath: cloudbrain.PretrainModelMountPath + "/" + req.CkptName,
})
}

@@ -248,7 +251,8 @@ func GenerateNotebookJob(ctx *context.Context, req *GenerateNotebookJobReq) (job
log.Info("debug command:" + req.Command)

}

datasetGrampusJson, _ := json.Marshal(datasetGrampus)
log.Info("datasetGrampusJson=" + string(datasetGrampusJson))
jobResult, err := createNotebookJob(models.CreateGrampusNotebookRequest{
Name: req.JobName,
Tasks: []models.GrampusNotebookTask{
@@ -299,6 +303,7 @@ func GenerateNotebookJob(ctx *context.Context, req *GenerateNotebookJobReq) (job
LabelName: req.LabelName,
PreTrainModelUrl: req.PreTrainModelUrl,
CkptName: req.CkptName,
ModelId: req.ModelId,
})

if err != nil {
@@ -355,13 +360,13 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (jobId str
} else if ProcessorTypeGPU == req.ProcessType {
datasetGrampus = getDatasetGPUGrampus(req.DatasetInfos, "/tmp/dataset")
if len(req.ModelName) != 0 {
modelGrampus = []models.GrampusDataset{
modelGrampus = []models.GrampusDataset{ //model save as obs
{
Name: req.ModelName,
Bucket: setting.Attachment.Minio.Bucket,
EndPoint: setting.Attachment.Minio.Endpoint,
ObjectKey: req.PreTrainModelPath,
Bucket: setting.Bucket,
EndPoint: getEndPoint(),
ReadOnly: true,
ObjectKey: req.PreTrainModelPath,
ContainerPath: "/tmp/pretrainmodel/" + req.CkptName,
},
}
@@ -382,13 +387,13 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (jobId str
} else if ProcessorTypeGCU == req.ProcessType {
datasetGrampus = getDatasetGCUGrampus(req.DatasetInfos, "/tmp/dataset")
if len(req.ModelName) != 0 {
modelGrampus = []models.GrampusDataset{
modelGrampus = []models.GrampusDataset{ //model save as obs
{
Name: req.ModelName,
Bucket: setting.Attachment.Minio.Bucket,
EndPoint: setting.Attachment.Minio.Endpoint,
ObjectKey: req.PreTrainModelPath,
Bucket: setting.Bucket,
EndPoint: getEndPoint(),
ReadOnly: true,
ObjectKey: req.PreTrainModelPath,
ContainerPath: "/tmp/pretrainmodel/" + req.CkptName,
},
}
@@ -407,6 +412,9 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (jobId str
}
}

modelGrampusJson, _ := json.Marshal(modelGrampus)
log.Info("train job modelGrampus=" + string(modelGrampusJson))

jobResult, err := createJob(models.CreateGrampusJobRequest{
Name: req.JobName,
Tasks: []models.GrampusTasks{
@@ -465,6 +473,7 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (jobId str
LabelName: req.LabelName,
PreTrainModelUrl: req.PreTrainModelUrl,
CkptName: req.CkptName,
ModelId: req.ModelId,
})

if err != nil {


+ 6
- 0
modules/modelarts/modelarts.go View File

@@ -91,6 +91,7 @@ type GenerateTrainJobReq struct {
ModelName string
LabelName string
CkptName string
ModelId string
ModelVersion string
PreTrainModelUrl string
}
@@ -122,6 +123,7 @@ type GenerateInferenceJobReq struct {
ModelName string
ModelVersion string
CkptName string
ModelId string
ResultUrl string
Spec *models.Specification
DatasetName string
@@ -244,6 +246,7 @@ func GenerateNotebook2(ctx *context.Context, req cloudbrain.GenerateModelArtsNot
LabelName: req.LabelName,
PreTrainModelUrl: req.PreTrainModelUrl,
CkptName: req.CkptName,
ModelId: req.ModelId,
}

err = models.CreateCloudbrain(task)
@@ -366,6 +369,7 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (jobId str
LabelName: req.LabelName,
PreTrainModelUrl: req.PreTrainModelUrl,
CkptName: req.CkptName,
ModelId: req.ModelId,
})

if createErr != nil {
@@ -526,6 +530,7 @@ func GenerateTrainJobVersion(ctx *context.Context, req *GenerateTrainJobReq, job
LabelName: req.LabelName,
PreTrainModelUrl: req.PreTrainModelUrl,
CkptName: req.CkptName,
ModelId: req.ModelId,
})
if createErr != nil {
log.Error("CreateCloudbrain(%s) failed:%v", req.JobName, createErr.Error())
@@ -706,6 +711,7 @@ func GenerateInferenceJob(ctx *context.Context, req *GenerateInferenceJobReq) (j
ModelName: req.ModelName,
ModelVersion: req.ModelVersion,
CkptName: req.CkptName,
ModelId: req.ModelId,
ResultUrl: req.ResultUrl,
CreatedUnix: createTime,
UpdatedUnix: createTime,


+ 1
- 0
modules/modelarts_cd/modelarts.go View File

@@ -154,6 +154,7 @@ func GenerateNotebook(ctx *context.Context, req cloudbrain.GenerateModelArtsNote
LabelName: req.LabelName,
PreTrainModelUrl: req.PreTrainModelUrl,
CkptName: req.CkptName,
ModelId: req.ModelId,
}

err = models.CreateCloudbrain(task)


+ 3
- 3
modules/setting/setting.go View File

@@ -1757,7 +1757,7 @@ func getModelConvertConfig() {
ModelConvert.GPU_PYTORCH_IMAGE = sec.Key("GPU_PYTORCH_IMAGE").MustString("dockerhub.pcl.ac.cn:5000/user-images/openi:tensorRT_7_zouap")
ModelConvert.GpuQueue = sec.Key("GpuQueue").MustString("openidgx")
ModelConvert.GPU_TENSORFLOW_IMAGE = sec.Key("GPU_TENSORFLOW_IMAGE").MustString("dockerhub.pcl.ac.cn:5000/user-images/openi:tf2onnx")
ModelConvert.NPU_MINDSPORE_16_IMAGE = sec.Key("NPU_MINDSPORE_16_IMAGE").MustString("swr.cn-south-222.ai.pcl.cn/openi/mindspore1.6.1_train_v1_openi:v3_ascend")
ModelConvert.NPU_MINDSPORE_16_IMAGE = sec.Key("NPU_MINDSPORE_16_IMAGE").MustString("swr.cn-south-222.ai.pcl.cn/openi/mindspore1.8.1_train_openi_new:v1")
ModelConvert.PytorchOnnxBootFile = sec.Key("PytorchOnnxBootFile").MustString("convert_pytorch.py")
ModelConvert.PytorchTrTBootFile = sec.Key("PytorchTrTBootFile").MustString("convert_pytorch_tensorrt.py")
ModelConvert.MindsporeBootFile = sec.Key("MindsporeBootFile").MustString("convert_mindspore.py")
@@ -1767,8 +1767,8 @@ func getModelConvertConfig() {
ModelConvert.GPU_Resource_Specs_ID = sec.Key("GPU_Resource_Specs_ID").MustInt(1)
ModelConvert.NPU_FlavorCode = sec.Key("NPU_FlavorCode").MustString("modelarts.bm.910.arm.public.1")
ModelConvert.NPU_PoolID = sec.Key("NPU_PoolID").MustString("pool7908321a")
ModelConvert.NPU_MINDSPORE_IMAGE_ID = sec.Key("NPU_MINDSPORE_IMAGE_ID").MustInt(121)
ModelConvert.NPU_TENSORFLOW_IMAGE_ID = sec.Key("NPU_TENSORFLOW_IMAGE_ID").MustInt(35)
ModelConvert.NPU_MINDSPORE_IMAGE_ID = sec.Key("NPU_MINDSPORE_IMAGE_ID").MustInt(37)
ModelConvert.NPU_TENSORFLOW_IMAGE_ID = sec.Key("NPU_TENSORFLOW_IMAGE_ID").MustInt(38)
ModelConvert.GPU_PADDLE_IMAGE = sec.Key("GPU_PADDLE_IMAGE").MustString("dockerhub.pcl.ac.cn:5000/user-images/openi:paddle2.3.0_gpu_cuda11.2_cudnn8")
ModelConvert.GPU_MXNET_IMAGE = sec.Key("GPU_MXNET_IMAGE").MustString("dockerhub.pcl.ac.cn:5000/user-images/openi:mxnet191cu_cuda102_py37")
ModelConvert.PaddleOnnxBootFile = sec.Key("PaddleOnnxBootFile").MustString("convert_paddle.py")


+ 4
- 0
modules/storage/local.go View File

@@ -89,6 +89,10 @@ func (l *LocalStorage) UploadObject(fileName, filePath string) error {
return nil
}

func (l *LocalStorage) UploadContent(bucketName string, path string, r io.Reader) (int64, error) {
return int64(0), nil
}

func (l *LocalStorage) DeleteDir(dir string) error {
return nil
}

+ 4
- 0
modules/storage/minio.go View File

@@ -163,6 +163,10 @@ func (m *MinioStorage) UploadObject(fileName, filePath string) error {
return err
}

func (m *MinioStorage) UploadContent(bucketName string, path string, r io.Reader) (int64, error) {
return m.client.PutObject(bucketName, path, r, -1, minio.PutObjectOptions{ContentType: "application/octet-stream"})
}

func GetMinioPath(jobName, suffixPath string) string {
return setting.Attachment.Minio.RealPath + setting.Attachment.Minio.Bucket + "/" + setting.CBCodePathPrefix + jobName + suffixPath
}

+ 33
- 1
modules/storage/obs.go View File

@@ -371,11 +371,13 @@ func GetOneLevelAllObjectUnderDir(bucket string, prefixRootPath string, relative
for {
output, err := ObsCli.ListObjects(input)
if err == nil {
log.Info("Page:%d\n", index)
log.Info("Page:%d\n input.Prefix=v%", index, input.Prefix)
log.Info("input.Prefix=" + input.Prefix)
index++
for _, val := range output.Contents {
var isDir bool
var fileName string
log.Info("val.key=" + val.Key)
if val.Key == input.Prefix {
continue
}
@@ -707,3 +709,33 @@ func IsObjectExist4Obs(bucket, key string) (bool, error) {
}
return true, nil
}

func PutStringToObs(bucket, key string, fileContent string) error {
log.Info("PutStringToObs bucket=" + bucket + " key=" + key)
input := &obs.PutObjectInput{}
input.Bucket = bucket
input.Key = key
input.Body = strings.NewReader(fileContent)
_, err := ObsCli.PutObject(input)
if err != nil {
if obsError, ok := err.(obs.ObsError); ok {
log.Info("Message:%s\n", obsError.Message)
}
}
return err
}

func PutReaderToObs(bucket, key string, reader io.Reader) error {
log.Info("PutStringToObs bucket=" + bucket + " key=" + key)
input := &obs.PutObjectInput{}
input.Bucket = bucket
input.Key = key
input.Body = reader
_, err := ObsCli.PutObject(input)
if err != nil {
if obsError, ok := err.(obs.ObsError); ok {
log.Info("Message:%s\n", obsError.Message)
}
}
return err
}

+ 4
- 2
modules/storage/storage.go View File

@@ -5,12 +5,13 @@
package storage

import (
"fmt"
"io"

"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/obs"
"code.gitea.io/gitea/modules/setting"
"fmt"
"github.com/minio/minio-go"
"io"
)

const (
@@ -29,6 +30,7 @@ type ObjectStorage interface {
PresignedPutURL(path string) (string, error)
HasObject(path string) (bool, error)
UploadObject(fileName, filePath string) error
UploadContent(bucketName string, path string, r io.Reader) (int64, error)
}

// Copy copys a file from source ObjectStorage to dest ObjectStorage


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

@@ -16,6 +16,7 @@ type CreateGrampusTrainJobOption struct {
ModelName string `json:"model_name"`
ModelVersion string `json:"model_version"`
CkptName string `json:"ckpt_name"`
ModelId string `json:"model_id"`
LabelName string `json:"label_names"`
PreTrainModelUrl string `json:"pre_train_model_url"`
SpecId int64 `json:"spec_id" binding:"Required"`
@@ -36,6 +37,7 @@ type CreateTrainJobOption struct {
ModelName string `json:"model_name"`
ModelVersion string `json:"model_version"`
CkptName string `json:"ckpt_name"`
ModelId string `json:"model_id"`
LabelName string `json:"label_names"`
PreTrainModelUrl string `json:"pre_train_model_url"`
SpecId int64 `json:"spec_id" binding:"Required"`
@@ -52,6 +54,7 @@ type CreateNotebookOption struct {
ModelName string `json:"model_name"`
ModelVersion string `json:"model_version"`
CkptName string `json:"ckpt_name"`
ModelId string `json:"model_id"`
LabelName string `json:"label_names"`
PreTrainModelUrl string `json:"pre_train_model_url"`
SpecId int64 `json:"spec_id" binding:"Required"`
@@ -91,6 +94,7 @@ type Cloudbrain struct {
ModelName string `json:"model_name"` //模型名称
ModelVersion string `json:"model_version"` //模型版本
CkptName string `json:"ckpt_name"` //权重文件名称
ModelId string `json:"model_id"` //权重文件名称
StartTime int64 `json:"start_time"`
EndTime int64 `json:"end_time"`
VersionName string `json:"version_name"`


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

@@ -1059,6 +1059,7 @@ cloudbrain.time.starttime=Start run time
cloudbrain.time.endtime=End run time
cloudbrain.datasetdownload=Dataset download url
model_manager = Model
model_square = Model Square
model_experience = Model Experience
model_noright=You have no right to do the operation.
model_rename=Duplicate model name, please modify model name.
@@ -1263,6 +1264,7 @@ modelarts.infer_job.boot_file_helper=The startup file is the entry file for your
modelarts.infer_job.continue_helper=Check Reuse to copy the output result file of the last training task
modelarts.train_job.resource_helper=The "resource specification" is the hardware you use to run the task. In order for more people to use the resources of this platform, please select according to your actual needs
modelarts.infer_job.tooltip = The model has been deleted and cannot be viewed.
modelarts.infer_job.model_cant_see = You are currently unable to view the model, possibly due to permission restrictions or the model has been deleted.
modelarts.download_log=Download log file
modelarts.log_file = Log file
modelarts.fullscreen_log_file = View in full screen
@@ -1325,7 +1327,7 @@ model.manage.engine=Model engine
model.manage.select.engine=Select model engine
model.manage.modelfile=Model file
model.manage.modellabel=Model label
model.manage.modeldesc=Model description
model.manage.modeldesc=Model brief introduction
model.manage.modelaccess=Model Access
model.manage.modelaccess.public=Public
model.manage.modelaccess.private=Private
@@ -2139,7 +2141,7 @@ settings.wiki_deletion_success = The repository wiki data has been deleted.
settings.delete = Delete This Repository
settings.delete_desc = Deleting a repository is permanent and cannot be undone.
settings.delete_notices_1 = - This operation <strong>CANNOT</strong> be undone.
settings.delete_notices_2 = - This operation will permanently delete the <strong>%s</strong> repository including code, issues, comments, wiki data and collaborator settings.
settings.delete_notices_2 = - This operation will permanently delete the <strong>%s</strong> repository including the code, dataset, model, cloudbrain tasks, tasks, merge requests, and other contents.
settings.delete_notices_fork_1 = - Forks of this repository will become independent after deletion.
settings.deletion_success = The repository has been deleted.
settings.update_settings_success = The repository settings have been updated.
@@ -2619,6 +2621,7 @@ dashboard = Dashboard
users = User Accounts
organizations = Organizations
datasets= Dataset
models=Model
repositories = Repositories
hooks = Default Webhooks
systemhooks = System Webhooks
@@ -3161,7 +3164,7 @@ task_c2ent_gcutrainjob=`created GCU type train task <a href="%s/modelarts/train-
task_nputrainjob=`created NPU training task <a href="%s/modelarts/train-job/%s">%s</a>`
task_inferencejob=`created reasoning task <a href="%s/modelarts/inference-job/%s">%s</a>`
task_benchmark=`created profiling task <a href="%s/cloudbrain/benchmark/%s">%s</a>`
task_createmodel=`created new model <a href="%s/modelmanage/show_model_info?name=%s">%s</a>`
task_createmodel=`created new model <a href="%s/modelmanage/model_readme_tmpl?name=%s">%s</a>`
task_gputrainjob=`created CPU/GPU training task <a href="%s/cloudbrain/train-job/%s">%s</a>`
task_c2netnputrainjob=`created NPU training task <a href="%s/grampus/train-job/%s">%s</a>`
task_c2netgputrainjob=`created CPU/GPU training task <a href="%s/grampus/train-job/%s">%s</a>`


+ 6
- 3
options/locale/locale_zh-CN.ini View File

@@ -1058,6 +1058,7 @@ datasets.desc=数据集功能
cloudbrain_helper=使用GPU/NPU资源,开启Notebook、模型训练任务等

model_manager = 模型
model_square = 模型广场
model_experience = 模型体验
model_noright=您没有操作权限。
model_rename=模型名称重复,请修改模型名称
@@ -1275,6 +1276,7 @@ modelarts.infer_job.boot_file_helper=启动文件是您程序执行的入口文
modelarts.infer_job.continue_helper=勾选复用将拷贝上次训练任务输出结果文件
modelarts.train_job.resource_helper=「资源规格」是您运行该任务使用的硬件,为了更多人能够使用本平台的资源,请按照您的实际需求进行选择。
modelarts.infer_job.tooltip = 该模型已删除,无法查看。
modelarts.infer_job.model_cant_see = 您暂时无法查看该模型,可能因为权限限制或模型已被删除。
modelarts.download_log=下载日志文件
modelarts.log_file=日志文件
modelarts.fullscreen_log_file=全屏查看
@@ -1338,7 +1340,7 @@ model.manage.engine=模型框架
model.manage.select.engine=选择模型框架
model.manage.modelfile=模型文件
model.manage.modellabel=模型标签
model.manage.modeldesc=模型描述
model.manage.modeldesc=模型简介
model.manage.modelaccess=模型权限
model.manage.modelaccess.public=公开
model.manage.modelaccess.private=私有
@@ -2155,7 +2157,7 @@ settings.wiki_deletion_success=项目百科数据删除成功!
settings.delete=删除本项目
settings.delete_desc=删除项目是永久性的, 无法撤消。
settings.delete_notices_1=- 此操作 <strong>不可以</strong> 被回滚。
settings.delete_notices_2=- 此操作将永久删除项目 <strong>%s</strong>,包括 Git 数据、 任务、评论、百科和协作者的操作权限
settings.delete_notices_2=- 此操作将永久删除项目 <strong>%s</strong>,包括该项目中的代码、数据集、模型、云脑任务、任务、合并请求等内容
settings.delete_notices_fork_1=- 在此项目删除后,它的派生项目将变成独立项目。
settings.deletion_success=项目已被删除。
settings.deletion_notice_cloudbrain=请先停止项目内正在运行的云脑任务,然后再删除项目。
@@ -2638,6 +2640,7 @@ dashboard=管理面板
users=帐户管理
organizations=组织管理
datasets=数据集
models=模型
repositories=项目管理
hooks=默认Web钩子
systemhooks=系统 Web 钩子
@@ -3179,7 +3182,7 @@ task_c2ent_gcutrainjob=`创建了GCU类型训练任务 <a href="%s/grampus/train
task_nputrainjob=`创建了NPU类型训练任务 <a href="%s/modelarts/train-job/%s">%s</a>`
task_inferencejob=`创建了推理任务 <a href="%s/modelarts/inference-job/%s">%s</a>`
task_benchmark=`创建了评测任务 <a href="%s/cloudbrain/benchmark/%s">%s</a>`
task_createmodel=`导入了新模型 <a href="%s/modelmanage/show_model_info?name=%s">%s</a>`
task_createmodel=`导入了新模型 <a href="%s/modelmanage/model_readme_tmpl?name=%s">%s</a>`
task_gputrainjob=`创建了CPU/GPU类型训练任务 <a href="%s/cloudbrain/train-job/%s">%s</a>`
task_c2netnputrainjob=`创建了NPU类型训练任务 <a href="%s/grampus/train-job/%s">%s</a>`
task_c2netgputrainjob=`创建了CPU/GPU类型训练任务 <a href="%s/grampus/train-job/%s">%s</a>`


+ 1
- 1
public/home/home.js View File

@@ -290,7 +290,7 @@ function getTaskLink(record){
}else if(record.OpType == 29){
re = re + "/cloudbrain/benchmark/" + record.Content;
}else if(record.OpType == 30){
re = re + "/modelmanage/show_model_info?name=" + record.RefName;
re = re + "/modelmanage/model_readme_tmpl?name=" + record.RefName;
}else if(record.OpType == 31){
re = re + "/cloudbrain/train-job/" + record.Content;
}else if(record.OpType == 32 || record.OpType == 33 || record.OpType == 42){


+ 57
- 0
public/img/empty-box.svg View File

@@ -0,0 +1,57 @@
<svg xmlns="http://www.w3.org/2000/svg"
class="styles__StyledSVGIconPathComponent-sc-16fsqc8-0 fPsHiw svg-icon-path-icon" viewBox="0 0 430 298"
width="100" height="100" fill="none">
<defs data-reactroot=""></defs>
<g>
<path id="矩形 3" d="M391 124L430 124L392 62L391 62L391 124Z" fill-rule="evenodd"
fill="url(#paint_linear_3_11_0)"></path>
<path id="矩形 1" d="M54 62L235 62L235 298L78 298C64.7452 298 54 287.255 54 274L54 62Z" fill-rule="evenodd"
fill="url(#paint_linear_3_4_0)"></path>
<path id="矩形 1" d="M392 62L235 62L235 298L368 298C381.255 298 392 287.255 392 274L392 62Z"
fill-rule="evenodd" fill="url(#paint_linear_3_5_0)"></path>
<rect id="矩形 2" x="282.000000" y="143.000000" rx="10.000000" width="62.000000" height="20.000000"
fill="#AAAFBF"></rect>
<path id="矩形 3" d="M273 0L430 0L392 62L235 62L273 0Z" fill-rule="evenodd" fill="url(#paint_linear_3_7_0)">
</path>
<path id="矩形 3" d="M153 0L16 0L54 62L191 62L153 0Z" fill-rule="nonzero" fill="url(#paint_linear_3_8_0)">
</path>
<path id="矩形 3" d="M195 124L16 124L54 62L233 62L195 124Z" fill-rule="evenodd"
fill="url(#paint_linear_3_10_0)"></path>
<path id="减去顶层"
d="M54.5 217C24.4005 217 0 241.4 0 271.5C0 271.667 0.000747681 271.833 0.00224304 272L108.998 272C108.999 271.833 109 271.667 109 271.5C109 241.4 84.5995 217 54.5 217Z"
clip-rule="evenodd" fill-rule="evenodd" fill="#4D9AFF" fill-opacity="0.500000"></path>
<defs>
<linearGradient id="paint_linear_3_11_0" x1="430.000000" y1="124.000000" x2="430.000000" y2="62.000000"
gradientUnits="userSpaceOnUse">
<stop stop-color="#E2E3E7"></stop>
<stop offset="1.000000" stop-color="#AAAFBF"></stop>
</linearGradient>
<linearGradient id="paint_linear_3_4_0" x1="54.000084" y1="62.000008" x2="235.000000" y2="298.000000"
gradientUnits="userSpaceOnUse">
<stop stop-color="#DEE0E7"></stop>
<stop offset="1.000000" stop-color="#EBECF1"></stop>
</linearGradient>
<linearGradient id="paint_linear_3_5_0" x1="0.000092" y1="-0.000008" x2="157.000000" y2="236.000015"
gradientUnits="userSpaceOnUse">
<stop stop-color="#D8DAE2"></stop>
<stop offset="1.000000" stop-color="#D1D1DC"></stop>
</linearGradient>
<linearGradient id="paint_linear_3_7_0" x1="429.999969" y1="0.000000" x2="235.000000" y2="62.000061"
gradientUnits="userSpaceOnUse">
<stop stop-color="#D9DBE6"></stop>
<stop offset="0.992366" stop-color="#CACCD8"></stop>
</linearGradient>
<linearGradient id="paint_linear_3_8_0" x1="15.999954" y1="0.000000" x2="15.999954" y2="62.000000"
gradientUnits="userSpaceOnUse">
<stop stop-color="#EFEFF4"></stop>
<stop offset="0.992366" stop-color="#EAEBF0"></stop>
</linearGradient>
<linearGradient id="paint_linear_3_10_0" x1="16.000000" y1="124.000000" x2="16.000000" y2="62.000000"
gradientUnits="userSpaceOnUse">
<stop stop-color="#F5F4F7"></stop>
<stop offset="0.809160" stop-color="#E7E9EE"></stop>
<stop offset="1.000000" stop-color="#F2F2F5"></stop>
</linearGradient>
</defs>
</g>
</svg>

+ 4
- 2
routers/admin/dataset.go View File

@@ -1,11 +1,12 @@
package admin

import (
"code.gitea.io/gitea/modules/notification"
"net/http"
"strconv"
"strings"

"code.gitea.io/gitea/modules/notification"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
@@ -14,7 +15,8 @@ import (
)

const (
tplDatasets base.TplName = "admin/dataset/list"
tplDatasets base.TplName = "admin/dataset/list"
tplAdminModelManage base.TplName = "admin/model/list"
)

func Datasets(ctx *context.Context) {


+ 189
- 0
routers/admin/modelmanage.go View File

@@ -0,0 +1,189 @@
package admin

import (
"fmt"
"strings"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
)

func AdminModelManage(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("admin.models")
ctx.Data["PageIsAdmin"] = true
ctx.Data["PageIsAdminModels"] = true

page := ctx.QueryInt("page")
if page <= 0 {
page = 1
}
pageSize := ctx.QueryInt("pageSize")
if pageSize <= 0 {
pageSize = setting.UI.IssuePagingNum
}

var (
count int64
err error
orderBy string
)

ctx.Data["SortType"] = ctx.Query("sort")
switch ctx.Query("sort") {
case "newest":
orderBy = "created_unix DESC"
case "oldest":
orderBy = "created_unix ASC"
case "recentupdate":
orderBy = "updated_unix DESC"
case "leastupdate":
orderBy = "updated_unix ASC"
case "reversealphabetically":
orderBy = "name DESC"
case "alphabetically":
orderBy = "name ASC"
case "reversesize":
orderBy = "size DESC"
case "size":
orderBy = "size ASC"
case "downloadtimes":
orderBy = "download_count DESC"
case "mostusecount":
orderBy = "reference_count DESC"
case "fewestusecount":
orderBy = "reference_count ASC"
default:
ctx.Data["SortType"] = "recentupdate"
orderBy = "created_unix DESC"
}

keyword := strings.Trim(ctx.Query("q"), " ")

modelResult, count, err := models.QueryModel(&models.AiModelQueryOptions{
ListOptions: models.ListOptions{
Page: page,
PageSize: setting.UI.ExplorePagingNum,
},
Type: -1,
New: -1,
Status: -1,
IsQueryPrivate: true,
IsRecommend: ctx.QueryBool("recommend"),
UserID: -1,
IsCollected: false,
CollectedUserId: -1,
LabelFilter: "",
FrameFilter: -1,
ComputeResourceFilter: "",
Namelike: keyword,
SortType: orderBy,
RepoID: -1,
})
if err != nil {
ctx.ServerError("Cloudbrain", err)
return
}
userIds := make([]int64, len(modelResult))
modelIds := make([]string, len(modelResult))
repoIds := make([]int64, len(modelResult))
for i, model := range modelResult {
userIds[i] = model.UserId
modelIds[i] = model.ID
repoIds[i] = model.RepoId
}
repoInfo, err := queryRepoInfoByIds(repoIds)
userNameMap := queryUserName(userIds)

for _, model := range modelResult {
//removeIpInfo(model)
model.TrainTaskInfo = ""
value := userNameMap[model.UserId]
if value != nil {
model.UserName = value.Name
model.UserRelAvatarLink = value.RelAvatarLink()
}
if repoInfo != nil {
repo := repoInfo[model.RepoId]
if repo != nil {
model.RepoName = repo.Name
model.RepoOwnerName = repo.OwnerName
model.RepoDisplayName = repo.DisplayName()
}
}
}

ctx.Data["Keyword"] = keyword
ctx.Data["Total"] = count
ctx.Data["models"] = modelResult
ctx.Data["Recommend"] = ctx.QueryBool("recommend")
pager := context.NewPagination(int(count), setting.UI.ExplorePagingNum, page, 5)
pager.SetDefaultParams(ctx)
ctx.Data["Page"] = pager

ctx.HTML(200, tplAdminModelManage)
}

func ModifyModelRecommend(ctx *context.Context) {
id := ctx.Query("id")
isRecommend := ctx.QueryInt("recommend")
re := map[string]string{
"code": "-1",
}
task, err := models.QueryModelById(id)
if err != nil || task == nil {
re["msg"] = err.Error()
log.Error("no such model!", err.Error())
ctx.JSON(200, re)
return
}
if ctx.User == nil || !ctx.User.IsAdmin {
re["msg"] = "No right to operation."
ctx.JSON(200, re)
return
}

err = models.ModifyModelRecommend(id, isRecommend)
if err == nil {
re["code"] = "0"
ctx.JSON(200, re)
log.Info("modify success.")
} else {
re["msg"] = err.Error()
ctx.JSON(200, re)
log.Info("Failed to modify.id=" + id + " isprivate=" + fmt.Sprint(isRecommend) + " error:" + err.Error())
}
}

func queryUserName(intSlice []int64) map[int64]*models.User {
keys := make(map[int64]string)
uniqueElements := []int64{}
for _, entry := range intSlice {
if _, value := keys[entry]; !value {
keys[entry] = ""
uniqueElements = append(uniqueElements, entry)
}
}
result := make(map[int64]*models.User)
userLists, err := models.GetUsersByIDs(uniqueElements)
if err == nil {
for _, user := range userLists {
result[user.ID] = user
}
}
return result
}

func queryRepoInfoByIds(intSlice []int64) (map[int64]*models.Repository, error) {
keys := make(map[int64]string)
uniqueElements := []int64{}
for _, entry := range intSlice {
if _, value := keys[entry]; !value {
keys[entry] = ""
uniqueElements = append(uniqueElements, entry)
}
}
re, err := models.GetRepositoriesMapByIDs(uniqueElements)
return re, err
}

+ 14
- 14
routers/repo/ai_model_convert.go View File

@@ -57,8 +57,8 @@ const (
NetOutputFormat_FP32 = 0
NetOutputFormat_FP16 = 1

NPU_MINDSPORE_IMAGE_ID = 35
NPU_TENSORFLOW_IMAGE_ID = 121
//NPU_MINDSPORE_IMAGE_ID = 37
//NPU_TENSORFLOW_IMAGE_ID = 38

//GPU_Resource_Specs_ID = 1 //cpu 1, gpu 1

@@ -219,10 +219,10 @@ func createNpuTrainJob(modelConvert *models.AiModelConvert, ctx *context.Context
}

var engineId int64
engineId = int64(NPU_MINDSPORE_IMAGE_ID)
engineId = int64(setting.ModelConvert.NPU_MINDSPORE_IMAGE_ID)
bootfile := setting.ModelConvert.MindsporeBootFile
if modelConvert.SrcEngine == TENSORFLOW_ENGINE {
engineId = int64(NPU_TENSORFLOW_IMAGE_ID)
engineId = int64(setting.ModelConvert.NPU_TENSORFLOW_IMAGE_ID)
bootfile = setting.ModelConvert.TensorFlowNpuBootFile
}
userCommand := "/bin/bash /home/work/run_train.sh 's3://" + codeObsPath + "' 'code/" + bootfile + "' '/tmp/log/train.log' --'data_url'='s3://" + dataPath + "' --'train_url'='s3://" + outputObsPath + "'"
@@ -373,6 +373,16 @@ func createGpuTrainJob(modelConvert *models.AiModelConvert, ctx *context.Context
command := ""
IMAGE_URL := setting.ModelConvert.GPU_PYTORCH_IMAGE
dataActualPath := setting.Attachment.Minio.RealPath + modelRelativePath
if model.Type == models.TypeCloudBrainTwo {
//如果模型在OBS上,需要下载到本地,并上传到minio中
relatetiveModelPath := setting.JobPath + modelConvert.ID + "/dataset"
log.Info("local dataset path:" + relatetiveModelPath)
downloadFromObsToLocal(model, relatetiveModelPath)
uploadCodeToMinio(relatetiveModelPath+"/", modelConvert.ID, "/dataset/")
deleteLocalDir(relatetiveModelPath)
dataActualPath = setting.Attachment.Minio.RealPath + setting.Attachment.Minio.Bucket + "/" + setting.CBCodePathPrefix + modelConvert.ID + "/dataset"
}
log.Info("dataActualPath=" + dataActualPath)

if modelConvert.SrcEngine == PYTORCH_ENGINE {
if modelConvert.DestFormat == CONVERT_FORMAT_ONNX {
@@ -389,15 +399,6 @@ func createGpuTrainJob(modelConvert *models.AiModelConvert, ctx *context.Context
} else {
return errors.New("Not support the format.")
}
//如果模型在OBS上,需要下载到本地,并上传到minio中
if model.Type == models.TypeCloudBrainTwo {
relatetiveModelPath := setting.JobPath + modelConvert.ID + "/dataset"
log.Info("local dataset path:" + relatetiveModelPath)
downloadFromObsToLocal(model, relatetiveModelPath)
uploadCodeToMinio(relatetiveModelPath+"/", modelConvert.ID, "/dataset/")
deleteLocalDir(relatetiveModelPath)
dataActualPath = setting.Attachment.Minio.RealPath + setting.Attachment.Minio.Bucket + "/" + setting.CBCodePathPrefix + modelConvert.ID + "/dataset"
}
} else if modelConvert.SrcEngine == PADDLE_ENGINE {
IMAGE_URL = setting.ModelConvert.GPU_PADDLE_IMAGE
if modelConvert.DestFormat == CONVERT_FORMAT_ONNX {
@@ -413,7 +414,6 @@ func createGpuTrainJob(modelConvert *models.AiModelConvert, ctx *context.Context
return errors.New("Not support the format.")
}
}
log.Info("dataActualPath=" + dataActualPath)

log.Info("command=" + command)



+ 282
- 183
routers/repo/ai_model_manage.go View File

@@ -31,8 +31,8 @@ const (
tplModelManageIndex = "repo/modelmanage/index"
tplModelManageDownload = "repo/modelmanage/download"
tplModelInfo = "repo/modelmanage/showinfo"
tplCreateLocalModelInfo = "repo/modelmanage/create_local_1"
tplCreateLocalForUploadModelInfo = "repo/modelmanage/create_local_2"
tplCreateLocalModelInfo = "repo/modelmanage/create_local"
tplCreateLocalForUploadModelInfo = "repo/modelmanage/fileupload"
tplCreateOnlineModelInfo = "repo/modelmanage/create_online"

MODEL_LATEST = 1
@@ -75,14 +75,12 @@ func saveModelByParameters(jobId string, versionName string, name string, versio
}
}
}
cloudType := aiTask.Type
modelSelectedFile := ctx.Query("modelSelectedFile")
//download model zip //train type
if aiTask.ComputeResource == models.NPUResource || aiTask.ComputeResource == models.GCUResource {
cloudType = models.TypeCloudBrainTwo
} else if aiTask.ComputeResource == models.GPUResource {
cloudType = models.TypeCloudBrainOne
}

cloudType := models.TypeCloudBrainTwo

spec, err := resource.GetCloudbrainSpec(aiTask.ID)
if err == nil {
specJson, _ := json.Marshal(spec)
@@ -99,26 +97,27 @@ func saveModelByParameters(jobId string, versionName string, name string, versio
aiTaskJson, _ := json.Marshal(aiTask)
isPrivate := ctx.QueryBool("isPrivate")
model := &models.AiModelManage{
ID: id,
Version: version,
VersionCount: len(aimodels) + 1,
Label: label,
Name: name,
Description: description,
New: MODEL_LATEST,
Type: cloudType,
Path: modelPath,
Size: modelSize,
AttachmentId: aiTask.Uuid,
RepoId: aiTask.RepoID,
UserId: ctx.User.ID,
CodeBranch: aiTask.BranchName,
CodeCommitID: aiTask.CommitID,
Engine: int64(engine),
TrainTaskInfo: string(aiTaskJson),
Accuracy: string(accuracyJson),
Status: STATUS_COPY_MODEL,
IsPrivate: isPrivate,
ID: id,
Version: version,
VersionCount: len(aimodels) + 1,
Label: label,
Name: name,
Description: description,
New: MODEL_LATEST,
Type: cloudType,
Path: modelPath,
Size: modelSize,
AttachmentId: aiTask.Uuid,
RepoId: aiTask.RepoID,
UserId: ctx.User.ID,
CodeBranch: aiTask.BranchName,
CodeCommitID: aiTask.CommitID,
Engine: int64(engine),
TrainTaskInfo: string(aiTaskJson),
Accuracy: string(accuracyJson),
Status: STATUS_COPY_MODEL,
IsPrivate: isPrivate,
ComputeResource: aiTask.ComputeResource,
}

err = models.SaveModelToDb(model)
@@ -145,7 +144,9 @@ func saveModelByParameters(jobId string, versionName string, name string, versio
go asyncToCopyModel(aiTask, id, modelSelectedFile)

log.Info("save model end.")
notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, id, name, models.ActionCreateNewModelTask)
if !model.IsPrivate {
notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, id, name, models.ActionCreateNewModelTask)
}
return id, nil
}

@@ -157,6 +158,7 @@ func asyncToCopyModel(aiTask *models.Cloudbrain, id string, modelSelectedFile st
log.Info("download model from CloudBrainTwo faild." + err.Error())
} else {
updateStatus(id, modelSize, STATUS_FINISHED, modelPath, "")
insertModelFile(id)
}
} else if aiTask.ComputeResource == models.GPUResource {

@@ -166,7 +168,24 @@ func asyncToCopyModel(aiTask *models.Cloudbrain, id string, modelSelectedFile st
log.Info("download model from CloudBrainOne faild." + err.Error())
} else {
updateStatus(id, modelSize, STATUS_FINISHED, modelPath, "")
insertModelFile(id)
}
}
}

func insertModelFile(id string) {
model, _ := models.QueryModelById(id)
files, err := storage.GetAllObjectByBucketAndPrefix(setting.Bucket, model.Path[len(setting.Bucket)+1:])
if err != nil {
log.Info("Failed to query model size from obs. id=" + id)
}
for _, file := range files {
modelFile := &models.AiModelFile{
ModelID: id,
Name: file.FileName,
Size: file.Size,
}
models.SaveModelFile(modelFile)
}
}

@@ -229,13 +248,23 @@ func SaveLocalModel(ctx *context.Context) {
engine := ctx.QueryInt("engine")
taskType := ctx.QueryInt("type")
isPrivate := ctx.QueryBool("isPrivate")
if ctx.Repo.Repository.IsPrivate {
if !isPrivate {
re["msg"] = "Private repo cannot create public model."
ctx.JSON(200, re)
return
}
}
modelActualPath := ""
computeResource := ""
if taskType == models.TypeCloudBrainOne {
destKeyNamePrefix := Model_prefix + models.AttachmentRelativePath(id) + "/"
modelActualPath = setting.Attachment.Minio.Bucket + "/" + destKeyNamePrefix
computeResource = models.GPUResource
} else if taskType == models.TypeCloudBrainTwo {
destKeyNamePrefix := Model_prefix + models.AttachmentRelativePath(id) + "/"
modelActualPath = setting.Bucket + "/" + destKeyNamePrefix
computeResource = models.NPUResource
} else {
re["msg"] = "type is error."
ctx.JSON(200, re)
@@ -257,25 +286,26 @@ func SaveLocalModel(ctx *context.Context) {
}
}
model := &models.AiModelManage{
ID: id,
Version: version,
ModelType: MODEL_LOCAL_TYPE,
VersionCount: len(aimodels) + 1,
Label: label,
Name: name,
Description: description,
New: MODEL_LATEST,
Type: taskType,
Path: modelActualPath,
Size: 0,
AttachmentId: "",
RepoId: repoId,
UserId: ctx.User.ID,
Engine: int64(engine),
TrainTaskInfo: "",
Accuracy: "",
Status: STATUS_FINISHED,
IsPrivate: isPrivate,
ID: id,
Version: version,
ModelType: MODEL_LOCAL_TYPE,
VersionCount: len(aimodels) + 1,
Label: label,
Name: name,
Description: description,
New: MODEL_LATEST,
Type: taskType,
Path: modelActualPath,
Size: 0,
AttachmentId: "",
RepoId: repoId,
UserId: ctx.User.ID,
Engine: int64(engine),
TrainTaskInfo: "",
Accuracy: "",
Status: STATUS_FINISHED,
IsPrivate: isPrivate,
ComputeResource: computeResource,
}

err := models.SaveModelToDb(model)
@@ -302,7 +332,9 @@ func SaveLocalModel(ctx *context.Context) {
models.UpdateRepositoryUnits(ctx.Repo.Repository, units, deleteUnitTypes)

log.Info("save model end.")
notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, id, name, models.ActionCreateNewModelTask)
if !model.IsPrivate {
notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, id, name, models.ActionCreateNewModelTask)
}
re["code"] = "0"
re["id"] = id
ctx.JSON(200, re)
@@ -316,27 +348,33 @@ func getSize(files []storage.FileInfo) int64 {
return size
}

func UpdateModelSize(modeluuid string) {
func UpdateModelSize(modeluuid string, objectName string) {
model, err := models.QueryModelById(modeluuid)
if err == nil {
var size int64
if model.Type == models.TypeCloudBrainOne {
if strings.HasPrefix(model.Path, setting.Attachment.Minio.Bucket+"/"+Model_prefix) {
files, err := storage.GetAllObjectByBucketAndPrefixMinio(setting.Attachment.Minio.Bucket, model.Path[len(setting.Attachment.Minio.Bucket)+1:])
if err != nil {
log.Info("Failed to query model size from minio. id=" + modeluuid)
}
size = getSize(files)
models.ModifyModelSize(modeluuid, size)
if strings.HasPrefix(model.Path, setting.Bucket+"/"+Model_prefix) {
files, err := storage.GetAllObjectByBucketAndPrefix(setting.Bucket, model.Path[len(setting.Bucket)+1:])
if err != nil {
log.Info("Failed to query model size from obs. id=" + modeluuid)
}
} else if model.Type == models.TypeCloudBrainTwo {
if strings.HasPrefix(model.Path, setting.Bucket+"/"+Model_prefix) {
files, err := storage.GetAllObjectByBucketAndPrefix(setting.Bucket, model.Path[len(setting.Bucket)+1:])
if err != nil {
log.Info("Failed to query model size from obs. id=" + modeluuid)
size = getSize(files)
models.ModifyModelSize(modeluuid, size)
modelFileName := objectName
index := strings.LastIndex(objectName, "/")
if index > 0 {
modelFileName = objectName[index+1:]
}
log.Info("modelFileName=" + modelFileName)
for _, file := range files {
log.Info("fileName=" + file.FileName)
if file.FileName == modelFileName {
modelFile := &models.AiModelFile{
ModelID: modeluuid,
Name: file.FileName,
Size: file.Size,
}
models.SaveModelFile(modelFile)
}
size = getSize(files)
models.ModifyModelSize(modeluuid, size)
}
}
if model.Size == 0 && size > 0 {
@@ -365,6 +403,14 @@ func SaveModel(ctx *context.Context) {
re := map[string]string{
"code": "-1",
}
isPrivate := ctx.QueryBool("isPrivate")
if ctx.Repo.Repository.IsPrivate {
if !isPrivate {
re["msg"] = "Private repo cannot create public model."
ctx.JSON(200, re)
return
}
}
if JobId == "" || VersionName == "" {
re["msg"] = "JobId or VersionName is null."
ctx.JSON(200, re)
@@ -427,10 +473,10 @@ func downloadModelFromCloudBrainTwo(modelUUID string, jobName string, parentDir
}

func downloadModelFromCloudBrainOne(modelUUID string, jobName string, parentDir string, trainUrl string, modelSelectedFile string) (string, int64, error) {
modelActualPath := storage.GetMinioPath(jobName, "/model/")
log.Info("modelActualPath=" + modelActualPath)
modelSrcPrefix := setting.CBCodePathPrefix + jobName + "/model/"
destKeyNamePrefix := Model_prefix + models.AttachmentRelativePath(modelUUID) + "/"

modelSrcPrefix := setting.CBCodePathPrefix + jobName + "/model/"
//destKeyNamePrefix := Model_prefix + models.AttachmentRelativePath(modelUUID) + "/"
bucketName := setting.Attachment.Minio.Bucket
log.Info("destKeyNamePrefix=" + destKeyNamePrefix + " modelSrcPrefix=" + modelSrcPrefix + " bucket=" + bucketName)
filterFiles := strings.Split(modelSelectedFile, ";")
@@ -442,13 +488,25 @@ func downloadModelFromCloudBrainOne(modelUUID string, jobName string, parentDir
if float64(totalSize) > setting.MaxModelSize*MODEL_MAX_SIZE {
return "", 0, errors.New("Cannot create model, as model is exceed " + fmt.Sprint(setting.MaxModelSize) + "G.")
}
size, err := storage.MinioCopyFiles(bucketName, modelSrcPrefix, destKeyNamePrefix, filterFiles)
if err == nil {
dataActualPath := bucketName + "/" + destKeyNamePrefix
return dataActualPath, size, nil
} else {
return "", 0, nil

for i, modelFile := range Files {
reader, err := storage.Attachments.DownloadAFile(bucketName, modelFile)
if err == nil {
defer reader.Close()
log.Info("upload to bucket=" + setting.Bucket + " objectKey=" + destKeyNamePrefix + filterFiles[i])
obsErr := storage.PutReaderToObs(setting.Bucket, destKeyNamePrefix+filterFiles[i], reader)
if obsErr != nil {
log.Info("upload to obs failed.err=" + obsErr.Error())
return "", 0, nil
}
}
}

//size, err := storage.MinioCopyFiles(bucketName, modelSrcPrefix, destKeyNamePrefix, filterFiles)
dataActualPath := setting.Bucket + "/" + destKeyNamePrefix
//dataActualPath := bucketName + "/" + destKeyNamePrefix
return dataActualPath, totalSize, nil

}
func DeleteModelFile(ctx *context.Context) {
log.Info("delete model start.")
@@ -458,47 +516,32 @@ func DeleteModelFile(ctx *context.Context) {
if err == nil {
var totalSize int64
if model.ModelType == MODEL_LOCAL_TYPE {
if model.Type == models.TypeCloudBrainOne {
bucketName := setting.Attachment.Minio.Bucket
objectName := model.Path[len(bucketName)+1:] + fileName
log.Info("delete bucket=" + bucketName + " path=" + objectName)
if strings.HasPrefix(model.Path, bucketName+"/"+Model_prefix) {
totalSize = storage.MinioGetFilesSize(bucketName, []string{objectName})
err := storage.Attachments.DeleteDir(objectName)
if err != nil {
log.Info("Failed to delete model. id=" + id)
re := map[string]string{
"code": "-1",
}
re["msg"] = err.Error()
ctx.JSON(200, re)
return
} else {
log.Info("delete minio file size is:" + fmt.Sprint(totalSize))
models.ModifyModelSize(id, model.Size-totalSize)

bucketName := setting.Bucket
objectName := model.Path[len(setting.Bucket)+1:] + fileName
log.Info("delete bucket=" + setting.Bucket + " path=" + objectName)
if strings.HasPrefix(model.Path, bucketName+"/"+Model_prefix) {
totalSize = storage.ObsGetFilesSize(bucketName, []string{objectName})
err := storage.ObsRemoveObject(bucketName, objectName)
if err != nil {
log.Info("Failed to delete model. id=" + id)
re := map[string]string{
"code": "-1",
}
}
} else if model.Type == models.TypeCloudBrainTwo {
bucketName := setting.Bucket
objectName := model.Path[len(setting.Bucket)+1:] + fileName
log.Info("delete bucket=" + setting.Bucket + " path=" + objectName)
if strings.HasPrefix(model.Path, bucketName+"/"+Model_prefix) {
totalSize = storage.ObsGetFilesSize(bucketName, []string{objectName})
err := storage.ObsRemoveObject(bucketName, objectName)
if err != nil {
log.Info("Failed to delete model. id=" + id)
re := map[string]string{
"code": "-1",
}
re["msg"] = err.Error()
ctx.JSON(200, re)
return
} else {
log.Info("delete obs file size is:" + fmt.Sprint(totalSize))
models.ModifyModelSize(id, model.Size-totalSize)
re["msg"] = err.Error()
ctx.JSON(200, re)
return
} else {
log.Info("delete obs file size is:" + fmt.Sprint(totalSize))
models.ModifyModelSize(id, model.Size-totalSize)
modelFile := &models.AiModelFile{
Name: fileName,
ModelID: id,
}
models.DeleteModelFile(modelFile)
}
}

}
if (model.Size - totalSize) <= 0 {
go repository.ResetRepoModelNum(model.RepoId)
@@ -534,24 +577,12 @@ func deleteModelByID(ctx *context.Context, id string) error {
}
if err == nil {

if model.Type == models.TypeCloudBrainOne {
bucketName := setting.Attachment.Minio.Bucket
log.Info("bucket=" + bucketName + " path=" + model.Path)
if strings.HasPrefix(model.Path, bucketName+"/"+Model_prefix) {
err := storage.Attachments.DeleteDir(model.Path[len(bucketName)+1:])
if err != nil {
log.Info("Failed to delete model. id=" + id)
return err
}
}
} else if model.Type == models.TypeCloudBrainTwo {
log.Info("bucket=" + setting.Bucket + " path=" + model.Path)
if strings.HasPrefix(model.Path, setting.Bucket+"/"+Model_prefix) {
err := storage.ObsRemoveObject(setting.Bucket, model.Path[len(setting.Bucket)+1:])
if err != nil {
log.Info("Failed to delete model. id=" + id)
return err
}
log.Info("bucket=" + setting.Bucket + " path=" + model.Path)
if strings.HasPrefix(model.Path, setting.Bucket+"/"+Model_prefix) {
err := storage.ObsRemoveObject(setting.Bucket, model.Path[len(setting.Bucket)+1:])
if err != nil {
log.Info("Failed to delete model. id=" + id)
return err
}
}

@@ -908,20 +939,37 @@ func QueryModelById(ctx *context.Context) {

func ShowSingleModel(ctx *context.Context) {
name := ctx.Query("name")

log.Info("Show single ModelInfo start.name=" + name)
models := models.QueryModelByName(name, ctx.Repo.Repository.ID)

userIds := make([]int64, len(models))
for i, model := range models {
modelArrays := models.QueryModelByName(name, ctx.Repo.Repository.ID)
modelResult := make([]*models.AiModelManage, 0)
isCanReadPrivateModel := isQueryPrivateModel(ctx)
userIds := make([]int64, len(modelArrays))
for i, model := range modelArrays {
model.IsCanOper = isOperModifyOrDelete(ctx, model.UserId)
model.IsCanDownload = isCanDownload(ctx, model)
model.IsCanDelete = isCanDelete(ctx, model.UserId)

model.RepoName = ctx.Repo.Repository.Name
model.RepoOwnerName = ctx.Repo.Repository.OwnerName
model.RepoDisplayName = ctx.Repo.Repository.DisplayName()

userIds[i] = model.UserId
if ctx.User != nil {
re := models.QueryModelCollectByUserId(model.ID, ctx.User.ID)
if re != nil && len(re) > 0 {
model.IsCollected = true
}
}
if model.IsPrivate {
if !isCanReadPrivateModel {
continue
}
}
modelResult = append(modelResult, model)
}
userNameMap := queryUserName(userIds)

for _, model := range models {
for _, model := range modelResult {
removeIpInfo(model)
value := userNameMap[model.UserId]
if value != nil {
@@ -929,7 +977,7 @@ func ShowSingleModel(ctx *context.Context) {
model.UserRelAvatarLink = value.RelAvatarLink()
}
}
ctx.JSON(http.StatusOK, models)
ctx.JSON(http.StatusOK, modelResult)
}

func removeIpInfo(model *models.AiModelManage) {
@@ -1002,6 +1050,7 @@ func SetModelCount(ctx *context.Context) {
New: MODEL_LATEST,
IsOnlyThisRepo: true,
Status: -1,
FrameFilter: -1,
IsQueryPrivate: isQueryPrivate,
})
ctx.Data["MODEL_COUNT"] = count
@@ -1140,6 +1189,7 @@ func ShowModelPageInfo(ctx *context.Context) {
IsOnlyThisRepo: true,
Status: -1,
IsQueryPrivate: isQueryPrivate,
FrameFilter: -1,
})
if err != nil {
ctx.ServerError("Cloudbrain", err)
@@ -1171,16 +1221,6 @@ func ShowModelPageInfo(ctx *context.Context) {
ctx.JSON(http.StatusOK, mapInterface)
}

func ModifyModel(id string, description string) error {
err := models.ModifyModelDescription(id, description)
if err == nil {
log.Info("modify success.")
} else {
log.Info("Failed to modify.id=" + id + " desc=" + description + " error:" + err.Error())
}
return err
}

func ModifyModelPrivate(ctx *context.Context) {
id := ctx.Query("id")
isPrivate := ctx.QueryBool("isPrivate")
@@ -1230,34 +1270,34 @@ func ModifyModelInfo(ctx *context.Context) {
ctx.JSON(200, re)
return
}
if task.ModelType == MODEL_LOCAL_TYPE {
name := ctx.Query("name")
label := ctx.Query("label")
description := ctx.Query("description")
engine := ctx.QueryInt("engine")
isPrivate := ctx.QueryBool("isPrivate")
aimodels := models.QueryModelByName(name, task.RepoId)
if aimodels != nil && len(aimodels) > 0 {
if len(aimodels) == 1 {
if aimodels[0].ID != task.ID {
re["msg"] = ctx.Tr("repo.model.manage.create_error")
ctx.JSON(200, re)
return
}
} else {

name := ctx.Query("name")
label := ctx.Query("label")
description := ctx.Query("description")
engine := ctx.QueryInt("engine")
isPrivate := ctx.QueryBool("isPrivate")
aimodels := models.QueryModelByName(name, task.RepoId)
if aimodels != nil && len(aimodels) > 0 {
if len(aimodels) == 1 {
if aimodels[0].ID != task.ID {
re["msg"] = ctx.Tr("repo.model.manage.create_error")
ctx.JSON(200, re)
return
}
} else {
re["msg"] = ctx.Tr("repo.model.manage.create_error")
ctx.JSON(200, re)
return
}
}
err = models.ModifyLocalModel(id, name, label, description, engine, isPrivate)
if task.Name != name {
aimodels = models.QueryModelByName(task.Name, task.RepoId)
if aimodels != nil && len(aimodels) > 0 {
for _, model := range aimodels {
models.ModifyLocalModel(model.ID, name, model.Label, model.Description, int(model.Engine), model.IsPrivate)
}
}
err = models.ModifyLocalModel(id, name, label, description, engine, isPrivate)

} else {
label := ctx.Query("label")
description := ctx.Query("description")
engine := task.Engine
name := task.Name
err = models.ModifyLocalModel(id, name, label, description, int(engine), task.IsPrivate)
}

if err != nil {
@@ -1288,11 +1328,12 @@ func QueryModelListForPredict(ctx *context.Context) {
PageSize: pageSize,
},
RepoID: repoId,
Type: ctx.QueryInt("type"),
Type: -1,
New: -1,
Status: 0,
IsOnlyThisRepo: true,
IsQueryPrivate: isQueryPrivate,
FrameFilter: -1,
})
if err != nil {
ctx.ServerError("Cloudbrain", err)
@@ -1349,26 +1390,27 @@ func QueryOneLevelModelFile(ctx *context.Context) {
model, err := models.QueryModelById(id)
if err != nil {
log.Error("no such model!", err.Error())
ctx.ServerError("no such model:", err)
ctx.JSON(http.StatusOK, nil)
return
}
ctx.JSON(http.StatusOK, queryOneLevelModelFile(model, parentDir))
}

func queryOneLevelModelFile(model *models.AiModelManage, parentDir string) []storage.FileInfo {
fileinfos := make([]storage.FileInfo, 0)
if model.Type == models.TypeCloudBrainTwo {
log.Info("TypeCloudBrainTwo list model file.")
prefix := model.Path[len(setting.Bucket)+1:]
fileinfos, _ := storage.GetOneLevelAllObjectUnderDir(setting.Bucket, prefix, parentDir)
if fileinfos == nil {
fileinfos = make([]storage.FileInfo, 0)
}
ctx.JSON(http.StatusOK, fileinfos)
prefix := model.Path[len(setting.Bucket)+1:] + parentDir
fileinfos, _ = storage.GetOneLevelAllObjectUnderDir(setting.Bucket, prefix, "")
} else if model.Type == models.TypeCloudBrainOne {
log.Info("TypeCloudBrainOne list model file.")
prefix := model.Path[len(setting.Attachment.Minio.Bucket)+1:]
fileinfos, _ := storage.GetOneLevelAllObjectUnderDirMinio(setting.Attachment.Minio.Bucket, prefix, parentDir)
if fileinfos == nil {
fileinfos = make([]storage.FileInfo, 0)
}
ctx.JSON(http.StatusOK, fileinfos)
prefix := model.Path[len(setting.Attachment.Minio.Bucket)+1:] + parentDir
fileinfos, _ = storage.GetOneLevelAllObjectUnderDirMinio(setting.Attachment.Minio.Bucket, prefix, "")
}
if fileinfos == nil {
fileinfos = make([]storage.FileInfo, 0)
}
return fileinfos
}

func CreateLocalModel(ctx *context.Context) {
@@ -1392,3 +1434,60 @@ func CreateOnlineModel(ctx *context.Context) {

ctx.HTML(200, tplCreateOnlineModelInfo)
}

func QueryModelCollectNum(ctx *context.Context) {
id := ctx.Query("id")
record := models.QueryModelCollectNum(id)
ctx.JSON(200, record)
}

func ModelCollect(ctx *context.Context) {
id := ctx.Query("id")
isCollected := ctx.QueryBool("collected")
re := map[string]string{
"code": "-1",
}
task, err := models.QueryModelById(id)
if err != nil || task == nil {
re["msg"] = err.Error()
log.Error("no such model!", err.Error())
ctx.JSON(200, re)
return
}
if ctx.User == nil {
re["msg"] = "user not login."
re["code"] = "401"
ctx.JSON(200, re)
return
}
record := models.QueryModelCollectByUserId(id, ctx.User.ID)
if isCollected {
if record == nil || len(record) == 0 {
log.Info("user collect the model.user id=" + fmt.Sprint(ctx.User.ID) + " model id=" + id)
err := models.SaveModelCollect(&models.AiModelCollect{
ModelID: id,
UserId: ctx.User.ID,
})
if err == nil {
re["code"] = "0"
} else {
re["msg"] = err.Error()
}
}
} else {
if record != nil && len(record) > 0 {
log.Info("user delete collect the model.user id=" + fmt.Sprint(ctx.User.ID) + " model id=" + id)
err := models.DeleteModelCollect(&models.AiModelCollect{
ID: record[0].ID,
})
if err == nil {
re["code"] = "0"
} else {
re["msg"] = err.Error()
}
}
}
num := models.QueryModelCollectNum(id)
models.ModifyModelCollectedNum(id, num)
ctx.JSON(200, re)
}

+ 515
- 0
routers/repo/ai_model_square.go View File

@@ -0,0 +1,515 @@
package repo

import (
"encoding/json"
"fmt"
"io/ioutil"
"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/markup/markdown"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/services/cloudbrain/modelmanage"
"code.gitea.io/gitea/services/repository"
)

const (
tplModelSquareIndex = "model/square/index"
tplModelSquareReadMe = "repo/modelmanage/readme"
tplModelFileList = "repo/modelmanage/filelist"
tplModelSetting = "repo/modelmanage/setting"
tplModelEvolutionMap = "repo/modelmanage/evolution_map"
README_FILE_NAME = "README.md"
)

type ModelMap struct {
Type int //0:repo; 1:model
IsParent bool
IsCurrent bool
RepoName string
RepoOwnerName string
RepoDisplayName string
RepoId int64
Model *models.AiModelManage
Next []*ModelMap
}

func ModelSquareTmpl(ctx *context.Context) {
ctx.HTML(200, tplModelSquareIndex)
}

func ModelSquareData(ctx *context.Context) {
log.Info("ShowModel Square Info start.")
page := ctx.QueryInt("page")
if page <= 0 {
page = 1
}
pageSize := ctx.QueryInt("pageSize")
if pageSize <= 0 {
pageSize = setting.UI.IssuePagingNum
}
isRecommend := ctx.QueryBool("recommend")
queryType := ctx.QueryInt("queryType")
labelFilter := ctx.Query("label")
frameFilterStr := ctx.Query("frame")
orderBy := ctx.Query("orderBy")
Namelike := ctx.Query("q")
TypeStr := ctx.Query("type")
needModelFile := ctx.QueryBool("needModelFile")
frameFilterInt := -1
if frameFilterStr != "" {
frameFilterInt, _ = strconv.Atoi(frameFilterStr)
}
notNeedEmpty := ctx.QueryBool("notNeedEmpty")
computeResourceFilter := ctx.Query("compute_resource")
var IsQueryPrivate bool
var user_id int64
var IsQueryCollect bool
var collected_user_id int64
var repo_id int64
var typeInt int
typeInt = -1
if TypeStr != "" {
//typeInt, _ = strconv.Atoi(TypeStr)
}
if queryType == 1 {
IsQueryPrivate = false
user_id = 0
} else if queryType == 2 {
IsQueryPrivate = true
if ctx.User == nil {
log.Info("the user not login.")
ctx.JSON(http.StatusOK, nil)
return
}
user_id = ctx.User.ID
} else if queryType == 3 {
IsQueryCollect = true
IsQueryPrivate = true
user_id = 0
if ctx.User == nil {
log.Info("the user not login.")
ctx.JSON(http.StatusOK, nil)
return
}
collected_user_id = ctx.User.ID
} else if queryType == 4 {
if ctx.User == nil {
log.Info("the user not login.")
ctx.JSON(http.StatusOK, nil)
return
}
IsQueryPrivate = true
repoName := ctx.Query("repoName")
repoOwnerName := ctx.Query("repoOwnerName")
repo, err := models.GetRepositoryByOwnerAndName(repoOwnerName, repoName)
if err == nil {
repo_id = repo.ID
} else {
log.Info("the repo is not exist.repoName=" + repoName + " repoOwnerName=" + repoOwnerName)
ctx.JSON(http.StatusOK, nil)
return
}
} else {
log.Info("not support")
ctx.JSON(http.StatusOK, nil)
return
}
SortType := "ai_model_manage.recommend desc,ai_model_manage.collected_count desc,ai_model_manage.download_count desc,ai_model_manage.reference_count desc"
if orderBy != "" {
SortType = "ai_model_manage." + orderBy + " DESC"
}
modelResult, count, err := models.QueryModel(&models.AiModelQueryOptions{
ListOptions: models.ListOptions{
Page: page,
PageSize: pageSize,
},
Type: typeInt,
New: -1,
Status: -1,
IsQueryPrivate: IsQueryPrivate,
IsRecommend: isRecommend,
UserID: user_id,
IsCollected: IsQueryCollect,
CollectedUserId: collected_user_id,
LabelFilter: labelFilter,
FrameFilter: frameFilterInt,
ComputeResourceFilter: computeResourceFilter,
Namelike: Namelike,
SortType: SortType,
RepoID: repo_id,
NotNeedEmpty: notNeedEmpty,
})
if err != nil {
ctx.ServerError("Cloudbrain", err)
return
}
userIds := make([]int64, len(modelResult))
modelIds := make([]string, len(modelResult))
repoIds := make([]int64, len(modelResult))
for i, model := range modelResult {
userIds[i] = model.UserId
modelIds[i] = model.ID
repoIds[i] = model.RepoId
}
repoInfo, err := queryRepoInfoByIds(repoIds)
userNameMap := queryUserName(userIds)
var modelCollect map[string]*models.AiModelCollect
if ctx.User != nil && queryType != 4 {
modelCollect = models.QueryModelCollectedStatus(modelIds, ctx.User.ID)
}

for _, model := range modelResult {
//removeIpInfo(model)
model.TrainTaskInfo = ""
value := userNameMap[model.UserId]
if value != nil {
model.UserName = value.Name
model.UserRelAvatarLink = value.RelAvatarLink()
}
if repoInfo != nil {
repo := repoInfo[model.RepoId]
if repo != nil {
model.RepoName = repo.Name
model.RepoOwnerName = repo.OwnerName
model.RepoDisplayName = repo.DisplayName()
}
}
if ctx.User != nil && modelCollect != nil {
value := modelCollect[model.ID]
if value != nil {
model.IsCollected = true
}
} else {
model.IsCollected = false
}
if needModelFile && len(model.Path) > 0 {
//查询模型文件列表
model.ModelFileList = modelmanage.QueryModelFileByModel(model)
}
}
mapInterface := make(map[string]interface{})
mapInterface["data"] = modelResult
mapInterface["count"] = count
ctx.JSON(http.StatusOK, mapInterface)
}

func queryRepoInfoByIds(intSlice []int64) (map[int64]*models.Repository, error) {
keys := make(map[int64]string)
uniqueElements := []int64{}
for _, entry := range intSlice {
if _, value := keys[entry]; !value {
keys[entry] = ""
uniqueElements = append(uniqueElements, entry)
}
}
re, err := models.GetRepositoriesMapByIDs(uniqueElements)
return re, err
}

func ModelReadMeTmpl(ctx *context.Context) {
ctx.HTML(200, tplModelSquareReadMe)
}

func ModelFileListTmpl(ctx *context.Context) {
ctx.HTML(200, tplModelFileList)
}

func ModelFileSettingTmpl(ctx *context.Context) {
ctx.HTML(200, tplModelSetting)
}

func ModelEvolutionMapTmpl(ctx *context.Context) {
ctx.HTML(200, tplModelEvolutionMap)
}

func setModelUser(model *models.AiModelManage) {
user, err := models.GetUserByID(model.UserId)
if err == nil {
model.UserName = user.Name
model.UserRelAvatarLink = user.RelAvatarLink()
}
}

func setModelRepo(model *models.AiModelManage) {
repo, err := models.GetRepositoryByID(model.RepoId)
if err == nil {
model.RepoName = repo.Name
model.RepoOwnerName = repo.OwnerName
model.RepoDisplayName = repo.DisplayName()
}
}

func ModelEvolutionMapData(ctx *context.Context) {
id := ctx.Query("id")
model, err := models.QueryModelById(id)
re := map[string]interface{}{
"code": "-1",
}
if err == nil {
removeIpInfo(model)
repo, err := models.GetRepositoryByID(model.RepoId)
model.RepoName = repo.Name
model.RepoOwnerName = repo.OwnerName
model.RepoDisplayName = repo.DisplayName()
if err == nil {
setModelUser(model)
currentNode := &ModelMap{
Type: 1,
IsCurrent: true,
RepoName: repo.Name,
RepoOwnerName: repo.OwnerName,
RepoDisplayName: repo.DisplayName(),
RepoId: repo.ID,
Model: model,
}
ParentNode := findParent(model)
if ParentNode != nil {
nexts := make([]*ModelMap, 0)
nexts = append(nexts, currentNode)
ParentNode.Next = nexts
} else {
ParentNode = currentNode
}
findChild(currentNode)
re["code"] = "0"
re["node"] = ParentNode
ctx.JSON(200, ParentNode)
}
} else {
re["msg"] = "No such model."
ctx.JSON(200, re)
}
}

func findParent(model *models.AiModelManage) *ModelMap {
if model.TrainTaskInfo != "" {
var task models.Cloudbrain
err := json.Unmarshal([]byte(model.TrainTaskInfo), &task)
if err != nil {
log.Info("error=" + err.Error())
} else {
log.Info("find parent model name." + task.ModelName)
if task.ModelName != "" {
if task.ModelId != "" {
parentModel, err := models.QueryModelById(task.ModelId)
setModelRepo(parentModel)
setModelUser(parentModel)
if err == nil {
re := &ModelMap{
Type: 1,
IsParent: true,
Model: parentModel,
}
return re
}
} else {
modelList := models.QueryModelByName(task.ModelName, task.RepoID)
if modelList != nil && len(modelList) > 0 {
for _, parentModel := range modelList {
setModelUser(parentModel)
setModelRepo(parentModel)
if parentModel.Version == task.ModelVersion {
re := &ModelMap{
Type: 1,
IsParent: true,
Model: parentModel,
}
return re
}
}
}
}
}
}
}
return nil
}

func findChild(currentNode *ModelMap) {
log.Info("find child start.")
if currentNode.Model != nil {
currentModel := currentNode.Model
re, err := models.GetCloudBrainByModelId(currentModel.ID)
if err != nil || len(re) == 0 {
re, err = models.GetCloudBrainByRepoIdAndModelName(currentModel.RepoId, currentModel.Name)
}
log.Info("start to load child model.")
if err == nil && len(re) > 0 {
repoNodes := getRepoNodes(re)
currentNode.Next = repoNodes
for _, node := range repoNodes {
childModels := models.QueryModelByRepoId(node.RepoId)
childNodes := make([]*ModelMap, 0)
if childModels != nil && len(childModels) > 0 {
for _, childModel := range childModels {
if childModel.TrainTaskInfo != "" {
childModel.RepoName = node.RepoName
childModel.RepoOwnerName = node.RepoOwnerName
childModel.RepoDisplayName = node.RepoDisplayName
log.Info("childModel.RepoName=" + childModel.RepoName)
log.Info("childModel.RepoOwnerName=" + childModel.RepoOwnerName)
var task models.Cloudbrain
err := json.Unmarshal([]byte(childModel.TrainTaskInfo), &task)
if err != nil {
log.Info("error=" + err.Error())
} else {
log.Info("task.ModelId=%v,currentModel.ID=%v", task.ModelId, currentModel.ID)
if task.ModelId != "" && task.ModelId == currentModel.ID {
setModelUser(childModel)
modelMap := &ModelMap{
Type: 1,
Model: childModel,
}
childNodes = append(childNodes, modelMap)
} else {
log.Info("task.ModelName=%v,currentModel.Name=%v", task.ModelName, currentModel.Name)
log.Info("task.ModelVersion=%v,currentModel.Version=%v", task.ModelVersion, currentModel.Version)
if task.ModelName == currentModel.Name && task.ModelVersion == currentModel.Version {
setModelUser(childModel)
modelMap := &ModelMap{
Type: 1,
Model: childModel,
}
childNodes = append(childNodes, modelMap)
}
}
}
}
}

}
node.Next = childNodes
for _, child := range childNodes {
findChild(child)
}
}
}
} else {
log.Info("the current model is nil.")
}

}

func getRepoNodes(re []*models.Cloudbrain) []*ModelMap {
result := make([]*ModelMap, 0)
repoMap := make(map[int64]string, 0)
for _, task := range re {
repo, err := models.GetRepositoryByID(task.RepoID)
if err == nil {
if _, ok := repoMap[repo.ID]; !ok {
modelMap := &ModelMap{
Type: 0,
RepoName: repo.Name,
RepoOwnerName: repo.OwnerName,
RepoDisplayName: repo.DisplayName(),
RepoId: repo.ID,
}
result = append(result, modelMap)
}
repoMap[repo.ID] = "true"
}
}
return result
}

func ModifyModelReadMe(ctx *context.Context) {
id := ctx.Query("id")
model, err := models.QueryModelById(id)
re := map[string]string{
"code": "-1",
}
if err == nil {
content := ctx.Query("content")
path := Model_prefix + models.AttachmentRelativePath(id) + "/"
if model.Type == models.TypeCloudBrainTwo {
err = storage.PutStringToObs(setting.Bucket, path+README_FILE_NAME, content)
if err != nil {
re["msg"] = "Failed to created readme file."
log.Info("Failed to created readme file. as:" + err.Error())
} else {
re["code"] = "0"
}
} else {
re["msg"] = "Cannot support the model type=" + fmt.Sprint(model.Type)
}
ctx.JSON(200, re)
} else {
re["msg"] = "No such model."
ctx.JSON(200, re)
}
}

func QueryModelReadMe(ctx *context.Context) {
id := ctx.Query("id")
model, err := models.QueryModelById(id)
re := map[string]string{
"code": "-1",
}
if err == nil {
files := queryOneLevelModelFile(model, "")
find := false
var content []byte
for _, file := range files {
if strings.ToLower(file.FileName) == strings.ToLower(README_FILE_NAME) {
find = true
path := Model_prefix + models.AttachmentRelativePath(id) + "/"
body, err := storage.ObsDownloadAFile(setting.Bucket, path+file.FileName)
if err != nil {
log.Info("download file failed: %s\n", err.Error())
break
} else {
defer body.Close()
content, err = ioutil.ReadAll(body)
}
}
}
if find {
re["isExistMDFile"] = "true"
re["fileName"] = README_FILE_NAME
strc := string(content)
re["content"] = strc
re["htmlcontent"] = string(markdown.RenderRaw([]byte(strc), "", false))
} else {
re["isExistMDFile"] = "false"
re["fileName"] = README_FILE_NAME
url := setting.RecommentRepoAddr + "model/" + README_FILE_NAME
result, err := repository.RecommendContentFromPromote(url)
if err == nil {
re["content"] = result
re["htmlcontent"] = string(markdown.RenderRaw([]byte(result), "", false))
}
}
re["code"] = "0"
ctx.JSON(200, re)
} else {
re["msg"] = "No such model."
ctx.JSON(200, re)
}

}

func QueryModelLabel(ctx *context.Context) {
url := setting.RecommentRepoAddr + "model/label.json"
result, err := repository.RecommendContentFromPromote(url)
log.Info("label result=" + result)
remap := make([]map[string]string, 0)
if err == nil {
err = json.Unmarshal([]byte(result), &remap)
if err != nil {
log.Info("error=" + err.Error())
}
} else {
log.Info("error=" + err.Error())
}
if err == nil {
ctx.JSON(200, remap)
} else {
ctx.JSON(200, "")
}
}

+ 13
- 6
routers/repo/aisafety.go View File

@@ -669,7 +669,7 @@ func createForNPU(ctx *context.Context, jobName string) error {
log.Info("engine_id=" + fmt.Sprint(engineID))
poolID := ctx.Query("pool_id")
repo := ctx.Repo.Repository
modelId := ctx.Query("model_id")
trainUrl := ctx.Query("pre_train_model_url")
modelName := ctx.Query("model_name")
modelVersion := ctx.Query("model_version")
@@ -814,6 +814,7 @@ func createForNPU(ctx *context.Context, jobName string) error {
ModelName: modelName,
ModelVersion: modelVersion,
CkptName: ckptName,
ModelId: modelId,
ResultUrl: resultObsPath,
Spec: spec,
DatasetName: datasetNames,
@@ -839,13 +840,14 @@ func createForGPU(ctx *context.Context, jobName string) error {
evaluationIndex := ctx.Query("evaluation_index")
Params := ctx.Query("run_para_list")
specId := ctx.QueryInt64("spec_id")
TrainUrl := ctx.Query("pre_train_model_url")
//TrainUrl := ctx.Query("pre_train_model_url")
CkptName := ctx.Query("ckpt_name")
modelName := ctx.Query("model_name")
modelId := ctx.Query("model_id")
modelVersion := ctx.Query("model_version")

ckptUrl := setting.Attachment.Minio.RealPath + TrainUrl + CkptName
log.Info("ckpt url:" + ckptUrl)
//ckptUrl := setting.Attachment.Minio.RealPath + TrainUrl + CkptName
//log.Info("ckpt url:" + ckptUrl)
spec, err := resource.GetAndCheckSpec(ctx.User.ID, specId, models.FindSpecsOptions{
JobType: models.JobTypeBenchmark,
ComputeResource: models.GPU,
@@ -891,7 +893,11 @@ func createForGPU(ctx *context.Context, jobName string) error {
return errors.New(ctx.Tr("cloudbrain.error.dataset_select"))
}
log.Info("Command=" + command)

minioPreModelURL, err := dealModelInfo(modelId, jobName, CkptName)
if err != nil {
log.Error("Can not find model", err)
return errors.New(ctx.Tr("repo.modelconvert.manage.model_not_exist"))
}
req := cloudbrain.GenerateCloudBrainTaskReq{
Ctx: ctx,
DisplayJobName: displayJobName,
@@ -902,7 +908,7 @@ func createForGPU(ctx *context.Context, jobName string) error {
DatasetNames: datasetNames,
DatasetInfos: datasetInfos,
CodePath: storage.GetMinioPath(jobName, cloudbrain.CodeMountPath+"/"),
ModelPath: setting.Attachment.Minio.RealPath + TrainUrl,
ModelPath: setting.Attachment.Minio.RealPath + minioPreModelURL,
BenchmarkPath: storage.GetMinioPath(jobName, cloudbrain.BenchMarkMountPath+"/"),
Snn4ImageNetPath: storage.GetMinioPath(jobName, cloudbrain.Snn4imagenetMountPath+"/"),
BrainScorePath: storage.GetMinioPath(jobName, cloudbrain.BrainScoreMountPath+"/"),
@@ -915,6 +921,7 @@ func createForGPU(ctx *context.Context, jobName string) error {
ModelName: modelName,
ModelVersion: modelVersion,
CkptName: CkptName,
ModelId: modelId,
ResultPath: storage.GetMinioPath(jobName, cloudbrain.ResultPath+"/"),
Spec: spec,
LabelName: evaluationIndex,


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

@@ -314,7 +314,7 @@ func CompleteModelMultipart(ctx *context.Context) {
return
}
//更新模型大小信息
UpdateModelSize(modeluuid)
UpdateModelSize(modeluuid, fileChunk.ObjectName)

ctx.JSON(200, map[string]string{
"result_code": "0",


+ 77
- 20
routers/repo/cloudbrain.go View File

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

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

"code.gitea.io/gitea/services/lock"

cloudbrainService "code.gitea.io/gitea/services/cloudbrain"
@@ -152,6 +150,7 @@ func cloudBrainNewDataPrepare(ctx *context.Context, jobType string) error {
ctx.Data["model_name"] = ctx.Cloudbrain.ModelName
ctx.Data["label_name"] = ctx.Cloudbrain.LabelName
ctx.Data["ckpt_name"] = ctx.Cloudbrain.CkptName
ctx.Data["model_id"] = ctx.Cloudbrain.ModelId
ctx.Data["model_version"] = ctx.Cloudbrain.ModelVersion
ctx.Data["pre_train_model_url"] = ctx.Cloudbrain.PreTrainModelUrl
ctx.Data["compute_resource"] = ctx.Cloudbrain.ComputeResource
@@ -390,19 +389,21 @@ func cloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) {
}

if form.ModelName != "" { //使用预训练模型训练
_, err := models.QueryModelByPath(form.PreTrainModelUrl)

req.ModelName = form.ModelName
req.LabelName = form.LabelName
req.CkptName = form.CkptName
req.ModelId = form.ModelId
req.ModelVersion = form.ModelVersion
minioPreModelURL, err := dealModelInfo(form.ModelId, jobName, form.CkptName)
if err != nil {
log.Error("Can not find model", err)
cloudBrainNewDataPrepare(ctx, jobType)
ctx.RenderWithErr(ctx.Tr("repo.modelconvert.manage.model_not_exist"), tpl, &form)
return
}
req.ModelName = form.ModelName
req.LabelName = form.LabelName
req.CkptName = form.CkptName
req.ModelVersion = form.ModelVersion
req.PreTrainModelPath = setting.Attachment.Minio.RealPath + form.PreTrainModelUrl
req.PreTrainModelUrl = form.PreTrainModelUrl
req.PreTrainModelPath = setting.Attachment.Minio.RealPath + minioPreModelURL
req.PreTrainModelUrl = minioPreModelURL
}

if form.IsContinue { // qizhi GPU 继续训练,将旧任务输出文件拷贝至新任务输出路径
@@ -430,6 +431,43 @@ func cloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) {
}
}

func dealModelInfo(modelId string, jobName string, ckptName string) (string, error) {
preModel, err := models.QueryModelById(modelId)
if err != nil {
log.Error("Can not find model", err)
return "", err
}
minioPreModelURL, err := downloadModelFromObs(preModel, jobName, cloudbrain.PretrainModelMountPath, ckptName)
if err != nil {
log.Error("Can not find model", err)

return "", err
}
return minioPreModelURL, nil
}

func downloadModelFromObs(preModel *models.AiModelManage, jobName, suffixPath string, ckptFileName string) (string, error) {
destPath := setting.CBCodePathPrefix + jobName + suffixPath + "/"
destFile := destPath + ckptFileName
returnStr := setting.Attachment.Minio.Bucket + "/" + destPath
srcUrl := preModel.Path[len(setting.Bucket)+1:] + ckptFileName
log.Info("dest model Path=" + returnStr + " src path=" + preModel.Path + ckptFileName)
body, err := storage.ObsDownloadAFile(setting.Bucket, srcUrl)
if err == nil {
defer body.Close()
_, err = storage.Attachments.UploadContent(setting.Attachment.Minio.Bucket, destFile, body)
if err != nil {
log.Error("UploadObject(%s) failed: %s", preModel.Path+ckptFileName, err.Error())
return "", err
}
} else {
log.Info("download model failed. as " + err.Error())
return "", err
}
log.Info("download model from obs succeed")
return returnStr, nil
}

func MinioCopyResults(srcPath string, destPath string) error {
log.Info("prev task obs path:", setting.Attachment.Minio.Bucket+srcPath)
log.Info("current task obs path:", setting.Attachment.Minio.Bucket+destPath)
@@ -531,8 +569,8 @@ func CloudBrainInferenceJobCreate(ctx *context.Context, form auth.CreateCloudBra
return
}

ckptUrl := setting.Attachment.Minio.RealPath + form.TrainUrl + form.CkptName
log.Info("ckpt url:" + ckptUrl)
//ckptUrl := setting.Attachment.Minio.RealPath + form.TrainUrl + form.CkptName
//log.Info("ckpt url:" + ckptUrl)
command, err := getInferenceJobCommand(form)
if err != nil {
log.Error("getTrainJobCommand failed: %v", err)
@@ -607,6 +645,14 @@ func CloudBrainInferenceJobCreate(ctx *context.Context, form auth.CreateCloudBra
return
}

minioPreModelURL, err := dealModelInfo(form.ModelId, jobName, form.CkptName)
if err != nil {
log.Error("Can not find model", err)
cloudBrainNewDataPrepare(ctx, jobType)
ctx.RenderWithErr(ctx.Tr("repo.modelconvert.manage.model_not_exist"), tpl, &form)
return
}

req := cloudbrain.GenerateCloudBrainTaskReq{
Ctx: ctx,
DisplayJobName: displayJobName,
@@ -617,7 +663,7 @@ func CloudBrainInferenceJobCreate(ctx *context.Context, form auth.CreateCloudBra
DatasetNames: datasetNames,
DatasetInfos: datasetInfos,
CodePath: storage.GetMinioPath(jobName, cloudbrain.CodeMountPath+"/"),
ModelPath: setting.Attachment.Minio.RealPath + form.TrainUrl,
ModelPath: setting.Attachment.Minio.RealPath + minioPreModelURL,
BenchmarkPath: storage.GetMinioPath(jobName, cloudbrain.BenchMarkMountPath+"/"),
Snn4ImageNetPath: storage.GetMinioPath(jobName, cloudbrain.Snn4imagenetMountPath+"/"),
BrainScorePath: storage.GetMinioPath(jobName, cloudbrain.BrainScoreMountPath+"/"),
@@ -631,7 +677,8 @@ func CloudBrainInferenceJobCreate(ctx *context.Context, form auth.CreateCloudBra
ModelName: form.ModelName,
ModelVersion: form.ModelVersion,
CkptName: form.CkptName,
TrainUrl: form.TrainUrl,
ModelId: form.ModelId,
TrainUrl: form.PreTrainModelUrl,
LabelName: labelName,
Spec: spec,
}
@@ -740,11 +787,11 @@ func CloudBrainRestart(ctx *context.Context) {
break
}
}
if !modelmanage.HasModelFile(task) {
resultCode = "-1"
errorMsg = ctx.Tr("repo.debug.manage.model_not_exist")
break
}
// if !modelmanage.HasModelFile(task) {
// resultCode = "-1"
// errorMsg = ctx.Tr("repo.debug.manage.model_not_exist")
// break
// }

if hasDatasetDeleted(task) {
resultCode = "-1"
@@ -2734,8 +2781,18 @@ func ModelBenchmarkCreate(ctx *context.Context, form auth.CreateCloudBrainForm)
req.LabelName = form.LabelName
req.CkptName = form.CkptName
req.ModelVersion = form.ModelVersion
req.PreTrainModelPath = setting.Attachment.Minio.RealPath + form.PreTrainModelUrl
req.PreTrainModelUrl = form.PreTrainModelUrl
req.ModelId = form.ModelId

minioPreModelURL, err := dealModelInfo(form.ModelId, jobName, form.CkptName)
if err != nil {
log.Error("Can not find model", err)
cloudBrainNewDataPrepare(ctx, jobType)
ctx.RenderWithErr(ctx.Tr("repo.modelconvert.manage.model_not_exist"), tpl, &form)
return
}

req.PreTrainModelPath = setting.Attachment.Minio.RealPath + minioPreModelURL
req.PreTrainModelUrl = minioPreModelURL
}

_, err = cloudbrain.GenerateTask(req)


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

@@ -303,7 +303,7 @@ func GrampusNotebookCreate(ctx *context.Context, form auth.CreateGrampusNotebook

if form.ModelName != "" { //使用预训练模型训练

m, err := models.QueryModelByPath(form.PreTrainModelUrl)
m, err := models.QueryModelById(form.ModelId)
if err != nil {
log.Error("Can not find model", err)
grampusNotebookNewDataPrepare(ctx, processType)
@@ -319,7 +319,9 @@ func GrampusNotebookCreate(ctx *context.Context, form auth.CreateGrampusNotebook
req.ModelName = form.ModelName
req.LabelName = form.LabelName
req.CkptName = form.CkptName
req.ModelId = form.ModelId
req.ModelVersion = form.ModelVersion

req.PreTrainModelUrl = form.PreTrainModelUrl
req.PreTrainModelPath = getPreTrainModelPath(form.PreTrainModelUrl, form.CkptName)
req.ModelStorageType = m.Type
@@ -464,6 +466,7 @@ func grampusTrainJobNewDataPrepare(ctx *context.Context, processType string) err

ctx.Data["model_version"] = ctx.Cloudbrain.ModelVersion
ctx.Data["ckpt_name"] = ctx.Cloudbrain.CkptName
ctx.Data["model_id"] = ctx.Cloudbrain.ModelId
ctx.Data["label_names"] = ctx.Cloudbrain.LabelName
ctx.Data["pre_train_model_url"] = ctx.Cloudbrain.PreTrainModelUrl
spec, _ := resource.GetCloudbrainSpec(ctx.Cloudbrain.ID)
@@ -751,9 +754,9 @@ func grampusTrainJobGpuCreate(ctx *context.Context, form auth.CreateGrampusTrain
req.ModelName = form.ModelName
req.LabelName = form.LabelName
req.CkptName = form.CkptName
req.ModelId = form.ModelId
req.ModelVersion = form.ModelVersion
req.PreTrainModelUrl = form.PreTrainModelUrl

}

_, err = grampus.GenerateTrainJob(ctx, req)
@@ -980,7 +983,7 @@ func grampusTrainJobGcuCreate(ctx *context.Context, form auth.CreateGrampusTrain
req.CkptName = form.CkptName
req.ModelVersion = form.ModelVersion
req.PreTrainModelUrl = form.PreTrainModelUrl
req.ModelId = form.ModelId
}

_, err = grampus.GenerateTrainJob(ctx, req)
@@ -1241,6 +1244,7 @@ func grampusTrainJobNpuCreate(ctx *context.Context, form auth.CreateGrampusTrain
req.ModelName = form.ModelName
req.LabelName = form.LabelName
req.CkptName = form.CkptName
req.ModelId = form.ModelId
req.ModelVersion = form.ModelVersion
req.PreTrainModelUrl = form.PreTrainModelUrl
req.PreTrainModelPath = preTrainModelPath


+ 22
- 3
routers/repo/modelarts.go View File

@@ -282,6 +282,7 @@ func Notebook2Create(ctx *context.Context, form auth.CreateModelArtsNotebookForm
req.ModelName = form.ModelName
req.LabelName = form.LabelName
req.CkptName = form.CkptName
req.ModelId = form.ModelId
req.ModelVersion = form.ModelVersion
req.PreTrainModelUrl = form.PreTrainModelUrl

@@ -630,6 +631,7 @@ func NotebookRestart(ctx *context.Context) {
LabelName: task.LabelName,
PreTrainModelUrl: task.PreTrainModelUrl,
CkptName: task.CkptName,
ModelId: task.ModelId,
}

err = models.RestartCloudbrain(task, newTask)
@@ -1052,6 +1054,7 @@ func trainJobNewVersionDataPrepare(ctx *context.Context) error {
ctx.Data["model_name"] = task.ModelName
ctx.Data["model_version"] = task.ModelVersion
ctx.Data["ckpt_name"] = task.CkptName
ctx.Data["model_id"] = ctx.Cloudbrain.ModelId
ctx.Data["label_names"] = task.LabelName
ctx.Data["pre_train_model_url"] = task.PreTrainModelUrl

@@ -1351,6 +1354,7 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm)
req.ModelName = form.ModelName
req.LabelName = form.LabelName
req.CkptName = form.CkptName
req.ModelId = form.ModelId
req.ModelVersion = form.ModelVersion
req.PreTrainModelUrl = form.PreTrainModelUrl

@@ -1746,6 +1750,7 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ
req.ModelName = form.ModelName
req.LabelName = form.LabelName
req.CkptName = form.CkptName
req.ModelId = form.ModelId
req.ModelVersion = form.ModelVersion
req.PreTrainModelUrl = form.PreTrainModelUrl

@@ -2157,11 +2162,11 @@ func InferenceJobCreate(ctx *context.Context, form auth.CreateModelArtsInference
LabelName := form.LabelName
isLatestVersion := modelarts.IsLatestVersion
VersionCount := modelarts.VersionCountOne
trainUrl := form.TrainUrl
trainUrl := form.PreTrainModelUrl
modelName := form.ModelName
modelVersion := form.ModelVersion
ckptName := form.CkptName
ckptUrl := "/" + form.TrainUrl + form.CkptName
ckptUrl := "/" + form.PreTrainModelUrl + form.CkptName
log.Info("ckpt url:" + ckptUrl)

errStr := checkInferenceJobMultiNode(ctx.User.ID, form.WorkServerNumber)
@@ -2376,6 +2381,7 @@ func InferenceJobCreate(ctx *context.Context, form auth.CreateModelArtsInference
ModelName: modelName,
ModelVersion: modelVersion,
CkptName: ckptName,
ModelId: form.ModelId,
ResultUrl: resultObsPath,
Spec: spec,
DatasetName: datasetNames,
@@ -2493,6 +2499,18 @@ func InferenceJobIndex(ctx *context.Context) {
if tasks[i].ComputeResource == "" {
tasks[i].ComputeResource = models.NPUResource
}
if tasks[i].ModelId != "" {
model, err := models.QueryModelById(tasks[i].ModelId)
if err == nil && model != nil {
if model.RepoId != tasks[i].RepoID {
repo, err := models.GetRepositoryByID(model.RepoId)
if err == nil && repo != nil {
tasks[i].ModelRepoName = repo.Name
tasks[i].ModelRepoOwnerName = repo.OwnerName
}
}
}
}
}
isQueryPrivate := isQueryPrivateModel(ctx)
repoId := ctx.Repo.Repository.ID
@@ -2671,7 +2689,8 @@ func inferenceJobErrorNewDataPrepare(ctx *context.Context, form auth.CreateModel
ctx.Data["model_name"] = form.ModelName
ctx.Data["model_version"] = form.ModelVersion
ctx.Data["ckpt_name"] = form.CkptName
ctx.Data["train_url"] = form.TrainUrl
ctx.Data["model_id"] = form.ModelId
ctx.Data["pre_train_model_url"] = form.PreTrainModelUrl
ctx.Data["datasetType"] = models.TypeCloudBrainTwo
waitCount := cloudbrain.GetWaitingCloudbrainCount(models.TypeCloudBrainTwo, "")
ctx.Data["WaitCount"] = waitCount


+ 3
- 3
routers/repo/user_data_analysis.go View File

@@ -721,10 +721,10 @@ func TimingCountDataByDateAndReCount(date string, isReCount bool) {
log.Info("endTime time:" + endTime.Format("2006-01-02 15:04:05"))
warnEmailMessage := "用户统计信息入库失败,请尽快定位。"

startYear := time.Date(USER_YEAR, 1, 1, 0, 0, 0, 1, t.Location())
endYear := startYear.AddDate(1, 0, 0)
//startYear := time.Date(USER_YEAR, 1, 1, 0, 0, 0, 1, t.Location())
//endYear := startYear.AddDate(1, 0, 0)

models.RefreshUserYearTable(startYear, endYear)
//models.RefreshUserYearTable(startYear, endYear)

//query wiki data
log.Info("start to time count data")


+ 24
- 4
routers/routes/routes.go View File

@@ -366,6 +366,13 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/home/notice", routers.HomeNoticeTmpl)
m.Get("/home/privacy", routers.HomePrivacy)

m.Group("/modelsquare", func() {
m.Get("/main", repo.ModelSquareTmpl)
m.Get("/main_query_data", repo.ModelSquareData)
m.Put("/modify_model_collect", repo.ModelCollect)
m.Get("/main_query_label", repo.QueryModelLabel)
})

m.Group("/extension", func() {
// m.Get("", modelapp.ModelMainPage)
m.Get("/tuomin/upload", modelapp.ProcessImageUI)
@@ -610,6 +617,10 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Put("/:id/action/:action", admin.DatasetAction)
// m.Post("/delete", admin.DeleteDataset)
})
m.Group("/model", func() {
m.Get("", admin.AdminModelManage)
m.Put("/action", admin.ModifyModelRecommend)
})
m.Group("/cloudbrains", func() {
m.Get("", admin.CloudBrains)
m.Get("/download", admin.DownloadCloudBrains)
@@ -1309,20 +1320,29 @@ func RegisterRoutes(m *macaron.Macaron) {
})
}, context.RepoRef())
m.Group("/modelmanage", func() {
m.Get("/create_local_model_1", repo.CreateLocalModel)
m.Get("/create_local_model_2", repo.CreateLocalModelForUpload)
m.Get("/create_local_model", repo.CreateLocalModel)
m.Get("/create_online_model", repo.CreateOnlineModel)
m.Post("/create_local_model", repo.SaveLocalModel)
m.Delete("/delete_model_file", repo.DeleteModelFile)

m.Get("/model_readme_tmpl", repo.ModelReadMeTmpl)
m.Get("/model_readme_data", repo.QueryModelReadMe)
m.Post("/model_readme_data", repo.ModifyModelReadMe)
m.Get("/model_filelist_tmpl", repo.ModelFileListTmpl)
m.Get("/model_fileupload_tmpl", repo.CreateLocalModelForUpload)
m.Get("/model_setting", repo.ModelFileSettingTmpl)
m.Get("/model_evolution_map", repo.ModelEvolutionMapTmpl)
m.Get("/model_evolution_map_data", repo.ModelEvolutionMapData)

m.Post("/create_model", repo.SaveModel)
m.Post("/create_model_convert", reqWechatBind, reqRepoModelManageWriter, repo.SaveModelConvert)
m.Post("/create_new_model", repo.SaveNewNameModel)
m.Delete("/delete_model", repo.DeleteModel)
m.Post("/delete_model_convert/:id", repo.DeleteModelConvert)
m.Post("/convert_stop/:id", repo.StopModelConvert)
m.Put("/modify_model", repo.ModifyModelInfo)
m.Put("/modify_model_status", repo.ModifyModelPrivate)
m.Put("/modify_model", reqRepoModelManageWriter, repo.ModifyModelInfo)
m.Put("/modify_model_status", reqRepoModelManageWriter, repo.ModifyModelPrivate)
m.Get("/show_model_collect_num", reqRepoModelManageReader, repo.QueryModelCollectNum)
m.Get("/show_model", reqRepoModelManageReader, repo.ShowModelTemplate)
m.Get("/convert_model", reqRepoModelManageReader, repo.ConvertModelTemplate)
m.Get("/show_model_info", repo.ShowModelInfo)


+ 51
- 3
services/cloudbrain/cloudbrainTask/inference.go View File

@@ -2,8 +2,6 @@ package cloudbrainTask

import (
"bufio"
cloudbrainService "code.gitea.io/gitea/services/cloudbrain"
"code.gitea.io/gitea/services/lock"
"encoding/json"
"errors"
"io"
@@ -15,6 +13,9 @@ import (
"strings"
"unicode/utf8"

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

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

"code.gitea.io/gitea/modules/git"
@@ -147,6 +148,14 @@ func CloudBrainInferenceJobCreate(ctx *context.Context, option api.CreateTrainJo
return
}

minioPreModelURL, err := dealModelInfo(option.ModelId, jobName, option.CkptName)
if err != nil {
log.Error("Can not find model", err)
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.modelconvert.manage.model_not_exist")))
//ctx.RenderWithErr(ctx.Tr("repo.modelconvert.manage.model_not_exist"), tpl, &form)
return
}

req := cloudbrain.GenerateCloudBrainTaskReq{
Ctx: ctx,
DisplayJobName: displayJobName,
@@ -157,7 +166,7 @@ func CloudBrainInferenceJobCreate(ctx *context.Context, option api.CreateTrainJo
DatasetNames: datasetNames,
DatasetInfos: datasetInfos,
CodePath: storage.GetMinioPath(jobName, cloudbrain.CodeMountPath+"/"),
ModelPath: setting.Attachment.Minio.RealPath + option.PreTrainModelUrl,
ModelPath: setting.Attachment.Minio.RealPath + minioPreModelURL,
BenchmarkPath: storage.GetMinioPath(jobName, cloudbrain.BenchMarkMountPath+"/"),
Snn4ImageNetPath: storage.GetMinioPath(jobName, cloudbrain.Snn4imagenetMountPath+"/"),
BrainScorePath: storage.GetMinioPath(jobName, cloudbrain.BrainScoreMountPath+"/"),
@@ -171,6 +180,7 @@ func CloudBrainInferenceJobCreate(ctx *context.Context, option api.CreateTrainJo
ModelName: option.ModelName,
ModelVersion: option.ModelVersion,
CkptName: option.CkptName,
ModelId: option.ModelId,
TrainUrl: option.PreTrainModelUrl,
LabelName: labelName,
Spec: spec,
@@ -185,6 +195,43 @@ func CloudBrainInferenceJobCreate(ctx *context.Context, option api.CreateTrainJo
ctx.JSON(http.StatusOK, models.BaseMessageApi{Code: 0, Message: jobId})
}

func dealModelInfo(modelId string, jobName string, ckptName string) (string, error) {
preModel, err := models.QueryModelById(modelId)
if err != nil {
log.Error("Can not find model", err)
return "", err
}
minioPreModelURL, err := downloadModelFromObs(preModel, jobName, cloudbrain.PretrainModelMountPath, ckptName)
if err != nil {
log.Error("Can not find model", err)

return "", err
}
return minioPreModelURL, nil
}

func downloadModelFromObs(preModel *models.AiModelManage, jobName, suffixPath string, ckptFileName string) (string, error) {
destPath := setting.CBCodePathPrefix + jobName + suffixPath + "/"
destFile := destPath + ckptFileName
returnStr := setting.Attachment.Minio.Bucket + "/" + destPath
srcUrl := preModel.Path[len(setting.Bucket)+1:] + ckptFileName
log.Info("dest model Path=" + returnStr + " src path=" + preModel.Path + ckptFileName)
body, err := storage.ObsDownloadAFile(setting.Bucket, srcUrl)
if err == nil {
defer body.Close()
_, err = storage.Attachments.UploadContent(setting.Attachment.Minio.Bucket, destFile, body)
if err != nil {
log.Error("UploadObject(%s) failed: %s", preModel.Path+ckptFileName, err.Error())
return "", err
}
} else {
log.Info("download model failed. as " + err.Error())
return "", err
}
log.Info("download model from obs succeed")
return returnStr, nil
}

func ModelArtsInferenceJobCreate(ctx *context.Context, option api.CreateTrainJobOption) {
ctx.Data["PageIsTrainJob"] = true
VersionOutputPath := modelarts.GetOutputPathByCount(modelarts.TotalVersionCount)
@@ -418,6 +465,7 @@ func ModelArtsInferenceJobCreate(ctx *context.Context, option api.CreateTrainJob
ModelName: modelName,
ModelVersion: modelVersion,
CkptName: ckptName,
ModelId: option.ModelId,
ResultUrl: resultObsPath,
Spec: spec,
DatasetName: datasetNames,


+ 5
- 4
services/cloudbrain/cloudbrainTask/notebook.go View File

@@ -10,11 +10,9 @@ import (
"strconv"
"strings"

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

"code.gitea.io/gitea/modules/notification"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/services/cloudbrain/modelmanage"
"code.gitea.io/gitea/modules/grampus"

"code.gitea.io/gitea/services/lock"
@@ -227,7 +225,8 @@ func GrampusNotebookCreate(ctx *context.Context, option api.CreateNotebookOption

if option.ModelName != "" { //使用预训练模型

m, err := models.QueryModelByPath(option.PreTrainModelUrl)
m, err := models.QueryModelById(option.ModelId)
//(option.PreTrainModelUrl)
if err != nil {
log.Error("Can not find model", err)

@@ -242,6 +241,7 @@ func GrampusNotebookCreate(ctx *context.Context, option api.CreateNotebookOption
req.ModelName = option.ModelName
req.LabelName = option.LabelName
req.CkptName = option.CkptName
req.ModelId = option.ModelId
req.ModelVersion = option.ModelVersion
req.PreTrainModelUrl = option.PreTrainModelUrl
req.PreTrainModelPath = getPreTrainModelPath(option.PreTrainModelUrl, option.CkptName)
@@ -1086,6 +1086,7 @@ func GrampusNotebookRestart(ctx *context.Context) {
LabelName: task.LabelName,
PreTrainModelUrl: task.PreTrainModelUrl,
CkptName: task.CkptName,
ModelId: task.ModelId,
WorkServerNumber: 1,
}



+ 19
- 3
services/cloudbrain/cloudbrainTask/train.go View File

@@ -110,9 +110,21 @@ func CloudbrainOneTrainJobCreate(ctx *context.Context, option api.CreateTrainJob
req.ModelName = option.ModelName
req.LabelName = option.LabelName
req.CkptName = option.CkptName
req.ModelId = option.ModelId
req.ModelVersion = option.ModelVersion
req.PreTrainModelPath = setting.Attachment.Minio.RealPath + option.PreTrainModelUrl
req.PreTrainModelUrl = option.PreTrainModelUrl
minioPreModelURL, err := dealModelInfo(option.ModelId, jobName, option.CkptName)
if err != nil {
log.Error("Can not find model", err)
//cloudBrainNewDataPrepare(ctx, jobType)
//ctx.RenderWithErr(ctx.Tr("repo.modelconvert.manage.model_not_exist"), tpl, &form)
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.modelconvert.manage.model_not_exist")))
return
}
req.PreTrainModelPath = setting.Attachment.Minio.RealPath + minioPreModelURL
req.PreTrainModelUrl = minioPreModelURL

//req.PreTrainModelPath = setting.Attachment.Minio.RealPath + option.PreTrainModelUrl
//req.PreTrainModelUrl = option.PreTrainModelUrl

}

@@ -282,6 +294,7 @@ func ModelArtsTrainJobNpuCreate(ctx *context.Context, option api.CreateTrainJobO
req.ModelName = option.ModelName
req.LabelName = option.LabelName
req.CkptName = option.CkptName
req.ModelId = option.ModelId
req.ModelVersion = option.ModelVersion
req.PreTrainModelUrl = option.PreTrainModelUrl

@@ -414,9 +427,11 @@ func GrampusTrainJobGpuCreate(ctx *context.Context, option api.CreateTrainJobOpt
req.ModelName = option.ModelName
req.LabelName = option.LabelName
req.CkptName = option.CkptName
req.ModelId = option.ModelId
req.ModelVersion = option.ModelVersion
req.PreTrainModelUrl = option.PreTrainModelUrl

preTrainModelPath := getPreTrainModelPath(option.PreTrainModelUrl, option.CkptName)
req.PreTrainModelPath = preTrainModelPath
}

jobId, err := grampus.GenerateTrainJob(ctx, req)
@@ -641,6 +656,7 @@ func GrampusTrainJobNpuCreate(ctx *context.Context, option api.CreateTrainJobOpt
req.ModelName = option.ModelName
req.LabelName = option.LabelName
req.CkptName = option.CkptName
req.ModelId = option.ModelId
req.ModelVersion = option.ModelVersion
req.PreTrainModelUrl = option.PreTrainModelUrl
req.PreTrainModelPath = preTrainModelPath


+ 2
- 2
services/cloudbrain/modelmanage/model_manage.go View File

@@ -23,11 +23,11 @@ func QueryModelFileByModel(model *models.AiModelManage) []storage.FileInfo {
}

func HasModelFile(task *models.Cloudbrain) bool {
if task.PreTrainModelUrl == "" {
if task.ModelId == "" {
return true
}

model, err := models.QueryModelByPath(task.PreTrainModelUrl)
model, err := models.QueryModelById(task.ModelId)
if err != nil {
log.Error("Can not find model", err)
return false


+ 97
- 0
templates/admin/model/list.tmpl View File

@@ -0,0 +1,97 @@
{{template "base/head" .}}
<div class="admin user">
{{template "admin/navbar" .}}
<div class="ui container">
{{template "base/alert" .}}
<div class="ui negative message" style="display: none;">
</div>
<h4 class="ui top attached header">
{{.i18n.Tr "repo.model.manage.model_manage"}} ({{.i18n.Tr "admin.total" .Total}})
</h4>
<div class="ui attached segment">
{{template "admin/model/search" .}}
</div>
<div class="ui attached segment">
<div class="ui ten wide column">
<div id="model_set_recommend" r="{{.Recommend}}" class="ui checkbox" data-url="{{$.Link}}?sort={{.SortType}}&q={{.Keyword}}&tab={{.TabName}}&recommend={{if .Recommend}}false{{else}}true{{end}}">
<input type="checkbox" {{if .Recommend}}checked{{end}} />
<label>{{.i18n.Tr "admin.datasets.only_recommend"}}</label>
</div>
</div>
</div>
<script>
var MODELS = {{.models}};
</script>
<div class="ui attached table segment">
<table class="ui very basic striped table">
<thead>
<tr>
<th>ID</th>
<th>{{.i18n.Tr "admin.datasets.name"}}</th>
<th>{{.i18n.Tr "admin.repos.size"}}</th>
<th>{{.i18n.Tr "repo.issues.filter_sort.downloadtimes"}}</th>
<th>{{.i18n.Tr "repo.issues.filter_sort.citations"}}</th>
<th>{{.i18n.Tr "admin.datasets.private"}}</th>
<th>{{.i18n.Tr "admin.users.created"}}</th>
<th>{{.i18n.Tr "admin.notices.op"}}</th>
</tr>
</thead>
<tbody>
{{range .models}}
<tr>
<td>{{.ID}}</td>
<td style="display: flex;align-items: center;"><a href="{{AppSubUrl}}/{{.RepoOwnerName}}/{{.RepoName}}/modelmanage/model_readme_tmpl?name={{.Name}}">{{.Name}}</a>{{if .Recommend}}<img src="/img/jian.svg" style="margin-left: 0.5rem;">{{end}}</td>
<td>{{SizeFmt .Size}}</td>
<td>{{.DownloadCount}}</td>
<td>{{.ReferenceCount}}</td>
<td><i class="fa fa{{if .IsPrivate}}-check{{end}}-square-o"></i></td>
<td><span title="{{.CreatedUnix.FormatLong}}">{{.CreatedUnix.FormatShort}}</span></td>
<td>{{if .Recommend}}<span class="set_model" style="color: rgb(250, 140, 22);cursor: pointer;" data-url="{{$.Link}}/action?id={{.ID}}&recommend=0">{{$.i18n.Tr "admin.datasets.unrecommend"}}</span>{{else}}<span class="set_model" style="color: rgb(19, 194, 141);cursor: pointer;" data-url="{{$.Link}}/action?id={{.ID}}&recommend=1">{{$.i18n.Tr "admin.datasets.recommend"}}</span>{{end}}</td>
</tr>
{{end}}
</tbody>
</table>
</div>
{{template "base/paginate" .}}
</div>
</div>
{{template "base/footer" .}}
<script>
$(".set_model").on("click", function () {
const $this = $(this);
let link = $this.data("url");
$.ajax({
url: link,
type: "PUT",
success: function (res) {
console.log(res);
if (res.code == 0) {
window.location.reload();
} else {
$(".ui.negative.message")
.text(res.Message)
.show()
.delay(1500)
.fadeOut();
}
},
error: function (xhr) {
// 隐藏 loading
// 只有请求不正常(状态码不为200)才会执行
$(".ui.negative.message")
.html(xhr.responseText)
.show()
.delay(1500)
.fadeOut();
console.log(xhr);
},
complete: function (xhr) {
// $("#mask").css({"display":"none","z-index":"1"})
},
});
});
$("#model_set_recommend input").on('change', function(e) {
const url = $(this).closest('#model_set_recommend').data('url');
window.location.href = url;
});
</script>

+ 34
- 0
templates/admin/model/search.tmpl View File

@@ -0,0 +1,34 @@
<div class="ui right floated secondary filter menu">
<!-- Sort -->
<div class="ui dropdown type jump item">
<span class="text">
{{.i18n.Tr "repo.issues.filter_sort"}}
<i class="dropdown icon"></i>
</span>
<div class="menu">
<a class='{{if or (eq .SortType "oldest") (not .SortType)}}active{{end}} item' href='{{$.Link}}?sort=oldest&q={{$.Keyword}}&recommend={{$.Recommend}}'>{{.i18n.Tr "repo.issues.filter_sort.oldest"}}</a>
<a class='{{if eq .SortType "newest"}}active{{end}} item' href='{{$.Link}}?sort=newest&q={{$.Keyword}}&recommend={{$.Recommend}}'>{{.i18n.Tr "repo.issues.filter_sort.latest"}}</a>
<a class='{{if eq .SortType "alphabetically"}}active{{end}} item' href='{{$.Link}}?sort=alphabetically&q={{$.Keyword}}&recommend={{$.Recommend}}'>{{.i18n.Tr "repo.issues.label.filter_sort.alphabetically"}}</a>
<a class='{{if eq .SortType "reversealphabetically"}}active{{end}} item' href='{{$.Link}}?sort=reversealphabetically&q={{$.Keyword}}&recommend={{$.Recommend}}'>{{.i18n.Tr "repo.issues.label.filter_sort.reverse_alphabetically"}}</a>
<a class='{{if eq .SortType "recentupdate"}}active{{end}} item' href='{{$.Link}}?sort=recentupdate&q={{$.Keyword}}&recommend={{$.Recommend}}'>{{.i18n.Tr "repo.issues.filter_sort.recentupdate"}}</a>
<a class='{{if eq .SortType "leastupdate"}}active{{end}} item' href='{{$.Link}}?sort=leastupdate&q={{$.Keyword}}&recommend={{$.Recommend}}'>{{.i18n.Tr "repo.issues.filter_sort.leastupdate"}}</a>
<!--
<a class='{{if eq .SortType "moststars"}}active{{end}} item' href='{{$.Link}}?sort=moststars&q={{$.Keyword}}&tab={{$.TabName}}&recommend={{$.Recommend}}'>{{.i18n.Tr "repo.issues.filter_sort.moststars"}}</a>
<a class='{{if eq .SortType "feweststars"}}active{{end}} item' href='{{$.Link}}?sort=feweststars&q={{$.Keyword}}&tab={{$.TabName}}&recommend={{$.Recommend}}'>{{.i18n.Tr "repo.issues.filter_sort.feweststars"}}</a>
<a class='{{if eq .SortType "mostforks"}}active{{end}} item' href='{{$.Link}}?sort=mostforks&q={{$.Keyword}}&tab={{$.TabName}}&recommend={{$.Recommend}}'>{{.i18n.Tr "repo.issues.filter_sort.mostforks"}}</a>
<a class='{{if eq .SortType "fewestforks"}}active{{end}} item' href='{{$.Link}}?sort=fewestforks&q={{$.Keyword}}&tab={{$.TabName}}&recommend={{$.Recommend}}'>{{.i18n.Tr "repo.issues.filter_sort.fewestforks"}}</a>
-->
<a class='{{if eq .SortType "size"}}active{{end}} item' href='{{$.Link}}?sort=size&q={{$.Keyword}}&recommend={{$.Recommend}}'>{{.i18n.Tr "repo.issues.label.filter_sort.by_size"}}</a>
<a class='{{if eq .SortType "reversesize"}}active{{end}} item' href='{{$.Link}}?sort=reversesize&q={{$.Keyword}}&recommend={{$.Recommend}}'>{{.i18n.Tr "repo.issues.label.filter_sort.reverse_by_size"}}</a>

<a class='{{if eq .SortType "downloadtimes"}}active{{end}} item' href='{{$.Link}}?sort=downloadtimes&q={{$.Keyword}}&recommend={{$.Recommend}}'>{{.i18n.Tr "repo.issues.filter_sort.downloadtimes"}}</a>
<a class='{{if eq .SortType "mostusecount"}}active{{end}} item' href='{{$.Link}}?sort=mostusecount&q={{$.Keyword}}&recommend={{$.Recommend}}'>{{.i18n.Tr "repo.issues.filter_sort.citations"}}</a>
</div>
</div>
</div>
<form class="ui form ignore-dirty" style="max-width: 90%">
<div class="ui fluid action input">
<input name="q" value="{{.Keyword}}" placeholder='{{.i18n.Tr "explore.search"}}...' autofocus>
<button class="ui blue button">{{.i18n.Tr "explore.search"}}</button>
</div>
</form>

+ 3
- 0
templates/admin/navbar.tmpl View File

@@ -21,6 +21,9 @@
<a class="{{if .PageIsAdminDatasets}}active{{end}} item" href="{{AppSubUrl}}/admin/datasets">
{{.i18n.Tr "admin.datasets"}}
</a>
<a class="{{if .PageIsAdminModels}}active{{end}} item" href="{{AppSubUrl}}/admin/model">
{{.i18n.Tr "admin.models"}}
</a>
<a class="{{if .PageIsAdminCloudBrains}}active{{end}} item" href="{{AppSubUrl}}/admin/cloudbrains">
{{.i18n.Tr "repo.cloudbrain.task"}}
</a>


+ 1
- 1
templates/base/footer_content.tmpl View File

@@ -65,7 +65,7 @@
{{if .IsSigned}}
<a href="https://openi.pcl.ac.cn/zeizei/OpenI_Learning/issues/new" class="item" target="_blank"><i class="envelope icon"></i> {{.i18n.Tr "custom.foot.advice_feedback"}}</a>
{{else}}
<a href="{{AppSubUrl}}/user/login" class="item"><i class="envelope icon"></i> {{.i18n.Tr "custom.foot.advice_feedback"}}</a>
<a href="{{AppSubUrl}}/user/login?redirect_to={{AppSubUrl}}/zeizei/OpenI_Learning/issues/new" class="item"><i class="envelope icon"></i> {{.i18n.Tr "custom.foot.advice_feedback"}}</a>
{{end}}
<a href="{{AppSubUrl}}/resource_desc" class="item" target="_blank"><i class="server icon"></i> {{.i18n.Tr "custom.resource_description"}}</a>


+ 2
- 0
templates/base/head_navbar.tmpl View File

@@ -42,6 +42,7 @@
{{.i18n.Tr "repo.model_manager"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/modelsquare/main">{{.i18n.Tr "repo.model_square"}}</a>
<a class="item" href="{{AppSubUrl}}/extension/wenxin">{{.i18n.Tr "repo.model_experience"}}</a>
</div>
</div>
@@ -84,6 +85,7 @@
{{.i18n.Tr "repo.model_manager"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/modelsquare/main">{{.i18n.Tr "repo.model_square"}}</a>
<a class="item" href="{{AppSubUrl}}/extension/wenxin">{{.i18n.Tr "repo.model_experience"}}</a>
</div>
</div>


+ 2
- 0
templates/base/head_navbar_fluid.tmpl View File

@@ -39,6 +39,7 @@
{{.i18n.Tr "repo.model_manager"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/modelsquare/main">{{.i18n.Tr "repo.model_square"}}</a>
<a class="item" href="{{AppSubUrl}}/extension/wenxin">{{.i18n.Tr "repo.model_experience"}}</a>
</div>
</div>
@@ -80,6 +81,7 @@
{{.i18n.Tr "repo.model_manager"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/modelsquare/main">{{.i18n.Tr "repo.model_square"}}</a>
<a class="item" href="{{AppSubUrl}}/extension/wenxin">{{.i18n.Tr "repo.model_experience"}}</a>
</div>
</div>


+ 2
- 0
templates/base/head_navbar_home.tmpl View File

@@ -31,6 +31,7 @@
{{.i18n.Tr "repo.model_manager"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/modelsquare/main">{{.i18n.Tr "repo.model_square"}}</a>
<a class="item" href="{{AppSubUrl}}/extension/wenxin">{{.i18n.Tr "repo.model_experience"}}</a>
</div>
</div>
@@ -73,6 +74,7 @@
{{.i18n.Tr "repo.model_manager"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/modelsquare/main">{{.i18n.Tr "repo.model_square"}}</a>
<a class="item" href="{{AppSubUrl}}/extension/wenxin">{{.i18n.Tr "repo.model_experience"}}</a>
</div>
</div>


+ 2
- 0
templates/base/head_navbar_pro.tmpl View File

@@ -41,6 +41,7 @@
{{.i18n.Tr "repo.model_manager"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/modelsquare/main">{{.i18n.Tr "repo.model_square"}}</a>
<a class="item" href="{{AppSubUrl}}/extension/wenxin">{{.i18n.Tr "repo.model_experience"}}</a>
</div>
</div>
@@ -83,6 +84,7 @@
{{.i18n.Tr "repo.model_manager"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/modelsquare/main">{{.i18n.Tr "repo.model_square"}}</a>
<a class="item" href="{{AppSubUrl}}/extension/wenxin">{{.i18n.Tr "repo.model_experience"}}</a>
</div>
</div>


+ 5
- 0
templates/model/square/index.tmpl View File

@@ -0,0 +1,5 @@
{{template "base/head_home" .}}
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/vp-model-square.css?v={{MD5 AppVer}}" />
<div id="__vue-root"></div>
<script src="{{StaticUrlPrefix}}/js/vp-model-square.js?v={{MD5 AppVer}}"></script>
{{template "base/footer" .}}

+ 6
- 1
templates/repo/cloudbrain/benchmark/new.tmpl View File

@@ -88,7 +88,12 @@
<a id="benchmark_model_example" href="https://openi.pcl.ac.cn/BDIP/snn4imagenet"
target="_blank">{{.i18n.Tr "cloudbrain.view_sample"}}</a>
</div>
{{template "custom/select_model_required" .}}
<!--{{template "custom/select_model_required" .}}-->
<div>
<div class="select-multi-model" data-required="true" data-model-id="{{.model_id}}" data-model-name="{{.model_name}}" data-model-version="{{.model_version}}"
data-pre-train-model-url="{{.pre_train_model_url}}" data-ckpt-name="{{.ckpt_name}}"></div>
<div id="select-multi-model"></div>
</div>
<div id="images-new-cb">
</div>


+ 11
- 5
templates/repo/cloudbrain/inference/new.tmpl View File

@@ -50,7 +50,6 @@
{{.CsrfTokenHtml}}
<input type="hidden" name="action" value="update">
<input type="hidden" id="ai_image_name" value="{{.image}}">
<input type="hidden" id="ai_model_version" name="model_version" value="">

<input type="hidden" id="failed_train_url" value="{{$.train_url}}">
<input type="hidden" id="failed_model_name" value="{{$.model_name}}">
@@ -106,8 +105,10 @@
</div>
<div class="ui divider"></div>

<!-- 模型相关配置 -->
<h4 class="title ui header ">{{.i18n.Tr "repo.modelarts.train_job.parameter_setting"}}:</h4>
<!-- 模型相关配置 -->
<!--
<div class="required inline min_title fields" style="width: 94%;">
<label class="label-fix-width" style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.infer_job.select_model"}}</label>
<div class="six wide field">
@@ -144,10 +145,14 @@
</span>

</div>
<!-- AI引擎 -->
<div id="images-new-cb">

-->
<div>
<div class="select-multi-model" data-required="true" data-model-id="{{.model_id}}" data-model-name="{{.model_name}}" data-model-version="{{.model_version}}"
data-pre-train-model-url="{{.pre_train_model_url}}" data-ckpt-name="{{.ckpt_name}}"></div>
<div id="select-multi-model"></div>
</div>
<!-- AI引擎 -->
<div id="images-new-cb"></div>
<!-- 代码分支 -->
<div class="required min_title inline field">
<label class="label-fix-width" style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.code_version"}}</label>
@@ -293,6 +298,7 @@
let nameMap,nameList
// 获取模型列表和模型名称对应的模型版本
$(document).ready(function(){
return; // 用模型选择组件了
modelVersion()
modelCkpt()
$.get(`${RepoLink}/modelmanage/query_model_for_predict?type=0`, (data) => {


+ 27
- 48
templates/repo/cloudbrain/new.tmpl View File

@@ -145,36 +145,15 @@
{{end}}
</select>
</div>
<!--<div class="inline required field">
<label>{{.i18n.Tr "cloudbrain.gpu_type"}}</label>
<select id="cloudbrain_gpu_type" class="ui search dropdown gpu-type" placeholder="选择GPU类型"
style='width:385px' name="gpu_type">
{{range .gpu_types}}
<option value="{{.Queue}}">{{.Value}}</option>
{{end}}
</select>
</div>-->
{{template "custom/select_model" .}}
<div id="images-new-cb">

</div>
<div id="select-multi-dataset">

<!--{{template "custom/select_model" .}} -->
<div>
<div class="select-multi-model" data-model-id="{{.model_id}}" data-model-name="{{.model_name}}" data-model-version="{{.model_version}}"
data-pre-train-model-url="{{.pre_train_model_url}}" data-ckpt-name="{{.ckpt_name}}"></div>
<div id="select-multi-model"></div>
</div>
<div id="images-new-cb"></div>
<div id="select-multi-dataset"></div>
<span class="tooltips" style="margin-left: 11.5rem;margin-bottom: 1rem;"></span>
<!--<div class="inline required field">
<label>{{.i18n.Tr "cloudbrain.resource_specification"}}</label>
<select id="cloudbrain_resource_spec" class="ui search dropdown"
placeholder="{{.i18n.Tr "cloudbrain.select_specification"}}" style='width:385px'
name="resource_spec_id">
{{range .resource_specs}}
<option name="resource_spec_id" value="{{.Id}}">
{{$.i18n.Tr "cloudbrain.gpu_num"}}:{{.GpuNum}},{{$.i18n.Tr "cloudbrain.cpu_num"}}:{{.CpuNum}},{{$.i18n.Tr "cloudbrain.memory"}}(MB):{{.MemMiB}},{{$.i18n.Tr "cloudbrain.shared_memory"}}(MB):{{.ShareMemMiB}}
</option>
{{end}}
</select>
</div>-->
<div class="inline min_title required field">
<label class="label-fix-width" style="font-weight: normal;">{{.i18n.Tr "cloudbrain.resource_specification"}}</label>
<select id="__specs__" class="ui search dropdown width48"
@@ -194,26 +173,26 @@
</div>
{{end}}
</div>
<div class="inline required field cloudbrain_benchmark">
<label>{{.i18n.Tr "cloudbrain.benchmark_path"}}</label>
<input name="benchmark_path" id="cloudbrain_benchmark_path" value="{{.benchmark_path}}"
tabindex="3" disabled autofocus required maxlength="255" readonly="readonly">
</div>
<div class="inline required field cloudbrain_snn4imagenet">
<label>{{.i18n.Tr "cloudbrain.snn4imagenet_path"}}</label>
<input name="snn4imagenet_path" id="cloudbrain_snn4imagenet_path" value="{{.snn4imagenet_path}}"
tabindex="3" disabled autofocus required maxlength="255" readonly="readonly">
</div>
<div class="inline required field cloudbrain_brainscore">
<label>{{.i18n.Tr "cloudbrain.brainscore_path"}}</label>
<input name="brainscore_path" id="cloudbrain_brainscore_path" value="{{.brainscore_path}}"
tabindex="3" disabled autofocus required maxlength="255" readonly="readonly">
</div>
<div class="inline required field" hidden>
<label>{{.i18n.Tr "cloudbrain.start_command"}}</label>
<textarea name="command" rows="10" readonly="readonly">{{.command}}</textarea>
</div>
<div class="inline required field cloudbrain_benchmark">
<label>{{.i18n.Tr "cloudbrain.benchmark_path"}}</label>
<input name="benchmark_path" id="cloudbrain_benchmark_path" value="{{.benchmark_path}}"
tabindex="3" disabled autofocus required maxlength="255" readonly="readonly">
</div>
<div class="inline required field cloudbrain_snn4imagenet">
<label>{{.i18n.Tr "cloudbrain.snn4imagenet_path"}}</label>
<input name="snn4imagenet_path" id="cloudbrain_snn4imagenet_path" value="{{.snn4imagenet_path}}"
tabindex="3" disabled autofocus required maxlength="255" readonly="readonly">
</div>
<div class="inline required field cloudbrain_brainscore">
<label>{{.i18n.Tr "cloudbrain.brainscore_path"}}</label>
<input name="brainscore_path" id="cloudbrain_brainscore_path" value="{{.brainscore_path}}"
tabindex="3" disabled autofocus required maxlength="255" readonly="readonly">
</div>
<div class="inline required field" hidden>
<label>{{.i18n.Tr "cloudbrain.start_command"}}</label>
<textarea name="command" rows="10" readonly="readonly">{{.command}}</textarea>
</div>
<div class="inline field">
<label class="label-fix-width" style="font-weight: normal;"></label>


+ 6
- 1
templates/repo/cloudbrain/trainjob/new.tmpl View File

@@ -156,7 +156,12 @@
{{end}}
</select>
</div>
{{template "custom/select_model" .}}
<!--{{template "custom/select_model" .}} -->
<div>
<div class="select-multi-model" data-model-id="{{.model_id}}" data-model-name="{{.model_name}}" data-model-version="{{.model_version}}"
data-pre-train-model-url="{{.pre_train_model_url}}" data-ckpt-name="{{.ckpt_name}}"></div>
<div id="select-multi-model"></div>
</div>
<div class="inline required field" style="display: none;">
<label>{{.i18n.Tr "cloudbrain.task_type"}}</label>
<select id="cloudbrain_job_type" class="ui search dropdown" placeholder="选择任务类型" style='width:385px'


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

@@ -702,6 +702,7 @@
type: 'POST',
data: data,
success: function (res) {
const modelName = $('#formId #name').val();
$('input[name="engine_name"]').val("");
$('input[name="engine"]').val("");
$('input[name="jobId"]').val("");
@@ -710,7 +711,7 @@
var cityObj = $("#modelSelectedFile");
cityObj.attr("value", "");
document.getElementById("formId").reset();
location.href = `/${userName}/${repoPath}/modelmanage/show_model`
location.href = `/${userName}/${repoPath}/modelmanage/model_readme_tmpl?name=${encodeURIComponent(modelName)}`
$('.ui.modal.second').modal('hide')
},
error: function (xhr) {


+ 7
- 1
templates/repo/grampus/notebook/gcu/new.tmpl View File

@@ -99,7 +99,13 @@
{{end}}
</select>
</div>
<!-- {{template "custom/select_model" .}} -->
<!--{{template "custom/select_model" .}} -->
<div>
<div class="select-multi-model" data-model-id="{{.model_id}}" data-model-name="{{.model_name}}" data-model-version="{{.model_version}}"
data-pre-train-model-url="{{.pre_train_model_url}}" data-ckpt-name="{{.ckpt_name}}"></div>
<div id="select-multi-model"></div>
</div>

<div class="inline min_title required field">
<label class="label-fix-width" style="font-weight: normal;">{{.i18n.Tr "cloudbrain.mirror"}}</label>
<select class="ui search dropdown cloudbrain_image width48" placeholder="{{.i18n.Tr "cloudbrain.choose_mirror"}}" style='width:385px' name="image_id">


+ 6
- 1
templates/repo/grampus/notebook/gpu/new.tmpl View File

@@ -109,7 +109,12 @@
{{end}}
</select>
</div>
{{template "custom/select_model" .}}
<!--{{template "custom/select_model" .}} -->
<div>
<div class="select-multi-model" data-model-id="{{.model_id}}" data-model-name="{{.model_name}}" data-model-version="{{.model_version}}"
data-pre-train-model-url="{{.pre_train_model_url}}" data-ckpt-name="{{.ckpt_name}}"></div>
<div id="select-multi-model"></div>
</div>
<div id="images-new-cb">

</div>


+ 6
- 1
templates/repo/grampus/notebook/npu/new.tmpl View File

@@ -95,7 +95,12 @@
{{end}}
</select>
</div>
{{template "custom/select_model" .}}
<!--{{template "custom/select_model" .}} -->
<div>
<div class="select-multi-model" data-model-id="{{.model_id}}" data-model-name="{{.model_name}}" data-model-version="{{.model_version}}"
data-pre-train-model-url="{{.pre_train_model_url}}" data-ckpt-name="{{.ckpt_name}}"></div>
<div id="select-multi-model"></div>
</div>
<div class="inline min_title required field">
<label class="label-fix-width" style="font-weight: normal;">{{.i18n.Tr "cloudbrain.mirror"}}</label>
<select class="ui search dropdown cloudbrain_image width48" placeholder="{{.i18n.Tr "cloudbrain.choose_mirror"}}" style='width:385px' name="image_id">


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

@@ -378,7 +378,7 @@
{{if eq $.modelDownload.IsDelete true}}
{{$.modelDownload.Name}}({{$.i18n.Tr "dataset.file_deleted"}})
{{else}}
<a href="{{$.RepoLink}}/modelmanage/show_model_info?name={{.ModelName}}" target="_blank">{{$.modelDownload.Name}}</a>
<a href="{{$.RepoLink}}/modelmanage/model_readme_tmpl?name={{.ModelName}}" target="_blank">{{$.modelDownload.Name}}</a>
{{end}}
</td>
<td><div class="dataset_nowrap_two_line">{{$.modelDownload.DownloadLink}}</div></td>


+ 8
- 1
templates/repo/grampus/trainjob/gpu/new.tmpl View File

@@ -158,7 +158,14 @@
{{end}}
</select>
</div>
{{template "custom/select_model" .}}
<!--{{template "custom/select_model" .}} -->
<div>
<div class="select-multi-model" data-model-id="{{.model_id}}" data-model-name="{{.model_name}}" data-model-version="{{.model_version}}"
data-pre-train-model-url="{{.pre_train_model_url}}" data-ckpt-name="{{.ckpt_name}}"></div>
<div id="select-multi-model"></div>
</div>
<div id="images-new-grampus">

<div id="images-new-cb">
</div>



+ 7
- 2
templates/repo/grampus/trainjob/npu/new.tmpl View File

@@ -143,10 +143,15 @@
{{end}}
</select>
</div>
{{template "custom/select_model" .}}
<!--{{template "custom/select_model" .}} -->
<div>
<div class="select-multi-model" data-model-id="{{.model_id}}" data-model-name="{{.model_name}}" data-model-version="{{.model_version}}"
data-pre-train-model-url="{{.pre_train_model_url}}" data-ckpt-name="{{.ckpt_name}}"></div>
<div id="select-multi-model"></div>
</div>
<div class="required min_title inline field" id="engine_name">
<label class="label-fix-width" style="font-weight: normal;">{{.i18n.Tr "cloudbrain.mirror"}}</label>
<select class="ui dropdown width81" id="trainjob_images" name="image_id">
<select class="ui dropdown width48" id="trainjob_images" name="image_id">
{{if .image_id}}
{{range .images}}
{{if eq $.image_id .ID}}


+ 2
- 1
templates/repo/grampus/trainjob/show.tmpl View File

@@ -783,6 +783,7 @@
type: 'POST',
data: data,
success: function (res) {
const modelName = $('#formId #name').val();
$('input[name="engine_name"]').val("");
$('input[name="engine"]').val("");
$('input[name="jobId"]').val("");
@@ -791,7 +792,7 @@
var cityObj = $("#modelSelectedFile");
cityObj.attr("value", "");
document.getElementById("formId").reset();
location.href = `/${userName}/${repoPath}/modelmanage/show_model`
location.href = `/${userName}/${repoPath}/modelmanage/model_readme_tmpl?name=${encodeURIComponent(modelName)}`
$('.ui.modal.second').modal('hide')
},
error: function (xhr) {


+ 14
- 2
templates/repo/modelarts/inferencejob/index.tmpl View File

@@ -119,9 +119,21 @@
</a>
</div>
<!-- 模型版本 -->
<!-- href="{{$.RepoLink}}/modelmanage/show_model_info?name={{.ModelName}}" -->
<!-- href="{{$.RepoLink}}/modelmanage/model_readme_tmpl?name={{.ModelName}}" -->
<div class="three wide column text center padding0" style="display: flex;width: 17% !important;">
<a id="{{.JobName}}" class="goto_modelmanage nowrap" title="{{.ModelName}}" href="javascript:void(0);" data-variation="inverted" data-position="top center" data-content="{{$.i18n.Tr "repo.modelarts.infer_job.tooltip"}}" data-jobname={{.JobName}} data-modelname={{.ModelName}} data-version={{.ModelVersion}} data-repopath="{{$.RepoLink}}">{{.ModelName}}&nbsp;</a>&nbsp;<span style="font-size: 12px;">{{.ModelVersion}}</span>
<a id="{{.JobName}}" class="goto_modelmanage nowrap" title="{{.ModelName}}" href="javascript:void(0);"
data-variation="inverted"
data-position="top center"
data-content="{{$.i18n.Tr "repo.modelarts.infer_job.model_cant_see"}}"
data-jobname="{{.JobName}}"
data-modelid="{{.ModelId}}"
data-modelname="{{.ModelName}}"
data-version="{{.ModelVersion}}"
data-repopath="{{$.RepoLink}}"
data-modelrepoownername="{{.ModelRepoOwnerName}}"
data-modelreponame="{{.ModelRepoName}}"
>
{{.ModelName}}&nbsp;</a>&nbsp;<span style="font-size: 12px;">{{.ModelVersion}}</span>
</div>
<!-- 任务状态 -->
<div class="two wide column text center padding0" style="width: 10.5% !important;">


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

@@ -49,7 +49,6 @@
<input type="hidden" name="action" value="update">
<input type="hidden" id="ai_engine_name" name="engine_names" value="">
<input type="hidden" id="ai_flaver_name" name="flaver_names" value="">
<input type="hidden" id="ai_model_version" name="model_version" value="">

<input type="hidden" id="failed_train_url" value="{{$.train_url}}">
<input type="hidden" id="failed_model_name" value="{{$.model_name}}">
@@ -105,8 +104,9 @@
{{end}}
</div>
<div class="ui divider"></div>
<!-- 模型相关配置 -->
<h4 class="title ui header ">{{.i18n.Tr "repo.modelarts.train_job.parameter_setting"}}:</h4>
<!-- 模型相关配置 -->
<!--
<div class="required inline min_title fields" style="width: 94%;">
<label class="label-fix-width" style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.infer_job.select_model"}}</label>
<div class="six wide field">
@@ -142,6 +142,12 @@
<i class="question circle icon" data-content="{{.i18n.Tr "cloudbrain.model_file_path_rule"}} {{.i18n.Tr "cloudbrain.model_file_postfix_rule"}}" data-position="top center" data-variation="inverted mini"></i>
</span>
</div>
-->
<div>
<div class="select-multi-model" data-required="true" data-model-id="{{.model_id}}" data-model-name="{{.model_name}}" data-model-version="{{.model_version}}"
data-pre-train-model-url="{{.pre_train_model_url}}" data-ckpt-name="{{.ckpt_name}}"></div>
<div id="select-multi-model"></div>
</div>
<!-- AI引擎 -->
<div class="required inline min_title fields" style="width: 92.5%;">
<label class="label-fix-width" style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.AI_driver"}}</label>
@@ -323,6 +329,7 @@
// 获取模型列表和模型名称对应的模型版本

$(document).ready(function(){
return; // 用模型选择组件了
modelVersion()
modelCkpt()
$.get(`${RepoLink}/modelmanage/query_model_for_predict?type=1`, (data) => {


+ 6
- 1
templates/repo/modelarts/notebook/new.tmpl View File

@@ -73,7 +73,12 @@
<div class="ui divider"></div>
<h4 class="train-job-title ui header ">{{.i18n.Tr "repo.modelarts.train_job.parameter_setting"}}:</h4>
{{template "custom/select_model" .}}
<!--{{template "custom/select_model" .}} -->
<div>
<div class="select-multi-model" data-model-id="{{.model_id}}" data-model-name="{{.model_name}}" data-model-version="{{.model_version}}"
data-pre-train-model-url="{{.pre_train_model_url}}" data-ckpt-name="{{.ckpt_name}}"></div>
<div id="select-multi-model"></div>
</div>
<div class="inline min_title required field">
<label class="label-fix-width" style="font-weight: normal;">{{.i18n.Tr "cloudbrain.mirror"}}</label>
<select id="cloudbrain_image" class="ui search dropdown width48" placeholder="{{.i18n.Tr "cloudbrain.choose_mirror"}}" name="image_id">


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

@@ -285,7 +285,7 @@
{{if eq $.modelDownload.IsDelete true}}
{{$.modelDownload.Name}}({{$.i18n.Tr "dataset.file_deleted"}})
{{else}}
<a href="{{$.RepoLink}}/modelmanage/show_model_info?name={{.ModelName}}" target="_blank">{{$.modelDownload.Name}}</a>
<a href="{{$.RepoLink}}/modelmanage/model_readme_tmpl?name={{.ModelName}}" target="_blank">{{$.modelDownload.Name}}</a>
{{end}}
</td>
<td><div class="dataset_nowrap_two_line">{{$.modelDownload.DownloadLink}}</div></td>


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

@@ -147,7 +147,12 @@
</select>
</div>
{{template "custom/select_model" .}}
<!--{{template "custom/select_model" .}} -->
<div>
<div class="select-multi-model" data-model-id="{{.model_id}}" data-model-name="{{.model_name}}" data-model-version="{{.model_version}}"
data-pre-train-model-url="{{.pre_train_model_url}}" data-ckpt-name="{{.ckpt_name}}"></div>
<div id="select-multi-model"></div>
</div>

<div class="required inline min_title fields" style="width: 92.5%;">
<label class="label-fix-width" style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.AI_driver"}}</label>


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

@@ -788,6 +788,7 @@
type: 'POST',
data: data,
success: function (res) {
const modelName = $('#formId #name').val();
$('input[name="engine_name"]').val("");
$('input[name="engine"]').val("");
$('input[name="jobId"]').val("");
@@ -796,7 +797,7 @@
var cityObj = $("#modelSelectedFile");
cityObj.attr("value", "");
document.getElementById("formId").reset();
location.href = `/${userName}/${repoPath}/modelmanage/show_model`
location.href = `/${userName}/${repoPath}/modelmanage/model_readme_tmpl?name=${encodeURIComponent(modelName)}`
$('.ui.modal.second').modal('hide')
},
error: function (xhr) {


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

@@ -125,7 +125,12 @@

</div>

{{template "custom/select_model" .}}
<!--{{template "custom/select_model" .}} -->
<div>
<div class="select-multi-model" data-model-id="{{.model_id}}" data-model-name="{{.model_name}}" data-model-version="{{.model_version}}"
data-pre-train-model-url="{{.pre_train_model_url}}" data-ckpt-name="{{.ckpt_name}}"></div>
<div id="select-multi-model"></div>
</div>

<div class="required min_title inline fields" style="width: 92.5%;">
<label class="label-fix-width" style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.AI_driver"}}</label>


templates/repo/modelmanage/create_local_1.tmpl → templates/repo/modelmanage/create_local.tmpl View File

@@ -1,5 +1,5 @@
{{template "base/head" .}}
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/vp-modelmanage-local-create-1.css?v={{MD5 AppVer}}" />
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/vp-modelmanage-local-create.css?v={{MD5 AppVer}}" />
<div class="repository release dataset-list view">
{{template "repo/header" .}}
<script>var REPO_IS_PRIVATE = {{$.Repository.IsPrivate}};</script>
@@ -7,5 +7,5 @@
<div id="__vue-root"></div>
</div>
</div>
<script src="{{StaticUrlPrefix}}/js/vp-modelmanage-local-create-1.js?v={{MD5 AppVer}}"></script>
<script src="{{StaticUrlPrefix}}/js/vp-modelmanage-local-create.js?v={{MD5 AppVer}}"></script>
{{template "base/footer" .}}

+ 0
- 11
templates/repo/modelmanage/create_local_2.tmpl View File

@@ -1,11 +0,0 @@
{{template "base/head" .}}
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/vp-modelmanage-local-create-2.css?v={{MD5 AppVer}}" />
<div class="repository release dataset-list view">
{{template "repo/header" .}}
<script>var MAX_MODEL_SIZE = {{ .max_model_size }};</script>
<div class="ui container">
<div id="__vue-root"></div>
</div>
</div>
<script src="{{StaticUrlPrefix}}/js/vp-modelmanage-local-create-2.js?v={{MD5 AppVer}}"></script>
{{template "base/footer" .}}

+ 7
- 2
templates/repo/modelmanage/create_online.tmpl View File

@@ -579,8 +579,8 @@
url: url_href,
type: "POST",
data: data,
success: function (res) {
backToModelListPage();
success: function (res) {
goDetailModelPage(cName);
},
error: function (xhr) {
// 隐藏 loading
@@ -598,7 +598,12 @@
let url_href = location.href.split("create_online_model")[0] + 'show_model';
window.location.href = url_href;
}
function goDetailModelPage(name) {
let url_href = location.href.split("create_online_model")[0] + 'model_readme_tmpl?name=' + encodeURIComponent(name);
window.location.href = url_href;
}
window.submitSaveModel = submitSaveModel;
window.backToModelListPage = backToModelListPage;
window.goDetailModelPage = goDetailModelPage;
})();
</script>

+ 5
- 0
templates/repo/modelmanage/evolution_map.tmpl View File

@@ -0,0 +1,5 @@
{{template "base/head_home" .}}
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/vp-model-graph.css?v={{MD5 AppVer}}" />
<div id="__vue-root"></div>
<script src="{{StaticUrlPrefix}}/js/vp-model-graph.js?v={{MD5 AppVer}}"></script>
{{template "base/footer" .}}

+ 5
- 0
templates/repo/modelmanage/filelist.tmpl View File

@@ -0,0 +1,5 @@
{{template "base/head_home" .}}
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/vp-model-files.css?v={{MD5 AppVer}}" />
<div id="__vue-root"></div>
<script src="{{StaticUrlPrefix}}/js/vp-model-files.js?v={{MD5 AppVer}}"></script>
{{template "base/footer" .}}

+ 6
- 0
templates/repo/modelmanage/fileupload.tmpl View File

@@ -0,0 +1,6 @@
{{template "base/head" .}}
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/vp-model-fileupload.css?v={{MD5 AppVer}}" />
<script>var MAX_MODEL_SIZE = {{ .max_model_size }};</script>
<div id="__vue-root"></div>
<script src="{{StaticUrlPrefix}}/js/vp-model-fileupload.js?v={{MD5 AppVer}}"></script>
{{template "base/footer" .}}

+ 1
- 1
templates/repo/modelmanage/index.tmpl View File

@@ -76,7 +76,7 @@
<div class="column right aligned">
<!-- -->
<a class="ui button {{if .Permission.CanWrite $.UnitTypeModelManage}} blue m-blue-btn {{else}} disabled {{end}}"
href="{{.RepoLink}}/modelmanage/create_local_model_1">{{$.i18n.Tr "repo.model.manage.import_local_model"}}</a>
href="{{.RepoLink}}/modelmanage/create_local_model">{{$.i18n.Tr "repo.model.manage.import_local_model"}}</a>
<a class="ui button {{if .Permission.CanWrite $.UnitTypeModelManage}} green {{else}} disabled {{end}}"
href="{{.RepoLink}}/modelmanage/create_online_model">{{$.i18n.Tr "repo.model.manage.import_online_model"}}</a>
</div>


+ 5
- 0
templates/repo/modelmanage/readme.tmpl View File

@@ -0,0 +1,5 @@
{{template "base/head_home" .}}
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/vp-model-intro.css?v={{MD5 AppVer}}" />
<div id="__vue-root"></div>
<script src="{{StaticUrlPrefix}}/js/vp-model-intro.js?v={{MD5 AppVer}}"></script>
{{template "base/footer" .}}

+ 6
- 0
templates/repo/modelmanage/setting.tmpl View File

@@ -0,0 +1,6 @@
{{template "base/head_home" .}}
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/vp-model-settings.css?v={{MD5 AppVer}}" />
<script>window.REPO_IS_PRIVATE = {{$.Repository.IsPrivate}};</script>
<div id="__vue-root"></div>
<script src="{{StaticUrlPrefix}}/js/vp-model-settings.js?v={{MD5 AppVer}}"></script>
{{template "base/footer" .}}

+ 6
- 1
templates/repo/modelsafety/new.tmpl View File

@@ -141,7 +141,12 @@
onkeydown="this.value=this.value.substring(0, 255)"
onkeyup="this.value=this.value.substring(0, 255)">{{.description}}</textarea>
</div>
{{template "custom/select_model_required" .}}
<!--{{template "custom/select_model_required" .}}-->
<div>
<div class="select-multi-model" data-required="true" data-model-id="{{.model_id}}" data-model-name="{{.model_name}}" data-model-version="{{.model_version}}"
data-pre-train-model-url="{{.pre_train_model_url}}" data-ckpt-name="{{.ckpt_name}}"></div>
<div id="select-multi-model"></div>
</div>
<div class="required inline min_title field " style="display: none;">
<label class="label-fix-width" style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.resource_pool"}}</label>
<select class="ui dropdown width48" id="trainjob_resource_pool" name="pool_id">


+ 5
- 20
web_src/js/components/Model.vue View File

@@ -80,16 +80,6 @@
}}</span>
</template>
</el-table-column>
<el-table-column
prop="computeResource"
:label="i18n.model_compute_resource"
align="center"
min-width="8%"
>
<template slot-scope="scope">
<span class="text-over">{{ scope.row.computeResource }}</span>
</template>
</el-table-column>
<el-table-column
prop="isPrivate"
:label="i18n.model_status"
@@ -143,12 +133,10 @@
<template slot-scope="scope">
<div class="space-around" >
<a class="op-btn"
v-show="scope.row.modelType == 1"
:href="url + 'create_local_model_1?type=1&name=' + encodeURIComponent(scope.row.name) + '&id=' + scope.row.id"
<a class="op-btn"
:href="url + 'model_setting?type=1&name=' + encodeURIComponent(scope.row.name) + '&id=' + scope.row.id + '&back=' + encodeURIComponent(curHref)"
:class="{ disabled: !scope.row.isCanOper }"
>{{ i18n.modify }}</a>
<a class="op-btn" v-show="scope.row.modelType != 1" style="color:transparent;cursor:default;" >{{ i18n.modify }}</a>
>{{ i18n.modify }}</a>
<a class="op-btn" style="color: #13c28d;" v-show="repoIsPrivate == false && scope.row.isPrivate==true && scope.row.isCanOper" @click="
modifyModelStatus(scope.row.id, scope.row.cName, scope.row.rowKey,false)
">{{ i18n.modelaccess_setpublic }}</a>
@@ -196,6 +184,7 @@ export default {
components: {},
data() {
return {
curHref: window.location.href,
i18n: {},
currentPage: 1,
pageSize: 10,
@@ -232,8 +221,6 @@ export default {
for (let i = 0; i < tableData.length; i++) {
trainTaskInfo = JSON.parse(tableData[i].trainTaskInfo || '{}');
tableData[i].engineName = this.getEngineName(tableData[i]);
// tableData[i].computeResource = trainTaskInfo.ComputeResource;
tableData[i].computeResource = tableData[i].type == '0' ? 'CPU/GPU' : 'NPU';
tableData[i].cName = tableData[i].name;
tableData[i].rowKey = tableData[i].id + Math.random();
tableData[i].name = "";
@@ -517,8 +504,6 @@ export default {
this.tableData[i].engineName = this.getEngineName(
this.tableData[i]
);
// this.tableData[i].computeResource = trainTaskInfo.ComputeResource;
this.tableData[i].computeResource = this.tableData[i].type == '0' ? 'CPU/GPU' : 'NPU';
this.tableData[i].hasChildren = res.data.data[i].versionCount === 1 ? false : true;
if (this.tableData[i].status !== 1) {
countStatus++;
@@ -562,7 +547,7 @@ export default {
return this.url + "downloadall?id=";
},
showinfoHref() {
return this.url + "show_model_info?name=";
return this.url + "model_readme_tmpl?name=";
},
transStatus(){
return function (state) {


+ 691
- 0
web_src/js/components/model/ModelSelect.vue View File

@@ -0,0 +1,691 @@
<template>
<div class="model-select">
<div class="title" v-if="showTitle"><span :class="required ? 'required' : ''">{{ selfTitle }}</span></div>
<div class="content">
<input type="hidden" name="model_id" :value="form_model_id" required />
<input type="hidden" name="model_name" :value="form_model_name" required />
<input type="hidden" name="model_version" :value="form_model_version" required />
<input type="hidden" name="pre_train_model_url" :value="form_pre_train_model_url" required />
<input type="hidden" name="ckpt_name" :value="form_ckpt_name" required />
<div v-if="selectList.length" class="model-list-c" :class="errStatus ? 'error' : ''">
<div class="model-item" v-for="(item, index) in selectList" :key="item.id"
:title="item._modelName + '/' + item.name">
{{ item._modelName + '/' + item.name }}
</div>
</div>
<input v-if="selectList.length == 0" type="text" class="disabled" style="width:100%"
:required="required ? 'required' : undefined" :placeholder="$t('modelObj.model_select_placeholder')" />
</div>
<div class="btn-select" @click="dlgShow = true">
<i class="el-icon-plus"></i>
<span>{{ $t('modelObj.model_select') }}</span>
</div>
<div style="display:flex;align-items:center;justify-content:center;margin-left:6px;">
<el-tooltip placement="top" effect="light">
<i class="question circle icon link" style="margin-top:-7px"></i>
<div slot="content">
<div style="width:200px;text-align:center;">{{ $t('modelObj.model_suport_file_tips') }}</div>
</div>
</el-tooltip>
</div>
<el-dialog class="model-dlg" :visible.sync="dlgShow" :title="$t('modelObj.model_select')" width="1000px" :modal="true"
:close-on-click-modal="false" :show-close="true" :destroy-on-close="false" :before-close="beforeClose" @open="open"
@closed="closed">
<div class="dlg-content">
<div class="left-area" v-loading="dlgLoading">
<div class="model-tabs-c">
<el-tabs class="model-tabs" v-model="dlgActiveName" @tab-click="dlgTabClick">
<el-tab-pane :label="$t('modelObj.model_current_repo')" name="first"></el-tab-pane>
<el-tab-pane :label="$t('modelObj.model_my')" name="second"></el-tab-pane>
<el-tab-pane :label="$t('modelObj.model_public')" name="third"></el-tab-pane>
<el-tab-pane :label="$t('modelObj.model_collected')" name="fourth"></el-tab-pane>
</el-tabs>
<el-input size="small" class="search-inp" :placeholder="$t('modelObj.model_search_placeholder')"
v-model="dlgSearchValue" @keydown.enter.stop.native.prevent="inputSearch">
<div slot="suffix" class="search-inp-icon" @click="inputSearch">
<i class="el-icon-search"></i>
</div>
</el-input>
</div>
<el-tree :data="dlgModelTreeData" ref="dlgTreeRef" highlight-current show-checkbox node-key="id"
:default-expanded-keys="dlgInitTreeNode" :props="dlgTreeProps" :index="10" accordion
@check="onTreeCheckChange">
<span slot-scope="{ node, data }" class="slot-wrap">
<span v-if="data.parent" class="custom-tree-node">
<el-tooltip v-if="data.description" placement="top-start">
<div slot="content" class="multiple-wrap"> {{ data.description }}</div>
<span class="model-title model-nowrap">
<div class="model_flex">
<span style="flex: inherit" class="model-nowrap">{{ node.label }}<span
v-if="(data.version != '0.0.1' || data.new != 1)" class="model-version">
{{ data.version }} </span>
</span>
<img v-if="data.recommend == 1" style="margin-left: 0.4rem" src="/img/jian.svg" />
</div>
</span>
</el-tooltip>
<span v-else class="model-title model-nowrap">
<div class="model_flex">
<span style="flex: inherit" class="model-nowrap">{{ node.label }}<span
v-if="(data.version != '0.0.1' || data.new != 1)" class="model-version">
{{ data.version }} </span>
</span>
<img v-if="data.recommend == 1" style="margin-left: 0.4rem" src="/img/jian.svg" />
</div>
</span>
<span class="model-repolink model-nowrap" @click.stop="return false;">
<a :href="'/' + data.repoOwnerName + '/' + data.repoName + '/modelmanage/model_readme_tmpl?name=' + data.name"
target="_blank">
{{ data.repoOwnerName }}/{{ data.repoDisplayName }}
</a>
</span>
</span>
<span v-else style="display: flex">
<span class="model-nowrap" :title="node.label">
{{ node.label }}
</span>
</span>
</span>
</el-tree>
<div class="pagination-c">
<el-pagination background @current-change="dlgPageChange" :current-page="dlgPage" :page-size="dlgPageSize"
layout="total, prev, pager, next" :total="dlgTotal">
</el-pagination>
</div>
</div>
<div class="right-area">
<div class="right-title"><span>{{ $t('modelObj.model_selected') }}</span></div>
<div class="right-selected-list">
<el-checkbox-group v-model="dlgSelectedModel">
<el-checkbox v-for="(item, index) in dlgSelectedModelList" :key="item.id" :label="item.id"
:true-label="item.id" :title="item.name" @change="(checked) => dlgChangeSelect(checked, item)">
{{ item.name }}
</el-checkbox>
</el-checkbox-group>
</div>
<div class="right-btn-c">
<el-button type="primary" size="small" @click="confirm">{{ $t('modelObj.model_ok') }}</el-button>
</div>
</div>
</div>
</el-dialog>
</div>
</template>

<script>

const supportCheckPointFileExt = ["ckpt", "pb", "h5", "json", "pkl", "pth", "t7", "pdparams", "onnx", "pbtxt", "keras", "mlmodel", "cfg", "pt"];
export default {
name: "ModelSelect",
props: {
title: { type: String, default: '' },
showTitle: { type: Boolean, default: true },
maxCount: { type: Number, default: 1 },
},
data() {
return {
required: false,
userName: location.pathname.split('/')[1],
repoName: location.pathname.split('/')[2],
type: '1',

form_model_id: '',
form_model_name: '',
form_model_version: '',
form_pre_train_model_url: '',
form_ckpt_name: '',

selectList: [],
dlgShow: false,
dlgLoading: false,
dlgActiveName: 'first',
dlgSearchValue: '',

dlgTreeProps: {
children: "children",
label: "name",
},
dlgModelTreeData: [],
dlgInitTreeNode: [],

dlgPage: 1,
dlgPageSize: 10,
dlgTotal: 0,

dlgSelectedModel: [],
dlgSelectedModelList: [],

errStatus: false,
};
},
computed: {
selfTitle() {
return this.title || this.$t('modelObj.model_label');
}
},
watch: {
oriData: function (val) { },
},
methods: {
$t(str, object) {
const strList = str.split('.');
let obj = window.i18n || {};
let rStr = str;
for (let i = 0, iLen = strList.length; i < iLen; i++) {
if (typeof obj[strList[i]] == 'object') {
obj = obj[strList[i]];
}
if (typeof obj[strList[i]] == 'string') {
rStr = obj[strList[i]];
break;
}
}
if (object) {
for (let key in object) {
rStr = rStr.replace(new RegExp('\{\\s*' + key + '\\s*\}', 'g'), object[key]);
}
}
return rStr;
},
open() {
this.dlgTotal = 0;
this.dlgPage = 1;
this.dlgSelectedModel = [];
this.dlgSelectedModelList = [];
for (let i = 0, iLen = this.selectList.length; i < iLen; i++) {
const item = this.selectList[i];
this.dlgSelectedModel.push(item.id);
this.dlgSelectedModelList.push({
...item,
id: item.id,
name: item.name,
});
}
this.searchModelData();
},
beforeClose(done) {
done();
},
closed() { },
dlgTabClick(tab, event) {
this.dlgTotal = 0;
this.dlgPage = 1;
this.searchModelData();
},
transformTreeData(data) {
for (let i = 0, iLen = data.length; i < iLen; i++) {
const dataI = data[i];
const _children = dataI.modelFileList || [];
const children = [];
dataI.parent = true;
dataI.disabled = true;
_children.forEach(item => {
item.ModTimeNum = new Date(item.ModTime).getTime();
})
_children.sort((a, b) => a.FileName.localeCompare(b.FileName));
_children.sort((a, b) => b.ModTimeNum - a.ModTimeNum);
for (let j = 0, jLen = _children.length; j < jLen; j++) {
const file = _children[j];
if (file.IsDir) continue;
const arr = file.FileName.split('.');
if (!supportCheckPointFileExt.includes(arr[arr.length - 1])) continue;
file._modelID = dataI.id;
file._modelName = dataI.name;
file._modelVersion = dataI.version;
file._preTrainModelUrl = dataI.path;
file.name = file.FileName;
file.id = `${file._modelID}|${file._modelName}|${file._modelVersion}|${file._preTrainModelUrl}|${file.name}`;
children.push(file);
}
dataI.children = children;
}
return data;
},
inputSearch() {
this.dlgTotal = 0;
this.dlgPage = 1;
this.searchModelData();
},
searchModelData() {
const tabName = this.dlgActiveName;
const tabPrams = {
'first': 4,
'second': 2,
'third': 1,
'fourth': 3,
}
const params = {
type: this.type,
queryType: tabPrams[tabName],
repoOwnerName: this.userName,
repoName: this.repoName,
q: this.dlgSearchValue.trim(),
page: this.dlgPage,
needModelFile: true,
notNeedEmpty: true,
};
if (params.queryType == 2 || params.queryType == 4) {
params.orderBy = 'created_unix';
}
this.dlgLoading = true;
this.$axios.get(`/modelsquare/main_query_data`, {
params: params,
}).then(res => {
this.dlgLoading = false;
const data = res.data?.data || [];
this.dlgModelTreeData = this.transformTreeData(data);
this.dlgInitTreeNode = this.dlgModelTreeData[0]?.id
? [this.dlgModelTreeData[0].id]
: [];
this.dlgTotal = parseInt(res.data?.count || 0);
const setCheckedKeysList = this.dlgModelTreeData.reduce((pre, cur) => {
cur.children.forEach((item) => {
if (this.dlgSelectedModel.includes(item.id)) {
pre.push(item.id);
}
});
return pre;
}, []);
this.$refs.dlgTreeRef.setCheckedKeys(setCheckedKeysList);
}).catch(err => {
this.dlgLoading = false;
console.log(err);
});
},
dlgChangeSelect(checked, data) {
this.$refs.dlgTreeRef.setChecked(data.id, false, false);
const index = this.dlgSelectedModelList.findIndex((item) => {
return item.id === data.id;
});
this.dlgSelectedModelList.splice(index, 1);
this.dlgSelectedModel = this.dlgSelectedModelList.map(item => item.id);
},
dlgPageChange(page) {
this.dlgPage = page;
this.searchModelData();
},
onTreeCheckChange(data) {
if (
this.dlgSelectedModelList.length === 0 ||
this.dlgSelectedModelList.every((item) => item.id !== data.id)
) {
if (this.maxCount == 1) {
this.dlgSelectedModelList.forEach((item) => {
this.$refs.dlgTreeRef.setChecked(item.id, false, false);
});
this.dlgSelectedModelList.splice(0, Infinity);
this.dlgSelectedModelList.push(data);
} else {
if (
this.dlgSelectedModelList.some((item) => {
return item._modelID !== data._modelID;
})
) {
this.$refs.dlgTreeRef.setChecked(data.id, false, false);
this.$message.warning(this.$t('modelObj.model_should_same_model'));
} else if (this.dlgSelectedModelList.length === this.maxCount) {
this.$refs.dlgTreeRef.setChecked(data.id, false, false);
this.$message.warning(this.$t('modelObj.model_most', { msg: this.maxCount }));
} else {
this.dlgSelectedModelList.push(data);
}
}
} else {
const index = this.dlgSelectedModelList.findIndex((item) => {
return item.id === data.id;
});
this.dlgSelectedModelList.splice(index, 1);
}
this.dlgSelectedModel = this.dlgSelectedModelList.map((item) => {
return item.id;
});
},
confirm() {
const len = this.dlgSelectedModelList.length;
this.selectList.splice(0, Infinity);
if (len) {
const ckptNames = [];
for (let i = 0; i < len; i++) {
const item = this.dlgSelectedModelList[i];
this.form_model_id = item._modelID;
this.form_model_name = item._modelName;
this.form_model_version = item._modelVersion;
this.form_pre_train_model_url = item._preTrainModelUrl;
ckptNames.push(item.name);
this.selectList.push({ ...item });
}
this.form_ckpt_name = ckptNames.join(';');
} else {
this.form_model_id = '';
this.form_model_name = '';
this.form_model_version = '';
this.form_pre_train_model_url = '';
this.form_ckpt_name = '';
}
if (this.selectList.length) {
this.errStatus = false;
}
this.dlgShow = false;
},
check() {
if (!this.selectList.length) {
this.errStatus = true;
return false;
}
this.errStatus = false;
return true;
},
},
beforeMount() { },
mounted() {
const dataEl = this.$el.previousElementSibling;
if (dataEl) {
const dataset = dataEl.dataset;
if (dataset.modelId) {
this.form_model_id = dataset.modelId;
this.form_model_name = dataset.modelName;
this.form_model_version = dataset.modelVersion;
this.form_pre_train_model_url = dataset.preTrainModelUrl;
this.form_ckpt_name = dataset.ckptName;
const file = {
_modelID: dataset.modelId,
_modelName: dataset.modelName,
_modelVersion: dataset.modelVersion,
_preTrainModelUrl: dataset.preTrainModelUrl,
name: dataset.ckptName,
};
file.id = `${file._modelID}|${file._modelName}|${file._modelVersion}|${file._preTrainModelUrl}|${file.name}`;
this.selectList.push(file);
}
if (dataset.required) {
this.required = true;
}
}
},
};
</script>

<style scoped lang="less">
.model-select {
display: flex;
margin-bottom: 28px;

.title {
width: 130px;
text-align: right;
margin-right: 24px;
color: #101010;
font-size: 14px;
align-items: center;
display: flex;
justify-content: flex-end;

.required {
position: relative;

&::after {
position: absolute;
content: "*";
top: -3px;
right: -10px;
color: red;
}
}
}

.content {
display: inline-block;
width: 48.5%;
margin-right: 5px;

.model-list-c {
min-height: 36px;
border-radius: 4px;
border: 1px solid #DCDFE6;
box-sizing: border-box;
color: #606266;
padding: 5px 15px;

.model-item {
line-height: 26px;
font-size: 14px;
color: rgba(0, 0, 0, 0.87);
}

.model-item-model {
color: rgba(136, 136, 136, 1);
}

.model-item-file {
padding-left: 5px;
}

.model-item-placeholder {
height: 26px;
line-height: 26px;
color: rgba(0, 0, 0, 0.4);
opacity: 0.45 !important;
font-size: 14px;
}

&.error {
color: #9f3a38;
background: #fff6f6;
border-color: #e0b4b4;
}
}
}

.btn-select {
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
color: rgb(3, 102, 214);

i {
margin-right: 4px;
}
}
}

.model-dlg {
/deep/.el-dialog__body {
padding-top: 0;
}

.dlg-content {
display: flex;
min-height: 300px;

.left-area {
flex: 1;
margin-right: 15px;
border-right: 1px solid rgb(245, 245, 246);
padding-right: 15px;
position: relative;
overflow: hidden;

.model-tabs-c {
display: flex;
align-items: center;

.model-tabs {
flex: 1;
overflow: hidden;
margin-right: 5px;
}

.search-inp {
overflow: hidden;
width: 200px;
z-index: 5;
position: relative;
margin-top: -10px;

.search-inp-icon {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
width: 22px;
cursor: pointer;
}
}
}

.pagination-c {
margin-top: 25px;
text-align: center;
}
}

.right-area {
width: 300px;
padding-right: 30px;
position: relative;


.right-title {
font-size: 14px;
height: 40px;
text-align: left;
color: rgb(0, 102, 255);
line-height: 40px;
}

.right-selected-list {
margin: 14px 0;
}

.right-btn-c {
text-align: right;
position: absolute;
bottom: 15px;
width: 100%;
padding-right: 30px;

/deep/ .el-button {
background: rgb(56, 158, 13);
color: rgb(255, 255, 255);
border: 1px solid rgb(56, 158, 13);
}
}
}
}
}

.el-tree {
max-height: 400px;
overflow-y: auto;
overflow-x: hidden;
position: relative;
cursor: default;
background: #fff;
color: #606266;
font-family: SourceHanSansSC-medium;
}

.custom-tree-node {
display: flex;
align-items: center;
justify-content: space-between;
}

.custom-tree-node .model-title {
font-size: 14px;
color: #101010;
font-weight: 600;
flex: 1;
}

.custom-tree-node .model-repolink {
flex: 1;
text-align: right;
font-size: 12px;
}

.el-tree /deep/ .el-tree-node__content {
height: 40px;
background-color: #f5f5f6;
}

.el-tree /deep/ .el-tree-node__children .el-tree-node__content {
height: 30px;
background-color: #fff;
line-height: 20px;
font-size: 12px;
}

/deep/ .el-checkbox-group .el-checkbox {
max-width: 100%;
min-width: 80%;
}

/deep/ .el-checkbox-group .el-checkbox .el-checkbox__label {
max-width: 100%;
overflow: hidden;
vertical-align: middle;
text-overflow: ellipsis;
}

.model-nowrap {
overflow: hidden;
text-overflow: ellipsis;
}

.slot-wrap {
flex: 1;
padding-right: 2rem;
max-width: 93%;
}

.multiple-wrap {
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
display: -webkit-box;
max-width: 400px;
overflow: hidden;
}

.unzip-failed {
margin-left: 1rem;
color: red;
}

.zip-loading {
margin-left: 1rem;
color: #fcca00;
}

.model-search-vue {
z-index: 9999;
position: absolute;
right: 31%;
height: 30px;
top: 6px;
}

.select-model-label {
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
margin-right: 1rem;
white-space: nowrap;
}

.model_flex {
display: flex;
align-items: center;
}

.model-version {
margin-left: 4px;
border-radius: 4px;
color: rgba(16, 16, 16, 0.8);
border-radius: 4px;
font-size: 12px;
background: rgba(220, 220, 220, 0.8);
padding: 1px 3px;
}
</style>

+ 17
- 15
web_src/js/features/cloudrbanin.js View File

@@ -404,31 +404,33 @@ export default async function initCloudrain() {
const versionName = this.dataset.version;
stopVersion(versionName, ID, repoPath);
});
function getModelInfo(repoPath, modelName, versionName, jobName) {
function getModelInfo(repoPath, modelId, modelName, versionName, jobName) {
$.get(
`${repoPath}/modelmanage/show_model_info_api?name=${modelName}`,
`/api/v1/repos${repoPath}/modelmanage/query_model_byId?id=${modelId}`,
(data) => {
if (data.length === 0) {
$(`#${jobName}`).popup("toggle");
if (data && data.id == modelId) {
location.href = `${repoPath}/modelmanage/model_readme_tmpl?name=${data.name}`;
} else {
let versionData = data.filter((item) => {
return item.version === versionName;
});
if (versionData.length == 0) {
$(`#${jobName}`).popup("toggle");
} else {
location.href = `${repoPath}/modelmanage/show_model_info?name=${modelName}`;
}
$(`#${jobName}`).popup("toggle");
}
}
);
).catch(err => {
console.log(err);
$(`#${jobName}`).popup("toggle");
});
}
$(".goto_modelmanage").click(function () {
const repoPath = this.dataset.repopath;
let repoPath = this.dataset.repopath;
const modelId = this.dataset.modelid;
const modelName = this.dataset.modelname;
const versionName = this.dataset.version;
const jobName = this.dataset.jobname;
getModelInfo(repoPath, modelName, versionName, jobName);
const modelRepoOwnerName = this.dataset.modelrepoownername;
const modelRepoName = this.dataset.modelreponame;
if (modelRepoOwnerName && modelRepoName) {
repoPath = `/${modelRepoOwnerName}/${modelRepoName}`
}
getModelInfo(repoPath, modelId, modelName, versionName, jobName);
});
function debugAgain(ID, debugUrl, redirect_to) {
if ($("#" + ID + "-text").text() === "RUNNING") {


+ 30
- 0
web_src/js/features/i18nVue.js View File

@@ -136,6 +136,21 @@ export const i18nVue = {
openi: '启智',
c2net: '智算网络',
},
modelObj: {
model_label: '选择模型',
model_select_placeholder: '选择模型文件',
model_select: '选择模型',
model_current_repo: '本项目',
model_my: '我的模型',
model_public: '公开模型',
model_collected: '我收藏的',
model_search_placeholder: '搜索模型名称...',
model_selected: '已选模型文件',
model_ok: '确定',
model_most: '最多不超过 {msg} 个文件',
model_should_same_model: '选择同一模型下的文件',
model_suport_file_tips: '模型文件支持的格式为 [ckpt, pb, h5, json, pkl, pth, t7, pdparams, onnx, pbtxt, keras, mlmodel, cfg, pt]',
},
},
US: {
@@ -278,5 +293,20 @@ export const i18nVue = {
openi: 'OpenI',
c2net: 'C²NET',
},
modelObj: {
model_label: 'Select Model',
model_select_placeholder: 'Please select model file',
model_select: 'Select Model',
model_current_repo: 'Current repository',
model_my: 'My models',
model_public: 'Public models',
model_collected: 'My collection',
model_search_placeholder: 'Search model name...',
model_selected: 'Selected model file',
model_ok: 'OK',
model_most: 'Up to {msg} files.',
model_should_same_model: 'Select the files should in the same model.',
model_suport_file_tips: 'The supported format of the model file is [ckpt, pb, h5, json, pkl, pth, t7, pdparams, onnx, pbtxt, keras, mlmodel, cfg, pt]',
},
},
};

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

@@ -51,6 +51,7 @@ import initCloudrainSow from "./features/cloudbrainShow.js";
import initImage from "./features/images.js";
import selectDataset from "./components/dataset/selectDataset.vue";
import referenceDataset from "./components/dataset/referenceDataset.vue";
import ModelSelect from "./components/model/ModelSelect.vue";
// import $ from 'jquery.js'
import router from "./router/index.js";
import { Message } from "element-ui";
@@ -2985,6 +2986,7 @@ $(document).ready(async () => {
initVueIde();
initVueWxAutorize();
initVueselectDataset();
initVueModelSelect();
initVuereferenceDataset();
initTeamSettings();
initCtrlEnterSubmit();
@@ -4643,6 +4645,14 @@ function initVuereferenceDataset() {
render: (h) => h(referenceDataset),
});
}
function initVueModelSelect() {
const el = document.getElementById("select-multi-model");
if (!el) return;
new Vue({
el: el,
render: (h) => h(ModelSelect),
});
}
window.timeAddManual = function () {
$(".mini.modal")
.modal({


+ 1
- 0
web_src/js/standalone/cloudbrainNew.js View File

@@ -195,6 +195,7 @@
let flagModel = $(".cloudbrain-type").data("flag-model");
// 获取模型列表和模型名称对应的模型版本
$(document).ready(function () {
return; // 改用模型选择组件了
if (!flagModel) return;
else {
$.get(


+ 49
- 0
web_src/vuepages/apis/modules/modelmanage.js View File

@@ -1,5 +1,6 @@
import service from "../service";
import Qs from 'qs';
import { param } from "jquery";

// 保存本地模型
export const saveLocalModel = (data) => {
@@ -114,3 +115,51 @@ export const setCompleteMultipart = (data) => {
data: Qs.stringify(data),
});
};

// markdown预览
// data: { userName, repoName, context, text }
export const getMarkdownPreview = (data) => {
return service({
url: `/api/v1/repos/${data.repoOwnerName}/${data.repoName}/markdown`,
method: 'post',
headers: { 'Content-type': 'application/x-www-form-urlencoded' },
params: {},
data: Qs.stringify({
mode: 'gfm',
context: '',
text: data.text,
}),
});
}

// 获取模型介绍数据
export const getModelIntroduce = (params) => {
return service({
url: `${params.repo}/modelmanage/model_readme_data`,
method: 'get',
params: { id: params.id },
});
}

// 设置模型介绍数据
export const setModelIntroduce = (data) => {
return service({
url: `${data.repo}/modelmanage/model_readme_data`,
method: 'post',
headers: { 'Content-type': 'application/x-www-form-urlencoded' },
params: {},
data: Qs.stringify({
id: data.id,
content: data.content,
})
});
}

// 获取模型演化图谱数据
export const getModelEvolutionMap = (params) => {
return service({
url: `${params.repo}/modelmanage/model_evolution_map_data`,
method: 'get',
params: { id: params.id },
});
}

+ 44
- 0
web_src/vuepages/apis/modules/modelsquare.js View File

@@ -0,0 +1,44 @@
import service from "../service";
import Qs from 'qs';

// 获取模型广场列表
// q,engine,resource,label,recommend-bool
// queryType-1为查公开模型,2:我的模型,3:我收藏的模型, 4: 本项目
// repoName, repoOwnerName, needModelFile-结果是否带文件
// page,pageSize
export const getModelList = (params) => {
return service({
url: '/modelsquare/main_query_data',
method: 'get',
params,
});
}

// 获取模型广场筛选项
export const getModelSqaureFilters = () => {
return service({
url: '/modelsquare/main_query_label',
method: 'get',
params: {},
});
}

// 模型收藏/取消收藏
// data: id, collected-为true表示收藏此模型,为false表示取消收藏此模型
export const setModelFav = (data) => {
return service({
url: '/modelsquare/modify_model_collect',
method: 'put',
data: Qs.stringify(data),
});
}

// 模型推荐/取消推荐
// data: id, recommend-为true表示推荐此模型,为false表示取消推荐此模型
export const setModelRecommend = (data) => {
return service({
url: '/modelsquare/modify_model_recommend',
method: 'put',
data: Qs.stringify(data),
});
}

+ 22
- 0
web_src/vuepages/components/NotFound.vue View File

@@ -0,0 +1,22 @@
<template>
<div class="ui container center" style="min-height:80%;">
<div class="ui basic very padded segment">
<img class="ui centered medium image" src="/img/icon-404@2x.png">
<h2>{{ $t('emptyPage') }} </h2>
<p v-html="$t('emptyPageDescr')"></p>
</div>
</div>
</template>

<script>
export default {
name: "NotFound",
props: {
visible: { type: Boolean, default: false },
},
data() {
return {};
}
};
</script>
<style scoped lang="less"></style>

+ 563
- 0
web_src/vuepages/components/cloudbrain/ModelSelect.vue View File

@@ -0,0 +1,563 @@
<template>
<div class="model-select">
<div class="title" v-if="showTitle"><span :class="required ? 'required' : ''">{{ selfTitle }}</span></div>
<div class="content">
<div class="model-list-c" :class="errStatus ? 'error' : ''">
<div class="model-item" v-for="(item, index) in selectList" :key="item.id">
{{ item.name }};
</div>
<div v-if="selectList.length == 0" class="model-item-placeholder">
{{ $t('modelObj.model_select_placeholder') }}
</div>
</div>
<div class="btn-select" @click="dlgShow = true">
<i class="el-icon-plus"></i>
<span>{{ $t('modelObj.model_select') }}</span>
</div>
</div>
<el-dialog class="model-dlg" :visible.sync="dlgShow" :title="$t('modelObj.model_select')" width="1000px" :modal="true"
:close-on-click-modal="false" :show-close="true" :destroy-on-close="false" :before-close="beforeClose" @open="open"
@closed="closed">
<div class="dlg-content">
<div class="left-area" v-loading="dlgLoading">
<div class="model-tabs-c">
<el-tabs class="model-tabs" v-model="dlgActiveName" @tab-click="dlgTabClick">
<el-tab-pane :label="$t('modelObj.model_current_repo')" name="first"></el-tab-pane>
<el-tab-pane :label="$t('modelObj.model_my')" name="second"></el-tab-pane>
<el-tab-pane :label="$t('modelObj.model_public')" name="third"></el-tab-pane>
<el-tab-pane :label="$t('modelObj.model_collected')" name="fourth"></el-tab-pane>
</el-tabs>
<el-input class="search-inp" :placeholder="$t('modelObj.model_search_placeholder')" v-model="dlgSearchValue"
@keyup.enter.native="inputSearch">
<div slot="suffix" class="search-inp-icon" @click="inputSearch">
<i class="el-icon-search"></i>
</div>
</el-input>
</div>
<el-tree :data="dlgModelTreeData" ref="dlgTreeRef" highlight-current show-checkbox node-key="id"
:default-expanded-keys="dlgInitTreeNode" :props="dlgTreeProps" :index="10" accordion
@check="onTreeCheckChange">
<span slot-scope="{ node, data }" class="slot-wrap">
<span v-if="data.parent" class="custom-tree-node">
<el-tooltip v-if="data.description" placement="top-start">
<div slot="content" class="multiple-wrap"> {{ data.description }}</div>
<span class="model-title model-nowrap">
<div class="model_flex">
<span style="flex: inherit" class="model-nowrap">{{ node.label }}</span>
<img v-if="data.recommend == 1" style="margin-left: 0.4rem" src="/img/jian.svg" />
</div>
</span>
</el-tooltip>
<span v-else class="model-title model-nowrap">
<div class="model_flex">
<span style="flex: inherit" class="model-nowrap">{{ node.label }}</span>
<img v-if="data.recommend == 1" style="margin-left: 0.4rem" src="/img/jian.svg" />
</div>
</span>
<span class="model-repolink model-nowrap" @click.stop="return false;">
<a :href="'/' + data.repoOwnerName + '/' + data.repoName + '/modelmanage/model_readme_tmpl?name=' + data.name"
target="_blank">
{{ data.repoOwnerName }}/{{ data.repoName }}
</a>
</span>
</span>
<span v-else style="display: flex">
<span class="model-nowrap" :title="node.label">
{{ node.label }}
</span>
</span>
</span>
</el-tree>
<div class="pagination-c">
<el-pagination background @current-change="dlgPageChange" :current-page="dlgPage" :page-size="dlgPageSize"
layout="total, prev, pager, next" :total="dlgTotal">
</el-pagination>
</div>
</div>
<div class="right-area">
<div class="right-title"><span>{{ $t('modelObj.model_selected') }}</span></div>
<div class="right-selected-list">
<el-checkbox-group v-model="dlgSelectedModel">
<el-checkbox v-for="(item, index) in dlgSelectedModelList" :key="item.id" :label="item.id"
:true-label="item.id" :title="item.name" @change="(checked) => dlgChangeSelect(checked, item)">
{{ item.name }}
</el-checkbox>
</el-checkbox-group>
</div>
<div class="right-btn-c">
<el-button type="primary" @click="confirm">{{ $t('modelObj.model_ok') }}</el-button>
</div>
</div>
</div>
</el-dialog>
</div>
</template>

<script>
import { getModelList } from '~/apis/modules/modelsquare';

export default {
name: "ModelSelect",
props: {
title: { type: String, default: '' },
showTitle: { type: Boolean, default: true },
required: { type: Boolean, default: true },
type: { type: String, default: '' },
maxCount: { type: Number, default: 5 },
userName: { type: String, default: '' },
repoName: { type: String, default: '' },
oriData: { type: Array, default: () => [] },
},
data() {
return {
selectList: [],
dlgShow: false,
dlgLoading: false,
dlgActiveName: 'first',
dlgSearchValue: '',

dlgTreeProps: {
children: "children",
label: "name",
},
dlgModelTreeData: [],
dlgInitTreeNode: [],

dlgPage: 1,
dlgPageSize: 10,
dlgTotal: 0,

dlgSelectedModel: [],
dlgSelectedModelList: [],

exceedSize: 0,

errStatus: false,
};
},
computed: {
selfTitle() {
return this.title || this.$t('modelObj.model_label');
}
},
watch: {
oriData: function (val) { },
},
methods: {
open() {
this.dlgTotal = 0;
this.dlgPage = 1;
this.dlgSelectedModel = [];
this.dlgSelectedModelList = [];
for (let i = 0, iLen = this.selectList.length; i < iLen; i++) {
const item = this.selectList[i];
this.dlgSelectedModel.push(item.id);
this.dlgSelectedModelList.push({
id: item.id,
name: item.name,
});
}
this.searchModelData();
},
beforeClose(done) {
done();
},
closed() { },
dlgTabClick(tab, event) {
this.dlgTotal = 0;
this.dlgPage = 1;
this.searchModelData();
},
transformTreeData(data) {
for (let i = 0, iLen = data.length; i < iLen; i++) {
const dataI = data[i];
const children = dataI.modelFileList || [];
for (let j = 0, jLen = children.length; j < jLen; j++) {
const file = children[j];
file._modelID = dataI.id;
file._modelName = dataI.name;
file._modelVersion = dataI.version;
file._preTrainModelUrl = dataI.path;
file.name = file.FileName;
file.id = `${file._modelID}|${file._modelName}|${file._modelVersion}|${file._preTrainModelUrl}|${file.name}`;
}
dataI.children = children;
}
return data;
},
inputSearch() {
this.dlgTotal = 0;
this.dlgPage = 1;
this.searchModelData();
},
searchModelData() {
const tabName = this.dlgActiveName;
const tabPrams = {
'first': 4,
'second': 2,
'third': 1,
'fourth': 3,
}
const params = {
type: this.type,
queryType: tabPrams[tabName],
repoOwnerName: this.userName,
repoName: this.repoName,
q: this.dlgSearchValue.trim(),
page: this.dlgPage,
needModelFile: true,
notNeedEmpty: true,
};
this.dlgLoading = true;
getModelList(params).then(res => {
this.dlgLoading = false;
const data = res.data?.data || [];
this.dlgModelTreeData = this.transformTreeData(data);
console.log(this.dlgModelTreeData);
this.dlgInitTreeNode = this.dlgModelTreeData[0]?.id
? [this.dlgModelTreeData[0].id]
: [];
this.dlgTotal = parseInt(res.data?.count || 0);
const setCheckedKeysList = this.dlgModelTreeData.reduce((pre, cur) => {
cur.children.forEach((item) => {
if (this.dlgSelectedModel.includes(item.id)) {
pre.push(item.id);
}
});
return pre;
}, []);
this.$refs.dlgTreeRef.setCheckedKeys(setCheckedKeysList);
}).catch(err => {
this.dlgLoading = false;
console.log(err);
});
},
dlgChangeSelect(checked, data) {
this.$refs.dlgTreeRef.setChecked(data.id, false, false);
const index = this.dlgSelectedModelList.findIndex((item) => {
return item.id === data.id;
});
this.dlgSelectedModelList.splice(index, 1);
this.dlgSelectedModel = this.dlgSelectedModelList.map(item => item.id);
},
dlgPageChange(page) {
this.dlgPage = page;
this.searchModelData();
},
onTreeCheckChange(data) {
if (
this.dlgSelectedModelList.length === 0 ||
this.dlgSelectedModelList.every((item) => item.id !== data.id)
) {
/*if (
this.dlgSelectedModelList.some((item) => {
return item.name.split(".")[0] === data.name.split(".")[0];
})
) {
this.$refs.dlgTreeRef.setChecked(data.id, false, false);
this.$message.warning(this.$t('modelObj.model_not_equal_file'));
} else */
if (this.dlgSelectedModelList.length === this.maxCount) {
this.$refs.dlgTreeRef.setChecked(data.id, false, false);
this.$message.error(this.$t('modelObj.model_most', { msg: this.maxCount }));
} else {
this.dlgSelectedModelList.push(data);
}
} else {
const index = this.dlgSelectedModelList.findIndex((item) => {
return item.id === data.id;
});
this.dlgSelectedModelList.splice(index, 1);
}
this.dlgSelectedModel = this.dlgSelectedModelList.map((item) => {
return item.id;
});
},
confirm() {
const ids = this.dlgSelectedModelList.map(item => item.id);
const names = this.dlgSelectedModelList.map(item => item.name);
this.selectList.splice(0, Infinity);
for (let i = 0, iLen = ids.length; i < iLen; i++) {
this.selectList.push({
id: ids[i],
name: names[i]
});
}
if (this.selectList.length) {
this.errStatus = false;
}
this.dlgShow = false;
},
check() {
if (!this.selectList.length) {
this.errStatus = true;
return false;
}
this.errStatus = false;
return true;
},
},
beforeMount() { }
};
</script>

<style scoped lang="less">
.model-select {
display: flex;
margin-bottom: 28px;

.title {
width: 200px;
text-align: right;
margin-right: 24px;
color: #101010;
font-size: 14px;
display: flex;
justify-content: flex-end;
padding-top: 6px;

.required {
position: relative;

&::after {
position: absolute;
content: "*";
top: -3px;
right: -10px;
color: red;
}
}
}

.content {
flex: 1;
display: flex;

.model-list-c {
margin-right: 5px;
flex: 1;
min-height: 32px;
border-radius: 4px;
border: 1px solid #DCDFE6;
box-sizing: border-box;
color: #606266;
padding: 0 15px;

.model-item {
line-height: 32px;
font-size: 13px;
}

.model-item-placeholder {
line-height: 32px;
color: rgba(0, 0, 0, 0.6);
opacity: 0.45 !important;
font-size: 13px;
}

&.error {
color: #9f3a38;
background: #fff6f6;
border-color: #e0b4b4;
}
}

.btn-select {
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
color: rgb(3, 102, 214);

i {
margin-right: 2px;
}
}
}
}

.model-dlg {
/deep/.el-dialog__body {
padding-top: 0;
}

.dlg-content {
display: flex;
min-height: 300px;

.left-area {
flex: 1;
margin-right: 15px;
border-right: 1px solid rgb(245, 245, 246);
padding-right: 15px;
position: relative;
overflow: hidden;

.model-tabs-c {
display: flex;
align-items: center;

.model-tabs {
flex: 1;
overflow: hidden;
margin-right: 5px;
}

.search-inp {
overflow: hidden;
width: 200px;
z-index: 5;
position: relative;
margin-top: -10px;

.search-inp-icon {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
width: 22px;
cursor: pointer;
}
}
}

.pagination-c {
margin-top: 25px;
text-align: center;
}
}

.right-area {
width: 300px;
padding-right: 30px;
position: relative;


.right-title {
font-size: 14px;
height: 40px;
text-align: left;
color: rgb(0, 102, 255);
line-height: 40px;
}

.right-selected-list {
margin: 14px 0;
}

.right-btn-c {
text-align: right;
position: absolute;
bottom: 15px;
width: 100%;
padding-right: 30px;

}
}
}
}

.el-tree {
max-height: 400px;
overflow-y: auto;
overflow-x: hidden;
position: relative;
cursor: default;
background: #fff;
color: #606266;
font-family: SourceHanSansSC-medium;
}

.custom-tree-node {
display: flex;
align-items: center;
justify-content: space-between;
}

.custom-tree-node .model-title {
font-size: 14px;
color: #101010;
font-weight: 600;
flex: 1;
}

.custom-tree-node .model-repolink {
flex: 1;
text-align: right;
font-size: 12px;
}

.el-tree /deep/ .el-tree-node__content {
height: 40px;
background-color: #f5f5f6;
}

.el-tree /deep/ .el-tree-node__children .el-tree-node__content {
height: 30px;
background-color: #fff;
line-height: 20px;
font-size: 12px;
}

/deep/ .el-checkbox-group .el-checkbox {
max-width: 100%;
min-width: 80%;
}

/deep/ .el-checkbox-group .el-checkbox .el-checkbox__label {
max-width: 100%;
overflow: hidden;
vertical-align: middle;
text-overflow: ellipsis;
}

.model-nowrap {
overflow: hidden;
text-overflow: ellipsis;
}

.slot-wrap {
flex: 1;
padding-right: 2rem;
max-width: 93%;
}

.multiple-wrap {
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
display: -webkit-box;
max-width: 400px;
overflow: hidden;
}

.unzip-failed {
margin-left: 1rem;
color: red;
}

.zip-loading {
margin-left: 1rem;
color: #fcca00;
}

.model-search-vue {
z-index: 9999;
position: absolute;
right: 31%;
height: 30px;
top: 6px;
}

.select-model-label {
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
margin-right: 1rem;
white-space: nowrap;
}

.model_flex {
display: flex;
align-items: center;
}
</style>

+ 50
- 0
web_src/vuepages/langs/config/en-US.js View File

@@ -14,6 +14,11 @@ const en = {
edit: 'Edit',
delete: 'Delete',
tips: 'Tips',
expandMore: 'Expand More',
goBack: 'Go back',
star: 'Star',
unStar: 'UnStar',
submit: 'Submit',

accomplishTask: 'Accomplish Task',
adminOperate: 'Administrator Operation',
@@ -22,6 +27,7 @@ const en = {
succeeded: 'Succeeded',
debugTask: 'Debug Task',
trainTask: 'Train Task',
trainDuration: "Train task duration",
inferenceTask: 'Inference Task',
benchmarkTask: 'Benchmark Task',
createPublicProject: 'Create Public Projects',
@@ -82,6 +88,9 @@ const en = {
noPointGainRecord: 'No Point Earn Record Yet',
noPointConsumeRecord: 'No Point Consume Record Yet',

emptyPage: 'Request forbidden by administrative rules',
emptyPageDescr: 'The page you are trying to reach either <strong>does not exist</strong> or <strong>you are not authorized</strong> to view it.',

resourcesManagement: {
OpenI: 'OpenI',
C2Net: 'C2Net',
@@ -236,12 +245,17 @@ const en = {
createModel: 'Create Model',
importLocalModel: 'Import Local Model',
importOnlineModel: 'Import Online Model',
modelInfo: 'Model information',
modelBriefIntro: 'Model brief introduction',
trainingInfo: 'Training information',
modifyModelInfo: 'Modify model information',
modelFiles: 'Model files',
addModelFiles: 'Add model files',
uploadModelFiles: 'Upload model files',
pleaseInputModelName: 'Please input model name',
version: 'Version',
modelEngine: 'Model engine',
modelSource: 'Model source',
modelLabel: 'Model label',
modelLabelInputTips: 'Input labels, multiple labels are separated by spaces',
modelDescr: 'Model description',
@@ -291,6 +305,26 @@ const en = {
modelAccess:'Model Access',
modelAccessPublic:'Public',
modelAccessPrivate:'Private',
modelSettings: 'Model Settings',
edit: 'Edit',
editFiles: 'Edit files',
preview: 'Preview',
modelIntroduction: 'Model Introduction',
hasNoIntroForModel: 'No detailed model introduction yet',
createModelIntro: 'Create model Introduction',
briefIntroduction: 'Introduction',
addLabels: 'Add labels',
discardFileChanges: 'Discard current content modifications?',
editFileContentFirst: 'Please edit the file content first!',
ownerRepository: 'Repository',
creator: 'Creator',
modelEvolutionMap: 'Model Evolution Map',
settings: 'Settings',
parentModel: 'Parent Model',
currentModel: 'Current Model',
derivedModel: 'Derived Model',
refRepository: 'Reference repository',
modelDownloadAll: 'Download All',
},
repos: {
activeOrganization: 'Active Organization',
@@ -345,6 +379,22 @@ const en = {
raw_seconds: 'seconds',
raw_minutes: 'minutes',
},
modelObj: {
model_label: 'Select Model',
model_select_placeholder: 'Please select model file',
model_select: 'Select Model',
model_current_repo: 'Current repository',
model_my: 'My models',
model_public: 'Public models',
model_collected: 'My collection',
model_search_placeholder: 'Search model name...',
model_selected: 'Selected model file',
model_ok: 'OK',
model_most: 'Up to {msg} files.',
model_should_same_model: 'Select the files should in the same model.',
model_search: 'Search model',
model_square_empty: 'No Models',
},
datasets: {
computer_vision: "computer vision",
natural_language_processing: "natural language processing",


+ 50
- 0
web_src/vuepages/langs/config/zh-CN.js View File

@@ -13,6 +13,11 @@ const zh = {
edit: "修改",
delete: "删除",
tips: "提示",
expandMore: '展开更多',
goBack: '返回上一级',
star: '收藏',
unStar: '取消收藏',
submit: '提交',

accomplishTask: "积分任务",
adminOperate: "管理员操作",
@@ -21,6 +26,7 @@ const zh = {
succeeded: "已完成",
debugTask: "调试任务",
trainTask: "训练任务",
trainDuration: "训练时长",
inferenceTask: "推理任务",
benchmarkTask: "评测任务",
createPublicProject: "创建公开项目",
@@ -81,6 +87,9 @@ const zh = {
noPointGainRecord: "还没有积分获取记录",
noPointConsumeRecord: "还没有积分消耗记录",

emptyPage: '您的访问受限!',
emptyPageDescr: '您正尝试访问的页面 <strong>不存在</strong> 或 <strong>您尚未被授权</strong> 查看该页面。',

resourcesManagement: {
OpenI: "启智集群",
C2Net: "智算集群",
@@ -252,13 +261,18 @@ const zh = {
createModel: '创建模型',
importLocalModel: '导入本地模型',
importOnlineModel: '导入线上模型',
modelInfo: '模型信息',
modelBriefIntro: '模型简介',
trainingInfo: '训练相关信息',
modifyModelInfo: '修改模型信息',
modelFiles: '模型文件',
addModelFiles: '增加模型文件',
uploadModelFiles: '上传模型文件',
pleaseInputModelName: '请输入模型名称',
version: '版本',
modelEngine: '模型框架',
modelLabel: '模型标签',
modelSource: '模型来源',
modelLabelInputTips: '输入标签,多个标签用空格区分',
modelDescr: '模型描述',
modelDescrInputTips: '描述字数不超过255个字符',
@@ -307,6 +321,26 @@ const zh = {
modelAccess: '模型权限',
modelAccessPublic: '公开',
modelAccessPrivate: '私有',
modelSettings: '模型信息设置',
edit: '编辑',
editFiles: '编辑文件',
preview: '预览',
modelIntroduction: '模型介绍',
hasNoIntroForModel: '还没有详细的模型介绍',
createModelIntro: '创建模型介绍',
briefIntroduction: '简介',
addLabels: '新增标签',
discardFileChanges: '是否放弃当前内容修改?',
editFileContentFirst: '请先编辑文件内容!',
ownerRepository: '所属项目',
creator: '创建者',
modelEvolutionMap: '模型演化图谱',
settings: '设置',
parentModel: '父模型',
currentModel: '当前模型',
derivedModel: '衍生模型',
refRepository: '引用项目',
modelDownloadAll: '下载全部模型文件',
},
repos: {
activeOrganization: '活跃组织',
@@ -361,6 +395,22 @@ const zh = {
raw_seconds: '秒',
raw_minutes: '分钟',
},
modelObj: {
model_label: '选择模型',
model_select_placeholder: '选择模型文件',
model_select: '选择模型',
model_current_repo: '本项目',
model_my: '我的模型',
model_public: '公开模型',
model_collected: '我收藏的模型',
model_search_placeholder: '搜索模型名称...',
model_selected: '已选模型文件',
model_ok: '确定',
model_most: '最多不超过 {msg} 个文件',
model_should_same_model: '选择同一模型下的文件',
model_search: '搜索模型',
model_square_empty: '空荡荡的,什么都没有',
},
datasets:{
computer_vision: "计算机视觉",
natural_language_processing: "自然语言处理",


+ 65
- 32
web_src/vuepages/pages/dataset/square/components/PublicDataset.vue View File

@@ -2,12 +2,12 @@
<div v-loading="loading">
<div class="dataset_head_wrap">
<el-checkbox v-model="checked" style="padding: 0.5rem 1rem;" @change="handleCheckedChange">{{$t('datasets.platform_recommendations')}}</el-checkbox>
<el-dropdown trigger="click" style="cursor: pointer;">
<el-dropdown trigger="click" style="cursor: pointer;" size="default">
<span class="el-dropdown-link">
{{$t('datasets.sort')}}<i class="el-icon-caret-bottom el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :class="{'active':item.active}" v-for="item in sortList" :key="item.name" @click.native="handleSort(item)">{{$t('datasets.'+item.name)}}</el-dropdown-item>
<el-dropdown-item :class="sortSelect == item.name ? 'active' : ''" v-for="item in sortList" :key="item.name" @click.native="handleSort(item)">{{$t('datasets.'+item.name)}}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
@@ -53,11 +53,11 @@
:src="`/user/avatar/${item.User.Name}/-1`">
</a>
<span class="dataset_extra_time">{{item.CreatedUnix | DateTransfer}}</span>
<span class="dataset_extra_link":title="$t('datasets.downloadtimes')">
<span class="dataset_extra_link" :title="$t('datasets.citations')">
<i class="ri-link"></i>
<span class="dataset_extra_content">{{item.UseCount}}</span>
</span>
<span class="dataset_extra_download" :title="$t('datasets.citations')">
<span class="dataset_extra_download" :title="$t('datasets.downloadtimes')">
<i class="ri-download-line"></i>
<span class="dataset_extra_content">{{item.DownloadTimes}}</span>
</span>
@@ -115,7 +115,15 @@ export default {
licenseValue:{
type:String,
default:''
}
},
sortValue:{
type:String,
default:''
},
recommendValue:{
type:Boolean,
default:false
},
},
data() {
return {
@@ -132,12 +140,11 @@ export default {
license:''
},
checked:false,
sortSelect: 'default',
sortList:[
{name:'default',active:true},
{name:'latest',active:false},
{name:'oldest',active:false},
{name:'recentupdate',active:false},
{name:'leastupdate',active:false},
{name:'downloadtimes',active:false},
{name:'moststars',active:false},
{name:'mostusecount',active:false},
@@ -147,37 +154,71 @@ export default {
};
},
watch: {
dataGet(newVal) {
this.params.page = 1
this.$nextTick(() => {
this.getDataList()
});
},
searchValue(newVal){
if(!newVal){
if (!newVal) {
this.params.page = 1
this.params.q = newVal
this.getDataList(this.dataGet)
this.$nextTick(() => {
this.getDataList()
});
}
},
searchFlag(newVal){
this.params.page = 1
this.params.q = this.searchValue
this.getDataList(this.dataGet)
this.$nextTick(() => {
this.getDataList()
});
},
categoryValue(val){
this.params.category = val
this.params.page = 1
this.getDataList(this.dataGet)
this.$nextTick(() => {
this.getDataList()
});
},
taskValue(val){
this.params.task = val
this.params.page = 1
this.getDataList(this.dataGet)
this.$nextTick(() => {
this.getDataList()
});
},
licenseValue(val){
this.params.license = val
this.params.page = 1
this.getDataList(this.dataGet)
this.$nextTick(() => {
this.getDataList()
});
},
sortValue(value) {
this.sortSelect = value;
this.params.page = 1
this.$nextTick(() => {
this.getDataList()
});
},
recommendValue(value) {
this.checked = value;
this.params.page = 1
this.$nextTick(() => {
this.getDataList()
});
},
},
methods: {
getDataList(dataType){
let url = `/explore/${dataType}`
methods: {
getDataList(){
this.params.q = this.searchValue
this.params.category = this.categoryValue
this.params.task = this.taskValue
this.params.license = this.licenseValue
this.params.sort = this.sortValue
this.params.recommend = this.recommendValue
this.sortSelect = this.sortValue
this.checked = this.recommendValue
const dataType = this.dataGet
const url = `/explore/${dataType}`
this.loading=true
getDatasets(url,this.params).then((res)=>{
if(res.data.result_code==='0'){
@@ -249,22 +290,15 @@ export default {
})
},
handleCheckedChange(val){
this.params.recommend = val
this.getDataList(this.dataGet)
this.$emit('changeRecommend', val);
},
handleSort(item){
this.sortList.forEach(element => {
element.active = false
});
item.active = true
this.params.sort=item.name
this.getDataList(this.dataGet)
this.$emit('changeSort', item.name);
},
chooseLabel(item,type){
const data = {name:item,active:false,type:type}
this.$emit('getLabel',data)
},

},
filters:{
@@ -292,8 +326,7 @@ export default {
.el-icon-arrow-down {
font-size: 1rem;
}
.el-dropdown-menu__item{
padding: 2px 1rem;
.el-dropdown-menu__item {
font-size: 1rem;
}
.el-dropdown-menu__item.active{


+ 28
- 12
web_src/vuepages/pages/dataset/square/index.vue View File

@@ -56,7 +56,7 @@
</div>
<div class="ui sixteen wide mobile sixteen wide tablet twelve wide computer column">
<div class="ui sixteen wide column">
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tabs v-model="activeName">
<el-tab-pane :label="$t('datasets.publick_dataset')" name="public_datasets">
<div v-if="activeName==='public_datasets'">
<public-dataset
@@ -67,7 +67,11 @@
:categoryValue="categoryValue"
:taskValue="taskValue"
:licenseValue="licenseValue"
@getLabel="getChildLabel">
:sortValue="sortValue"
:recommendValue="recommendValue"
@getLabel="getChildLabel"
@changeSort="changeSort"
@changeRecommend="changeRecommend">
</public-dataset>
</div>
</el-tab-pane>
@@ -80,7 +84,12 @@
:searchFlag="searchFlag"
:categoryValue="categoryValue"
:taskValue="taskValue"
:licenseValue="licenseValue">
:licenseValue="licenseValue"
:sortValue="sortValue"
:recommendValue="recommendValue"
@getLabel="getChildLabel"
@changeSort="changeSort"
@changeRecommend="changeRecommend">
</public-dataset>
</div>
</el-tab-pane>
@@ -93,7 +102,12 @@
:searchFlag="searchFlag"
:categoryValue="categoryValue"
:taskValue="taskValue"
:licenseValue="licenseValue">
:licenseValue="licenseValue"
:sortValue="sortValue"
:recommendValue="recommendValue"
@getLabel="getChildLabel"
@changeSort="changeSort"
@changeRecommend="changeRecommend">
</public-dataset>
</div>
</el-tab-pane>
@@ -124,20 +138,22 @@ export default {
licenseFlag:false,
categoryValue:'',
taskValue:'',
licenseValue:'',
licenseValue:'',
sortValue: 'default',
recommendValue: false,
activeName: 'public_datasets',
isSigned:'false',
searchValue:'',
searchFlag:false
searchFlag:false,
}
},
computed: {

},
computed: { },
methods: {
handleClick(tab, event) {
this.searchValue = ''
this.clearAllSelctLeft()
changeSort(value) {
this.sortValue = value;
},
changeRecommend(value) {
this.recommendValue = value;
},
selectCategory(item){
this.Category.forEach(element => {


+ 0
- 715
web_src/vuepages/pages/modelmanage/common/modelmanage-common-detail.vue View File

@@ -1,715 +0,0 @@
<template>
<div>
<div class="ui header">
<div class="ui breadcrumb">
<a class="section" :href="`${repo}/modelmanage/show_model`">{{ $t('modelManage.modelManage') }}</a>
<div class="divider"> / </div>
<div class="active section">{{ this.state.name }}</div>
</div>
<div class="version">
<el-select v-model="curVersion" @change="changeVersion" placeholder="">
<el-option v-for="item in modelList" :value="item.version" :key="item.version" :label="item.version">
</el-option>
</el-select>
</div>
</div>
<div class="content">
<div class="detail-info">
<div class="title">{{ $t('modelManage.basicInfo') }}:</div>
<div class="area-c">
<div class="area">
<div class="row">
<div class="tit">{{ $t('modelManage.useCluster') }}:</div>
<div class="val">
<div class="txt-wrap" :title="state.typeStr">
{{ state.typeStr }}
</div>
</div>
</div>
<div class="row">
<div class="tit">{{ $t('modelManage.modelSize') }}:</div>
<div class="val">
<div class="txt-wrap" :title="state.modelSize">{{ state.modelSize }}</div>
</div>
</div>
<div class="row" :class="isEidtDescr ? 'edit-row' : ''">
<div class="tit">{{ $t('modelManage.descr') }}:</div>
<div class="val" :class="isEidtDescr ? 'edit-val' : ''">
<div v-if="!isEidtDescr" class="txt-wrap" :title="state.description"
style="max-width:100%;width:unset;padding-right:20px;">
<span>{{ state.description }}</span>
<i v-if="canOperate" style="position:absolute;right:0;top:3px;color:rgb(22, 132, 252);cursor:pointer;"
class="el-icon-edit" @click="editDescr = state._description; isEidtDescr = true;"></i>
</div>
<div class="txt-edit" v-if="isEidtDescr">
<el-input type="textarea" v-model="editDescr" :maxLength="255"
:placeholder="$t('modelManage.modelDescrInputTips')"></el-input>
<i style="position:absolute;right:-4px;bottom:20px;color:rgb(255, 37, 37);cursor:pointer;"
class="icon times" @click="isEidtDescr = false;"></i>
<i style="position:absolute;right:-5px;bottom:2px;color:rgb(39, 177, 72);cursor:pointer;"
@click="submitEidt('descr')" class="icon check"></i>
</div>
</div>
</div>
<div class="row">
<div class="tit">{{ $t('modelManage.modelAccess') }}:</div>
<div class="val">
<div class="txt-wrap">{{ state.isPrivate }}</div>
</div>
</div>
</div>
<div class="area">
<div class="row">
<div class="tit">{{ $t('modelManage.modelEngine') }}:</div>
<div class="val">
<div class="txt-wrap" :title="state.engineName">{{ state.engineName }}</div>
</div>
</div>
<div class="row">
<div class="tit">{{ $t('modelManage.createTime') }}:</div>
<div class="val">
<div class="txt-wrap" :title="state.createTime">{{ state.createTime }}</div>
</div>
</div>
<div class="row" :class="isEidtLabel ? 'edit-row' : ''">
<div class="tit">{{ $t('modelManage.label') }}:</div>
<div class="val" :class="isEidtLabel ? 'edit-val' : ''">
<div v-if="!isEidtLabel" class="txt-wrap" :title="state.label"
style="max-width:100%;width:unset;padding-right:20px;">
<span>{{ state.label }}</span>
<i v-if="canOperate" style="position:absolute;right:0;top:3px;color:rgb(22, 132, 252);cursor:pointer;"
class="el-icon-edit" @click="editLabel = state._label; isEidtLabel = true;"></i>
</div>
<div class="txt-edit" v-if="isEidtLabel">
<el-input v-model="editLabel" :maxLength="255" :placeholder="$t('modelManage.modelLabelInputTips')"
@input="labelInput"></el-input>
<i style="position:absolute;right:-5px;bottom:20px;color:rgb(255, 37, 37);cursor:pointer;"
class="icon times" @click="isEidtLabel = false;"></i>
<i style="position:absolute;right:-5px;bottom:2px;color:rgb(39, 177, 72);cursor:pointer;"
@click="submitEidt('label')" class="icon check"></i>
</div>
</div>
</div>
</div>
</div>
<div v-show="isExpanded" style="margin-top:8px;" class="title">{{ $t('modelManage.trainTaskInfo') }}:</div>
<div v-show="isExpanded" class="area-c">
<div class="area">
<div class="row">
<div class="tit">{{ $t('modelManage.trainTask') }}:</div>
<div class="val">
<div class="txt-wrap" v-html="state.displayJobName"></div>
</div>
</div>
<div class="row">
<div class="tit">{{ $t('modelManage.codeBranch') }}:</div>
<div class="val">
<div class="txt-wrap" v-html="state.branchName"></div>
</div>
</div>
<div class="row">
<div class="tit">{{ $t('modelManage.bootFile') }}:</div>
<div class="val">
<div class="txt-wrap" :title="state.bootFile">{{ state.bootFile }}</div>
</div>
</div>
<div class="row">
<div class="tit">{{ $t('modelManage.trainDataset') }}:</div>
<div class="val">
<div class="txt-wrap" :title="state.datasetName">{{ state.datasetName }}</div>
</div>
</div>
</div>
<div class="area">
<div class="row">
<div class="tit">{{ $t('modelManage.specInfo') }}:</div>
<div class="val">
<div class="txt-wrap" :title="state.specStr">{{ state.specStr }}</div>
</div>
</div>
<div class="row">
<div class="tit">{{ $t('modelManage.workServerNumber') }}:</div>
<div class="val">
<div class="txt-wrap" :title="state.workServerNumber">{{ state.workServerNumber }}</div>
</div>
</div>
<div class="row">
<div class="tit">{{ $t('modelManage.runParameters') }}:</div>
<div class="val">
<div class="txt-wrap" :title="state.parameters">{{ state.parameters }}</div>
</div>
</div>
</div>
</div>
</div>
<div class="expand-line">
<div class="line"></div>
<div class="expand-btn" @click="isExpanded = !isExpanded">
<i class="icon chevron circle down" :class="isExpanded ? 'up' : ''"></i>
<span>{{ isExpanded ? $t('modelManage.collapseDetails') : $t('modelManage.seeMore') }}</span>
</div>
<div class="line"></div>
</div>
<div class="files-info">
<div class="top">
<div style="width:100%;margin-right:20px;">
<div class="title">{{ $t('modelManage.modelFilesList') }}:</div>
<div class="title files-path-c" style="margin-top:8px;margin-bottom:4px">
<div class="file-path" v-for="(item, index) in filePath">
<span v-if="index == filePath.length - 1" class="path-name">{{ item.label }}</span>
<a v-if="index != filePath.length - 1" class="path-name canback" @click="goBackDir(item)">{{ item.label
}}</a>
<span style="color:rgba(0,0,0,.4);" class="divider"> / </span>
</div>
</div>
</div>
<div>
<el-button v-if="modelType == 1 && canOperate" type="primary" icon="el-icon-upload" @click="goUploadPage">
{{ $t('modelManage.uploadModelFiles') }}
</el-button>
</div>
</div>
<div class="table-container">
<el-table ref="tableRef" :data="filesList" row-key="sn" style="width: 100%" v-loading="loading" stripe>
<el-table-column column-key="FileName" prop="FileName" sortable
:sort-method="(a, b) => a.FileName.toLocaleLowerCase().localeCompare(b.FileName.toLocaleLowerCase())"
:label="$t('modelManage.fileName')" align="left" header-align="left">
<template slot-scope="scope">
<div class="tbl-file-name">
<a v-if="scope.row.IsDir" @click="goNextDir(scope.row)" href="javascript:;">
<div class="fitted" :title="scope.row.FileName">
<i class="icon folder" width="16" height="16" aria-hidden="true"></i>
<span>{{ scope.row.FileName }}</span>
</div>
</a>
<a v-else :class="!canDownload ? 'disabled-download' : ''"
:href="canDownload ? `${repo}/modelmanage/${state.id}/downloadsingle?parentDir=${filePath.length > 1 ? encodeURIComponent(filePath.map(item => item.path).join('/').slice(1) + '/') : ''}&fileName=${scope.row.FileName}` : 'javascript:;'">
<div class="fitted" :title="scope.row.FileName">
<i class="icon file" width="16" height="16" aria-hidden="true"></i>
<span>{{ scope.row.FileName }}</span>
</div>
</a>
</div>
</template>
</el-table-column>
<el-table-column column-key="SizeShow" prop="SizeShow" sortable :sort-method="(a, b) => a.Size - b.Size"
:label="$t('modelManage.fileSize')" align="center" header-align="center" width="200">
</el-table-column>
<el-table-column column-key="ModTime" prop="ModTime" sortable
:sort-method="(a, b) => a.ModTimeNum - b.ModTimeNum" :label="$t('modelManage.updateTime')" align="center"
header-align="center" width="200">
</el-table-column>
<el-table-column v-if="modelType == 1 && canDelete" column-key="operate" prop="operate"
:label="$t('modelManage.operate')" align="center" header-align="center" width="200">
<template slot-scope="scope">
<span v-if="!scope.row.IsDir" class="btn-del" @click="deleteFile(scope.row)">{{ $t('modelManage.delete')
}}</span>
</template>
</el-table-column>
</el-table>
</div>
</div>
</div>
</div>
</template>

<script>

import { getModelInfoByName, modifyModel, getModelFiles, deleteModelFile } from '~/apis/modules/modelmanage';
import { getUrlSearchParams, getListValueWithKey, transFileSize, renderSpecStr } from '~/utils';
import { MODEL_ENGINES } from '~/const';
import { formatDate } from 'element-ui/lib/utils/date-util';

const REPO_NAME = location.pathname.split('/')[2];
const MAX_LABEL_COUNT = 5;

export default {
data() {
return {
modelType: '0', // 1-本地, 0-线上
canOperate: false,
canDownload:false,
canDelete: false,
isExpanded: false,
loading: false,
repo: location.pathname.split('/').slice(0, 3).join('/'),
state: {
type: 0,
id: '',
name: '',
version: '0.0.1',
engine: '0',
label: '',
description: '',
},
editDescr: '',
isEidtDescr: false,
editLabel: '',
isEidtLabel: false,
engineList: MODEL_ENGINES,
curVersion: '',
modelList: [],
filesList: [],
filePath: [],
};
},
components: {},
methods: {
getDirFiles(dir) {
dir = dir.length ? dir.slice(1) : '';
getModelFiles({
repo: this.repo,
ID: this.state.id,
parentDir: dir,
}).then(res => {
const list = res.data || [];
list.forEach(item => {
item.SizeShow = item.IsDir ? '' : transFileSize(item.Size);
item.ModTimeNum = new Date(item.ModTime).getTime();
})
list.sort((a, b) => b.ModTimeNum - a.ModTimeNum);
this.filesList = list;
this.$refs['tableRef']?.clearSort();
}).catch(err => {
console.log(err);
});
},
goNextDir(item) {
this.filePath.push({
label: item.FileName,
path: item.FileName
});
const dir = this.filePath.map((item) => item.path).join('/');
this.getDirFiles(dir);
},
goBackDir(item) {
const index = this.filePath.findIndex(pth => item === pth);
this.filePath = this.filePath.slice(0, index + 1);
const dir = this.filePath.map((item) => item.path).join('/');
this.getDirFiles(dir);
},
changeVersion(version, noFileRefresh) {
const data = this.modelList.filter((model) => model.version == version)[0];
this.modelType = data.modelType;
this.canOperate = data.isCanOper;
this.canDownload = data.isCanDownload;
this.canDelete = data.isCanDelete;
this.state.type = data.type;
this.state.typeStr = data.type == 0 ? 'CPU/GPU' : data.type == 1 ? 'NPU' : '';
this.state.id = data.id;
this.state.name = data.name;
this.state.version = data.version;
this.state.engine = data.engine.toString();
this.state.engineName = getListValueWithKey(MODEL_ENGINES, data.engine.toString());
this.state.modelSize = transFileSize(data.size);
this.state.label = data.label || '--';
this.state._label = data.label;
this.state.description = data.description || '--';
this.state._description = data.description;
this.state.isPrivate= (data.isPrivate == true ? this.$t('modelManage.modelAccessPrivate'):this.$t('modelManage.modelAccessPublic'));
this.state.createTime = formatDate(new Date(data.createdUnix * 1000), 'yyyy-MM-dd HH:mm:ss');

const trainTaskInfo = data.trainTaskInfo ? JSON.parse(data.trainTaskInfo) : '';
Object.assign(this.state, {
displayJobName: '--',
branchName: '--',
bootFile: '--',
datasetName: '--',
parameters: '--',
workServerNumber: '--',
specStr: '--',
});
if (trainTaskInfo) {
const parameters = trainTaskInfo.Parameters ? JSON.parse(trainTaskInfo.Parameters).parameter : [];
const parametersStr = parameters.map((item) => { return item.label + '=' + item.value }).join('; ');
const taskType = trainTaskInfo.Type;
let taskUrl = location.href.split('modelmanage')[0];
if (taskType == 0) {
taskUrl = taskUrl + 'cloudbrain/train-job/' + trainTaskInfo.JobID;
} else if (taskType == 1) {
taskUrl = taskUrl + 'modelarts/train-job/' + trainTaskInfo.JobID;
} else if (taskType == 2) {
taskUrl = taskUrl + 'grampus/train-job/' + trainTaskInfo.JobID;
}
const versionName = trainTaskInfo.VersionName;
const versionHtml = versionName ? `<span class="append-txt" title="${versionName}">${versionName}</span>` : '';
const codeCommitID = data.codeCommitID;
const codeCommitIDHtml = codeCommitID ? `<span class="append-txt" title="${codeCommitID}">${codeCommitID.slice(0, 10)}</span>` : '';
let specObj;
try {
specObj = trainTaskInfo.FlavorName ? JSON.parse(trainTaskInfo.FlavorName) : '';
} catch (e) {
specObj = trainTaskInfo.FlavorName;
}
const sepcStr = typeof specObj == 'object' ? renderSpecStr(specObj, false) : specObj;
Object.assign(this.state, {
displayJobName: `<a href="${taskUrl}" title="${trainTaskInfo.DisplayJobName}">${trainTaskInfo.DisplayJobName}</a>${versionHtml}`,
branchName: `<span>${trainTaskInfo.BranchName}</span>${codeCommitIDHtml}`,
bootFile: trainTaskInfo.BootFile,
datasetName: trainTaskInfo.DatasetName,
parameters: parametersStr || '--',
workServerNumber: trainTaskInfo.WorkServerNumber || '1',
specStr: sepcStr || '--',
});
}
this.curVersion = version;
if (!noFileRefresh) {
this.filePath = [{ label: version, path: '' }];
this.getDirFiles('')
}
},
goUploadPage() {
window.location.href = `${this.repo}/modelmanage/create_local_model_2?type=1&name=${encodeURIComponent(this.state.name)}&id=${this.state.id}`;
},
backToModelListPage() {
const list = window.location.href.split('/');
list.pop();
list.push('show_model');
window.location.href = list.join('/');
},
labelInput() {
const hasEndSpace = this.editLabel[this.editLabel.length - 1] == ' ';
const list = this.editLabel.trim().split(' ').filter(label => label != '');
this.editLabel = list.slice(0, MAX_LABEL_COUNT).join(' ') + (hasEndSpace && list.length < MAX_LABEL_COUNT ? ' ' : '');
},
submitEidt(type) {
const obj = {
repo: this.repo,
type: this.state.type,
id: this.state.id,
name: this.state.name,
version: this.state.version,
engine: this.state.engine,
label: type == 'label' ? this.editLabel : this.state.label,
description: type == 'descr' ? this.editDescr : this.state.description,
};
modifyModel(obj).then(res => {
res = res.data;
if (res && res.code == '0') {
if (type == 'label') {
this.state.label = this.editLabel;
this.state._label = this.editLabel;
this.isEidtLabel = false;
} else if (type == 'descr') {
this.state.description = this.editDescr;
this.state._description = this.editDescr;
this.isEidtDescr = false;
}
} else {
this.$message({
type: 'error',
message: this.$t('modelManage.infoModificationFailed'),
});
}
}).catch(err => {
console.log(err);
this.$message({
type: 'error',
message: this.$t('modelManage.infoModificationFailed'),
});
});
},
deleteFile(file) {
this.$confirm(this.$t('modelManage.deleteModelFileConfirmTips'), this.$t('tips'), {
confirmButtonText: this.$t('confirm1'),
cancelButtonText: this.$t('cancel'),
type: 'warning',
lockScroll: false,
}).then(() => {
this.loading = true;
deleteModelFile({
repo: this.repo,
id: this.state.id,
fileName: file.FileName,
}).then(res => {
res = res.data;
if (res.code == '0') {
setTimeout(() => {
this.loading = false;
this.updateModelInfo();
const dir = this.filePath.map((item) => item.path).join('/');
this.getDirFiles(dir);
}, 30);
} else {
this.loading = false;
this.$message({
type: 'error',
message: this.$t('modelManage.modelFileDeleteFailed'),
});
}
}).catch(err => {
console.log(err);
this.$message({
type: 'error',
message: this.$t('modelManage.modelFileDeleteFailed'),
});
});
}).catch(() => { });
},
updateModelInfo() {
getModelInfoByName({
repo: this.repo,
name: this.state.name,
}).then(res => {
const list = res.data || [];
this.modelList = list;
const noFileRefresh = true;
this.changeVersion(this.curVersion, noFileRefresh);
}).catch(err => {
console.log(err);
});
},
},
mounted() {
const urlParams = getUrlSearchParams();
if (urlParams.name) {
this.state.name = urlParams.name;
this.loading = true;
getModelInfoByName({
repo: this.repo,
name: urlParams.name,
}).then(res => {
this.loading = false;
const list = res.data || [];
this.modelList = list;
if (list && list.length) {
const data = list[0];
this.changeVersion(data.version);
}
}).catch(err => {
this.loading = false;
console.log(err);
this.backToModelListPage();
});
} else {
this.backToModelListPage();
}
},
beforeDestroy() {
},
};
</script>

<style scoped lang="less">
.header {
display: flex;
align-items: center;

.version {
margin-left: 16px;
width: 90px;
}
}

.content {
.title {
font-weight: 550;
font-size: 14px;
color: rgb(16, 16, 16);
margin-bottom: 10px;
}

.detail-info {
border: 1px solid rgb(232, 232, 232);
border-bottom: none;
padding: 22px;
padding-bottom: 1px;

.area-c {
display: flex;

.area {
flex: 1;

.row {
display: flex;
height: 32px;
margin-bottom: 4px;
align-items: center;

&.edit-row {
height: unset;
}

.tit {
width: 160px;
text-align: right;
color: rgb(136, 136, 136);
}

.val {
flex: 1;
color: rgb(16, 16, 16);
position: relative;
height: 20px;

&.edit-val {
height: unset;
}

.txt-wrap {
position: absolute;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;

/deep/.append-txt {
margin-left: 6px;
background-color: gainsboro;
padding: 2px;
border-radius: 2px;
font-size: 12px;
}
}
}

.txt-edit {
padding-right: 20px;
}
}
}
}
}

.expand-line {
display: flex;
align-items: center;
border: 1px solid rgb(232, 232, 232);
border-top: none;
border-bottom: none;
padding: 16px 0;

.line {
flex: 1;
height: 1px;
background-color: rgb(232, 232, 232);
margin: 0 22px;
}

.expand-btn {
color: rgba(22, 132, 252, 1);
cursor: pointer;

.icon {
margin-right: 2px;
font-size: 14px;
color: rgba(22, 132, 252, 0.8),
}
}
}


.files-info {
border: 1px solid rgb(232, 232, 232);
border-top: none;
border-bottom: none;

.top {
padding: 0 22px 8px 22px;
display: flex;
align-items: center;
justify-content: space-between;
}

.files-path-c {
margin-bottom: 4px;
height: 20px;

.file-path {
margin-right: 6px;
float: left;

.path-name {
&.canback {
color: #4183c4;
}
}
}
}

.table-container {
/deep/ .el-table__header {
th {
background: rgb(245, 245, 246);
color: rgb(16, 16, 16);
font-weight: 400;
font-size: 14px;
}
}

/deep/ .el-table__body {
td {
color: rgb(16, 16, 16);
font-weight: 400;
font-size: 14px;
}
}

.tbl-file-name {
height: 32px;
display: flex;
align-items: center;
overflow: hidden;
font-size: 16px;
font-weight: 500;
position: relative;

a {
max-width: 100%;

.fitted {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 100%;
}
}

.disabled-download {
cursor: default;
pointer-events: none;
color: rgba(0, 0, 0, .6) !important;
opacity: .45 !important;
}
}

.btn-del {
color: #0366d6;
cursor: pointer;
}
}
}
}


.el-select-dropdown__item.selected {
color: rgba(0, 0, 0, .95);
}

/deep/ .el-select {
.is-focus {
.el-input__inner {
border-color: #85b7d9;
}
}
}

.el-select {
/deep/ .el-input__inner {
font-weight: 600;
}
}

/deep/ .el-input__inner {
&:focus {
border-color: #85b7d9;
}
}

/deep/ .el-textarea__inner {
&:focus {
border-color: #85b7d9;
}
}
</style>

+ 263
- 0
web_src/vuepages/pages/modelmanage/components/ModelHeader.vue View File

@@ -0,0 +1,263 @@
<template>
<div class="bg">
<div class="ui container">
<div class="title">
<div class="title-l">
<span>{{ modelName }}</span>
<img src="/img/jian.svg" v-if="model.recommend == 1">
</div>
<div class="title-r">
<div @click="changeFav">
<i v-if="!isCollected" class="heart outline icon" :title="$t('star')"></i>
<i v-if="isCollected" class="heart icon" :title="$t('unStar')"></i>
<span>{{ $t('star') }}</span>
</div>
<div>{{ collectedCount }}</div>
</div>
</div>
<div class="sub-title">
<span>
<span>{{ $t('modelManage.ownerRepository') }}:</span>
<a :href="`/${repoOwnerName}/${repoName}/modelmanage/show_model`">
{{ repoOwnerName + '/' + (model.repoDisplayName || repoName) }}
</a>
</span>
<span>
<span>{{ $t('modelManage.creator') }}:</span>
<a :href="`/${model.userName}`">{{ model.userName }}</a>
</span>
<span>
<span>{{ $t('datasets.downloadtimes') }}:</span>
<span style="color: rgb(16, 16, 16);">{{ model.downloadCount }}</span>
</span>
<span>
<span>{{ $t('datasets.citations') }}:</span>
<span style="color: rgb(16, 16, 16);">{{ model.referenceCount }}</span>
</span>
</div>
<div class="tabs-wrap">
<div class="tabs">
<div class="tabs-l">
<a
:href="`/${repoOwnerName}/${repoName}/modelmanage/model_readme_tmpl?name=${encodeURIComponent(modelName)}`">
<div class="tab" :class="tab == 'intro' ? 'focus' : ''">
<i class="ri-send-plane-2-line"></i><span>{{ $t('modelManage.modelIntroduction') }}</span>
</div>
</a>
<a
:href="`/${repoOwnerName}/${repoName}/modelmanage/model_filelist_tmpl?name=${encodeURIComponent(modelName)}`">
<div class="tab" :class="tab == 'files' ? 'focus' : ''">
<i class="ri-bar-chart-horizontal-line"></i><span>{{ $t('modelManage.modelFiles') }}</span>
</div>
</a>
<a
:href="`/${repoOwnerName}/${repoName}/modelmanage/model_evolution_map?name=${encodeURIComponent(modelName)}`">
<div class="tab" :class="tab == 'graph' ? 'focus' : ''">
<i class="ri-equalizer-line"></i><span>{{ $t('modelManage.modelEvolutionMap') }}</span>
</div>
</a>
</div>
<div class="tabs-r">
<a :href="`/${repoOwnerName}/${repoName}/modelmanage/model_setting?name=${encodeURIComponent(modelName)}`">
<div class="tab" :class="tab == 'settings' ? 'focus' : ''" v-if="model && model.isCanOper">
<i class="ri-settings-2-line"></i><span>{{ $t('modelManage.settings') }}</span>
</div>
</a>
</div>
</div>
</div>
</div>
</div>
</template>

<script>
import { setModelFav } from '~/apis/modules/modelsquare';

export default {
name: "ModelHeader",
props: {
model: { type: Object, default: () => ({}) },
repoOwnerName: { type: String, default: '' },
repoName: { type: String, default: '' },
modelName: { type: String, default: '' },
tab: { type: String, default: 'intro' }
},
components: {},
data() {
return {
favCnt: '',
isCollected: false,
collectedCount: '',
isSetting: false,
};
},
methods: {
changeFav() {
if (this.isSetting || !this.model.id) return;
this.isSetting = true;
setModelFav({
id: this.model.id,
collected: this.isCollected ? false : true,
}).then(res => {
this.isSetting = false;
if (res.data.code == '0') {
this.isCollected = !this.isCollected;
this.collectedCount = this.collectedCount + (this.isCollected ? 1 : -1);
this.$message.success(this.isCollected ? this.$t('datasets.starSuccess') : this.$t('datasets.unstarSuccess'));
} else if (res.data.code == '401') {
window.location.href = `/user/login?redirect_to=${encodeURIComponent(window.location.href)}`;
} else {
this.$message.error(res.data.msg);
}
}).catch(err => {
console.log(err);
this.$message.error(err);
this.isSetting = false;
});
}
},
watch: {
model: {
handler(newVal) {
this.isCollected = newVal.isCollected;
this.collectedCount = newVal.collectedCount;
},
immediate: true,
deep: true,
},
},
mounted() { },
};
</script>

<style scoped lang="less">
.bg {
height: 153px;
border-color: rgba(204, 204, 255, 0.6);
border-width: 0px 0px 1px;
border-style: solid;
background: url("data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20version%3D%221.1%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%221%22%20x1%3D%220%22%20x2%3D%221%22%20y1%3D%220%22%20y2%3D%220%22%20gradientTransform%3D%22matrix(-0.42400000000000015%2C%20-0.9990000000000001%2C%200.0112777734375%2C%20-0.42400000000000015%2C%201%2C%200.995)%22%3E%3Cstop%20stop-color%3D%22%23ccfff4%22%20stop-opacity%3D%221%22%20offset%3D%220.01%22%3E%3C%2Fstop%3E%3Cstop%20stop-color%3D%22%23e0e2ff%22%20stop-opacity%3D%221%22%20offset%3D%220.52%22%3E%3C%2Fstop%3E%3Cstop%20stop-color%3D%22%23f4eefd%22%20stop-opacity%3D%221%22%20offset%3D%221%22%3E%3C%2Fstop%3E%3C%2FlinearGradient%3E%3C%2Fdefs%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22url(%231)%22%3E%3C%2Frect%3E%3C%2Fsvg%3E");

.container {
position: relative;
height: 100%;
}
}

.title {
display: flex;
align-items: center;
justify-content: space-between;
height: 60px;
padding-top: 6px;
margin-bottom: 2px;

.title-l {
display: flex;
align-items: center;
max-width: 74%;

span {
font-size: 24px;
color: #101010;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
line-height: 40px;
height: 40px;
white-space: nowrap;
}

img {
height: 19px;
width: 20px;
margin-left: 8px;
}
}

.title-r {
display: flex;
align-items: center;

>div {
height: 30px;
border-color: rgb(225, 227, 230);
border-width: 1px;
border-style: solid;
border-radius: 4px 0px 0px 4px;
font-size: 14px;
padding: 4px 8px;
line-height: 21px;
background: rgb(247, 247, 247);
color: #101010;
cursor: pointer;

i {
margin-right: 6px;
color: rgb(250, 140, 22);
}

&:last-child {
border-radius: 0px 4px 4px 0px;
border-left: none;
cursor: default;
}
}
}
}

.sub-title {
>span {
margin-right: 12px;

>span {
font-size: 14px;
color: rgb(136, 136, 136);
}
}
}

.tabs-wrap {
position: absolute;
bottom: 0;
width: 100%;

.tabs {
display: flex;
justify-content: space-between;
margin-bottom: -1px;

.tabs-l {
display: flex;
align-items: center;
}

.tabs-r {
display: flex;
align-items: center;
}

.tab {
font-size: 14px;
padding: 10px 15px;
color: rgba(16, 16, 16, 0.5);
display: flex;
align-items: center;
cursor: pointer;

i {
margin-right: 4px;
}

&.focus {
color: #101010;
border-color: rgba(204, 204, 255, 0.6);
border-width: 1px 1px 0px;
border-style: solid;
border-radius: 4px 4px 0px 0px;
background: rgb(255, 255, 255);
}
}
}
}
</style>

+ 424
- 0
web_src/vuepages/pages/modelmanage/files/index.vue View File

@@ -0,0 +1,424 @@
<template>
<div>
<div v-if="emptyPage" style="padding-top:50px">
<NotFound></NotFound>
</div>
<div v-else>
<ModelHeader :tab="'files'" :repoOwnerName="repoOwnerName" :repoName="repoName" :modelName="modelName"
:model="modelData">
</ModelHeader>
<div class="ui container">
<div class="header">
<div class="version">
<el-select v-model="curVersion" @change="changeVersion" placeholder="">
<el-option v-for="item in modelList" :value="item.version" :key="item.version" :label="item.version">
</el-option>
</el-select>
</div>
</div>
<div class="content">
<div class="files-info">
<div class="top">
<div style="width:100%;margin-right:20px;">
<div class="title">{{ $t('modelManage.modelFilesList') }}:</div>
<div class="title files-path-c" style="margin-top:8px;margin-bottom:4px">
<div class="file-path" v-for="(item, index) in filePath">
<span v-if="index == filePath.length - 1" class="path-name">{{ item.label }}</span>
<a v-if="index != filePath.length - 1" class="path-name canback" @click="goBackDir(item)">{{
item.label
}}</a>
<span style="color:rgba(0,0,0,.4);" class="divider"> / </span>
</div>
</div>
</div>
<div class="right-btn-c">
<a class="download-btn" v-if="modelData.isCanDownload" download
:href="`${repoUrl}/modelmanage/downloadall?id=${modelData.id}`">
{{ $t('modelManage.modelDownloadAll') }}
</a>
<el-button v-if="modelType == 1 && canOperate" type="primary" icon="el-icon-upload" @click="goUploadPage">
{{ $t('modelManage.uploadModelFiles') }}
</el-button>
</div>
</div>
<div class="table-container">
<el-table ref="tableRef" :data="filesList" row-key="sn" style="width: 100%" v-loading="loading" stripe>
<el-table-column column-key="FileName" prop="FileName" sortable
:sort-method="(a, b) => a.FileName.toLocaleLowerCase().localeCompare(b.FileName.toLocaleLowerCase())"
:label="$t('modelManage.fileName')" align="left" header-align="left">
<template slot-scope="scope">
<div class="tbl-file-name">
<a v-if="scope.row.IsDir" @click="goNextDir(scope.row)" href="javascript:;">
<div class="fitted" :title="scope.row.FileName">
<i class="icon folder" width="16" height="16" aria-hidden="true"></i>
<span>{{ scope.row.FileName }}</span>
</div>
</a>
<a v-else :class="!canDownload ? 'disabled-download' : ''"
:href="canDownload ? `${repoUrl}/modelmanage/${state.id}/downloadsingle?parentDir=${filePath.length > 1 ? encodeURIComponent(filePath.map(item => item.path).join('/').slice(1) + '/') : ''}&fileName=${scope.row.FileName}` : 'javascript:;'">
<div class="fitted" :title="scope.row.FileName">
<i class="icon file" width="16" height="16" aria-hidden="true"></i>
<span>{{ scope.row.FileName }}</span>
</div>
</a>
</div>
</template>
</el-table-column>
<el-table-column column-key="SizeShow" prop="SizeShow" sortable :sort-method="(a, b) => a.Size - b.Size"
:label="$t('modelManage.fileSize')" align="center" header-align="center" width="200">
</el-table-column>
<el-table-column column-key="ModTime" prop="ModTime" sortable
:sort-method="(a, b) => a.ModTimeNum - b.ModTimeNum" :label="$t('modelManage.updateTime')"
align="center" header-align="center" width="200">
</el-table-column>
<el-table-column v-if="modelType == 1 && canDelete" column-key="operate" prop="operate"
:label="$t('modelManage.operate')" align="center" header-align="center" width="200">
<template slot-scope="scope">
<span v-if="!scope.row.IsDir" class="btn-del" @click="deleteFile(scope.row)">{{
$t('modelManage.delete')
}}</span>
</template>
</el-table-column>
</el-table>
</div>
</div>
</div>
</div>
</div>
</div>
</template>

<script>
import ModelHeader from '../components/ModelHeader.vue';
import NotFound from '~/components/NotFound.vue';
import { getModelInfoByName, getModelFiles, deleteModelFile } from '~/apis/modules/modelmanage';
import { getUrlSearchParams, getListValueWithKey, transFileSize, renderSpecStr } from '~/utils';
import { MODEL_ENGINES } from '~/const';
import { formatDate } from 'element-ui/lib/utils/date-util';

export default {
data() {
return {
emptyPage: false,

modelName: '',
repoOwnerName: location.pathname.split('/')[1],
repoName: location.pathname.split('/')[2],
repoUrl: location.pathname.split('/').slice(0, 3).join('/'),
modelData: {},

modelType: '0', // 1-本地, 0-线上
canOperate: false,
canDownload: false,
canDelete: false,
loading: false,

state: {
type: 0,
id: '',
name: '',
version: '0.0.1',
engine: '0',
label: '',
description: '',
},
engineList: MODEL_ENGINES,

curVersion: '',
modelList: [],
filesList: [],
filePath: [],
};
},
components: { ModelHeader, NotFound },
methods: {
getDirFiles(dir) {
dir = dir.length ? dir.slice(1) : '';
getModelFiles({
repo: this.repoUrl,
ID: this.state.id,
parentDir: dir,
}).then(res => {
const list = res.data || [];
list.forEach(item => {
item.SizeShow = item.IsDir ? '' : transFileSize(item.Size);
item.ModTimeNum = new Date(item.ModTime).getTime();
});
list.sort((a, b) => a.FileName.localeCompare(b.FileName));
list.sort((a, b) => b.ModTimeNum - a.ModTimeNum);
this.filesList = list;
this.$refs['tableRef']?.clearSort();
}).catch(err => {
console.log(err);
});
},
goNextDir(item) {
this.filePath.push({
label: item.FileName,
path: item.FileName
});
const dir = this.filePath.map((item) => item.path).join('/');
this.getDirFiles(dir);
},
goBackDir(item) {
const index = this.filePath.findIndex(pth => item === pth);
this.filePath = this.filePath.slice(0, index + 1);
const dir = this.filePath.map((item) => item.path).join('/');
this.getDirFiles(dir);
},
changeVersion(version) {
this.curVersion = version;
this.updateModelInfo();
this._changeVersion(version);
},
_changeVersion(version, noFileRefresh) {
const data = this.modelList.filter((model) => model.version == version)[0];
this.modelData = data;
this.modelType = data.modelType;
this.canOperate = data.isCanOper;
this.canDownload = data.isCanDownload;
this.canDelete = data.isCanDelete;
this.state.type = data.type;
this.state.typeStr = data.type == 0 ? 'CPU/GPU' : data.type == 1 ? 'NPU' : '';
this.state.id = data.id;
this.state.name = data.name;
this.state.version = data.version;
this.state.engine = data.engine.toString();
this.state.engineName = getListValueWithKey(MODEL_ENGINES, data.engine.toString());
this.state.modelSize = transFileSize(data.size);
this.state.label = data.label;
this.state.description = data.description;
this.state.isPrivate = data.isPrivate;
this.state.createTime = formatDate(new Date(data.createdUnix * 1000), 'yyyy-MM-dd HH:mm:ss');
this.state.trainTaskInfo = data.trainTaskInfo;
Object.assign(this.state, {
displayJobName: '--',
branchName: '--',
bootFile: '--',
datasetName: '--',
parameters: '--',
workServerNumber: '--',
specStr: '--',
});
this.curVersion = version;
if (!noFileRefresh) {
this.filePath = [{ label: version, path: '' }];
this.getDirFiles('')
}
},
goUploadPage() {
window.location.href = `${this.repoUrl}/modelmanage/model_fileupload_tmpl?type=1&name=${encodeURIComponent(this.state.name)}&id=${this.state.id}`;
},
backToModelListPage() {
const list = window.location.href.split('/');
list.pop();
list.push('show_model');
window.location.href = list.join('/');
},
deleteFile(file) {
this.$confirm(this.$t('modelManage.deleteModelFileConfirmTips'), this.$t('tips'), {
confirmButtonText: this.$t('confirm1'),
cancelButtonText: this.$t('cancel'),
type: 'warning',
lockScroll: false,
}).then(() => {
this.loading = true;
deleteModelFile({
repo: this.repoUrl,
id: this.state.id,
fileName: file.FileName,
}).then(res => {
res = res.data;
if (res.code == '0') {
setTimeout(() => {
this.loading = false;
this.updateModelInfo();
const dir = this.filePath.map((item) => item.path).join('/');
this.getDirFiles(dir);
}, 30);
} else {
this.loading = false;
this.$message({
type: 'error',
message: this.$t('modelManage.modelFileDeleteFailed'),
});
}
}).catch(err => {
console.log(err);
this.$message({
type: 'error',
message: this.$t('modelManage.modelFileDeleteFailed'),
});
});
}).catch(() => { });
},
updateModelInfo() {
getModelInfoByName({
repo: this.repoUrl,
name: this.state.name,
}).then(res => {
const list = res.data || [];
this.modelList = list;
const noFileRefresh = true;
this._changeVersion(this.curVersion, noFileRefresh);
}).catch(err => {
console.log(err);
});
},
},
beforeMount() {
const urlParams = getUrlSearchParams();
if (urlParams.name) {
this.modelName = urlParams.name;
this.state.name = urlParams.name;
this.loading = true;
getModelInfoByName({
repo: this.repoUrl,
name: urlParams.name,
}).then(res => {
this.loading = false;
const list = res.data || [];
this.modelList = list;
if (list && list.length) {
const data = list[0];
this._changeVersion(data.version);
} else {
this.emptyPage = true;
}
}).catch(err => {
console.log(err);
this.loading = false;
});
} else {
this.emptyPage = true;
}
},
mounted() { },
beforeDestroy() { },
};
</script>

<style scoped lang="less">
.header {
display: flex;
align-items: center;
margin-top: 32px;
margin-bottom: 22px;

.version {
width: 90px;
}
}

.content {
.files-info {
.top {
padding: 0 0 8px 0;
display: flex;
align-items: center;
justify-content: space-between;
}

.files-path-c {
margin-bottom: 10px;
height: 20px;

.file-path {
margin-right: 6px;
float: left;
font-size: 14px;
font-weight: 550;
color: #101010;

.path-name {
&.canback {
color: #4183c4;
}
}
}
}

.right-btn-c {
min-width: 300px;
display: flex;
align-items: center;
justify-content: flex-end;

.download-btn {
text-align: right;
margin-right: 16px;
}
}

.table-container {
/deep/ .el-table__header {
th {
background: rgb(245, 245, 246);
color: rgb(16, 16, 16);
font-weight: 400;
font-size: 14px;
}
}

/deep/ .el-table__body {
td {
color: rgb(16, 16, 16);
font-weight: 400;
font-size: 14px;
}
}

.tbl-file-name {
height: 32px;
display: flex;
align-items: center;
overflow: hidden;
font-size: 16px;
font-weight: 500;
position: relative;

a {
max-width: 100%;

.fitted {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 100%;
}
}

.disabled-download {
cursor: default;
pointer-events: none;
color: rgba(0, 0, 0, .6) !important;
opacity: .45 !important;
}
}

.btn-del {
color: #0366d6;
cursor: pointer;
}
}
}
}

.el-select-dropdown__item.selected {
color: rgba(0, 0, 0, .95);
}

/deep/ .el-select {
.is-focus {
.el-input__inner {
border-color: #85b7d9;
}
}
}

.el-select {
/deep/ .el-input__inner {
font-weight: 600;
}
}
</style>

web_src/vuepages/pages/modelmanage/common/vp-modelmanage-common-detail.js → web_src/vuepages/pages/modelmanage/files/vp-model-files.js View File

@@ -4,7 +4,7 @@ import 'element-ui/lib/theme-chalk/index.css';
import localeEn from 'element-ui/lib/locale/lang/en';
import localeZh from 'element-ui/lib/locale/lang/zh-CN';
import { i18n, lang } from '~/langs';
import App from './modelmanage-common-detail.vue';
import App from './index.vue';

Vue.use(ElementUI, {
locale: lang === 'zh-CN' ? localeZh : localeEn,

web_src/vuepages/pages/modelmanage/local/modelmanage-local-create-2.vue → web_src/vuepages/pages/modelmanage/fileupload/index.vue View File

@@ -1,69 +1,80 @@
<template>
<div>
<div class="header">
<span class="title">{{ type == '1' ? $t('modelManage.addModelFiles') : $t('modelManage.uploadModelFiles')
}}</span>
<div v-if="emptyPage" style="padding-top:50px">
<NotFound></NotFound>
</div>
<div class="content ui form">
<div class="guide-c" v-if="type != '1'">
<div class="step">
<div class="num">1</div>
<div class="txt">{{ $t('modelManage.createModel') }}</div>
<div v-else>
<ModelHeader :tab="'files'" :repoOwnerName="repoOwnerName" :repoName="repoName" :modelName="modelName" :model="modelData">
</ModelHeader>
<div class="ui container">
<div class="header">
<span class="title">{{ $t('modelManage.uploadModelFiles') }}</span>
</div>
<div class="line"></div>
<div class="step focused">
<div class="num">2</div>
<div class="txt">{{ $t('modelManage.uploadModelFiles') }}</div>
</div>
</div>
<div class="row-c">
<div class="row">
<div class="r-title"><label class="required">{{ $t('modelManage.modelName') }}</label></div>
<div class="r-content">
<el-input size="medium" class="input-disabled" v-model="state.name"
:placeholder="$t('modelManage.pleaseInputModelName')" readonly>
</el-input>
</div>
</div>
<div class="row" style="align-items:flex-start;">
<div class="r-title"><label class="required">{{ $t('modelManage.fileUpload') }}</label></div>
<div class="r-content">
<div style="position:relative">
<form class="dropzone" ref="dropzoneRef">
<div class="dropzon-err-tips ui red message" v-show="showUploadErr" style="display:none;margin:2.5rem">
{{ uploadErrTxt }}</div>
</form>
<div class="not-allowed-placeholder" v-show="uploading"></div>
<div class="content ui form">
<div class="guide-c" v-if="type != '1'">
<div class="step">
<div class="num">1</div>
<div class="txt">{{ $t('modelManage.createModel') }}</div>
</div>
<div class="line"></div>
<div class="step focused">
<div class="num">2</div>
<div class="txt">{{ $t('modelManage.uploadModelFiles') }}</div>
</div>
</div>
</div>
<div class="row" style="margin-top:10px">
<div class="r-title"><label></label></div>
<div class="r-content">
<el-button size="medium" class="green" @click="submit" :disabled="uploading">{{ $t('modelManage.upload') }}
</el-button>
<el-button size="medium" @click="cancel">{{ $t('modelManage.cancel') }}</el-button>
</div>
</div>
<div class="row" style="align-items:flex-start;">
<div class="r-title"><label>{{ $t('modelManage.uploadStatus') }}:</label></div>
<div class="r-content">
<div v-for="(item, index) in uploadFiles" :key="item.upload.uuid" class="datast-upload-progress">
<span class="dataset-name nowrap" :title="item.name">{{ item.name }}</span>
<div class="dataset-progress">
<el-progress :text-inside="true" :stroke-width="14" :percentage="uploadStatusList[index].progress">
</el-progress>
<div class="row-c">
<div class="row">
<div class="r-title"><label class="required">{{ $t('modelManage.modelName') }}</label></div>
<div class="r-content">
<el-input size="medium" class="input-disabled" v-model="state.name"
:placeholder="$t('modelManage.pleaseInputModelName')" readonly>
</el-input>
</div>
</div>
<div class="row" style="align-items:flex-start;">
<div class="r-title"><label class="required">{{ $t('modelManage.fileUpload') }}</label></div>
<div class="r-content">
<div style="position:relative">
<form class="dropzone" ref="dropzoneRef">
<div class="dropzon-err-tips ui red message" v-show="showUploadErr"
style="display:none;margin:2.5rem">
{{ uploadErrTxt }}</div>
</form>
<div class="not-allowed-placeholder" v-show="uploading"></div>
</div>
</div>
<div class="dataset-status nowrap">
<div class="status-flex">
<i v-if="uploadStatusList[index].infoCode === 1 || uploadStatusList[index].infoCode === 2"
class="ri-close-circle-line failed"></i>
<i v-if="uploadStatusList[index].infoCode === 0" class="ri-checkbox-circle-line success"></i>
<span>{{ uploadStatusList[index].status }}</span>
<el-tooltip v-if="uploadStatusList[index].infoCode === 1" class="item" effect="dark" placement="top">
<div slot="content"> {{ uploadStatusList[index].failedInfo }} </div>
<i style="font-size: 16px; margin-left: 0.5rem; cursor: pointer" class="ri-question-fill"></i>
</el-tooltip>
</div>
<div class="row" style="margin-top:10px">
<div class="r-title"><label></label></div>
<div class="r-content">
<el-button size="medium" class="green" @click="submit" :disabled="uploading">{{ $t('modelManage.upload')
}}
</el-button>
<el-button size="medium" @click="cancel">{{ $t('modelManage.cancel') }}</el-button>
</div>
</div>
<div class="row" style="align-items:flex-start;">
<div class="r-title"><label>{{ $t('modelManage.uploadStatus') }}:</label></div>
<div class="r-content">
<div v-for="(item, index) in uploadFiles" :key="item.upload.uuid" class="datast-upload-progress">
<span class="dataset-name nowrap" :title="item.name">{{ item.name }}</span>
<div class="dataset-progress">
<el-progress :text-inside="true" :stroke-width="14" :percentage="uploadStatusList[index].progress">
</el-progress>
</div>
<div class="dataset-status nowrap">
<div class="status-flex">
<i v-if="uploadStatusList[index].infoCode === 1 || uploadStatusList[index].infoCode === 2"
class="ri-close-circle-line failed"></i>
<i v-if="uploadStatusList[index].infoCode === 0" class="ri-checkbox-circle-line success"></i>
<span>{{ uploadStatusList[index].status }}</span>
<el-tooltip v-if="uploadStatusList[index].infoCode === 1" class="item" effect="dark"
placement="top">
<div slot="content"> {{ uploadStatusList[index].failedInfo }} </div>
<i style="font-size: 16px; margin-left: 0.5rem; cursor: pointer" class="ri-question-fill"></i>
</el-tooltip>
</div>
</div>
</div>
</div>
</div>
@@ -75,7 +86,8 @@
</template>

<script>

import ModelHeader from '../components/ModelHeader.vue';
import NotFound from '~/components/NotFound.vue';
import 'dropzone/dist/dropzone.css';
import Dropzone from 'dropzone';
import SparkMD5 from "spark-md5";
@@ -92,8 +104,15 @@ const maxModelFilesSize = window.MAX_MODEL_SIZE || 200 * 1024 * 1024 * 1024; //
export default {
data() {
return {
emptyPage: false,

modelName: '',
repoOwnerName: location.pathname.split('/')[1],
repoName: location.pathname.split('/')[2],
modelData: {},

dropzoneHandler: null,
type: '0', // 1-修改,其它-新增
type: '1', // 1-修改,其它-新增
state: {
type: '',
id: '',
@@ -116,12 +135,13 @@ export default {
uploading: false,
};
},
components: {},
components: { ModelHeader, NotFound },
methods: {
initModelData() {
const urlParams = getUrlSearchParams();
if (urlParams.name && urlParams.id) {
this.type = urlParams.type || '0';
this.modelName = urlParams.name;
this.type = urlParams.type || '1';
this.state.name = urlParams.name;
this.state.id = urlParams.id;
this.loading = true;
@@ -134,6 +154,7 @@ export default {
const list = res.data;
if (list && list.length) {
const data = list[0];
this.modelData = data;
this.state.type = data.type;
this.state.id = data.id;
this.state.name = data.name;
@@ -144,15 +165,14 @@ export default {
this.state.size = data.size || 0;
this.originData = data;
} else {
this.cancel();
this.emptyPage = true;
}
}).catch(err => {
this.loading = false;
console.log(err);
this.cancel();
});
} else {
this.cancel();
this.emptyPage = true;
}
},
initDropZone() {
@@ -481,7 +501,7 @@ export default {
this.uploading = false;
if (this.uploadSuccessLength == this.uploadLength) {
window.setTimeout(() => {
this.goDetail();
this.goFileListPage();
}, 1000);
} else {
if (this.uploadSuccessLength > 0) {
@@ -499,51 +519,50 @@ export default {
this.resetFileStatus();
this.uploadFiles = fileList;
this.uploading = true;
var md5map ={};
var md5map = {};
for (let i = 0, iLen = fileList.length; i < iLen; i++) {
const file = fileList[i];
file.modelUuid = this.state.id;
file.modelName = this.state.name;
this.calcFileMd5(file).then(res => { // 计算MD5
if(md5map[file.uniqueIdentifier] != null){
if (md5map[file.uniqueIdentifier] != null) {
var info = `${this.$t('modelManage.fileEqualInTheModel')} ${md5map[file.uniqueIdentifier]}`;
console.log("info=" + info);
this.uploadLength++;
this.uploadError(file, this.$t('modelManage.uploadFailed'));
this.updateFileStatus(file, this.$t('modelManage.uploadFailed'), 0, 1, info);
}else{
md5map[file.uniqueIdentifier]=file.name;
} else {
md5map[file.uniqueIdentifier] = file.name;
this.getChunksInfo(file).then(res => { // 获取Chunk信息
if (file.uploadID == '' || file.uuid == '') { // 未上传过
this.newUpload(file);
} else if (file.uploaded == '1') { // 已上传成功
if (file.attachID == '0') { // 删除数据集记录,未删除文件
// await addAttachment(file);
console.log(`file.attachID == '0'`);
if (file.uploadID == '' || file.uuid == '') { // 未上传过
this.newUpload(file);
} else if (file.uploaded == '1') { // 已上传成功
if (file.attachID == '0') { // 删除数据集记录,未删除文件
// await addAttachment(file);
console.log(`file.attachID == '0'`);
}
this.uploadLength++;
// 同一模型上传同一个文件
if (file._modelUuid) {
const info = `${this.$t('modelManage.fileHasAlreadyInTheModel')} ${file._modelName}`;
this.uploadError(file, info);
this.updateFileStatus(file, this.$t('modelManage.uploadFailed'), 0, 1, info);
} else { // 秒传
this.uploadSuccessLength++;
this.updateFileStatus(file, this.$t('modelManage.uploadSuccess'), 100, 0);
this.uploadSuccess(file);
}
console.log(file.name, '文件处理完成');
} else { // 断点续传
this.breakpointUpload(file);
}
}).catch(err => {
console.info('getChunksInfo', err);
this.uploadLength++;
// 同一模型上传同一个文件
if (file._modelUuid) {
const info = `${this.$t('modelManage.fileHasAlreadyInTheModel')} ${file._modelName}`;
this.uploadError(file, info);
this.updateFileStatus(file, this.$t('modelManage.uploadFailed'), 0, 1, info);
} else { // 秒传
this.uploadSuccessLength++;
this.updateFileStatus(file, this.$t('modelManage.uploadSuccess'), 100, 0);
this.uploadSuccess(file);
}
console.log(file.name, '文件处理完成');
} else { // 断点续传
this.breakpointUpload(file);
}
}).catch(err => {
console.info('getChunksInfo', err);
this.uploadLength++;
this.uploadError(file, this.$t('modelManage.uploadFailed'));
this.updateFileStatus(file, this.$t('modelManage.uploadFailed'), 0, 2);
});

}
this.uploadError(file, this.$t('modelManage.uploadFailed'));
this.updateFileStatus(file, this.$t('modelManage.uploadFailed'), 0, 2);
});
}
}).catch(err => {
console.info('calcFileMd5', err);
this.uploadLength++;
@@ -553,22 +572,16 @@ export default {
}
},
cancel() {
if (this.type == '1') {
this.goDetail();
return;
}
const list = window.location.href.split('/');
list.pop();
list.push('show_model');
window.location.href = list.join('/');
this.goFileListPage();
},
goDetail() {
goFileListPage() {
const list = window.location.href.split('/');
list.pop();
list.push('show_model_info');
window.location.href = list.join('/') + '?name=' + this.state.name;
list.push('model_filelist_tmpl');
window.location.href = list.join('/') + '?name=' + encodeURIComponent(this.state.name);
}
},
beforeMount() { },
mounted() {
this.initModelData();
this.initDropZone();
@@ -589,6 +602,7 @@ export default {
background: rgb(240, 240, 240);
display: flex;
align-items: center;
margin-top: 32px;

.title {
font-weight: 600;

web_src/vuepages/pages/modelmanage/local/vp-modelmanage-local-create-1.js → web_src/vuepages/pages/modelmanage/fileupload/vp-model-fileupload.js View File

@@ -4,7 +4,7 @@ import 'element-ui/lib/theme-chalk/index.css';
import localeEn from 'element-ui/lib/locale/lang/en';
import localeZh from 'element-ui/lib/locale/lang/zh-CN';
import { i18n, lang } from '~/langs';
import App from './modelmanage-local-create-1.vue';
import App from './index.vue';

Vue.use(ElementUI, {
locale: lang === 'zh-CN' ? localeZh : localeEn,

+ 111
- 0
web_src/vuepages/pages/modelmanage/graph/index.vue View File

@@ -0,0 +1,111 @@
<template>
<div>
<div v-if="emptyPage" style="padding-top:50px">
<NotFound></NotFound>
</div>
<div v-else>
<ModelHeader :tab="'graph'" :repoOwnerName="repoOwnerName" :repoName="repoName" :modelName="modelName" :model="modelData">
</ModelHeader>
<div class="ui container">
<div ref="graphContainerRef" class="graph-container"></div>
</div>
</div>
</div>
</template>

<script>
import ModelHeader from '../components/ModelHeader.vue';
import NotFound from '~/components/NotFound.vue';
import { getModelInfoByName, getModelEvolutionMap } from '~/apis/modules/modelmanage';
import { getUrlSearchParams } from '~/utils';
import { ModelGraph } from './model-graph';
import './model-graph.css';

let modelGraph = null;

export default {
data() {
return {
emptyPage: false,

modelName: '',
repoOwnerName: location.pathname.split('/')[1],
repoName: location.pathname.split('/')[2],
modelData: {},
};
},
components: { ModelHeader, NotFound },
methods: {},
beforeMount() {
const urlParams = getUrlSearchParams();
if (urlParams.name) {
this.modelName = urlParams.name;
this.loading = true;
getModelInfoByName({ repo: `/${this.repoOwnerName}/${this.repoName}`, name: this.modelName }).then(res => {
const data = res.data;
this.loading = false;
if (data && data.length) {
const model = data[0];
this.modelData = model;
getModelEvolutionMap({
repo: `/${this.repoOwnerName}/${this.repoName}`,
id: model.id,
}).then(res => {
const data = res.data;
function runData(node) {
if (node.Type == 1) {
const model = node.Model || {};
node.type = 'model';
node.name = model.name;
node.isCurrent = node.IsCurrent;
node.isParent = node.IsParent;
node.isDerive = !node.IsCurrent && !node.IsParent;
node.creator = model.userName;
if (!node.isCurrent) {
node.link = `/${model.repoOwnerName}/${model.repoName}/modelmanage/model_readme_tmpl?name=${model.name}`;
}
}
if (node.Type == 0) {
node.type = 'repo';
node.name = node.RepoOwnerName + ' / ' + `<span style="font-weight:bold">${node.RepoDisplayName}</span>`;
node.link = `/${node.RepoOwnerName}/${node.RepoName}`;
}
node.children = node.Next || [];
for (let i = 0, iLen = node.children.length; i < iLen; i++) {
const child = node.children[i];
runData(child);
}
}
runData(data);
this.$nextTick(() => {
modelGraph = new ModelGraph();
modelGraph.init(this.$refs.graphContainerRef, data, {});
});
}).catch(err => {
console.log(err);
});
} else {
this.emptyPage = true;
}
}).catch(err => {
console.log(err);
});
} else {
this.emptyPage = true;
}
},
mounted() { },
beforeDestroy() { },
};
</script>

<style scoped lang="less">
.graph-container {
position: relative;
border: 1px solid rgb(225, 227, 230);
border-radius: 5px;
margin-top: 32px;
height: 75vh;
overflow: hidden;
}
</style>

+ 290
- 0
web_src/vuepages/pages/modelmanage/graph/model-graph.css View File

@@ -0,0 +1,290 @@
._tree-toolbar {
position: absolute;
top: 0;
left: 0;
display: flex;
align-items: center;
padding-left: 20px;
padding-top: 20px;
z-index: 10000;
}

._tree-toolbar .legend-c {
display: flex;
align-items: center;
border-radius: 4px;
border: 1px solid #e9edf2;
background: white;
box-shadow: rgba(0, 0, 0, 0.1) 0 4px 10px 0;
padding: 6px 12px;
}

._tree-toolbar .legend {
display: flex;
align-items: center;
margin-right: 18px;
}

._tree-toolbar .legend:last-child {
margin-right: 0;
}

._tree-toolbar .legend .legend-icon {
margin-right: 5px;
height: 14px;
width: 18px;
border-radius: 2px;
}

._tree-toolbar .legend .legend-name {
font-size: 12px;
color: rgba(136, 136, 136, 1);
}

._tree-toolbar .legend .legend-isprarent {
background: rgb(122, 184, 251);
}

._tree-toolbar .legend .legend-iscurrent {
background: rgb(22, 132, 252);
}

._tree-toolbar .legend .legend-isderive {
background: rgb(3, 102, 214);
}

._tree-toolbar .legend .legend-isrepo {
border-radius: 8px;
border: 1px solid rgb(3, 102, 214);
background: rgb(255, 255, 255);
}

._tree-layout {
position: absolute;
width: 1500px;
height: 500px;
/* border: 1px solid gray; */
transition: scale 30ms;
}

._tree-layout ._tree-layout-svg {
position: absolute;
width: 100%;
height: 100%;
}

._tree-layout ._tree-layout-svg path {
transition: all 250ms ease;
}

._tree-layout ._tree-node {
position: absolute;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: center;
word-break: break-all;
transition: all 250ms ease;
}

._tree-layout ._tree-node-block {
position: absolute;
height: 100%;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
padding: 10px;
}

._tree-layout ._tree-node .name {
cursor: pointer;
color: white;
}

._tree-layout ._tree-node .name:hover {
text-decoration: underline;
}

._tree-layout ._tree-node.model-parent {
border-style: none;
border-color: unset;
color: rgb(255, 255, 255);
border-radius: 5px;
font-size: 12px;
padding: 10px;
text-align: center;
line-height: 17px;
font-weight: normal;
font-style: normal;
background: rgb(122, 184, 251);
}

._tree-layout ._tree-node.model-current {
border-style: none;
border-color: unset;
box-shadow: rgba(3, 110, 255, 0.4) 0px 0px 20px 0px;
color: rgb(255, 255, 255);
border-radius: 5px;
font-size: 12px;
padding: 10px;
text-align: center;
line-height: 17px;
font-weight: bold;
font-style: normal;
background: rgb(22, 132, 252);
}

._tree-layout ._tree-node.model-current .name {
cursor: default;
}

._tree-layout ._tree-node.model-current .name:hover {
text-decoration: none;
}

._tree-layout ._tree-node.model-derive {
border-style: none;
border-color: unset;
color: rgb(255, 255, 255);
border-radius: 5px;
font-size: 12px;
padding: 10px;
text-align: center;
line-height: 17px;
font-weight: normal;
font-style: normal;
background: rgb(3, 102, 214);
}

._tree-layout ._tree-node.repo {
border-color: rgb(3, 102, 214);
border-width: 1px;
border-style: solid;
color: rgb(16, 16, 16);
border-radius: 20px;
font-size: 12px;
padding: 10px;
text-align: center;
line-height: 17px;
font-weight: normal;
font-style: normal;
background: rgb(255, 255, 255);
}

._tree-layout ._tree-node.repo .name {
color: #101010;
}

._tree-layout ._tree-node .descr {
position: absolute;
top: 100%;
width: 100%;
text-align: center;
font-size: 12px;
color: rgb(136, 136, 136);
font-weight: 300;
height: 22px;
padding-top: 6px;
line-height: 17px;
}

._tree-layout ._tree-node .creator {
position: absolute;
right: 100%;
font-size: 12px;
font-weight: normal;
width: 80px;
height: 17px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
text-align: right;
padding-right: 4px;
}

._tree-layout ._tree-node .creator a {
color: rgb(136, 136, 136);
}

._tree-layout ._tree-node .creator a:hover {
text-decoration: underline;
}

._tree-layout ._tree-node .node-expand-c {
position: absolute;
top: 50%;
left: 100%;
}

._tree-layout ._tree-node .node-expand-line {
position: absolute;
height: 1px;
width: 20px;
background-color: rgb(122, 184, 251);
}

._tree-layout ._tree-node .node-expand-icon {
position: absolute;
height: 16px;
width: 16px;
background-color: rgb(122, 184, 251);
color: white;
display: flex;
align-items: center;
justify-content: center;
left: 13px;
top: -8px;
border-radius: 100%;
cursor: pointer;
font-weight: bold;
line-height: 14px;
font-size: 13px;
user-select: none;
}

._model-info {
position: fixed;
width: 300px;
height: 258px;
border-color: rgb(225, 227, 230);
border-width: 1px;
border-style: solid;
box-shadow: rgb(225, 227, 230) 0px 2px 6px 0px;
border-radius: 5px;
font-size: 12px;
background: rgba(255, 255, 255, 0.96);
padding: 10px;
z-index: 10001;
}

._model-info .tit {
font-size: 12px;
color: rgb(22, 132, 252);
}

._model-info .row {
display: flex;
padding-left: 2px;
margin: 4px 0;
align-items: center;
}

._model-info .row>div:nth-child(1) {
width: 66px;
color: rgb(136, 136, 136);
text-align: right;
}

._model-info .row>div:nth-child(2) {
flex: 1;
color: rgb(16, 16, 16);
}

:lang(en-US) ._model-info .row>div:nth-child(1) {
width: 120px;
}

:lang(en-US) ._model-info {
width: 326px;
}

+ 496
- 0
web_src/vuepages/pages/modelmanage/graph/model-graph.js View File

@@ -0,0 +1,496 @@
import { BoundingBox, Layout } from '~/utils/treelayout';
import { getListValueWithKey, transFileSize, renderSpecStr } from '~/utils';
import { MODEL_ENGINES } from '~/const';
import { formatDate } from 'element-ui/lib/utils/date-util';
import { i18n } from '~/langs';

function ModelGraph() {
this.options = {
horizontalGap: 120,
verticalGap: 40,
parentModelNodeWidth: 100,
parentModelNodeHeight: 60,
currentModelNodeWidth: 100,
currentModelNodeHeight: 100,
deriveModelNodeWidth: 100,
deriveModelNodeHeight: 60,
repoNodeWidth: 140,
repoNodeHeight: 40,
collapsedIconMarginLeft: 20,
};

this.$el = null;
this.$layout = null;
this.viewWinWidth = 0;
this.viewWinHeight = 0;
this.layoutWidth = 0;
this.layoutHeight = 0;
this._layoutWidth = 0;
this._layoutHeight = 0;
this.initScale = 1;

this.drawOffsetLeft = 0;
this.drawOffsetTop = 0;

this.treeData = null;
this.nodeMap = {};
return this;
}

ModelGraph.prototype.init = function (element, data, options) {
if (!element) return;
if (!['absolute', 'relative', 'fixed'].includes(element.style.position)) {
element.style.position = 'relative';
element.style.overflow = 'hidden';
}
this.$el = element;
Object.assign(this.options, options);
this.treeData = data;
this.renderTree();
this.toolBarInit();
this.eventInit();
};

ModelGraph.prototype.walkTree = function (pNode, lvl) {
if (pNode.type == 'model') {
if (pNode.isParent) {
pNode.width = this.options.parentModelNodeHeight;
pNode.height = this.options.parentModelNodeWidth;
}
if (pNode.isCurrent) {
pNode.width = this.options.currentModelNodeHeight;
pNode.height = this.options.currentModelNodeWidth;
}
if (pNode.isDerive) {
pNode.width = this.options.deriveModelNodeHeight;
pNode.height = this.options.deriveModelNodeWidth;
}
}
if (pNode.type == 'repo') {
pNode.width = this.options.repoNodeHeight;
pNode.height = this.options.repoNodeWidth;
}
pNode.lvl = lvl;
const children = pNode.children || [];
for (let i = 0, iLen = children.length; i < iLen; i++) {
const child = children[i];
child.pNode = pNode;
this.walkTree(child, lvl + 1);
}
pNode._children = pNode._children || pNode.children;
if (pNode.isCollapsed) {
pNode.children = undefined;
} else {
pNode.children = pNode._children;
}
if (!pNode._id) {
pNode._id = `${lvl}-${Math.random()}`;
}
this.nodeMap[pNode._id] = pNode;
};

ModelGraph.prototype.renderTree = function () {
const bb = new BoundingBox(this.options.verticalGap, this.options.horizontalGap);
const layout = new Layout(bb);
this.walkTree(this.treeData, 0);
const { result, boundingBox } = layout.layout(this.treeData);
const viewWinEl = this.$el;
this.viewWinWidth = viewWinEl.clientWidth;
this.viewWinHeight = viewWinEl.clientHeight;
const layoutWidth = boundingBox.bottom;
const layoutHeight = boundingBox.right;
this._layoutWidth = layoutWidth;
this._layoutHeight = layoutHeight;
if (!this.$layout) {
this.layoutWidth = Math.max(boundingBox.bottom, boundingBox.right, 1000);
this.layoutHeight = Math.max(boundingBox.bottom, boundingBox.right, 1000);
const layoutEl = document.createElement('div');
layoutEl.classList.add('_tree-layout');
layoutEl.classList.add('model-graph-layout');
this.$el.append(layoutEl);
this.$layout = layoutEl;
this.$layout.style.width = this.layoutWidth * 4 + 'px';
this.$layout.style.height = this.layoutHeight * 4 + 'px';
this.$layout.style.left = - this.layoutWidth * 4 / 2 + this.viewWinWidth / 2 + 'px';
this.$layout.style.top = - this.layoutHeight * 4 / 2 + this.viewWinHeight / 2 + 'px';
const scale = Math.min(Math.min((this.viewWinWidth - 100) / layoutWidth, (this.viewWinHeight - 100) / layoutHeight), 1);
this.initScale = scale;
this.$layout.style.scale = scale;
this.$layout.innerHTML = '<svg class="_tree-layout-svg"><g></g></svg>';
this.drawOffsetLeft = this.layoutWidth * 1.5;
this.drawOffsetTop = this.layoutHeight * 1.5 + (this.layoutHeight - layoutHeight) / 2 - this.options.verticalGap / 4;
} else {
this.drawOffsetLeft = this.layoutWidth * 1.5;
this.drawOffsetTop = this.layoutHeight * 1.5 + (this.layoutHeight - layoutHeight) / 2 - this.options.verticalGap / 4;
}
this.drawTreeNode(this.treeData);
this.popupInit();
};

ModelGraph.prototype.drawTreeNode = function (treeData, setPoint) {
const children = treeData.children || [];
let divEl = this.$layout.querySelectorAll(`._tree-node[data-id="${treeData._id}"]`)[0];
if (!divEl) {
divEl = document.createElement('div');
divEl.classList = ['_tree-node'];
let innerHTML = treeData.link ? `<div class="_tree-node-block"><a class="name" target="_blank" href="${treeData.link}">${treeData.name}</a></div>` :
`<div class="_tree-node-block"><div class="name">${treeData.name}</div></div>`;
if (treeData.type == 'model') {
divEl.classList.add('model');
if (treeData.isParent) {
divEl.classList.add('model-parent');
innerHTML += `<div class="descr">${i18n.t('modelManage.parentModel')}</div>`;
}
if (treeData.isCurrent) {
divEl.classList.add('model-current');
innerHTML += `<div class="descr">${i18n.t('modelManage.currentModel')}</div>`;
}
if (treeData.isDerive) {
divEl.classList.add('model-derive');
}
if (treeData.pNode) {
innerHTML += `<div class="creator" style="margin-top:-14px"><a target="_blank" href="/${treeData.creator}">${treeData.creator}</a></div>`;
}
}
if (treeData.type == 'repo') {
divEl.classList.add('repo');
}
if (children.length || (treeData._children || []).length) {
innerHTML += `<div class="node-expand-c">
<div class="node-expand-line"></div>
<div class="node-expand-icon"></div>
</div>`;
}
divEl.style.width = treeData.height + 'px';
divEl.style.height = treeData.width + 'px';
divEl.style.zIndex = 10000 - treeData.lvl;
divEl.dataset.id = treeData._id;
divEl.innerHTML = innerHTML;
this.$layout.append(divEl);
}
if (divEl.querySelector('div.node-expand-icon')) {
divEl.querySelector('div.node-expand-icon').innerHTML = treeData.isCollapsed ? '+' : '-';
}
if (setPoint) {
divEl.style.left = setPoint.x - treeData.height / 2 + 'px';
divEl.style.top = setPoint.y - treeData.width / 2 + 'px';
divEl.style.scale = 0;
} else {
divEl.style.left = (treeData.y + this.drawOffsetLeft) + 'px';
divEl.style.top = (treeData.x + this.drawOffsetTop) + 'px';
divEl.style.scale = 1;
const creator = divEl.querySelector('div.creator');
if (creator && treeData.pNode) {
const marginTop = treeData.pNode.x + treeData.pNode.width / 2 >= treeData.x + treeData.width / 2 ? -14 : 14;
creator.style.marginTop = marginTop + 'px';
}
}
for (let i = 0, iLen = children.length; i < iLen; i++) {
this.drawTreeNode(children[i], setPoint);
this.drawLine(treeData, children[i], setPoint);
}
};

ModelGraph.prototype.drawLine = function (pNode, node, setPoint) {
const svgPathContainer = this.$layout.querySelector('._tree-layout-svg g');
const node1 = {
w: pNode.height,
h: pNode.width,
l: pNode.y + this.drawOffsetLeft,
t: pNode.x + this.drawOffsetTop
}
const node2 = {
w: node.height,
h: node.width,
l: node.y + this.drawOffsetLeft,
t: node.x + this.drawOffsetTop
}
const point1 = {
x: node1.l + node1.w + this.options.collapsedIconMarginLeft,
y: node1.t + node1.h / 2
};
const point2 = {
x: node2.l,
y: node2.t + node2.h / 2
}
let d = `M${point1.x} ${point1.y} C${(point1.x + point2.x) / 2} ${point1.y} ${(point1.x + point2.x) / 2} ${point2.y} ${point2.x} ${point2.y}`;
let path = svgPathContainer.querySelectorAll(`path[data-id="${node._id}"]`)[0];
if (!path) {
path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
path.setAttribute('fill', 'none');
path.setAttribute('stroke', 'rgb(122, 184, 251)');
path.setAttribute('stroke-width', 1);
path.setAttribute('id', node._id);
path.dataset.id = node._id;
svgPathContainer.append(path);
}
if (setPoint) {
d = `M${setPoint.x} ${setPoint.y} C${setPoint.x} ${setPoint.y} ${setPoint.x} ${setPoint.y} ${setPoint.x} ${setPoint.y}`;
}
path.setAttribute('d', d);
};

ModelGraph.prototype.removeNode = function (nodeList) {
const layout = this.$layout;
for (let i = 0, iLen = nodeList.length; i < iLen; i++) {
const curNode = nodeList[i];
const id = curNode._id;
const nodes = this.$layout.querySelectorAll(`[data-id="${id}"]`);
nodes.forEach(item => item.remove());
const children = curNode.children || [];
this.removeNode(children);
}
};

ModelGraph.prototype.showModelNodeInfo = function (nodeEl, nodeData, showOrHide) {
const self = this;
const elClass = '_model-info-' + nodeData._id.toString().replace('.', '-');
let showInfoEl = document.querySelector('.' + elClass);
if (showOrHide == 'show') {
if (showInfoEl) {
nodeData.delayHideTimer && clearTimeout(nodeData.delayHideTimer);
return;
}
const model = nodeData.Model || {};
let modelObj = { ...model };
const trainTaskInfo = model.trainTaskInfo ? JSON.parse(model.trainTaskInfo) : '';
if (trainTaskInfo) {
trainTaskInfo.DisplayJobName = trainTaskInfo.DisplayJobName == undefined ? '' : trainTaskInfo.DisplayJobName;
const taskType = trainTaskInfo.Type;
let taskUrl = `/${model.repoOwnerName}/${model.repoName}/`;
if (taskType == 0) {
taskUrl = taskUrl + 'cloudbrain/train-job/' + trainTaskInfo.JobID;
} else if (taskType == 1) {
taskUrl = taskUrl + 'modelarts/train-job/' + trainTaskInfo.JobID;
} else if (taskType == 2) {
taskUrl = taskUrl + 'grampus/train-job/' + trainTaskInfo.JobID;
}
let specObj;
try {
specObj = trainTaskInfo.FlavorName ? JSON.parse(trainTaskInfo.FlavorName) : '';
} catch (e) {
specObj = trainTaskInfo.FlavorName;
}
modelObj.displayJobNameHtml = `<a target="_blank" href="${trainTaskInfo.DisplayJobName ? taskUrl : 'javascript:;'}" title="${trainTaskInfo.DisplayJobName}">${trainTaskInfo.DisplayJobName}</a>`;
modelObj.trainJobDuration = trainTaskInfo.TrainJobDuration;
modelObj.sepcStr = typeof specObj == 'object' ? renderSpecStr(specObj, false) : specObj;
}
modelObj = {
...modelObj,
engineName: getListValueWithKey(MODEL_ENGINES, model.engine.toString()),
modelSize: transFileSize(model.size),
createTimeStr: formatDate(new Date(model.createdUnix * 1000), 'yyyy-MM-dd HH:mm:ss'),
isPrivateStr: model.isPrivate ? i18n.t('modelManage.modelAccessPrivate') : i18n.t('modelManage.modelAccessPublic'),
};
showInfoEl = document.createElement('div');
showInfoEl.classList.add('_model-info');
showInfoEl.classList.add(elClass);
showInfoEl.innerHTML = `
<div class="tit">${i18n.t('modelManage.modelInfo')}</div>
<div class="row">
<div>${i18n.t('modelManage.modelEngine')}:</div>
<div>${modelObj.engineName}</div>
</div>
<div class="row">
<div>${i18n.t('modelManage.modelSize')}:</div>
<div>${modelObj.modelSize}</div>
</div>
<div class="row">
<div>${i18n.t('modelManage.createTime')}:</div>
<div>${modelObj.createTimeStr}</div>
</div>
<div class="row">
<div>${i18n.t('modelManage.modelAccess')}:</div>
<div>${modelObj.isPrivateStr}</div>
</div>
<div class="tit" style="margin-top:8px">${i18n.t('modelManage.trainingInfo')}</div>
<div class="row">
<div>${i18n.t('trainTask')}:</div>
<div>${modelObj.displayJobNameHtml || '--'}</div>
</div>
<div class="row">
<div>${i18n.t('trainDuration')}:</div>
<div>${modelObj.trainJobDuration || '--'}</div>
</div>
<div class="row" style="height:40px">
<div>${i18n.t('modelManage.specInfo')}:</div>
<div>${modelObj.sepcStr || '--'}</div>
</div>`;
const posInfo = nodeEl.getBoundingClientRect();
const winW = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
showInfoEl.style.top = Math.max(posInfo.top - 258 + 6, 20) + 'px';
showInfoEl.style.left = Math.min(posInfo.left - 6 + posInfo.width, winW - 250 - 20) + 'px';
document.querySelector('body').append(showInfoEl);
const mouseEnter = function () {
self.showModelNodeInfo(nodeEl, nodeData, 'show');
};
const mouseLeave = function () {
self.showModelNodeInfo(nodeEl, nodeData, 'hide');
};
showInfoEl.addEventListener('mouseenter', mouseEnter);
showInfoEl.addEventListener('mouseleave', mouseLeave);
} else {
nodeData.delayHideTimer && clearTimeout(nodeData.delayHideTimer);
nodeData.delayHideTimer = setTimeout(() => {
if (showInfoEl) {
showInfoEl.remove();
}
}, 200);
}
};

ModelGraph.prototype.eventInit = function () {
if (this.$layout) {
this._drag(this.$layout);
this._scale(this.$layout);
const self = this;
this.$layout.addEventListener('click', function (evt) {
if (evt.target.classList.contains('node-expand-icon')) {
const nodeEl = evt.target.parentElement.parentElement;
const id = nodeEl.dataset.id;
const curNode = self.nodeMap[id];
const isCollapsed = curNode.isCollapsed;
if (isCollapsed) {
const n = {
w: curNode.height,
h: curNode.width,
l: curNode.y + self.drawOffsetLeft,
t: curNode.x + self.drawOffsetTop
}
const point = {
x: n.l + n.w + self.options.collapsedIconMarginLeft,
y: n.t + n.h / 2
};
const children = curNode._children || [];
for (let i = 0, iLen = children.length; i < iLen; i++) {
self.drawTreeNode(children[i], point);
self.drawLine(curNode, children[i], point);
}
curNode.isCollapsed = !curNode.isCollapsed;
self.renderTree();
} else {
const children = curNode.children || [];
curNode.isCollapsed = !curNode.isCollapsed;
self.renderTree();
const n = {
w: curNode.height,
h: curNode.width,
l: curNode.y + self.drawOffsetLeft,
t: curNode.x + self.drawOffsetTop
}
const point = {
x: n.l + n.w + self.options.collapsedIconMarginLeft,
y: n.t + n.h / 2
};
for (let i = 0, iLen = children.length; i < iLen; i++) {
self.drawTreeNode(children[i], point);
self.drawLine(curNode, children[i], point);
}
setTimeout(function () {
self.removeNode(children);
}, 260);
}
}
});
}
};

ModelGraph.prototype.popupInit = function () {
const modelsEls = this.$layout.querySelectorAll('._tree-node.model ._tree-node-block');
const self = this;
const mouseEnterHandler = function (evt) {
const nodeEl = evt.target.parentElement;
const id = nodeEl.dataset.id;
const curNode = self.nodeMap[id];
self.showModelNodeInfo(nodeEl, curNode, 'show');
};
const mouseLeaveHandler = function (evt) {
const nodeEl = evt.target.parentElement;
const id = nodeEl.dataset.id;
const curNode = self.nodeMap[id];
self.showModelNodeInfo(nodeEl, curNode, 'hide');
};

for (let i = 0, iLen = modelsEls.length; i < iLen; i++) {
const modelEl = modelsEls[i];
modelEl.removeEventListener('mouseenter', mouseEnterHandler);
modelEl.removeEventListener('mouseleave', mouseLeaveHandler);
modelEl.addEventListener('mouseenter', mouseEnterHandler);
modelEl.addEventListener('mouseleave', mouseLeaveHandler);
}
}

ModelGraph.prototype.toolBarInit = function () {
const toolBarEl = document.createElement('div');
toolBarEl.classList.add('_tree-toolbar');
toolBarEl.innerHTML = `<div class="legend-c">
<div class="legend"><div class="legend-icon legend-isprarent"></div><span class="legend-name">${i18n.t('modelManage.parentModel')}</span></div>
<div class="legend"><div class="legend-icon legend-iscurrent"></div><span class="legend-name">${i18n.t('modelManage.currentModel')}</span></div>
<div class="legend"><div class="legend-icon legend-isderive"></div><span class="legend-name">${i18n.t('modelManage.derivedModel')}</span></div>
<div class="legend"><div class="legend-icon legend-isrepo"></div><span class="legend-name">${i18n.t('modelManage.refRepository')}</span></div>
</div>`;
this.$el.append(toolBarEl);
};

ModelGraph.prototype._drag = function (el) {
var self = this;
el.onmousedown = mouseDown;
function mouseDown(event) {
event = event || window.event;
var disX = event.clientX - el.offsetLeft;
var disY = event.clientY - el.offsetTop;
if (event.target == this || event.target.classList.contains('_tree-layout-svg')) {
var oldCursor = el.style.cursor;
el.style.cursor = 'move';
document.onmousemove = function (event) {
event = event || window.event;
MouseMove(event, disX, disY);
}
document.onmouseup = function () {
document.onmousemove = null;
el.style.cursor = oldCursor;
}
}
}
function MouseMove(event, x1, y1) {
event = event || window.event;
var l = event.clientX - x1,
t = event.clientY - y1,
winW = document.documentElement.clientWidth || document.body.clientWidth,
winH = document.documentElement.clientHeight || document.body.clientHeight,
maxW = winW - el.offsetWidth,
maxH = winH - el.offsetHeight;
// if (l < 0) {
// l = 0;
// } else if (l > maxW - 15) {
// l = maxW - 15;
// }
// if (t < 0) {
// t = 0;
// } else if (t > maxH - 15) {
// t = maxH - 15;
// }
el.style.left = l + "px";
el.style.top = t + "px";
}
}

ModelGraph.prototype._scale = function (el) {
const self = this;
el.onmousewheel = function (event) {
var scale = parseFloat(el.style.scale || 1);
if (event.wheelDelta >= 0) {
scale *= 1.1;
} else {
scale /= 1.1;
}
if (scale >= self.initScale / 2 && scale < 1.5) {
el.style.scale = scale;
}
event.stopPropagation();
event.preventDefault();
event.returnValue = false;
}
}

export { ModelGraph }

web_src/vuepages/pages/modelmanage/local/vp-modelmanage-local-create-2.js → web_src/vuepages/pages/modelmanage/graph/vp-model-graph.js View File

@@ -4,7 +4,7 @@ import 'element-ui/lib/theme-chalk/index.css';
import localeEn from 'element-ui/lib/locale/lang/en';
import localeZh from 'element-ui/lib/locale/lang/zh-CN';
import { i18n, lang } from '~/langs';
import App from './modelmanage-local-create-2.vue';
import App from './index.vue';

Vue.use(ElementUI, {
locale: lang === 'zh-CN' ? localeZh : localeEn,

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save