Browse Source

Merge remote-tracking branch 'origin/V20221130' into zouap

pull/3286/head
zouap 2 years ago
parent
commit
eff5af723d
54 changed files with 2060 additions and 486 deletions
  1. +3
    -28
      models/cloudbrain_static.go
  2. +1
    -1
      models/repo.go
  3. +30
    -3
      models/repo_watch.go
  4. +9
    -0
      models/resource_specification.go
  5. +1
    -1
      models/user.go
  6. +21
    -0
      models/user_mail.go
  7. +4
    -0
      modules/auth/user_form.go
  8. +17
    -4
      modules/cloudbrain/cloudbrain.go
  9. +1
    -0
      modules/context/repo.go
  10. +94
    -8
      modules/modelarts/modelarts.go
  11. +1
    -1
      modules/modelarts/resty.go
  12. +8
    -45
      modules/modelarts_cd/modelarts.go
  13. +29
    -0
      modules/setting/setting.go
  14. +8
    -0
      modules/structs/cloudbrain.go
  15. +3
    -0
      modules/templates/helper.go
  16. +17
    -2
      options/locale/locale_en-US.ini
  17. +15
    -0
      options/locale/locale_zh-CN.ini
  18. +6
    -0
      routers/api/v1/api.go
  19. +72
    -2
      routers/api/v1/repo/cloudbrain.go
  20. +55
    -46
      routers/api/v1/repo/cloudbrain_dashboard.go
  21. +5
    -1
      routers/api/v1/repo/modelarts.go
  22. +4
    -5
      routers/repo/aisafety.go
  23. +12
    -53
      routers/repo/cloudbrain.go
  24. +27
    -24
      routers/repo/cloudbrain_statistic.go
  25. +1
    -3
      routers/repo/grampus.go
  26. +35
    -10
      routers/repo/modelarts.go
  27. +3
    -1
      routers/repo/repo.go
  28. +1
    -0
      routers/routes/routes.go
  29. +28
    -0
      routers/user/auth.go
  30. +362
    -0
      services/cloudbrain/cloudbrainTask/notebook.go
  31. +16
    -7
      services/cloudbrain/cloudbrainTask/sync_status.go
  32. +12
    -0
      services/cloudbrain/cloudbrainTask/train.go
  33. +26
    -0
      services/cloudbrain/util.go
  34. +2
    -0
      templates/admin/cloudbrain/list.tmpl
  35. +2
    -1
      templates/repo/cloudbrain/inference/show.tmpl
  36. +1
    -0
      templates/repo/cloudbrain/show.tmpl
  37. +1
    -0
      templates/repo/cloudbrain/trainjob/show.tmpl
  38. +8
    -2
      templates/repo/debugjob/index.tmpl
  39. +1
    -0
      templates/repo/grampus/trainjob/show.tmpl
  40. +45
    -2
      templates/repo/header.tmpl
  41. +29
    -29
      templates/repo/home.tmpl
  42. +1
    -0
      templates/repo/modelarts/inferencejob/show.tmpl
  43. +1
    -0
      templates/repo/modelarts/trainjob/show.tmpl
  44. +1
    -0
      templates/repo/modelsafety/show.tmpl
  45. +29
    -1
      templates/user/auth/activate.tmpl
  46. +6
    -4
      templates/user/dashboard/cloudbrains.tmpl
  47. +23
    -12
      web_src/js/features/cloudrbanin.js
  48. +14
    -0
      web_src/less/openi.less
  49. +47
    -0
      web_src/vuepages/apis/modules/notobook.js
  50. +0
    -1
      web_src/vuepages/apis/service.js
  51. +31
    -3
      web_src/vuepages/langs/config/en-US.js
  52. +217
    -186
      web_src/vuepages/langs/config/zh-CN.js
  53. +662
    -0
      web_src/vuepages/pages/notebook/debug/index.vue
  54. +12
    -0
      web_src/vuepages/pages/notebook/debug/vp-notebook-debug.js

+ 3
- 28
models/cloudbrain_static.go View File

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

import (
"fmt"
"strconv"
"time"

@@ -313,10 +312,6 @@ func InsertCloudbrainDurationStatistic(cloudbrainDurationStatistic *CloudbrainDu
return xStatistic.Insert(cloudbrainDurationStatistic)
}

func GetDurationStatisticByDate(date string, hour int, aiCenterCode string, accCardType string) (*CloudbrainDurationStatistic, error) {
cb := &CloudbrainDurationStatistic{DayTime: date, HourTime: hour, AiCenterCode: aiCenterCode, AccCardType: accCardType}
return getDurationStatistic(cb)
}
func getDurationStatistic(cb *CloudbrainDurationStatistic) (*CloudbrainDurationStatistic, error) {
has, err := x.Get(cb)
if err != nil {
@@ -327,26 +322,6 @@ func getDurationStatistic(cb *CloudbrainDurationStatistic) (*CloudbrainDurationS
return cb, nil
}

func DeleteCloudbrainDurationStatisticHour(date string, hour int, aiCenterCode string, accCardType string) error {
sess := xStatistic.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return fmt.Errorf("Begin: %v", err)
}

if _, err := sess.Where("day_time = ? AND hour_time = ? AND ai_center_code = ? AND acc_card_type = ?", date, hour, aiCenterCode, accCardType).Delete(&CloudbrainDurationStatistic{}); err != nil {
return fmt.Errorf("Delete: %v", err)
}

if err := sess.Commit(); err != nil {
sess.Close()
return fmt.Errorf("Commit: %v", err)
}

sess.Close()
return nil
}

func GetCanUseCardInfo() ([]*ResourceQueue, error) {
sess := x.NewSession()
defer sess.Close()
@@ -402,11 +377,11 @@ func GetDurationRecordUpdateTime() ([]*CloudbrainDurationStatistic, error) {
return CloudbrainDurationStatistics, nil
}

func DeleteCloudbrainDurationStatistic() error {
func DeleteCloudbrainDurationStatistic(beginTime timeutil.TimeStamp, endTime timeutil.TimeStamp) error {
sess := xStatistic.NewSession()
defer sess.Close()
if _, err := sess.Exec("TRUNCATE TABLE cloudbrain_duration_statistic"); err != nil {
log.Info("TRUNCATE cloudbrain_duration_statistic error.")
if _, err := sess.Exec("DELETE FROM cloudbrain_duration_statistic WHERE cloudbrain_duration_statistic.date_time BETWEEN ? AND ?", beginTime, endTime); err != nil {
log.Info("DELETE cloudbrain_duration_statistic data error.")
return err
}
return nil


+ 1
- 1
models/repo.go View File

@@ -1279,7 +1279,7 @@ func CreateRepository(ctx DBContext, doer, u *User, repo *Repository, opts ...Cr
}

if setting.Service.AutoWatchNewRepos {
if err = watchRepo(ctx.e, doer.ID, repo.ID, true); err != nil {
if err = watchRepo(ctx.e, doer.ID, repo.ID, true, ReceiveAllNotification); err != nil {
return fmt.Errorf("watchRepo: %v", err)
}
}


+ 30
- 3
models/repo_watch.go View File

@@ -24,6 +24,14 @@ const (
RepoWatchModeAuto // 3
)

// NotifyType specifies what kind of watch the user has on a repository
type NotifyType int8

const (
RejectAllNotification NotifyType = 0
ReceiveAllNotification NotifyType = 9
)

var ActionChan = make(chan *Action, 200)
var ActionChan4Task = make(chan Action, 200)

@@ -34,6 +42,7 @@ type Watch struct {
RepoID int64 `xorm:"UNIQUE(watch)"`
Mode RepoWatchMode `xorm:"SMALLINT NOT NULL DEFAULT 1"`
CreatedUnix int64 `xorm:"created"`
NotifyType NotifyType `xorm:"SMALLINT NOT NULL DEFAULT 0"`
}

// getWatch gets what kind of subscription a user has on a given repository; returns dummy record if none found
@@ -60,8 +69,20 @@ func IsWatching(userID, repoID int64) bool {
return err == nil && isWatchMode(watch.Mode)
}

// GetWatchNotifyType
func GetWatchNotifyType(userID, repoID int64) NotifyType {
watch, err := getWatch(x, userID, repoID)
if err != nil {
return RejectAllNotification
}
return watch.NotifyType
}

func watchRepoMode(e Engine, watch Watch, mode RepoWatchMode) (err error) {
if watch.Mode == mode {
if _, err := e.ID(watch.ID).Cols("notify_type").Update(watch); err != nil {
return err
}
return nil
}
if mode == RepoWatchModeAuto && (watch.Mode == RepoWatchModeDont || isWatchMode(watch.Mode)) {
@@ -109,7 +130,7 @@ func WatchRepoMode(userID, repoID int64, mode RepoWatchMode) (err error) {
return watchRepoMode(x, watch, mode)
}

func watchRepo(e Engine, userID, repoID int64, doWatch bool) (err error) {
func watchRepo(e Engine, userID, repoID int64, doWatch bool, notifyTypes ...NotifyType) (err error) {
var watch Watch
if watch, err = getWatch(e, userID, repoID); err != nil {
return err
@@ -119,14 +140,19 @@ func watchRepo(e Engine, userID, repoID int64, doWatch bool) (err error) {
} else if !doWatch {
err = watchRepoMode(e, watch, RepoWatchModeNone)
} else {
notifyType := RejectAllNotification
if len(notifyTypes) > 0 {
notifyType = notifyTypes[0]
}
watch.NotifyType = notifyType
err = watchRepoMode(e, watch, RepoWatchModeNormal)
}
return err
}

// WatchRepo watch or unwatch repository.
func WatchRepo(userID, repoID int64, watch bool) (err error) {
return watchRepo(x, userID, repoID, watch)
func WatchRepo(userID, repoID int64, watch bool, notifyType ...NotifyType) (err error) {
return watchRepo(x, userID, repoID, watch, notifyType...)
}

func getWatchers(e Engine, repoID int64) ([]*Watch, error) {
@@ -156,6 +182,7 @@ func getRepoWatchersIDs(e Engine, repoID int64) ([]int64, error) {
return ids, e.Table("watch").
Where("watch.repo_id=?", repoID).
And("watch.mode<>?", RepoWatchModeDont).
And("watch.notify_type > ?", RejectAllNotification).
Select("user_id").
Find(&ids)
}


+ 9
- 0
models/resource_specification.go View File

@@ -298,6 +298,15 @@ func ResourceSpecOffShelf(id int64) (int64, error) {
return n, err
}

func GetResourceSpecificationByIds(ids []int64) ([]*Specification, error) {
r := make([]*Specification, 0)
err := x.In("resource_specification.id", ids).
Join("INNER", "resource_queue", "resource_queue.id = resource_specification.queue_id").
Find(&r)
return r, err

}

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


+ 1
- 1
models/user.go View File

@@ -346,7 +346,7 @@ func (u *User) DashboardLink() string {
if u.IsOrganization() {
return setting.AppSubURL + "/org/" + u.Name + "/dashboard/"
}
return setting.AppSubURL + "/"
return setting.AppSubURL + "/dashboard"
}

// HomeLink returns the user or organization home page link.


+ 21
- 0
models/user_mail.go View File

@@ -216,6 +216,27 @@ func (email *EmailAddress) updateActivation(e Engine, activate bool) error {
return updateUserCols(e, user, "rands")
}

// UpdateEmailAddress update an email address of given user.
func (email *EmailAddress) UpdateEmailAddress(newEmailAddress string) error {
return email.updateEmailAddress(x, newEmailAddress)
}
func (email *EmailAddress) updateEmailAddress(e Engine, newEmailAddress string) error {
user, err := getUserByID(e, email.UID)
if err != nil {
return err
}
if user.Rands, err = GetUserSalt(); err != nil {
return err
}
user.Email = newEmailAddress
user.AvatarEmail = newEmailAddress
email.Email = newEmailAddress
if _, err := e.ID(email.ID).Cols("email").Update(email); err != nil {
return err
}
return updateUserCols(e, user, "email", "avatar_email")
}

// DeleteEmailAddress deletes an email address of given user.
func DeleteEmailAddress(email *EmailAddress) (err error) {
var deleted int64


+ 4
- 0
modules/auth/user_form.go View File

@@ -88,6 +88,10 @@ type RegisterForm struct {
Agree bool
}

type UpdateEmailForm struct {
NewEmail string `binding:"Required;MaxSize(254)"`
}

// Validate valideates the fields
func (f *RegisterForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)


+ 17
- 4
modules/cloudbrain/cloudbrain.go View File

@@ -145,7 +145,7 @@ func isAdminOrImageCreater(ctx *context.Context, image *models.Image, err error)
func AdminOrOwnerOrJobCreaterRight(ctx *context.Context) {

var id = ctx.Params(":id")
job, err := models.GetCloudbrainByID(id)
job, err := GetCloudBrainByIdOrJobId(id)
if err != nil {
log.Error("GetCloudbrainByID failed:%v", err.Error())
ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
@@ -161,7 +161,7 @@ func AdminOrOwnerOrJobCreaterRight(ctx *context.Context) {
func AdminOrJobCreaterRight(ctx *context.Context) {

var id = ctx.Params(":id")
job, err := models.GetCloudbrainByID(id)
job, err := GetCloudBrainByIdOrJobId(id)
if err != nil {
log.Error("GetCloudbrainByID failed:%v", err.Error())
ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
@@ -177,7 +177,7 @@ func AdminOrJobCreaterRight(ctx *context.Context) {
func AdminOrOwnerOrJobCreaterRightForTrain(ctx *context.Context) {

var jobID = ctx.Params(":jobid")
job, err := models.GetCloudbrainByJobID(jobID)
job, err := GetCloudBrainByIdOrJobId(jobID)
if err != nil {
log.Error("GetCloudbrainByJobID failed:%v", err.Error())
ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
@@ -193,7 +193,7 @@ func AdminOrOwnerOrJobCreaterRightForTrain(ctx *context.Context) {
func AdminOrJobCreaterRightForTrain(ctx *context.Context) {

var jobID = ctx.Params(":jobid")
job, err := models.GetCloudbrainByJobID(jobID)
job, err := GetCloudBrainByIdOrJobId(jobID)
if err != nil {
log.Error("GetCloudbrainByJobID failed:%v", err.Error())
ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
@@ -652,3 +652,16 @@ func IsElementExist(s []string, str string) bool {
}
return false
}

func GetCloudBrainByIdOrJobId(id string) (*models.Cloudbrain,error) {
_, err := strconv.ParseInt(id, 10, 64)
var job *models.Cloudbrain
if err != nil {

job, err = models.GetCloudbrainByJobID(id)
} else {
job, err = models.GetCloudbrainByID(id)

}
return job,err
}

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

@@ -474,6 +474,7 @@ func RepoAssignment() macaron.Handler {

if ctx.IsSigned {
ctx.Data["IsWatchingRepo"] = models.IsWatching(ctx.User.ID, repo.ID)
ctx.Data["WatchNotifyType"] = models.GetWatchNotifyType(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)


+ 94
- 8
modules/modelarts/modelarts.go View File

@@ -1,13 +1,18 @@
package modelarts

import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"path"
"strconv"
"strings"

"code.gitea.io/gitea/modules/cloudbrain"

"code.gitea.io/gitea/modules/modelarts_cd"

"code.gitea.io/gitea/models"
@@ -23,7 +28,7 @@ const (
//notebook
storageTypeOBS = "obs"
autoStopDuration = 4 * 60 * 60
autoStopDurationMs = 4 * 60 * 60 * 1000
AutoStopDurationMs = 4 * 60 * 60 * 1000
MORDELART_USER_IMAGE_ENGINE_ID = -1
DataSetMountPath = "/home/ma-user/work"
NotebookEnv = "Python3"
@@ -276,7 +281,7 @@ func GenerateTask(ctx *context.Context, jobName, uuid, description, flavor strin
return nil
}

func GenerateNotebook2(ctx *context.Context, displayJobName, jobName, uuid, description, imageId string, spec *models.Specification) error {
func GenerateNotebook2(ctx *context.Context, displayJobName, jobName, uuid, description, imageId string, spec *models.Specification, bootFile string,autoStopDurationInMs int64) (string, error) {
if poolInfos == nil {
json.Unmarshal([]byte(setting.PoolInfos), &poolInfos)
}
@@ -284,14 +289,14 @@ func GenerateNotebook2(ctx *context.Context, displayJobName, jobName, uuid, desc
imageName, err := GetNotebookImageName(imageId)
if err != nil {
log.Error("GetNotebookImageName failed: %v", err.Error())
return err
return "", err
}
createTime := timeutil.TimeStampNow()
jobResult, err := createNotebook2(models.CreateNotebook2Params{
JobName: jobName,
Description: description,
Flavor: spec.SourceSpecId,
Duration: autoStopDurationMs,
Duration: autoStopDurationInMs,
ImageID: imageId,
PoolID: poolInfos.PoolInfo[0].PoolId,
Feature: models.NotebookFeature,
@@ -316,10 +321,10 @@ func GenerateNotebook2(ctx *context.Context, displayJobName, jobName, uuid, desc
})
if errTemp != nil {
log.Error("InsertCloudbrainTemp failed: %v", errTemp.Error())
return errTemp
return "", errTemp
}
}
return err
return "", err
}
task := &models.Cloudbrain{
Status: jobResult.Status,
@@ -334,6 +339,7 @@ func GenerateNotebook2(ctx *context.Context, displayJobName, jobName, uuid, desc
Uuid: uuid,
ComputeResource: models.NPUResource,
Image: imageName,
BootFile: bootFile,
Description: description,
CreatedUnix: createTime,
UpdatedUnix: createTime,
@@ -342,12 +348,12 @@ func GenerateNotebook2(ctx *context.Context, displayJobName, jobName, uuid, desc

err = models.CreateCloudbrain(task)
if err != nil {
return err
return "", err
}

stringId := strconv.FormatInt(task.ID, 10)
notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, stringId, displayJobName, models.ActionCreateDebugNPUTask)
return nil
return jobResult.ID, nil
}

func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (jobId string, err error) {
@@ -907,6 +913,11 @@ func HandleNotebookInfo(task *models.Cloudbrain) error {
if task.FlavorCode == "" {
task.FlavorCode = result.Flavor
}

if oldStatus != task.Status && task.Status == string(models.ModelArtsRunning) && task.BootFile != "" {
uploadNoteBookFile(task, result)

}
err = models.UpdateJob(task)
if err != nil {
log.Error("UpdateJob(%s) failed:%v", task.DisplayJobName, err)
@@ -917,6 +928,81 @@ func HandleNotebookInfo(task *models.Cloudbrain) error {
return nil
}

func uploadNoteBookFile(task *models.Cloudbrain, result *models.GetNotebook2Result) {
jupyterUrl := result.Url + "?token=" + result.Token

cookies, xsrf := getCookiesAndCsrf(jupyterUrl)
if xsrf == "" {
log.Error("browser jupyterUrl failed:%v", task.DisplayJobName)
} else {

codePath := setting.JobPath + task.JobName + cloudbrain.CodeMountPath
fileContents, err := ioutil.ReadFile(codePath + "/" + task.BootFile)
if err != nil {
log.Error("read jupyter file failed:%v", task.DisplayJobName, err)
}

base64Content := base64.StdEncoding.EncodeToString(fileContents)
client := getRestyClient()
uploadUrl := getJupyterBaseUrl(result.Url) + "api/contents/" + path.Base(task.BootFile)
res, err := client.R().
SetCookies(cookies).
SetHeader("X-XSRFToken", xsrf).
SetBody(map[string]interface{}{
"type": "file",
"format": "base64",
"name": path.Base(task.BootFile),
"path": path.Base(task.BootFile),
"content": base64Content}).
Put(uploadUrl)
if err != nil {
log.Error("upload jupyter file failed:%v", task.DisplayJobName, err)
} else if res.StatusCode() != http.StatusCreated {
log.Error("upload jupyter file failed:%v", task.DisplayJobName, err)
}

}

}

func getJupyterBaseUrl(url string) string {
jupyterUrlLength := len(url)
baseUrl := url[0 : jupyterUrlLength-len(path.Base(url))]
return baseUrl
}

func getCookiesAndCsrf(jupyterUrl string) ([]*http.Cookie, string) {
log.Info("jupyter url:"+jupyterUrl)
var cookies []*http.Cookie
const retryTimes = 10
for i := 0; i < retryTimes; i++ {
res, err := http.Get(jupyterUrl)
if err != nil {
log.Error("browser jupyterUrl failed.",err)
if i==retryTimes-1{
return cookies, ""
}

} else {
cookies = res.Cookies()
xsrf := ""
for _, cookie := range cookies {
if cookie.Name == "_xsrf" {
xsrf = cookie.Value
break
}

}
if xsrf != "" {
return cookies, xsrf
}

}
}
return cookies, ""

}

func SyncTempStatusJob() {
jobs, err := models.GetCloudBrainTempJobs()
if err != nil {


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

@@ -280,7 +280,7 @@ sendjob:
SetHeader("Content-Type", "application/json").
SetAuthToken(TOKEN).
SetResult(&result).
Post(HOST + "/v1/" + setting.ProjectID + urlNotebook2 + "/" + jobID + "/" + param.Action + "?duration=" + strconv.Itoa(autoStopDurationMs))
Post(HOST + "/v1/" + setting.ProjectID + urlNotebook2 + "/" + jobID + "/" + param.Action + "?duration=" + strconv.Itoa(AutoStopDurationMs))

if err != nil {
return &result, fmt.Errorf("resty ManageNotebook2: %v", err)


+ 8
- 45
modules/modelarts_cd/modelarts.go View File

@@ -88,18 +88,18 @@ type Parameters struct {
} `json:"parameter"`
}

func GenerateNotebook(ctx *context.Context, displayJobName, jobName, uuid, description, imageId string, spec *models.Specification) error {
func GenerateNotebook(ctx *context.Context, displayJobName, jobName, uuid, description, imageId string, spec *models.Specification, bootFile string,autoStopDurationInMs int64) (string, error) {
imageName, err := GetNotebookImageName(imageId)
if err != nil {
log.Error("GetNotebookImageName failed: %v", err.Error())
return err
return "", err
}
createTime := timeutil.TimeStampNow()
jobResult, err := createNotebook(models.CreateNotebookWithoutPoolParams{
JobName: jobName,
Description: description,
Flavor: spec.SourceSpecId,
Duration: autoStopDurationMs,
Duration: autoStopDurationInMs,
ImageID: imageId,
Feature: models.NotebookFeature,
Volume: models.VolumeReq{
@@ -123,10 +123,10 @@ func GenerateNotebook(ctx *context.Context, displayJobName, jobName, uuid, descr
})
if errTemp != nil {
log.Error("InsertCloudbrainTemp failed: %v", errTemp.Error())
return errTemp
return "", errTemp
}
}
return err
return "", err
}
task := &models.Cloudbrain{
Status: jobResult.Status,
@@ -145,16 +145,17 @@ func GenerateNotebook(ctx *context.Context, displayJobName, jobName, uuid, descr
CreatedUnix: createTime,
UpdatedUnix: createTime,
Spec: spec,
BootFile: bootFile,
}

err = models.CreateCloudbrain(task)
if err != nil {
return err
return "", err
}

stringId := strconv.FormatInt(task.ID, 10)
notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, stringId, displayJobName, models.ActionCreateDebugNPUTask)
return nil
return jobResult.ID, nil
}

func GetNotebookImageName(imageId string) (string, error) {
@@ -175,41 +176,3 @@ func GetNotebookImageName(imageId string) (string, error) {

return imageName, nil
}

/*
func HandleNotebookInfo(task *models.Cloudbrain) error {

result, err := GetNotebook(task.JobID)
if err != nil {
log.Error("GetNotebook2(%s) failed:%v", task.DisplayJobName, err)
return err
}

if result != nil {
oldStatus := task.Status
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.CorrectCreateUnix()
task.ComputeAndSetDuration()
if oldStatus != task.Status {
notification.NotifyChangeCloudbrainStatus(task, oldStatus)
}
if task.FlavorCode == "" {
task.FlavorCode = result.Flavor
}
err = models.UpdateJob(task)
if err != nil {
log.Error("UpdateJob(%s) failed:%v", task.DisplayJobName, err)
return err
}
}

return nil
}

*/

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

@@ -715,6 +715,21 @@ var (
TeamName string
}{}

FileNoteBook = struct {
ProjectName string
ImageGPU string
SpecIdGPU int64
SpecIdCPU int64
ImageIdNPU string
SpecIdNPU int64
ImageIdNPUCD string
SpecIdNPUCD int64
ImageCPUDescription string
ImageGPUDescription string
ImageNPUDescription string
ImageNPUCDDescription string
}{}

ModelConvert = struct {
GPU_PYTORCH_IMAGE string
GpuQueue string
@@ -1580,6 +1595,20 @@ func NewContext() {
Course.OrgName = sec.Key("org_name").MustString("")
Course.TeamName = sec.Key("team_name").MustString("")

sec = Cfg.Section("file_notebook")
FileNoteBook.ProjectName = sec.Key("project_name").MustString("openi-notebook")
FileNoteBook.ImageIdNPU = sec.Key("imageid_npu").MustString("")
FileNoteBook.ImageGPU = sec.Key("image_gpu").MustString("")
FileNoteBook.SpecIdCPU = sec.Key("specid_cpu").MustInt64(-1)
FileNoteBook.SpecIdGPU = sec.Key("specid_gpu").MustInt64(-1)
FileNoteBook.SpecIdNPU = sec.Key("specid_npu").MustInt64(-1)
FileNoteBook.ImageIdNPUCD = sec.Key("imageid_npu_cd").MustString("")
FileNoteBook.SpecIdNPUCD = sec.Key("specid_npu_cd").MustInt64(-1)
FileNoteBook.ImageCPUDescription = sec.Key("image_cpu_desc").MustString("")
FileNoteBook.ImageGPUDescription = sec.Key("image_gpu_desc").MustString("")
FileNoteBook.ImageNPUDescription = sec.Key("image_npu_desc").MustString("")
FileNoteBook.ImageNPUCDDescription = sec.Key("image_npu_cd_desc").MustString("")

getGrampusConfig()
getModelartsCDConfig()
getModelConvertConfig()


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

@@ -41,6 +41,14 @@ type CreateTrainJobOption struct {
SpecId int64 `json:"spec_id" binding:"Required"`
}

type CreateFileNotebookJobOption struct {
Type int `json:"type"` //0 CPU 1 GPU 2 NPU
File string `json:"file" binding:"Required"`
BranchName string `json:"branch_name" binding:"Required"`
OwnerName string `json:"owner_name" binding:"Required"`
ProjectName string `json:"project_name" binding:"Required"`
}

type Cloudbrain struct {
ID int64 `json:"id"`
JobID string `json:"job_id"`


+ 3
- 0
modules/templates/helper.go View File

@@ -151,6 +151,9 @@ func NewFuncMap() []template.FuncMap {
"EscapePound": func(str string) string {
return strings.NewReplacer("%", "%25", "#", "%23", " ", "%20", "?", "%3F").Replace(str)
},
"IpynbBool":func(str string) bool{
return strings.Contains(str, ".ipynb")
},
"nl2br": func(text string) template.HTML {
return template.HTML(strings.Replace(template.HTMLEscapeString(text), "\n", "<br>", -1))
},


+ 17
- 2
options/locale/locale_en-US.ini View File

@@ -397,9 +397,12 @@ authorize_application_created_by = This application was created by %s.
authorize_application_description = If you grant the access, it will be able to access and write to all your account information, including private repos and organisations.
authorize_title = Authorize "%s" to access your account?
authorization_failed = Authorization failed
authorization_failed_desc = The authorization failed because we detected an invalid request. Please contact the maintainer of the app you've tried to authorize.
authorization_failed_desc = The authorization failed because we detected an invalid request. Please contact the maintainer of the app you have tried to authorize.
disable_forgot_password_mail = Account recovery is disabled. Please contact your site administrator.
sspi_auth_failed = SSPI authentication failed
change_email = Change email
change_email_address = Change email address
new_email_address = New email address
[phone]
format_err=The format of phone number is wrong.
query_err=Fail to query phone number, please try again later.
@@ -1015,6 +1018,8 @@ readme = README
readme_helper = Select a README file template.
auto_init = Initialize Repository (Adds .gitignore, License and README)
create_repo = Create Repository
failed_to_create_repo=Failed to create repository, please try again later.
failed_to_create_notebook_repo=Failed to create %s repository, please check whether you have the same name project, if yes please update or delete it first.
create_course = Publish Course
failed_to_create_course=Failed to publish course, please try again later.
default_branch = Default Branch
@@ -1049,6 +1054,10 @@ model_experience = Model Experience
model_noright=You have no right to do the operation.
model_rename=Duplicate model name, please modify model name.

notebook_file_not_exist=Notebook file does not exist.
notebook_select_wrong=Please select a Notebook(.ipynb) file first.
notebook_file_no_right=You have no right to access the Notebook(.ipynb) file.

date=Date
repo_add=Project Increment
repo_total=Project Total
@@ -1228,7 +1237,7 @@ cloudbrain.benchmark.evaluate_test=Test Script
cloudbrain.benchmark.types={"type":[{"id":1,"rank_link":"https://git.openi.org.cn/benchmark/?username=admin&algType=detection","first":"Target detection","second":[{"id":1,"value":"None","attachment":"84cf39c4-d8bc-41aa-aaa3-182ce289b105","owner":"yangzhx","repo_name":"detection_benchmark_script"}]},{"id":2,"rank_link":"https://git.openi.org.cn/benchmark/?username=admin&algType=reid","first":"Target re-identification","second":[{"id":1,"value":"Vehicle re-identification","attachment":"84cf39c4-d8bc-41aa-aaa3-182ce289b105","owner":"JiahongXu","repo_name":"benchmark_reID_script"},{"id":2,"value":"Image-based person re-identification","attachment":"84cf39c4-d8bc-41aa-aaa3-182ce289b105","owner":"JiahongXu","repo_name":"benchmark_reID_script"}]},{"id":3,"rank_link":"https://git.openi.org.cn/benchmark/?username=admin&algType=tracking","first":"Multi-target tracking","second":[{"id":1,"value":"None","attachment":"84cf39c4-d8bc-41aa-aaa3-182ce289b105","owner":"lix07","repo_name":"MOT_benchmark_script"}]}]}
cloudbrain.morethanonejob=You already have a running or waiting task, create it after that task is over.
cloudbrain.morethanonejob1=You have created an <span style="color:rgba(242, 113, 28, 1);"> equivalent task </span> that is waiting or running, please wait for the task to finish before creating it.
cloudbrain.morethanonejob2=You can view all your Cloud Brain tasks in <a href="/cloudbrains" target="_blank"> Home > Cloudbrain Task </a>.
cloudbrain.morethanonejob2=You can view all your Cloud Brain tasks in <a href="/cloudbrains" target="_blank"> Home > Cloudbrain Task </a>.

modelarts.infer_job_model = Model
modelarts.infer_job_model_file = Model File
@@ -1402,6 +1411,11 @@ star = Star
fork = Fork
download_archive = Download Repository
star_fail=Failed to %s the dataset.
watched=Watched
notWatched=Not watched
un_watch=Unwatch
watch_all=Watch all
watch_no_notify=Watch but not notify

no_desc = No Description
no_label = No labels
@@ -1443,6 +1457,7 @@ blame = Blame
normal_view = Normal View
line = line
lines = lines
notebook_open = Open in Notebook

editor.new_file = New File
editor.upload_file = Upload File


+ 15
- 0
options/locale/locale_zh-CN.ini View File

@@ -403,6 +403,9 @@ authorization_failed=授权失败
authorization_failed_desc=授权失败,这是一个无效的请求。请联系尝试授权应用的管理员。
disable_forgot_password_mail = Account recovery is disabled. Please contact your site administrator.
sspi_auth_failed=SSPI 认证失败
change_email=修改邮箱
change_email_address=修改邮箱地址
new_email_address=新邮箱地址
[phone]
format_err=手机号格式错误。
query_err=查询手机号失败,请稍后再试。
@@ -1020,6 +1023,8 @@ readme=自述
readme_helper=选择自述文件模板。
auto_init=初始化存储库 (添加. gitignore、许可证和自述文件)
create_repo=创建项目
failed_to_create_repo=创建项目失败,请稍后再试。
failed_to_create_notebook_repo=创建项目%s失败,请检查您是否有同名的项目,如果有请先手工修改或删除后重试。
create_course=发布课程
failed_to_create_course=发布课程失败,请稍后再试。
default_branch=默认分支
@@ -1048,6 +1053,9 @@ model_experience = 模型体验
model_noright=您没有操作权限。
model_rename=模型名称重复,请修改模型名称

notebook_file_not_exist=Notebook文件不存在。
notebook_select_wrong=请先选择Notebook(.ipynb)文件。
notebook_file_no_right=您没有这个Notebook文件的读权限。

date=日期
repo_add=新增项目
@@ -1418,6 +1426,11 @@ star=点赞
fork=派生
download_archive=下载此项目
star_fail=%s失败。
watched=已关注
notWatched=未关注
un_watch=不关注
watch_all=关注所有动态
watch_no_notify=关注但不提醒动态


no_desc=暂无描述
@@ -1461,6 +1474,8 @@ normal_view=普通视图
line=行
lines=行

notebook_open = 在Notebook中打开

editor.new_file=新建文件
editor.upload_file=上传文件
editor.edit_file=编辑文件


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

@@ -737,6 +737,12 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/my_favorite", repo.MyFavoriteDatasetMultiple)
}, reqToken(), repoAssignment())

m.Group("/file_notebook", func() {
m.Get("", reqToken(), repo.GetFileNoteBookInfo)
m.Post("/create", reqToken(), reqWeChat(), bind(api.CreateFileNotebookJobOption{}), repo.CreateFileNoteBook)

})

m.Group("/repos", func() {
m.Get("/search", repo.Search)



+ 72
- 2
routers/api/v1/repo/cloudbrain.go View File

@@ -79,6 +79,75 @@ func CloudBrainShow(ctx *context.APIContext) {
ctx.JSON(http.StatusOK, models.BaseMessageWithDataApi{Code: 0, Message: "", Data: convert.ToCloudBrain(task)})

}
func CreateFileNoteBook(ctx *context.APIContext, option api.CreateFileNotebookJobOption) {
cloudbrainTask.FileNotebookCreate(ctx.Context, option)
}

func GetFileNoteBookInfo(ctx *context.APIContext) {
//image description spec description waiting count

specs, err := models.GetResourceSpecificationByIds([]int64{setting.FileNoteBook.SpecIdCPU, setting.FileNoteBook.SpecIdGPU, setting.FileNoteBook.SpecIdNPU, setting.FileNoteBook.SpecIdNPUCD})
if err != nil {
log.Error("Fail to query specifications", err)
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_query_fail")))
return
}

var specCPU, specGpu, specNPU, specNPUCD *api.SpecificationShow
var specGpuQueueCode string
for _, spec := range specs {
if spec.ID == setting.FileNoteBook.SpecIdCPU {
specCPU = convert.ToSpecification(spec)
} else if spec.ID == setting.FileNoteBook.SpecIdGPU {
specGpu = convert.ToSpecification(spec)
specGpuQueueCode = spec.QueueCode
} else if spec.ID == setting.FileNoteBook.SpecIdNPU {
specNPU = convert.ToSpecification(spec)
} else if spec.ID == setting.FileNoteBook.SpecIdNPUCD {
specNPUCD = convert.ToSpecification(spec)
}
}

waitCountNpu := cloudbrain.GetWaitingCloudbrainCount(models.TypeCloudBrainTwo, "")

queuesMap, err := cloudbrain.GetQueuesDetail()
if err != nil {
log.Error("Fail to query gpu queues waiting count", err)
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_query_fail")))
return
}
waitCountGPU := (*queuesMap)[specGpuQueueCode]
if !setting.ModelartsCD.Enabled{
ctx.JSON(http.StatusOK, map[string]interface{}{
"code": 0,
"projectName":setting.FileNoteBook.ProjectName,
"specCpu": specCPU,
"specGpu": specGpu,
"specNpu": specNPU,
"waitCountGpu": waitCountGPU,
"waitCountNpu": waitCountNpu,
"imageCpuDescription": setting.FileNoteBook.ImageCPUDescription,
"imageGpuDescription": setting.FileNoteBook.ImageGPUDescription,
"imageNpuDescription": setting.FileNoteBook.ImageNPUDescription,

})
} else{
ctx.JSON(http.StatusOK, map[string]interface{}{
"code": 0,
"projectName":setting.FileNoteBook.ProjectName,
"specCpu": specCPU,
"specGpu": specGpu,
"specNpu": specNPUCD,
"waitCountGpu": waitCountGPU,
"waitCountNpu": waitCountNpu,
"imageCpuDescription": setting.FileNoteBook.ImageCPUDescription,
"imageGpuDescription": setting.FileNoteBook.ImageGPUDescription,
"imageNpuDescription": setting.FileNoteBook.ImageNPUCDDescription,
})

}

}

func CreateCloudBrain(ctx *context.APIContext, option api.CreateTrainJobOption) {
if option.Type == cloudbrainTask.TaskTypeCloudbrainOne {
@@ -141,10 +210,11 @@ func GetCloudbrainTask(ctx *context.APIContext) {
)

ID := ctx.Params(":id")
job, err := models.GetCloudbrainByID(ID)

job,err := cloudbrain.GetCloudBrainByIdOrJobId(ID)

if err != nil {
ctx.NotFound(err)
log.Error("GetCloudbrainByID failed:", err)
return
}
if job.JobType == string(models.JobTypeModelSafety) {


+ 55
- 46
routers/api/v1/repo/cloudbrain_dashboard.go View File

@@ -123,8 +123,9 @@ func GetOverviewDuration(ctx *context.Context) {
recordBeginTime := recordCloudbrain[0].Cloudbrain.CreatedUnix
now := time.Now()
endTime := now
// worker_server_num := 1
// cardNum := 1
var workServerNumber int64
var cardNum int64

durationAllSum := int64(0)
cardDuSum := int64(0)

@@ -138,52 +139,60 @@ func GetOverviewDuration(ctx *context.Context) {
c2NetDuration := int64(0)
cDCenterDuration := int64(0)

cloudbrains, _, err := models.CloudbrainAllStatic(&models.CloudbrainsOptions{
Type: models.TypeCloudBrainAll,
BeginTimeUnix: int64(recordBeginTime),
EndTimeUnix: endTime.Unix(),
})
if err != nil {
ctx.ServerError("Get cloudbrains failed:", err)
return
}
models.LoadSpecs4CloudbrainInfo(cloudbrains)

for _, cloudbrain := range cloudbrains {
cloudbrain = cloudbrainService.UpdateCloudbrainAiCenter(cloudbrain)
CardDurationString := repo.GetCloudbrainCardDuration(cloudbrain.Cloudbrain)
CardDuration := models.ConvertStrToDuration(CardDurationString)
// if cloudbrain.Cloudbrain.WorkServerNumber >= 1 {
// worker_server_num = cloudbrain.Cloudbrain.WorkServerNumber
// } else {
// worker_server_num = 1
// }
// if cloudbrain.Cloudbrain.Spec == nil {
// cardNum = 1
// } else {
// cardNum = cloudbrain.Cloudbrain.Spec.AccCardsNum
// }
// duration := cloudbrain.Duration
// duration := cloudbrain.Duration
duration := models.ConvertStrToDuration(cloudbrain.TrainJobDuration)
// CardDuration := cloudbrain.Duration * int64(worker_server_num) * int64(cardNum)

if cloudbrain.Cloudbrain.Type == models.TypeCloudBrainOne {
cloudBrainOneDuration += duration
cloudBrainOneCardDuSum += CardDuration
} else if cloudbrain.Cloudbrain.Type == models.TypeCloudBrainTwo {
cloudBrainTwoDuration += duration
cloudBrainTwoCardDuSum += CardDuration
} else if cloudbrain.Cloudbrain.Type == models.TypeC2Net {
c2NetDuration += duration
c2NetCardDuSum += CardDuration
} else if cloudbrain.Cloudbrain.Type == models.TypeCDCenter {
cDCenterDuration += duration
cDNetCardDuSum += CardDuration
page := 1
pagesize := 10000
count := pagesize
// Each time a maximum of 10000 pieces of data are detected to the memory, batch processing
for count == pagesize && count != 0 {
cloudbrains, _, err := models.CloudbrainAllStatic(&models.CloudbrainsOptions{
ListOptions: models.ListOptions{
Page: page,
PageSize: pagesize,
},
Type: models.TypeCloudBrainAll,
BeginTimeUnix: int64(recordBeginTime),
EndTimeUnix: endTime.Unix(),
})
if err != nil {
ctx.ServerError("Get cloudbrains failed:", err)
return
}
models.LoadSpecs4CloudbrainInfo(cloudbrains)

for _, cloudbrain := range cloudbrains {
cloudbrain = cloudbrainService.UpdateCloudbrainAiCenter(cloudbrain)
if cloudbrain.Cloudbrain.Spec != nil {
cardNum = int64(cloudbrain.Cloudbrain.Spec.AccCardsNum)
} else {
cardNum = 1
}
if cloudbrain.Cloudbrain.WorkServerNumber >= 1 {
workServerNumber = int64(cloudbrain.Cloudbrain.WorkServerNumber)
} else {
workServerNumber = 1
}
duration := models.ConvertStrToDuration(cloudbrain.TrainJobDuration)
CardDuration := workServerNumber * int64(cardNum) * duration

if cloudbrain.Cloudbrain.Type == models.TypeCloudBrainOne {
cloudBrainOneDuration += duration
cloudBrainOneCardDuSum += CardDuration
} else if cloudbrain.Cloudbrain.Type == models.TypeCloudBrainTwo {
cloudBrainTwoDuration += duration
cloudBrainTwoCardDuSum += CardDuration
} else if cloudbrain.Cloudbrain.Type == models.TypeC2Net {
c2NetDuration += duration
c2NetCardDuSum += CardDuration
} else if cloudbrain.Cloudbrain.Type == models.TypeCDCenter {
cDCenterDuration += duration
cDNetCardDuSum += CardDuration
}

durationAllSum += duration
cardDuSum += CardDuration
durationAllSum += duration
cardDuSum += CardDuration
}
count = len(cloudbrains)
page += 1
}
ctx.JSON(http.StatusOK, map[string]interface{}{
"cloudBrainOneCardDuSum": cloudBrainOneCardDuSum,


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

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

import (
"code.gitea.io/gitea/modules/cloudbrain"
"encoding/json"
"net/http"
"path"
@@ -37,11 +38,14 @@ func GetModelArtsNotebook2(ctx *context.APIContext) {
)

ID := ctx.Params(":id")
job, err := models.GetCloudbrainByID(ID)

job,err := cloudbrain.GetCloudBrainByIdOrJobId(ID)

if err != nil {
ctx.NotFound(err)
return
}

err = modelarts.HandleNotebookInfo(job)
if err != nil {
ctx.NotFound(err)


+ 4
- 5
routers/repo/aisafety.go View File

@@ -11,7 +11,8 @@ import (
"os"
"strconv"
"strings"
"time"

cloudbrainService "code.gitea.io/gitea/services/cloudbrain"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/aisafety"
@@ -483,7 +484,6 @@ func isTaskNotFinished(status string) bool {
}

func AiSafetyCreateForGetGPU(ctx *context.Context) {
t := time.Now()
ctx.Data["PageIsCloudBrain"] = true
ctx.Data["IsCreate"] = true
ctx.Data["type"] = models.TypeCloudBrainOne
@@ -497,7 +497,7 @@ func AiSafetyCreateForGetGPU(ctx *context.Context) {
log.Info("GPUBaseDataSetUUID=" + setting.ModelSafetyTest.GPUBaseDataSetUUID)
log.Info("GPUCombatDataSetName=" + setting.ModelSafetyTest.GPUCombatDataSetName)
log.Info("GPUCombatDataSetUUID=" + setting.ModelSafetyTest.GPUCombatDataSetUUID)
var displayJobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:]
var displayJobName = cloudbrainService.GetDisplayJobName(ctx.User.Name)
ctx.Data["display_job_name"] = displayJobName
prepareCloudbrainOneSpecs(ctx)
queuesDetail, _ := cloudbrain.GetQueuesDetail()
@@ -514,12 +514,11 @@ func AiSafetyCreateForGetGPU(ctx *context.Context) {
}

func AiSafetyCreateForGetNPU(ctx *context.Context) {
t := time.Now()
ctx.Data["PageIsCloudBrain"] = true
ctx.Data["IsCreate"] = true
ctx.Data["type"] = models.TypeCloudBrainTwo
ctx.Data["compute_resource"] = models.NPUResource
var displayJobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:]
var displayJobName = cloudbrainService.GetDisplayJobName(ctx.User.Name)
ctx.Data["display_job_name"] = displayJobName
ctx.Data["datasetType"] = models.TypeCloudBrainTwo
ctx.Data["BaseDataSetName"] = setting.ModelSafetyTest.NPUBaseDataSetName


+ 12
- 53
routers/repo/cloudbrain.go View File

@@ -15,6 +15,8 @@ import (
"time"
"unicode/utf8"

cloudbrainService "code.gitea.io/gitea/services/cloudbrain"

"code.gitea.io/gitea/modules/urfs_client/urchin"

"code.gitea.io/gitea/modules/dataset"
@@ -92,28 +94,9 @@ func MustEnableCloudbrain(ctx *context.Context) {
}
}

func cutString(str string, lens int) string {
if len(str) < lens {
return str
}
return str[:lens]
}

func jobNamePrefixValid(s string) string {
lowStr := strings.ToLower(s)
re := regexp.MustCompile(`[^a-z0-9_\\-]+`)

removeSpecial := re.ReplaceAllString(lowStr, "")

re = regexp.MustCompile(`^[_\\-]+`)
return re.ReplaceAllString(removeSpecial, "")

}

func cloudBrainNewDataPrepare(ctx *context.Context, jobType string) error {
ctx.Data["PageIsCloudBrain"] = true
t := time.Now()
var displayJobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:]
var displayJobName = cloudbrainService.GetDisplayJobName(ctx.User.Name)
ctx.Data["display_job_name"] = displayJobName

ctx.Data["command"] = cloudbrain.GetCloudbrainDebugCommand()
@@ -696,7 +679,7 @@ func CloudBrainRestart(ctx *context.Context) {
} else {
if count >= 1 {
log.Error("the user already has running or waiting task", ctx.Data["MsgID"])
resultCode = "-1"
resultCode = "2"
errorMsg = ctx.Tr("repo.cloudbrain.morethanonejob")
break
}
@@ -759,43 +742,13 @@ func cloudBrainShow(ctx *context.Context, tpName base.TplName, jobType models.Jo
return
}
if task.Status == string(models.JobWaiting) || task.Status == string(models.JobRunning) {
result, err := cloudbrain.GetJob(task.JobID)
task, err = cloudbrainTask.SyncCloudBrainOneStatus(task)
if err != nil {
log.Info("error:" + err.Error())
ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
return
}

if result != nil {
jobRes, _ := models.ConvertToJobResultPayload(result.Payload)
taskRoles := jobRes.TaskRoles
taskRes, _ := models.ConvertToTaskPod(taskRoles[cloudbrain.SubTaskName].(map[string]interface{}))
ctx.Data["taskRes"] = taskRes
ctx.Data["ExitDiagnostics"] = taskRes.TaskStatuses[0].ExitDiagnostics
oldStatus := task.Status
task.Status = taskRes.TaskStatuses[0].State
task.ContainerIp = ""
task.ContainerID = taskRes.TaskStatuses[0].ContainerID
models.ParseAndSetDurationFromCloudBrainOne(jobRes, task)

if task.DeletedAt.IsZero() { //normal record
if oldStatus != task.Status {
notification.NotifyChangeCloudbrainStatus(task, oldStatus)
}
err = models.UpdateJob(task)
if err != nil {
ctx.Data["error"] = err.Error()
return
}
} else { //deleted record

}

ctx.Data["result"] = jobRes
} else {
log.Info("error:" + err.Error())
return
}
}

user, err := models.GetUserByID(task.UserID)
@@ -889,7 +842,13 @@ func cloudBrainShow(ctx *context.Context, tpName base.TplName, jobType models.Jo
func CloudBrainDebug(ctx *context.Context) {
task := ctx.Cloudbrain
debugUrl := setting.DebugServerHost + "jpylab_" + task.JobID + "_" + task.SubTaskName
ctx.Redirect(debugUrl)
if task.BootFile!=""{
ctx.Redirect(getFileUrl(debugUrl,task.BootFile))

}else{
ctx.Redirect(debugUrl)
}

}

func prepareSpec4Show(ctx *context.Context, task *models.Cloudbrain) {


+ 27
- 24
routers/repo/cloudbrain_statistic.go View File

@@ -8,7 +8,6 @@ import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
cloudbrainService "code.gitea.io/gitea/services/cloudbrain"
)
@@ -23,9 +22,15 @@ func CloudbrainDurationStatisticHour() {
now := time.Now()
currentTime := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), 0, 0, 0, now.Location())
if err == nil && len(recordDurationUpdateTime) > 0 {
statisticTime = time.Unix(int64(recordDurationUpdateTime[0].DateTime), 0)
statisticTime = time.Unix(int64(recordDurationUpdateTime[0].DateTime), 0).Add(+1 * time.Hour)
} else {
statisticTime = currentTime.Add(-1 * time.Hour)
statisticTime = currentTime
}
deleteBeginTime := time.Unix(int64(recordDurationUpdateTime[0].DateTime), 0)

err = models.DeleteCloudbrainDurationStatistic(timeutil.TimeStamp(deleteBeginTime.Unix()), timeutil.TimeStamp(currentTime.Unix()))
if err != nil {
log.Error("DeleteCloudbrainDurationStatistic failed", err)
}

for statisticTime.Before(currentTime) || statisticTime.Equal(currentTime) {
@@ -35,13 +40,10 @@ func CloudbrainDurationStatisticHour() {
}
log.Info("summaryDurationStat count: %v", count)
}
func UpdateDurationStatisticHistoryData() int64 {
func UpdateDurationStatisticHistoryData(beginTime time.Time, endTime time.Time) int64 {
var count int64
recordBeginTime, _ := time.ParseInLocation("2006-01-02 15:04:05", setting.Grampus.UsageRateBeginTime, time.Local)
now := time.Now()
currentTime := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), 0, 0, 0, now.Location())
statisticTime := recordBeginTime.Add(+1 * time.Hour)

statisticTime := beginTime
currentTime := endTime
for statisticTime.Before(currentTime) || statisticTime.Equal(currentTime) {
countEach := summaryDurationStat(statisticTime)
count += countEach
@@ -105,13 +107,6 @@ func summaryDurationStat(statisticTime time.Time) int64 {
for cardType, cardDuration := range CardTypes {
cloudbrainTable := cloudbrainMap[centerCode+"/"+cardType]
if cloudbrainTable != nil {
if _, err := models.GetDurationStatisticByDate(dayTime, hourTime, centerCode, cardType); err == nil {
if err := models.DeleteCloudbrainDurationStatisticHour(dayTime, hourTime, centerCode, cardType); err != nil {
log.Error("DeleteCloudbrainDurationStatisticHour failed: %v", err.Error())
return 0
}
}

if _, ok := cardsTotalDurationMap[cloudbrainTable.Cluster+"/"+centerCode+"/"+cardType]; !ok {
cardsTotalDurationMap[cloudbrainTable.Cluster+"/"+centerCode+"/"+cardType] = 0
}
@@ -137,12 +132,6 @@ func summaryDurationStat(statisticTime time.Time) int64 {
}

for key, cardsTotalDuration := range cardsTotalDurationMap {
if _, err := models.GetDurationStatisticByDate(dayTime, hourTime, strings.Split(key, "/")[1], strings.Split(key, "/")[2]); err == nil {
if err := models.DeleteCloudbrainDurationStatisticHour(dayTime, hourTime, strings.Split(key, "/")[1], strings.Split(key, "/")[2]); err != nil {
log.Error("DeleteCloudbrainDurationStatisticHour failed: %v", err.Error())
return 0
}
}
cloudbrainDurationStat := models.CloudbrainDurationStatistic{
DateTime: dateTime,
DayTime: dayTime,
@@ -255,8 +244,22 @@ func getcloudBrainCenterCodeAndCardTypeInfo(ciTasks []*models.CloudbrainInfo, be
}

func CloudbrainUpdateHistoryData(ctx *context.Context) {
err := models.DeleteCloudbrainDurationStatistic()
count := UpdateDurationStatisticHistoryData()
beginTimeStr := ctx.QueryTrim("beginTime")
endTimeStr := ctx.QueryTrim("endTime")
var count int64
var err error
if beginTimeStr != "" && endTimeStr != "" {
beginTime, _ := time.ParseInLocation("2006-01-02 15:04:05", beginTimeStr, time.Local)
endTime, _ := time.ParseInLocation("2006-01-02 15:04:05", endTimeStr, time.Local)
if time.Now().Before(endTime) {
endTime = time.Now()
}
beginTimeUnix := timeutil.TimeStamp(beginTime.Unix())
endTimeUnix := timeutil.TimeStamp(endTime.Unix())

err = models.DeleteCloudbrainDurationStatistic(beginTimeUnix, endTimeUnix)
count = UpdateDurationStatisticHistoryData(beginTime.Add(+1*time.Hour), endTime)
}
ctx.JSON(http.StatusOK, map[string]interface{}{
"message": 0,
"count": count,


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

@@ -10,7 +10,6 @@ import (
"path"
"strconv"
"strings"
"time"

"code.gitea.io/gitea/modules/urfs_client/urchin"
"code.gitea.io/gitea/routers/response"
@@ -77,8 +76,7 @@ func GrampusTrainJobNPUNew(ctx *context.Context) {
func grampusTrainJobNewDataPrepare(ctx *context.Context, processType string) error {
ctx.Data["PageIsCloudBrain"] = true

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

//get valid images


+ 35
- 10
routers/repo/modelarts.go View File

@@ -15,6 +15,8 @@ import (
"time"
"unicode/utf8"

cloudbrainService "code.gitea.io/gitea/services/cloudbrain"

"code.gitea.io/gitea/services/cloudbrain/cloudbrainTask"

"code.gitea.io/gitea/modules/dataset"
@@ -128,8 +130,7 @@ func NotebookNew(ctx *context.Context) {

func notebookNewDataPrepare(ctx *context.Context) error {
ctx.Data["PageIsCloudBrain"] = true
t := time.Now()
var displayJobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:]
var displayJobName = cloudbrainService.GetDisplayJobName(ctx.User.Name)
ctx.Data["display_job_name"] = displayJobName

attachs, err := models.GetModelArtsUserAttachments(ctx.User.ID)
@@ -239,9 +240,9 @@ func Notebook2Create(ctx *context.Context, form auth.CreateModelArtsNotebookForm
}

if setting.ModelartsCD.Enabled {
err = modelarts_cd.GenerateNotebook(ctx, displayJobName, jobName, uuid, description, imageId, spec)
_, err = modelarts_cd.GenerateNotebook(ctx, displayJobName, jobName, uuid, description, imageId, spec, "",modelarts.AutoStopDurationMs)
} else {
err = modelarts.GenerateNotebook2(ctx, displayJobName, jobName, uuid, description, imageId, spec)
_, err = modelarts.GenerateNotebook2(ctx, displayJobName, jobName, uuid, description, imageId, spec, "",modelarts.AutoStopDurationMs)
}

if err != nil {
@@ -387,8 +388,33 @@ func NotebookDebug2(ctx *context.Context) {
ctx.RenderWithErr(err.Error(), tplModelArtsNotebookIndex, nil)
return
}
if task.BootFile!=""{
ctx.Redirect(getFileUrl(result.Url,task.BootFile) + "?token="+ result.Token)
}else{
ctx.Redirect(result.Url + "?token=" + result.Token)
}


ctx.Redirect(result.Url + "?token=" + result.Token)
}

func getFileUrl(url string,filename string) string{
middle:=""
if url[len(url)-3:]=="lab" || url[len(url)-4:]=="lab/" {
if url[len(url)-1] == '/' {
middle="tree/"
} else {
middle= "/tree/"
}
}else{
if url[len(url)-1] == '/' {
middle = "lab/tree/"
} else {
middle= "/lab/tree/"
}
}


return url+middle+path.Base(filename)
}

func NotebookRestart(ctx *context.Context) {
@@ -420,7 +446,8 @@ func NotebookRestart(ctx *context.Context) {
} else {
if count >= 1 {
log.Error("the user already has running or waiting task", ctx.Data["MsgID"])
errorMsg = "you have already a running or waiting task, can not create more"
resultCode="2"
errorMsg = ctx.Tr("repo.cloudbrain.morethanonejob")
break
}
}
@@ -714,8 +741,7 @@ func trainJobNewDataPrepare(ctx *context.Context) error {
// return
//}

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

attachs, err := models.GetModelArtsTrainAttachments(ctx.User.ID)
@@ -2351,8 +2377,7 @@ func inferenceJobNewDataPrepare(ctx *context.Context) error {
ctx.Data["PageIsCloudBrain"] = true
ctx.Data["newInference"] = true

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

attachs, err := models.GetModelArtsTrainAttachments(ctx.User.ID)


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

@@ -414,7 +414,9 @@ func Action(ctx *context.Context) {
var err error
switch ctx.Params(":action") {
case "watch":
err = models.WatchRepo(ctx.User.ID, ctx.Repo.Repository.ID, true)
err = models.WatchRepo(ctx.User.ID, ctx.Repo.Repository.ID, true, models.ReceiveAllNotification)
case "watch_but_reject":
err = models.WatchRepo(ctx.User.ID, ctx.Repo.Repository.ID, true, models.RejectAllNotification)
case "unwatch":
err = models.WatchRepo(ctx.User.ID, ctx.Repo.Repository.ID, false)
case "star":


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

@@ -519,6 +519,7 @@ func RegisterRoutes(m *macaron.Macaron) {
// r.Get("/feeds", binding.Bind(auth.FeedsForm{}), user.Feeds)
m.Any("/activate", user.Activate, reqSignIn)
m.Any("/activate_email", user.ActivateEmail)
m.Post("/update_email", bindIgnErr(auth.UpdateEmailForm{}), user.UpdateEmailPost)
m.Get("/avatar/:username/:size", user.Avatar)
m.Get("/email2user", user.Email2User)
m.Get("/recover_account", user.ResetPasswd)


+ 28
- 0
routers/user/auth.go View File

@@ -1413,6 +1413,34 @@ func SignUpPost(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterFo
handleSignInFull(ctx, u, false, true)
}

//update user emailAddress
func UpdateEmailPost(ctx *context.Context, form auth.UpdateEmailForm) {
newEmailAddress := ctx.Query("NewEmail")
if newEmailAddress == "" {
log.Error("please input the newEmail")
return
}
if used, _ := models.IsEmailUsed(newEmailAddress); used {
ctx.RenderWithErr(ctx.Tr("form.email_been_used"), TplActivate, &form)
return
}
user := ctx.User
email, err := models.GetEmailAddressByIDAndEmail(user.ID, user.Email)
if err != nil {
ctx.ServerError("GetEmailAddressByIDAndEmail failed", err)
return
}
err = email.UpdateEmailAddress(newEmailAddress)
if err != nil {
ctx.ServerError("UpdateEmailAddress failed", err)
return
}
ctx.Data["Email"] = newEmailAddress
ctx.User.Email = newEmailAddress
Activate(ctx)

}

// Activate render activate user page
func Activate(ctx *context.Context) {
code := ctx.Query("code")


+ 362
- 0
services/cloudbrain/cloudbrainTask/notebook.go View File

@@ -0,0 +1,362 @@
package cloudbrainTask

import (
"fmt"
"net/http"
"path"

"code.gitea.io/gitea/modules/modelarts"
"code.gitea.io/gitea/modules/modelarts_cd"

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

"code.gitea.io/gitea/modules/cloudbrain"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/redis/redis_key"
"code.gitea.io/gitea/modules/redis/redis_lock"
"code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/services/cloudbrain/resource"
"code.gitea.io/gitea/services/reward/point/account"

"code.gitea.io/gitea/modules/setting"
cloudbrainService "code.gitea.io/gitea/services/cloudbrain"
repo_service "code.gitea.io/gitea/services/repository"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
)

const NoteBookExtension = ".ipynb"

func FileNotebookCreate(ctx *context.Context, option api.CreateFileNotebookJobOption) {

if ctx.Written() {
return
}

if path.Ext(option.File) != NoteBookExtension {
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_select_wrong")))
return
}

isNotebookFileExist, _ := isNoteBookFileExist(ctx, option)
if !isNotebookFileExist {
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_file_not_exist")))
return
}

sourceRepo, err := models.GetRepositoryByOwnerAndName(option.OwnerName, option.ProjectName)
if err != nil {
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_file_not_exist")))
return
}

permission, err := models.GetUserRepoPermission(sourceRepo, ctx.User)
if err != nil {
log.Error("Get permission failed", err)
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_file_no_right")))
return
}

if !permission.CanRead(models.UnitTypeCode) {
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_file_no_right")))
return
}

//create repo if not exist
repo, err := models.GetRepositoryByName(ctx.User.ID, setting.FileNoteBook.ProjectName)
if repo == nil {
repo, err = repo_service.CreateRepository(ctx.User, ctx.User, models.CreateRepoOptions{
Name: setting.FileNoteBook.ProjectName,
Alias: "",
Description: "",
IssueLabels: "",
Gitignores: "",
License: "",
Readme: "Default",
IsPrivate: false,
AutoInit: true,
DefaultBranch: "master",
})
}
if err != nil {
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.failed_to_create_notebook_repo",setting.FileNoteBook.ProjectName)))
return
}
if option.Type <= 1 {
cloudBrainFileNoteBookCreate(ctx, option, repo, sourceRepo)
} else {
modelartsFileNoteBookCreate(ctx, option, repo, sourceRepo)
}

}

func cloudBrainFileNoteBookCreate(ctx *context.Context, option api.CreateFileNotebookJobOption, repo *models.Repository, sourceRepo *models.Repository) {

displayJobName := cloudbrainService.GetDisplayJobName(ctx.User.Name)
jobName := util.ConvertDisplayJobNameToJobName(displayJobName)
jobType := string(models.JobTypeDebug)

lock := redis_lock.NewDistributeLock(redis_key.CloudbrainBindingJobNameKey(fmt.Sprint(repo.ID), jobType, displayJobName))
defer lock.UnLock()
isOk, err := lock.Lock(models.CloudbrainKeyDuration)
if !isOk {
log.Error("lock processed failed:%v", err, ctx.Data["MsgID"])
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.cloudbrain_samejob_err")))
return
}

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"])
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.cloudbrain_samejob_err")))
return
}
} else {
if !models.IsErrJobNotExist(err) {
log.Error("system error, %v", err, ctx.Data["MsgID"])
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi("system error."))
return
}
}

count, err := GetNotFinalStatusTaskCount(ctx.User.ID, models.TypeCloudBrainOne, jobType)
if err != nil {
log.Error("GetCloudbrainCountByUserID failed:%v", err, ctx.Data["MsgID"])
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi("system error."))
return
} else {
if count >= 1 {
log.Error("the user already has running or waiting task", ctx.Data["MsgID"])
ctx.JSON(http.StatusOK,models.BaseMessageApi{
Code: 2,
Message: ctx.Tr("repo.cloudbrain.morethanonejob"),
})
return
}
}

errStr := uploadCodeFile(sourceRepo, getCodePath(jobName), option.BranchName, option.File, jobName)
if errStr != "" {
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_file_not_exist")))
return
}
command := cloudbrain.GetCloudbrainDebugCommand()
specId := setting.FileNoteBook.SpecIdGPU
if option.Type == 0 {
specId = setting.FileNoteBook.SpecIdCPU
}
spec, err := resource.GetAndCheckSpec(ctx.User.ID, specId, models.FindSpecsOptions{
JobType: models.JobType(jobType),
ComputeResource: models.GPU,
Cluster: models.OpenICluster,
AiCenterCode: models.AICenterOfCloudBrainOne})
if err != nil || spec == nil {
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("cloudbrain.wrong_specification")))
return
}

if !account.IsPointBalanceEnough(ctx.User.ID, spec.UnitPrice) {
log.Error("point balance is not enough,userId=%d specId=%d", ctx.User.ID, spec.ID)
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("points.insufficient_points_balance")))
return
}
ctx.Repo = &context.Repository{
Repository: repo,
}

req := cloudbrain.GenerateCloudBrainTaskReq{
Ctx: ctx,
DisplayJobName: displayJobName,
JobName: jobName,
Image: setting.FileNoteBook.ImageGPU,
Command: command,
Uuids: "",
DatasetNames: "",
DatasetInfos: nil,
CodePath: storage.GetMinioPath(jobName, cloudbrain.CodeMountPath+"/"),
ModelPath: storage.GetMinioPath(jobName, cloudbrain.ModelMountPath+"/"),
BenchmarkPath: storage.GetMinioPath(jobName, cloudbrain.BenchMarkMountPath+"/"),
Snn4ImageNetPath: storage.GetMinioPath(jobName, cloudbrain.Snn4imagenetMountPath+"/"),
BrainScorePath: storage.GetMinioPath(jobName, cloudbrain.BrainScoreMountPath+"/"),
JobType: jobType,
Description: getDescription(option),
BranchName: option.BranchName,
BootFile: option.File,
Params: "{\"parameter\":[]}",
CommitID: "",
BenchmarkTypeID: 0,
BenchmarkChildTypeID: 0,
ResultPath: storage.GetMinioPath(jobName, cloudbrain.ResultPath+"/"),
Spec: spec,
}

jobId, err := cloudbrain.GenerateTask(req)
if err != nil {
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(err.Error()))
return
}
ctx.JSON(http.StatusOK, models.BaseMessageApi{
Code: 0,
Message: jobId,
})

}

func getCodePath(jobName string) string {
return setting.JobPath + jobName + cloudbrain.CodeMountPath
}

func getDescription(option api.CreateFileNotebookJobOption) string {
return option.OwnerName + "/" + option.ProjectName + "/" + option.File
}

func modelartsFileNoteBookCreate(ctx *context.Context, option api.CreateFileNotebookJobOption, repo *models.Repository, sourceRepo *models.Repository) {
displayJobName := cloudbrainService.GetDisplayJobName(ctx.User.Name)
jobName := util.ConvertDisplayJobNameToJobName(displayJobName)

lock := redis_lock.NewDistributeLock(redis_key.CloudbrainBindingJobNameKey(fmt.Sprint(repo.ID), string(models.JobTypeDebug), displayJobName))
isOk, err := lock.Lock(models.CloudbrainKeyDuration)
if !isOk {
log.Error("lock processed failed:%v", err, ctx.Data["MsgID"])
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.cloudbrain_samejob_err")))
return
}
defer lock.UnLock()

count, err := GetNotFinalStatusTaskCount(ctx.User.ID, models.TypeCloudBrainTwo, string(models.JobTypeDebug))

if err != nil {
log.Error("GetCloudbrainNotebookCountByUserID failed:%v", err, ctx.Data["MsgID"])

ctx.JSON(http.StatusOK, models.BaseErrorMessageApi("system error."))
return
} else {
if count >= 1 {
log.Error("the user already has running or waiting task", ctx.Data["MsgID"])
ctx.JSON(http.StatusOK,models.BaseMessageApi{
Code: 2,
Message: ctx.Tr("repo.cloudbrain.morethanonejob"),
})
return
}
}

tasks, err := models.GetCloudbrainsByDisplayJobName(repo.ID, string(models.JobTypeDebug), displayJobName)
if err == nil {
if len(tasks) != 0 {
log.Error("the job name did already exist", ctx.Data["MsgID"])
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.cloudbrain_samejob_err")))
return
}
} else {
if !models.IsErrJobNotExist(err) {
log.Error("system error, %v", err, ctx.Data["MsgID"])
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi("system error."))
return
}
}

err = downloadCode(sourceRepo, getCodePath(jobName), option.BranchName)
if err != nil {
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("cloudbrain.load_code_failed")))
return
}

var aiCenterCode = models.AICenterOfCloudBrainTwo
var specId = setting.FileNoteBook.SpecIdNPU
if setting.ModelartsCD.Enabled {
aiCenterCode = models.AICenterOfChengdu
specId = setting.FileNoteBook.SpecIdNPUCD
}
spec, err := resource.GetAndCheckSpec(ctx.User.ID, specId, models.FindSpecsOptions{
JobType: models.JobTypeDebug,
ComputeResource: models.NPU,
Cluster: models.OpenICluster,
AiCenterCode: aiCenterCode})
if err != nil || spec == nil {
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("cloudbrain.wrong_specification")))
return
}
if !account.IsPointBalanceEnough(ctx.User.ID, spec.UnitPrice) {
log.Error("point balance is not enough,userId=%d specId=%d ", ctx.User.ID, spec.ID)
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("points.insufficient_points_balance")))
return
}
ctx.Repo = &context.Repository{
Repository: repo,
}

var jobId string
if setting.ModelartsCD.Enabled {
jobId, err = modelarts_cd.GenerateNotebook(ctx, displayJobName, jobName, "", getDescription(option), setting.FileNoteBook.ImageIdNPUCD, spec, option.File,modelarts.AutoStopDurationMs/4)
} else {
jobId, err = modelarts.GenerateNotebook2(ctx, displayJobName, jobName, "", getDescription(option), setting.FileNoteBook.ImageIdNPU, spec, option.File,modelarts.AutoStopDurationMs/4)
}

if err != nil {
log.Error("GenerateNotebook2 failed, %v", err, ctx.Data["MsgID"])

ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(err.Error()))

return
}

ctx.JSON(http.StatusOK, models.BaseMessageApi{
Code: 0,
Message: jobId,
})

}

func isNoteBookFileExist(ctx *context.Context, option api.CreateFileNotebookJobOption) (bool, error) {
repoPathOfNoteBook := models.RepoPath(option.OwnerName, option.ProjectName)

gitRepoOfNoteBook, err := git.OpenRepository(repoPathOfNoteBook)
if err != nil {
log.Error("RepoRef Invalid repo "+repoPathOfNoteBook, err.Error())
return false, err
}
// We opened it, we should close it
defer func() {
// If it's been set to nil then assume someone else has closed it.
if gitRepoOfNoteBook != nil {
gitRepoOfNoteBook.Close()
}
}()
fileExist, err := fileExists(gitRepoOfNoteBook, option.File, option.BranchName)
if err != nil || !fileExist {
log.Error("Get file error:", err, ctx.Data["MsgID"])

return false, err
}
return true, nil
}

func uploadCodeFile(repo *models.Repository, codePath string, branchName string, filePath string, jobName string) string {
err := downloadCode(repo, codePath, branchName)
if err != nil {
return "cloudbrain.load_code_failed"
}

err = uploadOneFileToMinio(codePath, filePath, jobName, cloudbrain.CodeMountPath+"/")
if err != nil {
return "cloudbrain.load_code_failed"
}
return ""
}

func fileExists(gitRepo *git.Repository, path string, branch string) (bool, error) {

commit, err := gitRepo.GetBranchCommit(branch)
if err != nil {
return false, err
}
if _, err := commit.GetTreeEntryByPath(path); err != nil {
return false, err
}
return true, nil
}

+ 16
- 7
services/cloudbrain/cloudbrainTask/sync_status.go View File

@@ -1,20 +1,21 @@
package cloudbrainTask

import (
"net/http"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/cloudbrain"
"code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification"
"code.gitea.io/gitea/modules/setting"
"net/http"
"strconv"
)

var noteBookOKMap = make(map[int64]int, 20)
var noteBookFailMap = make(map[int64]int, 20)

//if a task notebook url can get two times, the notebook can browser.
//if a task notebook url can get successfulCount times, the notebook can browser.
const successfulCount = 3
const maxSuccessfulCount=10

func SyncCloudBrainOneStatus(task *models.Cloudbrain) (*models.Cloudbrain, error) {
jobResult, err := cloudbrain.GetJob(task.JobID)
@@ -62,21 +63,29 @@ func isNoteBookReady(task *models.Cloudbrain) bool {
return true
}
noteBookUrl := setting.DebugServerHost + "jpylab_" + task.JobID + "_" + task.SubTaskName
r := httplib.Get(noteBookUrl)
res, err := r.Response()
res,err := http.Get(noteBookUrl)
if err != nil {
return false
}
log.Info("notebook success count:"+strconv.Itoa(noteBookOKMap[task.ID])+",fail count:"+strconv.Itoa(noteBookFailMap[task.ID]))
if res.StatusCode == http.StatusOK {
count := noteBookOKMap[task.ID]
if count < successfulCount-1 {
if count==0{ //如果是第一次成功,把失败数重置为0
noteBookFailMap[task.ID]=0
}

if count < successfulCount-1 || (noteBookFailMap[task.ID]==0 && count < maxSuccessfulCount-1) {
noteBookOKMap[task.ID] = count + 1
return false
} else {
log.Info("notebook success count:"+strconv.Itoa(count)+",fail count:"+strconv.Itoa(noteBookFailMap[task.ID]))
delete(noteBookOKMap, task.ID)
delete(noteBookFailMap, task.ID)
return true
}

}else{
noteBookFailMap[task.ID]+=1
}
return false



+ 12
- 0
services/cloudbrain/cloudbrainTask/train.go View File

@@ -810,6 +810,18 @@ func uploadCodeToMinio(codePath, jobName, parentDir string) error {
return nil
}

func uploadOneFileToMinio(codePath, filePath, jobName, parentDir string) error {
destObject := setting.CBCodePathPrefix + jobName + parentDir + path.Base(filePath)
sourceFile := codePath + "/" + filePath
err := storage.Attachments.UploadObject(destObject, sourceFile)
if err != nil {
log.Error("UploadObject(%s) failed: %s", filePath, err.Error())
return err
}
return nil

}

func readDir(dirname string) ([]os.FileInfo, error) {
f, err := os.Open(dirname)
if err != nil {


+ 26
- 0
services/cloudbrain/util.go View File

@@ -1,7 +1,11 @@
package cloudbrain

import (
"regexp"
"strconv"
"strings"
"time"


"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
@@ -33,6 +37,28 @@ func GetAiCenterShow(aiCenter string, ctx *context.Context) string {

}

func GetDisplayJobName(username string) string {
t := time.Now()
return jobNamePrefixValid(cutString(username, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:]
}

func cutString(str string, lens int) string {
if len(str) < lens {
return str
}
return str[:lens]
}

func jobNamePrefixValid(s string) string {
lowStr := strings.ToLower(s)
re := regexp.MustCompile(`[^a-z0-9_\\-]+`)

removeSpecial := re.ReplaceAllString(lowStr, "")

re = regexp.MustCompile(`^[_\\-]+`)
return re.ReplaceAllString(removeSpecial, "")
}

func GetAiCenterInfoByCenterCode(aiCenterCode string) *setting.C2NetSequenceInfo {
if setting.AiCenterCodeAndNameMapInfo != nil {
if info, ok := setting.AiCenterCodeAndNameMapInfo[aiCenterCode]; ok {


+ 2
- 0
templates/admin/cloudbrain/list.tmpl View File

@@ -238,6 +238,7 @@
{{$.i18n.Tr "repo.debug"}}
</a>
{{else}}
{{if not .BootFile}}
<a id="ai-debug-{{$JobID}}"
class='ui basic ai_debug {{if eq .Status "CREATING" "STOPPING" "WAITING" "STARTING"}} disabled {{else}}blue {{end}}button'
data-jobid="{{$JobID}}"
@@ -245,6 +246,7 @@
{{$.i18n.Tr "repo.debug_again"}}
</a>
{{end}}
{{end}}
</form>
</div>
{{end}}


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

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

<td class="ti-text-form-content">
@@ -444,6 +444,7 @@
<td class="ti-text-form-content">
<div class="text-span text-span-w">
{{.BranchName}}
<span style="margin-left:1rem" class="ui label">{{SubStr .CommitID 0 10}}</span>
</div>
</td>
</tr>


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

@@ -337,6 +337,7 @@
<td class="ti-text-form-content">
<div class="text-span text-span-w" id="{{.VersionName}}-code">
{{.BranchName}}
<span style="margin-left:1rem" class="ui label">{{SubStr .CommitID 0 10}}</span>
</div>
</td>
</tr>


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

@@ -410,6 +410,7 @@
<td class="ti-text-form-content">
<div class="text-span text-span-w">
{{.BranchName}}
<span style="margin-left:1rem" class="ui label">{{SubStr .CommitID 0 10}}</span>
</div>
</td>
</tr>


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

@@ -221,7 +221,7 @@
<!--任务状态 -->
<span class="job-status" id="{{.Cloudbrain.ID}}"
data-repopath="{{$.RepoRelPath}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}"
data-jobid="{{.Cloudbrain.ID}}" data-resource="{{.ComputeResource}}">
data-jobid="{{.Cloudbrain.ID}}" data-resource="{{.ComputeResource}}" data-bootfile="{{.BootFile}}">
<span><i id="{{.Cloudbrain.ID}}-icon" style="vertical-align: middle;"
class="{{.Status}}"></i><span id="{{.Cloudbrain.ID}}-text"
style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span>
@@ -266,6 +266,7 @@
{{$.i18n.Tr "repo.debug"}}
</a>
{{else}}
{{if not .BootFile}}
<a id="ai-debug-{{.Cloudbrain.ID}}"
class='ui basic ai_debug {{if eq .Status "CREATING" "STOPPING" "WAITING" "STARTING"}} disabled {{else}}blue {{end}}button'
data-jobid="{{.Cloudbrain.ID}}"
@@ -274,6 +275,7 @@
{{$.i18n.Tr "repo.debug_again"}}
</a>
{{end}}
{{end}}
{{else}}
{{if eq .Status "RUNNING" "WAITING" "CREATING" "STARTING"}}
<a class="ui basic disabled button">
@@ -294,7 +296,9 @@
<a id="ai-stop-{{.Cloudbrain.ID}}"
class='ui basic ai_stop {{if eq .Status "STOPPED" "FAILED" "START_FAILED" "STOPPING" "CREATING" "STARTING" "SUCCEEDED" "CREATE_FAILED" "DELETED"}}disabled {{else}}blue {{end}}button'
data-repopath="{{$.RepoLink}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}/{{.Cloudbrain.ID}}/stop"
data-jobid="{{.Cloudbrain.ID}}">
data-jobid="{{.Cloudbrain.ID}}"
{{if .BootFile}}data-bootfile="{{.BootFile}}"{{end}}>
{{$.i18n.Tr "repo.stop"}}
</a>
{{else}}
@@ -322,6 +326,7 @@
{{end}}
</form>
</div>
{{if not .BootFile}}
<div class="ui compact buttons"
style="{{if eq .ComputeResource "CPU/GPU"}} visibility: visible {{else}} visibility: hidden{{end}}">
<div class="ui dropdown" id="model_more"
@@ -363,6 +368,7 @@
</div>
</div>
</div>
{{end}}
</div>
<!-- 镜像列表弹窗 -->



+ 1
- 0
templates/repo/grampus/trainjob/show.tmpl View File

@@ -422,6 +422,7 @@
<td class="ti-text-form-content">
<div class="text-span text-span-w">
{{.BranchName}}
<span style="margin-left:1rem" class="ui label">{{SubStr .CommitID 0 10}}</span>
</div>
</td>
</tr>


+ 45
- 2
templates/repo/header.tmpl View File

@@ -51,6 +51,49 @@
</div>
{{if not .IsBeingCreated}}
<div class="repo-buttons">
<div class="ui labeled button" tabindex="0">
<div style="position:relative;" class="ui compact basic button" onclick="$('.__watch_btn__').dropdown('show')">
<i class="icon fa-eye{{if not $.IsWatchingRepo}}-slash{{end}}"></i>{{if $.IsWatchingRepo}}{{$.i18n.Tr "repo.watched"}}{{else}}{{$.i18n.Tr "repo.notWatched"}}{{end}}
<i class="dropdown icon" style="margin:0 -8px 0 4px"></i>
<div style="position:absolute;left:0;top:23px;" class="ui dropdown floating __watch_btn__" onclick="event.stopPropagation();">
<div class="text" style="display:none;"></div>
{{$WatchNotifyType := or $.WatchNotifyType 0}}
<div class="menu">
<div class="item {{if not $.IsWatchingRepo}}active selected{{end}}">
<form method="post" style="margin: 0;" action="{{$.RepoLink}}/action/unwatch?redirect_to={{$.Link}}">
{{$.CsrfTokenHtml}}
<button type="submit" style="border:none;background:transparent;width:100%;text-align:left;font-weight:inherit;cursor:pointer;">
<i class="check icon" style="{{if $.IsWatchingRepo}}opacity:0{{end}}"></i>
{{$.i18n.Tr "repo.un_watch"}}
</button>
</form>
</div>
<div class="item {{if and $.IsWatchingRepo (eq $WatchNotifyType 9)}}active selected{{end}}">
<form method="post" style="margin: 0;" action="{{$.RepoLink}}/action/watch?redirect_to={{$.Link}}">
{{$.CsrfTokenHtml}}
<button type="submit" style="border:none;background:transparent;width:100%;text-align:left;font-weight:inherit;cursor:pointer;">
<i class="check icon" style="{{if not (and $.IsWatchingRepo (eq $WatchNotifyType 9))}}opacity:0{{end}}"></i>
{{$.i18n.Tr "repo.watch_all"}}
</button>
</form>
</div>
<div class="item {{if and $.IsWatchingRepo (eq $WatchNotifyType 0)}}active selected{{end}}">
<form method="post" style="margin: 0;" action="{{$.RepoLink}}/action/watch_but_reject?redirect_to={{$.Link}}">
{{$.CsrfTokenHtml}}
<button type="submit" style="border:none;background:transparent;width:100%;text-align:left;font-weight:inherit;cursor:pointer;">
<i class="check icon" style="{{if not (and $.IsWatchingRepo (eq $WatchNotifyType 0))}}opacity:0{{end}}"></i>
{{$.i18n.Tr "repo.watch_no_notify"}}
</button>
</form>
</div>
</div>
</div>
</div>
<a class="ui basic label" href="{{.Link}}/watchers">
{{.NumWatches}}
</a>
</div>
<!--
<form method="post" style="margin: 0;" action="{{$.RepoLink}}/action/{{if $.IsWatchingRepo}}un{{end}}watch?redirect_to={{$.Link}}">
{{$.CsrfTokenHtml}}
<div class="ui labeled button" tabindex="0">
@@ -61,7 +104,7 @@
{{.NumWatches}}
</a>
</div>
</form>
</form> -->
<form method="post" style="margin: 0;" action="{{$.RepoLink}}/action/{{if $.IsStaringRepo}}un{{end}}star?redirect_to={{$.Link}}">
{{$.CsrfTokenHtml}}
<div class="ui labeled button" tabindex="0">
@@ -90,7 +133,7 @@
{{end}}
<div class="ui tabs container">
{{if not .Repository.IsBeingCreated}}
<div class="ui tabular menu navbar">
<div class="ui tabular menu navbar" style="overflow-x:auto;overflow-y:hidden">
{{if .Permission.CanRead $.UnitTypeCode}}
<div class="dropdown-menu">
<a class="{{if or .PageIsViewCode .PageIsReleaseList .PageIsWiki .PageIsActivity .PageIsViewCode}}active{{end}} item hover_active" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/{{.BranchNameSubURL | EscapePound}}{{end}}">


+ 29
- 29
templates/repo/home.tmpl View File

@@ -99,6 +99,9 @@
animation-fill-mode: both;
}
</style>
{{if and (.IsViewFile) (IpynbBool .TreePath)}}
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/vp-notebook-debug.css?v={{MD5 AppVer}}" />
{{end}}
<div class="repository file list">
{{template "repo/header" .}}
<div class="ui container">
@@ -154,7 +157,9 @@
{{.i18n.Tr "repo.archive.title"}}
</div>
{{end}}
{{if not .IsViewFile}}
{{template "repo/sub_menu" .}}
{{end}}
<div class="ui stackable secondary menu mobile--margin-between-items mobile--no-negative-margins">
{{template "repo/branch_dropdown" .}}
{{ $n := len .TreeNames}}
@@ -200,6 +205,7 @@
class="section"><a href="{{EscapePound $.BranchLink}}/{{EscapePound $p}}"
title="{{$v}}">{{EllipsisString $v 30}}</a></span>{{end}}{{end}}</span></div>
{{end}}
<div class="right fitted item" id="file-buttons">
<div class="ui tiny blue buttons">
{{if .Repository.CanEnableEditor}}
@@ -223,7 +229,6 @@
</a>
{{end}}
</div>

</div>
<div class="fitted item">
{{if eq $n 0}}
@@ -237,7 +242,6 @@
{{end}}
</div>
<div class="fitted item">

<!-- Only show clone panel in repository home page -->
{{if eq $n 0}}
<div class="ui action tiny input" id="clone-panel">
@@ -279,10 +283,22 @@
</div>
{{end}}
</div>
{{if and (.IsViewFile) (IpynbBool .TreePath)}}
<div class="right fitted item">
<button class="ui green button tiny" id="notebook-debug" style="display: flex;align-items: center;">
<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 3h18a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1zm1 2v14h16V5H4zm8 10h6v2h-6v-2zm-3.333-3L5.838 9.172l1.415-1.415L11.495 12l-4.242 4.243-1.415-1.415L8.667 12z" fill="rgba(255,255,255,1)"/></svg>
<span style="margin-left:0.3rem;font-size: 14px;">{{.i18n.Tr "repo.notebook_open"}}</span>
</button>
</div>
{{end}}
</div>
<div class="ui container">
<div class="ui mobile reversed stackable grid">
{{if not .IsViewFile}}
<div class="ui ten wide tablet twelve wide computer column">
{{else}}
<div class="ui sixteen wide tablet sixteen wide computer column">
{{end}}
{{if .IsViewFile}}
{{template "repo/view_file" .}}
{{else if .IsBlame}}
@@ -291,6 +307,7 @@
{{template "repo/view_list" .}}
{{end}}
</div>
{{if not .IsViewFile}}
<div class="ui six wide tablet four wide computer column">
<div id="repo-desc" data-IsAdmin="{{.Permission.IsAdmin}}"
data-IsArchived="{{.Repository.IsArchived}}">
@@ -303,25 +320,17 @@
{{else}}
<span class="no-description text-italic">{{.i18n.Tr "repo.no_desc"}}</span>
{{end}}

</p>

</div>



{{if .Repository.Website}}
<p class="ui">
<i class="gray linkify icon"></i>
<a class="link edit-link" target="_blank" title="{{.Repository.Website}}"
href="{{.Repository.Website}}">{{.Repository.Website}}</a>
</p>

{{end}}

<div class="ui" id="repo-topics" style="display: flex;position: relative;margin-bottom: 1.0rem;">
<i class="grey bookmark icon"></i>

<div id="repo-topics1" style="flex: 1;">
{{range .Topics}}
<a class="ui repo-topic small label topic"
@@ -334,32 +343,22 @@
</div>
<div id="topic_edit" class="vue_menu" style="display:none">
<div id="topic_edit1">

</div>
</div>

</div>


<p class="ui">
<i class="grey code icon"></i>
{{range .LanguageStats}}
{{.Language}}
{{end}}
</p>



{{if .LICENSE}}
<p class="ui">
<i class="grey clone icon"></i>
{{.LICENSE}}
</p>

{{end}}

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

<div>
<h4 class="ui header">
{{$lenCon := len .ContributorInfo}}
@@ -387,19 +386,20 @@
{{end}}
</div>
</div>

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

</div>
{{if and (.IsViewFile) (IpynbBool .TreePath)}}
<div id="__vue-root"></div>
<div id="__vue-self-data" data-branch="{{.BranchName}}" data-owner="{{.Repository.OwnerName}}" data-name="{{.SignedUser.Name}}" data-project="{{.Repository.Name}}"
data-file="{{.TreePath}}">
</div>
{{end}}
</div>

<script type="text/javascript">
// $(document).ready(function(){
// $(".membersmore").click(function(){
// $("#contributorInfo > a:nth-child(n+25)").show();
// });
// });
</script>
{{if and (.IsViewFile) (IpynbBool .TreePath)}}
<script src="{{StaticUrlPrefix}}/js/vp-notebook-debug.js?v={{MD5 AppVer}}"></script>
{{end}}
{{template "base/footer" .}}

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

@@ -388,6 +388,7 @@ td, th {
<td class="ti-text-form-content">
<div class="text-span text-span-w">
{{.BranchName}}
<span style="margin-left:1rem" class="ui label">{{SubStr .CommitID 0 10}}</span>
</div>
</td>
</tr>


+ 1
- 0
templates/repo/modelarts/trainjob/show.tmpl View File

@@ -442,6 +442,7 @@
<td class="ti-text-form-content">
<div class="text-span text-span-w">
{{.BranchName}}
<span style="margin-left:1rem" class="ui label">{{SubStr .CommitID 0 10}}</span>
</div>
</td>
</tr>


+ 1
- 0
templates/repo/modelsafety/show.tmpl View File

@@ -833,6 +833,7 @@
$(`[vfield="Description"]`).text(res['Description'] || '--');
$(`[vfield="Parameters"]`).text(res['Parameters'] || '--');
$(`[vfield="BranchName"]`).html(res['BranchName'] + '<span style="margin-left:1rem" class="ui label">' + res['CommitID'].slice(0, 10) + '</span>');

var imageName = res['Image'] || res['EngineName'];
$(`[vimagetitle="Image"] span`).hide();


+ 29
- 1
templates/user/auth/activate.tmpl View File

@@ -15,7 +15,7 @@
{{else if .ResendLimited}}
<p class="center">{{.i18n.Tr "auth.resent_limit_prompt"}}</p>
{{else}}
<p>{{.i18n.Tr "auth.confirmation_mail_sent_prompt" .SignedUser.Email .ActiveCodeLives | Str2html}}</p>
<p>{{.i18n.Tr "auth.confirmation_mail_sent_prompt" .Email .ActiveCodeLives | Str2html}}</p>
{{end}}
{{else}}
{{if .IsSendRegisterMail}}
@@ -26,6 +26,7 @@
<p>{{.i18n.Tr "auth.has_unconfirmed_mail" .SignedUser.Name .SignedUser.Email | Str2html}}</p>
<div class="ui divider"></div>
<div class="text right">
<button type="button" class="ui blue button change">{{.i18n.Tr "auth.change_email"}}</button>
<button class="ui blue button">{{.i18n.Tr "auth.resend_mail"}}</button>
</div>
{{end}}
@@ -34,5 +35,32 @@
</form>
</div>
</div>
<div>
<div class="ui modal chang-email">
<div class="header" style="padding: 1rem;background-color: rgba(240, 240, 240, 100);">
<h4>{{.i18n.Tr "auth.change_email_address"}}</h4>
</div>
<form id="formId" action="{{AppSubUrl}}/user/update_email" method="POST" class="ui form">
<div class="content content-padding">
<div class="ui error message">
</div>
{{$.CsrfTokenHtml}}
<div class="inline required field">
<label>{{.i18n.Tr "auth.new_email_address"}}</label>
<input style="width: 80%;" id="label" name="NewEmail" maxlength="255" value="{{.SignedUser.Email}}">
</div>
</div>
<div class="center actions">
<button class="ui green button">{{.i18n.Tr "repo.confirm_choice"}}</button>
<div class="ui deny button">{{.i18n.Tr "cancel"}}</div>
</div>
</form>
</div>
</div>
</div>
{{template "base/footer" .}}
<script>
$('.ui.blue.button.change').on('click',function(){
$('.ui.modal').modal('show')
})
</script>

+ 6
- 4
templates/user/dashboard/cloudbrains.tmpl View File

@@ -124,7 +124,8 @@
style="width: 8% !important;">
<span class="job-status" id="{{$JobID}}"
data-repopath='{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .JobType "DEBUG"}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}{{else if eq .JobType "INFERENCE"}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts{{end}}/inference-job{{else if eq .JobType "TRAIN"}}{{if eq .ComputeResource "NPU"}}/modelarts/train-job{{else}}/cloudbrain/train-job{{end}}{{else if eq .JobType "BENCHMARK" "MODELSAFETY"}}/cloudbrain{{end}}'
data-jobid="{{$JobID}}" data-version="{{.VersionName}}">
data-jobid="{{$JobID}}" data-version="{{.VersionName}}"
data-bootfile="{{.BootFile}}">
<span><i id="{{$JobID}}-icon" style="vertical-align: middle;"
class="{{.Status}}"></i><span id="{{$JobID}}-text"
style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span>
@@ -185,7 +186,6 @@
<a href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}"
title="{{.Repo.OwnerName}}/{{.Repo.Alias}}">{{.Repo.OwnerName}}/{{.Repo.Alias}}</a>
</div>

<div class="three wide column text center nowrap" style="width: 15%!important;">
{{if eq .JobType "DEBUG"}}
<div class="ui compact buttons">
@@ -199,6 +199,7 @@
{{$.i18n.Tr "repo.debug"}}
</a>
{{else}}
{{if not .BootFile}}
<a id="ai-debug-{{$JobID}}"
class='ui basic ai_debug {{if eq .Status "CREATING" "STOPPING" "WAITING" "STARTING"}} disabled {{else}}blue {{end}}button'
data-jobid="{{$JobID}}"
@@ -206,6 +207,7 @@
{{$.i18n.Tr "repo.debug_again"}}
</a>
{{end}}
{{end}}
</form>
</div>
{{end}}
@@ -228,7 +230,7 @@
<a style="padding: 0.5rem 1rem;" id="ai-stop-{{$JobID}}"
class='ui basic ai_stop {{if eq .Status "KILLED" "FAILED" "START_FAILED" "KILLING" "COMPLETED" "SUCCEEDED" "STOPPED" "STOPPING" "CREATE_FAILED"}}disabled {{else}} blue {{end}}button'
data-repopath='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else if eq .JobType "BENCHMARK" }}/cloudbrain/benchmark{{else if eq .ComputeResource "NPU" }}/modelarts/notebook{{end}}/{{$JobID}}/stop'
data-jobid="{{$JobID}}">
data-jobid="{{$JobID}}" data-bootfile="{{.BootFile}}">
{{$.i18n.Tr "repo.stop"}}
</a>
</form>
@@ -236,7 +238,7 @@
<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" "STOPPED" "SUCCEEDED" "CREATE_FAILED"}}disabled {{else}} blue {{end}}button'
data-repopath='{{.Repo.OwnerName}}/{{.Repo.Name}}/{{if eq .JobType "INFERENCE"}}{{if eq .Cloudbrain.Type 1}}modelarts/inference-job{{else}}cloudbrain/train-job{{end}}{{else if eq .JobType "TRAIN"}}{{if eq .Cloudbrain.Type 1}}modelarts/train-job{{else if eq .Cloudbrain.Type 0}}cloudbrain/train-job{{else if eq .Cloudbrain.Type 2}}grampus/train-job{{end}}{{end}}'
data-jobid="{{$JobID}}" data-version="{{.VersionName}}">
data-jobid="{{$JobID}}" data-version="{{.VersionName}}" data-bootfile="{{.BootFile}}">
{{$.i18n.Tr "repo.stop"}}
</a>
{{end}}


+ 23
- 12
web_src/js/features/cloudrbanin.js View File

@@ -24,6 +24,7 @@ export default async function initCloudrain() {
const repoPath = job.dataset.repopath;
// const computeResource = job.dataset.resource
const versionname = job.dataset.version;
const bootfile = job.dataset.bootfile;
const status_text = $(`#${ID}-text`).text();
const finalState = [
"STOPPED",
@@ -99,11 +100,16 @@ export default async function initCloudrain() {
"SUCCEEDED",
].includes(status)
) {
$("#ai-debug-" + ID)
.removeClass("disabled")
.addClass("blue")
.text(debug_again_button)
.css("margin", "0");
if (!bootfile) {
$("#ai-debug-" + ID)
.removeClass("disabled")
.addClass("blue")
.text(debug_again_button)
.css("margin", "0");
} else {
$("#ai-debug-" + ID).remove()
}
}
if (["RUNNING", "WAITING"].includes(status)) {
$("#ai-stop-" + ID)
@@ -289,7 +295,7 @@ export default async function initCloudrain() {
assertDelete(this);
}
});
function stopDebug(ID, stopUrl) {
function stopDebug(ID, stopUrl,bootFile) {
$.ajax({
type: "POST",
url: stopUrl,
@@ -301,11 +307,15 @@ export default async function initCloudrain() {
.addClass(res.status);
$("#" + ID + "-text").text(res.status);
if (res.status === "STOPPED") {
$("#ai-debug-" + ID)
.removeClass("disabled")
.addClass("blue")
.text(debug_again_button)
.css("margin", "0");
if (!bootFile) {
$("#ai-debug-" + ID)
.removeClass("disabled")
.addClass("blue")
.text(debug_again_button)
.css("margin", "0");
} else {
$("#ai-debug-" + ID).remove()
}
$("#ai-image-" + ID)
.removeClass("blue")
.addClass("disabled");
@@ -344,7 +354,8 @@ export default async function initCloudrain() {
$(".ui.basic.ai_stop").click(function () {
const ID = this.dataset.jobid;
const repoPath = this.dataset.repopath;
stopDebug(ID, repoPath);
const bootFile = this.dataset.bootfile
stopDebug(ID, repoPath,bootFile);
});

function stopVersion(version_name, ID, repoPath) {


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

@@ -1301,3 +1301,17 @@ i.SUCCEEDED {
max-height: 500px;
overflow: auto;
}
.chang-email .content {
display: block;
width: 100%;
font-size: 1em;
line-height: 1.4;
padding: 1.5rem;
background: #fff;
}
.chang-email .actions {
background: #f9fafb;
padding: 1rem 1rem;
border-top: 1px solid rgba(34,36,38,.15);
text-align: right;
}

+ 47
- 0
web_src/vuepages/apis/modules/notobook.js View File

@@ -0,0 +1,47 @@
import service from "../service";

// Notebook新建页面需要的信息
export const getFileNotebook = () => {
return service({
url: "/api/v1/file_notebook",
method: "get",
params: {},
});
};

// Notebook新建调试任务
// type, file, branch_name, owner_name, project_name
export const createNotebook = (data) => {
return service({
url: "/api/v1/file_notebook/create",
method: "post",
data,
params: {},
});
};

// Notebook获取云脑I调试任务状态
export const getCb1Notebook = (path,jobid) => {
return service({
url: `/api/v1/${path}/cloudbrain/${jobid}`,
method: "get",
params: {},
});
};

// Notebook获取云脑I调试任务状态
export const getCb2Notebook = (path,jobid) => {
return service({
url: `/api/v1/${path}/modelarts/notebook/${jobid}`,
method: "get",
params: {},
});
};

export const stopNotebook = (url) => {
return service({
url: url,
method: "post",
params: {},
});
};

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

@@ -2,7 +2,6 @@ import axios from 'axios';

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

service.interceptors.request.use((config) => {


+ 31
- 3
web_src/vuepages/langs/config/en-US.js View File

@@ -157,7 +157,7 @@ const en = {
computeCluster: 'Compute Cluster',
resourceSpecification: 'Resource Specification',
lastUpdateTime: 'Last Update Time',
resSceneDeleteConfirm: 'Are you sure to delete the current Resource Scene?',
resSceneDeleteConfirm: 'Are you sure to delete the current Resource Scene?',
resourceSpecificationIsAvailable: 'Specification Is Available',
resourceSpecificationIsAvailableAll: 'Specification Is Available(All)',
available: 'Available',
@@ -192,7 +192,35 @@ const en = {
dataDesensitizationModelExperience:'Data desensitization model experience',
dataDesensitizationModelDesc:'Use AI technology to desensitize the face and license plate number in the picture. For more information about this model, please visit the project',
limitFilesUpload:'Only jpg/jpeg/png files can be uploaded',
limitSizeUpload:'The size of the uploaded file cannot exceed 20M!',
limitSizeUpload: 'The size of the uploaded file cannot exceed 20M!',
notebook: {
createNewNotebook: "Create new notebook debug task",
sameTaskTips1: "You have created an",
sameTaskTips2: "equivalent task",
sameTaskTips3: "that is waiting or running, please wait for the task to finish before creating it.",
sameTaskTips4: "You can view all your Cloudbrain tasks in",
sameTaskTips5: "Home",
sameTaskTips6: "Cloudbrain Task",
sameTaskTips7: "",
cpuEnv: "CPU Environment",
gpuEnv: "GPU Environment",
npuEnv: "NPU environment",
newTask: "Create new task",
noQuene: "The current resources are sufficient and no tasks are queued",
queneTips1: "There are currently",
queneTips2: "tasks queued",
watiResource: "Waiting for resources to be allocated, please be patient",
debug: "Debug",
stop: "Stop",
stopping: "Stopping",
notebookRunning: "The debugging environment has been started, and it will automatically close after 4 hours ",
stopSuccess: "Stop task succeeded",
specification: "Specification",
graphicMemory: "Graphic Memory",
memory: "Memory",
sharedMemory: "Shared Memory",
tips: 'The newly created debugging task will be placed in the project openi-notebook under your name. If there is no such project, the system will automatically create a new one.'
},
modelManage: {
modelManage: 'Model management',
modelName: 'Model name',
@@ -200,7 +228,7 @@ const en = {
local: 'Local',
online: 'Online',
createModel: 'Create Model',
importLocalModel: 'Import Lacal Model',
importLocalModel: 'Import Local Model',
importOnlineModel: 'Import Online Model',
modifyModelInfo: 'Modify model information',
addModelFiles: 'Add model files',


+ 217
- 186
web_src/vuepages/langs/config/zh-CN.js View File

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

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

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

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

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

resourcesManagement: {
OpenI: '启智集群',
C2Net: '智算集群',
OpenIOne: '云脑一',
OpenITwo: '云脑二',
OpenIChengdu: '启智成都智算',
chengduCenter: '成都智算',
pclcci: '鹏城云计算所',
hefeiCenter: '合肥类脑类脑智能开放平台',
xuchangCenter: '中原人工智能计算中心',
willOnShelf: '待上架',
onShelf: '已上架',
offShelf: '已下架',
toOnShelf: '上架',
toOffShelf: '下架',
toSetPriceAndOnShelf: '定价上架',
status: '状态',
allStatus: '全部状态',
syncAiNetwork: '同步智算网络',
resQueue: '资源池(队列)',
allResQueue: '全部资源池(队列)',
addResQueue: '新建资源池(队列)',
addResQueueBtn: '新增资源池',
editResQueue: '修改资源池(队列)',
resQueueName: '资源池(队列)名称',
whichCluster: '所属集群',
allCluster: '全部集群',
aiCenter: '智算中心',
aiCenterID: '智算中心ID',
allAiCenter: '全部智算中心',
computeResource: '计算资源',
allComputeResource: '全部计算资源',
accCardType: '卡类型',
allAccCardType: '全部卡类型',
cardsTotalNum: '卡数',
accCardsNum: '卡数',
remark: '备注',
pleaseEnterRemark: '请输入备注(最大长度不超过255)',
pleaseEnterPositiveIntegerCardsTotalNum: '请输入正整数的卡数!',
addResSpecificationAndPriceInfo: '新增资源规格和单价信息',
addResSpecificationBtn: '新增资源规格',
editResSpecificationAndPriceInfo: '修改资源规格和单价信息',
resSpecificationAndPriceManagement: '资源规格单价管理',
sourceSpecCode: '对应资源编码',
sourceSpecCodeTips: '云脑II需要填写对应的资源编码',
sourceSpecId: '智算网络资源规格ID',
cpuNum: 'CPU数',
gpuMem: '显存',
mem: '内存',
shareMem: '共享内存',
unitPrice: '单价',
point_hr: '积分/时',
node: '节点',
free: '免费',
onShelfConfirm: '请确认上架该规格?',
offShelfConfirm: '请确认下架该规格?',
onShelfCode1001: '上架失败,资源池(队列)不可用。',
onShelfCode1003: '上架失败,资源规格不可用。',
offShelfDlgTip1: '当前资源规格已在以下场景中使用:',
offShelfDlgTip2: '请确认进行下架操作?',
resSceneManagement: '算力资源应用场景管理',
addResScene: '新建算力资源应用场景',
addResSceneBtn: '新增应用场景',
editResScene: '修改算力资源应用场景',
resSceneName: '应用场景名称',
jobType: '任务类型',
allJobType: '全部任务类型',
isExclusive: '是否专属',
allExclusiveAndCommonUse: '全部专属和通用',
exclusive: '专属',
commonUse: '通用',
exclusiveOrg: '专属组织',
exclusiveOrgTips: '多个组织名之间用英文分号隔开',
computeCluster: '算力集群',
resourceSpecification: '资源规格',
lastUpdateTime: '最后更新时间',
resSceneDeleteConfirm: '是否确认删除当前应用场景?',
resourceSpecificationIsAvailable: '资源规格是否可用',
resourceSpecificationIsAvailableAll: '资源规格是否可用(全部)',
available: '可用',
notAvailable: '不可用',
OpenI: "启智集群",
C2Net: "智算集群",
OpenIOne: "云脑一",
OpenITwo: "云脑二",
OpenIChengdu: "启智成都智算",
chengduCenter: "成都智算",
pclcci: "鹏城云计算所",
hefeiCenter: "合肥类脑类脑智能开放平台",
xuchangCenter: "中原人工智能计算中心",
willOnShelf: "待上架",
onShelf: "已上架",
offShelf: "已下架",
toOnShelf: "上架",
toOffShelf: "下架",
toSetPriceAndOnShelf: "定价上架",
status: "状态",
allStatus: "全部状态",
syncAiNetwork: "同步智算网络",
resQueue: "资源池(队列)",
allResQueue: "全部资源池(队列)",
addResQueue: "新建资源池(队列)",
addResQueueBtn: "新增资源池",
editResQueue: "修改资源池(队列)",
resQueueName: "资源池(队列)名称",
whichCluster: "所属集群",
allCluster: "全部集群",
aiCenter: "智算中心",
aiCenterID: "智算中心ID",
allAiCenter: "全部智算中心",
computeResource: "计算资源",
allComputeResource: "全部计算资源",
accCardType: "卡类型",
allAccCardType: "全部卡类型",
cardsTotalNum: "卡数",
accCardsNum: "卡数",
remark: "备注",
pleaseEnterRemark: "请输入备注(最大长度不超过255)",
pleaseEnterPositiveIntegerCardsTotalNum: "请输入正整数的卡数!",
addResSpecificationAndPriceInfo: "新增资源规格和单价信息",
addResSpecificationBtn: "新增资源规格",
editResSpecificationAndPriceInfo: "修改资源规格和单价信息",
resSpecificationAndPriceManagement: "资源规格单价管理",
sourceSpecCode: "对应资源编码",
sourceSpecCodeTips: "云脑II需要填写对应的资源编码",
sourceSpecId: "智算网络资源规格ID",
cpuNum: "CPU数",
gpuMem: "显存",
mem: "内存",
shareMem: "共享内存",
unitPrice: "单价",
point_hr: "积分/时",
node: "节点",
free: "免费",
onShelfConfirm: "请确认上架该规格?",
offShelfConfirm: "请确认下架该规格?",
onShelfCode1001: "上架失败,资源池(队列)不可用。",
onShelfCode1003: "上架失败,资源规格不可用。",
offShelfDlgTip1: "当前资源规格已在以下场景中使用:",
offShelfDlgTip2: "请确认进行下架操作?",
resSceneManagement: "算力资源应用场景管理",
addResScene: "新建算力资源应用场景",
addResSceneBtn: "新增应用场景",
editResScene: "修改算力资源应用场景",
resSceneName: "应用场景名称",
jobType: "任务类型",
allJobType: "全部任务类型",
isExclusive: "是否专属",
allExclusiveAndCommonUse: "全部专属和通用",
exclusive: "专属",
commonUse: "通用",
exclusiveOrg: "专属组织",
exclusiveOrgTips: "多个组织名之间用英文分号隔开",
computeCluster: "算力集群",
resourceSpecification: "资源规格",
lastUpdateTime: "最后更新时间",
resSceneDeleteConfirm: "是否确认删除当前应用场景?",
resourceSpecificationIsAvailable: "资源规格是否可用",
resourceSpecificationIsAvailableAll: "资源规格是否可用(全部)",
available: "可用",
notAvailable: "不可用",
},
user: {
inviteFriends: '邀请好友',
inviteFriendsTips: '复制二维码或者注册邀请链接分享给好友',
clickToViewTheEventDetails: '点击查看活动详情',
copyRegistrationInvitationLink: '复制注册邀请链接',
registrationAdress: '注册地址:',
recommender: '推荐人:',
invitedFriends: '已邀请好友',
registrationTime: '注册时间',
theSharedContentHasBeenCopiedToTheClipboard: '分享内容已复制到剪切板',
copyError: '复制错误',
Activated: '已激活',
notActive: '未激活',
inviteFriends: "邀请好友",
inviteFriendsTips: "复制二维码或者注册邀请链接分享给好友",
clickToViewTheEventDetails: "点击查看活动详情",
copyRegistrationInvitationLink: "复制注册邀请链接",
registrationAdress: "注册地址:",
recommender: "推荐人:",
invitedFriends: "已邀请好友",
registrationTime: "注册时间",
theSharedContentHasBeenCopiedToTheClipboard: "分享内容已复制到剪切板",
copyError: "复制错误",
Activated: "已激活",
notActive: "未激活",
},
tranformImageFailed: "图片脱敏失败",
originPicture: "原始图片",
desensitizationPicture: "脱敏图片",
desensitizationObject: "脱敏对象",
example: "示例",
startDesensitization: "开始处理",
all: "全部",
onlyFace: "仅人脸",
onlyLicensePlate: "仅车牌",
dragThePictureHere: "拖动图片到这里",
or: "或",
clickUpload: "点击上传",
dataDesensitizationModelExperience: "数据脱敏模型体验",
dataDesensitizationModelDesc:
"利用人工智能AI技术,把图片中的人脸、车牌号码进行脱敏处理。该模型更多信息请访问项目",
limitFilesUpload: "只能上传 jpg/jpeg/png 格式的文件",
limitSizeUpload: "上传文件大小不能超过 20M !",
notebook: {
createNewNotebook: "新建Notebook调试任务",
sameTaskTips1: "您已经有",
sameTaskTips2: "同类任务",
sameTaskTips3: "正在等待或运行中,请等待任务结束再创建",
sameTaskTips4: "可以在",
sameTaskTips5: "个人中心",
sameTaskTips6: "云脑任务",
sameTaskTips7: "查看您所有的云脑任务。",
cpuEnv: "CPU 环境",
gpuEnv: "GPU 环境",
npuEnv: "NPU 环境",
newTask: "新建任务",
noQuene: "当前资源充足,没有任务排队",
queneTips1: "当前有",
queneTips2: "个任务正在排队",
watiResource: "正在等待分配资源,请耐心等待",
debug: "调试",
stop: "停止",
stopping: "停止中",
notebookRunning: "调试环境已启动,单次连接 4 小时后自动关闭",
stopSuccess: "停止任务成功",
specification: "规格",
graphicMemory: "显存",
memory: "内存",
sharedMemory: "共享内存",
tips:'本次新建的调试任务会放在您名下项目openi-notebook中,如果没有该项目系统会自动新建一个。'
},
tranformImageFailed:'图片脱敏失败',
originPicture:'原始图片',
desensitizationPicture:'脱敏图片',
desensitizationObject:'脱敏对象',
example:'示例',
startDesensitization:'开始处理',
all:'全部',
onlyFace:'仅人脸',
onlyLicensePlate:'仅车牌',
dragThePictureHere:'拖动图片到这里',
or:'或',
clickUpload:'点击上传',
dataDesensitizationModelExperience:'数据脱敏模型体验',
dataDesensitizationModelDesc:'利用人工智能AI技术,把图片中的人脸、车牌号码进行脱敏处理。该模型更多信息请访问项目',
limitFilesUpload:'只能上传 jpg/jpeg/png 格式的文件',
limitSizeUpload:'上传文件大小不能超过 20M !',
modelManage: {
modelManage: '模型管理',
modelName: '模型名称',
@@ -254,6 +283,8 @@ const zh = {
deleteModelFileConfirmTips: '请确认是否删除当前模型文件?',
modelFileDeleteFailed: '模型文件删除失败',
},
}
};



export default zh;

+ 662
- 0
web_src/vuepages/pages/notebook/debug/index.vue View File

@@ -0,0 +1,662 @@
<template>
<div>
<el-dialog
:title="$t('notebook.createNewNotebook')"
:visible.sync="dialogVisible"
width="50%"
:close-on-click-modal="false"
@closed="handleClose"
>
<div class="wrapper" v-loading="loading" element-loading-spinner="el-icon-loading">
<div style="text-align: center;padding-bottom: 12px;">
<span class="text-tip">{{$t('notebook.tips')}}</span>
</div>
<div v-show="alertCb" class="ui message alert-info">
<div style="display: flex;align-items: center;">
<i class="ri-information-line" style="font-size: 35px;color: rgba(242, 113, 28, 1);;"></i>
<div style="text-align: left;margin-left: 1rem;">
<div style="font-weight: 600;line-height: 2;">{{$t('notebook.sameTaskTips1')}} <span style="color:rgba(242, 113, 28, 1);">{{$t('notebook.sameTaskTips2')}}</span> {{$t('notebook.sameTaskTips3')}}</div>
<div style="color:#939393">{{$t('notebook.sameTaskTips4')}} “<a href="/cloudbrains" target="_blank">{{$t('notebook.sameTaskTips5')}} &gt; {{$t('notebook.sameTaskTips6')}}</a>” {{$t('notebook.sameTaskTips7')}}</div>
</div>
</div>
</div>
<div
class="three-resource-type"
:class="{ active: selectIndex == 0 }"
@click="selectResource(0)"
>
<div class="resource-child-node">
<div class="resource-type-icon background-C">
<span class="text">C</span>
</div>
<div class="resource-type-detail">
<div class="detail-title"><span>{{$t('notebook.cpuEnv')}}</span></div>
<div class="detail-spec">
<span>{{cpuSpec}}</span>
</div>
<div class="detail-spec">
<span>{{$t('image')}}:{{notebookInfo.imageCpuDescription}}</span>
</div>
</div>
<div class="resource-select">
<i v-if="selectIndex===0" :class="{'slide-in-bottom': !slideActive && !initSelect }" class="ri-checkbox-circle-line green"></i>
<i
class="ri-checkbox-blank-circle-line gray"
:class="{'fade-out':selectIndex===0}"
></i>
</div>
</div>
</div>
<div
class="three-resource-type"
:class="{ active: selectIndex == 2 }"
@click="selectResource(2)"
>
<div class="resource-child-node">
<div class="resource-type-icon background-N">
<span class="text">N</span>
</div>
<div class="resource-type-detail">
<div class="detail-title"><span>{{$t('notebook.npuEnv')}}</span></div>
<div class="detail-spec">
<span>{{npuSpec}}</span>
</div>
<div class="detail-spec">
<span>{{$t('image')}}:{{notebookInfo.imageNpuDescription}}</span>
</div>
</div>
<div class="resource-select">
<i v-if="selectIndex===2" :class="[slideActive && !initSelect ?'slide-in-top':'slide-in-bottom']" class="ri-checkbox-circle-line green"></i>
<i
class="ri-checkbox-blank-circle-line gray"
:class="{'fade-out':selectIndex===2}"
></i>
</div>
</div>
</div>
<div
class="three-resource-type"
:class="{ active: selectIndex == 1 }"
@click="selectResource(1)"
>
<div class="resource-child-node">
<div class="resource-type-icon background-G">
<span class="text">G</span>
</div>
<div class="resource-type-detail">
<div class="detail-title"><span>{{$t('notebook.gpuEnv')}}</span></div>
<div class="detail-spec">
<span>{{gpuSpec}}</span>
</div>
<div class="detail-spec">
<span>{{$t('image')}}:{{notebookInfo.imageGpuDescription}}</span>
</div>
</div>
<div class="resource-select">
<i v-if="selectIndex===1 && !initSelect" :class="{'slide-in-top': slideActive && !initSelect}" class="ri-checkbox-circle-line green"></i>
<i v-if="selectIndex===1 && initSelect" class="ri-checkbox-circle-line green"></i>
<i
class="ri-checkbox-blank-circle-line gray"
:class="{'fade-out':selectIndex===1}"
></i>
</div>
</div>
</div>
<div class="resource-footer">
<div class="resource-operate" v-if="selectIndex==0">
<div v-if="btnStatus[0]===0">
<button class="ui green small button" @click="createTask(0)"></i>{{$t('notebook.newTask')}}</button>
<span v-if="notebookInfo.waitCountGpu==0" class="text">{{$t('notebook.noQuene')}}</span>
<span v-else class="text">{{$t('notebook.queneTips1')}} <span style="color: red;">{{notebookInfo.waitCountGpu}}</span> {{$t('notebook.queneTips2')}}</span>
</div>
<div v-else-if="btnStatus[0]===1">
<button class="ui disabled small button" style="background-color: #888;"><i class="loading spinner icon" style="color: #fff;"></i>{{$t('notebook.newTask')}}</button>
<span class="text">{{$t('notebook.watiResource')}}</span>
</div>
<div v-else-if="btnStatus[0]===2">
<button class="ui small button" style="background-color: #1684fc;">
<a style="color:#fff" :href="deubgUrlGpu" target="_blank">{{$t('notebook.debug')}}</a>
</button>
<button class="ui small button" @click="stopDebug(0)">{{$t('notebook.stop')}}</button>
<span class="text">{{$t('notebook.notebookRunning')}}</span>
</div>
<div v-else>
<button class="ui disabled small button" style="background-color: #888;"><i class="loading spinner icon" style="color: #fff;"></i>{{$t('notebook.stopping')}}</button>
</div>
</div>
<div class="resource-operate" v-if="selectIndex==2">
<div v-if="btnStatus[2]===0">
<button class="ui green small button" @click="createTask(2)"></i>{{$t('notebook.newTask')}}</button>
<span v-if="notebookInfo.waitCountNpu==0" class="text">{{$t('notebook.noQuene')}}</span>
<span v-else class="text">{{$t('notebook.queneTips1')}} <span style="color: red;">{{notebookInfo.waitCountNpu}}</span> {{$t('notebook.queneTips2')}}</span>
</div>
<div v-else-if="btnStatus[2]===1">
<button class="ui disabled small button" style="background-color: #888;"><i class="loading spinner icon" style="color: #fff;"></i>{{$t('notebook.newTask')}}</button>
<span class="text">{{$t('notebook.watiResource')}}</span>
</div>
<div v-else-if="btnStatus[2]===2">
<button class="ui small button" style="background-color: #1684fc;">
<a style="color:#fff" :href="deubgUrlNpu" target="_blank">{{$t('notebook.debug')}}</a>
</button>
<button class="ui small button" @click="stopDebug(2)">{{$t('notebook.stop')}}</button>
<span class="text">{{$t('notebook.notebookRunning')}}</span>
</div>
<div v-else>
<button class="ui disabled small button" style="background-color: #888;"><i class="loading spinner icon" style="color: #fff;"></i>{{$t('notebook.stopping')}}</button>
</div>
</div>
<div class="resource-operate" v-if="selectIndex==1">
<div v-if="btnStatus[1]===0">
<button class="ui green small button" @click="createTask(1)"></i>{{$t('notebook.newTask')}}</button>
<span v-if="notebookInfo.waitCountGpu==0" class="text">{{$t('notebook.noQuene')}}</span>
<span v-else class="text">{{$t('notebook.queneTips1')}} <span style="color: red;">{{notebookInfo.waitCountGpu}}</span> {{$t('notebook.queneTips2')}}</span>
</div>
<div v-else-if="btnStatus[1]===1">
<button class="ui disabled small button" style="background-color: #888;"><i class="loading spinner icon" style="color: #fff;"></i>{{$t('notebook.newTask')}}</button>
<span class="text">{{$t('notebook.watiResource')}}</span>
</div>
<div v-else-if="btnStatus[1]===2">
<button class="ui small button" style="background-color: #1684fc;">
<a style="color:#fff" :href="deubgUrlGpu" target="_blank">{{$t('notebook.debug')}}</a>
</button>
<button class="ui small button" @click="stopDebug(1)">{{$t('notebook.stop')}}</button>
<span class="text">{{$t('notebook.notebookRunning')}}</span>
</div>
<div v-else>
<button class="ui disabled small button" style="background-color: #888;"><i class="loading spinner icon" style="color: #fff;"></i>{{$t('notebook.stopping')}}</button>
</div>
</div>
</div>
</div>
</el-dialog>
</div>
</template>
<script>
import { getFileNotebook,createNotebook,getCb1Notebook,getCb2Notebook,stopNotebook } from "~/apis/modules/notobook";
import { Message } from "element-ui";
let timerCb1,timerCb2
let {AppSubUrl} = window.config
const finalState = [
"STOPPED",
"CREATE_FAILED",
"UNAVAILABLE",
"DELETED",
"RESIZE_FAILED",
"SUCCEEDED",
"IMAGE_FAILED",
"SUBMIT_FAILED",
"DELETE_FAILED",
"KILLED",
"COMPLETED",
"FAILED",
"CANCELED",
"LOST",
"START_FAILED",
"SUBMIT_MODEL_FAILED",
"DEPLOY_SERVICE_FAILED",
"CHECK_FAILED",
"STOPPING"
];
export default {
data() {
return {
dialogVisible: false,
selectIndex: 0,
slideActive:true,
initSelect:true,
notebookInfo:{specCpu:{acc_cards_num:0},specGpu:{acc_cards_num:0},specNpu:{acc_cards_num:0}},
fileInfo:{
file:'',
branch_name:'',
owner_name:'',
project_name:'',
sign_name:''
},
btnStatus:{0:0,1:0,2:0},
alertCb:false,
deubgUrlNpu:'',
deubgUrlGpu:'',
deubgUrlNpuStop:'',
deubgUrlGpuStop:'',
loading:false,
activeLoadFirst:true
};
},
methods: {
handleClose(done) {
this.initSelect = true
this.alertCb = false
},
selectResource(index) {
this.getNotebookInfo()
if(index==this.selectIndex){
return
}
if(index>this.selectIndex && this.selectIndex!==1){
this.slideActive = true
}else if(index<this.selectIndex && index==1){
this.slideActive = true
}else{
this.slideActive = false
}
this.selectIndex = index;
this.initSelect = false
this.alertCb = false
},
getNotebookInfo(){
if(this.activeLoadFirst){
this.loading = true
}
getFileNotebook().then((res)=>{
if(res.data.code==0){
this.notebookInfo = res.data
}else{
Message.error(res.data.message)
}
this.loading = false
this.activeLoadFirst = false
}).catch((err)=>{
Message.error(err)
this.loading = false
this.activeLoadFirst = false
})
},
getCb1NotebookInfo(path,id,index){
getCb1Notebook(path,id).then((res)=>{
if(res.status===200){
if(res.data.JobStatus==="RUNNING"){
this.btnStatus[index]=2
this.deubgUrlGpu = `${AppSubUrl}/${this.fileInfo.sign_name}/${this.notebookInfo.projectName}/cloudbrain/${id}/debug`
this.deubgUrlGpuStop = `${AppSubUrl}/${this.fileInfo.sign_name}/${this.notebookInfo.projectName}/cloudbrain/${id}/stop`
clearInterval(timerCb1)
}
if(finalState.includes(res.data.JobStatus)){
this.btnStatus[index] = 0
clearInterval(timerCb1)
}
}
})
},
getCb2NotebookInfo(path,id){
getCb2Notebook(path,id).then((res)=>{
if(res.status===200){
if(res.data.JobStatus==="RUNNING"){
this.btnStatus[2]=2
this.deubgUrlNpu = `${AppSubUrl}/${this.fileInfo.sign_name}/${this.notebookInfo.projectName}/modelarts/notebook/${id}/debug`
this.deubgUrlNpuStop = `${AppSubUrl}/${this.fileInfo.sign_name}/${this.notebookInfo.projectName}/modelarts/notebook/${id}/stop`
clearInterval(timerCb2)
}
if(finalState.includes(res.data.JobStatus)){
this.btnStatus[2] = 0
clearInterval(timerCb2)
}
}
})
},
stopDebug(index){
this.btnStatus[index]=3
let url = index===2 ? this.deubgUrlNpuStop :this.deubgUrlGpuStop
stopNotebook(url).then((res)=>{
if(res.data.result_code==='0'){
this.btnStatus[index]=0
Message.success(this.$t("notebook.stopSuccess"))
}else{
this.btnStatus[index]=0
Message.error(res.data.error_msg)
}
}).catch((err)=>{
this.btnStatus[index]=0
Message.error(err)
})
},
createTask(index){
this.btnStatus[index]=1
const data = {type:index,...this.fileInfo}
let repoPath = `repos/${this.fileInfo.owner_name}/${this.fileInfo.project_name}`
createNotebook(data).then((res)=>{
if(res.data.code===0 && res.status===200){
if(index===2){
timerCb2 = setInterval(() => {
setTimeout(this.getCb2NotebookInfo(repoPath,res.data.message), 0)
}, 10000)
}else{
timerCb1 = setInterval(() => {
setTimeout(this.getCb1NotebookInfo(repoPath,res.data.message,index), 0)
}, 10000)
}
this.alertCb = false
}else if(res.data.code==2){
this.btnStatus[index]=0
this.alertCb = true
}else{
this.btnStatus[index]=0
Message.error(res.data.message)
}
}).catch((err)=>{
if(err.response.status===403 && err.response.data.code===1 ){
location.href=`${AppSubUrl}/authentication/wechat/bind`
}
this.btnStatus[index]=0
this.alertCb = false
Message.error(err)
})
}
},
computed: {
cpuSpec(){
let cpu_spec = `${this.$t("notebook.specification")}:GPU: ${this.notebookInfo.specCpu.acc_cards_num}*${this.notebookInfo.specCpu.acc_card_type}, CPU: ${this.notebookInfo.specCpu.cpu_cores}`
if(this.notebookInfo.specCpu.gpu_mem_gi_b!==0){
cpu_spec += `, ${this.$t("notebook.graphicMemory")}: ${this.notebookInfo.specCpu.gpu_mem_gi_b}GB`
}
if(this.notebookInfo.specCpu.mem_gi_b!==0){
cpu_spec += `, ${this.$t("notebook.memory")}: ${this.notebookInfo.specCpu.mem_gi_b}GB`
}
if(this.notebookInfo.specCpu.share_mem_gi_b!==0){
cpu_spec += `, ${this.$t("notebook.sharedMemory")}: ${this.notebookInfo.specCpu.share_mem_gi_b}GB`
}
return cpu_spec
},
npuSpec(){
let acc_card_type = ''
if(this.notebookInfo.specNpu.acc_card_type==="ASCEND910"){
acc_card_type = "Ascend 910"
}
let npu_spec = `${this.$t("notebook.specification")}:NPU: ${this.notebookInfo.specNpu.acc_cards_num}*${acc_card_type}, CPU: ${this.notebookInfo.specNpu.cpu_cores}`
if(this.notebookInfo.specNpu.gpu_mem_gi_b!==0){
npu_spec += `, ${this.$t("notebook.graphicMemory")}: ${this.notebookInfo.specNpu.gpu_mem_gi_b}GB`
}
if(this.notebookInfo.specNpu.mem_gi_b!==0){
npu_spec += `, ${this.$t("notebook.memory")}: ${this.notebookInfo.specNpu.mem_gi_b}GB`
}
if(this.notebookInfo.specNpu.share_mem_gi_b!==0){
npu_spec += `, ${this.$t("notebook.sharedMemory")}: ${this.notebookInfo.specNpu.share_mem_gi_b}GB`
}
return npu_spec
},
gpuSpec(){
let gpu_spec = `${this.$t("notebook.specification")}:GPU: ${this.notebookInfo.specGpu.acc_cards_num}*${this.notebookInfo.specGpu.acc_card_type}, CPU: ${this.notebookInfo.specGpu.cpu_cores}`
if(this.notebookInfo.specGpu.gpu_mem_gi_b!==0){
gpu_spec += `, ${this.$t("notebook.graphicMemory")}: ${this.notebookInfo.specGpu.gpu_mem_gi_b}GB`
}
if(this.notebookInfo.specGpu.mem_gi_b!==0){
gpu_spec += `, ${this.$t("notebook.memory")}: ${this.notebookInfo.specGpu.mem_gi_b}GB`
}
if(this.notebookInfo.specGpu.share_mem_gi_b!==0){
gpu_spec += `, ${this.$t("notebook.sharedMemory")}: ${this.notebookInfo.specGpu.share_mem_gi_b}GB`
}
return gpu_spec
}
},
beforeDestroy() {
clearInterval(timerCb1)
clearInterval(timerCb2)
},
mounted() {
const selfData = document.querySelector('#__vue-self-data')
this.fileInfo.file = selfData.getAttribute('data-file')
this.fileInfo.branch_name = selfData.getAttribute('data-branch')
this.fileInfo.owner_name = selfData.getAttribute('data-owner')
this.fileInfo.project_name = selfData.getAttribute('data-project')
this.fileInfo.sign_name = selfData.getAttribute('data-name')
let that = this;
document
.querySelector("#notebook-debug")
.addEventListener("click", function () {
that.getNotebookInfo()
that.dialogVisible = true;
});
},
};
</script>
<style scoped lang="less">
/deep/ .el-dialog__header {
text-align: left;
height: 45px;
background: rgb(240, 240, 240);
border-radius: 5px 5px 0px 0px;
border-bottom: 1px solid rgb(212, 212, 213);
padding: 0 15px;
display: flex;
align-items: center;
font-weight: 500;
font-size: 16px;
color: rgb(16, 16, 16);
font-family: Roboto;

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

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

/deep/ .el-dialog__body {
padding: 55px 15px 0 15px;
}
.wrapper {
width: 100%;
.active {
background: linear-gradient(
269.2deg,
rgba(183, 247, 255, 0.5) 0%,
rgba(233, 233, 255, 0) 78.67%
);
border-radius: 5px 5px 0px 0px;
}
.text-tip{
color: #888;
font-size: 12px;
}
.text-tip::before{
content: '*';
color: #f2711c;
}
.alert-info{
width: 70%;
background-color: rgba(242, 113, 28, 0.05);
border: 1px solid rgb(242, 113, 28);
border-radius: 5px;
margin: 0 auto;
padding-bottom: 10px;
}
& >.three-resource-type:nth-child(2){
border:none;
}
.three-resource-type {
width: 70%;
margin: 0 auto;
display: flex;
border-top: 1px solid rgba(16, 16, 16, 0.1);
cursor: pointer;
.resource-child-node {
display: flex;
align-items: center;
width: 100%;
height: 115px;
.resource-type-icon {
width: 50px;
height: 50px;
line-height: 20px;
border-radius: 25px;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
.text {
font-size: 26px;
color: rgba(251, 251, 251, 1);
font-family: ZKXiaoWeiLogo-regular;
}
}
.background-C {
background: linear-gradient(
134.2deg,
rgba(130, 209, 246, 1) 0%,
rgba(41, 182, 244, 1) 51.94%,
rgba(0, 137, 205, 1) 102.83%
);
}
.background-N {
background: linear-gradient(
151.47deg,
rgba(123, 50, 178, 1) 20.02%,
rgba(64, 26, 93, 1) 100%
);
}
.background-G {
background: linear-gradient(
-25.25deg,
rgba(254, 86, 77, 1) 9.3%,
rgba(251, 155, 54, 1) 38.86%,
rgba(249, 202, 38, 1) 67.95%
);
}
}
.resource-type-detail {
margin-left: 23px;
.detail-title {
font-family: SourceHanSansSC;
font-weight: 500;
font-size: 16px;
color: rgb(16, 16, 16);
font-style: normal;
letter-spacing: 0px;
line-height: 32px;
text-decoration: none;
}
.detail-spec {
font-family: SourceHanSansSC;
font-weight: 400;
font-size: 14px;
color: rgba(136, 136, 136, 1);
font-style: normal;
letter-spacing: 0px;
line-height: 24px;
text-decoration: none;
}
}
.resource-select {
margin-left: auto;
margin-right: 20px;
font-size: 20px;
height: 100%;
display: flex;
align-items: center;
.green {
color: green;
}
.gray {
color: rgba(16, 16, 16, 0.1);
}
}
}
.resource-footer {
margin-top: 40px;
border-top: 1px solid rgba(16, 16, 16, 0.1);
height: 71px;
display: flex;
align-items: center;
.resource-operate {
display: flex;
align-items: center;
.text{
color: #101010;
margin-left: 20px;
}
}
}
.slide-in-top {
-webkit-animation: slide-in-top 1s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
animation: slide-in-top 1s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
}
.slide-in-bottom {
-webkit-animation: slide-in-bottom 1s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
animation: slide-in-bottom 1s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
}
.fade-out {
-webkit-animation: fade-in 1.2s cubic-bezier(0.39, 0.575, 0.565, 1) both;
animation: fade-in 1.2s cubic-bezier(0.39, 0.575, 0.565, 1) both;
position: absolute
}
@-webkit-keyframes slide-in-top {
0% {
-webkit-transform: translateY(-50px);
transform: translateY(-50px);
opacity: 0;
}
100% {
-webkit-transform: translateY(0);
transform: translateY(0);
opacity: 1;
}
}
@keyframes slide-in-top {
0% {
-webkit-transform: translateY(-50px);
transform: translateY(-50px);
opacity: 0;
}
100% {
-webkit-transform: translateY(0);
transform: translateY(0);
opacity: 1;
}
}
@-webkit-keyframes slide-in-bottom {
0% {
-webkit-transform: translateY(50px);
transform: translateY(50px);
opacity: 0;
}
100% {
-webkit-transform: translateY(0);
transform: translateY(0);
opacity: 1;
}
}
@keyframes slide-in-bottom {
0% {
-webkit-transform: translateY(50px);
transform: translateY(50px);
opacity: 0;
}
100% {
-webkit-transform: translateY(0);
transform: translateY(0);
opacity: 1;
}
}
@-webkit-keyframes fade-in {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@keyframes fade-in {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
}
</style>

+ 12
- 0
web_src/vuepages/pages/notebook/debug/vp-notebook-debug.js View File

@@ -0,0 +1,12 @@
import Vue from 'vue';
import {Dialog,Loading} from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import { i18n, lang } from '~/langs';
import App from './index.vue';

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

Loading…
Cancel
Save