@@ -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) | |||
@@ -79,6 +79,75 @@ func CloudBrainShow(ctx *context.APIContext) { | |||
ctx.JSON(http.StatusOK, models.BaseMessageWithDataApi{Code: 0, Message: "", Data: convert.ToCloudBrain(task)}) | |||
} | |||
func CreateFileNoteBook(ctx *context.APIContext, option api.CreateFileNotebookJobOption) { | |||
cloudbrainTask.FileNotebookCreate(ctx.Context, option) | |||
} | |||
func GetFileNoteBookInfo(ctx *context.APIContext) { | |||
//image description spec description waiting count | |||
specs, err := models.GetResourceSpecificationByIds([]int64{setting.FileNoteBook.SpecIdCPU, setting.FileNoteBook.SpecIdGPU, setting.FileNoteBook.SpecIdNPU, setting.FileNoteBook.SpecIdNPUCD}) | |||
if err != nil { | |||
log.Error("Fail to query specifications", err) | |||
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_query_fail"))) | |||
return | |||
} | |||
var specCPU, specGpu, specNPU, specNPUCD *api.SpecificationShow | |||
var specGpuQueueCode string | |||
for _, spec := range specs { | |||
if spec.ID == setting.FileNoteBook.SpecIdCPU { | |||
specCPU = convert.ToSpecification(spec) | |||
} else if spec.ID == setting.FileNoteBook.SpecIdGPU { | |||
specGpu = convert.ToSpecification(spec) | |||
specGpuQueueCode = spec.QueueCode | |||
} else if spec.ID == setting.FileNoteBook.SpecIdNPU { | |||
specNPU = convert.ToSpecification(spec) | |||
} else if spec.ID == setting.FileNoteBook.SpecIdNPUCD { | |||
specNPUCD = convert.ToSpecification(spec) | |||
} | |||
} | |||
waitCountNpu := cloudbrain.GetWaitingCloudbrainCount(models.TypeCloudBrainTwo, "") | |||
queuesMap, err := cloudbrain.GetQueuesDetail() | |||
if err != nil { | |||
log.Error("Fail to query gpu queues waiting count", err) | |||
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_query_fail"))) | |||
return | |||
} | |||
waitCountGPU := (*queuesMap)[specGpuQueueCode] | |||
if !setting.ModelartsCD.Enabled{ | |||
ctx.JSON(http.StatusOK, map[string]interface{}{ | |||
"code": 0, | |||
"projectName":setting.FileNoteBook.ProjectName, | |||
"specCpu": specCPU, | |||
"specGpu": specGpu, | |||
"specNpu": specNPU, | |||
"waitCountGpu": waitCountGPU, | |||
"waitCountNpu": waitCountNpu, | |||
"imageCpuDescription": setting.FileNoteBook.ImageCPUDescription, | |||
"imageGpuDescription": setting.FileNoteBook.ImageGPUDescription, | |||
"imageNpuDescription": setting.FileNoteBook.ImageNPUDescription, | |||
}) | |||
} else{ | |||
ctx.JSON(http.StatusOK, map[string]interface{}{ | |||
"code": 0, | |||
"projectName":setting.FileNoteBook.ProjectName, | |||
"specCpu": specCPU, | |||
"specGpu": specGpu, | |||
"specNpu": specNPUCD, | |||
"waitCountGpu": waitCountGPU, | |||
"waitCountNpu": waitCountNpu, | |||
"imageCpuDescription": setting.FileNoteBook.ImageCPUDescription, | |||
"imageGpuDescription": setting.FileNoteBook.ImageGPUDescription, | |||
"imageNpuDescription": setting.FileNoteBook.ImageNPUCDDescription, | |||
}) | |||
} | |||
} | |||
func CreateCloudBrain(ctx *context.APIContext, option api.CreateTrainJobOption) { | |||
if option.Type == cloudbrainTask.TaskTypeCloudbrainOne { | |||
@@ -141,10 +210,11 @@ func GetCloudbrainTask(ctx *context.APIContext) { | |||
) | |||
ID := ctx.Params(":id") | |||
job, err := models.GetCloudbrainByID(ID) | |||
job,err := cloudbrain.GetCloudBrainByIdOrJobId(ID) | |||
if err != nil { | |||
ctx.NotFound(err) | |||
log.Error("GetCloudbrainByID failed:", err) | |||
return | |||
} | |||
if job.JobType == string(models.JobTypeModelSafety) { | |||
@@ -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'); |