| @@ -1,7 +1,6 @@ | |||
| package models | |||
| import ( | |||
| "fmt" | |||
| "strconv" | |||
| "time" | |||
| @@ -313,10 +312,6 @@ func InsertCloudbrainDurationStatistic(cloudbrainDurationStatistic *CloudbrainDu | |||
| return xStatistic.Insert(cloudbrainDurationStatistic) | |||
| } | |||
| func GetDurationStatisticByDate(date string, hour int, aiCenterCode string, accCardType string) (*CloudbrainDurationStatistic, error) { | |||
| cb := &CloudbrainDurationStatistic{DayTime: date, HourTime: hour, AiCenterCode: aiCenterCode, AccCardType: accCardType} | |||
| return getDurationStatistic(cb) | |||
| } | |||
| func getDurationStatistic(cb *CloudbrainDurationStatistic) (*CloudbrainDurationStatistic, error) { | |||
| has, err := x.Get(cb) | |||
| if err != nil { | |||
| @@ -327,26 +322,6 @@ func getDurationStatistic(cb *CloudbrainDurationStatistic) (*CloudbrainDurationS | |||
| return cb, nil | |||
| } | |||
| func DeleteCloudbrainDurationStatisticHour(date string, hour int, aiCenterCode string, accCardType string) error { | |||
| sess := xStatistic.NewSession() | |||
| defer sess.Close() | |||
| if err := sess.Begin(); err != nil { | |||
| return fmt.Errorf("Begin: %v", err) | |||
| } | |||
| if _, err := sess.Where("day_time = ? AND hour_time = ? AND ai_center_code = ? AND acc_card_type = ?", date, hour, aiCenterCode, accCardType).Delete(&CloudbrainDurationStatistic{}); err != nil { | |||
| return fmt.Errorf("Delete: %v", err) | |||
| } | |||
| if err := sess.Commit(); err != nil { | |||
| sess.Close() | |||
| return fmt.Errorf("Commit: %v", err) | |||
| } | |||
| sess.Close() | |||
| return nil | |||
| } | |||
| func GetCanUseCardInfo() ([]*ResourceQueue, error) { | |||
| sess := x.NewSession() | |||
| defer sess.Close() | |||
| @@ -402,11 +377,11 @@ func GetDurationRecordUpdateTime() ([]*CloudbrainDurationStatistic, error) { | |||
| return CloudbrainDurationStatistics, nil | |||
| } | |||
| func DeleteCloudbrainDurationStatistic() error { | |||
| func DeleteCloudbrainDurationStatistic(beginTime timeutil.TimeStamp, endTime timeutil.TimeStamp) error { | |||
| sess := xStatistic.NewSession() | |||
| defer sess.Close() | |||
| if _, err := sess.Exec("TRUNCATE TABLE cloudbrain_duration_statistic"); err != nil { | |||
| log.Info("TRUNCATE cloudbrain_duration_statistic error.") | |||
| if _, err := sess.Exec("DELETE FROM cloudbrain_duration_statistic WHERE cloudbrain_duration_statistic.date_time BETWEEN ? AND ?", beginTime, endTime); err != nil { | |||
| log.Info("DELETE cloudbrain_duration_statistic data error.") | |||
| return err | |||
| } | |||
| return nil | |||
| @@ -1279,7 +1279,7 @@ func CreateRepository(ctx DBContext, doer, u *User, repo *Repository, opts ...Cr | |||
| } | |||
| if setting.Service.AutoWatchNewRepos { | |||
| if err = watchRepo(ctx.e, doer.ID, repo.ID, true); err != nil { | |||
| if err = watchRepo(ctx.e, doer.ID, repo.ID, true, ReceiveAllNotification); err != nil { | |||
| return fmt.Errorf("watchRepo: %v", err) | |||
| } | |||
| } | |||
| @@ -24,6 +24,14 @@ const ( | |||
| RepoWatchModeAuto // 3 | |||
| ) | |||
| // NotifyType specifies what kind of watch the user has on a repository | |||
| type NotifyType int8 | |||
| const ( | |||
| RejectAllNotification NotifyType = 0 | |||
| ReceiveAllNotification NotifyType = 9 | |||
| ) | |||
| var ActionChan = make(chan *Action, 200) | |||
| var ActionChan4Task = make(chan Action, 200) | |||
| @@ -34,6 +42,7 @@ type Watch struct { | |||
| RepoID int64 `xorm:"UNIQUE(watch)"` | |||
| Mode RepoWatchMode `xorm:"SMALLINT NOT NULL DEFAULT 1"` | |||
| CreatedUnix int64 `xorm:"created"` | |||
| NotifyType NotifyType `xorm:"SMALLINT NOT NULL DEFAULT 0"` | |||
| } | |||
| // getWatch gets what kind of subscription a user has on a given repository; returns dummy record if none found | |||
| @@ -60,8 +69,20 @@ func IsWatching(userID, repoID int64) bool { | |||
| return err == nil && isWatchMode(watch.Mode) | |||
| } | |||
| // GetWatchNotifyType | |||
| func GetWatchNotifyType(userID, repoID int64) NotifyType { | |||
| watch, err := getWatch(x, userID, repoID) | |||
| if err != nil { | |||
| return RejectAllNotification | |||
| } | |||
| return watch.NotifyType | |||
| } | |||
| func watchRepoMode(e Engine, watch Watch, mode RepoWatchMode) (err error) { | |||
| if watch.Mode == mode { | |||
| if _, err := e.ID(watch.ID).Cols("notify_type").Update(watch); err != nil { | |||
| return err | |||
| } | |||
| return nil | |||
| } | |||
| if mode == RepoWatchModeAuto && (watch.Mode == RepoWatchModeDont || isWatchMode(watch.Mode)) { | |||
| @@ -109,7 +130,7 @@ func WatchRepoMode(userID, repoID int64, mode RepoWatchMode) (err error) { | |||
| return watchRepoMode(x, watch, mode) | |||
| } | |||
| func watchRepo(e Engine, userID, repoID int64, doWatch bool) (err error) { | |||
| func watchRepo(e Engine, userID, repoID int64, doWatch bool, notifyTypes ...NotifyType) (err error) { | |||
| var watch Watch | |||
| if watch, err = getWatch(e, userID, repoID); err != nil { | |||
| return err | |||
| @@ -119,14 +140,19 @@ func watchRepo(e Engine, userID, repoID int64, doWatch bool) (err error) { | |||
| } else if !doWatch { | |||
| err = watchRepoMode(e, watch, RepoWatchModeNone) | |||
| } else { | |||
| notifyType := RejectAllNotification | |||
| if len(notifyTypes) > 0 { | |||
| notifyType = notifyTypes[0] | |||
| } | |||
| watch.NotifyType = notifyType | |||
| err = watchRepoMode(e, watch, RepoWatchModeNormal) | |||
| } | |||
| return err | |||
| } | |||
| // WatchRepo watch or unwatch repository. | |||
| func WatchRepo(userID, repoID int64, watch bool) (err error) { | |||
| return watchRepo(x, userID, repoID, watch) | |||
| func WatchRepo(userID, repoID int64, watch bool, notifyType ...NotifyType) (err error) { | |||
| return watchRepo(x, userID, repoID, watch, notifyType...) | |||
| } | |||
| func getWatchers(e Engine, repoID int64) ([]*Watch, error) { | |||
| @@ -156,6 +182,7 @@ func getRepoWatchersIDs(e Engine, repoID int64) ([]int64, error) { | |||
| return ids, e.Table("watch"). | |||
| Where("watch.repo_id=?", repoID). | |||
| And("watch.mode<>?", RepoWatchModeDont). | |||
| And("watch.notify_type > ?", RejectAllNotification). | |||
| Select("user_id"). | |||
| Find(&ids) | |||
| } | |||
| @@ -298,6 +298,15 @@ func ResourceSpecOffShelf(id int64) (int64, error) { | |||
| return n, err | |||
| } | |||
| func GetResourceSpecificationByIds(ids []int64) ([]*Specification, error) { | |||
| r := make([]*Specification, 0) | |||
| err := x.In("resource_specification.id", ids). | |||
| Join("INNER", "resource_queue", "resource_queue.id = resource_specification.queue_id"). | |||
| Find(&r) | |||
| return r, err | |||
| } | |||
| func GetResourceSpecification(r *ResourceSpecification) (*ResourceSpecification, error) { | |||
| has, err := x.Get(r) | |||
| if err != nil { | |||
| @@ -346,7 +346,7 @@ func (u *User) DashboardLink() string { | |||
| if u.IsOrganization() { | |||
| return setting.AppSubURL + "/org/" + u.Name + "/dashboard/" | |||
| } | |||
| return setting.AppSubURL + "/" | |||
| return setting.AppSubURL + "/dashboard" | |||
| } | |||
| // HomeLink returns the user or organization home page link. | |||
| @@ -216,6 +216,27 @@ func (email *EmailAddress) updateActivation(e Engine, activate bool) error { | |||
| return updateUserCols(e, user, "rands") | |||
| } | |||
| // UpdateEmailAddress update an email address of given user. | |||
| func (email *EmailAddress) UpdateEmailAddress(newEmailAddress string) error { | |||
| return email.updateEmailAddress(x, newEmailAddress) | |||
| } | |||
| func (email *EmailAddress) updateEmailAddress(e Engine, newEmailAddress string) error { | |||
| user, err := getUserByID(e, email.UID) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| if user.Rands, err = GetUserSalt(); err != nil { | |||
| return err | |||
| } | |||
| user.Email = newEmailAddress | |||
| user.AvatarEmail = newEmailAddress | |||
| email.Email = newEmailAddress | |||
| if _, err := e.ID(email.ID).Cols("email").Update(email); err != nil { | |||
| return err | |||
| } | |||
| return updateUserCols(e, user, "email", "avatar_email") | |||
| } | |||
| // DeleteEmailAddress deletes an email address of given user. | |||
| func DeleteEmailAddress(email *EmailAddress) (err error) { | |||
| var deleted int64 | |||
| @@ -88,6 +88,10 @@ type RegisterForm struct { | |||
| Agree bool | |||
| } | |||
| type UpdateEmailForm struct { | |||
| NewEmail string `binding:"Required;MaxSize(254)"` | |||
| } | |||
| // Validate valideates the fields | |||
| func (f *RegisterForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | |||
| return validate(errs, ctx.Data, f, ctx.Locale) | |||
| @@ -145,7 +145,7 @@ func isAdminOrImageCreater(ctx *context.Context, image *models.Image, err error) | |||
| func AdminOrOwnerOrJobCreaterRight(ctx *context.Context) { | |||
| var id = ctx.Params(":id") | |||
| job, err := models.GetCloudbrainByID(id) | |||
| job, err := GetCloudBrainByIdOrJobId(id) | |||
| if err != nil { | |||
| log.Error("GetCloudbrainByID failed:%v", err.Error()) | |||
| ctx.NotFound(ctx.Req.URL.RequestURI(), nil) | |||
| @@ -161,7 +161,7 @@ func AdminOrOwnerOrJobCreaterRight(ctx *context.Context) { | |||
| func AdminOrJobCreaterRight(ctx *context.Context) { | |||
| var id = ctx.Params(":id") | |||
| job, err := models.GetCloudbrainByID(id) | |||
| job, err := GetCloudBrainByIdOrJobId(id) | |||
| if err != nil { | |||
| log.Error("GetCloudbrainByID failed:%v", err.Error()) | |||
| ctx.NotFound(ctx.Req.URL.RequestURI(), nil) | |||
| @@ -177,7 +177,7 @@ func AdminOrJobCreaterRight(ctx *context.Context) { | |||
| func AdminOrOwnerOrJobCreaterRightForTrain(ctx *context.Context) { | |||
| var jobID = ctx.Params(":jobid") | |||
| job, err := models.GetCloudbrainByJobID(jobID) | |||
| job, err := GetCloudBrainByIdOrJobId(jobID) | |||
| if err != nil { | |||
| log.Error("GetCloudbrainByJobID failed:%v", err.Error()) | |||
| ctx.NotFound(ctx.Req.URL.RequestURI(), nil) | |||
| @@ -193,7 +193,7 @@ func AdminOrOwnerOrJobCreaterRightForTrain(ctx *context.Context) { | |||
| func AdminOrJobCreaterRightForTrain(ctx *context.Context) { | |||
| var jobID = ctx.Params(":jobid") | |||
| job, err := models.GetCloudbrainByJobID(jobID) | |||
| job, err := GetCloudBrainByIdOrJobId(jobID) | |||
| if err != nil { | |||
| log.Error("GetCloudbrainByJobID failed:%v", err.Error()) | |||
| ctx.NotFound(ctx.Req.URL.RequestURI(), nil) | |||
| @@ -652,3 +652,16 @@ func IsElementExist(s []string, str string) bool { | |||
| } | |||
| return false | |||
| } | |||
| func GetCloudBrainByIdOrJobId(id string) (*models.Cloudbrain,error) { | |||
| _, err := strconv.ParseInt(id, 10, 64) | |||
| var job *models.Cloudbrain | |||
| if err != nil { | |||
| job, err = models.GetCloudbrainByJobID(id) | |||
| } else { | |||
| job, err = models.GetCloudbrainByID(id) | |||
| } | |||
| return job,err | |||
| } | |||
| @@ -474,6 +474,7 @@ func RepoAssignment() macaron.Handler { | |||
| if ctx.IsSigned { | |||
| ctx.Data["IsWatchingRepo"] = models.IsWatching(ctx.User.ID, repo.ID) | |||
| ctx.Data["WatchNotifyType"] = models.GetWatchNotifyType(ctx.User.ID, repo.ID) | |||
| ctx.Data["IsStaringRepo"] = models.IsStaring(ctx.User.ID, repo.ID) | |||
| ctx.Data["IsStaringDataset"] = models.IsDatasetStaringByRepoId(ctx.User.ID, repo.ID) | |||
| @@ -1,13 +1,18 @@ | |||
| package modelarts | |||
| import ( | |||
| "encoding/base64" | |||
| "encoding/json" | |||
| "errors" | |||
| "fmt" | |||
| "io/ioutil" | |||
| "net/http" | |||
| "path" | |||
| "strconv" | |||
| "strings" | |||
| "code.gitea.io/gitea/modules/cloudbrain" | |||
| "code.gitea.io/gitea/modules/modelarts_cd" | |||
| "code.gitea.io/gitea/models" | |||
| @@ -23,7 +28,7 @@ const ( | |||
| //notebook | |||
| storageTypeOBS = "obs" | |||
| autoStopDuration = 4 * 60 * 60 | |||
| autoStopDurationMs = 4 * 60 * 60 * 1000 | |||
| AutoStopDurationMs = 4 * 60 * 60 * 1000 | |||
| MORDELART_USER_IMAGE_ENGINE_ID = -1 | |||
| DataSetMountPath = "/home/ma-user/work" | |||
| NotebookEnv = "Python3" | |||
| @@ -276,7 +281,7 @@ func GenerateTask(ctx *context.Context, jobName, uuid, description, flavor strin | |||
| return nil | |||
| } | |||
| func GenerateNotebook2(ctx *context.Context, displayJobName, jobName, uuid, description, imageId string, spec *models.Specification) error { | |||
| func GenerateNotebook2(ctx *context.Context, displayJobName, jobName, uuid, description, imageId string, spec *models.Specification, bootFile string,autoStopDurationInMs int64) (string, error) { | |||
| if poolInfos == nil { | |||
| json.Unmarshal([]byte(setting.PoolInfos), &poolInfos) | |||
| } | |||
| @@ -284,14 +289,14 @@ func GenerateNotebook2(ctx *context.Context, displayJobName, jobName, uuid, desc | |||
| imageName, err := GetNotebookImageName(imageId) | |||
| if err != nil { | |||
| log.Error("GetNotebookImageName failed: %v", err.Error()) | |||
| return err | |||
| return "", err | |||
| } | |||
| createTime := timeutil.TimeStampNow() | |||
| jobResult, err := createNotebook2(models.CreateNotebook2Params{ | |||
| JobName: jobName, | |||
| Description: description, | |||
| Flavor: spec.SourceSpecId, | |||
| Duration: autoStopDurationMs, | |||
| Duration: autoStopDurationInMs, | |||
| ImageID: imageId, | |||
| PoolID: poolInfos.PoolInfo[0].PoolId, | |||
| Feature: models.NotebookFeature, | |||
| @@ -316,10 +321,10 @@ func GenerateNotebook2(ctx *context.Context, displayJobName, jobName, uuid, desc | |||
| }) | |||
| if errTemp != nil { | |||
| log.Error("InsertCloudbrainTemp failed: %v", errTemp.Error()) | |||
| return errTemp | |||
| return "", errTemp | |||
| } | |||
| } | |||
| return err | |||
| return "", err | |||
| } | |||
| task := &models.Cloudbrain{ | |||
| Status: jobResult.Status, | |||
| @@ -334,6 +339,7 @@ func GenerateNotebook2(ctx *context.Context, displayJobName, jobName, uuid, desc | |||
| Uuid: uuid, | |||
| ComputeResource: models.NPUResource, | |||
| Image: imageName, | |||
| BootFile: bootFile, | |||
| Description: description, | |||
| CreatedUnix: createTime, | |||
| UpdatedUnix: createTime, | |||
| @@ -342,12 +348,12 @@ func GenerateNotebook2(ctx *context.Context, displayJobName, jobName, uuid, desc | |||
| err = models.CreateCloudbrain(task) | |||
| if err != nil { | |||
| return err | |||
| return "", err | |||
| } | |||
| stringId := strconv.FormatInt(task.ID, 10) | |||
| notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, stringId, displayJobName, models.ActionCreateDebugNPUTask) | |||
| return nil | |||
| return jobResult.ID, nil | |||
| } | |||
| func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (jobId string, err error) { | |||
| @@ -907,6 +913,11 @@ func HandleNotebookInfo(task *models.Cloudbrain) error { | |||
| if task.FlavorCode == "" { | |||
| task.FlavorCode = result.Flavor | |||
| } | |||
| if oldStatus != task.Status && task.Status == string(models.ModelArtsRunning) && task.BootFile != "" { | |||
| uploadNoteBookFile(task, result) | |||
| } | |||
| err = models.UpdateJob(task) | |||
| if err != nil { | |||
| log.Error("UpdateJob(%s) failed:%v", task.DisplayJobName, err) | |||
| @@ -917,6 +928,81 @@ func HandleNotebookInfo(task *models.Cloudbrain) error { | |||
| return nil | |||
| } | |||
| func uploadNoteBookFile(task *models.Cloudbrain, result *models.GetNotebook2Result) { | |||
| jupyterUrl := result.Url + "?token=" + result.Token | |||
| cookies, xsrf := getCookiesAndCsrf(jupyterUrl) | |||
| if xsrf == "" { | |||
| log.Error("browser jupyterUrl failed:%v", task.DisplayJobName) | |||
| } else { | |||
| codePath := setting.JobPath + task.JobName + cloudbrain.CodeMountPath | |||
| fileContents, err := ioutil.ReadFile(codePath + "/" + task.BootFile) | |||
| if err != nil { | |||
| log.Error("read jupyter file failed:%v", task.DisplayJobName, err) | |||
| } | |||
| base64Content := base64.StdEncoding.EncodeToString(fileContents) | |||
| client := getRestyClient() | |||
| uploadUrl := getJupyterBaseUrl(result.Url) + "api/contents/" + path.Base(task.BootFile) | |||
| res, err := client.R(). | |||
| SetCookies(cookies). | |||
| SetHeader("X-XSRFToken", xsrf). | |||
| SetBody(map[string]interface{}{ | |||
| "type": "file", | |||
| "format": "base64", | |||
| "name": path.Base(task.BootFile), | |||
| "path": path.Base(task.BootFile), | |||
| "content": base64Content}). | |||
| Put(uploadUrl) | |||
| if err != nil { | |||
| log.Error("upload jupyter file failed:%v", task.DisplayJobName, err) | |||
| } else if res.StatusCode() != http.StatusCreated { | |||
| log.Error("upload jupyter file failed:%v", task.DisplayJobName, err) | |||
| } | |||
| } | |||
| } | |||
| func getJupyterBaseUrl(url string) string { | |||
| jupyterUrlLength := len(url) | |||
| baseUrl := url[0 : jupyterUrlLength-len(path.Base(url))] | |||
| return baseUrl | |||
| } | |||
| func getCookiesAndCsrf(jupyterUrl string) ([]*http.Cookie, string) { | |||
| log.Info("jupyter url:"+jupyterUrl) | |||
| var cookies []*http.Cookie | |||
| const retryTimes = 10 | |||
| for i := 0; i < retryTimes; i++ { | |||
| res, err := http.Get(jupyterUrl) | |||
| if err != nil { | |||
| log.Error("browser jupyterUrl failed.",err) | |||
| if i==retryTimes-1{ | |||
| return cookies, "" | |||
| } | |||
| } else { | |||
| cookies = res.Cookies() | |||
| xsrf := "" | |||
| for _, cookie := range cookies { | |||
| if cookie.Name == "_xsrf" { | |||
| xsrf = cookie.Value | |||
| break | |||
| } | |||
| } | |||
| if xsrf != "" { | |||
| return cookies, xsrf | |||
| } | |||
| } | |||
| } | |||
| return cookies, "" | |||
| } | |||
| func SyncTempStatusJob() { | |||
| jobs, err := models.GetCloudBrainTempJobs() | |||
| if err != nil { | |||
| @@ -280,7 +280,7 @@ sendjob: | |||
| SetHeader("Content-Type", "application/json"). | |||
| SetAuthToken(TOKEN). | |||
| SetResult(&result). | |||
| Post(HOST + "/v1/" + setting.ProjectID + urlNotebook2 + "/" + jobID + "/" + param.Action + "?duration=" + strconv.Itoa(autoStopDurationMs)) | |||
| Post(HOST + "/v1/" + setting.ProjectID + urlNotebook2 + "/" + jobID + "/" + param.Action + "?duration=" + strconv.Itoa(AutoStopDurationMs)) | |||
| if err != nil { | |||
| return &result, fmt.Errorf("resty ManageNotebook2: %v", err) | |||
| @@ -88,18 +88,18 @@ type Parameters struct { | |||
| } `json:"parameter"` | |||
| } | |||
| func GenerateNotebook(ctx *context.Context, displayJobName, jobName, uuid, description, imageId string, spec *models.Specification) error { | |||
| func GenerateNotebook(ctx *context.Context, displayJobName, jobName, uuid, description, imageId string, spec *models.Specification, bootFile string,autoStopDurationInMs int64) (string, error) { | |||
| imageName, err := GetNotebookImageName(imageId) | |||
| if err != nil { | |||
| log.Error("GetNotebookImageName failed: %v", err.Error()) | |||
| return err | |||
| return "", err | |||
| } | |||
| createTime := timeutil.TimeStampNow() | |||
| jobResult, err := createNotebook(models.CreateNotebookWithoutPoolParams{ | |||
| JobName: jobName, | |||
| Description: description, | |||
| Flavor: spec.SourceSpecId, | |||
| Duration: autoStopDurationMs, | |||
| Duration: autoStopDurationInMs, | |||
| ImageID: imageId, | |||
| Feature: models.NotebookFeature, | |||
| Volume: models.VolumeReq{ | |||
| @@ -123,10 +123,10 @@ func GenerateNotebook(ctx *context.Context, displayJobName, jobName, uuid, descr | |||
| }) | |||
| if errTemp != nil { | |||
| log.Error("InsertCloudbrainTemp failed: %v", errTemp.Error()) | |||
| return errTemp | |||
| return "", errTemp | |||
| } | |||
| } | |||
| return err | |||
| return "", err | |||
| } | |||
| task := &models.Cloudbrain{ | |||
| Status: jobResult.Status, | |||
| @@ -145,16 +145,17 @@ func GenerateNotebook(ctx *context.Context, displayJobName, jobName, uuid, descr | |||
| CreatedUnix: createTime, | |||
| UpdatedUnix: createTime, | |||
| Spec: spec, | |||
| BootFile: bootFile, | |||
| } | |||
| err = models.CreateCloudbrain(task) | |||
| if err != nil { | |||
| return err | |||
| return "", err | |||
| } | |||
| stringId := strconv.FormatInt(task.ID, 10) | |||
| notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, stringId, displayJobName, models.ActionCreateDebugNPUTask) | |||
| return nil | |||
| return jobResult.ID, nil | |||
| } | |||
| func GetNotebookImageName(imageId string) (string, error) { | |||
| @@ -175,41 +176,3 @@ func GetNotebookImageName(imageId string) (string, error) { | |||
| return imageName, nil | |||
| } | |||
| /* | |||
| func HandleNotebookInfo(task *models.Cloudbrain) error { | |||
| result, err := GetNotebook(task.JobID) | |||
| if err != nil { | |||
| log.Error("GetNotebook2(%s) failed:%v", task.DisplayJobName, err) | |||
| return err | |||
| } | |||
| if result != nil { | |||
| oldStatus := task.Status | |||
| task.Status = result.Status | |||
| if task.StartTime == 0 && result.Lease.UpdateTime > 0 { | |||
| task.StartTime = timeutil.TimeStamp(result.Lease.UpdateTime / 1000) | |||
| } | |||
| if task.EndTime == 0 && models.IsModelArtsDebugJobTerminal(task.Status) { | |||
| task.EndTime = timeutil.TimeStampNow() | |||
| } | |||
| task.CorrectCreateUnix() | |||
| task.ComputeAndSetDuration() | |||
| if oldStatus != task.Status { | |||
| notification.NotifyChangeCloudbrainStatus(task, oldStatus) | |||
| } | |||
| if task.FlavorCode == "" { | |||
| task.FlavorCode = result.Flavor | |||
| } | |||
| err = models.UpdateJob(task) | |||
| if err != nil { | |||
| log.Error("UpdateJob(%s) failed:%v", task.DisplayJobName, err) | |||
| return err | |||
| } | |||
| } | |||
| return nil | |||
| } | |||
| */ | |||
| @@ -715,6 +715,21 @@ var ( | |||
| TeamName string | |||
| }{} | |||
| FileNoteBook = struct { | |||
| ProjectName string | |||
| ImageGPU string | |||
| SpecIdGPU int64 | |||
| SpecIdCPU int64 | |||
| ImageIdNPU string | |||
| SpecIdNPU int64 | |||
| ImageIdNPUCD string | |||
| SpecIdNPUCD int64 | |||
| ImageCPUDescription string | |||
| ImageGPUDescription string | |||
| ImageNPUDescription string | |||
| ImageNPUCDDescription string | |||
| }{} | |||
| ModelConvert = struct { | |||
| GPU_PYTORCH_IMAGE string | |||
| GpuQueue string | |||
| @@ -1580,6 +1595,20 @@ func NewContext() { | |||
| Course.OrgName = sec.Key("org_name").MustString("") | |||
| Course.TeamName = sec.Key("team_name").MustString("") | |||
| sec = Cfg.Section("file_notebook") | |||
| FileNoteBook.ProjectName = sec.Key("project_name").MustString("openi-notebook") | |||
| FileNoteBook.ImageIdNPU = sec.Key("imageid_npu").MustString("") | |||
| FileNoteBook.ImageGPU = sec.Key("image_gpu").MustString("") | |||
| FileNoteBook.SpecIdCPU = sec.Key("specid_cpu").MustInt64(-1) | |||
| FileNoteBook.SpecIdGPU = sec.Key("specid_gpu").MustInt64(-1) | |||
| FileNoteBook.SpecIdNPU = sec.Key("specid_npu").MustInt64(-1) | |||
| FileNoteBook.ImageIdNPUCD = sec.Key("imageid_npu_cd").MustString("") | |||
| FileNoteBook.SpecIdNPUCD = sec.Key("specid_npu_cd").MustInt64(-1) | |||
| FileNoteBook.ImageCPUDescription = sec.Key("image_cpu_desc").MustString("") | |||
| FileNoteBook.ImageGPUDescription = sec.Key("image_gpu_desc").MustString("") | |||
| FileNoteBook.ImageNPUDescription = sec.Key("image_npu_desc").MustString("") | |||
| FileNoteBook.ImageNPUCDDescription = sec.Key("image_npu_cd_desc").MustString("") | |||
| getGrampusConfig() | |||
| getModelartsCDConfig() | |||
| getModelConvertConfig() | |||
| @@ -41,6 +41,14 @@ type CreateTrainJobOption struct { | |||
| SpecId int64 `json:"spec_id" binding:"Required"` | |||
| } | |||
| type CreateFileNotebookJobOption struct { | |||
| Type int `json:"type"` //0 CPU 1 GPU 2 NPU | |||
| File string `json:"file" binding:"Required"` | |||
| BranchName string `json:"branch_name" binding:"Required"` | |||
| OwnerName string `json:"owner_name" binding:"Required"` | |||
| ProjectName string `json:"project_name" binding:"Required"` | |||
| } | |||
| type Cloudbrain struct { | |||
| ID int64 `json:"id"` | |||
| JobID string `json:"job_id"` | |||
| @@ -151,6 +151,9 @@ func NewFuncMap() []template.FuncMap { | |||
| "EscapePound": func(str string) string { | |||
| return strings.NewReplacer("%", "%25", "#", "%23", " ", "%20", "?", "%3F").Replace(str) | |||
| }, | |||
| "IpynbBool":func(str string) bool{ | |||
| return strings.Contains(str, ".ipynb") | |||
| }, | |||
| "nl2br": func(text string) template.HTML { | |||
| return template.HTML(strings.Replace(template.HTMLEscapeString(text), "\n", "<br>", -1)) | |||
| }, | |||
| @@ -397,9 +397,12 @@ authorize_application_created_by = This application was created by %s. | |||
| authorize_application_description = If you grant the access, it will be able to access and write to all your account information, including private repos and organisations. | |||
| authorize_title = Authorize "%s" to access your account? | |||
| authorization_failed = Authorization failed | |||
| authorization_failed_desc = The authorization failed because we detected an invalid request. Please contact the maintainer of the app you've tried to authorize. | |||
| authorization_failed_desc = The authorization failed because we detected an invalid request. Please contact the maintainer of the app you have tried to authorize. | |||
| disable_forgot_password_mail = Account recovery is disabled. Please contact your site administrator. | |||
| sspi_auth_failed = SSPI authentication failed | |||
| change_email = Change email | |||
| change_email_address = Change email address | |||
| new_email_address = New email address | |||
| [phone] | |||
| format_err=The format of phone number is wrong. | |||
| query_err=Fail to query phone number, please try again later. | |||
| @@ -1015,6 +1018,8 @@ readme = README | |||
| readme_helper = Select a README file template. | |||
| auto_init = Initialize Repository (Adds .gitignore, License and README) | |||
| create_repo = Create Repository | |||
| failed_to_create_repo=Failed to create repository, please try again later. | |||
| failed_to_create_notebook_repo=Failed to create %s repository, please check whether you have the same name project, if yes please update or delete it first. | |||
| create_course = Publish Course | |||
| failed_to_create_course=Failed to publish course, please try again later. | |||
| default_branch = Default Branch | |||
| @@ -1049,6 +1054,10 @@ model_experience = Model Experience | |||
| model_noright=You have no right to do the operation. | |||
| model_rename=Duplicate model name, please modify model name. | |||
| notebook_file_not_exist=Notebook file does not exist. | |||
| notebook_select_wrong=Please select a Notebook(.ipynb) file first. | |||
| notebook_file_no_right=You have no right to access the Notebook(.ipynb) file. | |||
| date=Date | |||
| repo_add=Project Increment | |||
| repo_total=Project Total | |||
| @@ -1228,7 +1237,7 @@ cloudbrain.benchmark.evaluate_test=Test Script | |||
| cloudbrain.benchmark.types={"type":[{"id":1,"rank_link":"https://git.openi.org.cn/benchmark/?username=admin&algType=detection","first":"Target detection","second":[{"id":1,"value":"None","attachment":"84cf39c4-d8bc-41aa-aaa3-182ce289b105","owner":"yangzhx","repo_name":"detection_benchmark_script"}]},{"id":2,"rank_link":"https://git.openi.org.cn/benchmark/?username=admin&algType=reid","first":"Target re-identification","second":[{"id":1,"value":"Vehicle re-identification","attachment":"84cf39c4-d8bc-41aa-aaa3-182ce289b105","owner":"JiahongXu","repo_name":"benchmark_reID_script"},{"id":2,"value":"Image-based person re-identification","attachment":"84cf39c4-d8bc-41aa-aaa3-182ce289b105","owner":"JiahongXu","repo_name":"benchmark_reID_script"}]},{"id":3,"rank_link":"https://git.openi.org.cn/benchmark/?username=admin&algType=tracking","first":"Multi-target tracking","second":[{"id":1,"value":"None","attachment":"84cf39c4-d8bc-41aa-aaa3-182ce289b105","owner":"lix07","repo_name":"MOT_benchmark_script"}]}]} | |||
| cloudbrain.morethanonejob=You already have a running or waiting task, create it after that task is over. | |||
| cloudbrain.morethanonejob1=You have created an <span style="color:rgba(242, 113, 28, 1);"> equivalent task </span> that is waiting or running, please wait for the task to finish before creating it. | |||
| cloudbrain.morethanonejob2=You can view all your Cloud Brain tasks in <a href="/cloudbrains" target="_blank"> Home > Cloudbrain Task </a>. | |||
| cloudbrain.morethanonejob2=You can view all your Cloud Brain tasks in <a href="/cloudbrains" target="_blank"> Home > Cloudbrain Task </a>. | |||
| modelarts.infer_job_model = Model | |||
| modelarts.infer_job_model_file = Model File | |||
| @@ -1402,6 +1411,11 @@ star = Star | |||
| fork = Fork | |||
| download_archive = Download Repository | |||
| star_fail=Failed to %s the dataset. | |||
| watched=Watched | |||
| notWatched=Not watched | |||
| un_watch=Unwatch | |||
| watch_all=Watch all | |||
| watch_no_notify=Watch but not notify | |||
| no_desc = No Description | |||
| no_label = No labels | |||
| @@ -1443,6 +1457,7 @@ blame = Blame | |||
| normal_view = Normal View | |||
| line = line | |||
| lines = lines | |||
| notebook_open = Open in Notebook | |||
| editor.new_file = New File | |||
| editor.upload_file = Upload File | |||
| @@ -403,6 +403,9 @@ authorization_failed=授权失败 | |||
| authorization_failed_desc=授权失败,这是一个无效的请求。请联系尝试授权应用的管理员。 | |||
| disable_forgot_password_mail = Account recovery is disabled. Please contact your site administrator. | |||
| sspi_auth_failed=SSPI 认证失败 | |||
| change_email=修改邮箱 | |||
| change_email_address=修改邮箱地址 | |||
| new_email_address=新邮箱地址 | |||
| [phone] | |||
| format_err=手机号格式错误。 | |||
| query_err=查询手机号失败,请稍后再试。 | |||
| @@ -1020,6 +1023,8 @@ readme=自述 | |||
| readme_helper=选择自述文件模板。 | |||
| auto_init=初始化存储库 (添加. gitignore、许可证和自述文件) | |||
| create_repo=创建项目 | |||
| failed_to_create_repo=创建项目失败,请稍后再试。 | |||
| failed_to_create_notebook_repo=创建项目%s失败,请检查您是否有同名的项目,如果有请先手工修改或删除后重试。 | |||
| create_course=发布课程 | |||
| failed_to_create_course=发布课程失败,请稍后再试。 | |||
| default_branch=默认分支 | |||
| @@ -1048,6 +1053,9 @@ model_experience = 模型体验 | |||
| model_noright=您没有操作权限。 | |||
| model_rename=模型名称重复,请修改模型名称 | |||
| notebook_file_not_exist=Notebook文件不存在。 | |||
| notebook_select_wrong=请先选择Notebook(.ipynb)文件。 | |||
| notebook_file_no_right=您没有这个Notebook文件的读权限。 | |||
| date=日期 | |||
| repo_add=新增项目 | |||
| @@ -1418,6 +1426,11 @@ star=点赞 | |||
| fork=派生 | |||
| download_archive=下载此项目 | |||
| star_fail=%s失败。 | |||
| watched=已关注 | |||
| notWatched=未关注 | |||
| un_watch=不关注 | |||
| watch_all=关注所有动态 | |||
| watch_no_notify=关注但不提醒动态 | |||
| no_desc=暂无描述 | |||
| @@ -1461,6 +1474,8 @@ normal_view=普通视图 | |||
| line=行 | |||
| lines=行 | |||
| notebook_open = 在Notebook中打开 | |||
| editor.new_file=新建文件 | |||
| editor.upload_file=上传文件 | |||
| editor.edit_file=编辑文件 | |||
| @@ -737,6 +737,12 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
| m.Get("/my_favorite", repo.MyFavoriteDatasetMultiple) | |||
| }, reqToken(), repoAssignment()) | |||
| m.Group("/file_notebook", func() { | |||
| m.Get("", reqToken(), repo.GetFileNoteBookInfo) | |||
| m.Post("/create", reqToken(), reqWeChat(), bind(api.CreateFileNotebookJobOption{}), repo.CreateFileNoteBook) | |||
| }) | |||
| m.Group("/repos", func() { | |||
| m.Get("/search", repo.Search) | |||
| @@ -78,6 +78,75 @@ func CloudBrainShow(ctx *context.APIContext) { | |||
| ctx.JSON(http.StatusOK, models.BaseMessageWithDataApi{Code: 0, Message: "", Data: convert.ToCloudBrain(task)}) | |||
| } | |||
| func CreateFileNoteBook(ctx *context.APIContext, option api.CreateFileNotebookJobOption) { | |||
| cloudbrainTask.FileNotebookCreate(ctx.Context, option) | |||
| } | |||
| func GetFileNoteBookInfo(ctx *context.APIContext) { | |||
| //image description spec description waiting count | |||
| specs, err := models.GetResourceSpecificationByIds([]int64{setting.FileNoteBook.SpecIdCPU, setting.FileNoteBook.SpecIdGPU, setting.FileNoteBook.SpecIdNPU, setting.FileNoteBook.SpecIdNPUCD}) | |||
| if err != nil { | |||
| log.Error("Fail to query specifications", err) | |||
| ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_query_fail"))) | |||
| return | |||
| } | |||
| var specCPU, specGpu, specNPU, specNPUCD *api.SpecificationShow | |||
| var specGpuQueueCode string | |||
| for _, spec := range specs { | |||
| if spec.ID == setting.FileNoteBook.SpecIdCPU { | |||
| specCPU = convert.ToSpecification(spec) | |||
| } else if spec.ID == setting.FileNoteBook.SpecIdGPU { | |||
| specGpu = convert.ToSpecification(spec) | |||
| specGpuQueueCode = spec.QueueCode | |||
| } else if spec.ID == setting.FileNoteBook.SpecIdNPU { | |||
| specNPU = convert.ToSpecification(spec) | |||
| } else if spec.ID == setting.FileNoteBook.SpecIdNPUCD { | |||
| specNPUCD = convert.ToSpecification(spec) | |||
| } | |||
| } | |||
| waitCountNpu := cloudbrain.GetWaitingCloudbrainCount(models.TypeCloudBrainTwo, "") | |||
| queuesMap, err := cloudbrain.GetQueuesDetail() | |||
| if err != nil { | |||
| log.Error("Fail to query gpu queues waiting count", err) | |||
| ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_query_fail"))) | |||
| return | |||
| } | |||
| waitCountGPU := (*queuesMap)[specGpuQueueCode] | |||
| if !setting.ModelartsCD.Enabled{ | |||
| ctx.JSON(http.StatusOK, map[string]interface{}{ | |||
| "code": 0, | |||
| "projectName":setting.FileNoteBook.ProjectName, | |||
| "specCpu": specCPU, | |||
| "specGpu": specGpu, | |||
| "specNpu": specNPU, | |||
| "waitCountGpu": waitCountGPU, | |||
| "waitCountNpu": waitCountNpu, | |||
| "imageCpuDescription": setting.FileNoteBook.ImageCPUDescription, | |||
| "imageGpuDescription": setting.FileNoteBook.ImageGPUDescription, | |||
| "imageNpuDescription": setting.FileNoteBook.ImageNPUDescription, | |||
| }) | |||
| } else{ | |||
| ctx.JSON(http.StatusOK, map[string]interface{}{ | |||
| "code": 0, | |||
| "projectName":setting.FileNoteBook.ProjectName, | |||
| "specCpu": specCPU, | |||
| "specGpu": specGpu, | |||
| "specNpu": specNPUCD, | |||
| "waitCountGpu": waitCountGPU, | |||
| "waitCountNpu": waitCountNpu, | |||
| "imageCpuDescription": setting.FileNoteBook.ImageCPUDescription, | |||
| "imageGpuDescription": setting.FileNoteBook.ImageGPUDescription, | |||
| "imageNpuDescription": setting.FileNoteBook.ImageNPUCDDescription, | |||
| }) | |||
| } | |||
| } | |||
| func CreateCloudBrain(ctx *context.APIContext, option api.CreateTrainJobOption) { | |||
| @@ -141,10 +210,11 @@ func GetCloudbrainTask(ctx *context.APIContext) { | |||
| ) | |||
| ID := ctx.Params(":id") | |||
| job, err := models.GetCloudbrainByID(ID) | |||
| job,err := cloudbrain.GetCloudBrainByIdOrJobId(ID) | |||
| if err != nil { | |||
| ctx.NotFound(err) | |||
| log.Error("GetCloudbrainByID failed:", err) | |||
| return | |||
| } | |||
| if job.JobType == string(models.JobTypeModelSafety) { | |||
| @@ -123,8 +123,9 @@ func GetOverviewDuration(ctx *context.Context) { | |||
| recordBeginTime := recordCloudbrain[0].Cloudbrain.CreatedUnix | |||
| now := time.Now() | |||
| endTime := now | |||
| // worker_server_num := 1 | |||
| // cardNum := 1 | |||
| var workServerNumber int64 | |||
| var cardNum int64 | |||
| durationAllSum := int64(0) | |||
| cardDuSum := int64(0) | |||
| @@ -138,52 +139,60 @@ func GetOverviewDuration(ctx *context.Context) { | |||
| c2NetDuration := int64(0) | |||
| cDCenterDuration := int64(0) | |||
| cloudbrains, _, err := models.CloudbrainAllStatic(&models.CloudbrainsOptions{ | |||
| Type: models.TypeCloudBrainAll, | |||
| BeginTimeUnix: int64(recordBeginTime), | |||
| EndTimeUnix: endTime.Unix(), | |||
| }) | |||
| if err != nil { | |||
| ctx.ServerError("Get cloudbrains failed:", err) | |||
| return | |||
| } | |||
| models.LoadSpecs4CloudbrainInfo(cloudbrains) | |||
| for _, cloudbrain := range cloudbrains { | |||
| cloudbrain = cloudbrainService.UpdateCloudbrainAiCenter(cloudbrain) | |||
| CardDurationString := repo.GetCloudbrainCardDuration(cloudbrain.Cloudbrain) | |||
| CardDuration := models.ConvertStrToDuration(CardDurationString) | |||
| // if cloudbrain.Cloudbrain.WorkServerNumber >= 1 { | |||
| // worker_server_num = cloudbrain.Cloudbrain.WorkServerNumber | |||
| // } else { | |||
| // worker_server_num = 1 | |||
| // } | |||
| // if cloudbrain.Cloudbrain.Spec == nil { | |||
| // cardNum = 1 | |||
| // } else { | |||
| // cardNum = cloudbrain.Cloudbrain.Spec.AccCardsNum | |||
| // } | |||
| // duration := cloudbrain.Duration | |||
| // duration := cloudbrain.Duration | |||
| duration := models.ConvertStrToDuration(cloudbrain.TrainJobDuration) | |||
| // CardDuration := cloudbrain.Duration * int64(worker_server_num) * int64(cardNum) | |||
| if cloudbrain.Cloudbrain.Type == models.TypeCloudBrainOne { | |||
| cloudBrainOneDuration += duration | |||
| cloudBrainOneCardDuSum += CardDuration | |||
| } else if cloudbrain.Cloudbrain.Type == models.TypeCloudBrainTwo { | |||
| cloudBrainTwoDuration += duration | |||
| cloudBrainTwoCardDuSum += CardDuration | |||
| } else if cloudbrain.Cloudbrain.Type == models.TypeC2Net { | |||
| c2NetDuration += duration | |||
| c2NetCardDuSum += CardDuration | |||
| } else if cloudbrain.Cloudbrain.Type == models.TypeCDCenter { | |||
| cDCenterDuration += duration | |||
| cDNetCardDuSum += CardDuration | |||
| page := 1 | |||
| pagesize := 10000 | |||
| count := pagesize | |||
| // Each time a maximum of 10000 pieces of data are detected to the memory, batch processing | |||
| for count == pagesize && count != 0 { | |||
| cloudbrains, _, err := models.CloudbrainAllStatic(&models.CloudbrainsOptions{ | |||
| ListOptions: models.ListOptions{ | |||
| Page: page, | |||
| PageSize: pagesize, | |||
| }, | |||
| Type: models.TypeCloudBrainAll, | |||
| BeginTimeUnix: int64(recordBeginTime), | |||
| EndTimeUnix: endTime.Unix(), | |||
| }) | |||
| if err != nil { | |||
| ctx.ServerError("Get cloudbrains failed:", err) | |||
| return | |||
| } | |||
| models.LoadSpecs4CloudbrainInfo(cloudbrains) | |||
| for _, cloudbrain := range cloudbrains { | |||
| cloudbrain = cloudbrainService.UpdateCloudbrainAiCenter(cloudbrain) | |||
| if cloudbrain.Cloudbrain.Spec != nil { | |||
| cardNum = int64(cloudbrain.Cloudbrain.Spec.AccCardsNum) | |||
| } else { | |||
| cardNum = 1 | |||
| } | |||
| if cloudbrain.Cloudbrain.WorkServerNumber >= 1 { | |||
| workServerNumber = int64(cloudbrain.Cloudbrain.WorkServerNumber) | |||
| } else { | |||
| workServerNumber = 1 | |||
| } | |||
| duration := models.ConvertStrToDuration(cloudbrain.TrainJobDuration) | |||
| CardDuration := workServerNumber * int64(cardNum) * duration | |||
| if cloudbrain.Cloudbrain.Type == models.TypeCloudBrainOne { | |||
| cloudBrainOneDuration += duration | |||
| cloudBrainOneCardDuSum += CardDuration | |||
| } else if cloudbrain.Cloudbrain.Type == models.TypeCloudBrainTwo { | |||
| cloudBrainTwoDuration += duration | |||
| cloudBrainTwoCardDuSum += CardDuration | |||
| } else if cloudbrain.Cloudbrain.Type == models.TypeC2Net { | |||
| c2NetDuration += duration | |||
| c2NetCardDuSum += CardDuration | |||
| } else if cloudbrain.Cloudbrain.Type == models.TypeCDCenter { | |||
| cDCenterDuration += duration | |||
| cDNetCardDuSum += CardDuration | |||
| } | |||
| durationAllSum += duration | |||
| cardDuSum += CardDuration | |||
| durationAllSum += duration | |||
| cardDuSum += CardDuration | |||
| } | |||
| count = len(cloudbrains) | |||
| page += 1 | |||
| } | |||
| ctx.JSON(http.StatusOK, map[string]interface{}{ | |||
| "cloudBrainOneCardDuSum": cloudBrainOneCardDuSum, | |||
| @@ -6,6 +6,7 @@ | |||
| package repo | |||
| import ( | |||
| "code.gitea.io/gitea/modules/cloudbrain" | |||
| "encoding/json" | |||
| "net/http" | |||
| "path" | |||
| @@ -37,11 +38,14 @@ func GetModelArtsNotebook2(ctx *context.APIContext) { | |||
| ) | |||
| ID := ctx.Params(":id") | |||
| job, err := models.GetCloudbrainByID(ID) | |||
| job,err := cloudbrain.GetCloudBrainByIdOrJobId(ID) | |||
| if err != nil { | |||
| ctx.NotFound(err) | |||
| return | |||
| } | |||
| err = modelarts.HandleNotebookInfo(job) | |||
| if err != nil { | |||
| ctx.NotFound(err) | |||
| @@ -11,7 +11,8 @@ import ( | |||
| "os" | |||
| "strconv" | |||
| "strings" | |||
| "time" | |||
| cloudbrainService "code.gitea.io/gitea/services/cloudbrain" | |||
| "code.gitea.io/gitea/models" | |||
| "code.gitea.io/gitea/modules/aisafety" | |||
| @@ -483,7 +484,6 @@ func isTaskNotFinished(status string) bool { | |||
| } | |||
| func AiSafetyCreateForGetGPU(ctx *context.Context) { | |||
| t := time.Now() | |||
| ctx.Data["PageIsCloudBrain"] = true | |||
| ctx.Data["IsCreate"] = true | |||
| ctx.Data["type"] = models.TypeCloudBrainOne | |||
| @@ -497,7 +497,7 @@ func AiSafetyCreateForGetGPU(ctx *context.Context) { | |||
| log.Info("GPUBaseDataSetUUID=" + setting.ModelSafetyTest.GPUBaseDataSetUUID) | |||
| log.Info("GPUCombatDataSetName=" + setting.ModelSafetyTest.GPUCombatDataSetName) | |||
| log.Info("GPUCombatDataSetUUID=" + setting.ModelSafetyTest.GPUCombatDataSetUUID) | |||
| var displayJobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:] | |||
| var displayJobName = cloudbrainService.GetDisplayJobName(ctx.User.Name) | |||
| ctx.Data["display_job_name"] = displayJobName | |||
| prepareCloudbrainOneSpecs(ctx) | |||
| queuesDetail, _ := cloudbrain.GetQueuesDetail() | |||
| @@ -514,12 +514,11 @@ func AiSafetyCreateForGetGPU(ctx *context.Context) { | |||
| } | |||
| func AiSafetyCreateForGetNPU(ctx *context.Context) { | |||
| t := time.Now() | |||
| ctx.Data["PageIsCloudBrain"] = true | |||
| ctx.Data["IsCreate"] = true | |||
| ctx.Data["type"] = models.TypeCloudBrainTwo | |||
| ctx.Data["compute_resource"] = models.NPUResource | |||
| var displayJobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:] | |||
| var displayJobName = cloudbrainService.GetDisplayJobName(ctx.User.Name) | |||
| ctx.Data["display_job_name"] = displayJobName | |||
| ctx.Data["datasetType"] = models.TypeCloudBrainTwo | |||
| ctx.Data["BaseDataSetName"] = setting.ModelSafetyTest.NPUBaseDataSetName | |||
| @@ -15,6 +15,8 @@ import ( | |||
| "time" | |||
| "unicode/utf8" | |||
| cloudbrainService "code.gitea.io/gitea/services/cloudbrain" | |||
| "code.gitea.io/gitea/modules/urfs_client/urchin" | |||
| "code.gitea.io/gitea/modules/dataset" | |||
| @@ -92,28 +94,9 @@ func MustEnableCloudbrain(ctx *context.Context) { | |||
| } | |||
| } | |||
| func cutString(str string, lens int) string { | |||
| if len(str) < lens { | |||
| return str | |||
| } | |||
| return str[:lens] | |||
| } | |||
| func jobNamePrefixValid(s string) string { | |||
| lowStr := strings.ToLower(s) | |||
| re := regexp.MustCompile(`[^a-z0-9_\\-]+`) | |||
| removeSpecial := re.ReplaceAllString(lowStr, "") | |||
| re = regexp.MustCompile(`^[_\\-]+`) | |||
| return re.ReplaceAllString(removeSpecial, "") | |||
| } | |||
| func cloudBrainNewDataPrepare(ctx *context.Context, jobType string) error { | |||
| ctx.Data["PageIsCloudBrain"] = true | |||
| t := time.Now() | |||
| var displayJobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:] | |||
| var displayJobName = cloudbrainService.GetDisplayJobName(ctx.User.Name) | |||
| ctx.Data["display_job_name"] = displayJobName | |||
| ctx.Data["command"] = cloudbrain.GetCloudbrainDebugCommand() | |||
| @@ -696,7 +679,7 @@ func CloudBrainRestart(ctx *context.Context) { | |||
| } else { | |||
| if count >= 1 { | |||
| log.Error("the user already has running or waiting task", ctx.Data["MsgID"]) | |||
| resultCode = "-1" | |||
| resultCode = "2" | |||
| errorMsg = ctx.Tr("repo.cloudbrain.morethanonejob") | |||
| break | |||
| } | |||
| @@ -759,43 +742,13 @@ func cloudBrainShow(ctx *context.Context, tpName base.TplName, jobType models.Jo | |||
| return | |||
| } | |||
| if task.Status == string(models.JobWaiting) || task.Status == string(models.JobRunning) { | |||
| result, err := cloudbrain.GetJob(task.JobID) | |||
| task, err = cloudbrainTask.SyncCloudBrainOneStatus(task) | |||
| if err != nil { | |||
| log.Info("error:" + err.Error()) | |||
| ctx.NotFound(ctx.Req.URL.RequestURI(), nil) | |||
| return | |||
| } | |||
| if result != nil { | |||
| jobRes, _ := models.ConvertToJobResultPayload(result.Payload) | |||
| taskRoles := jobRes.TaskRoles | |||
| taskRes, _ := models.ConvertToTaskPod(taskRoles[cloudbrain.SubTaskName].(map[string]interface{})) | |||
| ctx.Data["taskRes"] = taskRes | |||
| ctx.Data["ExitDiagnostics"] = taskRes.TaskStatuses[0].ExitDiagnostics | |||
| oldStatus := task.Status | |||
| task.Status = taskRes.TaskStatuses[0].State | |||
| task.ContainerIp = "" | |||
| task.ContainerID = taskRes.TaskStatuses[0].ContainerID | |||
| models.ParseAndSetDurationFromCloudBrainOne(jobRes, task) | |||
| if task.DeletedAt.IsZero() { //normal record | |||
| if oldStatus != task.Status { | |||
| notification.NotifyChangeCloudbrainStatus(task, oldStatus) | |||
| } | |||
| err = models.UpdateJob(task) | |||
| if err != nil { | |||
| ctx.Data["error"] = err.Error() | |||
| return | |||
| } | |||
| } else { //deleted record | |||
| } | |||
| ctx.Data["result"] = jobRes | |||
| } else { | |||
| log.Info("error:" + err.Error()) | |||
| return | |||
| } | |||
| } | |||
| user, err := models.GetUserByID(task.UserID) | |||
| @@ -889,7 +842,13 @@ func cloudBrainShow(ctx *context.Context, tpName base.TplName, jobType models.Jo | |||
| func CloudBrainDebug(ctx *context.Context) { | |||
| task := ctx.Cloudbrain | |||
| debugUrl := setting.DebugServerHost + "jpylab_" + task.JobID + "_" + task.SubTaskName | |||
| ctx.Redirect(debugUrl) | |||
| if task.BootFile!=""{ | |||
| ctx.Redirect(getFileUrl(debugUrl,task.BootFile)) | |||
| }else{ | |||
| ctx.Redirect(debugUrl) | |||
| } | |||
| } | |||
| func prepareSpec4Show(ctx *context.Context, task *models.Cloudbrain) { | |||
| @@ -8,7 +8,6 @@ import ( | |||
| "code.gitea.io/gitea/models" | |||
| "code.gitea.io/gitea/modules/context" | |||
| "code.gitea.io/gitea/modules/log" | |||
| "code.gitea.io/gitea/modules/setting" | |||
| "code.gitea.io/gitea/modules/timeutil" | |||
| cloudbrainService "code.gitea.io/gitea/services/cloudbrain" | |||
| ) | |||
| @@ -23,9 +22,15 @@ func CloudbrainDurationStatisticHour() { | |||
| now := time.Now() | |||
| currentTime := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), 0, 0, 0, now.Location()) | |||
| if err == nil && len(recordDurationUpdateTime) > 0 { | |||
| statisticTime = time.Unix(int64(recordDurationUpdateTime[0].DateTime), 0) | |||
| statisticTime = time.Unix(int64(recordDurationUpdateTime[0].DateTime), 0).Add(+1 * time.Hour) | |||
| } else { | |||
| statisticTime = currentTime.Add(-1 * time.Hour) | |||
| statisticTime = currentTime | |||
| } | |||
| deleteBeginTime := time.Unix(int64(recordDurationUpdateTime[0].DateTime), 0) | |||
| err = models.DeleteCloudbrainDurationStatistic(timeutil.TimeStamp(deleteBeginTime.Unix()), timeutil.TimeStamp(currentTime.Unix())) | |||
| if err != nil { | |||
| log.Error("DeleteCloudbrainDurationStatistic failed", err) | |||
| } | |||
| for statisticTime.Before(currentTime) || statisticTime.Equal(currentTime) { | |||
| @@ -35,13 +40,10 @@ func CloudbrainDurationStatisticHour() { | |||
| } | |||
| log.Info("summaryDurationStat count: %v", count) | |||
| } | |||
| func UpdateDurationStatisticHistoryData() int64 { | |||
| func UpdateDurationStatisticHistoryData(beginTime time.Time, endTime time.Time) int64 { | |||
| var count int64 | |||
| recordBeginTime, _ := time.ParseInLocation("2006-01-02 15:04:05", setting.Grampus.UsageRateBeginTime, time.Local) | |||
| now := time.Now() | |||
| currentTime := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), 0, 0, 0, now.Location()) | |||
| statisticTime := recordBeginTime.Add(+1 * time.Hour) | |||
| statisticTime := beginTime | |||
| currentTime := endTime | |||
| for statisticTime.Before(currentTime) || statisticTime.Equal(currentTime) { | |||
| countEach := summaryDurationStat(statisticTime) | |||
| count += countEach | |||
| @@ -105,13 +107,6 @@ func summaryDurationStat(statisticTime time.Time) int64 { | |||
| for cardType, cardDuration := range CardTypes { | |||
| cloudbrainTable := cloudbrainMap[centerCode+"/"+cardType] | |||
| if cloudbrainTable != nil { | |||
| if _, err := models.GetDurationStatisticByDate(dayTime, hourTime, centerCode, cardType); err == nil { | |||
| if err := models.DeleteCloudbrainDurationStatisticHour(dayTime, hourTime, centerCode, cardType); err != nil { | |||
| log.Error("DeleteCloudbrainDurationStatisticHour failed: %v", err.Error()) | |||
| return 0 | |||
| } | |||
| } | |||
| if _, ok := cardsTotalDurationMap[cloudbrainTable.Cluster+"/"+centerCode+"/"+cardType]; !ok { | |||
| cardsTotalDurationMap[cloudbrainTable.Cluster+"/"+centerCode+"/"+cardType] = 0 | |||
| } | |||
| @@ -137,12 +132,6 @@ func summaryDurationStat(statisticTime time.Time) int64 { | |||
| } | |||
| for key, cardsTotalDuration := range cardsTotalDurationMap { | |||
| if _, err := models.GetDurationStatisticByDate(dayTime, hourTime, strings.Split(key, "/")[1], strings.Split(key, "/")[2]); err == nil { | |||
| if err := models.DeleteCloudbrainDurationStatisticHour(dayTime, hourTime, strings.Split(key, "/")[1], strings.Split(key, "/")[2]); err != nil { | |||
| log.Error("DeleteCloudbrainDurationStatisticHour failed: %v", err.Error()) | |||
| return 0 | |||
| } | |||
| } | |||
| cloudbrainDurationStat := models.CloudbrainDurationStatistic{ | |||
| DateTime: dateTime, | |||
| DayTime: dayTime, | |||
| @@ -255,8 +244,22 @@ func getcloudBrainCenterCodeAndCardTypeInfo(ciTasks []*models.CloudbrainInfo, be | |||
| } | |||
| func CloudbrainUpdateHistoryData(ctx *context.Context) { | |||
| err := models.DeleteCloudbrainDurationStatistic() | |||
| count := UpdateDurationStatisticHistoryData() | |||
| beginTimeStr := ctx.QueryTrim("beginTime") | |||
| endTimeStr := ctx.QueryTrim("endTime") | |||
| var count int64 | |||
| var err error | |||
| if beginTimeStr != "" && endTimeStr != "" { | |||
| beginTime, _ := time.ParseInLocation("2006-01-02 15:04:05", beginTimeStr, time.Local) | |||
| endTime, _ := time.ParseInLocation("2006-01-02 15:04:05", endTimeStr, time.Local) | |||
| if time.Now().Before(endTime) { | |||
| endTime = time.Now() | |||
| } | |||
| beginTimeUnix := timeutil.TimeStamp(beginTime.Unix()) | |||
| endTimeUnix := timeutil.TimeStamp(endTime.Unix()) | |||
| err = models.DeleteCloudbrainDurationStatistic(beginTimeUnix, endTimeUnix) | |||
| count = UpdateDurationStatisticHistoryData(beginTime.Add(+1*time.Hour), endTime) | |||
| } | |||
| ctx.JSON(http.StatusOK, map[string]interface{}{ | |||
| "message": 0, | |||
| "count": count, | |||
| @@ -10,7 +10,6 @@ import ( | |||
| "path" | |||
| "strconv" | |||
| "strings" | |||
| "time" | |||
| "code.gitea.io/gitea/modules/urfs_client/urchin" | |||
| "code.gitea.io/gitea/routers/response" | |||
| @@ -77,8 +76,7 @@ func GrampusTrainJobNPUNew(ctx *context.Context) { | |||
| func grampusTrainJobNewDataPrepare(ctx *context.Context, processType string) error { | |||
| ctx.Data["PageIsCloudBrain"] = true | |||
| t := time.Now() | |||
| var displayJobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:] | |||
| var displayJobName = cloudbrainService.GetDisplayJobName(ctx.User.Name) | |||
| ctx.Data["display_job_name"] = displayJobName | |||
| //get valid images | |||
| @@ -15,6 +15,8 @@ import ( | |||
| "time" | |||
| "unicode/utf8" | |||
| cloudbrainService "code.gitea.io/gitea/services/cloudbrain" | |||
| "code.gitea.io/gitea/services/cloudbrain/cloudbrainTask" | |||
| "code.gitea.io/gitea/modules/dataset" | |||
| @@ -128,8 +130,7 @@ func NotebookNew(ctx *context.Context) { | |||
| func notebookNewDataPrepare(ctx *context.Context) error { | |||
| ctx.Data["PageIsCloudBrain"] = true | |||
| t := time.Now() | |||
| var displayJobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:] | |||
| var displayJobName = cloudbrainService.GetDisplayJobName(ctx.User.Name) | |||
| ctx.Data["display_job_name"] = displayJobName | |||
| attachs, err := models.GetModelArtsUserAttachments(ctx.User.ID) | |||
| @@ -239,9 +240,9 @@ func Notebook2Create(ctx *context.Context, form auth.CreateModelArtsNotebookForm | |||
| } | |||
| if setting.ModelartsCD.Enabled { | |||
| err = modelarts_cd.GenerateNotebook(ctx, displayJobName, jobName, uuid, description, imageId, spec) | |||
| _, err = modelarts_cd.GenerateNotebook(ctx, displayJobName, jobName, uuid, description, imageId, spec, "",modelarts.AutoStopDurationMs) | |||
| } else { | |||
| err = modelarts.GenerateNotebook2(ctx, displayJobName, jobName, uuid, description, imageId, spec) | |||
| _, err = modelarts.GenerateNotebook2(ctx, displayJobName, jobName, uuid, description, imageId, spec, "",modelarts.AutoStopDurationMs) | |||
| } | |||
| if err != nil { | |||
| @@ -387,8 +388,33 @@ func NotebookDebug2(ctx *context.Context) { | |||
| ctx.RenderWithErr(err.Error(), tplModelArtsNotebookIndex, nil) | |||
| return | |||
| } | |||
| if task.BootFile!=""{ | |||
| ctx.Redirect(getFileUrl(result.Url,task.BootFile) + "?token="+ result.Token) | |||
| }else{ | |||
| ctx.Redirect(result.Url + "?token=" + result.Token) | |||
| } | |||
| ctx.Redirect(result.Url + "?token=" + result.Token) | |||
| } | |||
| func getFileUrl(url string,filename string) string{ | |||
| middle:="" | |||
| if url[len(url)-3:]=="lab" || url[len(url)-4:]=="lab/" { | |||
| if url[len(url)-1] == '/' { | |||
| middle="tree/" | |||
| } else { | |||
| middle= "/tree/" | |||
| } | |||
| }else{ | |||
| if url[len(url)-1] == '/' { | |||
| middle = "lab/tree/" | |||
| } else { | |||
| middle= "/lab/tree/" | |||
| } | |||
| } | |||
| return url+middle+path.Base(filename) | |||
| } | |||
| func NotebookRestart(ctx *context.Context) { | |||
| @@ -420,7 +446,8 @@ func NotebookRestart(ctx *context.Context) { | |||
| } else { | |||
| if count >= 1 { | |||
| log.Error("the user already has running or waiting task", ctx.Data["MsgID"]) | |||
| errorMsg = "you have already a running or waiting task, can not create more" | |||
| resultCode="2" | |||
| errorMsg = ctx.Tr("repo.cloudbrain.morethanonejob") | |||
| break | |||
| } | |||
| } | |||
| @@ -714,8 +741,7 @@ func trainJobNewDataPrepare(ctx *context.Context) error { | |||
| // return | |||
| //} | |||
| t := time.Now() | |||
| var displayJobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:] | |||
| var displayJobName = cloudbrainService.GetDisplayJobName(ctx.User.Name) | |||
| ctx.Data["display_job_name"] = displayJobName | |||
| attachs, err := models.GetModelArtsTrainAttachments(ctx.User.ID) | |||
| @@ -2351,8 +2377,7 @@ func inferenceJobNewDataPrepare(ctx *context.Context) error { | |||
| ctx.Data["PageIsCloudBrain"] = true | |||
| ctx.Data["newInference"] = true | |||
| t := time.Now() | |||
| var displayJobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:] | |||
| var displayJobName = cloudbrainService.GetDisplayJobName(ctx.User.Name) | |||
| ctx.Data["display_job_name"] = displayJobName | |||
| attachs, err := models.GetModelArtsTrainAttachments(ctx.User.ID) | |||
| @@ -414,7 +414,9 @@ func Action(ctx *context.Context) { | |||
| var err error | |||
| switch ctx.Params(":action") { | |||
| case "watch": | |||
| err = models.WatchRepo(ctx.User.ID, ctx.Repo.Repository.ID, true) | |||
| err = models.WatchRepo(ctx.User.ID, ctx.Repo.Repository.ID, true, models.ReceiveAllNotification) | |||
| case "watch_but_reject": | |||
| err = models.WatchRepo(ctx.User.ID, ctx.Repo.Repository.ID, true, models.RejectAllNotification) | |||
| case "unwatch": | |||
| err = models.WatchRepo(ctx.User.ID, ctx.Repo.Repository.ID, false) | |||
| case "star": | |||
| @@ -519,6 +519,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
| // r.Get("/feeds", binding.Bind(auth.FeedsForm{}), user.Feeds) | |||
| m.Any("/activate", user.Activate, reqSignIn) | |||
| m.Any("/activate_email", user.ActivateEmail) | |||
| m.Post("/update_email", bindIgnErr(auth.UpdateEmailForm{}), user.UpdateEmailPost) | |||
| m.Get("/avatar/:username/:size", user.Avatar) | |||
| m.Get("/email2user", user.Email2User) | |||
| m.Get("/recover_account", user.ResetPasswd) | |||
| @@ -1413,6 +1413,34 @@ func SignUpPost(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterFo | |||
| handleSignInFull(ctx, u, false, true) | |||
| } | |||
| //update user emailAddress | |||
| func UpdateEmailPost(ctx *context.Context, form auth.UpdateEmailForm) { | |||
| newEmailAddress := ctx.Query("NewEmail") | |||
| if newEmailAddress == "" { | |||
| log.Error("please input the newEmail") | |||
| return | |||
| } | |||
| if used, _ := models.IsEmailUsed(newEmailAddress); used { | |||
| ctx.RenderWithErr(ctx.Tr("form.email_been_used"), TplActivate, &form) | |||
| return | |||
| } | |||
| user := ctx.User | |||
| email, err := models.GetEmailAddressByIDAndEmail(user.ID, user.Email) | |||
| if err != nil { | |||
| ctx.ServerError("GetEmailAddressByIDAndEmail failed", err) | |||
| return | |||
| } | |||
| err = email.UpdateEmailAddress(newEmailAddress) | |||
| if err != nil { | |||
| ctx.ServerError("UpdateEmailAddress failed", err) | |||
| return | |||
| } | |||
| ctx.Data["Email"] = newEmailAddress | |||
| ctx.User.Email = newEmailAddress | |||
| Activate(ctx) | |||
| } | |||
| // Activate render activate user page | |||
| func Activate(ctx *context.Context) { | |||
| code := ctx.Query("code") | |||
| @@ -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 | |||
| import ( | |||
| "net/http" | |||
| "code.gitea.io/gitea/models" | |||
| "code.gitea.io/gitea/modules/cloudbrain" | |||
| "code.gitea.io/gitea/modules/httplib" | |||
| "code.gitea.io/gitea/modules/log" | |||
| "code.gitea.io/gitea/modules/notification" | |||
| "code.gitea.io/gitea/modules/setting" | |||
| "net/http" | |||
| "strconv" | |||
| ) | |||
| var noteBookOKMap = make(map[int64]int, 20) | |||
| var noteBookFailMap = make(map[int64]int, 20) | |||
| //if a task notebook url can get two times, the notebook can browser. | |||
| //if a task notebook url can get successfulCount times, the notebook can browser. | |||
| const successfulCount = 3 | |||
| const maxSuccessfulCount=10 | |||
| func SyncCloudBrainOneStatus(task *models.Cloudbrain) (*models.Cloudbrain, error) { | |||
| jobResult, err := cloudbrain.GetJob(task.JobID) | |||
| @@ -62,21 +63,29 @@ func isNoteBookReady(task *models.Cloudbrain) bool { | |||
| return true | |||
| } | |||
| noteBookUrl := setting.DebugServerHost + "jpylab_" + task.JobID + "_" + task.SubTaskName | |||
| r := httplib.Get(noteBookUrl) | |||
| res, err := r.Response() | |||
| res,err := http.Get(noteBookUrl) | |||
| if err != nil { | |||
| return false | |||
| } | |||
| log.Info("notebook success count:"+strconv.Itoa(noteBookOKMap[task.ID])+",fail count:"+strconv.Itoa(noteBookFailMap[task.ID])) | |||
| if res.StatusCode == http.StatusOK { | |||
| count := noteBookOKMap[task.ID] | |||
| if count < successfulCount-1 { | |||
| if count==0{ //如果是第一次成功,把失败数重置为0 | |||
| noteBookFailMap[task.ID]=0 | |||
| } | |||
| if count < successfulCount-1 || (noteBookFailMap[task.ID]==0 && count < maxSuccessfulCount-1) { | |||
| noteBookOKMap[task.ID] = count + 1 | |||
| return false | |||
| } else { | |||
| log.Info("notebook success count:"+strconv.Itoa(count)+",fail count:"+strconv.Itoa(noteBookFailMap[task.ID])) | |||
| delete(noteBookOKMap, task.ID) | |||
| delete(noteBookFailMap, task.ID) | |||
| return true | |||
| } | |||
| }else{ | |||
| noteBookFailMap[task.ID]+=1 | |||
| } | |||
| return false | |||
| @@ -810,6 +810,18 @@ func uploadCodeToMinio(codePath, jobName, parentDir string) error { | |||
| return nil | |||
| } | |||
| func uploadOneFileToMinio(codePath, filePath, jobName, parentDir string) error { | |||
| destObject := setting.CBCodePathPrefix + jobName + parentDir + path.Base(filePath) | |||
| sourceFile := codePath + "/" + filePath | |||
| err := storage.Attachments.UploadObject(destObject, sourceFile) | |||
| if err != nil { | |||
| log.Error("UploadObject(%s) failed: %s", filePath, err.Error()) | |||
| return err | |||
| } | |||
| return nil | |||
| } | |||
| func readDir(dirname string) ([]os.FileInfo, error) { | |||
| f, err := os.Open(dirname) | |||
| if err != nil { | |||
| @@ -1,7 +1,11 @@ | |||
| package cloudbrain | |||
| import ( | |||
| "regexp" | |||
| "strconv" | |||
| "strings" | |||
| "time" | |||
| "code.gitea.io/gitea/models" | |||
| "code.gitea.io/gitea/modules/context" | |||
| @@ -33,6 +37,28 @@ func GetAiCenterShow(aiCenter string, ctx *context.Context) string { | |||
| } | |||
| func GetDisplayJobName(username string) string { | |||
| t := time.Now() | |||
| return jobNamePrefixValid(cutString(username, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:] | |||
| } | |||
| func cutString(str string, lens int) string { | |||
| if len(str) < lens { | |||
| return str | |||
| } | |||
| return str[:lens] | |||
| } | |||
| func jobNamePrefixValid(s string) string { | |||
| lowStr := strings.ToLower(s) | |||
| re := regexp.MustCompile(`[^a-z0-9_\\-]+`) | |||
| removeSpecial := re.ReplaceAllString(lowStr, "") | |||
| re = regexp.MustCompile(`^[_\\-]+`) | |||
| return re.ReplaceAllString(removeSpecial, "") | |||
| } | |||
| func GetAiCenterInfoByCenterCode(aiCenterCode string) *setting.C2NetSequenceInfo { | |||
| if setting.AiCenterCodeAndNameMapInfo != nil { | |||
| if info, ok := setting.AiCenterCodeAndNameMapInfo[aiCenterCode]; ok { | |||
| @@ -238,6 +238,7 @@ | |||
| {{$.i18n.Tr "repo.debug"}} | |||
| </a> | |||
| {{else}} | |||
| {{if not .BootFile}} | |||
| <a id="ai-debug-{{$JobID}}" | |||
| class='ui basic ai_debug {{if eq .Status "CREATING" "STOPPING" "WAITING" "STARTING"}} disabled {{else}}blue {{end}}button' | |||
| data-jobid="{{$JobID}}" | |||
| @@ -245,6 +246,7 @@ | |||
| {{$.i18n.Tr "repo.debug_again"}} | |||
| </a> | |||
| {{end}} | |||
| {{end}} | |||
| </form> | |||
| </div> | |||
| {{end}} | |||
| @@ -362,7 +362,7 @@ | |||
| </tr> | |||
| <tr class="ti-no-ng-animate"> | |||
| <td class="ti-no-ng-animate ti-text-form-label text-width80"> | |||
| 创建人 | |||
| {{$.i18n.Tr "repo.cloudbrain_creator"}} | |||
| </td> | |||
| <td class="ti-text-form-content"> | |||
| @@ -444,6 +444,7 @@ | |||
| <td class="ti-text-form-content"> | |||
| <div class="text-span text-span-w"> | |||
| {{.BranchName}} | |||
| <span style="margin-left:1rem" class="ui label">{{SubStr .CommitID 0 10}}</span> | |||
| </div> | |||
| </td> | |||
| </tr> | |||
| @@ -337,6 +337,7 @@ | |||
| <td class="ti-text-form-content"> | |||
| <div class="text-span text-span-w" id="{{.VersionName}}-code"> | |||
| {{.BranchName}} | |||
| <span style="margin-left:1rem" class="ui label">{{SubStr .CommitID 0 10}}</span> | |||
| </div> | |||
| </td> | |||
| </tr> | |||
| @@ -410,6 +410,7 @@ | |||
| <td class="ti-text-form-content"> | |||
| <div class="text-span text-span-w"> | |||
| {{.BranchName}} | |||
| <span style="margin-left:1rem" class="ui label">{{SubStr .CommitID 0 10}}</span> | |||
| </div> | |||
| </td> | |||
| </tr> | |||
| @@ -221,7 +221,7 @@ | |||
| <!--任务状态 --> | |||
| <span class="job-status" id="{{.Cloudbrain.ID}}" | |||
| data-repopath="{{$.RepoRelPath}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}" | |||
| data-jobid="{{.Cloudbrain.ID}}" data-resource="{{.ComputeResource}}"> | |||
| data-jobid="{{.Cloudbrain.ID}}" data-resource="{{.ComputeResource}}" data-bootfile="{{.BootFile}}"> | |||
| <span><i id="{{.Cloudbrain.ID}}-icon" style="vertical-align: middle;" | |||
| class="{{.Status}}"></i><span id="{{.Cloudbrain.ID}}-text" | |||
| style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span> | |||
| @@ -266,6 +266,7 @@ | |||
| {{$.i18n.Tr "repo.debug"}} | |||
| </a> | |||
| {{else}} | |||
| {{if not .BootFile}} | |||
| <a id="ai-debug-{{.Cloudbrain.ID}}" | |||
| class='ui basic ai_debug {{if eq .Status "CREATING" "STOPPING" "WAITING" "STARTING"}} disabled {{else}}blue {{end}}button' | |||
| data-jobid="{{.Cloudbrain.ID}}" | |||
| @@ -274,6 +275,7 @@ | |||
| {{$.i18n.Tr "repo.debug_again"}} | |||
| </a> | |||
| {{end}} | |||
| {{end}} | |||
| {{else}} | |||
| {{if eq .Status "RUNNING" "WAITING" "CREATING" "STARTING"}} | |||
| <a class="ui basic disabled button"> | |||
| @@ -294,7 +296,9 @@ | |||
| <a id="ai-stop-{{.Cloudbrain.ID}}" | |||
| class='ui basic ai_stop {{if eq .Status "STOPPED" "FAILED" "START_FAILED" "STOPPING" "CREATING" "STARTING" "SUCCEEDED" "CREATE_FAILED" "DELETED"}}disabled {{else}}blue {{end}}button' | |||
| data-repopath="{{$.RepoLink}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}/{{.Cloudbrain.ID}}/stop" | |||
| data-jobid="{{.Cloudbrain.ID}}"> | |||
| data-jobid="{{.Cloudbrain.ID}}" | |||
| {{if .BootFile}}data-bootfile="{{.BootFile}}"{{end}}> | |||
| {{$.i18n.Tr "repo.stop"}} | |||
| </a> | |||
| {{else}} | |||
| @@ -322,6 +326,7 @@ | |||
| {{end}} | |||
| </form> | |||
| </div> | |||
| {{if not .BootFile}} | |||
| <div class="ui compact buttons" | |||
| style="{{if eq .ComputeResource "CPU/GPU"}} visibility: visible {{else}} visibility: hidden{{end}}"> | |||
| <div class="ui dropdown" id="model_more" | |||
| @@ -363,6 +368,7 @@ | |||
| </div> | |||
| </div> | |||
| </div> | |||
| {{end}} | |||
| </div> | |||
| <!-- 镜像列表弹窗 --> | |||
| @@ -422,6 +422,7 @@ | |||
| <td class="ti-text-form-content"> | |||
| <div class="text-span text-span-w"> | |||
| {{.BranchName}} | |||
| <span style="margin-left:1rem" class="ui label">{{SubStr .CommitID 0 10}}</span> | |||
| </div> | |||
| </td> | |||
| </tr> | |||
| @@ -51,6 +51,49 @@ | |||
| </div> | |||
| {{if not .IsBeingCreated}} | |||
| <div class="repo-buttons"> | |||
| <div class="ui labeled button" tabindex="0"> | |||
| <div style="position:relative;" class="ui compact basic button" onclick="$('.__watch_btn__').dropdown('show')"> | |||
| <i class="icon fa-eye{{if not $.IsWatchingRepo}}-slash{{end}}"></i>{{if $.IsWatchingRepo}}{{$.i18n.Tr "repo.watched"}}{{else}}{{$.i18n.Tr "repo.notWatched"}}{{end}} | |||
| <i class="dropdown icon" style="margin:0 -8px 0 4px"></i> | |||
| <div style="position:absolute;left:0;top:23px;" class="ui dropdown floating __watch_btn__" onclick="event.stopPropagation();"> | |||
| <div class="text" style="display:none;"></div> | |||
| {{$WatchNotifyType := or $.WatchNotifyType 0}} | |||
| <div class="menu"> | |||
| <div class="item {{if not $.IsWatchingRepo}}active selected{{end}}"> | |||
| <form method="post" style="margin: 0;" action="{{$.RepoLink}}/action/unwatch?redirect_to={{$.Link}}"> | |||
| {{$.CsrfTokenHtml}} | |||
| <button type="submit" style="border:none;background:transparent;width:100%;text-align:left;font-weight:inherit;cursor:pointer;"> | |||
| <i class="check icon" style="{{if $.IsWatchingRepo}}opacity:0{{end}}"></i> | |||
| {{$.i18n.Tr "repo.un_watch"}} | |||
| </button> | |||
| </form> | |||
| </div> | |||
| <div class="item {{if and $.IsWatchingRepo (eq $WatchNotifyType 9)}}active selected{{end}}"> | |||
| <form method="post" style="margin: 0;" action="{{$.RepoLink}}/action/watch?redirect_to={{$.Link}}"> | |||
| {{$.CsrfTokenHtml}} | |||
| <button type="submit" style="border:none;background:transparent;width:100%;text-align:left;font-weight:inherit;cursor:pointer;"> | |||
| <i class="check icon" style="{{if not (and $.IsWatchingRepo (eq $WatchNotifyType 9))}}opacity:0{{end}}"></i> | |||
| {{$.i18n.Tr "repo.watch_all"}} | |||
| </button> | |||
| </form> | |||
| </div> | |||
| <div class="item {{if and $.IsWatchingRepo (eq $WatchNotifyType 0)}}active selected{{end}}"> | |||
| <form method="post" style="margin: 0;" action="{{$.RepoLink}}/action/watch_but_reject?redirect_to={{$.Link}}"> | |||
| {{$.CsrfTokenHtml}} | |||
| <button type="submit" style="border:none;background:transparent;width:100%;text-align:left;font-weight:inherit;cursor:pointer;"> | |||
| <i class="check icon" style="{{if not (and $.IsWatchingRepo (eq $WatchNotifyType 0))}}opacity:0{{end}}"></i> | |||
| {{$.i18n.Tr "repo.watch_no_notify"}} | |||
| </button> | |||
| </form> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <a class="ui basic label" href="{{.Link}}/watchers"> | |||
| {{.NumWatches}} | |||
| </a> | |||
| </div> | |||
| <!-- | |||
| <form method="post" style="margin: 0;" action="{{$.RepoLink}}/action/{{if $.IsWatchingRepo}}un{{end}}watch?redirect_to={{$.Link}}"> | |||
| {{$.CsrfTokenHtml}} | |||
| <div class="ui labeled button" tabindex="0"> | |||
| @@ -61,7 +104,7 @@ | |||
| {{.NumWatches}} | |||
| </a> | |||
| </div> | |||
| </form> | |||
| </form> --> | |||
| <form method="post" style="margin: 0;" action="{{$.RepoLink}}/action/{{if $.IsStaringRepo}}un{{end}}star?redirect_to={{$.Link}}"> | |||
| {{$.CsrfTokenHtml}} | |||
| <div class="ui labeled button" tabindex="0"> | |||
| @@ -90,7 +133,7 @@ | |||
| {{end}} | |||
| <div class="ui tabs container"> | |||
| {{if not .Repository.IsBeingCreated}} | |||
| <div class="ui tabular menu navbar"> | |||
| <div class="ui tabular menu navbar" style="overflow-x:auto;overflow-y:hidden"> | |||
| {{if .Permission.CanRead $.UnitTypeCode}} | |||
| <div class="dropdown-menu"> | |||
| <a class="{{if or .PageIsViewCode .PageIsReleaseList .PageIsWiki .PageIsActivity .PageIsViewCode}}active{{end}} item hover_active" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/{{.BranchNameSubURL | EscapePound}}{{end}}"> | |||
| @@ -99,6 +99,9 @@ | |||
| animation-fill-mode: both; | |||
| } | |||
| </style> | |||
| {{if and (.IsViewFile) (IpynbBool .TreePath)}} | |||
| <link rel="stylesheet" href="{{StaticUrlPrefix}}/css/vp-notebook-debug.css?v={{MD5 AppVer}}" /> | |||
| {{end}} | |||
| <div class="repository file list"> | |||
| {{template "repo/header" .}} | |||
| <div class="ui container"> | |||
| @@ -154,7 +157,9 @@ | |||
| {{.i18n.Tr "repo.archive.title"}} | |||
| </div> | |||
| {{end}} | |||
| {{if not .IsViewFile}} | |||
| {{template "repo/sub_menu" .}} | |||
| {{end}} | |||
| <div class="ui stackable secondary menu mobile--margin-between-items mobile--no-negative-margins"> | |||
| {{template "repo/branch_dropdown" .}} | |||
| {{ $n := len .TreeNames}} | |||
| @@ -200,6 +205,7 @@ | |||
| class="section"><a href="{{EscapePound $.BranchLink}}/{{EscapePound $p}}" | |||
| title="{{$v}}">{{EllipsisString $v 30}}</a></span>{{end}}{{end}}</span></div> | |||
| {{end}} | |||
| <div class="right fitted item" id="file-buttons"> | |||
| <div class="ui tiny blue buttons"> | |||
| {{if .Repository.CanEnableEditor}} | |||
| @@ -223,7 +229,6 @@ | |||
| </a> | |||
| {{end}} | |||
| </div> | |||
| </div> | |||
| <div class="fitted item"> | |||
| {{if eq $n 0}} | |||
| @@ -237,7 +242,6 @@ | |||
| {{end}} | |||
| </div> | |||
| <div class="fitted item"> | |||
| <!-- Only show clone panel in repository home page --> | |||
| {{if eq $n 0}} | |||
| <div class="ui action tiny input" id="clone-panel"> | |||
| @@ -279,10 +283,22 @@ | |||
| </div> | |||
| {{end}} | |||
| </div> | |||
| {{if and (.IsViewFile) (IpynbBool .TreePath)}} | |||
| <div class="right fitted item"> | |||
| <button class="ui green button tiny" id="notebook-debug" style="display: flex;align-items: center;"> | |||
| <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="none" d="M0 0h24v24H0z"/><path d="M3 3h18a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1zm1 2v14h16V5H4zm8 10h6v2h-6v-2zm-3.333-3L5.838 9.172l1.415-1.415L11.495 12l-4.242 4.243-1.415-1.415L8.667 12z" fill="rgba(255,255,255,1)"/></svg> | |||
| <span style="margin-left:0.3rem;font-size: 14px;">{{.i18n.Tr "repo.notebook_open"}}</span> | |||
| </button> | |||
| </div> | |||
| {{end}} | |||
| </div> | |||
| <div class="ui container"> | |||
| <div class="ui mobile reversed stackable grid"> | |||
| {{if not .IsViewFile}} | |||
| <div class="ui ten wide tablet twelve wide computer column"> | |||
| {{else}} | |||
| <div class="ui sixteen wide tablet sixteen wide computer column"> | |||
| {{end}} | |||
| {{if .IsViewFile}} | |||
| {{template "repo/view_file" .}} | |||
| {{else if .IsBlame}} | |||
| @@ -291,6 +307,7 @@ | |||
| {{template "repo/view_list" .}} | |||
| {{end}} | |||
| </div> | |||
| {{if not .IsViewFile}} | |||
| <div class="ui six wide tablet four wide computer column"> | |||
| <div id="repo-desc" data-IsAdmin="{{.Permission.IsAdmin}}" | |||
| data-IsArchived="{{.Repository.IsArchived}}"> | |||
| @@ -303,25 +320,17 @@ | |||
| {{else}} | |||
| <span class="no-description text-italic">{{.i18n.Tr "repo.no_desc"}}</span> | |||
| {{end}} | |||
| </p> | |||
| </div> | |||
| {{if .Repository.Website}} | |||
| <p class="ui"> | |||
| <i class="gray linkify icon"></i> | |||
| <a class="link edit-link" target="_blank" title="{{.Repository.Website}}" | |||
| href="{{.Repository.Website}}">{{.Repository.Website}}</a> | |||
| </p> | |||
| {{end}} | |||
| <div class="ui" id="repo-topics" style="display: flex;position: relative;margin-bottom: 1.0rem;"> | |||
| <i class="grey bookmark icon"></i> | |||
| <div id="repo-topics1" style="flex: 1;"> | |||
| {{range .Topics}} | |||
| <a class="ui repo-topic small label topic" | |||
| @@ -334,32 +343,22 @@ | |||
| </div> | |||
| <div id="topic_edit" class="vue_menu" style="display:none"> | |||
| <div id="topic_edit1"> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <p class="ui"> | |||
| <i class="grey code icon"></i> | |||
| {{range .LanguageStats}} | |||
| {{.Language}} | |||
| {{end}} | |||
| </p> | |||
| {{if .LICENSE}} | |||
| <p class="ui"> | |||
| <i class="grey clone icon"></i> | |||
| {{.LICENSE}} | |||
| </p> | |||
| {{end}} | |||
| <div class="ui divider"></div> | |||
| <div> | |||
| <h4 class="ui header"> | |||
| {{$lenCon := len .ContributorInfo}} | |||
| @@ -387,19 +386,20 @@ | |||
| {{end}} | |||
| </div> | |||
| </div> | |||
| </div> | |||
| {{end}} | |||
| </div> | |||
| </div> | |||
| </div> | |||
| {{if and (.IsViewFile) (IpynbBool .TreePath)}} | |||
| <div id="__vue-root"></div> | |||
| <div id="__vue-self-data" data-branch="{{.BranchName}}" data-owner="{{.Repository.OwnerName}}" data-name="{{.SignedUser.Name}}" data-project="{{.Repository.Name}}" | |||
| data-file="{{.TreePath}}"> | |||
| </div> | |||
| {{end}} | |||
| </div> | |||
| <script type="text/javascript"> | |||
| // $(document).ready(function(){ | |||
| // $(".membersmore").click(function(){ | |||
| // $("#contributorInfo > a:nth-child(n+25)").show(); | |||
| // }); | |||
| // }); | |||
| </script> | |||
| {{if and (.IsViewFile) (IpynbBool .TreePath)}} | |||
| <script src="{{StaticUrlPrefix}}/js/vp-notebook-debug.js?v={{MD5 AppVer}}"></script> | |||
| {{end}} | |||
| {{template "base/footer" .}} | |||
| @@ -388,6 +388,7 @@ td, th { | |||
| <td class="ti-text-form-content"> | |||
| <div class="text-span text-span-w"> | |||
| {{.BranchName}} | |||
| <span style="margin-left:1rem" class="ui label">{{SubStr .CommitID 0 10}}</span> | |||
| </div> | |||
| </td> | |||
| </tr> | |||
| @@ -442,6 +442,7 @@ | |||
| <td class="ti-text-form-content"> | |||
| <div class="text-span text-span-w"> | |||
| {{.BranchName}} | |||
| <span style="margin-left:1rem" class="ui label">{{SubStr .CommitID 0 10}}</span> | |||
| </div> | |||
| </td> | |||
| </tr> | |||
| @@ -833,6 +833,7 @@ | |||
| $(`[vfield="Description"]`).text(res['Description'] || '--'); | |||
| $(`[vfield="Parameters"]`).text(res['Parameters'] || '--'); | |||
| $(`[vfield="BranchName"]`).html(res['BranchName'] + '<span style="margin-left:1rem" class="ui label">' + res['CommitID'].slice(0, 10) + '</span>'); | |||
| var imageName = res['Image'] || res['EngineName']; | |||
| $(`[vimagetitle="Image"] span`).hide(); | |||
| @@ -15,7 +15,7 @@ | |||
| {{else if .ResendLimited}} | |||
| <p class="center">{{.i18n.Tr "auth.resent_limit_prompt"}}</p> | |||
| {{else}} | |||
| <p>{{.i18n.Tr "auth.confirmation_mail_sent_prompt" .SignedUser.Email .ActiveCodeLives | Str2html}}</p> | |||
| <p>{{.i18n.Tr "auth.confirmation_mail_sent_prompt" .Email .ActiveCodeLives | Str2html}}</p> | |||
| {{end}} | |||
| {{else}} | |||
| {{if .IsSendRegisterMail}} | |||
| @@ -26,6 +26,7 @@ | |||
| <p>{{.i18n.Tr "auth.has_unconfirmed_mail" .SignedUser.Name .SignedUser.Email | Str2html}}</p> | |||
| <div class="ui divider"></div> | |||
| <div class="text right"> | |||
| <button type="button" class="ui blue button change">{{.i18n.Tr "auth.change_email"}}</button> | |||
| <button class="ui blue button">{{.i18n.Tr "auth.resend_mail"}}</button> | |||
| </div> | |||
| {{end}} | |||
| @@ -34,5 +35,32 @@ | |||
| </form> | |||
| </div> | |||
| </div> | |||
| <div> | |||
| <div class="ui modal chang-email"> | |||
| <div class="header" style="padding: 1rem;background-color: rgba(240, 240, 240, 100);"> | |||
| <h4>{{.i18n.Tr "auth.change_email_address"}}</h4> | |||
| </div> | |||
| <form id="formId" action="{{AppSubUrl}}/user/update_email" method="POST" class="ui form"> | |||
| <div class="content content-padding"> | |||
| <div class="ui error message"> | |||
| </div> | |||
| {{$.CsrfTokenHtml}} | |||
| <div class="inline required field"> | |||
| <label>{{.i18n.Tr "auth.new_email_address"}}</label> | |||
| <input style="width: 80%;" id="label" name="NewEmail" maxlength="255" value="{{.SignedUser.Email}}"> | |||
| </div> | |||
| </div> | |||
| <div class="center actions"> | |||
| <button class="ui green button">{{.i18n.Tr "repo.confirm_choice"}}</button> | |||
| <div class="ui deny button">{{.i18n.Tr "cancel"}}</div> | |||
| </div> | |||
| </form> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| {{template "base/footer" .}} | |||
| <script> | |||
| $('.ui.blue.button.change').on('click',function(){ | |||
| $('.ui.modal').modal('show') | |||
| }) | |||
| </script> | |||
| @@ -124,7 +124,8 @@ | |||
| style="width: 8% !important;"> | |||
| <span class="job-status" id="{{$JobID}}" | |||
| data-repopath='{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .JobType "DEBUG"}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}{{else if eq .JobType "INFERENCE"}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts{{end}}/inference-job{{else if eq .JobType "TRAIN"}}{{if eq .ComputeResource "NPU"}}/modelarts/train-job{{else}}/cloudbrain/train-job{{end}}{{else if eq .JobType "BENCHMARK" "MODELSAFETY"}}/cloudbrain{{end}}' | |||
| data-jobid="{{$JobID}}" data-version="{{.VersionName}}"> | |||
| data-jobid="{{$JobID}}" data-version="{{.VersionName}}" | |||
| data-bootfile="{{.BootFile}}"> | |||
| <span><i id="{{$JobID}}-icon" style="vertical-align: middle;" | |||
| class="{{.Status}}"></i><span id="{{$JobID}}-text" | |||
| style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span> | |||
| @@ -185,7 +186,6 @@ | |||
| <a href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}" | |||
| title="{{.Repo.OwnerName}}/{{.Repo.Alias}}">{{.Repo.OwnerName}}/{{.Repo.Alias}}</a> | |||
| </div> | |||
| <div class="three wide column text center nowrap" style="width: 15%!important;"> | |||
| {{if eq .JobType "DEBUG"}} | |||
| <div class="ui compact buttons"> | |||
| @@ -199,6 +199,7 @@ | |||
| {{$.i18n.Tr "repo.debug"}} | |||
| </a> | |||
| {{else}} | |||
| {{if not .BootFile}} | |||
| <a id="ai-debug-{{$JobID}}" | |||
| class='ui basic ai_debug {{if eq .Status "CREATING" "STOPPING" "WAITING" "STARTING"}} disabled {{else}}blue {{end}}button' | |||
| data-jobid="{{$JobID}}" | |||
| @@ -206,6 +207,7 @@ | |||
| {{$.i18n.Tr "repo.debug_again"}} | |||
| </a> | |||
| {{end}} | |||
| {{end}} | |||
| </form> | |||
| </div> | |||
| {{end}} | |||
| @@ -228,7 +230,7 @@ | |||
| <a style="padding: 0.5rem 1rem;" id="ai-stop-{{$JobID}}" | |||
| class='ui basic ai_stop {{if eq .Status "KILLED" "FAILED" "START_FAILED" "KILLING" "COMPLETED" "SUCCEEDED" "STOPPED" "STOPPING" "CREATE_FAILED"}}disabled {{else}} blue {{end}}button' | |||
| data-repopath='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else if eq .JobType "BENCHMARK" }}/cloudbrain/benchmark{{else if eq .ComputeResource "NPU" }}/modelarts/notebook{{end}}/{{$JobID}}/stop' | |||
| data-jobid="{{$JobID}}"> | |||
| data-jobid="{{$JobID}}" data-bootfile="{{.BootFile}}"> | |||
| {{$.i18n.Tr "repo.stop"}} | |||
| </a> | |||
| </form> | |||
| @@ -236,7 +238,7 @@ | |||
| <a style="padding: 0.5rem 1rem;" id="ai-stop-{{$JobID}}" | |||
| class='ui basic ai_stop_version {{if eq .Status "KILLED" "FAILED" "START_FAILED" "KILLING" "COMPLETED" "STOPPED" "SUCCEEDED" "CREATE_FAILED"}}disabled {{else}} blue {{end}}button' | |||
| data-repopath='{{.Repo.OwnerName}}/{{.Repo.Name}}/{{if eq .JobType "INFERENCE"}}{{if eq .Cloudbrain.Type 1}}modelarts/inference-job{{else}}cloudbrain/train-job{{end}}{{else if eq .JobType "TRAIN"}}{{if eq .Cloudbrain.Type 1}}modelarts/train-job{{else if eq .Cloudbrain.Type 0}}cloudbrain/train-job{{else if eq .Cloudbrain.Type 2}}grampus/train-job{{end}}{{end}}' | |||
| data-jobid="{{$JobID}}" data-version="{{.VersionName}}"> | |||
| data-jobid="{{$JobID}}" data-version="{{.VersionName}}" data-bootfile="{{.BootFile}}"> | |||
| {{$.i18n.Tr "repo.stop"}} | |||
| </a> | |||
| {{end}} | |||
| @@ -24,6 +24,7 @@ export default async function initCloudrain() { | |||
| const repoPath = job.dataset.repopath; | |||
| // const computeResource = job.dataset.resource | |||
| const versionname = job.dataset.version; | |||
| const bootfile = job.dataset.bootfile; | |||
| const status_text = $(`#${ID}-text`).text(); | |||
| const finalState = [ | |||
| "STOPPED", | |||
| @@ -99,11 +100,16 @@ export default async function initCloudrain() { | |||
| "SUCCEEDED", | |||
| ].includes(status) | |||
| ) { | |||
| $("#ai-debug-" + ID) | |||
| .removeClass("disabled") | |||
| .addClass("blue") | |||
| .text(debug_again_button) | |||
| .css("margin", "0"); | |||
| if (!bootfile) { | |||
| $("#ai-debug-" + ID) | |||
| .removeClass("disabled") | |||
| .addClass("blue") | |||
| .text(debug_again_button) | |||
| .css("margin", "0"); | |||
| } else { | |||
| $("#ai-debug-" + ID).remove() | |||
| } | |||
| } | |||
| if (["RUNNING", "WAITING"].includes(status)) { | |||
| $("#ai-stop-" + ID) | |||
| @@ -289,7 +295,7 @@ export default async function initCloudrain() { | |||
| assertDelete(this); | |||
| } | |||
| }); | |||
| function stopDebug(ID, stopUrl) { | |||
| function stopDebug(ID, stopUrl,bootFile) { | |||
| $.ajax({ | |||
| type: "POST", | |||
| url: stopUrl, | |||
| @@ -301,11 +307,15 @@ export default async function initCloudrain() { | |||
| .addClass(res.status); | |||
| $("#" + ID + "-text").text(res.status); | |||
| if (res.status === "STOPPED") { | |||
| $("#ai-debug-" + ID) | |||
| .removeClass("disabled") | |||
| .addClass("blue") | |||
| .text(debug_again_button) | |||
| .css("margin", "0"); | |||
| if (!bootFile) { | |||
| $("#ai-debug-" + ID) | |||
| .removeClass("disabled") | |||
| .addClass("blue") | |||
| .text(debug_again_button) | |||
| .css("margin", "0"); | |||
| } else { | |||
| $("#ai-debug-" + ID).remove() | |||
| } | |||
| $("#ai-image-" + ID) | |||
| .removeClass("blue") | |||
| .addClass("disabled"); | |||
| @@ -344,7 +354,8 @@ export default async function initCloudrain() { | |||
| $(".ui.basic.ai_stop").click(function () { | |||
| const ID = this.dataset.jobid; | |||
| const repoPath = this.dataset.repopath; | |||
| stopDebug(ID, repoPath); | |||
| const bootFile = this.dataset.bootfile | |||
| stopDebug(ID, repoPath,bootFile); | |||
| }); | |||
| function stopVersion(version_name, ID, repoPath) { | |||
| @@ -1301,3 +1301,17 @@ i.SUCCEEDED { | |||
| max-height: 500px; | |||
| overflow: auto; | |||
| } | |||
| .chang-email .content { | |||
| display: block; | |||
| width: 100%; | |||
| font-size: 1em; | |||
| line-height: 1.4; | |||
| padding: 1.5rem; | |||
| background: #fff; | |||
| } | |||
| .chang-email .actions { | |||
| background: #f9fafb; | |||
| padding: 1rem 1rem; | |||
| border-top: 1px solid rgba(34,36,38,.15); | |||
| text-align: right; | |||
| } | |||
| @@ -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({ | |||
| baseURL: '/', | |||
| timeout: 20000, | |||
| }); | |||
| service.interceptors.request.use((config) => { | |||
| @@ -157,7 +157,7 @@ const en = { | |||
| computeCluster: 'Compute Cluster', | |||
| resourceSpecification: 'Resource Specification', | |||
| lastUpdateTime: 'Last Update Time', | |||
| resSceneDeleteConfirm: 'Are you sure to delete the current Resource Scene?', | |||
| resSceneDeleteConfirm: 'Are you sure to delete the current Resource Scene?', | |||
| resourceSpecificationIsAvailable: 'Specification Is Available', | |||
| resourceSpecificationIsAvailableAll: 'Specification Is Available(All)', | |||
| available: 'Available', | |||
| @@ -192,7 +192,35 @@ const en = { | |||
| dataDesensitizationModelExperience:'Data desensitization model experience', | |||
| dataDesensitizationModelDesc:'Use AI technology to desensitize the face and license plate number in the picture. For more information about this model, please visit the project', | |||
| limitFilesUpload:'Only jpg/jpeg/png files can be uploaded', | |||
| limitSizeUpload:'The size of the uploaded file cannot exceed 20M!', | |||
| limitSizeUpload: 'The size of the uploaded file cannot exceed 20M!', | |||
| notebook: { | |||
| createNewNotebook: "Create new notebook debug task", | |||
| sameTaskTips1: "You have created an", | |||
| sameTaskTips2: "equivalent task", | |||
| sameTaskTips3: "that is waiting or running, please wait for the task to finish before creating it.", | |||
| sameTaskTips4: "You can view all your Cloudbrain tasks in", | |||
| sameTaskTips5: "Home", | |||
| sameTaskTips6: "Cloudbrain Task", | |||
| sameTaskTips7: "", | |||
| cpuEnv: "CPU Environment", | |||
| gpuEnv: "GPU Environment", | |||
| npuEnv: "NPU environment", | |||
| newTask: "Create new task", | |||
| noQuene: "The current resources are sufficient and no tasks are queued", | |||
| queneTips1: "There are currently", | |||
| queneTips2: "tasks queued", | |||
| watiResource: "Waiting for resources to be allocated, please be patient", | |||
| debug: "Debug", | |||
| stop: "Stop", | |||
| stopping: "Stopping", | |||
| notebookRunning: "The debugging environment has been started, and it will automatically close after 4 hours ", | |||
| stopSuccess: "Stop task succeeded", | |||
| specification: "Specification", | |||
| graphicMemory: "Graphic Memory", | |||
| memory: "Memory", | |||
| sharedMemory: "Shared Memory", | |||
| tips: 'The newly created debugging task will be placed in the project openi-notebook under your name. If there is no such project, the system will automatically create a new one.' | |||
| }, | |||
| modelManage: { | |||
| modelManage: 'Model management', | |||
| modelName: 'Model name', | |||
| @@ -200,7 +228,7 @@ const en = { | |||
| local: 'Local', | |||
| online: 'Online', | |||
| createModel: 'Create Model', | |||
| importLocalModel: 'Import Lacal Model', | |||
| importLocalModel: 'Import Local Model', | |||
| importOnlineModel: 'Import Online Model', | |||
| modifyModelInfo: 'Modify model information', | |||
| addModelFiles: 'Add model files', | |||
| @@ -1,198 +1,227 @@ | |||
| const zh = { | |||
| loading: '加载中...', | |||
| noData: '暂无数据', | |||
| date: '日期', | |||
| loading: "加载中...", | |||
| noData: "暂无数据", | |||
| date: "日期", | |||
| confirm: '确定', | |||
| cancel: '取消', | |||
| confirm1: '确认', | |||
| pleaseCompleteTheInformationFirst: '请先完善信息!', | |||
| submittedSuccessfully: '提交成功!', | |||
| submittedFailed: '提交失败!', | |||
| operation: '操作', | |||
| edit: '修改', | |||
| delete: '删除', | |||
| tips: '提示', | |||
| confirm: "确定", | |||
| cancel: "取消", | |||
| confirm1: "确认", | |||
| pleaseCompleteTheInformationFirst: "请先完善信息!", | |||
| submittedSuccessfully: "提交成功!", | |||
| submittedFailed: "提交失败!", | |||
| operation: "操作", | |||
| edit: "修改", | |||
| delete: "删除", | |||
| tips: "提示", | |||
| accomplishTask: '积分任务', | |||
| adminOperate: '管理员操作', | |||
| runCloudBrainTask: '运行云脑任务', | |||
| operating: '消耗中', | |||
| succeeded: '已完成', | |||
| debugTask: '调试任务', | |||
| trainTask: '训练任务', | |||
| inferenceTask: '推理任务', | |||
| benchmarkTask: '评测任务', | |||
| createPublicProject: '创建公开项目', | |||
| dailyPutforwardTasks: '每日提出任务', | |||
| dailyPR: '每日提出PR', | |||
| comment: '发表评论', | |||
| uploadDatasetFile: '上传数据集文件', | |||
| importNewModel: '导入新模型', | |||
| completeWechatCodeScanningVerification: '完成微信扫码验证', | |||
| dailyRunCloudbrainTasks: '每日运行云脑任务', | |||
| datasetRecommendedByThePlatform: '数据集被平台推荐', | |||
| submitNewPublicImage: '提交新公开镜像', | |||
| imageRecommendedByThePlatform: '镜像被平台推荐', | |||
| firstChangeofAvatar: '首次更换头像', | |||
| dailyCommit: '每日commit', | |||
| calcPointDetails: '算力积分明细', | |||
| calcPointAcquisitionInstructions: '积分获取说明', | |||
| CurrAvailableCalcPoints: '当前可用算力积分(分)', | |||
| totalGainCalcPoints: '总获取算力积分(分)', | |||
| totalConsumeCalcPoints: '总消耗算力积分(分)', | |||
| gainDetail: '获取明细', | |||
| consumeDetail: '消耗明细', | |||
| serialNumber: '流水号', | |||
| time: '时间', | |||
| scene: '场景', | |||
| behaviorOfPoint: '积分行为', | |||
| explanation: '说明', | |||
| points: '积分', | |||
| status: '状态', | |||
| runTime: '运行时长', | |||
| taskName: '任务名称', | |||
| accomplishTask: "积分任务", | |||
| adminOperate: "管理员操作", | |||
| runCloudBrainTask: "运行云脑任务", | |||
| operating: "消耗中", | |||
| succeeded: "已完成", | |||
| debugTask: "调试任务", | |||
| trainTask: "训练任务", | |||
| inferenceTask: "推理任务", | |||
| benchmarkTask: "评测任务", | |||
| createPublicProject: "创建公开项目", | |||
| dailyPutforwardTasks: "每日提出任务", | |||
| dailyPR: "每日提出PR", | |||
| comment: "发表评论", | |||
| uploadDatasetFile: "上传数据集文件", | |||
| importNewModel: "导入新模型", | |||
| completeWechatCodeScanningVerification: "完成微信扫码验证", | |||
| dailyRunCloudbrainTasks: "每日运行云脑任务", | |||
| datasetRecommendedByThePlatform: "数据集被平台推荐", | |||
| submitNewPublicImage: "提交新公开镜像", | |||
| imageRecommendedByThePlatform: "镜像被平台推荐", | |||
| firstChangeofAvatar: "首次更换头像", | |||
| dailyCommit: "每日commit", | |||
| calcPointDetails: "算力积分明细", | |||
| calcPointAcquisitionInstructions: "积分获取说明", | |||
| CurrAvailableCalcPoints: "当前可用算力积分(分)", | |||
| totalGainCalcPoints: "总获取算力积分(分)", | |||
| totalConsumeCalcPoints: "总消耗算力积分(分)", | |||
| gainDetail: "获取明细", | |||
| consumeDetail: "消耗明细", | |||
| serialNumber: "流水号", | |||
| time: "时间", | |||
| scene: "场景", | |||
| behaviorOfPoint: "积分行为", | |||
| explanation: "说明", | |||
| points: "积分", | |||
| status: "状态", | |||
| runTime: "运行时长", | |||
| taskName: "任务名称", | |||
| createdRepository: '创建了项目', | |||
| repositoryWasDel: '项目已删除', | |||
| openedIssue: '创建了任务', | |||
| createdPullRequest: '创建了合并请求', | |||
| commentedOnIssue: '评论了任务', | |||
| uploadDataset: '上传了数据集文件', | |||
| createdNewModel: '导入了新模型', | |||
| firstBindingWechatRewards: '首次绑定微信奖励', | |||
| created: '创建了', | |||
| type: '类型', | |||
| dataset: '数据集', | |||
| setAsRecommendedDataset: '被设置为推荐数据集', | |||
| committedImage: '提交了镜像', | |||
| image: '镜像', | |||
| setAsRecommendedImage: '被设置为推荐镜像', | |||
| updatedAvatar: '更新了头像', | |||
| pushedBranch: '推送了{branch}分支代码到', | |||
| deleteBranch: '从{repo}删除分支{branch}', | |||
| pushedTag: '推送了标签{tag}到', | |||
| deleteTag: '从{repo}删除了标签{tag}', | |||
| dailyMaxTips: '达到每日上限积分,不能拿满分', | |||
| memory: '内存', | |||
| sharedMemory: '共享内存', | |||
| ';': ';', | |||
| createdRepository: "创建了项目", | |||
| repositoryWasDel: "项目已删除", | |||
| openedIssue: "创建了任务", | |||
| createdPullRequest: "创建了合并请求", | |||
| commentedOnIssue: "评论了任务", | |||
| uploadDataset: "上传了数据集文件", | |||
| createdNewModel: "导入了新模型", | |||
| firstBindingWechatRewards: "首次绑定微信奖励", | |||
| created: "创建了", | |||
| type: "类型", | |||
| dataset: "数据集", | |||
| setAsRecommendedDataset: "被设置为推荐数据集", | |||
| committedImage: "提交了镜像", | |||
| image: "镜像", | |||
| setAsRecommendedImage: "被设置为推荐镜像", | |||
| updatedAvatar: "更新了头像", | |||
| pushedBranch: "推送了{branch}分支代码到", | |||
| deleteBranch: "从{repo}删除分支{branch}", | |||
| pushedTag: "推送了标签{tag}到", | |||
| deleteTag: "从{repo}删除了标签{tag}", | |||
| dailyMaxTips: "达到每日上限积分,不能拿满分", | |||
| memory: "内存", | |||
| sharedMemory: "共享内存", | |||
| ";": ";", | |||
| noPointGainRecord: '还没有积分获取记录', | |||
| noPointConsumeRecord: '还没有积分消耗记录', | |||
| noPointGainRecord: "还没有积分获取记录", | |||
| noPointConsumeRecord: "还没有积分消耗记录", | |||
| resourcesManagement: { | |||
| OpenI: '启智集群', | |||
| C2Net: '智算集群', | |||
| OpenIOne: '云脑一', | |||
| OpenITwo: '云脑二', | |||
| OpenIChengdu: '启智成都智算', | |||
| chengduCenter: '成都智算', | |||
| pclcci: '鹏城云计算所', | |||
| hefeiCenter: '合肥类脑类脑智能开放平台', | |||
| xuchangCenter: '中原人工智能计算中心', | |||
| willOnShelf: '待上架', | |||
| onShelf: '已上架', | |||
| offShelf: '已下架', | |||
| toOnShelf: '上架', | |||
| toOffShelf: '下架', | |||
| toSetPriceAndOnShelf: '定价上架', | |||
| status: '状态', | |||
| allStatus: '全部状态', | |||
| syncAiNetwork: '同步智算网络', | |||
| resQueue: '资源池(队列)', | |||
| allResQueue: '全部资源池(队列)', | |||
| addResQueue: '新建资源池(队列)', | |||
| addResQueueBtn: '新增资源池', | |||
| editResQueue: '修改资源池(队列)', | |||
| resQueueName: '资源池(队列)名称', | |||
| whichCluster: '所属集群', | |||
| allCluster: '全部集群', | |||
| aiCenter: '智算中心', | |||
| aiCenterID: '智算中心ID', | |||
| allAiCenter: '全部智算中心', | |||
| computeResource: '计算资源', | |||
| allComputeResource: '全部计算资源', | |||
| accCardType: '卡类型', | |||
| allAccCardType: '全部卡类型', | |||
| cardsTotalNum: '卡数', | |||
| accCardsNum: '卡数', | |||
| remark: '备注', | |||
| pleaseEnterRemark: '请输入备注(最大长度不超过255)', | |||
| pleaseEnterPositiveIntegerCardsTotalNum: '请输入正整数的卡数!', | |||
| addResSpecificationAndPriceInfo: '新增资源规格和单价信息', | |||
| addResSpecificationBtn: '新增资源规格', | |||
| editResSpecificationAndPriceInfo: '修改资源规格和单价信息', | |||
| resSpecificationAndPriceManagement: '资源规格单价管理', | |||
| sourceSpecCode: '对应资源编码', | |||
| sourceSpecCodeTips: '云脑II需要填写对应的资源编码', | |||
| sourceSpecId: '智算网络资源规格ID', | |||
| cpuNum: 'CPU数', | |||
| gpuMem: '显存', | |||
| mem: '内存', | |||
| shareMem: '共享内存', | |||
| unitPrice: '单价', | |||
| point_hr: '积分/时', | |||
| node: '节点', | |||
| free: '免费', | |||
| onShelfConfirm: '请确认上架该规格?', | |||
| offShelfConfirm: '请确认下架该规格?', | |||
| onShelfCode1001: '上架失败,资源池(队列)不可用。', | |||
| onShelfCode1003: '上架失败,资源规格不可用。', | |||
| offShelfDlgTip1: '当前资源规格已在以下场景中使用:', | |||
| offShelfDlgTip2: '请确认进行下架操作?', | |||
| resSceneManagement: '算力资源应用场景管理', | |||
| addResScene: '新建算力资源应用场景', | |||
| addResSceneBtn: '新增应用场景', | |||
| editResScene: '修改算力资源应用场景', | |||
| resSceneName: '应用场景名称', | |||
| jobType: '任务类型', | |||
| allJobType: '全部任务类型', | |||
| isExclusive: '是否专属', | |||
| allExclusiveAndCommonUse: '全部专属和通用', | |||
| exclusive: '专属', | |||
| commonUse: '通用', | |||
| exclusiveOrg: '专属组织', | |||
| exclusiveOrgTips: '多个组织名之间用英文分号隔开', | |||
| computeCluster: '算力集群', | |||
| resourceSpecification: '资源规格', | |||
| lastUpdateTime: '最后更新时间', | |||
| resSceneDeleteConfirm: '是否确认删除当前应用场景?', | |||
| resourceSpecificationIsAvailable: '资源规格是否可用', | |||
| resourceSpecificationIsAvailableAll: '资源规格是否可用(全部)', | |||
| available: '可用', | |||
| notAvailable: '不可用', | |||
| OpenI: "启智集群", | |||
| C2Net: "智算集群", | |||
| OpenIOne: "云脑一", | |||
| OpenITwo: "云脑二", | |||
| OpenIChengdu: "启智成都智算", | |||
| chengduCenter: "成都智算", | |||
| pclcci: "鹏城云计算所", | |||
| hefeiCenter: "合肥类脑类脑智能开放平台", | |||
| xuchangCenter: "中原人工智能计算中心", | |||
| willOnShelf: "待上架", | |||
| onShelf: "已上架", | |||
| offShelf: "已下架", | |||
| toOnShelf: "上架", | |||
| toOffShelf: "下架", | |||
| toSetPriceAndOnShelf: "定价上架", | |||
| status: "状态", | |||
| allStatus: "全部状态", | |||
| syncAiNetwork: "同步智算网络", | |||
| resQueue: "资源池(队列)", | |||
| allResQueue: "全部资源池(队列)", | |||
| addResQueue: "新建资源池(队列)", | |||
| addResQueueBtn: "新增资源池", | |||
| editResQueue: "修改资源池(队列)", | |||
| resQueueName: "资源池(队列)名称", | |||
| whichCluster: "所属集群", | |||
| allCluster: "全部集群", | |||
| aiCenter: "智算中心", | |||
| aiCenterID: "智算中心ID", | |||
| allAiCenter: "全部智算中心", | |||
| computeResource: "计算资源", | |||
| allComputeResource: "全部计算资源", | |||
| accCardType: "卡类型", | |||
| allAccCardType: "全部卡类型", | |||
| cardsTotalNum: "卡数", | |||
| accCardsNum: "卡数", | |||
| remark: "备注", | |||
| pleaseEnterRemark: "请输入备注(最大长度不超过255)", | |||
| pleaseEnterPositiveIntegerCardsTotalNum: "请输入正整数的卡数!", | |||
| addResSpecificationAndPriceInfo: "新增资源规格和单价信息", | |||
| addResSpecificationBtn: "新增资源规格", | |||
| editResSpecificationAndPriceInfo: "修改资源规格和单价信息", | |||
| resSpecificationAndPriceManagement: "资源规格单价管理", | |||
| sourceSpecCode: "对应资源编码", | |||
| sourceSpecCodeTips: "云脑II需要填写对应的资源编码", | |||
| sourceSpecId: "智算网络资源规格ID", | |||
| cpuNum: "CPU数", | |||
| gpuMem: "显存", | |||
| mem: "内存", | |||
| shareMem: "共享内存", | |||
| unitPrice: "单价", | |||
| point_hr: "积分/时", | |||
| node: "节点", | |||
| free: "免费", | |||
| onShelfConfirm: "请确认上架该规格?", | |||
| offShelfConfirm: "请确认下架该规格?", | |||
| onShelfCode1001: "上架失败,资源池(队列)不可用。", | |||
| onShelfCode1003: "上架失败,资源规格不可用。", | |||
| offShelfDlgTip1: "当前资源规格已在以下场景中使用:", | |||
| offShelfDlgTip2: "请确认进行下架操作?", | |||
| resSceneManagement: "算力资源应用场景管理", | |||
| addResScene: "新建算力资源应用场景", | |||
| addResSceneBtn: "新增应用场景", | |||
| editResScene: "修改算力资源应用场景", | |||
| resSceneName: "应用场景名称", | |||
| jobType: "任务类型", | |||
| allJobType: "全部任务类型", | |||
| isExclusive: "是否专属", | |||
| allExclusiveAndCommonUse: "全部专属和通用", | |||
| exclusive: "专属", | |||
| commonUse: "通用", | |||
| exclusiveOrg: "专属组织", | |||
| exclusiveOrgTips: "多个组织名之间用英文分号隔开", | |||
| computeCluster: "算力集群", | |||
| resourceSpecification: "资源规格", | |||
| lastUpdateTime: "最后更新时间", | |||
| resSceneDeleteConfirm: "是否确认删除当前应用场景?", | |||
| resourceSpecificationIsAvailable: "资源规格是否可用", | |||
| resourceSpecificationIsAvailableAll: "资源规格是否可用(全部)", | |||
| available: "可用", | |||
| notAvailable: "不可用", | |||
| }, | |||
| user: { | |||
| inviteFriends: '邀请好友', | |||
| inviteFriendsTips: '复制二维码或者注册邀请链接分享给好友', | |||
| clickToViewTheEventDetails: '点击查看活动详情', | |||
| copyRegistrationInvitationLink: '复制注册邀请链接', | |||
| registrationAdress: '注册地址:', | |||
| recommender: '推荐人:', | |||
| invitedFriends: '已邀请好友', | |||
| registrationTime: '注册时间', | |||
| theSharedContentHasBeenCopiedToTheClipboard: '分享内容已复制到剪切板', | |||
| copyError: '复制错误', | |||
| Activated: '已激活', | |||
| notActive: '未激活', | |||
| inviteFriends: "邀请好友", | |||
| inviteFriendsTips: "复制二维码或者注册邀请链接分享给好友", | |||
| clickToViewTheEventDetails: "点击查看活动详情", | |||
| copyRegistrationInvitationLink: "复制注册邀请链接", | |||
| registrationAdress: "注册地址:", | |||
| recommender: "推荐人:", | |||
| invitedFriends: "已邀请好友", | |||
| registrationTime: "注册时间", | |||
| theSharedContentHasBeenCopiedToTheClipboard: "分享内容已复制到剪切板", | |||
| copyError: "复制错误", | |||
| Activated: "已激活", | |||
| notActive: "未激活", | |||
| }, | |||
| tranformImageFailed: "图片脱敏失败", | |||
| originPicture: "原始图片", | |||
| desensitizationPicture: "脱敏图片", | |||
| desensitizationObject: "脱敏对象", | |||
| example: "示例", | |||
| startDesensitization: "开始处理", | |||
| all: "全部", | |||
| onlyFace: "仅人脸", | |||
| onlyLicensePlate: "仅车牌", | |||
| dragThePictureHere: "拖动图片到这里", | |||
| or: "或", | |||
| clickUpload: "点击上传", | |||
| dataDesensitizationModelExperience: "数据脱敏模型体验", | |||
| dataDesensitizationModelDesc: | |||
| "利用人工智能AI技术,把图片中的人脸、车牌号码进行脱敏处理。该模型更多信息请访问项目", | |||
| limitFilesUpload: "只能上传 jpg/jpeg/png 格式的文件", | |||
| limitSizeUpload: "上传文件大小不能超过 20M !", | |||
| notebook: { | |||
| createNewNotebook: "新建Notebook调试任务", | |||
| sameTaskTips1: "您已经有", | |||
| sameTaskTips2: "同类任务", | |||
| sameTaskTips3: "正在等待或运行中,请等待任务结束再创建", | |||
| sameTaskTips4: "可以在", | |||
| sameTaskTips5: "个人中心", | |||
| sameTaskTips6: "云脑任务", | |||
| sameTaskTips7: "查看您所有的云脑任务。", | |||
| cpuEnv: "CPU 环境", | |||
| gpuEnv: "GPU 环境", | |||
| npuEnv: "NPU 环境", | |||
| newTask: "新建任务", | |||
| noQuene: "当前资源充足,没有任务排队", | |||
| queneTips1: "当前有", | |||
| queneTips2: "个任务正在排队", | |||
| watiResource: "正在等待分配资源,请耐心等待", | |||
| debug: "调试", | |||
| stop: "停止", | |||
| stopping: "停止中", | |||
| notebookRunning: "调试环境已启动,单次连接 4 小时后自动关闭", | |||
| stopSuccess: "停止任务成功", | |||
| specification: "规格", | |||
| graphicMemory: "显存", | |||
| memory: "内存", | |||
| sharedMemory: "共享内存", | |||
| tips:'本次新建的调试任务会放在您名下项目openi-notebook中,如果没有该项目系统会自动新建一个。' | |||
| }, | |||
| tranformImageFailed:'图片脱敏失败', | |||
| originPicture:'原始图片', | |||
| desensitizationPicture:'脱敏图片', | |||
| desensitizationObject:'脱敏对象', | |||
| example:'示例', | |||
| startDesensitization:'开始处理', | |||
| all:'全部', | |||
| onlyFace:'仅人脸', | |||
| onlyLicensePlate:'仅车牌', | |||
| dragThePictureHere:'拖动图片到这里', | |||
| or:'或', | |||
| clickUpload:'点击上传', | |||
| dataDesensitizationModelExperience:'数据脱敏模型体验', | |||
| dataDesensitizationModelDesc:'利用人工智能AI技术,把图片中的人脸、车牌号码进行脱敏处理。该模型更多信息请访问项目', | |||
| limitFilesUpload:'只能上传 jpg/jpeg/png 格式的文件', | |||
| limitSizeUpload:'上传文件大小不能超过 20M !', | |||
| modelManage: { | |||
| modelManage: '模型管理', | |||
| modelName: '模型名称', | |||
| @@ -254,6 +283,8 @@ const zh = { | |||
| deleteModelFileConfirmTips: '请确认是否删除当前模型文件?', | |||
| modelFileDeleteFailed: '模型文件删除失败', | |||
| }, | |||
| } | |||
| }; | |||
| export default zh; | |||
| @@ -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'); | |||