#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 {
go func() {
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"
urlGetAiCenter = urlOpenApiV1 + "sharescreen/aicenter"
urlGetImages = urlOpenApiV1 + "image"
urlListUserImages = urlOpenApiV1 + "listUserImage"
urlNotebookJob = urlOpenApiV1 + "notebook"
urlInferenceJob = urlOpenApiV1 + "inference"

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

if result.ErrorCode != 0 {
log.Error("GetImages failed(%d): %s", result.ErrorCode, result.ErrorMsg)
@@ -360,6 +363,45 @@ sendjob:
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) {
checkSetting()
client := getRestyClient()


+ 16
- 11
models/cloudbrain.go View File

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

type CommitImageParams struct {
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 {
@@ -2038,17 +2046,14 @@ type GrampusResourceQueue struct {

}

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

type GetGrampusImagesResult struct {


+ 262
- 41
models/cloudbrain_image.go View File

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

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

"xorm.io/builder"

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

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

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
@@ -64,24 +100,43 @@ type ImageTopic struct {
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}

type ImageVersionList struct {
}

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

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
SearchOrderBy
}
@@ -113,12 +168,20 @@ type CommitImageGrampusParams struct {
}
type CommitGrampusImageParams struct {
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 {
@@ -231,6 +294,15 @@ func GetImageByTag(tag string) (*Image, error) {
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) {
image := &Image{Tag: tag}
has, err := x.
@@ -403,6 +475,35 @@ func removeTopicFromImage(e Engine, imageId int64, topic *ImageTopic) error {
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) {
cond := SearchImageCondition(opts)
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(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)

@@ -473,6 +581,48 @@ func SearchImageCondition(opts *SearchImageOptions) builder.Cond {
if opts.Status >= 0 {
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 {

@@ -579,7 +729,8 @@ func CreateLocalImage(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
}

@@ -591,7 +742,7 @@ func UpdateLocalImageStatus(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
}

@@ -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(UserBusinessAnalysisLastWeek),
new(UserLoginLog),
new(UserLoginActionLog),
new(UserOtherInfo),
new(UserMetrics),
new(UserAnalysisPara),


+ 11
- 0
models/user_analysis_for_activity.go View File

@@ -589,3 +589,14 @@ func QueryUserAnnualReport(userId int64) *UserSummaryCurrentYear {
}
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
}

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) {

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

LoginActionCountMap := queryLoginActionCount(start_unix, end_unix)

OpenIIndexMap := queryUserRepoOpenIIndex(startTime.Unix(), end_unix)
CloudBrainTaskMap, CloudBrainTaskItemMap, _ := queryCloudBrainTask(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.CreateRepoCount = getMapValue(dateRecordAll.ID, CreateRepoCountMap)
dateRecordAll.LoginCount = getMapValue(dateRecordAll.ID, LoginCountMap)
dateRecordAll.LoginActionCount = getMapValue(dateRecordAll.ID, LoginActionCountMap)

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

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

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, " +
"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"

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.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.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) {
insertBatchSql += ","
}
@@ -2222,6 +2208,40 @@ func queryLoginCount(start_unix int64, end_unix int64) map[int64]int {
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 {
sess := x.NewSession()
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"`
//repo
CreateRepoCount int `xorm:"NOT NULL DEFAULT 0"`
//login count, from elk
//login count
LoginCount int `xorm:"NOT NULL DEFAULT 0"`
//openi index
OpenIIndex float64 `xorm:"NOT NULL DEFAULT 0"`
@@ -69,6 +69,8 @@ type UserBusinessAnalysisCurrentYear struct {
Phone string `xorm:"NULL"`
InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"`
ModelConvertCount int `xorm:"NOT NULL DEFAULT 0"`

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

type UserBusinessAnalysis struct {
@@ -692,4 +708,6 @@ type UserBusinessAnalysis struct {
Phone string `xorm:"NULL"`
InvitationUserNum 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 {
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 {
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 {
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 {
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 {


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

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

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 {
@@ -367,6 +374,13 @@ sendjob:
dbImage.IsPrivate = params.IsPrivate
dbImage.Description = params.ImageDescription
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
if err := models.UpdateLocalImage(dbImage); err != nil {
log.Error("Failed to update image record.", err)
@@ -406,15 +420,23 @@ func CommitAdminImage(params models.CommitImageParams, doer *models.User) error
}

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 {


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

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

if ctx.User != nil {
ctx.IsSigned = true
toCache(ctx)
ctx.Data["IsSigned"] = ctx.IsSigned
ctx.Data["SignedUser"] = ctx.User
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"
urlGetAiCenter = urlOpenApiV1 + "sharescreen/aicenter"
urlGetImages = urlOpenApiV1 + "image"
urlDelImages = urlOpenApiV1 + "delimage"
urlNotebookJob = urlOpenApiV1 + "notebook"

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

retry := 0

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

sendjob:
_, err := client.R().
SetAuthToken(TOKEN).
@@ -299,6 +298,35 @@ sendjob:
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) {
checkSetting()
client := getRestyClient()
@@ -643,7 +671,7 @@ sendjob:
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)

dbImage, err := models.GetImageByTag(imageTag)
@@ -697,36 +725,60 @@ sendjob:
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{
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 {
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 {
log.Error("Failed to insert image record.", err)
return fmt.Errorf("CommitImage err: %s", res.String())
@@ -789,6 +841,9 @@ func updateImageStatus(image models.Image) {
commitSuccess = true
image.Status = models.IMAGE_STATUS_SUCCESS
image.Place = result.Image.ImageFullAddr
if image.AiCenterImages != nil {
image.AiCenterImages[0].ImageUrl = result.Image.ImageFullAddr
}
models.UpdateLocalImageStatusAndPlace(&image)
return
}
@@ -797,7 +852,6 @@ func updateImageStatus(image models.Image) {
commitSuccess = false
break
}

}
}
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"))
}

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

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


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

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

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_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.IGNORE_FLAG = sec.Key("IGNORE_FLAG").MustString("")
}

func getModelAppConfig() {


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

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

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

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

const (
@@ -122,6 +123,10 @@ func SyncGrampusQueue(ctx *context.Context) {
ctx.JSON(http.StatusOK, response.ServerError(err.Error()))
return
}
err = resource.SyncGrampusImage(ctx.User.ID)
if err != nil {
log.Error("sync image error. %v", err)
}
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("/star", reqToken(), repo.GetStarImages)
m.Get("/npu", reqToken(), repo.GetNpuImages)
m.Get("/availableFilter", reqToken(),
repo.GetAvailableFilerInfo)

})

@@ -821,6 +823,7 @@ func RegisterRoutes(m *macaron.Macaron) {
})
}, 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_week", operationReq, repo_ext.QueryUserMetricsCurrentWeek)
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.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("/cloudbrain/get_newest_job", repo.GetNewestJobs)
m.Get("/cloudbrain/get_center_info", repo.GetAICenterInfo)


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

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

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

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
@@ -74,12 +75,20 @@ func NewMultipart(ctx *context.APIContext) {
})
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()
defer mutex.Unlock()
@@ -159,13 +168,20 @@ func NewModelMultipart(ctx *context.APIContext) {
})
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()
defer modelMutex.Unlock()


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

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

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

@@ -73,6 +75,7 @@ func GetStarImages(ctx *context.APIContext) {
Topics: ctx.Query("topic"),
Status: models.IMAGE_STATUS_SUCCESS,
CloudbrainType: ctx.QueryInt("cloudbrainType"),
TrainType: "",
}
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) {

var versionInfos modelarts.VersionInfo


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

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

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/cloudbrain"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert"
"code.gitea.io/gitea/modules/grampus"
@@ -361,30 +362,25 @@ func QueryModelConvertResultFileList(ctx *context.APIContext, id string) ([]stor
return nil, err
}
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 {
var versionName = "V0001"
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/modelappservice"

userlogin "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/highlight"
code_indexer "code.gitea.io/gitea/modules/indexer/code"
@@ -78,6 +79,8 @@ func NewServices() {
log.Info("labelmsg.Init() succeed.")
modelappservice.Init()
log.Info("modelappservice.Init() succeed.")
userlogin.UserActionChannelInit()
log.Info("UserActionChannelInit succeed.")
InitESClient()
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")
if job.IsGpuTrainTask() {
filePath := "jobs/" + jobName + "/model/" + parentDir
if !strings.HasSuffix(filePath, fileName) {
filePath += fileName
}
url, err := storage.Attachments.PresignedGetURL(filePath, fileName)
if err != nil {
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.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 {
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")))
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)
if err != nil {
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,
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)
if err != nil {
log.Error("CommitImagefailed")
@@ -1347,10 +1370,18 @@ func CloudBrainCommitImage(ctx *context.Context, form auth.CommitImageCloudBrain
ImageDescription: form.Description,
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)
if err != nil {
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.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)
if err != nil {
@@ -1746,6 +1797,11 @@ func GetImages(ctx *context.Context, opts *models.SearchImageOptions) {
Images: []*models.Image{},
})
} else {
for _, tmp := range imageList {
if tmp.Place == "" {
tmp.Place = getImageUrl(tmp, opts.AiCenterId)
}
}
ctx.JSON(http.StatusOK, models.ImagesPageResult{
Count: total,
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 {
var uid int64 = -1
if ctx.IsSigned {
@@ -1764,13 +1835,20 @@ func getUID(ctx *context.Context) int64 {
func GetAllImages(ctx *context.Context) {
uid := getUID(ctx)
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") != "" {


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

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

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

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

// 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) {
ctx.Data["PageIsCloudBrain"] = true
ctx.HTML(http.StatusOK, tplGrampusInferenceNew)
ctx.HTML(http.StatusOK, tplGrampusInferenceNew)
}

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

func GrampusNotebookNew(ctx *context.Context) {
@@ -1961,13 +1962,20 @@ func GrampusNotebookRestart(ctx *context.Context) {
}
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) {
ctx.Data["PageIsCloudBrain"] = true
ctx.Data["Type"] = ctx.Cloudbrain.Type
if ctx.Cloudbrain.ComputeResource == models.NPUResource {
ctx.Error(http.StatusBadRequest, "unsupported compute resource type")
}
ctx.Data["ComputeResource"] = getImageComputeResource(ctx.Cloudbrain.ComputeResource)
ctx.HTML(200, tplGrampusNotebookGPUImageShow)
}

@@ -1989,16 +1997,24 @@ func GrampusCommitImage(ctx *context.Context, form auth.CommitImageGrampusForm)
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{
ImageName: setting.Grampus.GPUImageCommonName,
Description: form.Description,
ImageVersion: form.Tag,
TaskName: "task0",
}, 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)

if err != nil {


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

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

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

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

func TimingCountData() {
log.Info("start to time count data")
context.UserActionMapClear()
currentTimeNow := time.Now()
log.Info("current time:" + currentTimeNow.Format("2006-01-02 15:04:05"))
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)
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,
},
))
m.Use(public.CustomJson(
&public.Options{
SkipLogging: setting.DisableRouterLog,
ExpiresAfter: 0,
},
))
m.Use(public.StaticHandler(
setting.AvatarUploadPath,
&public.Options{
@@ -363,7 +369,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/all/search/", routers.Search)
m.Get("/all/search/", routers.EmptySearch)
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/annual_privacy", routers.HomeAnnual)
m.Get("/home/model_privacy", routers.HomeWenxin)
@@ -538,7 +544,6 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/unbind", authentication.UnbindWechat)
m.Get("/bind", authentication.GetBindPage)
}, reqSignIn)

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

m.Combo("/applications").Get(userSetting.Applications).
Post(bindIgnErr(auth.NewAccessTokenForm{}), userSetting.ApplicationsPost)
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")
UserName := ctx.Query("UserName")
Password := ctx.Query("Password")
log.Info("0000000")
log.Info("u=" + UserName)
orderedOAuth2Names, oauth2Providers, err := models.GetActiveOAuth2Providers()
if err != nil {
ctx.ServerError("UserSignIn", err)
@@ -327,6 +327,7 @@ func SignInPostAPI(ctx *context.Context) {
}
u, err := models.UserSignIn(UserName, Password)
if err != nil {
log.Info("login failed.UserName=" + UserName + " Password=" + Password)
ctx.ServerError("UserSignIn", err)
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)

// 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)



+ 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
}

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

processType := computeSource.FullName
log.Info("processType=" + computeSource.FullName + " jobType=" + string(jobType))
images, err := grampus.GetImages(processType, string(jobType))
if err != nil {
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 {
if image.ID == imageId {
for _, centerInfo := range image.AICenterImage {
imageCenterIds = append(imageCenterIds, centerInfo.AICenterID)
imageCenterIds = append(imageCenterIds, centerInfo.AiCenterId)
}
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 {
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

//如果是智算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.")
jobName := ctx.Request.JobName
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,
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 {
result.Images = images
result.Images = append(result.Images, images...)
result.CanUseAllImages = canUseAll
}
return result, nil


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

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

import (
"fmt"
"strings"

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

func AddResourceQueue(req models.ResourceQueueReq) error {
@@ -54,6 +56,69 @@ func GetResourceAiCenters() ([]models.ResourceAiCenterRes, error) {
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 {
r, err := grampus.GetResourceQueue()
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" .}}
<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">
{{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 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" .}}
<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" .}}

+ 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" .}}
<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">
{{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>
{{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" .}}
<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">
{{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>
{{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" .}}
<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">
{{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 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" .}}

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

@@ -104,12 +104,6 @@
<td class="t-center point">-</td>
<td class="t-center"><span class="typ">累计</span>积分获取上限<span class="limit"> - </span></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 key="TaskInviteFriendRegister">
<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
}
}

if needsNew {
// FIXME: actionId.
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)

// Check that the token matches the expected value.
// Use constant time comparison to avoid timing attacks.
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;">
<el-checkbox v-model="checked" style="padding: 0.5rem 1rem;">{{
$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;">
<span class="el-dropdown-link">
{{ 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 }">{{
$i18n['cloudeBrainMirror']['c2net'] }}</el-dropdown-item>
</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 @command="handleApplyState" trigger="click"
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">{{
$i18n['cloudeBrainMirror']['create_cloud_brain_mirror'] }}</a>
</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">
<div style="display: flex;align-items: center;">
<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;">
</div>
</template>
</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">
<template slot-scope="scope">
<div class="image_desc" :title="scope.row.description">{{ scope.row.description }}
@@ -101,20 +111,48 @@
</div>
</template>
</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">
{{ 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>
</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">
<a v-if="scope.row.userName || scope.row.relAvatarLink" :href="'/' + scope.row.userName"
:title="scope.row.userName">
@@ -126,13 +164,13 @@
</template>
</el-table-column>
<el-table-column prop="createdUnix" :label="$i18n['cloudeBrainMirror']['creation_time']" align="center"
min-width="11%">
width="160px">
<template slot-scope="scope">
{{ scope.row.createdUnix | transformTimestamp }}
</template>
</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">
<span v-if="scope.row.apply_status == 0" style="">{{ '--' }}</span>
<div v-if="scope.row.apply_status === 1"
@@ -168,7 +206,8 @@
</div>
</template>
</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">
<div style="display: flex;justify-content: center;align-items: center;">
<div style="display: flex;align-items: center;padding: 0 1rem;" :title="$i18n['citations']">
@@ -253,6 +292,7 @@ export default {
search: '',
dropdownPrivate: '',
dropdownType: '',
dropdownComputeResource: '',
dropdownApplyState: '',
checked: false,
currentPageCustom: 1,
@@ -389,6 +429,12 @@ export default {
this.paramsCustom.page = 1
this.getImageListCustom()
},
handleCommandComputeResource(command) {
this.dropdownComputeResource = command.label
this.paramsCustom.computeResource = command.type
this.paramsCustom.page = 1
this.getImageListCustom()
},
handleApplyState(command) {
this.dropdownApplyState = command.label
this.paramsCustom.apply = command.type
@@ -470,6 +516,7 @@ export default {
this.dropdownPrivate = this.$i18n['all'];
this.dropdownType = this.$i18n['cloudeBrainMirror']['all_cluster'];
this.dropdownApplyState = this.$i18n['cloudeBrainMirror']['all_approval_status'];
this.dropdownComputeResource = this.$i18n['cloudeBrainMirror']['all_compute_resource'];
let params = new URLSearchParams(location.search)
if (location.search) {
this.firstSearch = true
@@ -483,6 +530,9 @@ export default {
if (params.has('cloudbrainType')) {
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')) {
const apply = params.get('apply');
switch (apply) {


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

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

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

// 获取promote配置数据
export const getPromoteData = (filePathName) => {
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) => {
const typeMap = {
'0': 'recommend',
@@ -17,9 +18,69 @@ export const getImages = (params) => {
method: "get",
params: {
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,
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;
height: 38px;
padding: 0 12px;
border-left: none;
margin-bottom: 5px;

i {


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

@@ -6,7 +6,8 @@
</div>
<div class="content" :class="errStatus ? 'error' : ''">
<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 class="right-area">
@@ -16,8 +17,8 @@
</div>
</div>
<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="main-area" v-loading="dlgLoading">
<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.myFavImage')" name="third"></el-tab-pane>
</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')"
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>
</div>
</el-input>
@@ -41,21 +56,35 @@
<span :title="item.tag">{{ item.tag }}</span>
<img v-if="item.type == 5" src="/img/jian.svg" />
</div>
<div></div>
</div>
<div class="item-l-m">
<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>
</div>
</div>
<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"
:src="item.relAvatarLink ? item.relAvatarLink : `/user/avatar/ghost/-1`" />
</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>
</div>
</div>
<div class="item-r">
<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">
<i class="CREATING"></i>
<span style="color:#5a5a5a">{{ $t('cloudbrainObj.submitting') }}</span>
@@ -81,15 +110,19 @@
</template>

<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 {
name: "ImageSelectV1",
props: {
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 },
required: { type: Boolean, default: true },
spec: { type: String, required: true, },
configs: { type: Object, default: () => ({}) },
},
data() {
return {
@@ -100,7 +133,38 @@ export default {
dlgLoading: false,
dlgActiveName: 'first',
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,
dlgPageSize: 5,
dlgTotal: 0,
@@ -116,6 +180,21 @@ export default {
this.imageUrl = newVal.toString();
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: {
@@ -135,11 +214,49 @@ export default {
this.dlgPage = 1;
this.searchImageData();
},
inputSearch() {
search() {
this.dlgTotal = 0;
this.dlgPage = 1;
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() {
const tabName = this.dlgActiveName;
const typeMap = {
@@ -153,11 +270,45 @@ export default {
page: this.dlgPage,
pageSize: this.dlgPageSize,
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;
getImages(params).then(res => {
this.dlgLoading = false;
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.dlgTotal = parseInt(res.data?.count || 0);
}).catch(err => {
@@ -192,7 +343,24 @@ export default {
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() { },
};
</script>
@@ -242,13 +410,40 @@ export default {
overflow: hidden;
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 {
overflow: hidden;
width: 200px;
width: 330px;
z-index: 5;
position: relative;
margin-top: -10px;
margin-top: -1px;

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

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

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

span {
display: flex;
align-items: center;
justify-content: center;
font-size: .85714286rem;
margin: 0 0.14285714em;
padding: 0.3em 0.5em;
@@ -314,8 +514,21 @@ export default {
color: rgba(0, 0, 0, .6);
font-weight: 700;
border-radius: 0.28571429rem;
line-height: 1;
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: '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 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_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 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')})` }];


+ 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',
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: {
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',
@@ -637,7 +692,7 @@ const en = {
recommendImage: 'Recommend Image',
myImage: 'My Images',
myFavImage: 'My collected images',
searchImagePlaceholder: 'Search image tag/description/label...',
searchImagePlaceholder: 'Search image tag/description/operating system/python library/label...',
useImage: 'Use',
submitting: 'Commiting',
submitFailed: 'Commit failed',


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

@@ -494,6 +494,61 @@ const zh = {
codeUseDlgTriggerTxt: '在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: {
resSelectTips: '「资源规格」是您运行该任务使用的硬件,为了更多人能够使用本平台的资源,请按照您的实际需求进行选择。',
no_use_resource: '暂无可用资源'
@@ -652,7 +707,7 @@ const zh = {
recommendImage: '平台推荐镜像',
myImage: '我的镜像',
myFavImage: '我收藏的镜像',
searchImagePlaceholder: '搜镜像Tag/描述/标签...',
searchImagePlaceholder: '搜镜像Tag/描述/操作系统/安装的软件包/标签...',
useImage: '使用',
submitting: '提交中',
submitFailed: '提交失败',


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

@@ -53,7 +53,7 @@ export const CreatePageConfigs = {
taskDescr: { required: false, },
branchName: { required: true, },
model: { required: false, multiple: true, useExceedSize: true },
imagev1: { required: true },
imagev1: { required: true, type: -1 },
dataset: { required: false, type: 0, useExceedSize: true },
networkType: { required: true },
spec: { required: true },
@@ -99,7 +99,7 @@ export const CreatePageConfigs = {
taskType: {},
branchName: {},
model: { required: false, multiple: true, useExceedSize: true },
imagev1: { required: true, type: 2 },
imagev1: { required: true, type: -1 },
dataset: { required: false, type: 0, useExceedSize: true },
networkType: { required: true },
spec: {},
@@ -134,7 +134,7 @@ export const CreatePageConfigs = {
taskDescr: { required: false, },
branchName: { required: true, },
model: { required: false, multiple: true, useExceedSize: true },
imagev2: { required: true, relatedSpec: true },
imagev1: { required: true, type: -1 },
dataset: { required: false, useExceedSize: true },
networkType: { required: true },
spec: { required: true },
@@ -154,7 +154,7 @@ export const CreatePageConfigs = {
taskDescr: { required: false, },
branchName: { required: true, },
model: { required: false, multiple: true, useExceedSize: true },
imagev2: { required: true, relatedSpec: true },
imagev1: { required: true, type: -1 },
dataset: { required: false, useExceedSize: true },
networkType: { required: true },
spec: { required: true },
@@ -194,7 +194,7 @@ export const CreatePageConfigs = {
taskDescr: { required: false, },
branchName: { required: true, },
model: { required: false, multiple: true, useExceedSize: true },
imagev2: { required: true, relatedSpec: true },
imagev1: { required: true, type: -1 },
dataset: { required: false, useExceedSize: true },
networkType: { required: true },
spec: { required: true },
@@ -214,7 +214,7 @@ export const CreatePageConfigs = {
taskDescr: { required: false, },
branchName: { required: true, },
model: { required: false, multiple: true, useExceedSize: true },
imagev2: { required: true, relatedSpec: true },
imagev1: { required: true, type: -1 },
dataset: { required: false, useExceedSize: true },
networkType: { required: true },
spec: { required: true },
@@ -244,7 +244,7 @@ export const CreatePageConfigs = {
taskDescr: { required: false, },
branchName: { required: 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' },
dataset: { required: true, type: 0 },
networkType: { required: true },
@@ -303,7 +303,7 @@ export const CreatePageConfigs = {
taskType: {},
branchName: {},
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' },
dataset: { required: true, type: 0 },
runParameters: { required: false },
@@ -351,7 +351,7 @@ export const CreatePageConfigs = {
taskDescr: { required: false, },
branchName: { required: 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' },
dataset: { required: true },
runParameters: { required: false },
@@ -375,7 +375,7 @@ export const CreatePageConfigs = {
taskDescr: { required: false, },
branchName: { required: true, },
model: { required: false, multiple: true },
imagev2: { required: true, relatedSpec: true },
imagev1: { required: true, type: -1 },
bootFile: { required: true, sampleUrl: '' },
dataset: { required: true },
runParameters: { required: false },
@@ -396,8 +396,8 @@ export const CreatePageConfigs = {
taskName: { required: true, },
taskDescr: { required: false, },
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: '' },
dataset: { required: true },
runParameters: { required: false },
@@ -458,7 +458,7 @@ export const CreatePageConfigs = {
taskType: {},
branchName: {},
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' },
dataset: { required: true, type: 0 },
runParameters: { required: false },
@@ -481,7 +481,7 @@ export const CreatePageConfigs = {
taskDescr: { required: false, },
branchName: { required: true, },
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' },
dataset: { required: true, type: 0 },
runParameters: { required: false },
@@ -511,7 +511,7 @@ export const CreatePageConfigs = {
branchName: { required: true, },
model: { required: false, },
algBechmarkType: { required: true, },
imagev1: { required: true, },
imagev1: { required: true, type: -1 },
networkType: { required: true },
spec: { required: true, },

@@ -581,7 +581,7 @@ export const CreatePageConfigs = {
branchName: {},
bootFile: { required: true, sampleUrl: 'https://openi.pcl.ac.cn/OpenIOSSG/Online-Inference_Example' },
model: { required: false, multiple: true },
imagev1: { required: true, type: 2 },
imagev1: { required: true, type: -1 },
dataset: { required: false, type: 0, useExceedSize: true },
networkType: { required: true },
spec: {},
@@ -1266,7 +1266,7 @@ export const DetailPageConfigs = {
branchName: { required: true, },
model: { required: false, },
algBechmarkType: { required: true, },
imagev1: { required: true, },
imagev1: { required: true, type: -1 },
networkType: { 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="form-body-content">
<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>
<ImageSelectV2 ref="imagev2Ref" v-if="formCfg.imagev2" v-model="state.image" :images="imageList"
:configs="pageCfg" :spec="state.spec" :networkType="state.networkType" @changeImages="changeImages"
@@ -443,6 +443,15 @@ export default {
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) {
this.imageList = images || [];
let image = this.imageList[0];
@@ -755,4 +764,4 @@ export default {
padding-right: 1em;
}
}
</style>
</style>

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

@@ -98,7 +98,7 @@ export class CloudBrainTools {
} else {
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;
if (task.canDebug) {
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