| @@ -1,7 +1,6 @@ | |||||
| package models | package models | ||||
| import ( | import ( | ||||
| "fmt" | |||||
| "strconv" | "strconv" | ||||
| "time" | "time" | ||||
| @@ -313,10 +312,6 @@ func InsertCloudbrainDurationStatistic(cloudbrainDurationStatistic *CloudbrainDu | |||||
| return xStatistic.Insert(cloudbrainDurationStatistic) | 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) { | func getDurationStatistic(cb *CloudbrainDurationStatistic) (*CloudbrainDurationStatistic, error) { | ||||
| has, err := x.Get(cb) | has, err := x.Get(cb) | ||||
| if err != nil { | if err != nil { | ||||
| @@ -327,26 +322,6 @@ func getDurationStatistic(cb *CloudbrainDurationStatistic) (*CloudbrainDurationS | |||||
| return cb, nil | 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) { | func GetCanUseCardInfo() ([]*ResourceQueue, error) { | ||||
| sess := x.NewSession() | sess := x.NewSession() | ||||
| defer sess.Close() | defer sess.Close() | ||||
| @@ -402,11 +377,11 @@ func GetDurationRecordUpdateTime() ([]*CloudbrainDurationStatistic, error) { | |||||
| return CloudbrainDurationStatistics, nil | return CloudbrainDurationStatistics, nil | ||||
| } | } | ||||
| func DeleteCloudbrainDurationStatistic() error { | |||||
| func DeleteCloudbrainDurationStatistic(beginTime timeutil.TimeStamp, endTime timeutil.TimeStamp) error { | |||||
| sess := xStatistic.NewSession() | sess := xStatistic.NewSession() | ||||
| defer sess.Close() | 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 err | ||||
| } | } | ||||
| return nil | return nil | ||||
| @@ -1279,7 +1279,7 @@ func CreateRepository(ctx DBContext, doer, u *User, repo *Repository, opts ...Cr | |||||
| } | } | ||||
| if setting.Service.AutoWatchNewRepos { | 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) | return fmt.Errorf("watchRepo: %v", err) | ||||
| } | } | ||||
| } | } | ||||
| @@ -24,6 +24,14 @@ const ( | |||||
| RepoWatchModeAuto // 3 | 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 ActionChan = make(chan *Action, 200) | ||||
| var ActionChan4Task = make(chan Action, 200) | var ActionChan4Task = make(chan Action, 200) | ||||
| @@ -34,6 +42,7 @@ type Watch struct { | |||||
| RepoID int64 `xorm:"UNIQUE(watch)"` | RepoID int64 `xorm:"UNIQUE(watch)"` | ||||
| Mode RepoWatchMode `xorm:"SMALLINT NOT NULL DEFAULT 1"` | Mode RepoWatchMode `xorm:"SMALLINT NOT NULL DEFAULT 1"` | ||||
| CreatedUnix int64 `xorm:"created"` | 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 | // 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) | 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) { | func watchRepoMode(e Engine, watch Watch, mode RepoWatchMode) (err error) { | ||||
| if watch.Mode == mode { | if watch.Mode == mode { | ||||
| if _, err := e.ID(watch.ID).Cols("notify_type").Update(watch); err != nil { | |||||
| return err | |||||
| } | |||||
| return nil | return nil | ||||
| } | } | ||||
| if mode == RepoWatchModeAuto && (watch.Mode == RepoWatchModeDont || isWatchMode(watch.Mode)) { | 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) | 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 | var watch Watch | ||||
| if watch, err = getWatch(e, userID, repoID); err != nil { | if watch, err = getWatch(e, userID, repoID); err != nil { | ||||
| return err | return err | ||||
| @@ -119,14 +140,19 @@ func watchRepo(e Engine, userID, repoID int64, doWatch bool) (err error) { | |||||
| } else if !doWatch { | } else if !doWatch { | ||||
| err = watchRepoMode(e, watch, RepoWatchModeNone) | err = watchRepoMode(e, watch, RepoWatchModeNone) | ||||
| } else { | } else { | ||||
| notifyType := RejectAllNotification | |||||
| if len(notifyTypes) > 0 { | |||||
| notifyType = notifyTypes[0] | |||||
| } | |||||
| watch.NotifyType = notifyType | |||||
| err = watchRepoMode(e, watch, RepoWatchModeNormal) | err = watchRepoMode(e, watch, RepoWatchModeNormal) | ||||
| } | } | ||||
| return err | return err | ||||
| } | } | ||||
| // WatchRepo watch or unwatch repository. | // 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) { | 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"). | return ids, e.Table("watch"). | ||||
| Where("watch.repo_id=?", repoID). | Where("watch.repo_id=?", repoID). | ||||
| And("watch.mode<>?", RepoWatchModeDont). | And("watch.mode<>?", RepoWatchModeDont). | ||||
| And("watch.notify_type > ?", RejectAllNotification). | |||||
| Select("user_id"). | Select("user_id"). | ||||
| Find(&ids) | Find(&ids) | ||||
| } | } | ||||
| @@ -298,6 +298,15 @@ func ResourceSpecOffShelf(id int64) (int64, error) { | |||||
| return n, err | 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) { | func GetResourceSpecification(r *ResourceSpecification) (*ResourceSpecification, error) { | ||||
| has, err := x.Get(r) | has, err := x.Get(r) | ||||
| if err != nil { | if err != nil { | ||||
| @@ -346,7 +346,7 @@ func (u *User) DashboardLink() string { | |||||
| if u.IsOrganization() { | if u.IsOrganization() { | ||||
| return setting.AppSubURL + "/org/" + u.Name + "/dashboard/" | return setting.AppSubURL + "/org/" + u.Name + "/dashboard/" | ||||
| } | } | ||||
| return setting.AppSubURL + "/" | |||||
| return setting.AppSubURL + "/dashboard" | |||||
| } | } | ||||
| // HomeLink returns the user or organization home page link. | // HomeLink returns the user or organization home page link. | ||||
| @@ -216,6 +216,27 @@ func (email *EmailAddress) updateActivation(e Engine, activate bool) error { | |||||
| return updateUserCols(e, user, "rands") | 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. | // DeleteEmailAddress deletes an email address of given user. | ||||
| func DeleteEmailAddress(email *EmailAddress) (err error) { | func DeleteEmailAddress(email *EmailAddress) (err error) { | ||||
| var deleted int64 | var deleted int64 | ||||
| @@ -88,6 +88,10 @@ type RegisterForm struct { | |||||
| Agree bool | Agree bool | ||||
| } | } | ||||
| type UpdateEmailForm struct { | |||||
| NewEmail string `binding:"Required;MaxSize(254)"` | |||||
| } | |||||
| // Validate valideates the fields | // Validate valideates the fields | ||||
| func (f *RegisterForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | func (f *RegisterForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | ||||
| return validate(errs, ctx.Data, f, ctx.Locale) | return validate(errs, ctx.Data, f, ctx.Locale) | ||||
| @@ -145,7 +145,7 @@ func isAdminOrImageCreater(ctx *context.Context, image *models.Image, err error) | |||||
| func AdminOrOwnerOrJobCreaterRight(ctx *context.Context) { | func AdminOrOwnerOrJobCreaterRight(ctx *context.Context) { | ||||
| var id = ctx.Params(":id") | var id = ctx.Params(":id") | ||||
| job, err := models.GetCloudbrainByID(id) | |||||
| job, err := GetCloudBrainByIdOrJobId(id) | |||||
| if err != nil { | if err != nil { | ||||
| log.Error("GetCloudbrainByID failed:%v", err.Error()) | log.Error("GetCloudbrainByID failed:%v", err.Error()) | ||||
| ctx.NotFound(ctx.Req.URL.RequestURI(), nil) | ctx.NotFound(ctx.Req.URL.RequestURI(), nil) | ||||
| @@ -161,7 +161,7 @@ func AdminOrOwnerOrJobCreaterRight(ctx *context.Context) { | |||||
| func AdminOrJobCreaterRight(ctx *context.Context) { | func AdminOrJobCreaterRight(ctx *context.Context) { | ||||
| var id = ctx.Params(":id") | var id = ctx.Params(":id") | ||||
| job, err := models.GetCloudbrainByID(id) | |||||
| job, err := GetCloudBrainByIdOrJobId(id) | |||||
| if err != nil { | if err != nil { | ||||
| log.Error("GetCloudbrainByID failed:%v", err.Error()) | log.Error("GetCloudbrainByID failed:%v", err.Error()) | ||||
| ctx.NotFound(ctx.Req.URL.RequestURI(), nil) | ctx.NotFound(ctx.Req.URL.RequestURI(), nil) | ||||
| @@ -177,7 +177,7 @@ func AdminOrJobCreaterRight(ctx *context.Context) { | |||||
| func AdminOrOwnerOrJobCreaterRightForTrain(ctx *context.Context) { | func AdminOrOwnerOrJobCreaterRightForTrain(ctx *context.Context) { | ||||
| var jobID = ctx.Params(":jobid") | var jobID = ctx.Params(":jobid") | ||||
| job, err := models.GetCloudbrainByJobID(jobID) | |||||
| job, err := GetCloudBrainByIdOrJobId(jobID) | |||||
| if err != nil { | if err != nil { | ||||
| log.Error("GetCloudbrainByJobID failed:%v", err.Error()) | log.Error("GetCloudbrainByJobID failed:%v", err.Error()) | ||||
| ctx.NotFound(ctx.Req.URL.RequestURI(), nil) | ctx.NotFound(ctx.Req.URL.RequestURI(), nil) | ||||
| @@ -193,7 +193,7 @@ func AdminOrOwnerOrJobCreaterRightForTrain(ctx *context.Context) { | |||||
| func AdminOrJobCreaterRightForTrain(ctx *context.Context) { | func AdminOrJobCreaterRightForTrain(ctx *context.Context) { | ||||
| var jobID = ctx.Params(":jobid") | var jobID = ctx.Params(":jobid") | ||||
| job, err := models.GetCloudbrainByJobID(jobID) | |||||
| job, err := GetCloudBrainByIdOrJobId(jobID) | |||||
| if err != nil { | if err != nil { | ||||
| log.Error("GetCloudbrainByJobID failed:%v", err.Error()) | log.Error("GetCloudbrainByJobID failed:%v", err.Error()) | ||||
| ctx.NotFound(ctx.Req.URL.RequestURI(), nil) | ctx.NotFound(ctx.Req.URL.RequestURI(), nil) | ||||
| @@ -652,3 +652,16 @@ func IsElementExist(s []string, str string) bool { | |||||
| } | } | ||||
| return false | 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 | |||||
| } | |||||
| @@ -474,6 +474,7 @@ func RepoAssignment() macaron.Handler { | |||||
| if ctx.IsSigned { | if ctx.IsSigned { | ||||
| ctx.Data["IsWatchingRepo"] = models.IsWatching(ctx.User.ID, repo.ID) | 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["IsStaringRepo"] = models.IsStaring(ctx.User.ID, repo.ID) | ||||
| ctx.Data["IsStaringDataset"] = models.IsDatasetStaringByRepoId(ctx.User.ID, repo.ID) | ctx.Data["IsStaringDataset"] = models.IsDatasetStaringByRepoId(ctx.User.ID, repo.ID) | ||||
| @@ -1,13 +1,18 @@ | |||||
| package modelarts | package modelarts | ||||
| import ( | import ( | ||||
| "encoding/base64" | |||||
| "encoding/json" | "encoding/json" | ||||
| "errors" | "errors" | ||||
| "fmt" | "fmt" | ||||
| "io/ioutil" | |||||
| "net/http" | |||||
| "path" | "path" | ||||
| "strconv" | "strconv" | ||||
| "strings" | "strings" | ||||
| "code.gitea.io/gitea/modules/cloudbrain" | |||||
| "code.gitea.io/gitea/modules/modelarts_cd" | "code.gitea.io/gitea/modules/modelarts_cd" | ||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| @@ -23,7 +28,7 @@ const ( | |||||
| //notebook | //notebook | ||||
| storageTypeOBS = "obs" | storageTypeOBS = "obs" | ||||
| autoStopDuration = 4 * 60 * 60 | autoStopDuration = 4 * 60 * 60 | ||||
| autoStopDurationMs = 4 * 60 * 60 * 1000 | |||||
| AutoStopDurationMs = 4 * 60 * 60 * 1000 | |||||
| MORDELART_USER_IMAGE_ENGINE_ID = -1 | MORDELART_USER_IMAGE_ENGINE_ID = -1 | ||||
| DataSetMountPath = "/home/ma-user/work" | DataSetMountPath = "/home/ma-user/work" | ||||
| NotebookEnv = "Python3" | NotebookEnv = "Python3" | ||||
| @@ -276,7 +281,7 @@ func GenerateTask(ctx *context.Context, jobName, uuid, description, flavor strin | |||||
| return nil | 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 { | if poolInfos == nil { | ||||
| json.Unmarshal([]byte(setting.PoolInfos), &poolInfos) | json.Unmarshal([]byte(setting.PoolInfos), &poolInfos) | ||||
| } | } | ||||
| @@ -284,14 +289,14 @@ func GenerateNotebook2(ctx *context.Context, displayJobName, jobName, uuid, desc | |||||
| imageName, err := GetNotebookImageName(imageId) | imageName, err := GetNotebookImageName(imageId) | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("GetNotebookImageName failed: %v", err.Error()) | log.Error("GetNotebookImageName failed: %v", err.Error()) | ||||
| return err | |||||
| return "", err | |||||
| } | } | ||||
| createTime := timeutil.TimeStampNow() | createTime := timeutil.TimeStampNow() | ||||
| jobResult, err := createNotebook2(models.CreateNotebook2Params{ | jobResult, err := createNotebook2(models.CreateNotebook2Params{ | ||||
| JobName: jobName, | JobName: jobName, | ||||
| Description: description, | Description: description, | ||||
| Flavor: spec.SourceSpecId, | Flavor: spec.SourceSpecId, | ||||
| Duration: autoStopDurationMs, | |||||
| Duration: autoStopDurationInMs, | |||||
| ImageID: imageId, | ImageID: imageId, | ||||
| PoolID: poolInfos.PoolInfo[0].PoolId, | PoolID: poolInfos.PoolInfo[0].PoolId, | ||||
| Feature: models.NotebookFeature, | Feature: models.NotebookFeature, | ||||
| @@ -316,10 +321,10 @@ func GenerateNotebook2(ctx *context.Context, displayJobName, jobName, uuid, desc | |||||
| }) | }) | ||||
| if errTemp != nil { | if errTemp != nil { | ||||
| log.Error("InsertCloudbrainTemp failed: %v", errTemp.Error()) | log.Error("InsertCloudbrainTemp failed: %v", errTemp.Error()) | ||||
| return errTemp | |||||
| return "", errTemp | |||||
| } | } | ||||
| } | } | ||||
| return err | |||||
| return "", err | |||||
| } | } | ||||
| task := &models.Cloudbrain{ | task := &models.Cloudbrain{ | ||||
| Status: jobResult.Status, | Status: jobResult.Status, | ||||
| @@ -334,6 +339,7 @@ func GenerateNotebook2(ctx *context.Context, displayJobName, jobName, uuid, desc | |||||
| Uuid: uuid, | Uuid: uuid, | ||||
| ComputeResource: models.NPUResource, | ComputeResource: models.NPUResource, | ||||
| Image: imageName, | Image: imageName, | ||||
| BootFile: bootFile, | |||||
| Description: description, | Description: description, | ||||
| CreatedUnix: createTime, | CreatedUnix: createTime, | ||||
| UpdatedUnix: createTime, | UpdatedUnix: createTime, | ||||
| @@ -342,12 +348,12 @@ func GenerateNotebook2(ctx *context.Context, displayJobName, jobName, uuid, desc | |||||
| err = models.CreateCloudbrain(task) | err = models.CreateCloudbrain(task) | ||||
| if err != nil { | if err != nil { | ||||
| return err | |||||
| return "", err | |||||
| } | } | ||||
| stringId := strconv.FormatInt(task.ID, 10) | stringId := strconv.FormatInt(task.ID, 10) | ||||
| notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, stringId, displayJobName, models.ActionCreateDebugNPUTask) | 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) { | func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (jobId string, err error) { | ||||
| @@ -907,6 +913,11 @@ func HandleNotebookInfo(task *models.Cloudbrain) error { | |||||
| if task.FlavorCode == "" { | if task.FlavorCode == "" { | ||||
| task.FlavorCode = result.Flavor | task.FlavorCode = result.Flavor | ||||
| } | } | ||||
| if oldStatus != task.Status && task.Status == string(models.ModelArtsRunning) && task.BootFile != "" { | |||||
| uploadNoteBookFile(task, result) | |||||
| } | |||||
| err = models.UpdateJob(task) | err = models.UpdateJob(task) | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("UpdateJob(%s) failed:%v", task.DisplayJobName, err) | log.Error("UpdateJob(%s) failed:%v", task.DisplayJobName, err) | ||||
| @@ -917,6 +928,81 @@ func HandleNotebookInfo(task *models.Cloudbrain) error { | |||||
| return nil | 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() { | func SyncTempStatusJob() { | ||||
| jobs, err := models.GetCloudBrainTempJobs() | jobs, err := models.GetCloudBrainTempJobs() | ||||
| if err != nil { | if err != nil { | ||||
| @@ -280,7 +280,7 @@ sendjob: | |||||
| SetHeader("Content-Type", "application/json"). | SetHeader("Content-Type", "application/json"). | ||||
| SetAuthToken(TOKEN). | SetAuthToken(TOKEN). | ||||
| SetResult(&result). | 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 { | if err != nil { | ||||
| return &result, fmt.Errorf("resty ManageNotebook2: %v", err) | return &result, fmt.Errorf("resty ManageNotebook2: %v", err) | ||||
| @@ -88,18 +88,18 @@ type Parameters struct { | |||||
| } `json:"parameter"` | } `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) | imageName, err := GetNotebookImageName(imageId) | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("GetNotebookImageName failed: %v", err.Error()) | log.Error("GetNotebookImageName failed: %v", err.Error()) | ||||
| return err | |||||
| return "", err | |||||
| } | } | ||||
| createTime := timeutil.TimeStampNow() | createTime := timeutil.TimeStampNow() | ||||
| jobResult, err := createNotebook(models.CreateNotebookWithoutPoolParams{ | jobResult, err := createNotebook(models.CreateNotebookWithoutPoolParams{ | ||||
| JobName: jobName, | JobName: jobName, | ||||
| Description: description, | Description: description, | ||||
| Flavor: spec.SourceSpecId, | Flavor: spec.SourceSpecId, | ||||
| Duration: autoStopDurationMs, | |||||
| Duration: autoStopDurationInMs, | |||||
| ImageID: imageId, | ImageID: imageId, | ||||
| Feature: models.NotebookFeature, | Feature: models.NotebookFeature, | ||||
| Volume: models.VolumeReq{ | Volume: models.VolumeReq{ | ||||
| @@ -123,10 +123,10 @@ func GenerateNotebook(ctx *context.Context, displayJobName, jobName, uuid, descr | |||||
| }) | }) | ||||
| if errTemp != nil { | if errTemp != nil { | ||||
| log.Error("InsertCloudbrainTemp failed: %v", errTemp.Error()) | log.Error("InsertCloudbrainTemp failed: %v", errTemp.Error()) | ||||
| return errTemp | |||||
| return "", errTemp | |||||
| } | } | ||||
| } | } | ||||
| return err | |||||
| return "", err | |||||
| } | } | ||||
| task := &models.Cloudbrain{ | task := &models.Cloudbrain{ | ||||
| Status: jobResult.Status, | Status: jobResult.Status, | ||||
| @@ -145,16 +145,17 @@ func GenerateNotebook(ctx *context.Context, displayJobName, jobName, uuid, descr | |||||
| CreatedUnix: createTime, | CreatedUnix: createTime, | ||||
| UpdatedUnix: createTime, | UpdatedUnix: createTime, | ||||
| Spec: spec, | Spec: spec, | ||||
| BootFile: bootFile, | |||||
| } | } | ||||
| err = models.CreateCloudbrain(task) | err = models.CreateCloudbrain(task) | ||||
| if err != nil { | if err != nil { | ||||
| return err | |||||
| return "", err | |||||
| } | } | ||||
| stringId := strconv.FormatInt(task.ID, 10) | stringId := strconv.FormatInt(task.ID, 10) | ||||
| notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, stringId, displayJobName, models.ActionCreateDebugNPUTask) | notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, stringId, displayJobName, models.ActionCreateDebugNPUTask) | ||||
| return nil | |||||
| return jobResult.ID, nil | |||||
| } | } | ||||
| func GetNotebookImageName(imageId string) (string, error) { | func GetNotebookImageName(imageId string) (string, error) { | ||||
| @@ -175,41 +176,3 @@ func GetNotebookImageName(imageId string) (string, error) { | |||||
| return imageName, nil | 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 | |||||
| } | |||||
| */ | |||||
| @@ -715,6 +715,21 @@ var ( | |||||
| TeamName string | 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 { | ModelConvert = struct { | ||||
| GPU_PYTORCH_IMAGE string | GPU_PYTORCH_IMAGE string | ||||
| GpuQueue string | GpuQueue string | ||||
| @@ -1580,6 +1595,20 @@ func NewContext() { | |||||
| Course.OrgName = sec.Key("org_name").MustString("") | Course.OrgName = sec.Key("org_name").MustString("") | ||||
| Course.TeamName = sec.Key("team_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() | getGrampusConfig() | ||||
| getModelartsCDConfig() | getModelartsCDConfig() | ||||
| getModelConvertConfig() | getModelConvertConfig() | ||||
| @@ -41,6 +41,14 @@ type CreateTrainJobOption struct { | |||||
| SpecId int64 `json:"spec_id" binding:"Required"` | 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 { | type Cloudbrain struct { | ||||
| ID int64 `json:"id"` | ID int64 `json:"id"` | ||||
| JobID string `json:"job_id"` | JobID string `json:"job_id"` | ||||
| @@ -151,6 +151,9 @@ func NewFuncMap() []template.FuncMap { | |||||
| "EscapePound": func(str string) string { | "EscapePound": func(str string) string { | ||||
| return strings.NewReplacer("%", "%25", "#", "%23", " ", "%20", "?", "%3F").Replace(str) | 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 { | "nl2br": func(text string) template.HTML { | ||||
| return template.HTML(strings.Replace(template.HTMLEscapeString(text), "\n", "<br>", -1)) | return template.HTML(strings.Replace(template.HTMLEscapeString(text), "\n", "<br>", -1)) | ||||
| }, | }, | ||||
| @@ -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_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? | authorize_title = Authorize "%s" to access your account? | ||||
| authorization_failed = Authorization failed | 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. | disable_forgot_password_mail = Account recovery is disabled. Please contact your site administrator. | ||||
| sspi_auth_failed = SSPI authentication failed | sspi_auth_failed = SSPI authentication failed | ||||
| change_email = Change email | |||||
| change_email_address = Change email address | |||||
| new_email_address = New email address | |||||
| [phone] | [phone] | ||||
| format_err=The format of phone number is wrong. | format_err=The format of phone number is wrong. | ||||
| query_err=Fail to query phone number, please try again later. | query_err=Fail to query phone number, please try again later. | ||||
| @@ -1015,6 +1018,8 @@ readme = README | |||||
| readme_helper = Select a README file template. | readme_helper = Select a README file template. | ||||
| auto_init = Initialize Repository (Adds .gitignore, License and README) | auto_init = Initialize Repository (Adds .gitignore, License and README) | ||||
| create_repo = Create Repository | 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 | create_course = Publish Course | ||||
| failed_to_create_course=Failed to publish course, please try again later. | failed_to_create_course=Failed to publish course, please try again later. | ||||
| default_branch = Default Branch | default_branch = Default Branch | ||||
| @@ -1049,6 +1054,10 @@ model_experience = Model Experience | |||||
| model_noright=You have no right to do the operation. | model_noright=You have no right to do the operation. | ||||
| model_rename=Duplicate model name, please modify model name. | 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 | date=Date | ||||
| repo_add=Project Increment | repo_add=Project Increment | ||||
| repo_total=Project Total | 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.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.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.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 = Model | ||||
| modelarts.infer_job_model_file = Model File | modelarts.infer_job_model_file = Model File | ||||
| @@ -1402,6 +1411,11 @@ star = Star | |||||
| fork = Fork | fork = Fork | ||||
| download_archive = Download Repository | download_archive = Download Repository | ||||
| star_fail=Failed to %s the dataset. | 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_desc = No Description | ||||
| no_label = No labels | no_label = No labels | ||||
| @@ -1443,6 +1457,7 @@ blame = Blame | |||||
| normal_view = Normal View | normal_view = Normal View | ||||
| line = line | line = line | ||||
| lines = lines | lines = lines | ||||
| notebook_open = Open in Notebook | |||||
| editor.new_file = New File | editor.new_file = New File | ||||
| editor.upload_file = Upload File | editor.upload_file = Upload File | ||||
| @@ -403,6 +403,9 @@ authorization_failed=授权失败 | |||||
| authorization_failed_desc=授权失败,这是一个无效的请求。请联系尝试授权应用的管理员。 | authorization_failed_desc=授权失败,这是一个无效的请求。请联系尝试授权应用的管理员。 | ||||
| disable_forgot_password_mail = Account recovery is disabled. Please contact your site administrator. | disable_forgot_password_mail = Account recovery is disabled. Please contact your site administrator. | ||||
| sspi_auth_failed=SSPI 认证失败 | sspi_auth_failed=SSPI 认证失败 | ||||
| change_email=修改邮箱 | |||||
| change_email_address=修改邮箱地址 | |||||
| new_email_address=新邮箱地址 | |||||
| [phone] | [phone] | ||||
| format_err=手机号格式错误。 | format_err=手机号格式错误。 | ||||
| query_err=查询手机号失败,请稍后再试。 | query_err=查询手机号失败,请稍后再试。 | ||||
| @@ -1020,6 +1023,8 @@ readme=自述 | |||||
| readme_helper=选择自述文件模板。 | readme_helper=选择自述文件模板。 | ||||
| auto_init=初始化存储库 (添加. gitignore、许可证和自述文件) | auto_init=初始化存储库 (添加. gitignore、许可证和自述文件) | ||||
| create_repo=创建项目 | create_repo=创建项目 | ||||
| failed_to_create_repo=创建项目失败,请稍后再试。 | |||||
| failed_to_create_notebook_repo=创建项目%s失败,请检查您是否有同名的项目,如果有请先手工修改或删除后重试。 | |||||
| create_course=发布课程 | create_course=发布课程 | ||||
| failed_to_create_course=发布课程失败,请稍后再试。 | failed_to_create_course=发布课程失败,请稍后再试。 | ||||
| default_branch=默认分支 | default_branch=默认分支 | ||||
| @@ -1048,6 +1053,9 @@ model_experience = 模型体验 | |||||
| model_noright=您没有操作权限。 | model_noright=您没有操作权限。 | ||||
| model_rename=模型名称重复,请修改模型名称 | model_rename=模型名称重复,请修改模型名称 | ||||
| notebook_file_not_exist=Notebook文件不存在。 | |||||
| notebook_select_wrong=请先选择Notebook(.ipynb)文件。 | |||||
| notebook_file_no_right=您没有这个Notebook文件的读权限。 | |||||
| date=日期 | date=日期 | ||||
| repo_add=新增项目 | repo_add=新增项目 | ||||
| @@ -1418,6 +1426,11 @@ star=点赞 | |||||
| fork=派生 | fork=派生 | ||||
| download_archive=下载此项目 | download_archive=下载此项目 | ||||
| star_fail=%s失败。 | star_fail=%s失败。 | ||||
| watched=已关注 | |||||
| notWatched=未关注 | |||||
| un_watch=不关注 | |||||
| watch_all=关注所有动态 | |||||
| watch_no_notify=关注但不提醒动态 | |||||
| no_desc=暂无描述 | no_desc=暂无描述 | ||||
| @@ -1461,6 +1474,8 @@ normal_view=普通视图 | |||||
| line=行 | line=行 | ||||
| lines=行 | lines=行 | ||||
| notebook_open = 在Notebook中打开 | |||||
| editor.new_file=新建文件 | editor.new_file=新建文件 | ||||
| editor.upload_file=上传文件 | editor.upload_file=上传文件 | ||||
| editor.edit_file=编辑文件 | editor.edit_file=编辑文件 | ||||
| @@ -737,6 +737,12 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| m.Get("/my_favorite", repo.MyFavoriteDatasetMultiple) | m.Get("/my_favorite", repo.MyFavoriteDatasetMultiple) | ||||
| }, reqToken(), repoAssignment()) | }, 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.Group("/repos", func() { | ||||
| m.Get("/search", repo.Search) | m.Get("/search", repo.Search) | ||||
| @@ -78,6 +78,75 @@ func CloudBrainShow(ctx *context.APIContext) { | |||||
| ctx.JSON(http.StatusOK, models.BaseMessageWithDataApi{Code: 0, Message: "", Data: convert.ToCloudBrain(task)}) | 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) { | func CreateCloudBrain(ctx *context.APIContext, option api.CreateTrainJobOption) { | ||||
| @@ -141,10 +210,11 @@ func GetCloudbrainTask(ctx *context.APIContext) { | |||||
| ) | ) | ||||
| ID := ctx.Params(":id") | ID := ctx.Params(":id") | ||||
| job, err := models.GetCloudbrainByID(ID) | |||||
| job,err := cloudbrain.GetCloudBrainByIdOrJobId(ID) | |||||
| if err != nil { | if err != nil { | ||||
| ctx.NotFound(err) | ctx.NotFound(err) | ||||
| log.Error("GetCloudbrainByID failed:", err) | |||||
| return | return | ||||
| } | } | ||||
| if job.JobType == string(models.JobTypeModelSafety) { | if job.JobType == string(models.JobTypeModelSafety) { | ||||
| @@ -123,8 +123,9 @@ func GetOverviewDuration(ctx *context.Context) { | |||||
| recordBeginTime := recordCloudbrain[0].Cloudbrain.CreatedUnix | recordBeginTime := recordCloudbrain[0].Cloudbrain.CreatedUnix | ||||
| now := time.Now() | now := time.Now() | ||||
| endTime := now | endTime := now | ||||
| // worker_server_num := 1 | |||||
| // cardNum := 1 | |||||
| var workServerNumber int64 | |||||
| var cardNum int64 | |||||
| durationAllSum := int64(0) | durationAllSum := int64(0) | ||||
| cardDuSum := int64(0) | cardDuSum := int64(0) | ||||
| @@ -138,52 +139,60 @@ func GetOverviewDuration(ctx *context.Context) { | |||||
| c2NetDuration := int64(0) | c2NetDuration := int64(0) | ||||
| cDCenterDuration := 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{}{ | ctx.JSON(http.StatusOK, map[string]interface{}{ | ||||
| "cloudBrainOneCardDuSum": cloudBrainOneCardDuSum, | "cloudBrainOneCardDuSum": cloudBrainOneCardDuSum, | ||||
| @@ -6,6 +6,7 @@ | |||||
| package repo | package repo | ||||
| import ( | import ( | ||||
| "code.gitea.io/gitea/modules/cloudbrain" | |||||
| "encoding/json" | "encoding/json" | ||||
| "net/http" | "net/http" | ||||
| "path" | "path" | ||||
| @@ -37,11 +38,14 @@ func GetModelArtsNotebook2(ctx *context.APIContext) { | |||||
| ) | ) | ||||
| ID := ctx.Params(":id") | ID := ctx.Params(":id") | ||||
| job, err := models.GetCloudbrainByID(ID) | |||||
| job,err := cloudbrain.GetCloudBrainByIdOrJobId(ID) | |||||
| if err != nil { | if err != nil { | ||||
| ctx.NotFound(err) | ctx.NotFound(err) | ||||
| return | return | ||||
| } | } | ||||
| err = modelarts.HandleNotebookInfo(job) | err = modelarts.HandleNotebookInfo(job) | ||||
| if err != nil { | if err != nil { | ||||
| ctx.NotFound(err) | ctx.NotFound(err) | ||||
| @@ -11,7 +11,8 @@ import ( | |||||
| "os" | "os" | ||||
| "strconv" | "strconv" | ||||
| "strings" | "strings" | ||||
| "time" | |||||
| cloudbrainService "code.gitea.io/gitea/services/cloudbrain" | |||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| "code.gitea.io/gitea/modules/aisafety" | "code.gitea.io/gitea/modules/aisafety" | ||||
| @@ -483,7 +484,6 @@ func isTaskNotFinished(status string) bool { | |||||
| } | } | ||||
| func AiSafetyCreateForGetGPU(ctx *context.Context) { | func AiSafetyCreateForGetGPU(ctx *context.Context) { | ||||
| t := time.Now() | |||||
| ctx.Data["PageIsCloudBrain"] = true | ctx.Data["PageIsCloudBrain"] = true | ||||
| ctx.Data["IsCreate"] = true | ctx.Data["IsCreate"] = true | ||||
| ctx.Data["type"] = models.TypeCloudBrainOne | ctx.Data["type"] = models.TypeCloudBrainOne | ||||
| @@ -497,7 +497,7 @@ func AiSafetyCreateForGetGPU(ctx *context.Context) { | |||||
| log.Info("GPUBaseDataSetUUID=" + setting.ModelSafetyTest.GPUBaseDataSetUUID) | log.Info("GPUBaseDataSetUUID=" + setting.ModelSafetyTest.GPUBaseDataSetUUID) | ||||
| log.Info("GPUCombatDataSetName=" + setting.ModelSafetyTest.GPUCombatDataSetName) | log.Info("GPUCombatDataSetName=" + setting.ModelSafetyTest.GPUCombatDataSetName) | ||||
| log.Info("GPUCombatDataSetUUID=" + setting.ModelSafetyTest.GPUCombatDataSetUUID) | 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 | ctx.Data["display_job_name"] = displayJobName | ||||
| prepareCloudbrainOneSpecs(ctx) | prepareCloudbrainOneSpecs(ctx) | ||||
| queuesDetail, _ := cloudbrain.GetQueuesDetail() | queuesDetail, _ := cloudbrain.GetQueuesDetail() | ||||
| @@ -514,12 +514,11 @@ func AiSafetyCreateForGetGPU(ctx *context.Context) { | |||||
| } | } | ||||
| func AiSafetyCreateForGetNPU(ctx *context.Context) { | func AiSafetyCreateForGetNPU(ctx *context.Context) { | ||||
| t := time.Now() | |||||
| ctx.Data["PageIsCloudBrain"] = true | ctx.Data["PageIsCloudBrain"] = true | ||||
| ctx.Data["IsCreate"] = true | ctx.Data["IsCreate"] = true | ||||
| ctx.Data["type"] = models.TypeCloudBrainTwo | ctx.Data["type"] = models.TypeCloudBrainTwo | ||||
| ctx.Data["compute_resource"] = models.NPUResource | 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["display_job_name"] = displayJobName | ||||
| ctx.Data["datasetType"] = models.TypeCloudBrainTwo | ctx.Data["datasetType"] = models.TypeCloudBrainTwo | ||||
| ctx.Data["BaseDataSetName"] = setting.ModelSafetyTest.NPUBaseDataSetName | ctx.Data["BaseDataSetName"] = setting.ModelSafetyTest.NPUBaseDataSetName | ||||
| @@ -15,6 +15,8 @@ import ( | |||||
| "time" | "time" | ||||
| "unicode/utf8" | "unicode/utf8" | ||||
| cloudbrainService "code.gitea.io/gitea/services/cloudbrain" | |||||
| "code.gitea.io/gitea/modules/urfs_client/urchin" | "code.gitea.io/gitea/modules/urfs_client/urchin" | ||||
| "code.gitea.io/gitea/modules/dataset" | "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 { | func cloudBrainNewDataPrepare(ctx *context.Context, jobType string) error { | ||||
| ctx.Data["PageIsCloudBrain"] = true | 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["display_job_name"] = displayJobName | ||||
| ctx.Data["command"] = cloudbrain.GetCloudbrainDebugCommand() | ctx.Data["command"] = cloudbrain.GetCloudbrainDebugCommand() | ||||
| @@ -696,7 +679,7 @@ func CloudBrainRestart(ctx *context.Context) { | |||||
| } else { | } else { | ||||
| if count >= 1 { | if count >= 1 { | ||||
| log.Error("the user already has running or waiting task", ctx.Data["MsgID"]) | log.Error("the user already has running or waiting task", ctx.Data["MsgID"]) | ||||
| resultCode = "-1" | |||||
| resultCode = "2" | |||||
| errorMsg = ctx.Tr("repo.cloudbrain.morethanonejob") | errorMsg = ctx.Tr("repo.cloudbrain.morethanonejob") | ||||
| break | break | ||||
| } | } | ||||
| @@ -759,43 +742,13 @@ func cloudBrainShow(ctx *context.Context, tpName base.TplName, jobType models.Jo | |||||
| return | return | ||||
| } | } | ||||
| if task.Status == string(models.JobWaiting) || task.Status == string(models.JobRunning) { | 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 { | if err != nil { | ||||
| log.Info("error:" + err.Error()) | log.Info("error:" + err.Error()) | ||||
| ctx.NotFound(ctx.Req.URL.RequestURI(), nil) | ctx.NotFound(ctx.Req.URL.RequestURI(), nil) | ||||
| return | 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) | 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) { | func CloudBrainDebug(ctx *context.Context) { | ||||
| task := ctx.Cloudbrain | task := ctx.Cloudbrain | ||||
| debugUrl := setting.DebugServerHost + "jpylab_" + task.JobID + "_" + task.SubTaskName | 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) { | func prepareSpec4Show(ctx *context.Context, task *models.Cloudbrain) { | ||||
| @@ -8,7 +8,6 @@ import ( | |||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| "code.gitea.io/gitea/modules/context" | "code.gitea.io/gitea/modules/context" | ||||
| "code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
| "code.gitea.io/gitea/modules/setting" | |||||
| "code.gitea.io/gitea/modules/timeutil" | "code.gitea.io/gitea/modules/timeutil" | ||||
| cloudbrainService "code.gitea.io/gitea/services/cloudbrain" | cloudbrainService "code.gitea.io/gitea/services/cloudbrain" | ||||
| ) | ) | ||||
| @@ -23,9 +22,15 @@ func CloudbrainDurationStatisticHour() { | |||||
| now := time.Now() | now := time.Now() | ||||
| currentTime := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), 0, 0, 0, now.Location()) | currentTime := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), 0, 0, 0, now.Location()) | ||||
| if err == nil && len(recordDurationUpdateTime) > 0 { | 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 { | } 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) { | for statisticTime.Before(currentTime) || statisticTime.Equal(currentTime) { | ||||
| @@ -35,13 +40,10 @@ func CloudbrainDurationStatisticHour() { | |||||
| } | } | ||||
| log.Info("summaryDurationStat count: %v", count) | log.Info("summaryDurationStat count: %v", count) | ||||
| } | } | ||||
| func UpdateDurationStatisticHistoryData() int64 { | |||||
| func UpdateDurationStatisticHistoryData(beginTime time.Time, endTime time.Time) int64 { | |||||
| var count 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) { | for statisticTime.Before(currentTime) || statisticTime.Equal(currentTime) { | ||||
| countEach := summaryDurationStat(statisticTime) | countEach := summaryDurationStat(statisticTime) | ||||
| count += countEach | count += countEach | ||||
| @@ -105,13 +107,6 @@ func summaryDurationStat(statisticTime time.Time) int64 { | |||||
| for cardType, cardDuration := range CardTypes { | for cardType, cardDuration := range CardTypes { | ||||
| cloudbrainTable := cloudbrainMap[centerCode+"/"+cardType] | cloudbrainTable := cloudbrainMap[centerCode+"/"+cardType] | ||||
| if cloudbrainTable != nil { | 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 { | if _, ok := cardsTotalDurationMap[cloudbrainTable.Cluster+"/"+centerCode+"/"+cardType]; !ok { | ||||
| cardsTotalDurationMap[cloudbrainTable.Cluster+"/"+centerCode+"/"+cardType] = 0 | cardsTotalDurationMap[cloudbrainTable.Cluster+"/"+centerCode+"/"+cardType] = 0 | ||||
| } | } | ||||
| @@ -137,12 +132,6 @@ func summaryDurationStat(statisticTime time.Time) int64 { | |||||
| } | } | ||||
| for key, cardsTotalDuration := range cardsTotalDurationMap { | 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{ | cloudbrainDurationStat := models.CloudbrainDurationStatistic{ | ||||
| DateTime: dateTime, | DateTime: dateTime, | ||||
| DayTime: dayTime, | DayTime: dayTime, | ||||
| @@ -255,8 +244,22 @@ func getcloudBrainCenterCodeAndCardTypeInfo(ciTasks []*models.CloudbrainInfo, be | |||||
| } | } | ||||
| func CloudbrainUpdateHistoryData(ctx *context.Context) { | 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{}{ | ctx.JSON(http.StatusOK, map[string]interface{}{ | ||||
| "message": 0, | "message": 0, | ||||
| "count": count, | "count": count, | ||||
| @@ -10,7 +10,6 @@ import ( | |||||
| "path" | "path" | ||||
| "strconv" | "strconv" | ||||
| "strings" | "strings" | ||||
| "time" | |||||
| "code.gitea.io/gitea/modules/urfs_client/urchin" | "code.gitea.io/gitea/modules/urfs_client/urchin" | ||||
| "code.gitea.io/gitea/routers/response" | "code.gitea.io/gitea/routers/response" | ||||
| @@ -77,8 +76,7 @@ func GrampusTrainJobNPUNew(ctx *context.Context) { | |||||
| func grampusTrainJobNewDataPrepare(ctx *context.Context, processType string) error { | func grampusTrainJobNewDataPrepare(ctx *context.Context, processType string) error { | ||||
| ctx.Data["PageIsCloudBrain"] = true | 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["display_job_name"] = displayJobName | ||||
| //get valid images | //get valid images | ||||
| @@ -15,6 +15,8 @@ import ( | |||||
| "time" | "time" | ||||
| "unicode/utf8" | "unicode/utf8" | ||||
| cloudbrainService "code.gitea.io/gitea/services/cloudbrain" | |||||
| "code.gitea.io/gitea/services/cloudbrain/cloudbrainTask" | "code.gitea.io/gitea/services/cloudbrain/cloudbrainTask" | ||||
| "code.gitea.io/gitea/modules/dataset" | "code.gitea.io/gitea/modules/dataset" | ||||
| @@ -128,8 +130,7 @@ func NotebookNew(ctx *context.Context) { | |||||
| func notebookNewDataPrepare(ctx *context.Context) error { | func notebookNewDataPrepare(ctx *context.Context) error { | ||||
| ctx.Data["PageIsCloudBrain"] = true | 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["display_job_name"] = displayJobName | ||||
| attachs, err := models.GetModelArtsUserAttachments(ctx.User.ID) | attachs, err := models.GetModelArtsUserAttachments(ctx.User.ID) | ||||
| @@ -239,9 +240,9 @@ func Notebook2Create(ctx *context.Context, form auth.CreateModelArtsNotebookForm | |||||
| } | } | ||||
| if setting.ModelartsCD.Enabled { | 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 { | } 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 { | if err != nil { | ||||
| @@ -387,8 +388,33 @@ func NotebookDebug2(ctx *context.Context) { | |||||
| ctx.RenderWithErr(err.Error(), tplModelArtsNotebookIndex, nil) | ctx.RenderWithErr(err.Error(), tplModelArtsNotebookIndex, nil) | ||||
| return | 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) { | func NotebookRestart(ctx *context.Context) { | ||||
| @@ -420,7 +446,8 @@ func NotebookRestart(ctx *context.Context) { | |||||
| } else { | } else { | ||||
| if count >= 1 { | if count >= 1 { | ||||
| log.Error("the user already has running or waiting task", ctx.Data["MsgID"]) | 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 | break | ||||
| } | } | ||||
| } | } | ||||
| @@ -714,8 +741,7 @@ func trainJobNewDataPrepare(ctx *context.Context) error { | |||||
| // return | // 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 | ctx.Data["display_job_name"] = displayJobName | ||||
| attachs, err := models.GetModelArtsTrainAttachments(ctx.User.ID) | attachs, err := models.GetModelArtsTrainAttachments(ctx.User.ID) | ||||
| @@ -2351,8 +2377,7 @@ func inferenceJobNewDataPrepare(ctx *context.Context) error { | |||||
| ctx.Data["PageIsCloudBrain"] = true | ctx.Data["PageIsCloudBrain"] = true | ||||
| ctx.Data["newInference"] = 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 | ctx.Data["display_job_name"] = displayJobName | ||||
| attachs, err := models.GetModelArtsTrainAttachments(ctx.User.ID) | attachs, err := models.GetModelArtsTrainAttachments(ctx.User.ID) | ||||
| @@ -414,7 +414,9 @@ func Action(ctx *context.Context) { | |||||
| var err error | var err error | ||||
| switch ctx.Params(":action") { | switch ctx.Params(":action") { | ||||
| case "watch": | 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": | case "unwatch": | ||||
| err = models.WatchRepo(ctx.User.ID, ctx.Repo.Repository.ID, false) | err = models.WatchRepo(ctx.User.ID, ctx.Repo.Repository.ID, false) | ||||
| case "star": | case "star": | ||||
| @@ -519,6 +519,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| // r.Get("/feeds", binding.Bind(auth.FeedsForm{}), user.Feeds) | // r.Get("/feeds", binding.Bind(auth.FeedsForm{}), user.Feeds) | ||||
| m.Any("/activate", user.Activate, reqSignIn) | m.Any("/activate", user.Activate, reqSignIn) | ||||
| m.Any("/activate_email", user.ActivateEmail) | m.Any("/activate_email", user.ActivateEmail) | ||||
| m.Post("/update_email", bindIgnErr(auth.UpdateEmailForm{}), user.UpdateEmailPost) | |||||
| m.Get("/avatar/:username/:size", user.Avatar) | m.Get("/avatar/:username/:size", user.Avatar) | ||||
| m.Get("/email2user", user.Email2User) | m.Get("/email2user", user.Email2User) | ||||
| m.Get("/recover_account", user.ResetPasswd) | m.Get("/recover_account", user.ResetPasswd) | ||||
| @@ -1413,6 +1413,34 @@ func SignUpPost(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterFo | |||||
| handleSignInFull(ctx, u, false, true) | 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 | // Activate render activate user page | ||||
| func Activate(ctx *context.Context) { | func Activate(ctx *context.Context) { | ||||
| code := ctx.Query("code") | code := ctx.Query("code") | ||||
| @@ -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 | |||||
| } | |||||
| @@ -1,20 +1,21 @@ | |||||
| package cloudbrainTask | package cloudbrainTask | ||||
| import ( | import ( | ||||
| "net/http" | |||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| "code.gitea.io/gitea/modules/cloudbrain" | "code.gitea.io/gitea/modules/cloudbrain" | ||||
| "code.gitea.io/gitea/modules/httplib" | |||||
| "code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
| "code.gitea.io/gitea/modules/notification" | "code.gitea.io/gitea/modules/notification" | ||||
| "code.gitea.io/gitea/modules/setting" | "code.gitea.io/gitea/modules/setting" | ||||
| "net/http" | |||||
| "strconv" | |||||
| ) | ) | ||||
| var noteBookOKMap = make(map[int64]int, 20) | 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 successfulCount = 3 | ||||
| const maxSuccessfulCount=10 | |||||
| func SyncCloudBrainOneStatus(task *models.Cloudbrain) (*models.Cloudbrain, error) { | func SyncCloudBrainOneStatus(task *models.Cloudbrain) (*models.Cloudbrain, error) { | ||||
| jobResult, err := cloudbrain.GetJob(task.JobID) | jobResult, err := cloudbrain.GetJob(task.JobID) | ||||
| @@ -62,21 +63,29 @@ func isNoteBookReady(task *models.Cloudbrain) bool { | |||||
| return true | return true | ||||
| } | } | ||||
| noteBookUrl := setting.DebugServerHost + "jpylab_" + task.JobID + "_" + task.SubTaskName | noteBookUrl := setting.DebugServerHost + "jpylab_" + task.JobID + "_" + task.SubTaskName | ||||
| r := httplib.Get(noteBookUrl) | |||||
| res, err := r.Response() | |||||
| res,err := http.Get(noteBookUrl) | |||||
| if err != nil { | if err != nil { | ||||
| return false | return false | ||||
| } | } | ||||
| log.Info("notebook success count:"+strconv.Itoa(noteBookOKMap[task.ID])+",fail count:"+strconv.Itoa(noteBookFailMap[task.ID])) | |||||
| if res.StatusCode == http.StatusOK { | if res.StatusCode == http.StatusOK { | ||||
| count := noteBookOKMap[task.ID] | 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 | noteBookOKMap[task.ID] = count + 1 | ||||
| return false | return false | ||||
| } else { | } else { | ||||
| log.Info("notebook success count:"+strconv.Itoa(count)+",fail count:"+strconv.Itoa(noteBookFailMap[task.ID])) | |||||
| delete(noteBookOKMap, task.ID) | delete(noteBookOKMap, task.ID) | ||||
| delete(noteBookFailMap, task.ID) | |||||
| return true | return true | ||||
| } | } | ||||
| }else{ | |||||
| noteBookFailMap[task.ID]+=1 | |||||
| } | } | ||||
| return false | return false | ||||
| @@ -810,6 +810,18 @@ func uploadCodeToMinio(codePath, jobName, parentDir string) error { | |||||
| return nil | 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) { | func readDir(dirname string) ([]os.FileInfo, error) { | ||||
| f, err := os.Open(dirname) | f, err := os.Open(dirname) | ||||
| if err != nil { | if err != nil { | ||||
| @@ -1,7 +1,11 @@ | |||||
| package cloudbrain | package cloudbrain | ||||
| import ( | import ( | ||||
| "regexp" | |||||
| "strconv" | |||||
| "strings" | "strings" | ||||
| "time" | |||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| "code.gitea.io/gitea/modules/context" | "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 { | func GetAiCenterInfoByCenterCode(aiCenterCode string) *setting.C2NetSequenceInfo { | ||||
| if setting.AiCenterCodeAndNameMapInfo != nil { | if setting.AiCenterCodeAndNameMapInfo != nil { | ||||
| if info, ok := setting.AiCenterCodeAndNameMapInfo[aiCenterCode]; ok { | if info, ok := setting.AiCenterCodeAndNameMapInfo[aiCenterCode]; ok { | ||||
| @@ -238,6 +238,7 @@ | |||||
| {{$.i18n.Tr "repo.debug"}} | {{$.i18n.Tr "repo.debug"}} | ||||
| </a> | </a> | ||||
| {{else}} | {{else}} | ||||
| {{if not .BootFile}} | |||||
| <a id="ai-debug-{{$JobID}}" | <a id="ai-debug-{{$JobID}}" | ||||
| class='ui basic ai_debug {{if eq .Status "CREATING" "STOPPING" "WAITING" "STARTING"}} disabled {{else}}blue {{end}}button' | class='ui basic ai_debug {{if eq .Status "CREATING" "STOPPING" "WAITING" "STARTING"}} disabled {{else}}blue {{end}}button' | ||||
| data-jobid="{{$JobID}}" | data-jobid="{{$JobID}}" | ||||
| @@ -245,6 +246,7 @@ | |||||
| {{$.i18n.Tr "repo.debug_again"}} | {{$.i18n.Tr "repo.debug_again"}} | ||||
| </a> | </a> | ||||
| {{end}} | {{end}} | ||||
| {{end}} | |||||
| </form> | </form> | ||||
| </div> | </div> | ||||
| {{end}} | {{end}} | ||||
| @@ -362,7 +362,7 @@ | |||||
| </tr> | </tr> | ||||
| <tr class="ti-no-ng-animate"> | <tr class="ti-no-ng-animate"> | ||||
| <td class="ti-no-ng-animate ti-text-form-label text-width80"> | <td class="ti-no-ng-animate ti-text-form-label text-width80"> | ||||
| 创建人 | |||||
| {{$.i18n.Tr "repo.cloudbrain_creator"}} | |||||
| </td> | </td> | ||||
| <td class="ti-text-form-content"> | <td class="ti-text-form-content"> | ||||
| @@ -444,6 +444,7 @@ | |||||
| <td class="ti-text-form-content"> | <td class="ti-text-form-content"> | ||||
| <div class="text-span text-span-w"> | <div class="text-span text-span-w"> | ||||
| {{.BranchName}} | {{.BranchName}} | ||||
| <span style="margin-left:1rem" class="ui label">{{SubStr .CommitID 0 10}}</span> | |||||
| </div> | </div> | ||||
| </td> | </td> | ||||
| </tr> | </tr> | ||||
| @@ -337,6 +337,7 @@ | |||||
| <td class="ti-text-form-content"> | <td class="ti-text-form-content"> | ||||
| <div class="text-span text-span-w" id="{{.VersionName}}-code"> | <div class="text-span text-span-w" id="{{.VersionName}}-code"> | ||||
| {{.BranchName}} | {{.BranchName}} | ||||
| <span style="margin-left:1rem" class="ui label">{{SubStr .CommitID 0 10}}</span> | |||||
| </div> | </div> | ||||
| </td> | </td> | ||||
| </tr> | </tr> | ||||
| @@ -410,6 +410,7 @@ | |||||
| <td class="ti-text-form-content"> | <td class="ti-text-form-content"> | ||||
| <div class="text-span text-span-w"> | <div class="text-span text-span-w"> | ||||
| {{.BranchName}} | {{.BranchName}} | ||||
| <span style="margin-left:1rem" class="ui label">{{SubStr .CommitID 0 10}}</span> | |||||
| </div> | </div> | ||||
| </td> | </td> | ||||
| </tr> | </tr> | ||||
| @@ -221,7 +221,7 @@ | |||||
| <!--任务状态 --> | <!--任务状态 --> | ||||
| <span class="job-status" id="{{.Cloudbrain.ID}}" | <span class="job-status" id="{{.Cloudbrain.ID}}" | ||||
| data-repopath="{{$.RepoRelPath}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}" | 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;" | <span><i id="{{.Cloudbrain.ID}}-icon" style="vertical-align: middle;" | ||||
| class="{{.Status}}"></i><span id="{{.Cloudbrain.ID}}-text" | class="{{.Status}}"></i><span id="{{.Cloudbrain.ID}}-text" | ||||
| style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span> | style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span> | ||||
| @@ -266,6 +266,7 @@ | |||||
| {{$.i18n.Tr "repo.debug"}} | {{$.i18n.Tr "repo.debug"}} | ||||
| </a> | </a> | ||||
| {{else}} | {{else}} | ||||
| {{if not .BootFile}} | |||||
| <a id="ai-debug-{{.Cloudbrain.ID}}" | <a id="ai-debug-{{.Cloudbrain.ID}}" | ||||
| class='ui basic ai_debug {{if eq .Status "CREATING" "STOPPING" "WAITING" "STARTING"}} disabled {{else}}blue {{end}}button' | class='ui basic ai_debug {{if eq .Status "CREATING" "STOPPING" "WAITING" "STARTING"}} disabled {{else}}blue {{end}}button' | ||||
| data-jobid="{{.Cloudbrain.ID}}" | data-jobid="{{.Cloudbrain.ID}}" | ||||
| @@ -274,6 +275,7 @@ | |||||
| {{$.i18n.Tr "repo.debug_again"}} | {{$.i18n.Tr "repo.debug_again"}} | ||||
| </a> | </a> | ||||
| {{end}} | {{end}} | ||||
| {{end}} | |||||
| {{else}} | {{else}} | ||||
| {{if eq .Status "RUNNING" "WAITING" "CREATING" "STARTING"}} | {{if eq .Status "RUNNING" "WAITING" "CREATING" "STARTING"}} | ||||
| <a class="ui basic disabled button"> | <a class="ui basic disabled button"> | ||||
| @@ -294,7 +296,9 @@ | |||||
| <a id="ai-stop-{{.Cloudbrain.ID}}" | <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' | 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-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"}} | {{$.i18n.Tr "repo.stop"}} | ||||
| </a> | </a> | ||||
| {{else}} | {{else}} | ||||
| @@ -322,6 +326,7 @@ | |||||
| {{end}} | {{end}} | ||||
| </form> | </form> | ||||
| </div> | </div> | ||||
| {{if not .BootFile}} | |||||
| <div class="ui compact buttons" | <div class="ui compact buttons" | ||||
| style="{{if eq .ComputeResource "CPU/GPU"}} visibility: visible {{else}} visibility: hidden{{end}}"> | style="{{if eq .ComputeResource "CPU/GPU"}} visibility: visible {{else}} visibility: hidden{{end}}"> | ||||
| <div class="ui dropdown" id="model_more" | <div class="ui dropdown" id="model_more" | ||||
| @@ -363,6 +368,7 @@ | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| {{end}} | |||||
| </div> | </div> | ||||
| <!-- 镜像列表弹窗 --> | <!-- 镜像列表弹窗 --> | ||||
| @@ -422,6 +422,7 @@ | |||||
| <td class="ti-text-form-content"> | <td class="ti-text-form-content"> | ||||
| <div class="text-span text-span-w"> | <div class="text-span text-span-w"> | ||||
| {{.BranchName}} | {{.BranchName}} | ||||
| <span style="margin-left:1rem" class="ui label">{{SubStr .CommitID 0 10}}</span> | |||||
| </div> | </div> | ||||
| </td> | </td> | ||||
| </tr> | </tr> | ||||
| @@ -51,6 +51,49 @@ | |||||
| </div> | </div> | ||||
| {{if not .IsBeingCreated}} | {{if not .IsBeingCreated}} | ||||
| <div class="repo-buttons"> | <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}}"> | <form method="post" style="margin: 0;" action="{{$.RepoLink}}/action/{{if $.IsWatchingRepo}}un{{end}}watch?redirect_to={{$.Link}}"> | ||||
| {{$.CsrfTokenHtml}} | {{$.CsrfTokenHtml}} | ||||
| <div class="ui labeled button" tabindex="0"> | <div class="ui labeled button" tabindex="0"> | ||||
| @@ -61,7 +104,7 @@ | |||||
| {{.NumWatches}} | {{.NumWatches}} | ||||
| </a> | </a> | ||||
| </div> | </div> | ||||
| </form> | |||||
| </form> --> | |||||
| <form method="post" style="margin: 0;" action="{{$.RepoLink}}/action/{{if $.IsStaringRepo}}un{{end}}star?redirect_to={{$.Link}}"> | <form method="post" style="margin: 0;" action="{{$.RepoLink}}/action/{{if $.IsStaringRepo}}un{{end}}star?redirect_to={{$.Link}}"> | ||||
| {{$.CsrfTokenHtml}} | {{$.CsrfTokenHtml}} | ||||
| <div class="ui labeled button" tabindex="0"> | <div class="ui labeled button" tabindex="0"> | ||||
| @@ -90,7 +133,7 @@ | |||||
| {{end}} | {{end}} | ||||
| <div class="ui tabs container"> | <div class="ui tabs container"> | ||||
| {{if not .Repository.IsBeingCreated}} | {{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}} | {{if .Permission.CanRead $.UnitTypeCode}} | ||||
| <div class="dropdown-menu"> | <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}}"> | <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}}"> | ||||
| @@ -99,6 +99,9 @@ | |||||
| animation-fill-mode: both; | animation-fill-mode: both; | ||||
| } | } | ||||
| </style> | </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"> | <div class="repository file list"> | ||||
| {{template "repo/header" .}} | {{template "repo/header" .}} | ||||
| <div class="ui container"> | <div class="ui container"> | ||||
| @@ -154,7 +157,9 @@ | |||||
| {{.i18n.Tr "repo.archive.title"}} | {{.i18n.Tr "repo.archive.title"}} | ||||
| </div> | </div> | ||||
| {{end}} | {{end}} | ||||
| {{if not .IsViewFile}} | |||||
| {{template "repo/sub_menu" .}} | {{template "repo/sub_menu" .}} | ||||
| {{end}} | |||||
| <div class="ui stackable secondary menu mobile--margin-between-items mobile--no-negative-margins"> | <div class="ui stackable secondary menu mobile--margin-between-items mobile--no-negative-margins"> | ||||
| {{template "repo/branch_dropdown" .}} | {{template "repo/branch_dropdown" .}} | ||||
| {{ $n := len .TreeNames}} | {{ $n := len .TreeNames}} | ||||
| @@ -200,6 +205,7 @@ | |||||
| class="section"><a href="{{EscapePound $.BranchLink}}/{{EscapePound $p}}" | class="section"><a href="{{EscapePound $.BranchLink}}/{{EscapePound $p}}" | ||||
| title="{{$v}}">{{EllipsisString $v 30}}</a></span>{{end}}{{end}}</span></div> | title="{{$v}}">{{EllipsisString $v 30}}</a></span>{{end}}{{end}}</span></div> | ||||
| {{end}} | {{end}} | ||||
| <div class="right fitted item" id="file-buttons"> | <div class="right fitted item" id="file-buttons"> | ||||
| <div class="ui tiny blue buttons"> | <div class="ui tiny blue buttons"> | ||||
| {{if .Repository.CanEnableEditor}} | {{if .Repository.CanEnableEditor}} | ||||
| @@ -223,7 +229,6 @@ | |||||
| </a> | </a> | ||||
| {{end}} | {{end}} | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="fitted item"> | <div class="fitted item"> | ||||
| {{if eq $n 0}} | {{if eq $n 0}} | ||||
| @@ -237,7 +242,6 @@ | |||||
| {{end}} | {{end}} | ||||
| </div> | </div> | ||||
| <div class="fitted item"> | <div class="fitted item"> | ||||
| <!-- Only show clone panel in repository home page --> | <!-- Only show clone panel in repository home page --> | ||||
| {{if eq $n 0}} | {{if eq $n 0}} | ||||
| <div class="ui action tiny input" id="clone-panel"> | <div class="ui action tiny input" id="clone-panel"> | ||||
| @@ -279,10 +283,22 @@ | |||||
| </div> | </div> | ||||
| {{end}} | {{end}} | ||||
| </div> | </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> | ||||
| <div class="ui container"> | <div class="ui container"> | ||||
| <div class="ui mobile reversed stackable grid"> | <div class="ui mobile reversed stackable grid"> | ||||
| {{if not .IsViewFile}} | |||||
| <div class="ui ten wide tablet twelve wide computer column"> | <div class="ui ten wide tablet twelve wide computer column"> | ||||
| {{else}} | |||||
| <div class="ui sixteen wide tablet sixteen wide computer column"> | |||||
| {{end}} | |||||
| {{if .IsViewFile}} | {{if .IsViewFile}} | ||||
| {{template "repo/view_file" .}} | {{template "repo/view_file" .}} | ||||
| {{else if .IsBlame}} | {{else if .IsBlame}} | ||||
| @@ -291,6 +307,7 @@ | |||||
| {{template "repo/view_list" .}} | {{template "repo/view_list" .}} | ||||
| {{end}} | {{end}} | ||||
| </div> | </div> | ||||
| {{if not .IsViewFile}} | |||||
| <div class="ui six wide tablet four wide computer column"> | <div class="ui six wide tablet four wide computer column"> | ||||
| <div id="repo-desc" data-IsAdmin="{{.Permission.IsAdmin}}" | <div id="repo-desc" data-IsAdmin="{{.Permission.IsAdmin}}" | ||||
| data-IsArchived="{{.Repository.IsArchived}}"> | data-IsArchived="{{.Repository.IsArchived}}"> | ||||
| @@ -303,25 +320,17 @@ | |||||
| {{else}} | {{else}} | ||||
| <span class="no-description text-italic">{{.i18n.Tr "repo.no_desc"}}</span> | <span class="no-description text-italic">{{.i18n.Tr "repo.no_desc"}}</span> | ||||
| {{end}} | {{end}} | ||||
| </p> | </p> | ||||
| </div> | </div> | ||||
| {{if .Repository.Website}} | {{if .Repository.Website}} | ||||
| <p class="ui"> | <p class="ui"> | ||||
| <i class="gray linkify icon"></i> | <i class="gray linkify icon"></i> | ||||
| <a class="link edit-link" target="_blank" title="{{.Repository.Website}}" | <a class="link edit-link" target="_blank" title="{{.Repository.Website}}" | ||||
| href="{{.Repository.Website}}">{{.Repository.Website}}</a> | href="{{.Repository.Website}}">{{.Repository.Website}}</a> | ||||
| </p> | </p> | ||||
| {{end}} | {{end}} | ||||
| <div class="ui" id="repo-topics" style="display: flex;position: relative;margin-bottom: 1.0rem;"> | <div class="ui" id="repo-topics" style="display: flex;position: relative;margin-bottom: 1.0rem;"> | ||||
| <i class="grey bookmark icon"></i> | <i class="grey bookmark icon"></i> | ||||
| <div id="repo-topics1" style="flex: 1;"> | <div id="repo-topics1" style="flex: 1;"> | ||||
| {{range .Topics}} | {{range .Topics}} | ||||
| <a class="ui repo-topic small label topic" | <a class="ui repo-topic small label topic" | ||||
| @@ -334,32 +343,22 @@ | |||||
| </div> | </div> | ||||
| <div id="topic_edit" class="vue_menu" style="display:none"> | <div id="topic_edit" class="vue_menu" style="display:none"> | ||||
| <div id="topic_edit1"> | <div id="topic_edit1"> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <p class="ui"> | <p class="ui"> | ||||
| <i class="grey code icon"></i> | <i class="grey code icon"></i> | ||||
| {{range .LanguageStats}} | {{range .LanguageStats}} | ||||
| {{.Language}} | {{.Language}} | ||||
| {{end}} | {{end}} | ||||
| </p> | </p> | ||||
| {{if .LICENSE}} | {{if .LICENSE}} | ||||
| <p class="ui"> | <p class="ui"> | ||||
| <i class="grey clone icon"></i> | <i class="grey clone icon"></i> | ||||
| {{.LICENSE}} | {{.LICENSE}} | ||||
| </p> | </p> | ||||
| {{end}} | {{end}} | ||||
| <div class="ui divider"></div> | <div class="ui divider"></div> | ||||
| <div> | <div> | ||||
| <h4 class="ui header"> | <h4 class="ui header"> | ||||
| {{$lenCon := len .ContributorInfo}} | {{$lenCon := len .ContributorInfo}} | ||||
| @@ -387,19 +386,20 @@ | |||||
| {{end}} | {{end}} | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| {{end}} | |||||
| </div> | </div> | ||||
| </div> | </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> | </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" .}} | {{template "base/footer" .}} | ||||
| @@ -388,6 +388,7 @@ td, th { | |||||
| <td class="ti-text-form-content"> | <td class="ti-text-form-content"> | ||||
| <div class="text-span text-span-w"> | <div class="text-span text-span-w"> | ||||
| {{.BranchName}} | {{.BranchName}} | ||||
| <span style="margin-left:1rem" class="ui label">{{SubStr .CommitID 0 10}}</span> | |||||
| </div> | </div> | ||||
| </td> | </td> | ||||
| </tr> | </tr> | ||||
| @@ -442,6 +442,7 @@ | |||||
| <td class="ti-text-form-content"> | <td class="ti-text-form-content"> | ||||
| <div class="text-span text-span-w"> | <div class="text-span text-span-w"> | ||||
| {{.BranchName}} | {{.BranchName}} | ||||
| <span style="margin-left:1rem" class="ui label">{{SubStr .CommitID 0 10}}</span> | |||||
| </div> | </div> | ||||
| </td> | </td> | ||||
| </tr> | </tr> | ||||
| @@ -833,6 +833,7 @@ | |||||
| $(`[vfield="Description"]`).text(res['Description'] || '--'); | $(`[vfield="Description"]`).text(res['Description'] || '--'); | ||||
| $(`[vfield="Parameters"]`).text(res['Parameters'] || '--'); | $(`[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']; | var imageName = res['Image'] || res['EngineName']; | ||||
| $(`[vimagetitle="Image"] span`).hide(); | $(`[vimagetitle="Image"] span`).hide(); | ||||
| @@ -15,7 +15,7 @@ | |||||
| {{else if .ResendLimited}} | {{else if .ResendLimited}} | ||||
| <p class="center">{{.i18n.Tr "auth.resent_limit_prompt"}}</p> | <p class="center">{{.i18n.Tr "auth.resent_limit_prompt"}}</p> | ||||
| {{else}} | {{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}} | {{end}} | ||||
| {{else}} | {{else}} | ||||
| {{if .IsSendRegisterMail}} | {{if .IsSendRegisterMail}} | ||||
| @@ -26,6 +26,7 @@ | |||||
| <p>{{.i18n.Tr "auth.has_unconfirmed_mail" .SignedUser.Name .SignedUser.Email | Str2html}}</p> | <p>{{.i18n.Tr "auth.has_unconfirmed_mail" .SignedUser.Name .SignedUser.Email | Str2html}}</p> | ||||
| <div class="ui divider"></div> | <div class="ui divider"></div> | ||||
| <div class="text right"> | <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> | <button class="ui blue button">{{.i18n.Tr "auth.resend_mail"}}</button> | ||||
| </div> | </div> | ||||
| {{end}} | {{end}} | ||||
| @@ -34,5 +35,32 @@ | |||||
| </form> | </form> | ||||
| </div> | </div> | ||||
| </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> | </div> | ||||
| {{template "base/footer" .}} | {{template "base/footer" .}} | ||||
| <script> | |||||
| $('.ui.blue.button.change').on('click',function(){ | |||||
| $('.ui.modal').modal('show') | |||||
| }) | |||||
| </script> | |||||
| @@ -124,7 +124,8 @@ | |||||
| style="width: 8% !important;"> | style="width: 8% !important;"> | ||||
| <span class="job-status" id="{{$JobID}}" | <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-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;" | <span><i id="{{$JobID}}-icon" style="vertical-align: middle;" | ||||
| class="{{.Status}}"></i><span id="{{$JobID}}-text" | class="{{.Status}}"></i><span id="{{$JobID}}-text" | ||||
| style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span> | style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span> | ||||
| @@ -185,7 +186,6 @@ | |||||
| <a href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}" | <a href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}" | ||||
| title="{{.Repo.OwnerName}}/{{.Repo.Alias}}">{{.Repo.OwnerName}}/{{.Repo.Alias}}</a> | title="{{.Repo.OwnerName}}/{{.Repo.Alias}}">{{.Repo.OwnerName}}/{{.Repo.Alias}}</a> | ||||
| </div> | </div> | ||||
| <div class="three wide column text center nowrap" style="width: 15%!important;"> | <div class="three wide column text center nowrap" style="width: 15%!important;"> | ||||
| {{if eq .JobType "DEBUG"}} | {{if eq .JobType "DEBUG"}} | ||||
| <div class="ui compact buttons"> | <div class="ui compact buttons"> | ||||
| @@ -199,6 +199,7 @@ | |||||
| {{$.i18n.Tr "repo.debug"}} | {{$.i18n.Tr "repo.debug"}} | ||||
| </a> | </a> | ||||
| {{else}} | {{else}} | ||||
| {{if not .BootFile}} | |||||
| <a id="ai-debug-{{$JobID}}" | <a id="ai-debug-{{$JobID}}" | ||||
| class='ui basic ai_debug {{if eq .Status "CREATING" "STOPPING" "WAITING" "STARTING"}} disabled {{else}}blue {{end}}button' | class='ui basic ai_debug {{if eq .Status "CREATING" "STOPPING" "WAITING" "STARTING"}} disabled {{else}}blue {{end}}button' | ||||
| data-jobid="{{$JobID}}" | data-jobid="{{$JobID}}" | ||||
| @@ -206,6 +207,7 @@ | |||||
| {{$.i18n.Tr "repo.debug_again"}} | {{$.i18n.Tr "repo.debug_again"}} | ||||
| </a> | </a> | ||||
| {{end}} | {{end}} | ||||
| {{end}} | |||||
| </form> | </form> | ||||
| </div> | </div> | ||||
| {{end}} | {{end}} | ||||
| @@ -228,7 +230,7 @@ | |||||
| <a style="padding: 0.5rem 1rem;" id="ai-stop-{{$JobID}}" | <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' | 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-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"}} | {{$.i18n.Tr "repo.stop"}} | ||||
| </a> | </a> | ||||
| </form> | </form> | ||||
| @@ -236,7 +238,7 @@ | |||||
| <a style="padding: 0.5rem 1rem;" id="ai-stop-{{$JobID}}" | <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' | 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-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"}} | {{$.i18n.Tr "repo.stop"}} | ||||
| </a> | </a> | ||||
| {{end}} | {{end}} | ||||
| @@ -24,6 +24,7 @@ export default async function initCloudrain() { | |||||
| const repoPath = job.dataset.repopath; | const repoPath = job.dataset.repopath; | ||||
| // const computeResource = job.dataset.resource | // const computeResource = job.dataset.resource | ||||
| const versionname = job.dataset.version; | const versionname = job.dataset.version; | ||||
| const bootfile = job.dataset.bootfile; | |||||
| const status_text = $(`#${ID}-text`).text(); | const status_text = $(`#${ID}-text`).text(); | ||||
| const finalState = [ | const finalState = [ | ||||
| "STOPPED", | "STOPPED", | ||||
| @@ -99,11 +100,16 @@ export default async function initCloudrain() { | |||||
| "SUCCEEDED", | "SUCCEEDED", | ||||
| ].includes(status) | ].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)) { | if (["RUNNING", "WAITING"].includes(status)) { | ||||
| $("#ai-stop-" + ID) | $("#ai-stop-" + ID) | ||||
| @@ -289,7 +295,7 @@ export default async function initCloudrain() { | |||||
| assertDelete(this); | assertDelete(this); | ||||
| } | } | ||||
| }); | }); | ||||
| function stopDebug(ID, stopUrl) { | |||||
| function stopDebug(ID, stopUrl,bootFile) { | |||||
| $.ajax({ | $.ajax({ | ||||
| type: "POST", | type: "POST", | ||||
| url: stopUrl, | url: stopUrl, | ||||
| @@ -301,11 +307,15 @@ export default async function initCloudrain() { | |||||
| .addClass(res.status); | .addClass(res.status); | ||||
| $("#" + ID + "-text").text(res.status); | $("#" + ID + "-text").text(res.status); | ||||
| if (res.status === "STOPPED") { | 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) | $("#ai-image-" + ID) | ||||
| .removeClass("blue") | .removeClass("blue") | ||||
| .addClass("disabled"); | .addClass("disabled"); | ||||
| @@ -344,7 +354,8 @@ export default async function initCloudrain() { | |||||
| $(".ui.basic.ai_stop").click(function () { | $(".ui.basic.ai_stop").click(function () { | ||||
| const ID = this.dataset.jobid; | const ID = this.dataset.jobid; | ||||
| const repoPath = this.dataset.repopath; | const repoPath = this.dataset.repopath; | ||||
| stopDebug(ID, repoPath); | |||||
| const bootFile = this.dataset.bootfile | |||||
| stopDebug(ID, repoPath,bootFile); | |||||
| }); | }); | ||||
| function stopVersion(version_name, ID, repoPath) { | function stopVersion(version_name, ID, repoPath) { | ||||
| @@ -1301,3 +1301,17 @@ i.SUCCEEDED { | |||||
| max-height: 500px; | max-height: 500px; | ||||
| overflow: auto; | 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; | |||||
| } | |||||
| @@ -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: {}, | |||||
| }); | |||||
| }; | |||||
| @@ -2,7 +2,6 @@ import axios from 'axios'; | |||||
| const service = axios.create({ | const service = axios.create({ | ||||
| baseURL: '/', | baseURL: '/', | ||||
| timeout: 20000, | |||||
| }); | }); | ||||
| service.interceptors.request.use((config) => { | service.interceptors.request.use((config) => { | ||||
| @@ -157,7 +157,7 @@ const en = { | |||||
| computeCluster: 'Compute Cluster', | computeCluster: 'Compute Cluster', | ||||
| resourceSpecification: 'Resource Specification', | resourceSpecification: 'Resource Specification', | ||||
| lastUpdateTime: 'Last Update Time', | 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', | resourceSpecificationIsAvailable: 'Specification Is Available', | ||||
| resourceSpecificationIsAvailableAll: 'Specification Is Available(All)', | resourceSpecificationIsAvailableAll: 'Specification Is Available(All)', | ||||
| available: 'Available', | available: 'Available', | ||||
| @@ -192,7 +192,35 @@ const en = { | |||||
| dataDesensitizationModelExperience:'Data desensitization model experience', | 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', | 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', | 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: { | ||||
| modelManage: 'Model management', | modelManage: 'Model management', | ||||
| modelName: 'Model name', | modelName: 'Model name', | ||||
| @@ -200,7 +228,7 @@ const en = { | |||||
| local: 'Local', | local: 'Local', | ||||
| online: 'Online', | online: 'Online', | ||||
| createModel: 'Create Model', | createModel: 'Create Model', | ||||
| importLocalModel: 'Import Lacal Model', | |||||
| importLocalModel: 'Import Local Model', | |||||
| importOnlineModel: 'Import Online Model', | importOnlineModel: 'Import Online Model', | ||||
| modifyModelInfo: 'Modify model information', | modifyModelInfo: 'Modify model information', | ||||
| addModelFiles: 'Add model files', | addModelFiles: 'Add model files', | ||||
| @@ -1,198 +1,227 @@ | |||||
| const zh = { | 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: { | 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: { | 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: { | ||||
| modelManage: '模型管理', | modelManage: '模型管理', | ||||
| modelName: '模型名称', | modelName: '模型名称', | ||||
| @@ -254,6 +283,8 @@ const zh = { | |||||
| deleteModelFileConfirmTips: '请确认是否删除当前模型文件?', | deleteModelFileConfirmTips: '请确认是否删除当前模型文件?', | ||||
| modelFileDeleteFailed: '模型文件删除失败', | modelFileDeleteFailed: '模型文件删除失败', | ||||
| }, | }, | ||||
| } | |||||
| }; | |||||
| export default zh; | export default zh; | ||||
| @@ -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')}} > {{$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> | |||||
| @@ -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'); | |||||