Reviewed-on: https://git.openi.org.cn/OpenI/aiforge/pulls/338tags/v1.21.12.1
| @@ -72,11 +72,11 @@ type CloudBrainLoginResult struct { | |||||
| type TaskRole struct { | type TaskRole struct { | ||||
| Name string `json:"name"` | Name string `json:"name"` | ||||
| TaskNumber int8 `json:"taskNumber"` | |||||
| MinSucceededTaskCount int8 `json:"minSucceededTaskCount"` | |||||
| MinFailedTaskCount int8 `json:"minFailedTaskCount"` | |||||
| CPUNumber int8 `json:"cpuNumber"` | |||||
| GPUNumber int8 `json:"gpuNumber"` | |||||
| TaskNumber int `json:"taskNumber"` | |||||
| MinSucceededTaskCount int `json:"minSucceededTaskCount"` | |||||
| MinFailedTaskCount int `json:"minFailedTaskCount"` | |||||
| CPUNumber int `json:"cpuNumber"` | |||||
| GPUNumber int `json:"gpuNumber"` | |||||
| MemoryMB int `json:"memoryMB"` | MemoryMB int `json:"memoryMB"` | ||||
| ShmMB int `json:"shmMB"` | ShmMB int `json:"shmMB"` | ||||
| Command string `json:"command"` | Command string `json:"command"` | ||||
| @@ -123,8 +123,9 @@ type GetImagesResult struct { | |||||
| } | } | ||||
| type GetImagesPayload struct { | type GetImagesPayload struct { | ||||
| Count int `json:"count"` | |||||
| ImageInfo []*ImageInfo `json:"rows"` | |||||
| Count int `json:"count"` | |||||
| TotalPages int `json:"totalPages,omitempty"` | |||||
| ImageInfo []*ImageInfo `json:"rows"` | |||||
| } | } | ||||
| type CloudbrainsOptions struct { | type CloudbrainsOptions struct { | ||||
| @@ -286,6 +287,37 @@ type GpuInfo struct { | |||||
| Queue string `json:"queue"` | Queue string `json:"queue"` | ||||
| } | } | ||||
| type ResourceSpecs struct { | |||||
| ResourceSpec []*ResourceSpec `json:"resorce_specs"` | |||||
| } | |||||
| type ResourceSpec struct { | |||||
| Id int `json:"id"` | |||||
| CpuNum int `json:"cpu"` | |||||
| GpuNum int `json:"gpu"` | |||||
| MemMiB int `json:"memMiB"` | |||||
| ShareMemMiB int `json:"shareMemMiB"` | |||||
| } | |||||
| type FlavorInfos struct { | |||||
| FlavorInfo []*FlavorInfo `json:"flavor_info"` | |||||
| } | |||||
| type FlavorInfo struct { | |||||
| Id int `json:"id"` | |||||
| Value string `json:"value"` | |||||
| } | |||||
| type PoolInfos struct { | |||||
| PoolInfo []*PoolInfo `json:"pool_info"` | |||||
| } | |||||
| type PoolInfo struct { | |||||
| PoolId string `json:"pool_id"` | |||||
| PoolName string `json:"pool_name"` | |||||
| PoolType string `json:"pool_type"` | |||||
| } | |||||
| type CommitImageParams struct { | type CommitImageParams struct { | ||||
| Ip string `json:"ip"` | Ip string `json:"ip"` | ||||
| TaskContainerId string `json:"taskContainerId"` | TaskContainerId string `json:"taskContainerId"` | ||||
| @@ -87,7 +87,7 @@ func InsertFileChunk(fileChunk *FileChunk) (_ *FileChunk, err error) { | |||||
| return fileChunk, nil | return fileChunk, nil | ||||
| } | } | ||||
| // UpdateAttachment updates the given attachment in database | |||||
| // UpdateFileChunk updates the given file_chunk in database | |||||
| func UpdateFileChunk(fileChunk *FileChunk) error { | func UpdateFileChunk(fileChunk *FileChunk) error { | ||||
| return updateFileChunk(x, fileChunk) | return updateFileChunk(x, fileChunk) | ||||
| } | } | ||||
| @@ -98,3 +98,13 @@ func updateFileChunk(e Engine, fileChunk *FileChunk) error { | |||||
| _, err := sess.Cols("is_uploaded").Update(fileChunk) | _, err := sess.Cols("is_uploaded").Update(fileChunk) | ||||
| return err | return err | ||||
| } | } | ||||
| // DeleteFileChunk delete the given file_chunk in database | |||||
| func DeleteFileChunk(fileChunk *FileChunk) error { | |||||
| return deleteFileChunk(x, fileChunk) | |||||
| } | |||||
| func deleteFileChunk(e Engine, fileChunk *FileChunk) error { | |||||
| _, err := e.ID(fileChunk.ID).Delete(fileChunk) | |||||
| return err | |||||
| } | |||||
| @@ -293,6 +293,16 @@ func MakeEmailPrimary(email *EmailAddress) error { | |||||
| } | } | ||||
| user.Email = email.Email | user.Email = email.Email | ||||
| has, err = sess.Where("id!=?", user.ID). | |||||
| And("type=?", user.Type). | |||||
| And("email=?", strings.ToLower(user.Email)). | |||||
| Get(new(User)) | |||||
| if err != nil { | |||||
| return err | |||||
| } else if has { | |||||
| return ErrEmailAlreadyUsed{user.Email} | |||||
| } | |||||
| if _, err = sess.ID(user.ID).Cols("email").Update(user); err != nil { | if _, err = sess.ID(user.ID).Cols("email").Update(user); err != nil { | ||||
| return err | return err | ||||
| } | } | ||||
| @@ -13,6 +13,7 @@ type CreateCloudBrainForm struct { | |||||
| JobType string `form:"job_type" binding:"Required"` | JobType string `form:"job_type" binding:"Required"` | ||||
| BenchmarkCategory string `form:"get_benchmark_category"` | BenchmarkCategory string `form:"get_benchmark_category"` | ||||
| GpuType string `form:"gpu_type"` | GpuType string `form:"gpu_type"` | ||||
| ResourceSpecId int `form:"resource_spec_id" binding:"Required"` | |||||
| } | } | ||||
| type CommitImageCloudBrainForm struct { | type CommitImageCloudBrainForm struct { | ||||
| @@ -23,12 +23,29 @@ const ( | |||||
| Success = "S000" | Success = "S000" | ||||
| ) | ) | ||||
| func GenerateTask(ctx *context.Context, jobName, image, command, uuid, codePath, modelPath, benchmarkPath, snn4imagenetPath, jobType, gpuQueue string) error { | |||||
| var ( | |||||
| ResourceSpecs *models.ResourceSpecs | |||||
| ) | |||||
| func GenerateTask(ctx *context.Context, jobName, image, command, uuid, codePath, modelPath, benchmarkPath, snn4imagenetPath, jobType, gpuQueue string, resourceSpecId int) error { | |||||
| dataActualPath := setting.Attachment.Minio.RealPath + | dataActualPath := setting.Attachment.Minio.RealPath + | ||||
| setting.Attachment.Minio.Bucket + "/" + | setting.Attachment.Minio.Bucket + "/" + | ||||
| setting.Attachment.Minio.BasePath + | setting.Attachment.Minio.BasePath + | ||||
| models.AttachmentRelativePath(uuid) + | models.AttachmentRelativePath(uuid) + | ||||
| uuid | uuid | ||||
| var resourceSpec *models.ResourceSpec | |||||
| for _, spec := range ResourceSpecs.ResourceSpec { | |||||
| if resourceSpecId == spec.Id { | |||||
| resourceSpec = spec | |||||
| } | |||||
| } | |||||
| if resourceSpec == nil { | |||||
| log.Error("no such resourceSpecId(%d)", resourceSpecId, ctx.Data["MsgID"]) | |||||
| return errors.New("no such resourceSpec") | |||||
| } | |||||
| jobResult, err := CreateJob(jobName, models.CreateJobParams{ | jobResult, err := CreateJob(jobName, models.CreateJobParams{ | ||||
| JobName: jobName, | JobName: jobName, | ||||
| RetryCount: 1, | RetryCount: 1, | ||||
| @@ -40,10 +57,10 @@ func GenerateTask(ctx *context.Context, jobName, image, command, uuid, codePath, | |||||
| TaskNumber: 1, | TaskNumber: 1, | ||||
| MinSucceededTaskCount: 1, | MinSucceededTaskCount: 1, | ||||
| MinFailedTaskCount: 1, | MinFailedTaskCount: 1, | ||||
| CPUNumber: 2, | |||||
| GPUNumber: 1, | |||||
| MemoryMB: 16384, | |||||
| ShmMB: 8192, | |||||
| CPUNumber: resourceSpec.CpuNum, | |||||
| GPUNumber: resourceSpec.GpuNum, | |||||
| MemoryMB: resourceSpec.MemMiB, | |||||
| ShmMB: resourceSpec.ShareMemMiB, | |||||
| Command: command, | Command: command, | ||||
| NeedIBDevice: false, | NeedIBDevice: false, | ||||
| IsMainRole: false, | IsMainRole: false, | ||||
| @@ -1,9 +1,11 @@ | |||||
| package cloudbrain | package cloudbrain | ||||
| import ( | import ( | ||||
| "code.gitea.io/gitea/modules/log" | |||||
| "encoding/json" | "encoding/json" | ||||
| "fmt" | "fmt" | ||||
| "strings" | |||||
| "code.gitea.io/gitea/modules/log" | |||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| "code.gitea.io/gitea/modules/setting" | "code.gitea.io/gitea/modules/setting" | ||||
| @@ -11,13 +13,16 @@ import ( | |||||
| ) | ) | ||||
| var ( | var ( | ||||
| restyClient *resty.Client | |||||
| HOST string | |||||
| TOKEN string | |||||
| restyClient *resty.Client | |||||
| HOST string | |||||
| TOKEN string | |||||
| ImagesUrlMap = map[string]string{Public: "/rest-server/api/v1/image/public/list/", Custom: "/rest-server/api/v1/image/list/"} | |||||
| ) | ) | ||||
| const ( | const ( | ||||
| JobHasBeenStopped = "S410" | JobHasBeenStopped = "S410" | ||||
| Public = "public" | |||||
| Custom = "custom" | |||||
| ) | ) | ||||
| func getRestyClient() *resty.Client { | func getRestyClient() *resty.Client { | ||||
| @@ -77,6 +82,12 @@ sendjob: | |||||
| Post(HOST + "/rest-server/api/v1/jobs/") | Post(HOST + "/rest-server/api/v1/jobs/") | ||||
| if err != nil { | if err != nil { | ||||
| if res != nil { | |||||
| var response models.CloudBrainResult | |||||
| json.Unmarshal(res.Body(), &response) | |||||
| log.Error("code(%s), msg(%s)", response.Code, response.Msg) | |||||
| return nil, fmt.Errorf(response.Msg) | |||||
| } | |||||
| return nil, fmt.Errorf("resty create job: %s", err) | return nil, fmt.Errorf("resty create job: %s", err) | ||||
| } | } | ||||
| @@ -126,6 +137,16 @@ sendjob: | |||||
| } | } | ||||
| func GetImages() (*models.GetImagesResult, error) { | func GetImages() (*models.GetImagesResult, error) { | ||||
| return GetImagesPageable(1, 100, Custom, "") | |||||
| } | |||||
| func GetPublicImages() (*models.GetImagesResult, error) { | |||||
| return GetImagesPageable(1, 100, Public, "") | |||||
| } | |||||
| func GetImagesPageable(page int, size int, imageType string, name string) (*models.GetImagesResult, error) { | |||||
| checkSetting() | checkSetting() | ||||
| client := getRestyClient() | client := getRestyClient() | ||||
| var getImagesResult models.GetImagesResult | var getImagesResult models.GetImagesResult | ||||
| @@ -136,9 +157,9 @@ sendjob: | |||||
| res, err := client.R(). | res, err := client.R(). | ||||
| SetHeader("Content-Type", "application/json"). | SetHeader("Content-Type", "application/json"). | ||||
| SetAuthToken(TOKEN). | SetAuthToken(TOKEN). | ||||
| SetQueryString("pageIndex=1&pageSize=100"). | |||||
| SetQueryString(getQueryString(page, size, name)). | |||||
| SetResult(&getImagesResult). | SetResult(&getImagesResult). | ||||
| Get(HOST + "/rest-server/api/v1/image/list/") | |||||
| Get(HOST + ImagesUrlMap[imageType]) | |||||
| if err != nil { | if err != nil { | ||||
| return nil, fmt.Errorf("resty GetImages: %v", err) | return nil, fmt.Errorf("resty GetImages: %v", err) | ||||
| @@ -157,48 +178,30 @@ sendjob: | |||||
| goto sendjob | goto sendjob | ||||
| } | } | ||||
| if len(response.Code) != 0 { | |||||
| log.Error("getImagesResult failed(%s): %s", response.Code, response.Msg) | |||||
| return &getImagesResult, fmt.Errorf("getImagesResult failed(%s): %s", response.Code, response.Msg) | |||||
| } | |||||
| if getImagesResult.Code != Success { | if getImagesResult.Code != Success { | ||||
| return &getImagesResult, fmt.Errorf("getImagesResult err: %s", res.String()) | return &getImagesResult, fmt.Errorf("getImagesResult err: %s", res.String()) | ||||
| } | } | ||||
| getImagesResult.Payload.TotalPages = getTotalPages(getImagesResult, size) | |||||
| return &getImagesResult, nil | return &getImagesResult, nil | ||||
| } | } | ||||
| func GetPublicImages() (*models.GetImagesResult, error) { | |||||
| checkSetting() | |||||
| client := getRestyClient() | |||||
| var getImagesResult models.GetImagesResult | |||||
| retry := 0 | |||||
| sendjob: | |||||
| res, err := client.R(). | |||||
| SetHeader("Content-Type", "application/json"). | |||||
| SetAuthToken(TOKEN). | |||||
| SetQueryString("pageIndex=1&pageSize=100"). | |||||
| SetResult(&getImagesResult). | |||||
| Get(HOST + "/rest-server/api/v1/image/public/list/") | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("resty GetPublicImages: %v", err) | |||||
| } | |||||
| if getImagesResult.Code == "S401" && retry < 1 { | |||||
| retry++ | |||||
| _ = loginCloudbrain() | |||||
| goto sendjob | |||||
| func getTotalPages(getImagesResult models.GetImagesResult, size int) int { | |||||
| totalCount := getImagesResult.Payload.Count | |||||
| var totalPages int | |||||
| if totalCount%size != 0 { | |||||
| totalPages = totalCount/size + 1 | |||||
| } else { | |||||
| totalPages = totalCount / size | |||||
| } | } | ||||
| return totalPages | |||||
| } | |||||
| if getImagesResult.Code != Success { | |||||
| return &getImagesResult, fmt.Errorf("getImgesResult err: %s", res.String()) | |||||
| func getQueryString(page int, size int, name string) string { | |||||
| if strings.TrimSpace(name) == "" { | |||||
| return fmt.Sprintf("pageIndex=%d&pageSize=%d", page, size) | |||||
| } | } | ||||
| return &getImagesResult, nil | |||||
| return fmt.Sprintf("pageIndex=%d&pageSize=%d&name=%s", page, size, name) | |||||
| } | } | ||||
| func CommitImage(jobID string, params models.CommitImageParams) error { | func CommitImage(jobID string, params models.CommitImageParams) error { | ||||
| @@ -2,6 +2,7 @@ package modelarts | |||||
| import ( | import ( | ||||
| "code.gitea.io/gitea/modules/setting" | "code.gitea.io/gitea/modules/setting" | ||||
| "encoding/json" | |||||
| "path" | "path" | ||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| @@ -12,30 +13,31 @@ import ( | |||||
| const ( | const ( | ||||
| storageTypeOBS = "obs" | storageTypeOBS = "obs" | ||||
| autoStopDuration = 4 * 60 * 60 | autoStopDuration = 4 * 60 * 60 | ||||
| flavor = "modelarts.kat1.xlarge" | |||||
| //profileID = "Python3-ascend910-arm" | |||||
| profileID = "efa847c0-7359-11eb-b34f-0255ac100057" | |||||
| poolID = "pool1328035d" | |||||
| poolName = "train-private-1" | |||||
| poolType = "USER_DEFINED" | |||||
| DataSetMountPath = "/home/ma-user/work" | DataSetMountPath = "/home/ma-user/work" | ||||
| NotebookEnv = "Python3" | NotebookEnv = "Python3" | ||||
| NotebookType = "Ascend" | NotebookType = "Ascend" | ||||
| FlavorInfo = "Ascend: 1*Ascend 910 CPU: 24 核 96GiB (modelarts.kat1.xlarge)" | |||||
| ) | |||||
| var ( | |||||
| poolInfos *models.PoolInfos | |||||
| FlavorInfos *models.FlavorInfos | |||||
| ) | ) | ||||
| func GenerateTask(ctx *context.Context, jobName, uuid, description string) error { | func GenerateTask(ctx *context.Context, jobName, uuid, description string) error { | ||||
| dataActualPath := setting.Bucket + "/" + setting.BasePath + path.Join(uuid[0:1], uuid[1:2]) + "/" + uuid + "/" | dataActualPath := setting.Bucket + "/" + setting.BasePath + path.Join(uuid[0:1], uuid[1:2]) + "/" + uuid + "/" | ||||
| if poolInfos == nil { | |||||
| json.Unmarshal([]byte(setting.PoolInfos), &poolInfos) | |||||
| } | |||||
| jobResult, err := CreateJob(models.CreateNotebookParams{ | jobResult, err := CreateJob(models.CreateNotebookParams{ | ||||
| JobName: jobName, | JobName: jobName, | ||||
| Description: description, | Description: description, | ||||
| ProfileID: profileID, | |||||
| Flavor: flavor, | |||||
| ProfileID: setting.ProfileID, | |||||
| Flavor: setting.Flavor, | |||||
| Pool: models.Pool{ | Pool: models.Pool{ | ||||
| ID: poolID, | |||||
| Name: poolName, | |||||
| Type: poolType, | |||||
| ID: poolInfos.PoolInfo[0].PoolId, | |||||
| Name: poolInfos.PoolInfo[0].PoolName, | |||||
| Type: poolInfos.PoolInfo[0].PoolType, | |||||
| }, | }, | ||||
| Spec: models.Spec{ | Spec: models.Spec{ | ||||
| Storage: models.Storage{ | Storage: models.Storage{ | ||||
| @@ -439,6 +439,7 @@ var ( | |||||
| JobType string | JobType string | ||||
| GpuTypes string | GpuTypes string | ||||
| DebugServerHost string | DebugServerHost string | ||||
| ResourceSpecs string | |||||
| //benchmark config | //benchmark config | ||||
| IsBenchmarkEnabled bool | IsBenchmarkEnabled bool | ||||
| @@ -472,6 +473,10 @@ var ( | |||||
| ModelArtsUsername string | ModelArtsUsername string | ||||
| ModelArtsPassword string | ModelArtsPassword string | ||||
| ModelArtsDomain string | ModelArtsDomain string | ||||
| ProfileID string | |||||
| PoolInfos string | |||||
| Flavor string | |||||
| FlavorInfos string | |||||
| ) | ) | ||||
| // DateLang transforms standard language locale name to corresponding value in datetime plugin. | // DateLang transforms standard language locale name to corresponding value in datetime plugin. | ||||
| @@ -1147,7 +1152,8 @@ func NewContext() { | |||||
| JobPath = sec.Key("JOB_PATH").MustString("/datasets/minio/data/opendata/jobs/") | JobPath = sec.Key("JOB_PATH").MustString("/datasets/minio/data/opendata/jobs/") | ||||
| DebugServerHost = sec.Key("DEBUG_SERVER_HOST").MustString("http://192.168.202.73") | DebugServerHost = sec.Key("DEBUG_SERVER_HOST").MustString("http://192.168.202.73") | ||||
| JobType = sec.Key("GPU_TYPE_DEFAULT").MustString("openidebug") | JobType = sec.Key("GPU_TYPE_DEFAULT").MustString("openidebug") | ||||
| GpuTypes = sec.Key("GPU_TYPES").MustString("openidebug,openidgx") | |||||
| GpuTypes = sec.Key("GPU_TYPES").MustString("") | |||||
| ResourceSpecs = sec.Key("RESOURCE_SPECS").MustString("") | |||||
| sec = Cfg.Section("benchmark") | sec = Cfg.Section("benchmark") | ||||
| IsBenchmarkEnabled = sec.Key("ENABLED").MustBool(false) | IsBenchmarkEnabled = sec.Key("ENABLED").MustBool(false) | ||||
| @@ -1180,6 +1186,10 @@ func NewContext() { | |||||
| ModelArtsUsername = sec.Key("USERNAME").MustString("") | ModelArtsUsername = sec.Key("USERNAME").MustString("") | ||||
| ModelArtsPassword = sec.Key("PASSWORD").MustString("") | ModelArtsPassword = sec.Key("PASSWORD").MustString("") | ||||
| ModelArtsDomain = sec.Key("DOMAIN").MustString("cn-south-222") | ModelArtsDomain = sec.Key("DOMAIN").MustString("cn-south-222") | ||||
| ProfileID = sec.Key("PROFILE_ID").MustString("") | |||||
| PoolInfos = sec.Key("POOL_INFOS").MustString("") | |||||
| Flavor = sec.Key("FLAVOR").MustString("") | |||||
| FlavorInfos = sec.Key("FLAVOR_INFOS").MustString("") | |||||
| } | } | ||||
| func loadInternalToken(sec *ini.Section) string { | func loadInternalToken(sec *ini.Section) string { | ||||
| @@ -223,6 +223,7 @@ issues.in_your_repos = In your repositories | |||||
| repos = Repositories | repos = Repositories | ||||
| users = Users | users = Users | ||||
| organizations = Organizations | organizations = Organizations | ||||
| images = CloudImages | |||||
| search = Search | search = Search | ||||
| code = Code | code = Code | ||||
| repo_no_results = No matching repositories found. | repo_no_results = No matching repositories found. | ||||
| @@ -767,6 +768,7 @@ cloudbrain2 = cloudbrain2 | |||||
| cloudbrain_selection = select cloudbrain | cloudbrain_selection = select cloudbrain | ||||
| cloudbrain_platform_selection = Select the cloudbrain platform you want to use: | cloudbrain_platform_selection = Select the cloudbrain platform you want to use: | ||||
| confirm_choice = confirm | confirm_choice = confirm | ||||
| cloudbran1_tips = Only data in zip format can create cloudbrain tasks | |||||
| template.items = Template Items | template.items = Template Items | ||||
| template.git_content = Git Content (Default Branch) | template.git_content = Git Content (Default Branch) | ||||
| @@ -224,6 +224,7 @@ issues.in_your_repos=属于该用户项目的 | |||||
| repos=项目 | repos=项目 | ||||
| users=用户 | users=用户 | ||||
| organizations=组织 | organizations=组织 | ||||
| images = 云脑镜像 | |||||
| search=搜索 | search=搜索 | ||||
| code=代码 | code=代码 | ||||
| repo_no_results=未找到匹配的项目。 | repo_no_results=未找到匹配的项目。 | ||||
| @@ -478,7 +479,7 @@ add_new_email=添加新的邮箱地址 | |||||
| add_new_openid=添加新的 OpenID URI | add_new_openid=添加新的 OpenID URI | ||||
| add_email=增加电子邮件地址 | add_email=增加电子邮件地址 | ||||
| add_openid=添加 OpenID URI | add_openid=添加 OpenID URI | ||||
| add_email_confirmation_sent=一封新的确认邮件已经被发送至 <b>%s</b>,请检查您的收件箱并在 %s 内完成确认注册操作。 | |||||
| add_email_confirmation_sent=一封新的确认邮件已经被发送至 <b>%s</b>,请检查您的收件箱并在 %s 内完成确认操作。 | |||||
| add_email_success=新的电子邮件地址已添加。 | add_email_success=新的电子邮件地址已添加。 | ||||
| email_preference_set_success=电子邮件首选项已成功设置。 | email_preference_set_success=电子邮件首选项已成功设置。 | ||||
| add_openid_success=新的 OpenID 地址已添加。 | add_openid_success=新的 OpenID 地址已添加。 | ||||
| @@ -769,6 +770,7 @@ cloudbrain2=云脑2 | |||||
| cloudbrain_selection=云脑选择 | cloudbrain_selection=云脑选择 | ||||
| cloudbrain_platform_selection=选择您准备使用的云脑平台: | cloudbrain_platform_selection=选择您准备使用的云脑平台: | ||||
| confirm_choice=确定 | confirm_choice=确定 | ||||
| cloudbran1_tips=只有zip格式的数据集才能发起云脑任务 | |||||
| template.items=模板选项 | template.items=模板选项 | ||||
| template.git_content=Git数据(默认分支) | template.git_content=Git数据(默认分支) | ||||
| @@ -1 +1,17 @@ | |||||
| <svg clip-rule="evenodd" fill-rule="evenodd" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m83.046 35.842c10.395-.091 22.52-.636 42.972-1.719 12.367.282-4.316 74.37-26.594 76.038h-37.152c-8.48-.911-19.934-18.203-21.002-23.216-9.735-.215-34.086-2.695-35.192-29.423-.707-17.106 12.085-23.932 21.632-23.904 3.736.01 7.213.478 9.708.505 19.935 1.104 30.433 1.729 42.366 1.733l.021 21.503 3.241 1.61zm18.936 34.047-31.267-15.193-15.192 31.267 31.267 15.192zm-71.781-26.813c-12.443-.333-15.784 7.423-14.833 14.834 1.782 13.885 11.469 17.605 22.851 19.11-4.462-12.24-6.666-20.251-8.018-33.944z" transform="matrix(.128847 0 0 .128847 -.779534 -1.26275)"/><g transform="matrix(.128847 0 0 .128847 -.779534 -1.26275)"><g transform="matrix(.940904 -.338674 .338674 .940904 0 0)"><circle cx="49.065" cy="90.078" r="3.475"/><circle cx="36.81" cy="102.105" r="3.475"/><circle cx="46.484" cy="111.439" r="3.475"/></g><path d="m97.333 18.062h2.673v27.261h-2.673z" transform="matrix(.898609 .43875 -.447723 .916987 .406685 -.832937)"/><path d="m76.558 68.116c12.976 6.396 13.013 4.102 4.891 20.908" fill="none" stroke="#000" stroke-width="2.68"/></g></svg> | |||||
| <svg clip-rule="evenodd" fill-rule="evenodd" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg"> | |||||
| <path class="st0" d="M54.7,15.9L34.8,4.5c-1.7-1-3.9-1-5.6,0L9.3,15.9c-1.8,1-2.8,2.9-2.8,4.9v22.8c0,2,1.1,3.9,2.8,4.9l4.9,2.8 | |||||
| c-0.1,0.2-0.1,0.5-0.1,0.7c0,2.1,1.6,3.8,3.7,3.8s3.8-1.6,3.8-3.7c0-2.1-1.6-3.8-3.7-3.8c-0.9,0-1.8,0.3-2.5,0.9l-4.9-2.8 | |||||
| c-1-0.6-1.6-1.7-1.6-2.9V20.9c0-1.2,0.6-2.3,1.6-2.9L30.3,6.5c1-0.6,2.3-0.6,3.3,0l19.8,11.4c1,0.6,1.7,1.7,1.6,2.9v22.8 | |||||
| c0,1.2-0.6,2.3-1.6,2.9L33.7,58c-0.2,0.1-0.4,0.2-0.6,0.3v-7.1c0-0.1,0-0.2,0-0.4v-4.1h0.1l11.4-7.6c0.3-0.2,0.5-0.6,0.5-1V25.9 | |||||
| c2-0.6,3.1-2.8,2.5-4.8c-0.6-2-2.8-3.1-4.8-2.5c-2,0.6-3.1,2.8-2.5,4.8c0.4,1.2,1.3,2.1,2.5,2.5v11.4L33,43.9V20.8 | |||||
| c2-0.6,3.1-2.8,2.5-4.8c-0.6-2-2.8-3.1-4.8-2.5c-2,0.6-3.1,2.8-2.5,4.8c0.4,1.2,1.3,2.1,2.5,2.5V36l-6.4-3.7v-6.1 | |||||
| c2-0.6,3.1-2.8,2.5-4.8c-0.6-2-2.8-3.1-4.8-2.5s-3.1,2.8-2.5,4.8c0.4,1.2,1.3,2.1,2.5,2.5v6.7c0,0.4,0.2,0.8,0.6,1l8.2,4.8V49 | |||||
| l-14.1-8.2v-10c2-0.6,3-2.7,2.4-4.7s-2.7-3-4.7-2.4c-2,0.6-3,2.7-2.4,4.7c0.4,1.1,1.3,2,2.4,2.4v10.7c0,0.4,0.2,0.8,0.6,1l15.8,9.1 | |||||
| v8c0,0,0,0.1,0,0.1v0.1V60c0,0,0,0,0,0.1l0.1,0.1l0,0l0.1,0.1l0,0c0,0,0.1,0.1,0.1,0.1l0,0l0.2,0.1l0,0h0.2l0,0c0.1,0,0.1,0,0.2,0 | |||||
| h0.2c1,0,2-0.3,2.8-0.7l19.8-11.4c1.8-0.9,2.9-2.7,3.1-4.7V20.9C57.5,18.9,56.4,17,54.7,15.9 M17.9,50.8c0.8,0,1.4,0.6,1.4,1.4 | |||||
| s-0.6,1.4-1.4,1.4s-1.4-0.6-1.4-1.4l0,0C16.5,51.4,17.1,50.8,17.9,50.8 M44,21c0.8,0,1.4,0.6,1.4,1.4c0,0.8-0.6,1.4-1.4,1.4 | |||||
| c-0.8,0-1.4-0.6-1.4-1.4l0,0C42.6,21.7,43.2,21,44,21 M23.1,21.3c0.8,0,1.4,0.6,1.4,1.4s-0.6,1.4-1.4,1.4c-0.8,0-1.4-0.6-1.4-1.4 | |||||
| S22.4,21.3,23.1,21.3L23.1,21.3 M15.5,25.9c0.8,0,1.4,0.6,1.4,1.4s-0.6,1.4-1.4,1.4c-0.8,0-1.4-0.6-1.4-1.4S14.7,25.9,15.5,25.9 | |||||
| L15.5,25.9 M31.9,15.8c0.8,0,1.4,0.6,1.4,1.4s-0.6,1.4-1.4,1.4c-0.8,0-1.4-0.6-1.4-1.4C30.4,16.4,31.1,15.8,31.9,15.8 | |||||
| C31.9,15.8,31.9,15.8,31.9,15.8"/> | |||||
| </svg> | |||||
| @@ -32,7 +32,8 @@ const ( | |||||
| // tplExploreOrganizations explore organizations page template | // tplExploreOrganizations explore organizations page template | ||||
| tplExploreOrganizations base.TplName = "explore/organizations" | tplExploreOrganizations base.TplName = "explore/organizations" | ||||
| // tplExploreCode explore code page template | // tplExploreCode explore code page template | ||||
| tplExploreCode base.TplName = "explore/code" | |||||
| tplExploreCode base.TplName = "explore/code" | |||||
| tplExploreImages base.TplName = "explore/images" | |||||
| ) | ) | ||||
| // Home render home page | // Home render home page | ||||
| @@ -475,6 +476,10 @@ func ExploreCode(ctx *context.Context) { | |||||
| ctx.HTML(200, tplExploreCode) | ctx.HTML(200, tplExploreCode) | ||||
| } | } | ||||
| func ExploreImages(ctx *context.Context) { | |||||
| ctx.HTML(200, tplExploreImages) | |||||
| } | |||||
| // NotFound render 404 page | // NotFound render 404 page | ||||
| func NotFound(ctx *context.Context) { | func NotFound(ctx *context.Context) { | ||||
| ctx.Data["Title"] = "Page Not Found" | ctx.Data["Title"] = "Page Not Found" | ||||
| @@ -483,16 +483,25 @@ func GetSuccessChunks(ctx *context.Context) { | |||||
| if typeCloudBrain == models.TypeCloudBrainOne { | if typeCloudBrain == models.TypeCloudBrainOne { | ||||
| chunks, err = storage.GetPartInfos(fileChunk.UUID, fileChunk.UploadID) | chunks, err = storage.GetPartInfos(fileChunk.UUID, fileChunk.UploadID) | ||||
| if err != nil { | if err != nil { | ||||
| ctx.ServerError("GetPartInfos failed", err) | |||||
| return | |||||
| log.Error("GetPartInfos failed:%v", err.Error()) | |||||
| } | } | ||||
| } else { | } else { | ||||
| chunks, err = storage.GetObsPartInfos(fileChunk.UUID, fileChunk.UploadID) | chunks, err = storage.GetObsPartInfos(fileChunk.UUID, fileChunk.UploadID) | ||||
| if err != nil { | if err != nil { | ||||
| ctx.ServerError("GetObsPartInfos failed", err) | |||||
| return | |||||
| log.Error("GetObsPartInfos failed:%v", err.Error()) | |||||
| } | } | ||||
| } | } | ||||
| if err != nil { | |||||
| models.DeleteFileChunk(fileChunk) | |||||
| ctx.JSON(200, map[string]string{ | |||||
| "uuid": "", | |||||
| "uploaded": "0", | |||||
| "uploadID": "", | |||||
| "chunks": "", | |||||
| }) | |||||
| return | |||||
| } | |||||
| } | } | ||||
| var attachID int64 | var attachID int64 | ||||
| @@ -1,17 +1,21 @@ | |||||
| package repo | package repo | ||||
| import ( | import ( | ||||
| "code.gitea.io/gitea/modules/git" | |||||
| "code.gitea.io/gitea/modules/storage" | |||||
| "bufio" | |||||
| "encoding/json" | "encoding/json" | ||||
| "errors" | "errors" | ||||
| "io" | |||||
| "net/http" | "net/http" | ||||
| "os" | "os" | ||||
| "os/exec" | "os/exec" | ||||
| "regexp" | |||||
| "strconv" | "strconv" | ||||
| "strings" | "strings" | ||||
| "time" | "time" | ||||
| "code.gitea.io/gitea/modules/git" | |||||
| "code.gitea.io/gitea/modules/storage" | |||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| "code.gitea.io/gitea/modules/auth" | "code.gitea.io/gitea/modules/auth" | ||||
| "code.gitea.io/gitea/modules/base" | "code.gitea.io/gitea/modules/base" | ||||
| @@ -22,10 +26,10 @@ import ( | |||||
| ) | ) | ||||
| const ( | const ( | ||||
| tplCloudBrainIndex base.TplName = "repo/cloudbrain/index" | |||||
| tplCloudBrainNew base.TplName = "repo/cloudbrain/new" | |||||
| tplCloudBrainShow base.TplName = "repo/cloudbrain/show" | |||||
| tplCloudBrainShowModels base.TplName = "repo/cloudbrain/models/index" | |||||
| tplCloudBrainIndex base.TplName = "repo/cloudbrain/index" | |||||
| tplCloudBrainNew base.TplName = "repo/cloudbrain/new" | |||||
| tplCloudBrainShow base.TplName = "repo/cloudbrain/show" | |||||
| tplCloudBrainShowModels base.TplName = "repo/cloudbrain/models/index" | |||||
| ) | ) | ||||
| var ( | var ( | ||||
| @@ -86,17 +90,27 @@ func cutString(str string, lens int) string { | |||||
| return str[:lens] | return str[:lens] | ||||
| } | } | ||||
| func CloudBrainNew(ctx *context.Context) { | |||||
| ctx.Data["PageIsCloudBrain"] = true | |||||
| 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) error{ | |||||
| ctx.Data["PageIsCloudBrain"] = true | |||||
| t := time.Now() | t := time.Now() | ||||
| var jobName = cutString(ctx.User.Name, 5) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:] | |||||
| var jobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:] | |||||
| ctx.Data["job_name"] = jobName | ctx.Data["job_name"] = jobName | ||||
| result, err := cloudbrain.GetImages() | result, err := cloudbrain.GetImages() | ||||
| if err != nil { | if err != nil { | ||||
| ctx.Data["error"] = err.Error() | ctx.Data["error"] = err.Error() | ||||
| log.Error("cloudbrain.GetImages failed:", err.Error(), ctx.Data["msgID"]) | |||||
| log.Error("cloudbrain.GetImages failed:", err.Error(), ctx.Data["MsgID"]) | |||||
| } | } | ||||
| for i, payload := range result.Payload.ImageInfo { | for i, payload := range result.Payload.ImageInfo { | ||||
| @@ -112,7 +126,7 @@ func CloudBrainNew(ctx *context.Context) { | |||||
| resultPublic, err := cloudbrain.GetPublicImages() | resultPublic, err := cloudbrain.GetPublicImages() | ||||
| if err != nil { | if err != nil { | ||||
| ctx.Data["error"] = err.Error() | ctx.Data["error"] = err.Error() | ||||
| log.Error("cloudbrain.GetPublicImages failed:", err.Error(), ctx.Data["msgID"]) | |||||
| log.Error("cloudbrain.GetPublicImages failed:", err.Error(), ctx.Data["MsgID"]) | |||||
| } | } | ||||
| for i, payload := range resultPublic.Payload.ImageInfo { | for i, payload := range resultPublic.Payload.ImageInfo { | ||||
| @@ -127,8 +141,8 @@ func CloudBrainNew(ctx *context.Context) { | |||||
| attachs, err := models.GetAllUserAttachments(ctx.User.ID) | attachs, err := models.GetAllUserAttachments(ctx.User.ID) | ||||
| if err != nil { | if err != nil { | ||||
| ctx.ServerError("GetAllUserAttachments failed:", err) | |||||
| return | |||||
| log.Error("GetAllUserAttachments failed: %v", err, ctx.Data["MsgID"]) | |||||
| return err | |||||
| } | } | ||||
| ctx.Data["attachments"] = attachs | ctx.Data["attachments"] = attachs | ||||
| @@ -148,8 +162,23 @@ func CloudBrainNew(ctx *context.Context) { | |||||
| json.Unmarshal([]byte(setting.GpuTypes), &gpuInfos) | json.Unmarshal([]byte(setting.GpuTypes), &gpuInfos) | ||||
| } | } | ||||
| ctx.Data["gpu_types"] = gpuInfos.GpuInfo | ctx.Data["gpu_types"] = gpuInfos.GpuInfo | ||||
| if cloudbrain.ResourceSpecs == nil { | |||||
| json.Unmarshal([]byte(setting.ResourceSpecs), &cloudbrain.ResourceSpecs) | |||||
| } | |||||
| ctx.Data["resource_specs"] = cloudbrain.ResourceSpecs.ResourceSpec | |||||
| ctx.Data["snn4imagenet_path"] = cloudbrain.Snn4imagenetMountPath | ctx.Data["snn4imagenet_path"] = cloudbrain.Snn4imagenetMountPath | ||||
| ctx.Data["is_snn4imagenet_enabled"] = setting.IsSnn4imagenetEnabled | ctx.Data["is_snn4imagenet_enabled"] = setting.IsSnn4imagenetEnabled | ||||
| return nil | |||||
| } | |||||
| func CloudBrainNew(ctx *context.Context) { | |||||
| err := cloudBrainNewDataPrepare(ctx) | |||||
| if err != nil { | |||||
| ctx.ServerError("get new cloudbrain info failed", err) | |||||
| return | |||||
| } | |||||
| ctx.HTML(200, tplCloudBrainNew) | ctx.HTML(200, tplCloudBrainNew) | ||||
| } | } | ||||
| @@ -162,9 +191,11 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) { | |||||
| jobType := form.JobType | jobType := form.JobType | ||||
| gpuQueue := setting.JobType | gpuQueue := setting.JobType | ||||
| codePath := setting.JobPath + jobName + cloudbrain.CodeMountPath | codePath := setting.JobPath + jobName + cloudbrain.CodeMountPath | ||||
| resourceSpecId := form.ResourceSpecId | |||||
| if jobType != string(models.JobTypeBenchmark) && jobType != string(models.JobTypeDebug) && jobType != string(models.JobTypeSnn4imagenet) { | if jobType != string(models.JobTypeBenchmark) && jobType != string(models.JobTypeDebug) && jobType != string(models.JobTypeSnn4imagenet) { | ||||
| log.Error("jobtype error:", jobType, ctx.Data["msgID"]) | |||||
| log.Error("jobtype error:", jobType, ctx.Data["MsgID"]) | |||||
| cloudBrainNewDataPrepare(ctx) | |||||
| ctx.RenderWithErr("jobtype error", tplCloudBrainNew, &form) | ctx.RenderWithErr("jobtype error", tplCloudBrainNew, &form) | ||||
| return | return | ||||
| } | } | ||||
| @@ -172,11 +203,13 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) { | |||||
| _, err := models.GetCloudbrainByName(jobName) | _, err := models.GetCloudbrainByName(jobName) | ||||
| if err == nil { | if err == nil { | ||||
| log.Error("the job name did already exist", ctx.Data["MsgID"]) | log.Error("the job name did already exist", ctx.Data["MsgID"]) | ||||
| cloudBrainNewDataPrepare(ctx) | |||||
| ctx.RenderWithErr("the job name did already exist", tplCloudBrainNew, &form) | ctx.RenderWithErr("the job name did already exist", tplCloudBrainNew, &form) | ||||
| return | return | ||||
| } else { | } else { | ||||
| if !models.IsErrJobNotExist(err) { | if !models.IsErrJobNotExist(err) { | ||||
| log.Error("system error, %v", err, ctx.Data["MsgID"]) | log.Error("system error, %v", err, ctx.Data["MsgID"]) | ||||
| cloudBrainNewDataPrepare(ctx) | |||||
| ctx.RenderWithErr("system error", tplCloudBrainNew, &form) | ctx.RenderWithErr("system error", tplCloudBrainNew, &form) | ||||
| return | return | ||||
| } | } | ||||
| @@ -187,6 +220,7 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) { | |||||
| modelPath := setting.JobPath + jobName + cloudbrain.ModelMountPath | modelPath := setting.JobPath + jobName + cloudbrain.ModelMountPath | ||||
| err = os.MkdirAll(modelPath, os.ModePerm) | err = os.MkdirAll(modelPath, os.ModePerm) | ||||
| if err != nil { | if err != nil { | ||||
| cloudBrainNewDataPrepare(ctx) | |||||
| ctx.RenderWithErr(err.Error(), tplCloudBrainNew, &form) | ctx.RenderWithErr(err.Error(), tplCloudBrainNew, &form) | ||||
| return | return | ||||
| } | } | ||||
| @@ -208,8 +242,9 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) { | |||||
| downloadRateCode(repo, jobName, setting.Snn4imagenetCode, snn4imagenetPath, "", "") | downloadRateCode(repo, jobName, setting.Snn4imagenetCode, snn4imagenetPath, "", "") | ||||
| } | } | ||||
| err = cloudbrain.GenerateTask(ctx, jobName, image, command, uuid, codePath, modelPath, benchmarkPath, snn4imagenetPath, jobType, gpuQueue) | |||||
| err = cloudbrain.GenerateTask(ctx, jobName, image, command, uuid, codePath, modelPath, benchmarkPath, snn4imagenetPath, jobType, gpuQueue, resourceSpecId) | |||||
| if err != nil { | if err != nil { | ||||
| cloudBrainNewDataPrepare(ctx) | |||||
| ctx.RenderWithErr(err.Error(), tplCloudBrainNew, &form) | ctx.RenderWithErr(err.Error(), tplCloudBrainNew, &form) | ||||
| return | return | ||||
| } | } | ||||
| @@ -384,6 +419,38 @@ func CloudBrainShowModels(ctx *context.Context) { | |||||
| ctx.HTML(200, tplCloudBrainShowModels) | ctx.HTML(200, tplCloudBrainShowModels) | ||||
| } | } | ||||
| func GetPublicImages(ctx *context.Context) { | |||||
| getImages(ctx, cloudbrain.Public) | |||||
| } | |||||
| func GetCustomImages(ctx *context.Context) { | |||||
| getImages(ctx, cloudbrain.Custom) | |||||
| } | |||||
| func getImages(ctx *context.Context, imageType string) { | |||||
| log.Info("Get images begin") | |||||
| page := ctx.QueryInt("page") | |||||
| size := ctx.QueryInt("size") | |||||
| name := ctx.Query("name") | |||||
| getImagesResult, err := cloudbrain.GetImagesPageable(page, size, imageType, name) | |||||
| if err != nil { | |||||
| log.Error("Can not get images:%v", err) | |||||
| ctx.JSON(http.StatusOK, models.GetImagesPayload{ | |||||
| Count: 0, | |||||
| TotalPages: 0, | |||||
| ImageInfo: []*models.ImageInfo{}, | |||||
| }) | |||||
| } else { | |||||
| ctx.JSON(http.StatusOK, getImagesResult.Payload) | |||||
| } | |||||
| log.Info("Get images end") | |||||
| } | |||||
| func getModelDirs(jobName string, parentDir string) (string, error) { | func getModelDirs(jobName string, parentDir string) (string, error) { | ||||
| var req string | var req string | ||||
| modelActualPath := setting.JobPath + jobName + "/model/" | modelActualPath := setting.JobPath + jobName + "/model/" | ||||
| @@ -400,7 +467,7 @@ func CloudBrainDownloadModel(ctx *context.Context) { | |||||
| parentDir := ctx.Query("parentDir") | parentDir := ctx.Query("parentDir") | ||||
| fileName := ctx.Query("fileName") | fileName := ctx.Query("fileName") | ||||
| jobName := ctx.Query("jobName") | jobName := ctx.Query("jobName") | ||||
| filePath := "jobs/" +jobName + "/model/" + parentDir | |||||
| filePath := "jobs/" + jobName + "/model/" + parentDir | |||||
| url, err := storage.Attachments.PresignedGetURL(filePath, fileName) | url, err := storage.Attachments.PresignedGetURL(filePath, fileName) | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("PresignedGetURL failed: %v", err.Error(), ctx.Data["msgID"]) | log.Error("PresignedGetURL failed: %v", err.Error(), ctx.Data["msgID"]) | ||||
| @@ -434,6 +501,45 @@ func downloadCode(repo *models.Repository, codePath string) error { | |||||
| return err | return err | ||||
| } | } | ||||
| configFile, err := os.OpenFile(codePath + "/.git/config", os.O_RDWR, 0666) | |||||
| if err != nil { | |||||
| log.Error("open file(%s) failed:%v", codePath + "/,git/config", err) | |||||
| return err | |||||
| } | |||||
| defer configFile.Close() | |||||
| pos := int64(0) | |||||
| reader := bufio.NewReader(configFile) | |||||
| for { | |||||
| line, err := reader.ReadString('\n') | |||||
| if err != nil { | |||||
| if err == io.EOF { | |||||
| log.Error("not find the remote-url") | |||||
| return nil | |||||
| } else { | |||||
| log.Error("read error: %v", err) | |||||
| return err | |||||
| } | |||||
| } | |||||
| if strings.Contains(line, "url") && strings.Contains(line, ".git"){ | |||||
| originUrl := "\turl = " + repo.CloneLink().HTTPS + "\n" | |||||
| if len(line) > len(originUrl) { | |||||
| originUrl += strings.Repeat( " ", len(line) - len(originUrl)) | |||||
| } | |||||
| bytes := []byte(originUrl) | |||||
| _, err := configFile.WriteAt(bytes, pos) | |||||
| if err != nil { | |||||
| log.Error("WriteAt failed:%v", err) | |||||
| return err | |||||
| } | |||||
| break | |||||
| } | |||||
| pos += int64(len(line)) | |||||
| } | |||||
| return nil | return nil | ||||
| } | } | ||||
| @@ -2,6 +2,7 @@ package repo | |||||
| import ( | import ( | ||||
| "code.gitea.io/gitea/modules/modelarts" | "code.gitea.io/gitea/modules/modelarts" | ||||
| "encoding/json" | |||||
| "errors" | "errors" | ||||
| "github.com/unknwon/com" | "github.com/unknwon/com" | ||||
| "strconv" | "strconv" | ||||
| @@ -71,7 +72,7 @@ func ModelArtsNew(ctx *context.Context) { | |||||
| ctx.Data["PageIsCloudBrain"] = true | ctx.Data["PageIsCloudBrain"] = true | ||||
| t := time.Now() | t := time.Now() | ||||
| var jobName = cutString(ctx.User.Name, 5) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:] | |||||
| var jobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:] | |||||
| ctx.Data["job_name"] = jobName | ctx.Data["job_name"] = jobName | ||||
| attachs, err := models.GetModelArtsUserAttachments(ctx.User.ID) | attachs, err := models.GetModelArtsUserAttachments(ctx.User.ID) | ||||
| @@ -84,7 +85,10 @@ func ModelArtsNew(ctx *context.Context) { | |||||
| ctx.Data["dataset_path"] = modelarts.DataSetMountPath | ctx.Data["dataset_path"] = modelarts.DataSetMountPath | ||||
| ctx.Data["env"] = modelarts.NotebookEnv | ctx.Data["env"] = modelarts.NotebookEnv | ||||
| ctx.Data["notebook_type"] = modelarts.NotebookType | ctx.Data["notebook_type"] = modelarts.NotebookType | ||||
| ctx.Data["flavor"] = modelarts.FlavorInfo | |||||
| if modelarts.FlavorInfos == nil { | |||||
| json.Unmarshal([]byte(setting.FlavorInfos), &modelarts.FlavorInfos) | |||||
| } | |||||
| ctx.Data["flavors"] = modelarts.FlavorInfos.FlavorInfo | |||||
| ctx.HTML(200, tplModelArtsNew) | ctx.HTML(200, tplModelArtsNew) | ||||
| } | } | ||||
| @@ -590,6 +590,9 @@ func Home(ctx *context.Context) { | |||||
| if err == nil && contributors != nil { | if err == nil && contributors != nil { | ||||
| var contributorInfos []*ContributorInfo | var contributorInfos []*ContributorInfo | ||||
| for _, c := range contributors { | for _, c := range contributors { | ||||
| if strings.Compare(c.Email,"") == 0 { | |||||
| continue | |||||
| } | |||||
| // get user info from committer email | // get user info from committer email | ||||
| user, err := models.GetUserByEmail(c.Email) | user, err := models.GetUserByEmail(c.Email) | ||||
| if err == nil { | if err == nil { | ||||
| @@ -6,13 +6,14 @@ package routes | |||||
| import ( | import ( | ||||
| "bytes" | "bytes" | ||||
| "code.gitea.io/gitea/routers/secure" | |||||
| "encoding/gob" | "encoding/gob" | ||||
| "net/http" | "net/http" | ||||
| "path" | "path" | ||||
| "text/template" | "text/template" | ||||
| "time" | "time" | ||||
| "code.gitea.io/gitea/routers/secure" | |||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| "code.gitea.io/gitea/modules/auth" | "code.gitea.io/gitea/modules/auth" | ||||
| "code.gitea.io/gitea/modules/context" | "code.gitea.io/gitea/modules/context" | ||||
| @@ -313,11 +314,14 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| m.Get("", func(ctx *context.Context) { | m.Get("", func(ctx *context.Context) { | ||||
| ctx.Redirect(setting.AppSubURL + "/explore/repos") | ctx.Redirect(setting.AppSubURL + "/explore/repos") | ||||
| }) | }) | ||||
| m.Get("/images/public", repo.GetPublicImages) | |||||
| m.Get("/images/custom", repo.GetCustomImages) | |||||
| m.Get("/repos", routers.ExploreRepos) | m.Get("/repos", routers.ExploreRepos) | ||||
| m.Get("/datasets", routers.ExploreDatasets) | m.Get("/datasets", routers.ExploreDatasets) | ||||
| m.Get("/users", routers.ExploreUsers) | m.Get("/users", routers.ExploreUsers) | ||||
| m.Get("/organizations", routers.ExploreOrganizations) | m.Get("/organizations", routers.ExploreOrganizations) | ||||
| m.Get("/code", routers.ExploreCode) | m.Get("/code", routers.ExploreCode) | ||||
| m.Get("/images", routers.ExploreImages) | |||||
| }, ignSignIn) | }, ignSignIn) | ||||
| m.Combo("/install", routers.InstallInit).Get(routers.Install). | m.Combo("/install", routers.InstallInit).Get(routers.Install). | ||||
| Post(bindIgnErr(auth.InstallForm{}), routers.InstallPost) | Post(bindIgnErr(auth.InstallForm{}), routers.InstallPost) | ||||
| @@ -80,6 +80,12 @@ func EmailPost(ctx *context.Context, form auth.AddEmailForm) { | |||||
| // Make emailaddress primary. | // Make emailaddress primary. | ||||
| if ctx.Query("_method") == "PRIMARY" { | if ctx.Query("_method") == "PRIMARY" { | ||||
| if err := models.MakeEmailPrimary(&models.EmailAddress{ID: ctx.QueryInt64("id")}); err != nil { | if err := models.MakeEmailPrimary(&models.EmailAddress{ID: ctx.QueryInt64("id")}); err != nil { | ||||
| if _, ok := err.(models.ErrEmailAlreadyUsed); ok { | |||||
| ctx.Flash.Error(ctx.Tr("form.email_been_used")) | |||||
| ctx.Redirect(setting.AppSubURL + "/user/settings/account") | |||||
| return | |||||
| } | |||||
| ctx.ServerError("MakeEmailPrimary", err) | ctx.ServerError("MakeEmailPrimary", err) | ||||
| return | return | ||||
| } | } | ||||
| @@ -90,23 +90,12 @@ func ProfilePost(ctx *context.Context, form auth.UpdateProfileForm) { | |||||
| } | } | ||||
| ctx.User.FullName = form.FullName | ctx.User.FullName = form.FullName | ||||
| ctx.User.Email = form.Email | |||||
| ctx.User.KeepEmailPrivate = form.KeepEmailPrivate | ctx.User.KeepEmailPrivate = form.KeepEmailPrivate | ||||
| ctx.User.Website = form.Website | ctx.User.Website = form.Website | ||||
| ctx.User.Location = form.Location | ctx.User.Location = form.Location | ||||
| ctx.User.Language = form.Language | ctx.User.Language = form.Language | ||||
| ctx.User.Description = form.Description | ctx.User.Description = form.Description | ||||
| isUsed, err := models.IsEmailUsed(form.Email) | |||||
| if err != nil { | |||||
| ctx.ServerError("IsEmailUsed", err) | |||||
| return | |||||
| } | |||||
| if isUsed { | |||||
| ctx.Flash.Error(ctx.Tr("form.email_been_used")) | |||||
| ctx.Redirect(setting.AppSubURL + "/user/settings") | |||||
| return | |||||
| } | |||||
| if err := models.UpdateUserSetting(ctx.User); err != nil { | if err := models.UpdateUserSetting(ctx.User); err != nil { | ||||
| if _, ok := err.(models.ErrEmailAlreadyUsed); ok { | if _, ok := err.(models.ErrEmailAlreadyUsed); ok { | ||||
| @@ -59,7 +59,7 @@ func SendTestMail(email string) error { | |||||
| func SendUserMail(language string, u *models.User, tpl base.TplName, code, subject, info string) { | func SendUserMail(language string, u *models.User, tpl base.TplName, code, subject, info string) { | ||||
| data := map[string]interface{}{ | data := map[string]interface{}{ | ||||
| "DisplayName": u.DisplayName(), | "DisplayName": u.DisplayName(), | ||||
| "ActiveCodeLives": timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, language), | |||||
| "ActiveCodeLives": timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, "en-US"), | |||||
| "ResetPwdCodeLives": timeutil.MinutesToFriendly(setting.Service.ResetPwdCodeLives, language), | "ResetPwdCodeLives": timeutil.MinutesToFriendly(setting.Service.ResetPwdCodeLives, language), | ||||
| "Code": code, | "Code": code, | ||||
| } | } | ||||
| @@ -97,7 +97,7 @@ func SendResetPasswordMail(locale Locale, u *models.User) { | |||||
| func SendActivateEmailMail(locale Locale, u *models.User, email *models.EmailAddress) { | func SendActivateEmailMail(locale Locale, u *models.User, email *models.EmailAddress) { | ||||
| data := map[string]interface{}{ | data := map[string]interface{}{ | ||||
| "DisplayName": u.DisplayName(), | "DisplayName": u.DisplayName(), | ||||
| "ActiveCodeLives": timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, locale.Language()), | |||||
| "ActiveCodeLives": timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, "en-US"), | |||||
| "Code": u.GenerateEmailActivateCode(email.Email), | "Code": u.GenerateEmailActivateCode(email.Email), | ||||
| "Email": email.Email, | "Email": email.Email, | ||||
| } | } | ||||
| @@ -28,6 +28,7 @@ | |||||
| <a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "custom.head.dataset"}}</a> | <a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "custom.head.dataset"}}</a> | ||||
| <a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a> | <a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a> | ||||
| <a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a> | <a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a> | ||||
| <a class="item" href="{{AppSubUrl}}/explore/images">{{.i18n.Tr "explore.images"}}</a> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| {{else if .IsLandingPageHome}} | {{else if .IsLandingPageHome}} | ||||
| @@ -42,6 +43,7 @@ | |||||
| <a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "datasets"}}</a> | <a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "datasets"}}</a> | ||||
| <a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a> | <a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a> | ||||
| <a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a> | <a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a> | ||||
| <a class="item" href="{{AppSubUrl}}/explore/images">{{.i18n.Tr "explore.images"}}</a> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| {{else if .IsLandingPageExplore}} | {{else if .IsLandingPageExplore}} | ||||
| @@ -28,6 +28,7 @@ | |||||
| <a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "custom.head.dataset"}}</a> | <a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "custom.head.dataset"}}</a> | ||||
| <a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a> | <a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a> | ||||
| <a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a> | <a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a> | ||||
| <a class="item" href="{{AppSubUrl}}/explore/images">{{.i18n.Tr "explore.images"}}</a> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| {{else if .IsLandingPageHome}} | {{else if .IsLandingPageHome}} | ||||
| @@ -42,6 +43,7 @@ | |||||
| <a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "datasets"}}</a> | <a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "datasets"}}</a> | ||||
| <a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a> | <a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a> | ||||
| <a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a> | <a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a> | ||||
| <a class="item" href="{{AppSubUrl}}/explore/images">{{.i18n.Tr "explore.images"}}</a> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| {{else if .IsLandingPageExplore}} | {{else if .IsLandingPageExplore}} | ||||
| @@ -0,0 +1,7 @@ | |||||
| {{template "base/head" .}} | |||||
| <div id="images"> | |||||
| </div> | |||||
| {{template "base/footer" .}} | |||||
| @@ -1,6 +1,6 @@ | |||||
| { | { | ||||
| "short_name": "Gitea", | |||||
| "name": "Gitea - Git with a cup of tea", | |||||
| "short_name": "OpenI", | |||||
| "name": "OpenI - 启智AI开发协作平台", | |||||
| "icons": [ | "icons": [ | ||||
| { | { | ||||
| "src": "{{StaticUrlPrefix}}/img/gitea-lg.png", | "src": "{{StaticUrlPrefix}}/img/gitea-lg.png", | ||||
| @@ -110,12 +110,16 @@ | |||||
| <div class="rect5"></div> | <div class="rect5"></div> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="repository"> | <div class="repository"> | ||||
| {{template "repo/header" .}} | {{template "repo/header" .}} | ||||
| <div class="repository new repo ui middle very relaxed page grid"> | <div class="repository new repo ui middle very relaxed page grid"> | ||||
| <div class="column"> | <div class="column"> | ||||
| {{template "base/alert" .}} | {{template "base/alert" .}} | ||||
| <form class="ui form" action="{{.Link}}" method="post"> | |||||
| <div class="ui positive message" id="messageInfo"> | |||||
| <p></p> | |||||
| </div> | |||||
| <form id="form_id" class="ui form" action="{{.Link}}" method="post"> | |||||
| {{.CsrfTokenHtml}} | {{.CsrfTokenHtml}} | ||||
| <h3 class="ui top attached header"> | <h3 class="ui top attached header"> | ||||
| {{.i18n.Tr "repo.cloudbrain.new"}} | {{.i18n.Tr "repo.cloudbrain.new"}} | ||||
| @@ -161,7 +165,7 @@ | |||||
| <div class="inline required field"> | <div class="inline required field"> | ||||
| <label>镜像</label> | <label>镜像</label> | ||||
| <input type="text" list="cloudbrain_image" placeholder="选择镜像" name="image"> | |||||
| <input type="text" list="cloudbrain_image" placeholder="选择镜像" name="image" required autofocus maxlength="255"> | |||||
| <datalist class="ui search" id="cloudbrain_image" style='width:385px;' name="image"> | <datalist class="ui search" id="cloudbrain_image" style='width:385px;' name="image"> | ||||
| {{range .images}} | {{range .images}} | ||||
| <option name="image" value="{{.Place}}">{{.PlaceView}}</option> | <option name="image" value="{{.Place}}">{{.PlaceView}}</option> | ||||
| @@ -181,17 +185,26 @@ | |||||
| </select> | </select> | ||||
| </div> | </div> | ||||
| <div class="inline required field"> | |||||
| <label>资源规格</label> | |||||
| <select id="cloudbrain_resource_spec" class="ui search dropdown" placeholder="选择资源规格" style='width:385px' name="resource_spec_id"> | |||||
| {{range .resource_specs}} | |||||
| <option name="resource_spec_id" value="{{.Id}}">GPU数:{{.GpuNum}},CPU数:{{.CpuNum}},内存(MB):{{.MemMiB}},共享内存(MB):{{.ShareMemMiB}}</option> | |||||
| {{end}} | |||||
| </select> | |||||
| </div> | |||||
| <div class="inline required field"> | <div class="inline required field"> | ||||
| <label>数据集存放路径</label> | <label>数据集存放路径</label> | ||||
| <input name="dataset_path" id="cloudbrain_dataset_path" value="{{.dataset_path}}" tabindex="3" autofocus required maxlength="255" readonly="readonly"> | |||||
| <input name="dataset_path" id="cloudbrain_dataset_path" value="{{.dataset_path}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | |||||
| </div> | </div> | ||||
| <div class="inline required field"> | <div class="inline required field"> | ||||
| <label>模型存放路径</label> | <label>模型存放路径</label> | ||||
| <input name="model_path" id="cloudbrain_model_path" value="{{.model_path}}" tabindex="3" autofocus required maxlength="255" readonly="readonly"> | |||||
| <input name="model_path" id="cloudbrain_model_path" value="{{.model_path}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | |||||
| </div> | </div> | ||||
| <div class="inline required field"> | <div class="inline required field"> | ||||
| <label>代码存放路径</label> | <label>代码存放路径</label> | ||||
| <input name="code_path" id="cloudbrain_code_path" value="{{.code_path}}" tabindex="3" autofocus required maxlength="255" readonly="readonly"> | |||||
| <input name="code_path" id="cloudbrain_code_path" value="{{.code_path}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | |||||
| </div> | </div> | ||||
| <div class="inline required field cloudbrain_benchmark"> | <div class="inline required field cloudbrain_benchmark"> | ||||
| <label>benchmark脚本存放路径</label> | <label>benchmark脚本存放路径</label> | ||||
| @@ -208,13 +221,15 @@ | |||||
| <div class="inline field"> | <div class="inline field"> | ||||
| <label></label> | <label></label> | ||||
| <button class="ui green button" onclick="showmask()"> | |||||
| <button class="ui green button" > | |||||
| {{.i18n.Tr "repo.cloudbrain.new"}} | {{.i18n.Tr "repo.cloudbrain.new"}} | ||||
| </button> | </button> | ||||
| <a class="ui button" href="/">{{.i18n.Tr "repo.cloudbrain.cancel"}}</a> | <a class="ui button" href="/">{{.i18n.Tr "repo.cloudbrain.cancel"}}</a> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </form> | </form> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| @@ -222,9 +237,37 @@ | |||||
| <link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" /> | <link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" /> | ||||
| <script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script> | <script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script> | ||||
| <script> | <script> | ||||
| // 点击按钮后遮罩层显示 | |||||
| function showmask() { | |||||
| let url_href = window.location.pathname.split('create')[0] | |||||
| $(".ui.button").attr('href',url_href) | |||||
| let form = document.getElementById('form_id'); | |||||
| let value_image = $("input[name='image']").val() | |||||
| console.log("value_image",$("input[name='image']").val()) | |||||
| $('#messageInfo').css('display','none') | |||||
| form.onsubmit = function(e){ | |||||
| let value_task = $("input[name='job_name']").val() | |||||
| let value_image = $("input[name='image']").val() | |||||
| let re = /^[a-z0-9][a-z0-9-_]{1,36}$/ | |||||
| let flag = re.test(value_task) | |||||
| if(!flag){ | |||||
| $('#messageInfo').css('display','block') | |||||
| let str = '只能以小写字母或数字开头且只包含小写字母、数字、_和-、最长36个字符。' | |||||
| $('#messageInfo p').text(str) | |||||
| return false | |||||
| } | |||||
| if(!value_image){ | |||||
| return false | |||||
| } | |||||
| let min_value_task = value_task.toLowerCase() | |||||
| console.log(min_value_task) | |||||
| $("input[name='job_name']").attr("value",min_value_task) | |||||
| document.getElementById("mask").style.display = "block" | document.getElementById("mask").style.display = "block" | ||||
| } | } | ||||
| // 页面加载完毕后遮罩层隐藏 | // 页面加载完毕后遮罩层隐藏 | ||||
| @@ -12,6 +12,15 @@ | |||||
| float: left; | float: left; | ||||
| margin: .25em; | margin: .25em; | ||||
| } | } | ||||
| .edit-link{ | |||||
| vertical-align: top; | |||||
| display: inline-block; | |||||
| overflow: hidden; | |||||
| word-break: keep-all; | |||||
| white-space: nowrap; | |||||
| text-overflow: ellipsis; | |||||
| width: 16.5em; | |||||
| } | |||||
| #contributorInfo > a.circular{ | #contributorInfo > a.circular{ | ||||
| height: 2.0em; | height: 2.0em; | ||||
| padding: 0; | padding: 0; | ||||
| @@ -218,7 +227,7 @@ | |||||
| {{if .Repository.Website}} | {{if .Repository.Website}} | ||||
| <p class="ui"> | <p class="ui"> | ||||
| <i class="gray linkify icon"></i> | <i class="gray linkify icon"></i> | ||||
| <a class="link" target="_blank" href="{{.Repository.Website}}">{{.Repository.Website}}</a> | |||||
| <a class="link edit-link" target="_blank" title="{{.Repository.Website}}" href="{{.Repository.Website}}">{{.Repository.Website}}</a> | |||||
| </p> | </p> | ||||
| {{end}} | {{end}} | ||||
| @@ -132,7 +132,12 @@ | |||||
| </div> | </div> | ||||
| <div class="inline required field"> | <div class="inline required field"> | ||||
| <label>规格</label> | <label>规格</label> | ||||
| <input name="flavor" id="cloudbrain_flavor" value="{{.flavor}}" tabindex="3" autofocus required maxlength="255" readonly="readonly"> | |||||
| <select id="cloudbrain_flavor" class="ui search dropdown" placeholder="选择规格" style='width:385px' name="flavor"> | |||||
| {{range .flavors}} | |||||
| <option name="flavor" value="{{.Value}}">{{.Value}}</option> | |||||
| {{end}} | |||||
| </select> | |||||
| </div> | </div> | ||||
| <div class="inline required field"> | <div class="inline required field"> | ||||
| <label>数据集存放路径</label> | <label>数据集存放路径</label> | ||||
| @@ -23,7 +23,8 @@ | |||||
| </div> | </div> | ||||
| <div class="required field {{if .Err_Email}}error{{end}}"> | <div class="required field {{if .Err_Email}}error{{end}}"> | ||||
| <label for="email">{{.i18n.Tr "email"}}</label> | <label for="email">{{.i18n.Tr "email"}}</label> | ||||
| <input id="email" name="email" value="{{.SignedUser.Email}}"> | |||||
| <input type="hidden" id="email" name="email" value="{{.SignedUser.Email}}" > | |||||
| <input disabled value="{{.SignedUser.Email}}"> | |||||
| </div> | </div> | ||||
| <div class="inline field"> | <div class="inline field"> | ||||
| <div class="ui checkbox" id="keep-email-private"> | <div class="ui checkbox" id="keep-email-private"> | ||||
| @@ -0,0 +1,508 @@ | |||||
| <template> | |||||
| <div> | |||||
| <div class="header-wrapper"> | |||||
| <div class="ui container"> | |||||
| <el-row class="image_text"> | |||||
| <h1>云脑镜像</h1> | |||||
| </el-row> | |||||
| </div> | |||||
| </div> | |||||
| <div class="ui container" id="header"> | |||||
| <el-tabs v-model="activeName" @tab-click="handleClick"> | |||||
| <el-tab-pane label="公共镜像(云脑1)" name="first" v-loading="loading"> | |||||
| <div class="ui sixteen wide column"> | |||||
| <div class="ui two column stackable grid"> | |||||
| <div class="column"> | |||||
| <el-input placeholder="请输入镜像名称关健词" v-model="search" class="input-with-select" @keyup.enter.native="searchName()"> | |||||
| <el-button id="success" slot="append" icon="el-icon-search" @click="searchName()">搜索</el-button> | |||||
| </el-input> | |||||
| </div> | |||||
| <!-- <div class="column right aligned"> | |||||
| <el-dropdown> | |||||
| <span class="el-dropdown-link"> | |||||
| 排序<i class="el-icon-caret-bottom"></i> | |||||
| </span> | |||||
| <el-dropdown-menu slot="dropdown"> | |||||
| <el-dropdown-item>最早创建</el-dropdown-item> | |||||
| <el-dropdown-item>最新创建</el-dropdown-item> | |||||
| <el-dropdown-item divided>按镜像字母顺序排序</el-dropdown-item> | |||||
| <el-dropdown-item>按镜像字母逆序排序</el-dropdown-item> | |||||
| </el-dropdown-menu> | |||||
| </el-dropdown> | |||||
| </div> --> | |||||
| </div> | |||||
| </div> | |||||
| <el-row style="margin-top:15px;"> | |||||
| <el-table | |||||
| :data="tableData" | |||||
| style="width: 100%" | |||||
| :header-cell-style="tableHeaderStyle" | |||||
| :default-sort="{prop:'createtime',order:'descending'}"> | |||||
| <el-table-column | |||||
| label="镜像名称" | |||||
| width="350" | |||||
| align="left" | |||||
| prop="name" | |||||
| sortable | |||||
| > | |||||
| <template slot-scope="scope"> | |||||
| <a class="text-over" style="cursor:default;color:#426290" :title="scope.row.name">{{ scope.row.name }}</a> | |||||
| </template> | |||||
| </el-table-column> | |||||
| <el-table-column | |||||
| label="文件路径/镜像描述" | |||||
| width="450" | |||||
| align="left" | |||||
| > | |||||
| <template slot-scope="scope"> | |||||
| <el-tooltip class="item" effect="dark" content="点击复制文件路径" placement="top"> | |||||
| <a class="text-over" style="display:block;" @click="copyUrl(scope.row.place)">{{ scope.row.place }}</a> | |||||
| </el-tooltip> | |||||
| <span class="text-over" :title="scope.row.description | clearP">{{ scope.row.description | clearP}}</span> | |||||
| </template> | |||||
| </el-table-column> | |||||
| <el-table-column | |||||
| prop="provider" | |||||
| label="提供者" | |||||
| width="120" | |||||
| align="left" | |||||
| sortable> | |||||
| </el-table-column> | |||||
| <el-table-column | |||||
| prop="createtime" | |||||
| label="创建时间" | |||||
| align="center" | |||||
| sortable> | |||||
| <template slot-scope="scope"> | |||||
| {{scope.row.createtime | transformTimestamp}} | |||||
| </template> | |||||
| </el-table-column> | |||||
| </el-table> | |||||
| </el-row> | |||||
| <div class="ui container" style="margin-top:50px;text-align:center"> | |||||
| <el-pagination | |||||
| background | |||||
| @size-change="handleSizeChange" | |||||
| @current-change="handleCurrentChange" | |||||
| :current-page="currentPage" | |||||
| :page-size="pageSize" | |||||
| :page-sizes="[5,10,20]" | |||||
| layout="total, sizes, prev, pager, next, jumper" | |||||
| :total="totalNum"> | |||||
| </el-pagination> | |||||
| </div> | |||||
| </el-tab-pane> | |||||
| <el-tab-pane label="自定义镜像(云脑1)" name="second" v-loading="loading1"> | |||||
| <div class="ui sixteen wide column"> | |||||
| <div class="ui two column stackable grid"> | |||||
| <div class="column"> | |||||
| <el-input placeholder="请输入镜像名称关健词" v-model="search" class="input-with-select" @keyup.enter.native="searchName()"> | |||||
| <el-button slot="append" id="success" icon="el-icon-search" @click="searchName()">搜索</el-button> | |||||
| </el-input> | |||||
| </div> | |||||
| <!-- <div class="column right aligned"> | |||||
| <el-dropdown> | |||||
| <span class="el-dropdown-link"> | |||||
| 排序<i class="el-icon-caret-bottom"></i> | |||||
| </span> | |||||
| <el-dropdown-menu slot="dropdown"> | |||||
| <el-dropdown-item>最早创建</el-dropdown-item> | |||||
| <el-dropdown-item>最新创建</el-dropdown-item> | |||||
| <el-dropdown-item divided>按镜像字母顺序排序</el-dropdown-item> | |||||
| <el-dropdown-item>按镜像字母逆序排序</el-dropdown-item> | |||||
| </el-dropdown-menu> | |||||
| </el-dropdown> | |||||
| </div> --> | |||||
| </div> | |||||
| </div> | |||||
| <el-row style="margin-top:15px;"> | |||||
| <el-table | |||||
| :data="tableData1" | |||||
| style="width: 100%" | |||||
| :header-cell-style="tableHeaderStyle" | |||||
| :default-sort="{prop:'createtime',order:'descending'}"> | |||||
| <el-table-column | |||||
| label="镜像名称" | |||||
| width="350" | |||||
| align="left" | |||||
| prop="name" | |||||
| sortable | |||||
| > | |||||
| <template slot-scope="scope"> | |||||
| <a class="text-over" :title="scope.row.name" style="cursor:default;color:#426290">{{ scope.row.name }}</a> | |||||
| </template> | |||||
| </el-table-column> | |||||
| <el-table-column | |||||
| label="文件路径/镜像描述" | |||||
| width="450" | |||||
| align="left" | |||||
| > | |||||
| <template slot-scope="scope"> | |||||
| <el-tooltip class="item" effect="dark" content="点击复制文件路径" placement="top"> | |||||
| <a class="text-over" style="display:block;" @click="copyUrl(scope.row.place)">{{ scope.row.place }}</a> | |||||
| </el-tooltip> | |||||
| <span class="text-over" :title="scope.row.description | clearP">{{ scope.row.description | clearP }}</span> | |||||
| </template> | |||||
| </el-table-column> | |||||
| <el-table-column | |||||
| prop="provider" | |||||
| label="提供者" | |||||
| width="120" | |||||
| align="left" | |||||
| sortable> | |||||
| </el-table-column> | |||||
| <el-table-column | |||||
| prop="createtime" | |||||
| label="创建时间" | |||||
| align="center" | |||||
| sortable> | |||||
| <template slot-scope="scope"> | |||||
| {{scope.row.createtime | transformTimestamp}} | |||||
| </template> | |||||
| </el-table-column> | |||||
| </el-table> | |||||
| </el-row> | |||||
| <div class="ui container" style="margin-top:50px;text-align:center"> | |||||
| <el-pagination | |||||
| background | |||||
| @size-change="handleSizeChange1" | |||||
| @current-change="handleCurrentChange1" | |||||
| :current-page="currentPage1" | |||||
| :page-size="pageSize1" | |||||
| :page-sizes="[5,10,20]" | |||||
| layout="total, sizes, prev, pager, next, jumper" | |||||
| :total="totalNum1"> | |||||
| </el-pagination> | |||||
| </div> | |||||
| </el-tab-pane> | |||||
| <el-tab-pane label="公共镜像(云脑2)" name="third"> | |||||
| <div class="ui sixteen wide column"> | |||||
| <div class="ui two column stackable grid"> | |||||
| <div class="column"> | |||||
| <el-input placeholder="请输入镜像名称关健词" v-model="search" class="input-with-select"> | |||||
| <el-button slot="append" id="success" icon="el-icon-search">搜索</el-button> | |||||
| </el-input> | |||||
| </div> | |||||
| <!-- <div class="column right aligned"> | |||||
| <el-dropdown> | |||||
| <span class="el-dropdown-link"> | |||||
| 排序<i class="el-icon-caret-bottom"></i> | |||||
| </span> | |||||
| <el-dropdown-menu slot="dropdown"> | |||||
| <el-dropdown-item>最早创建</el-dropdown-item> | |||||
| <el-dropdown-item>最新创建</el-dropdown-item> | |||||
| <el-dropdown-item divided>按镜像字母顺序排序</el-dropdown-item> | |||||
| <el-dropdown-item>按镜像字母逆序排序</el-dropdown-item> | |||||
| </el-dropdown-menu> | |||||
| </el-dropdown> | |||||
| </div> --> | |||||
| </div> | |||||
| </div> | |||||
| <el-empty :image-size="200"></el-empty> | |||||
| </el-tab-pane> | |||||
| <el-tab-pane label="自定义镜像(云脑2)" name="fourth"> | |||||
| <div class="ui sixteen wide column"> | |||||
| <div class="ui two column stackable grid"> | |||||
| <div class="column"> | |||||
| <el-input placeholder="请输入镜像名称关健词" v-model="search" class="input-with-select"> | |||||
| <el-button slot="append" id="success" icon="el-icon-search">搜索</el-button> | |||||
| </el-input> | |||||
| </div> | |||||
| <!-- <div class="column right aligned"> | |||||
| <el-dropdown> | |||||
| <span class="el-dropdown-link"> | |||||
| 排序<i class="el-icon-caret-bottom"></i> | |||||
| </span> | |||||
| <el-dropdown-menu slot="dropdown"> | |||||
| <el-dropdown-item>最早创建</el-dropdown-item> | |||||
| <el-dropdown-item>最新创建</el-dropdown-item> | |||||
| <el-dropdown-item divided>按镜像字母顺序排序</el-dropdown-item> | |||||
| <el-dropdown-item>按镜像字母逆序排序</el-dropdown-item> | |||||
| </el-dropdown-menu> | |||||
| </el-dropdown> | |||||
| </div> --> | |||||
| </div> | |||||
| </div> | |||||
| <el-empty :image-size="200"></el-empty> | |||||
| </el-tab-pane> | |||||
| </el-tabs> | |||||
| </div> | |||||
| </div> | |||||
| </template> | |||||
| <script> | |||||
| const {_AppSubUrl, _StaticUrlPrefix, csrf} = window.config; | |||||
| export default { | |||||
| components: { | |||||
| }, | |||||
| data() { | |||||
| return { | |||||
| activeName: 'first', | |||||
| search:'', | |||||
| currentPage:1, | |||||
| pageSize:10, | |||||
| totalNum:0, | |||||
| params:{page:1,size:10,name:''}, | |||||
| tableData: [], | |||||
| loading:false, | |||||
| currentPage1:1, | |||||
| pageSize1:10, | |||||
| totalNum1:0, | |||||
| params1:{page:1,size:10,name:''}, | |||||
| tableData1: [], | |||||
| loading1:false | |||||
| }; | |||||
| }, | |||||
| methods: { | |||||
| handleClick(tab, event) { | |||||
| if(tab.name=="first"){ | |||||
| this.getImageList() | |||||
| } | |||||
| if(tab.name=="second"){ | |||||
| this.getImageList1() | |||||
| } | |||||
| }, | |||||
| tableHeaderStyle({row,column,rowIndex,columnIndex}){ | |||||
| if(rowIndex===0){ | |||||
| return 'background:#f5f5f6;color:#606266' | |||||
| } | |||||
| }, | |||||
| handleSizeChange(val){ | |||||
| this.params.size = val | |||||
| this.getImageList() | |||||
| }, | |||||
| handleCurrentChange(val){ | |||||
| console.log(val) | |||||
| this.params.page = val | |||||
| this.getImageList() | |||||
| }, | |||||
| handleSizeChange1(val){ | |||||
| this.params1.size = val | |||||
| this.getImageList1() | |||||
| }, | |||||
| handleCurrentChange1(val){ | |||||
| this.params1.page = val | |||||
| this.getImageList1() | |||||
| }, | |||||
| getImageList(){ | |||||
| this.loading = true | |||||
| this.$axios.get('/explore/images/public',{ | |||||
| params:this.params | |||||
| }).then((res)=>{ | |||||
| this.totalNum = res.data.count | |||||
| this.tableData = res.data.rows | |||||
| this.loading = false | |||||
| }) | |||||
| }, | |||||
| getImageList1(){ | |||||
| this.loading1 = true | |||||
| this.$axios.get('/explore/images/custom',{ | |||||
| params:this.params1 | |||||
| }).then((res)=>{ | |||||
| this.totalNum1 = res.data.count | |||||
| this.tableData1 = res.data.rows | |||||
| this.loading1 = false | |||||
| }) | |||||
| }, | |||||
| copyUrl(url){ | |||||
| console.log(url) | |||||
| const cInput = document.createElement('input') | |||||
| cInput.value = url | |||||
| document.body.appendChild(cInput) | |||||
| cInput.select() | |||||
| document.execCommand('Copy') | |||||
| cInput.remove() | |||||
| }, | |||||
| searchName(){ | |||||
| if(this.activeName=='first'){ | |||||
| this.params.name = this.search | |||||
| this.params.page = 1 | |||||
| this.getImageList() | |||||
| } | |||||
| if(this.activeName=='second'){ | |||||
| this.params1.name = this.search | |||||
| this.params1.page = 1 | |||||
| this.getImageList1() | |||||
| } | |||||
| } | |||||
| }, | |||||
| filters:{ | |||||
| clearP(value){ | |||||
| console.log("sorce value",value) | |||||
| if(!value) return '' | |||||
| const reg = /\<\/?p\>/g; | |||||
| value = value.replace(reg,'') | |||||
| console.log("repalace:",value) | |||||
| return value | |||||
| }, | |||||
| transformTimestamp(timestamp){ | |||||
| console.log("timestamp",timestamp) | |||||
| let a = new Date(timestamp).getTime(); | |||||
| const date = new Date(a); | |||||
| const Y = date.getFullYear() + '-'; | |||||
| const M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-'; | |||||
| const D = (date.getDate() < 10 ? '0'+date.getDate() : date.getDate()) + ' '; | |||||
| const h = (date.getHours() < 10 ? '0'+date.getHours() : date.getHours()) + ':'; | |||||
| const m = (date.getMinutes() <10 ? '0'+date.getMinutes() : date.getMinutes()) + ':' ; | |||||
| const s = (date.getSeconds() <10 ? '0'+date.getSeconds() : date.getSeconds()) ; // 秒 | |||||
| const dateString = Y + M + D + h + m + s; | |||||
| // console.log('dateString', dateString); // > dateString 2021-07-06 14:23 | |||||
| return dateString; | |||||
| }, | |||||
| }, | |||||
| watch:{ | |||||
| search(val){ | |||||
| if(!val && this.activeName=='first'){ | |||||
| this.params.name = val | |||||
| this.getImageList() | |||||
| } | |||||
| if(!val && this.activeName=='second'){ | |||||
| this.params1.name = val | |||||
| this.getImageList1() | |||||
| } | |||||
| } | |||||
| }, | |||||
| mounted() { | |||||
| this.getImageList() | |||||
| }, | |||||
| created() { | |||||
| } | |||||
| }; | |||||
| </script> | |||||
| <style scoped> | |||||
| .header-wrapper { | |||||
| background-color: #f5f5f6; | |||||
| padding-top: 15px; | |||||
| } | |||||
| .image_text{ | |||||
| padding:25px 0 55px 0 ; | |||||
| } | |||||
| #header{ | |||||
| position: relative; | |||||
| top:-40px; | |||||
| } | |||||
| .el-dropdown-menu__item--divided{ | |||||
| border-top: 1px solid blue; | |||||
| } | |||||
| .el-table thead{ | |||||
| background-color: #f5f5f6; | |||||
| } | |||||
| /deep/ .el-tabs__item:hover{ | |||||
| color: #000; | |||||
| font-weight: 500; | |||||
| } | |||||
| /deep/ .el-tabs__item.is-active { | |||||
| color: #000; | |||||
| font-weight: 500; | |||||
| } | |||||
| /deep/ .el-tabs__active-bar{ | |||||
| background-color:#000 | |||||
| } | |||||
| /deep/ .el-pagination.is-background .el-pager li:not(.disabled).active { | |||||
| background-color: #5bb973; | |||||
| color: #FFF; | |||||
| } | |||||
| /deep/ .el-pagination.is-background .el-pager li.active { | |||||
| color: #fff; | |||||
| cursor: default; | |||||
| } | |||||
| /deep/ .el-pagination.is-background .el-pager li:hover { | |||||
| color: #5bb973; | |||||
| } | |||||
| /deep/ .el-pagination.is-background .el-pager li:not(.disabled):hover { | |||||
| color: #5bb973; | |||||
| } | |||||
| /deep/ .el-pagination.is-background .el-pager li:not(.disabled).active:hover { | |||||
| background-color: #5bb973; | |||||
| color: #FFF; | |||||
| } | |||||
| /deep/ .el-pager li.active { | |||||
| color: #08C0B9; | |||||
| cursor: default; | |||||
| } | |||||
| /deep/ .el-pagination .el-pager li:hover { | |||||
| color: #08C0B9; | |||||
| } | |||||
| /deep/ .el-pagination .el-pager li:not(.disabled):hover { | |||||
| color: #08C0B9; | |||||
| } | |||||
| /* /deep/ .el-pagination.is-background .el-pager li:not(.disabled).active{ | |||||
| background-color: #5bb973; | |||||
| color: #000; | |||||
| } */ | |||||
| /* /deep/ .el-pager li:hover{ | |||||
| color: #000; | |||||
| } */ | |||||
| #success{ | |||||
| background-color: #5bb973; | |||||
| color: white; | |||||
| } | |||||
| .text-over{ | |||||
| overflow: hidden; | |||||
| text-overflow: ellipsis; | |||||
| vertical-align: middle; | |||||
| white-space: nowrap; | |||||
| } | |||||
| </style> | |||||
| @@ -1,5 +1,8 @@ | |||||
| <template> | <template> | ||||
| <div class="dropzone-wrapper dataset-files"> | <div class="dropzone-wrapper dataset-files"> | ||||
| <div class="ui pointing below red basic label"> | |||||
| <i class="icon info circle"></i>只有zip格式的数据集才能发起云脑任务 | |||||
| </div> | |||||
| <div | <div | ||||
| id="dataset" | id="dataset" | ||||
| class="dropzone" | class="dropzone" | ||||
| @@ -335,8 +338,19 @@ export default { | |||||
| async function uploadMinio(url, e) { | async function uploadMinio(url, e) { | ||||
| const res = await axios.put(url, e.target.result); | const res = await axios.put(url, e.target.result); | ||||
| delete e.target.result | |||||
| delete e.target.result | |||||
| etags[currentChunk] = res.headers.etag; | etags[currentChunk] = res.headers.etag; | ||||
| } | |||||
| async function uploadMinioNewMethod(url,e){ | |||||
| var xhr = new XMLHttpRequest(); | |||||
| xhr.open('PUT', url, false); | |||||
| xhr.setRequestHeader('Content-Type', 'text/plain') | |||||
| xhr.send(e.target.result); | |||||
| var etagValue = xhr.getResponseHeader('etag'); | |||||
| //console.log(etagValue); | |||||
| etags[currentChunk] = etagValue; | |||||
| } | } | ||||
| async function updateChunk(currentChunk) { | async function updateChunk(currentChunk) { | ||||
| @@ -359,8 +373,10 @@ export default { | |||||
| // 获取分片上传url | // 获取分片上传url | ||||
| await getUploadChunkUrl(currentChunk, partSize); | await getUploadChunkUrl(currentChunk, partSize); | ||||
| if (urls[currentChunk] != '') { | if (urls[currentChunk] != '') { | ||||
| // 上传到minio | |||||
| await uploadMinio(urls[currentChunk], e); | |||||
| // 上传到minio | |||||
| //await uploadMinio(urls[currentChunk], e); | |||||
| await uploadMinioNewMethod(urls[currentChunk], e); | |||||
| if (etags[currentChunk] != '') { | if (etags[currentChunk] != '') { | ||||
| // 更新数据库:分片上传结果 | // 更新数据库:分片上传结果 | ||||
| //await updateChunk(currentChunk); | //await updateChunk(currentChunk); | ||||
| @@ -372,8 +388,9 @@ export default { | |||||
| } | } | ||||
| } | } | ||||
| } catch (error) { | } catch (error) { | ||||
| this.emitDropzoneFailed(file); | |||||
| console.log(error); | console.log(error); | ||||
| //this.emitDropzoneFailed(file); | |||||
| //console.log(error); | |||||
| } | } | ||||
| } | } | ||||
| @@ -35,6 +35,7 @@ import {createCodeEditor} from './features/codeeditor.js'; | |||||
| import MinioUploader from './components/MinioUploader.vue'; | import MinioUploader from './components/MinioUploader.vue'; | ||||
| import ObsUploader from './components/ObsUploader.vue'; | import ObsUploader from './components/ObsUploader.vue'; | ||||
| import EditAboutInfo from './components/EditAboutInfo.vue'; | import EditAboutInfo from './components/EditAboutInfo.vue'; | ||||
| import Images from './components/Images.vue' | |||||
| Vue.use(ElementUI); | Vue.use(ElementUI); | ||||
| Vue.prototype.$axios = axios; | Vue.prototype.$axios = axios; | ||||
| @@ -2966,6 +2967,7 @@ $(document).ready(async () => { | |||||
| initVueUploader(); | initVueUploader(); | ||||
| initObsUploader(); | initObsUploader(); | ||||
| initVueEditAbout(); | initVueEditAbout(); | ||||
| initVueImages(); | |||||
| initTeamSettings(); | initTeamSettings(); | ||||
| initCtrlEnterSubmit(); | initCtrlEnterSubmit(); | ||||
| initNavbarContentToggle(); | initNavbarContentToggle(); | ||||
| @@ -3653,7 +3655,7 @@ function initVueUploader() { | |||||
| function initVueEditAbout() { | function initVueEditAbout() { | ||||
| const el = document.getElementById('about-desc'); | const el = document.getElementById('about-desc'); | ||||
| console.log(el) | |||||
| if (!el) { | if (!el) { | ||||
| return; | return; | ||||
| } | } | ||||
| @@ -3664,6 +3666,21 @@ function initVueEditAbout() { | |||||
| }); | }); | ||||
| } | } | ||||
| function initVueImages() { | |||||
| const el = document.getElementById('images'); | |||||
| console.log("el",el) | |||||
| if (!el) { | |||||
| return; | |||||
| } | |||||
| new Vue({ | |||||
| el: '#images', | |||||
| render: h => h(Images) | |||||
| }); | |||||
| } | |||||
| // 新增 | // 新增 | ||||
| function initObsUploader() { | function initObsUploader() { | ||||
| const el = document.getElementById('obsUploader'); | const el = document.getElementById('obsUploader'); | ||||
| @@ -890,8 +890,6 @@ footer { | |||||
| .ui.menu.new-menu { | .ui.menu.new-menu { | ||||
| justify-content: center !important; | justify-content: center !important; | ||||
| padding-top: 15px !important; | |||||
| margin-top: -15px !important; | |||||
| margin-bottom: 15px !important; | margin-bottom: 15px !important; | ||||
| background-color: #fafafa !important; | background-color: #fafafa !important; | ||||
| border-width: 1px !important; | border-width: 1px !important; | ||||