diff --git a/custom/public/css/git.openi.css b/custom/public/css/git.openi.css index 3d7e15ada..3a4eff8e4 100755 --- a/custom/public/css/git.openi.css +++ b/custom/public/css/git.openi.css @@ -72,9 +72,7 @@ z-index: 10; } .ui.secondary.c2net.segment{ - /* background: #f8faff; - border: none;*/ - margin-bottom: 5em; + padding-bottom: 3em; padding-top: 2em; color: rgba(0,0,0,.87); background-image: linear-gradient(to bottom left,var(--tw-gradient-stops)); @@ -176,7 +174,7 @@ } .homeorg, .homepro, .homemodel, .i-env{ position: relative; - padding-bottom: 5em; + padding-bottom: 3em; } .homenews::before{ content: ''; @@ -203,7 +201,7 @@ padding: 0; } .newslist{ - height: 300px; + height: 260px; overflow: hidden; } @@ -278,7 +276,7 @@ z-index: 9; } .homeorg-list .card{ - background-image: linear-gradient(#FFF, #FFF 60%, #DFF0EF) !important; + /* background-image: linear-gradient(#FFF, #FFF 60%, #DFF0EF) !important; */ box-shadow: none !important; } .homeorg-list .card .ui.small.header .content{ @@ -299,7 +297,7 @@ background-color: #FFF; box-shadow: 0px 5px 10px 0px rgba(105, 192, 255, .3); border: 1px solid rgba(105, 192, 255, .4); - min-height: 10.8em; + /* min-height: 10.8em; */ } .homepro-list .ui.card>.content>.header{ line-height: 40px !important; @@ -307,7 +305,7 @@ .homepro-list .swiper-pagination-bullet-active, .homeorg-list .swiper-pagination-bullet-active{ width: 40px; - border-radius: 4px; + border-radius: 4px; } .i-env > div{ position: relative; @@ -317,6 +315,15 @@ } @media only screen and (max-width: 767px) { + .mobile-margin-left-20 { + margin-left: 20px !important; + } + .mobile-text-align-center { + text-align: center !important; + } + .mobile-justify-content-center { + justify-content: center !important; + } .am-mt-30{ margin-top: 1.5rem !important;} .ui.secondary.hometop.segment{ margin-bottom: 5.0rem; @@ -341,7 +348,7 @@ background: #FFF; } .homeorg{ - padding-left: 3.5em; + /* padding-left: 3.5em; */ } .homeorg-tit::after { left: -2.3em; diff --git a/custom/public/img/home-banner-01-en.jpg b/custom/public/img/home-banner-01-en.jpg new file mode 100644 index 000000000..59001d4f0 Binary files /dev/null and b/custom/public/img/home-banner-01-en.jpg differ diff --git a/custom/public/img/home-banner-01.jpg b/custom/public/img/home-banner-01.jpg new file mode 100644 index 000000000..1aa0563c4 Binary files /dev/null and b/custom/public/img/home-banner-01.jpg differ diff --git a/custom/public/img/home-bg-ps.png b/custom/public/img/home-bg-ps.png new file mode 100644 index 000000000..27e6ae1a1 Binary files /dev/null and b/custom/public/img/home-bg-ps.png differ diff --git a/custom/public/img/logo-footer.svg b/custom/public/img/logo-footer.svg new file mode 100644 index 000000000..3be0e5b18 --- /dev/null +++ b/custom/public/img/logo-footer.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/custom/public/img/logo-w.svg b/custom/public/img/logo-w.svg index 867acc1e2..133f63d23 100644 --- a/custom/public/img/logo-w.svg +++ b/custom/public/img/logo-w.svg @@ -1 +1,45 @@ -logo-w \ No newline at end of file + + + + + + + + + + + + + + + diff --git a/models/dataset.go b/models/dataset.go index 972503641..4c1dc24db 100755 --- a/models/dataset.go +++ b/models/dataset.go @@ -122,22 +122,22 @@ func (datasets DatasetList) loadAttachmentAttributes(opts *SearchDatasetOptions) for i := range datasets { if attachment.DatasetID == datasets[i].ID { - if !attachment.IsPrivate{ + if !attachment.IsPrivate { datasets[i].Attachments = append(datasets[i].Attachments, attachment) - }else{ + } else { permission, ok := permissionMap[datasets[i].ID] if !ok { permission = false datasets[i].Repo.GetOwner() if !permission { - if datasets[i].Repo.OwnerID==opts.User.ID{ + if datasets[i].Repo.OwnerID == opts.User.ID { permission = true - }else{ + } else { isCollaborator, _ := datasets[i].Repo.IsCollaborator(opts.User.ID) - isInRepoTeam,_:=datasets[i].Repo.IsInRepoTeam(opts.User.ID) + isInRepoTeam, _ := datasets[i].Repo.IsInRepoTeam(opts.User.ID) - if isCollaborator ||isInRepoTeam { + if isCollaborator || isInRepoTeam { permission = true } } @@ -603,3 +603,11 @@ func UpdateDatasetCreateUser(ID int64, user *User) error { } return nil } + +func QueryDatasetGroupByTask() ([]map[string]interface{}, error) { + rows, err := x.QueryInterface("SELECT count(*) as total,task FROM public.dataset where task <>'' group by task order by total desc limit 7") + if err != nil { + return nil, err + } + return rows, nil +} diff --git a/models/repo_activity_custom.go b/models/repo_activity_custom.go index b6fffca0e..26b2ea14f 100644 --- a/models/repo_activity_custom.go +++ b/models/repo_activity_custom.go @@ -263,7 +263,11 @@ func GetAllUserKPIStats(startTime time.Time, endTime time.Time) (map[string]*git log.Warn("get user kpi status err:"+repository.RepoPath(), err1.Error()) continue } - + // if repository.Name == "yolov5" { + // log.Info("repoName=" + repository.Name + " owner=" + repository.RepoPath()) + // authorsOneRepoJson, _ := json.Marshal(authorsOneRepo) + // log.Info("authorsOneRepoJson=" + string(authorsOneRepoJson)) + // } for key, value := range authorsOneRepo { if _, ok := authors[key]; !ok { authors[key] = &git.UserKPIStats{ diff --git a/models/resource_specification.go b/models/resource_specification.go index 7a11edd05..2f815818b 100644 --- a/models/resource_specification.go +++ b/models/resource_specification.go @@ -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 { diff --git a/models/user.go b/models/user.go index b21858e37..c421455bc 100755 --- a/models/user.go +++ b/models/user.go @@ -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. diff --git a/modules/cloudbrain/cloudbrain.go b/modules/cloudbrain/cloudbrain.go index 8d4e57670..f8a6525d2 100755 --- a/modules/cloudbrain/cloudbrain.go +++ b/modules/cloudbrain/cloudbrain.go @@ -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 +} diff --git a/modules/modelarts/modelarts.go b/modules/modelarts/modelarts.go index dd502dfd0..b59be307b 100755 --- a/modules/modelarts/modelarts.go +++ b/modules/modelarts/modelarts.go @@ -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 { diff --git a/modules/modelarts/resty.go b/modules/modelarts/resty.go index c38300606..3ccba9011 100755 --- a/modules/modelarts/resty.go +++ b/modules/modelarts/resty.go @@ -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) diff --git a/modules/modelarts_cd/modelarts.go b/modules/modelarts_cd/modelarts.go index 330b048ca..93032fa89 100755 --- a/modules/modelarts_cd/modelarts.go +++ b/modules/modelarts_cd/modelarts.go @@ -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 -} - -*/ diff --git a/modules/setting/setting.go b/modules/setting/setting.go index e237d5a8a..374e6a99d 100755 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -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() diff --git a/modules/structs/cloudbrain.go b/modules/structs/cloudbrain.go index 866c85dad..9ea5601c9 100644 --- a/modules/structs/cloudbrain.go +++ b/modules/structs/cloudbrain.go @@ -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"` diff --git a/modules/templates/helper.go b/modules/templates/helper.go index 3e424454b..c314127f1 100755 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -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", "
", -1)) }, diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 908bd37b1..b6c9ee404 100755 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -24,6 +24,7 @@ enable_javascript = This website works better with JavaScript. toc = Table of Contents return=Back OpenI calculation_points = Calculation Points +notice_announcement = Notice Announcement username = Username email = Email Address @@ -236,7 +237,7 @@ page_title=Explore Better AI page_small_title=OpenI AI Development Cooperation Platform page_description=The one-stop collaborative development environment for AI field provides AI development pipeline integrating code development, data management, model debugging, reasoning and evaluation page_use=Use Now -page_only_dynamic=Only show the dynamics of open source projects +page_only_dynamic=The dynamics of open source projects page_recommend_org=Recommended Organizations page_recommend_org_desc=These excellent organizations are using the OpenI AI Collaboration Platform for collaborative development of projects. To show your organization here, page_recommend_org_commit=Click here to submit. @@ -283,6 +284,7 @@ search_ge= wecome_AI_plt = Welcome to OpenI AI Collaboration Platform! explore_AI = Explore better AI, come here to find more interesting datasets = Datasets +datasets_descr = Open source dataset base, seamlessly integrated with your project. View all repositories = Repositories use_plt__fuction = To use the AI collaboration functions provided by this platform, such as: hosting code, sharing data, debugging algorithms or training models, start with provide_resoure = Computing resources of CPU/GPU/NPU are provided freely for various types of AI tasks. @@ -290,6 +292,12 @@ activity = Activity no_events = There are no events related or_t = or powerdby=Powered_by Pengcheng CloudBrain、China Computing NET(C²NET)、 +experience_officer=Experience Officer +openi_experience_officer_plan=OpenI AI experience officer growth plan +more_benefits=, More benefits +org_see=See +more_notice=More notices +vedio_detail=Video details [explore] repos = Repositories @@ -1012,6 +1020,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 @@ -1046,6 +1056,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 @@ -1225,7 +1239,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 equivalent task 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 Home > Cloudbrain Task . +cloudbrain.morethanonejob2=You can view all your Cloud Brain tasks in Home > Cloudbrain Task . modelarts.infer_job_model = Model modelarts.infer_job_model_file = Model File @@ -1445,6 +1459,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 @@ -3198,6 +3213,9 @@ foot.copyright= Copyright: New Generation Artificial Intelligence Open Source Op Platform_Tutorial = Tutorial foot.advice_feedback = Feedback resource_description = Resource Note +foot.openi_subscription_number = OpenI subscription number +foot.user_communication_group = User communication group + [cloudbrain] all_resource_cluster=All Cluster all_ai_center=All Computing NET diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 889a9298a..495063a1f 100755 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -24,6 +24,7 @@ enable_javascript=使用 JavaScript能使本网站更好的工作。 toc=目录 return=返回OpenI calculation_points=算力积分 +notice_announcement=通知公告 username=用户名 email=电子邮件地址 @@ -238,7 +239,7 @@ page_title=探索更好的AI page_small_title=启智AI协作平台 page_description=面向AI领域的一站式协同开发环境,提供集代码开发、数据管理、模型调试、推理和评测为一体的AI开发流水线 page_use=立即使用 -page_only_dynamic=仅展示开源项目动态 +page_only_dynamic=社区开源项目动态 page_recommend_org=推荐组织 page_recommend_org_desc=这些优秀的组织正在使用启智AI开发协作平台;你的组织也想展示到这里, page_recommend_org_commit=点此提交 @@ -285,6 +286,7 @@ search_ge=个 wecome_AI_plt=欢迎来到启智AI协作平台! explore_AI = 探索更好的AI,来这里发现更有意思的 datasets = 数据集 +datasets_descr=开源数据集大本营,同你的项目无缝集成。查看所有 repositories = 项目 use_plt__fuction = 使用本平台提供的AI协作功能,如:托管代码、共享数据、调试算法或训练模型,请先 provide_resoure = 平台目前提供CPU、GPU、NPU的普惠算力资源,可进行多种类型的AI任务。 @@ -293,7 +295,12 @@ activity = 活动 no_events = 还没有与您相关的活动 or_t = 或 powerdby=Powered_by 鹏城实验室云脑、中国算力网(C²NET)、 - +experience_officer=体验官 +openi_experience_officer_plan=启智社区体验官成长计划 +more_benefits=,超多福利大放送 +org_see=。查看 +more_notice=更多通知 +vedio_detail=详细介绍视频 [explore] repos=项目 @@ -1018,6 +1025,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=默认分支 @@ -1046,6 +1055,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=新增项目 @@ -1464,6 +1476,8 @@ normal_view=普通视图 line=行 lines=行 +notebook_open = 在Notebook中打开 + editor.new_file=新建文件 editor.upload_file=上传文件 editor.edit_file=编辑文件 @@ -3216,6 +3230,8 @@ foot.copyright= 版权所有:新一代人工智能开源开放平台(OpenI Platform_Tutorial=新手指引 foot.advice_feedback = 意见反馈 resource_description = 资源说明 +foot.openi_subscription_number = 启智社区订阅号 +foot.user_communication_group = 用户交流群 [cloudbrain] all_resource_cluster=全部集群 diff --git a/public/home/home.js b/public/home/home.js index 853c3ef23..43eab17e0 100755 --- a/public/home/home.js +++ b/public/home/home.js @@ -9,16 +9,20 @@ if(isEmpty(token)){ var swiperNewMessage = new Swiper(".newslist", { direction: "vertical", - slidesPerView: 9, + slidesPerView: 6, loop: true, + spaceBetween: 8, autoplay: { delay: 2500, disableOnInteraction: false, }, }); -var swiperEvent = new Swiper(".event-list", { - slidesPerView: 3, - spaceBetween: 30, + +var swiperRepo = new Swiper(".homepro-list", { + slidesPerView: 1, + // slidesPerColumn: 2, + // slidesPerColumnFill:'row', + spaceBetween: 20, pagination: { el: ".swiper-pagination", clickable: true, @@ -27,49 +31,130 @@ var swiperEvent = new Swiper(".event-list", { delay: 2500, disableOnInteraction: false, }, + breakpoints: { + 768: { + slidesPerView: 2, + }, + 1024: { + slidesPerView: 2, + }, + 1200: { + slidesPerView: 3, + }, + 1440: { + slidesPerView: 3, + }, + 1840: { + slidesPerView: 3, + }, + 1920: { + slidesPerView: 3, + }, + }, }); -var swiperRepo = new Swiper(".homepro-list", { + +var swiperOrg = new Swiper(".homeorg-list", { slidesPerView: 1, slidesPerColumn: 2, slidesPerColumnFill:'row', - spaceBetween: 30, + spaceBetween: 25, pagination: { el: ".swiper-pagination", clickable: true, }, autoplay: { - delay: 2500, + delay: 4500, disableOnInteraction: false, }, breakpoints: { 768: { - slidesPerView: 2, + slidesPerView: 3, + slidesPerColumn: 2, }, 1024: { slidesPerView: 3, + slidesPerColumn: 2, + }, + 1200: { + slidesPerView: 4, + slidesPerColumn: 2, + }, + 1440: { + slidesPerView: 4, + slidesPerColumn: 2, + }, + 1840: { + slidesPerView: 4, + slidesPerColumn: 2, + }, + 1920: { + slidesPerView: 4, + slidesPerColumn: 2, }, }, }); -var swiperOrg = new Swiper(".homeorg-list", { +var swiperUserExp = new Swiper(".home-user-exp-list", { slidesPerView: 1, - slidesPerColumn: 4, + spaceBetween: 0, + navigation: { + nextEl: '.homeuserexp .swiper-prev', + prevEl: '.homeuserexp .swiper-next', + }, + autoplay: { + delay: 2500, + disableOnInteraction: false, + }, + breakpoints: { + 768: { + slidesPerView: 2, + }, + 1200: { + slidesPerView: 3, + }, + 1440: { + slidesPerView: 4, + }, + 1840: { + slidesPerView: 4, + }, + 1920: { + slidesPerView: 5, + }, + }, +}); + +var swiperDataset = new Swiper(".home-dataset-list", { + slidesPerView: 2, + slidesPerColumn: 1, slidesPerColumnFill:'row', - spaceBetween: 15, + spaceBetween: 30, pagination: { el: ".swiper-pagination", clickable: true, }, autoplay: { - delay: 4500, + delay: 2500, disableOnInteraction: false, }, breakpoints: { + 676: { + slidesPerView: 3, + }, 768: { - slidesPerView: 2, + slidesPerView: 4, }, - 1024: { - slidesPerView: 3, + 1320: { + slidesPerView: 5, + }, + 1520: { + slidesPerView: 6, + }, + 1720: { + slidesPerView: 7, + }, + 1920: { + slidesPerView: 7, }, }, }); @@ -85,7 +170,7 @@ document.onreadystatechange = function () { if(document.readyState != "complete"){ return; } - console.log("Start to open WebSocket." + document.readyState); + console.log("Start to open WebSocket." + document.readyState); queryRecommendData(); var output = document.getElementById("newmessage"); @@ -101,6 +186,7 @@ document.onreadystatechange = function () { }; socket.onmessage = function (e) { + if (!output) return; var data =JSON.parse(e.data) var html = ""; if (data != null){ @@ -177,18 +263,17 @@ document.onreadystatechange = function () { var time = getTime(record.CreatedUnix,currentTime); html += " " + time; } - html += ""; + html += ""; html += ""; } } output.innerHTML = html; + $('#homenews p').show(); swiperNewMessage.updateSlides(); swiperNewMessage.updateProgress(); }; } - - function getTaskLink(record){ var re = getRepoLink(record); if(record.OpType == 24){ @@ -223,7 +308,7 @@ function getMsg(record){ }else{ console.log("act user is null."); } - html += " \"\"" + html += "
\"\"" html += "
" html += " " + name + "" return html; @@ -236,6 +321,7 @@ function getRepotext(record){ return record.Repo.OwnerName + "/" + record.Repo.Name; } } + function getRepoLink(record){ return encodeURI(record.Repo.OwnerName + "/" + record.Repo.Name); @@ -437,10 +523,6 @@ function getAction(opType,isZh){ } } - - - - function queryRecommendData(){ $.ajax({ type:"GET", @@ -453,7 +535,10 @@ function queryRecommendData(){ success:function(json){ displayOrg(json.org); displayRepo(json.repo); - displayActivity(json.image); + displayActivity(json.activity); + displayDataset(json.dataset); + displayUserExp(json.user_experience); + LetterAvatar && LetterAvatar.transform(); }, error:function(response) { } @@ -463,49 +548,99 @@ function queryRecommendData(){ function displayActivity(json){ var activityDiv = document.getElementById("recommendactivity"); + if (!activityDiv) return; var html = ""; if (json != null && json.length > 0){ for(var i = 0; i < json.length;i++){ - var record = json[i] - html += "
"; - html += ""; - html += "
" + var record = json[i]; + var name = isZh ? (record["name"] || '') : (record["name_en"] || record["name"]); + html += "
"; } + var swiperEvent = new Swiper(".event-list", { + slidesPerView: 1, + spaceBetween: 30, + // pagination: { + // el: ".swiper-pagination", + // clickable: true, + // }, + autoplay: { + delay: 2500, + disableOnInteraction: false, + }, + breakpoints: { + 768: { + slidesPerView: Math.min(2, json.length), + }, + 1024: { + slidesPerView: Math.min(3, json.length), + }, + 1200: { + slidesPerView: Math.min(3, json.length), + }, + 1440: { + slidesPerView: Math.min(4, json.length), + }, + 1840: { + slidesPerView: Math.min(4, json.length), + }, + 1920: { + slidesPerView: Math.min(4, json.length), + }, + }, + }); + activityDiv.innerHTML = html; + swiperEvent.updateSlides(); + swiperEvent.updateProgress(); } - activityDiv.innerHTML = html; - swiperEvent.updateSlides(); - swiperEvent.updateProgress(); } function displayRepo(json){ var orgRepo = document.getElementById("recommendrepo"); var html = ""; if (json != null && json.length > 0){ - for(var i = 0; i < json.length;i++){ - var record = json[i] - html += "
"; - html += "
"; - html += "
"; - html += " "; - html += " " + record["NumStars"] + "" + record["NumForks"]; - html += " "; - html += " "; - html += " " + record["Alias"] +""; - html += "
" + record["Description"] + "
"; - html += "
" - if(record["Topics"] != null){ - for(var j = 0; j < record["Topics"].length; j++){ - topic = record["Topics"][j]; - url = "/explore/repos?q=" + (topic) + "&topic=" - html += "" + topic + ""; - } + var repoMap = {}; + for (var i = 0, iLen = json.length; i < iLen; i++) { + var repo = json[i]; + var labelSearch = repo.Label; + var label = isZh ? repo.Label : repo.Label_en; + if (repoMap[label]) { + repoMap[label].push(repo); + } else { + repoMap[label] = [repo]; } - html += "
"; - html += "
"; - html += "
"; - html += "
"; + } + + for (var label in repoMap) { + var repos = repoMap[label]; + html += `
# ${label}
`; + for (var i = 0, iLen = repos.length; i < iLen; i++) { + if (i >= 4) break; + var repo = repos[i]; + // ${repo["NumStars"]}${repo["NumForks"]}
+ html += `
+
+ ${repo["Avatar"] ? `` : ``} + ${repo["Alias"]} +
${repo["Description"]}
+ `; + // if (repo["Topics"] != null) { + // for(var j = 0; j < repo["Topics"].length; j++){ + // var topic = repo["Topics"][j]; + // var url = "/explore/repos?q=" + (topic) + "&topic=" + // html += `${topic}`; + // } + // } + html += ` +
+
`; + } + html += '
' } } orgRepo.innerHTML = html; @@ -513,7 +648,6 @@ function displayRepo(json){ swiperRepo.updateProgress(); } - function getRepoOrOrg(key,isZhLang,numbers=1){ if(numbers > 1){ key+="1"; @@ -537,7 +671,7 @@ function displayOrg(json){ html += " "; html += "
"; html += " " + record["Name"] + " " + record["FullName"]; - html += "
" + record["NumRepos"] +" " + getRepoOrOrg(1,isZh,record["NumRepos"]) + " ・ " + record["NumMembers"] +" " + getRepoOrOrg(2,isZh,record["NumMembers"]) + " ・ " + record["NumTeams"] + " " + getRepoOrOrg(3,isZh,record["NumTeams"]) + "
"; + html += "
" + record["NumRepos"] +" " + getRepoOrOrg(1,isZh,record["NumRepos"]) + " ・ " + record["NumMembers"] +" " + getRepoOrOrg(2,isZh,record["NumMembers"]) + " ・ " + record["NumTeams"] + " " + getRepoOrOrg(3,isZh,record["NumTeams"]) + "
"; html += "
"; html += "
"; html += "
"; @@ -548,3 +682,173 @@ function displayOrg(json){ orgDiv.innerHTML = html; swiperOrg.updateSlides(); } + +function displayDataset(data) { + var homeDatasetEl = document.getElementById("home_dataset"); + if (!homeDatasetEl) return; + var html = ''; + var svgStrMap = { + '0': '', + '1': '', + '2': '', + '3': '', + '4': '', + '5': '', + '6': '', + } + for (var i = 0, iLen = data.length; i < iLen; i++) { + var dataI = data[i]; + html += `
+ +
+
+ ${svgStrMap[i % 7]} +
+
${i18n[dataI.task] || dataI.task}
+
${i18n['about']} ${dataI.total} ${i18n['count']}
+
+
+
` + } + homeDatasetEl.innerHTML = html; + swiperDataset.updateSlides(); + swiperDataset.updateProgress(); +} + +function displayUserExp(data) { + var homeUserExpEl = document.getElementById("home_user-exp"); + if (!homeUserExpEl) return; + var html = ''; + for (var i = 0, iLen = data.length; i < iLen; i++) { + var dataI = data[i]; + html += `
+
+
+ +
+
+
+
${dataI.fullname || dataI.name}
+
${dataI.desc}
+
+
` + } + homeUserExpEl.innerHTML = html; + swiperUserExp.updateSlides(); + swiperUserExp.updateProgress(); +} + +function getNotice() { + $.ajax({ + type:"GET", + url:"/dashboard/invitation", + headers: { authorization:token, }, + dataType:"json", + data: { + filename: 'notice/notice.json', + }, + success:function(json){ + if (json) { + try { + var noticeList = JSON.parse(json).Notices || []; + var noticeEls = $('._hm-recommend-info-area-1 a._hm-notice'); + for (var i = 0, iLen = noticeEls.length; i < iLen; i++) { + var noticeEl = noticeEls.eq(i); + var noticeObj = noticeList[i]; + if (noticeObj) { + var title = isZh ? noticeObj.Title : (noticeObj.Title_en || noticeObj.Title); + noticeEl.attr('href', noticeObj.Link); + noticeEl.find('span').text(title).attr('title', title); + noticeEl.show(); + } else { + noticeEl.hide(); + } + } + } catch (e) { + console.info(e); + } + } + }, + error:function(response) { + } + }); +} + +function getRecommendModule() { + $.ajax({ + type:"GET", + url:"/dashboard/invitation", + headers: { authorization:token, }, + dataType:"json", + data: { + filename: 'home/newfunction', + }, + success:function(json){ + if (json) { + try { + var recommendModuleList = JSON.parse(json) || []; + var recommendModuleEls = $('._hm-recommend-info-area a._hm-link'); + for (var i = 0, iLen = recommendModuleEls.length; i < iLen; i++) { + var recommendModuleEl = recommendModuleEls.eq(i); + var recommendModuleObj = recommendModuleList[i]; + if (recommendModuleObj) { + recommendModuleEl.attr('href', recommendModuleObj.image_link); + recommendModuleEl.text(isZh ? recommendModuleObj.name : (recommendModuleObj.name_en || recommendModuleObj.name)); + } else { + } + } + } catch (e) { + console.info(e); + } + } + }, + error:function(response) { + } + }); +} + +function initHomeTopBanner() { + var homeSlideTimer = null; + var homeSlideDuration = 8000; + function homeSlide(direction) { + var slidePages = $('._hm-pg-c ._hm-pg'); + var currentPage = slidePages.filter('._hm-pg-show'); + var currentIndex = currentPage.index(); + var next = direction == 'left' ? currentIndex - 1 : currentIndex + 1; + if (next < 0) next = slidePages.length - 1; + if (next == slidePages.length) next = 0; + slidePages.removeClass('_hm-pg-show'); + slidePages.eq(next).addClass('_hm-pg-show'); + } + + function startSlide() { + homeSlideTimer && clearTimeout(homeSlideTimer); + homeSlideTimer = setTimeout(function() { + homeSlide('right'); + startSlide(); + }, homeSlideDuration); + } + + function stopSlide() { + homeSlideTimer && clearTimeout(homeSlideTimer); + } + + $('._hm-slide-btn').on('click', function () { + if ($(this).hasClass('_hm-slide-btn-left')) { + homeSlide('left'); + } else { + homeSlide('right'); + } + startSlide(); + }); + $('._hm-pg #homenews').on('mouseenter', function() { + stopSlide(); + }).on('mouseleave', function() { + startSlide(); + }); + setTimeout(function() { startSlide(); }, 500); +} + +initHomeTopBanner(); +getNotice(); +getRecommendModule(); diff --git a/public/img/search.svg b/public/img/search.svg index ec91b07dd..a4d965f9a 100644 --- a/public/img/search.svg +++ b/public/img/search.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 2afbb9b7d..14badfdb4 100755 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -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) diff --git a/routers/api/v1/repo/cloudbrain.go b/routers/api/v1/repo/cloudbrain.go index 68baf3287..f1b2751f8 100755 --- a/routers/api/v1/repo/cloudbrain.go +++ b/routers/api/v1/repo/cloudbrain.go @@ -78,6 +78,75 @@ func CloudBrainShow(ctx *context.APIContext) { ctx.JSON(http.StatusOK, models.BaseMessageWithDataApi{Code: 0, Message: "", Data: convert.ToCloudBrain(task)}) +} +func CreateFileNoteBook(ctx *context.APIContext, option api.CreateFileNotebookJobOption) { + cloudbrainTask.FileNotebookCreate(ctx.Context, option) +} + +func GetFileNoteBookInfo(ctx *context.APIContext) { + //image description spec description waiting count + + specs, err := models.GetResourceSpecificationByIds([]int64{setting.FileNoteBook.SpecIdCPU, setting.FileNoteBook.SpecIdGPU, setting.FileNoteBook.SpecIdNPU, setting.FileNoteBook.SpecIdNPUCD}) + if err != nil { + log.Error("Fail to query specifications", err) + ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_query_fail"))) + return + } + + var specCPU, specGpu, specNPU, specNPUCD *api.SpecificationShow + var specGpuQueueCode string + for _, spec := range specs { + if spec.ID == setting.FileNoteBook.SpecIdCPU { + specCPU = convert.ToSpecification(spec) + } else if spec.ID == setting.FileNoteBook.SpecIdGPU { + specGpu = convert.ToSpecification(spec) + specGpuQueueCode = spec.QueueCode + } else if spec.ID == setting.FileNoteBook.SpecIdNPU { + specNPU = convert.ToSpecification(spec) + } else if spec.ID == setting.FileNoteBook.SpecIdNPUCD { + specNPUCD = convert.ToSpecification(spec) + } + } + + waitCountNpu := cloudbrain.GetWaitingCloudbrainCount(models.TypeCloudBrainTwo, "") + + queuesMap, err := cloudbrain.GetQueuesDetail() + if err != nil { + log.Error("Fail to query gpu queues waiting count", err) + ctx.JSON(http.StatusOK, models.BaseErrorMessageApi(ctx.Tr("repo.notebook_query_fail"))) + return + } + waitCountGPU := (*queuesMap)[specGpuQueueCode] + if !setting.ModelartsCD.Enabled{ + ctx.JSON(http.StatusOK, map[string]interface{}{ + "code": 0, + "projectName":setting.FileNoteBook.ProjectName, + "specCpu": specCPU, + "specGpu": specGpu, + "specNpu": specNPU, + "waitCountGpu": waitCountGPU, + "waitCountNpu": waitCountNpu, + "imageCpuDescription": setting.FileNoteBook.ImageCPUDescription, + "imageGpuDescription": setting.FileNoteBook.ImageGPUDescription, + "imageNpuDescription": setting.FileNoteBook.ImageNPUDescription, + + }) + } else{ + ctx.JSON(http.StatusOK, map[string]interface{}{ + "code": 0, + "projectName":setting.FileNoteBook.ProjectName, + "specCpu": specCPU, + "specGpu": specGpu, + "specNpu": specNPUCD, + "waitCountGpu": waitCountGPU, + "waitCountNpu": waitCountNpu, + "imageCpuDescription": setting.FileNoteBook.ImageCPUDescription, + "imageGpuDescription": setting.FileNoteBook.ImageGPUDescription, + "imageNpuDescription": setting.FileNoteBook.ImageNPUCDDescription, + }) + + } + } func CreateCloudBrain(ctx *context.APIContext, option api.CreateTrainJobOption) { @@ -141,10 +210,11 @@ func GetCloudbrainTask(ctx *context.APIContext) { ) ID := ctx.Params(":id") - job, err := models.GetCloudbrainByID(ID) + + job,err := cloudbrain.GetCloudBrainByIdOrJobId(ID) + if err != nil { ctx.NotFound(err) - log.Error("GetCloudbrainByID failed:", err) return } if job.JobType == string(models.JobTypeModelSafety) { @@ -566,7 +636,6 @@ func CloudbrainDownloadLogFile(ctx *context.Context) { url, err := storage.Attachments.PresignedGetURL(prefix+"/"+fileName, fileName) if err != nil { log.Error("Get minio get SignedUrl failed: %v", err.Error(), ctx.Data["msgID"]) - ctx.ServerError("Get minio get SignedUrl failed", err) return } log.Info("fileName=" + fileName) @@ -650,7 +719,7 @@ func CloudbrainGetLog(ctx *context.APIContext) { result = getLogFromModelDir(job.JobName, startLine, endLine, resultPath) if result == nil { log.Error("GetJobLog failed: %v", err, ctx.Data["MsgID"]) - ctx.ServerError(err.Error(), err) + //ctx.ServerError(err.Error(), err) return } } @@ -865,7 +934,7 @@ func CloudBrainModelConvertList(ctx *context.APIContext) { err = json.Unmarshal([]byte(dirs), &fileInfos) if err != nil { log.Error("json.Unmarshal failed:%v", err.Error(), ctx.Data["msgID"]) - ctx.ServerError("json.Unmarshal failed:", err) + //ctx.ServerError("json.Unmarshal failed:", err) return } @@ -896,7 +965,7 @@ func CloudBrainModelConvertList(ctx *context.APIContext) { models, err := storage.GetObsListObject(job.ID, "output/", parentDir, versionName) if err != nil { log.Info("get TrainJobListModel failed:", err) - ctx.ServerError("GetObsListObject:", err) + //ctx.ServerError("GetObsListObject:", err) return } @@ -941,7 +1010,7 @@ func CloudBrainModelList(ctx *context.APIContext) { err = json.Unmarshal([]byte(dirs), &fileInfos) if err != nil { log.Error("json.Unmarshal failed:%v", err.Error(), ctx.Data["msgID"]) - ctx.ServerError("json.Unmarshal failed:", err) + //ctx.ServerError("json.Unmarshal failed:", err) return } diff --git a/routers/api/v1/repo/modelarts.go b/routers/api/v1/repo/modelarts.go index e0db9eda3..127ddd835 100755 --- a/routers/api/v1/repo/modelarts.go +++ b/routers/api/v1/repo/modelarts.go @@ -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) diff --git a/routers/home.go b/routers/home.go index aab760611..092b30fe3 100755 --- a/routers/home.go +++ b/routers/home.go @@ -7,6 +7,7 @@ package routers import ( "bytes" + "encoding/json" "net/http" "strconv" "strings" @@ -672,7 +673,7 @@ func NotFound(ctx *context.Context) { } func getRecommendOrg() ([]map[string]interface{}, error) { - url := setting.RecommentRepoAddr + "organizations" + url := setting.RecommentRepoAddr + "home/organizations" result, err := repository.RecommendFromPromote(url) if err != nil { @@ -745,7 +746,7 @@ func GetMapInfo(ctx *context.Context) { } func GetRankUser(index string) ([]map[string]interface{}, error) { - url := setting.RecommentRepoAddr + "user_rank_" + index + url := setting.RecommentRepoAddr + "user_rank/user_rank_" + index result, err := repository.RecommendFromPromote(url) if err != nil { @@ -756,13 +757,25 @@ func GetRankUser(index string) ([]map[string]interface{}, error) { tmpIndex := strings.Index(userRank, " ") userName := userRank score := 0 + label := "" if tmpIndex != -1 { userName = userRank[0:tmpIndex] - tmpScore, err := strconv.Atoi(userRank[tmpIndex+1:]) - if err != nil { - log.Info("convert to int error.") + left := userRank[tmpIndex+1:] + tmpIndex1 := strings.Index(left, " ") + if tmpIndex1 != -1 { + tmpScore, err := strconv.Atoi(left[0:tmpIndex1]) + if err != nil { + log.Info("convert to int error.") + } + score = tmpScore + label = left[tmpIndex1+1:] + } else { + tmpScore, err := strconv.Atoi(left[tmpIndex+1:]) + if err != nil { + log.Info("convert to int error.") + } + score = tmpScore } - score = tmpScore } user, err := models.GetUserByName(userName) if err == nil { @@ -772,6 +785,7 @@ func GetRankUser(index string) ([]map[string]interface{}, error) { userMap["FullName"] = user.FullName userMap["HomeLink"] = user.HomeLink() userMap["ID"] = user.ID + userMap["Label"] = label userMap["Avatar"] = user.RelAvatarLink() userMap["Score"] = score resultOrg = append(resultOrg, userMap) @@ -792,25 +806,54 @@ func GetUserRankFromPromote(ctx *context.Context) { ctx.JSON(200, resultUserRank) } +func getMapContent(fileName string) []map[string]string { + url := setting.RecommentRepoAddr + fileName + result, err := repository.RecommendContentFromPromote(url) + remap := make([]map[string]string, 0) + if err == nil { + json.Unmarshal([]byte(result), &remap) + } + return remap +} + +func HomeNoticeTmpl(ctx *context.Context) { + ctx.Data["url_params"] = "" + ctx.HTML(200, "notice") +} + func RecommendHomeInfo(ctx *context.Context) { resultOrg, err := getRecommendOrg() if err != nil { log.Info("error." + err.Error()) } - resultRepo, err := repository.GetRecommendRepoFromPromote("projects") + repoMap := getMapContent("home/projects") + resultRepo, err := repository.GetRecommendRepoFromPromote(repoMap) if err != nil { log.Info("error." + err.Error()) } - resultImage, err := getImageInfo("picture_info") - if err != nil { - log.Info("error." + err.Error()) - } - + resultActivityInfo := getMapContent("home/activity_info") mapInterface := make(map[string]interface{}) mapInterface["org"] = resultOrg mapInterface["repo"] = resultRepo - mapInterface["image"] = resultImage - //mapInterface["cloudbrain"] = resultCloudBrain + mapInterface["activity"] = resultActivityInfo + + user_experience := getMapContent("home/user_experience") + for _, amap := range user_experience { + userId := amap["userid"] + userIntId, _ := strconv.Atoi(userId) + user, err := models.GetUserByID(int64(userIntId)) + if err == nil { + amap["name"] = user.Name + amap["fullname"] = user.FullName + amap["detail"] = user.Description + amap["avatar"] = user.AvatarLink() + } + } + mapInterface["user_experience"] = user_experience + dataset, err := models.QueryDatasetGroupByTask() + if err == nil { + mapInterface["dataset"] = dataset + } ctx.JSON(http.StatusOK, mapInterface) } @@ -824,4 +867,4 @@ func HomePrivacy(ctx *context.Context) { func HomeResoruceDesc(ctx *context.Context) { ctx.HTML(200, tplResoruceDesc) -} \ No newline at end of file +} diff --git a/routers/repo/ai_model_convert.go b/routers/repo/ai_model_convert.go index 962c76aae..405f0ef89 100644 --- a/routers/repo/ai_model_convert.go +++ b/routers/repo/ai_model_convert.go @@ -828,5 +828,4 @@ func ModelConvertDownloadModel(ctx *context.Context) { http.Redirect(ctx.Resp, ctx.Req.Request, url, http.StatusTemporaryRedirect) } } - } diff --git a/routers/repo/aisafety.go b/routers/repo/aisafety.go index b638a486b..6176fcda5 100644 --- a/routers/repo/aisafety.go +++ b/routers/repo/aisafety.go @@ -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 diff --git a/routers/repo/cloudbrain.go b/routers/repo/cloudbrain.go index 7d96d1b58..d3d76f440 100755 --- a/routers/repo/cloudbrain.go +++ b/routers/repo/cloudbrain.go @@ -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) { diff --git a/routers/repo/grampus.go b/routers/repo/grampus.go index de7bb454d..8f3182758 100755 --- a/routers/repo/grampus.go +++ b/routers/repo/grampus.go @@ -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 diff --git a/routers/repo/modelarts.go b/routers/repo/modelarts.go index fabf7e555..b992734e4 100755 --- a/routers/repo/modelarts.go +++ b/routers/repo/modelarts.go @@ -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) diff --git a/routers/routes/routes.go b/routers/routes/routes.go index a988e4849..60f0365ea 100755 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -359,6 +359,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Get("/all/dosearch/", routers.SearchApi) m.Post("/user/login/kanban", user.SignInPostAPI) m.Get("/home/term", routers.HomeTerm) + m.Get("/home/notice", routers.HomeNoticeTmpl) m.Get("/home/privacy", routers.HomePrivacy) m.Get("/extension/tuomin/upload", modelapp.ProcessImageUI) m.Post("/extension/tuomin/upload", reqSignIn, modelapp.ProcessImage) @@ -1273,8 +1274,8 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/modelsafety", func() { m.Group("/:id", func() { - m.Get("/show", reqRepoCloudBrainWriter, repo.GetAiSafetyTaskTmpl) - m.Get("", reqRepoCloudBrainWriter, repo.GetAiSafetyTask) + m.Get("/show", reqRepoCloudBrainReader, repo.GetAiSafetyTaskTmpl) + m.Get("", reqRepoCloudBrainReader, repo.GetAiSafetyTask) m.Post("/stop", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.StopAiSafetyTask) m.Post("/del", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.DelAiSafetyTask) }) diff --git a/services/cloudbrain/cloudbrainTask/notebook.go b/services/cloudbrain/cloudbrainTask/notebook.go new file mode 100644 index 000000000..6b2fcf707 --- /dev/null +++ b/services/cloudbrain/cloudbrainTask/notebook.go @@ -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 +} diff --git a/services/cloudbrain/cloudbrainTask/sync_status.go b/services/cloudbrain/cloudbrainTask/sync_status.go index 67dc4d3b7..973b9bbc2 100644 --- a/services/cloudbrain/cloudbrainTask/sync_status.go +++ b/services/cloudbrain/cloudbrainTask/sync_status.go @@ -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 diff --git a/services/cloudbrain/cloudbrainTask/train.go b/services/cloudbrain/cloudbrainTask/train.go index 8e4673d66..00d01a7ce 100644 --- a/services/cloudbrain/cloudbrainTask/train.go +++ b/services/cloudbrain/cloudbrainTask/train.go @@ -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 { diff --git a/services/cloudbrain/util.go b/services/cloudbrain/util.go index dc9177ecf..0a3096e3f 100644 --- a/services/cloudbrain/util.go +++ b/services/cloudbrain/util.go @@ -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 { diff --git a/services/repository/repository.go b/services/repository/repository.go index b4c047392..db25010ea 100644 --- a/services/repository/repository.go +++ b/services/repository/repository.go @@ -107,18 +107,13 @@ func GetRecommendCourseKeyWords() ([]string, error) { } -func GetRecommendRepoFromPromote(filename string) ([]map[string]interface{}, error) { +func GetRecommendRepoFromPromote(repoMap []map[string]string) ([]map[string]interface{}, error) { resultRepo := make([]map[string]interface{}, 0) - url := setting.RecommentRepoAddr + filename - result, err := RecommendFromPromote(url) - - if err != nil { - - return resultRepo, err - } //resultRepo := make([]*models.Repository, 0) - for _, repoName := range result { + for _, record := range repoMap { + repoName := record["project_url"] + //log.Info("repoName=" + repoName + " tmpIndex1=" + fmt.Sprint(tmpIndex1) + " len(repoName)=" + fmt.Sprint(len(repoName))) tmpIndex := strings.Index(repoName, "/") if tmpIndex == -1 { log.Info("error repo name format.") @@ -131,7 +126,8 @@ func GetRecommendRepoFromPromote(filename string) ([]map[string]interface{}, err repoMap["ID"] = fmt.Sprint(repo.ID) repoMap["Name"] = repo.Name repoMap["Alias"] = repo.Alias - + repoMap["Label"] = record["class"] + repoMap["Label_en"] = record["class_en"] repoMap["OwnerName"] = repo.OwnerName repoMap["NumStars"] = repo.NumStars repoMap["NumForks"] = repo.NumForks diff --git a/templates/admin/cloudbrain/list.tmpl b/templates/admin/cloudbrain/list.tmpl index 20e704a4d..94f80c0fa 100755 --- a/templates/admin/cloudbrain/list.tmpl +++ b/templates/admin/cloudbrain/list.tmpl @@ -238,6 +238,7 @@ {{$.i18n.Tr "repo.debug"}} {{else}} + {{if not .BootFile}} {{end}} + {{end}}
{{end}} diff --git a/templates/base/footer_content.tmpl b/templates/base/footer_content.tmpl index b4c8518c4..94940e7a9 100755 --- a/templates/base/footer_content.tmpl +++ b/templates/base/footer_content.tmpl @@ -1,15 +1,17 @@ -