Browse Source

Merge remote-tracking branch 'origin/develop' into fix-1656

fix-1656
liuzx 3 years ago
parent
commit
7c85bfc3d3
86 changed files with 8961 additions and 702 deletions
  1. +6
    -0
      README.md
  2. +0
    -70
      custom/public/css/git.openi.css
  3. +1
    -0
      models/action.go
  4. +132
    -0
      models/attachment.go
  5. +16
    -0
      models/base_message.go
  6. +94
    -14
      models/cloudbrain.go
  7. +71
    -29
      models/dataset.go
  8. +70
    -0
      models/dataset_star.go
  9. +186
    -0
      models/dbsql/dataset_foreigntable_for_es.sql
  10. +215
    -0
      models/dbsql/issue_foreigntable_for_es.sql
  11. +532
    -0
      models/dbsql/repo_foreigntable_for_es.sql
  12. +308
    -0
      models/dbsql/user_foreigntable_for_es.sql
  13. +0
    -5
      models/file_chunk.go
  14. +2
    -0
      models/models.go
  15. +38
    -7
      models/repo.go
  16. +7
    -3
      models/repo_list.go
  17. +83
    -0
      models/search_record.go
  18. +5
    -4
      models/user.go
  19. +3
    -0
      modules/auth/cloudbrain.go
  20. +17
    -6
      modules/auth/dataset.go
  21. +49
    -14
      modules/cloudbrain/cloudbrain.go
  22. +2
    -0
      modules/context/repo.go
  23. +17
    -0
      modules/dataset/dataset.go
  24. +2
    -1
      modules/modelarts/modelarts.go
  25. +21
    -14
      modules/setting/setting.go
  26. +26
    -20
      modules/templates/helper.go
  27. +67
    -9
      options/locale/locale_en-US.ini
  28. +61
    -2
      options/locale/locale_zh-CN.ini
  29. +9
    -5
      public/home/home.js
  30. +1281
    -0
      public/home/search.js
  31. +3
    -3
      public/self/dataset_preview.js
  32. +3
    -3
      routers/admin/cloudbrains.go
  33. +10
    -2
      routers/api/v1/api.go
  34. +61
    -6
      routers/api/v1/repo/cloudbrain.go
  35. +66
    -26
      routers/api/v1/repo/modelarts.go
  36. +29
    -6
      routers/home.go
  37. +2
    -0
      routers/init.go
  38. +2
    -0
      routers/private/internal.go
  39. +76
    -19
      routers/repo/attachment.go
  40. +370
    -44
      routers/repo/cloudbrain.go
  41. +367
    -52
      routers/repo/dataset.go
  42. +141
    -51
      routers/repo/modelarts.go
  43. +0
    -4
      routers/repo/setting.go
  44. +36
    -1
      routers/routes/routes.go
  45. +1190
    -0
      routers/search.go
  46. +1
    -1
      services/socketwrap/clientManager.go
  47. +3
    -3
      templates/base/head.tmpl
  48. +3
    -3
      templates/base/head_fluid.tmpl
  49. +3
    -3
      templates/base/head_home.tmpl
  50. +4
    -4
      templates/base/head_navbar.tmpl
  51. +4
    -4
      templates/base/head_navbar_fluid.tmpl
  52. +4
    -4
      templates/base/head_navbar_pro.tmpl
  53. +3
    -3
      templates/base/head_pro.tmpl
  54. +138
    -0
      templates/custom/select_dataset.tmpl
  55. +69
    -0
      templates/explore/dataset_left.tmpl
  56. +1
    -1
      templates/explore/dataset_list.tmpl
  57. +210
    -9
      templates/explore/datasets.tmpl
  58. +13
    -1
      templates/explore/repos.tmpl
  59. +95
    -0
      templates/explore/search_new.tmpl
  60. +45
    -0
      templates/repo/attachment/edit.tmpl
  61. +73
    -0
      templates/repo/attachment/upload.tmpl
  62. +14
    -2
      templates/repo/cloudbrain/benchmark/index.tmpl
  63. +14
    -2
      templates/repo/cloudbrain/benchmark/show.tmpl
  64. +5
    -11
      templates/repo/cloudbrain/new.tmpl
  65. +10
    -2
      templates/repo/cloudbrain/show.tmpl
  66. +451
    -0
      templates/repo/cloudbrain/trainjob/new.tmpl
  67. +653
    -0
      templates/repo/cloudbrain/trainjob/show.tmpl
  68. +69
    -0
      templates/repo/datasets/create.tmpl
  69. +72
    -0
      templates/repo/datasets/edit.tmpl
  70. +321
    -125
      templates/repo/datasets/index.tmpl
  71. +14
    -1
      templates/repo/debugjob/index.tmpl
  72. +1
    -1
      templates/repo/header.tmpl
  73. +14
    -1
      templates/repo/modelarts/inferencejob/index.tmpl
  74. +7
    -1
      templates/repo/modelarts/inferencejob/show.tmpl
  75. +10
    -30
      templates/repo/modelarts/notebook/new.tmpl
  76. +53
    -5
      templates/repo/modelarts/trainjob/index.tmpl
  77. +19
    -1
      templates/repo/modelarts/trainjob/new.tmpl
  78. +12
    -2
      templates/repo/modelarts/trainjob/show.tmpl
  79. +1
    -1
      templates/repo/modelmanage/index.tmpl
  80. +5
    -1
      templates/user/dashboard/feeds.tmpl
  81. +121
    -51
      web_src/js/components/MinioUploader.vue
  82. +4
    -1
      web_src/js/components/ObsUploader.vue
  83. +7
    -7
      web_src/js/components/ProAnalysis.vue
  84. +590
    -1
      web_src/js/index.js
  85. +35
    -0
      web_src/less/_dataset.less
  86. +113
    -0
      web_src/less/openi.less

+ 6
- 0
README.md View File

@@ -41,6 +41,7 @@
## 授权许可
本项目采用 MIT 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://git.openi.org.cn/OpenI/aiforge/src/branch/develop/LICENSE) 文件中。


## 需要帮助?
如果您在使用或者开发过程中遇到问题,可以在以下渠道咨询:
- 点击[这里](https://git.openi.org.cn/OpenI/aiforge/issues)在线提交问题(点击页面右上角绿色按钮**创建任务**)
@@ -49,3 +50,8 @@

## 启智社区小白训练营:
- 结合案例给大家详细讲解如何使用社区平台,帮助无技术背景的小白成长为启智社区达人 (https://git.openi.org.cn/zeizei/OpenI_Learning)

## 平台引用
如果本平台对您的科研工作提供了帮助,可在论文致谢中加入:
英文版:```Thanks for the support provided by OpenI Community (https://git.openi.org.cn).```
中文版:```感谢启智社区提供的技术支持(https://git.openi.org.cn)。```

+ 0
- 70
custom/public/css/git.openi.css View File

@@ -44,12 +44,6 @@
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.ui.label{
font-weight: normal;
}
.active {
color: #0366D6 !important;
}

.opacity5{ opacity:0.5;}
.radius15{ border-radius:1.5rem !important; }
@@ -287,70 +281,6 @@
position: relative;
}

/**seach**/
/**搜索导航条适配窄屏**/
.seachnav{
overflow-x: auto;
overflow-y: hidden;
scrollbar-width: none; /* firefox */
-ms-overflow-style: none; /* IE 10+ */
}
.seachnav::-webkit-scrollbar {
display: none; /* Chrome Safari */
}
.ui.green.button, .ui.green.buttons .button{
background-color: #5BB973;
}
.seach .repos--seach{
padding-bottom: 0;
border-bottom: none;
}
.seach .ui.secondary.pointing.menu{
border-bottom: none;
}
.seach .ui.secondary.pointing.menu .item > i{
margin-right: 5px;
}
.seach .ui.secondary.pointing.menu .active.item{
border-bottom-width: 2px;
margin: 0 0 -1px;
}
.seach .ui.menu .active.item>.label {
background: #1684FC;
color: #FFF;
}
.seach .ui.menu .item>.label:not(.active.item>.label) {
background: #e8e8e8;
color: rgba(0,0,0,.6);
}

.highlight{
color: red;
}
.ui.list .list>.item>img.image+.content, .ui.list>.item>img.image+.content {
width: calc(100% - 3.0em);
margin-left: 0;
}

.seach .ui.list .list>.item .header, .seach .ui.list>.item .header{
margin-bottom: 0.5em;
font-size: 1.4rem !important;
font-weight: normal;
}
.seach .time, .seach .time a{
font-size: 12px;
color: grey;
}

.seach .list .item.members .ui.avatar.image {
width: 3.2em;
height: 3.2em;
}
.ui.list .list>.item.members>img.image+.content, .ui.list>.item.members>img.image+.content {
width: calc(100% - 4.0em);
margin-left: 0;
}

@media only screen and (max-width: 767px) {
.am-mt-30{ margin-top: 1.5rem !important;}
.ui.secondary.hometop.segment{


+ 1
- 0
models/action.go View File

@@ -57,6 +57,7 @@ const (
ActionCreateInferenceTask // 28
ActionCreateBenchMarkTask //29
ActionCreateNewModelTask //30
ActionCreateGPUTrainTask //31
)

// Action represents user operation type and other information to


+ 132
- 0
models/attachment.go View File

@@ -9,6 +9,7 @@ import (
"fmt"
"io"
"path"
"strings"

"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/obs"
@@ -18,6 +19,7 @@ import (
"code.gitea.io/gitea/modules/timeutil"

gouuid "github.com/satori/go.uuid"
"xorm.io/builder"
"xorm.io/xorm"
)

@@ -38,6 +40,7 @@ type Attachment struct {
UploaderID int64 `xorm:"INDEX DEFAULT 0"` // Notice: will be zero before this column added
CommentID int64
Name string
Description string `xorm:"TEXT"`
DownloadCount int64 `xorm:"DEFAULT 0"`
Size int64 `xorm:"DEFAULT 0"`
IsPrivate bool `xorm:"DEFAULT false"`
@@ -47,6 +50,7 @@ type Attachment struct {

FileChunk *FileChunk `xorm:"-"`
CanDel bool `xorm:"-"`
Uploader *User `xorm:"-"`
}

type AttachmentUsername struct {
@@ -54,6 +58,27 @@ type AttachmentUsername struct {
Name string
}

type AttachmentInfo struct {
Attachment `xorm:"extends"`
Repo *Repository `xorm:"extends"`
RelAvatarLink string `xorm:"extends"`
UserName string `xorm:"extends"`
}

type AttachmentsOptions struct {
ListOptions
DatasetIDs []int64
DecompressState int
Type int
UploaderID int64
NeedDatasetIDs bool
NeedIsPrivate bool
IsPrivate bool
JustNeedZipFile bool
NeedRepoInfo bool
Keyword string
}

func (a *Attachment) AfterUpdate() {
if a.DatasetID > 0 {
datasetIsPublicCount, err := x.Where("dataset_id = ? AND is_private = ?", a.DatasetID, false).Count(new(Attachment))
@@ -326,6 +351,18 @@ func DeleteAttachmentsByComment(commentID int64, remove bool) (int, error) {
func UpdateAttachment(atta *Attachment) error {
return updateAttachment(x, atta)
}
func UpdateAttachmentDescription(atta *Attachment) error {
return updateAttachmentDescription(x, atta)
}

func updateAttachmentDescription(e Engine, atta *Attachment) error {
var sess *xorm.Session

sess = e.ID(atta.ID)

_, err := sess.Cols("description").Update(atta)
return err
}

func updateAttachment(e Engine, atta *Attachment) error {
var sess *xorm.Session
@@ -503,3 +540,98 @@ func GetAttachmentSizeByDatasetID(datasetID int64) (int64, error) {
func GetAllAttachmentSize() (int64, error) {
return x.SumInt(&Attachment{}, "size")
}

func Attachments(opts *AttachmentsOptions) ([]*AttachmentInfo, int64, error) {
sess := x.NewSession()
defer sess.Close()

var cond = builder.NewCond()
if opts.NeedDatasetIDs {
cond = cond.And(
builder.In("attachment.dataset_id", opts.DatasetIDs),
)
}

if opts.UploaderID > 0 {
cond = cond.And(
builder.Eq{"attachment.uploader_id": opts.UploaderID},
)
}

if (opts.Type) >= 0 {
cond = cond.And(
builder.Eq{"attachment.type": opts.Type},
)
}

if opts.NeedIsPrivate {
cond = cond.And(
builder.Eq{"attachment.is_private": opts.IsPrivate},
)
}

if opts.JustNeedZipFile {
var DecompressState []int32
DecompressState = append(DecompressState, DecompressStateDone, DecompressStateIng, DecompressStateFailed)
cond = cond.And(
builder.In("attachment.decompress_state", DecompressState),
)
}

var count int64
var err error
if len(opts.Keyword) == 0 {
count, err = sess.Where(cond).Count(new(Attachment))
} else {
lowerKeyWord := strings.ToLower(opts.Keyword)

cond = cond.And(builder.Or(builder.Like{"LOWER(attachment.name)", lowerKeyWord}, builder.Like{"LOWER(attachment.description)", lowerKeyWord}))
count, err = sess.Table(&Attachment{}).Where(cond).Count(new(AttachmentInfo))

}

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

if opts.Page >= 0 && opts.PageSize > 0 {
var start int
if opts.Page == 0 {
start = 0
} else {
start = (opts.Page - 1) * opts.PageSize
}
sess.Limit(opts.PageSize, start)
}

sess.OrderBy("attachment.created_unix DESC")
attachments := make([]*AttachmentInfo, 0, setting.UI.DatasetPagingNum)
if err := sess.Table(&Attachment{}).Where(cond).
Find(&attachments); err != nil {
return nil, 0, fmt.Errorf("Find: %v", err)
}

if opts.NeedRepoInfo {
for _, attachment := range attachments {
dataset, err := GetDatasetByID(attachment.DatasetID)
if err != nil {
return nil, 0, fmt.Errorf("GetDatasetByID failed error: %v", err)
}
repo, err := GetRepositoryByID(dataset.RepoID)
if err == nil {
attachment.Repo = repo
} else {
return nil, 0, fmt.Errorf("GetRepositoryByID failed error: %v", err)
}
user, err := GetUserByID(attachment.UploaderID)
if err == nil {
attachment.RelAvatarLink = user.RelAvatarLink()
attachment.UserName = user.Name
} else {
return nil, 0, fmt.Errorf("GetUserByID failed error: %v", err)
}
}
}

return attachments, count, nil
}

+ 16
- 0
models/base_message.go View File

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

type BaseMessage struct {
Code int
Message string
}

var BaseOKMessage = BaseMessage{
0, "",
}

func BaseErrorMessage(message string) BaseMessage {
return BaseMessage{
1, message,
}
}

+ 94
- 14
models/cloudbrain.go View File

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

import (
"code.gitea.io/gitea/modules/util"
"encoding/json"
"fmt"
"strconv"
@@ -20,8 +21,16 @@ type JobType string
type ModelArtsJobStatus string

const (
TypeCloudBrainOne int = iota
TypeCloudBrainTwo

TypeCloudBrainAll = -1
)

const (
NPUResource = "NPU"
GPUResource = "CPU/GPU"
AllResource = "all"

//notebook storage category
EVSCategory = "EVS"
@@ -86,6 +95,8 @@ const (
ModelArtsTrainJobCheckRunning ModelArtsJobStatus = "CHECK_RUNNING" //审核作业正在运行中
ModelArtsTrainJobCheckRunningCompleted ModelArtsJobStatus = "CHECK_RUNNING_COMPLETED" //审核作业已经完成
ModelArtsTrainJobCheckFailed ModelArtsJobStatus = "CHECK_FAILED" //审核作业失败

DURATION_STR_ZERO = "00:00:00"
)

type Cloudbrain struct {
@@ -102,15 +113,15 @@ type Cloudbrain struct {
ContainerIp string
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
Duration int64
TrainJobDuration string
Image string //镜像名称
GpuQueue string //GPU类型即GPU队列
ResourceSpecId int //GPU规格id
DeletedAt time.Time `xorm:"deleted"`
CanDebug bool `xorm:"-"`
CanDel bool `xorm:"-"`
CanModify bool `xorm:"-"`
Duration int64 `xorm:"DEFAULT 0"` //运行时长 单位秒
TrainJobDuration string `xorm:"DEFAULT '00:00:00'"`
Image string //镜像名称
GpuQueue string //GPU类型即GPU队列
ResourceSpecId int //GPU规格id
DeletedAt time.Time `xorm:"deleted"`
CanDebug bool `xorm:"-"`
CanDel bool `xorm:"-"`
CanModify bool `xorm:"-"`
Type int
BenchmarkTypeID int
BenchmarkChildTypeID int
@@ -150,6 +161,64 @@ type Cloudbrain struct {
Repo *Repository `xorm:"-"`
BenchmarkTypeName string `xorm:"-"`
BenchmarkTypeRankLink string `xorm:"-"`
StartTime timeutil.TimeStamp
EndTime timeutil.TimeStamp
}

func (task *Cloudbrain) ComputeAndSetDuration() {
var d int64
if task.StartTime == 0 {
d = 0
} else if task.EndTime == 0 {
if !task.IsTerminal() {
d = time.Now().Unix() - task.StartTime.AsTime().Unix()
}
} else {
d = task.EndTime.AsTime().Unix() - task.StartTime.AsTime().Unix()
}

if d < 0 {
d = 0
}
task.Duration = d
task.TrainJobDuration = ConvertDurationToStr(d)
}

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

func ConvertDurationToStr(duration int64) string {
if duration == 0 {
return DURATION_STR_ZERO
}
return util.AddZero(duration/3600) + ":" + util.AddZero(duration%3600/60) + ":" + util.AddZero(duration%60)
}

func IsTrainJobTerminal(status string) bool {
return status == string(ModelArtsTrainJobCompleted) || status == string(ModelArtsTrainJobFailed) || status == string(ModelArtsTrainJobKilled)
}

func IsModelArtsDebugJobTerminal(status string) bool {
return status == string(ModelArtsStopped)
}

func IsCloudBrainOneDebugJobTerminal(status string) bool {
return status == string(JobStopped) || status == string(JobFailed) || status == string(JobSucceeded)
}

func ParseAndSetDurationFromCloudBrainOne(result JobResultPayload, task *Cloudbrain) {
isActivated := result.JobStatus.CreatedTime > 0
if task.StartTime == 0 && isActivated {
task.StartTime = timeutil.TimeStamp(result.JobStatus.CreatedTime / 1000)
}
if task.EndTime == 0 && IsCloudBrainOneDebugJobTerminal(task.Status) && isActivated {
if result.JobStatus.CompletedTime > 0 {
task.EndTime = timeutil.TimeStamp(result.JobStatus.CompletedTime / 1000)
}
}
task.ComputeAndSetDuration()
}

type CloudbrainInfo struct {
@@ -319,7 +388,7 @@ type JobResultPayload struct {
AppProgress string `json:"appProgress"`
AppTrackingURL string `json:"appTrackingUrl"`
AppLaunchedTime int64 `json:"appLaunchedTime"`
AppCompletedTime interface{} `json:"appCompletedTime"`
AppCompletedTime int64 `json:"appCompletedTime"`
AppExitCode int `json:"appExitCode"`
AppExitDiagnostics string `json:"appExitDiagnostics"`
AppExitType interface{} `json:"appExitType"`
@@ -1019,6 +1088,7 @@ type GetTrainJobResult struct {
NasShareAddr string `json:"nas_share_addr"`
DatasetName string
ModelMetricList string `json:"model_metric_list"` //列表里包含f1_score,recall,precision,accuracy,若有的话
StartTime int64 `json:"start_time"` //训练作业开始时间。
}

type GetTrainJobLogResult struct {
@@ -1283,6 +1353,7 @@ func CloudbrainsVersionList(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int, e
}

func CreateCloudbrain(cloudbrain *Cloudbrain) (err error) {
cloudbrain.TrainJobDuration = DURATION_STR_ZERO
if _, err = x.Insert(cloudbrain); err != nil {
return err
}
@@ -1327,13 +1398,13 @@ func GetCloudbrainByJobIDAndIsLatestVersion(jobID string, isLatestVersion string

func GetCloudbrainsNeededStopByUserID(userID int64) ([]*Cloudbrain, error) {
cloudBrains := make([]*Cloudbrain, 0)
err := x.Cols("job_id", "status", "type", "job_type", "version_id").Where("user_id=? AND status !=?", userID, string(JobStopped)).Find(&cloudBrains)
err := x.Cols("job_id", "status", "type", "job_type", "version_id", "start_time").Where("user_id=? AND status !=?", userID, string(JobStopped)).Find(&cloudBrains)
return cloudBrains, err
}

func GetCloudbrainsNeededStopByRepoID(repoID int64) ([]*Cloudbrain, error) {
cloudBrains := make([]*Cloudbrain, 0)
err := x.Cols("job_id", "status", "type", "job_type", "version_id").Where("repo_id=? AND status !=?", repoID, string(JobStopped)).Find(&cloudBrains)
err := x.Cols("job_id", "status", "type", "job_type", "version_id", "start_time").Where("repo_id=? AND status !=?", repoID, string(JobStopped)).Find(&cloudBrains)
return cloudBrains, err
}

@@ -1377,7 +1448,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").Update(job)
_, err := sess.Cols("status", "train_job_duration", "duration", "start_time", "end_time").Update(job)
return err
}

@@ -1427,6 +1498,15 @@ func GetCloudBrainUnStoppedJob() ([]*Cloudbrain, error) {
Find(&cloudbrains)
}

func GetStoppedJobWithNoDurationJob() ([]*Cloudbrain, error) {
cloudbrains := make([]*Cloudbrain, 0)
return cloudbrains, x.
In("status", ModelArtsTrainJobCompleted, ModelArtsTrainJobFailed, ModelArtsTrainJobKilled, ModelArtsStopped, JobStopped, JobFailed, JobSucceeded).
Where("train_job_duration is null or train_job_duration = '' ").
Limit(100).
Find(&cloudbrains)
}

func GetCloudbrainCountByUserID(userID int64, jobType string) (int, error) {
count, err := x.In("status", JobWaiting, JobRunning).And("job_type = ? and user_id = ? and type = ?", jobType, userID, TypeCloudBrainOne).Count(new(Cloudbrain))
return int(count), err
@@ -1457,7 +1537,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").Update(job)
_, err := sess.Cols("status", "train_job_duration", "duration", "start_time", "end_time").Update(job)
return err
}
func RestartCloudbrain(old *Cloudbrain, new *Cloudbrain) (err error) {


+ 71
- 29
models/dataset.go View File

@@ -22,6 +22,7 @@ type Dataset struct {
Category string
Description string `xorm:"TEXT"`
DownloadTimes int64
NumStars int `xorm:"INDEX NOT NULL DEFAULT 0"`
License string
Task string
ReleaseID int64 `xorm:"INDEX"`
@@ -35,6 +36,11 @@ type Dataset struct {
Attachments []*Attachment `xorm:"-"`
}

type DatasetWithStar struct {
Dataset
IsStaring bool
}

func (d *Dataset) IsPrivate() bool {
switch d.Status {
case DatasetStatusPrivate:
@@ -91,33 +97,37 @@ type SearchDatasetOptions struct {
OwnerID int64
RepoID int64
IncludePublic bool
Category string
Task string
License string
ListOptions
SearchOrderBy
IsOwner bool
}

func CreateDataset(dataset *Dataset) (err error) {
if _, err = x.Insert(dataset); err != nil {

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

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

return nil
}

func CreateDefaultDatasetToRepo(repo *Repository) (err error) {
dataset := &Dataset{RepoID: repo.ID}
has, err := x.Get(dataset)
datasetByRepoId := &Dataset{RepoID: dataset.RepoID}
has, err := sess.Get(datasetByRepoId)
if err != nil {
return err
}
if !has {
dataset.Status = DatasetStatusPrivate
dataset.Title = repo.Name
if err = CreateDataset(dataset); err != nil {
return err
}
if has {
return fmt.Errorf("The dataset already exists.")
}
return nil

if _, err = sess.Insert(dataset); err != nil {
return err
}
return sess.Commit()

}

func SearchDataset(opts *SearchDatasetOptions) (DatasetList, int64, error) {
@@ -130,7 +140,18 @@ func SearchDatasetCondition(opts *SearchDatasetOptions) builder.Cond {
cond = cond.And(builder.Neq{"dataset.status": DatasetStatusDeleted})

if len(opts.Keyword) > 0 {
cond = cond.And(builder.Like{"dataset.title", opts.Keyword})
cond = cond.And(builder.Or(builder.Like{"dataset.title", opts.Keyword}, builder.Like{"dataset.description", opts.Keyword}))
}

if len(opts.Category) > 0 {
cond = cond.And(builder.Eq{"dataset.category": opts.Category})
}

if len(opts.Task) > 0 {
cond = cond.And(builder.Eq{"dataset.task": opts.Task})
}
if len(opts.License) > 0 {
cond = cond.And(builder.Eq{"dataset.license": opts.License})
}

if opts.RepoID > 0 {
@@ -139,12 +160,13 @@ func SearchDatasetCondition(opts *SearchDatasetOptions) builder.Cond {

if opts.IncludePublic {
cond = cond.And(builder.Eq{"dataset.status": DatasetStatusPublic})
cond = cond.And(builder.Eq{"attachment.is_private": false})
if opts.OwnerID > 0 {
if len(opts.Keyword) == 0 {
cond = cond.Or(builder.Eq{"repository.owner_id": opts.OwnerID})
} else {
subCon := builder.NewCond()
subCon = subCon.And(builder.Eq{"repository.owner_id": opts.OwnerID}, builder.Like{"dataset.title", opts.Keyword})
subCon = subCon.And(builder.Eq{"repository.owner_id": opts.OwnerID}, builder.Or(builder.Like{"dataset.title", opts.Keyword}, builder.Like{"dataset.description", opts.Keyword}))
cond = cond.Or(subCon)

}
@@ -153,6 +175,7 @@ func SearchDatasetCondition(opts *SearchDatasetOptions) builder.Cond {
cond = cond.And(builder.Eq{"repository.owner_id": opts.OwnerID})
if !opts.IsOwner {
cond = cond.And(builder.Eq{"dataset.status": DatasetStatusPublic})
cond = cond.And(builder.Eq{"attachment.is_private": false})
}
}

@@ -169,14 +192,20 @@ func SearchDatasetByCondition(opts *SearchDatasetOptions, cond builder.Cond) (Da
defer sess.Close()

datasets := make(DatasetList, 0, opts.PageSize)
selectColumnsSql := "distinct dataset.id,dataset.title, dataset.status, dataset.category, dataset.description, dataset.download_times, dataset.license, dataset.task, dataset.release_id, dataset.user_id, dataset.repo_id, dataset.created_unix,dataset.updated_unix,dataset.num_stars"

count, err := sess.Join("INNER", "repository", "repository.id = dataset.repo_id").Where(cond).Count(new(Dataset))
count, err := sess.Distinct("dataset.id").Join("INNER", "repository", "repository.id = dataset.repo_id").
Join("INNER", "attachment", "attachment.dataset_id=dataset.id").
Where(cond).Count(new(Dataset))

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

sess.Select("dataset.*").Join("INNER", "repository", "repository.id = dataset.repo_id").Where(cond).OrderBy(opts.SearchOrderBy.String())
sess.Select(selectColumnsSql).Join("INNER", "repository", "repository.id = dataset.repo_id").
Join("INNER", "attachment", "attachment.dataset_id=dataset.id").
Where(cond).OrderBy(opts.SearchOrderBy.String())

if opts.PageSize > 0 {
sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
}
@@ -231,13 +260,23 @@ func getDatasetAttachments(e Engine, typeCloudBrain int, isSigned bool, user *Us
sort.Sort(sortedRels)

// Select attachments
err = e.
Asc("dataset_id").
In("dataset_id", sortedRels.ID).
And("type = ?", typeCloudBrain).
Find(&attachments, Attachment{})
if err != nil {
return err
if typeCloudBrain == -1 {
err = e.
Asc("dataset_id").
In("dataset_id", sortedRels.ID).
Find(&attachments, Attachment{})
if err != nil {
return err
}
} else {
err = e.
Asc("dataset_id").
In("dataset_id", sortedRels.ID).
And("type = ?", typeCloudBrain).
Find(&attachments, Attachment{})
if err != nil {
return err
}
}

// merge join
@@ -301,9 +340,6 @@ func GetDatasetByID(id int64) (*Dataset, error) {
}

func GetDatasetByRepo(repo *Repository) (*Dataset, error) {
if err := CreateDefaultDatasetToRepo(repo); err != nil {
return nil, err
}
dataset := &Dataset{RepoID: repo.ID}
has, err := x.Get(dataset)
if err != nil {
@@ -312,10 +348,16 @@ func GetDatasetByRepo(repo *Repository) (*Dataset, error) {
if has {
return dataset, nil
} else {
return nil, errors.New("Not Found")
return nil, ErrNotExist{repo.ID}
}
}

func GetDatasetStarByUser(user *User) ([]*DatasetStar, error) {
datasetStars := make([]*DatasetStar, 0)
err := x.Cols("id", "uid", "dataset_id", "created_unix").Where("uid=?", user.ID).Find(&datasetStars)
return datasetStars, err
}

func DeleteDataset(datasetID int64, uid int64) error {
var err error
sess := x.NewSession()


+ 70
- 0
models/dataset_star.go View File

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

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

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

// StarRepo or unstar repository.
func StarDataset(userID, datasetID int64, star bool) error {
sess := x.NewSession()
defer sess.Close()

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

if star {
if isDatasetStaring(sess, userID, datasetID) {
return nil
}

if _, err := sess.Insert(&DatasetStar{UID: userID, DatasetID: datasetID}); err != nil {
return err
}
if _, err := sess.Exec("UPDATE `dataset` SET num_stars = num_stars + 1 WHERE id = ?", datasetID); err != nil {
return err
}
if _, err := sess.Exec("UPDATE `user` SET num_dataset_stars = num_dataset_stars + 1 WHERE id = ?", userID); err != nil {
return err
}
} else {
if !isDatasetStaring(sess, userID, datasetID) {
return nil
}

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

return sess.Commit()
}

func IsDatasetStaringByRepoId(userID, repoID int64) bool {
dataset, _ := GetDatasetByRepo(&Repository{ID: repoID})
if dataset == nil {
return false
}
return isDatasetStaring(x, userID, dataset.ID)
}

func IsDatasetStaring(userID, datasetID int64) bool {
return isDatasetStaring(x, userID, datasetID)

}

func isDatasetStaring(e Engine, userID, datasetID int64) bool {
has, _ := e.Get(&DatasetStar{0, userID, datasetID, 0})
return has
}

+ 186
- 0
models/dbsql/dataset_foreigntable_for_es.sql View File

@@ -0,0 +1,186 @@
DROP FOREIGN TABLE public.dataset_es;
CREATE FOREIGN TABLE public.dataset_es
(
id bigint NOT NULL,
title character varying(255),
status integer,
category character varying(255),
description text,
download_times bigint,
license character varying(255),
task character varying(255),
release_id bigint,
user_id bigint,
repo_id bigint,
created_unix bigint,
updated_unix bigint,
file_name text,
file_desc text
)SERVER multicorn_es
OPTIONS
(
host '192.168.207.94',
port '9200',
index 'dataset-es-index',
rowid_column 'id',
default_sort '_id'
)
;
DELETE FROM public.dataset_es;
INSERT INTO public.dataset_es(
id,
title,
status,
category,
description,
download_times,
license, task,
release_id,
user_id,
repo_id,
created_unix,
updated_unix,
file_name,
file_desc
)
SELECT
b.id,
b.title,
b.status,
b.category,
b.description,
b.download_times,
b.license,
b.task,
b.release_id,
b.user_id,
b.repo_id,
b.created_unix,
b.updated_unix,
(select array_to_string(array_agg(name order by created_unix desc),'-#,#-') from public.attachment a where a.dataset_id=b.id and a.is_private=false),
(select array_to_string(array_agg(description order by created_unix desc),'-#,#-') from public.attachment a where a.dataset_id=b.id and a.is_private=false)
FROM public.dataset b,public.repository c where b.repo_id=c.id and c.is_private=false;


DROP TRIGGER IF EXISTS es_insert_dataset on public.dataset;

CREATE OR REPLACE FUNCTION public.insert_dataset_data() RETURNS trigger AS
$def$
DECLARE
privateValue boolean=false;
BEGIN
select into privateValue is_private from public.repository where id=NEW.repo_id;
if not privateValue then
INSERT INTO public.dataset_es(
id,
title,
status,
category,
description,
download_times,
license,
task,
release_id,
user_id,
repo_id,
created_unix,
updated_unix)
VALUES (
NEW.id,
NEW.title,
NEW.status,
NEW.category,
NEW.description,
NEW.download_times,
NEW.license,
NEW.task,
NEW.release_id,
NEW.user_id,
NEW.repo_id,
NEW.created_unix,
NEW.updated_unix
);
end if;
RETURN NEW;
END;
$def$
LANGUAGE plpgsql;



CREATE TRIGGER es_insert_dataset
AFTER INSERT ON public.dataset
FOR EACH ROW EXECUTE PROCEDURE insert_dataset_data();

ALTER TABLE public.dataset ENABLE ALWAYS TRIGGER es_insert_dataset;


DROP TRIGGER IF EXISTS es_udpate_dataset_file_name on public.attachment;

CREATE OR REPLACE FUNCTION public.udpate_dataset_file_name() RETURNS trigger AS
$def$
BEGIN
if (TG_OP = 'UPDATE') then
update public.dataset_es SET file_desc=(select array_to_string(array_agg(description order by created_unix desc),'-#,#-') from public.attachment where dataset_id=NEW.dataset_id and is_private=false) where id=NEW.dataset_id;
elsif (TG_OP = 'INSERT') then
update public.dataset_es SET file_name=(select array_to_string(array_agg(name order by created_unix desc),'-#,#-') from public.attachment where dataset_id=NEW.dataset_id and is_private=false) where id=NEW.dataset_id;
elsif (TG_OP = 'DELETE') then
update public.dataset_es SET file_name=(select array_to_string(array_agg(name order by created_unix desc),'-#,#-') from public.attachment where dataset_id=OLD.dataset_id and is_private=false) where id=OLD.dataset_id;
update public.dataset_es SET file_desc=(select array_to_string(array_agg(description order by created_unix desc),'-#,#-') from public.attachment where dataset_id=OLD.dataset_id and is_private=false) where id=OLD.dataset_id;
end if;
return NEW;
END;
$def$
LANGUAGE plpgsql;


CREATE TRIGGER es_udpate_dataset_file_name
AFTER INSERT OR UPDATE OR DELETE ON public.attachment
FOR EACH ROW EXECUTE PROCEDURE udpate_dataset_file_name();

ALTER TABLE public.attachment ENABLE ALWAYS TRIGGER es_udpate_dataset_file_name;

DROP TRIGGER IF EXISTS es_update_dataset on public.dataset;

CREATE OR REPLACE FUNCTION public.update_dataset() RETURNS trigger AS
$def$
BEGIN
UPDATE public.dataset_es
SET description=NEW.description,
title=NEW.title,
category=NEW.category,
task=NEW.task,
download_times=NEW.download_times,
updated_unix=NEW.updated_unix,
file_name=(select array_to_string(array_agg(name order by created_unix desc),'-#,#-') from public.attachment where dataset_id=NEW.id and is_private=false),
file_desc=(select array_to_string(array_agg(description order by created_unix desc),'-#,#-') from public.attachment where dataset_id=NEW.id and is_private=false)
where id=NEW.id;
return new;
END
$def$
LANGUAGE plpgsql;

CREATE TRIGGER es_update_dataset
AFTER UPDATE ON public.dataset
FOR EACH ROW EXECUTE PROCEDURE update_dataset();

ALTER TABLE public.dataset ENABLE ALWAYS TRIGGER es_update_dataset;

DROP TRIGGER IF EXISTS es_delete_dataset on public.dataset;

CREATE OR REPLACE FUNCTION public.delete_dataset() RETURNS trigger AS
$def$
declare
BEGIN
DELETE FROM public.dataset_es where id=OLD.id;
return new;
END
$def$
LANGUAGE plpgsql;


CREATE TRIGGER es_delete_dataset
AFTER DELETE ON public.dataset
FOR EACH ROW EXECUTE PROCEDURE delete_dataset();

ALTER TABLE public.dataset ENABLE ALWAYS TRIGGER es_delete_dataset;

+ 215
- 0
models/dbsql/issue_foreigntable_for_es.sql View File

@@ -0,0 +1,215 @@
DROP FOREIGN TABLE public.issue_es;
CREATE FOREIGN TABLE public.issue_es
(
id bigint NOT NULL,
repo_id bigint,
index bigint,
poster_id bigint,
original_author character varying(255),
original_author_id bigint,
name character varying(255) ,
content text,
comment text,
milestone_id bigint,
priority integer,
is_closed boolean,
is_pull boolean,
pr_id bigint,
num_comments integer,
ref character varying(255),
deadline_unix bigint,
created_unix bigint,
updated_unix bigint,
closed_unix bigint,
is_locked boolean NOT NULL,
amount bigint,
is_transformed boolean NOT NULL
)SERVER multicorn_es
OPTIONS
(
host '192.168.207.94',
port '9200',
index 'issue-es-index',
rowid_column 'id',
default_sort '_id'
)
;

delete from public.issue_es;
INSERT INTO public.issue_es(
id,
repo_id,
index,
poster_id,
original_author,
original_author_id,
name,
content,
milestone_id,
priority,
is_closed,
is_pull,
num_comments,
ref,
deadline_unix,
created_unix,
updated_unix,
closed_unix,
is_locked,
amount,
is_transformed,comment,pr_id)
SELECT
b.id,
b.repo_id,
b.index,
b.poster_id,
b.original_author,
b.original_author_id,
b.name,
b.content,
b.milestone_id,
b.priority,
b.is_closed,
b.is_pull,
b.num_comments,
b.ref,
b.deadline_unix,
b.created_unix,
b.updated_unix,
b.closed_unix,
b.is_locked,
b.amount,
b.is_transformed,
(select array_to_string(array_agg(content order by created_unix desc),',') from public.comment a where a.issue_id=b.id),
(select id from public.pull_request d where b.id=d.issue_id and b.is_pull=true)
FROM public.issue b,public.repository c where b.repo_id=c.id and c.is_private=false;


CREATE OR REPLACE FUNCTION public.insert_issue_data() RETURNS trigger AS
$def$
DECLARE
privateValue boolean=false;
BEGIN
select into privateValue is_private from public.repository where id=NEW.repo_id;
if not privateValue then
INSERT INTO public.issue_es(
id,
repo_id,
index,
poster_id,
original_author,
original_author_id,
name,
content,
milestone_id,
priority,
is_closed,
is_pull,
num_comments,
ref,
deadline_unix,
created_unix,
updated_unix,
closed_unix,
is_locked,
amount,
is_transformed)
VALUES (
NEW.id,
NEW.repo_id,
NEW.index,
NEW.poster_id,
NEW.original_author,
NEW.original_author_id,
NEW.name,
NEW.content,
NEW.milestone_id,
NEW.priority,
NEW.is_closed,
NEW.is_pull,
NEW.num_comments,
NEW.ref,
NEW.deadline_unix,
NEW.created_unix,
NEW.updated_unix,
NEW.closed_unix,
NEW.is_locked,
NEW.amount,
NEW.is_transformed
);
end if;
RETURN NEW;
END;
$def$
LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS es_insert_issue on public.issue;

CREATE TRIGGER es_insert_issue
AFTER INSERT ON public.issue
FOR EACH ROW EXECUTE PROCEDURE insert_issue_data();

ALTER TABLE public.issue ENABLE ALWAYS TRIGGER es_insert_issue;

CREATE OR REPLACE FUNCTION public.udpate_issue_comment() RETURNS trigger AS
$def$
BEGIN
if (TG_OP = 'DELETE') then
update public.issue_es SET comment=(select array_to_string(array_agg(content order by created_unix desc),',') from public.comment where issue_id=OLD.issue_id) where id=OLD.issue_id;
elsif (TG_OP = 'UPDATE') then
update public.issue_es SET comment=(select array_to_string(array_agg(content order by created_unix desc),',') from public.comment where issue_id=NEW.issue_id) where id=NEW.issue_id;
end if;
return null;
END;
$def$
LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS es_udpate_issue_comment on public.comment;
CREATE TRIGGER es_udpate_issue_comment
AFTER DELETE OR UPDATE ON public.comment
FOR EACH ROW EXECUTE PROCEDURE udpate_issue_comment();

ALTER TABLE public.comment ENABLE ALWAYS TRIGGER es_udpate_issue_comment;


CREATE OR REPLACE FUNCTION public.update_issue() RETURNS trigger AS
$def$
declare
BEGIN
UPDATE public.issue_es
SET content=NEW.content,
name=NEW.name,
is_closed=NEW.is_closed,
num_comments=NEW.num_comments,
comment=(select array_to_string(array_agg(content order by created_unix desc),',') from public.comment where issue_id=NEW.id)
where id=NEW.id;
return new;
END
$def$
LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS es_update_issue on public.issue;

CREATE TRIGGER es_update_issue
AFTER UPDATE ON public.issue
FOR EACH ROW EXECUTE PROCEDURE update_issue();

ALTER TABLE public.issue ENABLE ALWAYS TRIGGER es_update_issue;

CREATE OR REPLACE FUNCTION public.delete_issue() RETURNS trigger AS
$def$
declare
BEGIN
DELETE FROM public.issue_es where id=OLD.id;
return new;
END
$def$
LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS es_delete_issue on public.issue;
CREATE TRIGGER es_delete_issue
AFTER DELETE ON public.issue
FOR EACH ROW EXECUTE PROCEDURE delete_issue();

ALTER TABLE public.issue ENABLE ALWAYS TRIGGER es_delete_issue;

+ 532
- 0
models/dbsql/repo_foreigntable_for_es.sql View File

@@ -0,0 +1,532 @@
-- 要处理项目从私有变为公有,并且从公有变成私有的情况
DROP FOREIGN table if exists public.repository_es;
CREATE FOREIGN TABLE public.repository_es (
id bigint NOT NULL,
owner_id bigint,
owner_name character varying(255),
lower_name character varying(255) NOT NULL,
name character varying(255) NOT NULL,
description text,
website character varying(2048),
original_service_type integer,
original_url character varying(2048),
default_branch character varying(255),
num_watches integer,
num_stars integer,
num_forks integer,
num_issues integer,
num_closed_issues integer,
num_pulls integer,
num_closed_pulls integer,
num_milestones integer DEFAULT 0 NOT NULL,
num_closed_milestones integer DEFAULT 0 NOT NULL,
is_private boolean,
is_empty boolean,
is_archived boolean,
is_mirror boolean,
status integer DEFAULT 0 NOT NULL,
is_fork boolean DEFAULT false NOT NULL,
fork_id bigint,
is_template boolean DEFAULT false NOT NULL,
template_id bigint,
size bigint DEFAULT 0 NOT NULL,
is_fsck_enabled boolean DEFAULT true NOT NULL,
close_issues_via_commit_in_any_branch boolean DEFAULT false NOT NULL,
topics text,
avatar character varying(64),
created_unix bigint,
updated_unix bigint,
contract_address character varying(255),
block_chain_status integer DEFAULT 0 NOT NULL,
balance character varying(255) DEFAULT '0'::character varying NOT NULL,
clone_cnt bigint DEFAULT 0 NOT NULL,
license character varying(100),
download_cnt bigint DEFAULT 0 NOT NULL,
num_commit bigint DEFAULT 0 NOT NULL,
git_clone_cnt bigint DEFAULT 0 NOT NULL,
creator_id bigint NOT NULL DEFAULT 0,
repo_type integer NOT NULL DEFAULT 0,
lang character varying(2048),
alias character varying(255),
lower_alias character varying(255)
) SERVER multicorn_es
OPTIONS
(
host '192.168.207.94',
port '9200',
index 'repository-es-index',
rowid_column 'id',
default_sort '_id'
)
;
delete from public.repository_es;
INSERT INTO public.repository_es (id,
owner_id,
owner_name,
lower_name,
name,
description,
website,
original_service_type,
original_url,
default_branch,
num_watches,
num_stars,
num_forks,
num_issues,
num_closed_issues,
num_pulls,
num_closed_pulls,
num_milestones,
num_closed_milestones,
is_private,
is_empty,
is_archived,
is_mirror,
status,
is_fork,
fork_id,
is_template,
template_id,
size,
is_fsck_enabled,
close_issues_via_commit_in_any_branch,
topics,
avatar,
created_unix,
updated_unix,
contract_address,
block_chain_status,
balance,
clone_cnt,
num_commit,
git_clone_cnt,
creator_id,
repo_type,
lang,
alias,
lower_alias
)
SELECT
id,
owner_id,
owner_name,
lower_name,
name,
description,
website,
original_service_type,
original_url,
default_branch,
num_watches,
num_stars,
num_forks,
num_issues,
num_closed_issues,
num_pulls,
num_closed_pulls,
num_milestones,
num_closed_milestones,
is_private,
is_empty,
is_archived,
is_mirror,
status,
is_fork,
fork_id,
is_template,
template_id,
size,
is_fsck_enabled,
close_issues_via_commit_in_any_branch,
topics,
avatar,
created_unix,
updated_unix,
contract_address,
block_chain_status,
balance,
clone_cnt,
num_commit,
git_clone_cnt,
creator_id,
repo_type,
(select array_to_string(array_agg(language order by percentage desc),',') from public.language_stat a where a.repo_id=b.id),
alias,
lower_alias
FROM public.repository b where b.is_private=false;

DROP TRIGGER IF EXISTS es_insert_repository on public.repository;

CREATE OR REPLACE FUNCTION public.insert_repository_data() RETURNS trigger AS
$def$
BEGIN
if not NEW.is_private then
INSERT INTO public.repository_es (id,
owner_id,
owner_name,
lower_name,
name,
description,
website,
original_service_type,
original_url,
default_branch,
num_watches,
num_stars,
num_forks,
num_issues,
num_closed_issues,
num_pulls,
num_closed_pulls,
num_milestones,
num_closed_milestones,
is_private,
is_empty,
is_archived,
is_mirror,
status,
is_fork,
fork_id,
is_template,
template_id,
size,
is_fsck_enabled,
close_issues_via_commit_in_any_branch,
topics,
avatar,
created_unix,
updated_unix,
contract_address,
block_chain_status,
balance,
clone_cnt,
num_commit,
git_clone_cnt,
creator_id,
repo_type,
alias,
lower_alias) VALUES
(NEW.id,
NEW.owner_id,
NEW.owner_name,
NEW.lower_name,
NEW.name,
NEW.description,
NEW.website,
NEW.original_service_type,
NEW.original_url,
NEW.default_branch,
NEW.num_watches,
NEW.num_stars,
NEW.num_forks,
NEW.num_issues,
NEW.num_closed_issues,
NEW.num_pulls,
NEW.num_closed_pulls,
NEW.num_milestones,
NEW.num_closed_milestones,
NEW.is_private,
NEW.is_empty,
NEW.is_archived,
NEW.is_mirror,
NEW.status,
NEW.is_fork,
NEW.fork_id,
NEW.is_template,
NEW.template_id,
NEW.size,
NEW.is_fsck_enabled,
NEW.close_issues_via_commit_in_any_branch,
NEW.topics,
NEW.avatar,
NEW.created_unix,
NEW.updated_unix,
NEW.contract_address,
NEW.block_chain_status,
NEW.balance,
NEW.clone_cnt,
NEW.num_commit,
NEW.git_clone_cnt,
NEW.creator_id,
NEW.repo_type,
NEW.alias,
NEW.lower_alias);
end if;
RETURN NEW;
END;
$def$
LANGUAGE plpgsql;


CREATE TRIGGER es_insert_repository
AFTER INSERT ON public.repository
FOR EACH ROW EXECUTE PROCEDURE insert_repository_data();

ALTER TABLE public.repository ENABLE ALWAYS TRIGGER es_insert_repository;

DROP TRIGGER IF EXISTS es_update_repository on public.repository;

CREATE OR REPLACE FUNCTION public.update_repository() RETURNS trigger AS
$def$
BEGIN
if OLD.is_private != NEW.is_private then
if OLD.is_private and not NEW.is_private then
--insert
INSERT INTO public.repository_es (id,
owner_id,
owner_name,
lower_name,
name,
description,
website,
original_service_type,
original_url,
default_branch,
num_watches,
num_stars,
num_forks,
num_issues,
num_closed_issues,
num_pulls,
num_closed_pulls,
num_milestones,
num_closed_milestones,
is_private,
is_empty,
is_archived,
is_mirror,
status,
is_fork,
fork_id,
is_template,
template_id,
size,
is_fsck_enabled,
close_issues_via_commit_in_any_branch,
topics,
avatar,
created_unix,
updated_unix,
contract_address,
block_chain_status,
balance,
clone_cnt,
num_commit,
git_clone_cnt,
creator_id,
repo_type,
lang,
alias,
lower_alias)
SELECT
id,
owner_id,
owner_name,
lower_name,
name,
description,
website,
original_service_type,
original_url,
default_branch,
num_watches,
num_stars,
num_forks,
num_issues,
num_closed_issues,
num_pulls,
num_closed_pulls,
num_milestones,
num_closed_milestones,
is_private,
is_empty,
is_archived,
is_mirror,
status,
is_fork,
fork_id,
is_template,
template_id,
size,
is_fsck_enabled,
close_issues_via_commit_in_any_branch,
topics,
avatar,
created_unix,
updated_unix,
contract_address,
block_chain_status,
balance,
clone_cnt,
num_commit,
git_clone_cnt,
creator_id,
repo_type,
(select array_to_string(array_agg(language order by percentage desc),',') from public.language_stat a where a.repo_id=b.id),
alias,
lower_alias
FROM public.repository b where b.id=NEW.id;
INSERT INTO public.dataset_es(
id,
title,
status,
category,
description,
download_times,
license, task,
release_id,
user_id,
repo_id,
created_unix,
updated_unix,file_name)
SELECT
b.id,
b.title,
b.status,
b.category,
b.description,
b.download_times,
b.license,
b.task,
b.release_id,
b.user_id,
b.repo_id,
b.created_unix,
b.updated_unix,(select array_to_string(array_agg(name order by created_unix desc),',') from public.attachment a where a.dataset_id=b.id and a.is_private=false)
FROM public.dataset b where b.repo_id=NEW.id;

INSERT INTO public.issue_es(
id,
repo_id,
index,
poster_id,
original_author,
original_author_id,
name,
content,
milestone_id,
priority,
is_closed,
is_pull,
num_comments,
ref,
deadline_unix,
created_unix,
updated_unix,
closed_unix,
is_locked,
amount,
is_transformed,comment,pr_id)
SELECT
b.id,
b.repo_id,
b.index,
b.poster_id,
b.original_author,
b.original_author_id,
b.name,
b.content,
b.milestone_id,
b.priority,
b.is_closed,
b.is_pull,
b.num_comments,
b.ref,
b.deadline_unix,
b.created_unix,
b.updated_unix,
b.closed_unix,
b.is_locked,
b.amount,
b.is_transformed,
(select array_to_string(array_agg(content order by created_unix desc),',') from public.comment a where a.issue_id=b.id),
(select id from public.pull_request d where d.issue_id=b.id)
FROM public.issue b where b.repo_id=NEW.id;
end if;

if not OLD.is_private and NEW.is_private then
delete from public.issue_es where repo_id=NEW.id;
delete from public.dataset_es where repo_id=NEW.id;
delete from public.repository_es where id=NEW.id;
end if;

end if;

if not NEW.is_private then
raise notice 'update repo,the updated_unix is %',NEW.updated_unix;
update public.repository_es SET description=NEW.description,
name=NEW.name,
lower_name=NEW.lower_name,
owner_name=NEW.owner_name,
website=NEW.website,
updated_unix=NEW.updated_unix,
num_watches=NEW.num_watches,
num_stars=NEW.num_stars,
num_forks=NEW.num_forks,
topics=NEW.topics,
alias = NEW.alias,
lower_alias = NEW.lower_alias,
avatar=NEW.avatar
where id=NEW.id;
end if;
return new;
END
$def$
LANGUAGE plpgsql;

CREATE TRIGGER es_update_repository
AFTER UPDATE ON public.repository
FOR EACH ROW EXECUTE PROCEDURE update_repository();

ALTER TABLE public.repository ENABLE ALWAYS TRIGGER es_update_repository;


DROP TRIGGER IF EXISTS es_delete_repository on public.repository;

CREATE OR REPLACE FUNCTION public.delete_repository() RETURNS trigger AS
$def$
declare
BEGIN
delete from public.issue_es where repo_id=OLD.id;
delete from public.dataset_es where repo_id=OLD.id;
DELETE FROM public.repository_es where id=OLD.id;
return new;
END
$def$
LANGUAGE plpgsql;


CREATE TRIGGER es_delete_repository
AFTER DELETE ON public.repository
FOR EACH ROW EXECUTE PROCEDURE delete_repository();

ALTER TABLE public.repository ENABLE ALWAYS TRIGGER es_delete_repository;



DROP TRIGGER IF EXISTS es_udpate_repository_lang on public.language_stat;

CREATE OR REPLACE FUNCTION public.udpate_repository_lang() RETURNS trigger AS
$def$
BEGIN
if (TG_OP = 'UPDATE') then
update public.repository_es SET lang=(select array_to_string(array_agg(language order by percentage desc),',') from public.language_stat where repo_id=NEW.repo_id) where id=NEW.repo_id;
elsif (TG_OP = 'INSERT') then
update public.repository_es SET lang=(select array_to_string(array_agg(language order by percentage desc),',') from public.language_stat where repo_id=NEW.repo_id) where id=NEW.repo_id;
elsif (TG_OP = 'DELETE') then
if exists(select 1 from public.repository where id=OLD.repo_id) then
update public.repository_es SET lang=(select array_to_string(array_agg(language order by percentage desc),',') from public.language_stat where repo_id=OLD.repo_id) where id=OLD.repo_id;
end if;
end if;
return null;
END;
$def$
LANGUAGE plpgsql;

CREATE TRIGGER es_udpate_repository_lang
AFTER INSERT OR UPDATE OR DELETE ON public.language_stat
FOR EACH ROW EXECUTE PROCEDURE udpate_repository_lang();

ALTER TABLE public.language_stat ENABLE ALWAYS TRIGGER es_udpate_repository_lang;

+ 308
- 0
models/dbsql/user_foreigntable_for_es.sql View File

@@ -0,0 +1,308 @@
DROP FOREIGN table if exists public.user_es;
CREATE FOREIGN TABLE public.user_es
(
id bigint NOT NULL ,
lower_name character varying(255) NULL,
name character varying(255) NULL,
full_name character varying(255),
email character varying(255),
keep_email_private boolean,
email_notifications_preference character varying(20) ,
passwd character varying(255) ,
passwd_hash_algo character varying(255) ,
must_change_password boolean NOT NULL DEFAULT false,
login_type integer,
login_source bigint NOT NULL DEFAULT 0,
login_name character varying(255) ,
type integer,
location character varying(255),
website character varying(255),
rands character varying(10),
salt character varying(10),
language character varying(5),
description character varying(255),
created_unix bigint,
updated_unix bigint,
last_login_unix bigint,
last_repo_visibility boolean,
max_repo_creation integer,
is_active boolean,
is_admin boolean,
is_restricted boolean NOT NULL DEFAULT false,
allow_git_hook boolean,
allow_import_local boolean,
allow_create_organization boolean DEFAULT true,
prohibit_login boolean NOT NULL DEFAULT false,
avatar character varying(2048) ,
avatar_email character varying(255),
use_custom_avatar boolean,
num_followers integer,
num_following integer NOT NULL DEFAULT 0,
num_stars integer,
num_repos integer,
num_teams integer,
num_members integer,
visibility integer NOT NULL DEFAULT 0,
repo_admin_change_team_access boolean NOT NULL DEFAULT false,
diff_view_style character varying(255),
theme character varying(255),
token character varying(1024) ,
public_key character varying(255),
private_key character varying(255),
is_operator boolean NOT NULL DEFAULT false,
num_dataset_stars integer NOT NULL DEFAULT 0
) SERVER multicorn_es
OPTIONS
(
host '192.168.207.94',
port '9200',
index 'user-es-index',
rowid_column 'id',
default_sort '_id'
)
;
delete from public.user_es;
INSERT INTO public.user_es(
id,
lower_name,
name,
full_name,
email,
keep_email_private,
email_notifications_preference,
must_change_password,
login_type,
login_source,
login_name,
type,
location,
website,
rands,
language,
description,
created_unix,
updated_unix,
last_login_unix,
last_repo_visibility,
max_repo_creation,
is_active,
is_restricted,
allow_git_hook,
allow_import_local,
allow_create_organization,
prohibit_login,
avatar,
avatar_email,
use_custom_avatar,
num_followers,
num_following,
num_stars,
num_repos,
num_teams,
num_members,
visibility,
repo_admin_change_team_access,
diff_view_style,
theme,
is_operator,
num_dataset_stars)
SELECT
id,
lower_name,
name,
full_name,
email,
keep_email_private,
email_notifications_preference,
must_change_password,
login_type,
login_source,
login_name,
type,
location,
website,
rands,
language,
description,
created_unix,
updated_unix,
last_login_unix,
last_repo_visibility,
max_repo_creation,
is_active,
is_restricted,
allow_git_hook,
allow_import_local,
allow_create_organization,
prohibit_login,
avatar,
avatar_email,
use_custom_avatar,
num_followers,
num_following,
num_stars,
num_repos,
num_teams,
num_members,
visibility,
repo_admin_change_team_access,
diff_view_style,
theme,
is_operator,
num_dataset_stars
FROM public.user;

DROP TRIGGER IF EXISTS es_insert_user on public.user;

CREATE OR REPLACE FUNCTION public.insert_user_data() RETURNS trigger AS
$def$
BEGIN
INSERT INTO public."user_es"(
id,
lower_name,
name,
full_name,
email,
keep_email_private,
email_notifications_preference,
must_change_password,
login_type,
login_source,
login_name,
type,
location,
website,
rands,
language,
description,
created_unix,
updated_unix,
last_login_unix,
last_repo_visibility,
max_repo_creation,
is_active,
is_restricted,
allow_git_hook,
allow_import_local,
allow_create_organization,
prohibit_login,
avatar,
avatar_email,
use_custom_avatar,
num_followers,
num_following,
num_stars,
num_repos,
num_teams,
num_members,
visibility,
repo_admin_change_team_access,
diff_view_style,
theme,
is_operator,
num_dataset_stars)
VALUES (
NEW.id,
NEW.lower_name,
NEW.name,
NEW.full_name,
NEW.email,
NEW.keep_email_private,
NEW.email_notifications_preference,
NEW.must_change_password,
NEW.login_type,
NEW.login_source,
NEW.login_name,
NEW.type,
NEW.location,
NEW.website,
NEW.rands,
NEW.language,
NEW.description,
NEW.created_unix,
NEW.updated_unix,
NEW.last_login_unix,
NEW.last_repo_visibility,
NEW.max_repo_creation,
NEW.is_active,
NEW.is_restricted,
NEW.allow_git_hook,
NEW.allow_import_local,
NEW.allow_create_organization,
NEW.prohibit_login,
NEW.avatar,
NEW.avatar_email,
NEW.use_custom_avatar,
NEW.num_followers,
NEW.num_following,
NEW.num_stars,
NEW.num_repos,
NEW.num_teams,
NEW.num_members,
NEW.visibility,
NEW.repo_admin_change_team_access,
NEW.diff_view_style,
NEW.theme,
NEW.is_operator,
NEW.num_dataset_stars
);

RETURN NEW;
END;
$def$
LANGUAGE plpgsql;



CREATE TRIGGER es_insert_user
AFTER INSERT ON public.user
FOR EACH ROW EXECUTE PROCEDURE insert_user_data();

ALTER TABLE public.user ENABLE ALWAYS TRIGGER es_insert_user;

DROP TRIGGER IF EXISTS es_update_user on public.user;

CREATE OR REPLACE FUNCTION public.update_user() RETURNS trigger AS
$def$
BEGIN
UPDATE public.user_es
SET description=NEW.description,
name=NEW.name,
full_name=NEW.full_name,
location=NEW.location,
website=NEW.website,
email=NEW.email,
num_dataset_stars=NEW.num_dataset_stars,
updated_unix=NEW.updated_unix
where id=NEW.id;
return new;
END
$def$
LANGUAGE plpgsql;



CREATE TRIGGER es_update_user
AFTER UPDATE ON public.user
FOR EACH ROW EXECUTE PROCEDURE update_user();

ALTER TABLE public.user ENABLE ALWAYS TRIGGER es_update_user;

DROP TRIGGER IF EXISTS es_delete_user on public.user;

CREATE OR REPLACE FUNCTION public.delete_user() RETURNS trigger AS
$def$
declare
BEGIN
DELETE FROM public.user_es where id=OLD.id;
return new;
END
$def$
LANGUAGE plpgsql;


CREATE TRIGGER es_delete_user
AFTER DELETE ON public.user
FOR EACH ROW EXECUTE PROCEDURE delete_user();
ALTER TABLE public.user ENABLE ALWAYS TRIGGER es_delete_user;

+ 0
- 5
models/file_chunk.go View File

@@ -13,11 +13,6 @@ const (
FileUploaded
)

const (
TypeCloudBrainOne int = iota
TypeCloudBrainTwo
)

type FileChunk struct {
ID int64 `xorm:"pk autoincr"`
UUID string `xorm:"uuid UNIQUE"`


+ 2
- 0
models/models.go View File

@@ -129,6 +129,7 @@ func init() {
new(LanguageStat),
new(EmailHash),
new(Dataset),
new(DatasetStar),
new(Cloudbrain),
new(FileChunk),
new(BlockChain),
@@ -137,6 +138,7 @@ func init() {
new(OfficialTag),
new(OfficialTagRepos),
new(WechatBindLog),
new(SearchRecord),
)

tablesStatistic = append(tablesStatistic,


+ 38
- 7
models/repo.go View File

@@ -6,13 +6,14 @@
package models

import (
"code.gitea.io/gitea/modules/git"
"context"
"crypto/md5"
"errors"
"fmt"
"html/template"
"math/rand"

"code.gitea.io/gitea/modules/git"
"xorm.io/xorm"

"code.gitea.io/gitea/modules/blockchain"
@@ -1280,10 +1281,6 @@ func CreateRepository(ctx DBContext, doer, u *User, repo *Repository, opts ...Cr
return fmt.Errorf("copyDefaultWebhooksToRepo: %v", err)
}

if err = CreateDefaultDatasetToRepo(repo); err != nil {
return fmt.Errorf("models.CreateDefaultDatasetToRepo: %v", err)
}

return nil
}

@@ -1601,6 +1598,36 @@ func updateRepository(e Engine, repo *Repository, visibilityChanged bool) (err e
if err != nil {
return err
}
//If repo has become private, we need set dataset and dataset_file to private
_, err = e.Where("repo_id = ? and status <> 2", repo.ID).Cols("status").Update(&Dataset{
Status: 0,
})
if err != nil {
return err
}

dataset, err := GetDatasetByRepo(repo)
if err != nil && !IsErrNotExist(err) {
return err
}
if dataset != nil {
_, err = e.Where("dataset_id = ?", dataset.ID).Cols("is_private").Update(&Attachment{
IsPrivate: true,
})
if err != nil {
return err
}
}

} else {
//If repo has become public, we need set dataset to public
_, err = e.Where("repo_id = ? and status <> 2", repo.ID).Cols("status").Update(&Dataset{
Status: 1,
})
if err != nil {
return err
}

}

// Create/Remove git-daemon-export-ok for git-daemon...
@@ -2691,7 +2718,7 @@ func ReadLatestFileInRepo(userName, repoName, refName, treePath string) (*RepoFi
log.Error("ReadLatestFileInRepo error when OpenRepository,error=%v", err)
return nil, err
}
commitID, err := gitRepo.GetBranchCommitID(refName)
_, err = gitRepo.GetBranchCommitID(refName)
if err != nil {
log.Error("ReadLatestFileInRepo error when GetBranchCommitID,error=%v", err)
return nil, err
@@ -2723,5 +2750,9 @@ func ReadLatestFileInRepo(userName, repoName, refName, treePath string) (*RepoFi
if n >= 0 {
buf = buf[:n]
}
return &RepoFile{CommitId: commitID, Content: buf}, nil
commitId := ""
if blob != nil {
commitId = fmt.Sprint(blob.ID)
}
return &RepoFile{CommitId: commitId, Content: buf}, nil
}

+ 7
- 3
models/repo_list.go View File

@@ -190,7 +190,8 @@ type SearchRepoOptions struct {
// None -> include all repos
// True -> include just courses
// False -> include just no courses
Course util.OptionalBool
Course util.OptionalBool
OnlySearchPrivate bool
}

//SearchOrderBy is used to sort the result
@@ -219,12 +220,15 @@ const (
SearchOrderByDownloadTimes SearchOrderBy = "download_times DESC"
SearchOrderByHot SearchOrderBy = "(num_watches + num_stars + num_forks + clone_cnt) DESC"
SearchOrderByActive SearchOrderBy = "(num_issues + num_pulls + num_commit) DESC"
SearchOrderByWatches SearchOrderBy = "num_watches DESC"
)

// SearchRepositoryCondition creates a query condition according search repository options
func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
var cond = builder.NewCond()

if opts.OnlySearchPrivate {
cond = cond.And(builder.Eq{"is_private": true})
}
if opts.Private {
if opts.Actor != nil && !opts.Actor.IsAdmin && opts.Actor.ID != opts.OwnerID {
// OK we're in the context of a User
@@ -337,7 +341,7 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
if !opts.TopicOnly {
var likes = builder.NewCond()
for _, v := range strings.Split(opts.Keyword, ",") {
likes = likes.Or(builder.Like{"lower_name", strings.ToLower(v)})
likes = likes.Or(builder.Like{"lower_alias", strings.ToLower(v)})
likes = likes.Or(builder.Like{"alias", v})
if opts.IncludeDescription {
likes = likes.Or(builder.Like{"LOWER(description)", strings.ToLower(v)})


+ 83
- 0
models/search_record.go View File

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

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

type SearchRecord struct {
ID int64 `xorm:"pk autoincr"`
//user
Keyword string `xorm:"NOT NULL"`
//
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
}

func SaveSearchKeywordToDb(keyword string) error {
record := &SearchRecord{
Keyword: keyword,
}
sess := x.NewSession()
defer sess.Close()
_, err := sess.Insert(record)
if err != nil {
log.Info("insert error." + err.Error())
return err
}
return nil
}

func setIssueQueryCondition(sess *xorm.Session, Keyword string, isPull bool, userId int64) {
sess.And("issue.poster_id=?", userId)
sess.And("issue.is_pull=?", isPull)
sess.And("(issue.name like '%" + Keyword + "%' or issue.content like '%" + Keyword + "%')")
sess.Join("INNER", "repository", "issue.repo_id = repository.id").And("repository.is_private = ?", true)
}

func SearchPrivateIssueOrPr(Page int, PageSize int, Keyword string, isPull bool, userId int64) ([]*Issue, int64, error) {
sess := x.NewSession()
defer sess.Close()
setIssueQueryCondition(sess, Keyword, isPull, userId)
count, err := sess.Count(new(Issue))
if err != nil {
return nil, 0, err
}

setIssueQueryCondition(sess, Keyword, isPull, userId)
sess.Desc("issue.created_unix")
sess.Limit(PageSize, (Page-1)*PageSize)
issues := make([]*Issue, 0)
if err := sess.Find(&issues); err != nil {
return nil, 0, err
} else {
return issues, count, nil
}
}

func setDataSetQueryCondition(sess *xorm.Session, Keyword string, userId int64) {
sess.And("dataset.user_id=?", userId)
sess.And("(dataset.title like '%" + Keyword + "%' or dataset.description like '%" + Keyword + "%')")
sess.Join("INNER", "repository", "dataset.repo_id = repository.id").And("repository.is_private = ?", true)
}

func SearchDatasetBySQL(Page int, PageSize int, Keyword string, userId int64) ([]*Dataset, int64, error) {
sess := x.NewSession()
defer sess.Close()
setDataSetQueryCondition(sess, Keyword, userId)
count, err := sess.Count(new(Dataset))
if err != nil {
return nil, 0, err
}

setDataSetQueryCondition(sess, Keyword, userId)
sess.Desc("dataset.created_unix")
sess.Limit(PageSize, (Page-1)*PageSize)
datasets := make([]*Dataset, 0)
if err := sess.Find(&datasets); err != nil {
return nil, 0, err
} else {
return datasets, count, nil
}

}

+ 5
- 4
models/user.go View File

@@ -153,10 +153,11 @@ type User struct {
UseCustomAvatar bool

// Counters
NumFollowers int
NumFollowing int `xorm:"NOT NULL DEFAULT 0"`
NumStars int
NumRepos int
NumFollowers int
NumFollowing int `xorm:"NOT NULL DEFAULT 0"`
NumStars int
NumDatasetStars int `xorm:"NOT NULL DEFAULT 0"`
NumRepos int

// For organization
NumTeams int


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

@@ -20,6 +20,9 @@ type CreateCloudBrainForm struct {
ResourceSpecId int `form:"resource_spec_id" binding:"Required"`
BenchmarkTypeID int `form:"benchmark_types_id"`
BenchmarkChildTypeID int `form:"benchmark_child_types_id"`
BootFile string `form:"boot_file"`
Params string `form:"run_para_list"`
BranchName string `form:"branch_name"`
}

type CommitImageCloudBrainForm struct {


+ 17
- 6
modules/auth/dataset.go View File

@@ -9,11 +9,10 @@ import (
type CreateDatasetForm struct {
Title string `binding:"Required"`
Category string `binding:"Required"`
Description string `binding:"Required;MaxSize(254)"`
Description string `binding:"Required"`
License string `binding:"Required;MaxSize(64)"`
Task string `binding:"Required;MaxSize(64)"`
ReleaseID int64 `xorm:"INDEX"`
Private bool
Files []string
}

@@ -25,11 +24,23 @@ type EditDatasetForm struct {
ID int64 `binding:"Required"`
Title string `binding:"Required"`
Category string `binding:"Required"`
Description string `binding:"Required;MaxSize(254)"`
Description string `binding:"Required"`
License string `binding:"Required;MaxSize(64)"`
Task string `binding:"Required;MaxSize(64)"`
Private bool
ReleaseID int64 `xorm:"INDEX"`
ReleaseID int64 `xorm:"INDEX"`
Files []string
Type string `binding:"Required"`
Type string `binding:"Required"`
}

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

type EditAttachmentForm struct {
ID int64 `binding:"Required"`
Description string
}

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

+ 49
- 14
modules/cloudbrain/cloudbrain.go View File

@@ -15,14 +15,13 @@ import (
)

const (
Command = `pip3 install jupyterlab==2.2.5 -i https://pypi.tuna.tsinghua.edu.cn/simple;
service ssh stop;
jupyter lab --no-browser --ip=0.0.0.0 --allow-root --notebook-dir="/code" --port=80 --LabApp.token="" --LabApp.allow_origin="self https://cloudbrain.pcl.ac.cn"`
Command = `pip3 install jupyterlab==2.2.5 -i https://pypi.tuna.tsinghua.edu.cn/simple;service ssh stop;jupyter lab --no-browser --ip=0.0.0.0 --allow-root --notebook-dir="/code" --port=80 --LabApp.token="" --LabApp.allow_origin="self https://cloudbrain.pcl.ac.cn"`
//CommandBenchmark = `echo "start benchmark";python /code/test.py;echo "end benchmark"`
CommandBenchmark = `echo "start benchmark";cd /benchmark && bash run_bk.sh;echo "end benchmark"`
CodeMountPath = "/code"
DataSetMountPath = "/dataset"
ModelMountPath = "/model"
LogFile = "log.txt"
BenchMarkMountPath = "/benchmark"
BenchMarkResourceID = 1
Snn4imagenetMountPath = "/snn4imagenet"
@@ -32,10 +31,13 @@ const (
SubTaskName = "task1"

Success = "S000"

DefaultBranchName = "master"
)

var (
ResourceSpecs *models.ResourceSpecs
ResourceSpecs *models.ResourceSpecs
TrainResourceSpecs *models.ResourceSpecs
)

func isAdminOrOwnerOrJobCreater(ctx *context.Context, job *models.Cloudbrain, err error) bool {
@@ -147,7 +149,7 @@ func AdminOrJobCreaterRightForTrain(ctx *context.Context) {

}

func GenerateTask(ctx *context.Context, displayJobName, jobName, image, command, uuid, codePath, modelPath, benchmarkPath, snn4imagenetPath, brainScorePath, jobType, gpuQueue, description string, benchmarkTypeID, benchmarkChildTypeID, resourceSpecId int) error {
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 +
@@ -155,13 +157,27 @@ func GenerateTask(ctx *context.Context, displayJobName, jobName, image, command,
uuid

var resourceSpec *models.ResourceSpec
if ResourceSpecs == nil {
json.Unmarshal([]byte(setting.ResourceSpecs), &ResourceSpecs)
}
for _, spec := range ResourceSpecs.ResourceSpec {
if resourceSpecId == spec.Id {
resourceSpec = spec
var versionCount int
if jobType == string(models.JobTypeTrain) {
versionCount = 1
if TrainResourceSpecs == nil {
json.Unmarshal([]byte(setting.TrainResourceSpecs), &TrainResourceSpecs)
}
for _, spec := range TrainResourceSpecs.ResourceSpec {
if resourceSpecId == spec.Id {
resourceSpec = spec
}
}
} else {
if ResourceSpecs == nil {
json.Unmarshal([]byte(setting.ResourceSpecs), &ResourceSpecs)
}
for _, spec := range ResourceSpecs.ResourceSpec {
if resourceSpecId == spec.Id {
resourceSpec = spec
}
}

}

if resourceSpec == nil {
@@ -169,6 +185,15 @@ func GenerateTask(ctx *context.Context, displayJobName, jobName, image, command,
return errors.New("no such resourceSpec")
}

var datasetName string
attach, err := models.GetAttachmentByUUID(uuid)
if err != nil {
//for benchmark, do not return error
log.Error("GetAttachmentByUUID failed:%v", err)
} else {
datasetName = attach.Name
}

jobResult, err := CreateJob(jobName, models.CreateJobParams{
JobName: jobName,
RetryCount: 1,
@@ -263,13 +288,19 @@ func GenerateTask(ctx *context.Context, displayJobName, jobName, image, command,
BenchmarkTypeID: benchmarkTypeID,
BenchmarkChildTypeID: benchmarkChildTypeID,
Description: description,
IsLatestVersion: "1",
VersionCount: versionCount,
BranchName: branchName,
BootFile: bootFile,
DatasetName: datasetName,
Parameters: params,
})

if err != nil {
return err
}

task, err := models.GetCloudbrainByName(jobName)
task, err := models.GetCloudbrainByJobID(jobID)
if err != nil {
log.Error("GetCloudbrainByName failed: %v", err.Error())
return err
@@ -278,6 +309,8 @@ func GenerateTask(ctx *context.Context, displayJobName, jobName, image, command,

if string(models.JobTypeBenchmark) == jobType {
notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, stringId, displayJobName, models.ActionCreateBenchMarkTask)
} else if string(models.JobTypeTrain) == jobType {
notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, jobID, displayJobName, models.ActionCreateGPUTrainTask)
} else {
notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, stringId, displayJobName, models.ActionCreateDebugGPUTask)
}
@@ -407,8 +440,10 @@ func RestartTask(ctx *context.Context, task *models.Cloudbrain, newID *string) e
return err
}

idString := strconv.FormatInt(newTask.ID, 10)
*newID = idString
stringId := strconv.FormatInt(newTask.ID, 10)
*newID = stringId

notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, stringId, task.DisplayJobName, models.ActionCreateDebugGPUTask)

return nil
}

+ 2
- 0
modules/context/repo.go View File

@@ -475,6 +475,8 @@ func RepoAssignment() macaron.Handler {
if ctx.IsSigned {
ctx.Data["IsWatchingRepo"] = models.IsWatching(ctx.User.ID, repo.ID)
ctx.Data["IsStaringRepo"] = models.IsStaring(ctx.User.ID, repo.ID)

ctx.Data["IsStaringDataset"] = models.IsDatasetStaringByRepoId(ctx.User.ID, repo.ID)
}

if repo.IsFork {


+ 17
- 0
modules/dataset/dataset.go View File

@@ -0,0 +1,17 @@
package dataset

func GetResourceType(cloudbrainType int) string {
if cloudbrainType == 0 {
return "CPU/GPU"
} else {
return "NPU"
}
}

func GetStatusText(isPrivate bool) string {
if isPrivate {
return "dataset.private"
} else {
return "dataset.public"
}
}

+ 2
- 1
modules/modelarts/modelarts.go View File

@@ -51,10 +51,11 @@ const (
DataUrl = "data_url"
ResultUrl = "result_url"
CkptUrl = "ckpt_url"
DeviceTarget = "device_target"
Ascend = "Ascend"
PerPage = 10
IsLatestVersion = "1"
NotLatestVersion = "0"
DebugType = -1
VersionCount = 1

SortByCreateTime = "create_time"


+ 21
- 14
modules/setting/setting.go View File

@@ -165,6 +165,7 @@ var (
ExplorePagingNum int
ContributorPagingNum int
IssuePagingNum int
DatasetPagingNum int
RepoSearchPagingNum int
MembersPagingNum int
FeedMaxCommitNum int
@@ -207,6 +208,7 @@ var (
ExplorePagingNum: 20,
ContributorPagingNum: 50,
IssuePagingNum: 10,
DatasetPagingNum: 5,
RepoSearchPagingNum: 10,
MembersPagingNum: 20,
FeedMaxCommitNum: 5,
@@ -435,7 +437,7 @@ var (

//home page
RecommentRepoAddr string
ESSearchURL string
//notice config
UserNameOfNoticeRepo string
RepoNameOfNoticeRepo string
@@ -450,16 +452,18 @@ var (
DecompressOBSTaskName string

//cloudbrain config
CBAuthUser string
CBAuthPassword string
RestServerHost string
JobPath string
CBCodePathPrefix string
JobType string
GpuTypes string
DebugServerHost string
ResourceSpecs string
MaxDuration int64
CBAuthUser string
CBAuthPassword string
RestServerHost string
JobPath string
CBCodePathPrefix string
JobType string
GpuTypes string
DebugServerHost string
ResourceSpecs string
MaxDuration int64
TrainGpuTypes string
TrainResourceSpecs string

//benchmark config
IsBenchmarkEnabled bool
@@ -512,9 +516,9 @@ var (
ProfileID string
PoolInfos string
Flavor string
DebugHost string
ImageInfos string
Capacity int
DebugHost string
ImageInfos string
Capacity int
//train-job
ResourcePools string
Engines string
@@ -1263,6 +1267,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")

sec = Cfg.Section("notice")
UserNameOfNoticeRepo = sec.Key("USER_NAME").MustString("OpenIOSSG")
@@ -1283,6 +1288,8 @@ func NewContext() {
GpuTypes = sec.Key("GPU_TYPES").MustString("")
ResourceSpecs = sec.Key("RESOURCE_SPECS").MustString("")
MaxDuration = sec.Key("MAX_DURATION").MustInt64(14400)
TrainGpuTypes = sec.Key("TRAIN_GPU_TYPES").MustString("")
TrainResourceSpecs = sec.Key("TRAIN_RESOURCE_SPECS").MustString("")

sec = Cfg.Section("benchmark")
IsBenchmarkEnabled = sec.Key("ENABLED").MustBool(false)


+ 26
- 20
modules/templates/helper.go View File

@@ -23,6 +23,8 @@ import (
"time"
"unicode"

"code.gitea.io/gitea/modules/dataset"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/emoji"
@@ -86,20 +88,22 @@ func NewFuncMap() []template.FuncMap {
"AllowedReactions": func() []string {
return setting.UI.Reactions
},
"AvatarLink": models.AvatarLink,
"Safe": Safe,
"SafeJS": SafeJS,
"Str2html": Str2html,
"TimeSince": timeutil.TimeSince,
"TimeSinceUnix": timeutil.TimeSinceUnix,
"TimeSinceUnix1": timeutil.TimeSinceUnix1,
"TimeSinceUnixShort": timeutil.TimeSinceUnixShort,
"RawTimeSince": timeutil.RawTimeSince,
"FileSize": base.FileSize,
"PrettyNumber": base.PrettyNumber,
"Subtract": base.Subtract,
"EntryIcon": base.EntryIcon,
"MigrationIcon": MigrationIcon,
"AvatarLink": models.AvatarLink,
"Safe": Safe,
"SafeJS": SafeJS,
"Str2html": Str2html,
"TimeSince": timeutil.TimeSince,
"TimeSinceUnix": timeutil.TimeSinceUnix,
"TimeSinceUnix1": timeutil.TimeSinceUnix1,
"AttachmentResourceType": dataset.GetResourceType,
"AttachmentStatus": dataset.GetStatusText,
"TimeSinceUnixShort": timeutil.TimeSinceUnixShort,
"RawTimeSince": timeutil.RawTimeSince,
"FileSize": base.FileSize,
"PrettyNumber": base.PrettyNumber,
"Subtract": base.Subtract,
"EntryIcon": base.EntryIcon,
"MigrationIcon": MigrationIcon,
"Add": func(a, b int) int {
return a + b
},
@@ -340,11 +344,13 @@ func NewTextFuncMap() []texttmpl.FuncMap {
"AppDomain": func() string {
return setting.Domain
},
"TimeSince": timeutil.TimeSince,
"TimeSinceUnix": timeutil.TimeSinceUnix,
"TimeSinceUnix1": timeutil.TimeSinceUnix1,
"TimeSinceUnixShort": timeutil.TimeSinceUnixShort,
"RawTimeSince": timeutil.RawTimeSince,
"TimeSince": timeutil.TimeSince,
"TimeSinceUnix": timeutil.TimeSinceUnix,
"TimeSinceUnix1": timeutil.TimeSinceUnix1,
"TimeSinceUnixShort": timeutil.TimeSinceUnixShort,
"RawTimeSince": timeutil.RawTimeSince,
"AttachmentResourceType": dataset.GetResourceType,
"AttachmentStatus": dataset.GetStatusText,
"DateFmtLong": func(t time.Time) string {
return t.Format(time.RFC1123Z)
},
@@ -746,5 +752,5 @@ func licenses() []string {

// Dataset tasks
func tasks() []string {
return []string{"machine_translation", "question_answering_system", "information_retrieval", "knowledge_graph", "text_annotation", "text_categorization", "emotion_analysis", "language_modeling", "speech_recognition", "automatic_digest", "information_extraction", "description_generation", "image_classification", "face_recognition", "image_search", "target_detection", "image_description_generation", "vehicle_license_plate_recognition", "medical_image_analysis", "unmanned", "unmanned_security", "drone", "vr_ar", "2_d_vision", "2.5_d_vision", "3_d_reconstruction", "image_processing", "video_processing", "visual_input_system", "speech_coding", "speech_enhancement", "speech_recognition", "speech_synthesis"}
return []string{"machine_translation", "question_answering_system", "information_retrieval", "knowledge_graph", "text_annotation", "text_categorization", "emotion_analysis", "language_modeling", "speech_recognition", "automatic_digest", "information_extraction", "description_generation", "image_classification", "face_recognition", "image_search", "target_detection", "image_description_generation", "vehicle_license_plate_recognition", "medical_image_analysis", "unmanned", "unmanned_security", "drone", "vr_ar", "2_d_vision", "2.5_d_vision", "3_d_reconstruction", "image_processing", "video_processing", "visual_input_system", "speech_coding", "speech_enhancement", "speech_synthesis"}
}

+ 67
- 9
options/locale/locale_en-US.ini View File

@@ -254,6 +254,18 @@ page_dev_yunlao_desc3=Developers can freely choose the corresponding computing r
page_dev_yunlao_desc4=If your model requires more computing resources, you can also apply for it separately.
page_dev_yunlao_apply=Apply Separately

search=Search
search_repo=Repository
search_dataset=DataSet
search_issue=Issue
search_pr=Pull Request
search_user=User
search_org=Organization
search_finded=Find
search_related=related
search_maybe=maybe
search_ge=

[explore]
repos = Repositories
select_repos = Select the project
@@ -723,8 +735,13 @@ alert = To initiate a cloud brain task, please upload the dataset in zip format.
dataset = Dataset
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.
create_dataset = Create Dataset
create_dataset_fail=Failed to create dataset.
query_dataset_fail=Failed to query dataset.
edit_attachment_fail=Failed to update description.
show_dataset= Dataset
edit_dataset= Edit Dataset
update_dataset= Update Dataset
@@ -743,7 +760,8 @@ private = private
public = public
dir = directory
back = back
copy_url=copy download url
copy_url=Copy Download Url
copy_md5 = Copy MD5
directory=preview of the datasets
create_label_task=create label task
visibility = visibility
@@ -794,12 +812,49 @@ category.computer_vision= computer vision
category.natural_language_processing= natural language processing
category.speech_processing= speech processing
category.computer_vision_natural_language_processing= computer vision and natural language processing
attachment.delete= delete this version of dataset
attachment.delete= Delete this version of dataset
attachment.delete_desc= Are you sure you will delete this version of dataset, once deleted can not be recovery
public= public
private= private
delete= delete

delete= Delete
select_dataset=Select Dataset
current_project=Current Project
owner_dataset=Owner Dataset
public_dataset=Public Dataset
I_liked = I Liked
use = Use
create_new_dataset = Create New Dataset
dataset_name = Dataset Name
dataset_description = Dataset Description
select_category = Select Category
select_task = Select Research Direction/Application Area
dataset_name_tooltips = Please enter letters, numbers, _ and - up to 100 characters.
dataset_no_create = No dataset has been created yet
dataset_explain = Dataset: CloudBrain I provides CPU/GPU resources, Cloudbrain II provides Ascend NPU resources, and the data set used for debugging also needs to be uploaded to the corresponding environment;
dataset_instructions_for_use = Instructions for use: You can refer to Qizhi AI Collaboration Platform
dataset_camp_course = Newcomer Training Camp Course;
dataset_upload = Upload
dataset_file_name = File Name
dataset_available_clusters = Available Clusters
dataset_upload_time = Upload Time
download = Download
modify_description = Modify Description
set_public = Set Public
set_private = Set Private
annotation = Annotation
upload_dataset_file = Upload Dataset File
file_description = File Description
data_upload = Dataset Upload
illustrate = Illustrate
illustrate.only = Only Datasets In
illustrate.zip = zip/tar.gz Format
illustrate.fisrt_end = Can Initiate Cloudbrain Tasks
modify_dataset = Modify Dataset
modify_dataset_description = Modify Dataset Description
search_dataset = Search Dataset Files
unzip_tooltips = If it has not been decompressed for a long time, please check whether the compressed package has encrypted files or file errors
zip_failed = Decompression failed, please check whether the compressed package is encrypted or contact technical support
dataset_desc = The description should not exceed 1024 characters
[repo]
owner = Owner
repo_name = Repository Name
@@ -829,7 +884,7 @@ repo_label_helpe = Press Enter to complete
issue_labels = Issue Labels
issue_labels_helper = Select an issue label set.
license = License
license_helper = Select a license file.
license_helper = Select a license file
readme = README
readme_helper = Select a README file template.
auto_init = Initialize Repository (Adds .gitignore, License and README)
@@ -870,7 +925,7 @@ gpu_type_all=All
model_download=Model Download
submit_image=Submit Image
download=Download
score=score
score=Score

cloudbrain=Cloudbrain
cloudbrain.new=New cloudbrain
@@ -885,7 +940,7 @@ cloudbrain1 = cloudbrain1
cloudbrain2 = cloudbrain2
cloudbrain_selection = select cloudbrain
cloudbrain_platform_selection = Select the cloudbrain platform you want to use:
confirm_choice = confirm
confirm_choice = Confirm
cloudbran1_tips = Only data in zip format can create cloudbrain tasks
cloudbrain_creator=Creator
cloudbrain_task = Task Name
@@ -967,7 +1022,8 @@ modelarts.train_job.parameter_value=Parameter Value
modelarts.train_job.resource_setting=resource_setting
modelarts.train_job.resource_setting_info=resource_setting_info
modelarts.train_job.resource_pool=resource_pool
modelarts.train_job.resource_type=resource_type
modelarts.train_job.resource_type=Resource Type
modelarts.train_job.train_dataset=Train Dataset
modelarts.train_job.standard=Standard
modelarts.train_job.NAS_address=NAS Address
modelarts.train_job.NAS_mount_path=NAS Mount Path
@@ -1095,6 +1151,7 @@ unstar = Unstar
star = Star
fork = Fork
download_archive = Download Repository
star_fail=Failed to %s the dataset.

no_desc = No Description
no_label = No labels
@@ -2746,10 +2803,11 @@ reject_pull_request = `suggested changes for <a href="%s/pulls/%s">%s#%[2]s</a>`
upload_dataset=`upload dataset <a href="%s/datasets?type=%s">%s</a>`
task_gpudebugjob=`created CPU/GPU type debugging task<a href="%s/cloudbrain/%s">%s</a>`
task_npudebugjob=`created NPU type debugging task <a href="%s/modelarts/notebook/%s">%s</a>`
task_trainjob=`created training task<a href="%s/modelarts/train-job/%s">%s</a>`
task_nputrainjob=`created NPU training task<a href="%s/modelarts/train-job/%s">%s</a>`
task_inferencejob=`created reasoning task <a href="%s/modelarts/inference-job/%s">%s</a>`
task_benchmark=`created profiling task <a href="%s/cloudbrain/benchmark/%s">%s</a>`
task_createmodel=`created new model <a href="%s/modelmanage/show_model_info?name=%s">%s</a>`
task_gputrainjob=`created CPU/GPU training task<a href="%s/cloudbrain/train-job/%s">%s</a>`

[tool]
ago = %s ago


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

@@ -256,6 +256,18 @@ page_dev_yunlao_desc3=开发者可以根据使用需求,自由选择相应计
page_dev_yunlao_desc4=如果您的模型需要更多的计算资源,也可以单独申请
page_dev_yunlao_apply=单独申请

search=搜索
search_repo=项目
search_dataset=数据集
search_issue=任务
search_pr=合并请求
search_user=用户
search_org=组织
search_finded=找到
search_related=相关
search_maybe=约为
search_ge=个

[explore]
repos=项目
select_repos=精选项目
@@ -726,8 +738,14 @@ alert=如果要发起云脑任务,请上传zip格式的数据集
dataset=数据集
dataset_setting=数据集设置
title=名称
title_format_err=名称最多允许输入100个字符,只允许字母,数字,中划线 (‘-’),下划线 (‘_’) 和点 (‘.’) 。
description=描述
description_format_err=描述最多允许输入1024个字符。
create_dataset=创建数据集
create_dataset_fail=创建数据集失败。
query_dataset_fail=查询数据集失败。
edit_attachment_fail=修改描述失败。

show_dataset=数据集
edit_dataset=编辑数据集
update_dataset=更新数据集
@@ -803,6 +821,44 @@ attachment.delete_desc= 你确定要删除该版本的数据集么?一旦删
public=公有
private=私有
delete=删除
select_dataset=选择数据集
current_project=当前项目
owner_dataset=我的数据集
public_dataset=公开数据集
I_liked=我收藏的
use=使用
create_new_dataset = 新建数据集
dataset_name=数据集名称
dataset_description = 数据集描述
select_category = 选择分类
select_task = 选择研究方向/应用领域
dataset_name_tooltips = 请输入字母、数字、_和-,最长100个字符。
dataset_no_create = 还未创建过数据集
dataset_explain = 数据集:云脑1提供 CPU / GPU 资源,云脑2提供 Ascend NPU 资源,调试使用的数据集也需要上传到对应的环境;
dataset_instructions_for_use = 使用说明:可以参考启智AI协作平台
dataset_camp_course = 小白训练营课程
dataset_upload = 上传
dataset_file_name = 文件名称
dataset_available_clusters = 可用集群
dataset_upload_time = 上传时间
download = 下载
modify_description = 修改描述
set_public = 设为公开
set_private = 设为私有
annotation = 标注
upload_dataset_file = 上传数据集文件
file_description = 文件描述
data_upload = 数据上传
illustrate = 说明
illustrate.only = 只有
illustrate.zip = zip/tar.gz格式
illustrate.fisrt_end = 的数据集才能发起云脑任务
modify_dataset = 修改数据集
modify_dataset_description = 修改数据集文件描述
search_dataset = 搜索数据集文件
unzip_tooltips = 如果长时间未解压,请检查压缩包是否有加密文件或者文件错误
zip_failed = 解压失败,请检查压缩包是否有加密或者联系技术支持人员。
dataset_desc = 描述字数不超过1024个字符

[repo]
owner=拥有者
@@ -833,7 +889,7 @@ repo_label_helpe=输入完成后回车键完成标签确定。
issue_labels=任务标签
issue_labels_helper=选择一个任务标签集
license=授权许可
license_helper=选择授权许可文件
license_helper=选择授权许可文件
readme=自述
readme_helper=选择自述文件模板。
auto_init=初始化存储库 (添加. gitignore、许可证和自述文件)
@@ -1101,6 +1157,8 @@ unstar=取消点赞
star=点赞
fork=派生
download_archive=下载此项目
star_fail=%s失败。


no_desc=暂无描述
no_label = 暂无标签
@@ -2751,10 +2809,11 @@ reject_pull_request=`建议变更 <a href="%s/pulls/%s">%s#%[2]s</a>`
upload_dataset=`上传了数据集文件 <a href="%s/datasets?type=%s">%s</a>`
task_gpudebugjob=`创建了CPU/GPU类型调试任务 <a href="%s/cloudbrain/%s">%s</a>`
task_npudebugjob=`创建了NPU类型调试任务 <a href="%s/modelarts/notebook/%s">%s</a>`
task_trainjob=`创建了训练任务 <a href="%s/modelarts/train-job/%s">%s</a>`
task_nputrainjob=`创建了NPU类型训练任务 <a href="%s/modelarts/train-job/%s">%s</a>`
task_inferencejob=`创建了推理任务 <a href="%s/modelarts/inference-job/%s">%s</a>`
task_benchmark=`创建了评测任务 <a href="%s/cloudbrain/benchmark/%s">%s</a>`
task_createmodel=`导入了新模型 <a href="%s/modelmanage/show_model_info?name=%s">%s</a>`
task_gputrainjob=`创建了CPU/GPU类型训练任务 <a href="%s/cloudbrain/train-job/%s">%s</a>`

[tool]
ago=%s前


+ 9
- 5
public/home/home.js View File

@@ -135,7 +135,7 @@ socket.onmessage = function (e) {
html += recordPrefix + actionName;
html += " <a href=\"" + getRepoLink(record) + "\" rel=\"nofollow\">" + getRepotext(record) + "</a>"
}
else if(record.OpType == "24" || record.OpType == "26" || record.OpType == "27" || record.OpType == "28" || record.OpType == "30"){
else if(record.OpType == "24" || record.OpType == "26" || record.OpType == "27" || record.OpType == "28" || record.OpType == "30" || record.OpType == "31"){
html += recordPrefix + actionName;
html += " <a href=\"" + getTaskLink(record) + "\" rel=\"nofollow\">" + record.RefName + "</a>"
}
@@ -175,6 +175,8 @@ function getTaskLink(record){
re = re + "/cloudbrain/benchmark/" + record.Content;
}else if(record.OpType == 30){
re = re + "/modelmanage/show_model_info?name=" + record.RefName;
}else if(record.OpType == 31){
re = re + "/cloudbrain/train-job/" + record.Content;
}
re = encodeURI(re);
return re;
@@ -321,10 +323,11 @@ var actionNameZH={
"24":"上传了数据集文件",
"25":"创建了CPU/GPU类型调试任务",
"26":"创建了NPU类型调试任务",
"27":"创建了训练任务",
"27":"创建了NPU类型训练任务",
"28":"创建了推理任务",
"29":"创建了评测任务",
"30":"导入了新模型"
"30":"导入了新模型",
"31":"创建了CPU/GPU类型训练任务"
};

var actionNameEN={
@@ -346,10 +349,11 @@ var actionNameEN={
"24":" upload dataset ",
"25":" created CPU/GPU type debugging task ",
"26":" created NPU type debugging task ",
"27":" created training task",
"27":" created NPU type training task",
"28":" created reasoning task",
"29":" created profiling task",
"30":" created new model"
"30":" created new model",
"31":" created CPU/GPU type training task",
};

var repoAndOrgZH={


+ 1281
- 0
public/home/search.js
File diff suppressed because it is too large
View File


+ 3
- 3
public/self/dataset_preview.js View File

@@ -620,10 +620,10 @@ function showfilelist(){
for (var i=0;i<labeltastresult.length;i++){
var fname = labeltastresult[i].pic_image_field.substring(labeltastresult[i].pic_image_field.lastIndexOf('/') + 1);
console.log(labeltastresult[i])
//console.log(labeltastresult[i])
if(labeltastresult[i].pic_image_field.length > 70){
var tmpIndex = labeltastresult[i].pic_image_field.indexOf("/",70);
console.log(tmpIndex)
//console.log(tmpIndex)
if(tmpIndex != -1){
fname = labeltastresult[i].pic_image_field.substring(tmpIndex + 1);
fname = fname.substring(fname.indexOf('/')+1);
@@ -679,7 +679,7 @@ function breadFiles(){
fname_full_path = tableData[fileindex].pic_image_field.substring(tmp_index + 1);
}
var fname_path = fname_full_path.split('/')
console.log(fname_path)
//console.log(fname_path)
// var filename_text = tableData[fileindex].pic_image_field.substring(tableData[fileindex].pic_image_field.lastIndexOf('/')+1)
var html_breadFile = ''
// var source_name = filename_title+'.zip'


+ 3
- 3
routers/admin/cloudbrains.go View File

@@ -41,7 +41,7 @@ func CloudBrains(ctx *context.Context) {
if page <= 0 {
page = 1
}
debugType := modelarts.DebugType
debugType := models.TypeCloudBrainAll
if listType == models.GPUResource {
debugType = models.TypeCloudBrainOne
} else if listType == models.NPUResource {
@@ -121,7 +121,7 @@ func DownloadCloudBrains(ctx *context.Context) {
Page: page,
PageSize: 1,
},
Type: modelarts.DebugType,
Type: models.TypeCloudBrainAll,
NeedRepoInfo: false,
IsLatestVersion: modelarts.IsLatestVersion,
})
@@ -151,7 +151,7 @@ func DownloadCloudBrains(ctx *context.Context) {
Page: page,
PageSize: pageSize,
},
Type: modelarts.DebugType,
Type: models.TypeCloudBrainAll,
NeedRepoInfo: true,
IsLatestVersion: modelarts.IsLatestVersion,
})


+ 10
- 2
routers/api/v1/api.go View File

@@ -62,10 +62,10 @@ import (
"net/http"
"strings"

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

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth"

"code.gitea.io/gitea/modules/cloudbrain"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
@@ -77,6 +77,7 @@ import (
"code.gitea.io/gitea/routers/api/v1/repo"
_ "code.gitea.io/gitea/routers/api/v1/swagger" // for swagger generation
"code.gitea.io/gitea/routers/api/v1/user"
"code.gitea.io/gitea/routers/authentication"
repo_ext "code.gitea.io/gitea/routers/repo"

"gitea.com/macaron/binding"
@@ -890,6 +891,13 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Group("/cloudbrain", func() {
m.Get("/:id", repo.GetCloudbrainTask)
m.Get("/:id/log", repo.CloudbrainGetLog)
m.Group("/train-job", func() {
m.Group("/:jobid", func() {
m.Get("", repo.GetModelArtsTrainJobVersion)
m.Get("/model_list", repo.CloudBrainModelList)
m.Post("/stop_version", cloudbrain.AdminOrOwnerOrJobCreaterRightForTrain, repo_ext.CloudBrainStop)
})
})
}, reqRepoReader(models.UnitTypeCloudBrain))
m.Group("/modelarts", func() {
m.Group("/notebook", func() {


+ 61
- 6
routers/api/v1/repo/cloudbrain.go View File

@@ -6,15 +6,18 @@
package repo

import (
"encoding/json"
"net/http"
"sort"
"strings"
"time"

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

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/cloudbrain"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/storage"
routerRepo "code.gitea.io/gitea/routers/repo"
)

// cloudbrain get job task by jobid
@@ -62,7 +65,7 @@ func GetCloudbrainTask(ctx *context.APIContext) {
log.Error("GetJob failed:", err)
return
}
result, err := models.ConvertToJobResultPayload(jobResult.Payload)
result, _ := models.ConvertToJobResultPayload(jobResult.Payload)
if err != nil {
ctx.NotFound(err)
log.Error("ConvertToJobResultPayload failed:", err)
@@ -70,16 +73,16 @@ func GetCloudbrainTask(ctx *context.APIContext) {
}

job.Status = result.JobStatus.State
taskRoles := result.TaskRoles
taskRes, _ := models.ConvertToTaskPod(taskRoles[cloudbrain.SubTaskName].(map[string]interface{}))
if result.JobStatus.State != string(models.JobWaiting) && result.JobStatus.State != string(models.JobFailed) {
taskRoles := result.TaskRoles
taskRes, _ := models.ConvertToTaskPod(taskRoles[cloudbrain.SubTaskName].(map[string]interface{}))

job.ContainerIp = taskRes.TaskStatuses[0].ContainerIP
job.ContainerID = taskRes.TaskStatuses[0].ContainerID
job.Status = taskRes.TaskStatuses[0].State
}

if result.JobStatus.State != string(models.JobWaiting) {
models.ParseAndSetDurationFromCloudBrainOne(result, job)
err = models.UpdateJob(job)
if err != nil {
log.Error("UpdateJob failed:", err)
@@ -152,3 +155,55 @@ func CloudbrainGetLog(ctx *context.Context) {

return
}

func CloudBrainModelList(ctx *context.APIContext) {
var (
err error
)

var jobID = ctx.Params(":jobid")
var versionName = ctx.Query("version_name")
parentDir := ctx.Query("parentDir")
dirArray := strings.Split(parentDir, "/")

task, err := models.GetCloudbrainByJobIDAndVersionName(jobID, versionName)
if err != nil {
log.Error("GetCloudbrainByJobID(%s) failed:%v", task.JobName, err.Error())
return
}

//get dirs
dirs, err := routerRepo.GetModelDirs(task.JobName, parentDir)
if err != nil {
log.Error("GetModelDirs failed:%v", err.Error(), ctx.Data["msgID"])
ctx.ServerError("GetModelDirs failed:", err)
return
}

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

for i, fileInfo := range fileInfos {
temp, _ := time.Parse("2006-01-02 15:04:05", fileInfo.ModTime)
fileInfos[i].ModTime = temp.Local().Format("2006-01-02 15:04:05")
}

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

ctx.JSON(http.StatusOK, map[string]interface{}{
"JobID": jobID,
"VersionName": versionName,
"StatusOK": 0,
"Path": dirArray,
"Dirs": fileInfos,
"task": task,
"PageIsCloudBrain": true,
})
}

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

@@ -10,13 +10,13 @@ import (
"strconv"
"strings"

"code.gitea.io/gitea/modules/util"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/cloudbrain"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/modelarts"
"code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/modules/timeutil"
routerRepo "code.gitea.io/gitea/routers/repo"
)

@@ -67,8 +67,14 @@ func GetModelArtsNotebook2(ctx *context.APIContext) {
ctx.NotFound(err)
return
}

if job.StartTime == 0 && result.Lease.UpdateTime > 0 {
job.StartTime = timeutil.TimeStamp(result.Lease.UpdateTime / 1000)
}
job.Status = result.Status
if job.EndTime == 0 && models.IsModelArtsDebugJobTerminal(job.Status) {
job.EndTime = timeutil.TimeStampNow()
}
job.ComputeAndSetDuration()
err = models.UpdateJob(job)
if err != nil {
log.Error("UpdateJob failed:", err)
@@ -128,26 +134,61 @@ func GetModelArtsTrainJobVersion(ctx *context.APIContext) {
ctx.NotFound(err)
return
}
result, err := modelarts.GetTrainJob(jobID, strconv.FormatInt(job.VersionID, 10))
if err != nil {
ctx.NotFound(err)
return
}

job.Status = modelarts.TransTrainJobStatus(result.IntStatus)
job.Duration = result.Duration
job.TrainJobDuration = result.TrainJobDuration
if job.Type == models.TypeCloudBrainOne {
jobResult, err := cloudbrain.GetJob(job.JobID)
if err != nil {
ctx.NotFound(err)
log.Error("GetJob failed:", err)
return
}
result, err := models.ConvertToJobResultPayload(jobResult.Payload)
if err != nil {
ctx.NotFound(err)
log.Error("ConvertToJobResultPayload failed:", err)
return
}

if result.Duration != 0 {
job.TrainJobDuration = util.AddZero(result.Duration/3600000) + ":" + util.AddZero(result.Duration%3600000/60000) + ":" + util.AddZero(result.Duration%60000/1000)
job.Status = result.JobStatus.State
if result.JobStatus.State != string(models.JobWaiting) && result.JobStatus.State != string(models.JobFailed) {
taskRoles := result.TaskRoles
taskRes, _ := models.ConvertToTaskPod(taskRoles[cloudbrain.SubTaskName].(map[string]interface{}))

job.ContainerIp = taskRes.TaskStatuses[0].ContainerIP
job.ContainerID = taskRes.TaskStatuses[0].ContainerID
job.Status = taskRes.TaskStatuses[0].State
}

if result.JobStatus.State != string(models.JobWaiting) {
err = models.UpdateJob(job)
if err != nil {
log.Error("UpdateJob failed:", err)
}
}
} else {
job.TrainJobDuration = "00:00:00"
}
result, err := modelarts.GetTrainJob(jobID, strconv.FormatInt(job.VersionID, 10))
if err != nil {
ctx.NotFound(err)
return
}

err = models.UpdateTrainJobVersion(job)
if err != nil {
log.Error("UpdateJob failed:", err)
if job.StartTime == 0 && result.StartTime > 0 {
job.StartTime = timeutil.TimeStamp(result.StartTime / 1000)
}
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)
}

err = models.UpdateTrainJobVersion(job)
if err != nil {
log.Error("UpdateJob failed:", err)
}
}

ctx.JSON(http.StatusOK, map[string]interface{}{
@@ -366,16 +407,15 @@ func GetModelArtsInferenceJob(ctx *context.APIContext) {
ctx.NotFound(err)
return
}

if job.StartTime == 0 && result.StartTime > 0 {
job.StartTime = timeutil.TimeStamp(result.StartTime / 1000)
}
job.Status = modelarts.TransTrainJobStatus(result.IntStatus)
job.Duration = result.Duration
job.TrainJobDuration = result.TrainJobDuration
job.Duration = result.Duration / 1000
job.TrainJobDuration = models.ConvertDurationToStr(job.Duration)

if result.Duration != 0 {
job.TrainJobDuration = util.AddZero(result.Duration/3600000) + ":" + util.AddZero(result.Duration%3600000/60000) + ":" + util.AddZero(result.Duration%60000/1000)

} else {
job.TrainJobDuration = "00:00:00"
if job.EndTime == 0 && models.IsTrainJobTerminal(job.Status) && job.StartTime > 0 {
job.EndTime = job.StartTime.Add(job.Duration)
}

err = models.UpdateInferenceJob(job)


+ 29
- 6
routers/home.go View File

@@ -274,10 +274,11 @@ func ExploreDatasets(ctx *context.Context) {
// ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled

var (
datasets []*models.Dataset
count int64
err error
orderBy models.SearchOrderBy
datasets []*models.Dataset
datasetsWithStar []*models.DatasetWithStar
count int64
err error
orderBy models.SearchOrderBy
)
page := ctx.QueryInt("page")
if page <= 0 {
@@ -301,6 +302,10 @@ func ExploreDatasets(ctx *context.Context) {
orderBy = models.SearchOrderBySizeReverse
case "downloadtimes":
orderBy = models.SearchOrderByDownloadTimes
case "moststars":
orderBy = models.SearchOrderByStarsReverse
case "feweststars":
orderBy = models.SearchOrderByStars
default:
ctx.Data["SortType"] = "recentupdate"
orderBy = models.SearchOrderByRecentUpdated
@@ -308,6 +313,9 @@ func ExploreDatasets(ctx *context.Context) {

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

category := ctx.Query("category")
task := ctx.Query("task")
license := ctx.Query("license")
var ownerID int64
if ctx.User != nil && !ctx.User.IsAdmin {
ownerID = ctx.User.ID
@@ -316,25 +324,40 @@ func ExploreDatasets(ctx *context.Context) {
Keyword: keyword,
IncludePublic: true,
SearchOrderBy: orderBy,
Category: category,
Task: task,
License: license,
OwnerID: ownerID,
ListOptions: models.ListOptions{
Page: page,
PageSize: setting.UI.ExplorePagingNum,
PageSize: 30,
},
}

datasets, count, err = models.SearchDataset(opts)

if err != nil {
ctx.ServerError("SearchDatasets", err)
return
}
for _, dataset := range datasets {
if !ctx.IsSigned {
datasetsWithStar = append(datasetsWithStar, &models.DatasetWithStar{Dataset: *dataset, IsStaring: false})
} else {
datasetsWithStar = append(datasetsWithStar, &models.DatasetWithStar{Dataset: *dataset, IsStaring: models.IsDatasetStaring(ctx.User.ID, dataset.ID)})
}

}

pager := context.NewPagination(int(count), opts.PageSize, page, 5)
ctx.Data["Keyword"] = opts.Keyword
ctx.Data["Category"] = category
ctx.Data["Task"] = task
ctx.Data["License"] = license
pager.SetDefaultParams(ctx)
ctx.Data["Page"] = pager

ctx.Data["Datasets"] = datasets
ctx.Data["Datasets"] = datasetsWithStar
ctx.Data["Total"] = count
ctx.Data["PageIsDatasets"] = true
ctx.HTML(200, tplExploreDataset)


+ 2
- 0
routers/init.go View File

@@ -71,6 +71,8 @@ func NewServices() {
log.Info("decompression.NewContext() succeed.")
labelmsg.Init()
log.Info("labelmsg.Init() succeed.")
InitESClient()
log.Info("ES Client succeed.")
}

// In case of problems connecting to DB, retry connection. Eg, PGSQL in Docker Container on Synology


+ 2
- 0
routers/private/internal.go View File

@@ -6,6 +6,7 @@
package private

import (
"code.gitea.io/gitea/routers/repo"
"strings"

"code.gitea.io/gitea/modules/log"
@@ -45,6 +46,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/tool/update_all_repo_commit_cnt", UpdateAllRepoCommitCnt)
m.Post("/tool/repo_stat/:date", RepoStatisticManually)
m.Post("/tool/update_repo_visit/:date", UpdateRepoVisit)
m.Post("/task/history_handle/duration", repo.HandleTaskWithNoDuration)

}, CheckInternalToken)
}

+ 76
- 19
routers/repo/attachment.go View File

@@ -15,6 +15,10 @@ import (
"strconv"
"strings"

"code.gitea.io/gitea/modules/auth"

"code.gitea.io/gitea/modules/base"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/labelmsg"
@@ -30,8 +34,10 @@ import (

const (
//result of decompress
DecompressSuccess = "0"
DecompressFailed = "1"
DecompressSuccess = "0"
DecompressFailed = "1"
tplAttachmentUpload base.TplName = "repo/attachment/upload"
tplAttachmentEdit base.TplName = "repo/attachment/edit"
)

type CloudBrainDataset struct {
@@ -63,6 +69,40 @@ func renderAttachmentSettings(ctx *context.Context) {
ctx.Data["AttachmentMaxFiles"] = setting.Attachment.MaxFiles
}

func UploadAttachmentUI(ctx *context.Context) {
ctx.Data["datasetId"] = ctx.Query("datasetId")
ctx.Data["PageIsDataset"] = true

ctx.HTML(200, tplAttachmentUpload)

}

func EditAttachmentUI(ctx *context.Context) {
id, _ := strconv.ParseInt(ctx.Params(":id"), 10, 64)
ctx.Data["PageIsDataset"] = true
attachment, _ := models.GetAttachmentByID(id)
if attachment == nil {
ctx.Error(404, "The attachment does not exits.")
}
ctx.Data["Attachment"] = attachment
ctx.HTML(200, tplAttachmentEdit)

}

func EditAttachment(ctx *context.Context, form auth.EditAttachmentForm) {

err := models.UpdateAttachmentDescription(&models.Attachment{
ID: form.ID,
Description: form.Description,
})
if err != nil {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.edit_attachment_fail")))
}
ctx.JSON(http.StatusOK, models.BaseOKMessage)

}

// UploadAttachment response for uploading issue's attachment
func UploadAttachment(ctx *context.Context) {
if !setting.Attachment.Enabled {
@@ -241,14 +281,20 @@ func GetAttachment(ctx *context.Context) {
}

if dataSet != nil {
isPermit, err := models.GetUserDataSetPermission(dataSet, ctx.User)
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetUserDataSetPermission", err.Error())
return
}
if !isPermit {
ctx.Error(http.StatusNotFound)
if !ctx.IsSigned {
ctx.SetCookie("redirect_to", setting.AppSubURL+ctx.Req.URL.RequestURI(), 0, setting.AppSubURL)
ctx.Redirect(setting.AppSubURL + "/user/login")
return
} else {
isPermit, err := models.GetUserDataSetPermission(dataSet, ctx.User)
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetUserDataSetPermission", err.Error())
return
}
if !isPermit {
ctx.Error(http.StatusNotFound)
return
}
}
}

@@ -387,11 +433,17 @@ func AddAttachment(ctx *context.Context) {
ctx.Error(404, "attachment has not been uploaded")
return
}
datasetId := ctx.QueryInt64("dataset_id")
dataset, err := models.GetDatasetByID(datasetId)
if err != nil {
ctx.Error(404, "dataset does not exist.")
return
}

attachment, err := models.InsertAttachment(&models.Attachment{
UUID: uuid,
UploaderID: ctx.User.ID,
IsPrivate: true,
IsPrivate: dataset.IsPrivate(),
Name: fileName,
Size: ctx.QueryInt64("size"),
DatasetID: ctx.QueryInt64("dataset_id"),
@@ -798,6 +850,9 @@ func CompleteMultipart(ctx *context.Context) {
typeCloudBrain := ctx.QueryInt("type")
fileName := ctx.Query("file_name")

log.Warn("uuid:" + uuid)
log.Warn("typeCloudBrain:" + strconv.Itoa(typeCloudBrain))

err := checkTypeCloudBrain(typeCloudBrain)
if err != nil {
ctx.ServerError("checkTypeCloudBrain failed", err)
@@ -835,22 +890,24 @@ func CompleteMultipart(ctx *context.Context) {
ctx.Error(500, fmt.Sprintf("UpdateFileChunk: %v", err))
return
}

dataset, _ := models.GetDatasetByID(ctx.QueryInt64("dataset_id"))
log.Warn("insert attachment to datasetId:" + strconv.FormatInt(dataset.ID, 10))
attachment, err := models.InsertAttachment(&models.Attachment{
UUID: uuid,
UploaderID: ctx.User.ID,
IsPrivate: true,
Name: fileName,
Size: ctx.QueryInt64("size"),
DatasetID: ctx.QueryInt64("dataset_id"),
Type: typeCloudBrain,
UUID: uuid,
UploaderID: ctx.User.ID,
IsPrivate: dataset.IsPrivate(),
Name: fileName,
Size: ctx.QueryInt64("size"),
DatasetID: ctx.QueryInt64("dataset_id"),
Description: ctx.Query("description"),
Type: typeCloudBrain,
})

if err != nil {
ctx.Error(500, fmt.Sprintf("InsertAttachment: %v", err))
return
}
dataset, _ := models.GetDatasetByID(attachment.DatasetID)
repository, _ := models.GetRepositoryByID(dataset.RepoID)
notification.NotifyOtherTask(ctx.User, repository, fmt.Sprint(attachment.Type), attachment.Name, models.ActionUploadAttachment)



+ 370
- 44
routers/repo/cloudbrain.go View File

@@ -2,6 +2,7 @@ package repo

import (
"bufio"
"code.gitea.io/gitea/modules/timeutil"
"encoding/json"
"errors"
"fmt"
@@ -36,6 +37,9 @@ const (
tplCloudBrainBenchmarkIndex base.TplName = "repo/cloudbrain/benchmark/index"
tplCloudBrainBenchmarkNew base.TplName = "repo/cloudbrain/benchmark/new"
tplCloudBrainBenchmarkShow base.TplName = "repo/cloudbrain/benchmark/show"

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

var (
@@ -44,6 +48,7 @@ var (
benchmarkTypes *models.BenchmarkTypes
benchmarkGpuInfos *models.GpuInfos
benchmarkResourceSpecs *models.ResourceSpecs
trainGpuInfos *models.GpuInfos
)

const BENCHMARK_TYPE_CODE = "repo.cloudbrain.benchmark.types"
@@ -142,6 +147,11 @@ func cloudBrainNewDataPrepare(ctx *context.Context) error {
}
ctx.Data["gpu_types"] = gpuInfos.GpuInfo

if trainGpuInfos == nil {
json.Unmarshal([]byte(setting.TrainGpuTypes), &trainGpuInfos)
}
ctx.Data["train_gpu_types"] = trainGpuInfos.GpuInfo

if benchmarkGpuInfos == nil {
json.Unmarshal([]byte(setting.BenchmarkGpuTypes), &benchmarkGpuInfos)
}
@@ -156,12 +166,22 @@ func cloudBrainNewDataPrepare(ctx *context.Context) error {
json.Unmarshal([]byte(setting.ResourceSpecs), &cloudbrain.ResourceSpecs)
}
ctx.Data["resource_specs"] = cloudbrain.ResourceSpecs.ResourceSpec

if cloudbrain.TrainResourceSpecs == nil {
json.Unmarshal([]byte(setting.TrainResourceSpecs), &cloudbrain.TrainResourceSpecs)
}
ctx.Data["train_resource_specs"] = cloudbrain.TrainResourceSpecs.ResourceSpec
ctx.Data["params"] = ""
ctx.Data["branchName"] = ctx.Repo.BranchName

ctx.Data["snn4imagenet_path"] = cloudbrain.Snn4imagenetMountPath
ctx.Data["is_snn4imagenet_enabled"] = setting.IsSnn4imagenetEnabled

ctx.Data["brainscore_path"] = cloudbrain.BrainScoreMountPath
ctx.Data["is_brainscore_enabled"] = setting.IsBrainScoreEnabled

ctx.Data["cloudbraintype"] = models.TypeCloudBrainOne

return nil
}

@@ -181,38 +201,52 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) {
image := form.Image
uuid := form.Attachment
jobType := form.JobType
command := cloudbrain.Command
gpuQueue := form.GpuType
codePath := setting.JobPath + jobName + cloudbrain.CodeMountPath
resourceSpecId := form.ResourceSpecId
branchName := form.BranchName
repo := ctx.Repo.Repository

tasks, err := models.GetCloudbrainsByDisplayJobName(repo.ID, string(models.JobTypeDebug), displayJobName)
tpl := tplCloudBrainNew
command := cloudbrain.Command
if jobType == string(models.JobTypeTrain) {
tpl = tplCloudBrainTrainJobNew
commandTrain, err := getTrainJobCommand(form)
if err != nil {
log.Error("getTrainJobCommand failed: %v", err)
ctx.RenderWithErr(err.Error(), tpl, &form)
return
}

command = commandTrain
}

tasks, err := models.GetCloudbrainsByDisplayJobName(repo.ID, jobType, displayJobName)
if err == nil {
if len(tasks) != 0 {
log.Error("the job name did already exist", ctx.Data["MsgID"])
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr("the job name did already exist", tplCloudBrainNew, &form)
ctx.RenderWithErr("the job name did already exist", tpl, &form)
return
}
} else {
if !models.IsErrJobNotExist(err) {
log.Error("system error, %v", err, ctx.Data["MsgID"])
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr("system error", tplCloudBrainNew, &form)
ctx.RenderWithErr("system error", tpl, &form)
return
}
}

if !jobNamePattern.MatchString(displayJobName) {
ctx.RenderWithErr(ctx.Tr("repo.cloudbrain_jobname_err"), tplCloudBrainNew, &form)
ctx.RenderWithErr(ctx.Tr("repo.cloudbrain_jobname_err"), tpl, &form)
return
}

if jobType != string(models.JobTypeBenchmark) && jobType != string(models.JobTypeDebug) && jobType != string(models.JobTypeSnn4imagenet) && jobType != string(models.JobTypeBrainScore) {
if jobType != string(models.JobTypeBenchmark) && jobType != string(models.JobTypeDebug) && jobType != string(models.JobTypeSnn4imagenet) && jobType != string(models.JobTypeBrainScore) && jobType != string(models.JobTypeTrain) {
log.Error("jobtype error:", jobType, ctx.Data["MsgID"])
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr("jobtype error", tplCloudBrainNew, &form)
ctx.RenderWithErr("jobtype error", tpl, &form)
return
}

@@ -220,18 +254,21 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) {
if err != nil {
log.Error("GetCloudbrainCountByUserID failed:%v", err, ctx.Data["MsgID"])
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr("system error", tplCloudBrainNew, &form)
ctx.RenderWithErr("system error", tpl, &form)
return
} else {
if count >= 1 {
log.Error("the user already has running or waiting task", ctx.Data["MsgID"])
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr("you have already a running or waiting task, can not create more", tplCloudBrainNew, &form)
ctx.RenderWithErr("you have already a running or waiting task, can not create more", tpl, &form)
return
}
}

downloadCode(repo, codePath)
if branchName == "" {
branchName = cloudbrain.DefaultBranchName
}
downloadCode(repo, codePath, branchName)
uploadCodeToMinio(codePath+"/", jobName, cloudbrain.CodeMountPath+"/")

modelPath := setting.JobPath + jobName + cloudbrain.ModelMountPath + "/"
@@ -265,15 +302,19 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) {
err = cloudbrain.GenerateTask(ctx, displayJobName, jobName, image, command, uuid, storage.GetMinioPath(jobName, cloudbrain.CodeMountPath+"/"),
storage.GetMinioPath(jobName, cloudbrain.ModelMountPath+"/"),
storage.GetMinioPath(jobName, cloudbrain.BenchMarkMountPath+"/"), storage.GetMinioPath(jobName, cloudbrain.Snn4imagenetMountPath+"/"),
storage.GetMinioPath(jobName, cloudbrain.BrainScoreMountPath+"/"), jobType, gpuQueue, form.Description,
storage.GetMinioPath(jobName, cloudbrain.BrainScoreMountPath+"/"), jobType, gpuQueue, form.Description, branchName, form.BootFile, form.Params,
0, 0, resourceSpecId)
if err != nil {
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr(err.Error(), tplCloudBrainNew, &form)
ctx.RenderWithErr(err.Error(), tpl, &form)
return
}

ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/debugjob?debugListType=all")
if jobType == string(models.JobTypeTrain) {
ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/train-job?listType=all")
} else {
ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/debugjob?debugListType=all")
}
}

func CloudBrainRestart(ctx *context.Context) {
@@ -339,18 +380,29 @@ func CloudBrainRestart(ctx *context.Context) {
}

func CloudBrainBenchMarkShow(ctx *context.Context) {
cloudBrainShow(ctx, tplCloudBrainBenchmarkShow)
cloudBrainShow(ctx, tplCloudBrainBenchmarkShow, models.JobTypeBenchmark)
}

func CloudBrainShow(ctx *context.Context) {
cloudBrainShow(ctx, tplCloudBrainShow)
cloudBrainShow(ctx, tplCloudBrainShow, models.JobTypeDebug)
}

func CloudBrainTrainJobShow(ctx *context.Context) {
cloudBrainShow(ctx, tplCloudBrainTrainJobShow, models.JobTypeTrain)
}

func cloudBrainShow(ctx *context.Context, tpName base.TplName) {
func cloudBrainShow(ctx *context.Context, tpName base.TplName, jobType models.JobType) {
ctx.Data["PageIsCloudBrain"] = true
var ID = ctx.Params(":id")
debugListType := ctx.Query("debugListType")
task, err := models.GetCloudbrainByID(ID)

var task *models.Cloudbrain
var err error
if jobType == models.JobTypeTrain {
task, err = models.GetCloudbrainByJobID(ctx.Params(":jobid"))
} else {
task, err = models.GetCloudbrainByID(ctx.Params(":id"))
}

if err != nil {
log.Info("error:" + err.Error())
ctx.Data["error"] = err.Error()
@@ -365,6 +417,16 @@ func cloudBrainShow(ctx *context.Context, tpName base.TplName) {
jobRes.Resource.Memory = strings.ReplaceAll(jobRes.Resource.Memory, "Mi", "MB")
spec := "GPU数:" + strconv.Itoa(jobRes.Resource.NvidiaComGpu) + ",CPU数:" + strconv.Itoa(jobRes.Resource.CPU) + ",内存(MB):" + jobRes.Resource.Memory
ctx.Data["resource_spec"] = spec
if task.JobType == string(models.JobTypeTrain) {
if trainGpuInfos == nil {
json.Unmarshal([]byte(setting.TrainGpuTypes), &trainGpuInfos)
}
for _, resourceType := range trainGpuInfos.GpuInfo {
if resourceType.Queue == jobRes.Config.GpuType {
ctx.Data["resource_type"] = resourceType.Value
}
}
}
taskRoles := jobRes.TaskRoles
if jobRes.JobStatus.State != string(models.JobFailed) {

@@ -373,6 +435,7 @@ func cloudBrainShow(ctx *context.Context, tpName base.TplName) {
task.Status = taskRes.TaskStatuses[0].State
task.ContainerID = taskRes.TaskStatuses[0].ContainerID
task.ContainerIp = taskRes.TaskStatuses[0].ContainerIP
models.ParseAndSetDurationFromCloudBrainOne(jobRes, task)
err = models.UpdateJob(task)
if err != nil {
ctx.Data["error"] = err.Error()
@@ -398,12 +461,6 @@ func cloudBrainShow(ctx *context.Context, tpName base.TplName) {
task.User = user
}

var duration int64
if task.Status == string(models.JobRunning) {
duration = time.Now().Unix() - int64(task.CreatedUnix)
} else {
duration = int64(task.UpdatedUnix) - int64(task.CreatedUnix)
}
if task.BenchmarkTypeID > 0 {
for _, benchmarkType := range GetBenchmarkTypes(ctx).BenchmarkType {
if task.BenchmarkTypeID == benchmarkType.Id {
@@ -418,8 +475,42 @@ func cloudBrainShow(ctx *context.Context, tpName base.TplName) {
}
}
}
if task.TrainJobDuration == "" {
if task.Duration == 0 {
var duration int64
if task.Status == string(models.JobRunning) {
duration = time.Now().Unix() - int64(task.CreatedUnix)
} else {
duration = int64(task.UpdatedUnix) - int64(task.CreatedUnix)
}
task.Duration = duration
}
task.TrainJobDuration = models.ConvertDurationToStr(task.Duration)
}
ctx.Data["duration"] = task.TrainJobDuration

if len(task.Parameters) > 0 {
var parameters models.Parameters

err := json.Unmarshal([]byte(task.Parameters), &parameters)
if err != nil {
log.Error("Failed to Unmarshal Parameters: %s (%v)", task.Parameters, err)
task.Parameters = ""
} else {
if len(parameters.Parameter) > 0 {
paramTemp := ""
for _, Parameter := range parameters.Parameter {
param := Parameter.Label + " = " + Parameter.Value + "; "
paramTemp = paramTemp + param
}
task.Parameters = paramTemp[:len(paramTemp)-2]
} else {
task.Parameters = ""
}
}

}

ctx.Data["duration"] = util.AddZero(duration/3600000) + ":" + util.AddZero(duration%3600000/60000) + ":" + util.AddZero(duration%60000/1000)
ctx.Data["task"] = task
ctx.Data["jobName"] = task.JobName
ctx.Data["displayJobName"] = task.DisplayJobName
@@ -427,6 +518,7 @@ func cloudBrainShow(ctx *context.Context, tpName base.TplName) {
version_list_task = append(version_list_task, task)
ctx.Data["version_list_task"] = version_list_task
ctx.Data["debugListType"] = debugListType
ctx.Data["canDownload"] = cloudbrain.CanDeleteJob(ctx, task)
ctx.HTML(200, tpName)
}

@@ -482,6 +574,10 @@ func CloudBrainStop(ctx *context.Context) {
}

task.Status = string(models.JobStopped)
if task.EndTime == 0 {
task.EndTime = timeutil.TimeStampNow()
}
task.ComputeAndSetDuration()
err = models.UpdateJob(task)
if err != nil {
log.Error("UpdateJob(%s) failed:%v", task.JobName, err, ctx.Data["msgID"])
@@ -494,11 +590,12 @@ func CloudBrainStop(ctx *context.Context) {
break
}

ctx.JSON(200, map[string]string{
ctx.JSON(200, map[string]interface{}{
"result_code": resultCode,
"error_msg": errorMsg,
"status": status,
"id": ID,
"StatusOK": 0,
})
}

@@ -575,6 +672,10 @@ func logErrorAndUpdateJobStatus(err error, taskInfo *models.Cloudbrain) {
log.Warn("Failed to stop cloudBrain job:"+taskInfo.JobID, err)
} else {
taskInfo.Status = string(models.JobStopped)
if taskInfo.EndTime == 0 {
taskInfo.EndTime = timeutil.TimeStampNow()
}
taskInfo.ComputeAndSetDuration()
err = models.UpdateJob(taskInfo)
if err != nil {
log.Warn("UpdateJob failed", err)
@@ -746,8 +847,8 @@ func GetRate(ctx *context.Context) {
}
}

func downloadCode(repo *models.Repository, codePath string) error {
if err := git.Clone(repo.RepoPath(), codePath, git.CloneRepoOptions{}); err != nil {
func downloadCode(repo *models.Repository, codePath, branchName string) error {
if err := git.Clone(repo.RepoPath(), codePath, git.CloneRepoOptions{Branch: branchName}); err != nil {
log.Error("Failed to clone repository: %s (%v)", repo.FullName(), err)
return err
}
@@ -945,7 +1046,7 @@ func SyncCloudbrainStatus() {
taskRes, _ := models.ConvertToTaskPod(taskRoles[cloudbrain.SubTaskName].(map[string]interface{}))
task.Status = taskRes.TaskStatuses[0].State
if task.Status != string(models.JobWaiting) {
task.Duration = time.Now().Unix() - taskRes.TaskStatuses[0].StartAt.Unix()
models.ParseAndSetDurationFromCloudBrainOne(jobRes, task)
err = models.UpdateJob(task)
if err != nil {
log.Error("UpdateJob(%s) failed:%v", task.JobName, err)
@@ -966,6 +1067,10 @@ func SyncCloudbrainStatus() {
continue
}
task.Status = string(models.JobStopped)
if task.EndTime == 0 {
task.EndTime = timeutil.TimeStampNow()
}
task.ComputeAndSetDuration()
err = models.UpdateJob(task)
if err != nil {
log.Error("UpdateJob(%s) failed:%v", task.JobName, err)
@@ -984,14 +1089,20 @@ func SyncCloudbrainStatus() {

if result != nil {
task.Status = result.Status

if task.StartTime == 0 && result.Lease.UpdateTime > 0 {
task.StartTime = timeutil.TimeStamp(result.Lease.UpdateTime / 1000)
}
if task.EndTime == 0 && models.IsModelArtsDebugJobTerminal(task.Status) {
task.EndTime = timeutil.TimeStampNow()
}
task.ComputeAndSetDuration()
err = models.UpdateJob(task)
if err != nil {
log.Error("UpdateJob(%s) failed:%v", task.JobName, err)
continue
}
}
} else if task.JobType == string(models.JobTypeTrain) {
} else if task.JobType == string(models.JobTypeTrain) || task.JobType == string(models.JobTypeInference) {
result, err := modelarts.GetTrainJob(task.JobID, strconv.FormatInt(task.VersionID, 10))
if err != nil {
log.Error("GetTrainJob(%s) failed:%v", task.JobName, err)
@@ -1000,16 +1111,160 @@ func SyncCloudbrainStatus() {

if result != nil {
task.Status = modelarts.TransTrainJobStatus(result.IntStatus)
task.Duration = result.Duration
task.Duration = result.Duration / 1000
task.TrainJobDuration = result.TrainJobDuration

if result.Duration != 0 {
task.TrainJobDuration = util.AddZero(result.Duration/3600000) + ":" + util.AddZero(result.Duration%3600000/60000) + ":" + util.AddZero(result.Duration%60000/1000)
if task.StartTime == 0 && result.StartTime > 0 {
task.StartTime = timeutil.TimeStamp(result.StartTime / 1000)
}
task.TrainJobDuration = models.ConvertDurationToStr(task.Duration)
if task.EndTime == 0 && models.IsTrainJobTerminal(task.Status) && task.StartTime > 0 {
task.EndTime = task.StartTime.Add(task.Duration)
}

err = models.UpdateJob(task)
if err != nil {
log.Error("UpdateJob(%s) failed:%v", task.JobName, err)
continue
}
}
} else {
log.Error("task.JobType(%s) is error:%s", task.JobName, task.JobType)
}

} else {
log.Error("task.Type(%s) is error:%d", task.JobName, task.Type)
}
}

return
}

func HandleTaskWithNoDuration(ctx *context.Context) {
log.Info("HandleTaskWithNoDuration start")
count := 0
start := time.Now().Unix()
for {
cloudBrains, err := models.GetStoppedJobWithNoDurationJob()
if err != nil {
log.Error("HandleTaskWithNoTrainJobDuration failed:", err.Error())
break
}
if len(cloudBrains) == 0 {
log.Info("HandleTaskWithNoTrainJobDuration:no task need handle")
break
}
handleNoDurationTask(cloudBrains)
count += len(cloudBrains)
if len(cloudBrains) < 100 {
log.Info("HandleTaskWithNoTrainJobDuration:task less than 100")
break
}
if time.Now().Unix()-start > 600 {
log.Info("HandleTaskWithNoDuration : time out")
ctx.JSON(200, fmt.Sprintf("task stop for time out,count=%d", count))
return
}
}
log.Info("HandleTaskWithNoTrainJobDuration:count=%d", count)
ctx.JSON(200, fmt.Sprintf("success,count=%d", count))
}

func handleNoDurationTask(cloudBrains []*models.Cloudbrain) {
for _, task := range cloudBrains {
time.Sleep(time.Millisecond * 100)
log.Info("Handle job ,%+v", task)
if task.Type == models.TypeCloudBrainOne {
result, err := cloudbrain.GetJob(task.JobID)
if err != nil {
log.Error("GetJob(%s) failed:%v", task.JobName, err)
updateDefaultDuration(task)
continue
}

if result != nil {
if result.Msg != "success" {
updateDefaultDuration(task)
continue
}
jobRes, err := models.ConvertToJobResultPayload(result.Payload)
if err != nil || len(jobRes.TaskRoles) == 0 {
updateDefaultDuration(task)
continue
}
taskRoles := jobRes.TaskRoles
taskRes, err := models.ConvertToTaskPod(taskRoles[cloudbrain.SubTaskName].(map[string]interface{}))
if err != nil || len(taskRes.TaskStatuses) == 0 {
updateDefaultDuration(task)
continue
}
task.Status = taskRes.TaskStatuses[0].State
log.Info("task startTime = %v endTime= %v ,jobId=%d", jobRes.JobStatus.StartTime, jobRes.JobStatus.EndTime, task.ID)
if jobRes.JobStatus.CreatedTime > 0 {
task.StartTime = timeutil.TimeStamp(jobRes.JobStatus.CreatedTime / 1000)
if jobRes.JobStatus.CompletedTime > 0 {
task.EndTime = timeutil.TimeStamp(jobRes.JobStatus.CompletedTime / 1000)
} else {
task.TrainJobDuration = "00:00:00"
task.EndTime = task.UpdatedUnix
}
} else {
task.StartTime = 0
task.EndTime = 0
}

if task.EndTime < task.StartTime {
log.Info("endTime[%v] is less than starTime[%v],jobId=%d", task.EndTime, task.StartTime, task.ID)
st := task.StartTime
task.StartTime = task.EndTime
task.EndTime = st
}
task.Duration = task.EndTime.AsTime().Unix() - task.StartTime.AsTime().Unix()
task.TrainJobDuration = models.ConvertDurationToStr(task.Duration)
err = models.UpdateJob(task)
if err != nil {
log.Error("UpdateJob(%s) failed:%v", task.JobName, err)
}
}
} else if task.Type == models.TypeCloudBrainTwo {
if task.JobType == string(models.JobTypeDebug) {
//result, err := modelarts.GetJob(task.JobID)
result, err := modelarts.GetNotebook2(task.JobID)
if err != nil {
log.Error("GetJob(%s) failed:%v", task.JobName, err)
updateDefaultDuration(task)
continue
}

if result != nil {
task.Status = result.Status
startTime := result.Lease.CreateTime
duration := result.Lease.Duration / 1000
if startTime > 0 {
task.StartTime = timeutil.TimeStamp(startTime / 1000)
task.EndTime = task.StartTime.Add(duration)
}
task.ComputeAndSetDuration()
err = models.UpdateJob(task)
if err != nil {
log.Error("UpdateJob(%s) failed:%v", task.JobName, err)
continue
}
}
} else if task.JobType == string(models.JobTypeTrain) || task.JobType == string(models.JobTypeInference) {
result, err := modelarts.GetTrainJob(task.JobID, strconv.FormatInt(task.VersionID, 10))
if err != nil {
log.Error("GetTrainJob(%s) failed:%v", task.JobName, err)
updateDefaultDuration(task)
continue
}

if result != nil {
startTime := result.StartTime / 1000
if startTime > 0 {
task.StartTime = timeutil.TimeStamp(startTime)
task.EndTime = task.StartTime.Add(result.Duration / 1000)
}
task.ComputeAndSetDuration()
err = models.UpdateJob(task)
if err != nil {
log.Error("UpdateJob(%s) failed:%v", task.JobName, err)
@@ -1024,8 +1279,17 @@ func SyncCloudbrainStatus() {
log.Error("task.Type(%s) is error:%d", task.JobName, task.Type)
}
}
}

return
func updateDefaultDuration(task *models.Cloudbrain) {
log.Info("updateDefaultDuration: taskId=%d", task.ID)
task.StartTime = task.CreatedUnix
task.EndTime = task.UpdatedUnix
task.ComputeAndSetDuration()
err := models.UpdateJob(task)
if err != nil {
log.Error("UpdateJob(%s) failed:%v", task.JobName, err)
}
}

func CloudBrainBenchmarkIndex(ctx *context.Context) {
@@ -1055,13 +1319,19 @@ func CloudBrainBenchmarkIndex(ctx *context.Context) {
for i, task := range ciTasks {
ciTasks[i].CanDel = cloudbrain.CanDeleteJob(ctx, &task.Cloudbrain)
ciTasks[i].Cloudbrain.ComputeResource = task.ComputeResource
var duration int64
if task.Status == string(models.JobRunning) {
duration = time.Now().Unix() - int64(task.Cloudbrain.CreatedUnix)
} else {
duration = int64(task.Cloudbrain.UpdatedUnix) - int64(task.Cloudbrain.CreatedUnix)
if ciTasks[i].TrainJobDuration == "" {
if ciTasks[i].Duration == 0 {
var duration int64
if task.Status == string(models.JobRunning) {
duration = time.Now().Unix() - int64(task.Cloudbrain.CreatedUnix)
} else {
duration = int64(task.Cloudbrain.UpdatedUnix) - int64(task.Cloudbrain.CreatedUnix)
}
ciTasks[i].Duration = duration
}
ciTasks[i].TrainJobDuration = models.ConvertDurationToStr(ciTasks[i].Duration)
}
ciTasks[i].TrainJobDuration = util.AddZero(duration/3600000) + ":" + util.AddZero(duration%3600000/60000) + ":" + util.AddZero(duration%60000/1000)
ciTasks[i].BenchmarkTypeName = ""
if task.BenchmarkTypeID > 0 {
for _, benchmarkType := range GetBenchmarkTypes(ctx).BenchmarkType {
@@ -1278,7 +1548,7 @@ func CloudBrainBenchmarkCreate(ctx *context.Context, form auth.CreateCloudBrainF
}

os.RemoveAll(codePath)
if err := downloadCode(repo, codePath); err != nil {
if err := downloadCode(repo, codePath, cloudbrain.DefaultBranchName); err != nil {
log.Error("downloadCode failed, %v", err, ctx.Data["MsgID"])
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr("system error", tplCloudBrainBenchmarkNew, &form)
@@ -1343,7 +1613,7 @@ func CloudBrainBenchmarkCreate(ctx *context.Context, form auth.CreateCloudBrainF
err = cloudbrain.GenerateTask(ctx, displayJobName, jobName, image, command, childInfo.Attachment, storage.GetMinioPath(jobName, cloudbrain.CodeMountPath+"/"),
storage.GetMinioPath(jobName, cloudbrain.ModelMountPath+"/"),
storage.GetMinioPath(jobName, cloudbrain.BenchMarkMountPath+"/"), storage.GetMinioPath(jobName, cloudbrain.Snn4imagenetMountPath+"/"),
storage.GetMinioPath(jobName, cloudbrain.BrainScoreMountPath+"/"), string(models.JobTypeBenchmark), gpuQueue, form.Description,
storage.GetMinioPath(jobName, cloudbrain.BrainScoreMountPath+"/"), string(models.JobTypeBenchmark), gpuQueue, form.Description, cloudbrain.DefaultBranchName, "", "",
benchmarkTypeID, benchmarkChildTypeID, resourceSpecId)
if err != nil {
cloudBrainNewDataPrepare(ctx)
@@ -1369,10 +1639,66 @@ func BenchmarkDel(ctx *context.Context) {
}
}

func CloudBrainTrainJobNew(ctx *context.Context) {
err := cloudBrainNewDataPrepare(ctx)
if err != nil {
ctx.ServerError("get new train-job info failed", err)
return
}
ctx.HTML(http.StatusOK, tplCloudBrainTrainJobNew)
}

func getTrainJobCommand(form auth.CreateCloudBrainForm) (string, error) {
var command string
bootFile := form.BootFile
params := form.Params

if !strings.HasSuffix(bootFile, ".py") {
log.Error("bootFile(%s) format error", bootFile)
return command, errors.New("bootFile format error")
}

var parameters models.Parameters
var param string
if len(params) != 0 {
err := json.Unmarshal([]byte(params), &parameters)
if err != nil {
log.Error("Failed to Unmarshal params: %s (%v)", params, err)
return command, err
}

for _, parameter := range parameters.Parameter {
param += " --" + parameter.Label + "=" + parameter.Value
}
}

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

return command, nil
}

func CloudBrainTrainJobDel(ctx *context.Context) {
var listType = ctx.Query("listType")
if err := deleteCloudbrainJob(ctx); err != nil {
log.Error("deleteCloudbrainJob failed: %v", err, ctx.Data["msgID"])
ctx.ServerError(err.Error(), err)
return
}

var isAdminPage = ctx.Query("isadminpage")
if ctx.IsUserSiteAdmin() && isAdminPage == "true" {
ctx.Redirect(setting.AppSubURL + "/admin" + "/cloudbrains")
} else {
ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/train-job?listType=" + listType)
}
}

func GetBenchmarkTypes(ctx *context.Context) *models.BenchmarkTypes {
var lang = ctx.Locale.Language()
if benchmarkTypesMap[lang] == nil {
var val = i18n.Tr(lang, BENCHMARK_TYPE_CODE)
//use config
val = setting.BenchmarkTypes
var tempType *models.BenchmarkTypes
if err := json.Unmarshal([]byte(val), &tempType); err != nil {
log.Error("json.Unmarshal BenchmarkTypes(%s) failed:%v", val, err, ctx.Data["MsgID"])


+ 367
- 52
routers/repo/dataset.go View File

@@ -1,7 +1,14 @@
package repo

import (
"encoding/json"
"fmt"
"net/http"
"regexp"
"sort"
"strconv"
"strings"
"unicode/utf8"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth"
@@ -12,9 +19,14 @@ import (
)

const (
tplIndex base.TplName = "repo/datasets/index"
tplIndex base.TplName = "repo/datasets/index"
tplDatasetCreate base.TplName = "repo/datasets/create"
tplDatasetEdit base.TplName = "repo/datasets/edit"
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) {
@@ -84,43 +96,34 @@ func QueryDataSet(ctx *context.Context) []*models.Attachment {
attachments := newFilterPrivateAttachments(ctx, dataset.Attachments, repo)

ctx.Data["SortType"] = ctx.Query("sort")
switch ctx.Query("sort") {
case "newest":
sort.Slice(attachments, func(i, j int) bool {
return attachments[i].CreatedUnix > attachments[j].CreatedUnix
})
case "oldest":
sort.Slice(attachments, func(i, j int) bool {
return attachments[i].CreatedUnix < attachments[j].CreatedUnix
})
default:
ctx.Data["SortType"] = "newest"
sort.Slice(attachments, func(i, j int) bool {
return attachments[i].CreatedUnix > attachments[j].CreatedUnix
})
}

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

return attachments
}

func DatasetIndex(ctx *context.Context) {
log.Info("dataset index 1")
MustEnableDataset(ctx)
ctx.Data["PageIsDataset"] = true

repo := ctx.Repo.Repository

dataset, err := models.GetDatasetByRepo(repo)
ctx.Data["CanWrite"] = ctx.Repo.CanWrite(models.UnitTypeDatasets)
if err != nil {
log.Error("query dataset, not found repo.")
ctx.NotFound("GetDatasetByRepo", err)
log.Warn("query dataset, not found.")
ctx.HTML(200, tplIndex)
return
}
cloudbrainType := -1
if ctx.Query("type") != "" {

if ctx.Query("type") == "" {
log.Error("query dataset, not found param type")
ctx.NotFound("type error", nil)
return
cloudbrainType = ctx.QueryInt("type")
}
err = models.GetDatasetAttachments(ctx.QueryInt("type"), ctx.IsSigned, ctx.User, dataset)
err = models.GetDatasetAttachments(cloudbrainType, ctx.IsSigned, ctx.User, dataset)
if err != nil {
ctx.ServerError("GetDatasetAttachments", err)
return
@@ -128,53 +131,138 @@ func DatasetIndex(ctx *context.Context) {

attachments := newFilterPrivateAttachments(ctx, dataset.Attachments, repo)

ctx.Data["SortType"] = ctx.Query("sort")
switch ctx.Query("sort") {
case "newest":
sort.Slice(attachments, func(i, j int) bool {
return attachments[i].CreatedUnix > attachments[j].CreatedUnix
})
case "oldest":
sort.Slice(attachments, func(i, j int) bool {
return attachments[i].CreatedUnix < attachments[j].CreatedUnix
})
default:
ctx.Data["SortType"] = "newest"
sort.Slice(attachments, func(i, j int) bool {
return attachments[i].CreatedUnix > attachments[j].CreatedUnix
})
sort.Slice(attachments, func(i, j int) bool {
return attachments[i].CreatedUnix > attachments[j].CreatedUnix
})

page := ctx.QueryInt("page")
if page <= 0 {
page = 1
}
pagesize := ctx.QueryInt("pagesize")
if pagesize <= 0 {
pagesize = 10
}
pager := context.NewPagination(len(attachments), pagesize, page, 5)

pageAttachments := getPageAttachments(attachments, page, pagesize)

//load attachment creator
for _, attachment := range pageAttachments {
uploader, _ := models.GetUserByID(attachment.UploaderID)
attachment.Uploader = uploader
}

ctx.Data["Page"] = pager

ctx.Data["PageIsDataset"] = true
ctx.Data["Title"] = ctx.Tr("dataset.show_dataset")
ctx.Data["Link"] = ctx.Repo.RepoLink + "/datasets"
ctx.Data["dataset"] = dataset
ctx.Data["Attachments"] = attachments
ctx.Data["Attachments"] = pageAttachments
ctx.Data["IsOwner"] = true
ctx.Data["StoreType"] = setting.Attachment.StoreType
ctx.Data["Type"] = ctx.QueryInt("type")
ctx.Data["Type"] = cloudbrainType

renderAttachmentSettings(ctx)

ctx.HTML(200, tplIndex)
}

func getPageAttachments(attachments []*models.Attachment, page int, pagesize int) []*models.Attachment {
begin := (page - 1) * pagesize
end := (page) * pagesize

if begin > len(attachments)-1 {
return nil
}
if end > len(attachments)-1 {
return attachments[begin:]
} else {
return attachments[begin:end]
}

}

func CreateDataset(ctx *context.Context) {

MustEnableDataset(ctx)
ctx.Data["PageIsDataset"] = true

ctx.HTML(200, tplDatasetCreate)
}

func EditDataset(ctx *context.Context) {

MustEnableDataset(ctx)
ctx.Data["PageIsDataset"] = true
datasetId, _ := strconv.ParseInt(ctx.Params(":id"), 10, 64)

dataset, _ := models.GetDatasetByID(datasetId)
if dataset == nil {
ctx.Error(http.StatusNotFound, "")
return
}
ctx.Data["Dataset"] = dataset

ctx.HTML(200, tplDatasetEdit)
}

func CreateDatasetPost(ctx *context.Context, form auth.CreateDatasetForm) {

dataset := &models.Dataset{}

if !titlePattern.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")))
return
}

dataset.RepoID = ctx.Repo.Repository.ID
dataset.UserID = ctx.User.ID
dataset.Category = form.Category
dataset.Task = form.Task
dataset.Title = form.Title
dataset.License = form.License
dataset.Description = form.Description
dataset.DownloadTimes = 0
if ctx.Repo.Repository.IsPrivate {
dataset.Status = 0
} else {
dataset.Status = 1
}
err := models.CreateDataset(dataset)
if err != nil {
log.Error("fail to create dataset", err)
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.create_dataset_fail")))
} else {
ctx.JSON(http.StatusOK, models.BaseOKMessage)
}

}

func EditDatasetPost(ctx *context.Context, form auth.EditDatasetForm) {
ctx.Data["PageIsDataset"] = true

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

if !titlePattern.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")))
return
}

rel, err := models.GetDatasetByID(form.ID)
ctx.Data["dataset"] = rel

if err != nil {
ctx.ServerError("GetDataset", err)
return
}

if ctx.HasError() {
ctx.Data["Error"] = true
ctx.HTML(200, tplIndex)
log.Error("failed to query dataset", err)
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.query_dataset_fail")))
return
}

@@ -184,9 +272,236 @@ func EditDatasetPost(ctx *context.Context, form auth.EditDatasetForm) {
rel.Task = form.Task
rel.License = form.License
if err = models.UpdateDataset(models.DefaultDBContext(), rel); err != nil {
ctx.Data["Error"] = true
ctx.HTML(200, tplIndex)
log.Error("%v", err)
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("dataset.query_dataset_fail")))
}
ctx.Redirect(ctx.Repo.RepoLink + "/datasets?type=" + form.Type)
ctx.JSON(http.StatusOK, models.BaseOKMessage)
}

func DatasetAction(ctx *context.Context) {
var err error
datasetId, _ := strconv.ParseInt(ctx.Params(":id"), 10, 64)
switch ctx.Params(":action") {
case "star":
err = models.StarDataset(ctx.User.ID, datasetId, true)
case "unstar":
err = models.StarDataset(ctx.User.ID, datasetId, 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)
}

}

func CurrentRepoDataset(ctx *context.Context) {
page := ctx.QueryInt("page")
cloudbrainType := ctx.QueryInt("type")
keyword := strings.Trim(ctx.Query("q"), " ")

repo := ctx.Repo.Repository
var datasetIDs []int64
dataset, err := models.GetDatasetByRepo(repo)
if err != nil {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("GetDatasetByRepo failed", err)))
return
}
datasetIDs = append(datasetIDs, dataset.ID)
datasets, count, err := models.Attachments(&models.AttachmentsOptions{
ListOptions: models.ListOptions{
Page: page,
PageSize: setting.UI.DatasetPagingNum,
},
Keyword: keyword,
NeedDatasetIDs: true,
DatasetIDs: datasetIDs,
Type: cloudbrainType,
NeedIsPrivate: false,
JustNeedZipFile: true,
NeedRepoInfo: true,
})
if err != nil {
ctx.ServerError("datasets", err)
return
}

data, err := json.Marshal(datasets)
if err != nil {
log.Error("json.Marshal failed:", err.Error())
ctx.JSON(200, map[string]string{
"result_code": "-1",
"error_msg": err.Error(),
"data": "",
})
return
}
ctx.JSON(200, map[string]string{
"result_code": "0",
"data": string(data),
"count": strconv.FormatInt(count, 10),
})
}

func MyDatasets(ctx *context.Context) {
page := ctx.QueryInt("page")
cloudbrainType := ctx.QueryInt("type")
keyword := strings.Trim(ctx.Query("q"), " ")

uploaderID := ctx.User.ID
datasets, count, err := models.Attachments(&models.AttachmentsOptions{
ListOptions: models.ListOptions{
Page: page,
PageSize: setting.UI.DatasetPagingNum,
},
Keyword: keyword,
NeedDatasetIDs: false,
UploaderID: uploaderID,
Type: cloudbrainType,
NeedIsPrivate: false,
JustNeedZipFile: true,
NeedRepoInfo: true,
})
if err != nil {
ctx.ServerError("datasets", err)
return
}

data, err := json.Marshal(datasets)
if err != nil {
log.Error("json.Marshal failed:", err.Error())
ctx.JSON(200, map[string]string{
"result_code": "-1",
"error_msg": err.Error(),
"data": "",
})
return
}
ctx.JSON(200, map[string]string{
"result_code": "0",
"data": string(data),
"count": strconv.FormatInt(count, 10),
})
}

func PublicDataset(ctx *context.Context) {
page := ctx.QueryInt("page")
cloudbrainType := ctx.QueryInt("type")
keyword := strings.Trim(ctx.Query("q"), " ")

datasets, count, err := models.Attachments(&models.AttachmentsOptions{
ListOptions: models.ListOptions{
Page: page,
PageSize: setting.UI.DatasetPagingNum,
},
Keyword: keyword,
NeedDatasetIDs: false,
NeedIsPrivate: true,
IsPrivate: false,
Type: cloudbrainType,
JustNeedZipFile: true,
NeedRepoInfo: true,
})
if err != nil {
ctx.ServerError("datasets", err)
return
}

data, err := json.Marshal(datasets)
if err != nil {
log.Error("json.Marshal failed:", err.Error())
ctx.JSON(200, map[string]string{
"result_code": "-1",
"error_msg": err.Error(),
"data": "",
})
return
}
ctx.JSON(200, map[string]string{
"result_code": "0",
"data": string(data),
"count": strconv.FormatInt(count, 10),
})
}

func MyFavoriteDataset(ctx *context.Context) {
page := ctx.QueryInt("page")
cloudbrainType := ctx.QueryInt("type")
keyword := strings.Trim(ctx.Query("q"), " ")
var datasetIDs []int64
datasetStars, err := models.GetDatasetStarByUser(ctx.User)
if err != nil {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("GetDatasetStarByUser failed", err)))
log.Error("GetDatasetStarByUser failed:", err.Error())
ctx.JSON(200, map[string]string{
"result_code": "-1",
"error_msg": err.Error(),
"data": "",
})
return
}
for i, _ := range datasetStars {
datasetIDs = append(datasetIDs, datasetStars[i].DatasetID)
}

datasets, count, err := models.Attachments(&models.AttachmentsOptions{
ListOptions: models.ListOptions{
Page: page,
PageSize: setting.UI.DatasetPagingNum,
},
Keyword: keyword,
NeedDatasetIDs: true,
DatasetIDs: datasetIDs,
NeedIsPrivate: true,
IsPrivate: false,
Type: cloudbrainType,
JustNeedZipFile: true,
NeedRepoInfo: true,
})
if err != nil {
ctx.ServerError("datasets", err)
return
}

data, err := json.Marshal(datasets)
if err != nil {
log.Error("json.Marshal failed:", err.Error())
ctx.JSON(200, map[string]string{
"result_code": "-1",
"error_msg": err.Error(),
"data": "",
})
return
}
ctx.JSON(200, map[string]string{
"result_code": "0",
"data": string(data),
"count": strconv.FormatInt(count, 10),
})

}

func GetDatasetStatus(ctx *context.Context) {

var (
err error
)

UUID := ctx.Params(":uuid")
attachment, err := models.GetAttachmentByUUID(UUID)
if err != nil {
log.Error("GetDatasetStarByUser failed:", err.Error())
ctx.JSON(200, map[string]string{
"result_code": "-1",
"error_msg": err.Error(),
"data": "",
})
return
}

ctx.JSON(200, map[string]string{
"result_code": "0",
"UUID": UUID,
"AttachmentStatus": fmt.Sprint(attachment.DecompressState),
})
}

+ 141
- 51
routers/repo/modelarts.go View File

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

import (
"archive/zip"
"code.gitea.io/gitea/modules/notification"
"code.gitea.io/gitea/modules/timeutil"
"encoding/json"
"errors"
"io"
@@ -46,20 +48,26 @@ const (
)

func DebugJobIndex(ctx *context.Context) {
debugListType := ctx.Query("debugListType")
ctx.Data["ListType"] = debugListType
listType := ctx.Query("debugListType")
ctx.Data["ListType"] = listType
MustEnableCloudbrain(ctx)
repo := ctx.Repo.Repository
page := ctx.QueryInt("page")
if page <= 0 {
page = 1
}
debugType := modelarts.DebugType
typeCloudBrain := models.TypeCloudBrainAll
jobTypeNot := false
if debugListType == models.GPUResource {
debugType = models.TypeCloudBrainOne
} else if debugListType == models.NPUResource {
debugType = models.TypeCloudBrainTwo
if listType == models.GPUResource {
typeCloudBrain = models.TypeCloudBrainOne
} else if listType == models.NPUResource {
typeCloudBrain = models.TypeCloudBrainTwo
} else if listType == models.AllResource {
typeCloudBrain = models.TypeCloudBrainAll
} else {
log.Error("listType(%s) error", listType)
ctx.ServerError("listType error", errors.New("listType error"))
return
}

var jobTypes []string
@@ -70,7 +78,7 @@ func DebugJobIndex(ctx *context.Context) {
PageSize: setting.UI.IssuePagingNum,
},
RepoID: repo.ID,
Type: debugType,
Type: typeCloudBrain,
JobTypeNot: jobTypeNot,
JobTypes: jobTypes,
})
@@ -92,7 +100,7 @@ func DebugJobIndex(ctx *context.Context) {
ctx.Data["Tasks"] = ciTasks
ctx.Data["CanCreate"] = cloudbrain.CanCreateOrDebugJob(ctx)
ctx.Data["RepoIsEmpty"] = repo.IsEmpty
ctx.Data["debugListType"] = debugListType
ctx.Data["debugListType"] = listType
ctx.HTML(200, tplDebugJobIndex)
}

@@ -133,6 +141,8 @@ func notebookNewDataPrepare(ctx *context.Context) error {
}
ctx.Data["flavors"] = modelarts.FlavorInfos.FlavorInfo

ctx.Data["cloudbraintype"] = models.TypeCloudBrainTwo

return nil
}

@@ -407,16 +417,46 @@ func NotebookManage(ctx *context.Context) {
break
}

task.Status = res.Status
err = models.UpdateJob(task)
if err != nil {
log.Error("UpdateJob(%s) failed:%v", task.JobName, err.Error(), ctx.Data["MsgID"])
resultCode = "-1"
errorMsg = "system error"
break
}
status = res.Status
if action == models.ActionStart {
newTask := &models.Cloudbrain{
Status: status,
UserID: task.UserID,
RepoID: task.RepoID,
JobID: task.JobID,
JobName: task.JobName,
DisplayJobName: task.DisplayJobName,
JobType: task.JobType,
Type: task.Type,
Uuid: task.Uuid,
Image: task.Image,
ComputeResource: task.ComputeResource,
Description: task.Description,
}

status = task.Status
err = models.RestartCloudbrain(task, newTask)
if err != nil {
log.Error("RestartCloudbrain(%s) failed:%v", task.JobName, err.Error(), ctx.Data["MsgID"])
resultCode = "-1"
errorMsg = "system error"
break
}
ID = strconv.FormatInt(newTask.ID, 10)
notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, ID, task.DisplayJobName, models.ActionCreateDebugNPUTask)
} else {
task.Status = res.Status
if task.EndTime == 0 && models.IsModelArtsDebugJobTerminal(task.Status) {
task.EndTime = timeutil.TimeStampNow()
}
task.ComputeAndSetDuration()
err = models.UpdateJob(task)
if err != nil {
log.Error("UpdateJob(%s) failed:%v", task.JobName, err.Error(), ctx.Data["MsgID"])
resultCode = "-1"
errorMsg = "system error"
break
}
}

break
}
@@ -473,6 +513,26 @@ func TrainJobIndex(ctx *context.Context) {
page = 1
}

listType := ctx.Query("listType")
if len(listType) == 0 {
listType = models.AllResource
}
ctx.Data["ListType"] = listType

typeCloudBrain := models.TypeCloudBrainAll
if listType == models.GPUResource {
typeCloudBrain = models.TypeCloudBrainOne
} else if listType == models.NPUResource {
typeCloudBrain = models.TypeCloudBrainTwo
} else if listType == models.AllResource {
typeCloudBrain = models.TypeCloudBrainAll
}
//else {
// log.Error("listType(%s) error", listType)
// ctx.ServerError("listType error", errors.New("listType error"))
// return
//}

var jobTypes []string
jobTypes = append(jobTypes, string(models.JobTypeTrain))
tasks, count, err := models.Cloudbrains(&models.CloudbrainsOptions{
@@ -481,7 +541,7 @@ func TrainJobIndex(ctx *context.Context) {
PageSize: setting.UI.IssuePagingNum,
},
RepoID: repo.ID,
Type: models.TypeCloudBrainTwo,
Type: typeCloudBrain,
JobTypeNot: false,
JobTypes: jobTypes,
IsLatestVersion: modelarts.IsLatestVersion,
@@ -494,11 +554,16 @@ func TrainJobIndex(ctx *context.Context) {
for i, task := range tasks {
tasks[i].CanDel = cloudbrain.CanDeleteJob(ctx, &task.Cloudbrain)
tasks[i].CanModify = cloudbrain.CanModifyJob(ctx, &task.Cloudbrain)
tasks[i].ComputeResource = models.NPUResource
if task.Cloudbrain.Type == models.TypeCloudBrainOne {
tasks[i].ComputeResource = models.GPUResource
} else if task.Cloudbrain.Type == models.TypeCloudBrainTwo {
tasks[i].ComputeResource = models.NPUResource
}
}

pager := context.NewPagination(int(count), setting.UI.IssuePagingNum, page, 5)
pager.SetDefaultParams(ctx)
pager.AddParam(ctx, "listType", "ListType")
ctx.Data["Page"] = pager

ctx.Data["PageIsCloudBrain"] = true
@@ -580,6 +645,7 @@ func trainJobNewDataPrepare(ctx *context.Context) error {
return err
}
ctx.Data["config_list"] = configList.ParaConfigs
ctx.Data["cloudbraintype"] = models.TypeCloudBrainTwo

return nil
}
@@ -746,6 +812,7 @@ func trainJobNewVersionDataPrepare(ctx *context.Context) error {
ctx.Data["uuid"] = task.Uuid
ctx.Data["flavor_code"] = task.FlavorCode
ctx.Data["engine_id"] = task.EngineID
ctx.Data["cloudbraintype"] = models.TypeCloudBrainTwo

configList, err := getConfigList(modelarts.PerPage, 1, modelarts.SortByCreateTime, "desc", "", modelarts.ConfigTypeCustom)
if err != nil {
@@ -953,17 +1020,9 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm)
return
}

//todo: del local code?

var parameters models.Parameters
param := make([]models.Parameter, 0)
param = append(param, models.Parameter{
Label: modelarts.TrainUrl,
Value: outputObsPath,
}, models.Parameter{
Label: modelarts.DataUrl,
Value: dataPath,
})
existDeviceTarget := false
if len(params) != 0 {
err := json.Unmarshal([]byte(params), &parameters)
if err != nil {
@@ -974,6 +1033,9 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm)
}

for _, parameter := range parameters.Parameter {
if parameter.Label == modelarts.DeviceTarget {
existDeviceTarget = true
}
if parameter.Label != modelarts.TrainUrl && parameter.Label != modelarts.DataUrl {
param = append(param, models.Parameter{
Label: parameter.Label,
@@ -982,9 +1044,22 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm)
}
}
}
if !existDeviceTarget {
param = append(param, models.Parameter{
Label: modelarts.DeviceTarget,
Value: modelarts.Ascend,
})
}

//save param config
if isSaveParam == "on" {
saveparams := append(param, models.Parameter{
Label: modelarts.TrainUrl,
Value: outputObsPath,
}, models.Parameter{
Label: modelarts.DataUrl,
Value: dataPath,
})
if form.ParameterTemplateName == "" {
log.Error("ParameterTemplateName is empty")
trainJobNewDataPrepare(ctx)
@@ -1006,7 +1081,7 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm)
EngineID: int64(engineID),
LogUrl: logObsPath,
PoolID: poolID,
Parameter: param,
Parameter: saveparams,
})

if err != nil {
@@ -1032,7 +1107,7 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm)
LogUrl: logObsPath,
PoolID: poolID,
Uuid: uuid,
Parameters: parameters.Parameter,
Parameters: param,
CommitID: commitID,
IsLatestVersion: isLatestVersion,
BranchName: branch_name,
@@ -1168,13 +1243,7 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ

var parameters models.Parameters
param := make([]models.Parameter, 0)
param = append(param, models.Parameter{
Label: modelarts.TrainUrl,
Value: outputObsPath,
}, models.Parameter{
Label: modelarts.DataUrl,
Value: dataPath,
})
existDeviceTarget := true
if len(params) != 0 {
err := json.Unmarshal([]byte(params), &parameters)
if err != nil {
@@ -1183,8 +1252,10 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ
ctx.RenderWithErr("运行参数错误", tplModelArtsTrainJobVersionNew, &form)
return
}

for _, parameter := range parameters.Parameter {
if parameter.Label == modelarts.DeviceTarget {
existDeviceTarget = true
}
if parameter.Label != modelarts.TrainUrl && parameter.Label != modelarts.DataUrl {
param = append(param, models.Parameter{
Label: parameter.Label,
@@ -1193,9 +1264,22 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ
}
}
}
if !existDeviceTarget {
param = append(param, models.Parameter{
Label: modelarts.DeviceTarget,
Value: modelarts.Ascend,
})
}

//save param config
if isSaveParam == "on" {
saveparams := append(param, models.Parameter{
Label: modelarts.TrainUrl,
Value: outputObsPath,
}, models.Parameter{
Label: modelarts.DataUrl,
Value: dataPath,
})
if form.ParameterTemplateName == "" {
log.Error("ParameterTemplateName is empty")
versionErrorDataPrepare(ctx, form)
@@ -1217,7 +1301,7 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ
EngineID: int64(engineID),
LogUrl: logObsPath,
PoolID: poolID,
Parameter: parameters.Parameter,
Parameter: saveparams,
})

if err != nil {
@@ -1228,12 +1312,6 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ
}
}

if err != nil {
log.Error("getFlavorNameByEngineID(%s) failed:%v", engineID, err.Error())
ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobVersionNew, &form)
return
}

task, err := models.GetCloudbrainByJobIDAndVersionName(jobID, PreVersionName)
if err != nil {
log.Error("GetCloudbrainByJobIDAndVersionName(%s) failed:%v", jobID, err.Error())
@@ -1257,7 +1335,7 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ
PoolID: poolID,
Uuid: uuid,
Params: form.Params,
Parameters: parameters.Parameter,
Parameters: param,
PreVersionId: task.VersionID,
CommitID: commitID,
BranchName: branch_name,
@@ -1535,6 +1613,7 @@ func trainJobGetLog(jobID string) (*models.GetTrainJobLogFileNamesResult, *model

func TrainJobDel(ctx *context.Context) {
var jobID = ctx.Params(":jobid")
var listType = ctx.Query("listType")
repo := ctx.Repo.Repository

var jobTypes []string
@@ -1576,12 +1655,13 @@ func TrainJobDel(ctx *context.Context) {
if ctx.IsUserSiteAdmin() && isAdminPage == "true" {
ctx.Redirect(setting.AppSubURL + "/admin" + "/cloudbrains")
} else {
ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/train-job")
ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/train-job?listType=" + listType)
}
}

func TrainJobStop(ctx *context.Context) {
var jobID = ctx.Params(":jobid")
var listType = ctx.Query("listType")
task := ctx.Cloudbrain

_, err := modelarts.StopTrainJob(jobID, strconv.FormatInt(task.VersionID, 10))
@@ -1591,7 +1671,7 @@ func TrainJobStop(ctx *context.Context) {
return
}

ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/train-job")
ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/train-job?listType=" + listType)
}

func canUserCreateTrainJob(uid int64) (bool, error) {
@@ -1782,7 +1862,6 @@ func InferenceJobCreate(ctx *context.Context, form auth.CreateModelArtsInference
return
}

//todo: del local code?
var parameters models.Parameters
param := make([]models.Parameter, 0)
param = append(param, models.Parameter{
@@ -1792,6 +1871,7 @@ func InferenceJobCreate(ctx *context.Context, form auth.CreateModelArtsInference
Label: modelarts.CkptUrl,
Value: "s3:/" + ckptUrl,
})
existDeviceTarget := false
if len(params) != 0 {
err := json.Unmarshal([]byte(params), &parameters)
if err != nil {
@@ -1802,6 +1882,9 @@ func InferenceJobCreate(ctx *context.Context, form auth.CreateModelArtsInference
}

for _, parameter := range parameters.Parameter {
if parameter.Label == modelarts.DeviceTarget {
existDeviceTarget = true
}
if parameter.Label != modelarts.TrainUrl && parameter.Label != modelarts.DataUrl {
param = append(param, models.Parameter{
Label: parameter.Label,
@@ -1810,6 +1893,12 @@ func InferenceJobCreate(ctx *context.Context, form auth.CreateModelArtsInference
}
}
}
if !existDeviceTarget {
param = append(param, models.Parameter{
Label: modelarts.DeviceTarget,
Value: modelarts.Ascend,
})
}

req := &modelarts.GenerateInferenceJobReq{
JobName: jobName,
@@ -1977,6 +2066,7 @@ func inferenceJobNewDataPrepare(ctx *context.Context) error {
New: MODEL_LATEST,
})
ctx.Data["MODEL_COUNT"] = model_count
ctx.Data["cloudbraintype"] = models.TypeCloudBrainTwo

return nil
}
@@ -2246,7 +2336,7 @@ func SetJobCount(ctx *context.Context) {
repoId := ctx.Repo.Repository.ID
_, jobCount, err := models.Cloudbrains(&models.CloudbrainsOptions{
RepoID: repoId,
Type: modelarts.DebugType,
Type: models.TypeCloudBrainAll,
})
if err != nil {
ctx.ServerError("Get job faild:", err)


+ 0
- 4
routers/repo/setting.go View File

@@ -245,10 +245,6 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) {
// This section doesn't require repo_name/RepoName to be set in the form, don't show it
// as an error on the UI for this action
ctx.Data["Err_RepoName"] = nil
if err := models.CreateDefaultDatasetToRepo(repo); err != nil {
ctx.ServerError("CreateDefaultDatasetToRepo", err)
return
}

if form.EnableDataset && !models.UnitTypeDatasets.UnitGlobalDisabled() {
units = append(units, models.RepoUnit{


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

@@ -323,6 +323,9 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/action/notification", routers.ActionNotification)
m.Get("/recommend/org", routers.RecommendOrgFromPromote)
m.Get("/recommend/repo", routers.RecommendRepoFromPromote)
m.Post("/all/search/", routers.Search)
m.Get("/all/search/", routers.EmptySearch)
m.Get("/all/dosearch/", routers.SearchApi)
m.Get("/home/term", routers.HomeTerm)
m.Group("/explore", func() {
m.Get("", func(ctx *context.Context) {
@@ -587,6 +590,8 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/delete", repo.DeleteAttachment)
m.Get("/get_pre_url", repo.GetPresignedPutObjectURL)
m.Post("/add", repo.AddAttachment)

m.Post("/edit", bindIgnErr(auth.EditAttachmentForm{}), repo.EditAttachment)
m.Post("/private", repo.UpdatePublicAttachment)
m.Get("/get_chunks", repo.GetSuccessChunks)
m.Get("/new_multipart", repo.NewMultipart)
@@ -979,7 +984,24 @@ func RegisterRoutes(m *macaron.Macaron) {

m.Group("/datasets", func() {
m.Get("", reqRepoDatasetReader, repo.DatasetIndex)
m.Post("", reqRepoDatasetWriter, bindIgnErr(auth.EditDatasetForm{}), repo.EditDatasetPost)
m.Put("/:id/:action", reqRepoDatasetReader, repo.DatasetAction)
m.Get("/create", reqRepoDatasetWriter, repo.CreateDataset)
m.Post("/create", reqRepoDatasetWriter, bindIgnErr(auth.CreateDatasetForm{}), repo.CreateDatasetPost)
m.Get("/edit/:id", reqRepoDatasetWriter, repo.EditDataset)
m.Post("/edit", reqRepoDatasetWriter, bindIgnErr(auth.EditDatasetForm{}), repo.EditDatasetPost)
m.Get("/current_repo", repo.CurrentRepoDataset)
m.Get("/my_datasets", repo.MyDatasets)
m.Get("/public_datasets", repo.PublicDataset)
m.Get("/my_favorite", repo.MyFavoriteDataset)

m.Group("/status", func() {
m.Get("/:uuid", repo.GetDatasetStatus)
})

m.Group("/attachments", func() {
m.Get("/upload", repo.UploadAttachmentUI)
m.Get("/edit/:id", repo.EditAttachmentUI)
}, reqSignIn)

m.Group("/dirs", func() {
m.Get("/:uuid", reqRepoDatasetReader, repo.DirIndex)
@@ -1017,6 +1039,19 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/create", reqWechatBind, reqRepoCloudBrainWriter, bindIgnErr(auth.CreateCloudBrainForm{}), repo.CloudBrainBenchmarkCreate)
m.Get("/get_child_types", repo.GetChildTypes)
})

m.Group("/train-job", func() {
m.Group("/:jobid", func() {
m.Get("", reqRepoCloudBrainReader, repo.CloudBrainTrainJobShow)
m.Post("/del", cloudbrain.AdminOrOwnerOrJobCreaterRightForTrain, repo.CloudBrainTrainJobDel)
//m.Get("/models", reqRepoCloudBrainReader, repo.CloudBrainShowModels)
m.Get("/download_model", cloudbrain.AdminOrOwnerOrJobCreaterRightForTrain, repo.CloudBrainDownloadModel)
//m.Get("/create_version", reqWechatBind, cloudbrain.AdminOrJobCreaterRightForTrain, repo.TrainJobNewVersion)
//m.Post("/create_version", reqWechatBind, cloudbrain.AdminOrJobCreaterRightForTrain, bindIgnErr(auth.CreateModelArtsTrainJobForm{}), repo.TrainJobCreateVersion)
})
m.Get("/create", reqWechatBind, reqRepoCloudBrainWriter, repo.CloudBrainTrainJobNew)
m.Post("/create", reqWechatBind, reqRepoCloudBrainWriter, bindIgnErr(auth.CreateCloudBrainForm{}), repo.CloudBrainCreate)
})
}, context.RepoRef())
m.Group("/modelmanage", func() {
m.Post("/create_model", reqRepoModelManageWriter, repo.SaveModel)


+ 1190
- 0
routers/search.go
File diff suppressed because it is too large
View File


+ 1
- 1
services/socketwrap/clientManager.go View File

@@ -10,7 +10,7 @@ import (
"github.com/elliotchance/orderedmap"
)

var opTypes = []int{1, 2, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 22, 23, 25, 26, 27, 28, 29, 30}
var opTypes = []int{1, 2, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 22, 23, 25, 26, 27, 28, 29, 30, 31}

type ClientsManager struct {
Clients *orderedmap.OrderedMap


+ 3
- 3
templates/base/head.tmpl View File

@@ -215,10 +215,10 @@ var _hmt = _hmt || [];
localStorage.setItem("isCloseNotice",true)
}
function isShowNotice(){
var current_notice = localStorage.getItem("notice")
var current_notice = localStorage.getItem("notices")

if (current_notice != "{{.notice.CommitId}}"){
localStorage.setItem('notice',"{{.notice.CommitId}}");
if (current_notice != "{{.notices.CommitId}}"){
localStorage.setItem('notices',"{{.notices.CommitId}}");
isNewNotice=true;
localStorage.setItem("isCloseNotice",false)
}else{


+ 3
- 3
templates/base/head_fluid.tmpl View File

@@ -216,10 +216,10 @@ var _hmt = _hmt || [];
localStorage.setItem("isCloseNotice",true)
}
function isShowNotice(){
var current_notice = localStorage.getItem("notice")
var current_notice = localStorage.getItem("notices")

if (current_notice != "{{.notice.CommitId}}"){
localStorage.setItem('notice',"{{.notice.CommitId}}");
if (current_notice != "{{.notices.CommitId}}"){
localStorage.setItem('notices',"{{.notices.CommitId}}");
isNewNotice=true;
localStorage.setItem("isCloseNotice",false)
}else{


+ 3
- 3
templates/base/head_home.tmpl View File

@@ -220,10 +220,10 @@ var _hmt = _hmt || [];
localStorage.setItem("isCloseNotice",true)
}
function isShowNotice(){
var current_notice = localStorage.getItem("notice")
var current_notice = localStorage.getItem("notices")

if (current_notice != "{{.notice.CommitId}}"){
localStorage.setItem('notice',"{{.notice.CommitId}}");
if (current_notice != "{{.notices.CommitId}}"){
localStorage.setItem('notices',"{{.notices.CommitId}}");
isNewNotice=true;
localStorage.setItem("isCloseNotice",false)
}else{


+ 4
- 4
templates/base/head_navbar.tmpl View File

@@ -95,9 +95,9 @@

{{if .IsSigned}}
<div class="right stackable menu">
<form class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty" style="margin:auto" action="/explore/repos">
<form id="searchForm" class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty" style="margin:auto" action="/all/search/" method="post">
<div class="ui fluid action input" style="background:#363840 ;border-radius: 5px;width: 200px;height:30px;border: #888888 solid 1px;">
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search_pro"}}..."
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search"}}..."
style="transition: background-color 5000s ease-in-out 0s;-webkit-text-fill-color:#888888;background:#363840 ;color:#888888;border: none;outline: none;">
<input type="hidden" name="tab" value="{{$.TabName}}">
<input type="hidden" name="sort" value="hot">
@@ -199,9 +199,9 @@
<!--a class="item" target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io">{{.i18n.Tr "help"}}</a-->
<div class="right stackable menu">
<form class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty" style="margin:auto" action="/explore/repos">
<form id="searchForm" class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty" style="margin:auto" action="/all/search/" method="post">
<div class="ui fluid action input" style="background:#363840 ;border-radius: 5px;width: 200px;height:30px;border: #888888 solid 1px;">
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search_pro"}}..."
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search"}}..."
style="transition: background-color 5000s ease-in-out 0s;-webkit-text-fill-color:#888888;background:#363840 ;color:#888888;border: none;outline: none;">
<input type="hidden" name="tab" value="{{$.TabName}}">
<input type="hidden" name="sort" value="hot">


+ 4
- 4
templates/base/head_navbar_fluid.tmpl View File

@@ -93,9 +93,9 @@

{{if .IsSigned}}
<div class="right stackable menu">
<form class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty" style="margin:auto" action="/explore/repos">
<form id="searchForm" class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty" style="margin:auto" action="/all/search/" method="post">
<div class="ui fluid action input" style="background:#363840 ;border-radius: 5px;width: 200px;height:30px;border: #888888 solid 1px;">
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search_pro"}}..."
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search"}}..."
style="transition: background-color 5000s ease-in-out 0s;-webkit-text-fill-color:#888888;background:#363840 ;color:#888888;border: none;outline: none;">
<input type="hidden" name="tab" value="{{$.TabName}}">
<input type="hidden" name="sort" value="{{$.SortType}}">
@@ -196,9 +196,9 @@

<!--a class="item" target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io">{{.i18n.Tr "help"}}</a-->
<div class="right stackable menu">
<form class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty" style="margin:auto" action="/explore/repos">
<form id="searchForm" class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty" style="margin:auto" action="/all/search/" method="post">
<div class="ui fluid action input" style="background:#363840 ;border-radius: 5px;width: 200px;height:30px;border: #888888 solid 1px;">
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search_pro"}}..."
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search"}}..."
style="transition: background-color 5000s ease-in-out 0s;-webkit-text-fill-color:#888888;background:#363840 ;color:#888888;border: none;outline: none;">
<input type="hidden" name="tab" value="{{$.TabName}}">
<input type="hidden" name="sort" value="{{$.SortType}}">


+ 4
- 4
templates/base/head_navbar_pro.tmpl View File

@@ -96,9 +96,9 @@

{{if .IsSigned}}
<div class="right stackable menu">
<form class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty" style="margin:auto" action="/explore/repos">
<form id="searchForm" class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty" style="margin:auto" action="/all/search/" method="post">
<div class="ui fluid action input" style="background:#363840 ;border-radius: 5px;width: 200px;height:30px;border: #888888 solid 1px;">
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search_pro"}}..."
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search"}}..."
style="transition: background-color 5000s ease-in-out 0s;-webkit-text-fill-color:#888888;background:#363840 ;color:#888888;border: none;outline: none;">
<input type="hidden" name="tab" value="{{$.TabName}}">
<input type="hidden" name="sort" value="{{$.SortType}}">
@@ -199,9 +199,9 @@

<!--a class="item" target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io">{{.i18n.Tr "help"}}</a-->
<div class="right stackable menu">
<form class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty" style="margin:auto" action="/explore/repos">
<form id="searchForm" class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty" style="margin:auto" action="/all/search/" method="post">
<div class="ui fluid action input" style="background:#363840 ;border-radius: 5px;width: 200px;height:30px;border: #888888 solid 1px;">
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search_pro"}}..."
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search"}}..."
style="transition: background-color 5000s ease-in-out 0s;-webkit-text-fill-color:#888888;background:#363840 ;color:#888888;border: none;outline: none;">
<input type="hidden" name="tab" value="{{$.TabName}}">
<input type="hidden" name="sort" value="{{$.SortType}}">


+ 3
- 3
templates/base/head_pro.tmpl View File

@@ -217,10 +217,10 @@ var _hmt = _hmt || [];
localStorage.setItem("isCloseNotice",true)
}
function isShowNotice(){
var current_notice = localStorage.getItem("notice")
var current_notice = localStorage.getItem("notices")

if (current_notice != "{{.notice.CommitId}}"){
localStorage.setItem('notice',"{{.notice.CommitId}}");
if (current_notice != "{{.notices.CommitId}}"){
localStorage.setItem('notices',"{{.notices.CommitId}}");
isNewNotice=true;
localStorage.setItem("isCloseNotice",false)
}else{


+ 138
- 0
templates/custom/select_dataset.tmpl View File

@@ -0,0 +1,138 @@

<div class="dataset-repolink" id="dataset-repolink-init" style="display: none;" data-repolink="{{.RepoLink}}" data-cloudranin-type="{{.cloudbraintype}}"></div>
<div class="inline {{if eq .cloudbraintype 0}} required {{end}} field" id="dataset-base">
<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();">
{{else}}
<input class="disabled" type="text" :value="dataset_name">
{{end}}
<el-button type="text" @click="dialogVisible = true" icon="el-icon-plus"> {{.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()">
</div>
<el-tabs v-model="activeName" @tab-click="handleClick('{{.RepoLink}}',activeName,{{.cloudbraintype}})">
<el-tab-pane label="{{.i18n.Tr "dataset.current_project"}}" name="first">
<div style="display: flex;align-items: center;justify-content: space-between;padding: 1rem 0;border-bottom:1px solid #F5F5F5" v-for="(dataset,index) in currentRepoDataset" :key="index">
<div style="width: 90%;">
<div style="display: flex;align-items: center;"><span class="panel_creator_reponam">${dataset.Repo.OwnerName}/${dataset.Repo.Alias} </span><span class="panel_dataset_name">${dataset.Name} </span></div>
<div style="margin-top: 8px;display: flex;">
<a :title="dataset.UserName" style="cursor: default;">
<img class="ui avatar mini image" style="width: 20px;height: 20px;" :src="dataset.RelAvatarLink">
</a>
<span class="panel_datset_desc">${dataset.Description}</span>
</div>
</div>
<div>
<button v-if="dataset.DecompressState===1" class="ui primary basic button mini" @click.stop.prevent="selectDataset(dataset.UUID,dataset.Name)">{{.i18n.Tr "dataset.use"}}</button>
<span v-if="dataset.DecompressState===2" style="display: flex;align-items: center;">
<i class="CREATING"></i>
<span style="margin-left: 0.4em;font-size: 12px;color: #5A5A5A;" data-tooltip="{{$.i18n.Tr "dataset.unzip_tooltips"}}" data-inverted="" data-variation="mini" data-position="left center">解压中</span>
</span>
<span v-if="dataset.DecompressState===3" style="display: flex;align-items: center;">
<i class="FAILED"></i>
<span style="margin-left: 0.4em;font-size: 12px;color:red;" data-tooltip="{{$.i18n.Tr "dataset.zip_failed"}}" data-inverted="" data-variation="mini" data-position="left center">解压失败</span>
</span>
</div>
</div>
</el-tab-pane>
<el-tab-pane label="{{.i18n.Tr "dataset.owner_dataset"}}" name="second">
<div style="display: flex;align-items: center;justify-content: space-between;padding: 1rem 0;border-bottom:1px solid #F5F5F5" v-for="(dataset,index) in myDataset" :key="index">
<div style="width: 90%;">
<div style="display: flex;align-items: center;"><span class="panel_creator_reponam">${dataset.Repo.OwnerName}/${dataset.Repo.Alias}</span><span class="panel_dataset_name">${dataset.Name}</span></div>
<div style="margin-top: 8px;display: flex;">
<a :title="dataset.UserName" style="cursor: default;">
<img class="ui avatar mini image" style="width: 20px;height: 20px;" :src="dataset.RelAvatarLink">
</a>
<span class="panel_datset_desc">${dataset.Description}</span>
</div>
</div>
<div>
<button v-if="dataset.DecompressState===1" class="ui primary basic button mini" @click.stop.prevent="selectDataset(dataset.UUID,dataset.Name)">{{.i18n.Tr "dataset.use"}}</button>
<span v-if="dataset.DecompressState===2" style="display: flex;align-items: center;">
<i class="CREATING"></i>
<span style="margin-left: 0.4em;font-size: 12px;color: #5A5A5A;" data-tooltip="{{$.i18n.Tr "dataset.unzip_tooltips"}}" data-inverted="" data-variation="mini" data-position="left center">解压中</span>
</span>
<span v-if="dataset.DecompressState===3" style="display: flex;align-items: center;">
<i class="FAILED"></i>
<span style="margin-left: 0.4em;font-size: 12px;color:red;" data-tooltip="{{$.i18n.Tr "dataset.zip_failed"}}" data-inverted="" data-variation="mini" data-position="left center">解压失败</span>
</span>
</div>
</div>

</el-tab-pane>
<el-tab-pane label="{{.i18n.Tr "dataset.public_dataset"}}" name="third">
<div style="display: flex;align-items: center;justify-content: space-between;padding: 1rem 0;border-bottom:1px solid #F5F5F5" v-for="(dataset,index) in publicDataset" :key="index">
<div style="width: 90%;">
<div style="display: flex;align-items: center;"><span class="panel_creator_reponam">${dataset.Repo.OwnerName}/${dataset.Repo.Alias}</span><span class="panel_dataset_name">${dataset.Name}</span></div>
<div style="margin-top: 8px;display: flex;">
<a :title="dataset.UserName" style="cursor: default;">
<img class="ui avatar mini image" style="width: 20px;height: 20px;" :src="dataset.RelAvatarLink">
</a>
<span class="panel_datset_desc">${dataset.Description}</span>
</div>
</div>
<div>
<button v-if="dataset.DecompressState===1" class="ui primary basic button mini" @click.stop.prevent="selectDataset(dataset.UUID,dataset.Name)">{{.i18n.Tr "dataset.use"}}</button>
<span v-if="dataset.DecompressState===2" style="display: flex;align-items: center;">
<i class="CREATING"></i>
<span style="margin-left: 0.4em;font-size: 12px;color: #5A5A5A;" data-tooltip="{{$.i18n.Tr "dataset.unzip_tooltips"}}" data-inverted="" data-variation="mini" data-position="left center">解压中</span>
</span>
<span v-if="dataset.DecompressState===3" style="display: flex;align-items: center;">
<i class="FAILED"></i>
<span style="margin-left: 0.4em;font-size: 12px;color:red;" data-tooltip="{{$.i18n.Tr "dataset.zip_failed"}}" data-inverted="" data-variation="mini" data-position="left center">解压失败</span>
</span>
</div>
</div>

</el-tab-pane>
<el-tab-pane label="{{.i18n.Tr "dataset.I_liked"}}" name="fourth">
<div style="display: flex;align-items: center;justify-content: space-between;padding: 1rem 0;border-bottom:1px solid #F5F5F5" v-for="(dataset,index) in myFavoriteDataset" :key="index">
<div style="width: 90%;">
<div style="display: flex;align-items: center;"><span class="panel_creator_reponam">${dataset.Repo.OwnerName}/${dataset.Repo.Alias}</span><span class="panel_dataset_name">${dataset.Name}</span></div>
<div style="margin-top: 8px;display: flex;">
<a :title="dataset.UserName" style="cursor: default;">
<img class="ui avatar mini image" style="width: 20px;height: 20px;" :src="dataset.RelAvatarLink">
</a>
<span class="panel_datset_desc">${dataset.Description}</span>
</div>
</div>
<div>
<button v-if="dataset.DecompressState===1" class="ui primary basic button mini" @click.stop.prevent="selectDataset(dataset.UUID,dataset.Name)">{{.i18n.Tr "dataset.use"}}</button>
<span v-if="dataset.DecompressState===2" style="display: flex;align-items: center;">
<i class="CREATING"></i>
<span style="margin-left: 0.4em;font-size: 12px;color: #5A5A5A;" data-tooltip="{{$.i18n.Tr "dataset.unzip_tooltips"}}" data-inverted="" data-variation="mini" data-position="left center">解压中</span>
</span>
<span v-if="dataset.DecompressState===3" style="display: flex;align-items: center;">
<i class="FAILED"></i>
<span style="margin-left: 0.4em;font-size: 12px;color:red;" data-tooltip="{{$.i18n.Tr "dataset.zip_failed"}}" data-inverted="" data-variation="mini" data-position="left center">解压失败</span>
</span>
</div>
</div>

</el-tab-pane>
</el-tabs>
<div class="center">
<el-pagination
background
@current-change="handleCurrentChange"
:current-page="page"
:page-size="5"
layout="total,prev, pager, next"
:total="totalnums">
</el-pagination>
</div>
</el-dialog>


</div>

+ 69
- 0
templates/explore/dataset_left.tmpl View File

@@ -0,0 +1,69 @@
<div class="computer only four wide computer column">
<div class="ui grid">
<div class="ui sixteen wide column">
<h2 class="ui medium header" style="margin-top: 0;visibility: hidden;" >
{{.i18n.Tr "datasets"}}
</h2>
<div id="task-square-range-value" style="display: none;">
{{range $task := tasks}}
<div class="item" data-task='{{$task}}'></div>
{{end}}
</div>
<div id="square-link" style="display: none;" data-link="{{$.Link}}"></div>
<div id="licenses-square-range-value" style="display: none;">
{{range $license := licenses}}
<div class="item" data-license="{{$license}}"></div>
{{end}}
</div>
<div class="mg-b-2">
<div class="flex mg-b-1">
<h3 class="font-medium">
{{.i18n.Tr "dataset.category"}}
{{if $.Category}}
<span class="mg-l-1 underline text-gray-400 text-sm clear_dataset_value" data-clear-value="category"style="cursor: pointer;" >Clear</span>
{{end}}
</h3>
</div>
<div class="flex flex-wrap">
{{range $category := categories}}
{{$Cate := $.i18n.Tr (printf "dataset.category.%s" $category)}}
<a class="tag {{if eq $category $.Category}} tag-active {{else}} tag-gray{{end}}" href="{{$.Link}}?sort={{$.SortType}}&q={{$.Keyword}}&tab={{$.TabName}}&category={{$category}}&task={{$.Task}}&license={{$.License}}"><span>{{$Cate}}</span></a>
{{end}}
</div>
</div>
<div class="mg-b-2">
<div class="flex mg-b-1">
<h3 class="font-medium">
{{.i18n.Tr "dataset.task"}}
{{if $.Task}}
<span class="mg-l-1 underline text-gray-400 text-sm clear_dataset_value" data-clear-value="task" style="cursor: pointer;" >Clear</span>
{{end}}
</h3>
</div>
<div class="flex flex-wrap history-content">
{{range $task := tasks}}
{{$Task := $.i18n.Tr (printf "dataset.task.%s" $task)}}
<a class="tag {{if eq $task $.Task}} tag-active {{else}} tag-gray{{end}}" href="{{$.Link}}?sort={{$.SortType}}&q={{$.Keyword}}&tab={{$.TabName}}&category={{$.Category}}&task={{$task}}&license={{$.License}}"><span>{{$Task}}</span></a>
{{end}}
</div>
</div>
<div class="mg-b-2">
<div class="flex mg-b-1">
<h3 class="font-medium">
{{.i18n.Tr "repo.license"}}
{{if $.License}}
<span class="mg-l-1 underline text-gray-400 text-sm clear_dataset_value" data-clear-value="license" style="cursor: pointer;" >Clear</span>
{{end}}
</h3>
</div>
<div class="flex flex-wrap">
{{range $license := licenses}}
<a class="tag {{if eq $license $.License}} tag-active {{else}} tag-gray {{end}}" href="{{$.Link}}?sort={{$.SortType}}&q={{$.Keyword}}&tab={{$.TabName}}&category={{$.Category}}&task={{$.Task}}&license={{$license}}"><span>{{$license}}</span></a>
{{end}}
</div>
</div>

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

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

@@ -25,7 +25,7 @@
{{range .Datasets}}
<div class="item">
<div class="ui header">
<a class="name" href="{{.Repo.Link}}/datasets?type=0">
<a class="name" href="{{.Repo.Link}}/datasets">
{{.Repo.OwnerName}} / {{.Repo.Alias}}
</a>
<div class="ui right metas">


+ 210
- 9
templates/explore/datasets.tmpl View File

@@ -1,15 +1,216 @@
{{template "base/head" .}}
<style>
.mg-b-1{
margin-bottom: 1rem;
}
.mg-b-2{
margin-bottom: 2rem;
}
.mg-l-1{
margin-left: 1rem;
}
.text-gray-400 {
--tw-text-opacity: 1;
color: rgba(156,163,175,var(--tw-text-opacity));
}
.text-sm {
font-size: .875rem;
line-height: 1.25rem;
}
.underline {
text-decoration: underline;
}
.flex{
display: flex;
}
.font-medium{
font-weight: 500;
}
.flex-wrap{
flex-wrap: wrap;
}
.tag {
background-image: linear-gradient(to bottom,var(--tw-gradient-stops));
border-color: transparent;
border-radius: 0.5rem;
border-width: 1px;
font-size: .875rem;
line-height: 1.25rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.tag-red {
--tw-gradient-from: #fef2f2;
--tw-gradient-stops: var(--tw-gradient-from),var(--tw-gradient-to,hsla(0,86%,97%,0));
--tw-gradient-to: #fef2f2;
--tw-text-opacity: 1;
color: rgba(153,27,27,var(--tw-text-opacity));
}
.tag-purple {
--tw-gradient-from: #f5f3ff;
--tw-gradient-stops: var(--tw-gradient-from),var(--tw-gradient-to,rgba(245,243,255,0));
--tw-gradient-to: #f5f3ff;
--tw-text-opacity: 1;
color: rgba(91,33,182,var(--tw-text-opacity));
}
.tag-blue {
--tw-gradient-from: #eff6ff;
--tw-gradient-stops: var(--tw-gradient-from),var(--tw-gradient-to,rgba(239,246,255,0));
--tw-gradient-to: #eff6ff;
--tw-text-opacity: 1;
color: rgba(30,64,175,var(--tw-text-opacity));
}
.tag.inactive {
filter: grayscale(100%);
opacity: .5;
}
.tag.tag-active{
background-color: #0366d6;
color: #ffffff;
}
.tag-gray{
background-color: #f8f9fa;
color: #415058;
}
.tag {
align-items: center;
display: inline-flex;
flex: none;
height: 2rem;
margin-bottom: 0.35rem;
margin-right: 0.35rem;
max-width: 100%;
}
.tag>span {
padding: 0.75rem;
font-size: 14px;
}
.repo_dataset_header{
font-size: 12px;
color: #3291F8;
}
.heart-stroke{
stroke: #666;
stroke-width: 2;
fill: #fff
}
.stars_active{
fill: #FA8C16 !important;
stroke:#FA8C16 !important
}
</style>
<div class="explore repositories">
{{template "explore/dataset_search" .}}
<div class="ui container">
<div class="ui grid">
{{template "explore/navbar" .}}
<div class="ui sixteen wide mobile ten wide tablet ten wide computer column">
{{template "explore/dataset_list" .}}
{{template "base/paginate" .}}
</div>
<div class="ui sixteen wide mobile six wide tablet three wide computer column">
{{template "explore/repo_right" .}}
<div>
<div class="ui container">
<div class="ui grid">
{{template "explore/dataset_left" .}}
<div class="ui sixteen wide mobile sixteen wide tablet twelve wide computer column">
<div class="ui row">
<h2 class="ui left floated medium header">
{{.i18n.Tr "datasets"}}
</h2>
<div class="ui right floated secondary filter menu">
<!-- Sort -->
<div class="ui right dropdown type jump item">
<span class="text">
{{.i18n.Tr "repo.issues.filter_sort"}}
<i class="dropdown icon"></i>
</span>
<div class="menu">
<a class="{{if eq .SortType "newest"}}active{{end}} item" href="{{$.Link}}?sort=newest&q={{$.Keyword}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.filter_sort.latest"}}</a>
<a class="{{if eq .SortType "oldest"}}active{{end}} item" href="{{$.Link}}?sort=oldest&q={{$.Keyword}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.filter_sort.oldest"}}</a>
<a class="{{if eq .SortType "recentupdate"}}active{{end}} item" href="{{$.Link}}?sort=recentupdate&q={{$.Keyword}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.filter_sort.recentupdate"}}</a>
<a class="{{if eq .SortType "leastupdate"}}active{{end}} item" href="{{$.Link}}?sort=leastupdate&q={{$.Keyword}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.filter_sort.leastupdate"}}</a>
<!-- <a class="{{if eq .SortType "downloadtimes"}}active{{end}} item" href="{{$.Link}}?sort=downloadtimes&q={{$.Keyword}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.filter_sort.downloadtimes"}}</a> -->
</div>
</div>
</div>
</div>
{{if .Datasets}}
<div id="datasets-square-range-value" style="display: none;">
{{range .Datasets}}
<div class="item" data-num-stars="{{.NumStars}}" data-star-active="{{.IsStaring}}"></div>
{{end}}
</div>
{{end}}

<div class="ui row" style="clear: both;" id="dataset-base">
<div class="ui two cards">
{{range $k, $v :=.Datasets}}
<div class="ui card" @click="gotoDataset('{{.Repo.Link}}/datasets')" style="cursor: pointer;box-shadow: 0px 4px 4px 0px rgba(232,232,232,0.6);border: 1px solid rgba(232, 232, 232, 1);">
<div class="content" style="border-bottom: none;">
<div class="repo_dataset_header" style="display: flex;align-items: center;justify-content: space-between;">
<a href="{{.Repo.Link}}/datasets" style="font-size: 12px;color: #3291F8;height: 24px;">{{.Repo.OwnerName}} / {{.Repo.Alias}}</a>
{{if $.IsSigned}}
<span style="display: flex;align-items: center;justify-content: flex-end;cursor: pointer;" @click.stop="postSquareStar({{.ID}},'{{.Repo.Link}}/datasets',{{$k}})">
<div style="line-height: 1;margin-right: 4px;margin-bottom: -2px;">
<svg width="1.4em" height="1.4em" viewBox="0 0 32 32" class="heart-stroke" :class='{stars_active:starActives[{{$k}}]}'><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>
</div>
<span style="line-height: 1;color: #101010;">${starItems[{{$k}}]}</span>
</span>
{{else}}
<span style="display: flex;align-items: center;justify-content: flex-end;cursor: pointer;">
<div style="line-height: 1;margin-right: 4px;margin-bottom: -2px;">
<svg width="1.4em" height="1.4em" viewBox="0 0 32 32" class="heart-stroke" :class='{stars_active:starActives[{{$k}}]}'><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>
</div>
<span style="line-height: 1;color: #101010;">${starItems[{{$k}}]}</span>
</span>
{{end}}
</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;">
{{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>
{{end}}
{{if .Task}}
{{$task := .Task}}
<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.task.%s" $task)}}</a>
{{end}}
{{if .License}}
<a class="ui repo-topic label topic" href="{{$.Link}}?sort={{$.SortType}}&q={{$.Keyword}}&tab={{$.TabName}}&category={{$.Category}}&task={{$.Task}}&license={{.License}}">{{.License}}</a>
{{end}}
</div>
{{end}}
<div class="description" style="-webkit-box-orient: vertical;-webkit-line-clamp: 2;display: -webkit-box;overflow: hidden;color:#999999;font-size: 14px;margin-top: 10px;">
<p>{{.Description}}</p>
</div>
</div>
<div class="extra content" style="border-top: none !important;">
<div style="display: flex;align-items: center;">
<a href="{{AppSubUrl}}/{{.Repo.OwnerName}}" title="{{.Repo.OwnerName}}">
<img class="ui avatar image" style="width: 22px;height:22px;" src="/user/avatar/{{.Repo.OwnerName}}/-1">
</a>
<span style="color: #999999;font-size: 14px;;">创建于:{{TimeSinceUnix1 .CreatedUnix}}</span>
</div>
</div>
</div>
{{end}}

</div>
</div>

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


+ 13
- 1
templates/explore/repos.tmpl View File

@@ -10,7 +10,19 @@

<div class="ui sixteen wide mobile twelve wide tablet ten wide computer column">
{{template "explore/repo_list" .}}
{{template "base/paginate" .}}
<div id="app" style="margin-top: 2rem;">
<div class="center">
<el-pagination
background
@current-change="handleCurrentChange"
:current-page="page"
:page-sizes="[20]"
:page-size="20"
layout="total, sizes, prev, pager, next, jumper"
:total="{{.Page.Paginater.Total}}">
</el-pagination>
</div>
</div>
</div>
<div class="computer only ui three wide computer column">
{{template "explore/repo_right" .}}


+ 95
- 0
templates/explore/search_new.tmpl View File

@@ -0,0 +1,95 @@
{{template "base/head" .}}

<div class="explore seach">
<div class="repos--seach">
<div class="ui container">
<div id="search_div" class="ui two column centered grid">
<div class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty" style="margin-top:1.2rem;margin-bottom: 1.2rem;">
<div class="ui fluid action input">
<input name="q" id="keyword_input" value="{{.Keyword}}" placeholder="{{.i18n.Tr "home.search"}}..." autofocus="">
<input type="hidden" name="topic" value="">
<input type="hidden" name="tab" value="">
<input type="hidden" name="sort" value="hot">
<button class="ui green button" onclick="search()">{{.i18n.Tr "home.search"}}</button>
</div>
</div>
</div>
<div id="search_label_div" style="display:none">
</div>
</div>

<div class="ui container seachnav">
<div class="ui secondary pointing menu">
<a id="repo_item" class="item" href="javascript:searchItem(1,10);">
{{.i18n.Tr "home.search_repo"}}
<span class="ui circular mini label" id="repo_total"></span>
</a>
<a id="dataset_item" class="item" href="javascript:searchItem(5,50);">
{{.i18n.Tr "home.search_dataset"}}
<span class="ui circular mini label" id="dataset_total"></span>
</a>
<a id="issue_item" class="item" href="javascript:searchItem(2,20);">
{{.i18n.Tr "home.search_issue"}}
<span class="ui circular mini label" id="issue_total"></span>
</a>
<a id="pr_item" class="item" href="javascript:searchItem(6,60);">
{{.i18n.Tr "home.search_pr"}}
<span class="ui circular mini label" id="pr_total"></span>
</a>
<a id="user_item" class="item" href="javascript:searchItem(3,30);">
{{.i18n.Tr "home.search_user"}}
<span class="ui circular mini label" id="user_total"></span>
</a>
<a id="org_item" class="item" href="javascript:searchItem(4,40);">
{{.i18n.Tr "home.search_org"}} <span class="ui circular mini label" id="org_total"></span>
</a>
</div>
</div>
</div>

<div class="ui container">
<span id="find_id">{{.i18n.Tr "home.search_finded"}}</span><span id="find_title"></span>
<div class="ui right floated secondary filter menu">
<!-- Sort -->
<div class="ui right dropdown type jump item">
<span class="text">
{{.i18n.Tr "repo.issues.filter_sort"}}
<i class="dropdown icon"></i>
</span>
<div class="menu" id="sort_type">
</div>
</div>

</div>
<div class="ui divider" style="margin-top: 1.25rem;"></div>

<div class="ui very relaxed divided list" id="child_search_item">

</div><!--seach list end-->
<div class="center page buttons" style="margin: 0px auto 15px">
<div class="ui borderless mini pagination menu" id="page_menu">
</div>
</div>
<div id="tipmsg"></div>
</div>

</div>
<script src="/self/js/jquery.min.js" type="text/javascript"></script>
<script src="/home/search.js?v={{MD5 AppVer}}" type="text/javascript"></script>
<div class="am-mt-30"></div>


{{template "base/footer" .}}

+ 45
- 0
templates/repo/attachment/edit.tmpl View File

@@ -0,0 +1,45 @@
<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>
{{template "base/head" .}}
<div class="repository">
{{template "repo/header" .}}
<div class="ui container">
<input type="hidden" id="dataset-file-desc" value="{{.Attachment.Description}}">
<div style="width: 80%;margin: auto;">
<h4 class="ui top attached header">
{{$.i18n.Tr "dataset.modify_dataset_description"}}
</h4>
<div class="ui attached segment" style="padding: 2em 3em;">
<div class="ui form" id="dataset-base">
<el-form label-width="140px">
{{.CsrfTokenHtml}}
<el-form-item label='{{$.i18n.Tr "dataset.dataset_available_clusters"}}:' prop="title">
<span style="display: flex;color: #3291F8;"><i class="ri-archive-drawer-line" style="margin-right: 10px;"></i>{{.Attachment.Type | AttachmentResourceType}}</span>
<!-- <span>请输入字母、数字、_和-,最长64个字符,且不能以中划线(-)结尾。</span> -->
</el-form-item>
<el-form-item label='{{$.i18n.Tr "dataset.file"}}:' prop="description">
<span>{{.Attachment.Name}}</span>
</el-form-item>
<el-form-item label='{{$.i18n.Tr "dataset.file_description"}}:' prop="description">
<el-input type="textarea" :rows="3" maxlength="255" placeholder="{{$.i18n.Tr "repo.modelarts.train_job.new_place"}}" v-model="descfile"></el-input>
</el-form-item>
<el-form-item>
<el-button style="background-color: #21ba45;" type="success" @click="editDatasetFile({{.Attachment.ID}},'{{$.RepoLink}}')">确定</el-button>
<el-button type="info" @click="cancelDataset('','{{$.RepoLink}}')">取消</el-button>
</el-form-item>
</el-form>
</div>
</div>
</div>
</div>
</div>
{{template "base/footer" .}}

+ 73
- 0
templates/repo/attachment/upload.tmpl View File

@@ -0,0 +1,73 @@

{{template "base/head" .}}
<div class="repository">
{{template "repo/header" .}}
<div class="ui container">
<input type="hidden" id="postPath" value="{{.Link}}">
<div style="width: 80%;margin: auto;">
<h4 class="ui top attached header">
{{$.i18n.Tr "dataset.upload_dataset_file"}}
</h4>
<div class="ui attached segment" style="padding: 2em 3em;">
<div class="ui form" id="dataset-base">
<el-form label-width="140px">
{{.CsrfTokenHtml}}
<el-form-item label='{{$.i18n.Tr "dataset.dataset_available_clusters"}}:' prop="title">
<el-button :class="{active:type==0}" size="small" style="margin: 0;border-radius: 0.28571429rem 0 0 0.28571429rem;" @click="uploadGpu">CPU/GPU</el-button>
<el-button :class="{active:type==1}" size="small" style="margin: 0 0 0 -4px;border-radius: 0 0.28571429rem 0.28571429rem 0;" @click="uploadNpu">NPU</el-button>
<!-- <span>请输入字母、数字、_和-,最长64个字符,且不能以中划线(-)结尾。</span> -->
</el-form-item>
<el-form-item label='{{$.i18n.Tr "dataset.file_description"}}:' prop="description">
<el-input type="textarea" :rows="3" maxlength="255" placeholder="{{$.i18n.Tr "repo.modelarts.train_job.new_place"}}" v-model="desc"></el-input>
</el-form-item>
<el-form-item label='{{$.i18n.Tr "dataset.data_upload"}}:' prop="category">
<minio-uploader :uploadtype="type" :desc="desc"></minio-uploader>
</el-form-item>
<div style='display:none;'
id="minioUploader-params"
data-uuid="{{.uuid}}"
data-add-url="{{.Repository.OwnerName}}/attachments/add"
data-accepts="{{.AttachmentAllowedTypes}}"
data-remove-url="{{AppSubUrl}}/attachments/delete"
data-csrf="{{.CsrfToken}}"
dataset-id={{.dataset.ID}}
data-max-file="100"
data-dataset-id="{{.dataset.ID}}"
data-max-size="{{.AttachmentMaxSize}}"
data-default-message="{{.i18n.Tr "dropzone.default_message"}}"
data-invalid-input-type="{{.i18n.Tr "dropzone.invalid_input_type"}}"
data-file-too-big="{{.i18n.Tr "dropzone.file_too_big"}}"
data-remove-file="{{.i18n.Tr "dropzone.remove_file"}}"
data-file-status='{{.i18n.Tr "dropzone.file_status"}}'
data-file-init-status='{{.i18n.Tr "dropzone.file_init_status"}}'
data-waitting-uploading='{{.i18n.Tr "dropzone.waitting_uploading"}}'
data-md5-computing='{{.i18n.Tr "dropzone.md5_computing"}}'
data-obs-connecting='{{.i18n.Tr "dropzone.obs-connecting"}}'
data-loading-file='{{.i18n.Tr "dropzone.loading_file"}}'
data-upload-complete='{{.i18n.Tr "dropzone.upload_complete"}}'
data-uploading='{{.i18n.Tr "dropzone.uploading"}}'
data-failed='{{.i18n.Tr "dropzone.failed"}}'
data-repopath='{{AppSubUrl}}{{$.RepoLink}}/datasets'
data-cancel='{{.i18n.Tr "cancel"}}'
data-upload='{{.i18n.Tr "dataset.dataset_upload"}}'
>
</div>
<div id="datasetId" datasetId="{{.datasetId}}"></div>
</el-form>
</div>
</div>
</div>
<div style="width: 80%;margin: auto;padding-top: 2em;">
<!-- <p>说明:<br>
- 只有<span class="text blue">zip格式</span>zip格式的数据集才能发起云脑任务;<br>
- 云脑1提供 <span class="text blue">CPU / GPU</span> 资源,云脑2提供 <span class="text blue">Ascend NPU</span> 资源;调试使用的数据集也需要上传到对应的环境。
</p> -->
<p style="color: 505559;">{{$.i18n.Tr "dataset.illustrate"}}:</p>
<p style="line-height: 1.5;color: #101010;">{{$.i18n.Tr "dataset.illustrate.only"}}<span class="text red">&nbsp;{{$.i18n.Tr "dataset.illustrate.zip"}}&nbsp;</span>{{$.i18n.Tr "dataset.illustrate.fisrt_end"}};</br>
{{$.i18n.Tr "dataset.dataset_explain"}}</p>

</div>
</div>
</div>
{{template "base/footer" .}}

+ 14
- 2
templates/repo/cloudbrain/benchmark/index.tmpl View File

@@ -155,7 +155,7 @@
{{end}}
</form>
<a class="ui basic button {{if $.IsSigned}} blue{{else}} disabled{{end}}" href="{{$.RepoLink}}/cloudbrain/{{.Cloudbrain.ID}}/rate" target="_blank">
{{$.i18n.Tr "repo.stop"}}
{{$.i18n.Tr "repo.score"}}
</a>

<!-- 删除任务 -->
@@ -185,7 +185,19 @@
</div>
-->

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

</div>


+ 14
- 2
templates/repo/cloudbrain/benchmark/show.tmpl View File

@@ -196,7 +196,13 @@ td, th {
<span class="accordion-panel-title-content">
<span>
<div class="ac-display-inblock title_text acc-margin-bottom">
<span class="cti-mgRight-sm">{{TimeSinceUnix1 .CreatedUnix}}</span>
<span class="cti-mgRight-sm">
{{if not (eq .StartTime 0)}}
<td>{{TimeSinceUnix1 .StartTime}}</td>
{{else}}
<td>{{TimeSinceUnix1 .CreatedUnix}}<td>
{{end}}
</span>

<span class="cti-mgRight-sm">{{$.i18n.Tr "repo.modelarts.status"}}:
<span id="{{.VersionName}}-status-span"><i id="icon" style="vertical-align: middle;" class="{{.Status}}"></i><span id="text" style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span>
@@ -252,7 +258,13 @@ td, th {

<td class="ti-text-form-content">
<div class="text-span text-span-w">
<span style="font-size: 12px;" class="">{{TimeSinceUnix1 .CreatedUnix}}</span>
<span style="font-size: 12px;" class="">
{{if not (eq .StartTime 0)}}
{{TimeSinceUnix1 .StartTime}}
{{else}}
{{TimeSinceUnix1 .CreatedUnix}}
{{end}}
</span>
</div>
</td>
</tr>


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

@@ -101,6 +101,7 @@
}



</style>

<div id="mask">
@@ -116,6 +117,7 @@
<div class="repository">
{{template "repo/header" .}}
<div class="repository new repo ui middle very relaxed page grid">
<div class="column">
{{template "base/alert" .}}
<div class="ui negative message" id="messageInfo">
@@ -200,17 +202,8 @@
{{end}}
</datalist>
</div>

<div class="inline required field">
<label>{{.i18n.Tr "cloudbrain.dataset"}}</label>
<select id="cloudbrain_dataset" class="ui search dropdown" placeholder="{{.i18n.Tr "cloudbrain.select_dataset"}}" style='width:385px' name="attachment" required>

{{range .attachments}}
<option name="attachment" value="{{.UUID}}">{{.Attachment.Name}}</option>
{{end}}
</select>
</div>

{{template "custom/select_dataset" .}}
<div class="inline required field">
<label>{{.i18n.Tr "cloudbrain.resource_specification"}}</label>
<select id="cloudbrain_resource_spec" class="ui search dropdown" placeholder="{{.i18n.Tr "cloudbrain.select_specification"}}" style='width:385px' name="resource_spec_id">
@@ -354,4 +347,5 @@
$('#store_category').attr("value", selected_value)
})


</script>

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

@@ -74,11 +74,19 @@
</tr>
<tr>
<td> 开始时间 </td>
<td>{{.JobStatus.StartTime}}</td>
{{if not (eq $.task.StartTime 0)}}
<td>{{TimeSinceUnix1 $.task.StartTime}}</td>
{{else}}
<td>无<td>
{{end}}
</tr>
<tr>
<td> 结束时间 </td>
<td>{{.JobStatus.EndTime}}</td>
{{if not (eq $.task.EndTime 0)}}
<td>{{TimeSinceUnix1 $.task.EndTime}}</td>
{{else}}
<td>无<td>
{{end}}
</tr>
<tr>
<td> ExitCode </td>


+ 451
- 0
templates/repo/cloudbrain/trainjob/new.tmpl View File

@@ -0,0 +1,451 @@
{{template "base/head" .}}
<style>

.unite{
font-family: SourceHanSansSC-medium !important;
color: rgba(16, 16, 16, 100) !important;
}

.title{
font-size: 16px !important;
padding-left: 3rem !important;
}
.min_title{
font-size: 14px !important;
padding-left: 6rem !important;
margin-bottom: 2rem !important;

}
.width{
width:100% !important;
}
.width80{
width: 80.7% !important;
margin-left: 10px;
}
.width806{
width: 80.6% !important;
margin-left: -2px;
}
.width85{
width: 85% !important;
margin-left: 4.5rem !important;
}
.width81{
margin-left: 1.5rem !important;
width: 81% !important;
}

.add{font-size: 18px;
padding: 0.5rem;
border: 1px solid rgba(187, 187, 187, 100);
border-radius: 0px 5px 5px 0px;
line-height: 21px;
text-align: center;
color: #C2C7CC;
}
.min{
font-size: 18px;
padding: 0.5rem;
border: 1px solid rgba(187, 187, 187, 100);
border-radius: 5px 0px 0px 5px;
line-height: 21px;
text-align: center;
color: #C2C7CC;
}

</style>
<!-- <div class="ui page dimmer">
<div class="ui text loader">{{.i18n.Tr "loading"}}</div>
</div> -->
<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="ui container">
{{template "base/alert" .}}
<h4 class="ui top attached header">
{{.i18n.Tr "repo.modelarts.train_job.new"}}
</h4>
<div class="ui attached segment">
<!-- equal width -->
<form class="ui form" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}}
<input type="hidden" name="action" value="update">
<input type="hidden" id="ai_engine_name" name="engine_names" value="">
<input type="hidden" id="ai_flaver_name" name="flaver_names" value="">
<h4 class="unite title ui header ">{{.i18n.Tr "repo.modelarts.train_job.basic_info"}}:</h4>
<div class="required unite min_title inline field">
<label style="font-weight: normal;">{{.i18n.Tr "cloudbrain.compute_resource"}}</label>
<div class="ui blue mini menu compact selectcloudbrain">
<a class="active item" href="{{.RepoLink}}/cloudbrain/train-job/create">
<svg class="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M3 2.992C3 2.444 3.445 2 3.993 2h16.014a1 1 0 0 1 .993.992v18.016a.993.993 0 0 1-.993.992H3.993A1 1 0 0 1 3 21.008V2.992zM19 11V4H5v7h14zm0 2H5v7h14v-7zM9 6h6v2H9V6zm0 9h6v2H9v-2z"/>
</svg>
CPU/GPU
</a>
<a class="item" href="{{.RepoLink}}/modelarts/train-job/create">
<svg class="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M3 2.992C3 2.444 3.445 2 3.993 2h16.014a1 1 0 0 1 .993.992v18.016a.993.993 0 0 1-.993.992H3.993A1 1 0 0 1 3 21.008V2.992zM19 11V4H5v7h14zm0 2H5v7h14v-7zM9 6h6v2H9V6zm0 9h6v2H9v-2z"/>
</svg>
Ascend NPU</a>
</div>
</div>
<div class="required unite min_title inline field">
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.job_name"}}</label>
<input style="width: 60%;" name="display_job_name" id="display_job_name" placeholder={{.i18n.Tr "repo.modelarts.train_job.job_name"}} value="{{.display_job_name}}" tabindex="3" onkeyup="this.value=this.value.replace(/[, ]/g,'')" autofocus required maxlength="64">
<span class="tooltips" style="display: block;">{{.i18n.Tr "cloudbrain.job_name_rule"}}</span>
</div>
<div class="unite min_title inline field">
<label style="font-weight: normal;" for="description">{{.i18n.Tr "repo.modelarts.train_job.description"}}&nbsp;&nbsp;</label>
<textarea style="width: 80%;" 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="ui divider"></div>

<h4 class="unite title ui header ">{{.i18n.Tr "repo.modelarts.train_job.parameter_setting"}}:</h4>

<div class="required unite min_title inline field">
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.code_version"}}</label>
<select class="ui dropdown width80 left2" id="code_version" name="branch_name">
{{if .branch_name}}
<option name="branch_name" value="{{.branch_name}}">{{.branch_name}}</option>
{{range $k, $v :=.Branches}}
{{ if ne $v $.branch_name }}
<option name="branch_name" value="{{$v}}">{{$v}}</option>
{{end}}
{{end}}
{{else}}
<option name="branch_name" value="{{.branchName}}">{{.branchName}}</option>
{{range $k, $v :=.Branches}}
{{ if ne $v $.branchName }}
<option name="branch_name" value="{{$v}}">{{$v}}</option>
{{end}}
{{end}}
{{end}}
</select>
</div>

<div class="inline required field" style="display: none;">
<label>{{.i18n.Tr "cloudbrain.task_type"}}</label>
<select id="cloudbrain_job_type" class="ui search dropdown" placeholder="选择任务类型" style='width:385px' name="job_type">
<option name="job_type" value="TRAIN">TRAIN</option>
</select>
</div>

<div class="required unite min_title inline field">
<label style="font-weight: normal;">{{.i18n.Tr "cloudbrain.gpu_type"}}</label>
<select id="cloudbrain_gpu_type" class="ui search width806 dropdown" placeholder="选择GPU类型" style='width:385px' name="gpu_type">
{{range .train_gpu_types}}
<option value="{{.Queue}}">{{.Value}}</option>
{{end}}
</select>
</div>

<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>
<datalist class="ui search" id="cloudbrain_image" 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>

<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: 33.5%;" name="boot_file" id="trainjob_boot_file" value="{{.bootFile}}" tabindex="3" autofocus required maxlength="255" >
{{else}}
<input style="width: 33.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>
</span>
<a href="https://git.openi.org.cn/OpenIOSSG/MNIST_PytorchExample_GPU" target="_blank">查看样例</a>
</div>

<div class="required unite min_title inline field" style="position: relative;">
<label style="font-weight: normal;">{{.i18n.Tr "cloudbrain.dataset"}}</label>
<select id="cloudbrain_dataset" class="ui search dropdown width80" placeholder="选择数据集" style='width:385px' name="attachment" required>

{{range .attachments}}
<option name="attachment" value="{{.UUID}}">{{.Attachment.Name}}</option>
{{end}}
</select>
<span class="tooltips">训练脚本存储在/code中,数据集存储在/dataset中,训练输出请存储在/model中以供后续下载。</span>
</div>
<div class="inline unite min_title field">
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.run_parameter"}}</label>
<span id="add_run_para" style="margin-left: 0.5rem;cursor:pointer;color: rgba(3, 102, 214, 100);font-size: 14px;line-height: 26px;font-family: SourceHanSansSC-medium;"><i class="plus square outline icon"></i>{{.i18n.Tr "repo.modelarts.train_job.add_run_parameter"}}</span>
<input id="store_run_para" type="hidden" name="run_para_list">
<div class="dynamic field" style="margin-top: 1rem;">
{{if .params}}
{{if ne 0 (len .params)}}
{{range $k ,$v := .params}}
<div class="two fields width85" id="para{{$k}}">
<div class="field">
<input type="text" name="shipping_first-name" value={{$v.Label}} required>
</div>
<div class="field">
<input type="text" name="shipping_last-name" value={{$v.Value}} required>
</div>
<span>
<i class="trash icon"></i>
</span>

</div>
{{end}}
{{end}}
{{end}}
</div>
</div>

<div class="required inline unite min_title field">
<label style="font-weight: normal;">{{.i18n.Tr "cloudbrain.resource_specification"}}</label>
<select id="cloudbrain_resource_spec" class="ui search dropdown" placeholder="选择资源规格" style='width:385px' name="resource_spec_id">
{{range .train_resource_specs}}
<option name="resource_spec_id" value="{{.Id}}">GPU数:{{.GpuNum}},CPU数:{{.CpuNum}},内存(MB):{{.MemMiB}},共享内存(MB):{{.ShareMemMiB}}</option>
{{end}}
</select>
</div>
<div class="inline unite min_title field">
<button class="ui create_train_job green button">
{{.i18n.Tr "repo.cloudbrain.new"}}
</button>
<a class="ui button" href="{{.RepoLink}}/modelarts/train-job">{{.i18n.Tr "repo.cloudbrain.cancel"}}</a>
</div>
<!-- 模态框 -->
</form>
</div>
</div>
</div>
{{template "base/footer" .}}

<script>
//let url_href = window.location.pathname.split('create')[0]
//$(".ui.button").attr('href',url_href)
$('select.dropdown')
.dropdown();

$('.menu .item')
.tab();

let sever_num = $('#trainjob_work_server_num')
$('.add').click(function(){
sever_num.val(parseInt(sever_num.val())+1)
if(sever_num.val()>=26){
sever_num.val(parseInt(sever_num.val())-1)
}
})
$('.min').click(function(){
sever_num.val(parseInt(sever_num.val())-1)
if(sever_num.val()<=0){
sever_num.val(parseInt(sever_num.val())+1)
}
})
// 参数增加、删除、修改、保存
function Add_parameter(i){
value = '<div class="two fields width85" id= "para'+ i +'">' +
'<div class="field">' +
'<input type="text" name="shipping_first-name" required placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_name"}}> ' +
'</div> ' +
'<div class="field"> ' +
'<input type="text" name="shipping_last-name" required placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_value"}}>' +
'</div>'+
'<span>' +
'<i class="trash icon">' +
'</i>' +
'</span>' +
'</div>'
$(".dynamic.field").append(value)
}

$('#add_run_para').click(function(){
var len = $(".dynamic.field .two.fields").length
Add_parameter(len)
});

$(".dynamic.field").on("click",".trash.icon", function() {
var index = $(this).parent().parent().index()
$(this).parent().parent().remove()
var len = $(".dynamic.field .two.fields").length
$(".dynamic.field .two.fields").each(function(){
var cur_index = $(this).index()
$(this).attr('id', 'para' + cur_index)
})
});

$('.ui.parameter.green.button').click(function(){
var parameters = [];
$('table tr').each(function() {
$(this).find('td:eq(1)').each(function(){
parameters.push($(this).text());
})
$(this).find('input').each(function(){
parameters.push($(this).text())
})
});
$('.ui.parameter.modal')
.modal('hide');
for(var i = 2; i < parameters.length; i++){
switch(i) {
// 数据集uuid待完成
// case (2):
// console.log(1)
// break;
// $("#trainjob_datasets").val(parameters[i]);
// console.log($("#trainjob_datasets").val())
case (3):
$("input[name='boot_file']").val(parameters[i]);
break;
case (4):
var para = parameters[i].split(" ")
for(var j = 0; j < para.length; j++){
var para_name = para[j].split('=')[0]
var para_value = para[j].split('=')[1]
var len = $(".dynamic.field .two.fields").length
Add_parameter(len)
var pid = 'para' + len
$(".dynamic.field"+ " #" + pid + "").find("input[name=shipping_first-name]").val(para_name)
$(".dynamic.field"+ " #" + pid + "").find("input[name=shipping_last-name]").val(para_value)
}
break;
// 数据集pool_id待完成
// case (5):
// $("select[name='pool_id']").val(parameters[i]);
// break;
case (6):
$("input[name='work_server_number']").val(parameters[i]);
break;
}
}
})

$('.ui.save.checkbox').click(function(){
$(this).checkbox({
onChange: function(){
if ($('.ui.save.checkbox').checkbox('is checked')){
$('#save_para').removeClass("disabled")
}else{
$('#save_para').addClass("disabled")
}
}
});
})

$('.question.circle.icon').hover(function(){
$(this).popup('show')
});

$(".item.active.parameter_config").click(function(){
$('.ui.parameter.modal')
.modal('setting', 'closable', false)
.modal('show');
})

$('.ui.deny.button').click(function(){
$('.ui.parameter.modal')
.modal('hide');
})
$('select.dropdown')
.dropdown();

function validate(){
$('.ui.form')
.form({
on: 'blur',
fields: {
boot_file: {
identifier : 'boot_file',
rules: [
{
type: 'regExp[/.+\.py$/g]',
}
]
},
display_job_name:{
identifier : 'display_job_name',
rules: [
{
type: 'regExp[/^[a-zA-Z0-9-_]{1,64}[a-zA-Z0-9_]$/]',
}
]
},
attachment:{
identifier : 'attachment',
rules: [
{
type: 'empty',
}
]

},
work_server_number: {
identifier : 'work_server_number',
rules: [
{
type : 'integer[1..25]',
}
]
}
},
onSuccess: function(){
// $('.ui.page.dimmer').dimmer('show')
document.getElementById("mask").style.display = "block"
},
onFailure: function(e){
return false;
}
})
}
document.onreadystatechange = function() {
if (document.readyState === "complete") {
document.getElementById("mask").style.display = "none"
}
}
function send_run_para(){
var run_parameters = []
var msg = {}
$(".dynamic.field .two.fields").each(function(){
var para_name = $(this).find('input[name=shipping_first-name]').val()
var para_value = $(this).find('input[name=shipping_last-name]').val()
run_parameters.push({"label": para_name, "value": para_value})
})
msg["parameter"] = run_parameters
msg = JSON.stringify(msg)
$('#store_run_para').val(msg)
}
function get_name(){
let name1=$("#engine_name .text").text()
let name2=$("#flaver_name .text").text()
$("input#ai_engine_name").val(name1)
$("input#ai_flaver_name").val(name2)

}
$('.ui.create_train_job.green.button').click(function(e) {
get_name()
send_run_para()
validate()
})
</script>

+ 653
- 0
templates/repo/cloudbrain/trainjob/show.tmpl View File

@@ -0,0 +1,653 @@
{{template "base/head" .}}
<style>
.according-panel-heading{
box-sizing: border-box;
padding: 8px 16px;
color: #252b3a;
background-color: #f2f5fc;
line-height: 1.5;
cursor: pointer;
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
-khtml-user-select: none;
user-select: none;
}
.accordion-panel-title {
margin-top: 0;
margin-bottom: 0;
color: #252b3a;
}
.accordion-panel-title-content{
vertical-align: middle;
display: inline-block;
width: calc(100% - 32px);
cursor: default;
}
.acc-margin-bottom {
margin-bottom: 5px;
}
.title_text {
font-size: 12px;
}
.ac-display-inblock {
display: inline-block;
}
.cti-mgRight-sm {
margin-right: 8px;
}
.ac-text-normal {
font-size: 14px;
color: #575d6c;
}
.uc-accordionTitle-black {
color: #333;
}
.accordion-border{
border:1px solid #cce2ff;
}
.padding0{
padding: 0 !important;
}
.content-pad{
padding: 15px 35px;
}
.content-margin{
margin:10px 5px ;
}
.tab_2_content {
min-height: 360px;
margin-left: 10px;
}
.ac-grid {
display: block;
*zoom: 1;
}
.ac-grid-col {
float: left;
width: 100%;
}
.ac-grid-col2 .ac-grid-col {
width: 50%;
}
.ti-form {
text-align: left;
max-width: 100%;
vertical-align: middle;
}
.ti-form>tbody {
font-size: 12px;
}
.ti-form>tbody, .ti-form>tbody>tr {
vertical-align: inherit;
}
.ti-text-form-label {
padding-bottom: 20px;
padding-right: 20px;
color: #8a8e99;
font-size: 12px;
white-space: nowrap !important;
width: 80px;
line-height: 30px;
}
.ti-text-form-content{
line-height: 30px;
padding-bottom: 20px;
}
.ti-form>tbody>tr>td {
vertical-align: top;
white-space: normal;
}
td, th {
padding: 0;
}
.ac-grid-col .text-span {
width: 450px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.redo-color{
color: #3291F8;
}
.ti-action-menu-item:not(:last-child){
margin-right: 10px;
padding-right: 11px;
text-decoration: none!important;
color: #526ecc;
cursor: pointer;
display: inline-block;
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
-khtml-user-select: none;
user-select: none;
position: relative;
}
.ti-action-menu-item:not(:last-child):after {
content: "";
display: inline-block;
position: absolute;
height: 12px;
right: 0;
top: 50%;
-webkit-transform: translateY(-6px);
-ms-transform: translateY(-6px);
-o-transform: translateY(-6px);
transform: translateY(-6px);
border-right: 1px solid #dfe1e6;
}
.text-width80{
width: 100px;
line-height: 30px;
}
.border-according{
border: 1px solid #dfe1e6;
}
.disabled {
cursor: default;
pointer-events: none;
color: rgba(0,0,0,.6) !important;
opacity: .45 !important;
}
.pad20{
border:0px !important;
}
.model_file_bread{
margin-bottom: -0.5rem !important;
padding-left: 1rem;
padding-top: 0.5rem ;
}
</style>
<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="ui container">
<h4 class="ui header" id="vertical-segment">
<div class="ui breadcrumb">
<a class="section" href="{{.RepoLink}}/debugjob?debugListType=all">
{{.i18n.Tr "repo.cloudbrain"}}
</a>
<div class="divider"> / </div>
<a class="section" href="{{$.RepoLink}}/modelarts/train-job">
{{$.i18n.Tr "repo.modelarts.train_job"}}
</a>
<div class="divider"> / </div>
<div class="active section">{{.displayJobName}}</div>
</div>
</h4>
{{range $k ,$v := .version_list_task}}
<div class="ui accordion border-according" id="accordion{{.VersionName}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}" data-version="{{.VersionName}}">
<input type="hidden" id="jobId_input" name="jobId_input" value="{{.JobID}}">
<div class="{{if eq $k 0}}active{{end}} title padding0">
<div class="according-panel-heading">
<div class="accordion-panel-title">
<i class="dropdown icon"></i>
<span class="accordion-panel-title-content">
<span>
<div class="ac-display-inblock title_text acc-margin-bottom">
<span class="cti-mgRight-sm">{{TimeSinceUnix1 .CreatedUnix}}</span>
<span class="cti-mgRight-sm">{{$.i18n.Tr "repo.modelarts.status"}}:
<span id="{{.VersionName}}-status-span"><i id="icon" style="vertical-align: middle;" class="{{.Status}}"></i><span id="text" style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span>
</span>
<span class="cti-mgRight-sm">{{$.i18n.Tr "repo.modelarts.train_job.dura_time"}}:</span>
<span class="cti-mgRight-sm uc-accordionTitle-black" id="{{.VersionName}}-duration-span">{{$.duration}}</span>
</div>
</span>
</span>
</div>
</div>
</div>
<div class="{{if eq $k 0}}active{{end}} content">
<div class="content-pad">
<div class="ui pointing secondary menu" style="border-bottom: 1px solid rgba(34,36,38,.15);">
<a class="active item" data-tab="first{{$k}}">{{$.i18n.Tr "repo.modelarts.train_job.config"}}</a>
<a class="item" data-tab="second{{$k}}" onclick="loadLog({{.VersionName}})">{{$.i18n.Tr "repo.modelarts.log"}}</a>
<a class="item" data-tab="third{{$k}}" onclick="loadModelFile({{.VersionName}},'','','init')">{{$.i18n.Tr "repo.model_download"}}</a>
</div>
<div class="ui tab active" data-tab="first{{$k}}">
<div style="padding-top: 10px;">
<div class="tab_2_content">
<div class="ac-grid ac-grid-col2">
<div class="ac-grid-col">
<table class="ti-form">
<tbody class="ti-text-form">
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
{{$.i18n.Tr "repo.cloudbrain_task"}}
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w">
{{.DisplayJobName}}
</div>
</td>
</tr>
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
{{$.i18n.Tr "repo.modelarts.status"}}
</td>

<td class="ti-text-form-content">
<div class="text-span text-span-w" id="{{.VersionName}}-status">
{{.Status}}
</div>
</td>
</tr>
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
{{$.i18n.Tr "repo.modelarts.train_job.start_time"}}
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w">
<span style="font-size: 12px;" class="">{{TimeSinceUnix1 .CreatedUnix}}</span>
</div>
</td>
</tr>
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
{{$.i18n.Tr "repo.modelarts.train_job.dura_time"}}
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w" id="{{.VersionName}}-duration">
{{$.duration}}
</div>
</td>
</tr>
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
{{$.i18n.Tr "repo.modelarts.train_job.resource_type"}}
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w">
{{$.resource_type}}
</div>
</td>
</tr>
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
{{$.i18n.Tr "repo.modelarts.train_job.standard"}}
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w">
{{$.resource_spec}}
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div class="ac-grid-col">
<table class="ti-form">
<tbody class="ti-text-form">
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
镜像
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w" id="{{.VersionName}}-mirror">
{{.Image}}
</div>
</td>
</tr>
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
{{$.i18n.Tr "repo.modelarts.code_version"}}
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w">
{{.BranchName}}
</div>
</td>
</tr>

<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
{{$.i18n.Tr "repo.modelarts.train_job.start_file"}}
</td>

<td class="ti-text-form-content">
<div class="text-span text-span-w">
{{.BootFile}}
</div>
</td>
</tr>

<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
{{$.i18n.Tr "repo.modelarts.train_job.train_dataset"}}
</td>

<td class="ti-text-form-content">
<div class="text-span text-span-w">
{{.DatasetName}}
</div>
</td>
</tr>

<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80" >
{{$.i18n.Tr "repo.modelarts.train_job.run_parameter"}}
</td>

<td class="ti-text-form-content">
<div class="text-span text-span-w" title="{{.Parameters}}">
{{.Parameters}}
</div>
</td>
</tr>

<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
{{$.i18n.Tr "repo.modelarts.train_job.description"}}
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w" title="{{.Description}}">
{{.Description}}
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="ui tab" data-tab="second{{$k}}">
<div>
<div class="ui message message{{.VersionName}}" style="display: none;">
<div id="header"></div>
</div>
<div class="ui attached log" id="log{{.VersionName}}" style="height: 300px !important; overflow: auto;">
<input type="hidden" name="end_line" value>
<input type="hidden" name="start_line" value>
<pre id="log_file{{.VersionName}}"></pre>
</div>
</div>
</div>

<div class="ui tab" data-tab="third{{$k}}">
<input type="hidden" name="model{{.VersionName}}" value="-1">
<input type="hidden" name="modelback{{.VersionName}}" value="-1">
<div class='ui breadcrumb model_file_bread' id='file_breadcrumb{{.VersionName}}'>
<div class="active section">{{.VersionName}}</div>
<div class="divider"> / </div>

</div>
<div id="dir_list{{.VersionName}}">
</div>
</div>
</div>
</div>
</div>
{{end}} {{template "base/paginate" .}}
</div>
<!-- 确认模态框 -->
<div id="deletemodel">
<div class="ui basic modal">
<div class="ui icon header">
<i class="trash icon"></i> 删除任务
</div>
<div class="content">
<p>你确认删除该任务么?此任务一旦删除不可恢复。</p>
</div>
<div class="actions">
<div class="ui red basic inverted cancel button">
<i class="remove icon"></i> 取消操作
</div>
<div class="ui green basic inverted ok button">
<i class="checkmark icon"></i> 确定操作
</div>
</div>
</div>
</div>
</div>
{{template "base/footer" .}}

<script>
$('.menu .item').tab()

$(document).ready(function(){
$('.ui.accordion').accordion({selector:{trigger:'.icon'}});
});
$(document).ready(function(){
$('.secondary.menu .item').tab();
});
let userName
let repoPath
let jobID
let downlaodFlag = {{$.canDownload}}
let taskID = {{$.task.ID}}
let realJobName = {{$.task.JobName}}
$(document).ready(function(){
let url = window.location.href;
let urlArr = url.split('/')
userName = urlArr.slice(-5)[0]
repoPath = urlArr.slice(-4)[0]
jobID = urlArr.slice(-1)[0]
})
function stopBubbling(e) {
e = window.event || e;
if (e.stopPropagation) {
e.stopPropagation(); //阻止事件 冒泡传播
} else {
e.cancelBubble = true; //ie兼容
}
}
let timeid = window.setInterval(loadJobStatus, 30000);
$(document).ready(loadJobStatus);

function loadLog(version_name){
document.getElementById("mask").style.display = "block"
$.get(`/api/v1/repos/${userName}/${repoPath}/cloudbrain/${taskID}/log?version_name=${version_name}&lines=50&order=asc`, (data) => {
$('input[name=end_line]').val(data.EndLine)
$('input[name=start_line]').val(data.StartLine)
$(`#log_file${version_name}`).text(data.Content)
document.getElementById("mask").style.display = "none"
}).fail(function(err) {
console.log(err);
document.getElementById("mask").style.display = "none"
});
}
function loadModelFile(version_name,parents,filename,init){
parents = parents || ''
filename = filename || ''
init = init || ''
console.log("start")
$.get(`/api/v1/repos/${userName}/${repoPath}/cloudbrain/train-job/${jobID}/model_list?version_name=${version_name}&parentDir=${parents}`, (data) => {
$(`#dir_list${version_name}`).empty()
renderDir(data,version_name)
if(init==="init"){
$(`input[name=model${version_name}]`).val("")
$(`input[name=modelback${version_name}]`).val(version_name)
$(`#file_breadcrumb${version_name}`).empty()
let htmlBread = ""
htmlBread += `<div class='active section'>${version_name}</div>`
htmlBread += "<div class='divider'> / </div>"
$(`#file_breadcrumb${version_name}`).append(htmlBread)
}else{
renderBrend(version_name,parents,filename,init)
}
}).fail(function(err) {
console.log(err,version_name);
});
}
function renderBrend(version_name,parents,filename,init){
if(init=="folder"){
let htmlBrend = ""
let sectionName=$(`#file_breadcrumb${version_name} .active.section`).text()
let parents1 = $(`input[name=model${version_name}]`).val()
let filename1 = $(`input[name=modelback${version_name}]`).val()
if(parents1===""){
$(`#file_breadcrumb${version_name} .active.section`).replaceWith(`<a class='section' onclick="loadModelFile('${version_name}','${parents1}','','init')">${sectionName}</a>`)
}else{
$(`#file_breadcrumb${version_name} .active.section`).replaceWith(`<a class='section' onclick="loadModelFile('${version_name}','${parents1}','${filename1}')">${sectionName}</a>`)
}
htmlBrend += `<div class='active section'>${filename}</div>`
htmlBrend += "<div class='divider'> / </div>"
$(`#file_breadcrumb${version_name}`).append(htmlBrend)
$(`input[name=model${version_name}]`).val(parents)
$(`input[name=modelback${version_name}]`).val(filename)
}else{
$(`input[name=model${version_name}]`).val(parents)
$(`input[name=modelback${version_name}]`).val(filename)
$(`#file_breadcrumb${version_name} a.section:contains(${filename})`).nextAll().remove()
$(`#file_breadcrumb${version_name} a.section:contains(${filename})`).replaceWith(`<div class='active section'>${filename}</div>`)
$(`#file_breadcrumb${version_name} div.section:contains(${filename})`).append("<div class='divider'> / </div>")
}
}
function renderDir(data,version_name){
let html=""
html += "<div class='ui grid' style='margin:0;'>"
html += "<div class='row' style='padding: 0;'>"
html += "<div class='ui sixteen wide column' style='padding:1rem;'>"
html += "<div class='dir list'>"
html += "<table id='repo-files-table' class='ui single line table pad20'>"
html += '<tbody>'
// html += "</tbody>"
for(let i=0;i<data.Dirs.length;i++){
let dirs_size = renderSize(data.Dirs[i].Size)
html += "<tr>"
html += "<td class='name six wid'>"
html += "<span class='truncate'>"
html += "<span class='octicon octicon-file-directory'>"
html += "</span>"
if(data.Dirs[i].IsDir){
html += `<a onclick="loadModelFile('${version_name}','${data.Dirs[i].ParenDir}','${data.Dirs[i].FileName}','folder')">`
html += "<span class='fitted'><i class='folder icon' width='16' height='16' aria-hidden='true'></i>" + data.Dirs[i].FileName + "</span>"
}else{
if(downlaodFlag){
html += `<a href="${location.href}/download_model?version_name=${version_name}&fileName=${data.Dirs[i].FileName}&parentDir=${data.Dirs[i].ParenDir}&jobName=${realJobName}">`
}
else{
html += `<a class="disabled">`
}
html += "<span class='fitted'><i class='file icon' width='16' height='16' aria-hidden='true'></i>" + data.Dirs[i].FileName + "</span>"
}
html += '</a>'
html += "</span>"
html += "</td>"
html += "<td class='message seven wide'>"
if(data.Dirs[i].IsDir){
html += "<span class='truncate has-emoji'></span>"
}else{
html += "<span class='truncate has-emoji'>"+ `${dirs_size}` + "</span>"
}
html += "</td>"

html += "<td class='text right age three wide'>"
html += "<span class='truncate has-emoji'>" + data.Dirs[i].ModTime + "</span>"
html += "</td>"
html += "</tr>"
}
html += "</tbody>"
html += "</table>"
html += "</div>"
html += "</div>"
html += "</div>"
html += "</div>"
$(`#dir_list${version_name}`).append(html)
}
function renderSize(value){
if(null==value||value==''){
return "0 Bytes";
}
var unitArr = new Array("Bytes","KB","MB","GB","TB","PB","EB","ZB","YB");
var index=0;
var srcsize = parseFloat(value);
index=Math.floor(Math.log(srcsize)/Math.log(1024));
var size =srcsize/Math.pow(1024,index);
size=size.toFixed(0);//保留的小数位数
return size+unitArr[index];
}
function loadJobStatus() {
$(".ui.accordion.border-according").each((index, job) => {
const jobID = job.dataset.jobid;
const repoPath = job.dataset.repopath;
const versionname = job.dataset.version
// ['IMAGE_FAILED','SUBMIT_FAILED','DELETE_FAILED','KILLED','COMPLETED','FAILED','CANCELED','LOST','START_FAILED']
// if (job.textContent.trim() == 'IMAGE_FAILED' || job.textContent.trim() == 'SUBMIT_FAILED' || job.textContent.trim() == 'DELETE_FAILED'
// || job.textContent.trim() == 'KILLED' || job.textContent.trim() == 'COMPLETED' || job.textContent.trim() == 'FAILED'
// || job.textContent.trim() == 'CANCELED' || job.textContent.trim() == 'LOST') {
// return
// }
let status = $(`#${versionname}-status-span`).text()
if(['IMAGE_FAILED','SUBMIT_FAILED','DELETE_FAILED','KILLED','COMPLETED','FAILED','CANCELED','LOST','START_FAILED','SUCCEEDED'].includes(status)){
return
}
let stopArray=["KILLED","FAILED","START_FAILED","KILLING","COMPLETED","SUCCEEDED"]
$.get(`/api/v1/repos/${repoPath}/cloudbrain/${taskID}?version_name=${versionname}`, (data) => {
//$(`#${versionname}-duration-span`).text(data.JobDuration)
$(`#${versionname}-status-span span`).text(data.JobStatus)
$(`#${versionname}-status-span i`).attr("class",data.JobStatus)
// detail status and duration
//$('#'+versionname+'-duration').text(data.JobDuration)
$('#'+versionname+'-status').text(data.JobStatus)
if(stopArray.includes(data.JobStatus)){
$('#'+versionname+'-stop').addClass('disabled')
}
if(data.JobStatus==="COMPLETED"){
$('#'+versionname+'-create-model').removeClass('disabled').addClass('blue')
}
}).fail(function(err) {
console.log(err);
});
});
};

function refreshStatus(version_name){
$.get(`/api/v1/repos/${userName}/${repoPath}/cloudbrain/${taskID}?version_name=${versionname}`,(data)=>{
// header status and duration
//$(`#${version_name}-duration-span`).text(data.JobDuration)
$(`#${version_name}-status-span span`).text(data.JobStatus)
$(`#${version_name}-status-span i`).attr("class",data.JobStatus)
// detail status and duration
//$('#'+version_name+'-duration').text(data.JobDuration)
$('#'+version_name+'-status').text(data.JobStatus)
loadLog(version_name)


}).fail(function(err) {
console.log(err);
});
stopBubbling(arguments.callee.caller.arguments[0])
}
</script>

+ 69
- 0
templates/repo/datasets/create.tmpl View File

@@ -0,0 +1,69 @@
<style>
#dataset-base>.field>label{
width:120px !important;
text-align:right !important;
}
</style>
<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>
{{template "base/head" .}}
<div class="repository">
{{template "repo/header" .}}
<div class="ui container">
<input type="hidden" id="postPath" value="{{.Link}}">
<div style="width: 80%;margin: auto;">
<h4 class="ui top attached header">
{{.i18n.Tr "dataset.create_new_dataset"}}
</h4>
<div class="ui attached segment" style="padding: 2em 3em;">
<div class="ui form" id="dataset-base">
<el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="140px">
{{.CsrfTokenHtml}}
<el-form-item label='{{.i18n.Tr "dataset.dataset_name"}}' prop="title">
<el-input v-model="ruleForm.title"></el-input>
<span style="font-size: 12px;color: #888;line-height: 1;margin-top: 0.5em;display: inline-block;">{{.i18n.Tr "dataset.dataset_name_tooltips"}}</span>
<!-- <span>请输入字母、数字、_和-,最长64个字符,且不能以中划线(-)结尾。</span> -->
</el-form-item>
<el-form-item label='{{.i18n.Tr "dataset.dataset_description"}}' prop="description">
<el-input type="textarea" :rows="3" maxlength="1024" placeholder="{{.i18n.Tr "dataset.dataset_desc"}}" v-model="ruleForm.description"></el-input>
</el-form-item>
<el-form-item label='{{.i18n.Tr "dataset.category"}}' prop="category">
<el-select v-model="ruleForm.category" placeholder='{{.i18n.Tr "dataset.select_category"}}' style="width: 60%;">
{{range $category := categories}}
<el-option label='{{$.i18n.Tr (printf "dataset.category.%s" $category)}}' value='{{$category}}'></el-option>
{{end}}
</el-select>
</el-form-item>
<el-form-item label='{{.i18n.Tr "dataset.task"}}' prop="task">
<el-select v-model="ruleForm.task" placeholder='{{.i18n.Tr "dataset.select_task"}}' style="width: 60%;">
{{range $task := tasks}}
<el-option label='{{$.i18n.Tr (printf "dataset.task.%s" $task)}}' value='{{$task}}'></el-option>
{{end}}
</el-select>
</el-form-item>
<el-form-item label='{{.i18n.Tr "repo.license"}}' prop="license">
<el-select v-model="ruleForm.license" placeholder='{{.i18n.Tr "repo.license_helper"}}' style="width: 60%;">
{{range $license := licenses}}
<el-option label='{{$license}}' value='{{$license}}'></el-option>
{{end}}
</el-select>
</el-form-item>
<el-form-item>
<el-button style="background-color: #21ba45;" type="success" @click="createDataset('ruleForm')">{{.i18n.Tr "repo.confirm_choice"}}</el-button>
<el-button type="info" @click="cancelDataset('create','')">{{.i18n.Tr "cancel"}}</el-button>
</el-form-item>
</el-form>
</div>
</div>
</div>
</div>
</div>
{{template "base/footer" .}}

+ 72
- 0
templates/repo/datasets/edit.tmpl View File

@@ -0,0 +1,72 @@
<style>
#dataset-base>.field>label{
width:120px !important;
text-align:right !important;
}
</style>
<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>
{{template "base/head" .}}
<div class="repository">
{{template "repo/header" .}}
<div class="ui container">
<input type="hidden" id="postPath" value="{{.Link}}">
<div style="width: 80%;margin: auto;">
<h4 class="ui top attached header">
{{.i18n.Tr "dataset.modify_dataset"}}
</h4>
<div id="dataset-edit-value" style="display: none;" data-edit-id="{{.Dataset.ID}}" data-edit-title="{{.Dataset.Title}}" data-edit-description="{{.Dataset.Description}}"
data-edit-category="{{.Dataset.Category}}" data-edit-task="{{.Dataset.Task}}" data-edit-license="{{.Dataset.License}}">
</div>

<div class="ui attached segment" style="padding: 2em 3em;">
<div class="ui form" id="dataset-base">
<el-form :model="ruleForm1" :rules="rules" ref="ruleForm" label-width="140px">
{{.CsrfTokenHtml}}
<el-form-item label='{{.i18n.Tr "dataset.dataset_name"}}' prop="title">
<el-input v-model="ruleForm1.title"></el-input>
<span style="font-size: 12px;color: #888;line-height: 1;margin-top: 0.5em;display: inline-block;">{{.i18n.Tr "dataset.dataset_name_tooltips"}}</span>
<!-- <span>请输入字母、数字、_和-,最长64个字符,且不能以中划线(-)结尾。</span> -->
</el-form-item>
<el-form-item label='{{.i18n.Tr "dataset.dataset_description"}}' prop="description">
<el-input type="textarea" :rows="3" maxlength="1024" placeholder="{{.i18n.Tr "dataset.dataset_desc"}}" v-model="ruleForm1.description"></el-input>
</el-form-item>
<el-form-item label='{{.i18n.Tr "dataset.category"}}' prop="category">
<el-select v-model="ruleForm1.category" placeholder='{{.i18n.Tr "dataset.select_category"}}' style="width: 60%;">
{{range $category := categories}}
<el-option label='{{$.i18n.Tr (printf "dataset.category.%s" $category)}}' value='{{$category}}'></el-option>
{{end}}
</el-select>
</el-form-item>
<el-form-item label='{{.i18n.Tr "dataset.task"}}' prop="task">
<el-select v-model="ruleForm1.task" placeholder='{{.i18n.Tr "dataset.select_task"}}' style="width: 60%;">
{{range $task := tasks}}
<el-option label='{{$.i18n.Tr (printf "dataset.task.%s" $task)}}' value='{{$task}}'></el-option>
{{end}}
</el-select>
</el-form-item>
<el-form-item label='{{.i18n.Tr "repo.license"}}' prop="license">
<el-select v-model="ruleForm1.license" placeholder='{{.i18n.Tr "repo.license_helper"}}' style="width: 60%;">
{{range $license := licenses}}
<el-option label='{{$license}}' value='{{$license}}'></el-option>
{{end}}
</el-select>
</el-form-item>
<el-form-item>
<el-button style="background-color: #21ba45;" type="success" @click="editDataset('ruleForm',{{.Dataset.ID}})">{{.i18n.Tr "repo.confirm_choice"}}</el-button>
<el-button type="info" @click="cancelDataset('edit','')">{{.i18n.Tr "cancel"}}</el-button>
</el-form-item>
</el-form>
</div>
</div>
</div>
</div>
</div>
{{template "base/footer" .}}

+ 321
- 125
templates/repo/datasets/index.tmpl View File

@@ -6,145 +6,341 @@
margin: -1px;
background: #FFF !important;
}

.dataset_title{
font-size: 14px;
max-width: 80%;
display: inline-block !important;
margin-left: 6px !important;
padding-right: 0 !important;
}
.wrapper {
display: flex;
overflow: hidden;
padding: 0 1rem;
}
.exp{
display: none;
}
.exp:checked+.text{
max-height: none;
}
.exp:checked+.text::after{
visibility: hidden;
}
.exp:checked+.text .btn::before{
visibility: hidden;
}
.exp:checked+.text .btn::after{
content:'{{$.i18n.Tr "org.fold"}}'
}

.wrapper>.text {
font-family: SourceHanSansSC-regular;
font-size: 14px;
color: #101010;
overflow: hidden;
text-overflow: ellipsis;
text-align: justify;
position: relative;
line-height: 1.5;
max-height: 3em;
transition: .3s max-height;
word-wrap: break-word;
word-break: break-all;
}
.wrapper>.text::before {
content: '';
height: calc(100% - 20px);
float: right;
}
.wrapper>.text::after {
content: '';
width: 999vw;
height: 999vw;
position: absolute;
box-shadow: inset calc(100px - 999vw) calc(30px - 999vw) 0 0 #fff;
margin-left: -100px;
}
.btn{
position: relative;
float: right;
clear: both;
margin-left: 20px;
font-size: 14px;
padding: 0 8px;
background: #3F51B5;
line-height: 20px;
border-radius: 4px;
color: #fff;
cursor: pointer;
/* margin-top: -30px; */
}
.btn::after{
content:'{{$.i18n.Tr "org.unfold"}}'
}
.btn::before{
content: '...';
position: absolute;
left: -5px;
color: #333;
transform: translateX(-100%)
}

.el-button--text{color:#0366d6 ;}
.heart-stroke{
stroke: #666;
stroke-width: 2;
fill: #fff
}
.stars_active{
fill: #FA8C16 !important;
stroke:#FA8C16 !important
}
.diy-popper{
max-width: 400px;
}
</style>
<div class="repository release dataset-list view">
<div class="repository">
{{template "repo/header" .}}
<script>
$(document).ready(function() {
const params = new URLSearchParams(window.location.search);
if (params.get('type') == 0){
$('.contorl_component').attr("id", 'minioUploader')
}else{
$('.contorl_component').attr("id", 'obsUploader')
}
});
</script>
<form class="ui container" action="{{.Link}}" method="post">
<input name="id" value="{{.dataset.ID}}" type="hidden" />
<!--
<span class="alert" style="font-size:20px;color:red">
<strong>{{.i18n.Tr "dataset.alert"}}</strong>
</span>
-->
<div id="datasetId" datasetId="{{.dataset.ID}}">
{{.CsrfTokenHtml}}
{{template "base/alert" .}}
<div class="ui stackable grid {{if .Error}}hide{{end}}" id="dataset-content">
<div class="row">
<div class="column sixteen {{if .Permission.CanWrite $.UnitTypeDatasets}}twelve{{end}} wide">
<h2>{{.dataset.Title}}</h2>
</div>
{{if .Permission.CanWrite $.UnitTypeDatasets}}
<div class="column four wide right aligned">
<a class="ui green button" href="javascript:void(0)" id="dataset-edit">
{{.i18n.Tr "dataset.edit"}}
</a>
</div>
{{end}}
</div>
<div class="row">
<div class="column sixteen wide">
{{if .dataset.Description }}
<span class="no-description text-italic">{{.dataset.Description}}</span>
{{else}}
<span class="no-description text-italic">{{.Repository.DescriptionHTML}}</span>
{{if .dataset}}
<div id="dataset-range-value" data-num-stars="{{.dataset.NumStars}}" data-star-active="{{$.IsStaringDataset}}" style="display: none;">
{{range .Attachments}}
<div class="item" data-private="{{.IsPrivate}}" data-decompress-state="{{.DecompressState}}"></div>
{{end}}
</div>
<div id="dataset-base">
<div class="ui container">
<div class="ui mobile reversed stackable grid">
<div class="row">
<div class="column thirteen wide"><h2>{{.dataset.Title}}</h2></div>
<div class="column three wide right aligned">
<span style="display: flex;align-items: center;justify-content: flex-end;height: 36px;">
{{if $.IsSigned}}
<div style="line-height: 1;margin-right: 4px;margin-bottom: -2px;padding: 0 10px;" @click="postStar({{.dataset.ID}},'{{.Link}}')">
<svg width="1.4em" height="1.4em" viewBox="0 0 32 32" class="heart-stroke" :class='{stars_active:star_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>
</div>
<span style="line-height: 1;">${num_stars}</span>
{{else}}
<div style="line-height: 1;margin-right: 4px;margin-bottom: -2px;padding: 0 10px;">
<svg width="1.4em" height="1.4em" viewBox="0 0 32 32" class="heart-stroke" :class='{stars_active:star_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>
</div>
<span style="line-height: 1;">${num_stars}</span>
{{end}}
<a style="margin-left:30px;" href="{{.RepoLink}}/datasets/edit/{{.dataset.ID}}" class="ui primary basic mini {{if not $.CanWrite}} disabled {{end}} button">{{.i18n.Tr "repo.modelarts.modify"}}</a>
</span>
</div>
{{if or (.dataset.Category) (.dataset.Task) (.dataset.License)}}
<div class="column thirteen wide">
{{if .dataset.Category}}
{{$category := .dataset.Category}}
<a class="ui repo-topic label topic" href="{{AppSubUrl}}/explore/datasets?sort={{$.SortType}}&q={{$.Keyword}}&tab={{$.TabName}}&category={{.dataset.Category}}&task={{$.Task}}&license={{$.License}}">{{$.i18n.Tr (printf "dataset.category.%s" $category)}}</a>
{{end}}
{{if .dataset.Task}}
{{$task := .dataset.Task}}
<a class="ui repo-topic label topic" href="{{AppSubUrl}}/explore/datasets?sort={{$.SortType}}&q={{$.Keyword}}&tab={{$.TabName}}&category={{$.Category}}&task={{.dataset.Task}}&license={{$.License}}">{{$.i18n.Tr (printf "dataset.task.%s" $task)}}</a>
{{end}}
{{if .dataset.License}}
<a class="ui repo-topic label topic" href="{{AppSubUrl}}/explore/datasets?sort={{$.SortType}}&q={{$.Keyword}}&tab={{$.TabName}}&category={{$.Category}}&task={{$.Task}}&license={{.dataset.License}}">{{.dataset.License}}</a>
{{end}}
</div>
{{end}}
</div>
</div>
</div>

<div class="ui grid form segment success {{if not .Error}}hide{{end}}" id="dataset-content-edit">
<label class="d-block">{{.i18n.Tr "dataset.title"}}</label>
<div class="sixteen wide column">
<input name="title" placeholder='{{.i18n.Tr "dataset.title"}}' value="{{.dataset.Title}}" autofocus required maxlength="255">
</div>
<label class="d-block">{{.i18n.Tr "dataset.description"}}</label>
<div class="sixteen wide column">
<textarea name="description" rows="3">{{.dataset.Description}}</textarea>
</div>
<input name="type" value="{{.Type}}" type="hidden" />
<div class="sixteen wide column">
<a class="ui button" id="cancel">{{.i18n.Tr "cancel"}}</a>
<button class="ui green button" id="submit">{{.i18n.Tr "dataset.update_dataset"}}</button>
</div>
</div>

<div class="ui blue mini menu selectcloudbrain">
<a class="{{if eq .Type 0}}active {{end}}item" href="{{.RepoLink}}/datasets?type=0">{{svg "octicon-server" 16}} CPU / GPU</a>
<a class="{{if eq .Type 1}}active {{end}}item" href="{{.RepoLink}}/datasets?type=1">{{svg "octicon-server" 16}} Ascend NPU</a>
</div>
<div class="ui stackable grid">
<div class="twelve wide column">
<div class="ui sixteen wide column">
<div class="ui two column stackable grid">
<div class="column">
<strong>{{if eq .Type 0}}{{.i18n.Tr "repo.cloudbrain1"}}{{else}}{{.i18n.Tr "repo.cloudbrain2"}}{{end}}-{{.i18n.Tr "datasets"}}</strong>
</div>
<div class="column right aligned" style="z-index:1">
<div class="ui right dropdown type jump item">
<span class="text">
{{.i18n.Tr "repo.issues.filter_sort"}}<i class="dropdown icon"></i>
</span>
<div class="menu">
<a class="item" href="{{$.Link}}?sort=newest&q={{$.Keyword}}&tab={{$.TabName}}&type={{.Type}}">{{.i18n.Tr "repo.issues.filter_sort.latest"}}</a>
<a class="item" href="{{$.Link}}?sort=oldest&q={{$.Keyword}}&tab={{$.TabName}}&type={{.Type}}">{{.i18n.Tr "repo.issues.filter_sort.oldest"}}</a>
{{if .dataset.Description}}
<div class="row" style="padding-top: 0;">
<div class=" wrapper">
<input id="exp1" class="exp" type="checkbox">
<div class="text">
<label class="btn" for="exp1"></label>
{{.dataset.Description}}
</div>
</div>
</div>
</div>
</div>
<div class="dataset list">
{{template "repo/datasets/dataset_list" .}}
{{end}}
<div class="row">
<div class="column ten wide"></div>
<div class="column six wide right aligned">
<el-select v-model="datasetType" style="width: 40%;" size="small" @change="changeDatasetType">
<i slot="prefix" style="display: inline-block;color: #101010;" class="el-input__icon ri-archive-drawer-line"></i>
<el-option label="{{$.i18n.Tr "repo.gpu_type_all"}}" value="-1"></el-option>
<el-option label="CPU/GPU" value="0"></el-option>
<el-option label="NPU" value="1"></el-option>
</el-select>
<el-button icon="el-icon-upload" {{if not $.CanWrite}} disabled {{end}} type="primary" size="small" @click="gotoUpload('{{.RepoLink}}',{{.dataset.ID}})">{{$.i18n.Tr "dataset.dataset_upload"}}</el-button>
</div>
</div>
<div class="dataset ui middle very relaxed page">
<div class="column">
{{if .Permission.CanWrite $.UnitTypeDatasets}}
<div style='display:none;'
id="minioUploader-params"
data-uuid="{{.uuid}}"
data-add-url="{{AppSubUrl}}/attachments/add"
data-accepts="{{.AttachmentAllowedTypes}}"
data-remove-url="{{AppSubUrl}}/attachments/delete"
data-csrf="{{.CsrfToken}}"
dataset-id={{.dataset.ID}}
data-max-file="100"
data-dataset-id="{{.dataset.ID}}"
data-max-size="{{.AttachmentMaxSize}}"
data-default-message="{{.i18n.Tr "dropzone.default_message"}}"
data-invalid-input-type="{{.i18n.Tr "dropzone.invalid_input_type"}}"
data-file-too-big="{{.i18n.Tr "dropzone.file_too_big"}}"
data-remove-file="{{.i18n.Tr "dropzone.remove_file"}}"
data-file-status='{{.i18n.Tr "dropzone.file_status"}}'
data-file-init-status='{{.i18n.Tr "dropzone.file_init_status"}}'
data-waitting-uploading='{{.i18n.Tr "dropzone.waitting_uploading"}}'
data-md5-computing='{{.i18n.Tr "dropzone.md5_computing"}}'
data-obs-connecting='{{.i18n.Tr "dropzone.obs-connecting"}}'
data-loading-file='{{.i18n.Tr "dropzone.loading_file"}}'
data-upload-complete='{{.i18n.Tr "dropzone.upload_complete"}}'
data-uploading='{{.i18n.Tr "dropzone.uploading"}}'
data-failed='{{.i18n.Tr "dropzone.failed"}}'
>
</div>
<div class="contorl_component"></div>
{{end}}
<div class="row">
<div class="ui sixteen wide column dataset">
<div class="ui grid stackable" style="background: #f0f0f0;;">
<div class="row">
<!-- 数据集名称 -->
<div class="four wide column" style="width: 24% !important;">
<span style="margin:0 6px">{{$.i18n.Tr "dataset.dataset_file_name"}}</span>
</div>
<div class="one wide column text center" style="width: 7.25% !important;">
{{$.i18n.Tr "repo.model.manage.size"}}
</div>
<div class="two wide column text center">
{{$.i18n.Tr "dataset.dataset_available_clusters"}}
</div>
<div class="one wide column text center">
{{$.i18n.Tr "repo.modelarts.status"}}
</div>
<div class="one wide column text center">
{{$.i18n.Tr "repo.cloudbrain_creator"}}
</div>
<div class="three wide column text center">
{{$.i18n.Tr "dataset.dataset_upload_time"}}
</div>
<div class="four wide column text center">
{{$.i18n.Tr "repo.cloudbrain_operate"}}
</div>
</div>
</div>
{{range $k, $v :=.Attachments}}
<div class="ui grid stackable item" id="{{.FileChunk.UUID}}">
<div class="row">
<!-- 数据集名称 -->

<div class="four wide column" style="width: 24% !important;display: flex;align-items: center;">
{{if .Description}}
<el-tooltip class="item" effect="dark" placement="top" popper-class="diy-popper">
<div slot="content" >{{.Description}}</br><span><i class="ri-download-line"></i>{{$.i18n.Tr "dataset.download"}}:{{.DownloadCount}}</span></div>
<a class="dataset_title title" href="{{.DownloadURL}}" title="{{.Name}}" style="border: none;">
{{.Name}}
</a>
</el-tooltip>
{{else}}
<el-tooltip class="item" effect="dark" placement="top" popper-class="diy-popper">
<div slot="content" ><span><i class="ri-download-line"></i>{{$.i18n.Tr "dataset.download"}}:{{.DownloadCount}}</span></div>
<a class="dataset_title title" href="{{.DownloadURL}}" title="{{.Name}}" style="border: none;">
{{.Name}}
</a>
</el-tooltip>
{{end}}
<i class="ri-lock-2-line" style="color: #fa8c16;" v-if="privates[{{$k}}]"></i>
<!-- <i class="COMPLETED" v-if="zipStatus[{{$k}}]==1"></i>
<i class="WAITING" v-if="zipStatus[{{$k}}]==2"></i>
<i class="FAILED" v-if="zipStatus[{{$k}}]==3"></i> -->
</div>
<div class="one wide column text center" style="width: 7.25% !important;">
{{.Size | FileSize}}
</div>
<div class="two wide column text center">
{{.Type | AttachmentResourceType}}
</div>
<div class="one wide column text center">
{{$x:=.IsPrivate | AttachmentStatus}}
<span style="color: #fa8c16;" v-if="privates[{{$k}}]">{{$.i18n.Tr "home.show_private"}}</span>
<span style="color: #13c28d;" v-else="privates[{{$k}}]">{{$.i18n.Tr "org.settings.visibility.public"}}</span>
</div>
<div class="one wide column text center">
{{if .Uploader.Name}}
<a href="{{AppSubUrl}}/{{.Uploader.Name}}" title="{{.Uploader.Name}}"><img class="ui avatar image" src="{{AppSubUrl}}/user/avatar/{{.Uploader.Name}}/-1"></a>
{{else}}
<a title="Ghost"><img class="ui avatar image" src="{{AppSubUrl}}/user/avatar/Ghost/-1"></a>
{{end}}
</div>
<div class="three wide column text center">
{{.CreatedUnix | TimeSinceUnix1}}
</div>
<div class="four wide column text right">
<!-- <el-button type="text">下载</el-button>
<el-button type="text">预览</el-button>
<el-button type="text">标注</el-button>
<el-button type="text">
<el-popover
placement="right"
width="400"
trigger="click">
<span>asdasd</span>
<el-button slot="reference" type="text"><i class="ri-more-line"></i></el-button>
</el-popover>
</el-button> -->
<div class="ui compact buttons">
<a class="ui basic blue button" href="{{.DownloadURL}}">{{$.i18n.Tr "dataset.download"}}</a>
{{if eq .DecompressState 1}}
<a class="ui basic blue button" href="datasets/dirs/{{.UUID}}?type={{$.Type}}" data-tooltip='{{$.i18n.Tr "dataset.directory"}}'>{{$.i18n.Tr "preview"}}</a>
{{end}}
{{if and (.CanDel) (not $.Repository.IsPrivate)}}
<span class="ui basic blue button" style="color: #13c28d !important;" @click="setPrivate('{{.FileChunk.UUID}}',false,{{$k}})" v-if="privates[{{$k}}]">{{$.i18n.Tr "dataset.set_public"}}</span>
<span class="ui basic blue button" style="color: #fa8c16 !important;" @click="setPrivate('{{.FileChunk.UUID}}',true,{{$k}})" v-else="privates[{{$k}}]">{{$.i18n.Tr "dataset.set_private"}}</span>
{{end}}
<!-- {{if $.CanRead}}
<a class="ui basic blue button" href="datasets/label/{{.UUID}}?type={{$.Type}}" data-tooltip='{{$.i18n.Tr "dataset.create_label_task"}}'>标注</a>
{{else}}
<a class="ui basic disabled button">标注</a>
{{end}} -->
<a class="ui basic blue button">
<el-dropdown size="medium">
<span class="el-dropdown-link">
{{$.i18n.Tr "repo.more"}}<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item class="clipboard" data-clipboard-text="{{.DownloadURL}}" data-clipboard-action="copy">{{$.i18n.Tr "dataset.copy_url"}}</el-dropdown-item>
<el-dropdown-item class="clipboard" data-clipboard-text="{{.FileChunk.Md5}}" data-clipboard-action="copy">{{$.i18n.Tr "dataset.copy_md5"}}</el-dropdown-item>
{{if and ($.CanWrite) (eq .DecompressState 1) }}
<el-dropdown-item @click.native="gotoAnnotate('{{$.RepoLink}}','{{.UUID}}',{{.Type}})">{{$.i18n.Tr "dataset.annotation"}}</el-dropdown-item>
{{end}}
{{if .CanDel}}
<el-dropdown-item @click.native="gotoDatasetEidt('{{$.RepoLink}}',{{.ID}})">{{$.i18n.Tr "dataset.modify_description"}}</el-dropdown-item>
<el-dropdown-item style="color: red;" @click.native="delDataset('{{.FileChunk.UUID}}')">{{$.i18n.Tr "dataset.delete"}}</el-dropdown-item>
{{end}}
</el-dropdown-menu>
</el-dropdown>
</a>
</div>
</div>
</div>
</div>
{{end}}
</div>

</div>
</div>
<div class="four wide column">
{{template "repo/datasets/right_side" .}}
</div>
</div>
</form>
</div>
<div id="app" style="margin-top: 2rem;">
<div class="center">
<el-pagination
background
@current-change="handleCurrentChange"
:current-page="page"
:page-sizes="[10]"
:page-size="10"
layout="total, sizes, prev, pager, next, jumper"
:total="{{.Page.Paginater.Total}}">
</el-pagination>
</div>
</div>
{{else}}
<div class="ui placeholder segment bgtask-none">
<div class="ui icon header bgtask-header-pic"></div>
<div class="bgtask-content-header">{{.i18n.Tr "dataset.dataset_no_create"}}</div>
{{if $.CanWrite}}
<a class="ui green button" href="{{.RepoLink}}/datasets/create">{{.i18n.Tr "dataset.create_new_dataset"}}</a>
{{end}}
<div class="bgtask-content">
<div class="bgtask-content-txt">{{.i18n.Tr "dataset.dataset_explain"}}</div>
<div class="bgtask-content-txt">{{.i18n.Tr "dataset.dataset_instructions_for_use"}}<a href="https://git.openi.org.cn/zeizei/OpenI_Learning">{{.i18n.Tr "dataset.dataset_camp_course"}}</a></div>
</div>
</div>
{{end}}
</div>

<div class="ui small basic delete modal" id="data-dataset-delete-modal">
<div class="ui icon header">
<i class="trash icon"></i>
<div class="ui icon header">
<i class="trash icon"></i>
{{.i18n.Tr "dataset.attachment.delete"}}
</div>
<div class="content">
<p>{{.i18n.Tr "dataset.attachment.delete_desc" | Str2html}}</p>
</div>
{{template "base/delete_modal_actions" .}}
</div>
<div class="content">
<p>{{.i18n.Tr "dataset.attachment.delete_desc" | Str2html}}</p>
</div>
{{template "base/delete_modal_actions" .}}
</div>
{{template "base/footer" .}}

+ 14
- 1
templates/repo/debugjob/index.tmpl View File

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


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

@@ -138,7 +138,7 @@
{{end}}

{{if .Permission.CanRead $.UnitTypeDatasets}}
<a class="{{if .PageIsDataset}}active{{end}} item" href="{{.RepoLink}}/datasets?type=0">
<a class="{{if .PageIsDataset}}active{{end}} item" href="{{.RepoLink}}/datasets">
<svg class="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="none" d="M0 0h24v24H0z"/><path d="M20.083 15.2l1.202.721a.5.5 0 0 1 0 .858l-8.77 5.262a1 1 0 0 1-1.03 0l-8.77-5.262a.5.5 0 0 1 0-.858l1.202-.721L12 20.05l8.083-4.85zm0-4.7l1.202.721a.5.5 0 0 1 0 .858L12 17.65l-9.285-5.571a.5.5 0 0 1 0-.858l1.202-.721L12 15.35l8.083-4.85zm-7.569-9.191l8.771 5.262a.5.5 0 0 1 0 .858L12 13 2.715 7.429a.5.5 0 0 1 0-.858l8.77-5.262a1 1 0 0 1 1.03 0zM12 3.332L5.887 7 12 10.668 18.113 7 12 3.332z"/></svg>
{{.i18n.Tr "datasets"}}
</a>


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

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

</div>


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

@@ -232,7 +232,13 @@ td, th {

<td class="ti-text-form-content">
<div class="text-span text-span-w">
<span style="font-size: 12px;" class="">{{TimeSinceUnix1 .CreatedUnix}}</span>
<span style="font-size: 12px;" class="">
{{if not (eq .StartTime 0)}}
{{TimeSinceUnix1 .StartTime}}
{{else}}
{{TimeSinceUnix1 .CreatedUnix}}
{{end}}
</span>
</div>
</td>
</tr>


+ 10
- 30
templates/repo/modelarts/notebook/new.tmpl View File

@@ -59,17 +59,7 @@
{{end}}
</select>
</div>

<div class="inline field">
<label>{{.i18n.Tr "cloudbrain.dataset"}}</label>
<input type="text" list="cloudbrain_dataset" placeholder="{{.i18n.Tr "cloudbrain.select_dataset"}}" name="" id="answerInput" autofocus maxlength="36">
<datalist id="cloudbrain_dataset" class="ui search" style='width:385px' name="attachment">
{{range .attachments}}
<option name="attachment" data-value="{{.UUID}}">{{.Attachment.Name}}</option>
{{end}}
</datalist>
<input type="hidden" name="attachment" id="answerInput-hidden">
</div>
{{template "custom/select_dataset" .}}

<!--<div class="inline required field">
<label>工作环境</label>
@@ -99,8 +89,8 @@
<div class="inline field">
<label></label>
<button class="ui green button">
{{.i18n.Tr "repo.cloudbrain.new"}}
</button>
{{.i18n.Tr "repo.cloudbrain.new"}}
</button>
<a class="ui button cancel" href="{{.RepoLink}}/debugjob?debugListType=all">{{.i18n.Tr "repo.cloudbrain.cancel"}}</a>
</div>
</div>
@@ -149,7 +139,6 @@

$('select.dropdown')
.dropdown();

$(function() {
$("#cloudbrain_job_type").change(function() {
if ($(this).val() == 'BENCHMARK') {
@@ -159,20 +148,11 @@
}
})
})
document.querySelector('input[list]').addEventListener('input',function(e){
var input = e.target,
list = input.getAttribute('list'),
options = document.querySelectorAll('#'+list+' option'),
hiddenInput = document.getElementById(input.getAttribute('id')+'-hidden'),
inputValue = input.value;
hiddenInput.value = inputValue;
for (let i=0;i<options.length;i++){
var option = options[i]
if(option.innerText===inputValue){

hiddenInput.value = option.getAttribute('data-value');
break
}
}
})
$(document).ready(function(){
$(document).keydown(function(event){
if(event.keyCode==13){
event.preventDefault();
}
});
});
</script>

+ 53
- 5
templates/repo/modelarts/trainjob/index.tmpl View File

@@ -39,8 +39,18 @@
</div>
</div>
<div class="column right aligned">
<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;">
{{svg "octicon-server" 16}}
<div class="default text" style="color: rgba(0,0,0,.87);"></div>
<i class="dropdown icon"></i>
<div class="menu">
<div class="item" data-value="all">{{$.i18n.Tr "repo.gpu_type_all"}}</div>
<div class="item" data-value="CPU/GPU">CPU/GPU</div>
<div class="item" data-value="NPU">NPU</div>
</div>
</div>
{{if .Permission.CanWrite $.UnitTypeCloudBrain}}
<a class="ui green button" href="{{.RepoLink}}/modelarts/train-job/create">{{$.i18n.Tr "repo.modelarts.train_job.new_train"}}</a>
<a class="ui green button" href="{{.RepoLink}}/cloudbrain/train-job/create">{{$.i18n.Tr "repo.modelarts.train_job.new_train"}}</a>
{{else}}
<a class="ui disabled button" >{{$.i18n.Tr "repo.modelarts.train_job.new_train"}}</a>
{{end}}
@@ -102,7 +112,7 @@

<!-- 任务名 -->
<div class="three wide column padding0">
<a class="title" href="{{$.Link}}/{{.JobID}}" title="{{.DisplayJobName}}" style="font-size: 14px;">
<a class="title" href='{{if eq .ComputeResource "NPU" }}{{$.Link}}/{{.JobID}}{{else}}{{$.RepoLink}}/cloudbrain/train-job/{{.JobID}}{{end}}' title="{{.DisplayJobName}}" style="font-size: 14px;">

<span class="fitted" style="width: 90%;vertical-align: middle;">{{.DisplayJobName}}</span>
</a>
@@ -143,7 +153,7 @@
<div class="ui compact buttons">
{{$.CsrfTokenHtml}}
{{if .CanDel}}
<a style="padding: 0.5rem 1rem;" id="ai-stop-{{.JobID}}" class="ui basic ai_stop_version {{if eq .Status "KILLED" "FAILED" "START_FAILED" "KILLING" "COMPLETED"}}disabled {{else}} blue {{end}}button" data-repopath="{{$.RepoRelPath}}/modelarts/train-job" data-jobid="{{.JobID}}" data-version="{{.VersionName}}">
<a style="padding: 0.5rem 1rem;" id="ai-stop-{{.JobID}}" class="ui basic ai_stop_version {{if eq .Status "KILLED" "FAILED" "START_FAILED" "KILLING" "COMPLETED" "SUCCEEDED" "STOPPED"}}disabled {{else}} blue {{end}}button" data-repopath='{{$.RepoRelPath}}{{if eq .ComputeResource "NPU"}}/modelarts/train-job{{else}}/cloudbrain/train-job{{end}}' data-jobid="{{.JobID}}" data-version="{{.VersionName}}">
{{$.i18n.Tr "repo.stop"}}
</a>
{{else}}
@@ -154,7 +164,8 @@

</div>
<!-- 删除任务 -->
<form class="ui compact buttons" id="delForm-{{.JobID}}" action="{{$.Link}}/{{.JobID}}/del" method="post">
<form class="ui compact buttons" id="delForm-{{.JobID}}" action='{{if eq .ComputeResource "NPU" }}{{$.Link}}/{{.JobID}}{{else}}{{$.RepoLink}}/cloudbrain/train-job/{{.JobID}}{{end}}/del' method="post">
<input type="hidden" name="listType" value="{{$.ListType}}">
{{$.CsrfTokenHtml}}
{{if .CanDel}}
<a style="padding: 0.5rem 1rem;margin-left:0.2rem" id="ai-delete-{{.JobID}}" class="ui basic ai_delete blue button" style="border-radius: .28571429rem;">
@@ -169,7 +180,20 @@
</div>
</div>
</div>
{{end}} {{template "base/paginate" .}}
{{end}}
<div id="app" style="margin-top: 2rem;">
<div class="center">
<el-pagination
background
@current-change="handleCurrentChange"
:current-page="page"
:page-sizes="[10]"
:page-size="10"
layout="total, sizes, prev, pager, next, jumper"
:total="{{.Page.Paginater.Total}}">
</el-pagination>
</div>
</div>
</div>

</div>
@@ -206,3 +230,27 @@
</div>
</div>
{{template "base/footer" .}}

<script>
let url = {{$.RepoLink}};
let all = {{$.i18n.Tr "repo.gpu_type_all"}}
$(document).ready(function(){
const params = new URLSearchParams(location.search)
if(!location.search){
$('.default.text').text(all)
}else{
if(params.has('listType') && params.get('listType')=='all'){
$('.default.text').text(all)
}
else{
$('.default.text').text(params.get('listType'))
}
}

$('.ui.selection.dropdown').dropdown({
onChange:function(value){
location.href = `${url}/modelarts/train-job?listType=${value}`
}
})
})
</script>

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

@@ -79,6 +79,24 @@
<input type="hidden" id="ai_flaver_name" name="flaver_names" value="">
<h4 class="unite title ui header ">{{.i18n.Tr "repo.modelarts.train_job.basic_info"}}:</h4>
<div class="required unite min_title inline field">
<label style="font-weight: normal;">{{.i18n.Tr "cloudbrain.compute_resource"}}</label>
<div class="ui blue mini menu compact selectcloudbrain">
<a class="item" href="{{.RepoLink}}/cloudbrain/train-job/create">
<svg class="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M3 2.992C3 2.444 3.445 2 3.993 2h16.014a1 1 0 0 1 .993.992v18.016a.993.993 0 0 1-.993.992H3.993A1 1 0 0 1 3 21.008V2.992zM19 11V4H5v7h14zm0 2H5v7h14v-7zM9 6h6v2H9V6zm0 9h6v2H9v-2z"/>
</svg>
CPU/GPU
</a>
<a class="active item" href="{{.RepoLink}}/modelarts/train-job/create">
<svg class="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M3 2.992C3 2.444 3.445 2 3.993 2h16.014a1 1 0 0 1 .993.992v18.016a.993.993 0 0 1-.993.992H3.993A1 1 0 0 1 3 21.008V2.992zM19 11V4H5v7h14zm0 2H5v7h14v-7zM9 6h6v2H9V6zm0 9h6v2H9v-2z"/>
</svg>
Ascend NPU</a>
</div>
</div>
<div class="required unite min_title inline field">
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.job_name"}}</label>
<input style="width: 60%;" name="display_job_name" id="display_job_name" placeholder={{.i18n.Tr "repo.modelarts.train_job.job_name"}} value="{{.display_job_name}}" tabindex="3" onkeyup="this.value=this.value.replace(/[, ]/g,'')" autofocus required maxlength="64">
<span class="tooltips" style="display: block;">{{.i18n.Tr "cloudbrain.job_name_rule"}}</span>
@@ -395,7 +413,7 @@
identifier : 'display_job_name',
rules: [
{
type: 'regExp[/^[a-zA-Z0-9-_]{1,64}[^-]$/]',
type: 'regExp[/^[a-zA-Z0-9-_]{1,64}[a-zA-Z0-9_]$/]',
}
]
},


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

@@ -223,7 +223,12 @@ td, th {
</div>
<div class="ac-display-inblock title_text acc-margin-bottom">

<span class="cti-mgRight-sm">{{TimeSinceUnix1 .Cloudbrain.CreatedUnix}}</span>
<span class="cti-mgRight-sm">
{{if not (eq .Cloudbrain.StartTime 0)}}
{{TimeSinceUnix1 .Cloudbrain.StartTime}}
{{else}}
{{TimeSinceUnix1 .Cloudbrain.CreatedUnix}}
{{end}}</span>
<span class="cti-mgRight-sm"> {{$.i18n.Tr "repo.modelarts.current_version"}}:{{.VersionName}}</span>
<span class="cti-mgRight-sm"> {{$.i18n.Tr "repo.modelarts.parent_version"}}:{{.PreVersionName}}</span>
<span class="cti-mgRight-sm">{{$.i18n.Tr "repo.modelarts.status"}}:
@@ -293,7 +298,12 @@ td, th {

<td class="ti-text-form-content">
<div class="text-span text-span-w">
<span style="font-size: 12px;" class="">{{TimeSinceUnix1 .Cloudbrain.CreatedUnix}}</span>
<span style="font-size: 12px;" class="">
{{if not (eq .Cloudbrain.StartTime 0)}}
{{TimeSinceUnix1 .Cloudbrain.StartTime}}
{{else}}
{{TimeSinceUnix1 .Cloudbrain.CreatedUnix}}
{{end}}</span>
</div>
</td>
</tr>


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

@@ -221,7 +221,7 @@
}
function loadTrainList(){
$.get(`${repolink}/modelmanage/query_train_job?repoId=${repoId}`, (data) => {
console.log(data)
const n_length = data.length
let train_html=''
for (let i=0;i<n_length;i++){


+ 5
- 1
templates/user/dashboard/feeds.tmpl View File

@@ -77,13 +77,15 @@
{{else if eq .GetOpType 26}}
{{$.i18n.Tr "action.task_npudebugjob" .GetRepoLink .Content .RefName | Str2html}}
{{else if eq .GetOpType 27}}
{{$.i18n.Tr "action.task_trainjob" .GetRepoLink .Content .RefName | Str2html}}
{{$.i18n.Tr "action.task_nputrainjob" .GetRepoLink .Content .RefName | Str2html}}
{{else if eq .GetOpType 28}}
{{$.i18n.Tr "action.task_inferencejob" .GetRepoLink .Content .RefName | Str2html}}
{{else if eq .GetOpType 29}}
{{$.i18n.Tr "action.task_benchmark" .GetRepoLink .Content .RefName | Str2html}}
{{else if eq .GetOpType 30}}
{{$.i18n.Tr "action.task_createmodel" .GetRepoLink .RefName .RefName | Str2html}}
{{else if eq .GetOpType 31}}
{{$.i18n.Tr "action.task_gputrainjob" .GetRepoLink .Content .RefName | Str2html}}
{{end}}
</p>
{{if or (eq .GetOpType 5) (eq .GetOpType 18)}}
@@ -129,6 +131,8 @@
<span class="text grey"><i class="ri-vip-crown-line icon big"></i></span>
{{else if eq .GetOpType 30}}
<span class="text grey"><i class="ri-picture-in-picture-exit-line icon big"></i></span>
{{else if eq .GetOpType 31}}
<span class="text grey"><i class="ri-character-recognition-line icon big"></i></span>
{{else}}
<span class="text grey">{{svg (printf "octicon-%s" (ActionIcon .GetOpType)) 32}}</span>
{{end}}


+ 121
- 51
web_src/js/components/MinioUploader.vue View File

@@ -8,9 +8,11 @@
{{ file_status_text }}
<strong class="success text red">{{ status }}</strong>
</p>
<p>说明:<br>
<el-button style="background-color: #21ba45;" type="success" :disabled="btnFlag" @click="onFileAdded">{{upload}}</el-button>
<el-button type="info" @click="cancelDataset">{{cancel}}</el-button>
<!-- <p>说明:<br>
- 只有zip格式的数据集才能发起云脑任务;<br>
- 云脑1提供 <span class="text blue">CPU / GPU</span> 资源,云脑2提供 <span class="text blue">Ascend NPU</span> 资源;调试使用的数据集也需要上传到对应的环境。</p>
- 云脑1提供 <span class="text blue">CPU / GPU</span> 资源,云脑2提供 <span class="text blue">Ascend NPU</span> 资源;调试使用的数据集也需要上传到对应的环境。</p> -->
</div>
</template>

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

const {_AppSubUrl, _StaticUrlPrefix, csrf} = window.config;
const CloudBrainType = 0;
// const uploadtype = 0;

export default {
props:{
uploadtype:{
type:Number,
required:true
},
desc:{
type:String,
default:''
}
},
data() {
return {
dropzoneUploader: null,
@@ -36,7 +48,12 @@ export default {
progress: 0,
status: '',
dropzoneParams: {},
file_status_text: ''
file_status_text: '',
file:{},
repoPath:'',
btnFlag:false,
cancel:'',
upload:'',
};
},

@@ -44,33 +61,47 @@ export default {
this.dropzoneParams = $('div#minioUploader-params');
this.file_status_text = this.dropzoneParams.data('file-status');
this.status = this.dropzoneParams.data('file-init-status');

let previewTemplate = '';
previewTemplate += '<div class="dz-preview dz-file-preview">\n ';
previewTemplate += ' <div class="dz-details">\n ';
previewTemplate += ' <div class="dz-filename">';
previewTemplate +=
' <span data-dz-name data-dz-thumbnail></span>';
previewTemplate += ' </div>\n ';
previewTemplate += ' <div class="dz-size" data-dz-size style="white-space: nowrap"></div>\n ';
previewTemplate += ' </div>\n ';
previewTemplate += ' <div class="dz-progress ui active progress">';
previewTemplate +=
' <div class="dz-upload bar" data-dz-uploadprogress><div class="progress"></div></div>\n ';
previewTemplate += ' </div>\n ';
previewTemplate += ' <div class="dz-success-mark">';
previewTemplate += ' <span>上传成功</span>';
previewTemplate += ' </div>\n ';
previewTemplate += ' <div class="dz-error-mark">';
previewTemplate += ' <span>上传失败</span>';
previewTemplate += ' </div>\n ';
previewTemplate += ' <div class="dz-error-message">';
previewTemplate += ' <span data-dz-errormessage></span>';
previewTemplate += ' </div>\n';
previewTemplate += '</div>';
this.repoPath = this.dropzoneParams.data('repopath');
this.cancel = this.dropzoneParams.data('cancel');
this.upload = this.dropzoneParams.data('upload');
// let previewTemplate = '';
// previewTemplate += '<div class="dz-preview dz-file-preview">\n ';
// previewTemplate += ' <div class="dz-details">\n ';
// previewTemplate += ' <div class="dz-filename">';
// previewTemplate +=
// ' <span data-dz-name data-dz-thumbnail></span>';
// previewTemplate += ' </div>\n ';
// previewTemplate += ' <div class="dz-size" data-dz-size style="white-space: nowrap"></div>\n ';
// previewTemplate += ' </div>\n ';
// previewTemplate += ' <div class="dz-progress ui active progress">';
// previewTemplate +=
// ' <div class="dz-upload bar" data-dz-uploadprogress><div class="progress"></div></div>\n ';
// previewTemplate += ' </div>\n ';
// previewTemplate += ' <div class="dz-success-mark">';
// previewTemplate += ' <span>上传成功</span>';
// previewTemplate += ' </div>\n ';
// previewTemplate += ' <div class="dz-error-mark">';
// previewTemplate += ' <span>上传失败</span>';
// previewTemplate += ' </div>\n ';
// previewTemplate += ' <div class="dz-error-message">';
// previewTemplate += ' <span data-dz-errormessage></span>';
// previewTemplate += ' </div>\n';
// previewTemplate += '</div>';
let previewTemplate = ''
previewTemplate += '<div class="dz-preview dz-file-preview" style="width:100%;background: none;">'
previewTemplate += '<div class="dz-details" style="opacity: 1;">'
previewTemplate += '<div class="dz-filename"><span data-dz-name></span></div>'
previewTemplate += '<div class="dz-size" data-dz-size></div>'
previewTemplate += '<div class="dz-progress ui active progress" style="top: 75%;width: 80%;left: 15%;"><div class="dz-upload bar" data-dz-uploadprogress><div class="progress"></div></div></div>'
// previewTemplate += '<img data-dz-thumbnail />'
previewTemplate += '</div>'
previewTemplate += '<div class="dz-success-mark"><span>✔</span></div>'
previewTemplate += '<div class="dz-error-mark"><span>✘</span></div>'
previewTemplate += '<div class="dz-error-message"><span data-dz-errormessage></span></div>'
previewTemplate += '</div>'

const $dropzone = $('div#dataset');
console.log('createDropzone');
const dropzoneUploader = await createDropzone($dropzone[0], {
url: '/todouploader',
maxFiles: this.maxFiles,
@@ -84,10 +115,7 @@ export default {
previewTemplate
});
dropzoneUploader.on('addedfile', (file) => {
setTimeout(() => {
// eslint-disable-next-line no-unused-expressions
file.accepted && this.onFileAdded(file);
}, 200);
this.file = file
});
dropzoneUploader.on('maxfilesexceeded', function (file) {
if (this.files[0].status !== 'success') {
@@ -102,14 +130,23 @@ export default {
this.dropzoneUploader = dropzoneUploader;
},
methods: {
cancelDataset(){
location.href = this.repoPath
this.dropzoneUploader.removeAllFiles(true)
},
resetStatus() {
this.progress = 0;
this.status = '';
console.log(this.uploadtype)
},
updateProgress(file, progress) {
console.log("progress---",progress)
file.previewTemplate.querySelector(
'.dz-upload'
).style.width = `${progress}%`;
).style.width = `${progress}%`
file.previewTemplate.querySelector(
'.dz-upload'
).style.background = '#409eff';
},
emitDropzoneSuccess(file) {
file.status = 'success';
@@ -122,18 +159,24 @@ export default {
this.dropzoneUploader.emit('error', file);
// this.dropzoneUploader.emit('complete', file);
},
onFileAdded(file) {
file.datasetId = document
onFileAdded() {
this.btnFlag = true
this.file.datasetId = document
.getElementById('datasetId')
.getAttribute('datasetId');
this.resetStatus();
this.computeMD5(file);
console.log(this.file,!this.file?.upload)
if(!this.file?.upload){
this.btnFlag = false
return
}
this.computeMD5(this.file);
},

finishUpload(file) {
this.emitDropzoneSuccess(file);
setTimeout(() => {
window.location.reload();
window.location.href = this.repoPath
}, 1000);
},

@@ -249,7 +292,7 @@ export default {
file_name: file.name,
size: file.size,
dataset_id: file.datasetId,
type: CloudBrainType,
type: this.uploadtype,
_csrf: csrf
})
);
@@ -260,6 +303,8 @@ export default {
const params = {
params: {
md5: file.uniqueIdentifier,
type: this.uploadtype,
file_name: file.name,
_csrf: csrf
}
};
@@ -282,13 +327,15 @@ export default {
},

async newMultiUpload(file) {
console.log(this.uploadtype,this)
const res = await axios.get('/attachments/new_multipart', {
params: {
totalChunkCounts: file.totalChunkCounts,
md5: file.uniqueIdentifier,
size: file.size,
fileType: file.type,
type: CloudBrainType,
type: this.uploadtype,
file_name: file.name,
_csrf: csrf
}
});
@@ -306,6 +353,7 @@ export default {
fileReader = new FileReader(),
time = new Date().getTime();
let currentChunk = 0;
let _this = this

function loadNext() {
const start = currentChunk * chunkSize;
@@ -329,7 +377,8 @@ export default {
uploadID: file.uploadID,
size: partSize,
chunkNumber: currentChunk + 1,
type: CloudBrainType,
type: _this.uploadtype,
file_name: file.name,
_csrf: csrf
}
});
@@ -343,14 +392,27 @@ export default {
}
async function uploadMinioNewMethod(url,e){
async function uploadMinioNewMethod(url,e){
var xhr = new XMLHttpRequest();
xhr.open('PUT', url, false);
xhr.setRequestHeader('Content-Type', 'text/plain')
xhr.send(e.target.result);
var etagValue = xhr.getResponseHeader('etag');
//console.log(etagValue);
etags[currentChunk] = etagValue;
xhr.open('PUT', url, false);
if(_this.uploadtype===0){
xhr.setRequestHeader('Content-Type', 'text/plain')
xhr.send(e.target.result);
var etagValue = xhr.getResponseHeader('etag');
etags[currentChunk] = etagValue;
}
else if(_this.uploadtype===1){
xhr.setRequestHeader('Content-Type', '')
xhr.send(e.target.result);
var etagValue = xhr.getResponseHeader('ETag');
//console.log(etagValue);
etags[currentChunk] = etagValue;
}
}

async function updateChunk(currentChunk) {
@@ -395,6 +457,7 @@ export default {
}

async function completeUpload() {
console.log(_this.uploadtype)
return await axios.post(
'/attachments/complete_multipart',
qs.stringify({
@@ -403,8 +466,9 @@ export default {
file_name: file.name,
size: file.size,
dataset_id: file.datasetId,
type: CloudBrainType,
_csrf: csrf
type: _this.uploadtype,
_csrf: csrf,
description:_this.desc
})
);
}
@@ -430,6 +494,7 @@ 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) *
@@ -443,6 +508,7 @@ export default {
file.size
} 用时:${(new Date().getTime() - time) / 1000} s`
);
this.updateProgress(file, 100);
this.progress = 100;
this.status = this.dropzoneParams.data('upload-complete');
this.finishUpload(file);
@@ -455,7 +521,7 @@ export default {

<style>
.dropzone-wrapper {
margin: 2em auto;
margin: 0;
}
.ui .dropzone {
border: 2px dashed #0087f5;
@@ -473,4 +539,8 @@ export default {
border-bottom: 1px solid #dadce0;
min-height: 0;
}
.upload-info{
margin-top: 1em;
margin-bottom: 3em;
}
</style>

+ 4
- 1
web_src/js/components/ObsUploader.vue View File

@@ -460,7 +460,7 @@ export default {

<style>
.dropzone-wrapper {
margin: 2em auto;
margin: 0;
}
.ui .dropzone {
border: 2px dashed #0087f5;
@@ -478,4 +478,7 @@ export default {
border-bottom: 1px solid #dadce0;
min-height: 0;
}
.upload-info{
margin-top: 0.2em;
}
</style>

+ 7
- 7
web_src/js/components/ProAnalysis.vue View File

@@ -150,21 +150,21 @@
align="center">
</el-table-column>
<el-table-column
prop="isMirror"
prop="isFork"
label="派生"
align="center">
<template slot-scope="scope">
{{scope.row.isMirror|changeType}}
</template>
</el-table-column>
{{scope.row.isFork|changeType}}
</template>
</el-table-column>
<el-table-column
prop="isFork"
prop="isMirror"
label="镜像"
align="center">
<template slot-scope="scope">
{{scope.row.isFork|changeType}}
{{scope.row.isMirror|changeType}}
</template>
</el-table-column>
</el-table-column>
<el-table-column
prop="createUnix"
label="项目创建时间"


+ 590
- 1
web_src/js/index.js View File

@@ -43,7 +43,7 @@ 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 $ from 'jquery.js'

Vue.use(ElementUI);
Vue.prototype.$axios = axios;
@@ -2918,6 +2918,7 @@ $(document).ready(async () => {
initVueApp();
initVueUploader();
initObsUploader();
initVueDataset();
initVueEditAbout();
initVueEditTopic();
initVueContributors();
@@ -3658,6 +3659,594 @@ function initVueEditAbout() {
});
}

function initVueDataset() {
const el = document.getElementById('dataset-base');
if (!el) {
return;
}
let link=$('#square-link').data('link')
let repolink = $('.dataset-repolink').data('repolink')
let cloudbrainType = $('.dataset-repolink').data('cloudranin-type')
const clearBtn = document.getElementsByClassName("clear_dataset_value");
const params = new URLSearchParams(location.search)
for (let i = 0; i < clearBtn.length; i++) {
clearBtn[i].addEventListener('click',function(e){
let searchType=e.target.getAttribute("data-clear-value")
if(params.has(searchType)){
params.delete(searchType)
let clearSearch = params.toString()
location.href = link + '?' + clearSearch
}
})
}
const items = []
const zipStatus = []
$('#dataset-range-value').find('.item').each(function(){
items.push($(this).data('private'))
zipStatus.push($(this).data('decompress-state'))
})
let num_stars = $('#dataset-range-value').data('num-stars')
let star_active = $('#dataset-range-value').data('star-active')
const ruleForm = {}
if(document.getElementById('dataset-edit-value')){
let $this = $('#dataset-edit-value')
ruleForm.title = $this.data('edit-title') || ''
ruleForm.description = $this.data('edit-description') || ''
ruleForm.category = $this.data('edit-category') || ''
ruleForm.task = $this.data('edit-task') || ''
ruleForm.license = $this.data('edit-license') || ''
ruleForm.id = $this.data('edit-id')|| ''
ruleForm._csrf = csrf
}

const starItems = []
const starActives = []
$('#datasets-square-range-value').find('.item').each(function(){
starItems.push($(this).data('num-stars'))
starActives.push($(this).data('star-active'))
})
const taskLists = []
const licenseLists = []
$('#task-square-range-value').find('.item').each(function(){
taskLists.push($(this).data('task'))
})
$('#task-square-range-value').find('.item').each(function(){
licenseLists.push($(this).data('license'))
})
let dataset_file_desc
if(document.getElementById('dataset-file-desc')){
dataset_file_desc = document.getElementById('dataset-file-desc').value
}
// getEditInit(){
// if($('#dataset-edit-value')){
// $this = $('#dataset-edit-value')
// this.ruleForm.title = $this.data('edit-title') || ''
// this.ruleForm.description = $this.data('edit-description') || ''
// this.ruleForm.category = $this.data('edit-category') || ''
// this.ruleForm.task = $this.data('edit-task') || ''
// this.ruleForm.license = $this.data('edit-license') || ''
// this.ruleForm.id = $this.data('edit-id')|| ''
// }
// },
new Vue({
delimiters: ['${', '}'],
el,
data: {
suburl: AppSubUrl,
url:'',
type:0,
desc:'',
descfile:'',
datasetType:'',
privates:[],
zipStatus:[],
starItems:[],
starActives:[],
taskLists:[],
taskShow:[],
licenseLists:[],
licenseShow:[],
hasMoreBthHis: false,
showMoreHis:false,
star_active:false,
num_stars:0,
dialogVisible:false,
activeName: 'first',
searchDataItem:'',
currentRepoDataset:[],
myDataset:[],
publicDataset:[],
myFavoriteDataset:[],
page:1,
totalnums:0,
repolink:'',
cloudbrainType:0,
dataset_uuid:'',
dataset_name:'',
loadingDataIndex:true,
timer:null,
ruleForm:{
title:'',
description:'',
category:'',
task:'',
license:'',
_csrf:csrf,
},
ruleForm1:{
title:'',
description:'',
category:'',
task:'',
license:'',
_csrf:'',
id:''
},
rules: {
title: [
{ required: true, message: '请输入数据集名称', trigger: 'blur' },
{ min: 1, max: 100, message: '长度在 1 到 100 个字符', trigger: 'blur' },
// {required:true,message:'test',pattern:'/^[a-zA-Z0-9-_]{1,100}[^-]$/',trigger:'blur'},
{ validator: (rule, value, callback) => {
if (/^[a-zA-Z0-9-_.]{0,100}$/.test(value) == false) {
callback(new Error("输入不符合数据集名称规则"));
} else {
callback();
}
}, trigger: 'blur'}
],
description: [
{ required: true, message: '请输入数据集描述详情', trigger: 'blur' }
],
category: [
{ required: true, message: '请选择分类', trigger: 'change' }
],
task: [
{ required: true, message: '请选择研究方向/应用领域', trigger: 'change' }
],
// license: [
// { required: true, message: '请选择活动区域', trigger: 'change' }
// ]
},
},
components: {
MinioUploader,
ObsUploader
},
mounted(){
// if(document.getElementById('postPath')){
// this.url = document.getElementById('postPath').value
// }
// this.privates = items
// this.num_stars = num_stars
// this.star_active = star_active
// this.ruleForm1 = ruleForm
// // this.getEditInit()
// this.getTypeList()
this.getTypeList()
if(!!document.getElementById('dataset-repolink-init')){
this.getCurrentRepoDataset(this.repolink,this.cloudbrainType)
}
},
created(){
if(document.getElementById('postPath')){
this.url = document.getElementById('postPath').value
}
this.privates = items
this.zipStatus = zipStatus
this.num_stars = num_stars
this.star_active = star_active
this.ruleForm1 = ruleForm
// this.getEditInit()
this.starItems = starItems
this.starActives = starActives
this.taskLists = taskLists
this.licenseLists = licenseLists
this.descfile = dataset_file_desc
this.repolink = repolink
this.cloudbrainType = cloudbrainType
},
methods:{
handleCurrentChange(val) {
this.page = val
switch(this.activeName){
case 'first':
this.getCurrentRepoDataset(this.repolink,this.cloudbrainType)
break
case 'second':
this.getMyDataset(this.repolink,this.cloudbrainType)
break
case 'third':
this.getPublicDataset(this.repolink,this.cloudbrainType)
break
case 'fourth':
this.getStarDataset(this.repolink,this.cloudbrainType)
break
}

},
createDataset(formName){
let _this = this
this.$refs[formName].validate((valid)=>{
if(valid){
document.getElementById("mask").style.display = "block"
_this.$axios.post(_this.url,_this.qs.stringify(_this.ruleForm)).then((res)=>{
if(res.data.Code===0){
document.getElementById("mask").style.display = "none"
location.href = _this.url.split('/create')[0]+'?type=-1'
}else{
console.log(res.data.Message)
}
document.getElementById("mask").style.display = "none"
}).catch(error=>{
console.log(error)
})
}
else{
return false
}
})
},
cancelDataset(getpage,attachment){
if(getpage && !attachment){
if(getpage==='create'){
location.href = this.url.split('/create')[0]+'?type=-1'
}else if(getpage==='edit'){
location.href = this.url.split('/edit')[0]+'?type=-1'
}else{
location.href='/'
}
}
else{
location.href = `${AppSubUrl}${attachment}/datasets`
}
},
gotoUpload(repolink,datsetId){
location.href = `${AppSubUrl}${repolink}/datasets/attachments/upload?datasetId=${datsetId}`
},
gotoDataset(datsetUrl){
location.href = datsetUrl
},
gotoAnnotate(repolink,uuid,type){
location.href = `${AppSubUrl}${repolink}/datasets/label/${uuid}?type=${type}`
},
uploadGpu(){
this.type=0
},
uploadNpu(){
this.type=1
},
setPrivate(uuid,privateFlag,index){
const params = {_csrf:csrf,file:uuid,is_private:privateFlag}
this.$axios.post('/attachments/private',this.qs.stringify(params)).then((res)=>{
this.$set(this.privates,index,privateFlag)
}).catch(error=>{
console.log(error)
})
},
delDataset(uuid){
let _this = this
const params = {_csrf:csrf,file:uuid}
$('#data-dataset-delete-modal')
.modal({
closable: false,
onApprove() {
_this.$axios.post('/attachments/delete',_this.qs.stringify(params)).then((res)=>{
// $('#'+uuid).hide()
location.reload()
}).catch(error=>{
console.log(error)
})
}
})
.modal('show');
},
// getEditInit(){
// if($('#dataset-edit-value')){
// $this = $('#dataset-edit-value')
// this.ruleForm.title = $this.data('edit-title') || ''
// this.ruleForm.description = $this.data('edit-description') || ''
// this.ruleForm.category = $this.data('edit-category') || ''
// this.ruleForm.task = $this.data('edit-task') || ''
// this.ruleForm.license = $this.data('edit-license') || ''
// this.ruleForm.id = $this.data('edit-id')|| ''
// }
// },
editDataset(formName,id){
let _this = this
this.url = this.url.split(`/${id}`)[0]
this.$refs[formName].validate((valid)=>{
if(valid){
document.getElementById("mask").style.display = "block"
_this.$axios.post(_this.url,_this.qs.stringify(_this.ruleForm1)).then((res)=>{
if(res.data.Code===0){
document.getElementById("mask").style.display = "none"
location.href = _this.url.split('/edit')[0]+'?type=-1'
}else{
console.log(res.data.Message)
}
document.getElementById("mask").style.display = "none"
}).catch((err)=>{
console.log(err)
})
}
else{
return false
}
})

},
editDatasetFile(id,backurl){
let url = '/attachments/edit'
const params={id:id,description:this.descfile,_csrf:csrf}
// document.getElementById("mask").style.display = "block"
this.$axios.post(url,this.qs.stringify(params)).then((res)=>{
if(res.data.Code===0){
location.href = `${AppSubUrl}${backurl}/datasets`
}else{
console.log(res.data.Message)
}
}).catch((err)=>{
console.log(err)
})
},
postStar(id,link){
if(this.star_active){
let url = link+'/'+ id + '/unstar'
this.$axios.put(url).then((res)=>{
if(res.data.Code===0){
this.star_active = false
this.num_stars = this.num_stars -1
}
})
}else{
let url = link+'/'+ id + '/star'
this.$axios.put(url).then((res)=>{
if(res.data.Code===0){
this.star_active = true
this.num_stars = this.num_stars + 1
}
})
}
},
postSquareStar(id,link,index){
if(this.starActives[index]){
let url = link+'/'+ id + '/unstar'
this.$axios.put(url).then((res)=>{
if(res.data.Code===0){
this.$set(this.starActives,index,false)
this.$set(this.starItems,index,this.starItems[index]-1)
}
})
}else{
let url = link+'/'+ id + '/star'
this.$axios.put(url).then((res)=>{
if(res.data.Code===0){
this.$set(this.starActives,index,true)
this.$set(this.starItems,index,this.starItems[index]+1)
}
})
}
},
getTypeList(){
const params = new URLSearchParams(window.location.search)
if( window.location.search && params.has('type')){
if(params.get('type')==0){
this.datasetType = '0'
}
if(params.get('type')==1){
this.datasetType = '1'
}
if(params.get('type')==-1){
this.datasetType = '-1'
}
}else {
this.datasetType = '-1'
}
},
changeDatasetType(val){
const searchParams = new URLSearchParams(window.location.search)
if (!window.location.search) {
window.location.href = window.location.href + '?type='+val
} else if (searchParams.has('type')) {
window.location.href = window.location.href.replace(/type=([0-9]|-[0-9])/g,'type='+val)
} else {
window.location.href=window.location.href+'&type='+val
}

},
gotoDatasetEidt(repolink,id){
location.href = `${repolink}/datasets/attachments/edit/${id}`

},
handleClick(repoLink, tabName,type) {
if(tabName=="first"){
this.page=1
this.searchDataItem=''
this.getCurrentRepoDataset(repoLink,type)
}
if(tabName=="second"){
this.page=1
this.searchDataItem=''
this.getMyDataset(repoLink,type)
}
if(tabName=="third"){
this.page=1
this.searchDataItem=''
this.getPublicDataset(repoLink,type)
}
if(tabName=="fourth"){
this.page=1
this.searchDataItem=''
this.getStarDataset(repoLink,type)
}
},
polling (checkStatuDataset,repoLink) {
this.timer = window.setInterval(() => {
setTimeout(() => {
this.getDatasetStatus(checkStatuDataset,repoLink)
},0)
},15000)

},

getDatasetStatus(checkStatuDataset,repoLink){
const getmap = checkStatuDataset.map((item)=>{
let url = `${AppSubUrl}${repolink}/datasets/status/${item.UUID}`
return this.$axios.get(url)
})
this.$axios.all(getmap)
.then((res)=>{
let flag = res.some((item)=>{
return item.data.AttachmentStatus == 1
})
flag && clearInterval(this.timer)
flag && this.refreshStatusDataset()
}
)

},
refreshStatusDataset(){
switch(this.activeName){
case 'first':
this.getCurrentRepoDataset(this.repolink,this.cloudbrainType)
break
case 'second':
this.getMyDataset(this.repolink,this.cloudbrainType)
break
case 'third':
this.getPublicDataset(this.repolink,this.cloudbrainType)
break
case 'fourth':
this.getStarDataset(this.repolink,this.cloudbrainType)
break
}
},
getCurrentRepoDataset(repoLink,type){
clearInterval(this.timer)
this.loadingDataIndex = true
let url = repoLink + '/datasets/current_repo'
this.$axios.get(url,{
params:{
type:type,
page:this.page,
q:this.searchDataItem
}
}).then((res)=>{
this.currentRepoDataset = JSON.parse(res.data.data)
const checkStatuDataset = this.currentRepoDataset.filter(item=>item.DecompressState===2)
if(checkStatuDataset.length>0){
this.polling(checkStatuDataset,repoLink)
}
this.totalnums = parseInt(res.data.count)
this.loadingDataIndex = false
})
},
getMyDataset(repoLink,type){
clearInterval(this.timer)
this.loadingDataIndex = true
let url = repoLink + '/datasets/my_datasets'
this.$axios.get(url,{
params:{
type:type,
page:this.page,
q:this.searchDataItem
}
}).then((res)=>{
this.myDataset = JSON.parse(res.data.data)
const checkStatuDataset = this.myDataset.filter(item=>item.DecompressState===2)
if(checkStatuDataset.length>0){
this.polling(checkStatuDataset,repoLink)
}
this.totalnums = parseInt(res.data.count)
this.loadingDataIndex = false
})

},
getPublicDataset(repoLink,type){
clearInterval(this.timer)
this.loadingDataIndex = true
let url = repoLink + '/datasets/public_datasets'
this.$axios.get(url,{
params:{
type:type,
page:this.page,
q:this.searchDataItem
}
}).then((res)=>{
this.publicDataset = JSON.parse(res.data.data)
const checkStatuDataset = this.publicDataset.filter(item=>item.DecompressState===2)
if(checkStatuDataset.length>0){
this.polling(checkStatuDataset,repoLink)
}
this.totalnums = parseInt(res.data.count)
this.loadingDataIndex = false
})

},
getStarDataset(repoLink,type){
clearInterval(this.timer)
this.loadingDataIndex = true
let url = repoLink + '/datasets/my_favorite'
this.$axios.get(url,{
params:{
type:type,
page:this.page,
q:this.searchDataItem
}
}).then((res)=>{
this.myFavoriteDataset = JSON.parse(res.data.data)
const checkStatuDataset = this.myFavoriteDataset.filter(item=>item.DecompressState===2)
if(checkStatuDataset.length>0){
this.polling(checkStatuDataset,repoLink)
}
this.totalnums= parseInt(res.data.count)
this.loadingDataIndex = false
})

},
selectDataset(uuid,name){
this.dataset_uuid = uuid
this.dataset_name = name
this.dialogVisible = false
},
searchDataset(){
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
}
}
},
});

}
function initVueEditTopic() {
const el = document.getElementById('topic_edit1');


+ 35
- 0
web_src/less/_dataset.less View File

@@ -222,3 +222,38 @@
}
}
}
.panel_creator_reponam{
display: inline-block;
border-radius: 4px;
padding: 4px;
font-size: 12px;
text-align: center;
background-color: rgba(161, 220, 255, 0.2);
color: #101010;
}
.panel_dataset_name{
font-size: 15px;
color: #0366D6;
text-align: center;
margin-left: 1rem;
}
.panel_datset_desc{
white-space: nowrap;
display: inline-block;
overflow: hidden;
width: 90%;
text-overflow: ellipsis;
}
.el-dialog__body{
padding-top:0
}
#dataset-base{
.active{
color: #0087f5!important;
border: 1px solid #0087f5!important;
/* margin: -1px!important; */
background: #fff!important;
}
}

+ 113
- 0
web_src/less/openi.less View File

@@ -375,6 +375,10 @@ display: block;
font-size: 18px;
margin-bottom: 1rem;
}
.bgtask-content-button{
margin-top: 1em;
margin-bottom: 1em;
}

.selectcloudbrain .active.item{
color: #0087f5 !important;
@@ -624,6 +628,32 @@ display: block;
.a_margin{
margin: 0px !important;
}

/*pages*/
.ui.borderless.pagination {border:none}
.ui.pagination.menu .item {
min-width: 32px;
text-align: center;
height: 32px;
border-radius: .28571429rem;
margin: 0 5px;
background-color: #F2F2F2;
}
.ui.pagination.menu>.item:first-child, .ui.pagination.menu .item:last-child {
background-color: #FFF !important;
}
.ui.ui.menu .item.disabled{
background-color: #F2F2F2;
}
.ui.pagination.menu .active.item {
background-color: #3291F8;
color: #FFF;
}
.ui.pagination.menu .item>.input {
margin: 0em .5em;
width: 3em;
height: 32px;
}
@media only screen and (max-width: 767px) {
.following.bar #navbar .brand{
padding-top: 6px;
@@ -779,4 +809,87 @@ display: block;
border: none !important;
color: #0366d6 !important;
box-shadow: -15px 0px 10px #fff;
}

/**seach**/
/**搜索导航条适配窄屏**/
.seachnav{
overflow-x: auto;
overflow-y: hidden;
scrollbar-width: none; /* firefox */
-ms-overflow-style: none; /* IE 10+ */
}
.seachnav::-webkit-scrollbar {
display: none; /* Chrome Safari */
}
.ui.green.button, .ui.green.buttons .button{
background-color: #5BB973;
}
.seach .repos--seach{
padding-bottom: 0;
border-bottom: none;
}
.seach .ui.secondary.pointing.menu{
border-bottom: none;
}
.seach .ui.secondary.pointing.menu .item > i{
margin-right: 5px;
}
.seach .ui.secondary.pointing.menu .active.item{
border-bottom-width: 2px;
margin: 0 0 -1px;
}
.seach .ui.menu .active.item>.label {
background: #1684FC;
color: #FFF;
}
.seach .ui.menu .item>.label:not(.active.item>.label) {
background: #e8e8e8;
color: rgba(0,0,0,.6);
}
.highlight{
color: red;
}
.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;
font-size: 1.4rem !important;
font-weight: normal;
}
.seach .time, .seach .time a{
font-size: 12px;
color: grey;
}
.seach .list .item.members .ui.avatar.image {
width: 3.2em;
height: 3.2em;
}
.ui.list .list>.item.members>img.image+.content, .ui.list>.item.members>img.image+.content {
width: calc(100% - 4.0em);
margin-left: 0;
}

.searchlabel{
color: rgba(16, 16, 16, 100);
font-size: 24px;
text-align: left;
font-family: SourceHanSansSC-medium;
}

.hiddenSearch{
margin: auto;
display: none;
}

#tipmsg {
display: none;
z-index: 9999;
width:150;
height: 80;
}

Loading…
Cancel
Save