#5259 自定义镜像功能合入主分支。

Merged
ychao_1983 merged 120 commits from zouap_dev into V20240129 2 months ago
  1. +1
    -1
      cmd/web.go
  2. +12
    -0
      custom/public/json/images_version.json
  3. +42
    -0
      manager/client/grampus/grampus.go
  4. +16
    -11
      models/cloudbrain.go
  5. +262
    -41
      models/cloudbrain_image.go
  6. +1
    -0
      models/models.go
  7. +11
    -0
      models/user_analysis_for_activity.go
  8. +40
    -20
      models/user_business_analysis.go
  9. +19
    -1
      models/user_business_struct.go
  10. +20
    -0
      models/user_login_action_log.go
  11. +51
    -20
      modules/auth/cloudbrain.go
  12. +40
    -18
      modules/cloudbrain/resty.go
  13. +1
    -0
      modules/context/context.go
  14. +42
    -0
      modules/context/user_action_count.go
  15. +111
    -28
      modules/grampus/resty.go
  16. +4
    -0
      modules/public/public.go
  17. +2
    -0
      modules/setting/setting.go
  18. +8
    -3
      routers/admin/resources.go
  19. +8
    -0
      routers/api/v1/api.go
  20. +29
    -13
      routers/api/v1/repo/attachments.go
  21. +28
    -0
      routers/api/v1/repo/images.go
  22. +17
    -21
      routers/api/v1/repo/modelmanage.go
  23. +3
    -0
      routers/init.go
  24. +3
    -0
      routers/repo/ai_model_convert.go
  25. +96
    -18
      routers/repo/cloudbrain.go
  26. +26
    -10
      routers/repo/grampus.go
  27. +22
    -0
      routers/repo/user_data_analysis.go
  28. +8
    -2
      routers/routes/routes.go
  29. +2
    -2
      routers/user/auth.go
  30. +17
    -3
      services/ai_task_service/cluster/c2net.go
  31. +6
    -1
      services/ai_task_service/container_builder/dataset_builder.go
  32. +18
    -2
      services/ai_task_service/task/task_creation_info.go
  33. +66
    -1
      services/cloudbrain/resource/resource_queue.go
  34. +10
    -148
      templates/admin/cloudbrain/imagecommit.tmpl
  35. +3
    -24
      templates/explore/images.tmpl
  36. +9
    -121
      templates/repo/cloudbrain/image/apply.tmpl
  37. +9
    -114
      templates/repo/cloudbrain/image/edit.tmpl
  38. +12
    -118
      templates/repo/cloudbrain/image/submit.tmpl
  39. +0
    -6
      templates/reward/point/rule.tmpl
  40. +0
    -1
      vendor/gitea.com/macaron/csrf/csrf.go
  41. +0
    -1
      vendor/gitea.com/macaron/csrf/xsrf.go
  42. +82
    -32
      web_src/js/components/images/adminImages.vue
  43. +32
    -4
      web_src/js/features/i18nVue.js
  44. +9
    -0
      web_src/vuepages/apis/modules/common.js
  45. +64
    -3
      web_src/vuepages/apis/modules/images.js
  46. +0
    -1
      web_src/vuepages/components/cloudbrain/FormTop.vue
  47. +232
    -19
      web_src/vuepages/components/cloudbrain/ImageSelectV1.vue
  48. +15
    -2
      web_src/vuepages/const/index.js
  49. +56
    -1
      web_src/vuepages/langs/config/en-US.js
  50. +56
    -1
      web_src/vuepages/langs/config/zh-CN.js
  51. +17
    -17
      web_src/vuepages/pages/cloudbrain/configs.js
  52. +13
    -4
      web_src/vuepages/pages/cloudbrain/create/index.vue
  53. +1
    -1
      web_src/vuepages/pages/cloudbrain/tools.js
  54. +162
    -0
      web_src/vuepages/pages/images/square/components/Condition.vue
  55. +289
    -0
      web_src/vuepages/pages/images/square/components/Filters.vue
  56. +487
    -0
      web_src/vuepages/pages/images/square/components/Item.vue
  57. +178
    -0
      web_src/vuepages/pages/images/square/components/List.vue
  58. +99
    -0
      web_src/vuepages/pages/images/square/index.vue
  59. +17
    -0
      web_src/vuepages/pages/images/square/vp-images-square.js
  60. +716
    -0
      web_src/vuepages/pages/images/submit/index.vue
  61. +17
    -0
      web_src/vuepages/pages/images/submit/vp-images-submit.js

+ 1
- 1
cmd/web.go View File

@@ -167,7 +167,7 @@ func runWeb(ctx *cli.Context) error {
if setting.EnablePprof { if setting.EnablePprof {
go func() { go func() {
log.Info("Starting pprof server on localhost:6060") log.Info("Starting pprof server on localhost:6060")
log.Info("%v", http.ListenAndServe("192.168.207.34:6060", nil))
log.Info("%v", http.ListenAndServe("localhost:6060", nil))
}() }()
} }




+ 12
- 0
custom/public/json/images_version.json View File

@@ -0,0 +1,12 @@
{
"compute_resource": ["GPU", "GCU", "MLU", "ILUVATAR-GPGPU", "METAX-GPGPU"],
"framework":["PyTorch","TensorFlow","MindSpore","PaddlePaddle","Other"],
"framework_version":{
"PyTorch":["2.1.1","2.1.0","2.0.1","2.0.0", "1.13.1","1.13.0","1.12.1","1.12.0","1.11.0","1.10.1","1.10.0","1.9.1","1.9.0","1.6.0"],
"TensorFlow":["2.15.0","2.14.1","2.13.1","2.13.0","2.12.1","2.12.0","2.11.1","2.11.0","2.10.1","2.9.3","2.9.2","2.8.4","2.8.3","2.7.4"],
"MindSpore":["2.2.10","2.2.1","2.1.0","2.0.0","1.10.1","1.10.0"],
"PaddlePaddle":["2.6.0","2.5.2","2.5.1","2.5.0","2.4.2","2.4.1","2.4.0","2.3.2","2.3.1","2.3.0","2.2.2"]
},
"python":["3.13","3.12","3.11","3.10","3.9","3.8","3.7","3.6"],
"cuda":["12.3","12.2","12.1","11.8","11.7","11.6","11.5","11.4","11.3","11.2","11.1","10.2","10.1","10.0"]
}

+ 42
- 0
manager/client/grampus/grampus.go View File

@@ -29,6 +29,7 @@ const (
urlGetResourceSpecs = urlOpenApiV1 + "resourcespec" urlGetResourceSpecs = urlOpenApiV1 + "resourcespec"
urlGetAiCenter = urlOpenApiV1 + "sharescreen/aicenter" urlGetAiCenter = urlOpenApiV1 + "sharescreen/aicenter"
urlGetImages = urlOpenApiV1 + "image" urlGetImages = urlOpenApiV1 + "image"
urlListUserImages = urlOpenApiV1 + "listUserImage"
urlNotebookJob = urlOpenApiV1 + "notebook" urlNotebookJob = urlOpenApiV1 + "notebook"
urlInferenceJob = urlOpenApiV1 + "inference" urlInferenceJob = urlOpenApiV1 + "inference"


@@ -351,6 +352,8 @@ sendjob:
_ = getToken() _ = getToken()
goto sendjob goto sendjob
} }
jsonstr, _ := json.Marshal(result)
log.Info("GetImages resp:", string(jsonstr))


if result.ErrorCode != 0 { if result.ErrorCode != 0 {
log.Error("GetImages failed(%d): %s", result.ErrorCode, result.ErrorMsg) log.Error("GetImages failed(%d): %s", result.ErrorCode, result.ErrorMsg)
@@ -360,6 +363,45 @@ sendjob:
return &result, nil return &result, nil
} }


func GetUserImages(processorType string, jobType string) (*models.GetGrampusImagesResult, error) {
checkSetting()
client := getRestyClient()
var result models.GetGrampusImagesResult

retry := 0

queryType := "TrainJob"
if jobType == string(models.JobTypeDebug) {
queryType = "Notebook"
}

sendjob:
_, err := client.R().
SetAuthToken(TOKEN).
SetResult(&result).
Get(HOST + urlListUserImages + "?processorType=" + processorType + "&trainType=" + queryType)

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

if result.ErrorCode == errorIllegalToken && retry < 1 {
retry++
log.Info("retry get token")
_ = getToken()
goto sendjob
}
jsonstr, _ := json.Marshal(result)
log.Info("GetUserImages resp:", string(jsonstr))

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

return &result, nil
}

func GetTrainJobLog(jobID string, nodeId ...int) (string, error) { func GetTrainJobLog(jobID string, nodeId ...int) (string, error) {
checkSetting() checkSetting()
client := getRestyClient() client := getRestyClient()


+ 16
- 11
models/cloudbrain.go View File

@@ -1148,12 +1148,20 @@ type CommitImageCloudBrainParams struct {


type CommitImageParams struct { type CommitImageParams struct {
CommitImageCloudBrainParams CommitImageCloudBrainParams
IsPrivate bool
Topics []string
CloudBrainType int
UID int64
Place string
Type int
IsPrivate bool
Topics []string
CloudBrainType int
UID int64
Place string
Type int
Framework string
FrameworkVersion string
CudaVersion string
PythonVersion string
OperationSystem string
OperationSystemVersion string
ThirdPackages string
ComputeResource string
} }


type CommitImageResult struct { type CommitImageResult struct {
@@ -2038,17 +2046,14 @@ type GrampusResourceQueue struct {


} }


type AICenterImage struct {
AICenterID string `json:"aiCenterId"`
ImageUrl string `json:"imageUrl"`
}
type GrampusImage struct { type GrampusImage struct {
CreatedAt int64 `json:"createdAt"` CreatedAt int64 `json:"createdAt"`
UpdatedAt int64 `json:"updatedAt"` UpdatedAt int64 `json:"updatedAt"`
ID string `json:"id"` ID string `json:"id"`
Name string `json:"name"` Name string `json:"name"`
ProcessorType string `json:"processorType"` ProcessorType string `json:"processorType"`
AICenterImage []AICenterImage `json:"aiCenterImages"`
TrainType string `json:"trainType"`
AICenterImage []AiCenterImage `json:"aiCenterImages"`
} }


type GetGrampusImagesResult struct { type GetGrampusImagesResult struct {


+ 262
- 41
models/cloudbrain_image.go View File

@@ -1,12 +1,15 @@
package models package models


import ( import (
"database/sql/driver"
"encoding/json"
"fmt" "fmt"
"strings" "strings"
"unicode/utf8" "unicode/utf8"


"xorm.io/builder" "xorm.io/builder"


"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
) )


@@ -24,27 +27,60 @@ const OKApply ApplyStatus = 3
const FailApply ApplyStatus = 4 const FailApply ApplyStatus = 4


type Image struct { type Image struct {
ID int64 `xorm:"pk autoincr" json:"id"`
ImageID string `json:"image_id"`
Type int `xorm:"INDEX NOT NULL" json:"type"` //0 normal 5官方推荐,中间值保留为后续扩展
CloudbrainType int `xorm:"INDEX NOT NULL" json:"cloudbrainType"` //0 云脑一 1云脑二
UID int64 `xorm:"INDEX NOT NULL" json:"uid"`
IsPrivate bool `xorm:"INDEX NOT NULL" json:"isPrivate"`
Tag string `xorm:"varchar(100) UNIQUE" json:"tag"`
Description string `xorm:"varchar(1000)" json:"description"`
Topics []string `xorm:"TEXT JSON" json:"topics"`
Place string `xorm:"varchar(300)" json:"place"`
NumStars int `xorm:"NOT NULL DEFAULT 0" json:"numStars"`
ComputeResource string `xorm:"varchar(30)" json:"compute_resource"`
IsStar bool `xorm:"-" json:"isStar"`
UserName string `xorm:"-" json:"userName"`
RelAvatarLink string `xorm:"-" json:"relAvatarLink"`
Status int `xorm:"INDEX NOT NULL DEFAULT 0" json:"status"` //0代表正在提交,1提交完成,2提交失败
ApplyStatus ApplyStatus `xorm:"DEFAULT 1" json:"apply_status"`
Message string `xorm:"varchar(500)" json:"message"`
UseCount int64 `xorm:"NOT NULL DEFAULT 0" json:"useCount"`
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created" json:"createdUnix"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated" json:"updatedUnix"`
ID int64 `xorm:"pk autoincr" json:"id"`
ImageID string `json:"image_id"`
Type int `xorm:"INDEX NOT NULL" json:"type"` //0 normal 5官方推荐,中间值保留为后续扩展
CloudbrainType int `xorm:"INDEX NOT NULL" json:"cloudbrainType"` //0 云脑一 1云脑二
UID int64 `xorm:"INDEX NOT NULL" json:"uid"`
IsPrivate bool `xorm:"INDEX NOT NULL" json:"isPrivate"`
Tag string `xorm:"varchar(100) UNIQUE" json:"tag"`
Description string `xorm:"varchar(1000)" json:"description"`
Topics []string `xorm:"TEXT JSON" json:"topics"`
Place string `xorm:"varchar(300)" json:"place"`
NumStars int `xorm:"NOT NULL DEFAULT 0" json:"numStars"`
ComputeResource string `xorm:"varchar(30)" json:"compute_resource"`
IsStar bool `xorm:"-" json:"isStar"`
UserName string `xorm:"-" json:"userName"`
RelAvatarLink string `xorm:"-" json:"relAvatarLink"`
Status int `xorm:"INDEX NOT NULL DEFAULT 0" json:"status"` //0代表正在提交,1提交完成,2提交失败
ApplyStatus ApplyStatus `xorm:"DEFAULT 1" json:"apply_status"`
Message string `xorm:"varchar(500)" json:"message"`
UseCount int64 `xorm:"NOT NULL DEFAULT 0" json:"useCount"`
Framework string `xorm:"varchar(100)" json:"framework" `
FrameworkVersion string `xorm:"varchar(50)" json:"frameworkVersion" `
CudaVersion string `xorm:"varchar(50)" json:"cudaVersion" `
PythonVersion string `xorm:"varchar(50)" json:"pythonVersion" `
OperationSystem string `xorm:"varchar(100)" json:"operationSystem" `
OperationSystemVersion string `xorm:"varchar(50)" json:"operationSystemVersion" `
ThirdPackages string `xorm:"varchar(1000)" json:"thirdPackages" `
AiCenterImages `gorm:"type:json;comment:智算中心镜像"`
GrampusBaseImage int `xorm:"" json:"grampusBaseImage"` //为1,表示分中心基础镜像
TrainType string `xorm:"varchar(30)" json:"trainType"` // 镜像适用调试还是训练
HuJingId string `xorm:"varchar(50)" json:"huJingId" ` //虎鲸侧镜像ID,用于同步镜像信息
ProcessorType string `xorm:"varchar(50)" json:"processorType" ` //虎鲸侧资源类型,用于同步镜像信息
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created" json:"createdUnix"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated" json:"updatedUnix"`
}

type AiCenterImages []AiCenterImage

type AiCenterImage struct {
AiCenterId string `json:"aiCenterId"`
ImageUrl string `json:"imageUrl"`
ImageId string `json:"imageId"`
}

func (r AiCenterImages) Value() (driver.Value, error) {
return json.Marshal(r)
}

func (r *AiCenterImages) Scan(input interface{}) error {
switch v := input.(type) {
case []byte:
return json.Unmarshal(input.([]byte), r)
default:
return fmt.Errorf("cannot Scan() from: %#v", v)
}
} }


type ImageList []*Image type ImageList []*Image
@@ -64,24 +100,43 @@ type ImageTopic struct {
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
} }


type ImageVersionList struct {
}

type ImageTopicRelation struct { type ImageTopicRelation struct {
ImageID int64 `xorm:"UNIQUE(s)"` ImageID int64 `xorm:"UNIQUE(s)"`
TopicID int64 `xorm:"UNIQUE(s)"` TopicID int64 `xorm:"UNIQUE(s)"`
} }
type SearchAvailableValueOptions struct {
Column string
Framework string
FrameworkVersion string
PythonVersion string
}


type SearchImageOptions struct { type SearchImageOptions struct {
Keyword string
UID int64
Status int
IncludePublicOnly bool
IncludeOfficialOnly bool
IncludePrivateOnly bool
IncludeStarByMe bool
IncludeCustom bool
IncludeOwnerOnly bool
Topics string
ApplyStatus int
CloudbrainType int
Keyword string
UID int64
Status int
IncludePublicOnly bool
IncludeOfficialOnly bool
IncludePrivateOnly bool
IncludeStarByMe bool
IncludeCustom bool
IncludeOwnerOnly bool
Topics string
ApplyStatus int
CloudbrainType int
Framework string
FrameworkVersion string
CudaVersion string
PythonVersion string
OperationSystem string
OperationSystemVersion string
ThirdPackages string
AiCenterId string
TrainType string
ComputeResource string
ListOptions ListOptions
SearchOrderBy SearchOrderBy
} }
@@ -113,12 +168,20 @@ type CommitImageGrampusParams struct {
} }
type CommitGrampusImageParams struct { type CommitGrampusImageParams struct {
CommitImageGrampusParams CommitImageGrampusParams
IsPrivate bool
Topics []string
CloudBrainType int
UID int64
Place string
Type int
IsPrivate bool
Topics []string
CloudBrainType int
UID int64
Place string
Type int
Framework string
FrameworkVersion string
CudaVersion string
PythonVersion string
OperationSystem string
OperationSystemVersion string
ThirdPackages string
ComputeResource string
} }


type CommitGrampusImageResult struct { type CommitGrampusImageResult struct {
@@ -231,6 +294,15 @@ func GetImageByTag(tag string) (*Image, error) {
return image, nil return image, nil
} }


func GetImageByComputeResource(computeResource string) ([]Image, error) {
images := make([]Image, 0)
err := x.Where("status=1 and compute_resource=?", computeResource).Find(&images)
if err != nil {
return nil, err
}
return images, nil
}

func GetImageByTagAndCloudbrainType(tag string, cloudbrainType int) (*Image, error) { func GetImageByTagAndCloudbrainType(tag string, cloudbrainType int) (*Image, error) {
image := &Image{Tag: tag} image := &Image{Tag: tag}
has, err := x. has, err := x.
@@ -403,6 +475,35 @@ func removeTopicFromImage(e Engine, imageId int64, topic *ImageTopic) error {
return nil return nil
} }


func GetImageAvailableColumnValues(opts *SearchAvailableValueOptions) []string {
var cond = builder.NewCond()

if opts.Framework != "" {
cond = cond.And(builder.Eq{"framework": opts.Framework})
}
if opts.FrameworkVersion != "" {
cond = cond.And(builder.Eq{"framework_version": opts.FrameworkVersion})
}
if opts.PythonVersion != "" {
cond = cond.And(builder.Eq{"python_version": opts.PythonVersion})
}
cond = cond.And(builder.NotNull{opts.Column})

var columnValues []string
var re []string
_ = x.Table("image").Where(cond).Distinct(opts.Column).Desc(opts.Column).Cols(opts.Column).Find(&columnValues)
if columnValues == nil {
return []string{}
} else {
for _, tmp := range columnValues {
if tmp != "" {
re = append(re, tmp)
}
}
}
return re
}

func SearchImage(opts *SearchImageOptions) (ImageList, int64, error) { func SearchImage(opts *SearchImageOptions) (ImageList, int64, error) {
cond := SearchImageCondition(opts) cond := SearchImageCondition(opts)
return SearchImageByCondition(opts, cond) return SearchImageByCondition(opts, cond)
@@ -431,6 +532,13 @@ func SearchImageCondition(opts *SearchImageOptions) builder.Cond {


likes = likes.Or(builder.Like{"LOWER(description)", strings.ToLower(v)}) likes = likes.Or(builder.Like{"LOWER(description)", strings.ToLower(v)})


likes = likes.Or(builder.Like{"LOWER(third_packages)", strings.ToLower(v)})

likes = likes.Or(builder.Like{"LOWER(CONCAT(framework,framework_version))", strings.ToLower(v)})
likes = likes.Or(builder.Like{"LOWER(CONCAT('cuda',cuda_version))", strings.ToLower(v)})
likes = likes.Or(builder.Like{"LOWER(CONCAT('python',python_version))", strings.ToLower(v)})
likes = likes.Or(builder.Like{"LOWER(CONCAT(operation_system,operation_system_version))", strings.ToLower(v)})

} }
keywordCond = keywordCond.Or(likes) keywordCond = keywordCond.Or(likes)


@@ -473,6 +581,48 @@ func SearchImageCondition(opts *SearchImageOptions) builder.Cond {
if opts.Status >= 0 { if opts.Status >= 0 {
cond = cond.And(builder.Eq{"status": opts.Status}) cond = cond.And(builder.Eq{"status": opts.Status})
} }
if opts.Framework != "" {
cond = cond.And(builder.Eq{"framework": opts.Framework})
}
if opts.FrameworkVersion != "" {
cond = cond.And(builder.Eq{"framework_version": opts.FrameworkVersion})
}
if opts.CudaVersion != "" {
cond = cond.And(builder.Eq{"cuda_version": opts.CudaVersion})
}
if opts.PythonVersion != "" {
cond = cond.And(builder.Eq{"python_version": opts.PythonVersion})
}
if opts.OperationSystem != "" {
cond = cond.And(builder.Eq{"operation_system": opts.OperationSystem})
}
if opts.OperationSystemVersion != "" {
cond = cond.And(builder.Eq{"operation_system_version": opts.OperationSystemVersion})
}
if opts.AiCenterId != "" {
cond = cond.And(builder.Like{"ai_center_images", "\"aiCenterId\":\"" + opts.AiCenterId + "\""})
}
if opts.TrainType != "" {
var orCond = builder.NewCond()
orCond = orCond.Or(builder.Eq{"train_type": opts.TrainType})
orCond = orCond.Or(builder.Eq{"train_type": ""})
log.Info("opts.TrainType=" + opts.TrainType)
cond = cond.And(orCond)
}
if opts.ComputeResource != "" {
cond = cond.And(builder.Eq{"compute_resource": opts.ComputeResource})
}
var third_likes, isValid = builder.NewCond(), false
for _, v := range strings.Split(opts.ThirdPackages, " ") {
if v != "" {
third_likes = third_likes.And(builder.Like{"LOWER(third_packages)", strings.ToLower(v)})
isValid = true
}

}
if isValid {
cond = cond.And(third_likes)
}


if opts.IncludeStarByMe { if opts.IncludeStarByMe {


@@ -579,7 +729,8 @@ func CreateLocalImage(image *Image) error {


func UpdateLocalImage(image *Image) error { func UpdateLocalImage(image *Image) error {


_, err := x.ID(image.ID).Cols("description", "is_private", "status", "message", "apply_status").Update(image)
_, err := x.ID(image.ID).Cols("description", "is_private", "status", "message", "apply_status", "framework", "framework_version",
"cuda_version", "python_version", "operation_system", "operation_system_version,third_packages").Update(image)
return err return err
} }


@@ -591,7 +742,7 @@ func UpdateLocalImageStatus(image *Image) error {


func UpdateLocalImageStatusAndPlace(image *Image) error { func UpdateLocalImageStatusAndPlace(image *Image) error {


_, err := x.ID(image.ID).Cols("status", "place").Update(image)
_, err := x.ID(image.ID).Cols("status", "place", "ai_center_images").Update(image)
return err return err
} }


@@ -672,3 +823,73 @@ func GetRecommondType(recommond bool) int {
} }


} }

func GetGrampusAllBaseImage() (ImageList, error) {
var cond = builder.NewCond()
cond = cond.And(builder.Eq{"grampus_base_image": 1})
images := make(ImageList, 0, 100)

err := x.Table("image").Where(cond).Find(&images)
if err != nil {
return nil, err
}
return images, nil
}

func getComputeResourceByProcessType(processType string) string {
tail := strings.LastIndex(processType, "/")
if tail > 0 {
return strings.ToUpper(processType[tail+1:])
}
return strings.ToUpper(processType)
}

func SyncGrampusAllBaseImageToDb(insertList []GrampusImage, updateList []GrampusImage, updateDbList []*Image, deleteList []*Image, doerId int64) error {
for _, tmp := range insertList {
log.Info("insert image,image name=" + tmp.Name)
insObj := &Image{
Type: 5,
CloudbrainType: 2,
UID: doerId,
IsPrivate: false,
Tag: tmp.Name,
ProcessorType: tmp.ProcessorType,
ComputeResource: getComputeResourceByProcessType(tmp.ProcessorType),
Status: 1,
GrampusBaseImage: 1,
TrainType: tmp.TrainType,
HuJingId: tmp.ID,
AiCenterImages: tmp.AICenterImage,
}
if insObj.ComputeResource == "GPU" && tmp.AICenterImage != nil && len(tmp.AICenterImage) > 0 {
insObj.Place = tmp.AICenterImage[0].ImageUrl
}
x.Insert(insObj)
}
for _, tmp := range deleteList {
log.Info("delete image,image name=" + tmp.Tag)
x.Delete(tmp)
}

for i, tmp := range updateList {
log.Info("update image,image name=" + tmp.Name)
updateDbList[i].AiCenterImages = tmp.AICenterImage
updateDbList[i].Tag = tmp.Name
x.ID(updateDbList[i].ID).Cols("ai_center_images", "tag").Update(updateDbList[i])
}

return nil
}

func GetGrampusSrcAllBaseImage(srcImageUrl string) (ImageList, error) {
var cond = builder.NewCond()
cond = cond.And(builder.Eq{"grampus_base_image": 1})
cond = cond.And(builder.Eq{"train_type": "Notebook"})
cond = cond.And(builder.Like{"ai_center_images", "\"imageUrl\":\"" + srcImageUrl + "\""})
images := make(ImageList, 0, 10)
err := x.Table("image").Where(cond).Find(&images)
if err != nil {
return nil, err
}
return images, nil
}

+ 1
- 0
models/models.go View File

@@ -195,6 +195,7 @@ func init() {
new(UserBusinessAnalysisYesterday), new(UserBusinessAnalysisYesterday),
new(UserBusinessAnalysisLastWeek), new(UserBusinessAnalysisLastWeek),
new(UserLoginLog), new(UserLoginLog),
new(UserLoginActionLog),
new(UserOtherInfo), new(UserOtherInfo),
new(UserMetrics), new(UserMetrics),
new(UserAnalysisPara), new(UserAnalysisPara),


+ 11
- 0
models/user_analysis_for_activity.go View File

@@ -589,3 +589,14 @@ func QueryUserAnnualReport(userId int64) *UserSummaryCurrentYear {
} }
return nil return nil
} }

func GetLastModifyTime() string {
statictisSess := xStatistic.NewSession()
defer statictisSess.Close()
userBusinessAnalysisLastMonth := &UserBusinessAnalysisLastMonth{}
err := statictisSess.Select("*").Table(new(UserBusinessAnalysisLastMonth)).Limit(1, 0).Find(userBusinessAnalysisLastMonth)
if err == nil {
return userBusinessAnalysisLastMonth.DataDate
}
return ""
}

+ 40
- 20
models/user_business_analysis.go View File

@@ -41,24 +41,6 @@ func (ulist UserBusinessAnalysisList) Less(i, j int) bool {
return ulist[i].ID > ulist[j].ID return ulist[i].ID > ulist[j].ID
} }


func getLastCountDate() int64 {
statictisSess := xStatistic.NewSession()
defer statictisSess.Close()
statictisSess.Limit(1, 0)
userBusinessAnalysisList := make([]*UserBusinessAnalysis, 0)
if err := statictisSess.Table("user_business_analysis").OrderBy("count_date desc").Limit(1, 0).
Find(&userBusinessAnalysisList); err == nil {
for _, userRecord := range userBusinessAnalysisList {
return userRecord.CountDate - 10000
}
} else {
log.Info("query error." + err.Error())
}
currentTimeNow := time.Now()
pageStartTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 0, 0, 0, 0, currentTimeNow.Location())
return pageStartTime.Unix()
}

func QueryMetricsPage(start int64, end int64) ([]*UserMetrics, int64) { func QueryMetricsPage(start int64, end int64) ([]*UserMetrics, int64) {


statictisSess := xStatistic.NewSession() statictisSess := xStatistic.NewSession()
@@ -633,6 +615,8 @@ func refreshUserStaticTable(wikiCountMap map[string]int, tableName string, pageS
CreateRepoCountMap, _, _, _ := queryUserCreateRepo(start_unix, end_unix) CreateRepoCountMap, _, _, _ := queryUserCreateRepo(start_unix, end_unix)
LoginCountMap := queryLoginCount(start_unix, end_unix) LoginCountMap := queryLoginCount(start_unix, end_unix)


LoginActionCountMap := queryLoginActionCount(start_unix, end_unix)

OpenIIndexMap := queryUserRepoOpenIIndex(startTime.Unix(), end_unix) OpenIIndexMap := queryUserRepoOpenIIndex(startTime.Unix(), end_unix)
CloudBrainTaskMap, CloudBrainTaskItemMap, _ := queryCloudBrainTask(start_unix, end_unix) CloudBrainTaskMap, CloudBrainTaskItemMap, _ := queryCloudBrainTask(start_unix, end_unix)
AiModelManageMap := queryUserModel(start_unix, end_unix) AiModelManageMap := queryUserModel(start_unix, end_unix)
@@ -695,6 +679,7 @@ func refreshUserStaticTable(wikiCountMap map[string]int, tableName string, pageS
dateRecordAll.EncyclopediasCount = getMapKeyStringValue(dateRecordAll.Name, wikiCountMap) dateRecordAll.EncyclopediasCount = getMapKeyStringValue(dateRecordAll.Name, wikiCountMap)
dateRecordAll.CreateRepoCount = getMapValue(dateRecordAll.ID, CreateRepoCountMap) dateRecordAll.CreateRepoCount = getMapValue(dateRecordAll.ID, CreateRepoCountMap)
dateRecordAll.LoginCount = getMapValue(dateRecordAll.ID, LoginCountMap) dateRecordAll.LoginCount = getMapValue(dateRecordAll.ID, LoginCountMap)
dateRecordAll.LoginActionCount = getMapValue(dateRecordAll.ID, LoginActionCountMap)


if _, ok := OpenIIndexMap[dateRecordAll.ID]; !ok { if _, ok := OpenIIndexMap[dateRecordAll.ID]; !ok {
dateRecordAll.OpenIIndex = 0 dateRecordAll.OpenIIndex = 0
@@ -720,6 +705,7 @@ func refreshUserStaticTable(wikiCountMap map[string]int, tableName string, pageS
dateRecordAll.RecommendImage = getMapValue(dateRecordAll.ID, RecommendImage) dateRecordAll.RecommendImage = getMapValue(dateRecordAll.ID, RecommendImage)
dateRecordAll.InvitationUserNum = getMapValue(dateRecordAll.ID, InvitationMap) dateRecordAll.InvitationUserNum = getMapValue(dateRecordAll.ID, InvitationMap)
dateRecordAll.UserIndexPrimitive = getUserIndexFromAnalysisAll(dateRecordAll, ParaWeight) dateRecordAll.UserIndexPrimitive = getUserIndexFromAnalysisAll(dateRecordAll, ParaWeight)

userIndexMap[dateRecordAll.ID] = dateRecordAll.UserIndexPrimitive userIndexMap[dateRecordAll.ID] = dateRecordAll.UserIndexPrimitive
if maxUserIndex < dateRecordAll.UserIndexPrimitive { if maxUserIndex < dateRecordAll.UserIndexPrimitive {
maxUserIndex = dateRecordAll.UserIndexPrimitive maxUserIndex = dateRecordAll.UserIndexPrimitive
@@ -933,7 +919,7 @@ func insertTable(dateRecords []UserBusinessAnalysisAll, tableName string, static


insertBatchSql := "INSERT INTO public." + tableName + insertBatchSql := "INSERT INTO public." + tableName +
"(id, count_date, code_merge_count, commit_count, issue_count, comment_count, focus_repo_count, star_repo_count, watched_count, gitea_age_month, commit_code_size, commit_dataset_size, " + "(id, count_date, code_merge_count, commit_count, issue_count, comment_count, focus_repo_count, star_repo_count, watched_count, gitea_age_month, commit_code_size, commit_dataset_size, " +
"commit_model_count, solve_issue_count, encyclopedias_count, regist_date, create_repo_count, login_count, open_i_index, email, name, data_date,cloud_brain_task_num,gpu_debug_job,npu_debug_job,gpu_train_job,npu_train_job,npu_inference_job,gpu_bench_mark_job,cloud_brain_run_time,commit_dataset_num,user_index,user_location,focus_other_user,collect_dataset,collected_dataset,recommend_dataset,collect_image,collected_image,recommend_image,user_index_primitive,phone,invitation_user_num,model_convert_count) " +
"commit_model_count, solve_issue_count, encyclopedias_count, regist_date, create_repo_count, login_count, open_i_index, email, name, data_date,cloud_brain_task_num,gpu_debug_job,npu_debug_job,gpu_train_job,npu_train_job,npu_inference_job,gpu_bench_mark_job,cloud_brain_run_time,commit_dataset_num,user_index,user_location,focus_other_user,collect_dataset,collected_dataset,recommend_dataset,collect_image,collected_image,recommend_image,user_index_primitive,phone,invitation_user_num,model_convert_count,login_action_count) " +
"VALUES" "VALUES"


for i, record := range dateRecords { for i, record := range dateRecords {
@@ -942,7 +928,7 @@ func insertTable(dateRecords []UserBusinessAnalysisAll, tableName string, static
", " + fmt.Sprint(record.WatchedCount) + ", " + fmt.Sprint(record.GiteaAgeMonth) + ", " + fmt.Sprint(record.CommitCodeSize) + ", " + fmt.Sprint(record.CommitDatasetSize) + ", " + fmt.Sprint(record.WatchedCount) + ", " + fmt.Sprint(record.GiteaAgeMonth) + ", " + fmt.Sprint(record.CommitCodeSize) + ", " + fmt.Sprint(record.CommitDatasetSize) +
", " + fmt.Sprint(record.CommitModelCount) + ", " + fmt.Sprint(record.SolveIssueCount) + ", " + fmt.Sprint(record.EncyclopediasCount) + ", " + fmt.Sprint(record.RegistDate) + ", " + fmt.Sprint(record.CommitModelCount) + ", " + fmt.Sprint(record.SolveIssueCount) + ", " + fmt.Sprint(record.EncyclopediasCount) + ", " + fmt.Sprint(record.RegistDate) +
", " + fmt.Sprint(record.CreateRepoCount) + ", " + fmt.Sprint(record.LoginCount) + ", " + fmt.Sprint(record.OpenIIndex) + ", '" + record.Email + "', '" + record.Name + "', '" + record.DataDate + "'," + fmt.Sprint(record.CloudBrainTaskNum) + "," + fmt.Sprint(record.GpuDebugJob) + "," + fmt.Sprint(record.NpuDebugJob) + "," + fmt.Sprint(record.GpuTrainJob) + "," + fmt.Sprint(record.NpuTrainJob) + "," + fmt.Sprint(record.NpuInferenceJob) + "," + fmt.Sprint(record.GpuBenchMarkJob) + "," + fmt.Sprint(record.CloudBrainRunTime) + "," + fmt.Sprint(record.CommitDatasetNum) + "," + fmt.Sprint(record.UserIndex) + ",'" + record.UserLocation + "'," + ", " + fmt.Sprint(record.CreateRepoCount) + ", " + fmt.Sprint(record.LoginCount) + ", " + fmt.Sprint(record.OpenIIndex) + ", '" + record.Email + "', '" + record.Name + "', '" + record.DataDate + "'," + fmt.Sprint(record.CloudBrainTaskNum) + "," + fmt.Sprint(record.GpuDebugJob) + "," + fmt.Sprint(record.NpuDebugJob) + "," + fmt.Sprint(record.GpuTrainJob) + "," + fmt.Sprint(record.NpuTrainJob) + "," + fmt.Sprint(record.NpuInferenceJob) + "," + fmt.Sprint(record.GpuBenchMarkJob) + "," + fmt.Sprint(record.CloudBrainRunTime) + "," + fmt.Sprint(record.CommitDatasetNum) + "," + fmt.Sprint(record.UserIndex) + ",'" + record.UserLocation + "'," +
fmt.Sprint(record.FocusOtherUser) + "," + fmt.Sprint(record.CollectDataset) + "," + fmt.Sprint(record.CollectedDataset) + "," + fmt.Sprint(record.RecommendDataset) + "," + fmt.Sprint(record.CollectImage) + "," + fmt.Sprint(record.CollectedImage) + "," + fmt.Sprint(record.RecommendImage) + "," + fmt.Sprint(record.UserIndexPrimitive) + ",'" + record.Phone + "'" + "," + fmt.Sprint(record.InvitationUserNum) + "," + fmt.Sprint(record.ModelConvertCount) + ")"
fmt.Sprint(record.FocusOtherUser) + "," + fmt.Sprint(record.CollectDataset) + "," + fmt.Sprint(record.CollectedDataset) + "," + fmt.Sprint(record.RecommendDataset) + "," + fmt.Sprint(record.CollectImage) + "," + fmt.Sprint(record.CollectedImage) + "," + fmt.Sprint(record.RecommendImage) + "," + fmt.Sprint(record.UserIndexPrimitive) + ",'" + record.Phone + "'" + "," + fmt.Sprint(record.InvitationUserNum) + "," + fmt.Sprint(record.ModelConvertCount) + "," + fmt.Sprint(record.LoginActionCount) + ")"
if i < (len(dateRecords) - 1) { if i < (len(dateRecords) - 1) {
insertBatchSql += "," insertBatchSql += ","
} }
@@ -2222,6 +2208,40 @@ func queryLoginCount(start_unix int64, end_unix int64) map[int64]int {
return resultMap return resultMap
} }


func queryLoginActionCount(start_unix int64, end_unix int64) map[int64]int {
statictisSess := xStatistic.NewSession()
defer statictisSess.Close()

resultMap := make(map[int64]int)
cond := "created_unix>=" + fmt.Sprint(start_unix) + " and created_unix<=" + fmt.Sprint(end_unix)
count, err := statictisSess.Where(cond).Count(new(UserLoginActionLog))
if err != nil {
log.Info("query UserLoginActionLog error. return.")
return resultMap
}
var indexTotal int64
indexTotal = 0
for {
statictisSess.Select("id,u_id").Table("user_login_action_log").Where(cond).OrderBy("id asc").Limit(PAGE_SIZE, int(indexTotal))
userLoginActionLogList := make([]*UserLoginActionLog, 0)
statictisSess.Find(&userLoginActionLogList)
log.Info("query user login action size=" + fmt.Sprint(len(userLoginActionLogList)))
for _, loginRecord := range userLoginActionLogList {
if _, ok := resultMap[loginRecord.UId]; !ok {
resultMap[loginRecord.UId] = 1
} else {
resultMap[loginRecord.UId] += 1
}
}
indexTotal += PAGE_SIZE
if indexTotal >= count {
break
}
}
log.Info("user login action size=" + fmt.Sprint(len(resultMap)))
return resultMap
}

func queryUserModel(start_unix int64, end_unix int64) map[int64]int { func queryUserModel(start_unix int64, end_unix int64) map[int64]int {
sess := x.NewSession() sess := x.NewSession()
defer sess.Close() defer sess.Close()


+ 19
- 1
models/user_business_struct.go View File

@@ -35,7 +35,7 @@ type UserBusinessAnalysisCurrentYear struct {
RegistDate timeutil.TimeStamp `xorm:"NOT NULL"` RegistDate timeutil.TimeStamp `xorm:"NOT NULL"`
//repo //repo
CreateRepoCount int `xorm:"NOT NULL DEFAULT 0"` CreateRepoCount int `xorm:"NOT NULL DEFAULT 0"`
//login count, from elk
//login count
LoginCount int `xorm:"NOT NULL DEFAULT 0"` LoginCount int `xorm:"NOT NULL DEFAULT 0"`
//openi index //openi index
OpenIIndex float64 `xorm:"NOT NULL DEFAULT 0"` OpenIIndex float64 `xorm:"NOT NULL DEFAULT 0"`
@@ -69,6 +69,8 @@ type UserBusinessAnalysisCurrentYear struct {
Phone string `xorm:"NULL"` Phone string `xorm:"NULL"`
InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"` InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"`
ModelConvertCount int `xorm:"NOT NULL DEFAULT 0"` ModelConvertCount int `xorm:"NOT NULL DEFAULT 0"`

LoginActionCount int `xorm:"NOT NULL DEFAULT 0"`
} }


type UserBusinessAnalysisLast30Day struct { type UserBusinessAnalysisLast30Day struct {
@@ -138,6 +140,8 @@ type UserBusinessAnalysisLast30Day struct {
Phone string `xorm:"NULL"` Phone string `xorm:"NULL"`
InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"` InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"`
ModelConvertCount int `xorm:"NOT NULL DEFAULT 0"` ModelConvertCount int `xorm:"NOT NULL DEFAULT 0"`

LoginActionCount int `xorm:"NOT NULL DEFAULT 0"`
} }


type UserBusinessAnalysisLastMonth struct { type UserBusinessAnalysisLastMonth struct {
@@ -207,6 +211,8 @@ type UserBusinessAnalysisLastMonth struct {
Phone string `xorm:"NULL"` Phone string `xorm:"NULL"`
InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"` InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"`
ModelConvertCount int `xorm:"NOT NULL DEFAULT 0"` ModelConvertCount int `xorm:"NOT NULL DEFAULT 0"`

LoginActionCount int `xorm:"NOT NULL DEFAULT 0"`
} }


type UserBusinessAnalysisCurrentMonth struct { type UserBusinessAnalysisCurrentMonth struct {
@@ -276,6 +282,8 @@ type UserBusinessAnalysisCurrentMonth struct {
Phone string `xorm:"NULL"` Phone string `xorm:"NULL"`
InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"` InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"`
ModelConvertCount int `xorm:"NOT NULL DEFAULT 0"` ModelConvertCount int `xorm:"NOT NULL DEFAULT 0"`

LoginActionCount int `xorm:"NOT NULL DEFAULT 0"`
} }


type UserBusinessAnalysisCurrentWeek struct { type UserBusinessAnalysisCurrentWeek struct {
@@ -346,6 +354,8 @@ type UserBusinessAnalysisCurrentWeek struct {
Phone string `xorm:"NULL"` Phone string `xorm:"NULL"`
InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"` InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"`
ModelConvertCount int `xorm:"NOT NULL DEFAULT 0"` ModelConvertCount int `xorm:"NOT NULL DEFAULT 0"`

LoginActionCount int `xorm:"NOT NULL DEFAULT 0"`
} }


type UserBusinessAnalysisYesterday struct { type UserBusinessAnalysisYesterday struct {
@@ -416,6 +426,8 @@ type UserBusinessAnalysisYesterday struct {
Phone string `xorm:"NULL"` Phone string `xorm:"NULL"`
InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"` InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"`
ModelConvertCount int `xorm:"NOT NULL DEFAULT 0"` ModelConvertCount int `xorm:"NOT NULL DEFAULT 0"`

LoginActionCount int `xorm:"NOT NULL DEFAULT 0"`
} }


type UserBusinessAnalysisLastWeek struct { type UserBusinessAnalysisLastWeek struct {
@@ -486,6 +498,8 @@ type UserBusinessAnalysisLastWeek struct {
Phone string `xorm:"NULL"` Phone string `xorm:"NULL"`
InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"` InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"`
ModelConvertCount int `xorm:"NOT NULL DEFAULT 0"` ModelConvertCount int `xorm:"NOT NULL DEFAULT 0"`

LoginActionCount int `xorm:"NOT NULL DEFAULT 0"`
} }


type UserAnalysisPara struct { type UserAnalysisPara struct {
@@ -603,6 +617,8 @@ type UserBusinessAnalysisAll struct {
Phone string `xorm:"NULL"` Phone string `xorm:"NULL"`
InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"` InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"`
ModelConvertCount int `xorm:"NOT NULL DEFAULT 0"` ModelConvertCount int `xorm:"NOT NULL DEFAULT 0"`

LoginActionCount int `xorm:"NOT NULL DEFAULT 0"`
} }


type UserBusinessAnalysis struct { type UserBusinessAnalysis struct {
@@ -692,4 +708,6 @@ type UserBusinessAnalysis struct {
Phone string `xorm:"NULL"` Phone string `xorm:"NULL"`
InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"` InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"`
ModelConvertCount int `xorm:"NOT NULL DEFAULT 0"` ModelConvertCount int `xorm:"NOT NULL DEFAULT 0"`

LoginActionCount int `xorm:"NOT NULL DEFAULT 0"`
} }

+ 20
- 0
models/user_login_action_log.go View File

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

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

//用户活跃信息表
type UserLoginActionLog struct {
ID int64 `xorm:"pk autoincr"`
UId int64 `xorm:"NOT NULL"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
}

func SaveLoginActionToDb(uid int64) {
statictisSess := xStatistic.NewSession()
defer statictisSess.Close()
var dateRecord UserLoginActionLog
dateRecord.UId = uid
statictisSess.Insert(&dateRecord)
}

+ 51
- 20
modules/auth/cloudbrain.go View File

@@ -36,34 +36,65 @@ type CreateCloudBrainForm struct {
} }


type CommitImageCloudBrainForm struct { type CommitImageCloudBrainForm struct {
Description string `form:"description" binding:"Required"`
Type int `form:"type" binding:"Required"`
Tag string `form:"tag" binding:"Required;MaxSize(100)" `
IsPrivate bool `form:"isPrivate" binding:"Required"`
Topics string `form:"topics"`
Description string `form:"description" binding:"Required"`
Type int `form:"type" binding:"Required"`
Tag string `form:"tag" binding:"Required;MaxSize(100)" `
IsPrivate bool `form:"isPrivate" binding:"Required"`
Topics string `form:"topics"`
Framework string `form:"framework" binding:"Required"`
FrameworkVersion string `form:"frameworkVersion"`
CudaVersion string `form:"cudaVersion" binding:"Required"`
PythonVersion string `form:"pythonVersion" binding:"Required"`
OperationSystem string `form:"operationSystem"`
OperationSystemVersion string `form:"operationSystemVersion"`
ThirdPackages string `form:"thirdPackages"`
ComputeResource string `form:"computeResource"`
} }
type CommitImageGrampusForm struct { type CommitImageGrampusForm struct {
Description string `form:"description" binding:"Required"`
Type int `form:"type" binding:"Required"`
Tag string `form:"tag" binding:"Required;MaxSize(50)" `
IsPrivate bool `form:"isPrivate" binding:"Required"`
Topics string `form:"topics"`
Description string `form:"description" binding:"Required"`
Type int `form:"type" binding:"Required"`
Tag string `form:"tag" binding:"Required;MaxSize(50)" `
IsPrivate bool `form:"isPrivate" binding:"Required"`
Topics string `form:"topics"`
Framework string `form:"framework" binding:"Required"`
FrameworkVersion string `form:"frameworkVersion"`
CudaVersion string `form:"cudaVersion" binding:"Required"`
PythonVersion string `form:"pythonVersion" binding:"Required"`
OperationSystem string `form:"operationSystem"`
OperationSystemVersion string `form:"operationSystemVersion"`
ThirdPackages string `form:"thirdPackages"`
ComputeResource string `form:"computeResource"`
} }


type CommitAdminImageCloudBrainForm struct { type CommitAdminImageCloudBrainForm struct {
Description string `form:"description" binding:"Required"`
Type int `form:"type" binding:"Required"`
Tag string `form:"tag" binding:"Required;MaxSize(100)" `
IsPrivate bool `form:"isPrivate" binding:"Required"`
Topics string `form:"topics"`
Place string `form:"place" binding:"Required"`
IsRecommend bool `form:"isRecommend" binding:"Required"`
Description string `form:"description" binding:"Required"`
Type int `form:"type" binding:"Required"`
Tag string `form:"tag" binding:"Required;MaxSize(100)" `
IsPrivate bool `form:"isPrivate" binding:"Required"`
Topics string `form:"topics"`
Place string `form:"place" binding:"Required"`
IsRecommend bool `form:"isRecommend" binding:"Required"`
Framework string `form:"framework" binding:"Required"`
FrameworkVersion string `form:"frameworkVersion" binding:"Required"`
CudaVersion string `form:"cudaVersion" binding:"Required"`
PythonVersion string `form:"pythonVersion" binding:"Required"`
OperationSystem string `form:"operationSystem"`
OperationSystemVersion string `form:"operationSystemVersion"`
ThirdPackages string `form:"thirdPackages"`
ComputeResource string `form:"computeResource"`
} }


type EditImageCloudBrainForm struct { type EditImageCloudBrainForm struct {
ID int64 `form:"id" binding:"Required"`
Description string `form:"description" binding:"Required"`
Topics string `form:"topics"`
ID int64 `form:"id" binding:"Required"`
Description string `form:"description" binding:"Required"`
Topics string `form:"topics"`
Framework string `form:"framework" binding:"Required"`
FrameworkVersion string `form:"frameworkVersion" binding:"Required"`
CudaVersion string `form:"cudaVersion" binding:"Required"`
PythonVersion string `form:"pythonVersion" binding:"Required"`
OperationSystem string `form:"operationSystem"`
OperationSystemVersion string `form:"operationSystemVersion"`
ThirdPackages string `form:"thirdPackages"`
} }


type ReviewImageForm struct { type ReviewImageForm struct {


+ 40
- 18
modules/cloudbrain/resty.go View File

@@ -350,15 +350,22 @@ sendjob:
} }


image := models.Image{ image := models.Image{
Type: models.NORMAL_TYPE,
CloudbrainType: params.CloudBrainType,
UID: params.UID,
IsPrivate: params.IsPrivate,
Tag: imageTag,
Description: params.ImageDescription,
Place: setting.Cloudbrain.ImageURLPrefix + imageTag,
Status: models.IMAGE_STATUS_COMMIT,
ApplyStatus: models.NoneApply,
Type: models.NORMAL_TYPE,
CloudbrainType: params.CloudBrainType,
UID: params.UID,
IsPrivate: params.IsPrivate,
Tag: imageTag,
Description: params.ImageDescription,
Place: setting.Cloudbrain.ImageURLPrefix + imageTag,
Status: models.IMAGE_STATUS_COMMIT,
ApplyStatus: models.NoneApply,
Framework: params.Framework,
FrameworkVersion: params.FrameworkVersion,
CudaVersion: params.CudaVersion,
PythonVersion: params.PythonVersion,
OperationSystem: params.OperationSystem,
OperationSystemVersion: params.OperationSystemVersion,
ThirdPackages: params.ThirdPackages,
} }


err = models.WithTx(func(ctx models.DBContext) error { err = models.WithTx(func(ctx models.DBContext) error {
@@ -367,6 +374,13 @@ sendjob:
dbImage.IsPrivate = params.IsPrivate dbImage.IsPrivate = params.IsPrivate
dbImage.Description = params.ImageDescription dbImage.Description = params.ImageDescription
dbImage.Status = models.IMAGE_STATUS_COMMIT dbImage.Status = models.IMAGE_STATUS_COMMIT
dbImage.Framework = params.Framework
dbImage.FrameworkVersion = params.FrameworkVersion
dbImage.CudaVersion = params.CudaVersion
dbImage.PythonVersion = params.PythonVersion
dbImage.OperationSystem = params.OperationSystem
dbImage.OperationSystemVersion = params.OperationSystemVersion
dbImage.ThirdPackages = params.ThirdPackages
image = *dbImage image = *dbImage
if err := models.UpdateLocalImage(dbImage); err != nil { if err := models.UpdateLocalImage(dbImage); err != nil {
log.Error("Failed to update image record.", err) log.Error("Failed to update image record.", err)
@@ -406,15 +420,23 @@ func CommitAdminImage(params models.CommitImageParams, doer *models.User) error
} }


image := models.Image{ image := models.Image{
CloudbrainType: params.CloudBrainType,
UID: params.UID,
IsPrivate: params.IsPrivate,
Tag: imageTag,
Description: params.ImageDescription,
Place: params.Place,
Status: models.IMAGE_STATUS_SUCCESS,
Type: params.Type,
ApplyStatus: models.NoneApply,
CloudbrainType: params.CloudBrainType,
UID: params.UID,
IsPrivate: params.IsPrivate,
Tag: imageTag,
Description: params.ImageDescription,
Place: params.Place,
Status: models.IMAGE_STATUS_SUCCESS,
Type: params.Type,
ApplyStatus: models.NoneApply,
Framework: params.Framework,
FrameworkVersion: params.FrameworkVersion,
CudaVersion: params.CudaVersion,
PythonVersion: params.PythonVersion,
OperationSystem: params.OperationSystem,
OperationSystemVersion: params.OperationSystemVersion,
ThirdPackages: params.ThirdPackages,
ComputeResource: params.ComputeResource,
} }


err = models.WithTx(func(ctx models.DBContext) error { err = models.WithTx(func(ctx models.DBContext) error {


+ 1
- 0
modules/context/context.go View File

@@ -307,6 +307,7 @@ func Contexter() macaron.Handler {


if ctx.User != nil { if ctx.User != nil {
ctx.IsSigned = true ctx.IsSigned = true
toCache(ctx)
ctx.Data["IsSigned"] = ctx.IsSigned ctx.Data["IsSigned"] = ctx.IsSigned
ctx.Data["SignedUser"] = ctx.User ctx.Data["SignedUser"] = ctx.User
ctx.Data["SignedUserID"] = ctx.User.ID ctx.Data["SignedUserID"] = ctx.User.ID


+ 42
- 0
modules/context/user_action_count.go View File

@@ -0,0 +1,42 @@
package context

import (
"fmt"
"sync"
"time"

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

var userActionMap sync.Map

var userChannel = make(chan int64, 10000)

func UserActionChannelInit() {
go consumerUserAction(userChannel)
}

func UserActionMapClear() {
userActionMap.Range(func(key, value any) bool {
userActionMap.Delete(key)
return true
})
}

func toCache(ctx *Context) {
if ctx.User != nil {
_, ok := userActionMap.Load(ctx.User.ID)
if !ok {
log.Info("add user uid to login action db. uid=" + fmt.Sprint(ctx.User.ID))
userActionMap.Store(ctx.User.ID, time.Now())
userChannel <- ctx.User.ID
}
}
}

func consumerUserAction(in <-chan int64) {
for uid := range in {
models.SaveLoginActionToDb(uid)
}
}

+ 111
- 28
modules/grampus/resty.go View File

@@ -32,6 +32,7 @@ const (
urlGetResourceSpecs = urlOpenApiV1 + "resourcespec" urlGetResourceSpecs = urlOpenApiV1 + "resourcespec"
urlGetAiCenter = urlOpenApiV1 + "sharescreen/aicenter" urlGetAiCenter = urlOpenApiV1 + "sharescreen/aicenter"
urlGetImages = urlOpenApiV1 + "image" urlGetImages = urlOpenApiV1 + "image"
urlDelImages = urlOpenApiV1 + "delimage"
urlNotebookJob = urlOpenApiV1 + "notebook" urlNotebookJob = urlOpenApiV1 + "notebook"


errorIllegalToken = 1005 errorIllegalToken = 1005
@@ -268,12 +269,10 @@ func GetImages(processorType string, jobType string) (*models.GetGrampusImagesRe
var result models.GetGrampusImagesResult var result models.GetGrampusImagesResult


retry := 0 retry := 0

queryType := "TrainJob" queryType := "TrainJob"
if jobType == string(models.JobTypeDebug) { if jobType == string(models.JobTypeDebug) {
queryType = "Notebook" queryType = "Notebook"
} }

sendjob: sendjob:
_, err := client.R(). _, err := client.R().
SetAuthToken(TOKEN). SetAuthToken(TOKEN).
@@ -299,6 +298,35 @@ sendjob:
return &result, nil return &result, nil
} }


func GetAllBaseImages() (*models.GetGrampusImagesResult, error) {
checkSetting()
client := getRestyClient()
var result models.GetGrampusImagesResult
retry := 0
sendjob:
_, err := client.R().
SetAuthToken(TOKEN).
SetResult(&result).
Get(HOST + urlGetImages)

if err != nil {
return nil, fmt.Errorf("resty GetAllBaseImages: %v", err)
}
if result.ErrorCode == errorIllegalToken && retry < 1 {
retry++
log.Info("retry get token")
_ = getToken()
goto sendjob
}

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

return &result, nil
}

func GetTrainJobLog(jobID string, nodeId ...int) (string, error) { func GetTrainJobLog(jobID string, nodeId ...int) (string, error) {
checkSetting() checkSetting()
client := getRestyClient() client := getRestyClient()
@@ -643,7 +671,7 @@ sendjob:
return restartResponse, nil return restartResponse, nil
} }


func CommitImage(jobID string, params models.CommitGrampusImageParams, doer *models.User) error {
func CommitImage(jobID, computeResource, aiCenterId string, params models.CommitGrampusImageParams, doer *models.User) error {
imageTag := strings.TrimSpace(params.ImageVersion) imageTag := strings.TrimSpace(params.ImageVersion)


dbImage, err := models.GetImageByTag(imageTag) dbImage, err := models.GetImageByTag(imageTag)
@@ -697,36 +725,60 @@ sendjob:
return fmt.Errorf("CommitImage err: imageid is empty. %s", result.ErrorMsg) return fmt.Errorf("CommitImage err: imageid is empty. %s", result.ErrorMsg)
} }


aiCenterList := make(models.AiCenterImages, 0)
ai := models.AiCenterImage{
AiCenterId: aiCenterId,
}
aiCenterList = append(aiCenterList, ai)

image := models.Image{ image := models.Image{
Type: models.NORMAL_TYPE,
CloudbrainType: params.CloudBrainType,
UID: params.UID,
IsPrivate: params.IsPrivate,
Tag: imageTag,
Description: params.Description,
ImageID: result.ImageId,
Status: models.IMAGE_STATUS_COMMIT,
ApplyStatus: models.NoneApply,
Type: models.NORMAL_TYPE,
CloudbrainType: params.CloudBrainType,
UID: params.UID,
IsPrivate: params.IsPrivate,
Tag: imageTag,
Description: params.Description,
ImageID: result.ImageId,
Status: models.IMAGE_STATUS_COMMIT,
ApplyStatus: models.NoneApply,
Framework: params.Framework,
FrameworkVersion: params.FrameworkVersion,
CudaVersion: params.CudaVersion,
PythonVersion: params.PythonVersion,
OperationSystem: params.OperationSystem,
OperationSystemVersion: params.OperationSystemVersion,
ThirdPackages: params.ThirdPackages,
ComputeResource: params.ComputeResource,
GrampusBaseImage: 0,
AiCenterImages: aiCenterList,
} }


err = models.WithTx(func(ctx models.DBContext) error { err = models.WithTx(func(ctx models.DBContext) error {
models.UpdateAutoIncrementIndex() models.UpdateAutoIncrementIndex()
if dbImage != nil {
dbImage.IsPrivate = params.IsPrivate
dbImage.Description = params.Description
dbImage.Status = models.IMAGE_STATUS_COMMIT
image = *dbImage
if err := models.UpdateLocalImage(dbImage); err != nil {
log.Error("Failed to update image record.", err)
return fmt.Errorf("CommitImage err: %s", res.String())
}

} else {
if err := models.CreateLocalImage(&image); err != nil {
log.Error("Failed to insert image record.", err)
return fmt.Errorf("CommitImage err: %s", res.String())
}
// if dbImage != nil {
// dbImage.IsPrivate = params.IsPrivate
// dbImage.Description = params.Description
// dbImage.Status = models.IMAGE_STATUS_COMMIT
// dbImage.Framework = params.Framework
// dbImage.FrameworkVersion = params.FrameworkVersion
// dbImage.CudaVersion = params.CudaVersion
// dbImage.PythonVersion = params.PythonVersion
// dbImage.OperationSystem = params.OperationSystem
// dbImage.OperationSystemVersion = params.OperationSystemVersion
// dbImage.ThirdPackages = params.ThirdPackages
// dbImage.ComputeResource = params.ComputeResource
// image = *dbImage
// if err := models.UpdateLocalImage(dbImage); err != nil {
// log.Error("Failed to update image record.", err)
// return fmt.Errorf("CommitImage err: %s", res.String())
// }

// } else {
if err := models.CreateLocalImage(&image); err != nil {
log.Error("Failed to insert image record.", err)
return fmt.Errorf("CommitImage err: %s", res.String())
} }
//}
if err := models.SaveImageTopics(image.ID, params.Topics...); err != nil { if err := models.SaveImageTopics(image.ID, params.Topics...); err != nil {
log.Error("Failed to insert image record.", err) log.Error("Failed to insert image record.", err)
return fmt.Errorf("CommitImage err: %s", res.String()) return fmt.Errorf("CommitImage err: %s", res.String())
@@ -789,6 +841,9 @@ func updateImageStatus(image models.Image) {
commitSuccess = true commitSuccess = true
image.Status = models.IMAGE_STATUS_SUCCESS image.Status = models.IMAGE_STATUS_SUCCESS
image.Place = result.Image.ImageFullAddr image.Place = result.Image.ImageFullAddr
if image.AiCenterImages != nil {
image.AiCenterImages[0].ImageUrl = result.Image.ImageFullAddr
}
models.UpdateLocalImageStatusAndPlace(&image) models.UpdateLocalImageStatusAndPlace(&image)
return return
} }
@@ -797,7 +852,6 @@ func updateImageStatus(image models.Image) {
commitSuccess = false commitSuccess = false
break break
} }

} }
} }
if !commitSuccess { if !commitSuccess {
@@ -806,3 +860,32 @@ func updateImageStatus(image models.Image) {
} }


} }

func DeleteImage(image *models.Image) error {
checkSetting()
client := getRestyClient()
log.Info("start to delete remote image=" + image.ImageID)
var result models.GrampusResult
retry := 0
sendjob:
res, err := client.R().
SetAuthToken(TOKEN).
SetResult(&result).
Delete(HOST + urlDelImages + "/" + image.ImageID)

if err != nil {
log.Info("DeleteImage error=" + err.Error())
return fmt.Errorf("resty DeleteImage: %v", err)
}
if result.ErrorCode == errorIllegalToken && retry < 1 {
retry++
log.Info("retry get token")
_ = getToken()
goto sendjob
}
if res.StatusCode() != http.StatusOK {
log.Info("res status code=" + fmt.Sprint(res.StatusCode()))
return fmt.Errorf("DeleteImage err: %s", res.String())
}
return nil
}

+ 4
- 0
modules/public/public.go View File

@@ -44,6 +44,10 @@ func Custom(opts *Options) macaron.Handler {
return opts.staticHandler(path.Join(setting.CustomPath, "public")) return opts.staticHandler(path.Join(setting.CustomPath, "public"))
} }


func CustomJson(opts *Options) macaron.Handler {
return opts.staticHandler(path.Join(setting.CustomPath, "public", "json"))
}

// staticFileSystem implements http.FileSystem interface. // staticFileSystem implements http.FileSystem interface.
type staticFileSystem struct { type staticFileSystem struct {
dir *http.Dir dir *http.Dir


+ 2
- 0
modules/setting/setting.go View File

@@ -874,6 +874,7 @@ var (
ATTACHEMENT_NUM_A_USER_LAST10M int ATTACHEMENT_NUM_A_USER_LAST10M int
ATTACHEMENT_SIZE_A_USER int64 //G ATTACHEMENT_SIZE_A_USER int64 //G
ALL_ATTACHEMENT_NUM_SDK int ALL_ATTACHEMENT_NUM_SDK int
IGNORE_FLAG string
}{} }{}


LLM_CHAT_API = struct { LLM_CHAT_API = struct {
@@ -1878,6 +1879,7 @@ func getFlowControlConfig() {
FLOW_CONTROL.ATTACHEMENT_NUM_A_USER_LAST24HOUR = sec.Key("ATTACHEMENT_NUM_A_USER_LAST24HOUR").MustInt(1000) FLOW_CONTROL.ATTACHEMENT_NUM_A_USER_LAST24HOUR = sec.Key("ATTACHEMENT_NUM_A_USER_LAST24HOUR").MustInt(1000)
FLOW_CONTROL.ATTACHEMENT_NUM_A_USER_LAST10M = sec.Key("ATTACHEMENT_NUM_A_USER_LAST10M").MustInt(10) FLOW_CONTROL.ATTACHEMENT_NUM_A_USER_LAST10M = sec.Key("ATTACHEMENT_NUM_A_USER_LAST10M").MustInt(10)
FLOW_CONTROL.ATTACHEMENT_SIZE_A_USER = sec.Key("ATTACHEMENT_SIZE_A_USER").MustInt64(500) FLOW_CONTROL.ATTACHEMENT_SIZE_A_USER = sec.Key("ATTACHEMENT_SIZE_A_USER").MustInt64(500)
FLOW_CONTROL.IGNORE_FLAG = sec.Key("IGNORE_FLAG").MustString("")
} }


func getModelAppConfig() { func getModelAppConfig() {


+ 8
- 3
routers/admin/resources.go View File

@@ -1,15 +1,16 @@
package admin package admin


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

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


const ( const (
@@ -122,6 +123,10 @@ func SyncGrampusQueue(ctx *context.Context) {
ctx.JSON(http.StatusOK, response.ServerError(err.Error())) ctx.JSON(http.StatusOK, response.ServerError(err.Error()))
return return
} }
err = resource.SyncGrampusImage(ctx.User.ID)
if err != nil {
log.Error("sync image error. %v", err)
}
ctx.JSON(http.StatusOK, response.Success()) ctx.JSON(http.StatusOK, response.Success())
} }




+ 8
- 0
routers/api/v1/api.go View File

@@ -709,6 +709,8 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/custom", reqToken(), repo.GetCustomImages) m.Get("/custom", reqToken(), repo.GetCustomImages)
m.Get("/star", reqToken(), repo.GetStarImages) m.Get("/star", reqToken(), repo.GetStarImages)
m.Get("/npu", reqToken(), repo.GetNpuImages) m.Get("/npu", reqToken(), repo.GetNpuImages)
m.Get("/availableFilter", reqToken(),
repo.GetAvailableFilerInfo)


}) })


@@ -821,6 +823,7 @@ func RegisterRoutes(m *macaron.Macaron) {
}) })
}, operationReq) }, operationReq)


m.Get("/query_user_count_time_info", operationReq, repo_ext.QueryUserCountTimeInfo)
m.Get("/query_metrics_current_month", operationReq, repo_ext.QueryUserMetricsCurrentMonth) m.Get("/query_metrics_current_month", operationReq, repo_ext.QueryUserMetricsCurrentMonth)
m.Get("/query_metrics_current_week", operationReq, repo_ext.QueryUserMetricsCurrentWeek) m.Get("/query_metrics_current_week", operationReq, repo_ext.QueryUserMetricsCurrentWeek)
m.Get("/query_metrics_current_year", operationReq, repo_ext.QueryUserMetricsCurrentYear) m.Get("/query_metrics_current_year", operationReq, repo_ext.QueryUserMetricsCurrentYear)
@@ -1480,6 +1483,11 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/prd/event", authentication.ValidEventSource) m.Get("/prd/event", authentication.ValidEventSource)
m.Post("/prd/event", authentication.AcceptWechatEvent) m.Post("/prd/event", authentication.AcceptWechatEvent)
}) })
m.Group("/authentication/wechat", func() {
m.Get("/qrCode4Bind", authentication.GetQRCode4Bind)
m.Get("/bindStatus", authentication.GetBindStatus)
m.Post("/unbind", authentication.UnbindWechat)
}, reqToken())
m.Get("/wechat/material", authentication.GetMaterial) m.Get("/wechat/material", authentication.GetMaterial)
m.Get("/cloudbrain/get_newest_job", repo.GetNewestJobs) m.Get("/cloudbrain/get_newest_job", repo.GetNewestJobs)
m.Get("/cloudbrain/get_center_info", repo.GetAICenterInfo) m.Get("/cloudbrain/get_center_info", repo.GetAICenterInfo)


+ 29
- 13
routers/api/v1/repo/attachments.go View File

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


"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"


"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
@@ -74,12 +75,20 @@ func NewMultipart(ctx *context.APIContext) {
}) })
return return
} }
if err := routeRepo.CheckFlowForDatasetSDK(); err != nil {
ctx.JSON(200, map[string]string{
"result_code": "-1",
"msg": err.Error(),
})
return
ignore := false
if setting.FLOW_CONTROL.IGNORE_FLAG != "" {
if ctx.Query("IGNORE_FLAG") == setting.FLOW_CONTROL.IGNORE_FLAG {
ignore = true
}
}
if !ignore {
if err := routeRepo.CheckFlowForDatasetSDK(); err != nil {
ctx.JSON(200, map[string]string{
"result_code": "-1",
"msg": err.Error(),
})
return
}
} }
mutex.Lock() mutex.Lock()
defer mutex.Unlock() defer mutex.Unlock()
@@ -159,13 +168,20 @@ func NewModelMultipart(ctx *context.APIContext) {
}) })
return return
} }

if err := routeRepo.CheckFlowForModelSDK(); err != nil {
ctx.JSON(200, map[string]string{
"result_code": "-1",
"msg": err.Error(),
})
return
ignore := false
if setting.FLOW_CONTROL.IGNORE_FLAG != "" {
if ctx.Query("IGNORE_FLAG") == setting.FLOW_CONTROL.IGNORE_FLAG {
ignore = true
}
}
if !ignore {
if err := routeRepo.CheckFlowForModelSDK(); err != nil {
ctx.JSON(200, map[string]string{
"result_code": "-1",
"msg": err.Error(),
})
return
}
} }
modelMutex.Lock() modelMutex.Lock()
defer modelMutex.Unlock() defer modelMutex.Unlock()


+ 28
- 0
routers/api/v1/repo/images.go View File

@@ -28,6 +28,7 @@ func GetRecommendImages(ctx *context.APIContext) {
IncludeOfficialOnly: true, IncludeOfficialOnly: true,
Status: models.IMAGE_STATUS_SUCCESS, Status: models.IMAGE_STATUS_SUCCESS,
CloudbrainType: ctx.QueryInt("cloudbrainType"), CloudbrainType: ctx.QueryInt("cloudbrainType"),
TrainType: ctx.Query("trainType"),
} }


routeRepo.GetImages(ctx.Context, &opts) routeRepo.GetImages(ctx.Context, &opts)
@@ -59,6 +60,7 @@ func GetCustomImages(ctx *context.APIContext) {
Topics: ctx.Query("topic"), Topics: ctx.Query("topic"),
Status: -1, Status: -1,
CloudbrainType: ctx.QueryInt("cloudbrainType"), CloudbrainType: ctx.QueryInt("cloudbrainType"),
TrainType: "",
} }
routeRepo.GetImages(ctx.Context, &opts) routeRepo.GetImages(ctx.Context, &opts)


@@ -73,6 +75,7 @@ func GetStarImages(ctx *context.APIContext) {
Topics: ctx.Query("topic"), Topics: ctx.Query("topic"),
Status: models.IMAGE_STATUS_SUCCESS, Status: models.IMAGE_STATUS_SUCCESS,
CloudbrainType: ctx.QueryInt("cloudbrainType"), CloudbrainType: ctx.QueryInt("cloudbrainType"),
TrainType: "",
} }
routeRepo.GetImages(ctx.Context, &opts) routeRepo.GetImages(ctx.Context, &opts)


@@ -88,6 +91,31 @@ func GetNpuImages(ctx *context.APIContext) {
} }
} }


func GetAvailableFilerInfo(ctx *context.APIContext) {
columns := []string{"framework", "framework_version", "python_version", "cuda_version"}

i := ctx.QueryInt("index")

frameWork := ctx.Query("framework")
version := ctx.Query("version")
pythonVersion := ctx.Query("python")
if i < 0 || i >= len(columns) {
i = 0
}
columnName := columns[i]

opts := &models.SearchAvailableValueOptions{
Column: columnName,
Framework: frameWork,
FrameworkVersion: version,
PythonVersion: pythonVersion,
}
ctx.JSON(http.StatusOK, models.BaseMessageWithDataApi{
Data: models.GetImageAvailableColumnValues(opts),
})

}

func getModelArtsImages(ctx *context.APIContext) { func getModelArtsImages(ctx *context.APIContext) {


var versionInfos modelarts.VersionInfo var versionInfos modelarts.VersionInfo


+ 17
- 21
routers/api/v1/repo/modelmanage.go View File

@@ -9,6 +9,7 @@ import (
"time" "time"


"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/cloudbrain"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert" "code.gitea.io/gitea/modules/convert"
"code.gitea.io/gitea/modules/grampus" "code.gitea.io/gitea/modules/grampus"
@@ -361,30 +362,25 @@ func QueryModelConvertResultFileList(ctx *context.APIContext, id string) ([]stor
return nil, err return nil, err
} }
if job.IsGpuTrainTask() { if job.IsGpuTrainTask() {
//get dirs
dirs, err := routerRepo.GetModelDirs(job.ID, parentDir)
if err != nil {
log.Error("GetModelDirs failed:%v", err.Error(), ctx.Data["msgID"])
return nil, err
}

var fileInfos []storage.FileInfo
err = json.Unmarshal([]byte(dirs), &fileInfos)
if err != nil {
log.Error("json.Unmarshal failed:%v", err.Error(), ctx.Data["msgID"])
return nil, err
}


for i, fileInfo := range fileInfos {
temp, _ := time.Parse("2006-01-02 15:04:05", fileInfo.ModTime)
fileInfos[i].ModTime = temp.Local().Format("2006-01-02 15:04:05")
}
path := setting.CBCodePathPrefix + job.ID + cloudbrain.ModelMountPath + "/"
log.Info("get model convert result file path=" + path)
fileInfos, err := storage.GetAllObjectByBucketAndPrefixMinio(setting.Attachment.Minio.Bucket, path)
if err == nil {
for i, fileInfo := range fileInfos {
temp, _ := time.Parse("2006-01-02 15:04:05", fileInfo.ModTime)
fileInfos[i].ModTime = temp.Local().Format("2006-01-02 15:04:05")
}


sort.Slice(fileInfos, func(i, j int) bool {
return fileInfos[i].ModTime > fileInfos[j].ModTime
})
sort.Slice(fileInfos, func(i, j int) bool {
return fileInfos[i].ModTime > fileInfos[j].ModTime
})


return fileInfos, nil
return fileInfos, nil
} else {
log.Info("query models path error.")
return nil, err
}
} else { } else {
var versionName = "V0001" var versionName = "V0001"
models, err := storage.GetObsListObject(job.ID, "output/", parentDir, versionName) models, err := storage.GetObsListObject(job.ID, "output/", parentDir, versionName)


+ 3
- 0
routers/init.go View File

@@ -21,6 +21,7 @@ import (
"code.gitea.io/gitea/modules/eventsource" "code.gitea.io/gitea/modules/eventsource"
"code.gitea.io/gitea/modules/modelappservice" "code.gitea.io/gitea/modules/modelappservice"


userlogin "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/highlight" "code.gitea.io/gitea/modules/highlight"
code_indexer "code.gitea.io/gitea/modules/indexer/code" code_indexer "code.gitea.io/gitea/modules/indexer/code"
@@ -78,6 +79,8 @@ func NewServices() {
log.Info("labelmsg.Init() succeed.") log.Info("labelmsg.Init() succeed.")
modelappservice.Init() modelappservice.Init()
log.Info("modelappservice.Init() succeed.") log.Info("modelappservice.Init() succeed.")
userlogin.UserActionChannelInit()
log.Info("UserActionChannelInit succeed.")
InitESClient() InitESClient()
log.Info("ES Client succeed.") log.Info("ES Client succeed.")
} }


+ 3
- 0
routers/repo/ai_model_convert.go View File

@@ -1109,6 +1109,9 @@ func ModelConvertDownloadModel(ctx *context.Context) {
jobName := ctx.Query("jobName") jobName := ctx.Query("jobName")
if job.IsGpuTrainTask() { if job.IsGpuTrainTask() {
filePath := "jobs/" + jobName + "/model/" + parentDir filePath := "jobs/" + jobName + "/model/" + parentDir
if !strings.HasSuffix(filePath, fileName) {
filePath += fileName
}
url, err := storage.Attachments.PresignedGetURL(filePath, fileName) url, err := storage.Attachments.PresignedGetURL(filePath, fileName)
if err != nil { if err != nil {
log.Error("PresignedGetURL failed: %v", err.Error(), ctx.Data["msgID"]) log.Error("PresignedGetURL failed: %v", err.Error(), ctx.Data["msgID"])


+ 96
- 18
routers/repo/cloudbrain.go View File

@@ -1227,6 +1227,13 @@ func CloudBrainImageEditPost(ctx *context.Context, form auth.EditImageCloudBrain
} }


image.Description = form.Description image.Description = form.Description
image.Framework = form.Framework
image.FrameworkVersion = form.FrameworkVersion
image.CudaVersion = form.CudaVersion
image.PythonVersion = form.PythonVersion
image.OperationSystem = form.OperationSystem
image.OperationSystemVersion = form.OperationSystemVersion
image.ThirdPackages = form.ThirdPackages


err = models.WithTx(func(ctx models.DBContext) error { err = models.WithTx(func(ctx models.DBContext) error {
if err := models.UpdateLocalImage(image); err != nil { if err := models.UpdateLocalImage(image); err != nil {
@@ -1255,7 +1262,15 @@ func CloudBrainImageDelete(ctx *context.Context) {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("repo.image_not_exist"))) ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("repo.image_not_exist")))
return return
} }

image, err := models.GetImageByID(id)
if err != nil {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("repo.image_not_exist")))
return
}
err = grampus.DeleteImage(image)
if err != nil {
log.Info("Delete remote delete failed.error=" + err.Error())
}
err = models.DeleteLocalImage(id) err = models.DeleteLocalImage(id)
if err != nil { if err != nil {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("repo.image_delete_fail"))) ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("repo.image_delete_fail")))
@@ -1298,12 +1313,20 @@ func CloudBrainAdminCommitImage(ctx *context.Context, form auth.CommitAdminImage
ImageDescription: form.Description, ImageDescription: form.Description,
ImageTag: form.Tag, ImageTag: form.Tag,
}, },
IsPrivate: form.IsPrivate,
CloudBrainType: form.Type,
Topics: validTopics,
UID: ctx.User.ID,
Type: models.GetRecommondType(form.IsRecommend),
Place: form.Place,
IsPrivate: form.IsPrivate,
CloudBrainType: form.Type,
Topics: validTopics,
UID: ctx.User.ID,
Type: models.GetRecommondType(form.IsRecommend),
Place: form.Place,
Framework: form.Framework,
FrameworkVersion: form.FrameworkVersion,
CudaVersion: form.CudaVersion,
PythonVersion: form.PythonVersion,
OperationSystem: form.OperationSystem,
OperationSystemVersion: form.OperationSystemVersion,
ThirdPackages: form.ThirdPackages,
ComputeResource: form.ComputeResource,
}, ctx.User) }, ctx.User)
if err != nil { if err != nil {
log.Error("CommitImagefailed") log.Error("CommitImagefailed")
@@ -1347,10 +1370,18 @@ func CloudBrainCommitImage(ctx *context.Context, form auth.CommitImageCloudBrain
ImageDescription: form.Description, ImageDescription: form.Description,
ImageTag: form.Tag, ImageTag: form.Tag,
}, },
IsPrivate: form.IsPrivate,
CloudBrainType: form.Type,
Topics: validTopics,
UID: ctx.User.ID,
IsPrivate: form.IsPrivate,
CloudBrainType: form.Type,
Topics: validTopics,
UID: ctx.User.ID,
Framework: form.Framework,
FrameworkVersion: form.FrameworkVersion,
CudaVersion: form.CudaVersion,
PythonVersion: form.PythonVersion,
OperationSystem: form.OperationSystem,
OperationSystemVersion: form.OperationSystemVersion,
ThirdPackages: form.ThirdPackages,
ComputeResource: form.ComputeResource,
}, ctx.User) }, ctx.User)
if err != nil { if err != nil {
log.Error("CommitImage(%s) failed:%v", ctx.Cloudbrain.JobName, err.Error(), ctx.Data["msgID"]) log.Error("CommitImage(%s) failed:%v", ctx.Cloudbrain.JobName, err.Error(), ctx.Data["msgID"])
@@ -1737,6 +1768,26 @@ func GetImages(ctx *context.Context, opts *models.SearchImageOptions) {
} }


opts.SearchOrderBy = orderBy opts.SearchOrderBy = orderBy
opts.Framework = ctx.Query("framework")
opts.FrameworkVersion = ctx.Query("frameworkVersion")
opts.CudaVersion = ctx.Query("cuda")
opts.PythonVersion = ctx.Query("python")
opts.OperationSystem = ctx.Query("os")
opts.OperationSystemVersion = ctx.Query("osVersion")
opts.ThirdPackages = ctx.Query("thirdParty")

if ctx.QueryInt64("spec") > 0 {
spec, err := models.FindSpecs(models.FindSpecsOptions{
SpecId: ctx.QueryInt64("spec"),
})
if err == nil {
opts.AiCenterId = spec[0].AiCenterCode
}
}
computeResource := ctx.Query("computeResource")
if computeResource != "" {
opts.ComputeResource = computeResource
}


imageList, total, err := models.SearchImage(opts) imageList, total, err := models.SearchImage(opts)
if err != nil { if err != nil {
@@ -1746,6 +1797,11 @@ func GetImages(ctx *context.Context, opts *models.SearchImageOptions) {
Images: []*models.Image{}, Images: []*models.Image{},
}) })
} else { } else {
for _, tmp := range imageList {
if tmp.Place == "" {
tmp.Place = getImageUrl(tmp, opts.AiCenterId)
}
}
ctx.JSON(http.StatusOK, models.ImagesPageResult{ ctx.JSON(http.StatusOK, models.ImagesPageResult{
Count: total, Count: total,
Images: imageList, Images: imageList,
@@ -1753,6 +1809,21 @@ func GetImages(ctx *context.Context, opts *models.SearchImageOptions) {
} }
} }


func getImageUrl(image *models.Image, aiCenterID string) string {
if image.AiCenterImages != nil {
for _, tmp := range image.AiCenterImages {
if aiCenterID == "" {
return tmp.ImageUrl
}
if tmp.AiCenterId == aiCenterID {
log.Info("tmp.url=" + tmp.ImageUrl)
return tmp.ImageUrl
}
}
}
return ""
}

func getUID(ctx *context.Context) int64 { func getUID(ctx *context.Context) int64 {
var uid int64 = -1 var uid int64 = -1
if ctx.IsSigned { if ctx.IsSigned {
@@ -1764,13 +1835,20 @@ func getUID(ctx *context.Context) int64 {
func GetAllImages(ctx *context.Context) { func GetAllImages(ctx *context.Context) {
uid := getUID(ctx) uid := getUID(ctx)
opts := models.SearchImageOptions{ opts := models.SearchImageOptions{
UID: uid,
Keyword: ctx.Query("q"),
ApplyStatus: ctx.QueryInt("apply"),
Topics: ctx.Query("topic"),
IncludeOfficialOnly: ctx.QueryBool("recommend"),
CloudbrainType: ctx.QueryInt("cloudbrainType"),
Status: -1,
UID: uid,
Keyword: ctx.Query("q"),
ApplyStatus: ctx.QueryInt("apply"),
Topics: ctx.Query("topic"),
IncludeOfficialOnly: ctx.QueryBool("recommend"),
CloudbrainType: ctx.QueryInt("cloudbrainType"),
Status: -1,
Framework: ctx.Query("framework"),
FrameworkVersion: ctx.Query("frameworkVesion"),
CudaVersion: ctx.Query("cuda"),
PythonVersion: ctx.Query("python"),
OperationSystem: ctx.Query("os"),
OperationSystemVersion: ctx.Query("osVersion"),
ThirdPackages: ctx.Query("thirdParty"),
} }


if ctx.Query("private") != "" { if ctx.Query("private") != "" {


+ 26
- 10
routers/repo/grampus.go View File

@@ -31,6 +31,7 @@ import (
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/grampus" "code.gitea.io/gitea/modules/grampus"
"code.gitea.io/gitea/modules/modelarts" "code.gitea.io/gitea/modules/modelarts"

// "code.gitea.io/gitea/modules/notification" // "code.gitea.io/gitea/modules/notification"
// "code.gitea.io/gitea/modules/timeutil" // "code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
@@ -70,21 +71,21 @@ const (
tplGrampusTrainJobIluvatarGPGPUNew base.TplName = "repo/grampus/trainjob/iluvatar-gpgpu/new" tplGrampusTrainJobIluvatarGPGPUNew base.TplName = "repo/grampus/trainjob/iluvatar-gpgpu/new"


//C2NET notebook //C2NET notebook
tplGrampusNotebookNew base.TplName = "repo/grampus/notebook/new"
tplGrampusNotebookNew base.TplName = "repo/grampus/notebook/new"


// Inference job // Inference job
tplGrampusInferenceNew base.TplName = "repo/grampus/inference/new"
tplGrampusInferenceShow base.TplName = "repo/grampus/inference/show"
tplGrampusInferenceNew base.TplName = "repo/grampus/inference/new"
tplGrampusInferenceShow base.TplName = "repo/grampus/inference/show"
) )


func GrampusInferenceNew(ctx *context.Context) { func GrampusInferenceNew(ctx *context.Context) {
ctx.Data["PageIsCloudBrain"] = true ctx.Data["PageIsCloudBrain"] = true
ctx.HTML(http.StatusOK, tplGrampusInferenceNew)
ctx.HTML(http.StatusOK, tplGrampusInferenceNew)
} }


func GrampusInferenceShow(ctx *context.Context) { func GrampusInferenceShow(ctx *context.Context) {
ctx.Data["PageIsCloudBrain"] = true ctx.Data["PageIsCloudBrain"] = true
ctx.HTML(http.StatusOK, tplGrampusInferenceShow)
ctx.HTML(http.StatusOK, tplGrampusInferenceShow)
} }


func GrampusNotebookNew(ctx *context.Context) { func GrampusNotebookNew(ctx *context.Context) {
@@ -1961,13 +1962,20 @@ func GrampusNotebookRestart(ctx *context.Context) {
} }
cloudbrainTask.GrampusNotebookRestart(ctx) cloudbrainTask.GrampusNotebookRestart(ctx)
} }

func getImageComputeResource(processType string) string {
tail := strings.LastIndex(processType, "/")
if tail > 0 {
return strings.ToUpper(processType[tail+1:])
}
return strings.ToUpper(processType)
}
func GrampusCommitImageShow(ctx *context.Context) { func GrampusCommitImageShow(ctx *context.Context) {
ctx.Data["PageIsCloudBrain"] = true ctx.Data["PageIsCloudBrain"] = true
ctx.Data["Type"] = ctx.Cloudbrain.Type ctx.Data["Type"] = ctx.Cloudbrain.Type
if ctx.Cloudbrain.ComputeResource == models.NPUResource { if ctx.Cloudbrain.ComputeResource == models.NPUResource {
ctx.Error(http.StatusBadRequest, "unsupported compute resource type") ctx.Error(http.StatusBadRequest, "unsupported compute resource type")
} }
ctx.Data["ComputeResource"] = getImageComputeResource(ctx.Cloudbrain.ComputeResource)
ctx.HTML(200, tplGrampusNotebookGPUImageShow) ctx.HTML(200, tplGrampusNotebookGPUImageShow)
} }


@@ -1989,16 +1997,24 @@ func GrampusCommitImage(ctx *context.Context, form auth.CommitImageGrampusForm)
return return
} }


err := grampus.CommitImage(ctx.Cloudbrain.JobID, models.CommitGrampusImageParams{
err := grampus.CommitImage(ctx.Cloudbrain.JobID, ctx.Cloudbrain.ComputeResource, ctx.Cloudbrain.GetAiCenter(), models.CommitGrampusImageParams{
CommitImageGrampusParams: models.CommitImageGrampusParams{ CommitImageGrampusParams: models.CommitImageGrampusParams{
ImageName: setting.Grampus.GPUImageCommonName, ImageName: setting.Grampus.GPUImageCommonName,
Description: form.Description, Description: form.Description,
ImageVersion: form.Tag, ImageVersion: form.Tag,
TaskName: "task0", TaskName: "task0",
}, IsPrivate: form.IsPrivate, }, IsPrivate: form.IsPrivate,
CloudBrainType: form.Type,
Topics: validTopics,
UID: ctx.User.ID,
CloudBrainType: form.Type,
Topics: validTopics,
UID: ctx.User.ID,
Framework: form.Framework,
FrameworkVersion: form.FrameworkVersion,
PythonVersion: form.PythonVersion,
CudaVersion: form.CudaVersion,
OperationSystem: form.OperationSystem,
OperationSystemVersion: form.OperationSystemVersion,
ThirdPackages: form.ThirdPackages,
ComputeResource: form.ComputeResource,
}, ctx.User) }, ctx.User)


if err != nil { if err != nil {


+ 22
- 0
routers/repo/user_data_analysis.go View File

@@ -24,6 +24,11 @@ const (
USER_YEAR = 2023 USER_YEAR = 2023
) )


type ProjectsPeriodData struct {
RecordBeginTime string `json:"recordBeginTime"`
LastUpdatedTime string `json:"lastUpdatedTime"`
}

func getUserMetricsExcelHeader(ctx *context.Context) map[string]string { func getUserMetricsExcelHeader(ctx *context.Context) map[string]string {
excelHeader := make([]string, 0) excelHeader := make([]string, 0)
excelHeader = append(excelHeader, ctx.Tr("user.metrics.date")) excelHeader = append(excelHeader, ctx.Tr("user.metrics.date"))
@@ -749,6 +754,7 @@ func TimingCountDataByDate(date string) {


func TimingCountData() { func TimingCountData() {
log.Info("start to time count data") log.Info("start to time count data")
context.UserActionMapClear()
currentTimeNow := time.Now() currentTimeNow := time.Now()
log.Info("current time:" + currentTimeNow.Format("2006-01-02 15:04:05")) log.Info("current time:" + currentTimeNow.Format("2006-01-02 15:04:05"))
startTime := currentTimeNow.AddDate(0, 0, -1).Format("2006-01-02") startTime := currentTimeNow.AddDate(0, 0, -1).Format("2006-01-02")
@@ -951,3 +957,19 @@ func QueryUserAnnualReport(ctx *context.Context) {
result := models.QueryUserAnnualReport(ctx.User.ID) result := models.QueryUserAnnualReport(ctx.User.ID)
ctx.JSON(http.StatusOK, result) ctx.JSON(http.StatusOK, result)
} }

func getRecordBeginTime() (time.Time, error) {
return time.ParseInLocation("2006-01-02", setting.RadarMap.RecordBeginTime, time.Local)
}

func QueryUserCountTimeInfo(ctx *context.Context) {
recordBeginTime, err := getRecordBeginTime()
if err != nil {
recordBeginTime = time.Now()
}
projectsPeriodData := ProjectsPeriodData{
RecordBeginTime: recordBeginTime.Format("2006-01-02"),
LastUpdatedTime: models.GetLastModifyTime(),
}
ctx.JSON(http.StatusOK, projectsPeriodData)
}

+ 8
- 2
routers/routes/routes.go View File

@@ -196,6 +196,12 @@ func NewMacaron() *macaron.Macaron {
ExpiresAfter: setting.StaticCacheTime, ExpiresAfter: setting.StaticCacheTime,
}, },
)) ))
m.Use(public.CustomJson(
&public.Options{
SkipLogging: setting.DisableRouterLog,
ExpiresAfter: 0,
},
))
m.Use(public.StaticHandler( m.Use(public.StaticHandler(
setting.AvatarUploadPath, setting.AvatarUploadPath,
&public.Options{ &public.Options{
@@ -363,7 +369,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/all/search/", routers.Search) m.Post("/all/search/", routers.Search)
m.Get("/all/search/", routers.EmptySearch) m.Get("/all/search/", routers.EmptySearch)
m.Get("/all/dosearch/", routers.SearchApi) m.Get("/all/dosearch/", routers.SearchApi)
m.Post("/user/login/kanban", user.SignInPostAPI)
m.Post("/user/login/kanban", user.SignInPostAPI, reqSignOut)
m.Get("/home/term", routers.HomeTerm) m.Get("/home/term", routers.HomeTerm)
m.Get("/home/annual_privacy", routers.HomeAnnual) m.Get("/home/annual_privacy", routers.HomeAnnual)
m.Get("/home/model_privacy", routers.HomeWenxin) m.Get("/home/model_privacy", routers.HomeWenxin)
@@ -538,7 +544,6 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/unbind", authentication.UnbindWechat) m.Post("/unbind", authentication.UnbindWechat)
m.Get("/bind", authentication.GetBindPage) m.Get("/bind", authentication.GetBindPage)
}, reqSignIn) }, reqSignIn)

m.Group("/user/settings", func() { m.Group("/user/settings", func() {
m.Get("", userSetting.Profile) m.Get("", userSetting.Profile)
m.Post("", bindIgnErr(auth.UpdateProfileForm{}), userSetting.ProfilePost) m.Post("", bindIgnErr(auth.UpdateProfileForm{}), userSetting.ProfilePost)
@@ -581,6 +586,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/delete", userSetting.DeleteOAuth2Application) m.Post("/delete", userSetting.DeleteOAuth2Application)
m.Post("/revoke", userSetting.RevokeOAuth2Grant) m.Post("/revoke", userSetting.RevokeOAuth2Grant)
}) })

m.Combo("/applications").Get(userSetting.Applications). m.Combo("/applications").Get(userSetting.Applications).
Post(bindIgnErr(auth.NewAccessTokenForm{}), userSetting.ApplicationsPost) Post(bindIgnErr(auth.NewAccessTokenForm{}), userSetting.ApplicationsPost)
m.Post("/applications/delete", userSetting.DeleteApplication) m.Post("/applications/delete", userSetting.DeleteApplication)


+ 2
- 2
routers/user/auth.go View File

@@ -306,7 +306,7 @@ func SignInPostAPI(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("sign_in") ctx.Data["Title"] = ctx.Tr("sign_in")
UserName := ctx.Query("UserName") UserName := ctx.Query("UserName")
Password := ctx.Query("Password") Password := ctx.Query("Password")
log.Info("0000000")
log.Info("u=" + UserName)
orderedOAuth2Names, oauth2Providers, err := models.GetActiveOAuth2Providers() orderedOAuth2Names, oauth2Providers, err := models.GetActiveOAuth2Providers()
if err != nil { if err != nil {
ctx.ServerError("UserSignIn", err) ctx.ServerError("UserSignIn", err)
@@ -327,6 +327,7 @@ func SignInPostAPI(ctx *context.Context) {
} }
u, err := models.UserSignIn(UserName, Password) u, err := models.UserSignIn(UserName, Password)
if err != nil { if err != nil {
log.Info("login failed.UserName=" + UserName + " Password=" + Password)
ctx.ServerError("UserSignIn", err) ctx.ServerError("UserSignIn", err)
return return
} }
@@ -786,7 +787,6 @@ func handleSignInFullNotRedirect(ctx *context.Context, u *models.User, remember
} }


ctx.SetCookie("lang", u.Language, nil, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true) ctx.SetCookie("lang", u.Language, nil, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true)

// Clear whatever CSRF has right now, force to generate a new one // Clear whatever CSRF has right now, force to generate a new one
ctx.SetCookie(setting.CSRFCookieName, "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true) ctx.SetCookie(setting.CSRFCookieName, "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true)




+ 17
- 3
services/ai_task_service/cluster/c2net.go View File

@@ -91,14 +91,14 @@ func (c C2NetClusterAdapter) GetNotebookImages(req entity.GetImageReq, centerId
return r, false, nil return r, false, nil
} }


func hasIntersection(imageCenterInfos []models.AICenterImage, centerId ...string) bool {
func hasIntersection(imageCenterInfos []models.AiCenterImage, centerId ...string) bool {
if len(centerId) == 0 || len(imageCenterInfos) == 0 { if len(centerId) == 0 || len(imageCenterInfos) == 0 {
//如果没传centerId或者查询的镜像不含可用中心信息,不进行判断,直接返回true //如果没传centerId或者查询的镜像不含可用中心信息,不进行判断,直接返回true
return true return true
} }
for _, aicenterImage := range imageCenterInfos { for _, aicenterImage := range imageCenterInfos {
for _, centerCode := range centerId { for _, centerCode := range centerId {
if aicenterImage.AICenterID == centerCode {
if aicenterImage.AiCenterId == centerCode {
return true return true
} }
} }
@@ -294,6 +294,7 @@ func getGrampusAvailableCenterIds(queues []models.ResourceQueue, imageId string,
} }


processType := computeSource.FullName processType := computeSource.FullName
log.Info("processType=" + computeSource.FullName + " jobType=" + string(jobType))
images, err := grampus.GetImages(processType, string(jobType)) images, err := grampus.GetImages(processType, string(jobType))
if err != nil { if err != nil {
log.Warn("can not get image info from grampus", err) log.Warn("can not get image info from grampus", err)
@@ -303,11 +304,24 @@ func getGrampusAvailableCenterIds(queues []models.ResourceQueue, imageId string,
for _, image := range images.Infos { for _, image := range images.Infos {
if image.ID == imageId { if image.ID == imageId {
for _, centerInfo := range image.AICenterImage { for _, centerInfo := range image.AICenterImage {
imageCenterIds = append(imageCenterIds, centerInfo.AICenterID)
imageCenterIds = append(imageCenterIds, centerInfo.AiCenterId)
} }
break break
} }
} }
images, err = grampus.GetUserImages(processType, string(jobType))
if err == nil {
for _, image := range images.Infos {
if image.ID == imageId {
for _, centerInfo := range image.AICenterImage {
imageCenterIds = append(imageCenterIds, centerInfo.AiCenterId)
}
break
}
}
} else {
log.Warn("can not get user image info from grampus", err)
}
if len(imageCenterIds) == 0 { if len(imageCenterIds) == 0 {
return []string{}, errors.New("image not available") return []string{}, errors.New("image not available")
} }


+ 6
- 1
services/ai_task_service/container_builder/dataset_builder.go View File

@@ -29,7 +29,12 @@ func (b *DatasetBuilder) Build(ctx *context.CreationContext) ([]entity.Container
var data []entity.ContainerData var data []entity.ContainerData


//如果是智算GPU调试任务,需要把dataset文件夹也挂载,这样提交镜像时才不会把dataset下的文件提交到镜像中 //如果是智算GPU调试任务,需要把dataset文件夹也挂载,这样提交镜像时才不会把dataset下的文件提交到镜像中
if ctx.Request.Cluster == entity.C2Net && (ctx.Request.JobType == models.JobTypeDebug || ctx.Request.JobType == models.JobTypeTrain) && ctx.Request.ComputeSource.Name == models.GPU {
if ctx.Request.Cluster == entity.C2Net && (ctx.Request.JobType == models.JobTypeDebug || ctx.Request.JobType == models.JobTypeTrain) &&
(ctx.Request.ComputeSource.Name == models.GPU ||
ctx.Request.ComputeSource.Name == models.MLU ||
ctx.Request.ComputeSource.Name == models.GCU ||
ctx.Request.ComputeSource.Name == models.ILUVATAR ||
ctx.Request.ComputeSource.Name == models.METAX) {
log.Info("mount dataset directory.") log.Info("mount dataset directory.")
jobName := ctx.Request.JobName jobName := ctx.Request.JobName
storageTypes := b.Opts.AcceptStorageType storageTypes := b.Opts.AcceptStorageType


+ 18
- 2
services/ai_task_service/task/task_creation_info.go View File

@@ -115,9 +115,25 @@ func GetAvailableImageInfoBySpec(req entity.GetAITaskCreationImageInfoReq) (*ent
UserId: req.UserID, UserId: req.UserID,
JobType: req.JobType, JobType: req.JobType,
}) })

//load from db
reImages := make([]entity.ClusterImage, 0, 10)
result.Images = reImages
log.Info("start to query db,params=" + req.ComputeSource.FullName + " 2=" + req.ComputeSource.Name)
imageList, dbErr := models.GetImageByComputeResource(req.ComputeSource.Name)
if dbErr != nil {
log.Info("query error" + dbErr.Error())
} else {
for _, tmpImage := range imageList {
clusterImage := entity.ClusterImage{
ImageId: tmpImage.ImageID,
ImageName: tmpImage.Tag,
ImageUrl: tmpImage.Place,
}
result.Images = append(result.Images, clusterImage)
}
}
if images, canUseAll, err := t.GetImages(*req.ComputeSource, centerIds...); err == nil { if images, canUseAll, err := t.GetImages(*req.ComputeSource, centerIds...); err == nil {
result.Images = images
result.Images = append(result.Images, images...)
result.CanUseAllImages = canUseAll result.CanUseAllImages = canUseAll
} }
return result, nil return result, nil


+ 66
- 1
services/cloudbrain/resource/resource_queue.go View File

@@ -1,10 +1,12 @@
package resource package resource


import ( import (
"fmt"
"strings"

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


func AddResourceQueue(req models.ResourceQueueReq) error { func AddResourceQueue(req models.ResourceQueueReq) error {
@@ -54,6 +56,69 @@ func GetResourceAiCenters() ([]models.ResourceAiCenterRes, error) {
return r, nil return r, nil
} }


func filterGrampusImage(all []models.GrampusImage) []models.GrampusImage {
result := make([]models.GrampusImage, 0)
for _, tmp := range all {
computeResource := getComputeResourceByProcessType(tmp.ProcessorType)
if computeResource == "GCU" || computeResource == "MLU" || computeResource == "ILUVATAR-GPGPU" || computeResource == "METAX-GPGPU" {
result = append(result, tmp)
}
}
return result
}

func getComputeResourceByProcessType(processType string) string {
tail := strings.LastIndex(processType, "/")
if tail > 0 {
return strings.ToUpper(processType[tail+1:])
}
return strings.ToUpper(processType)
}

func SyncGrampusImage(doerId int64) error {
allImage, err := grampus.GetAllBaseImages()
if err == nil {
log.Info("Query image from grampus, length=" + fmt.Sprint(len(allImage.Infos)))
dbAllImage, err := models.GetGrampusAllBaseImage()
if err == nil {
log.Info("start to deal db image update.")
filterGrampusImageList := filterGrampusImage(allImage.Infos)
log.Info("sync grampus image length=." + fmt.Sprint(len(filterGrampusImageList)))
grampusMap := make(map[string]models.GrampusImage, 0)
for _, image := range filterGrampusImageList {
grampusMap[image.ID] = image
}
dbMap := make(map[string]*models.Image, 0)
for _, image := range dbAllImage {
dbMap[image.HuJingId] = image
}
insertList := make([]models.GrampusImage, 0)
updateList := make([]models.GrampusImage, 0)
updateDbList := make([]*models.Image, 0)
for _, image := range filterGrampusImageList {
if dbImage, ok := dbMap[image.ID]; !ok {
insertList = append(insertList, image)
} else {
updateList = append(updateList, image)
updateDbList = append(updateDbList, dbImage)
}
}
deleteList := make([]*models.Image, 0)
for _, image := range dbAllImage {
if _, ok := grampusMap[image.HuJingId]; !ok {
deleteList = append(deleteList, image)
}
}
models.SyncGrampusAllBaseImageToDb(insertList, updateList, updateDbList, deleteList, doerId)
} else {
log.Error("failed query image db.error=" + err.Error())
}
} else {
log.Error("failed query image grampus.error=" + err.Error())
}
return err
}

func SyncGrampusQueue(doerId int64) error { func SyncGrampusQueue(doerId int64) error {
r, err := grampus.GetResourceQueue() r, err := grampus.GetResourceQueue()
if err != nil { if err != nil {


+ 10
- 148
templates/admin/cloudbrain/imagecommit.tmpl View File

@@ -1,152 +1,14 @@
<style>
.label_color{
color:#505559 !important;
width: 6% !important;
text-align: center;
}
</style>
{{template "base/head" .}} {{template "base/head" .}}
<div id="mask">
<div id="loadingPage">
<div class="rect1"></div>
<div class="rect2"></div>
<div class="rect3"></div>
<div class="rect4"></div>
<div class="rect5"></div>
</div>
</div>
<div class="repository"> <div class="repository">
{{template "repo/header" .}} {{template "repo/header" .}}
<div class="alert"></div>
<div class="ui container">
<div>
<div class="ui negative message" style="display: none;">
</div>
<div class="ui info message" style="display: none;">
</div>
<div class="ui positive message" style="display: none;">
</div>
<h4 class="ui top attached header">
{{.i18n.Tr "repo.submit_image"}}
</h4>
<div class="submit-image-tmplvalue" style="display: none;" data-link="{{$.Link}}" data-edit-page="{{.PageIsAdminImages}}"></div>
<div class="ui attached segment" style="padding: 2em 3em;padding-bottom: 7rem;">
<div class="ui form" id="form_image">
<input type="hidden" name="edit" value="edit">
{{.CsrfTokenHtml}}
<div class="inline field">
<label class="label_color" for="">{{$.i18n.Tr "dataset.dataset_available_clusters"}}</label>
<!-- <div class="ui basic label" style="border: none !important;color:#3291f8;">
<svg class="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="14" height="14"><path fill="none" d="M0 0h24v24H0z"></path><path d="M4 3h16a1 1 0 0 1 1 1v7H3V4a1 1 0 0 1 1-1zM3 13h18v7a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-7zm4 3v2h3v-2H7zM7 6v2h3V6H7z"></path></svg>
CPU/GPU
</div> -->
<div class="ui blue mini menu compact selectcloudbrain" id="adminCommitImage">
<a class="active item" data-type="0">
<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="item" data-type="2">
<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>
</div>
<input type="hidden" value="0" name="type">
</div>
<div class="inline required field">
<label class="label_color" for="">{{$.i18n.Tr "repo.images.name"}}</label>
<input type="text" name="tag" required placeholder="{{$.i18n.Tr "repo.images.name_placerholder"}}" style="width: 80%;" maxlength="100">
<span class="tooltips" style="display: block;padding-left: 1.5rem;">
{{.i18n.Tr "repo.images.name_rule50"}}
</span>
</div>
<div class="inline required field">
<label class="label_color" for="">{{$.i18n.Tr "repo.images"}}</label>
<input type="text" name="place" required placeholder="{{$.i18n.Tr "cloudbrain.input_mirror"}}" style="width: 80%;" maxlength="300">
</div>
<div class="inline required field">
<label class="label_color" for="">{{$.i18n.Tr "dataset.description"}}</label>
<textarea style="width: 80%;" required id="description" name="description" rows="3" maxlength="1000" placeholder={{.i18n.Tr "repo.images.descr_placerholder"}} onchange="this.value=this.value.substring(0, 1000)" onkeydown="this.value=this.value.substring(0, 1000)" onkeyup="this.value=this.value.substring(0, 1000)"></textarea>
</div>
<div class="inline field" style="display: flex;align-items: center;">
<label class="label_color" for="">{{$.i18n.Tr "repo.model.manage.label"}}</label>&nbsp;
<div class="ui multiple search selection dropdown" id="dropdown_image" style="width: 80%;">
<input type="hidden" name="topics" value="" required>
<div class="default text" id="default_text">{{.i18n.Tr "repo.repo_label_helpe"}}</div>
<div class="menu" id="course_label_item"></div>
</div>
</div>
<span class="tooltips" style="display: block;padding-left: 1.5rem;margin-top: 0.5rem;margin-bottom: 1rem;">{{.i18n.Tr "repo.image.label_tooltips"}}</span>
<div class="inline fields">
<label class="label_color" for="" style="visibility: hidden;"></label>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="isRecommend" checked="checked" value="true">
<label>{{.i18n.Tr "admin.images.recommend"}}</label>
</div>
</div>
<div class="field" style="flex: 0.15;">
<div class="ui radio checkbox" >
<input type="radio" name="isRecommend" value="false">
<label>{{.i18n.Tr "admin.images.unrecommend"}}</label>
</div>
</div>
</div>
<!--
<div class="inline fields">
<label class="label_color" for="" style="visibility: hidden;"></label>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="isPrivate" checked="checked" value="false">
<label>{{.i18n.Tr "org.settings.visibility.public"}}</label>
</div>
</div>
<div class="field" style="flex: 0.15;">
<div class="ui radio checkbox" >
<input type="radio" name="isPrivate" value="true">
<label>{{.i18n.Tr "home.show_private"}}</label>
</div>
</div>
<div class="field">
<span class="label_color">{{.i18n.Tr "repo.images.public_tooltips"}}</span>
</div>
</div>
-->
<div class="inline required field">
<label class="label_color" for="" style="visibility: hidden;"></label>
<span style="color: rgb(255, 94, 0);display: inline-flex;"><i class="ri-error-warning-line" style="margin-right: 0.3rem;"></i>{{.i18n.Tr "repo.images.submit_tooltips"}}</span>
</div>
<div class="inline required field">
<label class="label_color" for="" style="visibility: hidden;"></label>
<button class="ui create_image green button" type="button">
{{.i18n.Tr "repo.cloudbrain.commit_image"}}
</button>
<a class="ui button" id="cancel_submit_image">{{.i18n.Tr "repo.cloudbrain.cancel"}}</a>
</div>
</div>
</div>
</div>
</div>
<script>
window._PageType = "submitAdmin";
window._Image = {{.Image}};
window._PageFrom = 'imageAdmin';
window._PageSubmitLink = "{{$.Link}}";
</script>
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/vp-images-submit.css?v={{MD5 AppVer}}" />
<div id="__vue-root"></div>
<script src="{{StaticUrlPrefix}}/js/vp-images-submit.js?v={{MD5 AppVer}}"></script>
</div> </div>

<!-- 确认模态框 -->
<div>
<div class="ui modal image_confirm_submit">
<div class="header">{{.i18n.Tr "repo.submit_image"}}</div>
<div class="content text red center">
<p><i class="exclamation icon"></i>{{.i18n.Tr "repo.image_overwrite"}}</p>
</div>
<div class="actions">
<button class="ui deny small button">{{.i18n.Tr "cloudbrain.operate_cancel"}}</button>
<button class="ui green small approve button">{{.i18n.Tr "cloudbrain.operate_confirm"}}</button>
</div>
</div>
</div>
{{template "base/footer" .}}
{{template "base/footer" .}}

+ 3
- 24
templates/explore/images.tmpl View File

@@ -1,26 +1,5 @@
{{template "base/head" .}} {{template "base/head" .}}
<div class="alert"></div>
<div id="images"></div>
<!-- 确认模态框 -->
<div id="deletemodel">
<div class="ui basic modal images">
<div class="ui icon header">
<i class="trash icon"></i> {{.i18n.Tr "repo.images.delete_task"}}
</div>

<div class="content">
<p>{{.i18n.Tr "repo.images.task_delete_confirm"}}</p>
</div>
<div class="actions">
<div class="ui red basic inverted cancel button">
<i class="remove icon"></i>
{{.i18n.Tr "cloudbrain.operate_cancel"}}
</div>
<div class="ui green basic inverted ok button">
<i class="checkmark icon"></i>
{{.i18n.Tr "cloudbrain.operate_confirm"}}
</div>
</div>
</div>
</div>
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/vp-images-square.css?v={{MD5 AppVer}}" />
<div id="__vue-root"></div>
<script src="{{StaticUrlPrefix}}/js/vp-images-square.js?v={{MD5 AppVer}}"></script>
{{template "base/footer" .}} {{template "base/footer" .}}

+ 9
- 121
templates/repo/cloudbrain/image/apply.tmpl View File

@@ -1,126 +1,14 @@
<style>
.label_color{
color:#505559 !important;
width: 6% !important;
text-align: center;
}
.descr-tip-box {
display: inline-block;
border: 1px solid #f2711c;
background-color: rgba(242,113,28,0.05);
width: 80%;
padding: 10px;
}
.descr-tip-head {
color: #888888;
font-size: 14px;
margin-bottom: 4px;
}
.descr-tip-item {
margin: 2px 0;
padding-left: 10px;
color: rgb(242 113 28);
font-size: 12px;
}
</style>
{{template "base/head" .}} {{template "base/head" .}}
<div id="mask">
<div id="loadingPage">
<div class="rect1"></div>
<div class="rect2"></div>
<div class="rect3"></div>
<div class="rect4"></div>
<div class="rect5"></div>
</div>
</div>
<div class="repository"> <div class="repository">
{{template "repo/header" .}} {{template "repo/header" .}}
<div class="alert"></div>
<div class="ui container">
<div>
<div class="ui negative message" style="display: none;">
</div>
<div class="ui info message" style="display: none;">
</div>
<div class="ui positive message" style="display: none;">
</div>
<h4 class="ui top attached header">
{{$.i18n.Tr "admin.images.applyrecommendImage"}}
</h4>
<div class="submit-image-tmplvalue" style="display: none;" data-link="/image/{{$.Image.ID}}/apply" data-edit-page="{{.PageFrom}}"></div>
<div class="ui attached segment" style="padding: 2em 3em;padding-bottom: 7rem;">
<div class="ui form" id="form_image">
<input type="hidden" name="edit" value="edit">
{{.CsrfTokenHtml}}
<input type="hidden" name="id" value="{{.Image.ID}}">
<div class="inline field">
<label class="label_color" for="">{{$.i18n.Tr "dataset.dataset_available_clusters"}}</label>
<div class="ui basic label" style="border: none !important;color:#3291f8;">
<svg class="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="14" height="14"><path fill="none" d="M0 0h24v24H0z"></path><path d="M4 3h16a1 1 0 0 1 1 1v7H3V4a1 1 0 0 1 1-1zM3 13h18v7a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-7zm4 3v2h3v-2H7zM7 6v2h3V6H7z"></path></svg>
{{if eq .Image.CloudbrainType 2}}
{{$.i18n.Tr "cloudbrain.resource_cluster_c2net_simple"}} GPU
{{else}}
{{$.i18n.Tr "cloudbrain.resource_cluster_openi_simple"}} GPU
{{end}}
</div>
<input type="hidden" value="{{.Image.CloudbrainType}}" name="type">
</div>
<div class="inline required field">
<label class="label_color" for="">{{$.i18n.Tr "repo.images.name"}}</label>
<input type="hidden" name="tag" value="{{.Image.Tag}}" >
<input disabled value="{{.Image.Tag}}" style="width: 80%;">
<span class="tooltips" style="display: block;padding-left: 1.5rem;">
{{if eq .Image.CloudbrainType 2}}
{{.i18n.Tr "repo.images.name_rule50"}}
{{else}}
{{.i18n.Tr "repo.images.name_rule100"}}
{{end}}
</span>
</div>
<div class="inline required field">
<label class="label_color" for="">{{$.i18n.Tr "dataset.description"}}</label>
<textarea style="width: 80%;" required id="description" value="{{.Image.Description}}" name="description" rows="3" maxlength="1000" placeholder={{.i18n.Tr "repo.images.descr_placerholder"}} onchange="this.value=this.value.substring(0, 1000)" onkeydown="this.value=this.value.substring(0, 1000)" onkeyup="this.value=this.value.substring(0, 1000)">{{.Image.Description}}</textarea>
</div>
<div class="inline field">
<label class="label_color" style="color:transparent !important;">x</label>
<div class="descr-tip-box">
<div class="descr-tip-head">{{$.i18n.Tr "admin.images.descrTip"}}</div>
<div class="descr-tip-item">{{$.i18n.Tr "admin.images.framework"}}:pytorch1.9.1;</div>
<div class="descr-tip-item">CUDA:cuda11;</div>
<div class="descr-tip-item">{{$.i18n.Tr "admin.images.pythonVersion"}}:python 3.7.11;</div>
<div class="descr-tip-item">{{$.i18n.Tr "admin.images.operatingSystem"}}:Ubuntu 20.02;</div>
<div class="descr-tip-item">{{$.i18n.Tr "admin.images.installedSoftwarePackage"}}:numpy1.21.2</div>
</div>
</div>
<div class="inline field" style="display: flex;align-items: center;">
{{$lenTopics := len .Image.Topics}}
{{$subTopics := subOne $lenTopics}}
<label class="label_color" for="">{{$.i18n.Tr "repo.model.manage.label"}}</label>&nbsp;
<div class="ui multiple search selection dropdown" id="dropdown_image" style="width: 80%;">
<input type="hidden" name="topics" value="{{range $k,$v := .Image.Topics}}{{$v}}{{if ne $k $subTopics}},{{end}}{{end}}" required>
{{range .Image.Topics}}
<a class="ui label transition visible" data-value="{{.}}" style="display: inline-block !important;">{{.}}<i class="delete icon"></i></a>
{{end}}
<div class="default text" id="default_text">{{.i18n.Tr "repo.repo_label_helpe"}}</div>
<div class="menu" id="course_label_item"></div>
</div>
</div>
<span class="tooltips" style="display: block;padding-left: 1.5rem;margin-top: 0.5rem;margin-bottom: 1rem;">{{.i18n.Tr "repo.image.label_tooltips"}}</span>
<div class="inline required field" style="padding-top: 2rem;">
<label class="label_color" for="" style="visibility: hidden;"></label>
<button class="ui create_image green button" type="button">
{{$.i18n.Tr "admin.images.submitApply"}}
</button>
<a class="ui button" id="cancel_submit_image">{{.i18n.Tr "repo.cloudbrain.cancel"}}</a>
</div>
</div>
</div>
</div>
</div>
<script>
window._PageType = "apply";
window._Image = {{.Image}};
window._PageFrom = {{.PageFrom}};
window._PageSubmitLink = "/image/{{$.Image.ID}}/apply";
</script>
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/vp-images-submit.css?v={{MD5 AppVer}}" />
<div id="__vue-root"></div>
<script src="{{StaticUrlPrefix}}/js/vp-images-submit.js?v={{MD5 AppVer}}"></script>
</div> </div>
{{template "base/footer" .}} {{template "base/footer" .}}
<script>
;(function(){
var Images = {{.Image}};
})();
</script>

+ 9
- 114
templates/repo/cloudbrain/image/edit.tmpl View File

@@ -1,119 +1,14 @@
<style>
.label_color{
color:#505559 !important;
width: 6% !important;
text-align: center;
}
</style>
{{template "base/head" .}} {{template "base/head" .}}
<div id="mask">
<div id="loadingPage">
<div class="rect1"></div>
<div class="rect2"></div>
<div class="rect3"></div>
<div class="rect4"></div>
<div class="rect5"></div>
</div>
</div>
<div class="repository"> <div class="repository">
{{template "repo/header" .}} {{template "repo/header" .}}
<div class="alert"></div>
<div class="ui container">
<div>
<div class="ui negative message" style="display: none;">
</div>
<div class="ui info message" style="display: none;">
</div>
<div class="ui positive message" style="display: none;">
</div>
<h4 class="ui top attached header">
{{.i18n.Tr "repo.modify_image"}}
</h4>
<div class="submit-image-tmplvalue" style="display: none;" data-link="/image/{{$.Image.ID}}" data-edit-page="{{.PageFrom}}"></div>
<div class="ui attached segment" style="padding: 2em 3em;padding-bottom: 7rem;">
<div class="ui form" id="form_image">
<input type="hidden" name="edit" value="edit">
{{.CsrfTokenHtml}}
<input type="hidden" name="id" value="{{.Image.ID}}">
<div class="inline field">
<label class="label_color" for="">{{$.i18n.Tr "dataset.dataset_available_clusters"}}</label>
<div class="ui basic label" style="border: none !important;color:#3291f8;">
<svg class="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="14" height="14"><path fill="none" d="M0 0h24v24H0z"></path><path d="M4 3h16a1 1 0 0 1 1 1v7H3V4a1 1 0 0 1 1-1zM3 13h18v7a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-7zm4 3v2h3v-2H7zM7 6v2h3V6H7z"></path></svg>
{{if eq .Image.CloudbrainType 2}}
{{$.i18n.Tr "cloudbrain.resource_cluster_c2net_simple"}} GPU
{{else}}
{{$.i18n.Tr "cloudbrain.resource_cluster_openi_simple"}} GPU
{{end}}
</div>
<input type="hidden" value="{{.Image.CloudbrainType}}" name="type">
</div>
<div class="inline required field">
<label class="label_color" for="">{{$.i18n.Tr "repo.images.name"}}</label>
<input type="hidden" name="tag" value="{{.Image.Tag}}" >
<input disabled value="{{.Image.Tag}}" style="width: 80%;">
<span class="tooltips" style="display: block;padding-left: 1.5rem;">
{{if eq .Image.CloudbrainType 2}}
{{.i18n.Tr "repo.images.name_rule50"}}
{{else}}
{{.i18n.Tr "repo.images.name_rule100"}}
{{end}}
</span>
</div>
<div class="inline required field">
<label class="label_color" for="">{{$.i18n.Tr "dataset.description"}}</label>
<textarea style="width: 80%;" required id="description" value="{{.Image.Description}}" name="description" rows="3" maxlength="1000" placeholder={{.i18n.Tr "repo.images.descr_placerholder"}} onchange="this.value=this.value.substring(0, 1000)" onkeydown="this.value=this.value.substring(0, 1000)" onkeyup="this.value=this.value.substring(0, 1000)">{{.Image.Description}}</textarea>
</div>
<div class="inline field" style="display: flex;align-items: center;">
{{$lenTopics := len .Image.Topics}}
{{$subTopics := subOne $lenTopics}}
<label class="label_color" for="">{{$.i18n.Tr "repo.model.manage.label"}}</label>&nbsp;
<div class="ui multiple search selection dropdown" id="dropdown_image" style="width: 80%;">
<input type="hidden" name="topics" value="{{range $k,$v := .Image.Topics}}{{$v}}{{if ne $k $subTopics}},{{end}}{{end}}" required>
{{range .Image.Topics}}
<a class="ui label transition visible" data-value="{{.}}" style="display: inline-block !important;">{{.}}<i class="delete icon"></i></a>
{{end}}
<div class="default text" id="default_text">{{.i18n.Tr "repo.repo_label_helpe"}}</div>
<div class="menu" id="course_label_item"></div>
</div>
</div>
<span class="tooltips" style="display: block;padding-left: 1.5rem;margin-top: 0.5rem;margin-bottom: 1rem;">{{.i18n.Tr "repo.image.label_tooltips"}}</span>
<!--
<div class="inline fields">
<label class="label_color" for="" style="visibility: hidden;"></label>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="isPrivate" {{if not .Image.IsPrivate}} checked {{end}} value="false">
<label>{{.i18n.Tr "org.settings.visibility.public"}}</label>
</div>
</div>
<div class="field" style="flex: 0.15;">
<div class="ui radio checkbox" >
<input type="radio" name="isPrivate" {{if .Image.IsPrivate}} checked {{end}} value="true">
<label>{{.i18n.Tr "home.show_private"}}</label>
</div>
</div>
<div class="field">
<span class="label_color">{{.i18n.Tr "repo.images.public_tooltips"}}</span>
</div>
</div>
-->
<div class="inline required field">
<label class="label_color" for="" style="visibility: hidden;"></label>
<span style="color: rgb(255, 94, 0);display: inline-flex;"><i class="ri-error-warning-line" style="margin-right: 0.3rem;"></i>{{.i18n.Tr "repo.images.submit_tooltips"}}</span>
</div>
<div class="inline required field">
<label class="label_color" for="" style="visibility: hidden;"></label>
<button class="ui create_image green button" type="button">
{{.i18n.Tr "explore.save"}}
</button>
<a class="ui button" id="cancel_submit_image">{{.i18n.Tr "repo.cloudbrain.cancel"}}</a>
</div>
</div>
</div>
</div>
</div>
<script>
window._PageType = "edit";
window._Image = {{.Image}};
window._PageFrom = {{.PageFrom}};
window._PageSubmitLink = "/image/{{$.Image.ID}}";
</script>
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/vp-images-submit.css?v={{MD5 AppVer}}" />
<div id="__vue-root"></div>
<script src="{{StaticUrlPrefix}}/js/vp-images-submit.js?v={{MD5 AppVer}}"></script>
</div> </div>
{{template "base/footer" .}} {{template "base/footer" .}}
<script>
console.log({{.Image}})
</script>

+ 12
- 118
templates/repo/cloudbrain/image/submit.tmpl View File

@@ -1,123 +1,17 @@
<style>
.label_color{
color:#505559 !important;
width: 6% !important;
text-align: center;
}
</style>
{{template "base/head" .}} {{template "base/head" .}}
<div id="mask">
<div id="loadingPage">
<div class="rect1"></div>
<div class="rect2"></div>
<div class="rect3"></div>
<div class="rect4"></div>
<div class="rect5"></div>
</div>
</div>
<div class="repository"> <div class="repository">
{{template "repo/header" .}} {{template "repo/header" .}}
<div class="alert"></div>
<div class="ui container">
<div>
<div class="ui negative message" style="display: none;">
</div>
<div class="ui info message" style="display: none;">
</div>
<div class="ui positive message" style="display: none;">
</div>
<h4 class="ui top attached header">
{{.i18n.Tr "repo.submit_image"}}
</h4>
<div class="submit-image-tmplvalue" style="display: none;" data-link="{{$.Link}}" data-repo-link="{{$.RepoLink}}" data-edit-page="submit"></div>
<div class="ui attached segment" style="padding: 2em 3em;padding-bottom: 7rem;">
<div class="ui form" id="form_image">
{{.CsrfTokenHtml}}
<div class="inline field">
<label class="label_color" for="">{{$.i18n.Tr "dataset.dataset_available_clusters"}}</label>
<div class="ui basic label" style="border: none !important;color:#3291f8;">
<svg class="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="14" height="14"><path fill="none" d="M0 0h24v24H0z"></path><path d="M4 3h16a1 1 0 0 1 1 1v7H3V4a1 1 0 0 1 1-1zM3 13h18v7a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-7zm4 3v2h3v-2H7zM7 6v2h3V6H7z"></path></svg>
{{if eq .Type 2}}
{{$.i18n.Tr "cloudbrain.resource_cluster_c2net_simple"}} GPU
{{else}}
{{$.i18n.Tr "cloudbrain.resource_cluster_openi_simple"}} GPU
{{end}}
</div>
<input type="hidden" value="{{.Type}}" name="type">
</div>
<div class="inline required field">
<label class="label_color" for="">{{$.i18n.Tr "repo.images.name"}}</label>
<input type="text" name="tag" required placeholder="{{$.i18n.Tr "repo.images.name_placerholder"}}" style="width: 80%;" maxlength="{{if eq .Type 2}} 50 {{else}} 100 {{end}}">
<span class="tooltips" style="display: block;padding-left: 1.5rem;">
{{if eq .Type 2}}
{{.i18n.Tr "repo.images.name_rule50"}}
{{else}}
{{.i18n.Tr "repo.images.name_rule100"}}
{{end}}
</span>
</div>
<div class="inline required field">
<label class="label_color" for="">{{$.i18n.Tr "dataset.description"}}</label>
<textarea style="width: 80%;" required id="description" name="description" rows="3" maxlength="1000" placeholder={{.i18n.Tr "repo.images.descr_placerholder"}} onchange="this.value=this.value.substring(0, 1000)" onkeydown="this.value=this.value.substring(0, 1000)" onkeyup="this.value=this.value.substring(0, 1000)"></textarea>
</div>
<div class="inline field" style="display: flex;align-items: center;">
<label class="label_color" for="">{{$.i18n.Tr "repo.model.manage.label"}}</label>&nbsp;
<div class="ui multiple search selection dropdown" id="dropdown_image" style="width: 80%;">
<input type="hidden" name="topics" value="" required>
<div class="default text" id="default_text">{{.i18n.Tr "repo.repo_label_helpe"}}</div>
<div class="menu" id="course_label_item"></div>
</div>
</div>
<span class="tooltips" style="display: block;padding-left: 1.5rem;margin-top: 0.5rem;margin-bottom: 1rem;">{{.i18n.Tr "repo.image.label_tooltips"}}</span>
<!--
<div class="inline fields">
<label class="label_color" for="" style="visibility: hidden;"></label>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="isPrivate" checked="checked" value="false">
<label>{{.i18n.Tr "org.settings.visibility.public"}}</label>
</div>
</div>
<div class="field" style="flex: 0.15;">
<div class="ui radio checkbox" >
<input type="radio" name="isPrivate" value="true">
<label>{{.i18n.Tr "home.show_private"}}</label>
</div>
</div>
<div class="field">
<span class="label_color">{{.i18n.Tr "repo.images.public_tooltips"}}</span>
</div>
</div>
-->
<div class="inline required field">
<label class="label_color" for="" style="visibility: hidden;"></label>
<span style="color: rgb(255, 94, 0);display: inline-flex;"><i class="ri-error-warning-line" style="margin-right: 0.3rem;"></i>{{.i18n.Tr "repo.images.submit_tooltips"}}</span>
</div>
<div class="inline required field">
<label class="label_color" for="" style="visibility: hidden;"></label>
<button class="ui create_image green button" type="button">
{{.i18n.Tr "repo.cloudbrain.commit_image"}}
</button>
<a class="ui button" id="cancel_submit_image">{{.i18n.Tr "repo.cloudbrain.cancel"}}</a>
</div>
</div>
</div>
</div>
</div>
<script>
window._PageType = "submit";
window._Image = {{.Image}};
window._ImageType = {{.Type}};
window._ImageComputeResource = {{.ComputeResource}};
window._PageFrom = "submit";
window._PageSubmitLink = {{$.Link}};
window._PageRepoLink = {{$.RepoLink}};
</script>
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/vp-images-submit.css?v={{MD5 AppVer}}" />
<div id="__vue-root"></div>
<script src="{{StaticUrlPrefix}}/js/vp-images-submit.js?v={{MD5 AppVer}}"></script>
</div> </div>

<!-- 确认模态框 -->
<div>
<div class="ui modal image_confirm_submit">
<div class="header">{{.i18n.Tr "repo.submit_image"}}</div>
<div class="content text red center">
<p><i class="exclamation icon"></i>{{.i18n.Tr "repo.image_overwrite"}}</p>
</div>
<div class="actions">
<button class="ui deny small button">{{.i18n.Tr "cloudbrain.operate_cancel"}}</button>
<button class="ui green small approve button">{{.i18n.Tr "cloudbrain.operate_confirm"}}</button>
</div>
</div>
</div>
{{template "base/footer" .}} {{template "base/footer" .}}

+ 0
- 6
templates/reward/point/rule.tmpl View File

@@ -104,12 +104,6 @@
<td class="t-center point">-</td> <td class="t-center point">-</td>
<td class="t-center"><span class="typ">累计</span>积分获取上限<span class="limit"> - </span></td> <td class="t-center"><span class="typ">累计</span>积分获取上限<span class="limit"> - </span></td>
<td>首次更换头像,获得积分。</td> <td>首次更换头像,获得积分。</td>
</tr>
<tr key="TaskInviteFriendRegister">
<td class="t-center">邀请好友</td>
<td class="t-center point">-</td>
<td class="t-center"><span class="typ">累计</span>积分获取上限<span class="limit"> - </span></td>
<td>邀请好友获得积分。</td>
</tr> </tr>
<tr key="TaskInviteFriendRegister"> <tr key="TaskInviteFriendRegister">
<td class="t-center">邀请好友</td> <td class="t-center">邀请好友</td>


+ 0
- 1
vendor/gitea.com/macaron/csrf/csrf.go View File

@@ -222,7 +222,6 @@ func Generate(options ...Options) macaron.Handler {
needsNew = true needsNew = true
} }
} }

if needsNew { if needsNew {
// FIXME: actionId. // FIXME: actionId.
x.Token = GenerateToken(x.Secret, x.ID, "POST") x.Token = GenerateToken(x.Secret, x.ID, "POST")


+ 0
- 1
vendor/gitea.com/macaron/csrf/xsrf.go View File

@@ -90,7 +90,6 @@ func validTokenAtTime(token, key, userID, actionID string, now time.Time) bool {
} }


expected := generateTokenAtTime(key, userID, actionID, issueTime) expected := generateTokenAtTime(key, userID, actionID, issueTime)

// Check that the token matches the expected value. // Check that the token matches the expected value.
// Use constant time comparison to avoid timing attacks. // Use constant time comparison to avoid timing attacks.
return subtle.ConstantTimeCompare([]byte(token), []byte(expected)) == 1 return subtle.ConstantTimeCompare([]byte(token), []byte(expected)) == 1


+ 82
- 32
web_src/js/components/images/adminImages.vue View File

@@ -15,18 +15,7 @@
<div class="ui ten wide column" style="margin: 1rem 0;"> <div class="ui ten wide column" style="margin: 1rem 0;">
<el-checkbox v-model="checked" style="padding: 0.5rem 1rem;">{{ <el-checkbox v-model="checked" style="padding: 0.5rem 1rem;">{{
$i18n['cloudeBrainMirror']['platform_recommendations'] }}</el-checkbox> $i18n['cloudeBrainMirror']['platform_recommendations'] }}</el-checkbox>
<!-- <el-dropdown @command="handleCommand" trigger="click"
style="border: 1px solid rgba(34,36,38,.15);border-radius: 4px;padding: 0.5rem 1rem;">
<span class="el-dropdown-link">
{{dropdownPrivate}}<i class="el-icon-caret-bottom el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="{label:$i18n['all'],private:''}">{{$i18n['all']}}</el-dropdown-item>
<el-dropdown-item :command="{label:$i18n['cloudeBrainMirror']['public'],private:false}">{{$i18n['cloudeBrainMirror']['public']}}</el-dropdown-item>
<el-dropdown-item :command="{label:$i18n['cloudeBrainMirror']['private'],private:true}">{{$i18n['cloudeBrainMirror']['private']}}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown> -->
<el-dropdown @command="handleCommandType" trigger="click"
<!-- <el-dropdown @command="handleCommandType" trigger="click"
style="border: 1px solid rgba(34,36,38,.15);border-radius: 4px;padding: 0.5rem 1rem;"> style="border: 1px solid rgba(34,36,38,.15);border-radius: 4px;padding: 0.5rem 1rem;">
<span class="el-dropdown-link"> <span class="el-dropdown-link">
{{ dropdownType }}<i class="el-icon-caret-bottom el-icon--right"></i> {{ dropdownType }}<i class="el-icon-caret-bottom el-icon--right"></i>
@@ -39,6 +28,28 @@
<el-dropdown-item :command="{ label: $i18n['cloudeBrainMirror']['c2net'], type: 2 }">{{ <el-dropdown-item :command="{ label: $i18n['cloudeBrainMirror']['c2net'], type: 2 }">{{
$i18n['cloudeBrainMirror']['c2net'] }}</el-dropdown-item> $i18n['cloudeBrainMirror']['c2net'] }}</el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</el-dropdown> -->
<el-dropdown @command="handleCommandComputeResource" trigger="click"
style="border: 1px solid rgba(34,36,38,.15);border-radius: 4px;padding: 0.5rem 1rem;">
<span class="el-dropdown-link">
{{ dropdownComputeResource }}<i class="el-icon-caret-bottom el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="{ label: $i18n['cloudeBrainMirror']['all_compute_resource'], type: '' }">{{
$i18n['cloudeBrainMirror']['all_compute_resource'] }}</el-dropdown-item>
<el-dropdown-item :command="{ label: $i18n['computeResourceTitle']['GPU'], type: 'GPU' }">{{
$i18n['computeResourceTitle']['GPU'] }}</el-dropdown-item>
<el-dropdown-item :command="{ label: $i18n['computeResourceTitle']['GCU'], type: 'GCU' }">{{
$i18n['computeResourceTitle']['GCU'] }}</el-dropdown-item>
<el-dropdown-item :command="{ label: $i18n['computeResourceTitle']['MLU'], type: 'MLU' }">{{
$i18n['computeResourceTitle']['MLU'] }}</el-dropdown-item>
<el-dropdown-item
:command="{ label: $i18n['computeResourceTitle']['ILUVATAR-GPGPU'], type: 'ILUVATAR-GPGPU' }">{{
$i18n['computeResourceTitle']['ILUVATAR-GPGPU'] }}</el-dropdown-item>
<el-dropdown-item
:command="{ label: $i18n['computeResourceTitle']['METAX-GPGPU'], type: 'METAX-GPGPU' }">{{
$i18n['computeResourceTitle']['METAX-GPGPU'] }}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown> </el-dropdown>
<el-dropdown @command="handleApplyState" trigger="click" <el-dropdown @command="handleApplyState" trigger="click"
style="border: 1px solid rgba(34,36,38,.15);border-radius: 4px;padding: 0.5rem 1rem;"> style="border: 1px solid rgba(34,36,38,.15);border-radius: 4px;padding: 0.5rem 1rem;">
@@ -79,18 +90,17 @@
<a class="ui blue small button" href="/admin/images/commit_image">{{ <a class="ui blue small button" href="/admin/images/commit_image">{{
$i18n['cloudeBrainMirror']['create_cloud_brain_mirror'] }}</a> $i18n['cloudeBrainMirror']['create_cloud_brain_mirror'] }}</a>
</div> </div>
<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="$i18n['cloudeBrainMirror']['mirror_tag']" min-width="19%" align="left" prop="tag">
<div class="ui sixteen wide column" style="padding: 0;overflow-x:auto;">
<el-table :data="tableDataCustom" style="min-width:100%;" :header-cell-style="tableHeaderStyle">
<el-table-column :label="$i18n['cloudeBrainMirror']['mirror_tag']" width="250px" align="left" prop="tag">
<template slot-scope="scope"> <template slot-scope="scope">
<div style="display: flex;align-items: center;"> <div style="display: flex;align-items: center;">
<a class="text-over image_title" :title="scope.row.tag">{{ scope.row.tag }}</a> <a class="text-over image_title" :title="scope.row.tag">{{ scope.row.tag }}</a>
<!-- <i class="ri-lock-2-line" style="color: #fa8c16;padding: 0 1rem;" v-if="scope.row.isPrivate"></i> -->
<img v-if="scope.row.type == 5" src="/img/jian.svg" style="margin-left: 0.5rem;"> <img v-if="scope.row.type == 5" src="/img/jian.svg" style="margin-left: 0.5rem;">
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column :label="$i18n['cloudeBrainMirror']['mirror_description']" min-width="26%" align="left"
<el-table-column :label="$i18n['cloudeBrainMirror']['mirror_description']" width="300px" align="left"
prop="description"> prop="description">
<template slot-scope="scope"> <template slot-scope="scope">
<div class="image_desc" :title="scope.row.description">{{ scope.row.description }} <div class="image_desc" :title="scope.row.description">{{ scope.row.description }}
@@ -101,20 +111,48 @@
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="cloudbrainType" :label="$i18n['cloudeBrainMirror']['available_clusters']"
min-width="10%" align="center">
<el-table-column :label="$i18n['model_compute_resource']" width="150px" align="left"
prop="compute_resource">
<template slot-scope="scope"> <template slot-scope="scope">
{{ scope.row.cloudbrainType | transformType(vm) }}
<span :title="scope.row.compute_resource">{{ $i18n['computeResourceTitle'][scope.row.compute_resource]
|| scope.row.compute_resource }}</span>
</template>
</el-table-column>
<el-table-column :label="$i18n['cloudeBrainMirror']['framework']" width="150px" align="left"
prop="framework">
<template slot-scope="scope">
{{ scope.row.framework }}<br />{{ scope.row.frameworkVersion }}
</template>
</el-table-column>
<el-table-column :label="'Python'" width="100px" align="left" prop="python">
<template slot-scope="scope">
{{ scope.row.pythonVersion }}
</template>
</el-table-column>
<el-table-column :label="'Cuda'" width="100px" align="left" prop="cudaVersion">
<template slot-scope="scope">
{{ scope.row.cudaVersion || '--' }}
</template>
</el-table-column>
<el-table-column :label="$i18n['cloudeBrainMirror']['operationSystem']" width="180px" align="left"
prop="python">
<template slot-scope="scope">
{{ scope.row.operationSystem }}<br />{{ scope.row.operationSystemVersion }}
</template>
</el-table-column>
<el-table-column :label="$i18n['cloudeBrainMirror']['thirdPackages']" width="220px" align="left"
prop="thirdpackages">
<template slot-scope="scope">
<div class="image_desc" :title="scope.row.thirdPackages">{{ scope.row.thirdPackages }}</div>
</template> </template>
</el-table-column> </el-table-column>
<!-- <el-table-column prop="isPrivate" :label="$i18n['cloudeBrainMirror']['state']" min-width="6%" align="center">
<template slot-scope="scope">
<span v-if="scope.row.isPrivate" style="color: rgb(250, 140, 22);">{{$i18n['cloudeBrainMirror']['private']}}</span>
<span v-else style="color: rgb(19, 194, 141);">{{$i18n['cloudeBrainMirror']['public']}}</span>
</template>
</el-table-column> -->
<el-table-column prop="creator" :label="$i18n['cloudeBrainMirror']['creator']" min-width="6%"
align="center">
<!-- <el-table-column prop="cloudbrainType" :label="$i18n['cloudeBrainMirror']['available_clusters']"
width="120px" align="center">
<template slot-scope="scope">
{{ scope.row.cloudbrainType | transformType(vm) }}
</template>
</el-table-column> -->
<el-table-column prop="creator" :label="$i18n['cloudeBrainMirror']['creator']" width="80px" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<a v-if="scope.row.userName || scope.row.relAvatarLink" :href="'/' + scope.row.userName" <a v-if="scope.row.userName || scope.row.relAvatarLink" :href="'/' + scope.row.userName"
:title="scope.row.userName"> :title="scope.row.userName">
@@ -126,13 +164,13 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="createdUnix" :label="$i18n['cloudeBrainMirror']['creation_time']" align="center" <el-table-column prop="createdUnix" :label="$i18n['cloudeBrainMirror']['creation_time']" align="center"
min-width="11%">
width="160px">
<template slot-scope="scope"> <template slot-scope="scope">
{{ scope.row.createdUnix | transformTimestamp }} {{ scope.row.createdUnix | transformTimestamp }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="apply_status" :label="$i18n['cloudeBrainMirror']['approval_status']" min-width="10%"
align="center">
<el-table-column prop="apply_status" :label="$i18n['cloudeBrainMirror']['approval_status']" width="120px"
fixed="right" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<span v-if="scope.row.apply_status == 0" style="">{{ '--' }}</span> <span v-if="scope.row.apply_status == 0" style="">{{ '--' }}</span>
<div v-if="scope.row.apply_status === 1" <div v-if="scope.row.apply_status === 1"
@@ -168,7 +206,8 @@
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column align="center" min-width="33%" :label="$i18n['cloudeBrainMirror']['operation']">
<el-table-column align="center" width="400px" :label="$i18n['cloudeBrainMirror']['operation']"
fixed="right">
<template slot-scope="scope"> <template slot-scope="scope">
<div style="display: flex;justify-content: center;align-items: center;"> <div style="display: flex;justify-content: center;align-items: center;">
<div style="display: flex;align-items: center;padding: 0 1rem;" :title="$i18n['citations']"> <div style="display: flex;align-items: center;padding: 0 1rem;" :title="$i18n['citations']">
@@ -253,6 +292,7 @@ export default {
search: '', search: '',
dropdownPrivate: '', dropdownPrivate: '',
dropdownType: '', dropdownType: '',
dropdownComputeResource: '',
dropdownApplyState: '', dropdownApplyState: '',
checked: false, checked: false,
currentPageCustom: 1, currentPageCustom: 1,
@@ -389,6 +429,12 @@ export default {
this.paramsCustom.page = 1 this.paramsCustom.page = 1
this.getImageListCustom() this.getImageListCustom()
}, },
handleCommandComputeResource(command) {
this.dropdownComputeResource = command.label
this.paramsCustom.computeResource = command.type
this.paramsCustom.page = 1
this.getImageListCustom()
},
handleApplyState(command) { handleApplyState(command) {
this.dropdownApplyState = command.label this.dropdownApplyState = command.label
this.paramsCustom.apply = command.type this.paramsCustom.apply = command.type
@@ -470,6 +516,7 @@ export default {
this.dropdownPrivate = this.$i18n['all']; this.dropdownPrivate = this.$i18n['all'];
this.dropdownType = this.$i18n['cloudeBrainMirror']['all_cluster']; this.dropdownType = this.$i18n['cloudeBrainMirror']['all_cluster'];
this.dropdownApplyState = this.$i18n['cloudeBrainMirror']['all_approval_status']; this.dropdownApplyState = this.$i18n['cloudeBrainMirror']['all_approval_status'];
this.dropdownComputeResource = this.$i18n['cloudeBrainMirror']['all_compute_resource'];
let params = new URLSearchParams(location.search) let params = new URLSearchParams(location.search)
if (location.search) { if (location.search) {
this.firstSearch = true this.firstSearch = true
@@ -483,6 +530,9 @@ export default {
if (params.has('cloudbrainType')) { if (params.has('cloudbrainType')) {
this.dropdownType = params.get('cloudbrainType') === 0 ? this.$i18n['cloudeBrainMirror']['openi'] : this.$i18n['cloudeBrainMirror']['c2net'] this.dropdownType = params.get('cloudbrainType') === 0 ? this.$i18n['cloudeBrainMirror']['openi'] : this.$i18n['cloudeBrainMirror']['c2net']
} }
if (params.has('computeResource')) {
this.dropdownComputeResource = this.$i18n['computeResourceTitle'][params.get('computeResource')];
}
if (params.has('apply')) { if (params.has('apply')) {
const apply = params.get('apply'); const apply = params.get('apply');
switch (apply) { switch (apply) {


+ 32
- 4
web_src/js/features/i18nVue.js View File

@@ -201,6 +201,10 @@ export const i18nVue = {
not_recommend: '不同意推荐', not_recommend: '不同意推荐',
reason: '原因', reason: '原因',
pleaseEnterReason: '请输入原因', pleaseEnterReason: '请输入原因',
framework: '框架',
operationSystem: '操作系统',
thirdPackages: 'Python依赖库',
all_compute_resource: '全部计算资源',
}, },
modelObj: { modelObj: {
model_label: '选择模型', model_label: '选择模型',
@@ -223,7 +227,17 @@ export const i18nVue = {
export_success: '导出成功', export_success: '导出成功',
exporting: '正在导出', exporting: '正在导出',
please_select_file:'请先选择文件', please_select_file:'请先选择文件',
}
},
computeResourceTitle: {
'CPU/GPU': '英伟达GPU',
GPU: '英伟达GPU',
NPU: '昇腾NPU',
GCU: '燧原GCU',
MLU: '寒武纪MLU',
DCU: '海光DCU',
'ILUVATAR-GPGPU': '天数智芯GPGPU',
'METAX-GPGPU': '沐曦GPGPU',
},
}, },
US: { US: {
computer_vision: "computer vision", computer_vision: "computer vision",
@@ -383,10 +397,10 @@ export const i18nVue = {
public_mirror: 'Public Mirror', public_mirror: 'Public Mirror',
recommendImages: 'Recommend Mirror', recommendImages: 'Recommend Mirror',
platform_recommendations:'Show platform recommendations only', platform_recommendations:'Show platform recommendations only',
placeholder: 'Search Mirror tag / description / tag ... ',
placeholder: 'Search Mirror tag / description / label ... ',
search:'Search', search:'Search',
mirror_tag:'Mirror Tag', mirror_tag:'Mirror Tag',
mirror_description:'mirror_description ',
mirror_description:'Mirror Description ',
available_clusters: 'Cluster/Compute Resources', available_clusters: 'Cluster/Compute Resources',
creator: 'Creator', creator: 'Creator',
creation_time: 'Creation time', creation_time: 'Creation time',
@@ -430,6 +444,10 @@ export const i18nVue = {
not_recommend: 'Not recommend', not_recommend: 'Not recommend',
reason: 'Reason', reason: 'Reason',
pleaseEnterReason: 'Please enter the reason', pleaseEnterReason: 'Please enter the reason',
framework: 'Framework',
operationSystem: 'Operating system',
thirdPackages: 'Python libraries',
all_compute_resource: 'All Compute Resource',
}, },
modelObj: { modelObj: {
model_label: 'Select Model', model_label: 'Select Model',
@@ -452,6 +470,16 @@ export const i18nVue = {
export_success: 'Export success', export_success: 'Export success',
exporting: ' Exporting', exporting: ' Exporting',
please_select_file: 'Please select a file first', please_select_file: 'Please select a file first',
}
},
computeResourceTitle: {
'CPU/GPU': 'NVIDIA GPU',
GPU: 'NVIDIA GPU',
NPU: 'Ascend NPU',
GCU: 'Enflame GCU',
MLU: 'Cambricon MLU',
DCU: 'HYGON DCU',
'ILUVATAR-GPGPU': 'Iluvatar CoreX GPGPU',
'METAX-GPGPU': 'MetaX GPGPU',
},
}, },
}; };

+ 9
- 0
web_src/vuepages/apis/modules/common.js View File

@@ -1,5 +1,14 @@
import service from '../service'; import service from '../service';


// 获取promote配置数据
export const getStaticFile = (filePathName) => {
return service({
url: `${filePathName}`,
method: 'get',
params: {},
});
}

// 获取promote配置数据 // 获取promote配置数据
export const getPromoteData = (filePathName) => { export const getPromoteData = (filePathName) => {
return service({ return service({


+ 64
- 3
web_src/vuepages/apis/modules/images.js View File

@@ -1,7 +1,8 @@
import service from "../service";
import service from '../service';
import Qs from 'qs';


// 获取镜像 // 获取镜像
// params: { type-0|1|2, q, page, pageSize, cloudbrainType-0,1 }
// params: { type-0|1|2, q, page, pageSize, cloudbrainType-0,1, sort, framework, frameworkVersion, cuda, python }
export const getImages = (params) => { export const getImages = (params) => {
const typeMap = { const typeMap = {
'0': 'recommend', '0': 'recommend',
@@ -17,9 +18,69 @@ export const getImages = (params) => {
method: "get", method: "get",
params: { params: {
q: params.q || '', q: params.q || '',
cloudbrainType: params.cloudbrainType || '-1',
sort: params.sort,
computeResource: params.computeResource,
framework: params.framework,
frameworkVersion: params.frameworkVersion,
cuda: params.cuda,
python: params.python,
spec: params.spec,
trainType: params.trainType,
page: params.page || 1, page: params.page || 1,
pageSize: params.pageSize || 5, pageSize: params.pageSize || 5,
cloudbrainType: params.cloudbrainType,
} }
}); });
}; };

export const putImageAction = (params) => {
return service({
url: `/image/${params.id}/action/${params.action}`,
method: 'put',
params: {}
});
};

export const deleteImage = (params) => {
return service({
url: `/image/${params.id}`,
method: 'delete',
params: {}
});
};

export const submitImage = (data) => {
return service({
url: data.link,
method: 'post',
params: {},
data: Qs.stringify(data),
});
};

export const getImageById = (params) => {
return service({
url: `/image/${params.id}`,
method: 'get',
params: {},
});
};

export const searchImageTopics = (params) => {
return service({
url: `/api/v1/image/topics/search`,
method: 'get',
params: { q: params.q }
});
};

// 查询选择镜像过滤条件
// params: index-查询类型:0(可用框架)|1(可用框架版本)|2(可用python版本)|3(可用cuda版本)
// framework-框架,version-框架版本,python-python版本
export const getImageAvailabelFilter = (params) => {
return service({
url: `/api/v1/images/availableFilter`,
method: 'get',
params: { ...params }
});
};

+ 0
- 1
web_src/vuepages/components/cloudbrain/FormTop.vue View File

@@ -115,7 +115,6 @@ export default {
margin-left: -1px; margin-left: -1px;
height: 38px; height: 38px;
padding: 0 12px; padding: 0 12px;
border-left: none;
margin-bottom: 5px; margin-bottom: 5px;


i { i {


+ 232
- 19
web_src/vuepages/components/cloudbrain/ImageSelectV1.vue View File

@@ -6,7 +6,8 @@
</div> </div>
<div class="content" :class="errStatus ? 'error' : ''"> <div class="content" :class="errStatus ? 'error' : ''">
<el-input class="field-input" v-model="imageUrl" @input="imageChange" <el-input class="field-input" v-model="imageUrl" @input="imageChange"
:placeholder="$t('cloudbrainObj.selectImagePlaceholder')"></el-input>
:readonly="configs.computerResouce != 'GPU'" :placeholder="configs.computerResouce == 'GPU' ?
$t('cloudbrainObj.selectImagePlaceholder') : $t('cloudbrainObj.selectImage')"></el-input>
</div> </div>
</div> </div>
<div class="right-area"> <div class="right-area">
@@ -16,8 +17,8 @@
</div> </div>
</div> </div>
<el-dialog class="model-dlg" :visible.sync="dlgShow" :title="$t('cloudbrainObj.selectImage')" width="1000px" <el-dialog class="model-dlg" :visible.sync="dlgShow" :title="$t('cloudbrainObj.selectImage')" width="1000px"
:modal="true" :close-on-click-modal="false" :show-close="true" :destroy-on-close="false" :before-close="beforeClose"
@open="open" @closed="closed">
:modal="true" :close-on-click-modal="false" :show-close="true" :destroy-on-close="false"
:before-close="beforeClose" @open="open" @closed="closed">
<div class="dlg-content"> <div class="dlg-content">
<div class="main-area" v-loading="dlgLoading"> <div class="main-area" v-loading="dlgLoading">
<div class="image-tabs-c"> <div class="image-tabs-c">
@@ -26,9 +27,23 @@
<el-tab-pane :label="$t('cloudbrainObj.myImage')" name="second"></el-tab-pane> <el-tab-pane :label="$t('cloudbrainObj.myImage')" name="second"></el-tab-pane>
<el-tab-pane :label="$t('cloudbrainObj.myFavImage')" name="third"></el-tab-pane> <el-tab-pane :label="$t('cloudbrainObj.myFavImage')" name="third"></el-tab-pane>
</el-tabs> </el-tabs>
</div>
<div class="filter-c">
<div class="cascader-c">
<span class="cascader-tit">{{ $t('imagesObj.filterImages') }}:</span>
<div class="cascader-content-c">
<div class="cascader-tips">{{ filterImagesPlaceholder }}</div>
<el-cascader class="image-filter" ref="cascaderFilterRef" v-model="dlgCascaderFilter.value"
:props="dlgCascaderProps" :options="dlgCascaderFilter.options" clearable
:placeholder="$t('imagesObj.filterImages')" :popper-class="dlgCascaderFilter.popperClass"
@expand-change="handleDlgCascaderFilterExpandChange"
@visible-change="handleDlgCascaderFilterVisibleChange"
@change="handleDlgCascaderFilterChange"></el-cascader>
</div>
</div>
<el-input size="small" class="search-inp" :placeholder="$t('cloudbrainObj.searchImagePlaceholder')" <el-input size="small" class="search-inp" :placeholder="$t('cloudbrainObj.searchImagePlaceholder')"
v-model="dlgSearchValue" @keydown.enter.stop.native.prevent="inputSearch">
<div slot="suffix" class="search-inp-icon" @click="inputSearch">
v-model="dlgSearchValue" @keydown.enter.stop.native.prevent="search">
<div slot="suffix" class="search-inp-icon" @click="search">
<i class="el-icon-search"></i> <i class="el-icon-search"></i>
</div> </div>
</el-input> </el-input>
@@ -41,21 +56,35 @@
<span :title="item.tag">{{ item.tag }}</span> <span :title="item.tag">{{ item.tag }}</span>
<img v-if="item.type == 5" src="/img/jian.svg" /> <img v-if="item.type == 5" src="/img/jian.svg" />
</div> </div>
<div></div>
</div>
<div class="item-l-m">
<div class="item-topics"> <div class="item-topics">
<span class="type-compute-resource"
:style="item.computeResourceColor ? `background-color: ${item.computeResourceColor};` : ''">{{
item.computeResourceShow }}</span>
<span class="type-sys" v-for="(topic, index) in item.topicsSys" :key="`sys-${index}`">{{ topic
}}</span>
<span class="type-pkg" v-for="(topic, index) in item.topicsPkg" :key="`pkg-${index}`">{{ topic
}}</span>
<span v-for="(topic, index) in item.topics" :key="index">{{ topic }}</span> <span v-for="(topic, index) in item.topics" :key="index">{{ topic }}</span>
</div> </div>
</div> </div>
<div class="item-l-b"> <div class="item-l-b">
<a class="item-creator" :href="`/${item.userName}`">
<a v-if="item.userName" class="item-creator" :href="`/${item.userName}`">
<img class="ui avatar mini image" style="width: 20px; height: 20px" <img class="ui avatar mini image" style="width: 20px; height: 20px"
:src="item.relAvatarLink ? item.relAvatarLink : `/user/avatar/ghost/-1`" /> :src="item.relAvatarLink ? item.relAvatarLink : `/user/avatar/ghost/-1`" />
</a> </a>
<a v-else class="item-creator" href="javascript:;">
<img class="ui avatar mini image" style="width: 20px; height: 20px"
:src="`/user/avatar/ghost/-1`" />
</a>
<span class="item-descr" :title="item.description">{{ item.description }}</span> <span class="item-descr" :title="item.description">{{ item.description }}</span>
</div> </div>
</div> </div>
<div class="item-r"> <div class="item-r">
<el-button v-if="item.status == 1" @click="chooseImage(item)">{{ $t('cloudbrainObj.useImage') <el-button v-if="item.status == 1" @click="chooseImage(item)">{{ $t('cloudbrainObj.useImage')
}}</el-button>
}}</el-button>
<span class="error-content" v-if="item.status == 0"> <span class="error-content" v-if="item.status == 0">
<i class="CREATING"></i> <i class="CREATING"></i>
<span style="color:#5a5a5a">{{ $t('cloudbrainObj.submitting') }}</span> <span style="color:#5a5a5a">{{ $t('cloudbrainObj.submitting') }}</span>
@@ -81,15 +110,19 @@
</template> </template>


<script> <script>
import { getImages } from '~/apis/modules/images';
import { getImages, getImageAvailabelFilter } from '~/apis/modules/images';
import { COMPUTER_RESOURCES_COLORS, JOB_TYPE } from '~/const';
import { getListValueWithKey } from '~/utils';


export default { export default {
name: "ImageSelectV1", name: "ImageSelectV1",
props: { props: {
value: { type: String, required: true }, value: { type: String, required: true },
type: { type: Number, default: 0 },
type: { type: Number, default: 0 }, // -1-全部,0-启智GPU,2-智算GPU
showTitle: { type: Boolean, default: true }, showTitle: { type: Boolean, default: true },
required: { type: Boolean, default: true }, required: { type: Boolean, default: true },
spec: { type: String, required: true, },
configs: { type: Object, default: () => ({}) },
}, },
data() { data() {
return { return {
@@ -100,7 +133,38 @@ export default {
dlgLoading: false, dlgLoading: false,
dlgActiveName: 'first', dlgActiveName: 'first',
dlgSearchValue: '', dlgSearchValue: '',

dlgCascaderFilter: {
popperClass: `popper-filter-${Math.random().toString().replace('.', '')}`,
value: [],
options: []
},
dlgCascaderProps: {
lazy: true,
lazyLoad: (node, resolve) => {
const { level } = node;
const framework = node.path[0];
const version = node.path[1];
const python = node.path[2];
getImageAvailabelFilter({ index: level, framework, version, python }).then(res => {
if (res.data.code == 0) {
const nodes = (res.data.data).map(item => ({
value: item,
label: item ? item : 'None',
leaf: level >= (this.configs.computerResouce == 'GPU' ? 3 : 2)
}));
if (!nodes.length) {
node.config.leaf = true;
}
resolve(nodes);
} else {
resolve([]);
}
}).catch(err => {
console.log(err);
resolve([]);
});
}
},
dlgPage: 1, dlgPage: 1,
dlgPageSize: 5, dlgPageSize: 5,
dlgTotal: 0, dlgTotal: 0,
@@ -116,6 +180,21 @@ export default {
this.imageUrl = newVal.toString(); this.imageUrl = newVal.toString();
this.$emit('input', newVal); this.$emit('input', newVal);
} }
},
spec: {
immediate: true,
handler(newVal) {
this.$emit('changeImage');
}
},
},
computed: {
filterImagesPlaceholder() {
const list = this.$t('imagesObj.filterImagesPlaceholder').split('/');
if (this.configs.computerResouce != 'GPU') {
list.pop();
}
return list.join('/');
} }
}, },
methods: { methods: {
@@ -135,11 +214,49 @@ export default {
this.dlgPage = 1; this.dlgPage = 1;
this.searchImageData(); this.searchImageData();
}, },
inputSearch() {
search() {
this.dlgTotal = 0; this.dlgTotal = 0;
this.dlgPage = 1; this.dlgPage = 1;
this.searchImageData(); this.searchImageData();
}, },
handleDlgCascaderFilterVisibleChange() {
const popper = document.querySelector(`.${this.dlgCascaderFilter.popperClass}`);
if (popper && popper.querySelector('.popper-filter-title')) return;
const title = document.createElement('div');
title.classList = ['popper-filter-title'];
title.style = 'display:flex;margin-left:-1px;';
let innerHtml = '';
const titles = [
this.$t('imagesObj.frameworkName'), this.$t('imagesObj.frameworkVersion'),
this.$t('imagesObj.pyVersion'), this.$t('imagesObj.cudaVersion')
];
if (this.configs.computerResouce != 'GPU') {
titles.pop();
}
titles.forEach((item, index) => {
innerHtml += `<div class="popper-filter-title-item"
style="display:flex;align-items:center;height:30px;width:180px;box-sizing:border-box;color:rgb(136, 136, 136);
${index != 0 ? 'border-left:1px solid #E4E7ED;display:none;' : ''}padding-top:10px;padding-left:30px;font-size:12px;">${item}</div>`;
})
title.innerHTML = innerHtml;
popper.prepend(title);
},
handleDlgCascaderFilterExpandChange(value) {
const popper = document.querySelector(`.${this.dlgCascaderFilter.popperClass}`);
const title = popper.querySelector('.popper-filter-title');
const items = title.querySelectorAll('.popper-filter-title-item');
items.forEach((item, index) => {
const style = item.style;
if (index <= value.length) {
style.display = 'flex';
} else {
style.display = 'none';
}
})
},
handleDlgCascaderFilterChange() {
this.search();
},
searchImageData() { searchImageData() {
const tabName = this.dlgActiveName; const tabName = this.dlgActiveName;
const typeMap = { const typeMap = {
@@ -153,11 +270,45 @@ export default {
page: this.dlgPage, page: this.dlgPage,
pageSize: this.dlgPageSize, pageSize: this.dlgPageSize,
cloudbrainType: this.type, cloudbrainType: this.type,
computeResource: this.configs.computerResouce,
framework: this.dlgCascaderFilter.value[0],
frameworkVersion: this.dlgCascaderFilter.value[1],
python: this.dlgCascaderFilter.value[2],
cuda: this.dlgCascaderFilter.value[3],
spec: this.configs.computerResouce == 'GPU' ? -1 : this.spec,
trainType: this.configs.computerResouce == 'GPU' ? undefined : getListValueWithKey(JOB_TYPE, this.configs.taskType, 'k', 'train_type'),
} }
this.dlgLoading = true; this.dlgLoading = true;
getImages(params).then(res => { getImages(params).then(res => {
this.dlgLoading = false; this.dlgLoading = false;
const data = res.data?.images || []; const data = res.data?.images || [];
data.forEach(item => {
const topicsSys = [];
if (item.framework) {
topicsSys.push(`${item.framework} ${item.frameworkVersion}`.trim());
}
if (item.pythonVersion) {
topicsSys.push(`Python ${item.pythonVersion}`);
}
if (item.cudaVersion) {
topicsSys.push(`Cuda ${item.cudaVersion}`);
}
if (item.operationSystem) {
topicsSys.push(`${item.operationSystem} ${item.operationSystemVersion}`.trim());
}
const topicsPkg = [];
const thirdPackages = item.thirdPackages.split('\n');
thirdPackages.forEach(pkgLine => {
if (pkgLine) {
topicsPkg.push(pkgLine.trim().replace('==', ' '));
}
});
item.topicsSys = topicsSys;
item.topicsPkg = topicsPkg;
const compute_resource = item.compute_resource || 'GPU';
item.computeResourceColor = COMPUTER_RESOURCES_COLORS[compute_resource];
item.computeResourceShow = this.$t('computeResourceTitle.' + compute_resource);
});
this.imageList = data; this.imageList = data;
this.dlgTotal = parseInt(res.data?.count || 0); this.dlgTotal = parseInt(res.data?.count || 0);
}).catch(err => { }).catch(err => {
@@ -192,7 +343,24 @@ export default {
return !this.errStatus; return !this.errStatus;
}, },
}, },
beforeMount() { },
beforeMount() {
getImageAvailabelFilter({ index: 0 }).then(res => {
if (res.data.code == 0) {
let data = res.data.data || [];
if (data.indexOf('Other') >= 0) {
data = data.filter(item => item !== 'Other');
data.push('Other');
}
this.dlgCascaderFilter.options = data.map(item => ({
value: item,
label: item,
leaf: item == 'Other' ? true : false,
}));
}
}).catch(err => {
console.log(err);
})
},
mounted() { }, mounted() { },
}; };
</script> </script>
@@ -242,13 +410,40 @@ export default {
overflow: hidden; overflow: hidden;
margin-right: 5px; margin-right: 5px;
} }
}

.filter-c {
display: flex;
align-items: flex-end;
justify-content: space-between;

.cascader-c {
display: flex;
align-items: flex-end;

.cascader-tit {
margin-bottom: 6px;
}

.cascader-content-c {
.cascader-tips {
font-size: 12px;
padding-left: 16px;
}
}
}

.image-filter {
margin-top: -1px;
width: 340px;
}


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


.search-inp-icon { .search-inp-icon {
height: 100%; height: 100%;
@@ -299,14 +494,19 @@ export default {
vertical-align: middle; vertical-align: middle;
} }
} }
}

.item-l-m {
margin-top: 4px;


.item-topics { .item-topics {
display: inline-block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: flex;
flex-wrap: wrap;


span { span {
display: flex;
align-items: center;
justify-content: center;
font-size: .85714286rem; font-size: .85714286rem;
margin: 0 0.14285714em; margin: 0 0.14285714em;
padding: 0.3em 0.5em; padding: 0.3em 0.5em;
@@ -314,8 +514,21 @@ export default {
color: rgba(0, 0, 0, .6); color: rgba(0, 0, 0, .6);
font-weight: 700; font-weight: 700;
border-radius: 0.28571429rem; border-radius: 0.28571429rem;
line-height: 1;
cursor: pointer; cursor: pointer;
line-height: 1;
margin-bottom: 3px;

&.type-sys {
background: rgba(50, 145, 248, 0.2);
}

&.type-pkg {
background: rgba(91, 185, 115, 0.2);
}

&.type-compute-resource {
color: white;
}
} }
} }
} }


+ 15
- 2
web_src/vuepages/const/index.js View File

@@ -6,12 +6,25 @@ export const POINT_ACTIONS = [
{ k: 'CreatePublicRepo', v: i18n.t('createPublicProject') }, { k: 'CreateIssue', v: i18n.t('dailyPutforwardTasks') }, { k: 'CreatePullRequest', v: i18n.t('dailyPR') }, { k: 'CommentIssue', v: i18n.t('comment') }, { k: 'UploadAttachment', v: i18n.t('uploadDatasetFile') }, { k: 'CreateNewModelTask', v: i18n.t('importNewModel') }, { k: 'BindWechat', v: i18n.t('completeWechatCodeScanningVerification') }, { k: 'CreatePublicRepo', v: i18n.t('createPublicProject') }, { k: 'CreateIssue', v: i18n.t('dailyPutforwardTasks') }, { k: 'CreatePullRequest', v: i18n.t('dailyPR') }, { k: 'CommentIssue', v: i18n.t('comment') }, { k: 'UploadAttachment', v: i18n.t('uploadDatasetFile') }, { k: 'CreateNewModelTask', v: i18n.t('importNewModel') }, { k: 'BindWechat', v: i18n.t('completeWechatCodeScanningVerification') },
{ k: 'CreateCloudbrainTask', v: i18n.t('dailyRunCloudbrainTasks') }, { k: 'DatasetRecommended', v: i18n.t('datasetRecommendedByThePlatform') }, { k: 'CreateImage', v: i18n.t('submitNewPublicImage') }, { k: 'ImageRecommend', v: i18n.t('imageRecommendedByThePlatform') }, { k: 'ChangeUserAvatar', v: i18n.t('firstChangeofAvatar') }, { k: 'PushCommits', v: i18n.t('dailyCommit') }, { k: 'TaskInviteFriendRegister', v: i18n.t('user.inviteFriends') } { k: 'CreateCloudbrainTask', v: i18n.t('dailyRunCloudbrainTasks') }, { k: 'DatasetRecommended', v: i18n.t('datasetRecommendedByThePlatform') }, { k: 'CreateImage', v: i18n.t('submitNewPublicImage') }, { k: 'ImageRecommend', v: i18n.t('imageRecommendedByThePlatform') }, { k: 'ChangeUserAvatar', v: i18n.t('firstChangeofAvatar') }, { k: 'PushCommits', v: i18n.t('dailyCommit') }, { k: 'TaskInviteFriendRegister', v: i18n.t('user.inviteFriends') }
]; ];
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') }, { k: 'ONLINEINFERENCE', v: i18n.t('onlineinfer') }, { k: 'HPC', v: i18n.t('superComputeTask') }];

export const JOB_TYPE = [
{ k: 'DEBUG', v: i18n.t('debugTask'), train_type: 'Notebook' },
{ k: 'TRAIN', v: i18n.t('trainTask'), train_type: 'TrainJob' },
{ k: 'INFERENCE', v: i18n.t('inferenceTask'), train_type: 'Inference' },
{ k: 'BENCHMARK', v: i18n.t('benchmarkTask') },
{ k: 'ONLINEINFERENCE', v: i18n.t('onlineinfer') },
{ k: 'HPC', v: i18n.t('superComputeTask') }
];
// 资源管理 // 资源管理
export const CLUSTERS = [{ k: 'OpenI', v: i18n.t('resourcesManagement.OpenI') }, { k: 'C2Net', v: i18n.t('resourcesManagement.C2Net') }]; 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: 'OpenIChengdu', v: i18n.t('resourcesManagement.OpenIChengdu') }, { k: 'pclcci', v: i18n.t('resourcesManagement.pclcci') }, { k: 'hefei', v: i18n.t('resourcesManagement.hefeiCenter') }, { k: 'xuchang', v: i18n.t('resourcesManagement.xuchangCenter') }]; export const AI_CENTER = [{ k: 'OpenIOne', v: i18n.t('resourcesManagement.OpenIOne') }, { k: 'OpenITwo', v: i18n.t('resourcesManagement.OpenITwo') }, { k: 'OpenIChengdu', v: i18n.t('resourcesManagement.OpenIChengdu') }, { 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: 'CPU', v: 'CPU' }, { k: 'GPU', v: 'GPU' }, { k: 'NPU', v: 'NPU' }, { k: 'GCU', v: 'GCU' }, { k: 'MLU', v: 'MLU' }, { k: 'DCU', v: 'DCU' }, { k: 'ILUVATAR-GPGPU', v: 'ILUVATAR-GPGPU' }, { k: 'METAX-GPGPU', v: 'METAX-GPGPU' }]; export const COMPUTER_RESOURCES = [{ k: 'CPU', v: 'CPU' }, { k: 'GPU', v: 'GPU' }, { k: 'NPU', v: 'NPU' }, { k: 'GCU', v: 'GCU' }, { k: 'MLU', v: 'MLU' }, { k: 'DCU', v: 'DCU' }, { k: 'ILUVATAR-GPGPU', v: 'ILUVATAR-GPGPU' }, { k: 'METAX-GPGPU', v: 'METAX-GPGPU' }];
export const COMPUTER_RESOURCES_COLORS = {
'GPU': '#4fb62f',
'GCU': '#e73828',
'MLU': '#0077ed',
'ILUVATAR-GPGPU': '#0038bd',
'METAX-GPGPU': '#5c246a',
};
export const ACC_CARD_TYPE = [{ k: 'T4', v: 'T4' }, { k: 'A100', v: 'A100' }, { k: 'V100', v: 'V100' }, { k: 'ASCEND910', v: 'Ascend 910' }, { k: 'ASCEND-D910B', v: 'Ascend-D910B' }, { k: 'MLU270', v: 'MLU270' }, { k: 'MLU290', v: 'MLU290' }, { k: 'RTX3080', v: 'RTX3080' }, { k: 'ENFLAME-T20', v: 'ENFLAME-T20' }, { k: 'DCU', v: 'DCU' }, { k: 'BI-V100', v: 'BI-V100' }, { k: 'MR-V100', v: 'MR-V100' }, { k: 'N100', v: 'N100' }]; export const ACC_CARD_TYPE = [{ k: 'T4', v: 'T4' }, { k: 'A100', v: 'A100' }, { k: 'V100', v: 'V100' }, { k: 'ASCEND910', v: 'Ascend 910' }, { k: 'ASCEND-D910B', v: 'Ascend-D910B' }, { k: 'MLU270', v: 'MLU270' }, { k: 'MLU290', v: 'MLU290' }, { k: 'RTX3080', v: 'RTX3080' }, { k: 'ENFLAME-T20', v: 'ENFLAME-T20' }, { k: 'DCU', v: 'DCU' }, { k: 'BI-V100', v: 'BI-V100' }, { k: 'MR-V100', v: 'MR-V100' }, { k: 'N100', v: 'N100' }];
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') }]; 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') }];
export const NETWORK_TYPE = [{ k: 1, v: `${i18n.t('cloudbrainObj.networkType')}(${i18n.t('cloudbrainObj.noInternet')})` }, { k: 2, v: `${i18n.t('cloudbrainObj.networkType')}(${i18n.t('cloudbrainObj.hasInternet')})` }]; export const NETWORK_TYPE = [{ k: 1, v: `${i18n.t('cloudbrainObj.networkType')}(${i18n.t('cloudbrainObj.noInternet')})` }, { k: 2, v: `${i18n.t('cloudbrainObj.networkType')}(${i18n.t('cloudbrainObj.hasInternet')})` }];


+ 56
- 1
web_src/vuepages/langs/config/en-US.js View File

@@ -478,6 +478,61 @@ const en = {
codeUseDlgTriggerTxt: 'How to use datasets in OpenI', codeUseDlgTriggerTxt: 'How to use datasets in OpenI',
codeUseDlgTitle: 'How to use datasets on the OpenI', codeUseDlgTitle: 'How to use datasets on the OpenI',
}, },
imagesObj: {
cloudbrain_images: 'Cloud Brain Mirror',
openIGPU: 'OpenI GPU',
c2netGPU: 'C2Net GPU',
images_search: 'Search Mirror tag/description/operating system/python library/label...',
image_public: 'Recommend Mirror',
image_my: 'My Mirror',
image_collected: 'Favorite Mirror',
framework: 'Framework',
version: 'Version',
frameworkName: 'Framework name',
frameworkVersion: 'Framework version',
pyVersion: 'Python version',
cudaVersion: 'Cuda version',
deleteTips: 'Are you sure you want to delete this image? Once this image is deleted, it cannot be recovered.',
deleteSuccessTips: 'Successfully deleted',
editImage: 'Modify image',
submitImage: 'Submit image',
appyImage: 'Apply to become a platform recommended image',
imageTag: 'Image tag',
imageTagPlaceholder: 'Please enter the image tag',
imageTagInputTips: 'Please enter letters, numbers, _ and -, with a maximum length of 50 characters and starting with a letter.',
imageAdress: 'Image address',
imageAdressPlaceholder: 'Please enter the image adress',
imageAiCenterPlaceholder: 'Please enter the ai center. For example:\n[{"aiCenterId":"ai-center-id","imageUrl":"image-url","imageId":"image-id"}]',
operationSystem: 'Operating system',
operationSystemName: 'Operating system name',
operationSystemNamePlaceholder: 'Please enter the operating system name',
operationSystemVersionPlaceholder: 'Please enter the operating system version',
pyPackge: 'Python libraries',
thirdPackages: 'Python libraries and version',
thirdPackagesPlaceholder: `Please enter the installed Python library and version, one per line, up to 10 lines. For example:\ntorch==1.13.1\ntransformers==4.25.1`,
topic: 'Label',
topicPlaceholder: 'After entering, press enter to confirm the label',
topicTips: `Please provide additional information in the label field for the image, such as the compatible card type: <span class="light">T4</span>`,
descr: 'Image description',
descrPlaceholder: 'Please enter the image description, which should not exceed 1000 characters',
recommend: 'Recommend',
notRecommend: 'Unrecommend',
submitTips: 'The code directory /tmp/code, dataset directory /tmp/dataset will not be submitted with the image, and other directories will be packaged into the image.',
submitApply: 'Submit Apply',
imageCommitting: 'Image committing...',
imageCommitSuccess: 'Image committed successfully',
imageCommitErrorTips: 'Check whether the size of the submitted image exceeds 20G!',
committing: 'Commiting',
commitSuccess: 'Committed successfully',
commitFailed: 'Committed failed',
recommendNeedReview: 'Pending approval',
recommendReviewApproved: 'Approved',
recommendReviewFailed: 'Not approved',
applyForRecommend: 'Apply recommendation',
copyAdress: 'Copy adress',
filterImages: 'Filter images',
filterImagesPlaceholder: 'Framework name/Framework version/Python version/Cuda version',
},
specObj: { specObj: {
resSelectTips: 'The "resource specification" is the hardware you use to run the task. In order for more people to use the resources of this platform, please select according to your actual needs', resSelectTips: 'The "resource specification" is the hardware you use to run the task. In order for more people to use the resources of this platform, please select according to your actual needs',
no_use_resource: 'No resources available', no_use_resource: 'No resources available',
@@ -637,7 +692,7 @@ const en = {
recommendImage: 'Recommend Image', recommendImage: 'Recommend Image',
myImage: 'My Images', myImage: 'My Images',
myFavImage: 'My collected images', myFavImage: 'My collected images',
searchImagePlaceholder: 'Search image tag/description/label...',
searchImagePlaceholder: 'Search image tag/description/operating system/python library/label...',
useImage: 'Use', useImage: 'Use',
submitting: 'Commiting', submitting: 'Commiting',
submitFailed: 'Commit failed', submitFailed: 'Commit failed',


+ 56
- 1
web_src/vuepages/langs/config/zh-CN.js View File

@@ -494,6 +494,61 @@ const zh = {
codeUseDlgTriggerTxt: '在OpenI如何使用数据集', codeUseDlgTriggerTxt: '在OpenI如何使用数据集',
codeUseDlgTitle: '如何在OpenI协作平台使用数据集', codeUseDlgTitle: '如何在OpenI协作平台使用数据集',
}, },
imagesObj: {
cloudbrain_images: '云脑镜像',
openIGPU: '启智GPU',
c2netGPU: '智算GPU',
images_search: '搜镜像Tag/描述/操作系统/安装的软件包/标签',
image_public: '平台推荐镜像',
image_my: '我的镜像',
image_collected: '我收藏的镜像',
framework: '框架',
version: '版本',
frameworkName: '框架名称',
frameworkVersion: '框架版本',
pyVersion: 'Python版本',
cudaVersion: 'Cuda版本',
deleteTips: '你确认删除该镜像么?此镜像一旦删除不可恢复。',
deleteSuccessTips: '删除成功',
editImage: '修改镜像',
submitImage: '提交镜像',
appyImage: '申请成为平台推荐镜像',
imageTag: '镜像Tag',
imageTagPlaceholder: '请输入镜像Tag',
imageTagInputTips: '请输入字母、数字、_和-,最长50个字符,且以字母开头。',
imageAdress: '镜像地址',
imageAdressPlaceholder: '请输入镜像地址',
imageAiCenterPlaceholder: '请输入智算中心JSON格式,如:\n[{"aiCenterId":"ai-center-id","imageUrl":"image-url","imageId":"image-id"}]',
operationSystem: '操作系统',
operationSystemName: '操作系统名称',
operationSystemNamePlaceholder: '请输入操作系统名称',
operationSystemVersionPlaceholder: '请输入操作系统版本',
pyPackge: 'Python依赖库',
thirdPackages: 'Python依赖库及版本',
thirdPackagesPlaceholder: `请输入安装的软件包及版本,一行一个,最多10行。如:\ntorch==1.13.1\ntransformers==4.25.1`,
topic: '标签',
topicPlaceholder: '输入完成后回车键完成标签确定',
topicTips: `请在标签字段补充镜像中其他信息,如:适配的卡类型:<span class="light">T4</span>`,
descr: '镜像简介',
descrPlaceholder: '请输入镜像简介,不超过1000个字符',
recommend: '推荐',
notRecommend: '不推荐',
submitTips: '代码目录/tmp/code,数据集目录/tmp/dataset不会随镜像提交,其他目录都会打包到镜像中。',
submitApply: '提交申请',
imageCommitting: '镜像提交中...',
imageCommitSuccess: '镜像提交成功',
imageCommitErrorTips: '检测提交镜像是否大小超过20G!',
committing: '提交中',
commitSuccess: '提交成功',
commitFailed: '提交失败',
recommendNeedReview: '推荐待审核',
recommendReviewApproved: '通过推荐审核',
recommendReviewFailed: '未通过推荐审核',
applyForRecommend: '申请推荐',
copyAdress: '复制地址',
filterImages: '筛选镜像',
filterImagesPlaceholder: '框架名称/框架版本/Python版本/Cuda版本',
},
specObj: { specObj: {
resSelectTips: '「资源规格」是您运行该任务使用的硬件,为了更多人能够使用本平台的资源,请按照您的实际需求进行选择。', resSelectTips: '「资源规格」是您运行该任务使用的硬件,为了更多人能够使用本平台的资源,请按照您的实际需求进行选择。',
no_use_resource: '暂无可用资源' no_use_resource: '暂无可用资源'
@@ -652,7 +707,7 @@ const zh = {
recommendImage: '平台推荐镜像', recommendImage: '平台推荐镜像',
myImage: '我的镜像', myImage: '我的镜像',
myFavImage: '我收藏的镜像', myFavImage: '我收藏的镜像',
searchImagePlaceholder: '搜镜像Tag/描述/标签...',
searchImagePlaceholder: '搜镜像Tag/描述/操作系统/安装的软件包/标签...',
useImage: '使用', useImage: '使用',
submitting: '提交中', submitting: '提交中',
submitFailed: '提交失败', submitFailed: '提交失败',


+ 17
- 17
web_src/vuepages/pages/cloudbrain/configs.js View File

@@ -53,7 +53,7 @@ export const CreatePageConfigs = {
taskDescr: { required: false, }, taskDescr: { required: false, },
branchName: { required: true, }, branchName: { required: true, },
model: { required: false, multiple: true, useExceedSize: true }, model: { required: false, multiple: true, useExceedSize: true },
imagev1: { required: true },
imagev1: { required: true, type: -1 },
dataset: { required: false, type: 0, useExceedSize: true }, dataset: { required: false, type: 0, useExceedSize: true },
networkType: { required: true }, networkType: { required: true },
spec: { required: true }, spec: { required: true },
@@ -99,7 +99,7 @@ export const CreatePageConfigs = {
taskType: {}, taskType: {},
branchName: {}, branchName: {},
model: { required: false, multiple: true, useExceedSize: true }, model: { required: false, multiple: true, useExceedSize: true },
imagev1: { required: true, type: 2 },
imagev1: { required: true, type: -1 },
dataset: { required: false, type: 0, useExceedSize: true }, dataset: { required: false, type: 0, useExceedSize: true },
networkType: { required: true }, networkType: { required: true },
spec: {}, spec: {},
@@ -134,7 +134,7 @@ export const CreatePageConfigs = {
taskDescr: { required: false, }, taskDescr: { required: false, },
branchName: { required: true, }, branchName: { required: true, },
model: { required: false, multiple: true, useExceedSize: true }, model: { required: false, multiple: true, useExceedSize: true },
imagev2: { required: true, relatedSpec: true },
imagev1: { required: true, type: -1 },
dataset: { required: false, useExceedSize: true }, dataset: { required: false, useExceedSize: true },
networkType: { required: true }, networkType: { required: true },
spec: { required: true }, spec: { required: true },
@@ -154,7 +154,7 @@ export const CreatePageConfigs = {
taskDescr: { required: false, }, taskDescr: { required: false, },
branchName: { required: true, }, branchName: { required: true, },
model: { required: false, multiple: true, useExceedSize: true }, model: { required: false, multiple: true, useExceedSize: true },
imagev2: { required: true, relatedSpec: true },
imagev1: { required: true, type: -1 },
dataset: { required: false, useExceedSize: true }, dataset: { required: false, useExceedSize: true },
networkType: { required: true }, networkType: { required: true },
spec: { required: true }, spec: { required: true },
@@ -194,7 +194,7 @@ export const CreatePageConfigs = {
taskDescr: { required: false, }, taskDescr: { required: false, },
branchName: { required: true, }, branchName: { required: true, },
model: { required: false, multiple: true, useExceedSize: true }, model: { required: false, multiple: true, useExceedSize: true },
imagev2: { required: true, relatedSpec: true },
imagev1: { required: true, type: -1 },
dataset: { required: false, useExceedSize: true }, dataset: { required: false, useExceedSize: true },
networkType: { required: true }, networkType: { required: true },
spec: { required: true }, spec: { required: true },
@@ -214,7 +214,7 @@ export const CreatePageConfigs = {
taskDescr: { required: false, }, taskDescr: { required: false, },
branchName: { required: true, }, branchName: { required: true, },
model: { required: false, multiple: true, useExceedSize: true }, model: { required: false, multiple: true, useExceedSize: true },
imagev2: { required: true, relatedSpec: true },
imagev1: { required: true, type: -1 },
dataset: { required: false, useExceedSize: true }, dataset: { required: false, useExceedSize: true },
networkType: { required: true }, networkType: { required: true },
spec: { required: true }, spec: { required: true },
@@ -244,7 +244,7 @@ export const CreatePageConfigs = {
taskDescr: { required: false, }, taskDescr: { required: false, },
branchName: { required: true, }, branchName: { required: true, },
model: { required: false, multiple: true }, model: { required: false, multiple: true },
imagev1: { required: true },
imagev1: { required: true, type: -1 },
bootFile: { required: true, sampleUrl: 'https://openi.pcl.ac.cn/OpenIOSSG/MNIST_PytorchExample_GPU' }, bootFile: { required: true, sampleUrl: 'https://openi.pcl.ac.cn/OpenIOSSG/MNIST_PytorchExample_GPU' },
dataset: { required: true, type: 0 }, dataset: { required: true, type: 0 },
networkType: { required: true }, networkType: { required: true },
@@ -303,7 +303,7 @@ export const CreatePageConfigs = {
taskType: {}, taskType: {},
branchName: {}, branchName: {},
model: { multiple: true }, model: { multiple: true },
imagev1: { required: true, type: 2 },
imagev1: { required: true, type: -1 },
bootFile: { required: true, sampleUrl: 'https://openi.pcl.ac.cn/OpenIOSSG/OpenI_Cloudbrain_Example/src/branch/master/gpu_mnist_example/train.py' }, bootFile: { required: true, sampleUrl: 'https://openi.pcl.ac.cn/OpenIOSSG/OpenI_Cloudbrain_Example/src/branch/master/gpu_mnist_example/train.py' },
dataset: { required: true, type: 0 }, dataset: { required: true, type: 0 },
runParameters: { required: false }, runParameters: { required: false },
@@ -351,7 +351,7 @@ export const CreatePageConfigs = {
taskDescr: { required: false, }, taskDescr: { required: false, },
branchName: { required: true, }, branchName: { required: true, },
model: { required: false, multiple: true }, model: { required: false, multiple: true },
imagev2: { required: true, relatedSpec: true },
imagev1: { required: true, type: -1 },
bootFile: { required: true, sampleUrl: 'https://openi.pcl.ac.cn/OpenIOSSG/OpenI_Cloudbrain_Example/src/branch/master/gcu_mnist_example/train.py' }, bootFile: { required: true, sampleUrl: 'https://openi.pcl.ac.cn/OpenIOSSG/OpenI_Cloudbrain_Example/src/branch/master/gcu_mnist_example/train.py' },
dataset: { required: true }, dataset: { required: true },
runParameters: { required: false }, runParameters: { required: false },
@@ -375,7 +375,7 @@ export const CreatePageConfigs = {
taskDescr: { required: false, }, taskDescr: { required: false, },
branchName: { required: true, }, branchName: { required: true, },
model: { required: false, multiple: true }, model: { required: false, multiple: true },
imagev2: { required: true, relatedSpec: true },
imagev1: { required: true, type: -1 },
bootFile: { required: true, sampleUrl: '' }, bootFile: { required: true, sampleUrl: '' },
dataset: { required: true }, dataset: { required: true },
runParameters: { required: false }, runParameters: { required: false },
@@ -396,8 +396,8 @@ export const CreatePageConfigs = {
taskName: { required: true, }, taskName: { required: true, },
taskDescr: { required: false, }, taskDescr: { required: false, },
branchName: { required: true, }, branchName: { required: true, },
model: { required: false, multiple: false },
imagev2: { required: true, relatedSpec: true },
model: { required: false, multiple: true },
imagev1: { required: true, type: -1 },
bootFile: { required: true, sampleUrl: '' }, bootFile: { required: true, sampleUrl: '' },
dataset: { required: true }, dataset: { required: true },
runParameters: { required: false }, runParameters: { required: false },
@@ -458,7 +458,7 @@ export const CreatePageConfigs = {
taskType: {}, taskType: {},
branchName: {}, branchName: {},
model: { required: true, multiple: false }, model: { required: true, multiple: false },
imagev1: { required: true, type: 2 },
imagev1: { required: true, type: -1 },
bootFile: { required: true, sampleUrl: 'https://openi.pcl.ac.cn/OpenIOSSG/OpenI_Cloudbrain_Example/src/branch/master/gpu_mnist_example/inference.py' }, bootFile: { required: true, sampleUrl: 'https://openi.pcl.ac.cn/OpenIOSSG/OpenI_Cloudbrain_Example/src/branch/master/gpu_mnist_example/inference.py' },
dataset: { required: true, type: 0 }, dataset: { required: true, type: 0 },
runParameters: { required: false }, runParameters: { required: false },
@@ -481,7 +481,7 @@ export const CreatePageConfigs = {
taskDescr: { required: false, }, taskDescr: { required: false, },
branchName: { required: true, }, branchName: { required: true, },
model: { required: true, multiple: false }, model: { required: true, multiple: false },
imagev2: { required: true, relatedSpec: true },
imagev1: { required: true, type: -1 },
bootFile: { required: true, sampleUrl: 'https://openi.pcl.ac.cn/OpenIOSSG/OpenI_Cloudbrain_Example/src/branch/master/gpgpu_mnist_example/inference.py' }, bootFile: { required: true, sampleUrl: 'https://openi.pcl.ac.cn/OpenIOSSG/OpenI_Cloudbrain_Example/src/branch/master/gpgpu_mnist_example/inference.py' },
dataset: { required: true, type: 0 }, dataset: { required: true, type: 0 },
runParameters: { required: false }, runParameters: { required: false },
@@ -511,7 +511,7 @@ export const CreatePageConfigs = {
branchName: { required: true, }, branchName: { required: true, },
model: { required: false, }, model: { required: false, },
algBechmarkType: { required: true, }, algBechmarkType: { required: true, },
imagev1: { required: true, },
imagev1: { required: true, type: -1 },
networkType: { required: true }, networkType: { required: true },
spec: { required: true, }, spec: { required: true, },


@@ -581,7 +581,7 @@ export const CreatePageConfigs = {
branchName: {}, branchName: {},
bootFile: { required: true, sampleUrl: 'https://openi.pcl.ac.cn/OpenIOSSG/Online-Inference_Example' }, bootFile: { required: true, sampleUrl: 'https://openi.pcl.ac.cn/OpenIOSSG/Online-Inference_Example' },
model: { required: false, multiple: true }, model: { required: false, multiple: true },
imagev1: { required: true, type: 2 },
imagev1: { required: true, type: -1 },
dataset: { required: false, type: 0, useExceedSize: true }, dataset: { required: false, type: 0, useExceedSize: true },
networkType: { required: true }, networkType: { required: true },
spec: {}, spec: {},
@@ -1266,7 +1266,7 @@ export const DetailPageConfigs = {
branchName: { required: true, }, branchName: { required: true, },
model: { required: false, }, model: { required: false, },
algBechmarkType: { required: true, }, algBechmarkType: { required: true, },
imagev1: { required: true, },
imagev1: { required: true, type: -1 },
networkType: { required: true }, networkType: { required: true },
spec: { required: true, }, spec: { required: true, },




+ 13
- 4
web_src/vuepages/pages/cloudbrain/create/index.vue View File

@@ -44,9 +44,9 @@
<div class="line"></div> <div class="line"></div>
<div class="form-body-content"> <div class="form-body-content">
<div class="main-title params-setting">{{ $t('cloudbrainObj.paramsSetting') }}:</div> <div class="main-title params-setting">{{ $t('cloudbrainObj.paramsSetting') }}:</div>
<ImageSelectV1 ref="imagev1Ref" v-if="formCfg.imagev1" v-model="state.image_url"
:required="formCfg.imagev1.required"
:type="formCfg.imagev1.type != undefined ? formCfg.imagev1.type : 0">
<ImageSelectV1 ref="imagev1Ref" v-if="formCfg.imagev1" v-model="state.image_url" :configs="pageCfg"
:spec="state.spec" :required="formCfg.imagev1.required"
:type="formCfg.imagev1.type != undefined ? formCfg.imagev1.type : 0" @changeImage="changeImage">
</ImageSelectV1> </ImageSelectV1>
<ImageSelectV2 ref="imagev2Ref" v-if="formCfg.imagev2" v-model="state.image" :images="imageList" <ImageSelectV2 ref="imagev2Ref" v-if="formCfg.imagev2" v-model="state.image" :images="imageList"
:configs="pageCfg" :spec="state.spec" :networkType="state.networkType" @changeImages="changeImages" :configs="pageCfg" :spec="state.spec" :networkType="state.networkType" @changeImages="changeImages"
@@ -443,6 +443,15 @@ export default {
console.log(err); console.log(err);
}); });
}, },
changeImage() {
if (this.isModifyTask && this.modeifyTaskId && this.oldTask.image_url && this.state.spec == this.oldTask.spec.id
&& this.state.image_url == this.oldTask.image_url
) {
return;
} else {
this.state.image_url = '';
}
},
changeImages(images) { changeImages(images) {
this.imageList = images || []; this.imageList = images || [];
let image = this.imageList[0]; let image = this.imageList[0];
@@ -755,4 +764,4 @@ export default {
padding-right: 1em; padding-right: 1em;
} }
} }
</style>
</style>

+ 1
- 1
web_src/vuepages/pages/cloudbrain/tools.js View File

@@ -98,7 +98,7 @@ export class CloudBrainTools {
} else { } else {
task.canDelete = false; task.canDelete = false;
} }
if (task.compute_source == 'GPU' && task.job_type == 'DEBUG') {
if ((task.compute_source == 'GPU' || task.compute_source == 'GCU' || task.compute_source == 'MLU'|| task.compute_source == 'ILUVATAR-GPGPU'|| task.compute_source == 'METAX-GPGPU') && task.job_type == 'DEBUG') {
task.hasDebugMore = true; task.hasDebugMore = true;
if (task.canDebug) { if (task.canDebug) {
task.canSaveImage = true; task.canSaveImage = true;


+ 162
- 0
web_src/vuepages/pages/images/square/components/Condition.vue View File

@@ -0,0 +1,162 @@
<template>
<div class="condition-wrap">
<div>
<div class="tab-c">
<div class="tab-item" v-for="(item) in tabList" :class="conds.tab == item.key ? 'focus' : ''" :key="item.key"
@click="changeTab(item)">
{{ item.label }}
</div>
</div>
</div>
<div class="condition-b">
<div class="only-recommend-c"></div>
<div>
<el-dropdown class="sort-c" trigger="click" size="default">
<span class="el-dropdown-link">
{{ $t('datasets.sort') }}<i class="el-icon-caret-bottom el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :class="conds.sort == item.key ? 'active' : ''" v-for="item in sortList" :key="item.key"
@click.native="changeSort(item)">
{{ item.label }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</div>
</div>
</template>

<script>
import { i18n } from '~/langs';

const SortList = [{
key: '',
label: i18n.t('datasets.default'),
}, {
key: 'moststars',
label: i18n.t('datasets.moststars'),
}, {
key: 'mostused',
label: i18n.t('datasets.mostusecount'),
}, {
key: 'newest',
label: i18n.t('datasets.newest'),
}];

export default {
name: "Condition",
props: {
condition: { type: Object, default: () => ({}) },
},
components: {},
data() {
return {
isLogin: false,
tabList: [{
key: '0',
label: this.$t('imagesObj.image_public'),
}, {
key: '1',
label: this.$t('imagesObj.image_my'),
}, {
key: '2',
label: this.$t('imagesObj.image_collected'),
}],
sortList: [],
conds: {
tab: '0',
sort: '',
},
};
},
methods: {
changeTab(item) {
this.conds.tab = item.key;
if (item.key == '1') {
this.conds.sort = 'newest';
this.sortList = SortList.slice(1);
} else {
this.conds.sort = '';
this.sortList = SortList.slice(0);
}
this.$emit('changeCondition', {
tab: item.key,
sort: this.conds.sort,
});
},
changeSort(item) {
this.conds.sort = item.key;
this.$emit('changeCondition', {
sort: this.conds.sort,
});
}
},
watch: {
condition: {
handler(newVal) {
this.conds.tab = newVal.tab || '0';
this.conds.sort = newVal.sort || '';
this.sortList = SortList.slice(newVal.tab == '1' ? 1 : 0);
},
immediate: true,
deep: true,
},
},
beforeMount() {
this.isLogin = !!document.querySelector('meta[name="_uid"]');
if (!this.isLogin) {
this.tabList.splice(1, Infinity);
}
},
mounted() { },
};
</script>

<style scoped lang="less">
.condition-wrap {
margin: 10px 0 2px;

.tab-c {
display: flex;
align-items: center;

.tab-item {
margin-right: 32px;
padding: 4px 0;
text-align: center;
font-size: 14px;
color: rgb(65, 80, 88);
cursor: pointer;

&.focus {
font-weight: bold;
color: #3291f8;
font-weight: bold;
border-bottom: 2px solid;
}
}
}

.condition-b {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8px;

.only-recommend-c {
margin-right: 22px;
}

.sort-c {
color: rgba(0, 0, 0, 0.87);
cursor: pointer;
}
}
}

/deep/.el-dropdown-menu__item.active {
color: #409EFF;
background-color: rgba(179, 216, 255, 0.3);
}
</style>

+ 289
- 0
web_src/vuepages/pages/images/square/components/Filters.vue View File

@@ -0,0 +1,289 @@
<template>
<div>
<div class="top-title">{{ $t('imagesObj.cloudbrain_images') }}</div>
<div class="filter-main" v-show="!showSecond">
<div class="filter-c" v-for="(item, index) in mainData" :key="index"
v-show="item.key != 'framework_version' || item.data.length > 0">
<div class="filter-title">{{ item.title }}</div>
<div class="filter-item-c">
<div class="filter-item" v-for="(_item) in item.showData" @click="changeFilter(item, _item)" :key="_item.k"
:style="conds[item.key] == _item.k ?
{ backgroundColor: _item.focusBgColor || item.focusBgColor, color: _item.focusColor || item.focusColor }
: { backgroundColor: _item.bgColor || item.bgColor, color: _item.color || item.color }">
{{ _item.v }}
</div>
</div>
<div class="filter-view-more-c" v-if="item.data.length > item.showMaxLen">
<span class="filter-view-more" @click="goMore(item)">
<i class="el-icon-arrow-down"></i><span>{{ $t('expandMore') }}</span>
</span>
</div>
</div>
</div>
<div class="filter-second" v-show="showSecond">
<div class="filter-second-hd">
<span class="filter-second-go-back" @click="goBack()">
<i class="el-icon-back"></i><span>{{ $t('goBack') }}</span>
</span>
</div>
<div class="filter-title">{{ secondData.title }}</div>
<div class="filter-search">
<el-input :placeholder="$t('repos.search')" prefix-icon="el-icon-search" v-model="searchKeyword" clearable
@input="searchFilterItem">
</el-input>
</div>
<div class="filter-item-c">
<div class="filter-item" v-for="(_item) in secondData.secondShowData" :key="_item.k"
@click="changeFilter(secondData, _item)" :style="conds[secondData.key] == _item.k ?
{ backgroundColor: _item.focusBgColor || item.focusBgColor, color: _item.focusColor || item.focusColor }
: { backgroundColor: _item.bgColor || item.bgColor, color: _item.color || item.color }">
{{ _item.v }}
</div>
</div>
</div>
</div>
</template>

<script>
import { getStaticFile } from '~/apis/modules/common';
import { COMPUTER_RESOURCES_COLORS } from '~/const';

export default {
name: "Filters",
props: {
condition: { type: Object, default: () => ({}) },
},
components: {},
data() {
return {
mainData: [{
title: this.$t('cloudbrainObj.computeResource'),
key: 'compute_resource',
bgColor: 'rgb(248, 249, 250)',
color: 'rgb(65, 80, 88)',
focusBgColor: 'rgb(3, 102, 214)',
focusColor: 'rgb(255, 255, 255)',
data: [],
showData: [],
showMaxLen: 10,
}, {
title: this.$t('imagesObj.framework'),
key: 'framework',
bgColor: 'rgb(248, 249, 250)',
color: 'rgb(65, 80, 88)',
focusBgColor: 'rgb(3, 102, 214)',
focusColor: 'rgb(255, 255, 255)',
data: [],
showData: [],
showMaxLen: 10,
}, {
title: this.$t('imagesObj.frameworkVersion'),
key: 'framework_version',
bgColor: 'rgb(248, 249, 250)',
color: 'rgb(65, 80, 88)',
focusBgColor: 'rgb(3, 102, 214)',
focusColor: 'rgb(255, 255, 255)',
data: [],
showData: [],
showMaxLen: 20,
}, {
title: this.$t('imagesObj.pyVersion'),
key: 'python',
bgColor: 'rgb(248, 249, 250)',
color: 'rgb(65, 80, 88)',
focusBgColor: 'rgb(3, 102, 214)',
focusColor: 'rgb(255, 255, 255)',
data: [],
showData: [],
showMaxLen: 20,
}, {
title: this.$t('imagesObj.cudaVersion'),
key: 'cuda',
bgColor: 'rgb(248, 249, 250)',
color: 'rgb(65, 80, 88)',
focusBgColor: 'rgb(3, 102, 214)',
focusColor: 'rgb(255, 255, 255)',
data: [],
showData: [],
showMaxLen: 20,
}],
filterData: {},
conds: {
compute_resource: '',
framework: '',
framework_version: '',
python: '',
cuda: '',
},
showSecond: false,
secondData: {},
searchKeyword: '',
};
},
methods: {
goMore(item) {
this.secondData = item;
this.searchKeyword = '';
this.secondData.secondShowData = item.data;
this.showSecond = true;
},
goBack() {
this.showSecond = false;
},
changeFilter(item, _item) {
const value = this.conds[item.key] == _item.k ? '' : _item.k;
const changes = {
[item.key]: value,
};
if (item.key == 'framework') {
const frameworkVersionObj = this.mainData.find(itm => itm.key == 'framework_version');
changes['framework_version'] = '';
frameworkVersionObj.data = value ? ((this.filterData['framework_version'] || {})[value] || []).map(item => ({ k: item, v: item })) : [];
frameworkVersionObj.showData = frameworkVersionObj.data.slice(0, frameworkVersionObj.showMaxLen);
}
this.$emit('changeCondition', changes);
},
searchFilterItem() {
const keyword = this.searchKeyword.trim().toLocaleLowerCase();
this.secondData.secondShowData = this.secondData.data.filter(item => {
return item.v.toString().toLocaleLowerCase().indexOf(keyword) >= 0;
});
},
},
watch: {
condition: {
handler(newVal) {
this.conds.compute_resource = newVal.compute_resource || '';
this.conds.framework = newVal.framework || '';
this.conds.framework_version = newVal.framework_version || '';
const frameworkVersionObj = this.mainData.find(itm => itm.key == 'framework_version');
frameworkVersionObj.data = this.conds.framework ? ((this.filterData['framework_version'] || {})[this.conds.framework] || []).map(item => ({ k: item, v: item })) : [];
frameworkVersionObj.showData = frameworkVersionObj.data.slice(0, frameworkVersionObj.showMaxLen);
this.conds.python = newVal.python || '';
this.conds.cuda = newVal.cuda || '';
},
immediate: true,
deep: true,
},
},
beforeMount() {
getStaticFile('/images_version.json').then(res => {
const data = res.data;
if (data) {
this.filterData = data;
for (let i = 0, iLen = this.mainData.length; i < iLen; i++) {
const filterItem = this.mainData[i];
const key = filterItem.key;
const max = filterItem.showMaxLen;
if (data[key] && key != 'framework_version') {
if (key == 'compute_resource') {
filterItem.data = data[key].map(item => ({
k: item,
v: this.$t('computeResourceTitle.' + item) || item,
focusBgColor: COMPUTER_RESOURCES_COLORS[item],
}));
} else {
filterItem.data = data[key].map(item => ({ k: item, v: item }));
}
if (filterItem.sortOr) {
filterItem.data = filterItem.data.sort((a, b) => a.v.localeCompare(b.v));
}
filterItem.showData = filterItem.data.slice(0, max);
}
}
}
}).catch(err => {
console.log(err);
});
},
mounted() { },
};
</script>

<style scoped lang="less">
.top-title {
font-size: 24px;
color: #101010;
height: 40px;
line-height: 40px;
margin: 10px 0 20px;
}

.filter-c {
margin-bottom: 32px;

.filter-title {
font-size: 18px;
color: rgba(50, 145, 248, 1);
margin: 14px 0;
}

.filter-item-c {
display: flex;
flex-wrap: wrap;

.filter-item {
border-radius: 3px;
padding: 3px 10px;
margin-right: 10px;
margin-bottom: 10px;
cursor: pointer;
}
}

.filter-view-more-c {
margin-top: 6px;

.filter-view-more {
color: rgb(50, 145, 248);
font-size: 14px;
cursor: pointer;

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

.filter-second {
.filter-second-hd {
margin: 14px 0 24px 0;

.filter-second-go-back {
color: rgb(50, 145, 248);
font-size: 14px;
cursor: pointer;

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

.filter-title {
font-size: 18px;
color: rgba(50, 145, 248, 1);
margin: 14px 0;
}

.filter-search {
margin: 5px 10px 10px 0;
}

.filter-item-c {
display: flex;
flex-wrap: wrap;
max-height: 605px;
overflow-y: auto;

.filter-item {
border-radius: 3px;
padding: 3px 10px;
margin-right: 10px;
margin-bottom: 10px;
cursor: pointer;
}
}
}
</style>

+ 487
- 0
web_src/vuepages/pages/images/square/components/Item.vue View File

@@ -0,0 +1,487 @@
<template>
<div class="item">
<div class="item-top">
<div class="title-c">
<div class="title">
<span>
<span :title="data.tag">{{ data.tag }}</span>
</span>
<img v-show="data.type == 5" src="/img/jian.svg" style="margin-left:4px">
</div>
<div class="fav-c" :class="condition.tab == 1 ? 'fav-disabled' : ''" @click.prevent.stop="changeFav(data)">
<i v-if="!isStar" class="heart outline icon" :title="$t('star')"></i>
<i v-if="isStar" class="heart icon" :title="$t('unStar')"></i>
<span>{{ numStars }}</span>
</div>
</div>
<div class="labels">
<a class="label label-compute-resource"
:style="data.computeResourceColor ? `background-color: ${data.computeResourceColor};` : ''">{{
data.computeResourceShow }}</a>
<a v-for="(item, index) in data.topics" :key="index" class="label">{{ item }}</a>
</div>
<div class="content-row">
<div class="content-row-item half">
<div>{{ $t('imagesObj.framework') }}:</div>
<div>{{ data.framework }} {{ data.frameworkVersion }}</div>
</div>
<div class="content-row-item half">
<div>{{ $t('imagesObj.pyVersion') }}:</div>
<div v-show="data.pythonVersion">Python {{ data.pythonVersion }}</div>
</div>
</div>
<div class="content-row">
<div class="content-row-item half">
<div>{{ $t('imagesObj.cudaVersion') }}:</div>
<div v-if="data.compute_resource != 'GPU'">--</div>
<div v-else>
<span v-show="data.cudaVersion">Cuda {{ data.cudaVersion }}</span>
</div>
</div>
<div class="content-row-item half">
<div>{{ $t('imagesObj.operationSystem') }}:</div>
<div>{{ data.operationSystem }} {{ data.operationSystemVersion }}</div>
</div>
</div>
<div class="content-row">
<div class="content-row-item">
<div>{{ $t('imagesObj.pyPackge') }}:</div>
<div>{{ data.thirdPackagesShow }}</div>
</div>
</div>
<div class="content-row">
<div class="content-row-item">
<div>{{ $t('imagesObj.descr') }}:</div>
<div class="descr" :title="data.description"> {{ data.description }} </div>
</div>
</div>
</div>
<div class="footer">
<div class="footer-l">
<a v-if="data.userName" :href="`/${data.userName}`" class="avatar-c" :title="data.userName">
<img class="avatar" :src="data.relAvatarLink">
</a>
<a v-else href="javascript:;" class="avatar-c">
<img class="avatar" src="/user/avatar/Ghost/-1">
</a>
<span style="margin-right:10px;" :title="$t('modelManage.createTime')"> {{ data.createTimeStr }} </span>
<span style="margin-right:10px;" :title="$t('datasets.citations')">
<i class="el-icon-link"></i>
<span style="color:rgba(16, 16, 16, 0.9);">{{ data.useCount }}</span>
</span>
<span class="status" style="margin-right:10px;" v-if="condition.tab == 1">
<el-tooltip v-if="data.status == 0" effect="dark" :content="$t('imagesObj.imageCommitting')" placement="top">
<i class="CREATING" style="margin-right:3px"></i>
</el-tooltip>
<el-tooltip v-if="data.status == 1" effect="dark" :content="$t('imagesObj.imageCommitSuccess')" placement="top">
<i class="SUCCEEDED" style="margin-right:3px"></i>
</el-tooltip>
<el-tooltip v-if="data.status == 2" effect="dark" :content="$t('imagesObj.imageCommitErrorTips')"
placement="top">
<i class="FAILED" style="margin-right:3px"></i>
</el-tooltip>
<span v-if="data.status === 0">{{ $t('imagesObj.committing') }}</span>
<span v-if="data.status === 1">{{ $t('imagesObj.commitSuccess') }}</span>
<span v-if="data.status === 2">{{ $t('imagesObj.commitFailed') }}</span>
</span>
<span class="apply-status">
<div v-if="data.status === 1">
<div v-if="data.apply_status === 2" style="display: flex;align-items:center;justify-content:center;">
<el-tooltip effect="dark" :content="$t('imagesObj.recommendNeedReview')" placement="top">
<i class="CLOCK" style="margin-right:3px"></i>
</el-tooltip>
<span style="color: rgb(250, 140, 22);">{{ $t('imagesObj.recommendNeedReview') }}</span>
</div>
<div v-if="data.apply_status === 3" style="display: flex;align-items:center;justify-content:center;">
<el-tooltip effect="dark" :content="$t('imagesObj.recommendReviewApproved')" placement="top">
<i class="SUCCEEDED" style="margin-right:3px"></i>
</el-tooltip>
<span style="color: rgb(19, 194, 141);">{{ $t('imagesObj.recommendReviewApproved') }}</span>
</div>
<div v-if="data.apply_status === 4" style="display: flex;align-items:center;justify-content:center;">
<el-tooltip effect="dark" :content="data.message" placement="top">
<i class="FAILED" style="margin-right:3px"></i>
</el-tooltip>
<span style="color: red">{{ $t('imagesObj.recommendReviewFailed') }}</span>
</div>
</div>
</span>
</div>
<div class="footer-r" v-if="data.status != 0">
<a class="btn-op" href="javascript:void(0);" v-if="data.place" @click="copy(data)">
<i class="el-icon-document-copy"></i>
<span>{{ $t('imagesObj.copyAdress') }}</span>
</a>
<a v-if="condition.tab == 1 && (data.apply_status == 0 || data.apply_status == 1 || data.apply_status === 4) && data.type != 5"
class="btn-op" href="javascript:void(0);" @click="apply(data)">
<i class="ri-rocket-2-line" style="margin-right:0;"></i>
<span>{{ $t('imagesObj.applyForRecommend') }}</span>
</a>
<a v-if="condition.tab == 1" class="btn-op" href="javascript:void(0);" @click="edit(data)">
<i class="el-icon-edit-outline"></i>
<span>{{ $t('modelManage.edit') }}</span>
</a>
<a v-if="condition.tab == 1" class="btn-op delete" href="javascript:void(0);" @click="deleteImage(data)">
<i class="el-icon-delete"></i>
<span>{{ $t('cloudbrainObj.delete') }}</span>
</a>
</div>
</div>
</div>
</template>

<script>
import { putImageAction, deleteImage, getImageById } from '~/apis/modules/images';

export default {
name: "Item",
props: {
condition: { type: Object, default: () => ({}) },
data: { type: Object, default: () => ({}) },
},
components: {},
data() {
return {
isStar: false,
numStars: 0,
isSetting: false,
hasOnlineUrl: 0,
refreshTimer: null,
};
},
watch: {
data: {
handler(newVal) {
this.isStar = newVal.isStar;
this.numStars = newVal.numStars;
this.refreshStatus();
},
immediate: true,
deep: true,
},
},
methods: {
changeFav(item) {
if (this.condition.tab == 1) return;
if (this.isSetting) return;
this.isSetting = true;
putImageAction({
id: item.id,
action: this.isStar ? 'unstar' : 'star',
}).then(res => {
this.isSetting = false;
if (res.data.Code == '0') {
this.isStar = !this.isStar;
this.numStars = this.numStars + (this.isStar ? 1 : -1);
this.$message.success(this.isStar ? this.$t('datasets.starSuccess') : this.$t('datasets.unstarSuccess'));
this.$emit('changeImage');
} else if (res.data.code == '401') {
window.location.href = `/user/login?redirect_to=${encodeURIComponent(window.location.href)}`;
} else {
this.$message.error(res.data.msg);
}
}).catch(err => {
console.log(err);
this.isSetting = false;
if (err.request.responseURL.indexOf('/user/login') >= 0) {
window.location.href = `/user/login?redirect_to=${encodeURIComponent(window.location.href)}`;
} else {
this.$message.error(err);
}
});
},
copy(item) {
const tInput = document.createElement("input");
tInput.value = item.place;
document.body.appendChild(tInput);
tInput.select();
document.execCommand("Copy");
tInput.remove();
this.$message({
type: 'success',
message: this.$t('copySuccess'),
});
},
refreshStatus() {
this.refreshTimer && clearInterval(this.refreshTimer);
this.refreshTimer = setInterval(() => {
if (this.data.status != 0) {
this.refreshTimer && clearInterval(this.refreshTimer);
return;
}
getImageById({ id: this.data.id }).then(res => {
res = res.data;
if (res.id == this.data.id) {
const updateData = { ...res };
delete updateData['userName'];
delete updateData['relAvatarLink'];
this.$emit('refreshImage', updateData);
}
}).catch(err => {
console.log(err);
});
}, 5 * 1000);
},
deleteImage(item) {
if (this.condition.tab != 1) return;
if (this.isSetting) return;
this.$confirm(this.$t('imagesObj.deleteTips'), this.$t('tips'), {
confirmButtonText: this.$t('confirm1'),
cancelButtonText: this.$t('cancel'),
type: 'warning',
lockScroll: false,
}).then(() => {
this.isSetting = true;
deleteImage({ id: item.id }).then(res => {
this.isSetting = false;
res = res.data;
if (res.Code == '0') {
this.$message({
type: 'success',
message: this.$t('imagesObj.deleteSuccessTips'),
});
this.$emit('changeImage');
} else {
this.$message({
type: 'error',
message: res.Message,
});
}
}).catch(err => {
this.isSetting = false;
console.log(err);
this.$message({
type: 'error',
message: this.$t('operationFailed'),
});
});
}).catch(() => { });
},
apply(item) {
location.href = `/image/${item.id}/apply`;
},
edit(item) {
location.href = `/image/${item.id}/imageSquare`;
}
},
beforeMount() {
this.isStar = this.data.isStar;
this.numStars = this.data.numStars;
},
mounted() {
this.data.status == 0 && this.refreshStatus();
},
beforeDestroy() {
this.refreshTimer && clearInterval(this.refreshTimer);
}
};
</script>

<style scoped lang="less">
.item {
border-color: rgb(232, 232, 232);
border-width: 1px;
border-style: solid;
border-radius: 5px;
box-shadow: rgba(232, 232, 232, 0.6) 0px 4px 4px 0px;
overflow: hidden;
padding: 15px;
background: transparent;
display: flex;
justify-content: space-between;
flex-direction: column;
height: 100%;

&:hover {
border-color: rgba(204, 204, 255, 0.6);
box-shadow: rgba(204, 204, 255, 0.6) 0px 4px 8px 0px;
background: url("data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20version%3D%221.1%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%221%22%20x1%3D%220%22%20x2%3D%221%22%20y1%3D%220%22%20y2%3D%220%22%20gradientTransform%3D%22matrix(-0.34000000000000014%2C%20-0.643%2C%200.07087403200000002%2C%20-0.34000000000000014%2C%200.935%2C%200.997)%22%3E%3Cstop%20stop-color%3D%22%23ccfff4%22%20stop-opacity%3D%220.6%22%20offset%3D%220.01%22%3E%3C%2Fstop%3E%3Cstop%20stop-color%3D%22%23ece2ff%22%20stop-opacity%3D%220%22%20offset%3D%221%22%3E%3C%2Fstop%3E%3C%2FlinearGradient%3E%3C%2Fdefs%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22url(%231)%22%3E%3C%2Frect%3E%3C%2Fsvg%3E");

.title span {
color: rgba(125, 3, 214, 1)
}
}
}

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

.title {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: flex;
align-items: center;
color: rgb(3, 102, 214);

a {
max-width: calc(100% - 40px);
overflow: hidden;
text-overflow: ellipsis;

span {
font-weight: 500;
font-size: 16px;
color: rgb(3, 102, 214);
}
}
}
}

.fav-c {
display: flex;
font-size: 12px;

i {
color: rgb(250, 140, 22);
cursor: pointer;
}

span {
color: rgb(16, 16, 16);
}

&.fav-disabled {
i {
cursor: default;
}
}
}

.content-row {
display: flex;
align-items: center;
font-size: 12px;
color: rgb(136, 136, 136);
margin-top: 4px;

.content-row-item {
display: flex;
min-width: 100%;

&.half {
min-width: 50%;
}

div:nth-child(1) {
width: 90px;
text-align: right;
}

div:nth-child(2) {
flex: 1;
width: 0;
color: rgb(16, 16, 16)
}
}
}

.descr {
font-size: 12px;
font-weight: 300;
color: rgb(136, 136, 136);
text-overflow: ellipsis;
word-break: break-all;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
max-height: 41px;
overflow: hidden;
}

.labels {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-top: 8px;

.label {
color: rgba(16, 16, 16, 0.8);
border-radius: 4px;
font-size: 12px;
background: rgba(232, 232, 232, 0.6);
padding: 2px 6px;
margin-right: 8px;
cursor: default;
}

.label-compute-resource {
color: white;
}
}

.footer {
margin-top: 12px;
display: flex;
align-items: center;
justify-content: space-between;
height: 20px;
font-size: 12px;

.footer-l {
font-size: 12px;
font-weight: 400;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: rgba(136, 136, 136, 1);
display: flex;
align-items: center;

.avatar-c {
margin-right: 4px;
display: inline-block;
height: 24px;

.avatar {
display: inline-block;
width: 24px;
height: 24px;
border-radius: 100%;
}
}

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

.apply-status {
display: flex;
align-items: center;
}
}

.footer-r {
display: flex;
justify-content: end;
font-size: 12px;
font-weight: 300;
color: rgb(136, 136, 136);

.btn-op {
margin-left: 12px;
display: flex;
align-items: center;

i {
margin-right: 2px;
}

&.delete {
color: rgb(255, 37, 37);
}
}
}
}

:lang(en-US) .content-row {
.content-row-item {
div:nth-child(1) {
width: 112px;
}
}
}
</style>

+ 178
- 0
web_src/vuepages/pages/images/square/components/List.vue View File

@@ -0,0 +1,178 @@
<template>
<div class="list-container">
<div class="list-item-container" v-loading="loading">
<div class="item-container" v-for="(item) in list" :key="item.ID">
<ImageItem :data="item" :condition="condition" @changeImage="changeImage" @refreshImage="refreshImage">
</ImageItem>
</div>
<div v-show="(!list.length && !loading)" class="no-data">
<div class="item-empty">
<div class="item-empty-icon"></div>
<div class="item-empty-tips">{{ $t('modelObj.model_square_empty') }}</div>
</div>
</div>
</div>
<div class="center" v-show="list.length">
<el-pagination ref="paginationRef" background @current-change="currentChange" @size-change="sizeChange"
:current-page.sync="iPage" :page-sizes="iPageSizes" :page-size.sync="iPageSize"
layout="total, sizes, prev, pager, next, jumper" :total="total">
</el-pagination>
</div>
</div>
</template>

<script>
import ImageItem from './Item.vue';
import { getImages } from '~/apis/modules/images';
import { formatDate } from 'element-ui/lib/utils/date-util';
import { COMPUTER_RESOURCES_COLORS } from '~/const';

export default {
name: "List",
props: {
condition: { type: Object, default: () => ({}) },
},
components: { ImageItem },
data() {
return {
loading: false,
list: [],
iPageSizes: [15],
iPageSize: 15,
iPage: 1,
total: 0,
};
},
methods: {
getListData() {
this.loading = true;
const params = {
q: this.condition.q,
type: this.condition.tab,
sort: this.condition.sort,
computeResource: this.condition.compute_resource,
framework: this.condition.framework,
frameworkVersion: this.condition.framework_version,
python: this.condition.python,
cuda: this.condition.cuda,
page: this.condition.page,
pageSize: this.condition.pageSize,
};
getImages(params).then(res => {
res = res.data;
this.loading = false;
this.total = res.count || 0;
this.list = (res.images || []).map(item => {
const thirdPackagesList = [];
const thirdPackages = item.thirdPackages.split('\n');
thirdPackages.forEach(pkgLine => {
if (pkgLine) {
thirdPackagesList.push(pkgLine.trim().replace('==', ' '));
}
});
const compute_resource = item.compute_resource || 'GPU';
return {
...item,
computeResourceColor: COMPUTER_RESOURCES_COLORS[compute_resource],
computeResourceShow: this.$t('computeResourceTitle.' + compute_resource),
thirdPackagesShow: thirdPackagesList.join('; '),
createTimeStr: formatDate(new Date(item.createdUnix * 1000), 'yyyy-MM-dd HH:mm:ss'),
}
});
}).catch(err => {
console.log(err);
this.loading = false;
this.list = [];
this.total = 0;
});
},
search() {
this.getListData();
},
changeImage() {
this.getListData();
},
refreshImage(data) {
const find = this.list.find(item => item.id == data.id);
find && Object.assign(find, data);
},
currentChange(page) {
this.iPage = page;
this.$emit('changeCondition', {
page: this.iPage,
pageSize: this.iPageSize,
changePage: true,
});
},
sizeChange(pageSize) {
this.iPageSize = pageSize;
this.$emit('changeCondition', {
page: this.iPage,
pageSize: this.iPageSize,
});
},
},
watch: {
condition: {
handler(newVal) {
this.iPage = newVal.page;
this.iPageSize = newVal.pageSize;
},
immediate: true,
deep: true,
},
},
mounted() { },
};
</script>

<style scoped lang="less">
.list-container {
.list-item-container {
display: flex;
flex-wrap: wrap;

.item-container {
width: 100%;
padding: 12px 0;
}
}
}

.center {
text-align: center;
margin-top: 10px;
}

.no-data {
display: flex;
justify-content: center;
padding: 12px 0;
width: 100%;

.item-empty {
height: 391px;
width: 100%;
overflow: hidden;
padding: 15px;
background: transparent;
display: flex;
flex-direction: column;
justify-content: center;
background-color: rgba(245, 245, 246, 0.5);

.item-empty-icon {
height: 80px;
width: 100%;
background: url(/img/empty-box.svg) center center no-repeat;
}

.item-empty-tips {
text-align: center;
margin-top: 20px;
font-size: 18px;
color: rgb(63, 63, 64);
}
}
}
</style>

+ 99
- 0
web_src/vuepages/pages/images/square/index.vue View File

@@ -0,0 +1,99 @@
<template>
<div class="explore repositories">
<div class="repos--seach">
<div class="ui container">
<div class="search-bar-wrap ui two column centered grid">
<div class="search-bar fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty">
<div class="ui fluid action input">
<input type="text" v-model="condition.q" :placeholder="$t('imagesObj.images_search')" :autofocus="true"
@keyup.enter="conditionChange" />
<button class="ui green button" @click="conditionChange">{{ $t('repos.search') }}</button>
</div>
</div>
</div>
</div>
</div>
<div class="ui container">
<div class="content">
<div class="content-l">
<Filters :condition="condition" @changeCondition="conditionChange"></Filters>
</div>
<div class="content-r">
<Condition :condition="condition" @changeCondition="conditionChange"></Condition>
<List ref="imagesListRef" :condition="condition" @changeCondition="conditionChange"></List>
</div>
</div>
</div>
</div>
</template>

<script>
import Filters from './components/Filters.vue';
import Condition from './components/Condition.vue';
import List from './components/List.vue';
import { getUrlSearchParams } from '~/utils';

export default {
data() {
return {
condition: {
q: '',
tab: '0',
sort: '',
compute_resource: '',
framework: '',
framework_version: '',
python: '',
cuda: '',
page: 1,
pageSize: 15,
},
pageSizes: [15],
};
},
components: { Filters, Condition, List },
methods: {
conditionChange(params = {}) {
this.condition = {
...this.condition,
...params,
};
if (!params.changePage) {
this.condition.page = 1;
}
this.$nextTick(() => {
this.$refs.imagesListRef.search();
});
},
},
beforeMount() {
const urlParams = getUrlSearchParams();
if (urlParams.type == 'myimage') {
this.condition.tab = '1';
this.condition.sort = 'newest';
}
this.$nextTick(() => {
this.$refs.imagesListRef.search();
});
},
mounted() { },
beforeDestroy() { },
};
</script>

<style scoped lang="less">
.content {
display: flex;
margin-top: -6px;

.content-l {
flex: 1;
padding-left: 12px;
}

.content-r {
margin-left: 10px;
flex: 3;
}
}
</style>

+ 17
- 0
web_src/vuepages/pages/images/square/vp-images-square.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');

+ 716
- 0
web_src/vuepages/pages/images/submit/index.vue View File

@@ -0,0 +1,716 @@
<template>
<div>
<div class="ui container">
<div class="form-container" v-loading="submitLoading">
<div class="form-head">
<h4 v-if="pageType == 'edit'">{{ $t('imagesObj.editImage') }}</h4>
<h4 v-if="pageType == 'submit' || pageType == 'submitAdmin'">{{ $t('imagesObj.submitImage') }}</h4>
<h4 v-if="pageType == 'apply'">{{ $t('imagesObj.appyImage') }}</h4>
</div>
<div class="form-body">
<div class="form-body-content">
<div class="form-row form-row-cluster" v-show="false">
<div class="title align-items-center"><span class="required">{{ $t('modelManage.useCluster') }}</span>
</div>
<div class="content">
<div class="list">
<a class="item" href="javascript:;" v-for="item in typeList" :key="item.k" @click="changeType(item)"
:class="form.type == item.k ? 'focus' : ''">
<i class="icon ri-archive-drawer-line"></i>
<span>{{ item.v }}</span>
</a>
</div>
</div>
</div>
<div class="form-row">
<div class="title"><span class="required">{{ $t('imagesObj.imageTag') }}</span></div>
<div class="content" :class="errors.tag ? 'error' : ''">
<el-input class="field-input" :disabled="pageType != 'submit' && pageType != 'submitAdmin'"
v-model="form.tag" @blur="checkTag" @input="checkTag" :maxlength="50"
:placeholder="$t('imagesObj.imageTagPlaceholder')" :autofocus="true"></el-input>
<div class="tips">{{ $t('imagesObj.imageTagInputTips') }}</div>
</div>
</div>
<div class="form-row">
<div class="title"><span class="required">{{ $t('cloudbrainObj.computeResource') }}</span></div>
<div class="content">
<el-select class="field-input" v-model="form.compute_resource" :disabled="pageType != 'submitAdmin'"
@change="changeComputeResource">
<el-option v-for="item in computeResourceList" :key="item.k" :value="item.k"
:label="item.v"></el-option>
</el-select>
</div>
</div>
<div class="form-row" v-if="pageType == 'submitAdmin'">
<div class="title"><span :class="form.compute_resource == 'GPU' ? 'required' : ''">{{
$t('imagesObj.imageAdress') }}</span></div>
<div class="content" :class="errors.place ? 'error' : ''">
<el-input class="field-input" v-model="form.place" @blur="checkNull('place')"
@input="checkNull('place')" :maxlength="300"
:placeholder="$t('imagesObj.imageAdressPlaceholder')"></el-input>
</div>
</div>
<div class="form-row" v-if="pageType == 'submitAdmin' || (pageType == 'edit' && pageFrom == 'imageAdmin')">
<div class="title"><span>{{ $t('resourcesManagement.aiCenter') }}</span></div>
<div class="content">
<el-input class="field-input" type="textarea" v-model="form.aICenterImage" :maxlength="1500" :rows="3"
:placeholder="$t('imagesObj.imageAiCenterPlaceholder')"></el-input>
</div>
</div>
<div class="form-row">
<div class="left">
<div class="title"><span class="required">{{ $t('imagesObj.frameworkName') }}</span></div>
<div class="content" :class="errors.framework ? 'error' : ''">
<el-select class="field-input" v-model="form.framework" @change="changeFramework">
<el-option v-for="item in frameworkList" :key="item.k" :value="item.k" :label="item.v"></el-option>
</el-select>
</div>
</div>
<div class="right">
<div v-show="frameworkVersionList.length" class="title"><span class="required">{{
$t('imagesObj.version')
}}</span></div>
<div v-show="frameworkVersionList.length" class="content"
:class="errors.framework_version ? 'error' : ''">
<el-select class="field-input" v-model="form.framework_version"
@change="checkNull('framework_version')">
<el-option v-for="item in frameworkVersionList" :key="item.k" :value="item.k"
:label="item.v"></el-option>
</el-select>
</div>
</div>
</div>
<div class="form-row">
<div class="title"><span class="required">{{ $t('imagesObj.pyVersion') }}</span></div>
<div class="content" :class="errors.python ? 'error' : ''">
<el-select class="field-input" v-model="form.python" @change="checkNull('python')">
<el-option v-for="item in pythonList" :key="item.k" :value="item.k" :label="item.v"></el-option>
</el-select>
</div>
</div>
<div class="form-row" v-if="form.compute_resource == 'GPU'">
<div class="title"><span class="required">{{ $t('imagesObj.cudaVersion') }}</span></div>
<div class="content" :class="errors.cuda ? 'error' : ''">
<el-select class="field-input" v-model="form.cuda" @change="checkNull('cuda')">
<el-option v-for="item in cudaList" :key="item.k" :value="item.k" :label="item.v"></el-option>
</el-select>
</div>
</div>
<div class="form-row">
<div class="left">
<div class="title"><span>{{ $t('imagesObj.operationSystemName') }}</span></div>
<div class="content">
<el-input class="field-input" v-model="form.operationSystem" :maxlength="100"
:placeholder="$t('imagesObj.operationSystemNamePlaceholder')"></el-input>
</div>
</div>
<div class="right">
<div class="title"><span>{{ $t('imagesObj.version') }}</span></div>
<div class="content">
<el-input class="field-input" v-model="form.operationSystemVersion" :maxlength="50"
:placeholder="$t('imagesObj.operationSystemVersionPlaceholder')"></el-input>
</div>
</div>
</div>
<div class="form-row">
<div class="title"><span>{{ $t('imagesObj.thirdPackages') }}</span></div>
<div class="content">
<el-input class="field-input" type="textarea" v-model="form.thirdPackages" :rows="3"
@blur="checkThirdPackages" :placeholder="$t('imagesObj.thirdPackagesPlaceholder')"
:maxlength="1000"></el-input>
</div>
</div>
<div class="form-row">
<div class="title"><span>{{ $t('imagesObj.topic') }}</span></div>
<div class="content">
<el-select class="field-input topic-input" popper-class="popper-topic-input" v-model="form.topics"
remote :remote-method="topicRemoteMethod" :loading="topicLoading" multiple filterable allow-create
default-first-option clearable :placeholder="$t('imagesObj.topicPlaceholder')">
<el-option v-for="item in topicList" :key="item.k" :value="item.k" :label="item.v"></el-option>
</el-select>
<div class="tips tips-ext">
<i class="ri-error-warning-line"></i>
<span v-html="$t('imagesObj.topicTips')"></span>
</div>
</div>
</div>
<div class="form-row">
<div class="title"><span class="required">{{ $t('imagesObj.descr') }}</span></div>
<div class="content" :class="errors.description ? 'error' : ''">
<el-input class="field-input" type="textarea" v-model="form.description" :rows="3"
@blur="checkNull('description')" @input="checkNull('description')"
:placeholder="$t('imagesObj.descrPlaceholder')" :maxlength="1000"></el-input>
</div>
</div>
<div class="form-row" style="margin-top:-10px" v-if="pageType == 'submitAdmin'">
<div class="title"><span></span></div>
<div class="content" style="padding-top:8px">
<el-radio-group v-model="form.isRecommend">
<el-radio label="true">{{ $t('imagesObj.recommend') }}</el-radio>
<el-radio label="false">{{ $t('imagesObj.notRecommend') }}</el-radio>
</el-radio-group>
</div>
</div>
<div class="form-row" style="margin-top:-10px">
<div class="title"><span></span></div>
<div class="content">
<div class="tips tips-ext" style="font-size:14px;">
<i class="ri-error-warning-line"></i>
<span>
<span class="light" v-html="$t('imagesObj.submitTips')"></span>
</span>
</div>
</div>
</div>
<div class="form-row">
<div class="title"></div>
<div class="content">
<el-button type="primary" size="default" class="submit-btn" @click="submit">{{
pageType == 'apply' ? $t('imagesObj.submitApply') : $t('submit') }}</el-button>
<el-button class="cancel-btn" size="default" @click="cancel">{{ $t('cancel') }}</el-button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>

<script>
import { getStaticFile } from '~/apis/modules/common';
import { searchImageTopics, submitImage } from '~/apis/modules/images';
import { i18n } from '~/langs';

export default {
data() {
return {
pageType: window._PageType, // submit|submitAdmin|edit|apply
pageFrom: window._PageFrom, // submit|imageSquare|imageAdmin|apply
pageSubmitLink: window._PageSubmitLink,
pageRepoLink: window._PageRepoLink,
originData: {},
form: {
type: window._ImageType || '2', // 0启智GPU|2智算GPU
tag: '',
compute_resource: window._ImageComputeResource || 'GPU',
place: '',
aICenterImage: '',
framework: '',
framework_version: '',
python: '',
cuda: '',
operationSystem: '',
operationSystemVersion: '',
thirdPackages: '',
topics: [],
description: '',
isRecommend: 'true',
},
typeList: [{ k: '0', v: i18n.t('imagesObj.openIGPU') }, { k: '2', v: i18n.t('imagesObj.c2netGPU') }],
computeResourceList: [],
topicLoading: false,
topicList: [],
frameworkList: [],
frameworkVersionList: [],
pythonList: [],
cudaList: [],
condtionsData: {},
errors: {
tag: false,
place: false,
framework: false,
framework_version: false,
python: false,
cuda: false,
description: false,
},
submitLoading: false,
};
},
components: {},
methods: {
changeType(item) {
this.form.type = item.k;
},
checkTag() {
this.form.tag = this.form.tag.trim();
const reg = /^[a-zA-Z][\w|\-|\.]*$/;
this.errors.tag = !reg.test(this.form.tag);
return this.errors.tag;
},
checkNull(key) {
this.form[key] = this.form[key];
this.errors[key] = !this.form[key];
if (key == 'place' && this.form.compute_resource != 'GPU') {
this.errors[key] = false;
}
return this.errors[key];
},
checkThirdPackages() {
const last = this.form.thirdPackages.slice(-1) == '\n' ? '\n' : '';
const lines = this.form.thirdPackages.split('\n');
this.form.thirdPackages = lines.map(item => item.trim()).filter(item => item).slice(0, 10).join('\n') + last;
},
changeComputeResource() {
if (this.form.compute_resource == 'GPU') {
this.form.cuda = this.cudaList.length ? this.cudaList[0].k : '';
} else {
this.form.cuda = '';
this.errors.cuda = false;
this.errors.place = false;
}
},
changeFramework(framework) {
const versions = (this.condtionsData['framework_version'][framework] || []).map(item => {
return {
k: item,
v: item,
};
});
this.form.framework_version = '';
this.frameworkVersionList = versions;
if (versions.length) {
this.form.framework_version = versions[0].k;
} else {
this.form.framework_version = '';
}
this.checkNull('framework');
},
topicRemoteMethod(query) {
if (query !== '') {
this.topicLoading = true;
searchImageTopics({ q: query }).then(res => {
this.topicLoading = false;
res = res.data;
if (res.topics) {
this.topicList = [];
const find = res.topics.find(item => item.topic_name === query);
find && this.topicList.push({ k: find.topic_name, v: find.topic_name });
for (let i = 0, iLen = res.topics.length; i < iLen; i++) {
const topic = res.topics[i];
if (!find || topic.topic_name !== find.topic_name) {
this.topicList.push({
k: topic.topic_name,
v: topic.topic_name
});
}
}
} else {
this.topicList = [];
}
}).catch(err => {
this.topicLoading = false;
this.topicList = [];
console.log(err);
});
} else {
this.topicList = [];
}
},
submit() {
let hasError = false;
for (let key in this.errors) {
let isError = false;
if (this.pageType == 'submit' && key == 'place') continue;
if (key == 'place' && this.form.compute_resource != 'GPU') continue;
if (key == 'tag') {
isError = this.checkTag();
} else if (key == 'framework_version') {
if (this.frameworkVersionList.length) {
isError = this.checkNull(key);
}
} else if (key == 'cuda') {
if (this.form.compute_resource == 'GPU') {
isError = this.checkNull(key);
}
} else {
isError = this.checkNull(key);
}
if (isError) {
hasError = true;
}
}
if (hasError) return;
const subData = {
link: this.pageSubmitLink,
type: this.form.type,
tag: this.form.tag.trim(),
computeResource: this.form.compute_resource,
framework: this.form.framework,
frameworkVersion: this.form.framework_version,
cudaVersion: this.form.cuda,
pythonVersion: this.form.python,
operationSystem: this.form.operationSystem.trim(),
operationSystemVersion: this.form.operationSystemVersion.trim(),
thirdPackages: this.form.thirdPackages.trim(),
topics: this.form.topics.join(','),
description: this.form.description.trim(),
};
if (this.pageType == 'edit' || this.pageType == 'apply') {
subData.id = this.originData.id;
}
if (this.pageType == 'submitAdmin') {
subData.isRecommend = this.form.isRecommend;
subData.place = this.form.place;
}
if (this.pageType == 'submitAdmin' || (this.pageType == 'edit' && this.pageFrom == 'imageAdmin')) {
subData.aICenterImage = this.form.aICenterImage;
}
// console.log('submit subData', this.pageSubmitLink, this.pageRepoLink, subData);
// return;
this.submitLoading = true;
submitImage(subData).then(res => {
res = res.data;
if (res.Code == 0) {
this.$message({
type: 'success',
message: this.$t('submittedSuccessfully'),
});
setTimeout(() => {
this.cancel('success');
}, 200);
} else {
this.submitLoading = false;
this.$message({
type: 'error',
message: res.Message,
});
}
}).catch(err => {
this.submitLoading = false;
console.log(err);
this.$message({
type: 'error',
message: this.$t('submittedFailed'),
});
});
},
cancel(status) {
if (this.pageFrom == 'submit') {
if (status == 'success') {
location.href = `/explore/images?type=myimage`;
} else {
window.close();
}
} else if (this.pageFrom == 'imageSquare' || this.pageFrom == 'apply') {
location.href = `/explore/images?type=myimage`;
} else if (this.pageFrom == 'imageAdmin') {
location.href = `/admin/images` + window.location.search;
}
}
},
beforeMount() {
if (this.pageType == 'submit') {
this.typeList = this.typeList.filter(item => item.k == this.form.type);
}
if (this.pageType == 'submitAdmin') {
this.form.type = '2';
this.form.compute_resource = 'GPU';
this.typeList = this.typeList.filter(item => item.k == this.form.type);
}
if (this.pageType == 'edit' || this.pageType == 'apply') {
const image = window._Image || {};
this.originData = image;
this.form.type = image.cloudbrainType.toString();
this.typeList = this.typeList.filter(item => item.k == this.form.type);
this.form.tag = image.tag;
this.form.compute_resource = image.compute_resource || 'GPU';
this.form.place = image.place;
this.form.aICenterImage = JSON.stringify(image.AiCenterImages || []);
this.form.operationSystem = image.operationSystem;
this.form.operationSystemVersion = image.operationSystemVersion;
this.form.thirdPackages = image.thirdPackages;
this.form.topics = image.topics || [];
this.form.description = image.description;
}
getStaticFile('/images_version.json').then(res => {
const data = res.data;
if (data) {
this.condtionsData = data;
this.frameworkList = (data.framework || []).map(item => {
return { k: item, v: item };
});
this.form.framework = this.frameworkList.length ? this.frameworkList[0].k : '';
this.changeFramework(this.form.framework);
this.cudaList = (data.cuda || []).map(item => {
return { k: item, v: `Cuda ${item}` };
});
this.form.cuda = this.cudaList.length ? this.cudaList[0].k : '';
this.pythonList = (data.python || []).map(item => {
return { k: item, v: `Python ${item}` };
});
this.form.python = this.pythonList.length ? this.pythonList[0].k : '';
if (this.pageType == 'edit' || this.pageType == 'apply') {
const image = window._Image || {};
this.form.framework = image.framework;
this.changeFramework(this.form.framework);
this.form.framework_version = image.frameworkVersion;
this.form.cuda = image.cudaVersion;
this.form.python = image.pythonVersion;
}
this.computeResourceList = (data.compute_resource || []).map(item => {
return { k: item, v: this.$t('computeResourceTitle.' + item) || item };
});
}
}).catch(err => {
console.log(err);
});
},
mounted() { },
beforeDestroy() { },
};
</script>

<style scoped lang="less">
.form-container {
.form-head {
background: #f0f0f0;
border-radius: 0.28571429rem 0.28571429rem 0 0;
padding: 0.78571429rem 1rem;
margin: 0 -1px;
box-shadow: none;
border: 1px solid #d4d4d5;
}

.form-body {
min-height: 400px;
border: 1px solid #d4d4d5;
margin: -1px -1px;
padding: 3em;
background-color: #FFF;

.form-body-content {
max-width: 1200px;
margin: 0 auto;
padding-right: 120px;

.form-row {
display: flex;
margin-bottom: 28px;

.left {
display: flex;
width: 65%;
}

.right {
display: flex;
width: 35%;

.title {
width: 100px;
}
}

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

&.align-items-center {
padding-top: 0;
align-items: center;
}

.required {
position: relative;

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

.content {
flex: 1;

.field-input {
width: 100%;
}

.tips {
font-size: 12px;
color: rgba(136, 136, 136, 1);
margin-top: 10px;

&.tips-ext {
display: flex;
font-size: 12px;
align-items: center;

i {
color: #f2711c;
margin-right: 5px;
font-size: 14px;
}

/deep/.light,
.light {
color: #f2711c;
}
}
}

.field-input {
/deep/.el-input__inner {
height: 37.6px;
}

/deep/.el-input__inner,
/deep/.el-textarea__inner {
color: rgba(0, 0, 0, .95);
font-size: 14px;
line-height: 22px;

&:visited {
border-color: #85b7d9;
}

&:focus {
border-color: #85b7d9;
}

&:active {
border-color: #85b7d9;
}
}
}

.topic-input {

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

&.error {
.field-input {

/deep/.el-input__inner,
/deep/.el-textarea__inner {
color: #9f3a38;
background: #fff6f6;
border-color: #e0b4b4;

&:visited {
border-color: #e0b4b4;
}

&:focus {
border-color: #e0b4b4;
}

&:active {
border-color: #e0b4b4;
}
}
}
}

.list {
display: flex;
align-items: center;
flex-wrap: wrap;

.item {
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
color: rgba(0, 0, 0, .87);
border: 1px solid rgba(34, 36, 38, .15);
margin-left: -1px;
height: 38px;
padding: 0 12px;
border-left: none;
margin-bottom: 5px;

i {
margin-top: -7px;
font-size: 14px;
}

&.focus {
color: #0087f5;
border-color: #0087f5;
border-left: 1px solid #0087f5;
}

&:first-child {
border-top-left-radius: 0.28571429rem;
border-bottom-left-radius: 0.28571429rem;
border-left: 1px solid rgba(34, 36, 38, .15);

&.focus {
border-color: #0087f5;
}
}

&:last-child {
border-top-right-radius: 0.28571429rem;
border-bottom-right-radius: 0.28571429rem;
}

&:hover:not(.focus) {
background: rgba(0, 0, 0, .03);
}
}
}
}
}

.submit-btn {
background-color: #5bb973;
color: #fff;
border-color: #5bb973;

&:hover {
background-color: #16ab39;
}

&:active {
background-color: #198f35;
}

&.is-disabled,
&.is-disabled:active,
&.is-disabled:focus,
&.is-disabled:hover {
background-color: #5bb973;
opacity: .45;
}

a {
color: #fff;
font-weight: 700;
}
}

.cancel-btn {
background: #e0e1e2;
color: rgba(0, 0, 0, .6);
border-color: #e0e1e2;

&:hover {
background-color: #cacbcd;
}

&:active {
background-color: #babbbc;
}
}
}
}
}
</style>

+ 17
- 0
web_src/vuepages/pages/images/submit/vp-images-submit.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');

Loading…
Cancel
Save