Browse Source

Merge branch 'V20220830' into fix-2392

tags/v1.22.8.2^2
zouap 3 years ago
parent
commit
2002f99f42
86 changed files with 25251 additions and 399 deletions
  1. +49
    -0
      models/admin_operate_log.go
  2. +49
    -3
      models/cloudbrain.go
  3. +5
    -0
      models/models.go
  4. +349
    -0
      models/resource_queue.go
  5. +329
    -0
      models/resource_scene.go
  6. +285
    -0
      models/resource_specification.go
  7. +13
    -0
      modules/cron/tasks_basic.go
  8. +33
    -0
      modules/grampus/resty.go
  9. +70
    -11
      options/locale/locale_en-US.ini
  10. +62
    -2
      options/locale/locale_zh-CN.ini
  11. +19351
    -17
      package-lock.json
  12. +1
    -0
      package.json
  13. +248
    -0
      routers/admin/resources.go
  14. +2
    -2
      routers/repo/cloudbrain.go
  15. +1
    -1
      routers/repo/grampus.go
  16. +56
    -29
      routers/repo/modelarts.go
  17. +14
    -0
      routers/response/error.go
  18. +5
    -1
      routers/response/response.go
  19. +4
    -0
      routers/response/response_list.go
  20. +27
    -1
      routers/routes/routes.go
  21. +14
    -0
      services/admin/operate_log/operate_log.go
  22. +122
    -0
      services/cloudbrain/resource/resource_queue.go
  23. +35
    -0
      services/cloudbrain/resource/resource_scene.go
  24. +186
    -0
      services/cloudbrain/resource/resource_specification.go
  25. +3
    -4
      templates/admin/cloudbrain/images.tmpl
  26. +14
    -15
      templates/admin/cloudbrain/list.tmpl
  27. +63
    -43
      templates/admin/navbar.tmpl
  28. +10
    -0
      templates/admin/resources/queue.tmpl
  29. +10
    -0
      templates/admin/resources/scene.tmpl
  30. +10
    -0
      templates/admin/resources/specification.tmpl
  31. +10
    -10
      templates/custom/select_dataset_train.tmpl
  32. +1
    -1
      templates/custom/wait_count_train.tmpl
  33. +2
    -1
      templates/repo/attachment/upload.tmpl
  34. +5
    -9
      templates/repo/cloudbrain/benchmark/new.tmpl
  35. +11
    -5
      templates/repo/cloudbrain/inference/new.tmpl
  36. +7
    -1
      templates/repo/cloudbrain/inference/show.tmpl
  37. +7
    -1
      templates/repo/cloudbrain/show.tmpl
  38. +8
    -6
      templates/repo/cloudbrain/trainjob/new.tmpl
  39. +16
    -10
      templates/repo/cloudbrain/trainjob/show.tmpl
  40. +2
    -2
      templates/repo/datasets/index.tmpl
  41. +5
    -3
      templates/repo/grampus/trainjob/gpu/new.tmpl
  42. +4
    -2
      templates/repo/grampus/trainjob/npu/new.tmpl
  43. +17
    -12
      templates/repo/grampus/trainjob/show.tmpl
  44. +5
    -4
      templates/repo/modelarts/inferencejob/new.tmpl
  45. +9
    -2
      templates/repo/modelarts/inferencejob/show.tmpl
  46. +10
    -4
      templates/repo/modelarts/notebook/show.tmpl
  47. +6
    -4
      templates/repo/modelarts/trainjob/edit_para.tmpl
  48. +4
    -2
      templates/repo/modelarts/trainjob/new.tmpl
  49. +19
    -13
      templates/repo/modelarts/trainjob/show.tmpl
  50. +45
    -5
      templates/repo/modelarts/trainjob/version_new.tmpl
  51. +28
    -29
      templates/repo/modelmanage/convertIndex.tmpl
  52. +11
    -11
      templates/repo/modelmanage/convertshowinfo.tmpl
  53. +19
    -20
      templates/repo/modelmanage/index.tmpl
  54. +2
    -2
      templates/repo/modelmanage/showinfo.tmpl
  55. +3
    -1
      web_src/js/components/MinioUploader.vue
  56. +22
    -15
      web_src/js/components/Model.vue
  57. +33
    -21
      web_src/js/components/dataset/referenceDataset.vue
  58. +42
    -28
      web_src/js/components/dataset/selectDataset.vue
  59. +5
    -6
      web_src/js/components/images/adminImages.vue
  60. +17
    -10
      web_src/js/components/images/selectGrampusImages.vue
  61. +24
    -18
      web_src/js/components/images/selectImages.vue
  62. +92
    -11
      web_src/js/features/i18nVue.js
  63. +55
    -1
      web_src/less/_admin.less
  64. +41
    -0
      web_src/vuepages/apis/modules/point.js
  65. +174
    -0
      web_src/vuepages/apis/modules/resources.js
  66. +26
    -0
      web_src/vuepages/apis/service.js
  67. +99
    -0
      web_src/vuepages/components/BaseDialog.vue
  68. +16
    -0
      web_src/vuepages/const/index.js
  69. +156
    -0
      web_src/vuepages/langs/config/en-US.js
  70. +156
    -0
      web_src/vuepages/langs/config/zh-CN.js
  71. +16
    -0
      web_src/vuepages/langs/index.js
  72. +259
    -0
      web_src/vuepages/pages/resources/components/QueueDialog.vue
  73. +366
    -0
      web_src/vuepages/pages/resources/components/SceneDialog.vue
  74. +336
    -0
      web_src/vuepages/pages/resources/components/SpecificationDialog.vue
  75. +284
    -0
      web_src/vuepages/pages/resources/queue/index.vue
  76. +17
    -0
      web_src/vuepages/pages/resources/queue/vp-resources-queue.js
  77. +361
    -0
      web_src/vuepages/pages/resources/scene/index.vue
  78. +17
    -0
      web_src/vuepages/pages/resources/scene/vp-resources-scene.js
  79. +451
    -0
      web_src/vuepages/pages/resources/specification/index.vue
  80. +17
    -0
      web_src/vuepages/pages/resources/specification/vp-resources-specification.js
  81. +148
    -0
      web_src/vuepages/pages/reward/point/utils.js
  82. +16
    -0
      web_src/vuepages/pages/reward/point/vp-point.js
  83. +308
    -0
      web_src/vuepages/pages/reward/point/vp-point.vue
  84. +7
    -0
      web_src/vuepages/utils/index.js
  85. +7
    -0
      webpack.config.js
  86. +7
    -0
      webpack_pro.config.js

+ 49
- 0
models/admin_operate_log.go View File

@@ -0,0 +1,49 @@
package models

import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/timeutil"
"encoding/json"
)

type AdminOperateLog struct {
ID int64 `xorm:"pk autoincr"`
BizType string
OperateType string
OldValue string `xorm:"TEXT"`
NewValue string `xorm:"TEXT"`
RelatedId string `xorm:"INDEX"`
Comment string
CreatedTime timeutil.TimeStamp `xorm:"created"`
CreatedBy int64
}

type LogValues struct {
Params []LogValue
}

type LogValue struct {
Key string
Val interface{}
}

func (l *LogValues) Add(key string, val interface{}) *LogValues {
l.Params = append(l.Params, LogValue{Key: key, Val: val})
return l
}

func (l *LogValues) JsonString() string {
if len(l.Params) == 0 {
return ""
}
b, err := json.Marshal(l)
if err != nil {
log.Error("LogValues JsonString error . %v", err)
return ""
}
return string(b)
}

func InsertAdminOperateLog(log AdminOperateLog) (int64, error) {
return x.Insert(&log)
}

+ 49
- 3
models/cloudbrain.go View File

@@ -71,6 +71,7 @@ const (
ModelArtsStopping ModelArtsJobStatus = "STOPPING" //停止中
ModelArtsStopped ModelArtsJobStatus = "STOPPED" //停止
ModelArtsUnavailable ModelArtsJobStatus = "UNAVAILABLE" //故障
ModelArtsDeleting ModelArtsJobStatus = "DELETING" //删除中
ModelArtsDeleted ModelArtsJobStatus = "DELETED" //已删除
ModelArtsResizing ModelArtsJobStatus = "RESIZING" //规格变更中
ModelArtsResizFailed ModelArtsJobStatus = "RESIZE_FAILED" //规格变更失败
@@ -111,6 +112,16 @@ const (
GrampusStatusWaiting = "WAITING"
)

const (
//cluster
OpenICluster = "OpenI"
C2NetCluster = "C2Net"

//AI center
AICenterOfCloudBrainOne = "OpenIOne"
AICenterOfCloudBrainTwo = "OpenITwo"
)

type Cloudbrain struct {
ID int64 `xorm:"pk autoincr"`
JobID string `xorm:"INDEX NOT NULL"`
@@ -1082,6 +1093,7 @@ type DatasetDownload struct {
DatasetName string `json:"dataset_name"`
DatasetDownloadLink string `json:"dataset_download_link"`
RepositoryLink string `json:"repository_link"`
IsDelete bool `json:"is_delete"`
}

type DataSource struct {
@@ -1338,6 +1350,34 @@ type GrampusSpec struct {
Name string `json:"name"`
ProcessorType string `json:"processorType"`
Centers []Center `json:"centers"`
SpecInfo SpecInfo `json:"specInfo"`
}

type GrampusAiCenter struct {
AccDevices []GrampusAccDevice `json:"accDevices"`
Id string `json:"id"`
Name string `json:"name"`
Resource []GrampusCenterResource `json:"resource"`
}

type GrampusAccDevice struct {
Kind string `json:"kind"` //加速卡类别, npu.huawei.com/NPU,nvidia.com/gpu,cambricon.com/mlu
Model string `json:"model"` //加速卡型号
}

type GrampusCenterResource struct {
Allocated string `json:"allocated"`
Capacity string `json:"capacity"`
Name string `json:"name"`
}

type SpecInfo struct {
AccDeviceKind string `json:"accDeviceKind"`
AccDeviceMemory string `json:"accDeviceMemory"`
AccDeviceModel string `json:"accDeviceModel"`
AccDeviceNum int `json:"accDeviceNum"`
CpuCoreNum int `json:"cpuCoreNum"`
MemorySize string `json:"memorySize"`
}

type GetGrampusResourceSpecsResult struct {
@@ -1345,6 +1385,12 @@ type GetGrampusResourceSpecsResult struct {
Infos []GrampusSpec `json:"resourceSpecs"`
}

type GetGrampusAiCentersResult struct {
GrampusResult
Infos []GrampusAiCenter `json:"aiCenterInfos"`
TotalSize int `json:"totalSize"`
}

type GrampusImage struct {
CreatedAt int64 `json:"createdAt"`
UpdatedAt int64 `json:"updatedAt"`
@@ -1889,9 +1935,9 @@ func GetCloudbrainCountByUserID(userID int64, jobType string) (int, error) {

func GetCloudbrainRunCountByRepoID(repoID int64) (int, error) {
count, err := x.In("status", JobWaiting, JobRunning, ModelArtsCreateQueue, ModelArtsCreating, ModelArtsStarting,
ModelArtsReadyToStart, ModelArtsResizing, ModelArtsStartQueuing, ModelArtsRunning, ModelArtsRestarting, ModelArtsTrainJobInit,
ModelArtsTrainJobImageCreating, ModelArtsTrainJobSubmitTrying, ModelArtsTrainJobWaiting, ModelArtsTrainJobRunning,
ModelArtsTrainJobScaling, ModelArtsTrainJobCheckInit, ModelArtsTrainJobCheckRunning, ModelArtsTrainJobCheckRunningCompleted).And("repo_id = ?", repoID).Count(new(Cloudbrain))
ModelArtsReadyToStart, ModelArtsResizing, ModelArtsStartQueuing, ModelArtsRunning, ModelArtsDeleting, ModelArtsRestarting, ModelArtsTrainJobInit,
ModelArtsTrainJobImageCreating, ModelArtsTrainJobSubmitTrying, ModelArtsTrainJobWaiting, ModelArtsTrainJobRunning, ModelArtsStopping, ModelArtsResizing,
ModelArtsTrainJobScaling, ModelArtsTrainJobCheckInit, ModelArtsTrainJobCheckRunning, ModelArtsTrainJobKilling, ModelArtsTrainJobCheckRunningCompleted).And("repo_id = ?", repoID).Count(new(Cloudbrain))
return int(count), err
}



+ 5
- 0
models/models.go View File

@@ -145,6 +145,11 @@ func init() {
new(OrgStatistic),
new(SearchRecord),
new(AiModelConvert),
new(ResourceQueue),
new(ResourceSpecification),
new(ResourceScene),
new(ResourceSceneSpec),
new(AdminOperateLog),
new(CloudbrainTemp),
new(DatasetReference),
)


+ 349
- 0
models/resource_queue.go View File

@@ -0,0 +1,349 @@
package models

import (
"code.gitea.io/gitea/modules/timeutil"
"errors"
"strconv"
"strings"
"xorm.io/builder"
)

type ResourceQueue struct {
ID int64 `xorm:"pk autoincr"`
QueueCode string
Cluster string `xorm:"notnull"`
AiCenterCode string
AiCenterName string
ComputeResource string
AccCardType string
CardsTotalNum int
IsAutomaticSync bool
Remark string
DeletedTime timeutil.TimeStamp `xorm:"deleted"`
CreatedTime timeutil.TimeStamp `xorm:"created"`
CreatedBy int64
UpdatedTime timeutil.TimeStamp `xorm:"updated"`
UpdatedBy int64
}

func (r ResourceQueue) ConvertToRes() *ResourceQueueRes {
return &ResourceQueueRes{
ID: r.ID,
QueueCode: r.QueueCode,
Cluster: r.Cluster,
AiCenterCode: r.AiCenterCode,
AiCenterName: r.AiCenterName,
ComputeResource: r.ComputeResource,
AccCardType: r.AccCardType,
CardsTotalNum: r.CardsTotalNum,
UpdatedTime: r.UpdatedTime,
Remark: r.Remark,
}
}

type ResourceQueueReq struct {
QueueCode string
Cluster string `binding:"Required"`
AiCenterCode string
ComputeResource string `binding:"Required"`
AccCardType string `binding:"Required"`
CardsTotalNum int
CreatorId int64
IsAutomaticSync bool
Remark string
}

func (r ResourceQueueReq) ToDTO() ResourceQueue {
q := ResourceQueue{
QueueCode: r.QueueCode,
Cluster: r.Cluster,
AiCenterCode: r.AiCenterCode,
ComputeResource: strings.ToUpper(r.ComputeResource),
AccCardType: strings.ToUpper(r.AccCardType),
CardsTotalNum: r.CardsTotalNum,
IsAutomaticSync: r.IsAutomaticSync,
Remark: r.Remark,
CreatedBy: r.CreatorId,
UpdatedBy: r.CreatorId,
}
if r.Cluster == OpenICluster {
if r.AiCenterCode == AICenterOfCloudBrainOne {
q.AiCenterName = "云脑一"
} else if r.AiCenterCode == AICenterOfCloudBrainTwo {
q.AiCenterName = "云脑二"
}
}
return q
}

type SearchResourceQueueOptions struct {
ListOptions
Cluster string
AiCenterCode string
ComputeResource string
AccCardType string
}

type ResourceQueueListRes struct {
TotalSize int64
List []*ResourceQueueRes
}

type ResourceQueueCodesRes struct {
ID int64
QueueCode string
Cluster string
AiCenterCode string
AiCenterName string
}

func (ResourceQueueCodesRes) TableName() string {
return "resource_queue"
}

type ResourceAiCenterRes struct {
AiCenterCode string
AiCenterName string
}

type GetQueueCodesOptions struct {
Cluster string
}

func NewResourceQueueListRes(totalSize int64, list []ResourceQueue) *ResourceQueueListRes {
resList := make([]*ResourceQueueRes, len(list))
for i, v := range list {
resList[i] = v.ConvertToRes()
}
return &ResourceQueueListRes{
TotalSize: totalSize,
List: resList,
}
}

type ResourceQueueRes struct {
ID int64
QueueCode string
Cluster string
AiCenterCode string
AiCenterName string
ComputeResource string
AccCardType string
CardsTotalNum int
UpdatedTime timeutil.TimeStamp
Remark string
}

func InsertResourceQueue(queue ResourceQueue) (int64, error) {
return x.Insert(&queue)
}

func UpdateResourceQueueById(queueId int64, queue ResourceQueue) (int64, error) {
return x.ID(queueId).Update(&queue)
}

func SearchResourceQueue(opts SearchResourceQueueOptions) (int64, []ResourceQueue, error) {
var cond = builder.NewCond()
if opts.Page <= 0 {
opts.Page = 1
}
if opts.Cluster != "" {
cond = cond.And(builder.Eq{"cluster": opts.Cluster})
}
if opts.AiCenterCode != "" {
cond = cond.And(builder.Eq{"ai_center_code": opts.AiCenterCode})
}
if opts.ComputeResource != "" {
cond = cond.And(builder.Eq{"compute_resource": opts.ComputeResource})
}
if opts.AccCardType != "" {
cond = cond.And(builder.Eq{"acc_card_type": opts.AccCardType})
}
n, err := x.Where(cond).Unscoped().Count(&ResourceQueue{})
if err != nil {
return 0, nil, err
}

r := make([]ResourceQueue, 0)
err = x.Where(cond).Desc("id").Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).Unscoped().Find(&r)
if err != nil {
return 0, nil, err
}
return n, r, nil
}

func GetResourceQueueCodes(opts GetQueueCodesOptions) ([]*ResourceQueueCodesRes, error) {
cond := builder.NewCond()
if opts.Cluster != "" {
cond = cond.And(builder.Eq{"cluster": opts.Cluster})
}
cond = cond.And(builder.Or(builder.IsNull{"deleted_time"}, builder.Eq{"deleted_time": 0}))
r := make([]*ResourceQueueCodesRes, 0)
err := x.Where(cond).OrderBy("cluster desc,ai_center_code asc").Find(&r)
if err != nil {
return nil, err
}
return r, nil
}

func GetResourceQueue(r *ResourceQueue) (*ResourceQueue, error) {
has, err := x.Get(r)
if err != nil {
return nil, err
} else if !has {
return nil, nil
}
return r, nil
}

func ParseComputeResourceFormGrampus(grampusDeviceKind string) string {
t := strings.Split(grampusDeviceKind, "/")
if len(t) < 2 {
return ""
}
return strings.ToUpper(t[1])
}

type MemSize struct {
Sizes []string
Hex int
}

var memSize = MemSize{Sizes: []string{"K", "M", "G", "T", "P", "E"}, Hex: 1000}
var iMemSize = MemSize{Sizes: []string{"Ki", "Mi", "Gi", "Ti", "Pi", "Ei"}, Hex: 1024}

func MatchMemSize(memSize MemSize, val string) (int, float32, error) {
for i, v := range memSize.Sizes {
if strings.HasSuffix(val, v) {
s := strings.TrimSuffix(val, v)
f, err := strconv.ParseFloat(s, 32)
if err != nil {
return 0, 0, err
}
return i, float32(f), nil
}
}
return -1, 0, nil
}

//TransferMemSize transfer oldValue format from old index to new index
//eg: memSize.Sizes = []string{"M", "G", "T", "P", "E"}, oldValue = 10 , oldIndex = 1 , newIndex = 0. it means transfer 10G to 10000M
//so it returns 10000
func TransferMemSize(memSize MemSize, oldValue float32, oldIndex int, newIndex int) float32 {
diff := oldIndex - newIndex
r := oldValue
if diff > 0 {
r = oldValue * float32(diff) * float32(memSize.Hex)
} else if diff < 0 {
r = oldValue / float32(-1*diff) / float32(memSize.Hex)
}
return r
}

//ParseMemSize find the memSize which matches value's format,and parse the number from value
func ParseMemSize(value string, memSize MemSize, newIndex int) (bool, float32, error) {
index, r, err := MatchMemSize(memSize, value)
if err != nil {
return false, 0, err
}
if index < 0 {
return false, 0, nil
}
return true, TransferMemSize(memSize, r, index, newIndex), nil
}

func ParseMemSizeFromGrampus(grampusMemSize string) (float32, error) {
if grampusMemSize == "" {
return 0, nil
}
memflag, memResult, err := ParseMemSize(grampusMemSize, memSize, 2)
if err != nil {
return 0, err
}
if memflag {
return memResult, nil
}

iMemFlag, imemResult, err := ParseMemSize(grampusMemSize, iMemSize, 2)
if err != nil {
return 0, err
}
if iMemFlag {
return imemResult, nil
}
return 0, errors.New("grampus memSize format error")
}

func SyncGrampusQueues(updateList []ResourceQueue, insertList []ResourceQueue, existIds []int64) error {
sess := x.NewSession()
var err error
defer func() {
if err != nil {
sess.Rollback()
}
sess.Close()
}()

//delete queues that no longer exists
deleteQueueIds := make([]int64, 0)
queueCond := builder.NewCond()
queueCond = queueCond.And(builder.NotIn("resource_queue.id", existIds)).And(builder.Eq{"resource_queue.cluster": C2NetCluster})
if err := sess.Cols("resource_queue.id").Table("resource_queue").
Where(queueCond).Find(&deleteQueueIds); err != nil {
return err
}

if len(deleteQueueIds) > 0 {
if _, err = sess.In("id", deleteQueueIds).Update(&ResourceQueue{Remark: "自动同步时被下架"}); err != nil {
return err
}
if _, err = sess.In("id", deleteQueueIds).Delete(&ResourceQueue{}); err != nil {
return err
}

//delete specs and scene that no longer exists
deleteSpcIds := make([]int64, 0)
if err := sess.Cols("resource_specification.id").Table("resource_specification").
In("queue_id", deleteQueueIds).Find(&deleteSpcIds); err != nil {
return err
}
if len(deleteSpcIds) > 0 {
if _, err = sess.In("id", deleteSpcIds).Update(&ResourceSpecification{Status: SpecOffShelf}); err != nil {
return err
}
if _, err = sess.In("spec_id", deleteSpcIds).Delete(&ResourceSceneSpec{}); err != nil {
return err
}
}

}

//update exists specs
if len(updateList) > 0 {
for _, v := range updateList {
if _, err = sess.ID(v.ID).Update(&v); err != nil {
return err
}
}

}

//insert new specs
if len(insertList) > 0 {
if _, err = sess.Insert(insertList); err != nil {
return err
}
}

return sess.Commit()
}

func GetResourceAiCenters() ([]ResourceAiCenterRes, error) {
r := make([]ResourceAiCenterRes, 0)

err := x.SQL("SELECT t.ai_center_code, t.ai_center_name FROM (SELECT DISTINCT ai_center_code, ai_center_name,cluster FROM resource_queue WHERE (deleted_time IS NULL OR deleted_time=0)) t ORDER BY cluster desc,ai_center_code asc").Find(&r)
if err != nil {
return nil, err
}
return r, nil
}

+ 329
- 0
models/resource_scene.go View File

@@ -0,0 +1,329 @@
package models

import (
"code.gitea.io/gitea/modules/timeutil"
"errors"
"xorm.io/builder"
)

const (
Exclusive = iota + 1
NotExclusive
)

type ResourceScene struct {
ID int64 `xorm:"pk autoincr"`
SceneName string
JobType string
IsExclusive bool
ExclusiveOrg string
CreatedTime timeutil.TimeStamp `xorm:"created"`
CreatedBy int64
UpdatedTime timeutil.TimeStamp `xorm:"updated"`
UpdatedBy int64
DeleteTime timeutil.TimeStamp `xorm:"deleted"`
DeletedBy int64
}

type ResourceSceneSpec struct {
ID int64 `xorm:"pk autoincr"`
SceneId int64 `xorm:"unique(idx_scene_spec)"`
SpecId int64 `xorm:"unique(idx_scene_spec)"`
CreatedTime timeutil.TimeStamp `xorm:"created"`
}

type ResourceSceneReq struct {
ID int64
SceneName string
JobType string
IsExclusive bool
ExclusiveOrg string
CreatorId int64
SpecIds []int64
}

type SearchResourceSceneOptions struct {
ListOptions
JobType string
IsExclusive int
AiCenterCode string
QueueId int64
}

type ResourceSceneListRes struct {
TotalSize int64
List []ResourceSceneRes
}

func NewResourceSceneListRes(totalSize int64, list []ResourceSceneRes) *ResourceSceneListRes {
return &ResourceSceneListRes{
TotalSize: totalSize,
List: list,
}
}

type ResourceSceneRes struct {
ID int64
SceneName string
JobType JobType
IsExclusive bool
ExclusiveOrg string
Specs []ResourceSpecWithSceneId
}

func (ResourceSceneRes) TableName() string {
return "resource_scene"
}

type ResourceSceneBriefRes struct {
ID int64
SceneName string
}

func (ResourceSceneBriefRes) TableName() string {
return "resource_scene"
}

type ResourceSpecWithSceneId struct {
ID int64
SourceSpecId string
AccCardsNum int
CpuCores int
MemGiB float32
GPUMemGiB float32
ShareMemGiB float32
UnitPrice int
Status int
UpdatedTime timeutil.TimeStamp
SceneId int64
//queue
Cluster string
AiCenterCode string
AiCenterName string
QueueCode string
QueueId int64
ComputeResource string
AccCardType string
}

func (ResourceSpecWithSceneId) TableName() string {
return "resource_specification"
}

func InsertResourceScene(r ResourceSceneReq) error {
sess := x.NewSession()
defer sess.Close()

//check
specs := make([]ResourceSpecification, 0)
cond := builder.In("id", r.SpecIds).And(builder.Eq{"status": SpecOnShelf})
if err := sess.Where(cond).Find(&specs); err != nil {
return err
}
if len(specs) < len(r.SpecIds) {
return errors.New("specIds not correct")
}

rs := ResourceScene{
SceneName: r.SceneName,
JobType: r.JobType,
IsExclusive: r.IsExclusive,
ExclusiveOrg: r.ExclusiveOrg,
CreatedBy: r.CreatorId,
UpdatedBy: r.CreatorId,
}
_, err := sess.InsertOne(&rs)
if err != nil {
sess.Rollback()
return err
}

if len(r.SpecIds) == 0 {
return sess.Commit()
}
rss := make([]ResourceSceneSpec, len(r.SpecIds))
for i, v := range r.SpecIds {
rss[i] = ResourceSceneSpec{
SceneId: rs.ID,
SpecId: v,
}
}

_, err = sess.Insert(&rss)
if err != nil {
sess.Rollback()
return err
}

return sess.Commit()
}

func UpdateResourceScene(r ResourceSceneReq) error {
sess := x.NewSession()
var err error
defer func() {
if err != nil {
sess.Rollback()
}
sess.Close()
}()

// find old scene
old := ResourceScene{}
if has, _ := sess.ID(r.ID).Get(&old); !has {
return errors.New("ResourceScene not exist")
}
//check specification
specs := make([]ResourceSpecification, 0)
cond := builder.In("id", r.SpecIds).And(builder.Eq{"status": SpecOnShelf})
if err := sess.Where(cond).Find(&specs); err != nil {
return err
}
if len(specs) < len(r.SpecIds) {
return errors.New("specIds not correct")
}

//update scene
rs := ResourceScene{
SceneName: r.SceneName,
IsExclusive: r.IsExclusive,
ExclusiveOrg: r.ExclusiveOrg,
}
if _, err = sess.ID(r.ID).UseBool("is_exclusive").Update(&rs); err != nil {
return err
}

//delete scene spec relation
if _, err = sess.Where("scene_id = ? ", r.ID).Delete(&ResourceSceneSpec{}); err != nil {
sess.Rollback()
return err
}

if len(r.SpecIds) == 0 {
return sess.Commit()
}
//build new scene spec relation
rss := make([]ResourceSceneSpec, len(r.SpecIds))
for i, v := range r.SpecIds {
rss[i] = ResourceSceneSpec{
SceneId: r.ID,
SpecId: v,
}
}
if _, err = sess.Insert(&rss); err != nil {
sess.Rollback()
return err
}

return sess.Commit()
}

func DeleteResourceScene(sceneId int64) error {
sess := x.NewSession()
var err error
defer func() {
if err != nil {
sess.Rollback()
}
sess.Close()
}()

if _, err = sess.ID(sceneId).Delete(&ResourceScene{}); err != nil {
return err
}
if _, err = sess.Where("scene_id = ? ", sceneId).Delete(&ResourceSceneSpec{}); err != nil {
return err
}
return sess.Commit()
}

func SearchResourceScene(opts SearchResourceSceneOptions) (int64, []ResourceSceneRes, error) {
var cond = builder.NewCond()
if opts.Page <= 0 {
opts.Page = 1
}
if opts.JobType != "" {
cond = cond.And(builder.Eq{"resource_scene.job_type": opts.JobType})
}
if opts.IsExclusive == Exclusive {
cond = cond.And(builder.Eq{"resource_scene.is_exclusive": 1})
} else if opts.IsExclusive == NotExclusive {
cond = cond.And(builder.Eq{"resource_scene.is_exclusive": 0})
}
if opts.AiCenterCode != "" {
cond = cond.And(builder.Eq{"resource_queue.ai_center_code": opts.AiCenterCode})
}
if opts.QueueId > 0 {
cond = cond.And(builder.Eq{"resource_queue.id": opts.QueueId})
}
cond = cond.And(builder.NewCond().Or(builder.Eq{"resource_scene.delete_time": 0}).Or(builder.IsNull{"resource_scene.delete_time"}))
cols := []string{"resource_scene.id", "resource_scene.scene_name", "resource_scene.job_type", "resource_scene.is_exclusive",
"resource_scene.exclusive_org"}
count, err := x.Where(cond).
Distinct("resource_scene.id").
Join("INNER", "resource_scene_spec", "resource_scene_spec.scene_id = resource_scene.id").
Join("INNER", "resource_specification", "resource_specification.id = resource_scene_spec.spec_id").
Join("INNER", "resource_queue", "resource_queue.id = resource_specification.queue_id").
Count(&ResourceSceneRes{})
if err != nil {
return 0, nil, err
}

r := make([]ResourceSceneRes, 0)
if err = x.Where(cond).Distinct(cols...).
Join("INNER", "resource_scene_spec", "resource_scene_spec.scene_id = resource_scene.id").
Join("INNER", "resource_specification", "resource_specification.id = resource_scene_spec.spec_id").
Join("INNER", "resource_queue", "resource_queue.id = resource_specification.queue_id").
Desc("resource_scene.id").
Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).
Find(&r); err != nil {
return 0, nil, err
}

if len(r) == 0 {
return 0, r, err
}
//find related specs
sceneIds := make([]int64, 0, len(r))
for _, v := range r {
sceneIds = append(sceneIds, v.ID)
}

specs := make([]ResourceSpecWithSceneId, 0)

if err := x.Cols("resource_specification.id", "resource_specification.source_spec_id",
"resource_specification.acc_cards_num", "resource_specification.cpu_cores",
"resource_specification.mem_gi_b", "resource_specification.gpu_mem_gi_b",
"resource_specification.share_mem_gi_b", "resource_specification.unit_price",
"resource_specification.status", "resource_specification.updated_time",
"resource_scene_spec.scene_id", "resource_queue.cluster",
"resource_queue.ai_center_code", "resource_queue.acc_card_type",
"resource_queue.id as queue_id", "resource_queue.compute_resource",
"resource_queue.queue_code", "resource_queue.ai_center_name",
).In("resource_scene_spec.scene_id", sceneIds).
Join("INNER", "resource_scene_spec", "resource_scene_spec.spec_id = resource_specification.id").
Join("INNER", "resource_queue", "resource_queue.ID = resource_specification.queue_id").
OrderBy("resource_specification.acc_cards_num").
Find(&specs); err != nil {
return 0, nil, err
}

specsMap := make(map[int64][]ResourceSpecWithSceneId, 0)
for _, v := range specs {
if _, ok := specsMap[v.SceneId]; !ok {
specsMap[v.SceneId] = []ResourceSpecWithSceneId{v}
} else {
specsMap[v.SceneId] = append(specsMap[v.SceneId], v)
}
}

for i, v := range r {
s := specsMap[v.ID]
if s == nil {
s = make([]ResourceSpecWithSceneId, 0)
}
r[i].Specs = s
}

return count, r, nil
}

+ 285
- 0
models/resource_specification.go View File

@@ -0,0 +1,285 @@
package models

import (
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/builder"
)

const (
SpecNotVerified int = iota + 1
SpecOnShelf
SpecOffShelf
)

type ResourceSpecification struct {
ID int64 `xorm:"pk autoincr"`
QueueId int64 `xorm:"INDEX"`
SourceSpecId string `xorm:"INDEX"`
AccCardsNum int
CpuCores int
MemGiB float32
GPUMemGiB float32
ShareMemGiB float32
UnitPrice int
Status int
IsAutomaticSync bool
CreatedTime timeutil.TimeStamp `xorm:"created"`
CreatedBy int64
UpdatedTime timeutil.TimeStamp `xorm:"updated"`
UpdatedBy int64
}

func (r ResourceSpecification) ConvertToRes() *ResourceSpecificationRes {
return &ResourceSpecificationRes{
ID: r.ID,
SourceSpecId: r.SourceSpecId,
AccCardsNum: r.AccCardsNum,
CpuCores: r.CpuCores,
MemGiB: r.MemGiB,
ShareMemGiB: r.ShareMemGiB,
GPUMemGiB: r.GPUMemGiB,
UnitPrice: r.UnitPrice,
Status: r.Status,
UpdatedTime: r.UpdatedTime,
}
}

type ResourceSpecificationReq struct {
QueueId int64 `binding:"Required"`
SourceSpecId string
AccCardsNum int
CpuCores int
MemGiB float32
GPUMemGiB float32
ShareMemGiB float32
UnitPrice int
Status int
IsAutomaticSync bool
CreatorId int64
}

func (r ResourceSpecificationReq) ToDTO() ResourceSpecification {
return ResourceSpecification{
QueueId: r.QueueId,
SourceSpecId: r.SourceSpecId,
AccCardsNum: r.AccCardsNum,
CpuCores: r.CpuCores,
MemGiB: r.MemGiB,
GPUMemGiB: r.GPUMemGiB,
ShareMemGiB: r.ShareMemGiB,
UnitPrice: r.UnitPrice,
Status: r.Status,
IsAutomaticSync: r.IsAutomaticSync,
CreatedBy: r.CreatorId,
UpdatedBy: r.CreatorId,
}
}

type SearchResourceSpecificationOptions struct {
ListOptions
QueueId int64
Status int
Cluster string
}

type SearchResourceBriefSpecificationOptions struct {
QueueId int64
Cluster string
}

type ResourceSpecAndQueueListRes struct {
TotalSize int64
List []*ResourceSpecAndQueueRes
}

func NewResourceSpecAndQueueListRes(totalSize int64, list []ResourceSpecAndQueue) *ResourceSpecAndQueueListRes {
resList := make([]*ResourceSpecAndQueueRes, len(list))
for i, v := range list {
resList[i] = v.ConvertToRes()
}
return &ResourceSpecAndQueueListRes{
TotalSize: totalSize,
List: resList,
}
}

type ResourceSpecificationRes struct {
ID int64
SourceSpecId string
AccCardsNum int
CpuCores int
MemGiB float32
GPUMemGiB float32
ShareMemGiB float32
UnitPrice int
Status int
UpdatedTime timeutil.TimeStamp
}

func (ResourceSpecificationRes) TableName() string {
return "resource_specification"
}

type ResourceSpecAndQueueRes struct {
Spec *ResourceSpecificationRes
Queue *ResourceQueueRes
}

type ResourceSpecAndQueue struct {
ResourceSpecification `xorm:"extends"`
ResourceQueue `xorm:"extends"`
}

func (*ResourceSpecAndQueue) TableName() string {
return "resource_specification"
}

func (r ResourceSpecAndQueue) ConvertToRes() *ResourceSpecAndQueueRes {
return &ResourceSpecAndQueueRes{
Spec: r.ResourceSpecification.ConvertToRes(),
Queue: r.ResourceQueue.ConvertToRes(),
}
}

func InsertResourceSpecification(r ResourceSpecification) (int64, error) {
return x.Insert(&r)
}

func UpdateResourceSpecificationById(queueId int64, spec ResourceSpecification) (int64, error) {
return x.ID(queueId).Update(&spec)
}
func UpdateSpecUnitPriceById(id int64, unitPrice int) error {
_, err := x.Exec("update resource_specification set unit_price = ? ,updated_time = ? where id = ?", unitPrice, timeutil.TimeStampNow(), id)
return err
}

func SearchResourceSpecification(opts SearchResourceSpecificationOptions) (int64, []ResourceSpecAndQueue, error) {
var cond = builder.NewCond()
if opts.Page <= 0 {
opts.Page = 1
}
if opts.QueueId > 0 {
cond = cond.And(builder.Eq{"resource_specification.queue_id": opts.QueueId})
}
if opts.Status > 0 {
cond = cond.And(builder.Eq{"resource_specification.status": opts.Status})
}
if opts.Cluster != "" {
cond = cond.And(builder.Eq{"resource_queue.cluster": opts.Cluster})
}
//cond = cond.And(builder.Or(builder.Eq{"resource_queue.deleted_time": 0}).Or(builder.IsNull{"resource_queue.deleted_time"}))
n, err := x.Where(cond).Join("INNER", "resource_queue", "resource_queue.ID = resource_specification.queue_id").
Unscoped().Count(&ResourceSpecAndQueue{})
if err != nil {
return 0, nil, err
}

r := make([]ResourceSpecAndQueue, 0)
err = x.Where(cond).
Join("INNER", "resource_queue", "resource_queue.ID = resource_specification.queue_id").
Desc("resource_specification.id").
Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).
Unscoped().Find(&r)
if err != nil {
return 0, nil, err
}
return n, r, nil
}

func GetSpecScenes(specId int64) ([]ResourceSceneBriefRes, error) {
r := make([]ResourceSceneBriefRes, 0)
err := x.Where("resource_scene_spec.spec_id = ?", specId).
Join("INNER", "resource_scene_spec", "resource_scene_spec.scene_id = resource_scene.id").
Find(&r)
if err != nil {
return nil, err
}
return r, nil
}

func ResourceSpecOnShelf(id int64, unitPrice int) error {
_, err := x.Exec("update resource_specification set unit_price = ?,updated_time = ?,status = ? where id = ?", unitPrice, timeutil.TimeStampNow(), SpecOnShelf, id)
return err
}

func ResourceSpecOffShelf(id int64) (int64, error) {
sess := x.NewSession()
var err error
defer func() {
if err != nil {
sess.Rollback()
}
sess.Close()
}()
//delete scene spec relation
if _, err = sess.Where("spec_id = ?", id).Delete(&ResourceSceneSpec{}); err != nil {
return 0, err
}

param := ResourceSpecification{
Status: SpecOffShelf,
}
n, err := sess.Where("id = ? and status = ?", id, SpecOnShelf).Update(&param)
if err != nil {
return 0, err
}
sess.Commit()
return n, err
}

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

func SyncGrampusSpecs(updateList []ResourceSpecification, insertList []ResourceSpecification, existIds []int64) error {
sess := x.NewSession()
var err error
defer func() {
if err != nil {
sess.Rollback()
}
sess.Close()
}()
//delete specs and scene that no longer exists
deleteIds := make([]int64, 0)
cond := builder.NewCond()
cond = cond.And(builder.NotIn("resource_specification.id", existIds)).And(builder.Eq{"resource_queue.cluster": C2NetCluster})
if err := sess.Cols("resource_specification.id").Table("resource_specification").
Where(cond).Join("INNER", "resource_queue", "resource_queue.id = resource_specification.queue_id").
Find(&deleteIds); err != nil {
return err
}
if len(deleteIds) > 0 {
if _, err = sess.In("id", deleteIds).Update(&ResourceSpecification{Status: SpecOffShelf}); err != nil {
return err
}
if _, err = sess.In("spec_id", deleteIds).Delete(&ResourceSceneSpec{}); err != nil {
return err
}
}

//update exists specs
if len(updateList) > 0 {
for _, v := range updateList {
if _, err = sess.ID(v.ID).Update(&v); err != nil {
return err
}
}

}

//insert new specs
if len(insertList) > 0 {
if _, err = sess.Insert(insertList); err != nil {
return err
}
}

return sess.Commit()
}

+ 13
- 0
modules/cron/tasks_basic.go View File

@@ -5,6 +5,7 @@
package cron

import (
"code.gitea.io/gitea/services/cloudbrain/resource"
"code.gitea.io/gitea/modules/modelarts"
"context"
"time"
@@ -208,6 +209,17 @@ func registerSyncCloudbrainStatus() {
})
}

func registerSyncResourceSpecs() {
RegisterTaskFatal("sync_grampus_specs", &BaseConfig{
Enabled: true,
RunAtStart: true,
Schedule: "0 0 1 * * ?",
}, func(ctx context.Context, _ *models.User, _ Config) error {
resource.SyncGrampusQueueAndSpecs()
return nil
})
}

func registerSyncModelArtsTempJobs() {
RegisterTaskFatal("sync_model_arts_temp_jobs", &BaseConfig{
Enabled: true,
@@ -239,5 +251,6 @@ func initBasicTasks() {

registerSyncCloudbrainStatus()
registerHandleOrgStatistic()
registerSyncResourceSpecs()
registerSyncModelArtsTempJobs()
}

+ 33
- 0
modules/grampus/resty.go View File

@@ -23,6 +23,7 @@ const (
urlGetToken = urlOpenApiV1 + "token"
urlTrainJob = urlOpenApiV1 + "trainjob"
urlGetResourceSpecs = urlOpenApiV1 + "resourcespec"
urlGetAiCenter = urlOpenApiV1 + "sharescreen/aicenter"
urlGetImages = urlOpenApiV1 + "image"

errorIllegalToken = 1005
@@ -275,3 +276,35 @@ sendjob:

return &result, nil
}

func GetAiCenters(pageIndex, pageSize int) (*models.GetGrampusAiCentersResult, error) {
checkSetting()
client := getRestyClient()
var result models.GetGrampusAiCentersResult

retry := 0

sendjob:
_, err := client.R().
SetAuthToken(TOKEN).
SetResult(&result).
Get(HOST + urlGetAiCenter + "?pageIndex=" + fmt.Sprint(pageIndex) + "&pageSize=" + fmt.Sprint(pageSize))

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

if result.ErrorCode == errorIllegalToken && retry < 1 {
retry++
log.Info("retry get token")
_ = getToken()
goto sendjob
}

if result.ErrorCode != 0 {
log.Error("GetAiCenters failed(%d): %s", result.ErrorCode, result.ErrorMsg)
return &result, fmt.Errorf("GetAiCenters failed(%d): %s", result.ErrorCode, result.ErrorMsg)
}

return &result, nil
}

+ 70
- 11
options/locale/locale_en-US.ini View File

@@ -827,6 +827,7 @@ description_format_err=Description's length can be up to %s characters long.
create_dataset = Create Dataset
download_url=Download Url
download_oper=Operation
download_copy=Copy URL
create_dataset_fail=Failed to create dataset.
query_dataset_fail=Failed to query dataset.
edit_attachment_fail=Failed to update description.
@@ -922,9 +923,10 @@ select_task = Select Research Direction/Application Area
dataset_name_tooltips = Please enter letters, numbers, _ and - up to 100 characters.
dataset_no_create = No dataset has been created yet
dataset_explain = Dataset: CloudBrain I provides CPU/GPU resources, Cloudbrain II provides Ascend NPU resources, and the data set used for debugging also needs to be uploaded to the corresponding environment;
dataset_instructions_for_use = Instructions for use: You can refer to Qizhi AI Collaboration Platform
dataset_camp_course = Newcomer Training Camp Course;
dataset_instructions_for_use = Instructions for use: You can refer to Openi AI Collaboration Platform
dataset_camp_course = OpenI_Learning;
dataset_upload = Upload
dataset_upload_status= Upload Status
dataset_file_name = File Name
dataset_available_clusters = Available Clusters
dataset_upload_time = Upload Time
@@ -953,9 +955,12 @@ unzip_status=Unzip Status
collection_num=Collection Nums
current_dataset=Current Dataset
linked_dataset=Linked Dataset
unfavorite=Unfavorite
favorite=Favorite
unfavorite=Unlike
favorite=Like
disassociate=Disassociate
benchmark_dataset_tip=Note: first use the dataset function to upload the model, and then select the model from the dataset list.
file_deleted=The file has been deleted

[repo]
owner = Owner
repo_name = Repository Name
@@ -1136,7 +1141,7 @@ modelarts.train_job.compute_node=Compute Node
modelarts.create_model = Create Model
modelarts.model_label=Model Label
modelarts.infer_dataset = Inference Dataset
modelarts.train_job.label_place=Input labels, multiple labels are separated by spaces

modelarts.train_job.basic_info=Basic Info
modelarts.train_job.job_status=Job Status
@@ -1205,7 +1210,7 @@ modelarts.infer_job_model_file = Model File
modelarts.infer_job = Inference Job
modelarts.infer_job.model_version = Model/Version
modelarts.infer_job.select_model = Select Model
modelarts.infer_job.boot_file_helper=The startup file is the entry file for your program execution and must end in.py.Such as inference.py, main.py, example/inference. Py, case/main.py.
modelarts.infer_job.boot_file_helper=The startup file is the entry file for your program execution and must end in.py.Such as inference.py, main.py, example/inference.py, case/main.py.
modelarts.infer_job.tooltip = The model has been deleted and cannot be viewed.
modelarts.download_log=Download log file

@@ -1217,7 +1222,10 @@ model_Evaluation_not_created = Model evaluation has not been created
repo_not_initialized = Code version: You have not initialized the code repository, please <a href="%s"> initialized </a> first ;
debug_task_running_limit =Running time: no more than 4 hours, it will automatically stop if it exceeds 4 hours;
dataset_desc = Dataset: Cloud Brain 1 provides CPU/GPU,Cloud Brain 2 provides Ascend NPU.And dataset also needs to be uploaded to the corresponding environment;
platform_instructions = Instructions for use: You can refer to the <a href="https://git.openi.org.cn/zeizei/OpenI_Learning">Xiaobai training camp </a> course of Qizhi AI collaboration platform.
platform_instructions = Instructions for use: You can refer to the <a href="https://git.openi.org.cn/zeizei/OpenI_Learning"> OpenI_Learning </a> course of Qizhi AI collaboration platform.
platform_instructions1 = Instructions for use: You can refer to the
platform_instructions2 = OpenI_Learning
platform_instructions3 = course of Openi AI collaboration platform.
model_not_exist = Model file: You do not have a model file yet, please generate and <a href="%s/modelmanage/show_model">export the model</a> through the <a href="%s/modelarts/train-job">training task</a> first ;
benchmark_leaderboards = Benchmark leaderboards

@@ -1228,7 +1236,7 @@ model.manage.version = Version
model.manage.label = Label
model.manage.size = Size
model.manage.create_time = Create Time
model.manage.Description = Description
model.manage.description = Description
model.manage.Accuracy = Accuracy
model.manage.F1 = F1
model.manage.Precision = Precision
@@ -1240,6 +1248,49 @@ model.convert=Model Transformation
model.list=Model List
model.manage.create_new_convert_task=Create Model Transformation Task

model.manage.notcreatemodel=No model has been created
model.manage.init1=Code version: You have not initialized the code repository, please
model.manage.init2=initialized first ;
model.manage.createtrainjob_tip=Training task: you haven't created a training task, please create it first
model.manage.createtrainjob=Training task.
model.manage.delete=Delete Model
model.manage.delete_confirm=Are you sure to delete this model? Once this model is deleted, it cannot be restored.
model.manage.select.trainjob=Select train task
model.manage.select.version=Select version
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.baseinfo=Base Information
modelconvert.notcreate=No model conversion task has been created.
modelconvert.importfirst1=Please import the
modelconvert.importfirst2=model
modelconvert.importfirst3=first, then converts it.
modelconvert.download=Download
modelconvert.taskname=Task name
modelconvert.modelname=Model name
modelconvert.selectmodel=Select model
modelconvert.modelversion=Model version
modelconvert.selectversion=Select version
modelconvert.selectmodelfile=Select model file
modelconvert.taskstatus=Status
modelconvert.srcengine=Source model engine
modelconvert.outputformat=Output format
modelconvert.createtime=Created time
modelconvert.inputdataformat=Input data format
modelconvert.inputshape=Input tensor shape
modelconvert.inputshapetip=For example: 1,1,32,32, corresponding to the input data format.
modelconvert.netoutputdata=Network output data type
modelconvert.taskdesc=Task description
modelconvert.newtask=New
modelconvert.createtask=Create model transformation task
modelconvert.taskurlname=Model transformation task
log_scroll_start=Scroll to top
log_scroll_end=Scroll to bottom
modelconvert.tasknameempty=Please enter a task name.
modelconvert.inputshapeerror=Format input error, please input such as: 1,1,32,32, corresponding to the input data format.

modelconvert.manage.create_error1=A model transformation task with the same name already exists.
modelconvert.manage.create_error2=Only one running model transformation task can be created.
modelconvert.manage.model_not_exist=The model does not exist.
@@ -2336,7 +2387,7 @@ topic.count_prompt = You can not select more than 25 topics
topic.format_prompt = Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.
imagetopic.format_prompt = Topics can be up to 35 characters long.
use_repo_agreement=I promise that the content of this warehouse does not violate any national laws and regulations. During the use of the warehouse, I will abide by the OpenI community management regulations and platform usage rules, and will not conduct malicious attacks, mining, or any other illegal or disruptive platform order. Information release and related behaviors. For more information please refer to
openi_use_agreement=OpenI Qizhi Community Platform Use Agreement.
openi_use_agreement=OpenI Openi Community Platform Use Agreement.
[org]
org_name_holder = Organization Name
org_full_name_holder = Organization Full Name
@@ -2977,6 +3028,13 @@ notices.desc = Description
notices.op = Op.
notices.delete_success = The system notices have been deleted.

user_management = User Management
resource_management = Resource Management
resource_pool = Resource Pool(queue)
resource_price = Resource Price
application_scenario = Application Scenario
system_configuration = System Configuration

[action]
create_repo = created repository <a href="%s">%s</a>
rename_repo = renamed repository from <code>%[1]s</code> to <a href="%[2]s">%[3]s</a>
@@ -3128,11 +3186,11 @@ specification = specification
select_specification = select specification
description = description
wrong_specification=You cannot use this specification, please choose another item.
resource_use=Resource Occupancy

job_name_rule = Please enter letters, numbers, _ and - up to 64 characters and cannot end with a dash (-).
train_dataset_path_rule = The dataset location is stored in the environment variable <strong style="color:#010101">data_url</strong>, and the output path is stored in the environment variable <strong style="color:#010101">train_url</strong>.
infer_dataset_path_rule = The dataset location is stored in the environment variable <strong style="color:#010101">data_url</strong>, and the output path is stored in the environment variable <strong style="color:#010101">train_url</strong>.
infer_dataset_path_rule = The dataset location is stored in the environment variable <strong style="color:#010101">data_url</strong>, and the output path is stored in the environment variable <strong style="color:#010101">result_url</strong>.
view_sample = View sample
inference_output_path_rule = The inference output path is stored in the environment variable result_url.
model_file_path_rule=The model file location is stored in the environment variable ckpt_url
@@ -3166,3 +3224,4 @@ load_code_failed=Fail to load code, please check if the right branch is selected

error.dataset_select = dataset select error:the count exceed the limit or has same name
new_train_gpu_tooltips = The code is storaged in <strong style="color:#010101">%s</strong>, the dataset is storaged in <strong style="color:#010101">%s</strong>, and please put your model into <strong style="color:#010101">%s</strong> then you can download it online
new_infer_gpu_tooltips = The dataset is stored in <strong style="color:#010101">%s</strong>, the model file is stored in <strong style="color:#010101">%s</strong>, please store the inference output in <strong style="color:#010101">%s</strong> for subsequent downloads.

+ 62
- 2
options/locale/locale_zh-CN.ini View File

@@ -838,6 +838,7 @@ reference_dataset_fail=关联数据集失败,请稍后再试。
cancel_reference_dataset_fail=取消关联数据集失败,请稍后再试。

download_url=数据集下载地址
download_copy=复制链接
download_oper=操作
show_dataset=数据集
edit_dataset=编辑数据集
@@ -931,6 +932,7 @@ dataset_explain = 数据集:云脑1提供 CPU / GPU 资源,云脑2提供 Asc
dataset_instructions_for_use = 使用说明:可以参考启智AI协作平台
dataset_camp_course = 小白训练营课程
dataset_upload = 上传
dataset_upload_status = 上传状态
dataset_file_name = 文件名称
dataset_available_clusters = 可用集群
dataset_upload_time = 上传时间
@@ -962,6 +964,8 @@ linked_dataset=关联数据集
unfavorite=取消收藏
favorite=收藏
disassociate=取消关联
benchmark_dataset_tip=说明:先使用数据集功能上传模型,然后从数据集列表选模型。
file_deleted=文件已经被删除

[repo]
owner=拥有者
@@ -1232,6 +1236,10 @@ repo_not_initialized = 代码版本:您还没有初始化代码仓库,请先
debug_task_running_limit = 运行时长:最长不超过4个小时,超过4个小时将自动停止;
dataset_desc = 数据集:云脑1提供 CPU / GPU 资源,云脑2提供 Ascend NPU 资源,调试使用的数据集也需要上传到对应的环境;
platform_instructions = 使用说明:可以参考启智AI协作平台<a href="https://git.openi.org.cn/zeizei/OpenI_Learning">小白训练营课程</a>。
platform_instructions1 = 使用说明:可以参考启智AI协作平台
platform_instructions2 = 小白训练营课程
platform_instructions3 = 。

model_not_exist = 模型文件:您还没有模型文件,请先通过<a href="%s/modelarts/train-job">训练任务</a>产生并 <a href="%s/modelmanage/show_model">导出模型</a> ;
benchmark_leaderboards = 基准测试排行榜

@@ -1254,6 +1262,50 @@ model.convert=模型转换任务
model.list=模型列表
model.manage.create_new_convert_task=创建模型转换任务

model.manage.notcreatemodel=未创建过模型
model.manage.init1=代码版本:您还没有初始化代码仓库,请先
model.manage.init2=创建代码版本;
model.manage.createtrainjob_tip=训练任务:您还没创建过训练任务,请先创建
model.manage.createtrainjob=训练任务。
model.manage.delete=删除模型
model.manage.delete_confirm=你确认删除该模型么?此模型一旦删除不可恢复。
model.manage.select.trainjob=选择训练任务
model.manage.select.version=选择版本
model.manage.engine=模型框架
model.manage.select.engine=选择模型框架
model.manage.modelfile=模型文件
model.manage.modellabel=模型标签
model.manage.modeldesc=模型描述
model.manage.baseinfo=基本信息
modelconvert.notcreate=未创建过模型转换任务
modelconvert.importfirst1=请您先导入
modelconvert.importfirst2=模型
modelconvert.importfirst3=,然后再对其进行转换。
modelconvert.download=下载
modelconvert.taskname=任务名称
modelconvert.modelname=模型名称
modelconvert.selectmodel=选择模型
modelconvert.modelversion=模型版本
modelconvert.selectversion=选择版本
modelconvert.selectmodelfile=选择模型文件
modelconvert.taskstatus=状态
modelconvert.srcengine=原模型框架
modelconvert.outputformat=转换后格式
modelconvert.createtime=创建时间
modelconvert.inputdataformat=输入数据格式
modelconvert.inputshape=输入张量形状
modelconvert.inputshapetip=如:1,1,32,32,与输入数据格式对应。
modelconvert.netoutputdata=网络输出数据类型
modelconvert.taskdesc=任务描述
modelconvert.newtask=新建任务
modelconvert.createtask=创建模型转换任务

modelconvert.taskurlname=模型转换任务
log_scroll_start=滚动到顶部
log_scroll_end=滚动到底部
modelconvert.tasknameempty=请输入任务名称。
modelconvert.inputshapeerror=格式输入错误,请输入如:1,1,32,32,与输入数据格式对应。

modelconvert.manage.create_error1=相同的名称模型转换任务已经存在。
modelconvert.manage.create_error2=只能创建一个正在运行的模型转换任务。
modelconvert.manage.model_not_exist=选择的模型不存在。
@@ -2993,6 +3045,13 @@ notices.desc=提示描述
notices.op=操作
notices.delete_success=系统通知已被删除。

user_management = 用户管理
resource_management = 资源管理
resource_pool = 资源池(队列)
resource_price = 资源规格单价
application_scenario = 应用场景
system_configuration = 系统配置

[action]
create_repo=创建了项目 <a href="%s">%s</a>
rename_repo=重命名项目 <code>%[1]s</code> 为 <a href="%[2]s">%[3]s</a>
@@ -3149,7 +3208,7 @@ wrong_specification=您目前不能使用这个资源规格,请选择其他资

job_name_rule = 请输入字母、数字、_和-,最长64个字符,且不能以中划线(-)结尾。
train_dataset_path_rule = 数据集位置存储在环境变量<strong style="color:#010101">data_url</strong>中,训练输出路径存储在环境变量<strong style="color:#010101">train_url</strong>中。
infer_dataset_path_rule = 数据集位置存储在环境变量<strong style="color:#010101">data_url</strong>中,推理输出路径存储在环境变量<strong style="color:#010101">train_url</strong>中。
infer_dataset_path_rule = 数据集位置存储在环境变量<strong style="color:#010101">data_url</strong>中,推理输出路径存储在环境变量<strong style="color:#010101">result_url</strong>中。
view_sample = 查看样例
inference_output_path_rule = 推理输出路径存储在环境变量result_url中。
model_file_path_rule = 模型文件位置存储在环境变量ckpt_url中。
@@ -3159,7 +3218,7 @@ delete_task = 删除任务
task_delete_confirm = 你确认删除该任务么?此任务一旦删除不可恢复。
operate_confirm = 确定操作
operate_cancel = 取消操作
resource_use=资源占用情况

gpu_num = GPU数
cpu_num = CPU数
@@ -3184,3 +3243,4 @@ load_code_failed=代码加载失败,请确认选择了正确的分支。

error.dataset_select = 数据集选择错误:数量超过限制或者有同名数据集
new_train_gpu_tooltips =训练脚本存储在<strong style="color:#010101">%s</strong>中,数据集存储在<strong style="color:#010101">%s</strong>中,训练输出请存储在<strong style="color:#010101">%s</strong>中以供后续下载。
new_infer_gpu_tooltips = 数据集存储在<strong style="color:#010101">%s</strong>中,模型文件存储在<strong style="color:#010101">%s</strong>中,推理输出请存储在<strong style="color:#010101">%s</strong>中以供后续下载。

+ 19351
- 17
package-lock.json
File diff suppressed because it is too large
View File


+ 1
- 0
package.json View File

@@ -54,6 +54,7 @@
"vue": "2.6.11",
"vue-bar-graph": "1.2.0",
"vue-calendar-heatmap": "0.8.4",
"vue-i18n": "6.1.3",
"vue-loader": "15.9.2",
"vue-router": "3.3.4",
"vue-template-compiler": "2.6.11",


+ 248
- 0
routers/admin/resources.go View File

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

import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/routers/response"
"code.gitea.io/gitea/services/cloudbrain/resource"
"net/http"
)

const (
tplResourceQueue base.TplName = "admin/resources/queue"
tplResourceSpecification base.TplName = "admin/resources/specification"
tplResourceScene base.TplName = "admin/resources/scene"
)

func GetQueuePage(ctx *context.Context) {
ctx.Data["PageIsAdmin"] = true
ctx.Data["PageIsAdminResources"] = true
ctx.Data["PageIsAdminResourcesQueue"] = true
ctx.HTML(200, tplResourceQueue)
}

func GetSpecificationPage(ctx *context.Context) {
ctx.Data["PageIsAdmin"] = true
ctx.Data["PageIsAdminResources"] = true
ctx.Data["PageIsAdminResourcesSpecification"] = true
ctx.HTML(200, tplResourceSpecification)
}

func GetScenePage(ctx *context.Context) {
ctx.Data["PageIsAdmin"] = true
ctx.Data["PageIsAdminResources"] = true
ctx.Data["PageIsAdminResourcesScene"] = true
ctx.HTML(200, tplResourceScene)
}

func GetResourceQueueList(ctx *context.Context) {
page := ctx.QueryInt("page")
cluster := ctx.Query("cluster")
aiCenterCode := ctx.Query("center")
computeResource := ctx.Query("resource")
accCardType := ctx.Query("card")
list, err := resource.GetResourceQueueList(models.SearchResourceQueueOptions{
ListOptions: models.ListOptions{Page: page, PageSize: 10},
Cluster: cluster,
AiCenterCode: aiCenterCode,
ComputeResource: computeResource,
AccCardType: accCardType,
})
if err != nil {
log.Error("GetResourceQueueList error.%v", err)
ctx.JSON(http.StatusOK, response.ServerError(err.Error()))
return
}
ctx.JSON(http.StatusOK, response.SuccessWithData(list))
}

func GetResourceQueueCodes(ctx *context.Context) {
cluster := ctx.Query("cluster")
list, err := resource.GetResourceQueueCodes(models.GetQueueCodesOptions{Cluster: cluster})
if err != nil {
log.Error("GetResourceQueueCodes error.%v", err)
ctx.JSON(http.StatusOK, response.ServerError(err.Error()))
return
}
ctx.JSON(http.StatusOK, response.SuccessWithData(list))
}

func GetResourceAiCenters(ctx *context.Context) {
list, err := resource.GetResourceAiCenters()
if err != nil {
log.Error("GetResourceAiCenters error.%v", err)
ctx.JSON(http.StatusOK, response.ServerError(err.Error()))
return
}
ctx.JSON(http.StatusOK, response.SuccessWithData(list))
}

func AddResourceQueue(ctx *context.Context, req models.ResourceQueueReq) {
req.IsAutomaticSync = false
req.CreatorId = ctx.User.ID
err := resource.AddResourceQueue(req)
if err != nil {
log.Error("AddResourceQueue error. %v", err)
ctx.JSON(http.StatusOK, response.ServerError(err.Error()))
return
}
ctx.JSON(http.StatusOK, response.Success())
}

func UpdateResourceQueue(ctx *context.Context, req models.ResourceQueueReq) {
queueId := ctx.ParamsInt64(":id")
//only CardsTotalNum permitted to change
err := resource.UpdateResourceQueue(queueId, req)
if err != nil {
log.Error("UpdateResourceQueue error. %v", err)
ctx.JSON(http.StatusOK, response.ServerError(err.Error()))
return
}
ctx.JSON(http.StatusOK, response.Success())
}

func SyncGrampusQueue(ctx *context.Context) {
err := resource.SyncGrampusQueue(ctx.User.ID)
if err != nil {
log.Error("AddResourceQueue error. %v", err)
ctx.JSON(http.StatusOK, response.ServerError(err.Error()))
return
}
ctx.JSON(http.StatusOK, response.Success())
}

func GetResourceSpecificationList(ctx *context.Context) {
page := ctx.QueryInt("page")
queue := ctx.QueryInt64("queue")
status := ctx.QueryInt("status")
cluster := ctx.Query("cluster")
list, err := resource.GetResourceSpecificationList(models.SearchResourceSpecificationOptions{
ListOptions: models.ListOptions{Page: page, PageSize: 10},
QueueId: queue,
Status: status,
Cluster: cluster,
})
if err != nil {
log.Error("GetResourceSpecificationList error.%v", err)
ctx.JSON(http.StatusOK, response.ServerError(err.Error()))
return
}
ctx.JSON(http.StatusOK, response.SuccessWithData(list))
}

func GetResourceSpecificationScenes(ctx *context.Context) {
specId := ctx.ParamsInt64(":id")
list, err := resource.GetResourceSpecificationScenes(specId)
if err != nil {
log.Error("GetResourceSpecificationScenes error.%v", err)
ctx.JSON(http.StatusOK, response.ServerError(err.Error()))
return
}
r := make(map[string]interface{})
r["List"] = list
ctx.JSON(http.StatusOK, response.SuccessWithData(r))
}

func AddResourceSpecification(ctx *context.Context, req models.ResourceSpecificationReq) {
req.IsAutomaticSync = false
req.CreatorId = ctx.User.ID
err := resource.AddResourceSpecification(ctx.User.ID, req)
if err != nil {
log.Error("AddResourceQueue error. %v", err)
ctx.JSON(http.StatusOK, response.ServerError(err.Error()))
return
}
ctx.JSON(http.StatusOK, response.Success())
}

func UpdateResourceSpecification(ctx *context.Context, req models.ResourceSpecificationReq) {
id := ctx.ParamsInt64(":id")
action := ctx.Query("action")

var err *response.BizError
switch action {
case "edit":
if req.UnitPrice < 0 {
ctx.JSON(http.StatusOK, response.ServerError("param error"))
return
}
//only UnitPrice and permitted to change
err = resource.UpdateSpecUnitPrice(ctx.User.ID, id, req.UnitPrice)
case "on-shelf":
err = resource.ResourceSpecOnShelf(ctx.User.ID, id, req.UnitPrice)
case "off-shelf":
err = resource.ResourceSpecOffShelf(ctx.User.ID, id)
}

if err != nil {
log.Error("UpdateResourceSpecification error. %v", err)
ctx.JSON(http.StatusOK, response.ResponseError(err))
return
}
ctx.JSON(http.StatusOK, response.Success())
}

func SyncGrampusSpecs(ctx *context.Context) {
err := resource.SyncGrampusSpecs(ctx.User.ID)
if err != nil {
log.Error("AddResourceQueue error. %v", err)
ctx.JSON(http.StatusOK, response.ServerError(err.Error()))
return
}
ctx.JSON(http.StatusOK, response.Success())
}

func GetResourceSceneList(ctx *context.Context) {
page := ctx.QueryInt("page")
jobType := ctx.Query("jobType")
aiCenterCode := ctx.Query("center")
queueId := ctx.QueryInt64("queue")
isExclusive := ctx.QueryInt("IsExclusive")
list, err := resource.GetResourceSceneList(models.SearchResourceSceneOptions{
ListOptions: models.ListOptions{Page: page, PageSize: 10},
JobType: jobType,
IsExclusive: isExclusive,
AiCenterCode: aiCenterCode,
QueueId: queueId,
})
if err != nil {
log.Error("GetResourceSceneList error.%v", err)
ctx.JSON(http.StatusOK, response.ServerError(err.Error()))
return
}
ctx.JSON(http.StatusOK, response.SuccessWithData(list))
}

func AddResourceScene(ctx *context.Context, req models.ResourceSceneReq) {
req.CreatorId = ctx.User.ID
err := resource.AddResourceScene(req)
if err != nil {
log.Error("AddResourceScene error. %v", err)
ctx.JSON(http.StatusOK, response.ServerError(err.Error()))
return
}
ctx.JSON(http.StatusOK, response.Success())
}

func UpdateResourceScene(ctx *context.Context, req models.ResourceSceneReq) {
id := ctx.ParamsInt64(":id")
action := ctx.Query("action")

req.ID = id
var err error
switch action {
case "edit":
err = resource.UpdateResourceScene(req)
case "delete":
err = resource.DeleteResourceScene(id)
}

if err != nil {
log.Error("UpdateResourceScene error. %v", err)
ctx.JSON(http.StatusOK, response.ServerError(err.Error()))
return
}
ctx.JSON(http.StatusOK, response.Success())
}

+ 2
- 2
routers/repo/cloudbrain.go View File

@@ -928,7 +928,7 @@ func cloudBrainShow(ctx *context.Context, tpName base.TplName, jobType models.Jo
}

}
ctx.Data["datasetDownload"] = GetCloudBrainDataSetInfo(task.Uuid, false)
ctx.Data["datasetDownload"] = GetCloudBrainDataSetInfo(task.Uuid, task.DatasetName, false)
ctx.Data["task"] = task
labelName := strings.Fields(task.LabelName)
ctx.Data["LabelName"] = labelName
@@ -2718,7 +2718,7 @@ func getTrainJobCommand(form auth.CreateCloudBrainForm) (string, error) {
}
}

command += "python /code/" + bootFile + param + " > " + cloudbrain.ModelMountPath + "/" + form.DisplayJobName + "-" + cloudbrain.LogFile
command += "python /code/" + bootFile + param + " | tee " + cloudbrain.ModelMountPath + "/" + form.DisplayJobName + "-" + cloudbrain.LogFile

return command, nil
}


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

@@ -713,7 +713,7 @@ func GrampusTrainJobShow(ctx *context.Context) {
taskList := make([]*models.Cloudbrain, 0)
taskList = append(taskList, task)
ctx.Data["version_list_task"] = taskList
ctx.Data["datasetDownload"] = GetCloudBrainDataSetInfo(task.Uuid, false)
ctx.Data["datasetDownload"] = GetCloudBrainDataSetInfo(task.Uuid, task.DatasetName, false)
ctx.Data["canDownload"] = cloudbrain.CanModifyJob(ctx, task)
ctx.Data["displayJobName"] = task.DisplayJobName



+ 56
- 29
routers/repo/modelarts.go View File

@@ -285,7 +285,7 @@ func NotebookShow(ctx *context.Context) {
datasetDownload := make([]models.DatasetDownload, 0)
if ctx.IsSigned {
if task.Uuid != "" && task.UserID == ctx.User.ID {
datasetDownload = GetCloudBrainDataSetInfo(task.Uuid, true)
datasetDownload = GetCloudBrainDataSetInfo(task.Uuid, task.DatasetName, true)
}
}
user, err := models.GetUserByID(task.UserID)
@@ -331,34 +331,43 @@ func NotebookShow(ctx *context.Context) {
ctx.HTML(200, tplModelArtsNotebookShow)
}

func GetCloudBrainDataSetInfo(uuid string, isNeedDown bool) []models.DatasetDownload {
func GetCloudBrainDataSetInfo(uuid string, datasetname string, isNeedDown bool) []models.DatasetDownload {
datasetDownload := make([]models.DatasetDownload, 0)

uuidList := strings.Split(uuid, ";")
for _, uuidStr := range uuidList {
datasetnameList := strings.Split(datasetname, ";")
for i, uuidStr := range uuidList {
name := ""
link := ""
isDelete := false
attachment, err := models.GetAttachmentByUUID(uuidStr)
if err != nil {
log.Error("GetAttachmentByUUID failed:%v", err.Error())
return datasetDownload
}
dataset, err := models.GetDatasetByID(attachment.DatasetID)
if err != nil {
log.Error("GetDatasetByID failed:%v", err.Error())
return datasetDownload
}
repo, err := models.GetRepositoryByID(dataset.RepoID)
if err != nil {
log.Error("GetRepositoryByID failed:%v", err.Error())
return datasetDownload
name = datasetnameList[i]
isDelete = true
} else {
name = attachment.Name
dataset, err := models.GetDatasetByID(attachment.DatasetID)
if err != nil {
log.Error("GetDatasetByID failed:%v", err.Error())
} else {
repo, err := models.GetRepositoryByID(dataset.RepoID)
if err != nil {
log.Error("GetRepositoryByID failed:%v", err.Error())
} else {
link = repo.Link() + "/datasets"
}
}
}
url := ""
if isNeedDown {
url = attachment.S3DownloadURL()
}
datasetDownload = append(datasetDownload, models.DatasetDownload{
DatasetName: attachment.Name,
DatasetName: name,
DatasetDownloadLink: url,
RepositoryLink: repo.Link() + "/datasets",
RepositoryLink: link,
IsDelete: isDelete,
})
}
return datasetDownload
@@ -949,14 +958,17 @@ func trainJobNewVersionDataPrepare(ctx *context.Context) error {
}
_, _, datasetNames, _, err := getDatasUrlListByUUIDS(task.Uuid)
if err != nil {
ctx.ServerError("GetAllUserAttachments failed:", err)
return err
log.Info("query dataset error," + err.Error())
//ctx.ServerError("GetAllUserAttachments failed:", err)
//return err
} else {
ctx.Data["dataset_name"] = datasetNames
}
ctx.Data["branches"] = branches
ctx.Data["branch_name"] = task.BranchName
ctx.Data["description"] = task.Description
ctx.Data["boot_file"] = task.BootFile
ctx.Data["dataset_name"] = datasetNames
ctx.Data["work_server_number"] = task.WorkServerNumber
ctx.Data["flavor_name"] = task.FlavorName
ctx.Data["engine_name"] = task.EngineName
@@ -1810,7 +1822,7 @@ func TrainJobShow(ctx *context.Context) {
} else {
VersionListTasks[i].Parameters = ""
}
datasetList = append(datasetList, GetCloudBrainDataSetInfo(task.Uuid, false))
datasetList = append(datasetList, GetCloudBrainDataSetInfo(task.Uuid, task.DatasetName, false))
VersionListTasks[i].CanDel = cloudbrain.CanDeleteJob(ctx, &task.Cloudbrain)
VersionListTasks[i].CanModify = cloudbrain.CanModifyJob(ctx, &task.Cloudbrain)
}
@@ -2066,13 +2078,6 @@ func InferenceJobCreate(ctx *context.Context, form auth.CreateModelArtsInference
gitRepo, _ := git.OpenRepository(repo.RepoPath())
commitID, _ := gitRepo.GetBranchCommitID(branchName)

_, dataUrl, datasetNames, _, err := getDatasUrlListByUUIDS(uuid)
if err != nil {
inferenceJobErrorNewDataPrepare(ctx, form)
ctx.RenderWithErr(ctx.Tr(errStr), tplModelArtsInferenceJobNew, &form)
return
}

if err := downloadCode(repo, codeLocalPath, branchName); err != nil {
log.Error("Create task failed, server timed out: %s (%v)", repo.FullName(), err)
inferenceJobErrorNewDataPrepare(ctx, form)
@@ -2111,6 +2116,28 @@ func InferenceJobCreate(ctx *context.Context, form auth.CreateModelArtsInference
Label: modelarts.CkptUrl,
Value: "s3:/" + ckptUrl,
})

datasUrlList, dataUrl, datasetNames, isMultiDataset, err := getDatasUrlListByUUIDS(uuid)
if err != nil {
inferenceJobErrorNewDataPrepare(ctx, form)
ctx.RenderWithErr(ctx.Tr(errStr), tplModelArtsInferenceJobNew, &form)
return
}
dataPath := dataUrl
jsondatas, err := json.Marshal(datasUrlList)
if err != nil {
log.Error("Failed to Marshal: %v", err)
inferenceJobErrorNewDataPrepare(ctx, form)
ctx.RenderWithErr("json error:"+err.Error(), tplModelArtsInferenceJobNew, &form)
return
}
if isMultiDataset {
param = append(param, models.Parameter{
Label: modelarts.MultiDataUrl,
Value: string(jsondatas),
})
}

existDeviceTarget := false
if len(params) != 0 {
err := json.Unmarshal([]byte(params), &parameters)
@@ -2143,7 +2170,7 @@ func InferenceJobCreate(ctx *context.Context, form auth.CreateModelArtsInference
req := &modelarts.GenerateInferenceJobReq{
JobName: jobName,
DisplayJobName: displayJobName,
DataUrl: dataUrl,
DataUrl: dataPath,
Description: description,
CodeObsPath: codeObsPath,
BootFileUrl: codeObsPath + bootFile,
@@ -2511,7 +2538,7 @@ func InferenceJobShow(ctx *context.Context) {
ctx.Data["displayJobName"] = task.DisplayJobName
ctx.Data["task"] = task
ctx.Data["canDownload"] = cloudbrain.CanModifyJob(ctx, task)
ctx.Data["datasetDownload"] = GetCloudBrainDataSetInfo(task.Uuid, false)
ctx.Data["datasetDownload"] = GetCloudBrainDataSetInfo(task.Uuid, task.DatasetName, false)
tempUids := []int64{}
tempUids = append(tempUids, task.UserID)
JobCreater, err := models.GetUserNamesByIDs(tempUids)


+ 14
- 0
routers/response/error.go View File

@@ -0,0 +1,14 @@
package response

type BizError struct {
Code int
Err string
}

func (b BizError) Error() string {
return b.Err
}

func NewBizError(err error) *BizError {
return &BizError{Code: RESPONSE_CODE_ERROR_DEFAULT, Err: err.Error()}
}

+ 5
- 1
routers/response/response.go View File

@@ -24,8 +24,12 @@ func ServerError(msg string) *AiforgeResponse {
return &AiforgeResponse{Code: RESPONSE_CODE_ERROR_DEFAULT, Msg: msg}
}

func ResponseError(err *BizError) *AiforgeResponse {
return &AiforgeResponse{Code: err.Code, Msg: err.Err}
}

func SuccessWithData(data interface{}) *AiforgeResponse {
return &AiforgeResponse{Code: RESPONSE_CODE_ERROR_DEFAULT, Msg: RESPONSE_MSG_SUCCESS, Data: data}
return &AiforgeResponse{Code: RESPONSE_CODE_SUCCESS, Msg: RESPONSE_MSG_SUCCESS, Data: data}
}
func ErrorWithData(code int, msg string, data interface{}) *AiforgeResponse {
return &AiforgeResponse{Code: code, Msg: msg, Data: data}


+ 4
- 0
routers/response/response_list.go View File

@@ -0,0 +1,4 @@
package response

var RESOURCE_QUEUE_NOT_AVAILABLE = &BizError{Code: 1001, Err: "resource queue not available"}
var SPECIFICATION_NOT_EXIST = &BizError{Code: 1002, Err: "specification not exist"}

+ 27
- 1
routers/routes/routes.go View File

@@ -605,6 +605,32 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/delete", admin.DeleteNotices)
m.Post("/empty", admin.EmptyNotices)
})

m.Group("/resources", func() {
m.Group("/queue", func() {
m.Get("", admin.GetQueuePage)
m.Get("/list", admin.GetResourceQueueList)
m.Post("/grampus/sync", admin.SyncGrampusQueue)
m.Get("/codes", admin.GetResourceQueueCodes)
m.Get("/centers", admin.GetResourceAiCenters)
m.Post("/add", binding.Bind(models.ResourceQueueReq{}), admin.AddResourceQueue)
m.Post("/update/:id", binding.BindIgnErr(models.ResourceQueueReq{}), admin.UpdateResourceQueue)
})
m.Group("/specification", func() {
m.Get("", admin.GetSpecificationPage)
m.Get("/list", admin.GetResourceSpecificationList)
m.Get("/scenes/:id", admin.GetResourceSpecificationScenes)
m.Post("/grampus/sync", admin.SyncGrampusSpecs)
m.Post("/add", binding.Bind(models.ResourceSpecificationReq{}), admin.AddResourceSpecification)
m.Post("/update/:id", binding.BindIgnErr(models.ResourceSpecificationReq{}), admin.UpdateResourceSpecification)
})
m.Group("/scene", func() {
m.Get("", admin.GetScenePage)
m.Get("/list", admin.GetResourceSceneList)
m.Post("/add", binding.Bind(models.ResourceSceneReq{}), admin.AddResourceScene)
m.Post("/update/:id", binding.BindIgnErr(models.ResourceSceneReq{}), admin.UpdateResourceScene)
})
})
}, adminReq)
// ***** END: Admin *****

@@ -1193,7 +1219,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("", reqRepoCloudBrainReader, repo.NotebookShow)
m.Get("/debug", cloudbrain.AdminOrJobCreaterRight, repo.NotebookDebug2)
m.Post("/restart", cloudbrain.AdminOrJobCreaterRight, repo.NotebookRestart)
m.Post("/stop", cloudbrain.AdminOrJobCreaterRight, repo.NotebookStop)
m.Post("/stop", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.NotebookStop)
m.Post("/del", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.NotebookDel)
})
m.Get("/create", reqWechatBind, reqRepoCloudBrainWriter, repo.NotebookNew)


+ 14
- 0
services/admin/operate_log/operate_log.go View File

@@ -0,0 +1,14 @@
package operate_log

import (
"code.gitea.io/gitea/models"
)

func Log(log models.AdminOperateLog) error {
_, err := models.InsertAdminOperateLog(log)
return err
}

func NewLogValues() *models.LogValues {
return &models.LogValues{Params: make([]models.LogValue, 0)}
}

+ 122
- 0
services/cloudbrain/resource/resource_queue.go View File

@@ -0,0 +1,122 @@
package resource

import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/grampus"
"code.gitea.io/gitea/modules/log"
"fmt"
"strings"
)

func AddResourceQueue(req models.ResourceQueueReq) error {
if _, err := models.InsertResourceQueue(req.ToDTO()); err != nil {
return err
}
return nil
}

func UpdateResourceQueue(queueId int64, req models.ResourceQueueReq) error {
if _, err := models.UpdateResourceQueueById(queueId, models.ResourceQueue{
CardsTotalNum: req.CardsTotalNum,
Remark: req.Remark,
}); err != nil {
return err
}
return nil
}

func GetResourceQueueList(opts models.SearchResourceQueueOptions) (*models.ResourceQueueListRes, error) {
n, r, err := models.SearchResourceQueue(opts)
if err != nil {
return nil, err
}

return models.NewResourceQueueListRes(n, r), nil
}

func GetResourceQueueCodes(opts models.GetQueueCodesOptions) ([]*models.ResourceQueueCodesRes, error) {
r, err := models.GetResourceQueueCodes(opts)
if err != nil {
return nil, err
}

return r, nil
}

func GetResourceAiCenters() ([]models.ResourceAiCenterRes, error) {
r, err := models.GetResourceAiCenters()
if err != nil {
return nil, err
}

return r, nil
}

func SyncGrampusQueue(doerId int64) error {
r, err := grampus.GetAiCenters(1, 100)
if err != nil {
return err
}
log.Info("SyncGrampusQueue result = %+v", r)
queueUpdateList := make([]models.ResourceQueue, 0)
queueInsertList := make([]models.ResourceQueue, 0)
existIds := make([]int64, 0)

for _, center := range r.Infos {
for _, device := range center.AccDevices {
computeResource := models.ParseComputeResourceFormGrampus(device.Kind)
accCardType := strings.ToUpper(device.Model)
if computeResource == "" {
continue
}
//Determine if this quque already exists.if exist,update params
//if not exist,insert a new record
oldQueue, err := models.GetResourceQueue(&models.ResourceQueue{
Cluster: models.C2NetCluster,
AiCenterCode: center.Id,
ComputeResource: computeResource,
AccCardType: accCardType,
})
if err != nil {
return err
}

if oldQueue == nil {
queueInsertList = append(queueInsertList, models.ResourceQueue{
Cluster: models.C2NetCluster,
AiCenterCode: center.Id,
AiCenterName: center.Name,
ComputeResource: computeResource,
AccCardType: accCardType,
IsAutomaticSync: true,
CreatedBy: doerId,
UpdatedBy: doerId,
})
} else {
existIds = append(existIds, oldQueue.ID)
queueUpdateList = append(queueUpdateList, models.ResourceQueue{
ID: oldQueue.ID,
ComputeResource: computeResource,
AiCenterName: center.Name,
AccCardType: accCardType,
UpdatedBy: doerId,
})
}

}
}
return models.SyncGrampusQueues(queueUpdateList, queueInsertList, existIds)
}

func SyncGrampusQueueAndSpecs() {
defer func() {
if err := recover(); err != nil {
combinedErr := fmt.Errorf("%s\n%s", err, log.Stack(2))
log.Error("PANIC:", combinedErr)
}
}()
log.Info("start to sync grampus queue and specs")
SyncGrampusQueue(0)
SyncGrampusSpecs(0)
log.Info("sync grampus queue and specs finished")
}

+ 35
- 0
services/cloudbrain/resource/resource_scene.go View File

@@ -0,0 +1,35 @@
package resource

import (
"code.gitea.io/gitea/models"
)

func AddResourceScene(req models.ResourceSceneReq) error {
if err := models.InsertResourceScene(req); err != nil {
return err
}
return nil
}

func UpdateResourceScene(req models.ResourceSceneReq) error {
if err := models.UpdateResourceScene(req); err != nil {
return err
}
return nil
}

func DeleteResourceScene(id int64) error {
if err := models.DeleteResourceScene(id); err != nil {
return err
}
return nil
}

func GetResourceSceneList(opts models.SearchResourceSceneOptions) (*models.ResourceSceneListRes, error) {
n, r, err := models.SearchResourceScene(opts)
if err != nil {
return nil, err
}

return models.NewResourceSceneListRes(n, r), nil
}

+ 186
- 0
services/cloudbrain/resource/resource_specification.go View File

@@ -0,0 +1,186 @@
package resource

import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/grampus"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/routers/response"
"code.gitea.io/gitea/services/admin/operate_log"
"fmt"
"strings"
)

func AddResourceSpecification(doerId int64, req models.ResourceSpecificationReq) error {
if req.Status == 0 {
req.Status = models.SpecNotVerified
}
spec := req.ToDTO()
if _, err := models.InsertResourceSpecification(spec); err != nil {
return err
}
return nil
}

func UpdateSpecUnitPrice(doerId int64, specId int64, unitPrice int) *response.BizError {
oldSpec, err := models.GetResourceSpecification(&models.ResourceSpecification{ID: specId})
if err != nil {
return response.NewBizError(err)
}
if oldSpec == nil {
return response.SPECIFICATION_NOT_EXIST
}
err = models.UpdateSpecUnitPriceById(specId, unitPrice)
if err != nil {
return response.NewBizError(err)
}

if oldSpec.UnitPrice != unitPrice {
AddSpecOperateLog(doerId, "edit", operate_log.NewLogValues().Add("unitPrice", unitPrice), operate_log.NewLogValues().Add("unitPrice", oldSpec.UnitPrice), specId, fmt.Sprintf("修改资源规格单价从%d积分到%d积分", oldSpec.UnitPrice, unitPrice))
}
return nil
}

func SyncGrampusSpecs(doerId int64) error {
r, err := grampus.GetResourceSpecs("")
if err != nil {
return err
}
log.Info("SyncGrampusSpecs result = %+v", r)
specUpdateList := make([]models.ResourceSpecification, 0)
specInsertList := make([]models.ResourceSpecification, 0)
existIds := make([]int64, 0)
for _, spec := range r.Infos {
for _, c := range spec.Centers {
computeResource := models.ParseComputeResourceFormGrampus(spec.SpecInfo.AccDeviceKind)
if computeResource == "" {
continue
}
accCardType := strings.ToUpper(spec.SpecInfo.AccDeviceModel)
memGiB, err := models.ParseMemSizeFromGrampus(spec.SpecInfo.MemorySize)
gpuMemGiB, err := models.ParseMemSizeFromGrampus(spec.SpecInfo.AccDeviceMemory)
if err != nil {
log.Error("ParseMemSizeFromGrampus error. MemorySize=%s AccDeviceMemory=%s", spec.SpecInfo.MemorySize, spec.SpecInfo.AccDeviceMemory)
}
// get resource queue.if queue not exist,skip it
r, err := models.GetResourceQueue(&models.ResourceQueue{
Cluster: models.C2NetCluster,
AiCenterCode: c.ID,
ComputeResource: computeResource,
AccCardType: accCardType,
})
if err != nil || r == nil {
continue
}

//Determine if this specification already exists.if exist,update params
//if not exist,insert a new record and status is SpecNotVerified
oldSpec, err := models.GetResourceSpecification(&models.ResourceSpecification{
QueueId: r.ID,
SourceSpecId: spec.ID,
})
if err != nil {
return err
}

if oldSpec == nil {
specInsertList = append(specInsertList, models.ResourceSpecification{
QueueId: r.ID,
SourceSpecId: spec.ID,
AccCardsNum: spec.SpecInfo.AccDeviceNum,
CpuCores: spec.SpecInfo.CpuCoreNum,
MemGiB: memGiB,
GPUMemGiB: gpuMemGiB,
Status: models.SpecNotVerified,
IsAutomaticSync: true,
CreatedBy: doerId,
UpdatedBy: doerId,
})
} else {
existIds = append(existIds, oldSpec.ID)
specUpdateList = append(specUpdateList, models.ResourceSpecification{
ID: oldSpec.ID,
AccCardsNum: spec.SpecInfo.AccDeviceNum,
CpuCores: spec.SpecInfo.CpuCoreNum,
MemGiB: memGiB,
GPUMemGiB: gpuMemGiB,
UpdatedBy: doerId,
})
}

}
}
return models.SyncGrampusSpecs(specUpdateList, specInsertList, existIds)
}

//GetResourceSpecificationList returns specification and queue
func GetResourceSpecificationList(opts models.SearchResourceSpecificationOptions) (*models.ResourceSpecAndQueueListRes, error) {
n, r, err := models.SearchResourceSpecification(opts)
if err != nil {
return nil, err
}

return models.NewResourceSpecAndQueueListRes(n, r), nil
}

func GetResourceSpecificationScenes(specId int64) ([]models.ResourceSceneBriefRes, error) {
r, err := models.GetSpecScenes(specId)
if err != nil {
return nil, err
}

return r, nil
}

func ResourceSpecOnShelf(doerId int64, id int64, unitPrice int) *response.BizError {
spec, err := models.GetResourceSpecification(&models.ResourceSpecification{ID: id})
if err != nil {
return response.NewBizError(err)
}
if spec == nil {
return response.SPECIFICATION_NOT_EXIST
}
if q, err := models.GetResourceQueue(&models.ResourceQueue{ID: spec.QueueId}); err != nil || q == nil {
return response.RESOURCE_QUEUE_NOT_AVAILABLE
}

err = models.ResourceSpecOnShelf(id, unitPrice)
if err != nil {
return response.NewBizError(err)
}
if spec.UnitPrice != unitPrice {
AddSpecOperateLog(doerId, "on-shelf", operate_log.NewLogValues().Add("UnitPrice", unitPrice), operate_log.NewLogValues().Add("UnitPrice", spec.UnitPrice), id, fmt.Sprintf("定价上架资源规格,单价为%d", unitPrice))
} else {
AddSpecOperateLog(doerId, "on-shelf", nil, nil, id, "上架资源规格")
}
return nil

}
func ResourceSpecOffShelf(doerId int64, id int64) *response.BizError {
_, err := models.ResourceSpecOffShelf(id)
if err != nil {
return response.NewBizError(err)
}
AddSpecOperateLog(doerId, "off-shelf", nil, nil, id, "下架资源规格")
return nil

}

func AddSpecOperateLog(doerId int64, operateType string, newValue, oldValue *models.LogValues, specId int64, comment string) {
var newString = ""
var oldString = ""
if newValue != nil {
newString = newValue.JsonString()
}
if oldValue != nil {
oldString = oldValue.JsonString()
}
operate_log.Log(models.AdminOperateLog{
BizType: "SpecOperate",
OperateType: operateType,
OldValue: oldString,
NewValue: newString,
RelatedId: fmt.Sprint(specId),
CreatedBy: doerId,
Comment: comment,
})
}

+ 3
- 4
templates/admin/cloudbrain/images.tmpl View File

@@ -13,10 +13,9 @@
<div class="alert"></div>
<div class="admin user">
{{template "admin/navbar" .}}
<div id="images-admin">

</div>
<div class="ui container">
<div id="images-admin"></div>
</div>
</div>
<!-- 确认模态框 -->
<div>


+ 14
- 15
templates/admin/cloudbrain/list.tmpl View File

@@ -22,19 +22,19 @@
data-all-compute="{{.i18n.Tr "admin.cloudbrain.all_computing_resources"}}"
data-all-status="{{.i18n.Tr "admin.cloudbrain.all_status"}}"></div>
{{template "admin/navbar" .}}
<div class="ui container" style="width: 95%;">
<div class="ui container">
{{template "base/alert" .}}
<div class="ui grid">
<div class="row" style="border: 1px solid #d4d4d5;margin-top: 15px;padding-top: 0;">
<div class="ui grid" style="margin:0">
<div class="row" style="border: 1px solid #d4d4d5;margin-top: 0px;padding-top: 0;">
{{template "admin/cloudbrain/search" .}}
<div class="ui six wide column right aligned" style="margin: 1rem 0;">
<a class="ui compact blue basic icon button" style="box-shadow: none !important; padding: 0.8em;"
href="/admin/cloudbrains/download"><i
class="ri-download-line middle aligned icon"></i>{{.i18n.Tr "admin.cloudbrain.download_report"}}</a>
</div>
<div class="ui sixteen wide column">
<div class="ui sixteen wide column" style="overflow-x:auto;">
<!-- 任务展示 -->
<div class="dataset list">
<div class="dataset list" style="min-width:2100px;margin-top:15px;margin-bottom:15px;">
<!-- 表头 -->
<div class="ui grid stackable" style="background: #f0f0f0;;">
<div class="row">
@@ -412,17 +412,16 @@
</div>

{{end}}
{{end}}
<div id="app" style="margin-top: 2rem;">
<div class="center">
<el-pagination background @current-change="handleCurrentChange" :current-page="page"
:page-sizes="[10]" :page-size="10" layout="total, sizes, prev, pager, next, jumper"
:total="{{.Page.Paginater.Total}}">
</el-pagination>
</div>
</div>
{{end}}
</div>
</div>
<div id="app" style="margin-top: 2rem;width:100%;">
<div class="center">
<el-pagination background @current-change="handleCurrentChange" :current-page="page"
:page-sizes="[10]" :page-size="10" layout="total, sizes, prev, pager, next, jumper"
:total="{{.Page.Paginater.Total}}">
</el-pagination>
</div>

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


+ 63
- 43
templates/admin/navbar.tmpl View File

@@ -1,44 +1,64 @@
<div class="ui secondary pointing tabular top attached borderless menu stackable new-menu navbar">
<a class="{{if .PageIsAdminDashboard}}active{{end}} item" href="{{AppSubUrl}}/admin">
{{.i18n.Tr "admin.dashboard"}}
</a>
<a class="{{if .PageIsAdminUsers}}active{{end}} item" href="{{AppSubUrl}}/admin/users">
{{.i18n.Tr "admin.users"}}
</a>
<a class="{{if .PageIsAdminOrganizations}}active{{end}} item" href="{{AppSubUrl}}/admin/orgs">
{{.i18n.Tr "admin.organizations"}}
</a>
<a class="{{if .PageIsAdminRepositories}}active{{end}} item" href="{{AppSubUrl}}/admin/repos">
{{.i18n.Tr "admin.repositories"}}
</a>
<a class="{{if .PageIsAdminDatasets}}active{{end}} item" href="{{AppSubUrl}}/admin/datasets">
{{.i18n.Tr "admin.datasets"}}
</a>
<a class="{{if .PageIsAdminCloudBrains}}active{{end}} item" href="{{AppSubUrl}}/admin/cloudbrains">
{{.i18n.Tr "repo.cloudbrain.task"}}
</a>
<a class="{{if .PageIsAdminImages}}active{{end}} item" href="{{AppSubUrl}}/admin/images">
{{.i18n.Tr "explore.images"}}
</a>
<a class="{{if .PageIsAdminHooks}}active{{end}} item" href="{{AppSubUrl}}/admin/hooks">
{{.i18n.Tr "admin.hooks"}}
</a>
<a class="{{if .PageIsAdminSystemHooks}}active{{end}} item" href="{{AppSubUrl}}/admin/system-hooks">
{{.i18n.Tr "admin.systemhooks"}}
</a>
<a class="{{if .PageIsAdminAuthentications}}active{{end}} item" href="{{AppSubUrl}}/admin/auths">
{{.i18n.Tr "admin.authentication"}}
</a>
<a class="{{if .PageIsAdminEmails}}active{{end}} item" href="{{AppSubUrl}}/admin/emails">
{{.i18n.Tr "admin.emails"}}
</a>
<a class="{{if .PageIsAdminConfig}}active{{end}} item" href="{{AppSubUrl}}/admin/config">
{{.i18n.Tr "admin.config"}}
</a>
<a class="{{if .PageIsAdminNotices}}active{{end}} item" href="{{AppSubUrl}}/admin/notices">
{{.i18n.Tr "admin.notices"}}
</a>
<a class="{{if .PageIsAdminMonitor}}active{{end}} item" href="{{AppSubUrl}}/admin/monitor">
{{.i18n.Tr "admin.monitor"}}
</a>
<div class="ui secondary pointing tabular top attached borderless menu stackable new-menu navbar" style="margin-top:0">
<div class="item-container">
<a class="{{if .PageIsAdminDashboard}}active{{end}} item" href="{{AppSubUrl}}/admin">
{{.i18n.Tr "admin.dashboard"}}
</a>
<a class="item item-first" href="javascript:void(0);">
{{.i18n.Tr "admin.user_management"}}
</a>
<a class="{{if .PageIsAdminUsers}}active{{end}} item item-next" href="{{AppSubUrl}}/admin/users">
{{.i18n.Tr "admin.users"}}
</a>
<a class="{{if .PageIsAdminEmails}}active{{end}} item item-next" href="{{AppSubUrl}}/admin/emails">
{{.i18n.Tr "admin.emails"}}
</a>
<a class="{{if .PageIsAdminOrganizations}}active{{end}} item" href="{{AppSubUrl}}/admin/orgs">
{{.i18n.Tr "admin.organizations"}}
</a>
<a class="{{if .PageIsAdminRepositories}}active{{end}} item" href="{{AppSubUrl}}/admin/repos">
{{.i18n.Tr "admin.repositories"}}
</a>
<a class="{{if .PageIsAdminDatasets}}active{{end}} item" href="{{AppSubUrl}}/admin/datasets">
{{.i18n.Tr "admin.datasets"}}
</a>
<a class="{{if .PageIsAdminCloudBrains}}active{{end}} item" href="{{AppSubUrl}}/admin/cloudbrains">
{{.i18n.Tr "repo.cloudbrain.task"}}
</a>
<a class="{{if .PageIsAdminImages}}active{{end}} item" href="{{AppSubUrl}}/admin/images">
{{.i18n.Tr "explore.images"}}
</a>
<a class="item item-first" href="javascript:void(0);">
{{.i18n.Tr "admin.resource_management"}}
</a>
<a class="{{if .PageIsAdminResourcesQueue}}active{{end}} item item-next" href="{{AppSubUrl}}/admin/resources/queue">
{{.i18n.Tr "admin.resource_pool"}}
</a>
<a class="{{if .PageIsAdminResourcesSpecification}}active{{end}} item item-next" href="{{AppSubUrl}}/admin/resources/specification">
{{.i18n.Tr "admin.resource_price"}}
</a>
<a class="{{if .PageIsAdminResourcesScene}}active{{end}} item item-next" href="{{AppSubUrl}}/admin/resources/scene">
{{.i18n.Tr "admin.application_scenario"}}
</a>
<a class="item item-first" href="javascript:void(0);">
{{.i18n.Tr "admin.system_configuration"}}
</a>
<a class="{{if .PageIsAdminMonitor}}active{{end}} item item-next" href="{{AppSubUrl}}/admin/monitor">
{{.i18n.Tr "admin.monitor"}}
</a>
<a class="{{if .PageIsAdminHooks}}active{{end}} item item-next" href="{{AppSubUrl}}/admin/hooks">
{{.i18n.Tr "admin.hooks"}}
</a>
<a class="{{if .PageIsAdminSystemHooks}}active{{end}} item item-next" href="{{AppSubUrl}}/admin/system-hooks">
{{.i18n.Tr "admin.systemhooks"}}
</a>
<a class="{{if .PageIsAdminAuthentications}}active{{end}} item item-next" href="{{AppSubUrl}}/admin/auths">
{{.i18n.Tr "admin.authentication"}}
</a>
<a class="{{if .PageIsAdminConfig}}active{{end}} item item-next" href="{{AppSubUrl}}/admin/config">
{{.i18n.Tr "admin.config"}}
</a>
<a class="{{if .PageIsAdminNotices}}active{{end}} item item-next" href="{{AppSubUrl}}/admin/notices">
{{.i18n.Tr "admin.notices"}}
</a>
</div>
</div>

+ 10
- 0
templates/admin/resources/queue.tmpl View File

@@ -0,0 +1,10 @@
{{template "base/head" .}}
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/vp-resources-queue.css?v={{MD5 AppVer}}" />
<div class="admin resource">
{{template "admin/navbar" .}}
<div class="ui container">
<div id="__vue-root"></div>
</duv>
</div>
<script src="{{StaticUrlPrefix}}/js/vp-resources-queue.js?v={{MD5 AppVer}}"></script>
{{template "base/footer" .}}

+ 10
- 0
templates/admin/resources/scene.tmpl View File

@@ -0,0 +1,10 @@
{{template "base/head" .}}
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/vp-resources-scene.css?v={{MD5 AppVer}}" />
<div class="admin resource">
{{template "admin/navbar" .}}
<div class="ui container">
<div id="__vue-root"></div>
</duv>
</div>
<script src="{{StaticUrlPrefix}}/js/vp-resources-scene.js?v={{MD5 AppVer}}"></script>
{{template "base/footer" .}}

+ 10
- 0
templates/admin/resources/specification.tmpl View File

@@ -0,0 +1,10 @@
{{template "base/head" .}}
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/vp-resources-specification.css?v={{MD5 AppVer}}" />
<div class="admin resource">
{{template "admin/navbar" .}}
<div class="ui container">
<div id="__vue-root"></div>
</duv>
</div>
<script src="{{StaticUrlPrefix}}/js/vp-resources-specification.js?v={{MD5 AppVer}}"></script>
{{template "base/footer" .}}

+ 10
- 10
templates/custom/select_dataset_train.tmpl View File

@@ -15,7 +15,7 @@
{{if .benchmarkMode}}{{.i18n.Tr "repo.modelarts.infer_job.select_model"}}{{else}}{{.i18n.Tr "dataset.select_dataset"}}{{end}}
</el-button>
{{if .benchmarkMode}}
<span class="tooltips" style="display: block;margin-left:11.5rem;">说明:先使用数据集功能上传模型,然后从数据集列表选模型。</span>
<span class="tooltips" style="display: block;margin-left:11.5rem;">{{.i18n.Tr "dataset.benchmark_dataset_tip"}}</span>
{{end}}
<el-dialog title="{{.i18n.Tr "dataset.select_dataset"}}" :visible.sync="dialogVisible" width="50%">
<div v-loading="loadingDataIndex" style="position: relative;">
@@ -49,13 +49,13 @@
<i class="CREATING"></i>
<span style="margin-left: 0.4em;font-size: 12px;color: #5A5A5A;"
data-tooltip="{{$.i18n.Tr "dataset.unzip_tooltips"}}" data-inverted=""
data-variation="mini" data-position="left center">解压中</span>
data-variation="mini" data-position="left center">{{$.i18n.Tr "dataset.unzip_stared"}}</span>
</span>
<span v-if="dataset.DecompressState===3" style="display: flex;align-items: center;">
<i class="FAILED"></i>
<span style="margin-left: 0.4em;font-size: 12px;color:red;"
data-tooltip="{{$.i18n.Tr "dataset.zip_failed"}}" data-inverted=""
data-variation="mini" data-position="left center">解压失败</span>
data-tooltip="{{$.i18n.Tr "dataset.unzip_failed"}}" data-inverted=""
data-variation="mini" data-position="left center">{{$.i18n.Tr "dataset.unzip_failed"}}</span>
</span>
</div>
</div>
@@ -85,13 +85,13 @@
<i class="CREATING"></i>
<span style="margin-left: 0.4em;font-size: 12px;color: #5A5A5A;"
data-tooltip="{{$.i18n.Tr "dataset.unzip_tooltips"}}" data-inverted=""
data-variation="mini" data-position="left center">解压中</span>
data-variation="mini" data-position="left center">{{$.i18n.Tr "dataset.unzip_stared"}}</span>
</span>
<span v-if="dataset.DecompressState===3" style="display: flex;align-items: center;">
<i class="FAILED"></i>
<span style="margin-left: 0.4em;font-size: 12px;color:red;"
data-tooltip="{{$.i18n.Tr "dataset.zip_failed"}}" data-inverted=""
data-variation="mini" data-position="left center">解压失败</span>
data-variation="mini" data-position="left center">{{$.i18n.Tr "dataset.unzip_failed"}}</span>
</span>
</div>
</div>
@@ -120,13 +120,13 @@
<i class="CREATING"></i>
<span style="margin-left: 0.4em;font-size: 12px;color: #5A5A5A;"
data-tooltip="{{$.i18n.Tr "dataset.unzip_tooltips"}}" data-inverted=""
data-variation="mini" data-position="left center">解压中</span>
data-variation="mini" data-position="left center">{{$.i18n.Tr "dataset.unzip_stared"}}</span>
</span>
<span v-if="dataset.DecompressState===3" style="display: flex;align-items: center;">
<i class="FAILED"></i>
<span style="margin-left: 0.4em;font-size: 12px;color:red;"
data-tooltip="{{$.i18n.Tr "dataset.zip_failed"}}" data-inverted=""
data-variation="mini" data-position="left center">解压失败</span>
data-variation="mini" data-position="left center">{{$.i18n.Tr "dataset.unzip_failed"}}</span>
</span>
</div>
</div>
@@ -155,13 +155,13 @@
<i class="CREATING"></i>
<span style="margin-left: 0.4em;font-size: 12px;color: #5A5A5A;"
data-tooltip="{{$.i18n.Tr "dataset.unzip_tooltips"}}" data-inverted=""
data-variation="mini" data-position="left center">解压中</span>
data-variation="mini" data-position="left center">{{$.i18n.Tr "dataset.unzip_stared"}}</span>
</span>
<span v-if="dataset.DecompressState===3" style="display: flex;align-items: center;">
<i class="FAILED"></i>
<span style="margin-left: 0.4em;font-size: 12px;color:red;"
data-tooltip="{{$.i18n.Tr "dataset.zip_failed"}}" data-inverted=""
data-variation="mini" data-position="left center">解压失败</span>
data-variation="mini" data-position="left center">{{$.i18n.Tr "dataset.unzip_failed"}}</span>
</span>
</div>
</div>


+ 1
- 1
templates/custom/wait_count_train.tmpl View File

@@ -18,7 +18,7 @@
></i>
<span id="gpu-nums" style="font-size: 12px"
>{{.ctx.i18n.Tr "repo.wait_count_start"}}
{{if .type}}
{{if .ctx.QueuesDetail}}
{{ $gpuQueue }}
{{else}}
{{.ctx.WaitCount}}


+ 2
- 1
templates/repo/attachment/upload.tmpl View File

@@ -47,7 +47,8 @@
data-uploading='{{.i18n.Tr "dropzone.uploading"}}'
data-failed='{{.i18n.Tr "dropzone.failed"}}'
data-repopath='{{AppSubUrl}}{{$.RepoLink}}/datasets' data-cancel='{{.i18n.Tr "cancel"}}'
data-upload='{{.i18n.Tr "dataset.dataset_upload"}}'>
data-upload='{{.i18n.Tr "dataset.dataset_upload"}}'
data-upload-status='{{.i18n.Tr "dataset.dataset_upload_status"}}'>
</div>
<div id="datasetId" datasetId="{{.datasetId}}"></div>
</el-form>


+ 5
- 9
templates/repo/cloudbrain/benchmark/new.tmpl View File

@@ -134,7 +134,7 @@
</div>
</form>
{{else}}
<form class="ui form alogrithm_form" action="{{.Link}}?benchmarkMode=alogrithm" method="post">
<form id="form_id" class="ui form alogrithm_form" action="{{.Link}}?benchmarkMode=alogrithm" method="post">
{{.CsrfTokenHtml}}
<input type="hidden" name="action" value="update">
<input type="hidden" name="job_type" value="BENCHMARK">
@@ -178,8 +178,7 @@
</div>
<div class="required unite inline min_title fields" style="width: 90%;margin-left: 5.7rem;">&nbsp;
<div class="required eight wide field">
<label
style="font-weight: normal;white-space: nowrap;">{{.i18n.Tr "repo.cloudbrain.benchmark.evaluate_type"}}</label>
<label style="font-weight: normal;white-space: nowrap;">{{.i18n.Tr "repo.cloudbrain.benchmark.evaluate_type"}}</label>
<select class="ui fluid selection search dropdown" id="benchmark_types_id"
name="benchmark_types_id">
@@ -193,12 +192,9 @@
</select>
</div>
<div class="eight wide field" id="engine_name">
<input type="hidden" id="benchmark_child_types_id_hidden"
name="benchmark_child_types_id_hidden" value="{{.benchmark_child_types_id_hidden}}">
<label
style="font-weight: normal;white-space: nowrap;">{{.i18n.Tr "repo.cloudbrain.benchmark.evaluate_child_type"}}</label>
<select class="ui fluid selection dropdown nowrapx" id="benchmark_child_types_id"
style='width: 100%;' name="benchmark_child_types_id">
<input type="hidden" id="benchmark_child_types_id_hidden" name="benchmark_child_types_id_hidden" value="{{.benchmark_child_types_id_hidden}}">
<label style="font-weight: normal;white-space: nowrap;">{{.i18n.Tr "repo.cloudbrain.benchmark.evaluate_child_type"}}</label>
<select class="ui fluid selection dropdown nowrapx" id="benchmark_child_types_id" style='width: 100%;' name="benchmark_child_types_id">
</select>
</div>
</div>


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

@@ -83,6 +83,10 @@
Ascend NPU</a>
</div>
{{template "custom/wait_count_train" Dict "ctx" $ "type" .inference_gpu_types}}
<div style="display: flex;align-items: center;margin-left: 155px;margin-top: 0.5rem;">
<i class="ri-error-warning-line" style="color: #f2711c;margin-right: 0.5rem;"></i>
<span style="color: #888;font-size: 12px;">{{.i18n.Tr "cloudbrain.new_infer_gpu_tooltips" "/dataset" "/model" "/result" | Safe}}</span>
</div>
</div>
<div class="required min_title inline field">
<label class="label-fix-width" style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.job_name"}}</label>
@@ -229,18 +233,18 @@
{{if .resource_spec_id}}
{{range .inference_resource_specs}}
{{if eq $.resource_spec_id .Id}}
<option value="{{.Id}}">GPU数:{{.GpuNum}},CPU数:{{.CpuNum}},内存(MB):{{.MemMiB}},共享内存(MB):{{.ShareMemMiB}}</option>
<option value="{{.Id}}">{{$.i18n.Tr "cloudbrain.gpu_num"}}:{{.GpuNum}},{{$.i18n.Tr "cloudbrain.cpu_num"}}:{{.CpuNum}},{{$.i18n.Tr "cloudbrain.memory"}}(MB):{{$.i18n.Tr "cloudbrain.shared_memory"}}(MB):{{.ShareMemMiB}}</option>
{{end}}
{{end}}
{{range .inference_resource_specs}}
{{if ne $.resource_spec_id .Id}}
<option value="{{.Id}}">GPU数:{{.GpuNum}},CPU数:{{.CpuNum}},内存(MB):{{.MemMiB}},共享内存(MB):{{.ShareMemMiB}}</option>
<option value="{{.Id}}">{{$.i18n.Tr "cloudbrain.gpu_num"}}:{{.GpuNum}},{{$.i18n.Tr "cloudbrain.cpu_num"}}:{{.CpuNum}},{{$.i18n.Tr "cloudbrain.memory"}}(MB):{{$.i18n.Tr "cloudbrain.shared_memory"}}(MB):{{.ShareMemMiB}}</option>
{{end}}
{{end}}
{{else}}
{{range .inference_resource_specs}}
<option name="resource_spec_id" value="{{.Id}}">
GPU数:{{.GpuNum}},CPU数:{{.CpuNum}},内存(MB):{{.MemMiB}},共享内存(MB):{{.ShareMemMiB}}</option>
{{$.i18n.Tr "cloudbrain.gpu_num"}}:{{.GpuNum}},{{$.i18n.Tr "cloudbrain.cpu_num"}}:{{.CpuNum}},{{$.i18n.Tr "cloudbrain.memory"}}(MB):{{$.i18n.Tr "cloudbrain.shared_memory"}}(MB):{{.ShareMemMiB}}</option>
{{end}}
{{end}}
</select>
@@ -384,17 +388,19 @@
let value = ''
value += `<div class="two fields width85" id= "para${i}">`
value += '<div class="field">'
let placeholder_value='{{.i18n.Tr "repo.modelarts.train_job.parameter_value"}}'
let placeholder_name='{{.i18n.Tr "repo.modelarts.train_job.parameter_name"}}'
if(flag){
value +=`<input type="text" class="shipping_first-name" value="${paramsObject.label}">`
}else{
value +='<input type="text" class="shipping_first-name" required placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_name"}}>'
value +='<input type="text" class="shipping_first-name" required placeholder="' + placeholder_name+ '">'
}
value += '</div>'
value += '<div class="field">'
if(flag){
value +=`<input type="text" class="shipping_last-name" value="${paramsObject.value}">`
}else{
value +='<input type="text" class="shipping_last-name" required placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_value"}}>'
value +='<input type="text" class="shipping_last-name" required placeholder="' + placeholder_value+ '">'
}
value += '</div>'
value += '<span><i class="trash icon"></i></span>'


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

@@ -500,7 +500,13 @@
<tbody>
{{range $m ,$n := $.datasetDownload}}
<tr>
<td style="word-wrap: break-word;word-break: break-all;"><a href="{{.RepositoryLink}}" target="_blank">{{.DatasetName}}</a></td>
<td style="word-wrap: break-word;word-break: break-all;">
{{if eq .IsDelete true}}
{{.DatasetName}}({{$.i18n.Tr "dataset.file_deleted"}})
{{else}}
<a href="{{.RepositoryLink}}" target="_blank">{{.DatasetName}}</a>
{{end}}
</td>
</tr>
{{end}}


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

@@ -498,7 +498,13 @@
<tbody>
{{range $m ,$n := $.datasetDownload}}
<tr>
<td style="word-wrap: break-word;word-break: break-all;"><a href="{{.RepositoryLink}}" target="_blank">{{.DatasetName}}</a></td>
<td style="word-wrap: break-word;word-break: break-all;">
{{if eq .IsDelete true}}
{{.DatasetName}}({{$.i18n.Tr "dataset.file_deleted"}})
{{else}}
<a href="{{.RepositoryLink}}" target="_blank">{{.DatasetName}}</a>
{{end}}
</td>
</tr>
{{end}}


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

@@ -211,7 +211,7 @@
data-content={{.i18n.Tr "repo.modelarts.train_job.boot_file_helper"}}
data-position="right center" data-variation="mini"></i>
</span>
<a href="https://git.openi.org.cn/OpenIOSSG/MNIST_PytorchExample_GPU" target="_blank">查看样例</a>
<a href="https://git.openi.org.cn/OpenIOSSG/MNIST_PytorchExample_GPU" target="_blank">{{.i18n.Tr "cloudbrain.view_sample"}}</a>
</div>
<div id="select-multi-dataset">

@@ -234,18 +234,18 @@
{{if .resource_spec_id}}
{{range .train_resource_specs}}
{{if eq $.resource_spec_id .Id}}
<option value="{{.Id}}">GPU数:{{.GpuNum}},CPU数:{{.CpuNum}},内存(MB):{{.MemMiB}},共享内存(MB):{{.ShareMemMiB}}</option>
<option 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}}
{{end}}
{{range .train_resource_specs}}
{{if ne $.resource_spec_id .Id}}
<option value="{{.Id}}">GPU数:{{.GpuNum}},CPU数:{{.CpuNum}},内存(MB):{{.MemMiB}},共享内存(MB):{{.ShareMemMiB}}</option>
<option 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}}
{{end}}
{{else}}
{{range .train_resource_specs}}
<option name="resource_spec_id" value="{{.Id}}">
GPU数:{{.GpuNum}},CPU数:{{.CpuNum}},内存(MB):{{.MemMiB}},共享内存(MB):{{.ShareMemMiB}}</option>
{{$.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}}
{{end}}
</select>
@@ -297,17 +297,19 @@
let value = ''
value += `<div class="two fields width85" id= "para${i}">`
value += '<div class="field">'
let placeholder_value='{{.i18n.Tr "repo.modelarts.train_job.parameter_value"}}'
let placeholder_name='{{.i18n.Tr "repo.modelarts.train_job.parameter_name"}}'
if(flag){
value +=`<input type="text" class="shipping_first-name" value="${paramsObject.label}">`
}else{
value +='<input type="text" class="shipping_first-name" required placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_name"}}>'
value +='<input type="text" class="shipping_first-name" required placeholder="' + placeholder_name+ '">'
}
value += '</div>'
value += '<div class="field">'
if(flag){
value +=`<input type="text" class="shipping_last-name" value="${paramsObject.value}">`
}else{
value +='<input type="text" class="shipping_last-name" required placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_value"}}>'
value +='<input type="text" class="shipping_last-name" required placeholder="' + placeholder_value + '">'
}
value += '</div>'
value += '<span><i class="trash icon"></i></span>'


+ 16
- 10
templates/repo/cloudbrain/trainjob/show.tmpl View File

@@ -464,7 +464,13 @@
<tbody>
{{range $m ,$n := $.datasetDownload}}
<tr>
<td style="word-wrap: break-word;word-break: break-all;"><a href="{{.RepositoryLink}}" target="_blank">{{.DatasetName}}</a></td>
<td style="word-wrap: break-word;word-break: break-all;">
{{if eq .IsDelete true}}
{{.DatasetName}}({{$.i18n.Tr "dataset.file_deleted"}})
{{else}}
<a href="{{.RepositoryLink}}" target="_blank">{{.DatasetName}}</a>
{{end}}
</td>
</tr>
{{end}}
@@ -584,7 +590,7 @@
<div id="newmodel">
<div class="ui modal second">
<div class="header" style="padding: 1rem;background-color: rgba(240, 240, 240, 100);">
<h4 id="model_header">导入新模型</h4>
<h4 id="model_header">{{.i18n.Tr "repo.model.manage.import_new_model"}}</h4>
</div>
<div class="content content-padding">
<form id="formId" method="POST" class="ui form">
@@ -594,26 +600,26 @@
<input type="hidden" name="trainTaskCreate" value="true">

<div class="required inline field">
<label>训练任务</label>
<label>{{.i18n.Tr "repo.model.manage.createtrainjob"}}</label>
<input type="hidden" class="width83" id="JobId" name="JobId" readonly required>
<input type="hidden" id="VersionName" name="VersionName" value="V0001">
<input style="width: 45%;" id="JobName" readonly required>
</div>

<div class="required inline field" id="modelname">
<label>模型名称</label>
<label>{{.i18n.Tr "repo.model.manage.model_name"}}</label>
<input style="width: 45%;" id="name" name="Name" required maxlength="25"
onkeyup="this.value=this.value.replace(/[, ]/g,'')">
</div>
<div class="required inline field" id="verionname">
<label>模型版本</label>
<label>{{.i18n.Tr "repo.modelconvert.modelversion"}}</label>
<input style="width: 45%;" id="version" name="Version" value="" readonly required maxlength="255">
</div>
<div class="unite min_title inline field required">
<label>模型框架</label>
<label>{{.i18n.Tr "repo.model.manage.engine"}}</label>
<div class="ui dropdown selection search width70" id="choice_Engine">
<input type="hidden" id="Engine" name="Engine" required>
<div class="default text">选择模型框架</div>
<div class="default text">{{.i18n.Tr "repo.model.manage.select.engine"}}</div>
<i class="dropdown icon"></i>
<div class="menu" id="job-Engine">
<option class="active item" data-value="0">PyTorch</option>
@@ -628,7 +634,7 @@
</div>
<div class="unite min_title inline fields required">
<div class="field required">
<label for="modelSelectedFile">模型文件</label>
<label for="modelSelectedFile">{{.i18n.Tr "repo.model.manage.modelfile"}}</label>
</div>
<div class="thirteen wide field" style="position:relative">
<input id="modelSelectedFile" type="text" readonly required onclick="showMenu();" name="modelSelectedFile" >
@@ -638,12 +644,12 @@
</div>
</div>
<div class="inline field">
<label>模型标签</label>
<label>{{.i18n.Tr "repo.model.manage.modellabel"}}</label>
<input style="width: 83%;margin-left: 7px;" id="label" name="Label" maxlength="255"
placeholder='{{.i18n.Tr "repo.modelarts.train_job.label_place"}}'>
</div>
<div class="inline field">
<label for="description">模型描述</label>
<label for="description">{{.i18n.Tr "repo.model.manage.modeldesc"}}</label>
<textarea style="width: 83%;margin-left: 7px;" id="Description" name="Description" rows="3"
maxlength="255" placeholder='{{.i18n.Tr "repo.modelarts.train_job.new_place"}}'
onchange="this.value=this.value.substring(0, 255)"


+ 2
- 2
templates/repo/datasets/index.tmpl View File

@@ -406,7 +406,7 @@
<a class="item" href="{{.RepoLink}}/datasets/reference_datasets">{{$.i18n.Tr "dataset.linked_dataset"}}</a>
</div>
{{if $.CanWrite}}
<a class="ui green button" href="{{.RepoLink}}/datasets/create">{{$.i18n.Tr "dataset.new_dataset"}}</a>
<a class="ui green button" href="{{.RepoLink}}/datasets/create">{{$.i18n.Tr "new_dataset"}}</a>
{{end}}
</div>
</div>
@@ -417,7 +417,7 @@
<div class="bgtask-content">
<div class="bgtask-content-txt">{{.i18n.Tr "dataset.dataset_explain"}}</div>
<div class="bgtask-content-txt">{{.i18n.Tr "dataset.dataset_instructions_for_use"}}<a
href="https://git.openi.org.cn/zeizei/OpenI_Learning">{{.i18n.Tr "dataset.dataset_camp_course"}}</a></div>
href="https://git.openi.org.cn/zeizei/OpenI_Learning">&nbsp;{{.i18n.Tr "dataset.dataset_camp_course"}}</a></div>
</div>
</div>
</div>


+ 5
- 3
templates/repo/grampus/trainjob/gpu/new.tmpl View File

@@ -164,7 +164,7 @@
<span>
<i class="question circle icon link" data-content={{.i18n.Tr "repo.modelarts.train_job.boot_file_helper"}} data-position="right center" data-variation="mini"></i>
</span>
<a href="https://git.openi.org.cn/OpenIOSSG/MNIST_PytorchExample_GPU/src/branch/master/train_for_c2net.py" target="_blank">查看样例</a>
<a href="https://git.openi.org.cn/OpenIOSSG/MNIST_PytorchExample_GPU/src/branch/master/train_for_c2net.py" target="_blank">{{.i18n.Tr "cloudbrain.view_sample"}}</a>
</div>

@@ -243,17 +243,19 @@
let value = ''
value += `<div class="two fields width85" id= "para${i}">`
value += '<div class="field">'
let placeholder_value='{{.i18n.Tr "repo.modelarts.train_job.parameter_value"}}'
let placeholder_name='{{.i18n.Tr "repo.modelarts.train_job.parameter_name"}}'
if(flag){
value +=`<input type="text" class="shipping_first-name" value="${paramsObject.label}">`
}else{
value +='<input type="text" class="shipping_first-name" required placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_name"}}>'
value +='<input type="text" class="shipping_first-name" required placeholder="' + placeholder_name+ '">'
}
value += '</div>'
value += '<div class="field">'
if(flag){
value +=`<input type="text" class="shipping_last-name" value="${paramsObject.value}">`
}else{
value +='<input type="text" class="shipping_last-name" required placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_value"}}>'
value +='<input type="text" class="shipping_last-name" required placeholder="' + placeholder_value+ '">'
}
value += '</div>'
value += '<span><i class="trash icon"></i></span>'


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

@@ -269,17 +269,19 @@
let value = ''
value += `<div class="two fields width85" id= "para${i}">`
value += '<div class="field">'
let placeholder_value='{{.i18n.Tr "repo.modelarts.train_job.parameter_value"}}'
let placeholder_name='{{.i18n.Tr "repo.modelarts.train_job.parameter_name"}}'
if(flag){
value +=`<input type="text" class="shipping_first-name" value="${paramsObject.label}">`
}else{
value +='<input type="text" class="shipping_first-name" required placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_name"}}>'
value +='<input type="text" class="shipping_first-name" required placeholder="' + placeholder_name+ '">'
}
value += '</div>'
value += '<div class="field">'
if(flag){
value +=`<input type="text" class="shipping_last-name" value="${paramsObject.value}">`
}else{
value +='<input type="text" class="shipping_last-name" required placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_value"}}>'
value +='<input type="text" class="shipping_last-name" required placeholder="' + placeholder_value+ '">'
}
value += '</div>'
value += '<span><i class="trash icon"></i></span>'


+ 17
- 12
templates/repo/grampus/trainjob/show.tmpl View File

@@ -419,7 +419,12 @@
<td class="ti-text-form-content">
<div class="text-span text-span-w">
{{range $m ,$n := $.datasetDownload}}
<a href="{{.RepositoryLink}}" target="_blank">{{.DatasetName}}</a>
{{if eq .IsDelete true}}
{{.DatasetName}}({{$.i18n.Tr "dataset.file_deleted"}})
{{else}}
<a href="{{.RepositoryLink}}" target="_blank">{{.DatasetName}}</a>
{{end}}
{{end}}
</div>
</td>
@@ -470,11 +475,11 @@
<div class="ui tab" data-tab="second{{$k}}">
<div style="position: relative;">
<span>
<a title="滚动到顶部" style="position: absolute; right: -32px;cursor: pointer;"
<a title="{{$.i18n.Tr "repo.log_scroll_start"}}" style="position: absolute; right: -32px;cursor: pointer;"
class="log_top" data-version="{{.VersionName}}"><i class="icon-to-top"></i></a>
</span>
<span class="log-info-{{.VersionName}}">
<a title="滚动到底部" style="position: absolute; bottom: 10px;right: -32px;cursor: pointer;"
<a title="{{$.i18n.Tr "repo.log_scroll_end"}}" style="position: absolute; bottom: 10px;right: -32px;cursor: pointer;"
class="log_bottom" data-version="{{.VersionName}}"><i
class="icon-to-bottom"></i></a>
</span>
@@ -541,7 +546,7 @@
<div id="newmodel">
<div class="ui modal second">
<div class="header" style="padding: 1rem;background-color: rgba(240, 240, 240, 100);">
<h4 id="model_header">导入新模型</h4>
<h4 id="model_header">{{.i18n.Tr "repo.model.manage.import_new_model"}}</h4>
</div>
<div class="content content-padding">
<form id="formId" method="POST" class="ui form">
@@ -551,26 +556,26 @@
<input type="hidden" name="trainTaskCreate" value="true">

<div class="required inline field">
<label>训练任务</label>
<label>{{.i18n.Tr "repo.model.manage.createtrainjob"}}</label>
<input type="hidden" class="width83" id="JobId" name="JobId" readonly required>
<input type="hidden" id="VersionName" name="VersionName" value="V0001">
<input style="width: 45%;" id="JobName" readonly required>
</div>

<div class="required inline field" id="modelname">
<label>模型名称</label>
<label>{{.i18n.Tr "repo.model.manage.model_name"}}</label>
<input style="width: 45%;" id="name" name="Name" required maxlength="25"
onkeyup="this.value=this.value.replace(/[, ]/g,'')">
</div>
<div class="required inline field" id="verionname">
<label>模型版本</label>
<label>{{.i18n.Tr "repo.modelconvert.modelversion"}}</label>
<input style="width: 45%;" id="version" name="Version" value="" readonly required maxlength="255">
</div>
<div class="unite min_title inline field required">
<label>模型框架</label>
<label>{{.i18n.Tr "repo.model.manage.engine"}}</label>
<div class="ui dropdown selection search width70" id="choice_Engine">
<input type="hidden" id="Engine" name="Engine" required>
<div class="default text">选择模型框架</div>
<div class="default text">{{.i18n.Tr "repo.model.manage.select.engine"}}</div>
<i class="dropdown icon"></i>
<div class="menu" id="job-Engine">
<option class="active item" data-value="0">PyTorch</option>
@@ -586,7 +591,7 @@
</div>
<div class="unite min_title inline fields required">
<div class="field required">
<label for="modelSelectedFile">模型文件</label>
<label for="modelSelectedFile">{{.i18n.Tr "repo.model.manage.modelfile"}}</label>
</div>
<div class="thirteen wide field" style="position:relative">
<input id="modelSelectedFile" type="text" readonly required onclick="showMenu();" name="modelSelectedFile" >
@@ -596,12 +601,12 @@
</div>
</div>
<div class="inline field">
<label>模型标签</label>
<label>{{.i18n.Tr "repo.model.manage.modellabel"}}</label>
<input style="width: 83%;margin-left: 7px;" id="label" name="Label" maxlength="255"
placeholder='{{.i18n.Tr "repo.modelarts.train_job.label_place"}}'>
</div>
<div class="inline field">
<label for="description">模型描述</label>
<label for="description">{{.i18n.Tr "repo.model.manage.modeldesc"}}</label>
<textarea style="width: 83%;margin-left: 7px;" id="Description" name="Description" rows="3"
maxlength="255" placeholder='{{.i18n.Tr "repo.modelarts.train_job.new_place"}}'
onchange="this.value=this.value.substring(0, 255)"


+ 5
- 4
templates/repo/modelarts/inferencejob/new.tmpl View File

@@ -196,7 +196,7 @@
{{end}}
</select>
</div>
<span class="tooltips" style="margin-left: 11.5rem;margin-bottom: 1rem;"></span>
<!-- 数据集 -->
<div id="select-multi-dataset">

@@ -277,7 +277,6 @@
<div class="ui labeled input" style="width: 5%;">
<input style="border-radius: 0;text-align: center;" name="work_server_number" id="trainjob_work_server_num" tabindex="3" autofocus required maxlength="255" value="1" readonly>
</div>
<span class="tooltips" style="margin-left: 11.5rem;display: block;">{{.i18n.Tr "cloudbrain.inference_output_path_rule"}}</span>
</div>
<!-- 表单操作 -->
<div class="inline min_title field">
@@ -410,12 +409,14 @@

// 参数增加、删除、修改、保存
function Add_parameter(i){
let placeholder_value='{{.i18n.Tr "repo.modelarts.train_job.parameter_value"}}'
let placeholder_name='{{.i18n.Tr "repo.modelarts.train_job.parameter_name"}}'
value = '<div class="two fields width85" id= "para'+ i +'">' +
'<div class="field">' +
'<input type="text" name="shipping_first-name" required placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_name"}}> ' +
'<input type="text" name="shipping_first-name" required placeholder="' + placeholder_name+ '">' +
'</div> ' +
'<div class="field"> ' +
'<input type="text" name="shipping_last-name" required placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_value"}}>' +
'<input type="text" name="shipping_last-name" required placeholder="' + placeholder_value + '">' +
'</div>'+
'<span>' +
'<i class="trash icon">' +


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

@@ -314,7 +314,7 @@ td, th {
</tr>
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
创建人
{{$.i18n.Tr "repo.cloudbrain_creator"}}
</td>

<td class="ti-text-form-content">
@@ -441,7 +441,14 @@ td, th {
<tbody>
{{range $m ,$n := $.datasetDownload}}
<tr>
<td style="word-wrap: break-word;word-break: break-all;"><a href="{{.RepositoryLink}}" target="_blank">{{.DatasetName}}</a></td>
<td style="word-wrap: break-word;word-break: break-all;">
{{if eq .IsDelete true}}
{{.DatasetName}}({{$.i18n.Tr "dataset.file_deleted"}})
{{else}}
<a href="{{.RepositoryLink}}" target="_blank">{{.DatasetName}}</a>
{{end}}
</td>
</tr>
{{end}}


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

@@ -350,7 +350,7 @@

<td class="ti-text-form-content">
<div class="text-span text-span-w" id="{{.VersionName}}-mirror">
<span class="ui poping up clipboard" data-position="top center" id="clipboard-btn" style="cursor:pointer"
<span class="ui poping up clipboard" data-position="top center" id="clipboard-btn-image" style="cursor:pointer"
data-clipboard-text="{{.Image}}"
data-success="{{$.i18n.Tr "repo.copy_link_success"}}"
data-error="{{$.i18n.Tr "repo.copy_link_error"}}"
@@ -439,9 +439,16 @@
<tbody>
{{range $.datasetDownload}}
<tr>
<td style="word-wrap: break-word;word-break: break-all;"><a href="{{.RepositoryLink}}" target="_blank">{{.DatasetName}}</a></td>
<td style="word-wrap: break-word;word-break: break-all;">
{{if eq .IsDelete true}}
{{.DatasetName}}({{$.i18n.Tr "dataset.file_deleted"}})
{{else}}
<a href="{{.RepositoryLink}}" target="_blank">{{.DatasetName}}</a>
{{end}}
</td>
<td style="word-wrap: break-word;word-break: break-all;">{{.DatasetDownloadLink}}</td>
<td class="center aligned"><a class="ui poping up clipboard" id="clipboard-btn1" data-original="{{$.i18n.Tr "repo.copy_link"}}" data-success="{{$.i18n.Tr "repo.copy_link_success"}}" data-error="{{$.i18n.Tr "repo.copy_link_error"}}" data-content="{{$.i18n.Tr "repo.copy_link"}}" data-variation="inverted tiny" data-clipboard-text="{{.DatasetDownloadLink}}">复制链接</a></td>
<td class="center aligned"><a class="ui poping up clipboard" id="clipboard-btn-dataset" data-original="{{$.i18n.Tr "repo.copy_link"}}" data-success="{{$.i18n.Tr "repo.copy_link_success"}}" data-error="{{$.i18n.Tr "repo.copy_link_error"}}" data-content="{{$.i18n.Tr "repo.copy_link"}}" data-variation="inverted tiny" data-clipboard-text="{{.DatasetDownloadLink}}">{{$.i18n.Tr "dataset.download_copy"}}</a></td>
</tr>
{{end}}
</tbody>
@@ -491,5 +498,4 @@
$(document).ready(function () {
$('.secondary.menu .item').tab();
});
console.log({{$.datasetDownload}})
</script>

+ 6
- 4
templates/repo/modelarts/trainjob/edit_para.tmpl View File

@@ -76,10 +76,10 @@
{{range .para}}
<div class="two fields">
<div class="field">
<input type="text" name="shipping_first-name" placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_name"}}>
<input type="text" name="shipping_first-name" placeholder="{{.i18n.Tr "repo.modelarts.train_job.parameter_name"}}">
</div>
<div class="field">
<input type="text" name="shipping_last-name" placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_value"}}>
<input type="text" name="shipping_last-name" placeholder="{{.i18n.Tr "repo.modelarts.train_job.parameter_value"}}">
</div>
<span>
<i class="trash icon">
@@ -151,12 +151,14 @@

// 参数增加、删除、修改、保存
function Add_parameter(){
let placeholder_value='{{.i18n.Tr "repo.modelarts.train_job.parameter_value"}}'
let placeholder_name='{{.i18n.Tr "repo.modelarts.train_job.parameter_name"}}'
value = '<div class="two fields">' +
'<div class="field">' +
'<input type="text" name="shipping_first-name" placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_name"}}> ' +
'<input type="text" name="shipping_first-name" placeholder="' + placeholder_name+ '">' +
'</div> ' +
'<div class="field"> ' +
'<input type="text" name="shipping_last-name" placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_value"}}>' +
'<input type="text" name="shipping_last-name" placeholder="' + placeholder_value+ '">' +
'</div>'+
'<span>' +
'<i class="trash icon">' +


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

@@ -332,12 +332,14 @@
});
// 参数增加、删除、修改、保存
function Add_parameter(i) {
let placeholder_value='{{.i18n.Tr "repo.modelarts.train_job.parameter_value"}}'
let placeholder_name='{{.i18n.Tr "repo.modelarts.train_job.parameter_name"}}'
value = '<div class="two fields width85" id= "para' + i + '">' +
'<div class="field">' +
'<input type="text" name="shipping_first-name" required placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_name"}}> ' +
'<input type="text" name="shipping_first-name" required placeholder="' + placeholder_name+ '">' +
'</div> ' +
'<div class="field"> ' +
'<input type="text" name="shipping_last-name" required placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_value"}}>' +
'<input type="text" name="shipping_last-name" required placeholder="' + placeholder_value+ '">' +
'</div>' +
'<span>' +
'<i class="trash icon">' +


+ 19
- 13
templates/repo/modelarts/trainjob/show.tmpl View File

@@ -320,7 +320,7 @@
data-tab="first{{$k}}">{{$.i18n.Tr "repo.modelarts.train_job.config"}}</a>
<a class="item log_bottom" data-tab="second{{$k}}"
data-version="{{.VersionName}}">{{$.i18n.Tr "repo.modelarts.log"}}</a>
<a class="item metric_chart" data-tab="four{{$k}}" data-version="{{.VersionName}}">资源占用情况</a>
<a class="item metric_chart" data-tab="four{{$k}}" data-version="{{.VersionName}}">{{$.i18n.Tr "cloudbrain.resource_use"}}</a>
<a class="item load-model-file" data-tab="third{{$k}}" data-download-flag="{{$.canDownload}}" data-path="{{$.RepoLink}}/modelarts/train-job/{{.JobID}}/model_list" data-version="{{.VersionName}}" data-parents="" data-filename="" data-init="init" >{{$.i18n.Tr "repo.model_download"}}</a>
</div>
<div class="ui tab active" data-tab="first{{$k}}">
@@ -489,7 +489,13 @@
{{if eq $k $m}}
{{range $f ,$g := $n}}
<tr>
<td style="word-wrap: break-word;word-break: break-all;"><a href="{{.RepositoryLink}}" target="_blank">{{.DatasetName}}</a></td>
<td style="word-wrap: break-word;word-break: break-all;">
{{if eq .IsDelete true}}
{{.DatasetName}}({{$.i18n.Tr "dataset.file_deleted"}})
{{else}}
<a href="{{.RepositoryLink}}" target="_blank">{{.DatasetName}}</a>
{{end}}
</td>
</tr>
{{end}}
{{end}}
@@ -514,11 +520,11 @@
<div
style="position: relative;border: 1px solid rgba(0,0,0,.2);padding: 0 10px;margin-top: 10px;">
<span>
<a title="滚动到顶部" style="position: absolute; right: -32px;cursor: pointer;"
<a title="{{$.i18n.Tr "repo.log_scroll_start"}}" style="position: absolute; right: -32px;cursor: pointer;"
class="log_top" data-version="{{.VersionName}}"><i class="icon-to-top"></i></a>
</span>
<span class="log-info-{{.VersionName}}">
<a title="滚动到底部" style="position: absolute; bottom: 10px;right: -32px;cursor: pointer;"
<a title="{{$.i18n.Tr "repo.log_scroll_end"}}" style="position: absolute; bottom: 10px;right: -32px;cursor: pointer;"
class="log_bottom" data-version="{{.VersionName}}"><i
class="icon-to-bottom"></i></a>
</span>
@@ -593,7 +599,7 @@
<div id="newmodel">
<div class="ui modal second">
<div class="header" style="padding: 1rem;background-color: rgba(240, 240, 240, 100);">
<h4 id="model_header">导入新模型</h4>
<h4 id="model_header">{{.i18n.Tr "repo.model.manage.import_new_model"}}</h4>
</div>
<div class="content content-padding">
<form id="formId" method="POST" class="ui form">
@@ -604,34 +610,34 @@

<div class="two inline fields ">
<div class="required ten wide field">
<label style="margin-left: -23px;">选择训练任务</label>
<label style="margin-left: -23px;">{{.i18n.Tr "repo.model.manage.createtrainjob"}}</label>
<input type="hidden" class="width83" id="JobId" name="JobId" readonly required>
<input class="width83" id="JobName" readonly required>

</div>
<div class="required six widde field">
<label>版本</label>
<label>{{.i18n.Tr "repo.model.manage.version"}}</label>
<input class="width70" id="VersionName" name="VersionName" readonly required>
</div>
</div>

<div class="required inline field" id="modelname">
<label>模型名称</label>
<label>{{.i18n.Tr "repo.model.manage.model_name"}}</label>
<input style="width: 45%;" id="name" name="Name" required maxlength="25"
onkeyup="this.value=this.value.replace(/[, ]/g,'')">
</div>
<div class="required inline field" id="verionname">
<label>模型版本</label>
<label>{{.i18n.Tr "repo.modelconvert.modelversion"}}</label>
<input style="width: 45%;" id="version" name="Version" value="" readonly required maxlength="255">
</div>
<div class="unite min_title inline field required">
<label>模型框架</label>
<label>{{.i18n.Tr "repo.model.manage.engine"}}</label>
<input type="hidden" id="Engine" name="Engine" required>
<input style="width: 45%;" id="Engine_name" name="Engine_name" readonly required maxlength="255">
</div>
<div class="unite min_title inline fields required">
<div class="field required">
<label for="modelSelectedFile">模型文件</label>
<label for="modelSelectedFile">{{.i18n.Tr "repo.model.manage.modelfile"}}</label>
</div>
<div class="thirteen wide field" style="position:relative">
<input id="modelSelectedFile" type="text" readonly required onclick="showMenu();" name="modelSelectedFile" >
@@ -641,12 +647,12 @@
</div>
</div>
<div class="inline field">
<label>模型标签</label>
<label>{{.i18n.Tr "repo.model.manage.modellabel"}}</label>
<input style="width: 83%;margin-left: 7px;" id="label" name="Label" maxlength="255"
placeholder='{{.i18n.Tr "repo.modelarts.train_job.label_place"}}'>
</div>
<div class="inline field">
<label for="description">模型描述</label>
<label for="description">{{.i18n.Tr "repo.model.manage.modeldesc"}}</label>
<textarea style="width: 83%;margin-left: 7px;" id="Description" name="Description" rows="3"
maxlength="255" placeholder='{{.i18n.Tr "repo.modelarts.train_job.new_place"}}'
onchange="this.value=this.value.substring(0, 255)"


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

@@ -73,12 +73,50 @@
<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="display_job_name" name="display_job_name" value="{{.display_job_name}}">
{{template "custom/wait_count_train" Dict "ctx" $}}
<div style="display: flex;align-items: center;margin-left: 155px;margin-top: 0.5rem;">
<h4 class="unite title ui header ">{{.i18n.Tr "repo.modelarts.train_job.basic_info"}}:</h4>
<div class="required min_title inline field">
<label class="" style="font-weight: normal;">{{.i18n.Tr "cloudbrain.resource_cluster"}}</label>
<div class="ui blue mini menu compact selectcloudbrain">
<a class="active item" href="javascript:void 0;" style="cursor:not-allowed;">
<svg class="svg" sxmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="none" d="M0 0h24v24H0z"></path><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-2.29-2.333A17.9 17.9 0 0 1 8.027 13H4.062a8.008 8.008 0 0 0 5.648 6.667zM10.03 13c.151 2.439.848 4.73 1.97 6.752A15.905 15.905 0 0 0 13.97 13h-3.94zm9.908 0h-3.965a17.9 17.9 0 0 1-1.683 6.667A8.008 8.008 0 0 0 19.938 13zM4.062 11h3.965A17.9 17.9 0 0 1 9.71 4.333 8.008 8.008 0 0 0 4.062 11zm5.969 0h3.938A15.905 15.905 0 0 0 12 4.248 15.905 15.905 0 0 0 10.03 11zm4.259-6.667A17.9 17.9 0 0 1 15.973 11h3.965a8.008 8.008 0 0 0-5.648-6.667z"></path></svg>
{{.i18n.Tr "cloudbrain.resource_cluster_openi"}}
</a>
<a class="item" href="javascript:void 0;" style="cursor:not-allowed;background:rgba(0,0,0,.03);">
<svg class="svg" sxmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="none" d="M0 0h24v24H0z"></path><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-2.29-2.333A17.9 17.9 0 0 1 8.027 13H4.062a8.008 8.008 0 0 0 5.648 6.667zM10.03 13c.151 2.439.848 4.73 1.97 6.752A15.905 15.905 0 0 0 13.97 13h-3.94zm9.908 0h-3.965a17.9 17.9 0 0 1-1.683 6.667A8.008 8.008 0 0 0 19.938 13zM4.062 11h3.965A17.9 17.9 0 0 1 9.71 4.333 8.008 8.008 0 0 0 4.062 11zm5.969 0h3.938A15.905 15.905 0 0 0 12 4.248 15.905 15.905 0 0 0 10.03 11zm4.259-6.667A17.9 17.9 0 0 1 15.973 11h3.965a8.008 8.008 0 0 0-5.648-6.667z"></path></svg>
{{.i18n.Tr "cloudbrain.resource_cluster_c2net"}}(Beta)
</a>
</div>
</div>
<div class="required inline min_title field">
<label class="" style="font-weight: normal;">{{.i18n.Tr "cloudbrain.compute_resource"}}</label>
<div class="ui blue mini menu compact selectcloudbrain">
<a class="item" href="javascript:void 0;" style="cursor:not-allowed;background:rgba(0,0,0,.03);">
<svg class="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16"
height="16">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M3 2.992C3 2.444 3.445 2 3.993 2h16.014a1 1 0 0 1 .993.992v18.016a.993.993 0 0 1-.993.992H3.993A1 1 0 0 1 3 21.008V2.992zM19 11V4H5v7h14zm0 2H5v7h14v-7zM9 6h6v2H9V6zm0 9h6v2H9v-2z" />
</svg>
CPU/GPU
</a>
<a class="active item" href="javascript:void 0;" style="cursor:not-allowed;">
<svg class="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16"
height="16">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M3 2.992C3 2.444 3.445 2 3.993 2h16.014a1 1 0 0 1 .993.992v18.016a.993.993 0 0 1-.993.992H3.993A1 1 0 0 1 3 21.008V2.992zM19 11V4H5v7h14zm0 2H5v7h14v-7zM9 6h6v2H9V6zm0 9h6v2H9v-2z" />
</svg>
Ascend NPU</a>
</div>
</div>
<div style="margin-top:-5px;">
{{template "custom/wait_count_train" Dict "ctx" $}}
</div>
<div style="display: flex;align-items: center;margin-left: 155px;margin-top: 0.5rem;margin-bottom: 1.5rem;">
<i class="ri-error-warning-line" style="color: #f2711c;margin-right: 0.5rem;"></i>
<span style="color: #888;font-size: 12px;">{{.i18n.Tr "cloudbrain.train_dataset_path_rule" | Safe}}</span>
</div>
<h4 class="unite title ui header ">{{.i18n.Tr "repo.modelarts.train_job.basic_info"}}:</h4>
<div class="required unite min_title inline field">
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.job_name"}}</label>
<input type="hidden" style="width: 60%;" name="job_name" id="job_name" value="{{.job_name}}">
@@ -280,12 +318,14 @@

// 参数增加、删除、修改、保存
function Add_parameter(i){
let placeholder_value='{{.i18n.Tr "repo.modelarts.train_job.parameter_value"}}'
let placeholder_name='{{.i18n.Tr "repo.modelarts.train_job.parameter_name"}}'
value = '<div class="two fields width85" id= "para'+ i +'">' +
'<div class="field">' +
'<input type="text" name="shipping_first-name" required placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_name"}}> ' +
'<input type="text" name="shipping_first-name" required placeholder="' + placeholder_name+ '">' +
'</div> ' +
'<div class="field"> ' +
'<input type="text" name="shipping_last-name" required placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_value"}}>' +
'<input type="text" name="shipping_last-name" required placeholder="' + placeholder_value+ '">' +
'</div>'+
'<span>' +
'<i class="trash icon">' +


+ 28
- 29
templates/repo/modelmanage/convertIndex.tmpl View File

@@ -47,13 +47,12 @@
{{if eq .MODEL_CONVERT_COUNT 0}}
<div class="ui placeholder segment bgtask-none">
<div class="ui icon header bgtask-header-pic"></div>
<div class="bgtask-content-header">未创建过模型转换任务</div>
<div class="bgtask-content-header">{{$.i18n.Tr "repo.modelconvert.notcreate"}}</div>
<div class="bgtask-content">
{{if eq .MODEL_COUNT 0}}
<div class="bgtask-content-txt">请您先导入<a href="{{.RepoLink}}/modelmanage/show_model">模型</a>,然后再对其进行转换。</div>
<div class="bgtask-content-txt">{{$.i18n.Tr "repo.modelconvert.importfirst1"}}<a href="{{.RepoLink}}/modelmanage/show_model">&nbsp;{{$.i18n.Tr "repo.modelconvert.importfirst2"}}&nbsp;</a>{{$.i18n.Tr "repo.modelconvert.importfirst3"}}</div>
{{end}}
<div class="bgtask-content-txt">使用说明:可以参考启智AI协作平台<a
href="https://git.openi.org.cn/zeizei/OpenI_Learning">小白训练营课程。</a></div>
<div class="bgtask-content-txt">{{$.i18n.Tr "repo.platform_instructions1"}}<a href="https://git.openi.org.cn/zeizei/OpenI_Learning">&nbsp;{{$.i18n.Tr "repo.platform_instructions2"}}&nbsp;</a>{{$.i18n.Tr "repo.platform_instructions3"}}</div>

</div>
</div>
@@ -68,19 +67,19 @@
<div class="ui grid stackable" style="background: #f0f0f0;;">
<div class="row">
<div class="three wide column padding0">
<span style="margin:0 6px">任务名称</span>
<span style="margin:0 6px">{{$.i18n.Tr "repo.modelconvert.taskname"}}</span>
</div>
<div class="two wide column text center padding0">
<span>状态</span>
<span>{{$.i18n.Tr "repo.modelconvert.taskstatus"}}</span>
</div>
<div class="two wide column text center padding0">
<span>原模型框架</span>
<span>{{$.i18n.Tr "repo.modelconvert.srcengine"}}</span>
</div>
<div class="two wide column text center padding0">
<span>转换后格式</span>
<span>{{$.i18n.Tr "repo.modelconvert.outputformat"}}</span>
</div>
<div class="two wide column text center padding0">
<span>创建时间</span>
<span>{{$.i18n.Tr "repo.modelconvert.createtime"}}</span>
</div>
<div class="one wide column text center padding0">
<span>{{$.i18n.Tr "repo.cloudbrain_creator"}}</span>
@@ -143,10 +142,10 @@

{{if .IsCanOper}}
<a id="ai-download-{{.ID}}" href="{{$.Repository.HTMLURL}}/modelmanage/download_model_convert/{{.ID}}?AllDownload=true&a=1" class='ui basic {{if eq .Status "SUCCEEDED" "COMPLETED"}}blue {{else}}disabled {{end}}button' style="border-radius: .28571429rem;">
下载
{{$.i18n.Tr "repo.modelconvert.download"}}
</a>
{{else}}
<a class="ui basic disabled button">下载</a>
<a class="ui basic disabled button">{{$.i18n.Tr "repo.modelconvert.download"}}</a>
{{end}}
</div>
</div>
@@ -211,7 +210,7 @@
<input type="hidden" name="_csrf" value="">
<div class="unite min_title required inline fields" id="task_name">
<div class="three wide field right aligned">
<label for="model_convert_name">任务名称</label>
<label for="model_convert_name">{{$.i18n.Tr "repo.modelconvert.taskname"}}</label>
</div>
<div class="twelve wide field">
<input id="model_convert_name" name="model_convert_name" required maxlength="25" onkeyup="this.value=this.value.replace(/[, ]/g,'')">
@@ -219,10 +218,10 @@
</div>
<div class="unite min_title inline fields required">
<div class="three wide field right aligned">
<label for="choice_model">模型名称</label>
<label for="choice_model">{{$.i18n.Tr "repo.modelconvert.modelname"}}</label>
</div>
<div class="ui dropdown selection search eight wide field loading" id="choice_model" name="choice_model">
<div class="default text">选择模型</div>
<div class="default text">{{$.i18n.Tr "repo.modelconvert.selectmodel"}}</div>
<i class="dropdown icon"></i>
<div class="menu" id="model-name">
</div>
@@ -230,12 +229,12 @@
</div>
<div class="unite min_title inline fields required">
<div class="three wide field right aligned">
<label for="choice_version">模型版本</label>
<label for="choice_version">{{$.i18n.Tr "repo.modelconvert.modelversion"}}</label>
</div>
<div class="ui dropdown selection search eight wide field" id="choice_version">
<input type="hidden" id="ModelVersion" name="ModelVersion" required>
<div class="default text">选择版本</div>
<div class="default text">{{$.i18n.Tr "repo.modelconvert.selectversion"}}</div>
<i class="dropdown icon"></i>
<div class="menu" id="model-version">

@@ -244,11 +243,11 @@
</div>
<div class="unite min_title inline fields required">
<div class="three wide field right aligned">
<label for="choice_file">模型文件</label>
<label for="choice_file">{{$.i18n.Tr "repo.model.manage.modelfile"}}</label>
</div>
<div class="ui dropdown selection search eight wide field" id="choice_file">
<input type="hidden" id="ModelFile" name="ModelFile" required>
<div class="default text">选择模型文件</div>
<div class="default text">{{$.i18n.Tr "repo.modelconvert.selectmodelfile"}}</div>
<i class="dropdown icon"></i>
<div class="menu" id="model-file">

@@ -261,7 +260,7 @@
</div>
<div class="unite min_title inline fields required">
<div class="three wide field right aligned">
<label for="SrcEngine">原模型框架</label>
<label for="SrcEngine">{{$.i18n.Tr "repo.modelconvert.srcengine"}}</label>
</div>
<select id="SrcEngine" class="ui search dropdown eight wide field" placeholder="" style='color:#000000;' name="SrcEngine" onchange="javascript:srcEngineChanged()">
@@ -270,7 +269,7 @@
</div>
<div class="unite min_title required inline fields" id="inputdataformat_div">
<div class="three wide field right aligned">
<label for="inputdataformat">输入数据格式</label>
<label for="inputdataformat">{{$.i18n.Tr "repo.modelconvert.inputdataformat"}}</label>
</div>
<select id="inputdataformat" class="ui search dropdown eight wide field" placeholder="" style='width:50%' name="inputdataformat">
@@ -281,16 +280,16 @@
</div>
<div class="unite min_title required inline fields" id="inputshape_div">
<div class="three wide field right aligned">
<label for="inputshape">输入张量形状</label>
<label for="inputshape">{{$.i18n.Tr "repo.modelconvert.inputshape"}}</label>
</div>
<div class="eight wide field">
<input id="inputshape" name="inputshape" placeholder="如:1,1,32,32,与输入数据格式对应。" required maxlength="25">
<input id="inputshape" name="inputshape" placeholder="{{$.i18n.Tr "repo.modelconvert.inputshapetip"}}" required maxlength="25">
</div>
</div>
<div class="unite min_title inline fields required">
<div class="three wide field right aligned">
<label for="DestFormat">转换后格式</label>
<label for="DestFormat">{{$.i18n.Tr "repo.modelconvert.outputformat"}}</label>
</div>
<select id="DestFormat" class="ui search dropdown eight wide field" placeholder="" style='width:50%' name="DestFormat">
@@ -300,7 +299,7 @@
</div>
<div class="unite min_title inline fields">
<div class="three wide field right aligned">
<label for="NetOutputFormat">网络输出数据类型&nbsp;&nbsp;</label>
<label for="NetOutputFormat">{{$.i18n.Tr "repo.modelconvert.netoutputdata"}}&nbsp;&nbsp;</label>
</div>
<select id="NetOutputFormat" class="ui search dropdown eight wide field" placeholder="" style='width:50%' name="NetOutputFormat">
@@ -310,7 +309,7 @@
</div>
<div class="unite min_title inline fields">
<div class="three wide field right aligned">
<label for="Description">任务描述&nbsp;&nbsp;</label>
<label for="Description">{{$.i18n.Tr "repo.modelconvert.taskdesc"}}&nbsp;&nbsp;</label>
</div>
<div class="twelve wide field">
<textarea id="Description" name="Description" rows="1" maxlength="255" placeholder='{{.i18n.Tr "repo.modelarts.train_job.new_place"}}' onchange="this.value=this.value.substring(0, 255)" onkeydown="this.value=this.value.substring(0, 255)" onkeyup="this.value=this.value.substring(0, 256)"></textarea>
@@ -318,7 +317,7 @@
</div>
<div class="unite min_title inline field">
<button id="submitId" name="submitId" type="button" class="ui create_train_job green button" style="position: absolute;margin-left: 150px;">
新建任务
{{$.i18n.Tr "repo.modelconvert.newtask"}}
</button>
</div>
</div>
@@ -357,7 +356,7 @@
data['name']= $('#model_convert_name').val()

if(data['name']==""){
$('.ui.error.message').text("请输入任务名称。")
$('.ui.error.message').text("{{.i18n.Tr "repo.modelconvert.tasknameempty"}}")
$('.ui.error.message').css('display','block')
$("#task_name").addClass("error")
return false
@@ -371,7 +370,7 @@
data['inputshape']= $('#inputshape').val();

if(inputshapeNotValid(data['inputshape'])){
$('.ui.error.message').text("格式输入错误,请输入如:1,1,32,32,与输入数据格式对应。")
$('.ui.error.message').text("{{.i18n.Tr "repo.modelconvert.inputshapeerror"}}")
$('.ui.error.message').css('display','block')
$("#inputshape_div").addClass("error")
return false
@@ -411,7 +410,7 @@
.modal({
centered: false,
onShow:function(){
$('#model_header').text("创建模型转换任务")
$('#model_header').text("{{.i18n.Tr "repo.modelconvert.createtask"}}")
$('.ui.dimmer').css({"background-color":"rgb(136, 136, 136,0.7)"})
createModelName()
loadModelList()


+ 11
- 11
templates/repo/modelmanage/convertshowinfo.tmpl View File

@@ -173,7 +173,7 @@ td, th {
<h4 class="ui header" id="vertical-segment">
<div class="ui breadcrumb">
<a class="section" href="{{$.RepoLink}}/modelmanage/convert_model">
模型转换任务
{{.i18n.Tr "repo.modelconvert.taskurlname"}}
</a>
<div class="divider"> / </div>
<div class="active section">{{.Name}}</div>
@@ -243,7 +243,7 @@ td, th {

<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
输入张量形状
{{$.i18n.Tr "repo.modelconvert.inputshape"}}
</td>

<td class="ti-text-form-content">
@@ -255,7 +255,7 @@ td, th {
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
输入数据格式
{{$.i18n.Tr "repo.modelconvert.inputdataformat"}}
</td>

<td class="ti-text-form-content">
@@ -291,7 +291,7 @@ td, th {

<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
网络输出数据类型
{{$.i18n.Tr "repo.modelconvert.netoutputdata"}}
</td>
<td class="ti-text-form-content">
@@ -311,7 +311,7 @@ td, th {
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
模型名称
{{$.i18n.Tr "repo.modelconvert.modelname"}}
</td>

<td class="ti-text-form-content">
@@ -324,7 +324,7 @@ td, th {

<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
模型版本
{{$.i18n.Tr "repo.modelconvert.modelversion"}}
</td>

<td class="ti-text-form-content">
@@ -337,7 +337,7 @@ td, th {

<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
模型文件
{{$.i18n.Tr "repo.model.manage.modelfile"}}
</td>

<td class="ti-text-form-content">
@@ -350,7 +350,7 @@ td, th {

<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
原模型框架
{{$.i18n.Tr "repo.modelconvert.srcengine"}}
</td>

<td class="ti-text-form-content">
@@ -362,7 +362,7 @@ td, th {

<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
转换后格式
{{$.i18n.Tr "repo.modelconvert.outputformat"}}
</td>

<td class="ti-text-form-content">
@@ -439,10 +439,10 @@ td, th {
<div class="ui tab" data-tab="five">
<div style="position: relative;">
<span>
<a title="滚动到顶部" style="position: absolute; right: -32px;cursor: pointer;" class="log_top" data-version="V0001"><i class="icon-to-top"></i></a>
<a title="{{$.i18n.Tr "repo.log_scroll_start"}}" style="position: absolute; right: -32px;cursor: pointer;" class="log_top" data-version="V0001"><i class="icon-to-top"></i></a>
</span>
<span>
<a title="滚动到底部" style="position: absolute; bottom: 10px;right: -32px;cursor: pointer;" class="log_bottom" data-version="V0001"><i class="icon-to-bottom"></i></a>
<a title="{{$.i18n.Tr "repo.log_scroll_end"}}" style="position: absolute; bottom: 10px;right: -32px;cursor: pointer;" class="log_bottom" data-version="V0001"><i class="icon-to-bottom"></i></a>
</span>
<div id="log_npu_message" class="ui message message" style="display: none;">
<div id="log_npu_header"></div>


+ 19
- 20
templates/repo/modelmanage/index.tmpl View File

@@ -64,17 +64,16 @@
{{if eq $.MODEL_COUNT 0}}
<div class="ui placeholder segment bgtask-none">
<div class="ui icon header bgtask-header-pic"></div>
<div class="bgtask-content-header">未创建过模型</div>
<div class="bgtask-content-header">{{$.i18n.Tr "repo.model.manage.notcreatemodel"}}</div>
<div class="bgtask-content">
{{if $.RepoIsEmpty}}
<div class="bgtask-content-txt">代码版本:您还没有初始化代码仓库,请先<a href="{{.RepoLink}}">创建代码版本;</a></div>
<div class="bgtask-content-txt">{{$.i18n.Tr "repo.model.manage.init1"}}<a href="{{.RepoLink}}">{{$.i18n.Tr "repo.model.manage.init2"}}</a></div>
{{end}}
{{if eq $.TRAIN_COUNT 0}}
<div class="bgtask-content-txt">训练任务:您还没创建过训练任务,请先创建<a
href="{{.RepoLink}}/modelarts/train-job">训练任务</a>。</div>
<div class="bgtask-content-txt">{{$.i18n.Tr "repo.model.manage.createtrainjob_tip"}}<a
href="{{.RepoLink}}/modelarts/train-job">&nbsp;{{$.i18n.Tr "repo.model.manage.createtrainjob"}}</a></div>
{{end}}
<div class="bgtask-content-txt">使用说明:可以参考启智AI协作平台<a
href="https://git.openi.org.cn/zeizei/OpenI_Learning">小白训练营课程。</a></div>
<div class="bgtask-content-txt">{{$.i18n.Tr "repo.platform_instructions1"}}<a href="https://git.openi.org.cn/zeizei/OpenI_Learning">&nbsp;{{$.i18n.Tr "repo.platform_instructions2"}}&nbsp;</a>{{$.i18n.Tr "repo.platform_instructions3"}}</div>

</div>
<div style="display: none;">
@@ -108,11 +107,11 @@
<div id="deletemodel">
<div class="ui basic modal first">
<div class="ui icon header">
<i class="trash icon"></i> 删除模型
<i class="trash icon"></i> {{.i18n.Tr "repo.model.manage.delete"}}
</div>

<div class="content">
<p>你确认删除该模型么?此模型一旦删除不可恢复。</p>
<p>{{.i18n.Tr "repo.model.manage.delete_confirm"}}</p>
</div>
<div class="actions">
<div class="ui red basic inverted cancel button">
@@ -139,21 +138,21 @@
<input type="hidden" name="_csrf" value="">
<div class="inline fields">
<div class="required two wide field right aligned">
<label for="JobId">选择训练任务</label>
<label for="JobId">{{.i18n.Tr "repo.model.manage.select.trainjob"}}</label>
</div>
<div class="required thirteen wide inline field">
<div class="ui dropdown selection search loading" id="choice_model">
<input type="hidden" id="JobId" name="JobId" required>
<div class="default text">选择训练任务</div>
<div class="default text">{{.i18n.Tr "repo.model.manage.select.trainjob"}}</div>
<i class="dropdown icon"></i>
<div class="menu" id="job-name">
</div>
</div>
<label for="VersionName">版本</label>
<label for="VersionName">{{.i18n.Tr "repo.model.manage.version"}}</label>
<span>&nbsp;</span>
<div class="ui dropdown selection search" id="choice_version">
<input type="hidden" id="VersionName" name="VersionName" required>
<div class="default text">选择版本</div>
<div class="default text">{{.i18n.Tr "repo.model.manage.select.version"}}</div>
<i class="dropdown icon"></i>
<div class="menu" id="job-version">

@@ -163,7 +162,7 @@
</div>
<div class="required inline fields" id="modelname">
<div class="two wide field right aligned">
<label for="Name">模型名称</label>
<label for="Name">{{.i18n.Tr "repo.model.manage.model_name"}}</label>
</div>
<div class="eight wide field">
<input id="name" name="Name" required maxlength="25" onkeyup="this.value=this.value.replace(/[, ]/g,'')">
@@ -171,7 +170,7 @@
</div>
<div class="required inline fields" id="verionname">
<div class="two wide field right aligned">
<label for="Version">模型版本</label>
<label for="Version">{{.i18n.Tr "repo.model.manage.version"}}</label>
</div>
<div class="eight wide field">
<input id="version" name="Version" value="" readonly required maxlength="255">
@@ -180,11 +179,11 @@

<div class="unite min_title inline fields required">
<div class="two wide field right aligned">
<label for="Engine">模型框架</label>
<label for="Engine">{{.i18n.Tr "repo.model.manage.engine"}}</label>
</div>
<div class="ui ten wide field dropdown selection search" id="choice_Engine">
<input type="hidden" id="Engine" name="Engine" required>
<div class="default text newtext">选择模型框架</div>
<div class="default text newtext">{{.i18n.Tr "repo.model.manage.select.engine"}}</div>
<i class="dropdown icon"></i>
<div class="menu" id="job-Engine">

@@ -195,7 +194,7 @@

<div class="unite min_title inline fields required">
<div class="two wide field right aligned">
<label for="modelSelectedFile">模型文件</label>
<label for="modelSelectedFile">{{.i18n.Tr "repo.model.manage.modelfile"}}</label>
</div>
<div class="thirteen wide field" style="position:relative">
<input id="modelSelectedFile" type="text" readonly required onclick="showMenu();" name="modelSelectedFile" >
@@ -207,7 +206,7 @@

<div class="inline fields">
<div class="two wide field right aligned">
<label for="Label">模型标签 &nbsp</label>
<label for="Label">{{.i18n.Tr "repo.model.manage.modellabel"}} &nbsp</label>
</div>
<div class="thirteen wide field">
<input id="label" name="Label" maxlength="255" placeholder='{{.i18n.Tr "repo.modelarts.train_job.label_place"}}'>
@@ -215,7 +214,7 @@
</div>
<div class="inline fields">
<div class="two wide field right aligned">
<label for="description">模型描述 &nbsp</label>
<label for="description">{{.i18n.Tr "repo.model.manage.modeldesc"}} &nbsp</label>
</div>
<div class="thirteen wide field">
<textarea id="Description" name="Description" rows="3"
@@ -331,7 +330,7 @@
.modal({
centered: false,
onShow: function () {
$('#model_header').text("导入新模型")
$('#model_header').text({{.i18n.Tr "repo.model.manage.import_new_model"}})
$('input[name="Version"]').addClass('model_disabled')
$('.ui.dimmer').css({ "background-color": "rgb(136, 136, 136,0.7)" })
$("#job-name").empty()


+ 2
- 2
templates/repo/modelmanage/showinfo.tmpl View File

@@ -86,7 +86,7 @@
</div>
<div class="ui tab active" data-tab="first">
<div class="half-table">
<span class="model_header_text">基本信息</span>
<span class="model_header_text">{{$.i18n.Tr "repo.model.manage.baseinfo"}}</span>
<table class="tableStyle" style="margin-top:20px;">
<tbody>
<tr>
@@ -125,7 +125,7 @@
</td>
</tr>
<tr>
<td class="ti-text-form-label text-width80">训练任务</td>
<td class="ti-text-form-label text-width80">{{$.i18n.Tr "repo.model.manage.createtrainjob"}}</td>
<td class="ti-text-form-content word-elipsis">
<a id="DisplayJobNameHref" class="title" style="font-size: 14px;" target="_blank">
<span id="DisplayJobName" class="fitted" style="width: 90%;vertical-align: middle;"></span>


+ 3
- 1
web_src/js/components/MinioUploader.vue View File

@@ -18,7 +18,7 @@
<label
class="el-form-item__label"
style="width: 140px; position: absolute; left: -140px"
>上传状态:</label
>{{ upload_status }}:</label
>
<div v-for="(item, index) in uploadFiles" class="datast-upload-progress">
<span class="dataset-name nowrap" :title="item.name">{{
@@ -103,6 +103,7 @@ export default {
btnFlag: false,
cancel: "",
upload: "",
upload_status: "",
uploadFiles: [],
uploadFilesAddId: [],
// allUploadFiles: [],
@@ -119,6 +120,7 @@ export default {
this.repoPath = this.dropzoneParams.data("repopath");
this.cancel = this.dropzoneParams.data("cancel");
this.upload = this.dropzoneParams.data("upload");
this.upload_status = this.dropzoneParams.data("upload-status");
let previewTemplate = `
<div class="dz-preview dz-file-preview">
<div class="dz-image">


+ 22
- 15
web_src/js/components/Model.vue View File

@@ -14,7 +14,7 @@
>
<el-table-column
prop="Name"
label="模型名称"
:label="i18n.model_name"
align="left"
min-width="18%"
>
@@ -28,7 +28,7 @@
</el-table-column>
<el-table-column
prop="Version"
label="版本"
:label="i18n.model_version"
align="center"
min-width="6.5%"
>
@@ -39,7 +39,7 @@
<el-table-column
prop="VersionCount"
label="版本数"
:label="i18n.model_version_num"
align="center"
min-width="7.5%"
>
@@ -50,7 +50,7 @@

<el-table-column
prop="Size"
label="模型大小"
:label="i18n.model_size"
align="center"
min-width="10.5%"
>
@@ -60,7 +60,7 @@
</el-table-column>
<el-table-column
prop="EngineName"
label="模型框架"
:label="i18n.model_egine"
align="center"
min-width="8.5%"
>
@@ -70,7 +70,7 @@
</el-table-column>
<el-table-column
prop="ComputeResource"
label="计算资源"
:label="i18n.model_compute_resource"
align="center"
min-width="10.5%"
>
@@ -80,7 +80,7 @@
</el-table-column>
<el-table-column
prop="CreatedUnix"
label="创建时间"
:label="i18n.model_create_time"
align="center"
min-width="13.75%"
>
@@ -90,7 +90,7 @@
</el-table-column>
<el-table-column
prop="UserName"
label="创建者"
:label="i18n.model_creator"
align="center"
min-width="6.75%"
>
@@ -101,12 +101,12 @@
</template>
</el-table-column>

<el-table-column label="操作" min-width="18%" align="center">
<el-table-column :label="i18n.model_operation" min-width="18%" align="center">
<template slot-scope="scope">
<div class="space-around">
<a :style="{visibility:!scope.row.Children ? 'visible':'hidden'}" :class="{'disabled':!scope.row.IsCanOper}" @click="showcreateVue(scope.row.Name,scope.row.Version,scope.row.Label)">创建新版本</a>
<a :href="loadhref+scope.row.ID" :class="{'disabled':!scope.row.IsCanOper}">下载</a>
<a :class="{'disabled':!scope.row.IsCanDelete}" @click="deleteModel(scope.row.ID,scope.row.cName,scope.row.rowKey)">删除</a>
<a :style="{visibility:!scope.row.Children ? 'visible':'hidden'}" :class="{'disabled':!scope.row.IsCanOper}" @click="showcreateVue(scope.row.Name,scope.row.Version,scope.row.Label)">{{i18n.model_create_new_ver}}</a>
<a :href="loadhref+scope.row.ID" :class="{'disabled':!scope.row.IsCanOper}">{{i18n.model_download}}</a>
<a :class="{'disabled':!scope.row.IsCanDelete}" @click="deleteModel(scope.row.ID,scope.row.cName,scope.row.rowKey)">{{i18n.model_delete}}</a>
</div>
</template>
@@ -141,7 +141,7 @@ export default {
},
data() {
return {
i18n: {},
currentPage:1,
pageSize:10,
totalNum:0,
@@ -197,11 +197,12 @@ export default {
this.getModelList()
},
showcreateVue(name,version,label){
let title= this.i18n.model_create_version_title;
$('.ui.modal.second')
.modal({
centered: false,
onShow:function(){
$('#model_header').text("创建模型新版本")
$('#model_header').text(title)
$('input[name="Name"]').addClass('model_disabled')
$('input[name="Name"]').attr('readonly','readonly')
$('input[name="modelSelectedFile"]').attr('readonly','readonly')
@@ -451,7 +452,13 @@ export default {
this.url_create_newVersion = this.url + 'create_model'
this.url_create_newModel = this.url + 'create_new_model'
},

created() {
if (document.documentElement.attributes["lang"].nodeValue == "en-US") {
this.i18n = this.$locale.US;
} else {
this.i18n = this.$locale.CN;
}
},
beforeDestroy() { // 实例销毁之前对点击事件进行解绑
this.submitId.removeEventListener('click', this.submit);
}


+ 33
- 21
web_src/js/components/dataset/referenceDataset.vue View File

@@ -84,18 +84,21 @@
v-if="item.Category"
:href="'/explore/datasets?category=' + item.Category"
class="ui repo-topic label topic"
@click.stop
>{{ i18n[item.Category] || item.Category }}</a
>
<a
v-if="item.Task"
:href="'/explore/datasets?task=' + item.Task"
class="ui repo-topic label topic"
@click.stop
>{{ i18n[item.Task] || item.Task }}</a
>
<a
v-if="item.License"
:href="'/explore/datasets?license=' + item.License"
class="ui repo-topic label topic"
@click.stop
>{{ item.License }}</a
>
</div>
@@ -111,6 +114,7 @@
<a
:href="'/' + item.Repo.OwnerName"
:title="item.Repo.OwnerName"
@click.stop
>
<img
class="ui avatar image"
@@ -278,6 +282,7 @@
:href="`/${item.Repo.OwnerName}/${item.Repo.Name}/datasets`"
:title="`${item.Repo.OwnerName}/${item.Repo.Alias}`"
style="font-size: 12px"
target="_blank"
>{{ item.Repo.OwnerName }}/{{ item.Repo.Alias }}</a
>
</div>
@@ -334,19 +339,18 @@
overflow-y: auto;
"
>
<el-checkbox-group v-model="checkList">
<el-checkbox
v-for="(item, index) in selectDatasetArray"
:key="index"
:label="item.ID"
:title="item.Title"
@change="(checked) => changeCheckSelected(checked, item)"
style="display: flex; margin: 0.5rem 0"
><span class="select-data-right">{{
item.Title
}}</span></el-checkbox
>
</el-checkbox-group>
<el-checkbox
v-for="(item, index) in selectDatasetArray"
:key="index"
:label="item.ID"
:title="item.Title"
:value="item.isChecked"
@change="(checked) => changeCheckSelected(checked, item)"
style="display: flex; margin: 0.5rem 0"
><span class="select-data-right">{{
item.Title
}}</span></el-checkbox
>
</div>
<div style="text-align: end">
<el-button
@@ -401,7 +405,11 @@ export default {
methods: {
openDataset() {
this.checkList = this.datasetList.map((item) => {
this.selectDatasetArray.push({ ID: item.ID, Title: item.Title });
this.selectDatasetArray.push({
ID: item.ID,
Title: item.Title,
isChecked: true,
});
return item.ID;
});
this.dialogVisible = true;
@@ -468,7 +476,11 @@ export default {
return;
}
if (checked) {
this.selectDatasetArray.push({ ID: item.ID, Title: item.Title });
this.selectDatasetArray.push({
ID: item.ID,
Title: item.Title,
isChecked: true,
});
} else {
let index = this.selectDatasetArray.findIndex((element) => {
return element.ID === item.ID;
@@ -477,12 +489,11 @@ export default {
}
},
changeCheckSelected(checked, item) {
if (!checked) {
let index = this.selectDatasetArray.findIndex((element) => {
return element.ID === item.ID;
});
this.selectDatasetArray.splice(index, 1);
}
let index = this.selectDatasetArray.findIndex((element) => {
return element.ID === item.ID;
});
this.selectDatasetArray.splice(index, 1);
this.checkList.splice(index, 1);
},
postStar(item, isSigned) {
if (!isSigned) {
@@ -705,6 +716,7 @@ export default {
max-width: 35%;
word-break: initial;
margin-left: 1rem;
white-space: nowrap;
}
/deep/ .el-checkbox-group .el-checkbox .el-checkbox__label {
display: flex;


+ 42
- 28
web_src/js/components/dataset/selectDataset.vue View File

@@ -11,9 +11,9 @@
v-if="benchmarkNew"
class="label-fix-width"
style="font-weight: normal"
>数据集</label
>{{ i18n.dataset_label }}</label
>
<label v-else>数据集</label>
<label v-else>{{ i18n.dataset_label }}</label>
<span
:class="
benchmarkNew === true ? 'dataset-train-span' : 'dataset-debug-span'
@@ -38,7 +38,7 @@
type="text"
class="disabled"
style="width: 48.5%"
placeholder="选择数据集文件"
:placeholder="i18n.dataset_select_placeholder"
required
/>
<input
@@ -46,7 +46,7 @@
type="text"
class="disabled"
:required="required"
placeholder="选择数据集文件"
:placeholder="i18n.dataset_select_placeholder"
/>
</span>

@@ -59,10 +59,10 @@
? 'select-dataset-button'
: 'select-dataset-button-color'
"
>选择数据集
>{{ i18n.dataset_select }}
</el-button>
<el-dialog
title="选择数据集"
:title="i18n.dataset_select"
:visible.sync="dialogVisible"
:width="dialogWidth"
>
@@ -74,7 +74,7 @@
></i>
<input
type="text"
placeholder="搜数据集名称/描述..."
:placeholder="i18n.dataset_search_placeholder"
v-model="search"
@keydown.enter.stop.prevent="searchName"
/>
@@ -90,7 +90,11 @@
>
<el-tabs v-model="activeName" @tab-click="handleClick">
<!-- 当前项目的数据集 -->
<el-tab-pane label="本项目" name="first" v-loading="loadingCurrent">
<el-tab-pane
:label="i18n.dataset_current_repo"
name="first"
v-loading="loadingCurrent"
>
<el-row>
<el-tree
:data="currentDatasetList"
@@ -144,7 +148,7 @@
<i
class="ri-links-line"
style="color: #21ba45; margin-right: 0.3rem"
title="关联数据集"
:title="i18n.dataset_relate"
v-if="
'/' + data.Repo.OwnerName + '/' + data.Repo.Name !==
repoLink
@@ -172,13 +176,13 @@
class="zip-loading"
v-if="data.DecompressState === 2"
>
正在解压缩
{{ i18n.dataset_unziping }}
</span>
<span
class="unzip-failed"
v-if="data.DecompressState === 3"
>
解压失败
{{ i18n.dataset_unzip_failed }}
</span>
</span>
</span>
@@ -201,7 +205,11 @@
</div>
</el-tab-pane>
<!-- 我上传的数据集 -->
<el-tab-pane label="我上传的" name="second" v-loading="loadingMy">
<el-tab-pane
:label="i18n.dataset_my_upload"
name="second"
v-loading="loadingMy"
>
<el-row>
<el-tree
:data="myDatasetList"
@@ -274,13 +282,13 @@
class="zip-loading"
v-if="data.DecompressState === 2"
>
正在解压缩
{{ i18n.dataset_unziping }}
</span>
<span
class="unzip-failed"
v-if="data.DecompressState === 3"
>
解压失败
{{ i18n.dataset_unzip_failed }}
</span>
</span>
</span>
@@ -304,7 +312,7 @@
</el-tab-pane>
<!-- 公开的数据集 -->
<el-tab-pane
label="公开数据集"
:label="i18n.dataset_public"
name="third"
v-loading="loadingPublic"
>
@@ -380,13 +388,13 @@
class="zip-loading"
v-if="data.DecompressState === 2"
>
正在解压缩
{{ i18n.dataset_unziping }}
</span>
<span
class="unzip-failed"
v-if="data.DecompressState === 3"
>
解压失败
{{ i18n.dataset_unzip_failed }}
</span>
</span>
</span>
@@ -410,7 +418,7 @@
</el-tab-pane>
<!-- 我点赞的数据集 -->
<el-tab-pane
label="我收藏的"
:label="i18n.dataset_collected"
name="four"
v-loading="loadingFavorite"
>
@@ -486,13 +494,13 @@
class="zip-loading"
v-if="data.DecompressState === 2"
>
正在解压缩
{{ i18n.dataset_unziping }}
</span>
<span
class="unzip-failed"
v-if="data.DecompressState === 3"
>
解压失败
{{ i18n.dataset_unzip_failed }}
</span>
</span>
</span>
@@ -536,7 +544,7 @@
line-height: 40px;
"
>
已选数据文件
{{ i18n.dataset_selected }}
</div>
<div style="flex: 1; margin-top: 1.5rem">
<el-checkbox-group v-model="checkList">
@@ -558,7 +566,7 @@
color: #fff;
border: 1px solid #389e0d;
"
>确定</el-button
>{{ i18n.dataset_ok }}</el-button
>
</div>
</el-col>
@@ -585,7 +593,7 @@ export default {
activeName: "first",
search: "",
required: true,
i18n: {},
type: 0,
repoLink: "",
selectDatasetArray: [],
@@ -666,10 +674,10 @@ export default {
})
) {
this.$refs[data.ref].setChecked(data.id, false, false);
this.$message.warning("不能选择相同名称的数据文件");
this.$message.warning(this.i18n.dataset_not_equal_file);
} else if (this.selectDatasetArray.length === 5) {
this.$refs[data.ref].setChecked(data.id, false, false);
this.$message.error("最多不超过五个文件");
this.$message.error(this.i18n.dataset_most);
} else {
this.selectDatasetArray.push(data);
}
@@ -732,7 +740,6 @@ export default {
.then((res) => {
this.loadingCurrent = false;
let data = JSON.parse(res.data.data);
console.log(data);
this.currentDatasetList = this.transformeTreeData(
data,
"currentTree",
@@ -978,7 +985,10 @@ export default {
let hasSelectDatasetName = $(".cloudbrain-type")
.data("dataset-name")
.split(";");
if (this.hasSelectDatasetList.length !== 0) {
if (
this.hasSelectDatasetList.length !== 0 &&
hasSelectDatasetName[0] !== ""
) {
this.saveStatusList = this.hasSelectDatasetList;
this.checkList = hasSelectDatasetName;
this.hasSelectDatasetList.forEach((item, index) => {
@@ -996,7 +1006,6 @@ export default {
location.href.indexOf("train-job") !== -1 ||
location.href.indexOf("inference") !== -1
) {
console.log("this.benchmarkNew");
this.benchmarkNew = true;
}
if (
@@ -1013,6 +1022,11 @@ export default {
},
created() {
this.setDialogWidth();
if (document.documentElement.attributes["lang"].nodeValue == "en-US") {
this.i18n = this.$locale.US;
} else {
this.i18n = this.$locale.CN;
}
},
};
</script>


+ 5
- 6
web_src/js/components/images/adminImages.vue View File

@@ -1,9 +1,8 @@
<template>
<div>
<div class="ui container" style="width: 80%;">
<div class="ui grid">
<div class="row" style="border: 1px solid #d4d4d5;margin-top: 15px;padding-top: 0;">

<div class="ui container" style="width: 100% !important;padding-right: 0;">
<div class="ui grid" style="margin: 0 !important">
<div class="row" style="border: 1px solid #d4d4d5;margin-top:0px;padding-top: 0;">
<div class="ui attached segment">
<div class="ui form ignore-dirty">
<div class="ui fluid action input">
@@ -31,8 +30,8 @@
<div class="ui six wide column right aligned" style="margin: 1rem 0;">
<a class="ui blue small button" href="/admin/images/commit_image">创建云脑镜像</a>
</div>
<div class="ui sixteen wide column" style="padding: 0;">
<el-table :data="tableDataCustom" style="width: 100%" :header-cell-style="tableHeaderStyle">
<div class="ui sixteen wide column" style="padding: 0;overflow-x: auto;">
<el-table :data="tableDataCustom" style="width: 100%;min-width:1700px;" :header-cell-style="tableHeaderStyle">
<el-table-column label="镜像Tag" min-width="19%" align="left" prop="tag">
<template slot-scope="scope">
<div style="display: flex;align-items: center;">


+ 17
- 10
web_src/js/components/images/selectGrampusImages.vue View File

@@ -7,32 +7,32 @@
v-if="benchmarkNew"
class="label-fix-width"
style="font-weight: normal"
>镜像</label
>{{i18n.image_label}}</label
>
<label v-else>镜像</label>
<label v-else>{{i18n.image_label}}</label>
<input
v-if="benchmarkNew"
type="text"
name="image"
:value="imageAddress"
style="width: 48.5%"
placeholder="选择镜像或输入镜像地址"
:placeholder="i18n.image_select_placeholder"
/>
<input
v-else
type="text"
name="image"
:value="imageAddress"
placeholder="选择镜像或输入镜像地址"
:placeholder="i18n.image_select_placeholder"
/>
<el-button
type="text"
@click="dialogVisible = true"
icon="el-icon-plus"
style="color: #0366d6"
>选择镜像
>{{i18n.image_select}}
</el-button>
<el-dialog title="选择镜像" :visible.sync="dialogVisible" width="50%">
<el-dialog :title="i18n.image_select" :visible.sync="dialogVisible" width="50%">
<div
class="ui icon input"
style="z-index: 9999; position: absolute; right: 50px; height: 30px"
@@ -43,12 +43,12 @@
></i>
<input
type="text"
placeholder="搜镜像Tag/描述/标签..."
:placeholder="i18n.image_search_placeholder"
v-model="search"
/>
</div>
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="公开镜像" name="first" v-loading="loadingPublic">
<el-tab-pane :label="i18n.image_public" name="first" v-loading="loadingPublic">
<div
style="
display: flex;
@@ -120,7 +120,7 @@
selectImages(publicData.place, publicData.tag)
"
>
使用
{{i18n.image_use}}
</button>
</div>
</div>
@@ -169,6 +169,7 @@ export default {
},
tableDataPublic: [],
loadingPublic: false,
i18n: {},
};
},
methods: {
@@ -236,7 +237,13 @@ export default {
this.benchmarkNew = true;
}
},
created() {},
created() {
if (document.documentElement.attributes["lang"].nodeValue == "en-US") {
this.i18n = this.$locale.US;
} else {
this.i18n = this.$locale.CN;
}
},
};
</script>



+ 24
- 18
web_src/js/components/images/selectImages.vue View File

@@ -7,16 +7,16 @@
v-if="benchmarkNew"
class="label-fix-width"
style="font-weight: normal"
>镜像</label
>{{i18n.image_label}}</label
>
<label v-else>镜像</label>
<label v-else>{{i18n.image_label}}</label>
<input
v-if="benchmarkNew"
type="text"
name="image"
:value="imageAddress"
style="width: 48.5%"
placeholder="选择镜像或输入镜像地址"
:placeholder="i18n.image_select_placeholder"
required
/>
<input
@@ -24,7 +24,7 @@
type="text"
name="image"
:value="imageAddress"
placeholder="选择镜像或输入镜像地址"
:placeholder="i18n.image_select_placeholder"
required
/>
<el-button
@@ -32,9 +32,9 @@
@click="dialogVisible = true"
icon="el-icon-plus"
style="color: #0366d6"
>选择镜像
>{{i18n.image_select}}
</el-button>
<el-dialog title="选择镜像" :visible.sync="dialogVisible" width="50%">
<el-dialog :title="i18n.image_select" :visible.sync="dialogVisible" width="50%">
<div
class="ui icon input"
style="z-index: 9999; position: absolute; right: 50px; height: 30px"
@@ -45,12 +45,12 @@
></i>
<input
type="text"
placeholder="搜镜像Tag/描述/标签..."
:placeholder="i18n.image_search_placeholder"
v-model="search"
/>
</div>
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="公开镜像" name="first" v-loading="loadingPublic">
<el-tab-pane :label="i18n.image_public" name="first" v-loading="loadingPublic">
<div
style="
display: flex;
@@ -122,7 +122,7 @@
selectImages(publicData.place, publicData.tag)
"
>
使用
{{i18n.image_use}}
</button>
</div>
</div>
@@ -142,7 +142,7 @@
</div>
</el-tab-pane>

<el-tab-pane label="我的镜像" name="second" v-loading="loadingCustom">
<el-tab-pane :label="i18n.image_my" name="second" v-loading="loadingCustom">
<div
style="
display: flex;
@@ -208,7 +208,7 @@
selectImages(customData.place, customData.tag)
"
>
使用
{{i18n.image_use}}
</button>
<span
v-if="customData.status === 0"
@@ -217,7 +217,7 @@
<i class="CREATING"></i>
<span
style="margin-left: 0.4em; font-size: 12px; color: #5a5a5a"
>提交中</span
>{{i18n.image_commit}}</span
>
</span>
<span
@@ -228,11 +228,11 @@
<el-tooltip
class="item"
effect="dark"
content="检测提交镜像是否大小超过20G!"
:content="i18n.image_commit_content"
placement="left"
>
<span style="margin-left: 0.4em; font-size: 12px; color: red"
>提交失败</span
>{{i18n.image_commit_failed}}</span
>
</el-tooltip>
</span>
@@ -254,7 +254,7 @@
</div>
</el-tab-pane>

<el-tab-pane label="我收藏的镜像" name="third">
<el-tab-pane :label="i18n.image_collected" name="third">
<div
style="
display: flex;
@@ -324,7 +324,7 @@
class="ui primary basic button mini"
@click.stop.prevent="selectImages(starData.place, starData.tag)"
>
使用
{{i18n.image_use}}
</button>
</div>
</div>
@@ -368,7 +368,7 @@ export default {
paramsPublic: { page: 1, pageSize: 5, q: "", recommend: false },
tableDataPublic: [],
loadingPublic: false,
i18n: {},
currentPageCustom: 1,
pageSizeCustom: 5,
totalNumCustom: 0,
@@ -513,7 +513,13 @@ export default {
this.benchmark = true;
}
},
created() {},
created() {
if (document.documentElement.attributes["lang"].nodeValue == "en-US") {
this.i18n = this.$locale.US;
} else {
this.i18n = this.$locale.CN;
}
},
};
</script>



+ 92
- 11
web_src/js/features/i18nVue.js View File

@@ -50,13 +50,53 @@ export const i18nVue = {
downloads: "下载次数",
not_link_dataset: "还未关联过数据集",
no_link_dataset_tips1:
"您可以通过单击新建关联数据集按钮,将平台上公开数据集展示在这里。",
"您可以通过单击关联数据集按钮,将平台上公开数据集展示在这里。",
dataset_instructions_for_use: "使用说明:可以参考启智AI协作平台",
dataset_camp_course: "小白训练营课程",
dataset_link_success: "关联数据集成功!",
dataset_link_failed: "关联数据集失败!",
dataset_over_nums: "关联超过?个数据集",
cancel_link_dataset: "取消?关联数据集成功!",
cancel_link_dataset: "取消关联数据集?成功!",
image_label: "镜像",
image_select_placeholder: "选择镜像或输入镜像地址",
image_select: "选择镜像",
image_search_placeholder: "搜镜像Tag/描述/标签...",
image_public: "公开镜像",
image_use: "使用",
image_my: "我的镜像",
image_commit: "提交中",
image_commit_content: "检测提交镜像是否大小超过20G!",
image_commit_failed: "提交失败",
image_collected: "我收藏的镜像",
dataset_label: "数据集",
dataset_select_placeholder: "选择数据集文件",
dataset_select: "选择数据集",
dataset_search_placeholder: "搜数据集名称/描述...",
dataset_unziping: "正在解压缩",
dataset_unzip_failed: "解压失败",
dataset_my_upload: "我上传的",
dataset_current_repo: "本项目",
dataset_public: "公开数据集",
dataset_relate: "关联数据集",
dataset_collected: "我收藏的",
dataset_selected: "已选数据文件",
dataset_ok: "确定",
dataset_not_equal_file: "不能选择相同名称的数据文件",
dataset_most: "最多不超过五个文件",
model_name: "模型名称",
model_version: "版本",
model_version_num: "版本数",
model_size: "模型大小",
model_egine: "模型框架",
model_compute_resource: "计算资源",
model_create_time: "创建时间",
model_creator: "创建者",
model_operation: "操作",
model_create_new_ver: "创建新版本",
model_download: "下载",
model_delete: "删除",
model_create_title: "导入新模型",
model_create_version_title: "创建模型新版本",
},
US: {
computer_vision: "computer vision",
@@ -99,25 +139,66 @@ export const i18nVue = {
speech_synthesis: "speech synthesis",
current_dataset: "Current Dataset",
linked_datasets: "Linked Datasets",
unfavorite: "Unfavorite",
favorite: "Favorite",
disassociate: "Disassociate",
unfavorite: "UnLike",
favorite: "Like",
disassociate: "Unlink",
public_dataset: "Public Dataset",
selected_data_file: "Selected DataSets",
sure: "Ok",
sure: "OK",
search_dataset: "Search dataset name/description ...",
citations: "Citations",
downloads: "Downloads",
not_link_dataset: "No datasets have been associated yet",
not_link_dataset: "No datasets have been linked yet",
no_link_dataset_tips1:
"You can display public datasets on the platform here by clicking the New Linked Dataset button.",
"You can display public datasets on the platform here by clicking the Linked Datasets button.",

dataset_instructions_for_use:
"Instructions for use: You can refer to Qizhi AI Collaboration Platform ",
dataset_camp_course: "Newcomer Training Camp Course",
"Instructions for use: You can refer to OpenI AI Collaboration Platform ",
dataset_camp_course: " OpenI_Learning",
dataset_link_success: "Linked dataset succeeded!",
dataset_link_failed: "Linked dataset Failed!",
dataset_over_nums: "Linked over ? datasets!",
cancel_link_dataset: "Cancel ? Linked dataset succeeded!",
cancel_link_dataset: "Cancel Linked dataset ? succeeded!",
image_label: "Image",
image_select_placeholder: "Select image or input image url",
image_select: "Select Image",
image_search_placeholder: "Search image tag/description/label...",
image_public: "Public Image",
image_use: "Use",
image_my: "My Images",
image_commit: "Commiting",
image_commit_content:
"Check whether the size of the submitted image exceeds 20g.",
image_commit_failed: "Commit failed",
image_collected: "My collected images",
dataset_label: "Dataset",
dataset_select_placeholder: "Select dataset file",
dataset_select: "Select dataset",
dataset_search_placeholder: "Search dataset name/description ...",
dataset_unziping: "Decompressing",
dataset_unzip_failed: "Decompression failed",
dataset_my_upload: "Upload by me",
dataset_current_repo: "Current Repository",
dataset_public: "Public dataset",
dataset_collected: "My collection",
dataset_relate: "Related dataset",
dataset_selected: "Selected dataset file",
dataset_ok: "OK",
dataset_not_equal_file: "Cannot select a data file with the same name.",
dataset_most: "Up to five files.",
model_name: "Model Name",
model_version: "Version",
model_version_num: "Total",
model_size: "Size",
model_egine: "Engine",
model_compute_resource: "Compute Resource",
model_create_time: "Created Time",
model_creator: "Creator",
model_operation: "Operation",
model_create_new_ver: "New Version",
model_download: "Download",
model_delete: "Delete",
model_create_title: "Import new model",
model_create_version_title: "Create a new version of the model",
},
};

+ 55
- 1
web_src/less/_admin.less View File

@@ -1,5 +1,5 @@
.admin {
padding-top: 15px;
padding-top: 15px !important;

.table.segment {
padding: 0;
@@ -75,4 +75,58 @@
white-space: pre-wrap;
word-wrap: break-word;
}
display: flex;
.new-menu.navbar {
width: 230px !important;
display: flex;
flex-direction: column;
justify-content: flex-start !important;
border-bottom: none !important;
background-color: transparent !important;
.item-container {
display: flex;
flex-direction: column;
padding-top: 8px;
padding-bottom: 8px;
margin-left: 10px !important;
margin-right: 10px !important;
border: 1px solid #d4d4d5;
border-radius: 4px;
box-shadow: 0 1px 2px 0 rgb(34 36 38 / 15%);
background-color: #fafafa !important;
.item {
align-self: flex-start !important;
width: 100%;
padding-left: 20px;
&.active {
color: #40a9ff !important;
border-right: 4px solid #40a9ff;
border-radius: 0 !important;
border-bottom: none !important;
background-color: rgb(255, 255, 255);
}
&:hover {
background-color: #ffffff !important;
}
&:active {
border-color: transparent !important;
}
&.item-next {
padding-left: 45px;
}
&.item-first {
color: rgba(0,0,0,.87);
&:hover {
background-color: transparent !important;
}
}
}
}

}
>.ui.container {
flex: 1 !important;
padding-right: 10px;
}
}

+ 41
- 0
web_src/vuepages/apis/modules/point.js View File

@@ -0,0 +1,41 @@
import service from '../service';

// 算力积分概要
export const getPointAccount = () => {
return service({
url: '/reward/point/account',
method: 'get',
params: {},
});
}

// 算力积分获取、消耗明细
// operate-INCREASE 表示获取明细 DECREASE表示消耗明细, page-当前页, pageSize-每页条数
export const getPointList = (params) => {
return service({
url: '/reward/point/record/list',
method: 'get',
params,
});
}

// 管理员充值、扣减用户积分
// TargetUserId, OperateType-INCREASE,DECREASE, Amount, Remark, RewardType-POINT
export const setPointOperate = (data) => {
return service({
url: '/operation/reward/point/account/operate',
method: 'post',
data,
params: {}
});
}

// 算力积分页面
export const getPoint = () => {
return service({
url: '/reward/point',
method: 'get',
params: {},
data: {},
});
}

+ 174
- 0
web_src/vuepages/apis/modules/resources.js View File

@@ -0,0 +1,174 @@
import service from '../service';

// 查询智算列表
export const getAiCenterList = () => {
return service({
url: '/admin/resources/queue/centers',
method: 'get',
params: {},
data: {},
});
}

// 查询资源队列列表
// page 当前页数,从1开始
// cluster 所属集群 :OpenI 启智集群,C2Net 智算集群
// center 智算中心:OpenIOne 云脑一,OpenITwo 云脑二, chendu 成都人工智能计算中心, pclcci 鹏城云计算所 ,hefei 合肥类脑类脑智能开放平台, xuchang 中原人工智能计算中心
// resource 计算资源: GPU NPU
// card XPU类型: T4、A100、V100、Ascend 910
export const getResQueueList = (params) => {
return service({
url: '/admin/resources/queue/list',
method: 'get',
params,
});
}

// 新增资源队列
export const addResQueue = (data) => { // Cluster,QueueCode,AiCenterCode,ComputeResource,AccCardType,CardsTotalNum,Remark
return service({
url: '/admin/resources/queue/add',
method: 'post',
params: {},
data,
});
}

// 更新资源队列
export const updateResQueue = (data) => { // CardsTotalNum,Remark
return service({
url: `/admin/resources/queue/update/${data.ID}`,
method: 'post',
params: {},
data,
});
}

// 查询所有资源队列名称列表
export const getResQueueCode = (params) => { // cluster
return service({
url: '/admin/resources/queue/codes',
method: 'get',
params,
data: {},
});
}

// 同步智算网络资源池(队列)
export const syncResQueue = () => {
return service({
url: '/admin/resources/queue/grampus/sync',
method: 'post',
params: {},
data: {},
});
}

// 新增资源规格
export const addResSpecification = (data) => {
return service({
url: '/admin/resources/specification/add',
method: 'post',
params: {},
data,
});
}

// 查询资源规格所属场景 - 下架时提醒
export const getResSpecificationScenes = (data) => { // data => { ID: 1 }
return service({
url: `/admin/resources/specification/scenes/${data.ID}`,
method: 'get',
params: {},
data: {}
});
}

// 更新资源规格
// params: action edit-编辑 on-shelf 上架 off-shelf 下架
// data: UnitPrice
export const updateResSpecification = (data) => { // data => { ID: 1, action: 'edit|on-shelf|off-shelf', UnitPrice: 1 | undefined }
return service({
url: `/admin/resources/specification/update/${data.ID}`,
method: 'post',
params: { action: data.action },
data: { UnitPrice: data.action === 'edit' || data.action === 'on-shelf' ? data.UnitPrice : undefined }
});
}

// 查询资源规格列表
// page
// cluster 所属集群 :OpenI 启智集群,C2Net 智算集群
// queue 所属队列id
// status 状态 : 1 待审核 2已上架 3已下架
export const getResSpecificationList = (params) => {
return service({
url: '/admin/resources/specification/list',
method: 'get',
params,
data: {},
});
}

// 同步智算网络资源池(队列)
export const syncResSpecification = () => {
return service({
url: '/admin/resources/specification/grampus/sync',
method: 'post',
params: {},
data: {},
});
}

// 新增资源应用场景
/*
{
"SceneName":"启智集群调试任务", //应用场景名
"JobType":"TRAIN", //任务类型 DEBUG调试任务 BENCHMARK 评测任务 TRAIN 训练 INFERENCE 推理
"IsExclusive":true, //是否专属
"ExclusiveOrg":"123,456", //专属组织
"SpecIds":[2,3] // 资源规格id
}
*/
export const addResScene = (data) => {
return service({
url: '/admin/resources/scene/add',
method: 'post',
params: {},
data,
});
}

// 更新资源应用场景
// params: action:edit-编辑 delete-删除,
// data: {
// "SceneName":"启智集群调试任务", //应用场景名
// "IsExclusive":true, //是否专属
// "ExclusiveOrg":"123,456", //专属组织
// "SpecIds":[2,3] // 资源规格id
//}
export const updateResScene = (data) => {
return service({
url: `/admin/resources/scene/update/${data.ID}`,
method: 'post',
params: { action: data.action },
data: {
...data
},
});
}

// 查询资源应用场景
// page
// jobType
// center
// queue 所属队列
// IsExclusive 是否专属 1 专属 2 非专属
export const getResSceneList = (params) => {
return service({
url: '/admin/resources/scene/list',
method: 'get',
params,
data: {},
});
}

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

@@ -0,0 +1,26 @@
import axios from 'axios';

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

service.interceptors.request.use((config) => {
config.data && Object.assign(config.data, {
_csrf: window.config ? window.config.csrf : '',
});
config.params && Object.assign(config.params, {
_csrf: window.config ? window.config.csrf : '',
});
return config;
}, (error) => {
return Promise.reject(error);
});

service.interceptors.response.use((response) => {
return response;
}, (error) => {
return Promise.reject(error);
});

export default service;

+ 99
- 0
web_src/vuepages/components/BaseDialog.vue View File

@@ -0,0 +1,99 @@
<template>
<div class="base-dlg">
<el-dialog :visible.sync="dialogShow" :title="title" :width="width" :fullscreen="fullscreen" :top="top"
:modal="modal" :modal-append-to-body="modalAppendToBody" :append-to-body="appendToBody" :lock-scroll="lockScroll"
:custom-class="customClass" :close-on-click-modal="closeOnClickModal" :close-on-press-escape="closeOnPressEscape"
:show-close="showClose" :center="center" :destroy-on-close="destroyOnClose" :before-close="beforeClose"
@open="open" @opened="opened" @close="close" @closed="closed">
<template v-slot:title>
<slot name="title"></slot>
</template>
<template v-slot:default>
<slot name="default"></slot>
</template>
<template v-slot:footer>
<slot name="footer"></slot>
</template>
</el-dialog>
</div>
</template>
<script>
export default {
name: "BaseDialog",
props: {
visible: { type: Boolean, default: false },
title: { type: String, default: "" },
width: { type: String, default: "" },
fullscreen: { type: Boolean, default: false },
top: { type: String },
modal: { type: Boolean, default: true },
modalAppendToBody: { type: Boolean, default: true },
appendToBody: { type: Boolean, default: false },
lockScroll: { type: Boolean, default: false },
customClass: { type: String, default: "" },
closeOnClickModal: { type: Boolean, default: false },
closeOnPressEscape: { type: Boolean, default: true },
showClose: { type: Boolean, default: true },
beforeClose: { type: Function },
center: { type: Boolean, default: false },
destroyOnClose: { type: Boolean, default: false },
},
data() {
return {
dialogShow: false,
};
},
watch: {
visible: function (val) {
this.dialogShow = val;
},
},
methods: {
open() {
this.$emit("open");
},
opened() {
this.$emit("opened");
},
close() {
this.$emit("close");
},
closed() {
this.$emit("closed");
this.$emit("update:visible", false);
},
},
};
</script>
<style scoped lang="less">
.base-dlg {
/deep/ .el-dialog__header {
text-align: left;
height: 45px;
background: rgb(240, 240, 240);
border-radius: 5px 5px 0px 0px;
border-bottom: 1px solid rgb(212, 212, 213);
padding: 0 15px;
display: flex;
align-items: center;
font-weight: 500;
font-size: 16px;
color: rgb(16, 16, 16);

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

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

/deep/ .el-dialog__body {
padding: 15px 15px;
}
}
</style>

+ 16
- 0
web_src/vuepages/const/index.js View File

@@ -0,0 +1,16 @@
import { i18n } from '~/langs';

export const SOURCE_TYPE = [{ k: 'ACCOMPLISH_TASK', v: i18n.t('accomplishTask') }, { k: 'ADMIN_OPERATE', v: i18n.t('adminOperate') }, { k: 'RUN_CLOUDBRAIN_TASK', v: i18n.t('runCloudBrainTask') }];
export const CONSUME_STATUS = [{ k: 'OPERATING', v: i18n.t('operating') }, { k: 'SUCCEEDED', v: i18n.t('succeeded') }];
export const POINT_ACTIONS = [
{ k: 1, v: i18n.t('createPublicProject') }, { k: 6, v: i18n.t('dailyPutforwardTasks') }, { k: 7, v: i18n.t('dailyPR') }, { k: 10, v: i18n.t('comment') }, { k: 24, v: i18n.t('uploadDatasetFile') }, { k: 30, v: i18n.t('importNewModel') }, { k: 34, v: i18n.t('completeWechatCodeScanningVerification') },
{ k: 35, v: i18n.t('dailyRunCloudbrainTasks') }, { k: 36, v: i18n.t('datasetRecommendedByThePlatform') }, { k: 37, v: i18n.t('submitNewPublicImage') }, { k: 38, v: i18n.t('imageRecommendedByThePlatform') }, { k: 39, v: i18n.t('firstChangeofAvatar') }, { k: 40, v: i18n.t('dailyCommit') },
];
export const JOB_TYPE = [{ k: 'DEBUG', v: i18n.t('debugTask') }, { k: 'TRAIN', v: i18n.t('trainTask') }, { k: 'INFERENCE', v: i18n.t('inferenceTask') }, { k: 'BENCHMARK', v: i18n.t('benchmarkTask') }];

// 资源管理
export const CLUSTERS = [{ k: 'OpenI', v: i18n.t('resourcesManagement.OpenI') }, { k: 'C2Net', v: i18n.t('resourcesManagement.C2Net') }];
export const AI_CENTER = [{ k: 'OpenIOne', v: i18n.t('resourcesManagement.OpenIOne') }, { k: 'OpenITwo', v: i18n.t('resourcesManagement.OpenITwo') }, { k: 'chendu', v: i18n.t('resourcesManagement.chenduCenter') }, { k: 'pclcci', v: i18n.t('resourcesManagement.pclcci') }, { k: 'hefei', v: i18n.t('resourcesManagement.hefeiCenter') }, { k: 'xuchang', v: i18n.t('resourcesManagement.xuchangCenter') }];
export const COMPUTER_RESOURCES = [{ k: 'GPU', v: 'GPU' }, { k: 'NPU', v: 'NPU' }, { k: 'MLU', v: 'MLU' }];
export const ACC_CARD_TYPE = [{ k: 'T4', v: 'T4' }, { k: 'A100', v: 'A100' }, { k: 'V100', v: 'V100' }, { k: 'ASCEND910', v: 'Ascend 910' }, { k: 'MLU270', v: 'MLU270' }, { k: 'RTX3080', v: 'RTX3080' }];
export const SPECIFICATION_STATUS = [{ k: '1', v: i18n.t('resourcesManagement.willOnShelf') }, { k: '2', v: i18n.t('resourcesManagement.onShelf') }, { k: '3', v: i18n.t('resourcesManagement.offShelf') }];

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

@@ -0,0 +1,156 @@
const en = {
loading: 'Loading...',
noData: 'No Data',
date: 'Date',

confirm: 'Confirm',
cancel: 'Cancel',
confirm1: 'Confirm',
pleaseCompleteTheInformationFirst: 'Please Complete the Information first!',
submittedSuccessfully: 'Submitted Successfully!',
submittedFailed: 'Submitted Failed!',
operation: 'Operation',
edit: 'Edit',
delete: 'Delete',
tips: 'Tips',

accomplishTask: 'Accomplish Task',
adminOperate: 'Administrator Operation',
runCloudBrainTask: 'Run CloudBrain Task',
operating: 'Operating',
succeeded: 'Succeeded',
debugTask: 'Debug Task',
trainTask: 'Train Task',
inferenceTask: 'Inference Task',
benchmarkTask: 'Benchmark Task',
createPublicProject: 'Create Public Projects',
dailyPutforwardTasks: 'Daily Put Forward Tasks',
dailyPR: 'Daily PR',
comment: 'Comment',
uploadDatasetFile: 'Upload Dataset Files',
importNewModel: 'Import New Models',
completeWechatCodeScanningVerification: 'Complete Wechat Code Scanning Verification',
dailyRunCloudbrainTasks: 'Daily Run Cloudbrain Tasks',
datasetRecommendedByThePlatform: 'Dataset Recommended by the Platform',
submitNewPublicImage: 'Submit New Public Images',
imageRecommendedByThePlatform: 'Image Recommended by the Platform',
firstChangeofAvatar: 'First Change of Avatar',
dailyCommit: 'Daily Commit',
calcPointDetails: 'Calculation Points Details',
calcPointAcquisitionInstructions: 'Calculation Points Acquisition Instructions',
CurrAvailableCalcPoints: 'Currently Available Calculation Points',
totalGainCalcPoints: 'Total Gain of Calculation Points',
totalConsumeCalcPoints: 'Total Consume of Calculation Points',
gainDetail: 'Gain Detail',
consumeDetail: 'Consume Detail',
serialNumber: 'Serial Number',
time: 'Time',
scene: 'Scene',
behaviorOfPoint: 'Behavior Of Point',
explanation: 'Explanation',
points: 'Points',
status: 'Status',
runTime: 'Run Time',
taskName: 'Task Name',

createdRepository: 'created repository ',
openedIssue: 'opened issue ',
createdPullRequest: 'created pull request ',
commentedOnIssue: 'commented on issue ',
uploadDataset: 'upload dataset ',
createdNewModel: 'created new model ',
firstBindingWechatRewards: 'first binding wechat rewards',
created: 'created ',
type: ' type ',
dataset: 'dataset ',
setAsRecommendedDataset: ' was set as recommended dataset',
committedImage: 'committed image ',
image: 'image ',
setAsRecommendedImage: ' was set as recommended image',
updatedAvatar: 'updated avatar',
pushedBranch: 'pushed to {branch} at ',
dailyMaxTips: `can't get full points when reach the daily upper limit`,
memory: 'Memory',
sharedMemory: 'Shared Memory',
';': ', ',

noPointGainRecord: 'No Point Earn Record Yet',
noPointConsumeRecord: 'No Point Consume Record Yet',

resourcesManagement: {
OpenI: 'OpenI',
C2Net: 'C2Net',
OpenIOne: 'OpenI One',
OpenITwo: 'OpenI Two',
chenduCenter: 'ChenDu AI Center',
pclcci: 'PCL Cloud Computer Institute',
hefeiCenter: 'HeFei AI Center',
xuchangCenter: 'XuChang AI Center',
willOnShelf: 'To Be On Shelf',
onShelf: 'On Shelf',
offShelf: 'Off Shelf',
toOnShelf: 'To On Shelf',
toOffShelf: 'To Off Shelf',
toSetPriceAndOnShelf: 'To Set Price and On Shelf',
status: 'Status',
allStatus: 'All Status',
syncAiNetwork: 'Sync AI Network',
resQueue: 'Resources Queue',
allResQueue: 'All Resources Queues',
addResQueue: 'Add Resources Queue',
addResQueueBtn: 'Add Resources Queue',
editResQueue: 'Edit Resources Queue',
resQueueName: 'Resources Queue Name',
whichCluster: 'Cluster',
allCluster: 'All Clusters',
aiCenter: 'AI Center',
aiCenterID: 'AI Center ID',
allAiCenter: 'All AI Centers',
computeResource: 'Compute Resource',
allComputeResource: 'All Compute Resources',
accCardType: 'Acc Card Type',
allAccCardType: 'All Acc Card Type',
cardsTotalNum: 'Cards Total Number',
accCardsNum: 'Acc Cards Number',
remark: 'Remark',
pleaseEnterRemark: 'Please Enter Remark(The maximum length shall not exceed 255)',
pleaseEnterPositiveIntegerCardsTotalNum: 'Please Enter Positive Integer Cards Total Number!',
addResSpecificationAndPriceInfo: 'Add Resources Specification and Price Info',
addResSpecificationBtn: 'Add Resources Specification',
editResSpecificationAndPriceInfo: 'Edit Resources Specification and Price Info',
resSpecificationAndPriceManagement: 'Resources Specification and Price Management',
sourceSpecCode: 'Source Specification Code',
sourceSpecCodeTips: 'OpenI Two Should Enter the Source Specification Code',
sourceSpecId: 'Source Specification ID',
cpuNum: 'CPU Number',
gpuMem: 'GPU Memory',
mem: 'Memory',
shareMem: 'Share Memory',
unitPrice: 'Unit Price',
point_hr: 'Point/hr',
onShelfConfirm: 'Are you sure to on shelf the resources specification?',
offShelfConfirm: 'Are you sure to off shelf the resources specification?',
onShelfCode1001: 'On shelf failed, the resources queues not available.',
offShelfDlgTip1: 'The resources specification has already used in scene:',
offShelfDlgTip2: 'Please confirm to off shelf?',
resSceneManagement: 'Resources Scene Management',
addResScene: 'Add Resources Scene',
addResSceneBtn: 'Add Resources Scene',
editResScene: 'Edit Resources Scene',
resSceneName: 'Resources Scene Name',
jobType: 'Job Type',
allJobType: 'All Job Type',
isExclusive: 'Is Exclusive?',
allExclusiveAndCommonUse: 'All Exclusive and Common Use',
exclusive: 'Exclusive',
commonUse: 'Common Use',
exclusiveOrg: 'Exclusive Organization',
exclusiveOrgTips: 'Multiple organization names are separated by semicolons',
computeCluster: 'Compute Cluster',
resourceSpecification: 'Resource Specification',
lastUpdateTime: 'Last Update Time',
resSceneDeleteConfirm: 'Are you sure to delete the current Resource Scene?',
},
}

export default en;

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

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

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

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

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

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

resourcesManagement: {
OpenI: '启智集群',
C2Net: '智算集群',
OpenIOne: '云脑一',
OpenITwo: '云脑二',
chenduCenter: '成都人工智能计算中心',
pclcci: '鹏城云计算所',
hefeiCenter: '合肥类脑类脑智能开放平台',
xuchangCenter: '中原人工智能计算中心',
willOnShelf: '待上架',
onShelf: '已上架',
offShelf: '已下架',
toOnShelf: '上架',
toOffShelf: '下架',
toSetPriceAndOnShelf: '定价上架',
status: '状态',
allStatus: '全部状态',
syncAiNetwork: '同步智算网络',
resQueue: '资源池(队列)',
allResQueue: '全部资源池(队列)',
addResQueue: '新建资源池(队列)',
addResQueueBtn: '新增资源池',
editResQueue: '修改资源池(队列)',
resQueueName: '资源池(队列)名称',
whichCluster: '所属集群',
allCluster: '全部集群',
aiCenter: '智算中心',
aiCenterID: '智算中心ID',
allAiCenter: '全部智算中心',
computeResource: '计算资源',
allComputeResource: '全部计算资源',
accCardType: '卡类型',
allAccCardType: '全部卡类型',
cardsTotalNum: '卡数',
accCardsNum: '卡数',
remark: '备注',
pleaseEnterRemark: '请输入备注(最大长度不超过255)',
pleaseEnterPositiveIntegerCardsTotalNum: '请输入正整数的卡数!',
addResSpecificationAndPriceInfo: '新增资源规格和单价信息',
addResSpecificationBtn: '新增资源规格',
editResSpecificationAndPriceInfo: '修改资源规格和单价信息',
resSpecificationAndPriceManagement: '资源规格单价管理',
sourceSpecCode: '对应资源编码',
sourceSpecCodeTips: '云脑II需要填写对应的资源编码',
sourceSpecId: '智算网络资源规格ID',
cpuNum: 'CPU数',
gpuMem: '显存',
mem: '内存',
shareMem: '共享内存',
unitPrice: '单价',
point_hr: '积分/时',
onShelfConfirm: '请确认上架该规格?',
offShelfConfirm: '请确认下架该规格?',
onShelfCode1001: '上架失败,资源池(队列)不可用。',
offShelfDlgTip1: '当前资源规格已在以下场景中使用:',
offShelfDlgTip2: '请确认进行下架操作?',
resSceneManagement: '算力资源应用场景管理',
addResScene: '新建算力资源应用场景',
addResSceneBtn: '新增应用场景',
editResScene: '修改算力资源应用场景',
resSceneName: '应用场景名称',
jobType: '任务类型',
allJobType: '全部任务类型',
isExclusive: '是否专属',
allExclusiveAndCommonUse: '全部专属和通用',
exclusive: '专属',
commonUse: '通用',
exclusiveOrg: '专属组织',
exclusiveOrgTips: '多个组织名之间用英文分号隔开',
computeCluster: '算力集群',
resourceSpecification: '资源规格',
lastUpdateTime: '最后更新时间',
resSceneDeleteConfirm: '是否确认删除当前应用场景?',
},
}

export default zh;

+ 16
- 0
web_src/vuepages/langs/index.js View File

@@ -0,0 +1,16 @@
import Vue from 'vue';
import VueI18n from 'vue-i18n';
import jsCookie from 'js-cookie';
import zh from './config/zh-CN';
import en from './config/en-US';

Vue.use(VueI18n);

export const lang = jsCookie.get('lang') || 'zh-CN';
export const i18n = new VueI18n({
locale: lang,
messages: {
'zh-CN': zh,
'en-US': en
},
});

+ 259
- 0
web_src/vuepages/pages/resources/components/QueueDialog.vue View File

@@ -0,0 +1,259 @@
<template>
<div class="base-dlg">
<BaseDialog :visible.sync="dialogShow" :width="`750px`"
:title="type === 'add' ? $t('resourcesManagement.addResQueue') : $t('resourcesManagement.editResQueue')"
@open="open" @opened="opened" @close="close" @closed="closed">
<div class="dlg-content">
<div class="form">
<div class="form-row">
<div class="title required">
<span>{{ $t('resourcesManagement.resQueueName') }}</span>
</div>
<div class="content">
<el-input v-model="dataInfo.QueueCode" placeholder="" :disabled="type === 'edit'" maxlength="255"></el-input>
</div>
</div>
<div class="form-row">
<div class="title required">
<span>{{ $t('resourcesManagement.whichCluster') }}</span>
</div>
<div class="content">
<el-select v-model="dataInfo.Cluster" :disabled="type === 'edit'">
<el-option v-for="item in clusterList" :key="item.k" :label="item.v" :value="item.k" />
</el-select>
</div>
</div>
<div class="form-row">
<div class="title required">
<span>{{ $t('resourcesManagement.aiCenter') }}</span>
</div>
<div class="content">
<el-select v-model="dataInfo.AiCenterCode" :disabled="type === 'edit'">
<el-option v-for="item in computingCenterList" :key="item.k" :label="item.v" :value="item.k" />
</el-select>
</div>
</div>
<div class="form-row">
<div class="title required">
<span>{{ $t('resourcesManagement.computeResource') }}</span>
</div>
<div class="content">
<el-select v-model="dataInfo.ComputeResource" :disabled="type === 'edit'">
<el-option v-for="item in computingTypeList" :key="item.k" :label="item.v" :value="item.k" />
</el-select>
</div>
</div>
<div class="form-row">
<div class="title required">
<span>{{ $t('resourcesManagement.accCardType') }}</span>
</div>
<div class="content">
<el-select v-model="dataInfo.AccCardType" :disabled="type === 'edit'">
<el-option v-for="item in cardTypeList" :key="item.k" :label="item.v" :value="item.k" />
</el-select>
</div>
</div>
<div class="form-row">
<div class="title required">
<span>{{ $t('resourcesManagement.cardsTotalNum') }}</span>
</div>
<div class="content">
<el-input v-model="dataInfo.CardsTotalNum" type="number" placeholder=""></el-input>
</div>
</div>
<div class="form-row" style="margin-top: 10px">
<div class="title"><span>{{ $t('resourcesManagement.remark') }}</span></div>
<div class="content" style="width: 400px">
<el-input type="textarea" :autosize="{ minRows: 3, maxRows: 4 }" maxlength="255"
:placeholder="$t('resourcesManagement.pleaseEnterRemark')" v-model="dataInfo.Remark">
</el-input>
</div>
</div>
<div class="form-row" style="margin-top: 20px">
<div class="title"></div>
<div class="content">
<el-button type="primary" class="btn confirm-btn" @click="confirm">{{ $t('confirm') }}</el-button>
<el-button class="btn" @click="cancel">{{ $t('cancel') }}</el-button>
</div>
</div>
</div>
</div>
</BaseDialog>
</div>
</template>
<script>
import BaseDialog from '~/components/BaseDialog.vue';
import { addResQueue, updateResQueue } from '~/apis/modules/resources';
import { CLUSTERS, AI_CENTER, COMPUTER_RESOURCES, ACC_CARD_TYPE } from '~/const';

export default {
name: "QueueDialog",
props: {
visible: { type: Boolean, default: false },
title: { type: String, default: '' },
type: { type: String, defalut: 'add' },
data: { type: Object, default: () => ({}) },
},
components: {
BaseDialog
},
data() {
return {
dialogShow: false,
clusterList: [CLUSTERS[0]],
computingCenterList: [AI_CENTER[0], AI_CENTER[1]],
computingTypeList: [...COMPUTER_RESOURCES],
cardTypeList: [...ACC_CARD_TYPE],

dataInfo: {},
};
},
watch: {
visible: function (val) {
this.dialogShow = val;
},
},
methods: {
resetDataInfo() {
this.dataInfo = {
ID: '',
QueueCode: '',
Cluster: '',
AiCenterCode: '',
ComputeResource: '',
AccCardType: '',
CardsTotalNum: '',
Remark: '',
}
},
open() {
this.resetDataInfo();
if (this.type === 'add') {
//
} else if (this.type === 'edit') {
this.dataInfo = Object.assign(this.dataInfo, { ...this.data });
}
this.$emit("open");
},
opened() {
this.$emit("opened");
},
close() {
this.$emit("close");
},
closed() {
this.$emit("closed");
this.$emit("update:visible", false);
},
confirm() {
if (!this.dataInfo.QueueCode || !this.dataInfo.Cluster || !this.dataInfo.AiCenterCode || !this.dataInfo.ComputeResource || !this.dataInfo.AccCardType || !this.dataInfo.CardsTotalNum) {
this.$message({
type: 'info',
message: this.$t('pleaseCompleteTheInformationFirst'),
});
return;
}
if (parseInt(this.dataInfo.CardsTotalNum) != Number(this.dataInfo.CardsTotalNum)) {
this.$message({
type: 'info',
message: this.$t('pleaseEnterPositiveIntegerCardsTotalNum')
});
return;
}
const setApi = this.type === 'add' ? addResQueue : updateResQueue;
setApi({ ...this.dataInfo, CardsTotalNum: Number(this.dataInfo.CardsTotalNum) }).then(res => {
res = res.data;
if (res.Code === 0) {
this.$message({
type: 'success',
message: this.$t('submittedSuccessfully')
});
this.$emit("confirm");
} else {
this.$message({
type: 'error',
message: this.$t('submittedFailed')
});
}
}).catch(err => {
console.log(err);
this.$message({
type: 'error',
message: this.$t('submittedFailed')
});
})
},
cancel() {
this.dialogShow = false;
this.$emit("update:visible", false);
}
},
mounted() {
this.resetDataInfo();
},
};
</script>
<style scoped lang="less">
.dlg-content {
margin: 20px 0 25px 0;
display: flex;
justify-content: center;

.form {
width: 600px;

.form-row {
display: flex;
min-height: 42px;
margin-bottom: 4px;

.title {
width: 160px;
display: flex;
justify-content: flex-end;
align-items: center;
margin-right: 20px;
color: rgb(136, 136, 136);
font-size: 14px;

&.required {
span {
position: relative;
}

span::after {
position: absolute;
right: -10px;
top: -2px;
vertical-align: top;
content: '*';
color: #db2828;
}
}
}

.content {
width: 300px;
display: flex;
align-items: center;

/deep/ .el-select {
width: 100%;
}
}
}
}

.btn {
color: rgb(2, 0, 4);
background-color: rgb(194, 199, 204);
border-color: rgb(194, 199, 204);

&.confirm-btn {
color: #fff;
background-color: rgb(56, 158, 13);
border-color: rgb(56, 158, 13);
}
}
}
</style>

+ 366
- 0
web_src/vuepages/pages/resources/components/SceneDialog.vue View File

@@ -0,0 +1,366 @@
<template>
<div class="base-dlg">
<BaseDialog :visible.sync="dialogShow" :width="`750px`"
:title="type === 'add' ? $t('resourcesManagement.addResScene') : $t('resourcesManagement.editResScene')"
@open="open" @opened="opened" @close="close" @closed="closed">
<div class="dlg-content">
<div class="form">
<div class="form-row">
<div class="title required">
<span>{{ $t('resourcesManagement.resSceneName') }}</span>
</div>
<div class="content">
<el-input v-model="dataInfo.SceneName" placeholder="" maxlength="255"></el-input>
</div>
</div>
<div class="form-row">
<div class="title required">
<span>{{ $t('resourcesManagement.jobType') }}</span>
</div>
<div class="content">
<el-select v-model="dataInfo.JobType" :disabled="type === 'edit'">
<el-option v-for="item in taskTypeList" :key="item.k" :label="item.v" :value="item.k" />
</el-select>
</div>
</div>
<div class="form-row">
<div class="title required">
<span>{{ $t('resourcesManagement.isExclusive') }}</span>
</div>
<div class="content">
<el-select v-model="dataInfo.IsExclusive" @change="changeIsExclusive">
<el-option v-for="item in isExclusiveList" :key="item.k" :label="item.v" :value="item.k" />
</el-select>
</div>
</div>
<div class="form-row" v-if="dataInfo.IsExclusive === '1'">
<div class="title required">
<span>{{ $t('resourcesManagement.exclusiveOrg') }}</span>
</div>
<div class="content">
<el-input v-model="dataInfo.ExclusiveOrg" :placeholder="$t('resourcesManagement.exclusiveOrgTips')"
maxlength="255">
</el-input>
</div>
</div>
<div class="form-row">
<div class="title required">
<span>{{ $t('resourcesManagement.computeCluster') }}</span>
</div>
<div class="content">
<el-select v-model="dataInfo.Cluster" @change="changeCluster" :disabled="type === 'edit'">
<el-option v-for="item in clusterList" :key="item.k" :label="item.v" :value="item.k" />
</el-select>
</div>
</div>
<div class="form-row">
<div class="title">
<span>{{ $t('resourcesManagement.resQueue') }}</span>
</div>
<div class="content">
<el-select v-model="dataInfo.QueueId" @change="changeQueue" :disabled="type === 'edit'">
<el-option v-for="item in queueList" :key="item.k" :label="item.v" :value="item.k" />
</el-select>
</div>
</div>
<div class="form-row">
<div class="title required">
<span>{{ $t('resourcesManagement.resourceSpecification') }}</span>
</div>
<div class="content">
<el-select v-model="dataInfo.SpecIds" multiple collapse-tags class="specSel">
<el-option v-for="item in specsList" :key="item.k" :label="item.v" :value="item.k" />
</el-select>
</div>
</div>
<div class="form-row" style="margin-top: 20px">
<div class="title"></div>
<div class="content">
<el-button type="primary" class="btn confirm-btn" @click="confirm">{{ $t('confirm') }}</el-button>
<el-button class="btn" @click="cancel">{{ $t('cancel') }}</el-button>
</div>
</div>
</div>
</div>
</BaseDialog>
</div>
</template>
<script>
import BaseDialog from '~/components/BaseDialog.vue';
import { getResQueueCode, getResSpecificationList, addResScene, updateResScene } from '~/apis/modules/resources';
import { JOB_TYPE, CLUSTERS, AI_CENTER, ACC_CARD_TYPE, SPECIFICATION_STATUS } from '~/const';
import { getListValueWithKey } from '~/utils';

export default {
name: "SceneDialog",
props: {
visible: { type: Boolean, default: false },
title: { type: String, default: '' },
type: { type: String, defalut: 'add' },
data: { type: Object, default: () => ({}) },
},
components: {
BaseDialog
},
data() {
return {
dialogShow: false,
dataInfo: {},
taskTypeList: [...JOB_TYPE],
clusterList: [...CLUSTERS],
accCardTypeList: [...ACC_CARD_TYPE],
statusList: [...SPECIFICATION_STATUS],
isExclusiveList: [{ k: '2', v: this.$t('resourcesManagement.commonUse') }, { k: '1', v: this.$t('resourcesManagement.exclusive') }],
queueList: [],
specsList: [],
};
},
watch: {
visible: function (val) {
this.dialogShow = val;
},
},
methods: {
resetDataInfo() {
this.dataInfo = {
SceneName: '',
JobType: '',
IsExclusive: '2',
ExclusiveOrg: '',
Cluster: '',
QueueId: '',
SpecIds: [],
}
this.queueList.splice(0, Infinity);
this.specsList.splice(0, Infinity);
},
getQueueList(next) {
return getResQueueCode({ cluster: this.dataInfo.Cluster }).then(res => {
res = res.data;
if (res.Code === 0) {
const data = res.Data;
const list = [];
for (let i = 0, iLen = data.length; i < iLen; i++) {
const item = data[i];
list.push({
k: item.ID,
v: `${item.QueueCode}(${getListValueWithKey(this.clusterList, item.Cluster)} - ${item.AiCenterName})`,
});
}
list.unshift({
k: '-1',
v: this.$t('resourcesManagement.allResQueue'),
});
this.queueList.splice(0, Infinity, ...list);
if (next) {
if (this.type === 'add') {
this.dataInfo.QueueId = '-1';
}
this.getResSpecificationList();
}
}
}).catch(err => {
console.log(err);
});
},
getResSpecificationList() {
const params = {
cluster: this.dataInfo.Cluster,
queue: this.dataInfo.QueueId === '-1' ? '' : this.dataInfo.QueueId,
status: 2,
page: 1,
};
return getResSpecificationList(params).then(res => {
res = res.data;
if (res.Code === 0) {
const list = res.Data.List;
const data = list.map((item) => {
const Queue = item.Queue;
const Spec = item.Spec;
// const NGPU = `${Queue.ComputeResource}:${Spec.AccCardsNum === 0 ? '0' : Spec.AccCardsNum + '*' + getListValueWithKey(this.accCardTypeList, Queue.AccCardType)}`;
const NGPU = `${Queue.ComputeResource}:${Spec.AccCardsNum + '*' + getListValueWithKey(this.accCardTypeList, Queue.AccCardType)}`;
return {
k: Spec.ID,
v: `${NGPU}, CPU:${Spec.CpuCores}, ${this.$t('resourcesManagement.gpuMem')}:${Spec.GPUMemGiB}GB, ${this.$t('resourcesManagement.mem')}:${Spec.MemGiB}GB, ${this.$t('resourcesManagement.shareMem')}:${Spec.ShareMemGiB}GB, ${this.$t('resourcesManagement.unitPrice')}:${Spec.UnitPrice}${this.$t('resourcesManagement.point_hr')}`,
}
});
this.specsList.splice(0, Infinity, ...data);
}
}).catch(err => {
console.log(err);
});
},
changeIsExclusive() {
this.dataInfo.ExclusiveOrg = '';
},
changeCluster() {
this.dataInfo.QueueId = '';
this.dataInfo.SpecIds = [];
this.queueList.splice(0, Infinity);
this.specsList.splice(0, Infinity);
this.getQueueList(true);
},
changeQueue() {
this.dataInfo.SpecIds = [];
this.specsList.splice(0, Infinity);
this.getResSpecificationList();
},
open() {
this.resetDataInfo();
if (this.type === 'add') {
//
} else if (this.type === 'edit') {
Object.assign(this.dataInfo, { ...this.data, QueueId: this.data.QueueIds.length === 1 ? this.data.QueueIds[0] : '-1' });
this.queueList.splice(0, Infinity);
this.specsList.splice(0, Infinity);
this.getQueueList(true);
}
this.$emit("open");
},
opened() {
this.$emit("opened");
},
close() {
this.$emit("close");
},
closed() {
this.$emit("closed");
this.$emit("update:visible", false);
},
confirm() {
if (!this.dataInfo.SceneName || !this.dataInfo.JobType || !this.dataInfo.SpecIds.length || (this.dataInfo.IsExclusive === '1' && !this.dataInfo.ExclusiveOrg)) {
this.$message({
type: 'info',
message: this.$t('pleaseCompleteTheInformationFirst')
});
return;
}
const setApi = this.type === 'add' ? addResScene : updateResScene;
setApi({
...this.dataInfo,
action: this.type === 'edit' ? 'edit' : undefined,
IsExclusive: this.dataInfo.IsExclusive === '1',
}).then(res => {
res = res.data;
if (res.Code === 0) {
this.$message({
type: 'success',
message: this.$t('submittedSuccessfully')
});
this.$emit("confirm");
} else {
this.$message({
type: 'error',
message: this.$t('submittedFailed')
});
}
}).catch(err => {
console.log(err);
this.$message({
type: 'error',
message: this.$t('submittedFailed')
});
})

},
cancel() {
this.dialogShow = false;
this.$emit("update:visible", false);
}
},
mounted() {
this.resetDataInfo();
},
};
</script>
<style scoped lang="less">
.dlg-content {
margin: 20px 0 25px 0;
display: flex;
justify-content: center;

.form {
width: 600px;

.form-row {
display: flex;
min-height: 42px;
margin-bottom: 4px;

.title {
width: 160px;
display: flex;
justify-content: flex-end;
align-items: center;
margin-right: 20px;
color: rgb(136, 136, 136);
font-size: 14px;

&.required {
span {
position: relative;
}

span::after {
position: absolute;
right: -10px;
top: -2px;
vertical-align: top;
content: '*';
color: #db2828;
}
}
}

.content {
width: 300px;
display: flex;
align-items: center;

/deep/ .el-select {
width: 100%;
}
}

.specSel {
/deep/ .el-tag.el-tag--info {
max-width: 81%;
display: flex;
align-items: center;

.el-select__tags-text {
overflow: hidden;
text-overflow: ellipsis;
}

.el-tag__close {
flex-shrink: 0;
right: -5px;
}
}
}
}
}

.btn {
color: rgb(2, 0, 4);
background-color: rgb(194, 199, 204);
border-color: rgb(194, 199, 204);

&.confirm-btn {
color: #fff;
background-color: rgb(56, 158, 13);
border-color: rgb(56, 158, 13);
}
}
}

.el-select-dropdown__item {
padding-left: 26px !important;
}

.el-select-dropdown__item.selected::after {
right: 0;
left: 6px;
}
</style>

+ 336
- 0
web_src/vuepages/pages/resources/components/SpecificationDialog.vue View File

@@ -0,0 +1,336 @@
<template>
<div class="base-dlg">
<BaseDialog :visible.sync="dialogShow" :width="`700px`"
:title="type === 'add' ? $t('resourcesManagement.addResSpecificationAndPriceInfo') : $t('resourcesManagement.editResSpecificationAndPriceInfo')"
@open="open" @opened="opened" @close="close" @closed="closed">
<div class="dlg-content">
<div class="form">
<div class="form-row">
<div class="title required">
<span>{{ $t('resourcesManagement.resQueue') }}</span>
</div>
<div class="content">
<el-select v-model="dataInfo.QueueId" :disabled="type === 'edit'">
<el-option v-for="item in this.queueList" :key="item.k" :label="item.v" :value="item.k" />
</el-select>
</div>
</div>
<div class="form-row">
<div class="title">
<span>{{ $t('resourcesManagement.sourceSpecCode') }}</span>
</div>
<div class="content">
<el-input v-model="dataInfo.SourceSpecId" :placeholder="$t('resourcesManagement.sourceSpecCodeTips')" maxlength="255"
:disabled="type === 'edit'">
</el-input>
</div>
</div>
<div class="form-row">
<div class="title required">
<span>{{ $t('resourcesManagement.accCardsNum') }}</span>
</div>
<div class="content">
<el-input v-model="dataInfo.AccCardsNum" type="number" placeholder="" :disabled="type === 'edit'">
</el-input>
</div>
</div>
<div class="form-row">
<div class="title required">
<span>{{ $t('resourcesManagement.cpuNum') }}</span>
</div>
<div class="content">
<el-input v-model="dataInfo.CpuCores" type="number" placeholder="" :disabled="type === 'edit'"></el-input>
</div>
</div>
<div class="form-row">
<div class="title required">
<span>{{ $t('resourcesManagement.gpuMem') }}(GB)</span>
</div>
<div class="content">
<el-input v-model="dataInfo.GPUMemGiB" type="number" placeholder="" :disabled="type === 'edit'">
</el-input>
</div>
</div>
<div class="form-row">
<div class="title required">
<span>{{ $t('resourcesManagement.mem') }}(GB)</span>
</div>
<div class="content">
<el-input v-model="dataInfo.MemGiB" type="number" placeholder="" :disabled="type === 'edit'"></el-input>
</div>
</div>
<div class="form-row">
<div class="title required">
<span>{{ $t('resourcesManagement.shareMem') }}(GB)</span>
</div>
<div class="content">
<el-input v-model="dataInfo.ShareMemGiB" type="number" placeholder="" :disabled="type === 'edit'">
</el-input>
</div>
</div>
<div class="form-row">
<div class="title required">
<span>{{ $t('resourcesManagement.unitPrice') }}({{ $t('resourcesManagement.point_hr') }})</span>
</div>
<div class="content">
<el-input v-model="dataInfo.UnitPrice" type="number" placeholder=""></el-input>
</div>
</div>
<div class="form-row">
<div class="title required">
<span>{{ $t('status') }}</span>
</div>
<div class="content">
<el-select v-model="dataInfo.Status" :disabled="type === 'edit'">
<el-option v-for="item in this.statusList" :key="item.k" :label="item.v" :value="item.k" />
</el-select>
</div>
</div>
<div class="form-row" style="margin-top: 20px">
<div class="title"></div>
<div class="content">
<el-button type="primary" class="btn confirm-btn" @click="confirm">{{ $t('confirm') }}</el-button>
<el-button class="btn" @click="cancel">{{ $t('cancel') }}</el-button>
</div>
</div>
</div>
</div>
</BaseDialog>
</div>
</template>
<script>
import BaseDialog from '~/components/BaseDialog.vue';
import { getResQueueCode, addResSpecification, updateResSpecification, getAiCenterList } from '~/apis/modules/resources';
import { SPECIFICATION_STATUS, CLUSTERS } from '~/const';
import { getListValueWithKey } from '~/utils';

export default {
name: "SpecificationDialog",
props: {
visible: { type: Boolean, default: false },
title: { type: String, default: '' },
type: { type: String, defalut: 'add' },
editOr: { type: Boolean, defalut: false },
data: { type: Object, default: () => ({}) },
},
components: {
BaseDialog
},
data() {
return {
dialogShow: false,
dataInfo: {},
queueList: [],
statusList: [...SPECIFICATION_STATUS],
clusterList: [...CLUSTERS],
aiCenterList: [],
};
},
watch: {
visible: function (val) {
this.dialogShow = val;
},
},
methods: {
resetDataInfo() {
this.dataInfo = {
QueueId: '',
AccCardsNum: '',
CpuCores: '',
MemGiB: '',
ShareMemGiB: '',
GPUMemGiB: '',
UnitPrice: '',
Status: '1',
}
},
getAiCenterList() {
getAiCenterList().then(res => {
res = res.data;
if (res.Code === 0) {
const list = res.Data;
const data = list.map(item => {
return {
k: item.AiCenterCode,
v: item.AiCenterName
};
});
this.aiCenterList.splice(0, Infinity, ...data);
}
}).catch(err => {
console.log(err);
});
},
getQueueList() {
getResQueueCode({ cluster: this.type === 'add' ? 'OpenI' : undefined }).then(res => {
res = res.data;
if (res.Code === 0) {
const data = res.Data;
const list = [];
for (let i = 0, iLen = data.length; i < iLen; i++) {
const item = data[i];
list.push({
k: item.ID,
v: `${item.QueueCode}(${getListValueWithKey(this.clusterList, item.Cluster)} - ${item.AiCenterName})`,
});
}
this.queueList.splice(0, Infinity, ...list);
}
}).catch(err => {
console.log(err);
});
},

open() {
this.resetDataInfo();
this.getQueueList();
this.getAiCenterList();
if (this.type === 'add') {
//
} else if (this.type === 'edit') {
this.dataInfo = Object.assign(this.dataInfo, { ...this.data, Status: '2' });
}
this.$emit("open");
},
opened() {
this.$emit("opened");
},
close() {
this.$emit("close");
},
closed() {
this.$emit("closed");
this.$emit("update:visible", false);
},
confirm() {
if (this.dataInfo.AccCardsNum === '' || this.dataInfo.CpuCores === '' || this.dataInfo.MemGiB === '' || this.dataInfo.ShareMemGiB === '' || this.dataInfo.GPUMemGiB === ''
|| this.dataInfo.UnitPrice === '' || !this.dataInfo.Status
) {
this.$message({
type: 'info',
message: this.$t('pleaseCompleteTheInformationFirst')
});
return;
}
if (parseInt(this.dataInfo.AccCardsNum) != Number(this.dataInfo.AccCardsNum)) {
this.$message({
type: 'info',
message: this.$t('pleaseEnterPositiveIntegerCardsTotalNum')
});
return;
}
const setApi = this.type === 'add' ? addResSpecification : updateResSpecification;
const action = this.editOr ? 'edit' : this.type === 'edit' ? 'on-shelf' : undefined;
setApi({
...this.dataInfo,
action: action,
AccCardsNum: Number(this.dataInfo.AccCardsNum),
CpuCores: Number(this.dataInfo.CpuCores),
MemGiB: Number(this.dataInfo.MemGiB),
ShareMemGiB: Number(this.dataInfo.ShareMemGiB),
GPUMemGiB: Number(this.dataInfo.GPUMemGiB),
UnitPrice: Number(this.dataInfo.UnitPrice),
Status: Number(this.dataInfo.Status),
}).then(res => {
res = res.data;
if (res.Code === 0) {
this.$message({
type: 'success',
message: this.$t('submittedSuccessfully')
});
this.$emit("confirm");
} else {
if (action === 'on-shelf' && res.Code === 1001) {
this.$message({
type: 'info',
message: this.$t('resourcesManagement.onShelfCode1001')
});
} else {
this.$message({
type: 'error',
message: this.$t('submittedFailed')
});
}
}
}).catch(err => {
console.log(err);
this.$message({
type: 'error',
message: this.$t('submittedFailed')
});
})
},
cancel() {
this.dialogShow = false;
this.$emit("update:visible", false);
}
},
mounted() {
this.resetDataInfo();
},
};
</script>
<style scoped lang="less">
.dlg-content {
margin: 20px 0 25px 0;
display: flex;
justify-content: center;

.form {
width: 600px;

.form-row {
display: flex;
min-height: 42px;
margin-bottom: 4px;

.title {
width: 160px;
display: flex;
justify-content: flex-end;
align-items: center;
margin-right: 20px;
color: rgb(136, 136, 136);
font-size: 14px;

&.required {
span {
position: relative;
}

span::after {
position: absolute;
right: -10px;
top: -2px;
vertical-align: top;
content: '*';
color: #db2828;
}
}
}

.content {
width: 300px;
display: flex;
align-items: center;

/deep/ .el-select {
width: 100%;
}
}
}
}

.btn {
color: rgb(2, 0, 4);
background-color: rgb(194, 199, 204);
border-color: rgb(194, 199, 204);

&.confirm-btn {
color: #fff;
background-color: rgb(56, 158, 13);
border-color: rgb(56, 158, 13);
}
}
}
</style>

+ 284
- 0
web_src/vuepages/pages/resources/queue/index.vue View File

@@ -0,0 +1,284 @@
<template>
<div>
<div class="title"><span>{{ $t('resourcesManagement.resQueue') }}</span></div>
<div class="tools-bar">
<div>
<el-select class="select" size="medium" v-model="selCluster" @change="selectChange">
<el-option v-for="item in clusterList" :key="item.k" :label="item.v" :value="item.k" />
</el-select>
<el-select class="select" size="medium" v-model="selComputingCenter" @change="selectChange">
<el-option v-for="item in computingCenterList" :key="item.k" :label="item.v" :value="item.k" />
</el-select>
<el-select class="select" size="medium" v-model="selComputingType" @change="selectChange">
<el-option v-for="item in computingTypeList" :key="item.k" :label="item.v" :value="item.k" />
</el-select>
<el-select class="select" size="medium" v-model="selCardType" @change="selectChange">
<el-option v-for="item in cardTypeList" :key="item.k" :label="item.v" :value="item.k" />
</el-select>
</div>
<div>
<el-button size="medium" icon="el-icon-refresh" @click="syncComputerNetwork" v-loading="syncLoading">
{{ $t('resourcesManagement.syncAiNetwork') }}</el-button>
<el-button type="primary" icon="el-icon-plus" size="medium" @click="showDialog('add')">
{{ $t('resourcesManagement.addResQueueBtn') }}</el-button>
</div>
</div>
<div class="table-container">
<div style="min-height:600px;">
<el-table border :data="tableData" style="width: 100%" v-loading="loading" stripe>
<el-table-column prop="ID" label="ID" align="center" header-align="center" width="80"></el-table-column>
<el-table-column prop="QueueCode" :label="$t('resourcesManagement.resQueueName')" align="center"
header-align="center"></el-table-column>
<el-table-column prop="ClusterName" :label="$t('resourcesManagement.whichCluster')" align="center"
header-align="center">
<template slot-scope="scope">
<span :title="scope.row.Cluster">{{ scope.row.ClusterName }}</span>
</template>
</el-table-column>
<el-table-column prop="AiCenterCode" :label="$t('resourcesManagement.aiCenterID')" align="center"
header-align="center"></el-table-column>
<el-table-column prop="AiCenterName" :label="$t('resourcesManagement.aiCenter')" align="center"
header-align="center"></el-table-column>
<el-table-column prop="ComputeResourceName" :label="$t('resourcesManagement.computeResource')" align="center"
header-align="center">
</el-table-column>
<el-table-column prop="AccCardTypeName" :label="$t('resourcesManagement.accCardType')" align="center"
header-align="center"></el-table-column>
<el-table-column prop="CardsTotalNum" :label="$t('resourcesManagement.cardsTotalNum')" align="center"
header-align="center"></el-table-column>
<el-table-column prop="UpdatedTimeStr" :label="$t('resourcesManagement.lastUpdateTime')" align="center"
header-align="center"></el-table-column>
<el-table-column prop="Remark" :label="$t('resourcesManagement.remark')" align="left" header-align="center"
min-width="160">
</el-table-column>
<el-table-column :label="$t('operation')" align="center" header-align="center" width="80">
<template slot-scope="scope">
<span v-if="scope.row.Cluster !== 'C2Net'" class="op-btn" @click="showDialog('edit', scope.row)">{{
$t('edit')
}}</span>
<span v-else class="op-btn" style="color:rgb(187, 187, 187);cursor:not-allowed">{{
$t('edit')
}}</span>
</template>
</el-table-column>
<template slot="empty">
<span style="font-size: 12px">{{
loading ? $t('loading') : $t('noData')
}}</span>
</template>
</el-table>
</div>
<div class="__r_p_pagination">
<div style="margin-top: 2rem">
<div class="center">
<el-pagination background @current-change="currentChange" :current-page="pageInfo.curpage"
:page-sizes="pageInfo.pageSizes" :page-size="pageInfo.pageSize"
layout="total, sizes, prev, pager, next, jumper" :total="pageInfo.total">
</el-pagination>
</div>
</div>
</div>
</div>
<QueueDialog :visible.sync="queueDialogShow" :type="queueDialogType" :data="queueDialogData"
@confirm="queueDialogConfirm"></QueueDialog>
</div>
</template>

<script>
import QueueDialog from '../components/QueueDialog.vue';
import { getAiCenterList, getResQueueList, addResQueue, updateResQueue, syncResQueue } from '~/apis/modules/resources';
import { CLUSTERS, COMPUTER_RESOURCES, ACC_CARD_TYPE } from '~/const';
import { getListValueWithKey } from '~/utils';
import { formatDate } from 'element-ui/lib/utils/date-util';

export default {
data() {
return {
selCluster: '',
clusterList: [{ k: '', v: this.$t('resourcesManagement.allCluster') }, ...CLUSTERS],
selComputingCenter: '',
computingCenterList: [{ k: '', v: this.$t('resourcesManagement.allAiCenter') }],
selComputingType: '',
computingTypeList: [{ k: '', v: this.$t('resourcesManagement.allComputeResource') }, ...COMPUTER_RESOURCES],
selCardType: '',
cardTypeList: [{ k: '', v: this.$t('resourcesManagement.allAccCardType') }, ...ACC_CARD_TYPE],
syncLoading: false,
loading: false,
tableData: [],
pageInfo: {
curpage: 1,
pageSize: 10,
pageSizes: [10],
total: 0,
},
queueDialogShow: false,
queueDialogType: 'add',
queueDialogData: {},
};
},
components: { QueueDialog },
methods: {
getAiCenterList() {
getAiCenterList().then(res => {
res = res.data;
if (res.Code === 0) {
const list = res.Data;
const data = list.map(item => {
return {
k: item.AiCenterCode,
v: item.AiCenterName
};
});
this.computingCenterList.splice(1, Infinity, ...data);
}
}).catch(err => {
console.log(err);
});
},
getTableData() {
const params = {
cluster: this.selCluster,
center: this.selComputingCenter,
resource: this.selComputingType,
card: this.selCardType,
page: this.pageInfo.curpage,
pagesize: this.pageInfo.pageSize,
};
this.loading = true;
getResQueueList(params).then(res => {
this.loading = false;
res = res.data;
if (res.Code === 0) {
const list = res.Data.List;
const data = list.map((item) => {
return {
...item,
QueueCode: item.QueueCode || '--',
ClusterName: getListValueWithKey(this.clusterList, item.Cluster),
ComputeResourceName: getListValueWithKey(this.computingTypeList, item.ComputeResource),
AccCardTypeName: getListValueWithKey(this.cardTypeList, item.AccCardType),
UpdatedTimeStr: formatDate(new Date(item.UpdatedTime * 1000), 'yyyy-MM-dd HH:mm:ss'),
}
});
this.tableData = data;
this.pageInfo.total = res.Data.TotalSize;
}
}).catch(err => {
console.log(err);
this.loading = false;
});
},
syncComputerNetwork() {
this.syncLoading = true;
syncResQueue().then(res => {
this.syncLoading = false;
res = res.data;
if (res.Code === 0) {
this.$message({
type: 'success',
message: this.$t('submittedSuccessfully')
});
this.getAiCenterList();
this.getTableData();
} else {
this.$message({
type: 'error',
message: this.$t('submittedFailed')
});
}
}).catch(err => {
console.log(err);
this.syncLoading = false;
this.$message({
type: 'error',
message: this.$t('submittedFailed')
});
});
},
selectChange() {
this.pageInfo.curpage = 1;
this.getTableData();
},
currentChange(val) {
this.pageInfo.curpage = val;
this.getTableData();
},
showDialog(type, data) {
this.queueDialogType = type;
this.queueDialogData = data ? { ...data } : {};
this.queueDialogShow = true;
},
queueDialogConfirm() {
this.queueDialogShow = false;
this.getAiCenterList();
this.getTableData();
}
},
mounted() {
this.getAiCenterList();
this.getTableData();
},
beforeDestroy() {
},
};
</script>

<style scoped lang="less">
.title {
height: 30px;
display: flex;
align-items: center;
margin-bottom: 5px;

span {
font-weight: 700;
font-size: 16px;
color: rgb(16, 16, 16);
}
}

.tools-bar {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 10px;

.select {
margin-right: 10px;

/deep/ .el-input__inner {
border-radius: 0;
}
}
}

.table-container {
margin-bottom: 16px;

/deep/ .el-table__header {
th {
background: rgb(245, 245, 246);
font-size: 12px;
color: rgb(36, 36, 36);
}
}

/deep/ .el-table__body {
td {
font-size: 12px;
}
}

.op-btn {
cursor: pointer;
font-size: 12px;
color: rgb(25, 103, 252);
margin: 0 5px;
}
}

.center {
display: flex;
justify-content: center;
}
</style>

+ 17
- 0
web_src/vuepages/pages/resources/queue/vp-resources-queue.js View File

@@ -0,0 +1,17 @@
import Vue from 'vue';
import ElementUI from 'element-ui';
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 './index.vue';

Vue.use(ElementUI, {
locale: lang === 'zh-CN' ? localeZh : localeEn,
size: 'small',
});

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

+ 361
- 0
web_src/vuepages/pages/resources/scene/index.vue View File

@@ -0,0 +1,361 @@
<template>
<div>
<div class="title"><span>{{ $t('resourcesManagement.resSceneManagement') }}</span></div>
<div class="tools-bar">
<div>
<el-select class="select" size="medium" v-model="selTaskType" @change="selectChange">
<el-option v-for="item in taskTypeList" :key="item.k" :label="item.v" :value="item.k" />
</el-select>
<el-select class="select" size="medium" v-model="selIsExclusive" @change="selectChange">
<el-option v-for="item in isExclusiveList" :key="item.k" :label="item.v" :value="item.k" />
</el-select>
<el-select class="select" size="medium" v-model="selAiCenter" @change="selectChange">
<el-option v-for="item in aiCenterList" :key="item.k" :label="item.v" :value="item.k" />
</el-select>
<el-select class="select" size="medium" v-model="selQueue" @change="selectChange">
<el-option v-for="item in queueList" :key="item.k" :label="item.v" :value="item.k" />
</el-select>
</div>
<div>
<el-button type="primary" icon="el-icon-plus" size="medium" @click="showDialog('add')">
{{ $t('resourcesManagement.addResSceneBtn') }}</el-button>
</div>
</div>
<div class="table-container">
<div style="min-height:600px;">
<el-table border :data="tableData" style="width: 100%" v-loading="loading" stripe>
<el-table-column prop="ID" label="ID" align="center" header-align="center" width="60"></el-table-column>
<el-table-column prop="SceneName" :label="$t('resourcesManagement.resSceneName')" align="center"
header-align="center"></el-table-column>
<el-table-column prop="JobTypeStr" :label="$t('resourcesManagement.jobType')" align="center"
header-align="center" width="120">
</el-table-column>
<el-table-column prop="IsExclusiveStr" :label="$t('resourcesManagement.isExclusive')" align="center"
header-align="center" width="120">
<template slot-scope="scope">
<span :style="{ color: scope.row.IsExclusive ? 'red' : '' }">{{ scope.row.IsExclusiveStr }}</span>
</template>
</el-table-column>
<el-table-column prop="ExclusiveOrg" :label="$t('resourcesManagement.exclusiveOrg')" align="center"
header-align="center">
<template slot-scope="scope">
<span>{{ scope.row.IsExclusive ? scope.row.ExclusiveOrg : '--' }}</span>
</template>
</el-table-column>
<el-table-column prop="AiCenterStr" :label="$t('resourcesManagement.aiCenter')" align="center"
header-align="center">
<template slot-scope="scope">
<div v-if="!scope.row.Queues.length">--</div>
<div v-for="item in scope.row.Queues" :key="item.QueueId">
<span>{{ item.AiCenterName }}</span>
</div>
</template>
</el-table-column>
<el-table-column prop="QueueStr" :label="$t('resourcesManagement.resQueue')" align="center"
header-align="center">
<template slot-scope="scope">
<div v-if="!scope.row.Queues.length">--</div>
<div v-for="item in scope.row.Queues" :key="item.QueueId">
<span>{{ item.QueueStr }}</span>
</div>
</template>
</el-table-column>
<el-table-column prop="SpecsList" :label="$t('resourcesManagement.resourceSpecification')" align="left"
header-align="center" min-width="180">
<template slot-scope="scope">
<div v-for="item in scope.row.SpecsList" :key="item.k">
<span>{{ item.v }}</span>
</div>
</template>
</el-table-column>
<el-table-column :label="$t('operation')" align="center" header-align="center" width="100">
<template slot-scope="scope">
<span class="op-btn" @click="showDialog('edit', scope.row)">{{ $t('edit') }}</span>
<span class="op-btn" style="color:red" @click="deleteRow(scope.row)">{{ $t('delete') }}</span>
</template>
</el-table-column>
<template slot="empty">
<span style="font-size: 12px">{{
loading ? $t('loading') : $t('noData')
}}</span>
</template>
</el-table>
</div>
<div class="__r_p_pagination">
<div style="margin-top: 2rem">
<div class="center">
<el-pagination background @current-change="currentChange" :current-page="pageInfo.curpage"
:page-sizes="pageInfo.pageSizes" :page-size="pageInfo.pageSize"
layout="total, sizes, prev, pager, next, jumper" :total="pageInfo.total">
</el-pagination>
</div>
</div>
</div>
</div>
<SceneDialog :visible.sync="sceneDialogShow" :type="sceneDialogType" :data="sceneDialogData"
@confirm="sceneDialogConfirm"></SceneDialog>
</div>
</template>

<script>
import SceneDialog from '../components/SceneDialog.vue';
import { getQueueList, getResQueueCode, getResSceneList, updateResScene, getAiCenterList } from '~/apis/modules/resources';
import { JOB_TYPE, CLUSTERS, ACC_CARD_TYPE } from '~/const';
import { getListValueWithKey } from '~/utils';
import { formatDate } from 'element-ui/lib/utils/date-util';

export default {
data() {
return {
selTaskType: '',
taskTypeList: [{ k: '', v: this.$t('resourcesManagement.allJobType') }, ...JOB_TYPE],
selIsExclusive: '',
isExclusiveList: [{ k: '', v: this.$t('resourcesManagement.allExclusiveAndCommonUse') }, { k: '1', v: this.$t('resourcesManagement.exclusive') }, { k: '2', v: this.$t('resourcesManagement.commonUse') }],
selQueue: '',
queueList: [{ k: '', v: this.$t('resourcesManagement.allResQueue') }],
clusterList: [...CLUSTERS],
selAiCenter: '',
aiCenterList: [{ k: '', v: this.$t('resourcesManagement.allAiCenter') }],
accCardTypeList: [...ACC_CARD_TYPE],
loading: false,
tableData: [],
pageInfo: {
curpage: 1,
pageSize: 10,
pageSizes: [10],
total: 0,
},
sceneDialogShow: false,
sceneDialogType: 'add',
sceneDialogData: {},
};
},
components: { SceneDialog },
methods: {
getAiCenterList() {
getAiCenterList().then(res => {
res = res.data;
if (res.Code === 0) {
const list = res.Data;
const data = list.map(item => {
return {
k: item.AiCenterCode,
v: item.AiCenterName
};
});
this.aiCenterList.splice(1, Infinity, ...data);
}
}).catch(err => {
console.log(err);
});
},
getQueueList() {
getResQueueCode().then(res => {
res = res.data;
if (res.Code === 0) {
const data = res.Data;
const list = [];
for (let i = 0, iLen = data.length; i < iLen; i++) {
const item = data[i];
list.push({
k: item.ID,
v: `${item.QueueCode}(${getListValueWithKey(this.clusterList, item.Cluster)} - ${item.AiCenterName})`,
});
}
this.queueList.push(...list);
}
}).catch(err => {
console.log(err);
});
},
getTableData() {
const params = {
jobType: this.selTaskType,
IsExclusive: this.selIsExclusive,
queue: this.selQueue,
center: this.selAiCenter,
page: this.pageInfo.curpage,
pagesize: this.pageInfo.pageSize,
};
this.loading = true;
getResSceneList(params).then(res => {
this.loading = false;
res = res.data;
if (res.Code === 0) {
const list = res.Data.List;
const data = list.map((item) => {
const Specs = item.Specs;
const specsList = [];
const queues = [];
const queueIds = [];
let cluster = '';
for (let i = 0, iLen = Specs.length; i < iLen; i++) {
const Spec = Specs[i];
// const NGPU = `${Spec.ComputeResource}:${Spec.AccCardsNum === 0 ? '0' : Spec.AccCardsNum + '*' + getListValueWithKey(this.accCardTypeList, Spec.AccCardType)}`;
const NGPU = `${Spec.ComputeResource}:${Spec.AccCardsNum + '*' + getListValueWithKey(this.accCardTypeList, Spec.AccCardType)}`;
specsList.push({
k: Spec.ID,
v: `${NGPU}, CPU:${Spec.CpuCores}, ${this.$t('resourcesManagement.gpuMem')}:${Spec.GPUMemGiB}GB, ${this.$t('resourcesManagement.mem')}:${Spec.MemGiB}GB, ${this.$t('resourcesManagement.shareMem')}:${Spec.ShareMemGiB}GB, ${this.$t('resourcesManagement.unitPrice')}:${Spec.UnitPrice}${this.$t('resourcesManagement.point_hr')}`,
});
cluster = Spec.Cluster;
if (queueIds.indexOf(Spec.QueueId) < 0) {
queues.push({
QueueId: Spec.QueueId,
QueueCode: Spec.QueueCode,
AiCenterCode: Spec.AiCenterCode,
AiCenterName: Spec.AiCenterName,
QueueStr: `${Spec.QueueCode}(${getListValueWithKey(this.clusterList, Spec.Cluster)} - ${Spec.AiCenterName})`,
});
queueIds.push(Spec.QueueId);
}
}
return {
ID: item.ID,
SceneName: item.SceneName,
JobType: item.JobType,
JobTypeStr: getListValueWithKey(this.taskTypeList, item.JobType),
IsExclusive: item.IsExclusive,
IsExclusiveStr: getListValueWithKey(this.isExclusiveList, item.IsExclusive ? '1' : '2'),
ExclusiveOrg: item.ExclusiveOrg,
Cluster: cluster,
QueueIds: queueIds,
Queues: queues,
SpecsList: specsList,
}
});
this.tableData = data;
this.pageInfo.total = res.Data.TotalSize;
}
}).catch(err => {
console.log(err);
this.loading = false;
});
},
selectChange() {
this.pageInfo.curpage = 1;
this.getTableData();
},
currentChange(val) {
this.pageInfo.curpage = val;
this.getTableData();
},
deleteRow(row) {
this.$confirm(this.$t('resourcesManagement.resSceneDeleteConfirm'), this.$t('tips'), {
confirmButtonText: this.$t('confirm1'),
cancelButtonText: this.$t('cancel'),
type: 'warning',
lockScroll: false,
}).then(() => {
updateResScene({
action: 'delete',
ID: row.ID,
}).then(res => {
res = res.data;
if (res.Code === 0) {
this.$message({
type: 'success',
message: this.$t('submittedSuccessfully')
});
this.getTableData();
} else {
this.$message({
type: 'error',
message: this.$t('submittedFailed')
});
}
}).catch(err => {
this.$message({
type: 'error',
message: this.$t('submittedFailed')
});
});
}).catch(() => { });
},
showDialog(type, data) {
this.sceneDialogType = type;
this.sceneDialogData = data ? {
ID: data.ID,
SceneName: data.SceneName,
JobType: data.JobType,
IsExclusive: data.IsExclusive ? '1' : '2',
ExclusiveOrg: data.ExclusiveOrg,
Cluster: data.Cluster,
QueueIds: data.QueueIds,
SpecIds: data.SpecsList.map((item) => item.k),
} : {};
this.sceneDialogShow = true;
},
sceneDialogConfirm() {
this.sceneDialogShow = false;
this.getTableData();
}
},
mounted() {
this.getAiCenterList();
this.getQueueList();
this.getTableData();
},
beforeDestroy() {
},
};
</script>

<style scoped lang="less">
.title {
height: 30px;
display: flex;
align-items: center;
margin-bottom: 5px;

span {
font-weight: 700;
font-size: 16px;
color: rgb(16, 16, 16);
}
}

.tools-bar {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 10px;

.select {
margin-right: 10px;

/deep/ .el-input__inner {
border-radius: 0;
}
}
}

.table-container {
margin-bottom: 16px;

/deep/ .el-table__header {
th {
background: rgb(245, 245, 246);
font-size: 12px;
color: rgb(36, 36, 36);
}
}

/deep/ .el-table__body {
td {
font-size: 12px;
}
}

.op-btn {
cursor: pointer;
font-size: 12px;
color: rgb(25, 103, 252);
margin: 0 5px;
}
}

.center {
display: flex;
justify-content: center;
}
</style>

+ 17
- 0
web_src/vuepages/pages/resources/scene/vp-resources-scene.js View File

@@ -0,0 +1,17 @@
import Vue from 'vue';
import ElementUI from 'element-ui';
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 './index.vue';

Vue.use(ElementUI, {
locale: lang === 'zh-CN' ? localeZh : localeEn,
size: 'small',
});

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

+ 451
- 0
web_src/vuepages/pages/resources/specification/index.vue View File

@@ -0,0 +1,451 @@
<template>
<div>
<div class="title"><span>{{ $t('resourcesManagement.resSpecificationAndPriceManagement') }}</span></div>
<div class="tools-bar">
<div>
<el-select class="select" size="medium" v-model="selQueue" @change="selectChange">
<el-option v-for="item in queueList" :key="item.k" :label="item.v" :value="item.k" />
</el-select>
<el-select class="select" size="medium" v-model="selStatus" @change="selectChange">
<el-option v-for="item in statusList" :key="item.k" :label="item.v" :value="item.k" />
</el-select>
</div>
<div>
<el-button size="medium" icon="el-icon-refresh" @click="syncComputerNetwork" v-loading="syncLoading">
{{ $t('resourcesManagement.syncAiNetwork') }}</el-button>
<el-button type="primary" icon="el-icon-plus" size="medium" @click="showDialog('add')">
{{ $t('resourcesManagement.addResSpecificationBtn') }}</el-button>
</div>
</div>
<div class="table-container">
<div style="min-height:600px;">
<el-table border :data="tableData" style="width: 100%" v-loading="loading" stripe>
<el-table-column prop="ID" label="ID" align="center" header-align="center" width="60"></el-table-column>
<el-table-column prop="SpecStr" :label="$t('resourcesManagement.resourceSpecification')" align="left"
header-align="center" min-width="160">
</el-table-column>
<el-table-column prop="QueueInfo" :label="$t('resourcesManagement.resQueue')" align="center"
header-align="center" min-width="100">
</el-table-column>
<el-table-column prop="SourceSpecId" :label="$t('resourcesManagement.sourceSpecCode')" align="center"
header-align="center">
</el-table-column>
<el-table-column prop="AccCardsNum" :label="$t('resourcesManagement.accCardsNum')" align="center"
header-align="center"></el-table-column>
<el-table-column prop="CpuCores" :label="$t('resourcesManagement.cpuNum')" align="center"
header-align="center"></el-table-column>
<el-table-column prop="GPUMemGiB" :label="`${$t('resourcesManagement.gpuMem')}(GB)`" align="center"
header-align="center"></el-table-column>
<el-table-column prop="MemGiB" :label="`${$t('resourcesManagement.mem')}(GB)`" align="center"
header-align="center"></el-table-column>
<el-table-column prop="ShareMemGiB" :label="`${$t('resourcesManagement.shareMem')}(GB)`" align="center"
header-align="center"></el-table-column>
<el-table-column prop="UpdatedTimeStr" :label="$t('resourcesManagement.lastUpdateTime')" align="center"
header-align="center"></el-table-column>
<el-table-column prop="UnitPrice"
:label="`${$t('resourcesManagement.unitPrice')}(${$t('resourcesManagement.point_hr')})`" align="center"
header-align="center">
<template slot-scope="scope">
<span style="font-weight:600;font-size:14px;">{{ scope.row.UnitPrice }}</span>
</template>
</el-table-column>
<el-table-column prop="StatusStr" :label="$t('resourcesManagement.status')" align="center"
header-align="center" width="100">
<template slot-scope="scope">
<span :style="{ color: scope.row.Status == '2' ? 'rgb(82, 196, 26)' : 'rgb(245, 34, 45)' }">{{
scope.row.StatusStr
}}</span>
</template>
</el-table-column>
<el-table-column :label="$t('operation')" align="center" header-align="center" width="100">
<template slot-scope="scope">
<span v-if="scope.row.Status == '1' && !scope.row.UnitPrice">
<span class="op-btn" @click="showDialog('edit', scope.row)">{{
$t('resourcesManagement.toSetPriceAndOnShelf')
}}</span>
</span>
<span v-if="scope.row.Status == '2'">
<span class="op-btn" @click="showDialog('edit', scope.row, true)">{{ $t('edit') }}</span>
<span class="op-btn" @click="offShelfPrev(scope.row)">{{
$t('resourcesManagement.toOffShelf')
}}</span>
</span>
<span v-if="scope.row.Status == '3' || scope.row.Status == '1' && scope.row.UnitPrice">
<span class="op-btn" @click="onShelf(scope.row)">{{
$t('resourcesManagement.toOnShelf')
}}</span>
</span>
</template>
</el-table-column>
<template slot="empty">
<span style="font-size: 12px">{{
loading ? $t('loading') : $t('noData')
}}</span>
</template>
</el-table>
</div>
<div class="__r_p_pagination">
<div style="margin-top: 2rem">
<div class="center">
<el-pagination background @current-change="currentChange" :current-page="pageInfo.curpage"
:page-sizes="pageInfo.pageSizes" :page-size="pageInfo.pageSize"
layout="total, sizes, prev, pager, next, jumper" :total="pageInfo.total">
</el-pagination>
</div>
</div>
</div>
</div>
<SpecificationDialog :visible.sync="specificationDialogShow" :type="specificationDialogType"
:editOr="specificationDialogEditOr" :data="specificationDialogData" @confirm="specificationDialogConfirm">
</SpecificationDialog>

<BaseDialog :visible.sync="offShelfDialogShow" :width="`600px`" :title="$t('tips')">
<div class="form">
<div class="form-row" style="flex-direction:column;">
<div class="content" style="margin:8px 0">{{ $t('resourcesManagement.offShelfDlgTip1') }}</div>
<div class="content" style="margin:8px 0;font-weight: bold;">{{ offSelfDialogContent }}</div>
<div class="content" style="margin:8px 0">{{ $t('resourcesManagement.offShelfDlgTip2') }}</div>
</div>
<div class="form-row" style="margin-top: 20px">
<div class="content">
<el-button type="primary" class="btn confirm-btn" @click="offShelf">{{ $t('confirm') }}</el-button>
<el-button class="btn" @click="offShelfDialogShow = false">{{ $t('cancel') }}</el-button>
</div>
</div>
</div>
</BaseDialog>
</div>
</template>

<script>
import SpecificationDialog from '../components/SpecificationDialog.vue';
import BaseDialog from '~/components/BaseDialog.vue';
import { getResQueueCode, getResSpecificationList, updateResSpecification, syncResSpecification, getResSpecificationScenes } from '~/apis/modules/resources';
import { SPECIFICATION_STATUS, CLUSTERS, ACC_CARD_TYPE } from '~/const';
import { getListValueWithKey } from '~/utils';
import { formatDate } from 'element-ui/lib/utils/date-util';

export default {
data() {
return {
selQueue: '',
queueList: [{ k: '', v: this.$t('resourcesManagement.allResQueue') }],
selStatus: '',
statusList: [{ k: '', v: this.$t('resourcesManagement.allStatus') }, ...SPECIFICATION_STATUS],
clusterList: [...CLUSTERS],
accCardTypeList: [...ACC_CARD_TYPE],
syncLoading: false,
loading: false,
tableData: [],
pageInfo: {
curpage: 1,
pageSize: 10,
pageSizes: [10],
total: 0,
},
specificationDialogShow: false,
specificationDialogType: 'add',
specificationDialogEditOr: false,
specificationDialogData: {},

offShelfDialogShow: false,
offShelfDialogData: {},
offSelfDialogContent: '',
};
},
components: { BaseDialog, SpecificationDialog },
methods: {
getQueueList() {
getResQueueCode().then(res => {
res = res.data;
if (res.Code === 0) {
const data = res.Data;
const list = [];
for (let i = 0, iLen = data.length; i < iLen; i++) {
const item = data[i];
list.push({
k: item.ID,
v: `${item.QueueCode}(${getListValueWithKey(this.clusterList, item.Cluster)} - ${item.AiCenterName})`,
});
}
this.queueList.push(...list);
}
}).catch(err => {
console.log(err);
});
},
getTableData() {
const params = {
queue: this.selQueue,
status: this.selStatus,
page: this.pageInfo.curpage,
pagesize: this.pageInfo.pageSize,
};
this.loading = true;
getResSpecificationList(params).then(res => {
this.loading = false;
res = res.data;
if (res.Code === 0) {
const list = res.Data.List;
const data = list.map((item) => {
const Queue = item.Queue;
const Spec = item.Spec;
// const NGPU = `${Queue.ComputeResource}:${Spec.AccCardsNum === 0 ? '0' : Spec.AccCardsNum + '*' + getListValueWithKey(this.accCardTypeList, Queue.AccCardType)}`;
const NGPU = `${Queue.ComputeResource}:${Spec.AccCardsNum + '*' + getListValueWithKey(this.accCardTypeList, Queue.AccCardType)}`;
return {
...Spec,
SourceSpecId: Spec.SourceSpecId || '--',
SpecStr: `${NGPU}, CPU:${Spec.CpuCores}, ${this.$t('resourcesManagement.gpuMem')}:${Spec.GPUMemGiB}GB, ${this.$t('resourcesManagement.mem')}:${Spec.MemGiB}GB, ${this.$t('resourcesManagement.shareMem')}:${Spec.ShareMemGiB}GB`,
QueueId: Queue.ID,
QueueInfo: `${Queue.QueueCode}(${getListValueWithKey(this.clusterList, Queue.Cluster)} - ${Queue.AiCenterName})`,
UpdatedTimeStr: formatDate(new Date(Spec.UpdatedTime * 1000), 'yyyy-MM-dd HH:mm:ss'),
Status: Spec.Status.toString(),
StatusStr: getListValueWithKey(this.statusList, Spec.Status.toString()),
}
});
this.tableData = data;
this.pageInfo.total = res.Data.TotalSize;
}
}).catch(err => {
console.log(err);
this.loading = false;
});
},
syncComputerNetwork() {
this.syncLoading = true;
syncResSpecification().then(res => {
this.syncLoading = false;
res = res.data;
if (res.Code === 0) {
this.$message({
type: 'success',
message: this.$t('submittedSuccessfully')
});
this.getTableData();
} else {
this.$message({
type: 'error',
message: this.$t('submittedFailed')
});
}
}).catch(err => {
console.log(err);
this.syncLoading = false;
this.$message({
type: 'error',
message: this.$t('submittedFailed')
});
});
},
selectChange() {
this.pageInfo.curpage = 1;
this.getTableData();
},
currentChange(val) {
this.pageInfo.curpage = val;
this.getTableData();
},
showDialog(type, data, editOr) {
this.specificationDialogType = type;
this.specificationDialogEditOr = !!editOr;
this.specificationDialogData = data ? { ...data } : {};
this.specificationDialogShow = true;
},
specificationDialogConfirm() {
this.specificationDialogShow = false;
this.getTableData();
},
onShelf(data) {
const type = 'on-shelf';
this.$confirm(type === 'on-shelf' ? this.$t('resourcesManagement.onShelfConfirm') : this.$t('resourcesManagement.offShelfConfirm'), this.$t('tips'), {
confirmButtonText: this.$t('confirm1'),
cancelButtonText: this.$t('cancel'),
type: 'warning',
lockScroll: false,
}).then(() => {
updateResSpecification({
ID: data.ID,
action: type
}).then(res => {
res = res.data;
if (res.Code === 0) {
this.$message({
type: 'success',
message: this.$t('submittedSuccessfully')
});
this.getTableData();
} else {
if (type === 'on-shelf' && res.Code === 1001) {
this.$message({
type: 'info',
message: this.$t('resourcesManagement.onShelfCode1001')
});
} else {
this.$message({
type: 'error',
message: this.$t('submittedFailed')
});
}
}
}).catch(err => {
console.log(err);
this.$message({
type: 'error',
message: this.$t('submittedFailed')
});
});
}).catch(() => { });
},
offShelfPrev(data) {
this.$confirm(this.$t('resourcesManagement.offShelfConfirm'), this.$t('tips'), {
confirmButtonText: this.$t('confirm1'),
cancelButtonText: this.$t('cancel'),
type: 'warning',
lockScroll: false,
}).then(() => {
this.offShelfDialogData = data;
getResSpecificationScenes({ ID: data.ID }).then(res => {
res = res.data;
if (res.Code === 0) {
if (res.Data.List.length) {
this.offShelfDialogShow = true;
this.offSelfDialogContent = res.Data.List.map((item) => `[${item.ID}]${item.SceneName}`).join(', ');
} else {
this.offShelf();
}
} else {
console.log(res);
}
}).catch(err => {
console.log(err);
});
}).catch(() => { });
},
offShelf() {
updateResSpecification({
ID: this.offShelfDialogData.ID,
action: 'off-shelf'
}).then(res => {
res = res.data;
if (res.Code === 0) {
this.$message({
type: 'success',
message: this.$t('submittedSuccessfully')
});
this.offShelfDialogShow = false;
this.getTableData();
} else {
this.$message({
type: 'error',
message: this.$t('submittedFailed')
});
}
}).catch(err => {
console.log(err);
this.$message({
type: 'error',
message: this.$t('submittedFailed')
});
});
},
},
mounted: function () {
this.getQueueList();
this.getTableData();
},
beforeDestroy: function () {
},
};
</script>

<style scoped lang="less">
.title {
height: 30px;
display: flex;
align-items: center;
margin-bottom: 5px;

span {
font-weight: 700;
font-size: 16px;
color: rgb(16, 16, 16);
}
}

.tools-bar {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 10px;

.select {
margin-right: 10px;

/deep/ .el-input__inner {
border-radius: 0;
}
}
}

.table-container {
margin-bottom: 16px;

/deep/ .el-table__header {
th {
background: rgb(245, 245, 246);
font-size: 12px;
color: rgb(36, 36, 36);
}
}

/deep/ .el-table__body {
td {
font-size: 12px;
}
}

.op-btn {
cursor: pointer;
font-size: 12px;
color: rgb(25, 103, 252);
margin-right: 4px;
}
}

.center {
display: flex;
justify-content: center;
}

.form {
margin: 5px 0 5px 0;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
}

.form-row {
display: flex;
min-height: 42px;
margin-bottom: 4px;

.content {
width: 500px;
display: flex;
align-items: center;
}
}

.btn {
color: rgb(2, 0, 4);
background-color: rgb(194, 199, 204);
border-color: rgb(194, 199, 204);

&.confirm-btn {
color: #fff;
background-color: rgb(56, 158, 13);
border-color: rgb(56, 158, 13);
}
}
</style>

+ 17
- 0
web_src/vuepages/pages/resources/specification/vp-resources-specification.js View File

@@ -0,0 +1,17 @@
import Vue from 'vue';
import ElementUI from 'element-ui';
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 './index.vue';

Vue.use(ElementUI, {
locale: lang === 'zh-CN' ? localeZh : localeEn,
size: 'small',
});

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

+ 148
- 0
web_src/vuepages/pages/reward/point/utils.js View File

@@ -0,0 +1,148 @@

import { formatDate } from 'element-ui/lib/utils/date-util';
import { SOURCE_TYPE, CONSUME_STATUS, POINT_ACTIONS, JOB_TYPE } from '~/const';
import { i18n } from '~/langs';

const getSourceType = (key) => {
const find = SOURCE_TYPE.filter(item => item.k === key);
return find.length ? find[0].v : key;
};

const getConsumeStatus = (key) => {
const find = CONSUME_STATUS.filter(item => item.k === key);
return find.length ? find[0].v : key;
};

const getPointAction = (key) => {
const find = POINT_ACTIONS.filter(item => item.k === key);
return find.length ? find[0].v : key;
};

const getJobType = (key) => {
const find = JOB_TYPE.filter(item => item.k === key);
return find.length ? find[0].v : key;
};

const getJobTypeLink = (record, type) => {
let link = type === 'INCREASE' ? record.Action.RepoLink : '/' + record.Cloudbrain.RepoFullName;
const cloudbrain = type === 'INCREASE' ? record.Action?.Cloudbrain : record.Cloudbrain;
switch (cloudbrain?.JobType) {
case 'DEBUG':
if (cloudbrain.ComputeResource === 'CPU/GPU') {
link += `/cloudbrain/${cloudbrain.ID}`;
} else {
link += `/modelarts/notebook/${cloudbrain.ID}`;
}
break;
case 'TRAIN':
if (cloudbrain.Type === 1) {
link += `/modelarts/train-job/${cloudbrain.JobID}`;
} else if (cloudbrain.Type === 0) {
link += `/cloudbrain/train-job/${cloudbrain.JobID}`;
} else if (cloudbrain.Type === 2) {
link += `/grampus/train-job/${cloudbrain.JobID}`;
}
break;
case 'INFERENCE':
link += `/modelarts/inference-job/${cloudbrain.JobID}`;
break;
case 'BENCHMARK':
link += `/cloudbrain/benchmark/${cloudbrain.ID}`;
break;
default:
break;
};
return link;
};

export const getRewardPointRecordInfo = (record) => {
const out = {
sn: record.SerialNo,
date: formatDate(new Date(record.LastOperateDate * 1000), 'yyyy-MM-dd HH:mm:ss'),
_status: record.Status,
status: getConsumeStatus(record.Status) || '--',
statusColor: record.Status === 'OPERATING' ? 'rgb(33, 186, 69)' : '',
_sourceType: record.SourceType,
sourceType: getSourceType(record.SourceType),
duration: record?.Cloudbrain?.Duration || '--',
taskName: record?.Cloudbrain?.DisplayJobName || '--',
taskId: record?.Cloudbrain?.ID,
action: record?.Action?.OpType ? getPointAction(record.Action.OpType) : '--',
remark: record.Remark,
amount: record.Amount,
};
if (record.OperateType === 'INCREASE') {
if (record.SourceType === 'ADMIN_OPERATE') {
out.remark = record.Remark;
} else if (record.SourceType === 'ACCOMPLISH_TASK') {
switch (record?.Action?.OpType) {
case 1: // 创建公开项目 - 创建了项目OpenI/aiforge
out.remark = `${i18n.t('createdRepository')}<a href="${record.Action.RepoLink}" rel="nofollow">${record.Action.ShortRepoFullDisplayName}</a>`;
break;
case 6: // 每日提出任务 - 创建了任务PCL-Platform.Intelligence/AISynergy#19
out.remark = `${i18n.t('openedIssue')}<a href="${record.Action.RepoLink}/issues/${record.Action.IssueInfos[0]}" rel="nofollow">${record.Action.ShortRepoFullDisplayName}#${record.Action.IssueInfos[0]}</a>`;
break;
case 7: // 每日提出PR - 创建了合并请求OpenI/aiforge#1
out.remark = `${i18n.t('createdPullRequest')}<a href="${record.Action.RepoLink}/pulls/${record.Action.IssueInfos[0]}" rel="nofollow">${record.Action.ShortRepoFullDisplayName}#${record.Action.IssueInfos[0]}</a>`;
break;
case 10: // 发表评论 - 评论了任务PCL-Platform.Intelligence/AISynergy#19
out.remark = `${i18n.t('commentedOnIssue')}<a href="${record.Action.CommentLink}" rel="nofollow">${record.Action.ShortRepoFullDisplayName}#${record.Action.IssueInfos[0]}</a>`;
break;
case 24: // 上传数据集文件 - 上传了数据集文件MMISTData.zip
out.remark = `${i18n.t('uploadDataset')}<a href="${record.Action.RepoLink}/datasets" rel="nofollow">${record.Action.RefName}</a>`;
break;
case 30: // 导入新模型 - 导入了新模型resnet50_qx7l
out.remark = `${i18n.t('createdNewModel')}<a href="${record.Action.RepoLink}/modelmanage/show_model_info?name=${record.Action.RefName}" rel="nofollow">${record.Action.RefName}</a>`;
break;
case 34: // 完成微信扫码验证 - 首次绑定微信奖励
out.remark = `${i18n.t('firstBindingWechatRewards')}`;
break;
case 35: // 每日运行云脑任务 - 创建了(CPU/GPU/NPU)类型(调试/训练/推理/评测)任务tangl202204131431995
out.remark = `${i18n.t('created')}${record.Action?.Cloudbrain?.ComputeResource}${i18n.t('type')}${getJobType(record.Action?.Cloudbrain?.JobType)} <a href="${getJobTypeLink(record, 'INCREASE')}" rel="nofollow">${record.Action.RefName}</a>`;
break;
case 36: // 数据集被平台推荐 - 数据集XXX被设置为推荐数据集
out.remark = `${i18n.t('dataset')}<a href="${record.Action.RepoLink}/datasets" rel="nofollow">${record.Action.Content && record.Action.Content.split('|')[1]}</a>${i18n.t('setAsRecommendedDataset')}`;
break;
case 37: // 提交新公开镜像 - 提交了镜像jiangxiang_ceshi_tang03
out.remark = `${i18n.t('committedImage')}<span style="font-weight:bold;">${record.Action.Content && record.Action.Content.split('|')[1]}</span>`;
break;
case 38: // 镜像被平台推荐 - 镜像XXX被设置为推荐镜像
out.remark = `${i18n.t('image')}<span style="font-weight:bold;">${record.Action.Content && record.Action.Content.split('|')[1]}</span>${i18n.t('setAsRecommendedImage')}`;
break;
case 39: // 首次更换头像 - 更新了头像
out.remark = `${i18n.t('updatedAvatar')}`;
break;
case 40: // 每日commit - 推送了xxxx分支的代码到OpenI/aiforge
const words = record.Action.RefName.split('/');
const branch = words[words.length - 1];
out.remark = `${i18n.t('pushedBranch', {
branch: `<a href="${record.Action.RepoLink}/src/branch/${branch}" rel="nofollow">${branch}</a>`
})}<a href="${record.Action.RepoLink}" rel="nofollow">${record.Action.ShortRepoFullDisplayName}</a>`;
break;
default:
break;
}
} else if (record.SourceType === 'RUN_CLOUDBRAIN_TASK') {
//
}
if (record.LossAmount !== 0) {
out.amount = record.Amount;
out.remark += `${out.remark ? i18n.t(';') : ''}${i18n.t('dailyMaxTips')}`;
}
} else if (record.OperateType === 'DECREASE') {
if (record.SourceType === 'ADMIN_OPERATE') {
out.remark = record.Remark;
} else if (record.SourceType === 'ACCOMPLISH_TASK') {
//
} else if (record.SourceType === 'RUN_CLOUDBRAIN_TASK') {
out.taskName = `<a href="${getJobTypeLink(record, 'DECREASE')}" rel="nofollow">${record?.Cloudbrain?.DisplayJobName}</a>`;
if (record?.Cloudbrain?.ComputeResource === 'CPU/GPU') {
const resourceSpec = record?.Cloudbrain?.ResourceSpec?.ResourceSpec;
out.remark = `【${getJobType(record?.Cloudbrain?.JobType)}】【${record?.Cloudbrain?.ComputeResource}】【GPU: ${resourceSpec?.gpu}, CPU: ${resourceSpec?.cpu}, ${i18n.t('memory')}: ${(resourceSpec?.memMiB / 1024).toFixed(2)}GB, ${i18n.t('sharedMemory')}: ${(resourceSpec?.shareMemMiB / 1024).toFixed(2)}GB】`;
} else {
out.remark = `【${getJobType(record?.Cloudbrain?.JobType)}】【${record?.Cloudbrain?.ComputeResource}】【${record?.Cloudbrain?.ResourceSpec.FlavorInfo.desc}】`;
}
}
}
return out;
};

+ 16
- 0
web_src/vuepages/pages/reward/point/vp-point.js View File

@@ -0,0 +1,16 @@
import Vue from 'vue';
import ElementUI from 'element-ui';
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 './vp-point.vue';

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

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

+ 308
- 0
web_src/vuepages/pages/reward/point/vp-point.vue View File

@@ -0,0 +1,308 @@
<template>
<div class="__reward-pointer-c">
<div class="ui container" style="width:80%;min-width:1200px;">
<div class="__r_p_header">
<div>
<p class="__title">{{ $t('calcPointDetails') }}</p>
</div>
<div style="padding: 0 5px; font-size: 14px">
<span>
<i class="question circle icon link" style="color: rgba(3, 102, 214, 1)" data-position="right center"
data-variation="mini"></i>
<a href="/reward/point/rule" target="_blank" style="color: rgba(3, 102, 214, 1)">{{
$t('calcPointAcquisitionInstructions')
}}</a>
</span>
</div>
</div>
<div class="__r_p_summary">
<div class="__r_p_summary_item-c __flex-1">
<div class="__val">{{ summaryInfo.available }}</div>
<div class="__exp">{{ $t('CurrAvailableCalcPoints') }}</div>
</div>
<div class="__r_p_summary_line"></div>
<div class="__r_p_summary_item-c __flex-1">
<div class="__val">{{ summaryInfo.gain }}</div>
<div class="__exp">{{ $t('totalGainCalcPoints') }}</div>
</div>
<div class="__r_p_summary_item-c __flex-1">
<div class="__val">{{ summaryInfo.used }}</div>
<div class="__exp">{{ $t('totalConsumeCalcPoints') }}</div>
</div>
</div>
<div class="__r_p_tab">
<div class="__r_p_tab-item" :class="tabIndex === 0 ? '__focus' : ''" style="border-radius: 5px 0px 0px 5px"
@click="tabChange(0)">
{{ $t('gainDetail') }}
</div>
<div class="__r_p_tab-item" :class="tabIndex === 1 ? '__focus' : ''" style="border-radius: 0px 5px 5px 0px"
@click="tabChange(1)">
{{ $t('consumeDetail') }}
</div>
</div>
<div class="__r_p_table">
<div v-show="tabIndex === 0">
<el-table :data="tableData" row-key="sn" style="width: 100%" v-loading="loading" stripe
v-if="tableData.length">
<el-table-column column-key="sn" prop="sn" :label="$t('serialNumber')" align="center" header-align="center"
width="180">
</el-table-column>
<el-table-column column-key="date" prop="date" :label="$t('time')" align="center" header-align="center"
width="180">
</el-table-column>
<el-table-column column-key="sourceType" prop="sourceType" :label="$t('scene')" align="center"
header-align="center" width="180"></el-table-column>
<el-table-column column-key="action" prop="action" :label="$t('behaviorOfPoint')" align="center"
header-align="center" width="200"></el-table-column>
<el-table-column column-key="remark" prop="remark" :label="$t('explanation')" align="left" min-width="200"
header-align="center">
<template slot-scope="scope">
<span v-html="scope.row.remark"></span>
</template>
</el-table-column>
<el-table-column column-key="amount" prop="amount" :label="$t('points')" align="center"
header-align="center" width="120"></el-table-column>
<template slot="empty">
<span>{{ loading ? $t('loading') : $t('noData') }}</span>
</template>
</el-table>
<el-empty v-else :image-size="140" :description="$t('noPointGainRecord')"></el-empty>
</div>
<div v-show="tabIndex === 1">
<el-table :data="tableData" row-key="sn" style="width: 100%" v-loading="loading" stripe
v-if="tableData.length">
<el-table-column column-key="sn" prop="sn" :label="$t('serialNumber')" align="center" header-align="center"
width="180">
</el-table-column>
<el-table-column column-key="date" prop="date" :label="$t('time')" align="center" header-align="center"
width="180">
</el-table-column>
<el-table-column column-key="status" prop="status" :label="$t('status')" align="center"
header-align="center" width="120">
<template slot-scope="scope">
<span :style="{ color: scope.row.statusColor }">{{ scope.row.status }}</span>
</template>
</el-table-column>
<el-table-column column-key="sourceType" prop="sourceType" :label="$t('scene')" align="center"
header-align="center" width="180"></el-table-column>
<el-table-column column-key="duration" prop="duration" :label="$t('runTime')" align="center"
header-align="center" width="120"></el-table-column>
<el-table-column column-key="remark" prop="remark" :label="$t('explanation')" align="left" min-width="200"
header-align="center">
<template slot-scope="scope">
<span v-html="scope.row.remark"></span>
</template>
</el-table-column>
<el-table-column column-key="taskName" prop="taskName" :label="$t('taskName')" align="center"
header-align="center" width="180">
<template slot-scope="scope">
<span v-html="scope.row.taskName"></span>
</template>
</el-table-column>
<el-table-column column-key="amount" prop="amount" :label="$t('points')" align="center"
header-align="center" width="120"></el-table-column>
<template slot="empty">
<span>{{ loading ? $t('loading') : $t('noData') }}</span>
</template>
</el-table>
<el-empty v-else :image-size="140" :description="$t('noPointConsumeRecord')"></el-empty>
</div>
<div class="__r_p_pagination" v-if="tableData.length">
<div style="margin-top: 2rem">
<div class="center">
<el-pagination background @current-change="currentChange" :current-page="pageInfo.curpage"
:page-sizes="pageInfo.pageSizes" :page-size="pageInfo.pageSize"
layout="total, sizes, prev, pager, next, jumper" :total="pageInfo.total">
</el-pagination>
</div>
</div>
</div>
</div>
</div>
</div>
</template>

<script>
import { getPoint, getPointAccount, getPointList } from "~/apis/modules/point";
import { getRewardPointRecordInfo } from './utils';
export default {
data() {
return {
loading: false,
summaryInfo: {
available: 0,
gain: 0,
used: 0,
},
tabIndex: 0,
tableData: [],
pageInfo: {
curpage: 1,
pageSize: 10,
pageSizes: [10],
total: 0,
},
eventSource: null,
};
},
components: {},
methods: {
currentChange: function (val) {
this.pageInfo.curpage = val;
this.getTableData();
},
tabChange: function (index) {
if (this.tabIndex === index) return;
this.tabIndex = index;
this.pageInfo.curpage = 1;
this.pageInfo.total = 0;
this.getTableData();
},
getSummaryInfo: function () {
getPointAccount().then(res => {
if (res.data && res.data.Code === 0) {
const data = res.data.Data;
this.summaryInfo.available = data.Balance;
this.summaryInfo.gain = data.TotalEarned;
this.summaryInfo.used = data.TotalConsumed;
}
}).catch(err => {
console.log(err);
})
},
getTableData: function () {
this.loading = true;
getPointList({
Operate: this.tabIndex === 0 ? 'INCREASE' : 'DECREASE',
Page: this.pageInfo.curpage,
// pageSize: this.pageInfo.pageSize,
}).then((res) => {
this.loading = false;
const tableData = [];
if (res.data && res.data.Code === 0) {
const data = res.data.Data;
const records = data.Records;
for (let i = 0, iLen = records.length; i < iLen; i++) {
const record = records[i];
tableData.push(getRewardPointRecordInfo(record));
}
this.tableData.splice(0, Infinity, ...tableData);
this.pageInfo.total = data.Total;
}
})
.catch((err) => {
console.log(err);
this.loading = false;
this.tableData.splice(0, Infinity);
});
},
},
mounted: function () {
this.getSummaryInfo();
this.getTableData();
const { AppSubUrl, csrf, NotificationSettings } = window.config;
if (NotificationSettings.EventSourceUpdateTime > 0 && !!window.EventSource) {
const source = new EventSource(`${AppSubUrl}/user/events`);
source.addEventListener('reward-operation', (e) => {
try {
this.getSummaryInfo();
this.getTableData();
} catch (err) {
console.error(err);
}
});
this.eventSource = source;
}
},
beforeDestroy: function () {
this.eventSource && this.eventSource.close();
},
};
</script>

<style scoped lang="less">
.__flex-1 {
flex: 1;
}

.__reward-pointer-c {
.__r_p_header {
height: 30px;
margin: 10px 0;
display: flex;
align-items: center;
justify-content: space-between;

.__title {
font-weight: 400;
font-size: 18px;
color: rgb(16, 16, 16);
line-height: 26px;
}
}

.__r_p_summary {
display: flex;
align-items: center;
height: 100px;
background-color: rgb(245, 245, 246);

.__r_p_summary_item-c {
.__val {
text-align: center;
margin: 12px 0;
font-weight: 400;
font-size: 28px;
color: rgb(16, 16, 16);
}

.__exp {
text-align: center;
font-weight: 400;
font-size: 14px;
color: rgba(54, 56, 64, 1);
}
}
}
}

.__r_p_summary_line {
width: 1px;
height: 80%;
background-color: rgb(212, 212, 213);
}

.__r_p_tab {
display: flex;
margin: 18px 0;

.__r_p_tab-item {
width: 115px;
height: 38px;
display: flex;
justify-content: center;
align-items: center;
border: 1px solid rgb(225, 227, 230);
color: #101010;
box-sizing: border-box;
cursor: pointer;

&.__focus {
border-color: rgb(50, 145, 248);
color: rgb(50, 145, 248);
cursor: default;
}
}
}

.__r_p_table {
/deep/ .el-table__header {
th {
background: rgb(245, 245, 246);
color: rgb(96, 98, 102);
font-weight: 400;
font-size: 14px;
}
}
}
</style>

+ 7
- 0
web_src/vuepages/utils/index.js View File

@@ -0,0 +1,7 @@
export const getListValueWithKey = (list, key, k = 'k', v = 'v') => {
for (let i = 0, iLen = list.length; i < iLen; i++) {
const listI = list[i];
if (listI[k] === key) return listI[v];
}
return '';
};

+ 7
- 0
webpack.config.js View File

@@ -29,6 +29,11 @@ for (const path of stadalonePaths) {
standalone[parse(path).name] = [path];
}

const vuePages = {};
for (const path of glob('web_src/vuepages/**/vp-*.js')) {
vuePages[parse(path).name] = [path];
}

const isProduction = process.env.NODE_ENV !== 'development';

module.exports = {
@@ -44,6 +49,7 @@ module.exports = {
icons: glob('node_modules/@primer/octicons/build/svg/**/*.svg'),
...standalone,
...themes,
...vuePages,
},
devtool: false,
output: {
@@ -267,6 +273,7 @@ module.exports = {
symlinks: false,
alias: {
vue$: 'vue/dist/vue.esm.js', // needed because vue's default export is the runtime only
'~': resolve(__dirname, 'web_src/vuepages'),
},
extensions: ['.tsx', '.ts', '.js']
},


+ 7
- 0
webpack_pro.config.js View File

@@ -29,6 +29,11 @@ for (const path of stadalonePaths) {
standalone[parse(path).name] = [path];
}

const vuePages = {};
for (const path of glob('web_src/vuepages/**/vp-*.js')) {
vuePages[parse(path).name] = [path];
}

const isProduction = process.env.NODE_ENV !== 'development';

module.exports = {
@@ -44,6 +49,7 @@ module.exports = {
icons: glob('node_modules/@primer/octicons/build/svg/**/*.svg'),
...standalone,
...themes,
...vuePages
},
devtool: false,
output: {
@@ -267,6 +273,7 @@ module.exports = {
symlinks: false,
alias: {
vue$: 'vue/dist/vue.esm.js', // needed because vue's default export is the runtime only
'~': resolve(__dirname, 'web_src/vuepages'),
},
extensions: ['.tsx', '.ts', '.js']
},


Loading…
Cancel
Save