Browse Source

fix issue

tags/v1.22.4.1^2
zhoupzh 3 years ago
parent
commit
602774c201
63 changed files with 3470 additions and 887 deletions
  1. +31
    -4
      models/cloudbrain.go
  2. +583
    -0
      models/cloudbrain_image.go
  3. +4
    -0
      models/models.go
  4. +1
    -0
      models/user.go
  5. +21
    -2
      models/user_business_analysis.go
  6. +19
    -1
      modules/auth/cloudbrain.go
  7. +36
    -0
      modules/cloudbrain/cloudbrain.go
  8. +123
    -2
      modules/cloudbrain/resty.go
  9. +10
    -0
      modules/convert/convert.go
  10. +1
    -0
      modules/labelmsg/redismsgsender.go
  11. +5
    -3
      modules/setting/cloudbrain.go
  12. +2
    -0
      modules/setting/setting.go
  13. +35
    -14
      modules/storage/obs.go
  14. +8
    -0
      modules/structs/repo_topic.go
  15. +5
    -1
      modules/templates/helper.go
  16. +21
    -4
      options/locale/locale_en-US.ini
  17. +1
    -0
      public/img/jian.svg
  18. +7
    -0
      routers/admin/cloudbrains.go
  19. +3
    -0
      routers/api/v1/api.go
  20. +4
    -4
      routers/api/v1/repo/modelarts.go
  21. +60
    -0
      routers/api/v1/repo/topic.go
  22. +30
    -0
      routers/image/image.go
  23. +16
    -10
      routers/repo/attachment.go
  24. +244
    -26
      routers/repo/cloudbrain.go
  25. +4
    -7
      routers/repo/dataset.go
  26. +2
    -0
      routers/repo/modelarts.go
  27. +72
    -73
      routers/repo/user_data_analysis.go
  28. +5
    -0
      routers/repo/util.go
  29. +17
    -0
      routers/routes/routes.go
  30. +6
    -6
      routers/search.go
  31. +2
    -1
      semantic.json
  32. +42
    -0
      templates/admin/cloudbrain/images.html
  33. +1
    -1
      templates/admin/cloudbrain/list.tmpl
  34. +1
    -1
      templates/admin/cloudbrain/search.tmpl
  35. +3
    -0
      templates/admin/navbar.tmpl
  36. +5
    -5
      templates/custom/select_dataset.tmpl
  37. +7
    -3
      templates/custom/select_dataset_train.tmpl
  38. +1
    -1
      templates/explore/datasets.tmpl
  39. +19
    -2
      templates/explore/images.tmpl
  40. +2
    -13
      templates/repo/cloudbrain/benchmark/new.tmpl
  41. +94
    -0
      templates/repo/cloudbrain/image/edit.tmpl
  42. +106
    -0
      templates/repo/cloudbrain/image/submit.tmpl
  43. +8
    -5
      templates/repo/cloudbrain/new.tmpl
  44. +7
    -3
      templates/repo/cloudbrain/trainjob/new.tmpl
  45. +1
    -1
      templates/repo/createCourse.tmpl
  46. +2
    -86
      templates/repo/debugjob/index.tmpl
  47. +2
    -2
      templates/repo/modelarts/inferencejob/index.tmpl
  48. +8
    -5
      templates/repo/modelarts/inferencejob/new.tmpl
  49. +2
    -2
      templates/repo/modelarts/trainjob/new.tmpl
  50. +1
    -1
      templates/user/settings/organization.tmpl
  51. +0
    -20
      web_src/js/components/Contributors.vue
  52. +1
    -1
      web_src/js/components/DataAnalysis.vue
  53. +0
    -503
      web_src/js/components/Images.vue
  54. +1
    -8
      web_src/js/components/MinioUploader.vue
  55. +0
    -36
      web_src/js/components/Model.vue
  56. +10
    -9
      web_src/js/components/UserAnalysis.vue
  57. +773
    -0
      web_src/js/components/images/Images.vue
  58. +397
    -0
      web_src/js/components/images/adminImages.vue
  59. +357
    -0
      web_src/js/components/images/selectImages.vue
  60. +195
    -0
      web_src/js/features/images.js
  61. +37
    -12
      web_src/js/index.js
  62. +1
    -1
      web_src/js/vendor/semanticdropdown.js
  63. +8
    -8
      web_src/less/openi.less

+ 31
- 4
models/cloudbrain.go View File

@@ -112,7 +112,7 @@ type Cloudbrain struct {
SubTaskName string
ContainerID string
ContainerIp string
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
CreatedUnix timeutil.TimeStamp `xorm:"INDEX"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
Duration int64 `xorm:"DEFAULT 0"` //运行时长 单位秒
TrainJobDuration string `xorm:"DEFAULT '00:00:00'"`
@@ -185,6 +185,12 @@ func (task *Cloudbrain) ComputeAndSetDuration() {
task.TrainJobDuration = ConvertDurationToStr(d)
}

func (task *Cloudbrain) CorrectCreateUnix() {
if task.StartTime > 0 && task.CreatedUnix > task.StartTime {
task.CreatedUnix = task.StartTime
}
}

func (task *Cloudbrain) IsTerminal() bool {
status := task.Status
return status == string(ModelArtsTrainJobCompleted) || status == string(ModelArtsTrainJobFailed) || status == string(ModelArtsTrainJobKilled) || status == string(ModelArtsStopped) || status == string(JobStopped) || status == string(JobFailed) || status == string(JobSucceeded)
@@ -219,9 +225,22 @@ func ParseAndSetDurationFromCloudBrainOne(result JobResultPayload, task *Cloudbr
task.EndTime = timeutil.TimeStamp(result.JobStatus.CompletedTime / 1000)
}
}
task.CorrectCreateUnix()
task.ComputeAndSetDuration()
}

func ParseAndSetDurationFromModelArtsNotebook(result *GetNotebook2Result, job *Cloudbrain) {
if job.StartTime == 0 && result.Lease.UpdateTime > 0 {
job.StartTime = timeutil.TimeStamp(result.Lease.UpdateTime / 1000)
}
job.Status = result.Status
if job.EndTime == 0 && IsModelArtsDebugJobTerminal(job.Status) {
job.EndTime = timeutil.TimeStampNow()
}
job.CorrectCreateUnix()
job.ComputeAndSetDuration()
}

type CloudbrainInfo struct {
Cloudbrain `xorm:"extends"`
User `xorm:"extends"`
@@ -548,13 +567,21 @@ type PoolInfo struct {
PoolType string `json:"pool_type"`
}

type CommitImageParams struct {
type CommitImageCloudBrainParams struct {
Ip string `json:"ip"`
TaskContainerId string `json:"taskContainerId"`
ImageTag string `json:"imageTag"`
ImageDescription string `json:"imageDescription"`
}

type CommitImageParams struct {
CommitImageCloudBrainParams
IsPrivate bool
Topics []string
CloudBrainType int
UID int64
}

type CommitImageResult struct {
Code string `json:"code"`
Msg string `json:"msg"`
@@ -1472,7 +1499,7 @@ func UpdateTrainJobVersion(job *Cloudbrain) error {
func updateJobTrainVersion(e Engine, job *Cloudbrain) error {
var sess *xorm.Session
sess = e.Where("job_id = ? AND version_name=?", job.JobID, job.VersionName)
_, err := sess.Cols("status", "train_job_duration", "duration", "start_time", "end_time").Update(job)
_, err := sess.Cols("status", "train_job_duration", "duration", "start_time", "end_time", "created_unix").Update(job)
return err
}

@@ -1561,7 +1588,7 @@ func UpdateInferenceJob(job *Cloudbrain) error {
func updateInferenceJob(e Engine, job *Cloudbrain) error {
var sess *xorm.Session
sess = e.Where("job_id = ?", job.JobID)
_, err := sess.Cols("status", "train_job_duration", "duration", "start_time", "end_time").Update(job)
_, err := sess.Cols("status", "train_job_duration", "duration", "start_time", "end_time", "created_unix").Update(job)
return err
}
func RestartCloudbrain(old *Cloudbrain, new *Cloudbrain) (err error) {


+ 583
- 0
models/cloudbrain_image.go View File

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

import (
"fmt"
"strings"
"unicode/utf8"

"xorm.io/builder"

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

const RECOMMOND_TYPE = 5
const NORMAL_TYPE = 0
const IMAGE_STATUS_COMMIT = 0
const IMAGE_STATUS_SUCCESS = 1
const IMAGE_STATUS_Failed = 2

type Image struct {
ID int64 `xorm:"pk autoincr" json:"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(765)" 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"`
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提交失败
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created" json:"createdUnix"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated" json:"updatedUnix"`
}

type ImageList []*Image

type ImageStar struct {
ID int64 `xorm:"pk autoincr"`
UID int64 `xorm:"UNIQUE(s)"`
ImageID int64 `xorm:"UNIQUE(s)"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
}

type ImageTopic struct {
ID int64
Name string `xorm:"UNIQUE VARCHAR(105)"`
ImageCount int
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}

type ImageTopicRelation struct {
ImageID int64 `xorm:"UNIQUE(s)"`
TopicID int64 `xorm:"UNIQUE(s)"`
}

type SearchImageOptions struct {
Keyword string
UID int64
Status int
IncludePublicOnly bool
IncludeOfficialOnly bool
IncludePrivateOnly bool
IncludeStarByMe bool
IncludeCustom bool
IncludeOwnerOnly bool
Topics string
ListOptions
SearchOrderBy
}
type ErrorImageTagExist struct {
Tag string
}

type ErrorImageCommitting struct {
Tag string
}

type ImagesPageResult struct {
Count int64 `json:"count"`
Images []*Image `json:"images"`
}

func (err ErrorImageTagExist) Error() string {
return fmt.Sprintf("Image already exists [tag: %s]", err.Tag)
}

func (err ErrorImageCommitting) Error() string {
return fmt.Sprintf("Image already exists [tag: %s]", err.Tag)
}

type ErrImageNotExist struct {
ID int64
Tag string
}

func (err ErrImageNotExist) Error() string {
return fmt.Sprintf("Image does not exist [id: %d] [tag: %s]", err.ID, err.Tag)
}

func IsErrorImageCommitting(err error) bool {
_, ok := err.(ErrorImageCommitting)
return ok
}

func IsErrImageNotExist(err error) bool {
_, ok := err.(ErrImageNotExist)
return ok
}

func IsErrImageTagExist(err error) bool {
_, ok := err.(ErrorImageTagExist)
return ok
}

func IsImageExist(tag string) (bool, error) {
return x.Exist(&Image{
Tag: tag,
})
}

func IsImageExistByUser(tag string, uid int64) (bool, error) {
return x.Exist(&Image{
Tag: tag,
UID: uid,
Status: IMAGE_STATUS_SUCCESS,
})
}

type FindImageTopicOptions struct {
ListOptions
ImageID int64
Keyword string
}

func (opts *FindImageTopicOptions) toConds() builder.Cond {
var cond = builder.NewCond()
if opts.ImageID > 0 {
cond = cond.And(builder.Eq{"image_topic_relation.image_id": opts.ImageID})
}

if opts.Keyword != "" {
cond = cond.And(builder.Like{"image_topic.name", strings.ToLower(opts.Keyword)})
}

return cond
}

func GetImageByID(id int64) (*Image, error) {
rel := new(Image)
has, err := x.
ID(id).
Get(rel)
if err != nil {
return nil, err
} else if !has {
return nil, ErrImageNotExist{ID: id}
}

return rel, nil
}

func GetImageByTag(tag string) (*Image, error) {

image := &Image{Tag: tag}
has, err := x.
Get(image)
if err != nil {
return nil, err
} else if !has {
return nil, ErrImageNotExist{Tag: tag}
}

return image, nil
}

func SanitizeAndValidateImageTopics(topics []string) (validTopics []string, invalidTopics []string) {
validTopics = make([]string, 0)
mValidTopics := make(map[string]struct{})
invalidTopics = make([]string, 0)

for _, topic := range topics {
topic = strings.TrimSpace(strings.ToLower(topic))
// ignore empty string
if len(topic) == 0 {
continue
}
// ignore same topic twice
if _, ok := mValidTopics[topic]; ok {
continue
}
if utf8.RuneCountInString(topic) <= 35 {
validTopics = append(validTopics, topic)
mValidTopics[topic] = struct{}{}
} else {
invalidTopics = append(invalidTopics, topic)
}
}

return validTopics, invalidTopics
}
func FindImageTopics(opts *FindImageTopicOptions) (topics []*ImageTopic, err error) {
sess := x.Select("image_topic.*").Where(opts.toConds())
if opts.ImageID > 0 {
sess.Join("INNER", "image_topic_relation", "image_topic_relation.topic_id = image_topic.id")
}
if opts.PageSize != 0 && opts.Page != 0 {
sess = opts.setSessionPagination(sess)
}
return topics, sess.Desc("image_topic.image_count").Find(&topics)
}

func SaveImageTopics(imageID int64, topicNames ...string) error {
topics, err := FindImageTopics(&FindImageTopicOptions{
ImageID: imageID,
})
if err != nil {
return err
}

sess := x.NewSession()
defer sess.Close()

if err := sess.Begin(); err != nil {
return err
}

var addedTopicNames []string
for _, topicName := range topicNames {
if strings.TrimSpace(topicName) == "" {
continue
}

var found bool
for _, t := range topics {
if strings.EqualFold(topicName, t.Name) {
found = true
break
}
}
if !found {
addedTopicNames = append(addedTopicNames, topicName)
}
}

var removeTopics []*ImageTopic
for _, t := range topics {
var found bool
for _, topicName := range topicNames {
if strings.EqualFold(topicName, t.Name) {
found = true
break
}
}
if !found {
removeTopics = append(removeTopics, t)
}
}

for _, topicName := range addedTopicNames {
_, err := addTopicByNameToImage(sess, imageID, topicName)
if err != nil {
return err
}
}

for _, topic := range removeTopics {
err := removeTopicFromImage(sess, imageID, topic)
if err != nil {
return err
}
}

topicNames = make([]string, 0, 25)
if err := sess.Table("image_topic").Cols("name").
Join("INNER", "image_topic_relation", "image_topic_relation.topic_id = image_topic.id").
Where("image_topic_relation.image_id = ?", imageID).Desc("image_topic.image_count").Find(&topicNames); err != nil {
return err
}

if _, err := sess.ID(imageID).Cols("topics").Update(&Image{
Topics: topicNames,
}); err != nil {
return err
}

return sess.Commit()
}

func addTopicByNameToImage(e Engine, imageID int64, topicName string) (*ImageTopic, error) {
var topic ImageTopic
has, err := e.Where("name = ?", topicName).Get(&topic)
if err != nil {
return nil, err
}
if !has {
topic.Name = topicName
topic.ImageCount = 1
if _, err := e.Insert(&topic); err != nil {
return nil, err
}
} else {
topic.ImageCount++
if _, err := e.ID(topic.ID).Cols("image_count").Update(&topic); err != nil {
return nil, err
}
}

if _, err := e.Insert(&ImageTopicRelation{
ImageID: imageID,
TopicID: topic.ID,
}); err != nil {
return nil, err
}

return &topic, nil
}

func removeTopicFromImage(e Engine, imageId int64, topic *ImageTopic) error {
topic.ImageCount--
if _, err := e.ID(topic.ID).Cols("image_count").Update(topic); err != nil {
return err
}

if _, err := e.Delete(&ImageTopicRelation{
ImageID: imageId,
TopicID: topic.ID,
}); err != nil {
return err
}

return nil
}

func SearchImage(opts *SearchImageOptions) (ImageList, int64, error) {
cond := SearchImageCondition(opts)
return SearchImageByCondition(opts, cond)
}

func SearchImageCondition(opts *SearchImageOptions) builder.Cond {
var cond = builder.NewCond()

if len(opts.Keyword) > 0 {

var subQueryCond = builder.NewCond()
for _, v := range strings.Split(opts.Keyword, ",") {

subQueryCond = subQueryCond.Or(builder.Like{"LOWER(image_topic.name)", strings.ToLower(v)})

}
subQuery := builder.Select("image_topic_relation.image_id").From("image_topic_relation").
Join("INNER", "image_topic", "image_topic.id = image_topic_relation.topic_id").
Where(subQueryCond).
GroupBy("image_topic_relation.image_id")
var keywordCond = builder.In("id", subQuery)

var likes = builder.NewCond()
for _, v := range strings.Split(opts.Keyword, ",") {
likes = likes.Or(builder.Like{"LOWER(tag)", strings.ToLower(v)})

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

}
keywordCond = keywordCond.Or(likes)

cond = cond.And(keywordCond)

}
if len(opts.Topics) > 0 { //标签精确匹配
var subQueryCond = builder.NewCond()
for _, v := range strings.Split(opts.Keyword, ",") {

subQueryCond = subQueryCond.Or(builder.Eq{"LOWER(image_topic.name)": strings.ToLower(v)})
subQuery := builder.Select("image_topic_relation.image_id").From("image_topic_relation").
Join("INNER", "image_topic", "image_topic.id = image_topic_relation.topic_id").
Where(subQueryCond).
GroupBy("image_topic_relation.image_id")
var topicCond = builder.In("id", subQuery)
cond = cond.And(topicCond)
}
}

if opts.IncludePublicOnly {
cond = cond.And(builder.Eq{"is_private": false})
}

if opts.IncludePrivateOnly {
cond = cond.And(builder.Eq{"is_private": true})
}

if opts.IncludeOwnerOnly {

cond = cond.And(builder.Eq{"uid": opts.UID})
}
if opts.IncludeOfficialOnly {
cond = cond.And(builder.Eq{"type": RECOMMOND_TYPE})
}
if opts.Status >= 0 {
cond = cond.And(builder.Eq{"status": opts.Status})
}

if opts.IncludeStarByMe {

subQuery := builder.Select("image_id").From("image_star").
Where(builder.Eq{"uid": opts.UID})
var starCond = builder.In("id", subQuery)
cond = cond.And(starCond)

}

return cond
}

func SearchImageByCondition(opts *SearchImageOptions, cond builder.Cond) (ImageList, int64, error) {
if opts.Page <= 0 {
opts.Page = 1
}

var err error
sess := x.NewSession()
defer sess.Close()

images := make(ImageList, 0, opts.PageSize)
count, err := sess.Where(cond).Count(new(Image))

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

sess.Where(cond).OrderBy(opts.SearchOrderBy.String())

if opts.PageSize > 0 {
sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
}
if err = sess.Find(&images); err != nil {
return nil, 0, fmt.Errorf("Images: %v", err)
}

if err = images.loadAttributes(sess, opts.UID); err != nil {
return nil, 0, fmt.Errorf("LoadAttributes: %v", err)
}

return images, count, nil
}

func (images ImageList) loadAttributes(e Engine, uid int64) error {
if len(images) == 0 {
return nil
}

set := make(map[int64]struct{})

for i := range images {
set[images[i].UID] = struct{}{}
}

// Load creators.
users := make(map[int64]*User, len(set))
if err := e.Table("\"user\"").
Cols("name", "lower_name", "avatar", "email").
Where("id > 0").
In("id", keysInt64(set)).
Find(&users); err != nil {
return fmt.Errorf("find users: %v", err)
}

for i := range images {
images[i].UserName = users[images[i].UID].Name
images[i].RelAvatarLink = users[images[i].UID].RelAvatarLink()
if uid == -1 {
images[i].IsStar = false
} else {
images[i].IsStar = isImageStaring(e, uid, images[i].ID)
}
}

return nil
}

func GetCommittingImageCount() int {

total, err := x.Where("status =?", 0).Count(new(Image))

if err != nil {
return 0
}
return int(total)
}

func CreateLocalImage(image *Image) error {

_, err := x.Insert(image)
return err
}

func UpdateLocalImage(image *Image) error {

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

func UpdateLocalImageStatus(image *Image) error {

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

func DeleteLocalImage(id int64) error {
image := new(Image)
_, err := x.ID(id).Delete(image)
return err
}

//star or unstar Image
func StarImage(userID, imageID int64, star bool) error {
sess := x.NewSession()
defer sess.Close()

if err := sess.Begin(); err != nil {
return err
}

if star {
if isImageStaring(sess, userID, imageID) {
return nil
}

if _, err := sess.Insert(&ImageStar{UID: userID, ImageID: imageID}); err != nil {
return err
}
if _, err := sess.Exec("UPDATE `image` SET num_stars = num_stars + 1 WHERE id = ?", imageID); err != nil {
return err
}
if _, err := sess.Exec("UPDATE `user` SET num_image_stars = num_image_stars + 1 WHERE id = ?", userID); err != nil {
return err
}
} else {
if !isImageStaring(sess, userID, imageID) {
return nil
}

if _, err := sess.Delete(&ImageStar{0, userID, imageID, 0}); err != nil {
return err
}
if _, err := sess.Exec("UPDATE `image` SET num_stars = num_stars - 1 WHERE id = ?", imageID); err != nil {
return err
}
if _, err := sess.Exec("UPDATE `user` SET num_image_stars = num_image_stars - 1 WHERE id = ?", userID); err != nil {
return err
}
}

return sess.Commit()
}

func IsImageStaring(userID, datasetID int64) bool {
return isImageStaring(x, userID, datasetID)

}

func isImageStaring(e Engine, userID, imageID int64) bool {
has, _ := e.Get(&ImageStar{0, userID, imageID, 0})
return has
}
func RecommendImage(imageId int64, recommond bool) error {

image := Image{Type: getRecommondType(recommond)}
_, err := x.ID(imageId).Cols("type").Update(image)
return err
}

func getRecommondType(recommond bool) int {
if recommond {

return RECOMMOND_TYPE
} else {
return NORMAL_TYPE
}

}

+ 4
- 0
models/models.go View File

@@ -131,6 +131,10 @@ func init() {
new(Dataset),
new(DatasetStar),
new(Cloudbrain),
new(Image),
new(ImageStar),
new(ImageTopic),
new(ImageTopicRelation),
new(FileChunk),
new(BlockChain),
new(RecommendOrg),


+ 1
- 0
models/user.go View File

@@ -157,6 +157,7 @@ type User struct {
NumFollowing int `xorm:"NOT NULL DEFAULT 0"`
NumStars int
NumDatasetStars int `xorm:"NOT NULL DEFAULT 0"`
NumImageStars int `xorm:"NOT NULL DEFAULT 0"`
NumRepos int

// For organization


+ 21
- 2
models/user_business_analysis.go View File

@@ -246,7 +246,7 @@ func QueryUserStaticDataByTableName(start int, pageSize int, tableName string, q
}
log.Info("query return total:" + fmt.Sprint(allCount))
userBusinessAnalysisAllList := make([]*UserBusinessAnalysisAll, 0)
if err := statictisSess.Table(tableName).Where(cond).OrderBy("commit_count desc,id desc").Limit(pageSize, start).
if err := statictisSess.Table(tableName).Where(cond).OrderBy("user_index desc,id desc").Limit(pageSize, start).
Find(&userBusinessAnalysisAllList); err != nil {
return nil, 0
}
@@ -448,6 +448,9 @@ func refreshUserStaticTable(wikiCountMap map[string]int, tableName string, pageS
var indexTotal int64
indexTotal = 0
insertCount := 0
userIndexMap := make(map[int64]float64, 0)
maxUserIndex := 0.0
minUserIndex := 100000000.0
dateRecordBatch := make([]UserBusinessAnalysisAll, 0)
for {
sess.Select("`user`.*").Table("user").Where(cond).OrderBy("id asc").Limit(PAGE_SIZE, int(indexTotal))
@@ -494,7 +497,13 @@ func refreshUserStaticTable(wikiCountMap map[string]int, tableName string, pageS
dateRecordAll.GpuBenchMarkJob = getMapKeyStringValue(fmt.Sprint(dateRecordAll.ID)+"_GpuBenchMarkJob", CloudBrainTaskItemMap)
dateRecordAll.CommitModelCount = getMapValue(dateRecordAll.ID, AiModelManageMap)
dateRecordAll.UserIndex = getUserIndexFromAnalysisAll(dateRecordAll, ParaWeight)

userIndexMap[dateRecordAll.ID] = dateRecordAll.UserIndex
if maxUserIndex < dateRecordAll.UserIndex {
maxUserIndex = dateRecordAll.UserIndex
}
if minUserIndex > dateRecordAll.UserIndex {
minUserIndex = dateRecordAll.UserIndex
}
dateRecordBatch = append(dateRecordBatch, dateRecordAll)
if len(dateRecordBatch) >= BATCH_INSERT_SIZE {
insertTable(dateRecordBatch, tableName, statictisSess)
@@ -523,9 +532,19 @@ func refreshUserStaticTable(wikiCountMap map[string]int, tableName string, pageS
}
}

//normalization
for k, v := range userIndexMap {
tmpResult := (v - minUserIndex) / (maxUserIndex - minUserIndex)
updateUserIndex(tableName, statictisSess, k, tmpResult)
}
log.Info("refresh data finished.tableName=" + tableName + " total record:" + fmt.Sprint(insertCount))
}

func updateUserIndex(tableName string, statictisSess *xorm.Session, userId int64, userIndex float64) {
updateSql := "UPDATE public." + tableName + " set user_index=" + fmt.Sprint(userIndex) + " where id=" + fmt.Sprint(userId)
statictisSess.Exec(updateSql)
}

func insertTable(dateRecords []UserBusinessAnalysisAll, tableName string, statictisSess *xorm.Session) {

insertBatchSql := "INSERT INTO public." + tableName +


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

@@ -27,9 +27,27 @@ type CreateCloudBrainForm struct {

type CommitImageCloudBrainForm struct {
Description string `form:"description" binding:"Required"`
Tag string `form:"tag" 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"`
}

type EditImageCloudBrainForm struct {
ID int64 `form:"id" binding:"Required"`
Description string `form:"description" binding:"Required"`
IsPrivate bool `form:"isPrivate" binding:"Required"`
Topics string `form:"topics"`
}

func (f *CreateCloudBrainForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}

func (f *CommitImageCloudBrainForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}

func (f *EditImageCloudBrainForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}

+ 36
- 0
modules/cloudbrain/cloudbrain.go View File

@@ -86,6 +86,18 @@ func isAdminOrJobCreater(ctx *context.Context, job *models.Cloudbrain, err error

}

func isAdminOrImageCreater(ctx *context.Context, image *models.Image, err error) bool {
if !ctx.IsSigned {
return false
}
if err != nil {
return ctx.IsUserSiteAdmin()
} else {
return ctx.IsUserSiteAdmin() || ctx.User.ID == image.UID
}

}

func AdminOrOwnerOrJobCreaterRight(ctx *context.Context) {

var ID = ctx.Params(":id")
@@ -150,7 +162,31 @@ func AdminOrJobCreaterRightForTrain(ctx *context.Context) {

}

func AdminOrImageCreaterRight(ctx *context.Context) {

id, err := strconv.ParseInt(ctx.Params(":id"), 10, 64)
var image *models.Image
if err != nil {
log.Error("Get Image by ID failed:%v", err.Error())

} else {
image, err = models.GetImageByID(id)
if err != nil {
log.Error("Get Image by ID failed:%v", err.Error())
return
}
}

if !isAdminOrImageCreater(ctx, image, err) {
log.Error("!isAdminOrImageCreater error:%v", err.Error())
ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
}

}


func GenerateTask(ctx *context.Context, displayJobName, jobName, image, command, uuid, codePath, modelPath, benchmarkPath, snn4imagenetPath, brainScorePath, jobType, gpuQueue, description, branchName, bootFile, params string, benchmarkTypeID, benchmarkChildTypeID, resourceSpecId int) error {

dataActualPath := setting.Attachment.Minio.RealPath +
setting.Attachment.Minio.Bucket + "/" +
setting.Attachment.Minio.BasePath +


+ 123
- 2
modules/cloudbrain/resty.go View File

@@ -4,9 +4,11 @@ import (
"encoding/json"
"errors"
"fmt"
"math"
"net/http"
"strconv"
"strings"
"time"

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

@@ -28,6 +30,7 @@ const (
Custom = "custom"
LogPageSize = 500
LogPageTokenExpired = "5m"
pageSize = 15
)

func getRestyClient() *resty.Client {
@@ -210,6 +213,42 @@ func getQueryString(page int, size int, name string) string {
}

func CommitImage(jobID string, params models.CommitImageParams) error {

dbImage, err := models.GetImageByTag(params.ImageTag)

if err != nil && !models.IsErrImageNotExist(err) {
return fmt.Errorf("resty CommitImage: %v", err)
}
var createTime time.Time
var isSetCreatedUnix = false
if dbImage != nil {
if dbImage.UID != params.UID {
return models.ErrorImageTagExist{
Tag: params.ImageTag,
}
} else {
if dbImage.Status == models.IMAGE_STATUS_COMMIT {
return models.ErrorImageCommitting{
Tag: params.ImageTag,
}

} else { //覆盖提交

result, err := GetImagesPageable(1, pageSize, Custom, "")
if err == nil && result.Code == "S000" {
for _, v := range result.Payload.ImageInfo {
if v.Place == dbImage.Place {
isSetCreatedUnix = true
createTime, _ = time.Parse(time.RFC3339, v.Createtime)
break
}
}
}

}
}
}

checkSetting()
client := getRestyClient()
var result models.CommitImageResult
@@ -220,7 +259,7 @@ sendjob:
res, err := client.R().
SetHeader("Content-Type", "application/json").
SetAuthToken(TOKEN).
SetBody(params).
SetBody(params.CommitImageCloudBrainParams).
SetResult(&result).
Post(HOST + "/rest-server/api/v1/jobs/" + jobID + "/commitImage")

@@ -238,7 +277,89 @@ sendjob:
return fmt.Errorf("CommitImage err: %s", res.String())
}

return nil
image := models.Image{
Type: models.NORMAL_TYPE,
CloudbrainType: params.CloudBrainType,
UID: params.UID,
IsPrivate: params.IsPrivate,
Tag: params.ImageTag,
Description: params.ImageDescription,
Place: setting.Cloudbrain.ImageURLPrefix + params.ImageTag,
Status: models.IMAGE_STATUS_COMMIT,
}

err = models.WithTx(func(ctx models.DBContext) error {
if dbImage != nil {
dbImage.IsPrivate = params.IsPrivate
dbImage.Description = params.ImageDescription
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 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())
}
return nil
})
if err == nil {

go updateImageStatus(image, isSetCreatedUnix, createTime)
}
return err
}

func updateImageStatus(image models.Image, isSetCreatedUnix bool, createTime time.Time) {
attemps := 5
commitSuccess := false
time.Sleep(5 * time.Second)
for i := 0; i < attemps; i++ {

if commitSuccess {
break
}

result, err := GetImagesPageable(1, pageSize, Custom, "")
if err == nil && result.Code == "S000" {
for _, v := range result.Payload.ImageInfo {
if v.Place == image.Place && (!isSetCreatedUnix || (isSetCreatedUnix && createTimeUpdated(v, createTime))) {
image.Status = models.IMAGE_STATUS_SUCCESS
models.UpdateLocalImageStatus(&image)
commitSuccess = true
break
}

}

}
//第一次循环等待4秒,第二次等待4的2次方16秒,...,第5次。。。 ,总共大概是20多分钟内进行5次重试
var sleepTime = time.Duration(int(math.Pow(4, (float64(i + 1)))))

time.Sleep(sleepTime * time.Second)

}
if !commitSuccess {
image.Status = models.IMAGE_STATUS_Failed
models.UpdateLocalImageStatus(&image)
}

}

func createTimeUpdated(v *models.ImageInfo, createTime time.Time) bool {
newTime, err := time.Parse(time.RFC3339, v.Createtime)
if err != nil {
return false
}
return newTime.After(createTime)
}

func StopJob(jobID string) error {


+ 10
- 0
modules/convert/convert.go View File

@@ -403,6 +403,16 @@ func ToTopicResponse(topic *models.Topic) *api.TopicResponse {
}
}

func ToImageTopicResponse(topic *models.ImageTopic) *api.ImageTopicResponse {
return &api.ImageTopicResponse{
ID: topic.ID,
Name: topic.Name,
ImageCount: topic.ImageCount,
Created: topic.CreatedUnix.AsTime(),
Updated: topic.UpdatedUnix.AsTime(),
}
}

// ToOAuth2Application convert from models.OAuth2Application to api.OAuth2Application
func ToOAuth2Application(app *models.OAuth2Application) *api.OAuth2Application {
return &api.OAuth2Application{


+ 1
- 0
modules/labelmsg/redismsgsender.go View File

@@ -50,6 +50,7 @@ func SendDecompressAttachToLabelOBS(attach string) error {
_, err := redisclient.Do("Publish", setting.DecompressOBSTaskName, attach)
if err != nil {
log.Critical("redis Publish failed.")
return err
}

log.Info("LabelDecompressOBSQueue(%s) success", attach)


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

@@ -1,9 +1,10 @@
package setting

type CloudbrainLoginConfig struct {
Username string
Password string
Host string
Username string
Password string
Host string
ImageURLPrefix string
}

var (
@@ -15,5 +16,6 @@ func GetCloudbrainConfig() CloudbrainLoginConfig {
Cloudbrain.Username = cloudbrainSec.Key("USERNAME").MustString("")
Cloudbrain.Password = cloudbrainSec.Key("PASSWORD").MustString("")
Cloudbrain.Host = cloudbrainSec.Key("REST_SERVER_HOST").MustString("")
Cloudbrain.ImageURLPrefix = cloudbrainSec.Key("IMAGE_URL_PREFIX").MustString("")
return Cloudbrain
}

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

@@ -438,6 +438,7 @@ var (
//home page
RecommentRepoAddr string
ESSearchURL string
INDEXPOSTFIX string
//notice config
UserNameOfNoticeRepo string
RepoNameOfNoticeRepo string
@@ -1268,6 +1269,7 @@ func NewContext() {
sec = Cfg.Section("homepage")
RecommentRepoAddr = sec.Key("Address").MustString("https://git.openi.org.cn/OpenIOSSG/promote/raw/branch/master/")
ESSearchURL = sec.Key("ESSearchURL").MustString("http://192.168.207.94:9200")
INDEXPOSTFIX = sec.Key("INDEXPOSTFIX").MustString("")

sec = Cfg.Section("notice")
UserNameOfNoticeRepo = sec.Key("USER_NAME").MustString("OpenIOSSG")


+ 35
- 14
modules/storage/obs.go View File

@@ -30,6 +30,8 @@ type FileInfo struct {
}
type FileInfoList []FileInfo

const MAX_LIST_PARTS = 1000

func (ulist FileInfoList) Swap(i, j int) { ulist[i], ulist[j] = ulist[j], ulist[i] }
func (ulist FileInfoList) Len() int { return len(ulist) }
func (ulist FileInfoList) Less(i, j int) bool {
@@ -97,29 +99,48 @@ func CompleteObsMultiPartUpload(uuid, uploadID, fileName string) error {
input.Bucket = setting.Bucket
input.Key = strings.TrimPrefix(path.Join(setting.BasePath, path.Join(uuid[0:1], uuid[1:2], uuid, fileName)), "/")
input.UploadId = uploadID
output, err := ObsCli.ListParts(&obs.ListPartsInput{
Bucket: setting.Bucket,
Key: input.Key,
UploadId: uploadID,
})
if err != nil {
log.Error("ListParts failed:", err.Error())
return err
}

for _, partInfo := range output.Parts {
input.Parts = append(input.Parts, obs.Part{
PartNumber: partInfo.PartNumber,
ETag: partInfo.ETag,
partNumberMarker := 0
for {
output, err := ObsCli.ListParts(&obs.ListPartsInput{
Bucket: setting.Bucket,
Key: input.Key,
UploadId: uploadID,
MaxParts: MAX_LIST_PARTS,
PartNumberMarker: partNumberMarker,
})
if err != nil {
log.Error("ListParts failed:", err.Error())
return err
}

partNumberMarker = output.NextPartNumberMarker
log.Info("uuid:%s, MaxParts:%d, PartNumberMarker:%d, NextPartNumberMarker:%d, len:%d", uuid, output.MaxParts, output.PartNumberMarker, output.NextPartNumberMarker, len(output.Parts))

for _, partInfo := range output.Parts {
input.Parts = append(input.Parts, obs.Part{
PartNumber: partInfo.PartNumber,
ETag: partInfo.ETag,
})
}

if len(output.Parts) < output.MaxParts {
break
} else {
continue
}

break
}

_, err = ObsCli.CompleteMultipartUpload(input)
output, err := ObsCli.CompleteMultipartUpload(input)
if err != nil {
log.Error("CompleteMultipartUpload failed:", err.Error())
return err
}

log.Info("uuid:%s, RequestId:%s", uuid, output.RequestId)

return nil
}



+ 8
- 0
modules/structs/repo_topic.go View File

@@ -17,6 +17,14 @@ type TopicResponse struct {
Updated time.Time `json:"updated"`
}

type ImageTopicResponse struct {
ID int64 `json:"id"`
Name string `json:"topic_name"`
ImageCount int `json:"image_count"`
Created time.Time `json:"created"`
Updated time.Time `json:"updated"`
}

// TopicName a list of repo topic names
type TopicName struct {
TopicNames []string `json:"topics"`


+ 5
- 1
modules/templates/helper.go View File

@@ -92,6 +92,7 @@ func NewFuncMap() []template.FuncMap {
"Safe": Safe,
"SafeJS": SafeJS,
"Str2html": Str2html,
"subOne": subOne,
"TimeSince": timeutil.TimeSince,
"TimeSinceUnix": timeutil.TimeSinceUnix,
"TimeSinceUnix1": timeutil.TimeSinceUnix1,
@@ -443,7 +444,10 @@ func SafeJS(raw string) template.JS {
func Str2html(raw string) template.HTML {
return template.HTML(markup.Sanitize(raw))
}

//
func subOne(length int)int{
return length-1
}
// Escape escapes a HTML string
func Escape(raw string) string {
return html.EscapeString(raw)


+ 21
- 4
options/locale/locale_en-US.ini View File

@@ -742,7 +742,7 @@ dataset_setting= Dataset Setting
title = Name
title_format_err=Name can only contain number,letter,'-','_' or '.', and can be up to 100 characters long.
description = Description
description_format_err=Description's length can be up to 1024 characters long.
description_format_err=Description's length can be up to %s characters long.
create_dataset = Create Dataset
create_dataset_fail=Failed to create dataset.
query_dataset_fail=Failed to query dataset.
@@ -895,7 +895,7 @@ readme_helper = Select a README file template.
auto_init = Initialize Repository (Adds .gitignore, License and README)
create_repo = Create Repository
create_course = Publish Course
failed_to_create_course=Fail to publish course, please try again later.
failed_to_create_course=Failed to publish course, please try again later.
default_branch = Default Branch
mirror_prune = Prune
mirror_prune_desc = Remove obsolete remote-tracking references
@@ -935,9 +935,25 @@ more=More
gpu_type_all=All
model_download=Model Download
submit_image=Submit Image
modify_image=Modify Image
image_exist=Image name has been used, please use a new one.
image_committing=Image is submitting, please try again later.
image_commit_fail=Failed to submit image, please try again later.
image_not_exist=Image does not exits.
image_edit_fail=Failed to edit image, please try again later.
image_delete_fail=Failed to delete image, please try again later.
image_overwrite=You had submitted the same name image before, are you sure to overwrite the original image?
download=Download
score=Score

images.name = Image Tag
images.name_placerholder = Please enter the image name
image.label_tooltips = Example Python 3.7, Tensorflow 2.0, cuda 10, pytorch 1.6
images.public_tooltips = After the image is set to public, it can be seen by other users.
images.name_rule = Please enter letters, numbers, _ and - up to 64 characters and cannot end with a dash (-).
images.delete_task = Delete image
images.task_delete_confirm = Are you sure you want to delete this image? Once this image is deleted, it cannot be recovered.

cloudbrain=Cloudbrain
cloudbrain.task = Cloudbrain Task
cloudbrain.search = Seach Task Name
@@ -973,7 +989,7 @@ total_count_get_error=Can not get the total page.
last_update_time_error=Can not get the last updated time.
get_repo_stat_error=Can not get the statistics of the repository.
get_repo_info_error=Can not get the information of the repository.
generate_statistic_file_error=Fail to generate file.
generate_statistic_file_error=Failed to generate file.
repo_stat_inspect=ProjectAnalysis
all=All

@@ -1123,7 +1139,7 @@ form.name_reserved = The repository name '%s' is reserved.
form.course_name_reserved=The course name '%s' is reserved.
form.name_pattern_not_allowed = The pattern '%s' is not allowed in a repository name.
form.course_name_pattern_not_allowed=The pattern '%s' is not allowed in a course name.
add_course_org_fail=Fail to add organization, please try again later.
add_course_org_fail=Failed to add organization, please try again later.

need_auth = Clone Authorization
migrate_type = Migration Type
@@ -2167,6 +2183,7 @@ topic.manage_topics = Manage Topics
topic.done = Done
topic.count_prompt = You can not select more than 25 topics
topic.format_prompt = Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.
imagetopic.format_prompt = Topics can be up to 35 characters long.

[org]
org_name_holder = Organization Name


+ 1
- 0
public/img/jian.svg View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="15.999999999999432" height="16.000000000000455"><path d="M0 14L0 2Q0 1.90175 0.00963055 1.80397Q0.0192611 1.70619 0.0384294 1.60982Q0.0575978 1.51345 0.0861193 1.41943Q0.114641 1.32541 0.152241 1.23463Q0.189841 1.14386 0.236157 1.05721Q0.282474 0.970554 0.337061 0.888859Q0.391648 0.807164 0.453979 0.731213Q0.516311 0.655262 0.585786 0.585786Q0.655262 0.516311 0.731213 0.453979Q0.807164 0.391648 0.888859 0.337061Q0.970554 0.282474 1.05721 0.236157Q1.14386 0.189841 1.23463 0.152241Q1.32541 0.114641 1.41943 0.0861193Q1.51345 0.0575978 1.60982 0.0384294Q1.70619 0.0192611 1.80397 0.00963055Q1.90175 0 2 0L14 0Q14.0983 0 14.196 0.00963055Q14.2938 0.0192611 14.3902 0.0384294Q14.4865 0.0575978 14.5806 0.0861193Q14.6746 0.114641 14.7654 0.152241Q14.8561 0.189841 14.9428 0.236157Q15.0294 0.282474 15.1111 0.337061Q15.1928 0.391648 15.2688 0.453979Q15.3447 0.516311 15.4142 0.585786Q15.4837 0.655262 15.546 0.731213Q15.6084 0.807164 15.6629 0.888859Q15.7175 0.970554 15.7638 1.05721Q15.8102 1.14386 15.8478 1.23463Q15.8854 1.32541 15.9139 1.41943Q15.9424 1.51345 15.9616 1.60982Q15.9807 1.70619 15.9904 1.80397Q16 1.90175 16 2L16 14Q16 14.0983 15.9904 14.196Q15.9807 14.2938 15.9616 14.3902Q15.9424 14.4865 15.9139 14.5806Q15.8854 14.6746 15.8478 14.7654Q15.8102 14.8561 15.7638 14.9428Q15.7175 15.0294 15.6629 15.1111Q15.6084 15.1928 15.546 15.2688Q15.4837 15.3447 15.4142 15.4142Q15.3447 15.4837 15.2688 15.546Q15.1928 15.6084 15.1111 15.6629Q15.0294 15.7175 14.9428 15.7638Q14.8561 15.8102 14.7654 15.8478Q14.6746 15.8854 14.5806 15.9139Q14.4865 15.9424 14.3902 15.9616Q14.2938 15.9807 14.196 15.9904Q14.0983 16 14 16L2 16Q1.90175 16 1.80397 15.9904Q1.70619 15.9807 1.60982 15.9616Q1.51345 15.9424 1.41943 15.9139Q1.32541 15.8854 1.23463 15.8478Q1.14386 15.8102 1.05721 15.7638Q0.970554 15.7175 0.888859 15.6629Q0.807164 15.6084 0.731213 15.546Q0.655262 15.4837 0.585786 15.4142Q0.516311 15.3447 0.453979 15.2688Q0.391648 15.1928 0.337061 15.1111Q0.282474 15.0294 0.236157 14.9428Q0.189841 14.8561 0.152241 14.7654Q0.114641 14.6746 0.0861193 14.5806Q0.0575978 14.4865 0.0384294 14.3902Q0.0192611 14.2938 0.00963055 14.196Q0 14.0983 0 14Z" style="mix-blend-mode:normal" fill="#5ab06f"></path><path d="M354.396 154.975L354.796 154.975L355.196 154.975L355.196 154.067L358.383 154.067L358.383 154.975L358.783 154.975L359.182 154.975L359.182 154.067L361.714 154.067L361.714 153.322L359.182 153.322L359.182 152.479L358.383 152.479L358.383 153.322L355.196 153.322L355.196 152.479L354.396 152.479L354.396 153.322L351.865 153.322L351.865 154.067L354.396 154.067L354.396 154.975ZM361.613 155.88L361.613 156.251L358.544 156.251L355.475 156.251C355.12 156.874 354.708 157.452 354.264 157.983L354.264 162.601L353.486 162.601L353.486 160.704L353.486 158.807C353.066 159.216 352.632 159.593 352.155 159.904C352.055 159.75 351.768 159.406 351.589 159.248C352.765 158.527 353.787 157.495 354.564 156.251L353.26 156.251L351.955 156.251L351.955 155.508L354.995 155.508C355.174 155.175 355.34 154.831 355.475 154.476L356.261 154.687C356.139 154.964 356.017 155.243 355.884 155.508L361.613 155.508L361.613 155.88ZM361.757 159.384L360.303 159.384L358.849 159.384L358.849 158.929C359.592 158.56 360.391 158.04 360.947 157.517L360.448 157.129L360.291 157.172L355.863 157.172L355.863 157.839L357.672 157.839L359.48 157.839C359.047 158.14 358.527 158.438 358.06 158.649L358.06 159.384L356.54 159.384L355.02 159.384L355.02 160.105L358.06 160.105L358.06 161.704C358.06 161.836 358.003 161.869 357.838 161.88C357.695 161.891 357.128 161.901 356.508 161.88C356.616 162.08 356.741 162.357 356.784 162.579C357.594 162.579 358.104 162.579 358.437 162.457C358.76 162.335 358.849 162.145 358.849 161.714L358.849 160.105L361.757 160.105L361.757 159.384Z" style="mix-blend-mode:normal" fill-rule="evenodd" fill="#ffffff" transform="translate(-348.8095656435228, -149.81200615956308)"></path></svg>

+ 7
- 0
routers/admin/cloudbrains.go View File

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

const (
tplCloudBrains base.TplName = "admin/cloudbrain/list"
tplImages base.TplName = "admin/cloudbrain/images"
EXCEL_DATE_FORMAT = "20060102150405"
CREATE_TIME_FORMAT = "2006/01/02 15:04:05"
)
@@ -107,6 +108,12 @@ func CloudBrains(ctx *context.Context) {

}

func Images(ctx *context.Context) {
ctx.Data["PageIsAdminImages"] = true
ctx.HTML(200, tplImages)

}

func DownloadCloudBrains(ctx *context.Context) {

page := 1


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

@@ -1011,6 +1011,9 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Group("/topics", func() {
m.Get("/search", repo.TopicSearch)
})
m.Group("/image/topics", func() {
m.Get("/search", repo.ImageTopicSearch)
})
m.Group("/from_wechat", func() {
m.Get("/event", authentication.ValidEventSource)
m.Post("/event", authentication.AcceptWechatEvent)


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

@@ -74,6 +74,7 @@ func GetModelArtsNotebook2(ctx *context.APIContext) {
if job.EndTime == 0 && models.IsModelArtsDebugJobTerminal(job.Status) {
job.EndTime = timeutil.TimeStampNow()
}
job.CorrectCreateUnix()
job.ComputeAndSetDuration()
err = models.UpdateJob(job)
if err != nil {
@@ -160,6 +161,7 @@ func GetModelArtsTrainJobVersion(ctx *context.APIContext) {
}

if result.JobStatus.State != string(models.JobWaiting) {
models.ParseAndSetDurationFromCloudBrainOne(result, job)
err = models.UpdateJob(job)
if err != nil {
log.Error("UpdateJob failed:", err)
@@ -177,14 +179,12 @@ func GetModelArtsTrainJobVersion(ctx *context.APIContext) {
}
job.Status = modelarts.TransTrainJobStatus(result.IntStatus)
job.Duration = result.Duration / 1000
job.TrainJobDuration = result.TrainJobDuration

job.TrainJobDuration = models.ConvertDurationToStr(job.Duration)

if job.EndTime == 0 && models.IsTrainJobTerminal(job.Status) && job.StartTime > 0 {
job.EndTime = job.StartTime.Add(job.Duration)
}
job.CorrectCreateUnix()
err = models.UpdateTrainJobVersion(job)
if err != nil {
log.Error("UpdateJob failed:", err)
@@ -417,7 +417,7 @@ func GetModelArtsInferenceJob(ctx *context.APIContext) {
if job.EndTime == 0 && models.IsTrainJobTerminal(job.Status) && job.StartTime > 0 {
job.EndTime = job.StartTime.Add(job.Duration)
}
job.CorrectCreateUnix()
err = models.UpdateInferenceJob(job)
if err != nil {
log.Error("UpdateJob failed:", err)


+ 60
- 0
routers/api/v1/repo/topic.go View File

@@ -300,3 +300,63 @@ func TopicSearch(ctx *context.APIContext) {
"topics": topicResponses,
})
}

func ImageTopicSearch(ctx *context.APIContext) {
// swagger:operation GET /image/topics/search image topicSearch
// ---
// summary: search topics via keyword
// produces:
// - application/json
// parameters:
// - name: q
// in: query
// description: keywords to search
// required: true
// type: string
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results, maximum page size is 50
// type: integer
// responses:
// "200":
// "$ref": "#/responses/TopicListResponse"
// "403":
// "$ref": "#/responses/forbidden"

if ctx.User == nil {
ctx.Error(http.StatusForbidden, "UserIsNil", "Only owners could change the topics.")
return
}

kw := ctx.Query("q")

listOptions := utils.GetListOptions(ctx)
if listOptions.Page < 1 {
listOptions.Page = 1
}
if listOptions.PageSize < 1 {
listOptions.PageSize = 10
}

topics, err := models.FindImageTopics(&models.FindImageTopicOptions{
Keyword: kw,
ListOptions: listOptions,
})
if err != nil {
log.Error("SearchImageTopics failed: %v", err)
ctx.InternalServerError(err)
return
}

topicResponses := make([]*api.ImageTopicResponse, len(topics))
for i, topic := range topics {
topicResponses[i] = convert.ToImageTopicResponse(topic)
}
ctx.JSON(http.StatusOK, map[string]interface{}{
"topics": topicResponses,
})
}

+ 30
- 0
routers/image/image.go View File

@@ -0,0 +1,30 @@
package image

import (
"net/http"
"strconv"

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

func Action(ctx *context.Context) {
var err error
imageId, _ := strconv.ParseInt(ctx.Params(":id"), 10, 64)
switch ctx.Params(":action") {

case "star":
err = models.StarImage(ctx.User.ID, imageId, true)
case "unstar":
err = models.StarImage(ctx.User.ID, imageId, false)
case "recommend":
err = models.RecommendImage(imageId, true)
case "unrecommend":
err = models.RecommendImage(imageId, false)
}
if err != nil {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("repo.star_fail", ctx.Params(":action"))))
} else {
ctx.JSON(http.StatusOK, models.BaseOKMessage)
}
}

+ 16
- 10
routers/repo/attachment.go View File

@@ -78,7 +78,7 @@ func UploadAttachmentUI(ctx *context.Context) {
}

func EditAttachmentUI(ctx *context.Context) {
id, _ := strconv.ParseInt(ctx.Params(":id"), 10, 64)
ctx.Data["PageIsDataset"] = true
attachment, _ := models.GetAttachmentByID(id)
@@ -986,23 +986,29 @@ func HandleUnDecompressAttachment() {
if attach.Type == models.TypeCloudBrainOne {
err = worker.SendDecompressTask(contexExt.Background(), attach.UUID, attach.Name)
if err != nil {
log.Error("SendDecompressTask(%s) failed:%s", attach.UUID, err.Error())
log.Error("SendDecompressTask(%s) failed:%s", attach.UUID, err.Error())
} else {
attach.DecompressState = models.DecompressStateIng
err = models.UpdateAttachment(attach)
if err != nil {
log.Error("UpdateAttachment state(%s) failed:%s", attach.UUID, err.Error())
}
updateAttachmentDecompressStateIng(attach)
}
} else if attach.Type == models.TypeCloudBrainTwo {
attachjson, _ := json.Marshal(attach)
labelmsg.SendDecompressAttachToLabelOBS(string(attachjson))
err = labelmsg.SendDecompressAttachToLabelOBS(string(attachjson))
if err != nil {
log.Error("SendDecompressTask to labelsystem (%s) failed:%s", attach.UUID, err.Error())
} else {
updateAttachmentDecompressStateIng(attach)
}
}

}

return
}
func updateAttachmentDecompressStateIng(attach *models.Attachment) {
attach.DecompressState = models.DecompressStateIng
err := models.UpdateAttachment(attach)
if err != nil {
log.Error("UpdateAttachment state(%s) failed:%s", attach.UUID, err.Error())
}
}

func QueryAllPublicDataset(ctx *context.Context) {
attachs, err := models.GetAllPublicAttachments()


+ 244
- 26
routers/repo/cloudbrain.go View File

@@ -13,6 +13,7 @@ import (
"strconv"
"strings"
"time"
"unicode/utf8"

"code.gitea.io/gitea/modules/timeutil"
"github.com/unknwon/i18n"
@@ -39,8 +40,13 @@ const (
tplCloudBrainBenchmarkNew base.TplName = "repo/cloudbrain/benchmark/new"
tplCloudBrainBenchmarkShow base.TplName = "repo/cloudbrain/benchmark/show"

tplCloudBrainImageSubmit base.TplName = "repo/cloudbrain/image/submit"
tplCloudBrainImageEdit base.TplName = "repo/cloudbrain/image/edit"


tplCloudBrainTrainJobNew base.TplName = "repo/cloudbrain/trainjob/new"
tplCloudBrainTrainJobShow base.TplName = "repo/cloudbrain/trainjob/show"

)

var (
@@ -603,26 +609,165 @@ func CloudBrainDebug(ctx *context.Context) {
ctx.Redirect(debugUrl)
}

func CloudBrainCommitImageShow(ctx *context.Context) {
ctx.Data["PageIsCloudBrain"] = true
ctx.Data["Type"] = ctx.Cloudbrain.Type
ctx.HTML(200, tplCloudBrainImageSubmit)
}

func CloudBrainImageEdit(ctx *context.Context) {
ctx.Data["PageIsImageEdit"] = true
ctx.Data["PageFrom"] = ctx.Params(":from")
var ID = ctx.Params(":id")
id, err := strconv.ParseInt(ID, 10, 64)
if err != nil {
log.Error("GetImageByID failed:%v", err.Error())
ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
}
image, err := models.GetImageByID(id)
if err != nil {
log.Error("GetImageByID failed:%v", err.Error())
ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
}
ctx.Data["Image"] = image
ctx.HTML(http.StatusOK, tplCloudBrainImageEdit)

}

func CloudBrainImageEditPost(ctx *context.Context, form auth.EditImageCloudBrainForm) {

if utf8.RuneCountInString(form.Description) > 255 {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.description_format_err", 255)))
return
}

validTopics, errMessage := checkTopics(form.Topics)
if errMessage != "" {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr(errMessage)))
return
}
image, err := models.GetImageByID(form.ID)
if err != nil {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("repo.image_not_exist")))

}

image.IsPrivate = form.IsPrivate
image.Description = form.Description

err = models.WithTx(func(ctx models.DBContext) error {
if err := models.UpdateLocalImage(image); err != nil {
return err
}
if err := models.SaveImageTopics(image.ID, validTopics...); err != nil {
return err
}
return nil

})

if err != nil {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("repo.image_not_exist")))

} else {
ctx.JSON(http.StatusOK, models.BaseOKMessage)
}

}

func CloudBrainImageDelete(ctx *context.Context) {
var ID = ctx.Params(":id")
id, err := strconv.ParseInt(ID, 10, 64)
if err != nil {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("repo.image_not_exist")))
return
}

err = models.DeleteLocalImage(id)
if err != nil {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("repo.image_delete_fail")))
} else {
ctx.JSON(http.StatusOK, models.BaseOKMessage)
}

}

func CloudBrainCommitImageCheck(ctx *context.Context, form auth.CommitImageCloudBrainForm) {
isExist, _ := models.IsImageExistByUser(form.Tag, ctx.User.ID)
if isExist {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("repo.image_overwrite")))
} else {
ctx.JSON(http.StatusOK, models.BaseOKMessage)
}

}

func CloudBrainCommitImage(ctx *context.Context, form auth.CommitImageCloudBrainForm) {

if !NamePattern.MatchString(form.Tag) {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.title_format_err")))
return
}

if utf8.RuneCountInString(form.Description) > 255 {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.description_format_err", 255)))
return
}

validTopics, errMessage := checkTopics(form.Topics)
if errMessage != "" {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr(errMessage)))
return
}

err := cloudbrain.CommitImage(ctx.Cloudbrain.JobID, models.CommitImageParams{
Ip: ctx.Cloudbrain.ContainerIp,
TaskContainerId: ctx.Cloudbrain.ContainerID,
ImageDescription: form.Description,
ImageTag: form.Tag,
CommitImageCloudBrainParams: models.CommitImageCloudBrainParams{
Ip: ctx.Cloudbrain.ContainerIp,
TaskContainerId: ctx.Cloudbrain.ContainerID,
ImageDescription: form.Description,
ImageTag: form.Tag,
},
IsPrivate: form.IsPrivate,
CloudBrainType: form.Type,
Topics: validTopics,
UID: ctx.User.ID,
})
if err != nil {
log.Error("CommitImage(%s) failed:%v", ctx.Cloudbrain.JobName, err.Error(), ctx.Data["msgID"])
ctx.JSON(200, map[string]string{
"result_code": "-1",
"error_msg": "CommitImage failed",
})
if models.IsErrImageTagExist(err) {
ctx.JSON(200, models.BaseErrorMessage(ctx.Tr("repo.image_exist")))

} else if models.IsErrorImageCommitting(err) {
ctx.JSON(200, models.BaseErrorMessage(ctx.Tr("repo.image_committing")))
} else {
ctx.JSON(200, models.BaseErrorMessage(ctx.Tr("repo.image_commit_fail")))
}

return
}

ctx.JSON(200, map[string]string{
"result_code": "0",
"error_msg": "",
})
ctx.JSON(200, models.BaseOKMessage)
}

func checkTopics(Topics string) ([]string, string) {
var topics = make([]string, 0)
var topicsStr = strings.TrimSpace(Topics)
if len(topicsStr) > 0 {
topics = strings.Split(topicsStr, ",")
}

validTopics, invalidTopics := models.SanitizeAndValidateImageTopics(topics)

if len(validTopics) > 25 {
return nil, "repo.topic.count_prompt"

}

if len(invalidTopics) > 0 {
return nil, "repo.imagetopic.format_prompt"

}
return validTopics, ""
}

func CloudBrainStop(ctx *context.Context) {
@@ -842,35 +987,106 @@ func CloudBrainShowModels(ctx *context.Context) {
}

func GetPublicImages(ctx *context.Context) {
uid := getUID(ctx)
opts := models.SearchImageOptions{
IncludePublicOnly: true,
UID: uid,
Keyword: ctx.Query("q"),
Topics: ctx.Query("topic"),
IncludeOfficialOnly: ctx.QueryBool("recommend"),
SearchOrderBy: "type desc, num_stars desc,id desc",
Status: models.IMAGE_STATUS_SUCCESS,
}

getImages(ctx, cloudbrain.Public)
getImages(ctx, &opts)

}

func GetCustomImages(ctx *context.Context) {
uid := getUID(ctx)
opts := models.SearchImageOptions{
UID: uid,
IncludeOwnerOnly: true,
Keyword: ctx.Query("q"),
Topics: ctx.Query("topic"),
Status: -1,
SearchOrderBy: "id desc",
}
getImages(ctx, &opts)

getImages(ctx, cloudbrain.Custom)
}
func GetStarImages(ctx *context.Context) {

uid := getUID(ctx)
opts := models.SearchImageOptions{
UID: uid,
IncludeStarByMe: true,
Keyword: ctx.Query("q"),
Topics: ctx.Query("topic"),
Status: models.IMAGE_STATUS_SUCCESS,
SearchOrderBy: "id desc",
}
getImages(ctx, &opts)

}

func getUID(ctx *context.Context) int64 {
var uid int64 = -1
if ctx.IsSigned {
uid = ctx.User.ID
}
return uid
}

func getImages(ctx *context.Context, imageType string) {
log.Info("Get images begin")
func GetAllImages(ctx *context.Context) {
uid := getUID(ctx)
opts := models.SearchImageOptions{
UID: uid,
Keyword: ctx.Query("q"),
Topics: ctx.Query("topic"),
IncludeOfficialOnly: ctx.QueryBool("recommend"),
SearchOrderBy: "id desc",
Status: -1,
}

if ctx.Query("private") != "" {
if ctx.QueryBool("private") {
opts.IncludePrivateOnly = true
} else {
opts.IncludePublicOnly = true
}
}
getImages(ctx, &opts)

}

func getImages(ctx *context.Context, opts *models.SearchImageOptions) {
page := ctx.QueryInt("page")
size := ctx.QueryInt("size")
name := ctx.Query("name")
getImagesResult, err := cloudbrain.GetImagesPageable(page, size, imageType, name)
if page <= 0 {
page = 1
}

pageSize := ctx.QueryInt("pageSize")
if pageSize <= 0 {
pageSize = 15
}
opts.ListOptions = models.ListOptions{
Page: page,
PageSize: pageSize,
}
imageList, total, err := models.SearchImage(opts)
if err != nil {
log.Error("Can not get images:%v", err)
ctx.JSON(http.StatusOK, models.GetImagesPayload{
Count: 0,
TotalPages: 0,
ImageInfo: []*models.ImageInfo{},
ctx.JSON(http.StatusOK, models.ImagesPageResult{
Count: 0,
Images: []*models.Image{},
})
} else {
ctx.JSON(http.StatusOK, getImagesResult.Payload)
ctx.JSON(http.StatusOK, models.ImagesPageResult{
Count: total,
Images: imageList,
})
}
log.Info("Get images end")
}

func GetModelDirs(jobName string, parentDir string) (string, error) {
@@ -1173,6 +1389,7 @@ func SyncCloudbrainStatus() {
if task.EndTime == 0 && models.IsModelArtsDebugJobTerminal(task.Status) {
task.EndTime = timeutil.TimeStampNow()
}
task.CorrectCreateUnix()
task.ComputeAndSetDuration()
err = models.UpdateJob(task)
if err != nil {
@@ -1199,7 +1416,7 @@ func SyncCloudbrainStatus() {
if task.EndTime == 0 && models.IsTrainJobTerminal(task.Status) && task.StartTime > 0 {
task.EndTime = task.StartTime.Add(task.Duration)
}
task.CorrectCreateUnix()
err = models.UpdateJob(task)
if err != nil {
log.Error("UpdateJob(%s) failed:%v", task.JobName, err)
@@ -1321,6 +1538,7 @@ func handleNoDurationTask(cloudBrains []*models.Cloudbrain) {
task.StartTime = timeutil.TimeStamp(startTime / 1000)
task.EndTime = task.StartTime.Add(duration)
}
task.CorrectCreateUnix()
task.ComputeAndSetDuration()
err = models.UpdateJob(task)
if err != nil {


+ 4
- 7
routers/repo/dataset.go View File

@@ -4,7 +4,6 @@ import (
"encoding/json"
"fmt"
"net/http"
"regexp"
"sort"
"strconv"
"strings"
@@ -25,8 +24,6 @@ const (
taskstplIndex base.TplName = "repo/datasets/tasks/index"
)

var titlePattern = regexp.MustCompile(`^[A-Za-z0-9-_\\.]{1,100}$`)

// MustEnableDataset check if repository enable internal dataset
func MustEnableDataset(ctx *context.Context) {
if !ctx.Repo.CanRead(models.UnitTypeDatasets) {
@@ -211,12 +208,12 @@ func CreateDatasetPost(ctx *context.Context, form auth.CreateDatasetForm) {

dataset := &models.Dataset{}

if !titlePattern.MatchString(form.Title) {
if !NamePattern.MatchString(form.Title) {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.title_format_err")))
return
}
if utf8.RuneCountInString(form.Description) > 1024 {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.description_format_err")))
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.description_format_err", 1024)))
return
}

@@ -248,12 +245,12 @@ func EditDatasetPost(ctx *context.Context, form auth.EditDatasetForm) {

ctx.Data["Title"] = ctx.Tr("dataset.edit_dataset")

if !titlePattern.MatchString(form.Title) {
if !NamePattern.MatchString(form.Title) {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.title_format_err")))
return
}
if utf8.RuneCountInString(form.Description) > 1024 {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.description_format_err")))
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.description_format_err", 1024)))
return
}



+ 2
- 0
routers/repo/modelarts.go View File

@@ -267,6 +267,7 @@ func NotebookShow(ctx *context.Context) {
if task.DeletedAt.IsZero() { //normal record
if task.Status != result.Status {
task.Status = result.Status
models.ParseAndSetDurationFromModelArtsNotebook(result, task)
err = models.UpdateJob(task)
if err != nil {
ctx.Data["error"] = err.Error()
@@ -2046,6 +2047,7 @@ func InferenceJobNew(ctx *context.Context) {
}
func inferenceJobNewDataPrepare(ctx *context.Context) error {
ctx.Data["PageIsCloudBrain"] = true
ctx.Data["newInference"] = true

t := time.Now()
var displayJobName = cutString(ctx.User.Name, 5) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:]


+ 72
- 73
routers/repo/user_data_analysis.go View File

@@ -41,24 +41,24 @@ func queryUserDataPage(ctx *context.Context, tableName string, queryObj interfac
"A1": ctx.Tr("user.static.id"),
"B1": ctx.Tr("user.static.name"),
"C1": ctx.Tr("user.static.codemergecount"),
"D1": ctx.Tr("user.static.commitcount"),
"E1": ctx.Tr("user.static.issuecount"),
"F1": ctx.Tr("user.static.commentcount"),
"G1": ctx.Tr("user.static.focusrepocount"),
"H1": ctx.Tr("user.static.starrepocount"),
"I1": ctx.Tr("user.static.logincount"),
"J1": ctx.Tr("user.static.watchedcount"),
"K1": ctx.Tr("user.static.commitcodesize"),
"L1": ctx.Tr("user.static.solveissuecount"),
"M1": ctx.Tr("user.static.encyclopediascount"),
"N1": ctx.Tr("user.static.createrepocount"),
"O1": ctx.Tr("user.static.openiindex"),
"P1": ctx.Tr("user.static.registdate"),
"Q1": ctx.Tr("user.static.CloudBrainTaskNum"),
"R1": ctx.Tr("user.static.CloudBrainRunTime"),
"S1": ctx.Tr("user.static.CommitDatasetNum"),
"T1": ctx.Tr("user.static.CommitModelCount"),
"U1": ctx.Tr("user.static.UserIndex"),
"D1": ctx.Tr("user.static.UserIndex"),
"E1": ctx.Tr("user.static.commitcount"),
"F1": ctx.Tr("user.static.issuecount"),
"G1": ctx.Tr("user.static.commentcount"),
"H1": ctx.Tr("user.static.focusrepocount"),
"I1": ctx.Tr("user.static.starrepocount"),
"J1": ctx.Tr("user.static.logincount"),
"K1": ctx.Tr("user.static.watchedcount"),
"L1": ctx.Tr("user.static.commitcodesize"),
"M1": ctx.Tr("user.static.solveissuecount"),
"N1": ctx.Tr("user.static.encyclopediascount"),
"O1": ctx.Tr("user.static.createrepocount"),
"P1": ctx.Tr("user.static.openiindex"),
"Q1": ctx.Tr("user.static.registdate"),
"R1": ctx.Tr("user.static.CloudBrainTaskNum"),
"S1": ctx.Tr("user.static.CloudBrainRunTime"),
"T1": ctx.Tr("user.static.CommitDatasetNum"),
"U1": ctx.Tr("user.static.CommitModelCount"),
"V1": ctx.Tr("user.static.countdate"),
}
for k, v := range dataHeader {
@@ -78,28 +78,27 @@ func queryUserDataPage(ctx *context.Context, tableName string, queryObj interfac
xlsx.SetCellValue(sheetName, "A"+rows, userRecord.ID)
xlsx.SetCellValue(sheetName, "B"+rows, userRecord.Name)
xlsx.SetCellValue(sheetName, "C"+rows, userRecord.CodeMergeCount)
xlsx.SetCellValue(sheetName, "D"+rows, userRecord.CommitCount)
xlsx.SetCellValue(sheetName, "E"+rows, userRecord.IssueCount)
xlsx.SetCellValue(sheetName, "F"+rows, userRecord.CommentCount)
xlsx.SetCellValue(sheetName, "G"+rows, userRecord.FocusRepoCount)
xlsx.SetCellValue(sheetName, "H"+rows, userRecord.StarRepoCount)
xlsx.SetCellValue(sheetName, "I"+rows, userRecord.LoginCount)
xlsx.SetCellValue(sheetName, "J"+rows, userRecord.WatchedCount)
xlsx.SetCellValue(sheetName, "K"+rows, userRecord.CommitCodeSize)
xlsx.SetCellValue(sheetName, "L"+rows, userRecord.SolveIssueCount)
xlsx.SetCellValue(sheetName, "M"+rows, userRecord.EncyclopediasCount)
xlsx.SetCellValue(sheetName, "N"+rows, userRecord.CreateRepoCount)
xlsx.SetCellValue(sheetName, "O"+rows, fmt.Sprintf("%.2f", userRecord.OpenIIndex))
xlsx.SetCellValue(sheetName, "D"+rows, fmt.Sprintf("%.2f", userRecord.UserIndex))
xlsx.SetCellValue(sheetName, "E"+rows, userRecord.CommitCount)
xlsx.SetCellValue(sheetName, "F"+rows, userRecord.IssueCount)
xlsx.SetCellValue(sheetName, "G"+rows, userRecord.CommentCount)
xlsx.SetCellValue(sheetName, "H"+rows, userRecord.FocusRepoCount)
xlsx.SetCellValue(sheetName, "I"+rows, userRecord.StarRepoCount)
xlsx.SetCellValue(sheetName, "J"+rows, userRecord.LoginCount)
xlsx.SetCellValue(sheetName, "K"+rows, userRecord.WatchedCount)
xlsx.SetCellValue(sheetName, "L"+rows, userRecord.CommitCodeSize)
xlsx.SetCellValue(sheetName, "M"+rows, userRecord.SolveIssueCount)
xlsx.SetCellValue(sheetName, "N"+rows, userRecord.EncyclopediasCount)
xlsx.SetCellValue(sheetName, "O"+rows, userRecord.CreateRepoCount)
xlsx.SetCellValue(sheetName, "P"+rows, fmt.Sprintf("%.2f", userRecord.OpenIIndex))

formatTime := userRecord.RegistDate.Format("2006-01-02 15:04:05")
xlsx.SetCellValue(sheetName, "P"+rows, formatTime[0:len(formatTime)-3])

xlsx.SetCellValue(sheetName, "Q"+rows, userRecord.CloudBrainTaskNum)
xlsx.SetCellValue(sheetName, "R"+rows, fmt.Sprintf("%.2f", float64(userRecord.CloudBrainRunTime)/3600))
xlsx.SetCellValue(sheetName, "S"+rows, userRecord.CommitDatasetNum)
xlsx.SetCellValue(sheetName, "T"+rows, userRecord.CommitModelCount)
xlsx.SetCellValue(sheetName, "U"+rows, fmt.Sprintf("%.2f", userRecord.UserIndex))
xlsx.SetCellValue(sheetName, "Q"+rows, formatTime[0:len(formatTime)-3])

xlsx.SetCellValue(sheetName, "R"+rows, userRecord.CloudBrainTaskNum)
xlsx.SetCellValue(sheetName, "S"+rows, fmt.Sprintf("%.2f", float64(userRecord.CloudBrainRunTime)/3600))
xlsx.SetCellValue(sheetName, "T"+rows, userRecord.CommitDatasetNum)
xlsx.SetCellValue(sheetName, "U"+rows, userRecord.CommitModelCount)
formatTime = userRecord.DataDate
xlsx.SetCellValue(sheetName, "V"+rows, formatTime)
}
@@ -243,24 +242,24 @@ func QueryUserStaticDataPage(ctx *context.Context) {
"A1": ctx.Tr("user.static.id"),
"B1": ctx.Tr("user.static.name"),
"C1": ctx.Tr("user.static.codemergecount"),
"D1": ctx.Tr("user.static.commitcount"),
"E1": ctx.Tr("user.static.issuecount"),
"F1": ctx.Tr("user.static.commentcount"),
"G1": ctx.Tr("user.static.focusrepocount"),
"H1": ctx.Tr("user.static.starrepocount"),
"I1": ctx.Tr("user.static.logincount"),
"J1": ctx.Tr("user.static.watchedcount"),
"K1": ctx.Tr("user.static.commitcodesize"),
"L1": ctx.Tr("user.static.solveissuecount"),
"M1": ctx.Tr("user.static.encyclopediascount"),
"N1": ctx.Tr("user.static.createrepocount"),
"O1": ctx.Tr("user.static.openiindex"),
"P1": ctx.Tr("user.static.registdate"),
"Q1": ctx.Tr("user.static.CloudBrainTaskNum"),
"R1": ctx.Tr("user.static.CloudBrainRunTime"),
"S1": ctx.Tr("user.static.CommitDatasetNum"),
"T1": ctx.Tr("user.static.CommitModelCount"),
"U1": ctx.Tr("user.static.UserIndex"),
"D1": ctx.Tr("user.static.UserIndex"),
"E1": ctx.Tr("user.static.commitcount"),
"F1": ctx.Tr("user.static.issuecount"),
"G1": ctx.Tr("user.static.commentcount"),
"H1": ctx.Tr("user.static.focusrepocount"),
"I1": ctx.Tr("user.static.starrepocount"),
"J1": ctx.Tr("user.static.logincount"),
"K1": ctx.Tr("user.static.watchedcount"),
"L1": ctx.Tr("user.static.commitcodesize"),
"M1": ctx.Tr("user.static.solveissuecount"),
"N1": ctx.Tr("user.static.encyclopediascount"),
"O1": ctx.Tr("user.static.createrepocount"),
"P1": ctx.Tr("user.static.openiindex"),
"Q1": ctx.Tr("user.static.registdate"),
"R1": ctx.Tr("user.static.CloudBrainTaskNum"),
"S1": ctx.Tr("user.static.CloudBrainRunTime"),
"T1": ctx.Tr("user.static.CommitDatasetNum"),
"U1": ctx.Tr("user.static.CommitModelCount"),
"V1": ctx.Tr("user.static.countdate"),
}
for k, v := range dataHeader {
@@ -274,26 +273,26 @@ func QueryUserStaticDataPage(ctx *context.Context) {
xlsx.SetCellValue(sheetName, "A"+rows, userRecord.ID)
xlsx.SetCellValue(sheetName, "B"+rows, userRecord.Name)
xlsx.SetCellValue(sheetName, "C"+rows, userRecord.CodeMergeCount)
xlsx.SetCellValue(sheetName, "D"+rows, userRecord.CommitCount)
xlsx.SetCellValue(sheetName, "E"+rows, userRecord.IssueCount)
xlsx.SetCellValue(sheetName, "F"+rows, userRecord.CommentCount)
xlsx.SetCellValue(sheetName, "G"+rows, userRecord.FocusRepoCount)
xlsx.SetCellValue(sheetName, "H"+rows, userRecord.StarRepoCount)
xlsx.SetCellValue(sheetName, "I"+rows, userRecord.LoginCount)
xlsx.SetCellValue(sheetName, "J"+rows, userRecord.WatchedCount)
xlsx.SetCellValue(sheetName, "K"+rows, userRecord.CommitCodeSize)
xlsx.SetCellValue(sheetName, "L"+rows, userRecord.SolveIssueCount)
xlsx.SetCellValue(sheetName, "M"+rows, userRecord.EncyclopediasCount)
xlsx.SetCellValue(sheetName, "N"+rows, userRecord.CreateRepoCount)
xlsx.SetCellValue(sheetName, "O"+rows, fmt.Sprintf("%.2f", userRecord.OpenIIndex))
xlsx.SetCellValue(sheetName, "D"+rows, fmt.Sprintf("%.2f", userRecord.UserIndex))
xlsx.SetCellValue(sheetName, "E"+rows, userRecord.CommitCount)
xlsx.SetCellValue(sheetName, "F"+rows, userRecord.IssueCount)
xlsx.SetCellValue(sheetName, "G"+rows, userRecord.CommentCount)
xlsx.SetCellValue(sheetName, "H"+rows, userRecord.FocusRepoCount)
xlsx.SetCellValue(sheetName, "I"+rows, userRecord.StarRepoCount)
xlsx.SetCellValue(sheetName, "J"+rows, userRecord.LoginCount)
xlsx.SetCellValue(sheetName, "K"+rows, userRecord.WatchedCount)
xlsx.SetCellValue(sheetName, "L"+rows, userRecord.CommitCodeSize)
xlsx.SetCellValue(sheetName, "M"+rows, userRecord.SolveIssueCount)
xlsx.SetCellValue(sheetName, "N"+rows, userRecord.EncyclopediasCount)
xlsx.SetCellValue(sheetName, "O"+rows, userRecord.CreateRepoCount)
xlsx.SetCellValue(sheetName, "P"+rows, fmt.Sprintf("%.2f", userRecord.OpenIIndex))

formatTime := userRecord.RegistDate.Format("2006-01-02 15:04:05")
xlsx.SetCellValue(sheetName, "P"+rows, formatTime[0:len(formatTime)-3])
xlsx.SetCellValue(sheetName, "Q"+rows, userRecord.CloudBrainTaskNum)
xlsx.SetCellValue(sheetName, "R"+rows, fmt.Sprintf("%.2f", float64(userRecord.CloudBrainRunTime)/3600))
xlsx.SetCellValue(sheetName, "S"+rows, userRecord.CommitDatasetNum)
xlsx.SetCellValue(sheetName, "T"+rows, userRecord.CommitModelCount)
xlsx.SetCellValue(sheetName, "U"+rows, fmt.Sprintf("%.2f", userRecord.UserIndex))
xlsx.SetCellValue(sheetName, "Q"+rows, formatTime[0:len(formatTime)-3])
xlsx.SetCellValue(sheetName, "R"+rows, userRecord.CloudBrainTaskNum)
xlsx.SetCellValue(sheetName, "S"+rows, fmt.Sprintf("%.2f", float64(userRecord.CloudBrainRunTime)/3600))
xlsx.SetCellValue(sheetName, "T"+rows, userRecord.CommitDatasetNum)
xlsx.SetCellValue(sheetName, "U"+rows, userRecord.CommitModelCount)
formatTime = userRecord.DataDate
xlsx.SetCellValue(sheetName, "V"+rows, formatTime)
}


+ 5
- 0
routers/repo/util.go View File

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

import "regexp"

var NamePattern = regexp.MustCompile(`^[A-Za-z0-9-_\\.]{1,100}$`)

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

@@ -12,6 +12,8 @@ import (
"text/template"
"time"

"code.gitea.io/gitea/routers/image"

"code.gitea.io/gitea/routers/authentication"

"code.gitea.io/gitea/modules/cloudbrain"
@@ -333,6 +335,8 @@ func RegisterRoutes(m *macaron.Macaron) {
})
m.Get("/images/public", repo.GetPublicImages)
m.Get("/images/custom", repo.GetCustomImages)
m.Get("/images/star", repo.GetStarImages)

m.Get("/repos", routers.ExploreRepos)
m.Get("/datasets", routers.ExploreDatasets)
m.Get("/users", routers.ExploreUsers)
@@ -527,6 +531,11 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("", admin.CloudBrains)
m.Get("/download", admin.DownloadCloudBrains)
})
m.Group("/images", func() {
m.Get("", admin.Images)
m.Get("/data", repo.GetAllImages)
})
m.Put("/image/:id/action/:action", image.Action)

m.Group("/^:configType(hooks|system-hooks)$", func() {
m.Get("", admin.DefaultOrSystemWebhooks)
@@ -975,6 +984,12 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/topics", repo.TopicsPost)
}, context.RepoAssignment(), context.RepoMustNotBeArchived(), reqRepoAdmin)

m.Group("/image/:id", func() {
m.Get("/:from", cloudbrain.AdminOrImageCreaterRight, repo.CloudBrainImageEdit)
m.Post("", cloudbrain.AdminOrImageCreaterRight, bindIgnErr(auth.EditImageCloudBrainForm{}), repo.CloudBrainImageEditPost)
m.Delete("", cloudbrain.AdminOrImageCreaterRight, repo.CloudBrainImageDelete)
m.Put("/action/:action", reqSignIn, image.Action)
})
m.Group("/:username/:reponame", func() {
m.Group("", func() {
m.Get("/^:type(issues|pulls)$", repo.Issues)
@@ -1016,6 +1031,8 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Group("/:id", func() {
m.Get("", reqRepoCloudBrainReader, repo.CloudBrainShow)
m.Get("/debug", cloudbrain.AdminOrJobCreaterRight, repo.CloudBrainDebug)
m.Get("/commit_image", cloudbrain.AdminOrJobCreaterRight, repo.CloudBrainCommitImageShow)
m.Post("/commit_image/check", cloudbrain.AdminOrJobCreaterRight, bindIgnErr(auth.CommitImageCloudBrainForm{}), repo.CloudBrainCommitImageCheck)
m.Post("/commit_image", cloudbrain.AdminOrJobCreaterRight, bindIgnErr(auth.CommitImageCloudBrainForm{}), repo.CloudBrainCommitImage)
m.Post("/stop", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.CloudBrainStop)
m.Post("/del", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.CloudBrainDel)


+ 6
- 6
routers/search.go View File

@@ -68,23 +68,23 @@ func SearchApi(ctx *context.Context) {
if OnlySearchLabel {
searchRepoByLabel(ctx, Key, Page, PageSize)
} else {
searchRepo(ctx, "repository-es-index", Key, Page, PageSize, OnlyReturnNum)
searchRepo(ctx, "repository-es-index"+setting.INDEXPOSTFIX, Key, Page, PageSize, OnlyReturnNum)
}
return
} else if TableName == "issue" {
searchIssueOrPr(ctx, "issue-es-index", Key, Page, PageSize, OnlyReturnNum, "f")
searchIssueOrPr(ctx, "issue-es-index"+setting.INDEXPOSTFIX, Key, Page, PageSize, OnlyReturnNum, "f")
return
} else if TableName == "user" {
searchUserOrOrg(ctx, "user-es-index", Key, Page, PageSize, true, OnlyReturnNum)
searchUserOrOrg(ctx, "user-es-index"+setting.INDEXPOSTFIX, Key, Page, PageSize, true, OnlyReturnNum)
return
} else if TableName == "org" {
searchUserOrOrg(ctx, "user-es-index", Key, Page, PageSize, false, OnlyReturnNum)
searchUserOrOrg(ctx, "user-es-index"+setting.INDEXPOSTFIX, Key, Page, PageSize, false, OnlyReturnNum)
return
} else if TableName == "dataset" {
searchDataSet(ctx, "dataset-es-index", Key, Page, PageSize, OnlyReturnNum)
searchDataSet(ctx, "dataset-es-index"+setting.INDEXPOSTFIX, Key, Page, PageSize, OnlyReturnNum)
return
} else if TableName == "pr" {
searchIssueOrPr(ctx, "issue-es-index", Key, Page, PageSize, OnlyReturnNum, "t")
searchIssueOrPr(ctx, "issue-es-index"+setting.INDEXPOSTFIX, Key, Page, PageSize, OnlyReturnNum, "t")
//searchPR(ctx, "issue-es-index", Key, Page, PageSize, OnlyReturnNum)
return
}


+ 2
- 1
semantic.json View File

@@ -56,6 +56,7 @@
"tab",
"table",
"text",
"transition"
"transition",
"toast"
]
}

+ 42
- 0
templates/admin/cloudbrain/images.html View File

@@ -0,0 +1,42 @@
{{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="alert"></div>
<div class="admin user">
{{template "admin/navbar" .}}
<div id="images-admin">

</div>
</div>
<!-- 确认模态框 -->
<div>
<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>
{{template "base/footer" .}}


+ 1
- 1
templates/admin/cloudbrain/list.tmpl View File

@@ -19,7 +19,7 @@
<div class="ui grid" >
<div class="row" style="border: 1px solid #d4d4d5;margin-top: 15px;padding-top: 0;">
{{template "admin/cloudbrain/search" .}}
<div class="ui ten wide column right aligned" style="margin: 1rem 0;">
<div class="ui six wide column right aligned" style="margin: 1rem 0;">
<a class="ui compact blue basic icon button" style="box-shadow: none !important; padding: 0.8em;" href="/admin/cloudbrains/download"><i class="ri-download-line middle aligned icon"></i>{{.i18n.Tr "admin.cloudbrain.download_report"}}</a>
</div>
<div class="ui sixteen wide column">


+ 1
- 1
templates/admin/cloudbrain/search.tmpl View File

@@ -6,7 +6,7 @@
</div>
</form>
</div>
<div class="ui {{if $.PageIsUserCloudBrain}}sixteen{{else}}six{{end}} wide column" style="margin: 1rem 0;" id="adminCloud">
<div class="ui ten wide column" style="margin: 1rem 0;" id="adminCloud">
<div class="ui selection dropdown" style="min-width: 10em;min-height:2.6em;border-radius: .28571429rem;margin-right: 1em;padding: .67em 3.2em .7em 1em;">
<div class="default text" style="color: rgba(0,0,0,.87);">{{.i18n.Tr "admin.cloudbrain.all_task_types"}}</div>
<i class="dropdown icon"></i>


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

@@ -17,6 +17,9 @@
<a class="{{if .PageIsAdminCloudBrains}}active{{end}} item" href="{{AppSubUrl}}/admin/cloudbrains">
{{.i18n.Tr "repo.cloudbrain.task"}}
</a>
<a class="{{if .PageIsAdminImages}}active{{end}} item" href="{{AppSubUrl}}/admin/images">
{{.i18n.Tr "explore.images"}}
</a>
<a class="{{if .PageIsAdminHooks}}active{{end}} item" href="{{AppSubUrl}}/admin/hooks">
{{.i18n.Tr "admin.hooks"}}
</a>


+ 5
- 5
templates/custom/select_dataset.tmpl View File

@@ -4,19 +4,19 @@
<label>{{.i18n.Tr "dataset.dataset"}}</label>
<input type="hidden" name="attachment" :value="dataset_uuid">
{{if eq .cloudbraintype 0}}
<input class="disabled" type="text" :value="dataset_name" required onfocus="this.blur();">
<input class="disabled" type="text" :value="dataset_name" placeholder="{{.i18n.Tr "cloudbrain.select_dataset"}}" required onfocus="this.blur();">
{{else}}
<input class="disabled" type="text" :value="dataset_name">
<input class="disabled" type="text" :value="dataset_name" placeholder="{{.i18n.Tr "cloudbrain.select_dataset"}}">
{{end}}
<el-button type="text" @click="dialogVisible = true" icon="el-icon-plus"> {{.i18n.Tr "dataset.select_dataset"}}</el-button>
<el-button type="text" @click="dialogVisible = true" icon="el-icon-plus" style="color: #0366d6;"> {{.i18n.Tr "dataset.select_dataset"}}</el-button>
<el-dialog
title="{{.i18n.Tr "dataset.select_dataset"}}"
:visible.sync="dialogVisible"
width="50%"
>
<div class="ui icon input" style="z-index: 9999;position: absolute;right: 50px;height:30px;">
<i class="search icon" style="cursor: pointer;pointer-events:auto" @click="searchDataset()"></i>
<input type="text" placeholder="{{.i18n.Tr "dataset.search_dataset"}}" v-model="searchDataItem" @keyup.enter="searchDataset()">
<i class="search icon"></i>
<input type="text" placeholder="{{.i18n.Tr "dataset.search_dataset"}}" v-model="searchDataItem">
</div>
<el-tabs v-model="activeName" @tab-click="handleClick('{{.RepoLink}}',activeName,{{.cloudbraintype}})">


+ 7
- 3
templates/custom/select_dataset_train.tmpl View File

@@ -1,9 +1,13 @@

<div class="dataset-repolink" id="dataset-repolink-init" style="display: none;" data-repolink="{{.RepoLink}}" data-cloudranin-type="{{.cloudbraintype}}"></div>
<div class="inline required unite min_title field" id="dataset-base" style="margin-bottom: 0 !important;">
{{if .newInference}}
<label style="font-weight: normal;">{{.i18n.Tr "dataset.dataset"}}</label>&nbsp;&nbsp;&nbsp;&nbsp;
{{else}}
<label style="font-weight: normal;">{{.i18n.Tr "dataset.dataset"}}</label>&nbsp;&nbsp;&nbsp;
{{end}}
<input type="hidden" name="attachment" :value="dataset_uuid">
<input class="disabled" type="text" :value="dataset_name" required onfocus="this.blur();" style="width: 35.5%;">
<input class="disabled" type="text" :value="dataset_name" required onfocus="this.blur();" style="width: 48.5%;">
<el-button type="text" @click="dialogVisible = true" icon="el-icon-plus" style="color: #0366d6;"> {{.i18n.Tr "dataset.select_dataset"}}</el-button>
<el-dialog
title="{{.i18n.Tr "dataset.select_dataset"}}"
@@ -11,8 +15,8 @@
width="50%"
>
<div class="ui icon input" style="z-index: 9999;position: absolute;right: 50px;height:30px;">
<i class="search icon" style="cursor: pointer;pointer-events:auto" @click="searchDataset()"></i>
<input type="text" placeholder="{{.i18n.Tr "dataset.search_dataset"}}" v-model="searchDataItem" @keyup.enter="searchDataset()">
<i class="search icon"></i>
<input type="text" placeholder="{{.i18n.Tr "dataset.search_dataset"}}" v-model="searchDataItem">
</div>
<el-tabs v-model="activeName" @tab-click="handleClick('{{.RepoLink}}',activeName,{{.cloudbraintype}})">


+ 1
- 1
templates/explore/datasets.tmpl View File

@@ -163,7 +163,7 @@
</div>
<div style="font-size: 16px;color:#0366D6;font-family: SourceHanSansSC-medium;height: 27px;font-weight: bold;">{{.Title}}</div>
{{if or (.Category) (.Task) (.License)}}
<div style="font-size: 12px;margin-top: 5px;height: 24px;">
<div style="font-size: 12px;margin-top: 5px;">
{{if .Category}}
{{$category := .Category}}
<a class="ui repo-topic label topic" href="{{$.Link}}?sort={{$.SortType}}&q={{$.Keyword}}&tab={{$.TabName}}&category={{.Category}}&task={{$.Task}}&license={{$.License}}">{{$.i18n.Tr (printf "dataset.category.%s" $category)}}</a>


+ 19
- 2
templates/explore/images.tmpl View File

@@ -1,7 +1,24 @@
{{template "base/head" .}}
<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>
{{template "base/footer" .}}

+ 2
- 13
templates/repo/cloudbrain/benchmark/new.tmpl View File

@@ -121,19 +121,8 @@
</div>
</div>

<div class="required unite min_title inline field">
<label style="font-weight: normal;">{{.i18n.Tr "repo.cloudbrain.benchmark.evaluate_mirror"}}</label>
<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
<input type="text" list="cloudbrain_image" placeholder="{{.i18n.Tr "cloudbrain.choose_mirror"}}" name="image" value="{{.image}}" class="required autofocus" style='width:492px;' maxlength="254">
<i class="times circle outline icon icons" style="visibility: hidden;" onclick="clearValue()"></i>
<datalist class="ui search" id="cloudbrain_image" style='width:385px;' name="image">
{{range .images}}
<option name="image" value="{{.Place}}">{{.PlaceView}}</option>
{{end}}
{{range .public_images}}
<option name="image" value="{{.Place}}">{{.PlaceView}}</option>
{{end}}
</datalist>
<div id="images-new-cb">

</div>




+ 94
- 0
templates/repo/cloudbrain/image/edit.tmpl View File

@@ -0,0 +1,94 @@
<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>
<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>
CPU/GPU
</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="hidden" name="tag" value="{{.Image.Tag}}" >
<input disabled value="{{.Image.Tag}}" style="width: 80%;">
<span class="tooltips" style="display: block;padding-left: 0.5rem;">{{.i18n.Tr "repo.images.name_rule"}}</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="255" placeholder={{.i18n.Tr "repo.modelarts.train_job.new_place"}} onchange="this.value=this.value.substring(0, 255)" onkeydown="this.value=this.value.substring(0, 255)" onkeyup="this.value=this.value.substring(0, 255)">{{.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: 0.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" style="padding-top: 2rem;">
<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>
</div>
{{template "base/footer" .}}

+ 106
- 0
templates/repo/cloudbrain/image/submit.tmpl View File

@@ -0,0 +1,106 @@
<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}}"></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>
CPU/GPU
</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="100">
<span class="tooltips" style="display: block;padding-left: 0.5rem;">{{.i18n.Tr "repo.images.name_rule"}}</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="255" placeholder={{.i18n.Tr "repo.modelarts.train_job.new_place"}} onchange="this.value=this.value.substring(0, 255)" onkeydown="this.value=this.value.substring(0, 255)" onkeyup="this.value=this.value.substring(0, 255)"></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: 0.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" style="padding-top: 2rem;">
<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>
</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" .}}

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

@@ -189,7 +189,7 @@
</select>
</div>

<div class="inline required field" style="position: relative;">
<!-- <div class="inline required field" style="position: relative;">
<label>{{.i18n.Tr "cloudbrain.mirror"}}</label>
<input type="text" list="cloudbrain_image" placeholder="{{.i18n.Tr "cloudbrain.choose_mirror"}}" name="image" required autofocus maxlength="255">
<i class="times circle outline icon icons" style="visibility: hidden;" onclick="clearValue()"></i>
@@ -201,6 +201,9 @@
<option name="image" value="{{.Place}}">{{.PlaceView}}</option>
{{end}}
</datalist>
</div> -->
<div id="images-new-cb">

</div>
{{template "custom/select_dataset" .}}
@@ -260,10 +263,10 @@
<script>
let form = document.getElementById('form_id');

let inputs = document.querySelectorAll('input[list]');
inputs[0].addEventListener('change', function() {
$(".icon.icons").css("visibility","visible")
});
// let inputs = document.querySelectorAll('input[list]');
// inputs[0].addEventListener('change', function() {
// $(".icon.icons").css("visibility","visible")
// });

$('#messageInfo').css('display','none')
function clearValue(){


+ 7
- 3
templates/repo/cloudbrain/trainjob/new.tmpl View File

@@ -152,7 +152,7 @@
</select>
</div>

<div class="required unite min_title inline field" style="position: relative;">
<!-- <div class="required unite min_title inline field" style="position: relative;">
<label style="font-weight: normal;">{{.i18n.Tr "cloudbrain.mirror"}}&nbsp;</label>
<input class="width81" type="text" list="cloudbrain_image" placeholder="{{.i18n.Tr "cloudbrain.choose_mirror"}}" name="image" required autofocus maxlength="255">
<i class="times circle outline icon icons" style="visibility: hidden;" onclick="clearValue()"></i>
@@ -164,14 +164,18 @@
<option name="image" value="{{.Place}}">{{.PlaceView}}</option>
{{end}}
</datalist>
</div> -->

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

</div>

<div class="inline unite min_title field required">
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.start_file"}}</label>
{{if .bootFile}}
<input style="width: 35.5%;" name="boot_file" id="trainjob_boot_file" value="{{.bootFile}}" tabindex="3" autofocus required maxlength="255" >
<input style="width: 48.5%;" name="boot_file" id="trainjob_boot_file" value="{{.bootFile}}" tabindex="3" autofocus required maxlength="255" >
{{else}}
<input style="width: 35.5%;" name="boot_file" id="trainjob_boot_file" value="" tabindex="3" autofocus required maxlength="255" >
<input style="width: 48.5%;" name="boot_file" id="trainjob_boot_file" value="" tabindex="3" autofocus required maxlength="255" >
{{end}}
<span>
<i class="question circle icon link" data-content={{.i18n.Tr "repo.modelarts.train_job.boot_file_helper"}} data-position="right center" data-variation="mini"></i>


+ 1
- 1
templates/repo/createCourse.tmpl View File

@@ -46,7 +46,7 @@
<span style="flex: 1;"></span>
</div>

<div class="inline field" style="display: flex;align-items:center;">
<div class="inline field" style="display: flex;align-items:center;">
<label style="margin: .035714em 1em 0 0;">{{.i18n.Tr "repo.model.manage.label"}}</label>
<div class="ui multiple search selection dropdown" id="dropdown_container">
<input type="hidden" name="topics" value="">


+ 2
- 86
templates/repo/debugjob/index.tmpl View File

@@ -379,10 +379,8 @@
<i class="dropdown icon"></i>
<div class="menu" style="right: auto;">
<div class="item" style="padding: 0 !important;">
<!-- 接收结果 -->
<iframe src="" frameborder="0" name="iframeContent" style="display: none;"></iframe>
{{if .CanDebug}}
<a id="model-image-{{.Cloudbrain.ID}}" class='imageBtn ui basic {{if ne .Status "RUNNING"}}disabled {{else}}blue {{end}}button'>{{$.i18n.Tr "repo.submit_image"}}</a>
<a id="model-image-{{.Cloudbrain.ID}}" class='imageBtn ui basic {{if ne .Status "RUNNING"}} {{else}}blue {{end}}button' href="{{$.RepoLink}}/cloudbrain/{{.Cloudbrain.ID}}/commit_image">{{$.i18n.Tr "repo.submit_image"}}</a>
{{else}}
<a class="imageBtn ui basic disabled button">{{$.i18n.Tr "repo.submit_image"}}</a>
{{end}}
@@ -407,40 +405,7 @@
</div>
</div>
<!-- 镜像列表弹窗 -->
<div id="imageModal" class="modal" style="display: none;">
<div class="modal-content">
<!-- 表格 -->
<div class="ui form">
<form id="commitImageForm" action="{{$.RepoLink}}/cloudbrain/{{.Cloudbrain.ID}}/commit_image" method="post" target="iframeContent">
{{$.CsrfTokenHtml}}
<div class="row">
<p style="display: inline;">提交任务镜像</p>
<span class="close">&times;</span>
</div>

<div class="ui divider"></div>

<div class="inline required field dis">
<label> {{$.i18n.Tr "repo.cloudbrain.mirror_tag"}}:</label>
<input name="tag" id="image_tag" tabindex="3" autofocus required maxlength="255" style="width:75%">
</div>

<div class="inline field">
<label class="label_after">{{$.i18n.Tr "repo.cloudbrain.mirror_description"}}:</label>
<textarea name="description" maxlength="255" rows="8" style="width:75%;margin-left: 0.2em;"></textarea>
</div>
<div class="ui divider"></div>
<div class="inline field">
<label></label>
<button class="ui green button" onclick="showmask()">
{{$.i18n.Tr "repo.cloudbrain.commit_image"}}
</button>
</div>
</form>
</div>

</div>
</div>
</div>
</div>
{{end}}
@@ -529,54 +494,5 @@
.transition('fade')
})
})
// 获取弹窗
var modal = document.getElementById('imageModal');

// 打开弹窗的按钮对象
var btns = document.getElementsByClassName("imageBtn");

// 获取 <span> 元素,用于关闭弹窗
var spans = document.getElementsByClassName('close');

// 点击按钮打开弹窗
for (i = 0; i < btns.length; i++) {
btns[i].onclick = function() {
modal.style.display = "block";
}
}

// 点击 <span> (x), 关闭弹窗
for (i = 0; i < spans.length; i++) {
spans[i].onclick = function() {
modal.style.display = "none";
}
}

// 在用户点击其他地方时,关闭弹窗
window.onclick = function(event) {
if (event.target == modal) {
modal.style.display = "none";
}
}
// 显示弹窗,弹出相应的信息
function showmask() {
var image_tag = !$('#image_tag').val()
if(image_tag){
return
}
$('#imageModal').css('display', 'none')
$('#mask').css('display', 'block')

$("iframe[name=iframeContent]").on("load", function() {  
var responseText = $("iframe")[0].contentDocument.body.getElementsByTagName("pre")[0].innerHTML; 
var json1 = JSON.parse(responseText)
$('#mask').css('display', 'none')
if (json1.result_code === "0") {
$('.alert').html('操作成功!').removeClass('alert-danger').addClass('alert-success').show().delay(1500).fadeOut();
} else {
$('.alert').html(json1.error_msg).removeClass('alert-success').addClass('alert-danger').show().delay(5000).fadeOut();
}
})
}

</script>

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

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


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

@@ -26,7 +26,9 @@
width: 35.5% !important;
}


.width48{
width: 48.5% !important;
}
.nowrapx {
white-space: nowrap !important;
}
@@ -150,7 +152,7 @@
<!-- 代码分支 -->
<div class="required unite min_title inline field">
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.code_version"}}</label>&nbsp;
<select class="ui dropdown width35" id="code_version" name="branch_name">
<select class="ui dropdown width48" id="code_version" name="branch_name">
{{if .branch_name}}
<option name="branch_name" value="{{.branch_name}}">{{.branch_name}}</option>
{{range $k, $v :=.Branches}}
@@ -172,11 +174,11 @@
{{template "custom/select_dataset_train" .}}
<span class="tooltips" style="margin-left: 11.5rem;margin-bottom: 2rem;">{{.i18n.Tr "cloudbrain.dataset_path_rule"}}</span>
<div class="inline unite min_title field required">
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.start_file"}}</label>
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.start_file"}}</label>&nbsp;
{{if .bootFile}}
<input style="width: 35.5%;" name="boot_file" id="trainjob_boot_file" value="{{.bootFile}}" tabindex="3" autofocus required maxlength="255" >
<input style="width: 48.5%;" name="boot_file" id="trainjob_boot_file" value="{{.bootFile}}" tabindex="3" autofocus required maxlength="255" >
{{else}}
<input style="width: 35.5%;" name="boot_file" id="trainjob_boot_file" value="" tabindex="3" autofocus required maxlength="255" >
<input style="width: 48.5%;" name="boot_file" id="trainjob_boot_file" value="" tabindex="3" autofocus required maxlength="255" >
{{end}}
<span >
<i class="question circle icon" data-content={{.i18n.Tr "repo.modelarts.infer_job.boot_file_helper"}} data-position="top center" data-variation="inverted mini"></i>
@@ -250,6 +252,7 @@
{{template "base/footer" .}}

<script>
console.log({{.newInference}})
const RepoLink = {{.RepoLink}}
const url_href = window.location.pathname.split('create')[0]
let nameMap,nameList


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

@@ -158,9 +158,9 @@
<div class="inline unite min_title field required">
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.start_file"}}</label>
{{if .bootFile}}
<input style="width: 35.5%;" name="boot_file" id="trainjob_boot_file" value="{{.bootFile}}" tabindex="3" autofocus required maxlength="255" >
<input style="width: 48.5%;" name="boot_file" id="trainjob_boot_file" value="{{.bootFile}}" tabindex="3" autofocus required maxlength="255" >
{{else}}
<input style="width: 35.5%;" name="boot_file" id="trainjob_boot_file" value="" tabindex="3" autofocus required maxlength="255" >
<input style="width: 48.5%;" name="boot_file" id="trainjob_boot_file" value="" tabindex="3" autofocus required maxlength="255" >
{{end}}
<span>
<i class="question circle icon link" data-content={{.i18n.Tr "repo.modelarts.train_job.boot_file_helper"}} data-position="right center" data-variation="mini"></i>


+ 1
- 1
templates/user/settings/organization.tmpl View File

@@ -11,7 +11,7 @@
</div>
{{end}}
</h4>
<div class="ui attached segment orgs">
<div class="ui attached segment">
{{if .Orgs}}
<div class="ui middle aligned divided list">
{{range .Orgs}}


+ 0
- 20
web_src/js/components/Contributors.vue View File

@@ -96,23 +96,3 @@ updated(){
};
</script>

<style scoped>
/deep/ .el-pagination.is-background .el-pager li:not(.disabled).active {
background-color: #5bb973;
color: #FFF;
}
/deep/ .el-pagination.is-background .el-pager li.active {
color: #fff;
cursor: default;
}
/deep/ .el-pagination.is-background .el-pager li:hover {
color: #5bb973;
}
/deep/ .el-pagination.is-background .el-pager li:not(.disabled):hover {
color: #5bb973;
}
/deep/ .el-pagination.is-background .el-pager li:not(.disabled).active:hover {
background-color: #5bb973;
color: #FFF;
}
</style>

+ 1
- 1
web_src/js/components/DataAnalysis.vue View File

@@ -31,7 +31,7 @@
<span slot="label">
<el-image style="width: 13px; height: 13px" src="/img/pro_rgb.svg">
</el-image>
云脑分析
云脑分析(建设中..)
</span>
</el-tab-pane>
</el-tabs>


+ 0
- 503
web_src/js/components/Images.vue View File

@@ -1,503 +0,0 @@
<template>
<div>
<div class="header-wrapper">
<div class="ui container">
<el-row class="image_text">
<h1>云脑镜像</h1>
</el-row>
</div>
</div>
<div class="ui container" id="header">
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="公共镜像(云脑1)" name="first" v-loading="loading">
<div class="ui sixteen wide column">
<div class="ui two column stackable grid">
<div class="column">
<el-input placeholder="请输入镜像名称关健词" v-model="search" class="input-with-select" @keyup.enter.native="searchName()">
<el-button id="success" slot="append" icon="el-icon-search" @click="searchName()">搜索</el-button>
</el-input>
</div>

<!-- <div class="column right aligned">
<el-dropdown>
<span class="el-dropdown-link">
排序<i class="el-icon-caret-bottom"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>最早创建</el-dropdown-item>
<el-dropdown-item>最新创建</el-dropdown-item>
<el-dropdown-item divided>按镜像字母顺序排序</el-dropdown-item>
<el-dropdown-item>按镜像字母逆序排序</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div> -->
</div>
</div>

<el-row style="margin-top:15px;">

<el-table
:data="tableData"
style="width: 100%"
:header-cell-style="tableHeaderStyle"
:default-sort="{prop:'createtime',order:'descending'}">
<el-table-column
label="镜像名称"
width="350"
align="left"
prop="name"
sortable
>
<template slot-scope="scope">
<a class="text-over" style="cursor:default;color:#426290" :title="scope.row.name">{{ scope.row.name }}</a>
</template>
</el-table-column>
<el-table-column
label="文件路径/镜像描述"
width="450"
align="left"
>
<template slot-scope="scope">
<el-tooltip class="item" effect="dark" content="点击复制文件路径" placement="top">
<a class="text-over" style="display:block;" @click="copyUrl(scope.row.place)">{{ scope.row.place }}</a>
</el-tooltip>
<span class="text-over" :title="scope.row.description | clearP">{{ scope.row.description | clearP}}</span>
</template>
</el-table-column>
<el-table-column
prop="provider"
label="提供者"
width="120"
align="left"
sortable>
</el-table-column>
<el-table-column
prop="createtime"
label="创建时间"
align="center"
sortable>
<template slot-scope="scope">
{{scope.row.createtime | transformTimestamp}}
</template>
</el-table-column>
</el-table>
</el-row>
<div class="ui container" style="margin-top:50px;text-align:center">
<el-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-size="pageSize"
:page-sizes="[5,10,20]"
layout="total, sizes, prev, pager, next, jumper"
:total="totalNum">
</el-pagination>
</div>
</el-tab-pane>
<el-tab-pane label="自定义镜像(云脑1)" name="second" v-loading="loading1">
<div class="ui sixteen wide column">
<div class="ui two column stackable grid">
<div class="column">
<el-input placeholder="请输入镜像名称关健词" v-model="search" class="input-with-select" @keyup.enter.native="searchName()">
<el-button slot="append" id="success" icon="el-icon-search" @click="searchName()">搜索</el-button>
</el-input>
</div>

<!-- <div class="column right aligned">
<el-dropdown>
<span class="el-dropdown-link">
排序<i class="el-icon-caret-bottom"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>最早创建</el-dropdown-item>
<el-dropdown-item>最新创建</el-dropdown-item>
<el-dropdown-item divided>按镜像字母顺序排序</el-dropdown-item>
<el-dropdown-item>按镜像字母逆序排序</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div> -->
</div>
</div>

<el-row style="margin-top:15px;">

<el-table
:data="tableData1"
style="width: 100%"
:header-cell-style="tableHeaderStyle"
:default-sort="{prop:'createtime',order:'descending'}">
<el-table-column
label="镜像名称"
width="350"
align="left"
prop="name"
sortable
>
<template slot-scope="scope">
<a class="text-over" :title="scope.row.name" style="cursor:default;color:#426290">{{ scope.row.name }}</a>
</template>
</el-table-column>
<el-table-column
label="文件路径/镜像描述"
width="450"
align="left"
>
<template slot-scope="scope">
<el-tooltip class="item" effect="dark" content="点击复制文件路径" placement="top">
<a class="text-over" style="display:block;" @click="copyUrl(scope.row.place)">{{ scope.row.place }}</a>
</el-tooltip>
<span class="text-over" :title="scope.row.description | clearP">{{ scope.row.description | clearP }}</span>
</template>
</el-table-column>
<el-table-column
prop="provider"
label="提供者"
width="120"
align="left"
sortable>
</el-table-column>
<el-table-column
prop="createtime"
label="创建时间"
align="center"
sortable>
<template slot-scope="scope">
{{scope.row.createtime | transformTimestamp}}
</template>
</el-table-column>
</el-table>
</el-row>
<div class="ui container" style="margin-top:50px;text-align:center">
<el-pagination
background
@size-change="handleSizeChange1"
@current-change="handleCurrentChange1"
:current-page="currentPage1"
:page-size="pageSize1"
:page-sizes="[5,10,20]"
layout="total, sizes, prev, pager, next, jumper"
:total="totalNum1">
</el-pagination>
</div>

</el-tab-pane>
<el-tab-pane label="公共镜像(云脑2)" name="third">
<div class="ui sixteen wide column">
<div class="ui two column stackable grid">
<div class="column">
<el-input placeholder="请输入镜像名称关健词" v-model="search" class="input-with-select">
<el-button slot="append" id="success" icon="el-icon-search">搜索</el-button>
</el-input>
</div>

<!-- <div class="column right aligned">
<el-dropdown>
<span class="el-dropdown-link">
排序<i class="el-icon-caret-bottom"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>最早创建</el-dropdown-item>
<el-dropdown-item>最新创建</el-dropdown-item>
<el-dropdown-item divided>按镜像字母顺序排序</el-dropdown-item>
<el-dropdown-item>按镜像字母逆序排序</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div> -->
</div>
</div>

<el-empty :image-size="200"></el-empty>
</el-tab-pane>
<el-tab-pane label="自定义镜像(云脑2)" name="fourth">
<div class="ui sixteen wide column">
<div class="ui two column stackable grid">
<div class="column">
<el-input placeholder="请输入镜像名称关健词" v-model="search" class="input-with-select">
<el-button slot="append" id="success" icon="el-icon-search">搜索</el-button>
</el-input>
</div>

<!-- <div class="column right aligned">
<el-dropdown>
<span class="el-dropdown-link">
排序<i class="el-icon-caret-bottom"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>最早创建</el-dropdown-item>
<el-dropdown-item>最新创建</el-dropdown-item>
<el-dropdown-item divided>按镜像字母顺序排序</el-dropdown-item>
<el-dropdown-item>按镜像字母逆序排序</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div> -->
</div>
</div>

<el-empty :image-size="200"></el-empty>

</el-tab-pane>
</el-tabs>
</div>
</div>

</template>

<script>

const {_AppSubUrl, _StaticUrlPrefix, csrf} = window.config;




export default {
components: {
},
data() {
return {
activeName: 'first',
search:'',
currentPage:1,
pageSize:10,
totalNum:0,
params:{page:1,size:10,name:''},
tableData: [],
loading:false,

currentPage1:1,
pageSize1:10,
totalNum1:0,
params1:{page:1,size:10,name:''},
tableData1: [],
loading1:false
};
},
methods: {
handleClick(tab, event) {
if(tab.name=="first"){
this.getImageList()
}
if(tab.name=="second"){
this.getImageList1()
}
},
tableHeaderStyle({row,column,rowIndex,columnIndex}){
if(rowIndex===0){
return 'background:#f5f5f6;color:#606266'
}
},
handleSizeChange(val){
this.params.size = val
this.getImageList()


},
handleCurrentChange(val){
this.params.page = val
this.getImageList()

},
handleSizeChange1(val){
this.params1.size = val
this.getImageList1()


},
handleCurrentChange1(val){
this.params1.page = val
this.getImageList1()

},
getImageList(){
this.loading = true
this.$axios.get('/explore/images/public',{
params:this.params
}).then((res)=>{
this.totalNum = res.data.count
this.tableData = res.data.rows
this.loading = false
})
},

getImageList1(){
this.loading1 = true
this.$axios.get('/explore/images/custom',{
params:this.params1
}).then((res)=>{
this.totalNum1 = res.data.count
this.tableData1 = res.data.rows
this.loading1 = false
})
},
copyUrl(url){
const cInput = document.createElement('input')
cInput.value = url
document.body.appendChild(cInput)
cInput.select()
document.execCommand('Copy')
cInput.remove()

},
searchName(){
if(this.activeName=='first'){
this.params.name = this.search
this.params.page = 1
this.getImageList()
}
if(this.activeName=='second'){
this.params1.name = this.search
this.params1.page = 1
this.getImageList1()
}
}

},
filters:{



clearP(value){
if(!value) return ''
const reg = /\<\/?p\>/g;
value = value.replace(reg,'')
return value

},
transformTimestamp(timestamp){
let a = new Date(timestamp).getTime();
const date = new Date(a);
const Y = date.getFullYear() + '-';
const M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
const D = (date.getDate() < 10 ? '0'+date.getDate() : date.getDate()) + ' ';
const h = (date.getHours() < 10 ? '0'+date.getHours() : date.getHours()) + ':';
const m = (date.getMinutes() <10 ? '0'+date.getMinutes() : date.getMinutes()) + ':' ;
const s = (date.getSeconds() <10 ? '0'+date.getSeconds() : date.getSeconds()) ; // 秒
const dateString = Y + M + D + h + m + s;
// console.log('dateString', dateString); // > dateString 2021-07-06 14:23
return dateString;
},
},
watch:{
search(val){
if(!val && this.activeName=='first'){
this.params.name = val
this.getImageList()
}
if(!val && this.activeName=='second'){
this.params1.name = val
this.getImageList1()
}
}

},
mounted() {
this.getImageList()
},
created() {
}

};
</script>

<style scoped>
.header-wrapper {
background-color: #f5f5f6;
padding-top: 15px;
}
.image_text{
padding:25px 0 55px 0 ;
}
#header{
position: relative;
top:-40px;
}
.el-dropdown-menu__item--divided{
border-top: 1px solid blue;
}
.el-table thead{
background-color: #f5f5f6;
}
/deep/ .el-tabs__item:hover{
color: #000;
font-weight: 500;
}
/deep/ .el-tabs__item.is-active {
color: #000;
font-weight: 500;
}
/deep/ .el-tabs__active-bar{
background-color:#000
}

/deep/ .el-pagination.is-background .el-pager li:not(.disabled).active {
background-color: #5bb973;
color: #FFF;
}
/deep/ .el-pagination.is-background .el-pager li.active {
color: #fff;
cursor: default;
}
/deep/ .el-pagination.is-background .el-pager li:hover {
color: #5bb973;
}
/deep/ .el-pagination.is-background .el-pager li:not(.disabled):hover {
color: #5bb973;
}
/deep/ .el-pagination.is-background .el-pager li:not(.disabled).active:hover {
background-color: #5bb973;
color: #FFF;
}

/deep/ .el-pager li.active {
color: #08C0B9;
cursor: default;
}
/deep/ .el-pagination .el-pager li:hover {
color: #08C0B9;
}
/deep/ .el-pagination .el-pager li:not(.disabled):hover {
color: #08C0B9;
}
/* /deep/ .el-pagination.is-background .el-pager li:not(.disabled).active{
background-color: #5bb973;
color: #000;
} */
/* /deep/ .el-pager li:hover{
color: #000;
} */
#success{
background-color: #5bb973;
color: white;
}
.text-over{
overflow: hidden;
text-overflow: ellipsis;
vertical-align: middle;
white-space: nowrap;
}
</style>

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

@@ -26,7 +26,7 @@ import qs from 'qs';
import createDropzone from '../features/dropzone.js';

const {_AppSubUrl, _StaticUrlPrefix, csrf} = window.config;
// const uploadtype = 0;
const chunkSize = 1024 * 1024 * 64;

export default {
props:{
@@ -137,7 +137,6 @@ export default {
resetStatus() {
this.progress = 0;
this.status = '';
console.log(this.uploadtype)
},
updateProgress(file, progress) {
console.log("progress---",progress)
@@ -165,7 +164,6 @@ export default {
.getElementById('datasetId')
.getAttribute('datasetId');
this.resetStatus();
console.log(this.file,!this.file?.upload)
if(!this.file?.upload){
this.btnFlag = false
return
@@ -186,7 +184,6 @@ export default {
File.prototype.slice ||
File.prototype.mozSlice ||
File.prototype.webkitSlice,
chunkSize = 1024 * 1024 * 64,
chunks = Math.ceil(file.size / chunkSize),
spark = new SparkMD5.ArrayBuffer(),
fileReader = new FileReader();
@@ -327,7 +324,6 @@ export default {
},

async newMultiUpload(file) {
console.log(this.uploadtype,this)
const res = await axios.get('/attachments/new_multipart', {
params: {
totalChunkCounts: file.totalChunkCounts,
@@ -348,7 +344,6 @@ export default {
File.prototype.slice ||
File.prototype.mozSlice ||
File.prototype.webkitSlice,
chunkSize = 1024 * 1024 * 32,
chunks = Math.ceil(file.size / chunkSize),
fileReader = new FileReader(),
time = new Date().getTime();
@@ -457,7 +452,6 @@ export default {
}

async function completeUpload() {
console.log(_this.uploadtype)
return await axios.post(
'/attachments/complete_multipart',
qs.stringify({
@@ -494,7 +488,6 @@ export default {
1}/${chunks}个分片上传`
);
this.progress = Math.ceil((currentChunk / chunks) * 100);
console.log("((currentChunk / chunks) * 100).toFixed(2)",((currentChunk / chunks) * 100).toFixed(2))
this.updateProgress(file, ((currentChunk / chunks) * 100).toFixed(2));
this.status = `${this.dropzoneParams.data('uploading')} ${(
(currentChunk / chunks) *


+ 0
- 36
web_src/js/components/Model.vue View File

@@ -426,42 +426,6 @@ export default {
</script>

<style scoped>






/deep/ .el-pagination.is-background .el-pager li:not(.disabled).active {
background-color: #5bb973;
color: #FFF;
}
/deep/ .el-pagination.is-background .el-pager li.active {
color: #fff;
cursor: default;
}
/deep/ .el-pagination.is-background .el-pager li:hover {
color: #5bb973;
}
/deep/ .el-pagination.is-background .el-pager li:not(.disabled):hover {
color: #5bb973;
}
/deep/ .el-pagination.is-background .el-pager li:not(.disabled).active:hover {
background-color: #5bb973;
color: #FFF;
}

/deep/ .el-pager li.active {
color: #08C0B9;
cursor: default;
}
/deep/ .el-pagination .el-pager li:hover {
color: #08C0B9;
}
/deep/ .el-pagination .el-pager li:not(.disabled):hover {
color: #08C0B9;
}

.text-over{
overflow: hidden;
text-overflow: ellipsis;


+ 10
- 9
web_src/js/components/UserAnalysis.vue View File

@@ -67,6 +67,15 @@
label="PR数"
align="center">
</el-table-column>
<el-table-column
prop="UserIndex"
label="用户指数"
width="120px"
align="center">
<template slot-scope="scope">
{{scope.row.UserIndex | rounding}}
</template>
</el-table-column>
<el-table-column
prop="CommitCount"
label="commit数"
@@ -161,15 +170,7 @@
width="120px"
align="center">
</el-table-column>
<el-table-column
prop="UserIndex"
label="用户指数"
width="120px"
align="center">
<template slot-scope="scope">
{{scope.row.UserIndex | rounding}}
</template>
</el-table-column>
<el-table-column
prop="DataDate"
label="系统统计时间"


+ 773
- 0
web_src/js/components/images/Images.vue View File

@@ -0,0 +1,773 @@
<template>
<div>
<div class="header-wrapper">
<div class="ui container">
<el-row class="image_text">
<h1>云脑镜像</h1>
</el-row>
</div>
</div>
<div class="ui container" id="header">
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="公开镜像" name="first" v-loading="loadingPublic">
<template v-if="tableDataPublic.length!==0">
<el-row style="align-items: center;display: flex;">
<el-col :span="12">
<div>
<el-checkbox v-model="checked">仅显示平台推荐</el-checkbox>
</div>
</el-col>
<el-col :span="4"><div style="visibility: hidden;">TODO</div></el-col>
<el-col :span="8">
<div>
<el-input placeholder="搜镜像Tag/描述/标签..." v-model="search" class="input-with-select" @keyup.enter.native="searchName()">
<el-button id="success" slot="append" icon="el-icon-search" @click="searchName()">搜索</el-button>
</el-input>
</div>
</el-col>
</el-row>
<el-row style="margin-top:15px;">

<el-table
:data="tableDataPublic"
style="width: 100%"
:header-cell-style="tableHeaderStyle"
>
<el-table-column
label="镜像Tag"
min-width="19%"
align="left"
prop="tag"
>
<template slot-scope="scope">
<div style="display: flex;align-items: center;">
<a class="text-over image_title" :title="scope.row.tag">{{ scope.row.tag }}</a>
<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="镜像描述"
min-width="28%"
align="left"
prop="description"
>
<template slot-scope="scope">
<div class="image_desc" :title="scope.row.description">{{ scope.row.description}}</div>
<div v-if="!!scope.row.topics">
<span v-for="(topic,index) in scope.row.topics" class="ui repo-topic label topic" style="cursor: default;">{{topic}}</span>
</div>
</template>
</el-table-column>
<el-table-column
prop="cloudbrainType"
label="可用集群"
min-width="10%"
align="center"
>
<template slot-scope="scope">
{{scope.row.cloudbrainType | transformType}}
</template>
</el-table-column>
<el-table-column
prop="creator"
label="创建者"
min-width="8%"
align="center"
>
<template slot-scope="scope">
<a :href="'/' + scope.row.userName" :title="scope.row.userName">
<img :src="scope.row.relAvatarLink" class="ui avatar image">
</a>
</template>
</el-table-column>
<el-table-column
prop="createdUnix"
label="创建时间"
align="center"
min-width="14%"
>
<template slot-scope="scope">
{{scope.row.createdUnix | transformTimestamp}}
</template>
</el-table-column>
<el-table-column
align="center"
min-width="21%"
label="操作"
>
<template slot-scope="scope">
<div style="display: flex;justify-content: flex-end;align-items: center;">
<div style="display: flex;align-items: center;cursor:pointer;padding: 0 1rem;" @click="imageStar(scope.$index,scope.row.id,scope.row.isStar)">
<svg width="1.4em" height="1.4em" viewBox="0 0 32 32" class="heart-stroke" :class='{stars_active:scope.row.isStar}'><path d="M4.4 6.54c-1.761 1.643-2.6 3.793-2.36 6.056.24 2.263 1.507 4.521 3.663 6.534a29110.9 29110.9 0 0010.296 9.633l10.297-9.633c2.157-2.013 3.424-4.273 3.664-6.536.24-2.264-.599-4.412-2.36-6.056-1.73-1.613-3.84-2.29-6.097-1.955-1.689.25-3.454 1.078-5.105 2.394l-.4.319-.398-.319c-1.649-1.316-3.414-2.143-5.105-2.394a7.612 7.612 0 00-1.113-.081c-1.838 0-3.541.694-4.983 2.038z"></path></svg>
<span style="line-height: 2;margin-left:0.3rem;">{{scope.row.numStars}}</span>
</div>
<span style="padding: 0 1rem;color:#0366d6;cursor:pointer;" @click="copyUrl(scope.row.place)">复制地址</span>
</div>
</template>
</el-table-column>
</el-table>
</el-row>
<div class="ui container" style="margin-top:50px;text-align:center">
<el-pagination
background
@size-change="handleSizeChangePublic"
@current-change="handleCurrentChangePublic"
:current-page="currentPagePublic"
:page-size="pageSizePublic"
:page-sizes="[5,10,15]"
layout="total, sizes, prev, pager, next, jumper"
:total="totalNumPublic">
</el-pagination>
</div>
</template>
<template v-else>
<el-row style="align-items: center;display: flex;">
<el-col :span="12">
<div>
<el-checkbox v-model="checked">仅显示平台推荐</el-checkbox>
</div>
</el-col>
<el-col :span="4"><div style="visibility: hidden;">TODO</div></el-col>
<el-col :span="8">
<div>
<el-input placeholder="搜镜像Tag/描述/标签..." v-model="search" class="input-with-select" @keyup.enter.native="searchName()">
<el-button id="success" slot="append" icon="el-icon-search" @click="searchName()">搜索</el-button>
</el-input>
</div>
</el-col>
</el-row>
<el-empty :image-size="200"></el-empty>
</template>
</el-tab-pane>
<el-tab-pane label="我的镜像" name="second" v-loading="loadingCustom">
<template v-if="tableDataCustom.length!==0">
<el-row style="align-items: center;display: flex;">
<el-col :span="12">
<div style="visibility: hidden;">
TODO
</div>
</el-col>
<el-col :span="4"><div style="visibility: hidden;">TODO</div></el-col>
<el-col :span="8">
<div>
<el-input placeholder="搜镜像Tag/描述/标签..." v-model="search" class="input-with-select" @keyup.enter.native="searchName()">
<el-button id="success" slot="append" icon="el-icon-search" @click="searchName()">搜索</el-button>
</el-input>
</div>
</el-col>
</el-row>
<el-row style="margin-top:15px;">
<el-table
:data="tableDataCustom"
style="width: 100%"
:header-cell-style="tableHeaderStyle"
>
<el-table-column
label="镜像Tag"
min-width="19%"
align="left"
prop="tag"
>
<template slot-scope="scope">
<div style="display: flex;align-items: center;">
<a class="text-over image_title" :title="scope.row.tag">{{ scope.row.tag }}</a>&nbsp;&nbsp;&nbsp;
<i class="ri-lock-2-line" style="color: #fa8c16;" 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="镜像描述"
min-width="27%"
align="left"
prop="description"
>
<template slot-scope="scope">
<div class="image_desc" :title="scope.row.description">{{ scope.row.description}}</div>
<div v-if="!!scope.row.topics">
<span v-for="(topic,index) in scope.row.topics" class="ui repo-topic label topic" style="cursor: default;">{{topic}}</span>
</div>
</template>
</el-table-column>
<el-table-column
prop="cloudbrainType"
label="可用集群"
min-width="9%"
align="center"
>
<template slot-scope="scope">
{{scope.row.cloudbrainType | transformType}}
</template>
</el-table-column>
<el-table-column
prop="isPrivate"
label="状态"
min-width="10%"
align="center"
>
<template slot-scope="scope">
<div style="display:flex;align-items: center;justify-content: center;">
<span v-if="scope.row.isPrivate" style="color: rgb(250, 140, 22);">私有</span>
<span v-else style="color: rgb(19, 194, 141);">公开</span>
<el-tooltip class="item" effect="dark" content="镜像提交中..." placement="top">
<i v-if="scope.row.status===0" class="CREATING" style="margin-left:0.3rem"></i>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="检测提交镜像是否大小超过20G!" placement="top">
<i v-if="scope.row.status===2" class="FAILED" style="margin-left:0.3rem"></i>
</el-tooltip>
<el-tooltip class="item" effect="dark" content="镜像提交成功" placement="top">
<i v-if="scope.row.status===1" class="SUCCEEDED" style="margin-left:0.3rem"></i>
</el-tooltip>
</div>
</template>
</el-table-column>
<el-table-column
prop="createdUnix"
label="创建时间"
align="center"
min-width="14%"
>
<template slot-scope="scope">
{{scope.row.createdUnix | transformTimestamp}}
</template>
</el-table-column>
<el-table-column
align="center"
min-width="21%"
label="操作"
>
<template slot-scope="scope">
<div style="display: flex;justify-content: flex-end;align-items: center;">
<div style="display: flex;align-items: center;cursor: default;;padding: 0 1rem;">
<svg width="1.4em" height="1.4em" viewBox="0 0 32 32" class="heart-stroke"><path d="M4.4 6.54c-1.761 1.643-2.6 3.793-2.36 6.056.24 2.263 1.507 4.521 3.663 6.534a29110.9 29110.9 0 0010.296 9.633l10.297-9.633c2.157-2.013 3.424-4.273 3.664-6.536.24-2.264-.599-4.412-2.36-6.056-1.73-1.613-3.84-2.29-6.097-1.955-1.689.25-3.454 1.078-5.105 2.394l-.4.319-.398-.319c-1.649-1.316-3.414-2.143-5.105-2.394a7.612 7.612 0 00-1.113-.081c-1.838 0-3.541.694-4.983 2.038z"></path></svg>
<span style="line-height: 2;margin-left:0.3rem;">{{scope.row.numStars}}</span>
</div>
<span style="padding: 0 1rem;color:#0366d6;cursor:pointer;" @click="copyUrl(scope.row.place)">复制地址</span>
<div style="padding-left:1rem;cursor:pointer;">
<el-dropdown size="medium">
<span class="el-dropdown-link">
更多<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="eidtImage(scope.row.id)">编辑</el-dropdown-item>
<el-dropdown-item style="color: red;" @click.native="deleteImage(scope.row.id)">删除</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</div>
</template>
</el-table-column>
</el-table>
</el-row>
<div class="ui container" style="margin-top:50px;text-align:center">
<el-pagination
background
@size-change="handleSizeChangeCustom"
@current-change="handleCurrentChangeCustom"
:current-page="currentPageCustom"
:page-size="pageSizeCustom"
:page-sizes="[5,10,15]"
layout="total, sizes, prev, pager, next, jumper"
:total="totalNumCustom">
</el-pagination>
</div>
</template>
<template v-else>
<el-row style="align-items: center;display: flex;">
<el-col :span="12">
<div style="visibility: hidden;">
TODO
</div>
</el-col>
<el-col :span="4"><div style="visibility: hidden;">TODO</div></el-col>
<el-col :span="8">
<div>
<el-input placeholder="搜镜像Tag/描述/标签..." v-model="search" class="input-with-select" @keyup.enter.native="searchName()">
<el-button id="success" slot="append" icon="el-icon-search" @click="searchName()">搜索</el-button>
</el-input>
</div>
</el-col>
</el-row>
<el-empty :image-size="200"></el-empty>
</template>
</el-tab-pane>
<el-tab-pane label="我收藏的镜像" name="third">
<template v-if="tableDataStar.length!==0">
<el-row style="align-items: center;display: flex;">
<el-col :span="12">
<div style="visibility: hidden;">
TODO
</div>
</el-col>
<el-col :span="4"><div style="visibility: hidden;">TODO</div></el-col>
<el-col :span="8">
<div>
<el-input placeholder="搜镜像Tag/描述/标签..." v-model="search" class="input-with-select" @keyup.enter.native="searchName()">
<el-button id="success" slot="append" icon="el-icon-search" @click="searchName()">搜索</el-button>
</el-input>
</div>
</el-col>
</el-row>
<el-row style="margin-top:15px;">

<el-table
:data="tableDataStar"
style="width: 100%"
:header-cell-style="tableHeaderStyle"
>
<el-table-column
label="镜像Tag"
min-width="19%"
align="left"
prop="tag"
>
<template slot-scope="scope">
<div style="display: flex;align-items: center;">
<a class="text-over image_title" :title="scope.row.tag">{{ scope.row.tag }}</a>
<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="镜像描述"
min-width="28%"
align="left"
prop="description"
>
<template slot-scope="scope">
<div class="image_desc" :title="scope.row.description">{{ scope.row.description}}</div>
<div v-if="!!scope.row.topics">
<span v-for="(topic,index) in scope.row.topics" class="ui repo-topic label topic" style="cursor: default;">{{topic}}</span>
</div>
</template>
</el-table-column>
<el-table-column
prop="cloudbrainType"
label="可用集群"
min-width="10%"
align="center"
>
<template slot-scope="scope">
{{scope.row.cloudbrainType | transformType}}
</template>
</el-table-column>
<el-table-column
prop="creator"
label="创建者"
min-width="8%"
align="center"
>
<template slot-scope="scope">
<a :href="'/' + scope.row.userName" :title="scope.row.userName">
<img :src="scope.row.relAvatarLink" class="ui avatar image">
</a>
</template>
</el-table-column>
<el-table-column
prop="createdUnix"
label="创建时间"
align="center"
min-width="14%"
>
<template slot-scope="scope">
{{scope.row.createdUnix | transformTimestamp}}
</template>
</el-table-column>
<el-table-column
align="center"
min-width="21%"
label="操作"
>
<template slot-scope="scope">
<div style="display: flex;justify-content: flex-end;align-items: center;">
<div style="display: flex;align-items: center;cursor:pointer;padding: 0 1rem;" @click="imageUnstar(scope.row.id)">
<svg width="1.4em" height="1.4em" viewBox="0 0 32 32" class="heart-stroke stars_active"><path d="M4.4 6.54c-1.761 1.643-2.6 3.793-2.36 6.056.24 2.263 1.507 4.521 3.663 6.534a29110.9 29110.9 0 0010.296 9.633l10.297-9.633c2.157-2.013 3.424-4.273 3.664-6.536.24-2.264-.599-4.412-2.36-6.056-1.73-1.613-3.84-2.29-6.097-1.955-1.689.25-3.454 1.078-5.105 2.394l-.4.319-.398-.319c-1.649-1.316-3.414-2.143-5.105-2.394a7.612 7.612 0 00-1.113-.081c-1.838 0-3.541.694-4.983 2.038z"></path></svg>
<span style="line-height: 2;margin-left:0.3rem;">{{scope.row.numStars}}</span>
</div>
<span style="padding: 0 1rem;color:#0366d6;cursor:pointer;" @click="copyUrl(scope.row.place)">复制地址</span>
</div>
</template>
</el-table-column>
</el-table>
</el-row>
<div class="ui container" style="margin-top:50px;text-align:center">
<el-pagination
background
@size-change="handleSizeChangeStar"
@current-change="handleCurrentChangeStar"
:current-page="currentPageStar"
:page-size="pageSizeStar"
:page-sizes="[5,10,15]"
layout="total, sizes, prev, pager, next, jumper"
:total="totalNumStar">
</el-pagination>
</div>
</template>
<template v-else>
<el-row style="align-items: center;display: flex;">
<el-col :span="12">
<div style="visibility: hidden;">
TODO
</div>
</el-col>
<el-col :span="4"><div style="visibility: hidden;">TODO</div></el-col>
<el-col :span="8">
<div>
<el-input placeholder="搜镜像Tag/描述/标签..." v-model="search" class="input-with-select" @keyup.enter.native="searchName()">
<el-button id="success" slot="append" icon="el-icon-search" @click="searchName()">搜索</el-button>
</el-input>
</div>
</el-col>
</el-row>
<el-empty :image-size="200"></el-empty>
</template>
</el-tab-pane>
</el-tabs>
</div>
</div>

</template>

<script>

const {_AppSubUrl, _StaticUrlPrefix, csrf} = window.config;




export default {
components: {
},
data() {
return {
activeName: 'first',
search:'',
checked:false,
currentPagePublic:1,
pageSizePublic:10,
totalNumPublic:0,
paramsPublic:{page:1,pageSize:10,q:'',recommend:false},
tableDataPublic: [],
loadingPublic:false,

currentPageCustom:1,
pageSizeCustom:10,
totalNumCustom:0,
paramsCustom:{page:1,pageSize:10,q:''},
tableDataCustom: [],
starCustom:[],
loadingCustom:false,

currentPageStar:1,
pageSizeStar:10,
totalNumStar:0,
paramsStar:{page:1,pageSize:10,q:''},
tableDataStar: [],
loadingStar:false
};
},
methods: {
handleClick(tab, event) {
this.search = ''
if(tab.name=="first"){
this.paramsPublic.q = ''
this.getImageListPublic()
}
if(tab.name=="second"){
this.getImageListCustom()
}
if(tab.name=="third"){
this.getImageListStar()
}
},
tableHeaderStyle({row,column,rowIndex,columnIndex}){
if(rowIndex===0){
return 'background:#f5f5f6;color:#606266'
}
},
handleSizeChangePublic(val){
this.paramsPublic.pageSize = val
this.getImageListPublic()


},
handleCurrentChangePublic(val){
this.paramsPublic.page = val
this.getImageListPublic()

},
handleSizeChangeCustom(val){
this.paramsCustom.pageSize = val
this.getImageListCustom()


},
handleCurrentChangeCustom(val){
this.paramsCustom.page = val
this.getImageListCustom()

},
handleSizeChangeStar(val){
this.paramsStar.pageSize = val
this.getImageListStar()


},
handleCurrentChangeStar(val){
this.paramsStar.page = val
this.getImageListStar()

},
getImageListPublic(){
this.loadingPublic = true
this.$axios.get('/explore/images/public',{
params:this.paramsPublic
}).then((res)=>{
this.totalNumPublic = res.data.count
this.tableDataPublic = res.data.images
this.loadingPublic = false
})
},

getImageListCustom(){
this.loadingCustom = true
this.$axios.get('/explore/images/custom',{
params:this.paramsCustom
}).then((res)=>{
this.totalNumCustom = res.data.count
this.tableDataCustom = res.data.images
this.tableDataCustom.forEach(element => {
this.starCustom.push({id:element.id,})
});
this.loadingCustom = false
})
},

getImageListStar(){
this.loadingStar = true
this.$axios.get('/explore/images/star',{
params:this.paramsStar
}).then((res)=>{
this.totalNumStar = res.data.count
this.tableDataStar = res.data.images
this.loadingStar = false
})
},

deleteImage(id){
let flag=1
let _this = this
$('.ui.basic.modal.images')
.modal({
onDeny: function() {
flag = false
},
onApprove: function() {
_this.$axios.delete('/image/'+id).then((res)=>{
_this.getImageListCustom()
})
flag = true
},
onHidden: function() {
if (flag == false) {
$('.alert').html('您已取消操作').removeClass('alert-success').addClass('alert-danger').show().delay(1500).fadeOut();
}else{
$('.alert').html('删除成功').removeClass('alert-danger').addClass('alert-success').show().delay(1500).fadeOut();
}
}
})
.modal('show')
},
eidtImage(id){
location.href = `/image/${id}/imageSquare`
},
imageStar(index,id,isStar){
if(isStar){
this.$axios.put(`/image/${id}/action/unstar`).then((res)=>{
if(res.data.Code==0){
this.tableDataPublic[index].numStars = this.tableDataPublic[index].numStars - 1
this.tableDataPublic[index].isStar = false
}else{
console.log(res.data.Message)
}
})
}else{
this.$axios.put(`/image/${id}/action/star`).then((res)=>{
if(res.data.Code==0){
this.tableDataPublic[index].numStars = this.tableDataPublic[index].numStars + 1
this.tableDataPublic[index].isStar = true
}else{
console.log(res.data.Message)
}
})
}
},
imageUnstar(id){
this.$axios.put(`/image/${id}/action/unstar`).then((res)=>{
if(res.data.Code==0){
this.getImageListStar()
}else{
console.log(res.data.Message)
}
})
},
copyUrl(url){
const cInput = document.createElement('input')
cInput.value = url
document.body.appendChild(cInput)
cInput.select()
document.execCommand('Copy')
cInput.remove()
},
searchName(){
if(this.activeName=='first'){
this.paramsPublic.q = this.search
this.paramsPublic.page = 1
this.getImageListPublic()
}
if(this.activeName=='second'){
this.paramsCustom.q = this.search
this.paramsCustom.page = 1
this.getImageListCustom()
}
if(this.activeName=='third'){
this.paramsStar.q = this.search
this.paramsStar.page = 1
this.getImageListStar()
}
},
},
filters:{
transformType(val){
if(val==0){
return "GPU"
}
},
transformPravite(val){
if(val){
return "私有"
}else{
return "公开"
}
},
transformTimestamp(timestamp){
const date = new Date(parseInt(timestamp) * 1000);
const Y = date.getFullYear() + '-';
const M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
const D = (date.getDate() < 10 ? '0'+date.getDate() : date.getDate()) + ' ';
const h = (date.getHours() < 10 ? '0'+date.getHours() : date.getHours()) + ':';
const m = (date.getMinutes() <10 ? '0'+date.getMinutes() : date.getMinutes()) + ':' ;
const s = (date.getSeconds() <10 ? '0'+date.getSeconds() : date.getSeconds()) ; // 秒
const dateString = Y + M + D + h + m + s;
return dateString;
},
},
watch:{
checked(val){
this.paramsPublic.page = 1
this.paramsPublic.recommend = val
this.getImageListPublic()
}
},
mounted() {
},
created() {
const params = new URLSearchParams(location.search)
if(params.has('type')&&params.get('type')=='myimage'){
this.activeName = 'second'
this.getImageListCustom()
}
else{
this.getImageListPublic()
}
}

};
</script>

<style scoped>
.header-wrapper {
background-color: #f5f5f6;
padding-top: 15px;
}
.image_text{
padding:25px 0 55px 0 ;
}
#header{
position: relative;
top:-40px;
}
.el-dropdown-menu__item--divided{
border-top: 1px solid blue;
}
.el-table thead{
background-color: #f5f5f6;
}
/deep/ .el-tabs__item:hover{
color: #000;
font-weight: 500;
}
/deep/ .el-tabs__item.is-active {
color: #000;
font-weight: 500;
}
/deep/ .el-tabs__active-bar{
background-color:#000
}

#success{
background-color: #5bb973;
color: white;
}
.text-over{
overflow: hidden;
text-overflow: ellipsis;
vertical-align: middle;
white-space: nowrap;
}
.image_title{
display: inline-block;
cursor: default;
color: rgb(66, 98, 144);
}
.image_desc{
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
display: -webkit-box;
text-overflow: ellipsis;
overflow: hidden;
}
.heart-stroke{
stroke: #FA8C16;
stroke-width: 2;
fill: #fff
}
.stars_active{
fill: #FA8C16 !important;
stroke:#FA8C16 !important
}
</style>

+ 397
- 0
web_src/js/components/images/adminImages.vue View File

@@ -0,0 +1,397 @@
<template>
<div>
<div class="ui container" style="width: 80%;">
<div class="row" style="border: 1px solid #d4d4d5;margin-top: 15px;padding-top: 0;">
<div class="ui attached segment">
<div class="ui form ignore-dirty">
<div class="ui fluid action input">
<input type="text" placeholder="搜镜像Tag/描述/标签..." v-model="search" @keyup.enter="searchName()">
<button class="ui blue button" @click="searchName()">搜索</button>
</div>
</div>
</div>
<el-row style="padding: 1rem;">
<el-col :span="2" style="margin-right: 1rem;">
<el-checkbox v-model="checked" style="padding: 0.5rem 1rem;">仅显示平台推荐</el-checkbox>
</el-col>
<el-col :span="6">
<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:'全部',private:''}">全部</el-dropdown-item>
<el-dropdown-item :command="{label:'公开',private:false}">公开</el-dropdown-item>
<el-dropdown-item :command="{label:'公开',private:true}">私有</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-col>
</el-row>
<el-row>
<el-table
:data="tableDataCustom"
style="width: 100%"
:header-cell-style="tableHeaderStyle"
>
<el-table-column
label="镜像Tag"
min-width="19%"
align="left"
prop="tag"
>
<template slot-scope="scope">
<div style="display: flex;align-items: center;">
<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="镜像描述"
min-width="28%"
align="left"
prop="description"
>
<template slot-scope="scope">
<div class="image_desc" :title="scope.row.description">{{ scope.row.description}}</div>
<div v-if="!!scope.row.topics">
<span v-for="(topic,index) in scope.row.topics" class="ui repo-topic label topic" style="cursor: default;">{{topic}}</span>
</div>
</template>
</el-table-column>
<el-table-column
prop="cloudbrainType"
label="可用集群"
min-width="10%"
align="center"
>
<template slot-scope="scope">
{{scope.row.cloudbrainType | transformType}}
</template>
</el-table-column>
<el-table-column
prop="isPrivate"
label="状态"
min-width="8%"
align="center"
>
<template slot-scope="scope">
<span v-if="scope.row.isPrivate" style="color: rgb(250, 140, 22);">私有</span>
<span v-else style="color: rgb(19, 194, 141);">公开</span>
</template>
</el-table-column>
<el-table-column
prop="creator"
label="创建者"
min-width="7%"
align="center"
>
<template slot-scope="scope">
<a :href="'/' + scope.row.userName" :title="scope.row.userName">
<img :src="scope.row.relAvatarLink" class="ui avatar image">
</a>
</template>
</el-table-column>
<el-table-column
prop="createdUnix"
label="创建时间"
align="center"
min-width="13%"
>
<template slot-scope="scope">
{{scope.row.createdUnix | transformTimestamp}}
</template>
</el-table-column>
<el-table-column
align="center"
min-width="23%"
label="操作"
>
<template slot-scope="scope">
<div style="display: flex;justify-content: flex-end;align-items: center;">
<div style="display: flex;align-items: center;cursor: default;;padding: 0 1rem;">
<svg width="1.4em" height="1.4em" viewBox="0 0 32 32" class="heart-stroke"><path d="M4.4 6.54c-1.761 1.643-2.6 3.793-2.36 6.056.24 2.263 1.507 4.521 3.663 6.534a29110.9 29110.9 0 0010.296 9.633l10.297-9.633c2.157-2.013 3.424-4.273 3.664-6.536.24-2.264-.599-4.412-2.36-6.056-1.73-1.613-3.84-2.29-6.097-1.955-1.689.25-3.454 1.078-5.105 2.394l-.4.319-.398-.319c-1.649-1.316-3.414-2.143-5.105-2.394a7.612 7.612 0 00-1.113-.081c-1.838 0-3.541.694-4.983 2.038z"></path></svg>
<span style="line-height: 2;margin-left:0.3rem;">{{scope.row.numStars}}</span>
</div>
<template v-if="!scope.row.isPrivate">
<span style="padding: 0 1rem;color: rgb(250, 140, 22);cursor:pointer;" v-if="scope.row.type==5" @click="unSetRecommend(scope.$index,scope.row.id)">取消推荐</span>
<span style="padding: 0 1rem;color: rgb(19, 194, 141);cursor:pointer;" v-else @click="setRecommend(scope.$index,scope.row.id)">设为推荐</span>
</template>
<span style="padding: 0 1rem;color:#0366d6;cursor:pointer;" @click="copyUrl(scope.row.place)">复制地址</span>
<div style="padding-left:1rem;cursor:pointer;">
<el-dropdown size="medium">
<span class="el-dropdown-link">
更多<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item @click.native="eidtImage(scope.row.id)">编辑</el-dropdown-item>
<el-dropdown-item style="color: red;" @click.native="deleteImage(scope.row.id)">删除</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
</div>
</template>
</el-table-column>
</el-table>
</el-row>
<div class="ui container" style="padding:2rem 0;text-align:center">
<el-pagination
background
@size-change="handleSizeChangeCustom"
@current-change="handleCurrentChangeCustom"
:current-page="currentPageCustom"
:page-size="pageSizeCustom"
:page-sizes="[5,15,20]"
layout="total, sizes, prev, pager, next, jumper"
:total="totalNumCustom">
</el-pagination>
</div>
</div>
</div>
</div>

</template>

<script>

const {_AppSubUrl, _StaticUrlPrefix, csrf} = window.config;




export default {
components: {
},
data() {
return {
search:'',
dropdownPrivate:'全部',
checked:false,
currentPageCustom:1,
pageSizeCustom:15,
totalNumCustom:0,
paramsCustom:{page:1,pageSize:15,q:'',recommend:false},
tableDataCustom: [],
starCustom:[],
loadingCustom:false,
};
},
methods: {
tableHeaderStyle({row,column,rowIndex,columnIndex}){
if(rowIndex===0){
return 'background:#f5f5f6;color:#606266'
}
},

handleSizeChangeCustom(val){
this.paramsCustom.pageSize = val
this.getImageListCustom()
},
handleCurrentChangeCustom(val){
this.paramsCustom.page = val
this.getImageListCustom()
},

getImageListCustom(){
this.loadingCustom = true
this.$axios.get('/admin/images/data',{
params:this.paramsCustom
}).then((res)=>{
this.totalNumCustom = res.data.count
this.tableDataCustom = res.data.images
this.tableDataCustom.forEach(element => {
this.starCustom.push({id:element.id,})
});
this.loadingCustom = false
})
},
deleteImage(id){
let flag=1
let _this = this
$('.ui.basic.modal.images')
.modal({
onDeny: function() {
flag = false
},
onApprove: function() {
_this.$axios.delete('/image/'+id).then((res)=>{
_this.getImageListCustom()
})
flag = true
},
onHidden: function() {
if (flag == false) {
$('.alert').html('您已取消操作').removeClass('alert-success').addClass('alert-danger').show().delay(1500).fadeOut();
}else{
$('.alert').html('删除成功').removeClass('alert-danger').addClass('alert-success').show().delay(1500).fadeOut();
}
}
})
.modal('show')
},
eidtImage(id){
location.href = `/image/${id}/imageAdmin`
},
imageStar(index,id,isStar){
if(isStar){
this.$axios.put(`/image/${id}/action/unstar`).then((res)=>{
this.tableDataPublic[index].numStars = this.tableDataPublic[index].numStars - 1
this.tableDataPublic[index].isStar = false
})
}else{
this.$axios.put(`/image/${id}/action/star`).then((res)=>{
this.tableDataPublic[index].numStars = this.tableDataPublic[index].numStars + 1
this.tableDataPublic[index].isStar = true
})
}
},
copyUrl(url){
const cInput = document.createElement('input')
cInput.value = url
document.body.appendChild(cInput)
cInput.select()
document.execCommand('Copy')
cInput.remove()
},
searchName(){
this.paramsCustom.q = this.search
this.paramsCustom.page = 1
this.getImageListCustom()
},
setRecommend(index,id){
this.$axios.put(`/admin/image/${id}/action/recommend`).then((res)=>{
this.tableDataCustom[index].type = 5
})
},
unSetRecommend(index,id){
this.$axios.put(`/admin/image/${id}/action/unrecommend`).then((res)=>{
this.tableDataCustom[index].type = 0
})
},
handleCommand(command){
this.dropdownPrivate = command.label
this.paramsCustom.private = command.private
this.getImageListCustom()

}
},
filters:{
transformType(val){
if(val==0){
return "GPU"
}
},
transformPravite(val){
if(val){
return "私有"
}else{
return "公开"
}
},
transformTimestamp(timestamp){
const date = new Date(parseInt(timestamp) * 1000);
const Y = date.getFullYear() + '-';
const M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
const D = (date.getDate() < 10 ? '0'+date.getDate() : date.getDate()) + ' ';
const h = (date.getHours() < 10 ? '0'+date.getHours() : date.getHours()) + ':';
const m = (date.getMinutes() <10 ? '0'+date.getMinutes() : date.getMinutes()) + ':' ;
const s = (date.getSeconds() <10 ? '0'+date.getSeconds() : date.getSeconds()) ; // 秒
const dateString = Y + M + D + h + m + s;
return dateString;
},
},
watch:{
checked(val){
this.paramsCustom.page = 1
this.paramsCustom.recommend = val
this.getImageListCustom()
}

},
mounted() {
this.getImageListCustom()
},
created() {
}

};
</script>

<style scoped>
.header-wrapper {
background-color: #f5f5f6;
padding-top: 15px;
}
.image_text{
padding:25px 0 55px 0 ;
}
#header{
position: relative;
top:-40px;
}
.el-dropdown-menu__item--divided{
border-top: 1px solid blue;
}
.el-table thead{
background-color: #f5f5f6;
}
/deep/ .el-tabs__item:hover{
color: #000;
font-weight: 500;
}
/deep/ .el-tabs__item.is-active {
color: #000;
font-weight: 500;
}
/deep/ .el-tabs__active-bar{
background-color:#000
}

#success{
background-color: #5bb973;
color: white;
}
.text-over{
overflow: hidden;
text-overflow: ellipsis;
vertical-align: middle;
white-space: nowrap;
}
.image_title{
display: inline-block;
cursor: default;
color: rgb(66, 98, 144);
}
.image_desc{
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
display: -webkit-box;
text-overflow: ellipsis;
overflow: hidden;
}
.heart-stroke{
stroke: #FA8C16;
stroke-width: 2;
fill: #fff
}
.stars_active{
fill: #FA8C16 !important;
stroke:#FA8C16 !important
}
.header-new-drop{
width: 100%;
}
</style>

+ 357
- 0
web_src/js/components/images/selectImages.vue View File

@@ -0,0 +1,357 @@
<template>

<div class="inline required field" :class="{ 'unite': benchmarkNew, 'min_title': benchmarkNew}">
<label v-if="benchmarkNew" style="font-weight: normal;">镜像</label>
<label v-else>镜像</label>
<span v-if="benchmarkNew">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
<input v-if="benchmarkNew" type="text" name="image" :value="imageAddress" style="width: 48.5%;" placeholder="选择镜像或输入镜像地址">
<input v-else type="text" name="image" :value="imageAddress" placeholder="选择镜像或输入镜像地址">
<el-button type="text" @click="dialogVisible = true" icon="el-icon-plus" style="color: #0366d6;">选择镜像</el-button>
<el-dialog
title="选择镜像"
:visible.sync="dialogVisible"
width="50%"
>
<div class="ui icon input" style="z-index: 9999;position: absolute;right: 50px;height:30px;">
<i class="search icon" style="cursor: pointer;pointer-events:auto"></i>
<input type="text" placeholder="搜镜像Tag/描述/标签..." v-model="search">
</div>
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="公开镜像" name="first" v-loading="loadingPublic">
<div style="display: flex;align-items: center;justify-content: space-between;padding: 1rem 0;border-bottom:1px solid #F5F5F5" v-for="(publicData,index) in tableDataPublic" :key="index">
<div style="width: 90%;">
<div style="display: flex;align-items: center;justify-content: space-between;">
<div style="display: flex;align-items: center;">
<span class="panel_dataset_name text-over" style="margin-left: 0;">{{publicData.tag}} </span>
<img v-if="publicData.type==5" src="/img/jian.svg" style="margin-left: 0.5rem;">
</div>
<div v-if="!!publicData.topics" class="text-over">
<span v-for="(topic,index) in publicData.topics" class="ui repo-topic label topic">{{topic}}</span>
</div>
</div>
<div style="margin-top: 8px;display: flex;">
<a :title="publicData.userName" style="cursor: default;">
<img class="ui avatar mini image" style="width: 20px;height: 20px;" :src="publicData.relAvatarLink">
</a>
<span class="panel_datset_desc">{{publicData.description}}</span>
</div>
</div>
<div>
<button class="ui primary basic button mini" @click.stop.prevent="selectImages(publicData.place,publicData.tag)">使用</button>
</div>
</div>
<div class="ui container" style="margin-top:50px;text-align:center">
<el-pagination
background
@current-change="handleCurrentChangePublic"
:current-page="currentPagePublic"
:page-size="pageSizePublic"
layout="total, prev, pager, next"
:total="totalNumPublic">
</el-pagination>
</div>
</el-tab-pane>

<el-tab-pane label="我的镜像" name="second" v-loading="loadingCustom">
<div style="display: flex;align-items: center;justify-content: space-between;padding: 1rem 0;border-bottom:1px solid #F5F5F5" v-for="(customData,index) in tableDataCustom" :key="index">
<div style="width: 90%;">
<div style="display: flex;align-items: center;justify-content: space-between;">
<span class="panel_dataset_name text-over" style="margin-left: 0;">{{customData.tag}} </span>
<div v-if="!!customData.topics" class="text-over">
<span v-for="(topic,index) in customData.topics" class="ui repo-topic label topic">{{topic}}</span>
</div>
</div>
<div style="margin-top: 8px;display: flex;">
<a :title="customData.userName" style="cursor: default;">
<img class="ui avatar mini image" style="width: 20px;height: 20px;" :src="customData.relAvatarLink">
</a>
<span class="panel_datset_desc">{{customData.description}}</span>
</div>
</div>
<div>
<button v-if="customData.status===1" class="ui primary basic button mini" @click.stop.prevent="selectImages(customData.place,customData.tag)">使用</button>
<span v-if="customData.status===0" style="display: flex;align-items: center;">
<i class="CREATING"></i>
<span style="margin-left: 0.4em;font-size: 12px;color: #5A5A5A;">提交中</span>
</span>
<span v-if="customData.status===2" style="display: flex;align-items: center;">
<i class="FAILED"></i>
<el-tooltip class="item" effect="dark" content="检测提交镜像是否大小超过20G!" placement="left">
<span style="margin-left: 0.4em;font-size: 12px;color:red;">提交失败</span>
</el-tooltip>
</span>
</div>
</div>
<div class="ui container" style="margin-top:50px;text-align:center">
<el-pagination
background
@current-change="handleCurrentChangeCustom"
:current-page="currentPageCustom"
:page-size="pageSizeCustom"
layout="total, prev, pager, next"
:total="totalNumCustom">
</el-pagination>
</div>
</el-tab-pane>

<el-tab-pane label="我收藏的镜像" name="third">
<div style="display: flex;align-items: center;justify-content: space-between;padding: 1rem 0;border-bottom:1px solid #F5F5F5" v-for="(starData,index) in tableDataStar" :key="index">
<div style="width: 90%;">
<div style="display: flex;align-items: center;justify-content: space-between;">
<div style="display: flex;align-items: center;">
<span class="panel_dataset_name text-over" style="margin-left: 0;">{{starData.tag}} </span>
<img v-if="starData.type==5" src="/img/jian.svg" style="margin-left: 0.5rem;">
</div>
<div v-if="!!starData.topics" class="text-over">
<span v-for="(topic,index) in starData.topics" class="ui repo-topic label topic">{{topic}}</span>
</div>
</div>
<div style="margin-top: 8px;display: flex;">
<a :title="starData.userName" style="cursor: default;">
<img class="ui avatar mini image" style="width: 20px;height: 20px;" :src="starData.relAvatarLink">
</a>
<span class="panel_datset_desc">{{starData.description}}</span>
</div>
</div>
<div>
<button class="ui primary basic button mini" @click.stop.prevent="selectImages(starData.place,starData.tag)">使用</button>
</div>
</div>
<div class="ui container" style="margin-top:50px;text-align:center">
<el-pagination
background
@current-change="handleCurrentChangeStar"
:current-page="currentPageStar"
:page-size="pageSizeStar"
layout="total, prev, pager, next"
:total="totalNumStar">
</el-pagination>
</div>
</el-tab-pane>
</el-tabs>
</el-dialog>
</div>

</template>

<script>

const {_AppSubUrl, _StaticUrlPrefix, csrf} = window.config;




export default {
components: {
},
data() {
return {
dialogVisible:false,
benchmarkNew:false,
imageAddress:'',
activeName: 'first',
search:'',
checked:false,
currentPagePublic:1,
pageSizePublic:5,
totalNumPublic:0,
paramsPublic:{page:1,pageSize:5,q:'',recommend:false},
tableDataPublic: [],
loadingPublic:false,

currentPageCustom:1,
pageSizeCustom:5,
totalNumCustom:0,
paramsCustom:{page:1,pageSize:5,q:''},
tableDataCustom: [],
starCustom:[],
loadingCustom:false,

currentPageStar:1,
pageSizeStar:5,
totalNumStar:0,
paramsStar:{page:1,pageSize:5,q:''},
tableDataStar: [],
loadingStar:false
};
},
methods: {
handleClick(tab, event) {
this.search = ''
if(tab.name=="first"){
this.paramsPublic.q = ''
this.getImageListPublic()
}
if(tab.name=="second"){
this.getImageListCustom()
}
if(tab.name=="third"){
this.getImageListStar()
}
},
tableHeaderStyle({row,column,rowIndex,columnIndex}){
if(rowIndex===0){
return 'background:#f5f5f6;color:#606266'
}
},

handleCurrentChangePublic(val){
this.paramsPublic.page = val
this.getImageListPublic()

},

handleCurrentChangeCustom(val){
this.paramsCustom.page = val
this.getImageListCustom()

},
handleCurrentChangeStar(val){
this.paramsStar.page = val
this.getImageListStar()

},
getImageListPublic(){
this.loadingPublic = true
this.$axios.get('/explore/images/public',{
params:this.paramsPublic
}).then((res)=>{
this.totalNumPublic = res.data.count
this.tableDataPublic = res.data.images
this.loadingPublic = false
})
},

getImageListCustom(){
this.loadingCustom = true
this.$axios.get('/explore/images/custom',{
params:this.paramsCustom
}).then((res)=>{
this.totalNumCustom = res.data.count
this.tableDataCustom = res.data.images
this.tableDataCustom.forEach(element => {
this.starCustom.push({id:element.id,})
});
this.loadingCustom = false
})
},

getImageListStar(){
this.loadingStar = true
this.$axios.get('/explore/images/star',{
params:this.paramsStar
}).then((res)=>{
this.totalNumStar = res.data.count
this.tableDataStar = res.data.images
this.loadingStar = false
})
},
searchName(){
if(this.activeName=='first'){
this.paramsPublic.q = this.search
this.paramsPublic.page = 1
this.getImageListPublic()
}
if(this.activeName=='second'){
this.paramsCustom.q = this.search
this.paramsCustom.page = 1
this.getImageListCustom()
}
if(this.activeName=='third'){
this.paramsStar.q = this.search
this.paramsStar.page = 1
this.getImageListStar()
}
},
selectImages(place){
this.imageAddress = place
this.dialogVisible = false
},

},
watch:{
search(val){
if(this.activeName=='first'){
this.paramsPublic.q = val
this.getImageListPublic()
}
if(this.activeName=='second'){
this.paramsCustom.q = val
this.getImageListCustom()
}
if(this.activeName=='third'){
this.paramsStar.q = val
this.getImageListStar()
}
}

},
mounted() {
this.getImageListPublic()
if(location.href.indexOf('benchmark')!==-1 || location.href.indexOf('train-job')!==-1){
this.benchmarkNew = true
}
},
created() {
}

};
</script>

<style scoped>
.header-wrapper {
background-color: #f5f5f6;
padding-top: 15px;
}
.image_text{
padding:25px 0 55px 0 ;
}
#header{
position: relative;
top:-40px;
}
#success{
background-color: #5bb973;
color: white;
}
.text-over{
overflow: hidden;
text-overflow: ellipsis;
vertical-align: middle;
white-space: nowrap;
}
.image_title{
display: inline-block;
width: 80%;
cursor: default;
color: rgb(66, 98, 144);
}
.image_desc{
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
display: -webkit-box;
text-overflow: ellipsis;
overflow: hidden;
}
.heart-stroke{
stroke: #666;
stroke-width: 2;
fill: #fff
}
.stars_active{
fill: #FA8C16 !important;
stroke:#FA8C16 !important
}
</style>

+ 195
- 0
web_src/js/features/images.js View File

@@ -0,0 +1,195 @@
import Images from '../components/images/Images.vue';
import adminImages from '../components/images/adminImages.vue';
import selectImages from '../components/images/selectImages.vue';
import Vue from 'vue';
export default async function initImage(){
function validate() {
$("#form_image")
.form({
on: 'blur',
// inline:true,
fields: {
tag: {
identifier : 'tag',
rules: [
{
type: 'regExp[/^[A-Za-z0-9_.-]{1,100}[A-Za-z0-9_.]$/]',
}
]
},
description:{
identifier : 'description',
rules: [
{
type: 'empty',
}
]
},
}
})
}
function $params(obj) {
var str = [];
for (var p in obj) {
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
}
return str.join("&");
}
function initDropdown(){
$('#dropdown_image')
.dropdown({
allowAdditions: true,
onChange: function(value, text, $selectedItem) {
$('#course_label_item').empty()
}
})
$('#dropdown_image input.search').bind('input propertychange', function (event) {
// $("#dropdown_container").removeAttr("style");
const query = $('input.search').val()
if(!query){
$('#course_label_item').empty()
}else{
$.get(`/api/v1/image/topics/search?q=${query}`,(data)=>{
if(data.topics.length!==0){
let html=''
$('#course_label_item').empty()
data.topics.forEach(element => {
html += `<div class="item" data-value="${element.topic_name}">${element.topic_name}</div>`
});
$('#course_label_item').append(html)
}
})
}
});
}
validate()
initDropdown()
let link = $('.submit-image-tmplvalue').data('link')
let pageform = $('.submit-image-tmplvalue').data('edit-page') || ''
function postImage(formData) {
$("#mask").css({"display":"block","z-index":"999"})
$.ajax({
url:link,
type:'POST',
data:formData,
success:function(res){
if(res.Code===1){
$('.ui.info.message').text(res.Message).show().delay(1500).fadeOut();
}else if(res.Code==0){
if(location.href.indexOf('imageAdmin')!==-1){
location.href = `${window.config.AppSubUrl}/admin/images`
}else{
location.href = `${window.config.AppSubUrl}/explore/images?type=myimage`
}
}
},
error: function(xhr){
// 隐藏 loading
// 只有请求不正常(状态码不为200)才会执行
$('.ui.negative.message').html(xhr.responseText).show().delay(1500).fadeOut();
},
complete:function(xhr){
$("#mask").css({"display":"none","z-index":"1"})
}
})
}
$('.ui.create_image.green.button').click(()=>{
let pattenTag = new RegExp(/^[A-Za-z0-9_.-]{1,100}[A-Za-z0-9_.]$/)
if(!pattenTag.test($("input[name='tag']").val())){
$("input[name='tag']").parent().addClass('error')
return false
}
if(!$("textarea[name='description']").val()){
$("textarea[name='description']").parent().addClass('error')
return false
}
const postData = {
_csrf:$("input[name='_csrf']").val(),
tag:$("input[name='tag']").val(),
description:$("textarea[name='description']").val(),
type:$("input[name='type']").val(),
isPrivate:$("input[name='isPrivate']:checked").val(),
topics:$("input[name='topics']").val(),
id:$("input[name='id']").val()
}
let formData = $params(postData)
if($("input[name='edit']").val()=="edit"){
postImage(formData)
}
else{
$.ajax({
url:link+'/check',
type:'POST',
data:formData,
success:function(res){
if(res.Code===1){
$('.ui.modal.image_confirm_submit')
.modal({
onApprove: function() {
postImage(formData)
},
})
.modal('show')
}else if(res.Code==0){
postImage(formData)
}
},
error: function(xhr){
$('.ui.negative.message').text(xhr.responseText).show().delay(1500).fadeOut();
}
})
}
})
$('#cancel_submit_image').click(()=>{
if(link.includes('cloudbrain')){
let repoLink = link.split('cloudbrain')[0]
location.href = `${window.config.AppSubUrl}${repoLink}debugjob?debugListType=all`
}else if(pageform=='imageSquare'){
location.href = `${window.config.AppSubUrl}/explore/images?type=myimage`
}else if(pageform=='imageAdmin'){
location.href = `${window.config.AppSubUrl}/admin/images`
}
})
function initVueImages() {
const el = document.getElementById('images');
if (!el) {
return;
}
new Vue({
el:el,
render: h => h(Images)
});
}
function initVueAdminImages() {
const el = document.getElementById('images-admin');
if (!el) {
return;
}
new Vue({
el:el,
render: h => h(adminImages)
});
}
function initVueselectImages() {
const el = document.getElementById('images-new-cb');
if (!el) {
return;
}
new Vue({
el:el,
render: h => h(selectImages)
});
}
initVueImages()
initVueAdminImages()
initVueselectImages()
}

+ 37
- 12
web_src/js/index.js View File

@@ -36,13 +36,14 @@ import {createCodeEditor} from './features/codeeditor.js';
import MinioUploader from './components/MinioUploader.vue';
import ObsUploader from './components/ObsUploader.vue';
import EditAboutInfo from './components/EditAboutInfo.vue';
import Images from './components/Images.vue';
// import Images from './components/Images.vue';
import EditTopics from './components/EditTopics.vue';
import DataAnalysis from './components/DataAnalysis.vue'
import Contributors from './components/Contributors.vue'
import Model from './components/Model.vue';
import WxAutorize from './components/WxAutorize.vue'
import initCloudrain from './features/cloudrbanin.js'
import initImage from './features/images.js'
// import $ from 'jquery.js'

Vue.use(ElementUI);
@@ -2962,7 +2963,7 @@ $(document).ready(async () => {
initVueEditAbout();
initVueEditTopic();
initVueContributors();
initVueImages();
// initVueImages();
initVueModel();
initVueDataAnalysis();
initVueWxAutorize();
@@ -2984,6 +2985,7 @@ $(document).ready(async () => {
initTribute();
initDropDown();
initCloudrain();
initImage();

// Repo clone url.
if ($('#repo-clone-url').length > 0) {
@@ -4284,6 +4286,29 @@ function initVueDataset() {
}
}
},
watch:{
searchDataItem(){
switch(this.activeName){
case 'first':
this.page = 1
this.getCurrentRepoDataset(this.repolink,this.cloudbrainType)
break
case 'second':
this.page = 1
this.getMyDataset(this.repolink,this.cloudbrainType)
break
case 'third':
this.page = 1
this.getPublicDataset(this.repolink,this.cloudbrainType)
break
case 'fourth':
this.page = 1
this.getStarDataset(this.repolink,this.cloudbrainType)
break
}
}

},
});

}
@@ -4315,20 +4340,20 @@ function initVueContributors() {
}


function initVueImages() {
const el = document.getElementById('images');
// function initVueImages() {
// const el = document.getElementById('images');
if (!el) {
return;
}
// if (!el) {
// return;
// }

new Vue({
el: '#images',
// new Vue({
// el: '#images',
render: h => h(Images)
});
}
// render: h => h(Images)
// });
// }
function initVueModel() {
const el = document.getElementById('model_list');


+ 1
- 1
web_src/js/vendor/semanticdropdown.js View File

@@ -1071,7 +1071,7 @@ $.fn.dropdown = function(parameters) {
;
if(settings.allowAdditions || (hasSelected && !module.is.multiple())) {
module.debug('Forcing partial selection to selected item', $selectedItem);
$selectedItem[0].click();
// $selectedItem[0].click();
return;
}
else {


+ 8
- 8
web_src/less/openi.less View File

@@ -396,10 +396,10 @@ display: block;
margin: -1px;
background: #FFF !important;
}
#deletemodel {
width: 100%;
height: 100%;
}
// #deletemodel {
// width: 100%;
// height: 100%;
// }
/* 弹窗 */

#mask {
@@ -969,10 +969,10 @@ display: block;
color: red;
}

.ui.list .list>.item>img.image+.content, .ui.list>.item>img.image+.content {
width: calc(100% - 4.0em);
margin-left: 0;
}
// .ui.list .list>.item>img.image+.content, .ui.list>.item>img.image+.content {
// width: calc(100% - 4.0em);
// margin-left: 0;
// }
.seach .ui.list .list>.item .header, .seach .ui.list>.item .header{
margin-bottom: 0.5em;


Loading…
Cancel
Save