Reviewed-on: https://git.openi.org.cn/OpenI/aiforge/pulls/713tags/v1.21.11.2
| @@ -379,7 +379,7 @@ func GetUnDecompressAttachments() ([]*Attachment, error) { | |||||
| func getUnDecompressAttachments(e Engine) ([]*Attachment, error) { | func getUnDecompressAttachments(e Engine) ([]*Attachment, error) { | ||||
| attachments := make([]*Attachment, 0, 10) | attachments := make([]*Attachment, 0, 10) | ||||
| return attachments, e.Where("decompress_state = ? and dataset_id != 0 and attachment.type = ? and (name like '%.zip' or name like '%.tar.gz' or name like '%.tgz')", DecompressStateInit, TypeCloudBrainOne).Find(&attachments) | |||||
| return attachments, e.Where("decompress_state = ? and dataset_id != 0 and (name like '%.zip' or name like '%.tar.gz' or name like '%.tgz')", DecompressStateInit).Find(&attachments) | |||||
| } | } | ||||
| func GetAllPublicAttachments() ([]*AttachmentUsername, error) { | func GetAllPublicAttachments() ([]*AttachmentUsername, error) { | ||||
| @@ -473,3 +473,7 @@ func GetAttachmentSizeByDatasetID(datasetID int64) (int64, error) { | |||||
| return total, nil | return total, nil | ||||
| } | } | ||||
| func GetAllAttachmentSize() (int64, error) { | |||||
| return x.SumInt(&Attachment{}, "size") | |||||
| } | |||||
| @@ -5,6 +5,7 @@ import ( | |||||
| "fmt" | "fmt" | ||||
| "strings" | "strings" | ||||
| "time" | "time" | ||||
| "xorm.io/builder" | "xorm.io/builder" | ||||
| "xorm.io/xorm" | "xorm.io/xorm" | ||||
| @@ -27,6 +28,8 @@ const ( | |||||
| JobTypeDebug JobType = "DEBUG" | JobTypeDebug JobType = "DEBUG" | ||||
| JobTypeBenchmark JobType = "BENCHMARK" | JobTypeBenchmark JobType = "BENCHMARK" | ||||
| JobTypeSnn4imagenet JobType = "SNN4IMAGENET" | JobTypeSnn4imagenet JobType = "SNN4IMAGENET" | ||||
| JobTypeBrainScore JobType = "BRAINSCORE" | |||||
| JobTypeTrain JobType = "TRAIN" | |||||
| ModelArtsCreateQueue ModelArtsJobStatus = "CREATE_QUEUING" //免费资源创建排队中 | ModelArtsCreateQueue ModelArtsJobStatus = "CREATE_QUEUING" //免费资源创建排队中 | ||||
| ModelArtsCreating ModelArtsJobStatus = "CREATING" //创建中 | ModelArtsCreating ModelArtsJobStatus = "CREATING" //创建中 | ||||
| @@ -46,22 +49,29 @@ const ( | |||||
| ) | ) | ||||
| type Cloudbrain struct { | type Cloudbrain struct { | ||||
| ID int64 `xorm:"pk autoincr"` | |||||
| JobID string `xorm:"INDEX NOT NULL"` | |||||
| JobType string `xorm:"INDEX NOT NULL DEFAULT 'DEBUG'"` | |||||
| JobName string `xorm:"INDEX"` | |||||
| Status string `xorm:"INDEX"` | |||||
| UserID int64 `xorm:"INDEX"` | |||||
| RepoID int64 `xorm:"INDEX"` | |||||
| SubTaskName string `xorm:"INDEX"` | |||||
| ContainerID string | |||||
| ContainerIp string | |||||
| CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | |||||
| UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` | |||||
| DeletedAt time.Time `xorm:"deleted"` | |||||
| CanDebug bool `xorm:"-"` | |||||
| CanDel bool `xorm:"-"` | |||||
| Type int `xorm:"INDEX DEFAULT 0"` | |||||
| ID int64 `xorm:"pk autoincr"` | |||||
| JobID string `xorm:"INDEX NOT NULL"` | |||||
| JobType string `xorm:"INDEX NOT NULL DEFAULT 'DEBUG'"` | |||||
| JobName string `xorm:"INDEX"` | |||||
| Status string `xorm:"INDEX"` | |||||
| UserID int64 `xorm:"INDEX"` | |||||
| RepoID int64 `xorm:"INDEX"` | |||||
| SubTaskName string `xorm:"INDEX"` | |||||
| ContainerID string | |||||
| ContainerIp string | |||||
| CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | |||||
| UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` | |||||
| Duration int64 `xorm:"INDEX duration"` | |||||
| TrainJobDuration string | |||||
| DeletedAt time.Time `xorm:"deleted"` | |||||
| CanDebug bool `xorm:"-"` | |||||
| CanDel bool `xorm:"-"` | |||||
| Type int `xorm:"INDEX DEFAULT 0"` | |||||
| VersionID int64 `xorm:"INDEX DEFAULT 0"` | |||||
| VersionName string | |||||
| Uuid string | |||||
| DatasetName string | |||||
| User *User `xorm:"-"` | User *User `xorm:"-"` | ||||
| Repo *Repository `xorm:"-"` | Repo *Repository `xorm:"-"` | ||||
| @@ -144,29 +154,49 @@ type CloudbrainsOptions struct { | |||||
| SortType string | SortType string | ||||
| CloudbrainIDs []int64 | CloudbrainIDs []int64 | ||||
| // JobStatus CloudbrainStatus | // JobStatus CloudbrainStatus | ||||
| Type int | |||||
| Type int | |||||
| JobType string | |||||
| } | } | ||||
| type TaskPod struct { | type TaskPod struct { | ||||
| TaskRoleStatus struct { | TaskRoleStatus struct { | ||||
| Name string `json:"name"` | Name string `json:"name"` | ||||
| } `json:"taskRoleStatus"` | } `json:"taskRoleStatus"` | ||||
| TaskStatuses []struct { | |||||
| TaskIndex int `json:"taskIndex"` | |||||
| PodUID string `json:"podUid"` | |||||
| PodIP string `json:"podIp"` | |||||
| PodName string `json:"podName"` | |||||
| ContainerID string `json:"containerId"` | |||||
| ContainerIP string `json:"containerIp"` | |||||
| ContainerGpus string `json:"containerGpus"` | |||||
| State string `json:"state"` | |||||
| StartAt time.Time `json:"startAt"` | |||||
| FinishedAt time.Time `json:"finishedAt"` | |||||
| ExitCode int `json:"exitCode"` | |||||
| ExitDiagnostics string `json:"exitDiagnostics"` | |||||
| RetriedCount int `json:"retriedCount"` | |||||
| StartTime string | |||||
| FinishedTime string | |||||
| } `json:"taskStatuses"` | |||||
| //TaskStatuses []struct { | |||||
| // TaskIndex int `json:"taskIndex"` | |||||
| // PodUID string `json:"podUid"` | |||||
| // PodIP string `json:"podIp"` | |||||
| // PodName string `json:"podName"` | |||||
| // ContainerID string `json:"containerId"` | |||||
| // ContainerIP string `json:"containerIp"` | |||||
| // ContainerGpus string `json:"containerGpus"` | |||||
| // State string `json:"state"` | |||||
| // StartAt time.Time `json:"startAt"` | |||||
| // FinishedAt time.Time `json:"finishedAt"` | |||||
| // ExitCode int `json:"exitCode"` | |||||
| // ExitDiagnostics string `json:"exitDiagnostics"` | |||||
| // RetriedCount int `json:"retriedCount"` | |||||
| // StartTime string | |||||
| // FinishedTime string | |||||
| //} `json:"taskStatuses"` | |||||
| TaskStatuses []TaskStatuses `json:"taskStatuses"` | |||||
| } | |||||
| type TaskStatuses struct { | |||||
| TaskIndex int `json:"taskIndex"` | |||||
| PodUID string `json:"podUid"` | |||||
| PodIP string `json:"podIp"` | |||||
| PodName string `json:"podName"` | |||||
| ContainerID string `json:"containerId"` | |||||
| ContainerIP string `json:"containerIp"` | |||||
| ContainerGpus string `json:"containerGpus"` | |||||
| State string `json:"state"` | |||||
| StartAt time.Time `json:"startAt"` | |||||
| FinishedAt time.Time `json:"finishedAt"` | |||||
| ExitCode int `json:"exitCode"` | |||||
| ExitDiagnostics string `json:"exitDiagnostics"` | |||||
| RetriedCount int `json:"retriedCount"` | |||||
| StartTime string | |||||
| FinishedTime string | |||||
| } | } | ||||
| type TaskInfo struct { | type TaskInfo struct { | ||||
| @@ -254,6 +284,11 @@ func ConvertToJobResultPayload(input map[string]interface{}) (JobResultPayload, | |||||
| err := json.Unmarshal(data, &jobResultPayload) | err := json.Unmarshal(data, &jobResultPayload) | ||||
| jobResultPayload.JobStatus.StartTime = time.Unix(jobResultPayload.JobStatus.CreatedTime/1000, 0).Format("2006-01-02 15:04:05") | jobResultPayload.JobStatus.StartTime = time.Unix(jobResultPayload.JobStatus.CreatedTime/1000, 0).Format("2006-01-02 15:04:05") | ||||
| jobResultPayload.JobStatus.EndTime = time.Unix(jobResultPayload.JobStatus.CompletedTime/1000, 0).Format("2006-01-02 15:04:05") | jobResultPayload.JobStatus.EndTime = time.Unix(jobResultPayload.JobStatus.CompletedTime/1000, 0).Format("2006-01-02 15:04:05") | ||||
| if jobResultPayload.JobStatus.State == string(JobWaiting) { | |||||
| jobResultPayload.JobStatus.StartTime = "-" | |||||
| jobResultPayload.JobStatus.EndTime = "-" | |||||
| } | |||||
| return jobResultPayload, err | return jobResultPayload, err | ||||
| } | } | ||||
| @@ -530,6 +565,260 @@ type NotebookDelResult struct { | |||||
| InstanceID string `json:"instance_id"` | InstanceID string `json:"instance_id"` | ||||
| } | } | ||||
| type CreateTrainJobParams struct { | |||||
| JobName string `json:"job_name"` | |||||
| Description string `json:"job_desc"` | |||||
| Config Config `json:"config"` | |||||
| WorkspaceID string `json:"workspace_id"` | |||||
| } | |||||
| type Config struct { | |||||
| WorkServerNum int `json:"worker_server_num"` | |||||
| AppUrl string `json:"app_url"` //训练作业的代码目录 | |||||
| BootFileUrl string `json:"boot_file_url"` //训练作业的代码启动文件,需要在代码目录下 | |||||
| Parameter []Parameter `json:"parameter"` | |||||
| DataUrl string `json:"data_url"` //训练作业需要的数据集OBS路径URL | |||||
| //DatasetID string `json:"dataset_id"` | |||||
| //DataVersionID string `json:"dataset_version_id"` | |||||
| //DataSource []DataSource `json:"data_source"` | |||||
| //SpecID int64 `json:"spec_id"` | |||||
| EngineID int64 `json:"engine_id"` | |||||
| //ModelID int64 `json:"model_id"` | |||||
| TrainUrl string `json:"train_url"` //训练作业的输出文件OBS路径URL | |||||
| LogUrl string `json:"log_url"` | |||||
| //UserImageUrl string `json:"user_image_url"` | |||||
| //UserCommand string `json:"user_command"` | |||||
| CreateVersion bool `json:"create_version"` | |||||
| //Volumes []Volumes `json:"volumes"` | |||||
| Flavor Flavor `json:"flavor"` | |||||
| PoolID string `json:"pool_id"` | |||||
| } | |||||
| type CreateConfigParams struct { | |||||
| ConfigName string `json:"config_name"` | |||||
| Description string `json:"config_desc"` | |||||
| WorkServerNum int `json:"worker_server_num"` | |||||
| AppUrl string `json:"app_url"` //训练作业的代码目录 | |||||
| BootFileUrl string `json:"boot_file_url"` //训练作业的代码启动文件,需要在代码目录下 | |||||
| Parameter []Parameter `json:"parameter"` | |||||
| DataUrl string `json:"data_url"` //训练作业需要的数据集OBS路径URL | |||||
| //DatasetID string `json:"dataset_id"` | |||||
| //DataVersionID string `json:"dataset_version_id"` | |||||
| //DataSource []DataSource `json:"data_source"` | |||||
| //SpecID int64 `json:"spec_id"` | |||||
| EngineID int64 `json:"engine_id"` | |||||
| //ModelID int64 `json:"model_id"` | |||||
| TrainUrl string `json:"train_url"` //训练作业的输出文件OBS路径URL | |||||
| LogUrl string `json:"log_url"` | |||||
| //UserImageUrl string `json:"user_image_url"` | |||||
| //UserCommand string `json:"user_command"` | |||||
| //CreateVersion bool `json:"create_version"` | |||||
| //Volumes []Volumes `json:"volumes"` | |||||
| Flavor Flavor `json:"flavor"` | |||||
| PoolID string `json:"pool_id"` | |||||
| } | |||||
| type Parameter struct { | |||||
| Label string `json:"label"` | |||||
| Value string `json:"value"` | |||||
| } | |||||
| type Parameters struct { | |||||
| Parameter []Parameter `json:"parameter"` | |||||
| } | |||||
| type DataSource struct { | |||||
| DatasetID string `json:"dataset_id"` | |||||
| DatasetVersion string `json:"dataset_version"` | |||||
| Type string `json:"type"` | |||||
| DataUrl string `json:"data_url"` | |||||
| } | |||||
| type Volumes struct { | |||||
| Nfs Nfs `json:"nfs"` | |||||
| HostPath HostPath `json:"host_path"` | |||||
| } | |||||
| type Nfs struct { | |||||
| ID string `json:"id"` | |||||
| SourcePath string `json:"src_path"` | |||||
| DestPath string `json:"dest_path"` | |||||
| ReadOnly bool `json:"read_only"` | |||||
| } | |||||
| type HostPath struct { | |||||
| SourcePath string `json:"src_path"` | |||||
| DestPath string `json:"dest_path"` | |||||
| ReadOnly bool `json:"read_only"` | |||||
| } | |||||
| type Flavor struct { | |||||
| Code string `json:"code"` | |||||
| } | |||||
| type CreateTrainJobResult struct { | |||||
| ErrorCode string `json:"error_code"` | |||||
| ErrorMsg string `json:"error_msg"` | |||||
| IsSuccess bool `json:"is_success"` | |||||
| JobName string `json:"job_name"` | |||||
| JobID int64 `json:"job_id"` | |||||
| Status int `json:"status"` | |||||
| CreateTime int64 `json:"create_time"` | |||||
| VersionID int64 `json:"version_id"` | |||||
| ResourceID string `json:"resource_id"` | |||||
| VersionName string `json:"version_name"` | |||||
| } | |||||
| type CreateTrainJobConfigResult struct { | |||||
| ErrorCode string `json:"error_code"` | |||||
| ErrorMsg string `json:"error_msg"` | |||||
| IsSuccess bool `json:"is_success"` | |||||
| } | |||||
| type GetResourceSpecsResult struct { | |||||
| ErrorCode string `json:"error_code"` | |||||
| ErrorMsg string `json:"error_msg"` | |||||
| IsSuccess bool `json:"is_success"` | |||||
| SpecTotalCount int `json:"spec_total_count"` | |||||
| Specs []Specs `json:"specs"` | |||||
| } | |||||
| type Specs struct { | |||||
| Core string `json:"core"` | |||||
| Cpu string `json:"cpu"` | |||||
| IsNoResource bool `json:"no_resource"` | |||||
| GpuType string `json:"gpu_type"` | |||||
| SpecID int64 `json:"spec_id"` | |||||
| GpuNum int `json:"gpu_num"` | |||||
| SpecCode string `json:"spec_code"` | |||||
| Storage string `json:"storage"` | |||||
| MaxNum int `json:"max_num"` | |||||
| UnitNum int `json:"unit_num"` | |||||
| InterfaceType int `json:"interface_type"` | |||||
| } | |||||
| type GetConfigListResult struct { | |||||
| ErrorCode string `json:"error_code"` | |||||
| ErrorMsg string `json:"error_msg"` | |||||
| IsSuccess bool `json:"is_success"` | |||||
| ConfigTotalCount int `json:"config_total_count"` | |||||
| ParaConfigs []ParaConfig `json:"configs"` | |||||
| } | |||||
| type ParaConfig struct { | |||||
| ConfigName string `json:"config_name"` | |||||
| ConfigDesc string `json:"config_desc"` | |||||
| CreateTime int64 `json:"create_time"` | |||||
| EngineType int `json:"engine_type"` | |||||
| EngineName string `json:"engine_name"` | |||||
| EngineId int64 `json:"engine_id"` | |||||
| EngineVersion string `json:"engine_version"` | |||||
| UserImageUrl string `json:"user_image_url"` | |||||
| UserCommand string `json:"user_command"` | |||||
| Result GetConfigResult | |||||
| } | |||||
| type GetConfigResult struct { | |||||
| ErrorCode string `json:"error_code"` | |||||
| ErrorMsg string `json:"error_msg"` | |||||
| IsSuccess bool `json:"is_success"` | |||||
| ConfigName string `json:"config_name"` | |||||
| Description string `json:"config_desc"` | |||||
| WorkServerNum int `json:"worker_server_num"` | |||||
| AppUrl string `json:"app_url"` //训练作业的代码目录 | |||||
| BootFileUrl string `json:"boot_file_url"` //训练作业的代码启动文件,需要在代码目录下 | |||||
| Parameter []Parameter `json:"parameter"` | |||||
| DataUrl string `json:"data_url"` //训练作业需要的数据集OBS路径URL | |||||
| //DatasetID string `json:"dataset_id"` | |||||
| //DataVersionID string `json:"dataset_version_id"` | |||||
| //DataSource []DataSource `json:"data_source"` | |||||
| //SpecID int64 `json:"spec_id"` | |||||
| EngineID int64 `json:"engine_id"` | |||||
| //ModelID int64 `json:"model_id"` | |||||
| TrainUrl string `json:"train_url"` //训练作业的输出文件OBS路径URL | |||||
| LogUrl string `json:"log_url"` | |||||
| //UserImageUrl string `json:"user_image_url"` | |||||
| //UserCommand string `json:"user_command"` | |||||
| //CreateVersion bool `json:"create_version"` | |||||
| //Volumes []Volumes `json:"volumes"` | |||||
| Flavor Flavor `json:"flavor"` | |||||
| PoolID string `json:"pool_id"` | |||||
| } | |||||
| type ErrorResult struct { | |||||
| ErrorCode string `json:"error_code"` | |||||
| ErrorMsg string `json:"error_message"` | |||||
| IsSuccess bool `json:"is_success"` | |||||
| } | |||||
| type GetTrainJobResult struct { | |||||
| IsSuccess bool `json:"is_success"` | |||||
| JobName string `json:"job_name"` | |||||
| JobID int64 `json:"job_id"` | |||||
| Description string `json:"job_desc"` | |||||
| IntStatus int `json:"status"` | |||||
| Status string | |||||
| LongCreateTime int64 `json:"create_time"` | |||||
| CreateTime string | |||||
| Duration int64 `json:"duration"` //训练作业的运行时间,单位为毫秒 | |||||
| TrainJobDuration string //训练作业的运行时间,格式为hh:mm:ss | |||||
| VersionID int64 `json:"version_id"` | |||||
| ResourceID string `json:"resource_id"` | |||||
| VersionName string `json:"version_name"` | |||||
| PreVersionID int64 `json:"pre_version_id"` | |||||
| WorkServerNum int `json:"worker_server_num"` | |||||
| AppUrl string `json:"app_url"` //训练作业的代码目录 | |||||
| BootFileUrl string `json:"boot_file_url"` //训练作业的代码启动文件,需要在代码目录下 | |||||
| Parameter []Parameter `json:"parameter"` | |||||
| DataUrl string `json:"data_url"` //训练作业需要的数据集OBS路径URL | |||||
| //DatasetID string `json:"dataset_id"` | |||||
| //DataVersionID string `json:"dataset_version_id"` | |||||
| //DataSource []DataSource `json:"data_source"` | |||||
| //SpecID int64 `json:"spec_id"` | |||||
| EngineID int64 `json:"engine_id"` | |||||
| EngineName string `json:"engine_name"` | |||||
| EngineVersion string `json:"engine_version"` | |||||
| //ModelID int64 `json:"model_id"` | |||||
| TrainUrl string `json:"train_url"` //训练作业的输出文件OBS路径URL | |||||
| LogUrl string `json:"log_url"` | |||||
| //UserImageUrl string `json:"user_image_url"` | |||||
| //UserCommand string `json:"user_command"` | |||||
| //Volumes []Volumes `json:"volumes"` | |||||
| Flavor Flavor `json:"flavor"` | |||||
| PoolID string `json:"pool_id"` | |||||
| PoolName string `json:"pool_name"` | |||||
| NasMountPath string `json:"nas_mount_path"` | |||||
| NasShareAddr string `json:"nas_share_addr"` | |||||
| DatasetName string | |||||
| } | |||||
| type GetTrainJobLogResult struct { | |||||
| ErrorCode string `json:"error_code"` | |||||
| ErrorMsg string `json:"error_msg"` | |||||
| IsSuccess bool `json:"is_success"` | |||||
| Content string `json:"content"` | |||||
| Lines int `json:"lines"` | |||||
| StartLine string `json:"start_line"` | |||||
| EndLine string `json:"end_line"` | |||||
| } | |||||
| type GetTrainJobLogFileNamesResult struct { | |||||
| ErrorCode string `json:"error_code"` | |||||
| ErrorMsg string `json:"error_msg"` | |||||
| IsSuccess bool `json:"is_success"` | |||||
| LogFileList []string `json:"log_file_list"` | |||||
| } | |||||
| type TrainJobResult struct { | |||||
| ErrorCode string `json:"error_code"` | |||||
| ErrorMsg string `json:"error_msg"` | |||||
| IsSuccess bool `json:"is_success"` | |||||
| } | |||||
| type LogFile struct { | |||||
| Name string | |||||
| } | |||||
| func Cloudbrains(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int64, error) { | func Cloudbrains(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int64, error) { | ||||
| sess := x.NewSession() | sess := x.NewSession() | ||||
| defer sess.Close() | defer sess.Close() | ||||
| @@ -559,6 +848,12 @@ func Cloudbrains(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int64, error) { | |||||
| ) | ) | ||||
| } | } | ||||
| if (opts.JobType) != "" { | |||||
| cond = cond.And( | |||||
| builder.Eq{"cloudbrain.job_type": opts.JobType}, | |||||
| ) | |||||
| } | |||||
| // switch opts.JobStatus { | // switch opts.JobStatus { | ||||
| // case JobWaiting: | // case JobWaiting: | ||||
| // cond.And(builder.Eq{"cloudbrain.status": int(JobWaiting)}) | // cond.And(builder.Eq{"cloudbrain.status": int(JobWaiting)}) | ||||
| @@ -647,6 +942,12 @@ func SetCloudbrainStatusByJobID(jobID string, status CloudbrainStatus) (err erro | |||||
| return | return | ||||
| } | } | ||||
| func SetTrainJobStatusByJobID(jobID string, status string, duration int64, trainjobduration string) (err error) { | |||||
| cb := &Cloudbrain{JobID: jobID, Status: string(status), Duration: duration, TrainJobDuration: trainjobduration} | |||||
| _, err = x.Cols("status", "duration", "train_job_duration").Where("cloudbrain.job_id=?", jobID).Update(cb) | |||||
| return | |||||
| } | |||||
| func UpdateJob(job *Cloudbrain) error { | func UpdateJob(job *Cloudbrain) error { | ||||
| return updateJob(x, job) | return updateJob(x, job) | ||||
| } | } | ||||
| @@ -658,6 +959,17 @@ func updateJob(e Engine, job *Cloudbrain) error { | |||||
| return err | return err | ||||
| } | } | ||||
| // func UpdateTrainJob(job *CloudbrainInfo) error { | |||||
| // return updateTrainJob(x, job) | |||||
| // } | |||||
| // func updateTrainJob(e Engine, job *CloudbrainInfo) error { | |||||
| // var sess *xorm.Session | |||||
| // sess = e.Where("job_id = ?", job.Cloudbrain.JobID) | |||||
| // _, err := sess.Cols("status", "container_id", "container_ip").Update(job) | |||||
| // return err | |||||
| // } | |||||
| func DeleteJob(job *Cloudbrain) error { | func DeleteJob(job *Cloudbrain) error { | ||||
| return deleteJob(x, job) | return deleteJob(x, job) | ||||
| } | } | ||||
| @@ -673,7 +985,7 @@ func GetCloudbrainByName(jobName string) (*Cloudbrain, error) { | |||||
| } | } | ||||
| func CanDelJob(isSigned bool, user *User, job *CloudbrainInfo) bool { | func CanDelJob(isSigned bool, user *User, job *CloudbrainInfo) bool { | ||||
| if !isSigned || job.Status != string(JobStopped) { | |||||
| if !isSigned || (job.Status != string(JobStopped) && job.Status != string(JobFailed) && job.Status != string(ModelArtsStartFailed) && job.Status != string(ModelArtsCreateFailed)) { | |||||
| return false | return false | ||||
| } | } | ||||
| repo, err := GetRepositoryByID(job.RepoID) | repo, err := GetRepositoryByID(job.RepoID) | ||||
| @@ -139,7 +139,14 @@ func SearchDatasetCondition(opts *SearchDatasetOptions) builder.Cond { | |||||
| if opts.IncludePublic { | if opts.IncludePublic { | ||||
| cond = cond.And(builder.Eq{"dataset.status": DatasetStatusPublic}) | cond = cond.And(builder.Eq{"dataset.status": DatasetStatusPublic}) | ||||
| if opts.OwnerID > 0 { | if opts.OwnerID > 0 { | ||||
| cond = cond.Or(builder.Eq{"repository.owner_id": opts.OwnerID}) | |||||
| if len(opts.Keyword) == 0 { | |||||
| cond = cond.Or(builder.Eq{"repository.owner_id": opts.OwnerID}) | |||||
| } else { | |||||
| subCon := builder.NewCond() | |||||
| subCon = subCon.And(builder.Eq{"repository.owner_id": opts.OwnerID}, builder.Like{"dataset.title", opts.Keyword}) | |||||
| cond = cond.Or(subCon) | |||||
| } | |||||
| } | } | ||||
| } else if opts.OwnerID > 0 { | } else if opts.OwnerID > 0 { | ||||
| cond = cond.And(builder.Eq{"repository.owner_id": opts.OwnerID}) | cond = cond.And(builder.Eq{"repository.owner_id": opts.OwnerID}) | ||||
| @@ -14,8 +14,8 @@ const ( | |||||
| ) | ) | ||||
| const ( | const ( | ||||
| TypeCloudBrainOne = 0 | |||||
| TypeCloudBrainTwo = 1 | |||||
| TypeCloudBrainOne int = iota | |||||
| TypeCloudBrainTwo | |||||
| ) | ) | ||||
| type FileChunk struct { | type FileChunk struct { | ||||
| @@ -137,7 +137,9 @@ func init() { | |||||
| tablesStatistic = append(tablesStatistic, | tablesStatistic = append(tablesStatistic, | ||||
| new(RepoStatistic), | new(RepoStatistic), | ||||
| new(SummaryStatistic), | |||||
| new(UserBusinessAnalysis), | new(UserBusinessAnalysis), | ||||
| new(UserLoginLog), | |||||
| ) | ) | ||||
| gonicNames := []string{"SSL", "UID"} | gonicNames := []string{"SSL", "UID"} | ||||
| @@ -6,13 +6,14 @@ | |||||
| package models | package models | ||||
| import ( | import ( | ||||
| "code.gitea.io/gitea/modules/blockchain" | |||||
| "context" | "context" | ||||
| "crypto/md5" | "crypto/md5" | ||||
| "errors" | "errors" | ||||
| "fmt" | "fmt" | ||||
| "html/template" | "html/template" | ||||
| "code.gitea.io/gitea/modules/blockchain" | |||||
| // Needed for jpeg support | // Needed for jpeg support | ||||
| _ "image/jpeg" | _ "image/jpeg" | ||||
| "image/png" | "image/png" | ||||
| @@ -171,11 +172,11 @@ type Repository struct { | |||||
| NumOpenIssues int `xorm:"-"` | NumOpenIssues int `xorm:"-"` | ||||
| NumPulls int | NumPulls int | ||||
| NumClosedPulls int | NumClosedPulls int | ||||
| NumOpenPulls int `xorm:"-"` | |||||
| NumMilestones int `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumClosedMilestones int `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumOpenMilestones int `xorm:"-"` | |||||
| NumCommit int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumOpenPulls int `xorm:"-"` | |||||
| NumMilestones int `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumClosedMilestones int `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumOpenMilestones int `xorm:"-"` | |||||
| NumCommit int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| IsPrivate bool `xorm:"INDEX"` | IsPrivate bool `xorm:"INDEX"` | ||||
| IsEmpty bool `xorm:"INDEX"` | IsEmpty bool `xorm:"INDEX"` | ||||
| @@ -215,8 +216,8 @@ type Repository struct { | |||||
| CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | ||||
| UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` | UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` | ||||
| Hot int64 `xorm:"-"` | |||||
| Active int64 `xorm:"-"` | |||||
| Hot int64 `xorm:"-"` | |||||
| Active int64 `xorm:"-"` | |||||
| } | } | ||||
| // SanitizedOriginalURL returns a sanitized OriginalURL | // SanitizedOriginalURL returns a sanitized OriginalURL | ||||
| @@ -1430,6 +1431,15 @@ func GetAllRepositoriesByFilterCols(columns ...string) ([]*Repository, error) { | |||||
| } | } | ||||
| func GetAllRepositoriesCount() (int64, error) { | |||||
| repo := new(Repository) | |||||
| return x.Count(repo) | |||||
| } | |||||
| func GetAllRepositoriesSize() (int64, error) { | |||||
| return x.SumInt(&Repository{}, "size") | |||||
| } | |||||
| func updateRepository(e Engine, repo *Repository, visibilityChanged bool) (err error) { | func updateRepository(e Engine, repo *Repository, visibilityChanged bool) (err error) { | ||||
| repo.LowerName = strings.ToLower(repo.Name) | repo.LowerName = strings.ToLower(repo.Name) | ||||
| @@ -2464,7 +2474,7 @@ func (repo *Repository) IncreaseCloneCnt() { | |||||
| } | } | ||||
| func UpdateRepositoryCommitNum(repo *Repository) error { | func UpdateRepositoryCommitNum(repo *Repository) error { | ||||
| if _,err := x.Exec("UPDATE `repository` SET num_commit = ? where id = ?", repo.NumCommit, repo.ID); err != nil { | |||||
| if _, err := x.Exec("UPDATE `repository` SET num_commit = ? where id = ?", repo.NumCommit, repo.ID); err != nil { | |||||
| return err | return err | ||||
| } | } | ||||
| @@ -1,13 +1,115 @@ | |||||
| package models | package models | ||||
| import "code.gitea.io/gitea/modules/git" | |||||
| import ( | |||||
| "fmt" | |||||
| "strings" | |||||
| "time" | |||||
| "code.gitea.io/gitea/modules/git" | |||||
| ) | |||||
| func GetRepoKPIStats(repo *Repository) (*git.RepoKPIStats, error) { | func GetRepoKPIStats(repo *Repository) (*git.RepoKPIStats, error) { | ||||
| wikiPath := "" | wikiPath := "" | ||||
| if repo.HasWiki() { | if repo.HasWiki() { | ||||
| wikiPath = repo.WikiPath() | wikiPath = repo.WikiPath() | ||||
| } | } | ||||
| return git.GetRepoKPIStats(repo.RepoPath(), wikiPath) | |||||
| return getRepoKPIStats(repo.RepoPath(), wikiPath) | |||||
| } | |||||
| func getRepoKPIStats(repoPath string, wikiPath string) (*git.RepoKPIStats, error) { | |||||
| stats := &git.RepoKPIStats{} | |||||
| contributors, err := git.GetContributors(repoPath) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| timeUntil := time.Now() | |||||
| fourMonthAgo := timeUntil.AddDate(0, -4, 0) | |||||
| recentlyContributors, err := git.GetContributorsDetail(repoPath, fourMonthAgo) | |||||
| newContributersDict := make(map[string]struct{}) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| if contributors != nil { | |||||
| contributorDistinctDict := make(map[string]int, 0) | |||||
| keyContributorsDict := make(map[string]struct{}, 0) | |||||
| for _, contributor := range contributors { | |||||
| if strings.Compare(contributor.Email, "") == 0 { | |||||
| continue | |||||
| } | |||||
| user, err := GetUserByActivateEmail(contributor.Email) | |||||
| if err == nil { | |||||
| value, ok := contributorDistinctDict[user.Email] | |||||
| if !ok { | |||||
| contributorDistinctDict[user.Email] = contributor.CommitCnt | |||||
| } else { | |||||
| contributorDistinctDict[user.Email] = value + contributor.CommitCnt | |||||
| } | |||||
| setKeyContributerDict(contributorDistinctDict, user.Email, keyContributorsDict) | |||||
| } else { | |||||
| value, ok := contributorDistinctDict[contributor.Email] | |||||
| if !ok { | |||||
| contributorDistinctDict[contributor.Email] = contributor.CommitCnt | |||||
| } else { | |||||
| contributorDistinctDict[contributor.Email] = value + contributor.CommitCnt | |||||
| } | |||||
| setKeyContributerDict(contributorDistinctDict, contributor.Email, keyContributorsDict) | |||||
| } | |||||
| } | |||||
| if recentlyContributors != nil { | |||||
| for _, recentlyContributor := range recentlyContributors { | |||||
| user, err := GetUserByActivateEmail(recentlyContributor.Email) | |||||
| var ok bool | |||||
| if err == nil { | |||||
| _, ok = contributorDistinctDict[user.Email] | |||||
| } else { | |||||
| _, ok = contributorDistinctDict[recentlyContributor.Email] | |||||
| } | |||||
| if !ok { | |||||
| stats.ContributorsAdded++ | |||||
| newContributersDict[recentlyContributor.Email] = struct{}{} | |||||
| } | |||||
| } | |||||
| } | |||||
| stats.Contributors = int64(len(contributorDistinctDict)) | |||||
| stats.KeyContributors = int64(len(keyContributorsDict)) | |||||
| } | |||||
| err = git.SetDevelopAge(repoPath, stats) | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("FillFromGit: %v", err) | |||||
| } | |||||
| err = git.SetRepoKPIStats(repoPath, fourMonthAgo, stats, newContributersDict) | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("FillFromGit: %v", err) | |||||
| } | |||||
| git.SetWikiPages(wikiPath, stats) | |||||
| return stats, nil | |||||
| } | |||||
| func setKeyContributerDict(contributorDistinctDict map[string]int, email string, keyContributorsDict map[string]struct{}) { | |||||
| if contributorDistinctDict[email] >= 3 { | |||||
| _, ok := keyContributorsDict[email] | |||||
| if !ok { | |||||
| keyContributorsDict[email] = struct{}{} | |||||
| } | |||||
| } | |||||
| } | } | ||||
| func GetAllUserKPIStats() (map[string]*git.UserKPIStats, error) { | func GetAllUserKPIStats() (map[string]*git.UserKPIStats, error) { | ||||
| @@ -1,38 +1,64 @@ | |||||
| package models | package models | ||||
| import ( | import ( | ||||
| "code.gitea.io/gitea/modules/timeutil" | |||||
| "fmt" | "fmt" | ||||
| "time" | |||||
| "code.gitea.io/gitea/modules/timeutil" | |||||
| ) | ) | ||||
| // RepoStatistic statistic info of all repository | // RepoStatistic statistic info of all repository | ||||
| type RepoStatistic struct { | type RepoStatistic struct { | ||||
| ID int64 `xorm:"pk autoincr"` | |||||
| RepoID int64 `xorm:"unique(s) NOT NULL"` | |||||
| Date string `xorm:"unique(s) NOT NULL"` | |||||
| NumWatches int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumStars int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumForks int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumDownloads int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumComments int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumVisits int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumClosedIssues int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumVersions int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| //develop months | |||||
| NumDevMonths int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| RepoSize int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| DatasetSize int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumModels int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumWikiViews int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumCommits int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumIssues int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumPulls int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| IssueFixedRate float32 `xorm:"NOT NULL"` | |||||
| NumContributor int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumKeyContributor int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | |||||
| UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` | |||||
| ID int64 `xorm:"pk autoincr"` | |||||
| RepoID int64 `xorm:"unique(s) NOT NULL"` | |||||
| Name string `xorm:"INDEX"` | |||||
| IsPrivate bool | |||||
| Date string `xorm:"unique(s) NOT NULL"` | |||||
| NumWatches int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumWatchesAdded int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumStars int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumStarsAdded int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumForks int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumForksAdded int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumDownloads int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumDownloadsAdded int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumComments int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumCommentsAdded int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumVisits int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumClosedIssues int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumClosedIssuesAdded int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumVersions int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumDevMonths int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| RepoSize int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| DatasetSize int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumModels int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumWikiViews int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumCommits int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumCommitsAdded int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumIssues int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumIssuesAdded int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumPulls int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumPullsAdded int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| IssueFixedRate float32 `xorm:"NOT NULL"` | |||||
| NumContributor int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumContributorAdded int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumKeyContributor int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumContributorsGrowth int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumCommitsGrowth int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumCommitLinesGrowth int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumIssuesGrowth int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumCommentsGrowth int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| Impact float64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| Completeness float64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| Liveness float64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| ProjectHealth float64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| TeamHealth float64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| Growth float64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| RadarTotal float64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | |||||
| UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` | |||||
| } | } | ||||
| func DeleteRepoStatDaily(date string) error { | func DeleteRepoStatDaily(date string) error { | ||||
| @@ -55,6 +81,35 @@ func DeleteRepoStatDaily(date string) error { | |||||
| return nil | return nil | ||||
| } | } | ||||
| func GetRepoStatisticByDate(date string) ([]*RepoStatistic, error) { | |||||
| repoStatistics := make([]*RepoStatistic, 0) | |||||
| err := xStatistic.Where("date = ?", date).Find(&repoStatistics) | |||||
| return repoStatistics, err | |||||
| } | |||||
| func GetOneRepoStatisticBeforeTime(time time.Time) (*RepoStatistic, error) { | |||||
| repoStatistics := make([]*RepoStatistic, 0) | |||||
| err := xStatistic.Where("created_unix >= ?", time.Unix()).OrderBy("created_unix").Limit(1).Find(&repoStatistics) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } else { | |||||
| if len(repoStatistics) == 0 { | |||||
| return nil, fmt.Errorf("the repo statistic record count is 0") | |||||
| } else { | |||||
| return repoStatistics[0], nil | |||||
| } | |||||
| } | |||||
| } | |||||
| func InsertRepoStat(repoStat *RepoStatistic) (int64, error) { | func InsertRepoStat(repoStat *RepoStatistic) (int64, error) { | ||||
| return xStatistic.Insert(repoStat) | return xStatistic.Insert(repoStat) | ||||
| } | } | ||||
| func UpdateRepoStat(repoStat *RepoStatistic) error { | |||||
| sql := "update repo_statistic set impact=?,completeness=?,liveness=?,project_health=?,team_health=?,growth=?,radar_total=? where repo_id=? and date=?" | |||||
| _, err := xStatistic.Exec(sql, repoStat.Impact, repoStat.Completeness, repoStat.Liveness, repoStat.ProjectHealth, repoStat.TeamHealth, repoStat.Growth, repoStat.RadarTotal, repoStat.RepoID, repoStat.Date) | |||||
| return err | |||||
| } | |||||
| @@ -0,0 +1,69 @@ | |||||
| package models | |||||
| import ( | |||||
| "fmt" | |||||
| "code.gitea.io/gitea/modules/timeutil" | |||||
| ) | |||||
| var DomainMap = map[string]int{ | |||||
| "大模型": 0, | |||||
| "ai开发工具": 1, | |||||
| "计算机视觉": 2, | |||||
| "自然语言处理": 3, | |||||
| "机器学习": 4, | |||||
| "神经网络": 5, | |||||
| "自动驾驶": 6, | |||||
| "机器人": 7, | |||||
| "联邦学习": 8, | |||||
| "数据挖掘": 9, | |||||
| "risc-v开发": 10, | |||||
| } | |||||
| type SummaryStatistic struct { | |||||
| ID int64 `xorm:"pk autoincr"` | |||||
| Date string `xorm:"unique(s) NOT NULL"` | |||||
| NumUsers int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| RepoSize int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| DatasetSize int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumOrganizations int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumModels int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumRepos int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumRepoBigModel int `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumRepoAI int `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumRepoVision int `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumRepoNLP int `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumRepoML int `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumRepoNN int `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumRepoAutoDrive int `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumRepoRobot int `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumRepoLeagueLearn int `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumRepoDataMining int `xorm:"NOT NULL DEFAULT 0"` | |||||
| NumRepoRISC int `xorm:"NOT NULL DEFAULT 0"` | |||||
| CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | |||||
| UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` | |||||
| } | |||||
| func DeleteSummaryStatisticDaily(date string) error { | |||||
| sess := xStatistic.NewSession() | |||||
| defer sess.Close() | |||||
| if err := sess.Begin(); err != nil { | |||||
| return fmt.Errorf("Begin: %v", err) | |||||
| } | |||||
| if _, err := sess.Where("date = ?", date).Delete(&SummaryStatistic{}); err != nil { | |||||
| return fmt.Errorf("Delete: %v", err) | |||||
| } | |||||
| if err := sess.Commit(); err != nil { | |||||
| sess.Close() | |||||
| return fmt.Errorf("Commit: %v", err) | |||||
| } | |||||
| sess.Close() | |||||
| return nil | |||||
| } | |||||
| func InsertSummaryStatistic(summaryStatistic *SummaryStatistic) (int64, error) { | |||||
| return xStatistic.Insert(summaryStatistic) | |||||
| } | |||||
| @@ -98,6 +98,13 @@ func GetTopicByName(name string) (*Topic, error) { | |||||
| return &topic, nil | return &topic, nil | ||||
| } | } | ||||
| func GetAllUsedTopics() ([]*Topic, error) { | |||||
| topics := make([]*Topic, 0) | |||||
| err := x.Where("repo_count > ?", 0).Find(&topics) | |||||
| return topics, err | |||||
| } | |||||
| // addTopicByNameToRepo adds a topic name to a repo and increments the topic count. | // addTopicByNameToRepo adds a topic name to a repo and increments the topic count. | ||||
| // Returns topic after the addition | // Returns topic after the addition | ||||
| func addTopicByNameToRepo(e Engine, repoID int64, topicName string) (*Topic, error) { | func addTopicByNameToRepo(e Engine, repoID int64, topicName string) (*Topic, error) { | ||||
| @@ -178,7 +185,7 @@ func (opts *FindTopicOptions) toConds() builder.Cond { | |||||
| } | } | ||||
| if opts.Keyword != "" { | if opts.Keyword != "" { | ||||
| cond = cond.And(builder.Like{"topic.name", opts.Keyword}) | |||||
| cond = cond.And(builder.Like{"topic.name", strings.ToLower(opts.Keyword)}) | |||||
| } | } | ||||
| return cond | return cond | ||||
| @@ -2071,6 +2071,18 @@ func SyncExternalUsers(ctx context.Context, updateExisting bool) error { | |||||
| return nil | return nil | ||||
| } | } | ||||
| func GetUsersCount() (int64, error) { | |||||
| user := new(User) | |||||
| return x.Where("type=0").Count(user) | |||||
| } | |||||
| func GetOrganizationsCount() (int64, error) { | |||||
| user := new(User) | |||||
| return x.Where("type=1").Count(user) | |||||
| } | |||||
| func GetBlockChainUnSuccessUsers() ([]*User, error) { | func GetBlockChainUnSuccessUsers() ([]*User, error) { | ||||
| users := make([]*User, 0, 10) | users := make([]*User, 0, 10) | ||||
| err := x.Where("public_key = ''"). | err := x.Where("public_key = ''"). | ||||
| @@ -71,7 +71,50 @@ type UserBusinessAnalysis struct { | |||||
| Name string `xorm:"NOT NULL"` | Name string `xorm:"NOT NULL"` | ||||
| } | } | ||||
| func CountData(wikiCountMap map[string]int) { | |||||
| func QueryUserStaticData(startTime int64, endTime int64) []*UserBusinessAnalysis { | |||||
| log.Info("query startTime =" + fmt.Sprint(startTime) + " endTime=" + fmt.Sprint(endTime)) | |||||
| statictisSess := xStatistic.NewSession() | |||||
| defer statictisSess.Close() | |||||
| statictisSess.Select("*").Table("user_business_analysis").Where(" count_date>=" + fmt.Sprint(startTime) + " and count_date<=" + fmt.Sprint(endTime)).OrderBy("count_date desc") | |||||
| userBusinessAnalysisList := make([]*UserBusinessAnalysis, 0) | |||||
| statictisSess.Find(&userBusinessAnalysisList) | |||||
| resultMap := make(map[int64]*UserBusinessAnalysis) | |||||
| log.Info("query result size=" + fmt.Sprint(len(userBusinessAnalysisList))) | |||||
| for _, userRecord := range userBusinessAnalysisList { | |||||
| if _, ok := resultMap[userRecord.ID]; !ok { | |||||
| resultMap[userRecord.ID] = userRecord | |||||
| } else { | |||||
| resultMap[userRecord.ID].CodeMergeCount += userRecord.CodeMergeCount | |||||
| resultMap[userRecord.ID].CommitCount += userRecord.CommitCount | |||||
| resultMap[userRecord.ID].IssueCount += userRecord.IssueCount | |||||
| resultMap[userRecord.ID].CommentCount += userRecord.CommentCount | |||||
| resultMap[userRecord.ID].FocusRepoCount += userRecord.FocusRepoCount | |||||
| resultMap[userRecord.ID].StarRepoCount += userRecord.StarRepoCount | |||||
| resultMap[userRecord.ID].WatchedCount += userRecord.WatchedCount | |||||
| resultMap[userRecord.ID].CommitCodeSize += userRecord.CommitCodeSize | |||||
| resultMap[userRecord.ID].CommitDatasetSize += userRecord.CommitDatasetSize | |||||
| resultMap[userRecord.ID].CommitModelCount += userRecord.CommitModelCount | |||||
| resultMap[userRecord.ID].SolveIssueCount += userRecord.SolveIssueCount | |||||
| resultMap[userRecord.ID].EncyclopediasCount += userRecord.EncyclopediasCount | |||||
| resultMap[userRecord.ID].CreateRepoCount += userRecord.CreateRepoCount | |||||
| resultMap[userRecord.ID].LoginCount += userRecord.LoginCount | |||||
| } | |||||
| } | |||||
| userBusinessAnalysisReturnList := make([]*UserBusinessAnalysis, len(resultMap)) | |||||
| index := 0 | |||||
| for _, v := range resultMap { | |||||
| userBusinessAnalysisReturnList[index] = v | |||||
| index += 1 | |||||
| } | |||||
| log.Info("return size=" + fmt.Sprint(len(userBusinessAnalysisReturnList))) | |||||
| return userBusinessAnalysisReturnList | |||||
| } | |||||
| func CounDataByDate(wikiCountMap map[string]int, startTime time.Time, endTime time.Time) { | |||||
| log.Info("start to count other user info data") | log.Info("start to count other user info data") | ||||
| sess := x.NewSession() | sess := x.NewSession() | ||||
| defer sess.Close() | defer sess.Close() | ||||
| @@ -82,17 +125,17 @@ func CountData(wikiCountMap map[string]int) { | |||||
| currentTimeNow := time.Now() | currentTimeNow := time.Now() | ||||
| log.Info("current time:" + currentTimeNow.Format("2006-01-02 15:04:05")) | log.Info("current time:" + currentTimeNow.Format("2006-01-02 15:04:05")) | ||||
| yesterday := currentTimeNow.AddDate(0, 0, -1) | |||||
| startTime := time.Date(yesterday.Year(), yesterday.Month(), yesterday.Day(), 0, 0, 0, 0, yesterday.Location()) | |||||
| //yesterday := currentTimeNow.AddDate(0, 0, -1) | |||||
| //startTime := time.Date(yesterday.Year(), yesterday.Month(), yesterday.Day(), 0, 0, 0, 0, yesterday.Location()) | |||||
| start_unix := startTime.Unix() | start_unix := startTime.Unix() | ||||
| log.Info("DB query time:" + startTime.Format("2006-01-02 15:04:05")) | log.Info("DB query time:" + startTime.Format("2006-01-02 15:04:05")) | ||||
| endTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 0, 0, 0, 0, currentTimeNow.Location()) | |||||
| //endTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 0, 0, 0, 0, currentTimeNow.Location()) | |||||
| end_unix := endTime.Unix() | end_unix := endTime.Unix() | ||||
| CountDate := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 0, 1, 0, 0, currentTimeNow.Location()) | CountDate := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 0, 1, 0, 0, currentTimeNow.Location()) | ||||
| CodeMergeCountMap := queryAction(start_unix, end_unix, 11) | |||||
| CodeMergeCountMap := queryPullRequest(start_unix, end_unix) | |||||
| CommitCountMap := queryAction(start_unix, end_unix, 5) | CommitCountMap := queryAction(start_unix, end_unix, 5) | ||||
| IssueCountMap := queryAction(start_unix, end_unix, 10) | IssueCountMap := queryAction(start_unix, end_unix, 10) | ||||
| @@ -110,12 +153,19 @@ func CountData(wikiCountMap map[string]int) { | |||||
| CommitDatasetSizeMap := queryDatasetSize(start_unix, end_unix) | CommitDatasetSizeMap := queryDatasetSize(start_unix, end_unix) | ||||
| SolveIssueCountMap := querySolveIssue(start_unix, end_unix) | SolveIssueCountMap := querySolveIssue(start_unix, end_unix) | ||||
| CreateRepoCountMap := queryUserCreateRepo(start_unix, end_unix) | CreateRepoCountMap := queryUserCreateRepo(start_unix, end_unix) | ||||
| LoginCountMap := queryLoginCount(start_unix, end_unix) | |||||
| statictisSess := xStatistic.NewSession() | |||||
| defer statictisSess.Close() | |||||
| for i, userRecord := range userList { | for i, userRecord := range userList { | ||||
| var dateRecord UserBusinessAnalysis | var dateRecord UserBusinessAnalysis | ||||
| dateRecord.ID = userRecord.ID | dateRecord.ID = userRecord.ID | ||||
| log.Info("i=" + fmt.Sprint(i) + " userName=" + userRecord.Name) | log.Info("i=" + fmt.Sprint(i) + " userName=" + userRecord.Name) | ||||
| dateRecord.CountDate = CountDate.Unix() | dateRecord.CountDate = CountDate.Unix() | ||||
| statictisSess.Delete(&dateRecord) | |||||
| dateRecord.Email = userRecord.Email | dateRecord.Email = userRecord.Email | ||||
| dateRecord.RegistDate = userRecord.CreatedUnix | dateRecord.RegistDate = userRecord.CreatedUnix | ||||
| dateRecord.Name = userRecord.Name | dateRecord.Name = userRecord.Name | ||||
| @@ -192,10 +242,14 @@ func CountData(wikiCountMap map[string]int) { | |||||
| dateRecord.CreateRepoCount = CreateRepoCountMap[dateRecord.ID] | dateRecord.CreateRepoCount = CreateRepoCountMap[dateRecord.ID] | ||||
| } | } | ||||
| if _, ok := LoginCountMap[dateRecord.ID]; !ok { | |||||
| dateRecord.LoginCount = 0 | |||||
| } else { | |||||
| dateRecord.LoginCount = LoginCountMap[dateRecord.ID] | |||||
| } | |||||
| dateRecord.CommitModelCount = 0 | dateRecord.CommitModelCount = 0 | ||||
| statictisSess := xStatistic.NewSession() | |||||
| defer statictisSess.Close() | |||||
| statictisSess.Insert(&dateRecord) | statictisSess.Insert(&dateRecord) | ||||
| } | } | ||||
| @@ -223,6 +277,28 @@ func querySolveIssue(start_unix int64, end_unix int64) map[int64]int { | |||||
| } | } | ||||
| func queryPullRequest(start_unix int64, end_unix int64) map[int64]int { | |||||
| sess := x.NewSession() | |||||
| defer sess.Close() | |||||
| sess.Select("issue.*").Table("issue"). | |||||
| Join("inner", "pull_request", "issue.id=pull_request.issue_id"). | |||||
| Where("pull_request.merged_unix>=" + fmt.Sprint(start_unix) + " and pull_request.merged_unix<=" + fmt.Sprint(end_unix)) | |||||
| issueList := make([]*Issue, 0) | |||||
| sess.Find(&issueList) | |||||
| resultMap := make(map[int64]int) | |||||
| log.Info("query issue(PR) size=" + fmt.Sprint(len(issueList))) | |||||
| for _, issueRecord := range issueList { | |||||
| if _, ok := resultMap[issueRecord.PosterID]; !ok { | |||||
| resultMap[issueRecord.PosterID] = 1 | |||||
| } else { | |||||
| resultMap[issueRecord.PosterID] += 1 | |||||
| } | |||||
| } | |||||
| return resultMap | |||||
| } | |||||
| func queryAction(start_unix int64, end_unix int64, actionType int64) map[int64]int { | func queryAction(start_unix int64, end_unix int64, actionType int64) map[int64]int { | ||||
| sess := x.NewSession() | sess := x.NewSession() | ||||
| defer sess.Close() | defer sess.Close() | ||||
| @@ -341,7 +417,7 @@ func queryDatasetSize(start_unix int64, end_unix int64) map[int64]int { | |||||
| func queryUserCreateRepo(start_unix int64, end_unix int64) map[int64]int { | func queryUserCreateRepo(start_unix int64, end_unix int64) map[int64]int { | ||||
| sess := x.NewSession() | sess := x.NewSession() | ||||
| defer sess.Close() | defer sess.Close() | ||||
| sess.Select("id,owner_id,name").Table("repository").Where(" created_unix>=" + fmt.Sprint(start_unix) + " and created_unix<=" + fmt.Sprint(end_unix)) | |||||
| sess.Select("id,owner_id,name").Table("repository").Where("is_fork=false and created_unix>=" + fmt.Sprint(start_unix) + " and created_unix<=" + fmt.Sprint(end_unix)) | |||||
| repoList := make([]*Repository, 0) | repoList := make([]*Repository, 0) | ||||
| sess.Find(&repoList) | sess.Find(&repoList) | ||||
| resultMap := make(map[int64]int) | resultMap := make(map[int64]int) | ||||
| @@ -354,7 +430,24 @@ func queryUserCreateRepo(start_unix int64, end_unix int64) map[int64]int { | |||||
| } | } | ||||
| } | } | ||||
| return resultMap | return resultMap | ||||
| } | |||||
| func queryLoginCount(start_unix int64, end_unix int64) map[int64]int { | |||||
| statictisSess := xStatistic.NewSession() | |||||
| defer statictisSess.Close() | |||||
| statictisSess.Select("id,u_id").Table("user_login_log").Where("created_unix>=" + fmt.Sprint(start_unix) + " and created_unix<=" + fmt.Sprint(end_unix)) | |||||
| userLoginLogList := make([]*UserLoginLog, 0) | |||||
| statictisSess.Find(&userLoginLogList) | |||||
| resultMap := make(map[int64]int) | |||||
| log.Info("query user login size=" + fmt.Sprint(len(userLoginLogList))) | |||||
| for _, loginRecord := range userLoginLogList { | |||||
| if _, ok := resultMap[loginRecord.UId]; !ok { | |||||
| resultMap[loginRecord.UId] = 1 | |||||
| } else { | |||||
| resultMap[loginRecord.UId] += 1 | |||||
| } | |||||
| } | |||||
| return resultMap | |||||
| } | } | ||||
| func subMonth(t1, t2 time.Time) (month int) { | func subMonth(t1, t2 time.Time) (month int) { | ||||
| @@ -0,0 +1,34 @@ | |||||
| package models | |||||
| import ( | |||||
| "net/http" | |||||
| "code.gitea.io/gitea/modules/timeutil" | |||||
| ) | |||||
| type UserLoginLog struct { | |||||
| ID int64 `xorm:"pk autoincr"` | |||||
| UId int64 `xorm:"NOT NULL"` | |||||
| IpAddr string `xorm:"default NULL"` | |||||
| CreatedUnix timeutil.TimeStamp `xorm:"created"` | |||||
| } | |||||
| func SaveLoginInfoToDb(r *http.Request, u *User) { | |||||
| statictisSess := xStatistic.NewSession() | |||||
| defer statictisSess.Close() | |||||
| var dateRecord UserLoginLog | |||||
| dateRecord.UId = u.ID | |||||
| dateRecord.IpAddr = getIP(r) | |||||
| statictisSess.Insert(&dateRecord) | |||||
| } | |||||
| func getIP(r *http.Request) string { | |||||
| forwarded := r.Header.Get("X-FORWARDED-FOR") | |||||
| if forwarded != "" { | |||||
| return forwarded | |||||
| } | |||||
| return r.RemoteAddr | |||||
| } | |||||
| @@ -14,3 +14,32 @@ type CreateModelArtsForm struct { | |||||
| func (f *CreateModelArtsForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | func (f *CreateModelArtsForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | ||||
| return validate(errs, ctx.Data, f, ctx.Locale) | return validate(errs, ctx.Data, f, ctx.Locale) | ||||
| } | } | ||||
| type CreateModelArtsNotebookForm struct { | |||||
| JobName string `form:"job_name" binding:"Required"` | |||||
| Attachment string `form:"attachment"` | |||||
| Description string `form:"description"` | |||||
| } | |||||
| func (f *CreateModelArtsNotebookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | |||||
| return validate(errs, ctx.Data, f, ctx.Locale) | |||||
| } | |||||
| type CreateModelArtsTrainJobForm struct { | |||||
| JobName string `form:"job_name" binding:"Required"` | |||||
| Attachment string `form:"attachment" binding:"Required"` | |||||
| BootFile string `form:"boot_file" binding:"Required"` | |||||
| WorkServerNumber int `form:"work_server_number" binding:"Required"` | |||||
| EngineID int `form:"engine_id" binding:"Required"` | |||||
| PoolID string `form:"pool_id" binding:"Required"` | |||||
| Flavor string `form:"flavor" binding:"Required"` | |||||
| Params string `form:"run_para_list" binding:"Required"` | |||||
| Description string `form:"description"` | |||||
| IsSaveParam string `form:"is_save_para"` | |||||
| ParameterTemplateName string `form:"parameter_template_name"` | |||||
| PrameterDescription string `form:"parameter_description"` | |||||
| } | |||||
| func (f *CreateModelArtsTrainJobForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | |||||
| return validate(errs, ctx.Data, f, ctx.Locale) | |||||
| } | |||||
| @@ -224,7 +224,7 @@ func SizedAvatarLinkWithDomain(email string, size int) string { | |||||
| // FileSize calculates the file size and generate user-friendly string. | // FileSize calculates the file size and generate user-friendly string. | ||||
| func FileSize(s int64) string { | func FileSize(s int64) string { | ||||
| return humanize.IBytes(uint64(s)) | |||||
| return humanize.Bytes(uint64(s)) | |||||
| } | } | ||||
| // PrettyNumber produces a string form of the given number in base 10 with | // PrettyNumber produces a string form of the given number in base 10 with | ||||
| @@ -16,6 +16,7 @@ const ( | |||||
| ModelMountPath = "/model" | ModelMountPath = "/model" | ||||
| BenchMarkMountPath = "/benchmark" | BenchMarkMountPath = "/benchmark" | ||||
| Snn4imagenetMountPath = "/snn4imagenet" | Snn4imagenetMountPath = "/snn4imagenet" | ||||
| BrainScoreMountPath = "/brainscore" | |||||
| TaskInfoName = "/taskInfo" | TaskInfoName = "/taskInfo" | ||||
| SubTaskName = "task1" | SubTaskName = "task1" | ||||
| @@ -27,7 +28,7 @@ var ( | |||||
| ResourceSpecs *models.ResourceSpecs | ResourceSpecs *models.ResourceSpecs | ||||
| ) | ) | ||||
| func GenerateTask(ctx *context.Context, jobName, image, command, uuid, codePath, modelPath, benchmarkPath, snn4imagenetPath, jobType, gpuQueue string, resourceSpecId int) error { | |||||
| func GenerateTask(ctx *context.Context, jobName, image, command, uuid, codePath, modelPath, benchmarkPath, snn4imagenetPath, brainScorePath, 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 + | ||||
| @@ -103,6 +104,13 @@ func GenerateTask(ctx *context.Context, jobName, image, command, uuid, codePath, | |||||
| ReadOnly: true, | ReadOnly: true, | ||||
| }, | }, | ||||
| }, | }, | ||||
| { | |||||
| HostPath: models.StHostPath{ | |||||
| Path: brainScorePath, | |||||
| MountPath: BrainScoreMountPath, | |||||
| ReadOnly: true, | |||||
| }, | |||||
| }, | |||||
| }, | }, | ||||
| }) | }) | ||||
| if err != nil { | if err != nil { | ||||
| @@ -123,7 +131,8 @@ func GenerateTask(ctx *context.Context, jobName, image, command, uuid, codePath, | |||||
| JobName: jobName, | JobName: jobName, | ||||
| SubTaskName: SubTaskName, | SubTaskName: SubTaskName, | ||||
| JobType: jobType, | JobType: jobType, | ||||
| Type: models.TypeCloudBrainOne, | |||||
| Type: models.TypeCloudBrainOne, | |||||
| Uuid: uuid, | |||||
| }) | }) | ||||
| if err != nil { | if err != nil { | ||||
| @@ -310,9 +310,11 @@ func Contexter() macaron.Handler { | |||||
| ctx.Data["SignedUserID"] = ctx.User.ID | ctx.Data["SignedUserID"] = ctx.User.ID | ||||
| ctx.Data["SignedUserName"] = ctx.User.Name | ctx.Data["SignedUserName"] = ctx.User.Name | ||||
| ctx.Data["IsAdmin"] = ctx.User.IsAdmin | ctx.Data["IsAdmin"] = ctx.User.IsAdmin | ||||
| c.Data["SignedUserName"] = ctx.User.Name | |||||
| } else { | } else { | ||||
| ctx.Data["SignedUserID"] = int64(0) | ctx.Data["SignedUserID"] = int64(0) | ||||
| ctx.Data["SignedUserName"] = "" | ctx.Data["SignedUserName"] = "" | ||||
| c.Data["SignedUserName"] = "" | |||||
| } | } | ||||
| // If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid. | // If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid. | ||||
| @@ -174,6 +174,16 @@ func registerHandleRepoStatistic() { | |||||
| }) | }) | ||||
| } | } | ||||
| func registerHandleSummaryStatistic() { | |||||
| RegisterTaskFatal("handle_summary_statistic", &BaseConfig{ | |||||
| Enabled: true, | |||||
| RunAtStart: false, | |||||
| Schedule: "@daily", | |||||
| }, func(ctx context.Context, _ *models.User, _ Config) error { | |||||
| repo.SummaryStatistic() | |||||
| return nil | |||||
| }) | |||||
| } | |||||
| func registerHandleUserStatistic() { | func registerHandleUserStatistic() { | ||||
| RegisterTaskFatal("handle_user_statistic", &BaseConfig{ | RegisterTaskFatal("handle_user_statistic", &BaseConfig{ | ||||
| Enabled: true, | Enabled: true, | ||||
| @@ -202,4 +212,5 @@ func initBasicTasks() { | |||||
| registerHandleRepoStatistic() | registerHandleRepoStatistic() | ||||
| registerHandleUserStatistic() | registerHandleUserStatistic() | ||||
| registerHandleSummaryStatistic() | |||||
| } | } | ||||
| @@ -35,58 +35,7 @@ type UserKPITypeStats struct { | |||||
| isNewContributor bool //是否是4个月内的新增贡献者 | isNewContributor bool //是否是4个月内的新增贡献者 | ||||
| } | } | ||||
| func GetRepoKPIStats(repoPath string, wikiPath string) (*RepoKPIStats, error) { | |||||
| stats := &RepoKPIStats{} | |||||
| contributors, err := GetContributors(repoPath) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| timeUntil := time.Now() | |||||
| fourMonthAgo := timeUntil.AddDate(0, -4, 0) | |||||
| recentlyContributors, err := getContributors(repoPath, fourMonthAgo) | |||||
| newContributersDict := make(map[string]struct{}) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| if contributors != nil { | |||||
| stats.Contributors = int64(len(contributors)) | |||||
| for _, contributor := range contributors { | |||||
| if contributor.CommitCnt >= 3 { | |||||
| stats.KeyContributors++ | |||||
| } | |||||
| if recentlyContributors != nil { | |||||
| for _, recentlyContributor := range recentlyContributors { | |||||
| if recentlyContributor.Email == contributor.Email && recentlyContributor.CommitCnt == contributor.CommitCnt { | |||||
| stats.ContributorsAdded++ | |||||
| newContributersDict[recentlyContributor.Email] = struct{}{} | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| err = setDevelopAge(repoPath, stats) | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("FillFromGit: %v", err) | |||||
| } | |||||
| err = setRepoKPIStats(repoPath, fourMonthAgo, stats, newContributersDict) | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("FillFromGit: %v", err) | |||||
| } | |||||
| setWikiPages(wikiPath, stats) | |||||
| return stats, nil | |||||
| } | |||||
| func setDevelopAge(repoPath string, stats *RepoKPIStats) error { | |||||
| func SetDevelopAge(repoPath string, stats *RepoKPIStats) error { | |||||
| args := []string{"log", "--no-merges", "--branches=*", "--format=%cd", "--date=short"} | args := []string{"log", "--no-merges", "--branches=*", "--format=%cd", "--date=short"} | ||||
| stdout, err := NewCommand(args...).RunInDirBytes(repoPath) | stdout, err := NewCommand(args...).RunInDirBytes(repoPath) | ||||
| if err != nil { | if err != nil { | ||||
| @@ -173,7 +122,7 @@ func GetUserKPIStats(repoPath string) (map[string]*UserKPIStats, error) { | |||||
| } | } | ||||
| func setRepoKPIStats(repoPath string, fromTime time.Time, stats *RepoKPIStats, newContributers map[string]struct{}) error { | |||||
| func SetRepoKPIStats(repoPath string, fromTime time.Time, stats *RepoKPIStats, newContributers map[string]struct{}) error { | |||||
| since := fromTime.Format(time.RFC3339) | since := fromTime.Format(time.RFC3339) | ||||
| args := []string{"log", "--numstat", "--no-merges", "--branches=*", "--pretty=format:---%n%h%n%an%n%ae%n", "--date=iso", fmt.Sprintf("--since='%s'", since)} | args := []string{"log", "--numstat", "--no-merges", "--branches=*", "--pretty=format:---%n%h%n%an%n%ae%n", "--date=iso", fmt.Sprintf("--since='%s'", since)} | ||||
| @@ -259,7 +208,7 @@ func setRepoKPIStats(repoPath string, fromTime time.Time, stats *RepoKPIStats, n | |||||
| } | } | ||||
| func getContributors(repoPath string, fromTime time.Time) ([]Contributor, error) { | |||||
| func GetContributorsDetail(repoPath string, fromTime time.Time) ([]Contributor, error) { | |||||
| since := fromTime.Format(time.RFC3339) | since := fromTime.Format(time.RFC3339) | ||||
| cmd := NewCommand("shortlog", "-sne", "--all", fmt.Sprintf("--since='%s'", since)) | cmd := NewCommand("shortlog", "-sne", "--all", fmt.Sprintf("--since='%s'", since)) | ||||
| stdout, err := cmd.RunInDir(repoPath) | stdout, err := cmd.RunInDir(repoPath) | ||||
| @@ -289,7 +238,7 @@ func getContributors(repoPath string, fromTime time.Time) ([]Contributor, error) | |||||
| return nil, nil | return nil, nil | ||||
| } | } | ||||
| func setWikiPages(wikiPath string, stats *RepoKPIStats) { | |||||
| func SetWikiPages(wikiPath string, stats *RepoKPIStats) { | |||||
| wikiPages := 0 | wikiPages := 0 | ||||
| if wikiPath == "" { | if wikiPath == "" { | ||||
| @@ -1,22 +1,53 @@ | |||||
| package modelarts | package modelarts | ||||
| import ( | import ( | ||||
| "encoding/json" | |||||
| "path" | |||||
| "strconv" | |||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| "code.gitea.io/gitea/modules/context" | "code.gitea.io/gitea/modules/context" | ||||
| "code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
| "code.gitea.io/gitea/modules/setting" | "code.gitea.io/gitea/modules/setting" | ||||
| "code.gitea.io/gitea/modules/storage" | "code.gitea.io/gitea/modules/storage" | ||||
| "encoding/json" | |||||
| "path" | |||||
| ) | ) | ||||
| const ( | const ( | ||||
| //notebook | |||||
| storageTypeOBS = "obs" | storageTypeOBS = "obs" | ||||
| autoStopDuration = 4 * 60 * 60 | autoStopDuration = 4 * 60 * 60 | ||||
| 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)" | |||||
| //train-job | |||||
| // ResourcePools = "{\"resource_pool\":[{\"id\":\"pool1328035d\", \"value\":\"专属资源池\"}]}" | |||||
| // Engines = "{\"engine\":[{\"id\":1, \"value\":\"Ascend-Powered-Engine\"}]}" | |||||
| // EngineVersions = "{\"version\":[{\"id\":118,\"value\":\"MindSpore-1.0.0-c75-python3.7-euleros2.8-aarch64\"}," + | |||||
| // "{\"id\":119,\"value\":\"MindSpore-1.1.1-c76-python3.7-euleros2.8-aarch64\"}," + | |||||
| // "{\"id\":120,\"value\":\"MindSpore-1.1.1-c76-tr5-python3.7-euleros2.8-aarch64\"}," + | |||||
| // "{\"id\":117,\"value\":\"TF-1.15-c75-python3.7-euleros2.8-aarch64\"}" + | |||||
| // "]}" | |||||
| // TrainJobFlavorInfo = "{\"flavor\":[{\"code\":\"modelarts.bm.910.arm.public.2\",\"value\":\"Ascend : 2 * Ascend 910 CPU:48 核 512GiB\"}," + | |||||
| // "{\"code\":\"modelarts.bm.910.arm.public.8\",\"value\":\"Ascend : 8 * Ascend 910 CPU:192 核 2048GiB\"}," + | |||||
| // "{\"code\":\"modelarts.bm.910.arm.public.4\",\"value\":\"Ascend : 4 * Ascend 910 CPU:96 核 1024GiB\"}," + | |||||
| // "{\"code\":\"modelarts.bm.910.arm.public.1\",\"value\":\"Ascend : 1 * Ascend 910 CPU:24 核 256GiB\"}" + | |||||
| // "]}" | |||||
| CodePath = "/code/" | |||||
| OutputPath = "/output/" | |||||
| LogPath = "/log/" | |||||
| JobPath = "/job/" | |||||
| OrderDesc = "desc" //向下查询 | |||||
| OrderAsc = "asc" //向上查询 | |||||
| Lines = 20 | |||||
| TrainUrl = "train_url" | |||||
| DataUrl = "data_url" | |||||
| PerPage = 10 | |||||
| SortByCreateTime = "create_time" | |||||
| ConfigTypeCustom = "custom" | |||||
| ) | ) | ||||
| var ( | var ( | ||||
| @@ -24,6 +55,50 @@ var ( | |||||
| FlavorInfos *models.FlavorInfos | FlavorInfos *models.FlavorInfos | ||||
| ) | ) | ||||
| type GenerateTrainJobReq struct { | |||||
| JobName string | |||||
| Uuid string | |||||
| Description string | |||||
| CodeObsPath string | |||||
| BootFile string | |||||
| DataUrl string | |||||
| TrainUrl string | |||||
| FlavorCode string | |||||
| LogUrl string | |||||
| PoolID string | |||||
| WorkServerNumber int | |||||
| EngineID int64 | |||||
| Parameters []models.Parameter | |||||
| } | |||||
| type VersionInfo struct { | |||||
| Version []struct { | |||||
| ID int `json:"id"` | |||||
| Value string `json:"value"` | |||||
| } `json:"version"` | |||||
| } | |||||
| type Flavor struct { | |||||
| Info []struct { | |||||
| Code string `json:"code"` | |||||
| Value string `json:"value"` | |||||
| } `json:"flavor"` | |||||
| } | |||||
| type Engine struct { | |||||
| Info []struct { | |||||
| ID int `json:"id"` | |||||
| Value string `json:"value"` | |||||
| } `json:"engine"` | |||||
| } | |||||
| type ResourcePool struct { | |||||
| Info []struct { | |||||
| ID string `json:"id"` | |||||
| Value string `json:"value"` | |||||
| } `json:"resource_pool"` | |||||
| } | |||||
| func GenerateTask(ctx *context.Context, jobName, uuid, description string) error { | func GenerateTask(ctx *context.Context, jobName, uuid, description string) error { | ||||
| var dataActualPath string | var dataActualPath string | ||||
| if uuid != "" { | if uuid != "" { | ||||
| @@ -76,8 +151,8 @@ func GenerateTask(ctx *context.Context, jobName, uuid, description string) error | |||||
| log.Error("CreateJob failed: %v", err.Error()) | log.Error("CreateJob failed: %v", err.Error()) | ||||
| return err | return err | ||||
| } | } | ||||
| err = models.CreateCloudbrain(&models.Cloudbrain{ | err = models.CreateCloudbrain(&models.Cloudbrain{ | ||||
| Status: string(models.JobWaiting), | Status: string(models.JobWaiting), | ||||
| UserID: ctx.User.ID, | UserID: ctx.User.ID, | ||||
| RepoID: ctx.Repo.Repository.ID, | RepoID: ctx.Repo.Repository.ID, | ||||
| @@ -85,6 +160,7 @@ func GenerateTask(ctx *context.Context, jobName, uuid, description string) error | |||||
| JobName: jobName, | JobName: jobName, | ||||
| JobType: string(models.JobTypeDebug), | JobType: string(models.JobTypeDebug), | ||||
| Type: models.TypeCloudBrainTwo, | Type: models.TypeCloudBrainTwo, | ||||
| Uuid: uuid, | |||||
| }) | }) | ||||
| if err != nil { | if err != nil { | ||||
| @@ -93,3 +169,110 @@ func GenerateTask(ctx *context.Context, jobName, uuid, description string) error | |||||
| return nil | return nil | ||||
| } | } | ||||
| func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) error { | |||||
| jobResult, err := createTrainJob(models.CreateTrainJobParams{ | |||||
| JobName: req.JobName, | |||||
| Description: req.Description, | |||||
| Config: models.Config{ | |||||
| WorkServerNum: req.WorkServerNumber, | |||||
| AppUrl: req.CodeObsPath, | |||||
| BootFileUrl: req.BootFile, | |||||
| DataUrl: req.DataUrl, | |||||
| EngineID: req.EngineID, | |||||
| TrainUrl: req.TrainUrl, | |||||
| LogUrl: req.LogUrl, | |||||
| PoolID: req.PoolID, | |||||
| CreateVersion: true, | |||||
| Flavor: models.Flavor{ | |||||
| Code: req.FlavorCode, | |||||
| }, | |||||
| Parameter: req.Parameters, | |||||
| }, | |||||
| }) | |||||
| if err != nil { | |||||
| log.Error("CreateJob failed: %v", err.Error()) | |||||
| return err | |||||
| } | |||||
| attach, err := models.GetAttachmentByUUID(req.Uuid) | |||||
| if err != nil { | |||||
| log.Error("GetAttachmentByUUID(%s) failed:%v", strconv.FormatInt(jobResult.JobID, 10), err.Error()) | |||||
| return nil | |||||
| } | |||||
| err = models.CreateCloudbrain(&models.Cloudbrain{ | |||||
| Status: TransTrainJobStatus(jobResult.Status), | |||||
| UserID: ctx.User.ID, | |||||
| RepoID: ctx.Repo.Repository.ID, | |||||
| JobID: strconv.FormatInt(jobResult.JobID, 10), | |||||
| JobName: req.JobName, | |||||
| JobType: string(models.JobTypeTrain), | |||||
| Type: models.TypeCloudBrainTwo, | |||||
| VersionID: jobResult.VersionID, | |||||
| VersionName: jobResult.VersionName, | |||||
| Uuid: req.Uuid, | |||||
| DatasetName: attach.Name, | |||||
| }) | |||||
| if err != nil { | |||||
| log.Error("CreateCloudbrain(%s) failed:%v", req.JobName, err.Error()) | |||||
| return err | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func TransTrainJobStatus(status int) string { | |||||
| switch status { | |||||
| case 0: | |||||
| return "UNKNOWN" | |||||
| case 1: | |||||
| return "INIT" | |||||
| case 2: | |||||
| return "IMAGE_CREATING" | |||||
| case 3: | |||||
| return "IMAGE_FAILED" | |||||
| case 4: | |||||
| return "SUBMIT_TRYING" | |||||
| case 5: | |||||
| return "SUBMIT_FAILED" | |||||
| case 6: | |||||
| return "DELETE_FAILED" | |||||
| case 7: | |||||
| return "WAITING" | |||||
| case 8: | |||||
| return "RUNNING" | |||||
| case 9: | |||||
| return "KILLING" | |||||
| case 10: | |||||
| return "COMPLETED" | |||||
| case 11: | |||||
| return "FAILED" | |||||
| case 12: | |||||
| return "KILLED" | |||||
| case 13: | |||||
| return "CANCELED" | |||||
| case 14: | |||||
| return "LOST" | |||||
| case 15: | |||||
| return "SCALING" | |||||
| case 16: | |||||
| return "SUBMIT_MODEL_FAILED" | |||||
| case 17: | |||||
| return "DEPLOY_SERVICE_FAILED" | |||||
| case 18: | |||||
| return "CHECK_INIT" | |||||
| case 19: | |||||
| return "CHECK_RUNNING" | |||||
| case 20: | |||||
| return "CHECK_RUNNING_COMPLETED" | |||||
| case 21: | |||||
| return "CHECK_FAILED" | |||||
| default: | |||||
| return strconv.Itoa(status) | |||||
| } | |||||
| return "" | |||||
| } | |||||
| @@ -1,13 +1,14 @@ | |||||
| package modelarts | package modelarts | ||||
| import ( | import ( | ||||
| "code.gitea.io/gitea/modules/log" | |||||
| "crypto/tls" | "crypto/tls" | ||||
| "encoding/json" | "encoding/json" | ||||
| "fmt" | "fmt" | ||||
| "net/http" | "net/http" | ||||
| "strconv" | |||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| "code.gitea.io/gitea/modules/log" | |||||
| "code.gitea.io/gitea/modules/setting" | "code.gitea.io/gitea/modules/setting" | ||||
| "github.com/go-resty/resty/v2" | "github.com/go-resty/resty/v2" | ||||
| ) | ) | ||||
| @@ -23,6 +24,9 @@ const ( | |||||
| urlGetToken = "/v3/auth/tokens" | urlGetToken = "/v3/auth/tokens" | ||||
| urlNotebook = "/demanager/instances" | urlNotebook = "/demanager/instances" | ||||
| urlTrainJob = "/training-jobs" | |||||
| urlResourceSpecs = "/job/resource-specs" | |||||
| urlTrainJobConfig = "/training-job-configs" | |||||
| errorCodeExceedLimit = "ModelArts.0118" | errorCodeExceedLimit = "ModelArts.0118" | ||||
| ) | ) | ||||
| @@ -104,7 +108,7 @@ sendjob: | |||||
| Post(HOST + "/v1/" + setting.ProjectID + urlNotebook) | Post(HOST + "/v1/" + setting.ProjectID + urlNotebook) | ||||
| if err != nil { | if err != nil { | ||||
| return nil, fmt.Errorf("resty create job: %s", err) | |||||
| return nil, fmt.Errorf("resty create notebook: %s", err) | |||||
| } | } | ||||
| if res.StatusCode() == http.StatusUnauthorized && retry < 1 { | if res.StatusCode() == http.StatusUnauthorized && retry < 1 { | ||||
| @@ -121,11 +125,11 @@ sendjob: | |||||
| } | } | ||||
| if len(response.ErrorCode) != 0 { | if len(response.ErrorCode) != 0 { | ||||
| log.Error("CreateJob failed(%s): %s", response.ErrorCode, response.ErrorMsg) | |||||
| log.Error("createNotebook failed(%s): %s", response.ErrorCode, response.ErrorMsg) | |||||
| if response.ErrorCode == errorCodeExceedLimit { | if response.ErrorCode == errorCodeExceedLimit { | ||||
| response.ErrorMsg = "所选规格使用数量已超过最大配额限制。" | response.ErrorMsg = "所选规格使用数量已超过最大配额限制。" | ||||
| } | } | ||||
| return &result, fmt.Errorf("CreateJob failed(%s): %s", response.ErrorCode, response.ErrorMsg) | |||||
| return &result, fmt.Errorf("createNotebook failed(%s): %s", response.ErrorCode, response.ErrorMsg) | |||||
| } | } | ||||
| return &result, nil | return &result, nil | ||||
| @@ -210,6 +214,45 @@ sendjob: | |||||
| return &result, nil | return &result, nil | ||||
| } | } | ||||
| func DelNotebook(jobID string) (*models.NotebookDelResult, error) { | |||||
| checkSetting() | |||||
| client := getRestyClient() | |||||
| var result models.NotebookDelResult | |||||
| retry := 0 | |||||
| sendjob: | |||||
| res, err := client.R(). | |||||
| SetHeader("Content-Type", "application/json"). | |||||
| SetAuthToken(TOKEN). | |||||
| SetResult(&result). | |||||
| Delete(HOST + "/v1/" + setting.ProjectID + urlNotebook + "/" + jobID) | |||||
| if err != nil { | |||||
| return &result, fmt.Errorf("resty DelJob: %v", err) | |||||
| } | |||||
| if res.StatusCode() == http.StatusUnauthorized && retry < 1 { | |||||
| retry++ | |||||
| _ = getToken() | |||||
| goto sendjob | |||||
| } | |||||
| var response models.NotebookResult | |||||
| err = json.Unmarshal(res.Body(), &response) | |||||
| if err != nil { | |||||
| log.Error("json.Unmarshal failed: %s", err.Error()) | |||||
| return &result, fmt.Errorf("son.Unmarshal failed: %s", err.Error()) | |||||
| } | |||||
| if len(response.ErrorCode) != 0 { | |||||
| log.Error("DelJob failed(%s): %s", response.ErrorCode, response.ErrorMsg) | |||||
| return &result, fmt.Errorf("DelJob failed(%s): %s", response.ErrorCode, response.ErrorMsg) | |||||
| } | |||||
| return &result, nil | |||||
| } | |||||
| func DelJob(jobID string) (*models.NotebookDelResult, error) { | func DelJob(jobID string) (*models.NotebookDelResult, error) { | ||||
| checkSetting() | checkSetting() | ||||
| client := getRestyClient() | client := getRestyClient() | ||||
| @@ -287,3 +330,441 @@ sendjob: | |||||
| return &result, nil | return &result, nil | ||||
| } | } | ||||
| func createTrainJob(createJobParams models.CreateTrainJobParams) (*models.CreateTrainJobResult, error) { | |||||
| checkSetting() | |||||
| client := getRestyClient() | |||||
| var result models.CreateTrainJobResult | |||||
| retry := 0 | |||||
| sendjob: | |||||
| res, err := client.R(). | |||||
| SetHeader("Content-Type", "application/json"). | |||||
| SetAuthToken(TOKEN). | |||||
| SetBody(createJobParams). | |||||
| SetResult(&result). | |||||
| Post(HOST + "/v1/" + setting.ProjectID + urlTrainJob) | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("resty create train-job: %s", err) | |||||
| } | |||||
| req, _ := json.Marshal(createJobParams) | |||||
| log.Info("%s", req) | |||||
| if res.StatusCode() == http.StatusUnauthorized && retry < 1 { | |||||
| retry++ | |||||
| _ = getToken() | |||||
| goto sendjob | |||||
| } | |||||
| if res.StatusCode() != http.StatusOK { | |||||
| var temp models.ErrorResult | |||||
| if err = json.Unmarshal([]byte(res.String()), &temp); err != nil { | |||||
| log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error()) | |||||
| return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error()) | |||||
| } | |||||
| log.Error("createTrainJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) | |||||
| return &result, fmt.Errorf("createTrainJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) | |||||
| } | |||||
| if !result.IsSuccess { | |||||
| log.Error("createTrainJob failed(%s): %s", result.ErrorCode, result.ErrorMsg) | |||||
| return &result, fmt.Errorf("createTrainJob failed(%s): %s", result.ErrorCode, result.ErrorMsg) | |||||
| } | |||||
| return &result, nil | |||||
| } | |||||
| func GetResourceSpecs() (*models.GetResourceSpecsResult, error) { | |||||
| checkSetting() | |||||
| client := getRestyClient() | |||||
| var result models.GetResourceSpecsResult | |||||
| retry := 0 | |||||
| sendjob: | |||||
| res, err := client.R(). | |||||
| SetHeader("Content-Type", "application/json"). | |||||
| SetAuthToken(TOKEN). | |||||
| SetResult(&result). | |||||
| Get(HOST + "/v1/" + setting.ProjectID + urlResourceSpecs) | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("resty GetResourceSpecs: %v", err) | |||||
| } | |||||
| if res.StatusCode() == http.StatusUnauthorized && retry < 1 { | |||||
| retry++ | |||||
| _ = getToken() | |||||
| goto sendjob | |||||
| } | |||||
| if res.StatusCode() != http.StatusOK { | |||||
| var temp models.ErrorResult | |||||
| if err = json.Unmarshal([]byte(res.String()), &temp); err != nil { | |||||
| log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error()) | |||||
| return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error()) | |||||
| } | |||||
| log.Error("GetResourceSpecs failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) | |||||
| return &result, fmt.Errorf("GetResourceSpecs failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) | |||||
| } | |||||
| if !result.IsSuccess { | |||||
| log.Error("GetResourceSpecs failed(%s): %s", result.ErrorCode, result.ErrorMsg) | |||||
| return &result, fmt.Errorf("GetResourceSpecs failed(%s): %s", result.ErrorCode, result.ErrorMsg) | |||||
| } | |||||
| return &result, nil | |||||
| } | |||||
| func CreateTrainJobConfig(req models.CreateConfigParams) (*models.CreateTrainJobConfigResult, error) { | |||||
| checkSetting() | |||||
| client := getRestyClient() | |||||
| var result models.CreateTrainJobConfigResult | |||||
| retry := 0 | |||||
| sendjob: | |||||
| res, err := client.R(). | |||||
| SetHeader("Content-Type", "application/json"). | |||||
| SetAuthToken(TOKEN). | |||||
| SetBody(req). | |||||
| SetResult(&result). | |||||
| Post(HOST + "/v1/" + setting.ProjectID + urlTrainJobConfig) | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("resty CreateTrainJobConfig: %s", err) | |||||
| } | |||||
| if res.StatusCode() == http.StatusUnauthorized && retry < 1 { | |||||
| retry++ | |||||
| _ = getToken() | |||||
| goto sendjob | |||||
| } | |||||
| //temp, _ := json.Marshal(req) | |||||
| //log.Info("%s", temp) | |||||
| if res.StatusCode() != http.StatusOK { | |||||
| var temp models.ErrorResult | |||||
| if err = json.Unmarshal([]byte(res.String()), &temp); err != nil { | |||||
| log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error()) | |||||
| return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error()) | |||||
| } | |||||
| log.Error("CreateTrainJobConfig failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) | |||||
| return &result, fmt.Errorf("CreateTrainJobConfig failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) | |||||
| } | |||||
| if !result.IsSuccess { | |||||
| log.Error("CreateTrainJobConfig failed(%s): %s", result.ErrorCode, result.ErrorMsg) | |||||
| return &result, fmt.Errorf("CreateTrainJobConfig failed(%s): %s", result.ErrorCode, result.ErrorMsg) | |||||
| } | |||||
| return &result, nil | |||||
| } | |||||
| func GetConfigList(perPage, page int, sortBy, order, searchContent, configType string) (*models.GetConfigListResult, error) { | |||||
| checkSetting() | |||||
| client := getRestyClient() | |||||
| var result models.GetConfigListResult | |||||
| retry := 0 | |||||
| sendjob: | |||||
| res, err := client.R(). | |||||
| SetQueryParams(map[string]string{ | |||||
| "per_page": strconv.Itoa(perPage), | |||||
| "page": strconv.Itoa(page), | |||||
| "sortBy": sortBy, | |||||
| "order": order, | |||||
| "search_content": searchContent, | |||||
| "config_type": configType, | |||||
| }). | |||||
| SetAuthToken(TOKEN). | |||||
| SetResult(&result). | |||||
| Get(HOST + "/v1/" + setting.ProjectID + urlTrainJobConfig) | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("resty GetConfigList: %v", err) | |||||
| } | |||||
| if res.StatusCode() == http.StatusUnauthorized && retry < 1 { | |||||
| retry++ | |||||
| _ = getToken() | |||||
| goto sendjob | |||||
| } | |||||
| if res.StatusCode() != http.StatusOK { | |||||
| var temp models.ErrorResult | |||||
| if err = json.Unmarshal([]byte(res.String()), &temp); err != nil { | |||||
| log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error()) | |||||
| return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error()) | |||||
| } | |||||
| log.Error("GetConfigList failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) | |||||
| return &result, fmt.Errorf("获取参数配置列表失败(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) | |||||
| } | |||||
| if !result.IsSuccess { | |||||
| log.Error("GetConfigList failed(%s): %s", result.ErrorCode, result.ErrorMsg) | |||||
| return &result, fmt.Errorf("获取参数配置列表失败(%s): %s", result.ErrorCode, result.ErrorMsg) | |||||
| } | |||||
| return &result, nil | |||||
| } | |||||
| func GetParaConfig(configName, configType string) (models.GetConfigResult, error) { | |||||
| checkSetting() | |||||
| client := getRestyClient() | |||||
| var result models.GetConfigResult | |||||
| retry := 0 | |||||
| sendjob: | |||||
| res, err := client.R(). | |||||
| SetQueryParams(map[string]string{ | |||||
| "config_type": configType, | |||||
| }). | |||||
| SetAuthToken(TOKEN). | |||||
| SetResult(&result). | |||||
| Get(HOST + "/v1/" + setting.ProjectID + urlTrainJobConfig + "/" + configName) | |||||
| if err != nil { | |||||
| return result, fmt.Errorf("resty GetParaConfig: %v", err) | |||||
| } | |||||
| if res.StatusCode() == http.StatusUnauthorized && retry < 1 { | |||||
| retry++ | |||||
| _ = getToken() | |||||
| goto sendjob | |||||
| } | |||||
| if res.StatusCode() != http.StatusOK { | |||||
| var temp models.ErrorResult | |||||
| if err = json.Unmarshal([]byte(res.String()), &temp); err != nil { | |||||
| log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error()) | |||||
| return result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error()) | |||||
| } | |||||
| log.Error("GetParaConfig failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) | |||||
| return result, fmt.Errorf("获取参数配置详情失败(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) | |||||
| } | |||||
| if !result.IsSuccess { | |||||
| log.Error("GetParaConfig failed(%s): %s", result.ErrorCode, result.ErrorMsg) | |||||
| return result, fmt.Errorf("获取参数配置详情失败(%s): %s", result.ErrorCode, result.ErrorMsg) | |||||
| } | |||||
| return result, nil | |||||
| } | |||||
| func GetTrainJob(jobID, versionID string) (*models.GetTrainJobResult, error) { | |||||
| checkSetting() | |||||
| client := getRestyClient() | |||||
| var result models.GetTrainJobResult | |||||
| retry := 0 | |||||
| sendjob: | |||||
| res, err := client.R(). | |||||
| SetAuthToken(TOKEN). | |||||
| SetResult(&result). | |||||
| Get(HOST + "/v1/" + setting.ProjectID + urlTrainJob + "/" + jobID + "/versions/" + versionID) | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("resty GetTrainJob: %v", err) | |||||
| } | |||||
| if res.StatusCode() == http.StatusUnauthorized && retry < 1 { | |||||
| retry++ | |||||
| _ = getToken() | |||||
| goto sendjob | |||||
| } | |||||
| if res.StatusCode() != http.StatusOK { | |||||
| var temp models.ErrorResult | |||||
| if err = json.Unmarshal([]byte(res.String()), &temp); err != nil { | |||||
| log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error()) | |||||
| return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error()) | |||||
| } | |||||
| log.Error("GetTrainJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) | |||||
| return &result, fmt.Errorf("获取作业详情失败(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) | |||||
| } | |||||
| if !result.IsSuccess { | |||||
| log.Error("GetTrainJob(%s) failed", jobID) | |||||
| return &result, fmt.Errorf("获取作业详情失败") | |||||
| } | |||||
| return &result, nil | |||||
| } | |||||
| func GetTrainJobLog(jobID, versionID, baseLine, logFile, order string, lines int) (*models.GetTrainJobLogResult, error) { | |||||
| checkSetting() | |||||
| client := getRestyClient() | |||||
| var result models.GetTrainJobLogResult | |||||
| retry := 0 | |||||
| sendjob: | |||||
| res, err := client.R(). | |||||
| SetQueryParams(map[string]string{ | |||||
| "base_line": baseLine, | |||||
| "lines": strconv.Itoa(lines), | |||||
| "log_file": logFile, | |||||
| "order": order, | |||||
| }). | |||||
| SetAuthToken(TOKEN). | |||||
| SetResult(&result). | |||||
| Get(HOST + "/v1/" + setting.ProjectID + urlTrainJob + "/" + jobID + "/versions/" + versionID + "/aom-log") | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("resty GetTrainJobLog: %v", err) | |||||
| } | |||||
| if res.StatusCode() == http.StatusUnauthorized && retry < 1 { | |||||
| retry++ | |||||
| _ = getToken() | |||||
| goto sendjob | |||||
| } | |||||
| if res.StatusCode() != http.StatusOK { | |||||
| var temp models.ErrorResult | |||||
| if err = json.Unmarshal([]byte(res.String()), &temp); err != nil { | |||||
| log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error()) | |||||
| return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error()) | |||||
| } | |||||
| log.Error("GetTrainJobLog failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) | |||||
| return &result, fmt.Errorf("获取作业日志失败(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) | |||||
| } | |||||
| if !result.IsSuccess { | |||||
| log.Error("GetTrainJobLog(%s) failed", jobID) | |||||
| return &result, fmt.Errorf("获取作业日志失败:%s", result.ErrorMsg) | |||||
| } | |||||
| return &result, nil | |||||
| } | |||||
| func GetTrainJobLogFileNames(jobID, versionID string) (*models.GetTrainJobLogFileNamesResult, error) { | |||||
| checkSetting() | |||||
| client := getRestyClient() | |||||
| var result models.GetTrainJobLogFileNamesResult | |||||
| retry := 0 | |||||
| sendjob: | |||||
| res, err := client.R(). | |||||
| SetAuthToken(TOKEN). | |||||
| SetResult(&result). | |||||
| Get(HOST + "/v1/" + setting.ProjectID + urlTrainJob + "/" + jobID + "/versions/" + versionID + "/log/file-names") | |||||
| if err != nil { | |||||
| return nil, fmt.Errorf("resty GetTrainJobLogFileNames: %v", err) | |||||
| } | |||||
| if res.StatusCode() == http.StatusUnauthorized && retry < 1 { | |||||
| retry++ | |||||
| _ = getToken() | |||||
| goto sendjob | |||||
| } | |||||
| if res.StatusCode() != http.StatusOK { | |||||
| var temp models.ErrorResult | |||||
| if err = json.Unmarshal([]byte(res.String()), &temp); err != nil { | |||||
| log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error()) | |||||
| return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error()) | |||||
| } | |||||
| log.Error("GetTrainJobLogFileNames failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) | |||||
| return &result, fmt.Errorf("GetTrainJobLogFileNames failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) | |||||
| } | |||||
| if !result.IsSuccess { | |||||
| log.Error("GetTrainJobLogFileNames(%s) failed", jobID) | |||||
| return &result, fmt.Errorf("获取作业日志文件失败:%s", result.ErrorMsg) | |||||
| } | |||||
| return &result, nil | |||||
| } | |||||
| func DelTrainJob(jobID string) (*models.TrainJobResult, error) { | |||||
| checkSetting() | |||||
| client := getRestyClient() | |||||
| var result models.TrainJobResult | |||||
| retry := 0 | |||||
| sendjob: | |||||
| res, err := client.R(). | |||||
| SetAuthToken(TOKEN). | |||||
| SetResult(&result). | |||||
| Delete(HOST + "/v1/" + setting.ProjectID + urlTrainJob + "/" + jobID) | |||||
| if err != nil { | |||||
| return &result, fmt.Errorf("resty DelTrainJob: %v", err) | |||||
| } | |||||
| if res.StatusCode() == http.StatusUnauthorized && retry < 1 { | |||||
| retry++ | |||||
| _ = getToken() | |||||
| goto sendjob | |||||
| } | |||||
| if res.StatusCode() != http.StatusOK { | |||||
| var temp models.ErrorResult | |||||
| if err = json.Unmarshal([]byte(res.String()), &temp); err != nil { | |||||
| log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error()) | |||||
| return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error()) | |||||
| } | |||||
| log.Error("DelTrainJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) | |||||
| return &result, fmt.Errorf("删除训练作业失败(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) | |||||
| } | |||||
| if !result.IsSuccess { | |||||
| log.Error("DelTrainJob(%s) failed", jobID) | |||||
| return &result, fmt.Errorf("删除训练作业失败:%s", result.ErrorMsg) | |||||
| } | |||||
| return &result, nil | |||||
| } | |||||
| func StopTrainJob(jobID, versionID string) (*models.TrainJobResult, error) { | |||||
| checkSetting() | |||||
| client := getRestyClient() | |||||
| var result models.TrainJobResult | |||||
| retry := 0 | |||||
| sendjob: | |||||
| res, err := client.R(). | |||||
| SetAuthToken(TOKEN). | |||||
| SetResult(&result). | |||||
| Post(HOST + "/v1/" + setting.ProjectID + urlTrainJob + "/" + jobID + "/versions/" + versionID + "/stop") | |||||
| if err != nil { | |||||
| return &result, fmt.Errorf("resty StopTrainJob: %v", err) | |||||
| } | |||||
| if res.StatusCode() == http.StatusUnauthorized && retry < 1 { | |||||
| retry++ | |||||
| _ = getToken() | |||||
| goto sendjob | |||||
| } | |||||
| if res.StatusCode() != http.StatusOK { | |||||
| var temp models.ErrorResult | |||||
| if err = json.Unmarshal([]byte(res.String()), &temp); err != nil { | |||||
| log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error()) | |||||
| return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error()) | |||||
| } | |||||
| log.Error("StopTrainJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) | |||||
| return &result, fmt.Errorf("停止训练作业失败(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) | |||||
| } | |||||
| if !result.IsSuccess { | |||||
| log.Error("StopTrainJob(%s) failed", jobID) | |||||
| return &result, fmt.Errorf("停止训练作业失败:%s", result.ErrorMsg) | |||||
| } | |||||
| return &result, nil | |||||
| } | |||||
| @@ -0,0 +1,83 @@ | |||||
| package normalization | |||||
| import ( | |||||
| "code.gitea.io/gitea/modules/setting" | |||||
| ) | |||||
| func Normalization(value float64, minValue float64, maxValue float64) float64 { | |||||
| min := int64(minValue * 100) | |||||
| max := int64(maxValue * 100) | |||||
| if min == max { | |||||
| return 100.0 | |||||
| } else { | |||||
| return 100 * (value - minValue) / (maxValue - minValue) | |||||
| } | |||||
| } | |||||
| func GetRadarValue(impactValue float64, completeValue float64, livenessValue float64, projectHealthValue float64, teamHealthValue float64, growthValue float64) float64 { | |||||
| return setting.RadarMap.Impact*impactValue + | |||||
| setting.RadarMap.Completeness*completeValue + | |||||
| setting.RadarMap.Liveness*livenessValue + | |||||
| setting.RadarMap.ProjectHealth*projectHealthValue + | |||||
| setting.RadarMap.TeamHealth*teamHealthValue + | |||||
| setting.RadarMap.Growth*growthValue | |||||
| } | |||||
| func GetImpactInitValue(watch int64, star int64, fork int64, download int64, comments int64, browser int64) float64 { | |||||
| return setting.RadarMap.ImpactWatch*float64(watch) + | |||||
| setting.RadarMap.ImpactStar*float64(star) + | |||||
| setting.RadarMap.ImpactFork*float64(fork) + | |||||
| setting.RadarMap.ImpactCodeDownload*float64(download)*0.001 + | |||||
| setting.RadarMap.ImpactComments*float64(comments) + | |||||
| setting.RadarMap.ImpactBrowser*float64(browser)*0.001 | |||||
| } | |||||
| func GetCompleteInitValue(issuesClosed int64, releases int64, developAge int64, dataset int64, model int64, wiki int64) float64 { | |||||
| return setting.RadarMap.CompletenessIssuesClosed*float64(issuesClosed) + | |||||
| setting.RadarMap.CompletenessReleases*float64(releases) + | |||||
| setting.RadarMap.CompletenessDevelopAge*float64(developAge) + | |||||
| setting.RadarMap.CompletenessDataset*(float64(dataset)/(1024*1024)) + | |||||
| setting.RadarMap.CompletenessModel*float64(model) + | |||||
| setting.RadarMap.CompletenessWiki*float64(wiki) | |||||
| } | |||||
| func GetLivenessInitValue(commits int64, issues int64, pr int64, release int64) float64 { | |||||
| return setting.RadarMap.LivenessCommit*float64(commits) + | |||||
| setting.RadarMap.LivenessIssue*float64(issues) + | |||||
| setting.RadarMap.LivenessPR*float64(pr) + | |||||
| setting.RadarMap.LivenessRelease*float64(release) | |||||
| } | |||||
| func GetProjectHealthInitValue(issueClosedRatio float32) float64 { | |||||
| return setting.RadarMap.ProjectHealthIssueCompleteRatio * float64(issueClosedRatio) | |||||
| } | |||||
| func GetTeamHealthInitValue(contributors int64, keyContributors int64, newContributors int64) float64 { | |||||
| return setting.RadarMap.TeamHealthContributors*float64(contributors) + | |||||
| setting.RadarMap.TeamHealthKeyContributors*float64(keyContributors) + | |||||
| setting.RadarMap.TeamHealthContributorsAdded*float64(newContributors) | |||||
| } | |||||
| func GetRepoGrowthInitValue(codelinesGrowth int64, issueGrowth int64, commitsGrowth int64, newContributors int64, commentsGrowth int64) float64 { | |||||
| return setting.RadarMap.GrowthCodeLines*float64(codelinesGrowth) + | |||||
| setting.RadarMap.GrowthIssue*float64(issueGrowth) + | |||||
| setting.RadarMap.GrowthCommit*float64(commitsGrowth) + | |||||
| setting.RadarMap.GrowthContributors*float64(newContributors) + | |||||
| setting.RadarMap.GrowthComments*float64(commentsGrowth) | |||||
| } | |||||
| @@ -0,0 +1,7 @@ | |||||
| package setting | |||||
| func UpdateRadarMap() { | |||||
| Cfg.DeleteSection("radar_map") | |||||
| Cfg.Reload() | |||||
| SetRadarMapConfig() | |||||
| } | |||||
| @@ -457,18 +457,26 @@ var ( | |||||
| Snn4imagenetCode string | Snn4imagenetCode string | ||||
| Snn4imagenetServerHost string | Snn4imagenetServerHost string | ||||
| //snn4imagenet config | |||||
| IsBrainScoreEnabled bool | |||||
| BrainScoreCode string | |||||
| BrainScoreServerHost string | |||||
| //blockchain config | //blockchain config | ||||
| BlockChainHost string | BlockChainHost string | ||||
| CommitValidDate string | CommitValidDate string | ||||
| //obs config | //obs config | ||||
| Endpoint string | |||||
| AccessKeyID string | |||||
| SecretAccessKey string | |||||
| Bucket string | |||||
| Location string | |||||
| BasePath string | |||||
| UserBasePath string | |||||
| Endpoint string | |||||
| AccessKeyID string | |||||
| SecretAccessKey string | |||||
| Bucket string | |||||
| Location string | |||||
| BasePath string | |||||
| OutPutPath string | |||||
| TrainJobModelPath string | |||||
| CodePathPrefix string | |||||
| UserBasePath string | |||||
| //modelarts config | //modelarts config | ||||
| ModelArtsHost string | ModelArtsHost string | ||||
| @@ -478,10 +486,16 @@ var ( | |||||
| ModelArtsUsername string | ModelArtsUsername string | ||||
| ModelArtsPassword string | ModelArtsPassword string | ||||
| ModelArtsDomain string | ModelArtsDomain string | ||||
| AllowedOrg string | |||||
| ProfileID string | ProfileID string | ||||
| PoolInfos string | PoolInfos string | ||||
| Flavor string | Flavor string | ||||
| FlavorInfos string | |||||
| //train-job | |||||
| ResourcePools string | |||||
| Engines string | |||||
| EngineVersions string | |||||
| FlavorInfos string | |||||
| TrainJobFLAVORINFOS string | |||||
| //elk config | //elk config | ||||
| ElkUrl string | ElkUrl string | ||||
| @@ -490,6 +504,47 @@ var ( | |||||
| Index string | Index string | ||||
| TimeField string | TimeField string | ||||
| ElkTimeFormat string | ElkTimeFormat string | ||||
| //nginx proxy | |||||
| PROXYURL string | |||||
| RadarMap = struct { | |||||
| Impact float64 | |||||
| ImpactWatch float64 | |||||
| ImpactStar float64 | |||||
| ImpactFork float64 | |||||
| ImpactCodeDownload float64 | |||||
| ImpactComments float64 | |||||
| ImpactBrowser float64 | |||||
| Completeness float64 | |||||
| CompletenessIssuesClosed float64 | |||||
| CompletenessReleases float64 | |||||
| CompletenessDevelopAge float64 | |||||
| CompletenessDataset float64 | |||||
| CompletenessModel float64 | |||||
| CompletenessWiki float64 | |||||
| Liveness float64 | |||||
| LivenessCommit float64 | |||||
| LivenessIssue float64 | |||||
| LivenessPR float64 | |||||
| LivenessRelease float64 | |||||
| ProjectHealth float64 | |||||
| ProjectHealthIssueCompleteRatio float64 | |||||
| TeamHealth float64 | |||||
| TeamHealthContributors float64 | |||||
| TeamHealthKeyContributors float64 | |||||
| TeamHealthContributorsAdded float64 | |||||
| Growth float64 | |||||
| GrowthCodeLines float64 | |||||
| GrowthIssue float64 | |||||
| GrowthContributors float64 | |||||
| GrowthCommit float64 | |||||
| GrowthComments float64 | |||||
| }{} | |||||
| ) | ) | ||||
| // DateLang transforms standard language locale name to corresponding value in datetime plugin. | // DateLang transforms standard language locale name to corresponding value in datetime plugin. | ||||
| @@ -1181,8 +1236,13 @@ func NewContext() { | |||||
| sec = Cfg.Section("snn4imagenet") | sec = Cfg.Section("snn4imagenet") | ||||
| IsSnn4imagenetEnabled = sec.Key("ENABLED").MustBool(false) | IsSnn4imagenetEnabled = sec.Key("ENABLED").MustBool(false) | ||||
| Snn4imagenetCode = sec.Key("SNN4IMAGENETCODE").MustString("https://yangzhx:justfortest123@git.openi.org.cn/yangzhx/detection_benchmark_script.git") | |||||
| Snn4imagenetServerHost = sec.Key("HOST").MustString("http://192.168.202.90:3366/") | |||||
| Snn4imagenetCode = sec.Key("SNN4IMAGENETCODE").MustString("https://yult:19910821ylt@git.openi.org.cn/yult/snn4imagenet_script.git") | |||||
| Snn4imagenetServerHost = sec.Key("HOST").MustString("http://192.168.207.76:8080/") | |||||
| sec = Cfg.Section("brainscore") | |||||
| IsBrainScoreEnabled = sec.Key("ENABLED").MustBool(false) | |||||
| BrainScoreCode = sec.Key("BRAINSCORECODE").MustString("https://yult:19910821ylt@git.openi.org.cn/yult/brainscore_script.git") | |||||
| BrainScoreServerHost = sec.Key("HOST").MustString("http://192.168.207.76:8080/") | |||||
| sec = Cfg.Section("blockchain") | sec = Cfg.Section("blockchain") | ||||
| BlockChainHost = sec.Key("HOST").MustString("http://192.168.136.66:3302/") | BlockChainHost = sec.Key("HOST").MustString("http://192.168.136.66:3302/") | ||||
| @@ -1195,7 +1255,11 @@ func NewContext() { | |||||
| Bucket = sec.Key("BUCKET").MustString("testopendata") | Bucket = sec.Key("BUCKET").MustString("testopendata") | ||||
| Location = sec.Key("LOCATION").MustString("cn-south-222") | Location = sec.Key("LOCATION").MustString("cn-south-222") | ||||
| BasePath = sec.Key("BASE_PATH").MustString("attachment/") | BasePath = sec.Key("BASE_PATH").MustString("attachment/") | ||||
| TrainJobModelPath = sec.Key("TrainJobModel_Path").MustString("job/") | |||||
| OutPutPath = sec.Key("Output_Path").MustString("output/") | |||||
| CodePathPrefix = sec.Key("CODE_PATH_PREFIX").MustString("code/") | |||||
| UserBasePath = sec.Key("BASE_PATH_USER").MustString("users/") | UserBasePath = sec.Key("BASE_PATH_USER").MustString("users/") | ||||
| PROXYURL = sec.Key("PROXY_URL").MustString("") | |||||
| sec = Cfg.Section("modelarts") | sec = Cfg.Section("modelarts") | ||||
| ModelArtsHost = sec.Key("ENDPOINT").MustString("112.95.163.80") | ModelArtsHost = sec.Key("ENDPOINT").MustString("112.95.163.80") | ||||
| @@ -1205,10 +1269,15 @@ 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") | ||||
| AllowedOrg = sec.Key("ORGANIZATION").MustString("") | |||||
| ProfileID = sec.Key("PROFILE_ID").MustString("") | ProfileID = sec.Key("PROFILE_ID").MustString("") | ||||
| PoolInfos = sec.Key("POOL_INFOS").MustString("") | PoolInfos = sec.Key("POOL_INFOS").MustString("") | ||||
| Flavor = sec.Key("FLAVOR").MustString("") | |||||
| Flavor = sec.Key("FLAVOR").MustString("modelarts.bm.910.arm.public.2") | |||||
| ResourcePools = sec.Key("Resource_Pools").MustString("") | |||||
| Engines = sec.Key("Engines").MustString("") | |||||
| EngineVersions = sec.Key("Engine_Versions").MustString("") | |||||
| FlavorInfos = sec.Key("FLAVOR_INFOS").MustString("") | FlavorInfos = sec.Key("FLAVOR_INFOS").MustString("") | ||||
| TrainJobFLAVORINFOS = sec.Key("TrainJob_FLAVOR_INFOS").MustString("") | |||||
| sec = Cfg.Section("elk") | sec = Cfg.Section("elk") | ||||
| ElkUrl = sec.Key("ELKURL").MustString("http://192.168.207.35:5601/internal/bsearch") | ElkUrl = sec.Key("ELKURL").MustString("http://192.168.207.35:5601/internal/bsearch") | ||||
| @@ -1217,6 +1286,45 @@ func NewContext() { | |||||
| Index = sec.Key("INDEX").MustString("filebeat-7.3.2*") | Index = sec.Key("INDEX").MustString("filebeat-7.3.2*") | ||||
| TimeField = sec.Key("TIMEFIELD").MustString(" @timestamptest") | TimeField = sec.Key("TIMEFIELD").MustString(" @timestamptest") | ||||
| ElkTimeFormat = sec.Key("ELKTIMEFORMAT").MustString("date_time") | ElkTimeFormat = sec.Key("ELKTIMEFORMAT").MustString("date_time") | ||||
| SetRadarMapConfig() | |||||
| } | |||||
| func SetRadarMapConfig() { | |||||
| sec := Cfg.Section("radar_map") | |||||
| RadarMap.Impact = sec.Key("impact").MustFloat64(0.3) | |||||
| RadarMap.ImpactWatch = sec.Key("impact_watch").MustFloat64(0.1) | |||||
| RadarMap.ImpactStar = sec.Key("impact_star").MustFloat64(0.2) | |||||
| RadarMap.ImpactFork = sec.Key("impact_fork").MustFloat64(0.3) | |||||
| RadarMap.ImpactCodeDownload = sec.Key("impact_code_download").MustFloat64(0.2) | |||||
| RadarMap.ImpactComments = sec.Key("impact_comments").MustFloat64(0.1) | |||||
| RadarMap.ImpactBrowser = sec.Key("impact_browser").MustFloat64(0.1) | |||||
| RadarMap.Completeness = sec.Key("completeness").MustFloat64(0.1) | |||||
| RadarMap.CompletenessIssuesClosed = sec.Key("completeness_issues_closed").MustFloat64(0.2) | |||||
| RadarMap.CompletenessReleases = sec.Key("completeness_releases").MustFloat64(0.3) | |||||
| RadarMap.CompletenessDevelopAge = sec.Key("completeness_develop_age").MustFloat64(0.1) | |||||
| RadarMap.CompletenessDataset = sec.Key("completeness_dataset").MustFloat64(0.1) | |||||
| RadarMap.CompletenessModel = sec.Key("completeness_model").MustFloat64(0.1) | |||||
| RadarMap.CompletenessWiki = sec.Key("completeness_wiki").MustFloat64(0.1) | |||||
| RadarMap.Liveness = sec.Key("liveness").MustFloat64(0.3) | |||||
| RadarMap.LivenessCommit = sec.Key("liveness_commit").MustFloat64(0.2) | |||||
| RadarMap.LivenessIssue = sec.Key("liveness_issue").MustFloat64(0.2) | |||||
| RadarMap.LivenessPR = sec.Key("liveness_pr").MustFloat64(0.2) | |||||
| RadarMap.LivenessRelease = sec.Key("liveness_release").MustFloat64(0.4) | |||||
| RadarMap.ProjectHealth = sec.Key("project_health").MustFloat64(0.1) | |||||
| RadarMap.ProjectHealthIssueCompleteRatio = sec.Key("project_health_issue_complete_ratio").MustFloat64(100) | |||||
| RadarMap.TeamHealth = sec.Key("team_health").MustFloat64(0.1) | |||||
| RadarMap.TeamHealthContributors = sec.Key("team_health_contributors").MustFloat64(0.2) | |||||
| RadarMap.TeamHealthKeyContributors = sec.Key("team_health_key_contributors").MustFloat64(0.6) | |||||
| RadarMap.TeamHealthContributorsAdded = sec.Key("team_health_contributors_added").MustFloat64(0.2) | |||||
| RadarMap.Growth = sec.Key("growth").MustFloat64(0.1) | |||||
| RadarMap.GrowthCodeLines = sec.Key("growth_code_lines").MustFloat64(0.2) | |||||
| RadarMap.GrowthIssue = sec.Key("growth_issue").MustFloat64(0.2) | |||||
| RadarMap.GrowthContributors = sec.Key("growth_contributors").MustFloat64(0.2) | |||||
| RadarMap.GrowthCommit = sec.Key("growth_commit").MustFloat64(0.2) | |||||
| RadarMap.GrowthComments = sec.Key("growth_comments").MustFloat64(0.2) | |||||
| } | } | ||||
| func loadInternalToken(sec *ini.Section) string { | func loadInternalToken(sec *ini.Section) string { | ||||
| @@ -5,16 +5,27 @@ | |||||
| package storage | package storage | ||||
| import ( | import ( | ||||
| "github.com/unknwon/com" | |||||
| "io" | |||||
| "path" | "path" | ||||
| "strconv" | "strconv" | ||||
| "strings" | "strings" | ||||
| "github.com/unknwon/com" | |||||
| "code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
| "code.gitea.io/gitea/modules/obs" | "code.gitea.io/gitea/modules/obs" | ||||
| "code.gitea.io/gitea/modules/setting" | "code.gitea.io/gitea/modules/setting" | ||||
| ) | ) | ||||
| type FileInfo struct { | |||||
| FileName string `json:"FileName"` | |||||
| ModTime string `json:"ModTime"` | |||||
| IsDir bool `json:"IsDir"` | |||||
| Size int64 `json:"Size"` | |||||
| ParenDir string `json:"ParenDir"` | |||||
| UUID string `json:"UUID"` | |||||
| } | |||||
| //check if has the object | //check if has the object | ||||
| //todo:修改查询方式 | //todo:修改查询方式 | ||||
| func ObsHasObject(path string) (bool, error) { | func ObsHasObject(path string) (bool, error) { | ||||
| @@ -102,6 +113,108 @@ func CompleteObsMultiPartUpload(uuid, uploadID, fileName string) error { | |||||
| return nil | return nil | ||||
| } | } | ||||
| func ObsMultiPartUpload(uuid string, uploadId string, partNumber int, fileName string, putBody io.ReadCloser) error { | |||||
| input := &obs.UploadPartInput{} | |||||
| input.Bucket = setting.Bucket | |||||
| input.Key = strings.TrimPrefix(path.Join(setting.BasePath, path.Join(uuid[0:1], uuid[1:2], uuid, fileName)), "/") | |||||
| input.UploadId = uploadId | |||||
| input.PartNumber = partNumber | |||||
| input.Body = putBody | |||||
| output, err := ObsCli.UploadPart(input) | |||||
| if err == nil { | |||||
| log.Info("RequestId:%s\n", output.RequestId) | |||||
| log.Info("ETag:%s\n", output.ETag) | |||||
| return nil | |||||
| } else { | |||||
| if obsError, ok := err.(obs.ObsError); ok { | |||||
| log.Info(obsError.Code) | |||||
| log.Info(obsError.Message) | |||||
| return obsError | |||||
| } else { | |||||
| log.Error("error:", err.Error()) | |||||
| return err | |||||
| } | |||||
| } | |||||
| } | |||||
| func ObsDownload(uuid string, fileName string) (io.ReadCloser, error) { | |||||
| input := &obs.GetObjectInput{} | |||||
| input.Bucket = setting.Bucket | |||||
| input.Key = strings.TrimPrefix(path.Join(setting.BasePath, path.Join(uuid[0:1], uuid[1:2], uuid, fileName)), "/") | |||||
| // input.Key = strings.TrimPrefix(path.Join(setting.BasePath, path.Join(uuid[0:1], uuid[1:2], uuid)), "/") | |||||
| output, err := ObsCli.GetObject(input) | |||||
| if err == nil { | |||||
| log.Info("StorageClass:%s, ETag:%s, ContentType:%s, ContentLength:%d, LastModified:%s\n", | |||||
| output.StorageClass, output.ETag, output.ContentType, output.ContentLength, output.LastModified) | |||||
| return output.Body, nil | |||||
| } else if obsError, ok := err.(obs.ObsError); ok { | |||||
| log.Error("Code:%s, Message:%s", obsError.Code, obsError.Message) | |||||
| return nil, obsError | |||||
| } else { | |||||
| return nil, err | |||||
| } | |||||
| } | |||||
| func ObsModelDownload(JobName string, fileName string) (io.ReadCloser, error) { | |||||
| input := &obs.GetObjectInput{} | |||||
| input.Bucket = setting.Bucket | |||||
| input.Key = strings.TrimPrefix(path.Join(setting.TrainJobModelPath, JobName, setting.OutPutPath, fileName), "/") | |||||
| // input.Key = strings.TrimPrefix(path.Join(setting.BasePath, path.Join(uuid[0:1], uuid[1:2], uuid)), "/") | |||||
| output, err := ObsCli.GetObject(input) | |||||
| if err == nil { | |||||
| log.Info("StorageClass:%s, ETag:%s, ContentType:%s, ContentLength:%d, LastModified:%s\n", | |||||
| output.StorageClass, output.ETag, output.ContentType, output.ContentLength, output.LastModified) | |||||
| return output.Body, nil | |||||
| } else if obsError, ok := err.(obs.ObsError); ok { | |||||
| log.Error("Code:%s, Message:%s", obsError.Code, obsError.Message) | |||||
| return nil, obsError | |||||
| } else { | |||||
| return nil, err | |||||
| } | |||||
| } | |||||
| func GetObsListObject(jobName, parentDir string) ([]FileInfo, error) { | |||||
| input := &obs.ListObjectsInput{} | |||||
| input.Bucket = setting.Bucket | |||||
| input.Prefix = strings.TrimPrefix(path.Join(setting.TrainJobModelPath, jobName, setting.OutPutPath, parentDir), "/") | |||||
| output, err := ObsCli.ListObjects(input) | |||||
| fileInfos := make([]FileInfo, 0) | |||||
| if err == nil { | |||||
| for _, val := range output.Contents { | |||||
| str1 := strings.Split(val.Key, "/") | |||||
| var isDir bool | |||||
| var fileName,nextParentDir string | |||||
| if strings.HasSuffix(val.Key, "/") { | |||||
| fileName = str1[len(str1)-2] | |||||
| isDir = true | |||||
| nextParentDir = fileName | |||||
| if fileName == parentDir || (fileName + "/") == setting.OutPutPath { | |||||
| continue | |||||
| } | |||||
| } else { | |||||
| fileName = str1[len(str1)-1] | |||||
| isDir = false | |||||
| } | |||||
| fileInfo := FileInfo{ | |||||
| ModTime: val.LastModified.Format("2006-01-02 15:04:05"), | |||||
| FileName: fileName, | |||||
| Size: val.Size, | |||||
| IsDir:isDir, | |||||
| ParenDir: nextParentDir, | |||||
| } | |||||
| fileInfos = append(fileInfos, fileInfo) | |||||
| } | |||||
| return fileInfos, err | |||||
| } else { | |||||
| if obsError, ok := err.(obs.ObsError); ok { | |||||
| log.Error("Code:%s, Message:%s", obsError.Code, obsError.Message) | |||||
| } | |||||
| return nil, err | |||||
| } | |||||
| } | |||||
| func ObsGenMultiPartSignedUrl(uuid string, uploadId string, partNumber int, fileName string) (string, error) { | func ObsGenMultiPartSignedUrl(uuid string, uploadId string, partNumber int, fileName string) (string, error) { | ||||
| input := &obs.CreateSignedUrlInput{} | input := &obs.CreateSignedUrlInput{} | ||||
| @@ -125,6 +238,26 @@ func ObsGenMultiPartSignedUrl(uuid string, uploadId string, partNumber int, file | |||||
| return output.SignedUrl, nil | return output.SignedUrl, nil | ||||
| } | } | ||||
| func GetObsCreateSignedUrl(jobName, parentDir, fileName string) (string, error) { | |||||
| input := &obs.CreateSignedUrlInput{} | |||||
| input.Bucket = setting.Bucket | |||||
| input.Key = strings.TrimPrefix(path.Join(setting.TrainJobModelPath, jobName, setting.OutPutPath, parentDir, fileName), "/") | |||||
| input.Expires = 60 * 60 | |||||
| input.Method = obs.HttpMethodGet | |||||
| reqParams := make(map[string]string) | |||||
| reqParams["response-content-disposition"] = "attachment; filename=\"" + fileName + "\"" | |||||
| input.QueryParams = reqParams | |||||
| output, err := ObsCli.CreateSignedUrl(input) | |||||
| if err != nil { | |||||
| log.Error("CreateSignedUrl failed:", err.Error()) | |||||
| return "", err | |||||
| } | |||||
| return output.SignedUrl, nil | |||||
| } | |||||
| func ObsGetPreSignedUrl(uuid, fileName string) (string, error) { | func ObsGetPreSignedUrl(uuid, fileName string) (string, error) { | ||||
| input := &obs.CreateSignedUrlInput{} | input := &obs.CreateSignedUrlInput{} | ||||
| input.Method = obs.HttpMethodGet | input.Method = obs.HttpMethodGet | ||||
| @@ -755,15 +755,24 @@ unit_disabled = The site administrator has disabled this repository section. | |||||
| language_other = Other | language_other = Other | ||||
| datasets = Datasets | datasets = Datasets | ||||
| datasets.desc = Enable Dataset | datasets.desc = Enable Dataset | ||||
| cloudbrain=cloudbrain | |||||
| debug=Debug | |||||
| stop=Stop | |||||
| delete=Delete | |||||
| model_download=Model Download | |||||
| submit_image=Submit Image | |||||
| download=Download | |||||
| cloudbrain=Cloudbrain | |||||
| cloudbrain.new=New cloudbrain | cloudbrain.new=New cloudbrain | ||||
| cloudbrain.desc=cloudbrain | |||||
| cloudbrain.desc=Cloudbrain | |||||
| cloudbrain.cancel=Cancel | cloudbrain.cancel=Cancel | ||||
| cloudbrain.commit_image = submit | |||||
| clone_cnt=download | |||||
| balance = balance | |||||
| balance.total_view = total balance | |||||
| balance.available = available balance: | |||||
| cloudbrain.commit_image = Submit | |||||
| clone_cnt=Download | |||||
| balance = Balance | |||||
| balance.total_view = Total Balance | |||||
| balance.available = Available Balance: | |||||
| cloudbrain1 = cloudbrain1 | cloudbrain1 = cloudbrain1 | ||||
| cloudbrain2 = cloudbrain2 | cloudbrain2 = cloudbrain2 | ||||
| cloudbrain_selection = select cloudbrain | cloudbrain_selection = select cloudbrain | ||||
| @@ -774,6 +783,60 @@ cloudbrain_creator=Creator | |||||
| cloudbrain_task = Task Name | cloudbrain_task = Task Name | ||||
| cloudbrain_operate = Operate | cloudbrain_operate = Operate | ||||
| cloudbrain_status_createtime = Status/Createtime | cloudbrain_status_createtime = Status/Createtime | ||||
| cloudbrain_status_runtime = Running Time | |||||
| modelarts.notebook=Debug Task | |||||
| modelarts.train_job=Train Task | |||||
| modelarts.train_job.new_debug= New Debug Task | |||||
| modelarts.train_job.new_train=New Train Task | |||||
| modelarts.train_job.config=Configuration information | |||||
| modelarts.train_job.new=New train Task | |||||
| modelarts.train_job.new_place=The description should not exceed 256 characters | |||||
| modelarts.train_job.basic_info=Basic Info | |||||
| modelarts.train_job.job_status=Job Status | |||||
| modelarts.train_job.job_name=Job Name | |||||
| modelarts.train_job.version=Job Version | |||||
| modelarts.train_job.start_time=Start Time | |||||
| modelarts.train_job.dura_time=Running Time | |||||
| modelarts.train_job.description=Description | |||||
| modelarts.train_job.parameter_setting=Parameter setting | |||||
| modelarts.train_job.parameter_setting_info=Parameter Info | |||||
| modelarts.train_job.fast_parameter_setting=fast_parameter_setting | |||||
| modelarts.train_job.fast_parameter_setting_config=fast_parameter_setting_config | |||||
| modelarts.train_job.fast_parameter_setting_config_link=fast_parameter_setting_config_link | |||||
| modelarts.train_job.frames=frames | |||||
| modelarts.train_job.algorithm_origin=Algorithm Origin | |||||
| modelarts.train_job.AI_driver=AI Engine | |||||
| modelarts.train_job.start_file=Start File | |||||
| modelarts.train_job.boot_file_helper=The startup file is the entry file that your program executes, and it must be a file ending in .py | |||||
| modelarts.train_job.dataset=Dataset | |||||
| modelarts.train_job.run_parameter=Run Parameter | |||||
| modelarts.train_job.add_run_parameter=Add Run Parameter | |||||
| modelarts.train_job.parameter_name=Parameter Name | |||||
| modelarts.train_job.parameter_value=Parameter Value | |||||
| modelarts.train_job.resource_setting=resource_setting | |||||
| modelarts.train_job.resource_setting_info=resource_setting_info | |||||
| modelarts.train_job.resource_pool=resource_pool | |||||
| modelarts.train_job.resource_type=resource_type | |||||
| modelarts.train_job.standard=Standard | |||||
| modelarts.train_job.NAS_address=NAS Address | |||||
| modelarts.train_job.NAS_mount_path=NAS Mount Path | |||||
| modelarts.train_job.query_whether_save_parameter=query_whether_save_parameter | |||||
| modelarts.train_job.save_helper=save_helper | |||||
| modelarts.train_job.common_frame=common_frame | |||||
| modelarts.train_job.amount_of_compute_node=Amount of Compute Node | |||||
| modelarts.train_job.job_parameter_name=job_parameter_name | |||||
| modelarts.train_job.parameter_description=parameter_description | |||||
| modelarts.log=Log | |||||
| modelarts.version_manage=Version Manage | |||||
| modelarts.back=Back | |||||
| modelarts.train_job_para_admin=train_job_para_admin | |||||
| modelarts.train_job_para.edit=train_job_para.edit | |||||
| modelarts.train_job_para.connfirm=train_job_para.connfirm | |||||
| template.items = Template Items | template.items = Template Items | ||||
| template.git_content = Git Content (Default Branch) | template.git_content = Git Content (Default Branch) | ||||
| @@ -757,6 +757,14 @@ unit_disabled=站点管理员已禁用此项目单元。 | |||||
| language_other=其它 | language_other=其它 | ||||
| datasets=数据集 | datasets=数据集 | ||||
| datasets.desc=数据集功能 | datasets.desc=数据集功能 | ||||
| debug=调试 | |||||
| stop=停止 | |||||
| delete=删除 | |||||
| model_download=模型下载 | |||||
| submit_image=提交镜像 | |||||
| download=模型下载 | |||||
| cloudbrain=云脑 | cloudbrain=云脑 | ||||
| cloudbrain.new=新建任务 | cloudbrain.new=新建任务 | ||||
| cloudbrain.desc=云脑功能 | cloudbrain.desc=云脑功能 | ||||
| @@ -776,6 +784,62 @@ cloudbrain_creator=创建者 | |||||
| cloudbrain_task=任务名称 | cloudbrain_task=任务名称 | ||||
| cloudbrain_operate=操作 | cloudbrain_operate=操作 | ||||
| cloudbrain_status_createtime=状态/创建时间 | cloudbrain_status_createtime=状态/创建时间 | ||||
| cloudbrain_status_runtime = 运行时长 | |||||
| cloudbrain_jobname_err=只能以小写字母或数字开头且只包含小写字母、数字、_和-,不能以_结尾,最长36个字符。 | |||||
| modelarts.notebook=调试任务 | |||||
| modelarts.train_job=训练任务 | |||||
| modelarts.train_job.new_debug=新建调试任务 | |||||
| modelarts.train_job.new_train=新建训练任务 | |||||
| modelarts.train_job.config=配置信息 | |||||
| modelarts.train_job.new=新建训练任务 | |||||
| modelarts.train_job.new_place=描述字数不超过256个字符 | |||||
| modelarts.train_job.basic_info=基本信息 | |||||
| modelarts.train_job.job_status=任务状态 | |||||
| modelarts.train_job.job_name=任务名称 | |||||
| modelarts.train_job.version=任务版本 | |||||
| modelarts.train_job.start_time=开始时间 | |||||
| modelarts.train_job.dura_time=运行时长 | |||||
| modelarts.train_job.description=任务描述 | |||||
| modelarts.train_job.parameter_setting=参数设置 | |||||
| modelarts.train_job.parameter_setting_info=参数信息 | |||||
| modelarts.train_job.fast_parameter_setting=一键式参数配置 | |||||
| modelarts.train_job.fast_parameter_setting_config=如您已保存过参数配置,可单击 | |||||
| modelarts.train_job.fast_parameter_setting_config_link=这里 | |||||
| modelarts.train_job.frames=常用框架 | |||||
| modelarts.train_job.algorithm_origin=算法来源 | |||||
| modelarts.train_job.AI_driver=AI引擎 | |||||
| modelarts.train_job.start_file=启动文件 | |||||
| modelarts.train_job.boot_file_helper=启动文件是您程序执行的入口文件,必须是以.py结尾的文件。 | |||||
| modelarts.train_job.boot_file_place=填写启动文件路径,默认为train.py | |||||
| modelarts.train_job.dataset=数据集 | |||||
| modelarts.train_job.run_parameter=运行参数 | |||||
| modelarts.train_job.add_run_parameter=增加运行参数 | |||||
| modelarts.train_job.parameter_name=参数名 | |||||
| modelarts.train_job.parameter_value=参数值 | |||||
| modelarts.train_job.resource_setting=资源设置 | |||||
| modelarts.train_job.resource_setting_info=资源信息 | |||||
| modelarts.train_job.resource_pool=资源池 | |||||
| modelarts.train_job.resource_type=资源类型 | |||||
| modelarts.train_job.standard=规格 | |||||
| modelarts.train_job.NAS_address=NAS地址 | |||||
| modelarts.train_job.NAS_mount_path=NAS挂载路径 | |||||
| modelarts.train_job.query_whether_save_parameter=保存作业参数 | |||||
| modelarts.train_job.save_helper=保存当前作业的配置参数,后续您可以使用已保存的配置参数快速创建训练作业。 | |||||
| modelarts.train_job.common_frame=常用框架 | |||||
| modelarts.train_job.amount_of_compute_node=计算节点个数 | |||||
| modelarts.train_job.job_parameter_name=任务参数名称 | |||||
| modelarts.train_job.parameter_description=任务参数描述 | |||||
| modelarts.log=日志 | |||||
| modelarts.version_manage=版本管理 | |||||
| modelarts.back=返回 | |||||
| modelarts.train_job_para_admin=任务参数管理 | |||||
| modelarts.train_job_para.edit=编辑 | |||||
| modelarts.train_job_para.connfirm=确定 | |||||
| template.items=模板选项 | template.items=模板选项 | ||||
| template.git_content=Git数据(默认分支) | template.git_content=Git数据(默认分支) | ||||
| @@ -41,7 +41,7 @@ function setDataSetTask(){ | |||||
| //dislpayUser(); | //dislpayUser(); | ||||
| getLabelPropertyTask(); | getLabelPropertyTask(); | ||||
| displayLabelPropertyTask(); | displayLabelPropertyTask(); | ||||
| dataset_sele_Change(""); | |||||
| $(".ui.dataset.modal").modal("show"); | $(".ui.dataset.modal").modal("show"); | ||||
| } | } | ||||
| @@ -132,6 +132,7 @@ function setPredictTask(){ | |||||
| get_model_list(); | get_model_list(); | ||||
| displayModelTask(); | displayModelTask(); | ||||
| dataset_auto_sele_Change(""); | |||||
| $(".ui.predict.modal").modal("show"); | $(".ui.predict.modal").modal("show"); | ||||
| } | } | ||||
| @@ -197,13 +198,13 @@ function sele_export_Change(sele){ | |||||
| function dataset_sele_Change(sele){ | function dataset_sele_Change(sele){ | ||||
| var dataset_listName = $('#dataset_list option:selected').text(); | var dataset_listName = $('#dataset_list option:selected').text(); | ||||
| console.log("select dataset_list =" + dataset_listName); | console.log("select dataset_list =" + dataset_listName); | ||||
| $("#datasetlabeltaskname").attr({value:dataset_listName+"-人工标注"}); | |||||
| $("#datasetlabeltaskname").val(dataset_listName+"-人工标注"); | |||||
| } | } | ||||
| function dataset_auto_sele_Change(sele){ | function dataset_auto_sele_Change(sele){ | ||||
| var dataset_listName = $('#dataset_list_auto option:selected').text(); | var dataset_listName = $('#dataset_list_auto option:selected').text(); | ||||
| console.log("select dataset_list_auto =" + dataset_listName); | console.log("select dataset_list_auto =" + dataset_listName); | ||||
| $("#autolabeltaskname").attr({value:dataset_listName+"-自动标注"}); | |||||
| $("#autolabeltaskname").val(dataset_listName+"-自动标注"); | |||||
| } | } | ||||
| @@ -309,11 +310,11 @@ function label_task_create(task_name, relate_task_id, taskType,assign_user_id,la | |||||
| success:function(res){ | success:function(res){ | ||||
| console.log(res); | console.log(res); | ||||
| if(res.code == 0){ | if(res.code == 0){ | ||||
| alert("自动标注任务创建成功!"); | |||||
| alert("标注任务创建成功!"); | |||||
| createsucced = true; | createsucced = true; | ||||
| } | } | ||||
| else{ | else{ | ||||
| alert("创建自动标注任务失败," + res.message); | |||||
| alert("创建标注任务失败," + res.message); | |||||
| createsucced = false; | createsucced = false; | ||||
| } | } | ||||
| }, | }, | ||||
| @@ -0,0 +1,28 @@ | |||||
| function displayDir(uuid){ | |||||
| console.log('uuid 1=' + uuid); | |||||
| var html="<tr>\ | |||||
| <th></th>\ | |||||
| <th id=\"dataset_head\"></th>\ | |||||
| <th>数据集名称</th>\ | |||||
| <th>数据集类型</th>\ | |||||
| <th>数据集描述</th>\ | |||||
| <th>数据集创建者</th>\ | |||||
| </tr>"; | |||||
| for (var i=0;i<1;i++){ | |||||
| var row = "<tr>\ | |||||
| <td><input type=\"checkbox\"/></td>\ | |||||
| <td id=\"dataset_id\">"+uuid+"</td>\ | |||||
| <td>" + uuid +"</td>\ | |||||
| <td>测试</td>\ | |||||
| <td>测试</td>\ | |||||
| <td>测试</td>\ | |||||
| </tr>"; | |||||
| html=html+row; | |||||
| } | |||||
| document.getElementById('dataset-files-table').innerHTML=html; | |||||
| console.log('uuid 2=' + uuid); | |||||
| } | |||||
| @@ -853,7 +853,15 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| m.Get("/:jobid", repo.GetCloudbrainTask) | m.Get("/:jobid", repo.GetCloudbrainTask) | ||||
| }, reqRepoReader(models.UnitTypeCloudBrain)) | }, reqRepoReader(models.UnitTypeCloudBrain)) | ||||
| m.Group("/modelarts", func() { | m.Group("/modelarts", func() { | ||||
| m.Get("/:jobid", repo.GetModelArtsTask) | |||||
| m.Group("/notebook", func() { | |||||
| m.Get("/:jobid", repo.GetModelArtsNotebook) | |||||
| }) | |||||
| m.Group("/train-job", func() { | |||||
| m.Group("/:jobid", func() { | |||||
| m.Get("", repo.GetModelArtsTrainJob) | |||||
| m.Get("/log", repo.TrainJobGetLog) | |||||
| }) | |||||
| }) | |||||
| }, reqRepoReader(models.UnitTypeCloudBrain)) | }, reqRepoReader(models.UnitTypeCloudBrain)) | ||||
| }, repoAssignment()) | }, repoAssignment()) | ||||
| }) | }) | ||||
| @@ -65,12 +65,15 @@ func GetCloudbrainTask(ctx *context.APIContext) { | |||||
| return | return | ||||
| } | } | ||||
| taskRoles := result.TaskRoles | |||||
| taskRes, _ := models.ConvertToTaskPod(taskRoles[cloudbrain.SubTaskName].(map[string]interface{})) | |||||
| job.Status = result.JobStatus.State | |||||
| if result.JobStatus.State != string(models.JobWaiting) && result.JobStatus.State != string(models.JobFailed) { | |||||
| taskRoles := result.TaskRoles | |||||
| taskRes, _ := models.ConvertToTaskPod(taskRoles[cloudbrain.SubTaskName].(map[string]interface{})) | |||||
| job.ContainerIp = taskRes.TaskStatuses[0].ContainerIP | |||||
| job.ContainerID = taskRes.TaskStatuses[0].ContainerID | |||||
| job.Status = taskRes.TaskStatuses[0].State | |||||
| job.ContainerIp = taskRes.TaskStatuses[0].ContainerIP | |||||
| job.ContainerID = taskRes.TaskStatuses[0].ContainerID | |||||
| job.Status = taskRes.TaskStatuses[0].State | |||||
| } | |||||
| if result.JobStatus.State != string(models.JobWaiting) { | if result.JobStatus.State != string(models.JobWaiting) { | ||||
| err = models.UpdateJob(job) | err = models.UpdateJob(job) | ||||
| @@ -11,9 +11,10 @@ import ( | |||||
| "code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
| "code.gitea.io/gitea/modules/modelarts" | "code.gitea.io/gitea/modules/modelarts" | ||||
| "net/http" | "net/http" | ||||
| "strconv" | |||||
| ) | ) | ||||
| func GetModelArtsTask(ctx *context.APIContext) { | |||||
| func GetModelArtsNotebook(ctx *context.APIContext) { | |||||
| var ( | var ( | ||||
| err error | err error | ||||
| ) | ) | ||||
| @@ -43,3 +44,84 @@ func GetModelArtsTask(ctx *context.APIContext) { | |||||
| }) | }) | ||||
| } | } | ||||
| func GetModelArtsTrainJob(ctx *context.APIContext) { | |||||
| var ( | |||||
| err error | |||||
| ) | |||||
| jobID := ctx.Params(":jobid") | |||||
| repoID := ctx.Repo.Repository.ID | |||||
| job, err := models.GetRepoCloudBrainByJobID(repoID, jobID) | |||||
| if err != nil { | |||||
| ctx.NotFound(err) | |||||
| return | |||||
| } | |||||
| result, err := modelarts.GetTrainJob(jobID, strconv.FormatInt(job.VersionID, 10)) | |||||
| if err != nil { | |||||
| ctx.NotFound(err) | |||||
| return | |||||
| } | |||||
| job.Status = modelarts.TransTrainJobStatus(result.IntStatus) | |||||
| job.Duration = result.Duration | |||||
| job.TrainJobDuration = result.TrainJobDuration | |||||
| err = models.UpdateJob(job) | |||||
| if err != nil { | |||||
| log.Error("UpdateJob failed:", err) | |||||
| } | |||||
| ctx.JSON(http.StatusOK, map[string]interface{}{ | |||||
| "JobID": jobID, | |||||
| "JobStatus": job.Status, | |||||
| "JobDuration": job.Duration, | |||||
| }) | |||||
| } | |||||
| func TrainJobGetLog(ctx *context.APIContext) { | |||||
| var ( | |||||
| err error | |||||
| ) | |||||
| log.Info("test") | |||||
| var jobID = ctx.Params(":jobid") | |||||
| var logFileName = ctx.Query("file_name") | |||||
| var baseLine = ctx.Query("base_line") | |||||
| var order = ctx.Query("order") | |||||
| if order != modelarts.OrderDesc && order != modelarts.OrderAsc { | |||||
| log.Error("order(%s) check failed", order) | |||||
| ctx.JSON(http.StatusBadRequest, map[string]interface{}{ | |||||
| "err_msg": "order check failed", | |||||
| }) | |||||
| return | |||||
| } | |||||
| task, err := models.GetCloudbrainByJobID(jobID) | |||||
| if err != nil { | |||||
| log.Error("GetCloudbrainByJobID(%s) failed:%v", jobID, err.Error()) | |||||
| ctx.JSON(http.StatusInternalServerError, map[string]interface{}{ | |||||
| "err_msg": "GetCloudbrainByJobID failed", | |||||
| }) | |||||
| return | |||||
| } | |||||
| result, err := modelarts.GetTrainJobLog(jobID, strconv.FormatInt(task.VersionID, 10), baseLine, logFileName, order, modelarts.Lines) | |||||
| if err != nil { | |||||
| log.Error("GetTrainJobLog(%s) failed:%v", jobID, err.Error()) | |||||
| ctx.JSON(http.StatusInternalServerError, map[string]interface{}{ | |||||
| "err_msg": "GetTrainJobLog failed", | |||||
| }) | |||||
| return | |||||
| } | |||||
| ctx.JSON(http.StatusOK, map[string]interface{}{ | |||||
| "JobID": jobID, | |||||
| "StartLine": result.StartLine, | |||||
| "EndLine": result.EndLine, | |||||
| "Content": result.Content, | |||||
| "Lines": result.Lines, | |||||
| }) | |||||
| } | |||||
| @@ -281,10 +281,10 @@ func ExploreDatasets(ctx *context.Context) { | |||||
| } | } | ||||
| pager := context.NewPagination(int(count), opts.PageSize, page, 5) | pager := context.NewPagination(int(count), opts.PageSize, page, 5) | ||||
| ctx.Data["Keyword"] = opts.Keyword | |||||
| pager.SetDefaultParams(ctx) | pager.SetDefaultParams(ctx) | ||||
| ctx.Data["Page"] = pager | ctx.Data["Page"] = pager | ||||
| ctx.Data["Keyword"] = opts.Keyword | |||||
| ctx.Data["Datasets"] = datasets | ctx.Data["Datasets"] = datasets | ||||
| ctx.Data["Total"] = count | ctx.Data["Total"] = count | ||||
| ctx.Data["PageIsDatasets"] = true | ctx.Data["PageIsDatasets"] = true | ||||
| @@ -41,4 +41,6 @@ func UpdateAllRepoCommitCnt(ctx *macaron.Context) { | |||||
| func RepoStatisticManually(ctx *macaron.Context) { | func RepoStatisticManually(ctx *macaron.Context) { | ||||
| date := ctx.Query("date") | date := ctx.Query("date") | ||||
| repo.RepoStatisticDaily(date) | repo.RepoStatisticDaily(date) | ||||
| repo.SummaryStatisticDaily(date) | |||||
| repo.TimingCountDataByDate(date) | |||||
| } | } | ||||
| @@ -262,10 +262,15 @@ func GetAttachment(ctx *context.Context) { | |||||
| return | return | ||||
| } | } | ||||
| } else { | } else { | ||||
| url, err = storage.ObsGetPreSignedUrl(attach.UUID, attach.Name) | |||||
| if err != nil { | |||||
| ctx.ServerError("ObsGetPreSignedUrl", err) | |||||
| return | |||||
| if setting.PROXYURL != "" { | |||||
| url = setting.PROXYURL + "/obs_proxy_download?uuid=" + attach.UUID + "&file_name=" + attach.Name | |||||
| log.Info("return url=" + url) | |||||
| } else { | |||||
| url, err = storage.ObsGetPreSignedUrl(attach.UUID, attach.Name) | |||||
| if err != nil { | |||||
| ctx.ServerError("ObsGetPreSignedUrl", err) | |||||
| return | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -273,8 +278,21 @@ func GetAttachment(ctx *context.Context) { | |||||
| ctx.ServerError("Update", err) | ctx.ServerError("Update", err) | ||||
| return | return | ||||
| } | } | ||||
| if dataSet != nil { | |||||
| http.Redirect(ctx.Resp, ctx.Req.Request, url, http.StatusMovedPermanently) | |||||
| } else { | |||||
| fr, err := storage.Attachments.Open(attach.RelativePath()) | |||||
| if err != nil { | |||||
| ctx.ServerError("Open", err) | |||||
| return | |||||
| } | |||||
| defer fr.Close() | |||||
| if err = ServeData(ctx, attach.Name, fr); err != nil { | |||||
| ctx.ServerError("ServeData", err) | |||||
| return | |||||
| } | |||||
| } | |||||
| http.Redirect(ctx.Resp, ctx.Req.Request, url, http.StatusMovedPermanently) | |||||
| } else { | } else { | ||||
| fr, err := storage.Attachments.Open(attach.RelativePath()) | fr, err := storage.Attachments.Open(attach.RelativePath()) | ||||
| if err != nil { | if err != nil { | ||||
| @@ -282,7 +300,6 @@ func GetAttachment(ctx *context.Context) { | |||||
| return | return | ||||
| } | } | ||||
| defer fr.Close() | defer fr.Close() | ||||
| if err = increaseDownloadCount(attach, dataSet); err != nil { | if err = increaseDownloadCount(attach, dataSet); err != nil { | ||||
| ctx.ServerError("Update", err) | ctx.ServerError("Update", err) | ||||
| return | return | ||||
| @@ -662,6 +679,53 @@ func NewMultipart(ctx *context.Context) { | |||||
| } | } | ||||
| } | } | ||||
| func PutOBSProxyUpload(ctx *context.Context) { | |||||
| uuid := ctx.Query("uuid") | |||||
| uploadID := ctx.Query("uploadId") | |||||
| partNumber := ctx.QueryInt("partNumber") | |||||
| fileName := ctx.Query("file_name") | |||||
| RequestBody := ctx.Req.Body() | |||||
| if RequestBody == nil { | |||||
| ctx.Error(500, fmt.Sprintf("FormFile: %v", RequestBody)) | |||||
| return | |||||
| } | |||||
| err := storage.ObsMultiPartUpload(uuid, uploadID, partNumber, fileName, RequestBody.ReadCloser()) | |||||
| if err != nil { | |||||
| log.Info("upload error.") | |||||
| } | |||||
| } | |||||
| func GetOBSProxyDownload(ctx *context.Context) { | |||||
| uuid := ctx.Query("uuid") | |||||
| fileName := ctx.Query("file_name") | |||||
| body, err := storage.ObsDownload(uuid, fileName) | |||||
| if err != nil { | |||||
| log.Info("upload error.") | |||||
| } else { | |||||
| defer body.Close() | |||||
| ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+fileName) | |||||
| ctx.Resp.Header().Set("Content-Type", "application/octet-stream") | |||||
| p := make([]byte, 1024) | |||||
| var readErr error | |||||
| var readCount int | |||||
| // 读取对象内容 | |||||
| for { | |||||
| readCount, readErr = body.Read(p) | |||||
| if readCount > 0 { | |||||
| ctx.Resp.Write(p[:readCount]) | |||||
| //fmt.Printf("%s", p[:readCount]) | |||||
| } | |||||
| if readErr != nil { | |||||
| break | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| func GetMultipartUploadUrl(ctx *context.Context) { | func GetMultipartUploadUrl(ctx *context.Context) { | ||||
| uuid := ctx.Query("uuid") | uuid := ctx.Query("uuid") | ||||
| uploadID := ctx.Query("uploadID") | uploadID := ctx.Query("uploadID") | ||||
| @@ -689,10 +753,16 @@ func GetMultipartUploadUrl(ctx *context.Context) { | |||||
| return | return | ||||
| } | } | ||||
| } else { | } else { | ||||
| url, err = storage.ObsGenMultiPartSignedUrl(uuid, uploadID, partNumber, fileName) | |||||
| if err != nil { | |||||
| ctx.Error(500, fmt.Sprintf("ObsGenMultiPartSignedUrl failed: %v", err)) | |||||
| return | |||||
| if setting.PROXYURL != "" { | |||||
| url = setting.PROXYURL + "/obs_proxy_multipart?uuid=" + uuid + "&uploadId=" + uploadID + "&partNumber=" + fmt.Sprint(partNumber) + "&file_name=" + fileName | |||||
| log.Info("return url=" + url) | |||||
| } else { | |||||
| url, err = storage.ObsGenMultiPartSignedUrl(uuid, uploadID, partNumber, fileName) | |||||
| if err != nil { | |||||
| ctx.Error(500, fmt.Sprintf("ObsGenMultiPartSignedUrl failed: %v", err)) | |||||
| return | |||||
| } | |||||
| log.Info("url=" + url) | |||||
| } | } | ||||
| } | } | ||||
| @@ -847,16 +917,22 @@ func HandleUnDecompressAttachment() { | |||||
| } | } | ||||
| for _, attach := range attachs { | for _, attach := range attachs { | ||||
| err = worker.SendDecompressTask(contexExt.Background(), attach.UUID, attach.Name) | |||||
| if err != nil { | |||||
| log.Error("SendDecompressTask(%s) failed:%s", attach.UUID, err.Error()) | |||||
| } else { | |||||
| attach.DecompressState = models.DecompressStateIng | |||||
| err = models.UpdateAttachment(attach) | |||||
| if attach.Type == models.TypeCloudBrainOne { | |||||
| err = worker.SendDecompressTask(contexExt.Background(), attach.UUID, attach.Name) | |||||
| if err != nil { | if err != nil { | ||||
| log.Error("UpdateAttachment state(%s) failed:%s", attach.UUID, err.Error()) | |||||
| log.Error("SendDecompressTask(%s) failed:%s", attach.UUID, err.Error()) | |||||
| } else { | |||||
| attach.DecompressState = models.DecompressStateIng | |||||
| err = models.UpdateAttachment(attach) | |||||
| if err != nil { | |||||
| log.Error("UpdateAttachment state(%s) failed:%s", attach.UUID, err.Error()) | |||||
| } | |||||
| } | } | ||||
| } else if attach.Type == models.TypeCloudBrainTwo { | |||||
| attachjson, _ := json.Marshal(attach) | |||||
| labelmsg.SendDecompressAttachToLabelOBS(string(attachjson)) | |||||
| } | } | ||||
| } | } | ||||
| return | return | ||||
| @@ -40,6 +40,8 @@ var ( | |||||
| categories *models.Categories | categories *models.Categories | ||||
| ) | ) | ||||
| var jobNamePattern = regexp.MustCompile(`^[a-z0-9][a-z0-9-_]{1,34}[a-z0-9-]$`) | |||||
| // MustEnableDataset check if repository enable internal cb | // MustEnableDataset check if repository enable internal cb | ||||
| func MustEnableCloudbrain(ctx *context.Context) { | func MustEnableCloudbrain(ctx *context.Context) { | ||||
| if !ctx.Repo.CanRead(models.UnitTypeCloudBrain) { | if !ctx.Repo.CanRead(models.UnitTypeCloudBrain) { | ||||
| @@ -98,15 +100,15 @@ func cutString(str string, lens int) string { | |||||
| func jobNamePrefixValid(s string) string { | func jobNamePrefixValid(s string) string { | ||||
| lowStr := strings.ToLower(s) | lowStr := strings.ToLower(s) | ||||
| re := regexp.MustCompile(`[^a-z0-9_\\-]+`) | re := regexp.MustCompile(`[^a-z0-9_\\-]+`) | ||||
| removeSpecial := re.ReplaceAllString(lowStr, "") | removeSpecial := re.ReplaceAllString(lowStr, "") | ||||
| re = regexp.MustCompile(`^[_\\-]+`) | re = regexp.MustCompile(`^[_\\-]+`) | ||||
| return re.ReplaceAllString(removeSpecial, "") | return re.ReplaceAllString(removeSpecial, "") | ||||
| } | } | ||||
| func cloudBrainNewDataPrepare(ctx *context.Context) error{ | |||||
| func cloudBrainNewDataPrepare(ctx *context.Context) error { | |||||
| ctx.Data["PageIsCloudBrain"] = true | ctx.Data["PageIsCloudBrain"] = true | ||||
| t := time.Now() | t := time.Now() | ||||
| var jobName = jobNamePrefixValid(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:] | ||||
| @@ -175,6 +177,9 @@ func cloudBrainNewDataPrepare(ctx *context.Context) error{ | |||||
| 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 | ||||
| ctx.Data["brainscore_path"] = cloudbrain.BrainScoreMountPath | |||||
| ctx.Data["is_brainscore_enabled"] = setting.IsBrainScoreEnabled | |||||
| return nil | return nil | ||||
| } | } | ||||
| @@ -198,7 +203,12 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) { | |||||
| codePath := setting.JobPath + jobName + cloudbrain.CodeMountPath | codePath := setting.JobPath + jobName + cloudbrain.CodeMountPath | ||||
| resourceSpecId := form.ResourceSpecId | resourceSpecId := form.ResourceSpecId | ||||
| if jobType != string(models.JobTypeBenchmark) && jobType != string(models.JobTypeDebug) && jobType != string(models.JobTypeSnn4imagenet) { | |||||
| if !jobNamePattern.MatchString(jobName) { | |||||
| ctx.RenderWithErr(ctx.Tr("repo.cloudbrain_jobname_err"), tplModelArtsNew, &form) | |||||
| return | |||||
| } | |||||
| if jobType != string(models.JobTypeBenchmark) && jobType != string(models.JobTypeDebug) && jobType != string(models.JobTypeSnn4imagenet) && jobType != string(models.JobTypeBrainScore) { | |||||
| log.Error("jobtype error:", jobType, ctx.Data["MsgID"]) | log.Error("jobtype error:", jobType, ctx.Data["MsgID"]) | ||||
| cloudBrainNewDataPrepare(ctx) | cloudBrainNewDataPrepare(ctx) | ||||
| ctx.RenderWithErr("jobtype error", tplCloudBrainNew, &form) | ctx.RenderWithErr("jobtype error", tplCloudBrainNew, &form) | ||||
| @@ -247,7 +257,12 @@ 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, resourceSpecId) | |||||
| brainScorePath := setting.JobPath + jobName + cloudbrain.BrainScoreMountPath | |||||
| if setting.IsBrainScoreEnabled && jobType == string(models.JobTypeBrainScore) { | |||||
| downloadRateCode(repo, jobName, setting.BrainScoreCode, brainScorePath, "", "") | |||||
| } | |||||
| err = cloudbrain.GenerateTask(ctx, jobName, image, command, uuid, codePath, modelPath, benchmarkPath, snn4imagenetPath, brainScorePath, jobType, gpuQueue, resourceSpecId) | |||||
| if err != nil { | if err != nil { | ||||
| cloudBrainNewDataPrepare(ctx) | cloudBrainNewDataPrepare(ctx) | ||||
| ctx.RenderWithErr(err.Error(), tplCloudBrainNew, &form) | ctx.RenderWithErr(err.Error(), tplCloudBrainNew, &form) | ||||
| @@ -273,17 +288,30 @@ func CloudBrainShow(ctx *context.Context) { | |||||
| if result != nil { | if result != nil { | ||||
| jobRes, _ := models.ConvertToJobResultPayload(result.Payload) | jobRes, _ := models.ConvertToJobResultPayload(result.Payload) | ||||
| jobRes.Resource.Memory = strings.ReplaceAll(jobRes.Resource.Memory, "Mi", "MB") | jobRes.Resource.Memory = strings.ReplaceAll(jobRes.Resource.Memory, "Mi", "MB") | ||||
| ctx.Data["result"] = jobRes | |||||
| taskRoles := jobRes.TaskRoles | taskRoles := jobRes.TaskRoles | ||||
| taskRes, _ := models.ConvertToTaskPod(taskRoles[cloudbrain.SubTaskName].(map[string]interface{})) | |||||
| ctx.Data["taskRes"] = taskRes | |||||
| task.Status = taskRes.TaskStatuses[0].State | |||||
| task.ContainerID = taskRes.TaskStatuses[0].ContainerID | |||||
| task.ContainerIp = taskRes.TaskStatuses[0].ContainerIP | |||||
| err = models.UpdateJob(task) | |||||
| if err != nil { | |||||
| ctx.Data["error"] = err.Error() | |||||
| if jobRes.JobStatus.State != string(models.JobFailed) { | |||||
| taskRes, _ := models.ConvertToTaskPod(taskRoles[cloudbrain.SubTaskName].(map[string]interface{})) | |||||
| ctx.Data["taskRes"] = taskRes | |||||
| task.Status = taskRes.TaskStatuses[0].State | |||||
| task.ContainerID = taskRes.TaskStatuses[0].ContainerID | |||||
| task.ContainerIp = taskRes.TaskStatuses[0].ContainerIP | |||||
| err = models.UpdateJob(task) | |||||
| if err != nil { | |||||
| ctx.Data["error"] = err.Error() | |||||
| } | |||||
| } else { | |||||
| task.Status = jobRes.JobStatus.State | |||||
| taskRes := models.TaskPod{TaskStatuses: []models.TaskStatuses{ | |||||
| { | |||||
| State: jobRes.JobStatus.State, | |||||
| }, | |||||
| }} | |||||
| ctx.Data["taskRes"] = taskRes | |||||
| jobRes.JobStatus.StartTime = time.Unix(int64(task.CreatedUnix), 0).Format("2006-01-02 15:04:05") | |||||
| jobRes.JobStatus.EndTime = time.Unix(int64(task.UpdatedUnix), 0).Format("2006-01-02 15:04:05") | |||||
| } | } | ||||
| ctx.Data["result"] = jobRes | |||||
| } | } | ||||
| ctx.Data["task"] = task | ctx.Data["task"] = task | ||||
| @@ -293,6 +321,11 @@ func CloudBrainShow(ctx *context.Context) { | |||||
| func CloudBrainDebug(ctx *context.Context) { | func CloudBrainDebug(ctx *context.Context) { | ||||
| var jobID = ctx.Params(":jobid") | var jobID = ctx.Params(":jobid") | ||||
| if !ctx.IsSigned { | |||||
| log.Error("the user has not signed in") | |||||
| ctx.Error(http.StatusForbidden, "","the user has not signed in") | |||||
| return | |||||
| } | |||||
| task, err := models.GetCloudbrainByJobID(jobID) | task, err := models.GetCloudbrainByJobID(jobID) | ||||
| if err != nil { | if err != nil { | ||||
| ctx.ServerError("GetCloudbrainByJobID failed", err) | ctx.ServerError("GetCloudbrainByJobID failed", err) | ||||
| @@ -305,6 +338,11 @@ func CloudBrainDebug(ctx *context.Context) { | |||||
| func CloudBrainCommitImage(ctx *context.Context, form auth.CommitImageCloudBrainForm) { | func CloudBrainCommitImage(ctx *context.Context, form auth.CommitImageCloudBrainForm) { | ||||
| var jobID = ctx.Params(":jobid") | var jobID = ctx.Params(":jobid") | ||||
| if !ctx.IsSigned { | |||||
| log.Error("the user has not signed in") | |||||
| ctx.Error(http.StatusForbidden, "","the user has not signed in") | |||||
| return | |||||
| } | |||||
| task, err := models.GetCloudbrainByJobID(jobID) | task, err := models.GetCloudbrainByJobID(jobID) | ||||
| if err != nil { | if err != nil { | ||||
| ctx.JSON(200, map[string]string{ | ctx.JSON(200, map[string]string{ | ||||
| @@ -343,7 +381,7 @@ func CloudBrainStop(ctx *context.Context) { | |||||
| return | return | ||||
| } | } | ||||
| if task.Status == string(models.JobStopped) { | |||||
| if task.Status == string(models.JobStopped) || task.Status == string(models.JobFailed) { | |||||
| log.Error("the job(%s) has been stopped", task.JobName, ctx.Data["msgID"]) | log.Error("the job(%s) has been stopped", task.JobName, ctx.Data["msgID"]) | ||||
| ctx.ServerError("the job has been stopped", errors.New("the job has been stopped")) | ctx.ServerError("the job has been stopped", errors.New("the job has been stopped")) | ||||
| return | return | ||||
| @@ -446,7 +484,7 @@ func CloudBrainDel(ctx *context.Context) { | |||||
| return | return | ||||
| } | } | ||||
| if task.Status != string(models.JobStopped) { | |||||
| if task.Status != string(models.JobStopped) && task.Status != string(models.JobFailed) { | |||||
| log.Error("the job(%s) has not been stopped", task.JobName, ctx.Data["msgID"]) | log.Error("the job(%s) has not been stopped", task.JobName, ctx.Data["msgID"]) | ||||
| ctx.ServerError("the job has not been stopped", errors.New("the job has not been stopped")) | ctx.ServerError("the job has not been stopped", errors.New("the job has not been stopped")) | ||||
| return | return | ||||
| @@ -482,7 +520,7 @@ func CloudBrainShowModels(ctx *context.Context) { | |||||
| return | return | ||||
| } | } | ||||
| var fileInfos []FileInfo | |||||
| var fileInfos []storage.FileInfo | |||||
| err = json.Unmarshal([]byte(dirs), &fileInfos) | err = json.Unmarshal([]byte(dirs), &fileInfos) | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("json.Unmarshal failed:%v", err.Error(), ctx.Data["msgID"]) | log.Error("json.Unmarshal failed:%v", err.Error(), ctx.Data["msgID"]) | ||||
| @@ -552,7 +590,6 @@ func CloudBrainDownloadModel(ctx *context.Context) { | |||||
| ctx.ServerError("PresignedGetURL", err) | ctx.ServerError("PresignedGetURL", err) | ||||
| return | return | ||||
| } | } | ||||
| http.Redirect(ctx.Resp, ctx.Req.Request, url, http.StatusMovedPermanently) | http.Redirect(ctx.Resp, ctx.Req.Request, url, http.StatusMovedPermanently) | ||||
| } | } | ||||
| @@ -568,6 +605,8 @@ func GetRate(ctx *context.Context) { | |||||
| ctx.Redirect(setting.BenchmarkServerHost + "?username=" + ctx.User.Name) | ctx.Redirect(setting.BenchmarkServerHost + "?username=" + ctx.User.Name) | ||||
| } else if job.JobType == string(models.JobTypeSnn4imagenet) { | } else if job.JobType == string(models.JobTypeSnn4imagenet) { | ||||
| ctx.Redirect(setting.Snn4imagenetServerHost) | ctx.Redirect(setting.Snn4imagenetServerHost) | ||||
| } else if job.JobType == string(models.JobTypeBrainScore) { | |||||
| ctx.Redirect(setting.BrainScoreServerHost) | |||||
| } else { | } else { | ||||
| log.Error("JobType error:%s", job.JobType, ctx.Data["msgID"]) | log.Error("JobType error:%s", job.JobType, ctx.Data["msgID"]) | ||||
| } | } | ||||
| @@ -579,9 +618,9 @@ func downloadCode(repo *models.Repository, codePath string) error { | |||||
| return err | return err | ||||
| } | } | ||||
| configFile, err := os.OpenFile(codePath + "/.git/config", os.O_RDWR, 0666) | |||||
| configFile, err := os.OpenFile(codePath+"/.git/config", os.O_RDWR, 0666) | |||||
| if err != nil { | if err != nil { | ||||
| log.Error("open file(%s) failed:%v", codePath + "/,git/config", err) | |||||
| log.Error("open file(%s) failed:%v", codePath+"/,git/config", err) | |||||
| return err | return err | ||||
| } | } | ||||
| @@ -601,10 +640,10 @@ func downloadCode(repo *models.Repository, codePath string) error { | |||||
| } | } | ||||
| } | } | ||||
| if strings.Contains(line, "url") && strings.Contains(line, ".git"){ | |||||
| if strings.Contains(line, "url") && strings.Contains(line, ".git") { | |||||
| originUrl := "\turl = " + repo.CloneLink().HTTPS + "\n" | originUrl := "\turl = " + repo.CloneLink().HTTPS + "\n" | ||||
| if len(line) > len(originUrl) { | if len(line) > len(originUrl) { | ||||
| originUrl += strings.Repeat( " ", len(line) - len(originUrl)) | |||||
| originUrl += strings.Repeat(" ", len(line)-len(originUrl)) | |||||
| } | } | ||||
| bytes := []byte(originUrl) | bytes := []byte(originUrl) | ||||
| _, err := configFile.WriteAt(bytes, pos) | _, err := configFile.WriteAt(bytes, pos) | ||||
| @@ -21,15 +21,6 @@ const ( | |||||
| tplDirIndex base.TplName = "repo/datasets/dirs/index" | tplDirIndex base.TplName = "repo/datasets/dirs/index" | ||||
| ) | ) | ||||
| type FileInfo struct { | |||||
| FileName string `json:"FileName"` | |||||
| ModTime string `json:"ModTime"` | |||||
| IsDir bool `json:"IsDir"` | |||||
| Size int64 `json:"Size"` | |||||
| ParenDir string `json:"ParenDir"` | |||||
| UUID string `json:"UUID"` | |||||
| } | |||||
| type RespGetDirs struct { | type RespGetDirs struct { | ||||
| ResultCode string `json:"resultCode"` | ResultCode string `json:"resultCode"` | ||||
| FileInfos string `json:"fileInfos"` | FileInfos string `json:"fileInfos"` | ||||
| @@ -59,7 +50,7 @@ func DeleteAllUnzipFile(attachment *models.Attachment, parentDir string) { | |||||
| return | return | ||||
| } | } | ||||
| var fileInfos []FileInfo | |||||
| var fileInfos []storage.FileInfo | |||||
| err = json.Unmarshal([]byte(dirs), &fileInfos) | err = json.Unmarshal([]byte(dirs), &fileInfos) | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("json.Unmarshal failed:", err.Error()) | log.Error("json.Unmarshal failed:", err.Error()) | ||||
| @@ -1,10 +1,13 @@ | |||||
| package repo | package repo | ||||
| import ( | import ( | ||||
| "code.gitea.io/gitea/modules/modelarts" | |||||
| "encoding/json" | "encoding/json" | ||||
| "errors" | "errors" | ||||
| "github.com/unknwon/com" | |||||
| "io" | |||||
| "io/ioutil" | |||||
| "net/http" | |||||
| "os" | |||||
| "path" | |||||
| "strconv" | "strconv" | ||||
| "strings" | "strings" | ||||
| "time" | "time" | ||||
| @@ -13,14 +16,30 @@ import ( | |||||
| "code.gitea.io/gitea/modules/auth" | "code.gitea.io/gitea/modules/auth" | ||||
| "code.gitea.io/gitea/modules/base" | "code.gitea.io/gitea/modules/base" | ||||
| "code.gitea.io/gitea/modules/context" | "code.gitea.io/gitea/modules/context" | ||||
| "code.gitea.io/gitea/modules/git" | |||||
| "code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
| "code.gitea.io/gitea/modules/modelarts" | |||||
| "code.gitea.io/gitea/modules/obs" | |||||
| "code.gitea.io/gitea/modules/setting" | "code.gitea.io/gitea/modules/setting" | ||||
| "code.gitea.io/gitea/modules/storage" | |||||
| "github.com/unknwon/com" | |||||
| ) | ) | ||||
| const ( | const ( | ||||
| // tplModelArtsNotebookIndex base.TplName = "repo/modelarts/notebook/index" | |||||
| tplModelArtsNotebookIndex base.TplName = "repo/modelarts/notebook/index" | |||||
| tplModelArtsNotebookNew base.TplName = "repo/modelarts/notebook/new" | |||||
| tplModelArtsNotebookShow base.TplName = "repo/modelarts/notebook/show" | |||||
| tplModelArtsIndex base.TplName = "repo/modelarts/index" | tplModelArtsIndex base.TplName = "repo/modelarts/index" | ||||
| tplModelArtsNew base.TplName = "repo/modelarts/new" | tplModelArtsNew base.TplName = "repo/modelarts/new" | ||||
| tplModelArtsShow base.TplName = "repo/modelarts/show" | tplModelArtsShow base.TplName = "repo/modelarts/show" | ||||
| tplModelArtsTrainJobIndex base.TplName = "repo/modelarts/trainjob/index" | |||||
| tplModelArtsTrainJobNew base.TplName = "repo/modelarts/trainjob/new" | |||||
| tplModelArtsTrainJobShow base.TplName = "repo/modelarts/trainjob/show" | |||||
| tplModelArtsTrainJobShowModels base.TplName = "repo/modelarts/trainjob/models/index" | |||||
| ) | ) | ||||
| // MustEnableDataset check if repository enable internal cb | // MustEnableDataset check if repository enable internal cb | ||||
| @@ -30,6 +49,7 @@ func MustEnableModelArts(ctx *context.Context) { | |||||
| return | return | ||||
| } | } | ||||
| } | } | ||||
| func ModelArtsIndex(ctx *context.Context) { | func ModelArtsIndex(ctx *context.Context) { | ||||
| MustEnableModelArts(ctx) | MustEnableModelArts(ctx) | ||||
| repo := ctx.Repo.Repository | repo := ctx.Repo.Repository | ||||
| @@ -100,7 +120,10 @@ func ModelArtsCreate(ctx *context.Context, form auth.CreateModelArtsForm) { | |||||
| uuid := form.Attachment | uuid := form.Attachment | ||||
| description := form.Description | description := form.Description | ||||
| //repo := ctx.Repo.Repository | //repo := ctx.Repo.Repository | ||||
| if !jobNamePattern.MatchString(jobName) { | |||||
| ctx.RenderWithErr(ctx.Tr("repo.cloudbrain_jobname_err"), tplModelArtsNew, &form) | |||||
| return | |||||
| } | |||||
| err := modelarts.GenerateTask(ctx, jobName, uuid, description) | err := modelarts.GenerateTask(ctx, jobName, uuid, description) | ||||
| if err != nil { | if err != nil { | ||||
| ctx.RenderWithErr(err.Error(), tplModelArtsNew, &form) | ctx.RenderWithErr(err.Error(), tplModelArtsNew, &form) | ||||
| @@ -228,7 +251,7 @@ func ModelArtsDel(ctx *context.Context) { | |||||
| return | return | ||||
| } | } | ||||
| if task.Status != string(models.JobStopped) { | |||||
| if task.Status != string(models.ModelArtsCreateFailed) && task.Status != string(models.ModelArtsStartFailed) && task.Status != string(models.ModelArtsStopped) { | |||||
| log.Error("the job(%s) has not been stopped", task.JobName) | log.Error("the job(%s) has not been stopped", task.JobName) | ||||
| ctx.ServerError("the job has not been stopped", errors.New("the job has not been stopped")) | ctx.ServerError("the job has not been stopped", errors.New("the job has not been stopped")) | ||||
| return | return | ||||
| @@ -249,3 +272,874 @@ func ModelArtsDel(ctx *context.Context) { | |||||
| ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts") | ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts") | ||||
| } | } | ||||
| func NotebookIndex(ctx *context.Context) { | |||||
| MustEnableModelArts(ctx) | |||||
| repo := ctx.Repo.Repository | |||||
| page := ctx.QueryInt("page") | |||||
| if page <= 0 { | |||||
| page = 1 | |||||
| } | |||||
| ciTasks, count, err := models.Cloudbrains(&models.CloudbrainsOptions{ | |||||
| ListOptions: models.ListOptions{ | |||||
| Page: page, | |||||
| PageSize: setting.UI.IssuePagingNum, | |||||
| }, | |||||
| RepoID: repo.ID, | |||||
| Type: models.TypeCloudBrainTwo, | |||||
| JobType: string(models.JobTypeDebug), | |||||
| }) | |||||
| if err != nil { | |||||
| ctx.ServerError("Cloudbrain", err) | |||||
| return | |||||
| } | |||||
| for i, task := range ciTasks { | |||||
| if task.Status == string(models.JobRunning) { | |||||
| ciTasks[i].CanDebug = true | |||||
| } else { | |||||
| ciTasks[i].CanDebug = false | |||||
| } | |||||
| } | |||||
| pager := context.NewPagination(int(count), setting.UI.IssuePagingNum, page, 5) | |||||
| pager.SetDefaultParams(ctx) | |||||
| ctx.Data["Page"] = pager | |||||
| ctx.Data["PageIsCloudBrain"] = true | |||||
| ctx.Data["Tasks"] = ciTasks | |||||
| ctx.HTML(200, tplModelArtsNotebookIndex) | |||||
| } | |||||
| func NotebookNew(ctx *context.Context) { | |||||
| ctx.Data["PageIsCloudBrain"] = true | |||||
| t := time.Now() | |||||
| var jobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:] | |||||
| ctx.Data["job_name"] = jobName | |||||
| attachs, err := models.GetModelArtsUserAttachments(ctx.User.ID) | |||||
| if err != nil { | |||||
| ctx.ServerError("GetAllUserAttachments failed:", err) | |||||
| return | |||||
| } | |||||
| ctx.Data["attachments"] = attachs | |||||
| ctx.Data["dataset_path"] = modelarts.DataSetMountPath | |||||
| ctx.Data["env"] = modelarts.NotebookEnv | |||||
| ctx.Data["notebook_type"] = modelarts.NotebookType | |||||
| if modelarts.FlavorInfos == nil { | |||||
| json.Unmarshal([]byte(setting.FlavorInfos), &modelarts.FlavorInfos) | |||||
| } | |||||
| ctx.Data["flavors"] = modelarts.FlavorInfos.FlavorInfo | |||||
| ctx.HTML(200, tplModelArtsNotebookNew) | |||||
| } | |||||
| func NotebookCreate(ctx *context.Context, form auth.CreateModelArtsNotebookForm) { | |||||
| ctx.Data["PageIsNotebook"] = true | |||||
| jobName := form.JobName | |||||
| uuid := form.Attachment | |||||
| description := form.Description | |||||
| err := modelarts.GenerateTask(ctx, jobName, uuid, description) | |||||
| if err != nil { | |||||
| ctx.RenderWithErr(err.Error(), tplModelArtsNotebookNew, &form) | |||||
| return | |||||
| } | |||||
| ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/notebook") | |||||
| } | |||||
| func NotebookShow(ctx *context.Context) { | |||||
| ctx.Data["PageIsCloudBrain"] = true | |||||
| var jobID = ctx.Params(":jobid") | |||||
| task, err := models.GetCloudbrainByJobID(jobID) | |||||
| if err != nil { | |||||
| ctx.Data["error"] = err.Error() | |||||
| ctx.RenderWithErr(err.Error(), tplModelArtsNotebookShow, nil) | |||||
| return | |||||
| } | |||||
| result, err := modelarts.GetJob(jobID) | |||||
| if err != nil { | |||||
| ctx.Data["error"] = err.Error() | |||||
| ctx.RenderWithErr(err.Error(), tplModelArtsNotebookShow, nil) | |||||
| return | |||||
| } | |||||
| if result != nil { | |||||
| task.Status = result.Status | |||||
| err = models.UpdateJob(task) | |||||
| if err != nil { | |||||
| ctx.Data["error"] = err.Error() | |||||
| ctx.RenderWithErr(err.Error(), tplModelArtsNotebookShow, nil) | |||||
| return | |||||
| } | |||||
| createTime, _ := com.StrTo(result.CreationTimestamp).Int64() | |||||
| result.CreateTime = time.Unix(int64(createTime/1000), 0).Format("2006-01-02 15:04:05") | |||||
| endTime, _ := com.StrTo(result.LatestUpdateTimestamp).Int64() | |||||
| result.LatestUpdateTime = time.Unix(int64(endTime/1000), 0).Format("2006-01-02 15:04:05") | |||||
| result.QueuingInfo.BeginTime = time.Unix(int64(result.QueuingInfo.BeginTimestamp/1000), 0).Format("2006-01-02 15:04:05") | |||||
| result.QueuingInfo.EndTime = time.Unix(int64(result.QueuingInfo.EndTimestamp/1000), 0).Format("2006-01-02 15:04:05") | |||||
| } | |||||
| ctx.Data["task"] = task | |||||
| ctx.Data["jobID"] = jobID | |||||
| ctx.Data["result"] = result | |||||
| ctx.HTML(200, tplModelArtsNotebookShow) | |||||
| } | |||||
| func NotebookDebug(ctx *context.Context) { | |||||
| var jobID = ctx.Params(":jobid") | |||||
| _, err := models.GetCloudbrainByJobID(jobID) | |||||
| if err != nil { | |||||
| ctx.ServerError("GetCloudbrainByJobID failed", err) | |||||
| return | |||||
| } | |||||
| result, err := modelarts.GetJob(jobID) | |||||
| if err != nil { | |||||
| ctx.RenderWithErr(err.Error(), tplModelArtsNotebookIndex, nil) | |||||
| return | |||||
| } | |||||
| res, err := modelarts.GetJobToken(jobID) | |||||
| if err != nil { | |||||
| ctx.RenderWithErr(err.Error(), tplModelArtsNotebookIndex, nil) | |||||
| return | |||||
| } | |||||
| urls := strings.Split(result.Spec.Annotations.Url, "/") | |||||
| urlPrefix := result.Spec.Annotations.TargetDomain | |||||
| for i, url := range urls { | |||||
| if i > 2 { | |||||
| urlPrefix += "/" + url | |||||
| } | |||||
| } | |||||
| debugUrl := urlPrefix + "?token=" + res.Token | |||||
| ctx.Redirect(debugUrl) | |||||
| } | |||||
| func NotebookStop(ctx *context.Context) { | |||||
| var jobID = ctx.Params(":jobid") | |||||
| log.Info(jobID) | |||||
| task, err := models.GetCloudbrainByJobID(jobID) | |||||
| if err != nil { | |||||
| ctx.ServerError("GetCloudbrainByJobID failed", err) | |||||
| return | |||||
| } | |||||
| if task.Status != string(models.JobRunning) { | |||||
| log.Error("the job(%s) is not running", task.JobName) | |||||
| ctx.ServerError("the job is not running", errors.New("the job is not running")) | |||||
| return | |||||
| } | |||||
| param := models.NotebookAction{ | |||||
| Action: models.ActionStop, | |||||
| } | |||||
| res, err := modelarts.StopJob(jobID, param) | |||||
| if err != nil { | |||||
| log.Error("StopJob(%s) failed:%v", task.JobName, err.Error()) | |||||
| ctx.ServerError("StopJob failed", err) | |||||
| return | |||||
| } | |||||
| task.Status = res.CurrentStatus | |||||
| err = models.UpdateJob(task) | |||||
| if err != nil { | |||||
| ctx.ServerError("UpdateJob failed", err) | |||||
| return | |||||
| } | |||||
| ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/notebook") | |||||
| } | |||||
| func NotebookDel(ctx *context.Context) { | |||||
| var jobID = ctx.Params(":jobid") | |||||
| task, err := models.GetCloudbrainByJobID(jobID) | |||||
| if err != nil { | |||||
| ctx.ServerError("GetCloudbrainByJobID failed", err) | |||||
| return | |||||
| } | |||||
| if task.Status != string(models.JobStopped) { | |||||
| log.Error("the job(%s) has not been stopped", task.JobName) | |||||
| ctx.ServerError("the job has not been stopped", errors.New("the job has not been stopped")) | |||||
| return | |||||
| } | |||||
| _, err = modelarts.DelNotebook(jobID) | |||||
| if err != nil { | |||||
| log.Error("DelJob(%s) failed:%v", task.JobName, err.Error()) | |||||
| ctx.ServerError("DelJob failed", err) | |||||
| return | |||||
| } | |||||
| err = models.DeleteJob(task) | |||||
| if err != nil { | |||||
| ctx.ServerError("DeleteJob failed", err) | |||||
| return | |||||
| } | |||||
| ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/notebook") | |||||
| } | |||||
| func TrainJobIndex(ctx *context.Context) { | |||||
| MustEnableModelArts(ctx) | |||||
| //can, err := canUserCreateTrainJob(ctx.User.ID) | |||||
| //if err != nil { | |||||
| // ctx.ServerError("canUserCreateTrainJob", err) | |||||
| // return | |||||
| //} | |||||
| // | |||||
| //ctx.Data["CanCreate"] = can | |||||
| repo := ctx.Repo.Repository | |||||
| page := ctx.QueryInt("page") | |||||
| if page <= 0 { | |||||
| page = 1 | |||||
| } | |||||
| tasks, count, err := models.Cloudbrains(&models.CloudbrainsOptions{ | |||||
| ListOptions: models.ListOptions{ | |||||
| Page: page, | |||||
| PageSize: setting.UI.IssuePagingNum, | |||||
| }, | |||||
| RepoID: repo.ID, | |||||
| Type: models.TypeCloudBrainTwo, | |||||
| JobType: string(models.JobTypeTrain), | |||||
| }) | |||||
| if err != nil { | |||||
| ctx.ServerError("Cloudbrain", err) | |||||
| return | |||||
| } | |||||
| pager := context.NewPagination(int(count), setting.UI.IssuePagingNum, page, 5) | |||||
| pager.SetDefaultParams(ctx) | |||||
| ctx.Data["Page"] = pager | |||||
| ctx.Data["PageIsCloudBrain"] = true | |||||
| ctx.Data["Tasks"] = tasks | |||||
| ctx.HTML(200, tplModelArtsTrainJobIndex) | |||||
| } | |||||
| func TrainJobNew(ctx *context.Context) { | |||||
| err := trainJobNewDataPrepare(ctx) | |||||
| if err != nil { | |||||
| ctx.ServerError("get new train-job info failed", err) | |||||
| return | |||||
| } | |||||
| ctx.HTML(200, tplModelArtsTrainJobNew) | |||||
| } | |||||
| func trainJobNewDataPrepare(ctx *context.Context) error { | |||||
| ctx.Data["PageIsCloudBrain"] = true | |||||
| //can, err := canUserCreateTrainJob(ctx.User.ID) | |||||
| //if err != nil { | |||||
| // ctx.ServerError("canUserCreateTrainJob", err) | |||||
| // return | |||||
| //} | |||||
| // | |||||
| //if !can { | |||||
| // log.Error("the user can not create train-job") | |||||
| // ctx.ServerError("the user can not create train-job", fmt.Errorf("the user can not create train-job")) | |||||
| // return | |||||
| //} | |||||
| t := time.Now() | |||||
| var jobName = cutString(ctx.User.Name, 5) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:] | |||||
| ctx.Data["job_name"] = jobName | |||||
| attachs, err := models.GetModelArtsUserAttachments(ctx.User.ID) | |||||
| if err != nil { | |||||
| ctx.ServerError("GetAllUserAttachments failed:", err) | |||||
| return err | |||||
| } | |||||
| ctx.Data["attachments"] = attachs | |||||
| var resourcePools modelarts.ResourcePool | |||||
| if err = json.Unmarshal([]byte(setting.ResourcePools), &resourcePools); err != nil { | |||||
| ctx.ServerError("json.Unmarshal failed:", err) | |||||
| return err | |||||
| } | |||||
| ctx.Data["resource_pools"] = resourcePools.Info | |||||
| var engines modelarts.Engine | |||||
| if err = json.Unmarshal([]byte(setting.Engines), &engines); err != nil { | |||||
| ctx.ServerError("json.Unmarshal failed:", err) | |||||
| return err | |||||
| } | |||||
| ctx.Data["engines"] = engines.Info | |||||
| var versionInfos modelarts.VersionInfo | |||||
| if err = json.Unmarshal([]byte(setting.EngineVersions), &versionInfos); err != nil { | |||||
| ctx.ServerError("json.Unmarshal failed:", err) | |||||
| return err | |||||
| } | |||||
| ctx.Data["engine_versions"] = versionInfos.Version | |||||
| var flavorInfos modelarts.Flavor | |||||
| if err = json.Unmarshal([]byte(setting.TrainJobFLAVORINFOS), &flavorInfos); err != nil { | |||||
| ctx.ServerError("json.Unmarshal failed:", err) | |||||
| return err | |||||
| } | |||||
| ctx.Data["flavor_infos"] = flavorInfos.Info | |||||
| outputObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.OutputPath | |||||
| ctx.Data["train_url"] = outputObsPath | |||||
| configList, err := getConfigList(modelarts.PerPage, 1, modelarts.SortByCreateTime, "desc", "", modelarts.ConfigTypeCustom) | |||||
| if err != nil { | |||||
| ctx.ServerError("getConfigList failed:", err) | |||||
| return err | |||||
| } | |||||
| ctx.Data["config_list"] = configList.ParaConfigs | |||||
| return nil | |||||
| } | |||||
| func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm) { | |||||
| ctx.Data["PageIsTrainJob"] = true | |||||
| jobName := form.JobName | |||||
| uuid := form.Attachment | |||||
| description := form.Description | |||||
| workServerNumber := form.WorkServerNumber | |||||
| engineID := form.EngineID | |||||
| bootFile := form.BootFile | |||||
| flavorCode := form.Flavor | |||||
| params := form.Params | |||||
| poolID := form.PoolID | |||||
| isSaveParam := form.IsSaveParam | |||||
| repo := ctx.Repo.Repository | |||||
| codeLocalPath := setting.JobPath + jobName + modelarts.CodePath | |||||
| codeObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.CodePath | |||||
| outputObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.OutputPath | |||||
| logObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.LogPath | |||||
| dataPath := "/" + setting.Bucket + "/" + setting.BasePath + path.Join(uuid[0:1], uuid[1:2]) + "/" + uuid + uuid + "/" | |||||
| //can, err := canUserCreateTrainJob(ctx.User.ID) | |||||
| //if err != nil { | |||||
| // ctx.ServerError("canUserCreateTrainJob", err) | |||||
| // return | |||||
| //} | |||||
| // | |||||
| //if !can { | |||||
| // log.Error("the user can not create train-job") | |||||
| // ctx.RenderWithErr("the user can not create train-job", tplModelArtsTrainJobNew, &form) | |||||
| // return | |||||
| //} | |||||
| //param check | |||||
| if err := paramCheckCreateTrainJob(form); err != nil { | |||||
| log.Error("paramCheckCreateTrainJob failed:(%v)", err) | |||||
| trainJobNewDataPrepare(ctx) | |||||
| ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobNew, &form) | |||||
| return | |||||
| } | |||||
| attach, err := models.GetAttachmentByUUID(uuid) | |||||
| if err != nil { | |||||
| log.Error("GetAttachmentByUUID(%s) failed:%v", uuid, err.Error()) | |||||
| return | |||||
| } | |||||
| //todo: del the codeLocalPath | |||||
| _, err = ioutil.ReadDir(codeLocalPath) | |||||
| if err == nil { | |||||
| os.RemoveAll(codeLocalPath) | |||||
| } | |||||
| if err := git.Clone(repo.RepoPath(), codeLocalPath, git.CloneRepoOptions{}); err != nil { | |||||
| log.Error("创建任务失败,任务名称已存在!: %s (%v)", repo.FullName(), err) | |||||
| trainJobNewDataPrepare(ctx) | |||||
| ctx.Data["bootFile"] = form.BootFile | |||||
| ctx.Data["uuid"] = form.Attachment | |||||
| ctx.Data["datasetName"] = attach.Name | |||||
| ctx.Data["params"] = form.Params | |||||
| trainJobNewDataPrepare(ctx) | |||||
| // ctx.RenderWithErr("Failed to clone repository", tplModelArtsTrainJobNew, &form) | |||||
| ctx.RenderWithErr("创建任务失败,任务名称已存在!", tplModelArtsTrainJobNew, &form) | |||||
| // ctx.RenderWithErr(err, tplModelArtsTrainJobNew, &form) | |||||
| return | |||||
| } | |||||
| //todo: upload code (send to file_server todo this work?) | |||||
| if err := obsMkdir(setting.CodePathPrefix + jobName + modelarts.OutputPath); err != nil { | |||||
| log.Error("Failed to obsMkdir_output: %s (%v)", repo.FullName(), err) | |||||
| trainJobNewDataPrepare(ctx) | |||||
| ctx.RenderWithErr("Failed to obsMkdir_output", tplModelArtsTrainJobNew, &form) | |||||
| return | |||||
| } | |||||
| if err := obsMkdir(setting.CodePathPrefix + jobName + modelarts.LogPath); err != nil { | |||||
| log.Error("Failed to obsMkdir_log: %s (%v)", repo.FullName(), err) | |||||
| trainJobNewDataPrepare(ctx) | |||||
| ctx.RenderWithErr("Failed to obsMkdir_log", tplModelArtsTrainJobNew, &form) | |||||
| return | |||||
| } | |||||
| if err := uploadCodeToObs(codeLocalPath, jobName, ""); err != nil { | |||||
| log.Error("Failed to uploadCodeToObs: %s (%v)", repo.FullName(), err) | |||||
| trainJobNewDataPrepare(ctx) | |||||
| ctx.RenderWithErr("Failed to uploadCodeToObs", tplModelArtsTrainJobNew, &form) | |||||
| return | |||||
| } | |||||
| //todo: del local code? | |||||
| var parameters models.Parameters | |||||
| param := make([]models.Parameter, 0) | |||||
| param = append(param, models.Parameter{ | |||||
| Label: modelarts.TrainUrl, | |||||
| Value: outputObsPath, | |||||
| }, models.Parameter{ | |||||
| Label: modelarts.DataUrl, | |||||
| Value: dataPath, | |||||
| }) | |||||
| if len(params) != 0 { | |||||
| err := json.Unmarshal([]byte(params), ¶meters) | |||||
| if err != nil { | |||||
| log.Error("Failed to Unmarshal params: %s (%v)", params, err) | |||||
| trainJobNewDataPrepare(ctx) | |||||
| ctx.RenderWithErr("运行参数错误", tplModelArtsTrainJobNew, &form) | |||||
| return | |||||
| } | |||||
| for _, parameter := range parameters.Parameter { | |||||
| if parameter.Label != modelarts.TrainUrl && parameter.Label != modelarts.DataUrl { | |||||
| param = append(param, models.Parameter{ | |||||
| Label: parameter.Label, | |||||
| Value: parameter.Value, | |||||
| }) | |||||
| } | |||||
| } | |||||
| } | |||||
| //save param config | |||||
| if isSaveParam == "on" { | |||||
| if form.ParameterTemplateName == "" { | |||||
| log.Error("ParameterTemplateName is empty") | |||||
| trainJobNewDataPrepare(ctx) | |||||
| ctx.RenderWithErr("保存作业参数时,作业参数名称不能为空", tplModelArtsTrainJobNew, &form) | |||||
| return | |||||
| } | |||||
| _, err := modelarts.CreateTrainJobConfig(models.CreateConfigParams{ | |||||
| ConfigName: form.ParameterTemplateName, | |||||
| Description: form.PrameterDescription, | |||||
| DataUrl: dataPath, | |||||
| AppUrl: codeObsPath, | |||||
| BootFileUrl: codeObsPath + bootFile, | |||||
| TrainUrl: outputObsPath, | |||||
| Flavor: models.Flavor{ | |||||
| Code: flavorCode, | |||||
| }, | |||||
| WorkServerNum: workServerNumber, | |||||
| EngineID: int64(engineID), | |||||
| LogUrl: logObsPath, | |||||
| PoolID: poolID, | |||||
| Parameter: param, | |||||
| }) | |||||
| if err != nil { | |||||
| log.Error("Failed to CreateTrainJobConfig: %v", err) | |||||
| trainJobNewDataPrepare(ctx) | |||||
| ctx.RenderWithErr("保存作业参数失败:"+err.Error(), tplModelArtsTrainJobNew, &form) | |||||
| return | |||||
| } | |||||
| } | |||||
| req := &modelarts.GenerateTrainJobReq{ | |||||
| JobName: jobName, | |||||
| DataUrl: dataPath, | |||||
| Description: description, | |||||
| CodeObsPath: codeObsPath, | |||||
| BootFile: codeObsPath + bootFile, | |||||
| TrainUrl: outputObsPath, | |||||
| FlavorCode: flavorCode, | |||||
| WorkServerNumber: workServerNumber, | |||||
| EngineID: int64(engineID), | |||||
| LogUrl: logObsPath, | |||||
| PoolID: poolID, | |||||
| Uuid: uuid, | |||||
| Parameters: param, | |||||
| } | |||||
| err = modelarts.GenerateTrainJob(ctx, req) | |||||
| if err != nil { | |||||
| log.Error("GenerateTrainJob failed:%v", err.Error()) | |||||
| trainJobNewDataPrepare(ctx) | |||||
| ctx.Data["bootFile"] = form.BootFile | |||||
| ctx.Data["uuid"] = form.Attachment | |||||
| ctx.Data["datasetName"] = attach.Name | |||||
| ctx.Data["params"] = form.Params | |||||
| ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobNew, &form) | |||||
| return | |||||
| } | |||||
| ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/train-job") | |||||
| } | |||||
| // readDir reads the directory named by dirname and returns | |||||
| // a list of directory entries sorted by filename. | |||||
| func readDir(dirname string) ([]os.FileInfo, error) { | |||||
| f, err := os.Open(dirname) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| list, err := f.Readdir(100) | |||||
| f.Close() | |||||
| if err != nil { | |||||
| //todo: can not upload empty folder | |||||
| if err == io.EOF { | |||||
| return nil, nil | |||||
| } | |||||
| return nil, err | |||||
| } | |||||
| //sort.Slice(list, func(i, j int) bool { return list[i].Name() < list[j].Name() }) | |||||
| return list, nil | |||||
| } | |||||
| func uploadCodeToObs(codePath, jobName, parentDir string) error { | |||||
| files, err := readDir(codePath) | |||||
| if err != nil { | |||||
| log.Error("readDir(%s) failed: %s", codePath, err.Error()) | |||||
| return err | |||||
| } | |||||
| for _, file := range files { | |||||
| if file.IsDir() { | |||||
| input := &obs.PutObjectInput{} | |||||
| input.Bucket = setting.Bucket | |||||
| input.Key = parentDir + file.Name() + "/" | |||||
| _, err = storage.ObsCli.PutObject(input) | |||||
| if err != nil { | |||||
| log.Error("PutObject(%s) failed: %s", input.Key, err.Error()) | |||||
| return err | |||||
| } | |||||
| if err = uploadCodeToObs(codePath+file.Name()+"/", jobName, parentDir+file.Name()+"/"); err != nil { | |||||
| log.Error("uploadCodeToObs(%s) failed: %s", file.Name(), err.Error()) | |||||
| return err | |||||
| } | |||||
| } else { | |||||
| input := &obs.PutFileInput{} | |||||
| input.Bucket = setting.Bucket | |||||
| input.Key = setting.CodePathPrefix + jobName + "/code/" + parentDir + file.Name() | |||||
| input.SourceFile = codePath + file.Name() | |||||
| _, err = storage.ObsCli.PutFile(input) | |||||
| if err != nil { | |||||
| log.Error("PutFile(%s) failed: %s", input.SourceFile, err.Error()) | |||||
| return err | |||||
| } | |||||
| } | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func obsMkdir(dir string) error { | |||||
| input := &obs.PutObjectInput{} | |||||
| input.Bucket = setting.Bucket | |||||
| input.Key = dir | |||||
| _, err := storage.ObsCli.PutObject(input) | |||||
| if err != nil { | |||||
| log.Error("PutObject(%s) failed: %s", input.Key, err.Error()) | |||||
| return err | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func paramCheckCreateTrainJob(form auth.CreateModelArtsTrainJobForm) error { | |||||
| if !strings.HasSuffix(form.BootFile, ".py") { | |||||
| log.Error("the boot file(%s) must be a python file", form.BootFile) | |||||
| return errors.New("启动文件必须是python文件") | |||||
| } | |||||
| if form.WorkServerNumber > 25 || form.WorkServerNumber < 1 { | |||||
| log.Error("the WorkServerNumber(%d) must be in (1,25)", form.WorkServerNumber) | |||||
| return errors.New("计算节点数必须在1-25之间") | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func TrainJobShow(ctx *context.Context) { | |||||
| ctx.Data["PageIsCloudBrain"] = true | |||||
| var jobID = ctx.Params(":jobid") | |||||
| task, err := models.GetCloudbrainByJobID(jobID) | |||||
| if err != nil { | |||||
| log.Error("GetCloudbrainByJobID(%s) failed:%v", jobID, err.Error()) | |||||
| ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobShow, nil) | |||||
| return | |||||
| } | |||||
| // attach, err := models.GetAttachmentByUUID(task.Uuid) | |||||
| // if err != nil { | |||||
| // log.Error("GetAttachmentByUUID(%s) failed:%v", jobID, err.Error()) | |||||
| // ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobShow, nil) | |||||
| // return | |||||
| // } | |||||
| result, err := modelarts.GetTrainJob(jobID, strconv.FormatInt(task.VersionID, 10)) | |||||
| if err != nil { | |||||
| log.Error("GetJob(%s) failed:%v", jobID, err.Error()) | |||||
| ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobShow, nil) | |||||
| return | |||||
| } | |||||
| if result != nil { | |||||
| result.CreateTime = time.Unix(int64(result.LongCreateTime/1000), 0).Format("2006-01-02 15:04:05") | |||||
| if result.Duration != 0 { | |||||
| result.TrainJobDuration = addZero(result.Duration/3600000) + ":" + addZero(result.Duration%3600000/60000) + ":" + addZero(result.Duration%60000/1000) | |||||
| } else { | |||||
| result.TrainJobDuration = "00:00:00" | |||||
| } | |||||
| result.Status = modelarts.TransTrainJobStatus(result.IntStatus) | |||||
| err = models.SetTrainJobStatusByJobID(jobID, result.Status, result.Duration, string(result.TrainJobDuration)) | |||||
| if err != nil { | |||||
| ctx.ServerError("UpdateJob failed", err) | |||||
| return | |||||
| } | |||||
| result.DatasetName = task.DatasetName | |||||
| } | |||||
| resultLogFile, resultLog, err := trainJobGetLog(jobID) | |||||
| if err != nil { | |||||
| log.Error("trainJobGetLog(%s) failed:%v", jobID, err.Error()) | |||||
| ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobShow, nil) | |||||
| return | |||||
| } | |||||
| ctx.Data["log_file_name"] = resultLogFile.LogFileList[0] | |||||
| ctx.Data["log"] = resultLog | |||||
| ctx.Data["task"] = task | |||||
| ctx.Data["jobID"] = jobID | |||||
| ctx.Data["result"] = result | |||||
| ctx.HTML(http.StatusOK, tplModelArtsTrainJobShow) | |||||
| } | |||||
| func addZero(t int64) (m string) { | |||||
| if t < 10 { | |||||
| m = "0" + strconv.FormatInt(t, 10) | |||||
| return m | |||||
| } else { | |||||
| return strconv.FormatInt(t, 10) | |||||
| } | |||||
| } | |||||
| func TrainJobGetLog(ctx *context.Context) { | |||||
| ctx.Data["PageIsTrainJob"] = true | |||||
| var jobID = ctx.Params(":jobid") | |||||
| var logFileName = ctx.Query("file_name") | |||||
| var baseLine = ctx.Query("base_line") | |||||
| var order = ctx.Query("order") | |||||
| if order != modelarts.OrderDesc && order != modelarts.OrderAsc { | |||||
| log.Error("order(%s) check failed", order) | |||||
| ctx.HTML(http.StatusBadRequest, tplModelArtsTrainJobShow) | |||||
| return | |||||
| } | |||||
| task, err := models.GetCloudbrainByJobID(jobID) | |||||
| if err != nil { | |||||
| log.Error("GetCloudbrainByJobID(%s) failed:%v", jobID, err.Error()) | |||||
| ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobShow, nil) | |||||
| return | |||||
| } | |||||
| result, err := modelarts.GetTrainJobLog(jobID, strconv.FormatInt(task.VersionID, 10), baseLine, logFileName, order, modelarts.Lines) | |||||
| if err != nil { | |||||
| log.Error("GetTrainJobLog(%s) failed:%v", jobID, err.Error()) | |||||
| ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobShow, nil) | |||||
| return | |||||
| } | |||||
| ctx.Data["log"] = result | |||||
| //ctx.HTML(http.StatusOK, tplModelArtsTrainJobShow) | |||||
| } | |||||
| func trainJobGetLog(jobID string) (*models.GetTrainJobLogFileNamesResult, *models.GetTrainJobLogResult, error) { | |||||
| task, err := models.GetCloudbrainByJobID(jobID) | |||||
| if err != nil { | |||||
| log.Error("GetCloudbrainByJobID(%s) failed:%v", jobID, err.Error()) | |||||
| return nil, nil, err | |||||
| } | |||||
| resultLogFile, err := modelarts.GetTrainJobLogFileNames(jobID, strconv.FormatInt(task.VersionID, 10)) | |||||
| if err != nil { | |||||
| log.Error("GetTrainJobLogFileNames(%s) failed:%v", jobID, err.Error()) | |||||
| return nil, nil, err | |||||
| } | |||||
| result, err := modelarts.GetTrainJobLog(jobID, strconv.FormatInt(task.VersionID, 10), "", resultLogFile.LogFileList[0], modelarts.OrderDesc, modelarts.Lines) | |||||
| if err != nil { | |||||
| log.Error("GetTrainJobLog(%s) failed:%v", jobID, err.Error()) | |||||
| return nil, nil, err | |||||
| } | |||||
| return resultLogFile, result, err | |||||
| } | |||||
| func TrainJobDel(ctx *context.Context) { | |||||
| var jobID = ctx.Params(":jobid") | |||||
| task, err := models.GetCloudbrainByJobID(jobID) | |||||
| if err != nil { | |||||
| log.Error("GetCloudbrainByJobID(%s) failed:%v", task.JobName, err.Error()) | |||||
| ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobIndex, nil) | |||||
| return | |||||
| } | |||||
| _, err = modelarts.DelTrainJob(jobID) | |||||
| if err != nil { | |||||
| log.Error("DelTrainJob(%s) failed:%v", task.JobName, err.Error()) | |||||
| ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobIndex, nil) | |||||
| return | |||||
| } | |||||
| err = models.DeleteJob(task) | |||||
| if err != nil { | |||||
| ctx.ServerError("DeleteJob failed", err) | |||||
| return | |||||
| } | |||||
| ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/train-job") | |||||
| } | |||||
| func TrainJobStop(ctx *context.Context) { | |||||
| var jobID = ctx.Params(":jobid") | |||||
| task, err := models.GetCloudbrainByJobID(jobID) | |||||
| if err != nil { | |||||
| log.Error("GetCloudbrainByJobID(%s) failed:%v", task.JobName, err.Error()) | |||||
| ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobIndex, nil) | |||||
| return | |||||
| } | |||||
| _, err = modelarts.StopTrainJob(jobID, strconv.FormatInt(task.VersionID, 10)) | |||||
| if err != nil { | |||||
| log.Error("StopTrainJob(%s) failed:%v", task.JobName, err.Error()) | |||||
| ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobIndex, nil) | |||||
| return | |||||
| } | |||||
| ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/train-job") | |||||
| } | |||||
| func canUserCreateTrainJob(uid int64) (bool, error) { | |||||
| org, err := models.GetOrgByName(setting.AllowedOrg) | |||||
| if err != nil { | |||||
| log.Error("get allowed org failed: ", setting.AllowedOrg) | |||||
| return false, err | |||||
| } | |||||
| return org.IsOrgMember(uid) | |||||
| } | |||||
| func TrainJobGetConfigList(ctx *context.Context) { | |||||
| ctx.Data["PageIsTrainJob"] = true | |||||
| var jobID = ctx.Params(":jobid") | |||||
| var logFileName = ctx.Query("file_name") | |||||
| var baseLine = ctx.Query("base_line") | |||||
| var order = ctx.Query("order") | |||||
| if order != modelarts.OrderDesc && order != modelarts.OrderAsc { | |||||
| log.Error("order(%s) check failed", order) | |||||
| ctx.HTML(http.StatusBadRequest, tplModelArtsTrainJobShow) | |||||
| return | |||||
| } | |||||
| task, err := models.GetCloudbrainByJobID(jobID) | |||||
| if err != nil { | |||||
| log.Error("GetCloudbrainByJobID(%s) failed:%v", jobID, err.Error()) | |||||
| ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobShow, nil) | |||||
| return | |||||
| } | |||||
| result, err := modelarts.GetTrainJobLog(jobID, strconv.FormatInt(task.VersionID, 10), baseLine, logFileName, order, modelarts.Lines) | |||||
| if err != nil { | |||||
| log.Error("GetTrainJobLog(%s) failed:%v", jobID, err.Error()) | |||||
| ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobShow, nil) | |||||
| return | |||||
| } | |||||
| ctx.Data["log"] = result | |||||
| //ctx.HTML(http.StatusOK, tplModelArtsTrainJobShow) | |||||
| } | |||||
| func getConfigList(perPage, page int, sortBy, order, searchContent, configType string) (*models.GetConfigListResult, error) { | |||||
| var result models.GetConfigListResult | |||||
| list, err := modelarts.GetConfigList(perPage, page, sortBy, order, searchContent, configType) | |||||
| if err != nil { | |||||
| log.Error("GetConfigList failed:", err) | |||||
| return &result, err | |||||
| } | |||||
| for _, config := range list.ParaConfigs { | |||||
| paraConfig, err := modelarts.GetParaConfig(config.ConfigName, configType) | |||||
| if err != nil { | |||||
| log.Error("GetParaConfig failed:", err) | |||||
| return &result, err | |||||
| } | |||||
| config.Result = paraConfig | |||||
| } | |||||
| return list, nil | |||||
| } | |||||
| func TrainJobShowModels(ctx *context.Context) { | |||||
| ctx.Data["PageIsCloudBrain"] = true | |||||
| jobID := ctx.Params(":jobid") | |||||
| parentDir := ctx.Query("parentDir") | |||||
| dirArray := strings.Split(parentDir, "/") | |||||
| task, err := models.GetCloudbrainByJobID(jobID) | |||||
| if err != nil { | |||||
| log.Error("no such job!", ctx.Data["msgID"]) | |||||
| ctx.ServerError("no such job:", err) | |||||
| return | |||||
| } | |||||
| models, err := storage.GetObsListObject(task.JobName, parentDir) | |||||
| if err != nil { | |||||
| log.Info("get TrainJobListModel failed:", err) | |||||
| ctx.ServerError("GetObsListObject:", err) | |||||
| return | |||||
| } | |||||
| ctx.Data["Path"] = dirArray | |||||
| ctx.Data["Dirs"] = models | |||||
| ctx.Data["task"] = task | |||||
| ctx.Data["JobID"] = jobID | |||||
| ctx.HTML(200, tplModelArtsTrainJobShowModels) | |||||
| } | |||||
| func TrainJobDownloadModel(ctx *context.Context) { | |||||
| parentDir := ctx.Query("parentDir") | |||||
| fileName := ctx.Query("fileName") | |||||
| jobName := ctx.Query("jobName") | |||||
| url, err := storage.GetObsCreateSignedUrl(jobName, parentDir, fileName) | |||||
| if err != nil { | |||||
| log.Error("GetObsCreateSignedUrl failed: %v", err.Error(), ctx.Data["msgID"]) | |||||
| ctx.ServerError("GetObsCreateSignedUrl", err) | |||||
| return | |||||
| } | |||||
| http.Redirect(ctx.Resp, ctx.Req.Request, url, http.StatusMovedPermanently) | |||||
| } | |||||
| @@ -531,6 +531,8 @@ func Download(ctx *context.Context) { | |||||
| } | } | ||||
| } | } | ||||
| ctx.Repo.Repository.IncreaseCloneCnt() | |||||
| ctx.ServeFile(archivePath, ctx.Repo.Repository.Name+"-"+refName+ext) | ctx.ServeFile(archivePath, ctx.Repo.Repository.Name+"-"+refName+ext) | ||||
| } | } | ||||
| @@ -3,6 +3,10 @@ package repo | |||||
| import ( | import ( | ||||
| "time" | "time" | ||||
| "code.gitea.io/gitea/modules/setting" | |||||
| "code.gitea.io/gitea/modules/normalization" | |||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| "code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
| "code.gitea.io/gitea/modules/repository" | "code.gitea.io/gitea/modules/repository" | ||||
| @@ -12,11 +16,14 @@ import ( | |||||
| func RepoStatisticAuto() { | func RepoStatisticAuto() { | ||||
| log.Info("", time.Now()) | log.Info("", time.Now()) | ||||
| yesterday := time.Now().AddDate(0, 0, -1).Format("2006-01-02") | yesterday := time.Now().AddDate(0, 0, -1).Format("2006-01-02") | ||||
| setting.UpdateRadarMap() | |||||
| RepoStatisticDaily(yesterday) | RepoStatisticDaily(yesterday) | ||||
| } | } | ||||
| func RepoStatisticDaily(date string) { | func RepoStatisticDaily(date string) { | ||||
| log.Info("%s", date) | log.Info("%s", date) | ||||
| log.Info("begin Repo Statistic") | |||||
| t, _ := time.Parse("2006-01-02", date) | |||||
| if err := models.DeleteRepoStatDaily(date); err != nil { | if err := models.DeleteRepoStatDaily(date); err != nil { | ||||
| log.Error("DeleteRepoStatDaily failed: %v", err.Error()) | log.Error("DeleteRepoStatDaily failed: %v", err.Error()) | ||||
| return | return | ||||
| @@ -28,9 +35,14 @@ func RepoStatisticDaily(date string) { | |||||
| return | return | ||||
| } | } | ||||
| for _, repo := range repos { | |||||
| var reposRadar = make([]*models.RepoStatistic, 0) | |||||
| var minRepoRadar models.RepoStatistic | |||||
| var maxRepoRadar models.RepoStatistic | |||||
| for i, repo := range repos { | |||||
| log.Info("start statistic: %s", repo.Name) | log.Info("start statistic: %s", repo.Name) | ||||
| var numDevMonths,numWikiViews,numContributor,numKeyContributor int64 | |||||
| var numDevMonths, numWikiViews, numContributor, numKeyContributor, numCommitsGrowth, numCommitLinesGrowth, numContributorsGrowth int64 | |||||
| repoGitStat, err := models.GetRepoKPIStats(repo) | repoGitStat, err := models.GetRepoKPIStats(repo) | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("GetRepoKPIStats failed: %s", repo.Name) | log.Error("GetRepoKPIStats failed: %s", repo.Name) | ||||
| @@ -39,6 +51,9 @@ func RepoStatisticDaily(date string) { | |||||
| numKeyContributor = repoGitStat.KeyContributors | numKeyContributor = repoGitStat.KeyContributors | ||||
| numWikiViews = repoGitStat.WikiPages | numWikiViews = repoGitStat.WikiPages | ||||
| numContributor = repoGitStat.Contributors | numContributor = repoGitStat.Contributors | ||||
| numCommitsGrowth = repoGitStat.CommitsAdded | |||||
| numCommitLinesGrowth = repoGitStat.CommitLinesModified | |||||
| numContributorsGrowth = repoGitStat.ContributorsAdded | |||||
| } | } | ||||
| var issueFixedRate float32 | var issueFixedRate float32 | ||||
| @@ -46,7 +61,7 @@ func RepoStatisticDaily(date string) { | |||||
| issueFixedRate = float32(repo.NumClosedIssues) / float32(repo.NumIssues) | issueFixedRate = float32(repo.NumClosedIssues) / float32(repo.NumIssues) | ||||
| } | } | ||||
| var numVersions int64 | |||||
| var numVersions int64 | |||||
| numVersions, err = models.GetReleaseCountByRepoID(repo.ID, models.FindReleasesOptions{}) | numVersions, err = models.GetReleaseCountByRepoID(repo.ID, models.FindReleasesOptions{}) | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("GetReleaseCountByRepoID failed(%s): %v", repo.Name, err) | log.Error("GetReleaseCountByRepoID failed(%s): %v", repo.Name, err) | ||||
| @@ -72,26 +87,60 @@ func RepoStatisticDaily(date string) { | |||||
| } | } | ||||
| repoStat := models.RepoStatistic{ | repoStat := models.RepoStatistic{ | ||||
| RepoID: repo.ID, | |||||
| Date: date, | |||||
| NumWatches: int64(repo.NumWatches), | |||||
| NumStars: int64(repo.NumStars), | |||||
| NumDownloads: repo.CloneCnt, | |||||
| NumComments: numComments, | |||||
| NumVisits: int64(numVisits), | |||||
| NumClosedIssues: int64(repo.NumClosedIssues), | |||||
| NumVersions: numVersions, | |||||
| NumDevMonths: numDevMonths, | |||||
| RepoSize: repo.Size, | |||||
| DatasetSize: datasetSize, | |||||
| NumModels: 0, | |||||
| NumWikiViews: numWikiViews, | |||||
| NumCommits: repo.NumCommit, | |||||
| NumIssues: int64(repo.NumIssues), | |||||
| NumPulls: int64(repo.NumPulls), | |||||
| IssueFixedRate: issueFixedRate, | |||||
| NumContributor: numContributor, | |||||
| NumKeyContributor: numKeyContributor, | |||||
| RepoID: repo.ID, | |||||
| Date: date, | |||||
| Name: repo.Name, | |||||
| IsPrivate: repo.IsPrivate, | |||||
| NumWatches: int64(repo.NumWatches), | |||||
| NumStars: int64(repo.NumStars), | |||||
| NumDownloads: repo.CloneCnt, | |||||
| NumComments: numComments, | |||||
| NumVisits: int64(numVisits), | |||||
| NumClosedIssues: int64(repo.NumClosedIssues), | |||||
| NumVersions: numVersions, | |||||
| NumDevMonths: numDevMonths, | |||||
| RepoSize: repo.Size, | |||||
| DatasetSize: datasetSize, | |||||
| NumModels: 0, | |||||
| NumWikiViews: numWikiViews, | |||||
| NumCommits: repo.NumCommit, | |||||
| NumIssues: int64(repo.NumIssues), | |||||
| NumPulls: int64(repo.NumPulls), | |||||
| IssueFixedRate: issueFixedRate, | |||||
| NumContributor: numContributor, | |||||
| NumKeyContributor: numKeyContributor, | |||||
| NumCommitsGrowth: numCommitsGrowth, | |||||
| NumCommitLinesGrowth: numCommitLinesGrowth, | |||||
| NumContributorsGrowth: numContributorsGrowth, | |||||
| } | |||||
| dayBeforeDate := t.AddDate(0, 0, -1).Format("2006-01-02") | |||||
| repoStatisticsBefore, err := models.GetRepoStatisticByDate(dayBeforeDate) | |||||
| if err != nil { | |||||
| log.Error("get data of day before the date failed ", err) | |||||
| } else { | |||||
| if len(repoStatisticsBefore) > 0 { | |||||
| repoStatisticBefore := repoStatisticsBefore[0] | |||||
| repoStat.NumWatchesAdded = repoStat.NumWatches - repoStatisticBefore.NumWatches | |||||
| repoStat.NumStarsAdded = repoStat.NumStars - repoStatisticBefore.NumStars | |||||
| repoStat.NumForksAdded = repoStat.NumForks - repoStatisticBefore.NumForks | |||||
| repoStat.NumDownloadsAdded = repoStat.NumDownloads - repoStatisticBefore.NumDownloads | |||||
| repoStat.NumCommentsAdded = repoStat.NumComments - repoStatisticBefore.NumComments | |||||
| repoStat.NumClosedIssuesAdded = repoStat.NumClosedIssues - repoStatisticBefore.NumClosedIssues | |||||
| repoStat.NumCommitsAdded = repoStat.NumCommits - repoStatisticBefore.NumCommits | |||||
| repoStat.NumIssuesAdded = repoStat.NumIssues - repoStatisticBefore.NumIssues | |||||
| repoStat.NumPullsAdded = repoStat.NumPulls - repoStatisticBefore.NumPulls | |||||
| repoStat.NumContributorAdded = repoStat.NumContributor - repoStatisticBefore.NumContributor | |||||
| } | |||||
| } | |||||
| day4MonthsAgo := t.AddDate(0, -4, 0) | |||||
| repoStatisticFourMonthsAgo, err := models.GetOneRepoStatisticBeforeTime(day4MonthsAgo) | |||||
| if err != nil { | |||||
| log.Error("Get data of 4 moth ago failed.", err) | |||||
| } else { | |||||
| repoStat.NumCommentsGrowth = repoStat.NumComments - repoStatisticFourMonthsAgo.NumComments | |||||
| repoStat.NumIssuesGrowth = repoStat.NumIssues - repoStatisticFourMonthsAgo.NumIssues | |||||
| } | } | ||||
| if _, err = models.InsertRepoStat(&repoStat); err != nil { | if _, err = models.InsertRepoStat(&repoStat); err != nil { | ||||
| @@ -100,9 +149,92 @@ func RepoStatisticDaily(date string) { | |||||
| continue | continue | ||||
| } | } | ||||
| tempRepoStat := models.RepoStatistic{ | |||||
| RepoID: repoStat.RepoID, | |||||
| Date: repoStat.Date, | |||||
| Impact: normalization.GetImpactInitValue(repoStat.NumWatches, repoStat.NumStars, repoStat.NumForks, repoStat.NumDownloads, repoStat.NumComments, repoStat.NumVisits), | |||||
| Completeness: normalization.GetCompleteInitValue(repoStat.NumClosedIssues, repoStat.NumVersions, repoStat.NumDevMonths, repoStat.DatasetSize, repoStat.NumModels, repoStat.NumWikiViews), | |||||
| Liveness: normalization.GetLivenessInitValue(repoStat.NumCommits, repoStat.NumIssues, repoStat.NumPulls, repoStat.NumVisits), | |||||
| ProjectHealth: normalization.GetProjectHealthInitValue(repoStat.IssueFixedRate), | |||||
| TeamHealth: normalization.GetTeamHealthInitValue(repoStat.NumContributor, repoStat.NumKeyContributor, repoStat.NumContributorsGrowth), | |||||
| Growth: normalization.GetRepoGrowthInitValue(repoStat.NumCommitLinesGrowth, repoStat.NumIssuesGrowth, repoStat.NumCommitsGrowth, repoStat.NumContributorsGrowth, repoStat.NumCommentsGrowth), | |||||
| } | |||||
| reposRadar = append(reposRadar, &tempRepoStat) | |||||
| if i == 0 { | |||||
| minRepoRadar = tempRepoStat | |||||
| maxRepoRadar = tempRepoStat | |||||
| } else { | |||||
| if tempRepoStat.Impact < minRepoRadar.Impact { | |||||
| minRepoRadar.Impact = tempRepoStat.Impact | |||||
| } | |||||
| if tempRepoStat.Impact > maxRepoRadar.Impact { | |||||
| maxRepoRadar.Impact = tempRepoStat.Impact | |||||
| } | |||||
| if tempRepoStat.Completeness < minRepoRadar.Completeness { | |||||
| minRepoRadar.Completeness = tempRepoStat.Completeness | |||||
| } | |||||
| if tempRepoStat.Completeness > maxRepoRadar.Completeness { | |||||
| maxRepoRadar.Completeness = tempRepoStat.Completeness | |||||
| } | |||||
| if tempRepoStat.Liveness < minRepoRadar.Completeness { | |||||
| minRepoRadar.Liveness = tempRepoStat.Liveness | |||||
| } | |||||
| if tempRepoStat.Liveness > maxRepoRadar.Liveness { | |||||
| maxRepoRadar.Liveness = tempRepoStat.Liveness | |||||
| } | |||||
| if tempRepoStat.ProjectHealth < minRepoRadar.ProjectHealth { | |||||
| minRepoRadar.ProjectHealth = tempRepoStat.ProjectHealth | |||||
| } | |||||
| if tempRepoStat.ProjectHealth > maxRepoRadar.ProjectHealth { | |||||
| maxRepoRadar.ProjectHealth = tempRepoStat.ProjectHealth | |||||
| } | |||||
| if tempRepoStat.TeamHealth < minRepoRadar.TeamHealth { | |||||
| minRepoRadar.TeamHealth = tempRepoStat.TeamHealth | |||||
| } | |||||
| if tempRepoStat.TeamHealth > maxRepoRadar.TeamHealth { | |||||
| maxRepoRadar.TeamHealth = tempRepoStat.TeamHealth | |||||
| } | |||||
| if tempRepoStat.Growth < minRepoRadar.Growth { | |||||
| minRepoRadar.Growth = tempRepoStat.Growth | |||||
| } | |||||
| if tempRepoStat.Growth > maxRepoRadar.Growth { | |||||
| maxRepoRadar.Growth = tempRepoStat.Growth | |||||
| } | |||||
| } | |||||
| log.Info("finish statistic: %s", repo.Name) | log.Info("finish statistic: %s", repo.Name) | ||||
| } | } | ||||
| //radar map | |||||
| log.Info("begin statistic radar") | |||||
| for _, radarInit := range reposRadar { | |||||
| radarInit.Impact = normalization.Normalization(radarInit.Impact, minRepoRadar.Impact, maxRepoRadar.Impact) | |||||
| radarInit.Completeness = normalization.Normalization(radarInit.Completeness, minRepoRadar.Completeness, maxRepoRadar.Completeness) | |||||
| radarInit.Liveness = normalization.Normalization(radarInit.Liveness, minRepoRadar.Liveness, maxRepoRadar.Liveness) | |||||
| radarInit.ProjectHealth = normalization.Normalization(radarInit.ProjectHealth, minRepoRadar.ProjectHealth, maxRepoRadar.ProjectHealth) | |||||
| radarInit.TeamHealth = normalization.Normalization(radarInit.TeamHealth, minRepoRadar.TeamHealth, maxRepoRadar.TeamHealth) | |||||
| radarInit.Growth = normalization.Normalization(radarInit.Growth, minRepoRadar.Growth, maxRepoRadar.Growth) | |||||
| radarInit.RadarTotal = normalization.GetRadarValue(radarInit.Impact, radarInit.Completeness, radarInit.Liveness, radarInit.ProjectHealth, radarInit.TeamHealth, radarInit.Growth) | |||||
| models.UpdateRepoStat(radarInit) | |||||
| } | |||||
| log.Info("finish statistic: radar") | |||||
| } | } | ||||
| func getDatasetSize(repo *models.Repository) (int64, error) { | func getDatasetSize(repo *models.Repository) (int64, error) { | ||||
| @@ -0,0 +1,94 @@ | |||||
| package repo | |||||
| import ( | |||||
| "time" | |||||
| "code.gitea.io/gitea/models" | |||||
| "code.gitea.io/gitea/modules/log" | |||||
| ) | |||||
| func SummaryStatistic() { | |||||
| log.Info("Generate summary statistic begin") | |||||
| yesterday := time.Now().AddDate(0, 0, -1).Format("2006-01-02") | |||||
| SummaryStatisticDaily(yesterday) | |||||
| log.Info("Generate summary statistic end") | |||||
| } | |||||
| func SummaryStatisticDaily(date string) { | |||||
| log.Info("%s", date) | |||||
| if err := models.DeleteSummaryStatisticDaily(date); err != nil { | |||||
| log.Error("DeleteRepoStatDaily failed: %v", err.Error()) | |||||
| return | |||||
| } | |||||
| //user number | |||||
| userNumber, err := models.GetUsersCount() | |||||
| if err != nil { | |||||
| log.Error("can not get user number", err) | |||||
| userNumber = 0 | |||||
| } | |||||
| //organization number | |||||
| organizationNumber, err := models.GetOrganizationsCount() | |||||
| if err != nil { | |||||
| log.Error("can not get orgnazition number", err) | |||||
| organizationNumber = 0 | |||||
| } | |||||
| // repository number | |||||
| repositoryNumer, err := models.GetAllRepositoriesCount() | |||||
| if err != nil { | |||||
| log.Error("can not get repository number", err) | |||||
| repositoryNumer = 0 | |||||
| } | |||||
| //repository size | |||||
| repositorySize, err := models.GetAllRepositoriesSize() | |||||
| if err != nil { | |||||
| log.Error("can not get repository size", err) | |||||
| repositorySize = 0 | |||||
| } | |||||
| // dataset size | |||||
| allDatasetSize, err := models.GetAllAttachmentSize() | |||||
| if err != nil { | |||||
| log.Error("can not get dataset size", err) | |||||
| allDatasetSize = 0 | |||||
| } | |||||
| //topic repo number | |||||
| topics, err := models.GetAllUsedTopics() | |||||
| if err != nil { | |||||
| log.Error("can not get topics", err) | |||||
| } | |||||
| var topicsCount [11]int | |||||
| for _, topic := range topics { | |||||
| index, exists := models.DomainMap[topic.Name] | |||||
| if exists { | |||||
| topicsCount[index] = topic.RepoCount | |||||
| } | |||||
| } | |||||
| summaryStat := models.SummaryStatistic{ | |||||
| Date: date, | |||||
| NumUsers: userNumber, | |||||
| RepoSize: repositorySize, | |||||
| DatasetSize: allDatasetSize, | |||||
| NumOrganizations: organizationNumber, | |||||
| NumRepos: repositoryNumer, | |||||
| NumRepoBigModel: topicsCount[0], | |||||
| NumRepoAI: topicsCount[1], | |||||
| NumRepoVision: topicsCount[2], | |||||
| NumRepoNLP: topicsCount[3], | |||||
| NumRepoML: topicsCount[4], | |||||
| NumRepoNN: topicsCount[5], | |||||
| NumRepoAutoDrive: topicsCount[6], | |||||
| NumRepoRobot: topicsCount[7], | |||||
| NumRepoLeagueLearn: topicsCount[8], | |||||
| NumRepoDataMining: topicsCount[9], | |||||
| NumRepoRISC: topicsCount[10], | |||||
| } | |||||
| if _, err = models.InsertSummaryStatistic(&summaryStat); err != nil { | |||||
| log.Error("Insert summary Stat failed: %v", err.Error()) | |||||
| } | |||||
| log.Info("finish summary statistic") | |||||
| } | |||||
| @@ -8,16 +8,17 @@ import ( | |||||
| "code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
| ) | ) | ||||
| func TimingCountData() { | |||||
| func TimingCountDataByDate(date string) { | |||||
| t, _ := time.Parse("2006-01-02", date) | |||||
| startTime := time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location()) | |||||
| endTime := time.Date(t.Year(), t.Month(), t.Day(), 23, 59, 59, 0, t.Location()) | |||||
| //query wiki data | //query wiki data | ||||
| log.Info("start to time count data") | log.Info("start to time count data") | ||||
| wikiMap := make(map[string]int) | wikiMap := make(map[string]int) | ||||
| currentTimeNow := time.Now() | |||||
| log.Info("current time:" + currentTimeNow.Format("2006-01-02 15:04:05")) | |||||
| yesterday := currentTimeNow.AddDate(0, 0, -1) | |||||
| repoList, err := models.GetAllRepositories() | repoList, err := models.GetAllRepositories() | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("query repo error.") | log.Error("query repo error.") | ||||
| @@ -29,7 +30,7 @@ func TimingCountData() { | |||||
| time, err := git.GetLatestCommitTime(wikiPath) | time, err := git.GetLatestCommitTime(wikiPath) | ||||
| if err == nil { | if err == nil { | ||||
| log.Info("last commit time:" + time.Format("2006-01-02 15:04:05") + " wikiPath=" + wikiPath) | log.Info("last commit time:" + time.Format("2006-01-02 15:04:05") + " wikiPath=" + wikiPath) | ||||
| if time.After(yesterday) { | |||||
| if time.After(startTime) { | |||||
| wikiRepo, _, err := FindWikiRepoCommitByWikiPath(wikiPath) | wikiRepo, _, err := FindWikiRepoCommitByWikiPath(wikiPath) | ||||
| if err != nil { | if err != nil { | ||||
| log.Error("wiki not exist. wikiPath=" + wikiPath) | log.Error("wiki not exist. wikiPath=" + wikiPath) | ||||
| @@ -55,5 +56,16 @@ func TimingCountData() { | |||||
| } | } | ||||
| } | } | ||||
| //other user info data | //other user info data | ||||
| models.CountData(wikiMap) | |||||
| models.CounDataByDate(wikiMap, startTime, endTime) | |||||
| } | |||||
| func TimingCountData() { | |||||
| log.Info("start to time count data") | |||||
| currentTimeNow := time.Now() | |||||
| log.Info("current time:" + currentTimeNow.Format("2006-01-02 15:04:05")) | |||||
| startTime := currentTimeNow.AddDate(0, 0, -1).Format("2006-01-02") | |||||
| TimingCountDataByDate(startTime) | |||||
| } | } | ||||
| @@ -6,13 +6,15 @@ package routes | |||||
| import ( | import ( | ||||
| "bytes" | "bytes" | ||||
| "code.gitea.io/gitea/routers/operation" | |||||
| "encoding/gob" | "encoding/gob" | ||||
| "net/http" | "net/http" | ||||
| "path" | "path" | ||||
| "text/template" | "text/template" | ||||
| "time" | "time" | ||||
| "code.gitea.io/gitea/routers/operation" | |||||
| "code.gitea.io/gitea/routers/private" | |||||
| "code.gitea.io/gitea/routers/secure" | "code.gitea.io/gitea/routers/secure" | ||||
| "code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
| @@ -33,7 +35,6 @@ import ( | |||||
| "code.gitea.io/gitea/routers/dev" | "code.gitea.io/gitea/routers/dev" | ||||
| "code.gitea.io/gitea/routers/events" | "code.gitea.io/gitea/routers/events" | ||||
| "code.gitea.io/gitea/routers/org" | "code.gitea.io/gitea/routers/org" | ||||
| "code.gitea.io/gitea/routers/private" | |||||
| "code.gitea.io/gitea/routers/repo" | "code.gitea.io/gitea/routers/repo" | ||||
| "code.gitea.io/gitea/routers/user" | "code.gitea.io/gitea/routers/user" | ||||
| userSetting "code.gitea.io/gitea/routers/user/setting" | userSetting "code.gitea.io/gitea/routers/user/setting" | ||||
| @@ -113,14 +114,14 @@ func RouterHandler(level log.Level) func(ctx *macaron.Context) { | |||||
| } | } | ||||
| // SetLogMsgID set msgID in Context | // SetLogMsgID set msgID in Context | ||||
| func SetLogMsgID() func(ctx *macaron.Context) { | |||||
| func SetLogMsgID() macaron.Handler { | |||||
| return func(ctx *macaron.Context) { | return func(ctx *macaron.Context) { | ||||
| start := time.Now() | start := time.Now() | ||||
| uuid := gouuid.NewV4().String() | uuid := gouuid.NewV4().String() | ||||
| ctx.Data["MsgID"] = uuid | ctx.Data["MsgID"] = uuid | ||||
| log.Info("Started %s %s for %s", log.ColoredMethod(ctx.Req.Method), ctx.Req.URL.RequestURI(), ctx.RemoteAddr(), ctx.Data["MsgID"]) | |||||
| log.Info("%s Started %s %s for %s", ctx.Data["SignedUserName"], log.ColoredMethod(ctx.Req.Method), ctx.Req.URL.RequestURI(), ctx.RemoteAddr(), ctx.Data["MsgID"]) | |||||
| rw := ctx.Resp.(macaron.ResponseWriter) | rw := ctx.Resp.(macaron.ResponseWriter) | ||||
| ctx.Next() | ctx.Next() | ||||
| @@ -148,7 +149,7 @@ func NewMacaron() *macaron.Macaron { | |||||
| m.Use(macaron.Logger()) | m.Use(macaron.Logger()) | ||||
| } | } | ||||
| } | } | ||||
| m.Use(SetLogMsgID()) | |||||
| //m.Use(SetLogMsgID()) | |||||
| // Access Logger is similar to Router Log but more configurable and by default is more like the NCSA Common Log format | // Access Logger is similar to Router Log but more configurable and by default is more like the NCSA Common Log format | ||||
| if setting.EnableAccessLog { | if setting.EnableAccessLog { | ||||
| setupAccessLogger(m) | setupAccessLogger(m) | ||||
| @@ -256,6 +257,7 @@ func NewMacaron() *macaron.Macaron { | |||||
| DisableDebug: !setting.EnablePprof, | DisableDebug: !setting.EnablePprof, | ||||
| })) | })) | ||||
| m.Use(context.Contexter()) | m.Use(context.Contexter()) | ||||
| m.Use(SetLogMsgID()) | |||||
| // OK we are now set-up enough to allow us to create a nicer recovery than | // OK we are now set-up enough to allow us to create a nicer recovery than | ||||
| // the default macaron recovery | // the default macaron recovery | ||||
| m.Use(context.Recovery()) | m.Use(context.Recovery()) | ||||
| @@ -565,6 +567,8 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| m.Post("/private", repo.UpdatePublicAttachment) | m.Post("/private", repo.UpdatePublicAttachment) | ||||
| m.Get("/get_chunks", repo.GetSuccessChunks) | m.Get("/get_chunks", repo.GetSuccessChunks) | ||||
| m.Get("/new_multipart", repo.NewMultipart) | m.Get("/new_multipart", repo.NewMultipart) | ||||
| m.Put("/obs_proxy_multipart", repo.PutOBSProxyUpload) | |||||
| m.Get("/obs_proxy_download", repo.GetOBSProxyDownload) | |||||
| m.Get("/get_multipart_url", repo.GetMultipartUploadUrl) | m.Get("/get_multipart_url", repo.GetMultipartUploadUrl) | ||||
| m.Post("/complete_multipart", repo.CompleteMultipart) | m.Post("/complete_multipart", repo.CompleteMultipart) | ||||
| m.Post("/update_chunk", repo.UpdateMultipart) | m.Post("/update_chunk", repo.UpdateMultipart) | ||||
| @@ -958,15 +962,42 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| }, context.RepoRef()) | }, context.RepoRef()) | ||||
| m.Group("/modelarts", func() { | m.Group("/modelarts", func() { | ||||
| m.Get("", reqRepoCloudBrainReader, repo.ModelArtsIndex) | |||||
| m.Group("/:jobid", func() { | |||||
| m.Get("", reqRepoCloudBrainReader, repo.ModelArtsShow) | |||||
| m.Get("/debug", reqRepoCloudBrainReader, repo.ModelArtsDebug) | |||||
| m.Post("/stop", reqRepoCloudBrainWriter, repo.ModelArtsStop) | |||||
| m.Post("/del", reqRepoCloudBrainWriter, repo.ModelArtsDel) | |||||
| // m.Get("", reqRepoCloudBrainReader, repo.ModelArtsIndex) | |||||
| // m.Group("/:jobid", func() { | |||||
| // m.Get("", reqRepoCloudBrainReader, repo.ModelArtsShow) | |||||
| // m.Get("/debug", reqRepoCloudBrainReader, repo.ModelArtsDebug) | |||||
| // m.Post("/stop", reqRepoCloudBrainWriter, repo.ModelArtsStop) | |||||
| // m.Post("/del", reqRepoCloudBrainWriter, repo.ModelArtsDel) | |||||
| // }) | |||||
| // m.Get("/create", reqRepoCloudBrainWriter, repo.ModelArtsNew) | |||||
| // m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateModelArtsForm{}), repo.ModelArtsCreate) | |||||
| m.Group("/notebook", func() { | |||||
| m.Get("", reqRepoCloudBrainReader, repo.NotebookIndex) | |||||
| m.Group("/:jobid", func() { | |||||
| m.Get("", reqRepoCloudBrainReader, repo.NotebookShow) | |||||
| m.Get("/debug", reqRepoCloudBrainReader, repo.NotebookDebug) | |||||
| m.Post("/stop", reqRepoCloudBrainWriter, repo.NotebookStop) | |||||
| m.Post("/del", reqRepoCloudBrainWriter, repo.NotebookDel) | |||||
| }) | |||||
| m.Get("/create", reqRepoCloudBrainWriter, repo.NotebookNew) | |||||
| m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateModelArtsNotebookForm{}), repo.NotebookCreate) | |||||
| }) | |||||
| m.Group("/train-job", func() { | |||||
| m.Get("", reqRepoCloudBrainReader, repo.TrainJobIndex) | |||||
| m.Group("/:jobid", func() { | |||||
| m.Get("", reqRepoCloudBrainReader, repo.TrainJobShow) | |||||
| m.Post("/stop", reqRepoCloudBrainWriter, repo.TrainJobStop) | |||||
| m.Post("/del", reqRepoCloudBrainWriter, repo.TrainJobDel) | |||||
| m.Get("/log", reqRepoCloudBrainReader, repo.TrainJobGetLog) | |||||
| m.Get("/models", reqRepoCloudBrainReader, repo.TrainJobShowModels) | |||||
| m.Get("/download_model", reqRepoCloudBrainReader, repo.TrainJobDownloadModel) | |||||
| }) | |||||
| m.Get("/create", reqRepoCloudBrainReader, repo.TrainJobNew) | |||||
| m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateModelArtsTrainJobForm{}), repo.TrainJobCreate) | |||||
| m.Get("/para-config-list", reqRepoCloudBrainReader, repo.TrainJobGetConfigList) | |||||
| }) | }) | ||||
| m.Get("/create", reqRepoCloudBrainWriter, repo.ModelArtsNew) | |||||
| m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateModelArtsForm{}), repo.ModelArtsCreate) | |||||
| }, context.RepoRef()) | }, context.RepoRef()) | ||||
| m.Group("/blockchain", func() { | m.Group("/blockchain", func() { | ||||
| @@ -11,8 +11,6 @@ import ( | |||||
| "net/http" | "net/http" | ||||
| "strings" | "strings" | ||||
| "code.gitea.io/gitea/routers/repo" | |||||
| "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/auth/oauth2" | "code.gitea.io/gitea/modules/auth/oauth2" | ||||
| @@ -217,6 +215,7 @@ func SignInPost(ctx *context.Context, form auth.SignInForm) { | |||||
| } | } | ||||
| return | return | ||||
| } | } | ||||
| models.SaveLoginInfoToDb(ctx.Req.Request, u) | |||||
| // If this user is enrolled in 2FA, we can't sign the user in just yet. | // If this user is enrolled in 2FA, we can't sign the user in just yet. | ||||
| // Instead, redirect them to the 2FA authentication page. | // Instead, redirect them to the 2FA authentication page. | ||||
| _, err = models.GetTwoFactorByUID(u.ID) | _, err = models.GetTwoFactorByUID(u.ID) | ||||
| @@ -228,7 +227,6 @@ func SignInPost(ctx *context.Context, form auth.SignInForm) { | |||||
| } | } | ||||
| return | return | ||||
| } | } | ||||
| // User needs to use 2FA, save data and redirect to 2FA page. | // User needs to use 2FA, save data and redirect to 2FA page. | ||||
| if err := ctx.Session.Set("twofaUid", u.ID); err != nil { | if err := ctx.Session.Set("twofaUid", u.ID); err != nil { | ||||
| ctx.ServerError("UserSignIn: Unable to set twofaUid in session", err) | ctx.ServerError("UserSignIn: Unable to set twofaUid in session", err) | ||||
| @@ -242,7 +240,6 @@ func SignInPost(ctx *context.Context, form auth.SignInForm) { | |||||
| ctx.ServerError("UserSignIn: Unable to save session", err) | ctx.ServerError("UserSignIn: Unable to save session", err) | ||||
| return | return | ||||
| } | } | ||||
| regs, err := models.GetU2FRegistrationsByUID(u.ID) | regs, err := models.GetU2FRegistrationsByUID(u.ID) | ||||
| if err == nil && len(regs) > 0 { | if err == nil && len(regs) > 0 { | ||||
| ctx.Redirect(setting.AppSubURL + "/user/u2f") | ctx.Redirect(setting.AppSubURL + "/user/u2f") | ||||
| @@ -1058,7 +1055,6 @@ func SignOut(ctx *context.Context) { | |||||
| }) | }) | ||||
| } | } | ||||
| HandleSignOut(ctx) | HandleSignOut(ctx) | ||||
| go repo.StopJobsByUserID(ctx.User.ID) | |||||
| ctx.Redirect(setting.AppSubURL + "/") | ctx.Redirect(setting.AppSubURL + "/") | ||||
| } | } | ||||
| @@ -1171,8 +1167,8 @@ func SignUpPost(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterFo | |||||
| log.Trace("Account created: %s", u.Name, ctx.Data["MsgID"]) | log.Trace("Account created: %s", u.Name, ctx.Data["MsgID"]) | ||||
| err := models.AddEmailAddress(&models.EmailAddress{ | err := models.AddEmailAddress(&models.EmailAddress{ | ||||
| UID: u.ID, | |||||
| Email: form.Email, | |||||
| UID: u.ID, | |||||
| Email: form.Email, | |||||
| IsActivated: !setting.Service.RegisterEmailConfirm, | IsActivated: !setting.Service.RegisterEmailConfirm, | ||||
| }) | }) | ||||
| @@ -1270,7 +1266,7 @@ func Activate(ctx *context.Context) { | |||||
| } | } | ||||
| email, err := models.GetEmailAddressByIDAndEmail(user.ID, user.Email) | email, err := models.GetEmailAddressByIDAndEmail(user.ID, user.Email) | ||||
| if err != nil || email == nil{ | |||||
| if err != nil || email == nil { | |||||
| log.Error("GetEmailAddressByIDAndEmail failed", ctx.Data["MsgID"]) | log.Error("GetEmailAddressByIDAndEmail failed", ctx.Data["MsgID"]) | ||||
| } else { | } else { | ||||
| if err := email.Activate(); err != nil { | if err := email.Activate(); err != nil { | ||||
| @@ -8,6 +8,8 @@ package setting | |||||
| import ( | import ( | ||||
| "errors" | "errors" | ||||
| "code.gitea.io/gitea/routers/repo" | |||||
| "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" | ||||
| @@ -240,6 +242,7 @@ func DeleteAccount(ctx *context.Context) { | |||||
| ctx.ServerError("DeleteUser", err) | ctx.ServerError("DeleteUser", err) | ||||
| } | } | ||||
| } else { | } else { | ||||
| go repo.StopJobsByUserID(ctx.User.ID) | |||||
| log.Trace("Account deleted: %s", ctx.User.Name) | log.Trace("Account deleted: %s", ctx.User.Name) | ||||
| ctx.Redirect(setting.AppSubURL + "/") | ctx.Redirect(setting.AppSubURL + "/") | ||||
| } | } | ||||
| @@ -29,8 +29,12 @@ | |||||
| {{.Repo.OwnerName}} / {{.Title}} | {{.Repo.OwnerName}} / {{.Title}} | ||||
| </a> | </a> | ||||
| <div class="ui right metas"> | <div class="ui right metas"> | ||||
| <span class="text grey">{{svg "octicon-tasklist" 16}} {{$.i18n.Tr (printf "dataset.task.%s" .Task)}}</span> | |||||
| <span class="text grey">{{svg "octicon-tag" 16}}{{$.i18n.Tr (printf "dataset.category.%s" .Category)}}</span> | |||||
| {{if .Task}} | |||||
| <span class="text grey">{{svg "octicon-tasklist" 16}} {{$.i18n.Tr (printf "dataset.task.%s" .Task)}}</span> | |||||
| {{end}} | |||||
| {{if .Category}} | |||||
| <span class="text grey">{{svg "octicon-tag" 16}}{{$.i18n.Tr (printf "dataset.category.%s" .Category)}}</span> | |||||
| {{end}} | |||||
| <span class="text grey">{{svg "octicon-flame" 16}} {{.DownloadTimes}}</span> | <span class="text grey">{{svg "octicon-flame" 16}} {{.DownloadTimes}}</span> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| @@ -50,6 +50,13 @@ | |||||
| </a> | </a> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="swiper-slide"> | |||||
| <div class="ui card"> | |||||
| <a class="image" href="https://git.openi.org.cn/JD_Group"> | |||||
| <img src="/img/org-jd@2x-80.jpg" alt="京东" title="京东"> | |||||
| </a> | |||||
| </div> | |||||
| </div> | |||||
| <div class="swiper-slide"> | <div class="swiper-slide"> | ||||
| <div class="ui card"> | <div class="ui card"> | ||||
| <a class="image" href="https://git.openi.org.cn/TensorLayer"> | <a class="image" href="https://git.openi.org.cn/TensorLayer"> | ||||
| @@ -215,7 +215,7 @@ | |||||
| <div class="ui container"> | <div class="ui container"> | ||||
| <!-- 中间云脑和新建任务按钮 --> | <!-- 中间云脑和新建任务按钮 --> | ||||
| <div class="ui three column stack able grid"> | |||||
| <!-- <div class="ui three column stack able grid"> | |||||
| <div class="column"> | <div class="column"> | ||||
| <h2>{{.i18n.Tr "repo.cloudbrain1"}}</h2> | <h2>{{.i18n.Tr "repo.cloudbrain1"}}</h2> | ||||
| </div> | </div> | ||||
| @@ -233,6 +233,30 @@ | |||||
| <div class="ui blue mini menu selectcloudbrain"> | <div class="ui blue mini menu selectcloudbrain"> | ||||
| <a class="active item" href="{{.RepoLink}}/cloudbrain">{{svg "octicon-server" 16}} CPU / GPU</a> | <a class="active item" href="{{.RepoLink}}/cloudbrain">{{svg "octicon-server" 16}} CPU / GPU</a> | ||||
| <a class="item" href="{{.RepoLink}}/modelarts">{{svg "octicon-server" 16}} Ascend NPU</a> | <a class="item" href="{{.RepoLink}}/modelarts">{{svg "octicon-server" 16}} Ascend NPU</a> | ||||
| </div> --> | |||||
| <div class="ui two column stackable grid "> | |||||
| <div class="column"> | |||||
| <div class="ui blue small menu compact selectcloudbrain"> | |||||
| <a class="active item">{{$.i18n.Tr "repo.modelarts.notebook"}}</a> | |||||
| <!-- <a class="item" href="{{.RepoLink}}/modelarts">训练任务</a> --> | |||||
| </div> | |||||
| </div> | |||||
| <div class="column right aligned"> | |||||
| <div class="ui selection dropdown" style="min-width: 10em;min-height:2.6em;border-radius: .28571429rem;margin-right: 1em;padding: .67em 3.2em .7em 1em;"> | |||||
| {{svg "octicon-server" 16}} | |||||
| <div class="default text" style="color: rgba(0,0,0,.87);"> CPU / GPU</div> | |||||
| <i class="dropdown icon"></i> | |||||
| <div class="menu"> | |||||
| <a class="item" href="{{.RepoLink}}/cloudbrain" data-value="11">CPU / GPU</a> | |||||
| <a class="item" href="{{.RepoLink}}/modelarts/notebook" data-value="22">Ascend NPU</a> | |||||
| </div> | |||||
| </div> | |||||
| {{if .Permission.CanWrite $.UnitTypeCloudBrain}} | |||||
| <a class="ui green button" href="{{.RepoLink}}/cloudbrain/create">{{$.i18n.Tr "repo.modelarts.train_job.new_debug"}}</a>{{end}} | |||||
| </div> | |||||
| </div> | </div> | ||||
| <!-- 中下列表展示区 --> | <!-- 中下列表展示区 --> | ||||
| @@ -299,7 +323,7 @@ | |||||
| {{else}} | {{else}} | ||||
| {{.Status}} | {{.Status}} | ||||
| {{end}} --> | {{end}} --> | ||||
| <span><i style="vertical-align: middle;" class="{{.Status}}"></i><span style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span> | |||||
| <span><i id="{{.JobID}}-icon" style="vertical-align: middle;" class="{{.Status}}"></i><span id="{{.JobID}}-text" style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span> | |||||
| </span> | </span> | ||||
| <!-- <span class="job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}" > | <!-- <span class="job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}" > | ||||
| {{if eq .Status "STOPPED"}} | {{if eq .Status "STOPPED"}} | ||||
| @@ -333,35 +357,56 @@ | |||||
| {{end}} | {{end}} | ||||
| <!-- 调试 --> | <!-- 调试 --> | ||||
| <a class="ui basic {{if not .CanDebug}}disabled {{else}}blue {{end}}button" href="{{$.Link}}/{{.JobID}}/debug" target="_blank"> | |||||
| 调试 | |||||
| {{if $.Permission.CanWrite $.UnitTypeCloudBrain}} | |||||
| <a id="model-debug-{{.JobID}}" class="ui basic {{if not .CanDebug}}disabled {{else}}blue {{end}}button" href="{{$.Link}}/{{.JobID}}/debug" target="_blank"> | |||||
| {{$.i18n.Tr "repo.debug"}} | |||||
| </a> | |||||
| {{else}} | |||||
| <a class="ui basic disabled button" href="{{$.Link}}/{{.JobID}}/debug" target="_blank"> | |||||
| {{$.i18n.Tr "repo.debug"}} | |||||
| </a> | </a> | ||||
| {{end}} | |||||
| <form id="stopForm-{{.JobID}}" action="{{if eq .Status "STOPPED"}}javascript:void(0){{else}}{{$.Link}}/{{.JobID}}/stop{{end}}" method="post" style="margin-left:-1px;"> | |||||
| <form id="stopForm-{{.JobID}}" action="{{$.Link}}/{{.JobID}}/stop" method="post" style="margin-left:-1px;"> | |||||
| {{$.CsrfTokenHtml}} | {{$.CsrfTokenHtml}} | ||||
| <a class="ui basic {{if eq .Status "STOPPED"}}disabled {{else}}blue {{end}}button" onclick="document.getElementById('stopForm-{{.JobID}}').submit();"> | |||||
| 停止 | |||||
| {{if $.Permission.CanWrite $.UnitTypeCloudBrain}} | |||||
| <a id="stop-model-debug-{{.JobID}}" class="ui basic {{if or (eq .Status "STOPPED") (eq .Status "FAILED")}}disabled {{else}}blue {{end}}button" onclick="document.getElementById('stopForm-{{.JobID}}').submit();"> | |||||
| {{$.i18n.Tr "repo.stop"}} | |||||
| </a> | |||||
| {{else}} | |||||
| <a class="ui basic disabled button" onclick="document.getElementById('stopForm-{{.JobID}}').submit();"> | |||||
| {{$.i18n.Tr "repo.stop"}} | |||||
| </a> | </a> | ||||
| {{end}} | |||||
| </form> | </form> | ||||
| </div> | </div> | ||||
| <div class="ui compact buttons" style="margin-right:10px;"> | <div class="ui compact buttons" style="margin-right:10px;"> | ||||
| <!-- 模型下载 --> | <!-- 模型下载 --> | ||||
| <a class="ui basic blue button" href="{{$.Link}}/{{.JobID}}/models" target="_blank"> | <a class="ui basic blue button" href="{{$.Link}}/{{.JobID}}/models" target="_blank"> | ||||
| 模型下载 | |||||
| {{$.i18n.Tr "repo.download"}} | |||||
| </a> | </a> | ||||
| <!-- 接收结果 --> | <!-- 接收结果 --> | ||||
| <iframe src="" frameborder="0" name="iframeContent" style="display: none;"></iframe> | <iframe src="" frameborder="0" name="iframeContent" style="display: none;"></iframe> | ||||
| <a class="imageBtn ui basic {{if not .CanDebug}}disabled {{else}}blue {{end}}button" value="{{.CanDebug}}">提交镜像</a> | |||||
| {{if $.Permission.CanWrite $.UnitTypeCloudBrain}} | |||||
| <a id="model-image-{{.JobID}}" class="imageBtn ui basic {{if not .CanDebug}}disabled {{else}}blue {{end}}button" value="{{.CanDebug}}">{{$.i18n.Tr "repo.submit_image"}}</a> | |||||
| {{else}} | |||||
| <a class="imageBtn ui basic disabled button" value="{{.CanDebug}}">{{$.i18n.Tr "repo.submit_image"}}</a> | |||||
| {{end}} | |||||
| </div> | </div> | ||||
| <!-- 删除任务 --> | <!-- 删除任务 --> | ||||
| <form class="ui compact buttons" id="delForm-{{.JobID}}" action="{{if not .CanDel}}javascript:void(0){{else}}{{$.Link}}/{{.JobID}}/del{{end}}" method="post"> | |||||
| <form class="ui compact buttons" id="delForm-{{.JobID}}" action="{{$.Link}}/{{.JobID}}/del" method="post"> | |||||
| {{$.CsrfTokenHtml}} | {{$.CsrfTokenHtml}} | ||||
| <a class="ui compact {{if not .CanDel}}disabled {{else}}red {{end}}button" onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | |||||
| 删除 | |||||
| {{if $.Permission.CanWrite $.UnitTypeCloudBrain}} | |||||
| <a id="model-delete-{{.JobID}}" class="ui compact {{if not .CanDel}}disabled {{else}}red {{end}}button" onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | |||||
| {{$.i18n.Tr "repo.delete"}} | |||||
| </a> | </a> | ||||
| {{else}} | |||||
| <a class="ui compact disabled button" onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | |||||
| {{$.i18n.Tr "repo.delete"}} | |||||
| </a> | |||||
| {{end}} | |||||
| </form> | </form> | ||||
| </div> | </div> | ||||
| @@ -488,24 +533,52 @@ | |||||
| $(document).ready(loadJobStatus); | $(document).ready(loadJobStatus); | ||||
| function loadJobStatus() { | function loadJobStatus() { | ||||
| $(".job-status").each((index, job) => { | $(".job-status").each((index, job) => { | ||||
| console.log("---------",index,job) | |||||
| const jobID = job.dataset.jobid; | const jobID = job.dataset.jobid; | ||||
| const repoPath = job.dataset.repopath; | const repoPath = job.dataset.repopath; | ||||
| if (job.textContent.trim() == 'STOPPED') { | |||||
| if (job.textContent.trim() == 'STOPPED' || job.textContent.trim() == 'FAILED') { | |||||
| return | return | ||||
| } | } | ||||
| $.get(`/api/v1/repos/${repoPath}/cloudbrain/${jobID}`, (data) => { | $.get(`/api/v1/repos/${repoPath}/cloudbrain/${jobID}`, (data) => { | ||||
| const jobID = data.JobID | const jobID = data.JobID | ||||
| const status = data.JobStatus | const status = data.JobStatus | ||||
| console.log("status",status) | |||||
| if (status != job.textContent.trim()) { | if (status != job.textContent.trim()) { | ||||
| //$('#' + jobID).text(status) | |||||
| console.log("---------") | |||||
| $('#' + jobID+'-icon').removeClass().addClass(status) | |||||
| $('#' + jobID+ '-text').text(status) | |||||
| //if (status == 'STOPPED') { | //if (status == 'STOPPED') { | ||||
| window.location.reload() | |||||
| // window.location.reload() | |||||
| //} | //} | ||||
| } | } | ||||
| if(status==="RUNNING"){ | |||||
| $('#model-debug-'+jobID).removeClass('disabled') | |||||
| $('#model-debug-'+jobID).addClass('blue') | |||||
| $('#model-image-'+jobID).removeClass('disabled') | |||||
| $('#model-image-'+jobID).addClass('blue') | |||||
| } | |||||
| if(status!=="RUNNING"){ | |||||
| $('#model-debug-'+jobID).removeClass('blue') | |||||
| $('#model-debug-'+jobID).addClass('disabled') | |||||
| $('#model-image-'+jobID).removeClass('blue') | |||||
| $('#model-image-'+jobID).addClass('disabled') | |||||
| } | |||||
| if(status!=="STOPPED" || status!=="FAILED"){ | |||||
| $('#stop-model-debug-'+jobID).removeClass('disabled') | |||||
| $('#stop-model-debug-'+jobID).addClass('blue') | |||||
| $('#model-delete-'+jobID).removeClass('red') | |||||
| $('#model-delete-'+jobID).addClass('disabled') | |||||
| } | |||||
| if(status=="STOPPED" || status=="FAILED"){ | |||||
| $('#stop-model-debug-'+jobID).removeClass('blue') | |||||
| $('#stop-model-debug-'+jobID).addClass('disabled') | |||||
| $('#model-delete-'+jobID).removeClass('disabled') | |||||
| $('#model-delete-'+jobID).addClass('red') | |||||
| } | |||||
| }).fail(function(err) { | }).fail(function(err) { | ||||
| console.log(err); | console.log(err); | ||||
| }); | }); | ||||
| @@ -89,6 +89,10 @@ | |||||
| display: none; | display: none; | ||||
| } | } | ||||
| .inline.required.field.cloudbrain_brainscore { | |||||
| display: none; | |||||
| } | |||||
| .select2-container .select2-selection--single{ | .select2-container .select2-selection--single{ | ||||
| height:38px !important; | height:38px !important; | ||||
| } | } | ||||
| @@ -116,7 +120,7 @@ | |||||
| <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" .}} | ||||
| <div class="ui positive message" id="messageInfo"> | |||||
| <div class="ui negative message" id="messageInfo"> | |||||
| <p></p> | <p></p> | ||||
| </div> | </div> | ||||
| <form id="form_id" class="ui form" action="{{.Link}}" method="post"> | <form id="form_id" class="ui form" action="{{.Link}}" method="post"> | ||||
| @@ -130,12 +134,19 @@ | |||||
| <input name="job_name" id="cloudbrain_job_name" placeholder="任务名称" value="{{.job_name}}" tabindex="3" autofocus required maxlength="255"> | <input name="job_name" id="cloudbrain_job_name" placeholder="任务名称" value="{{.job_name}}" tabindex="3" autofocus required maxlength="255"> | ||||
| </div> | </div> | ||||
| <div class="inline required field" style="{{if .is_benchmark_enabled}}display:block;{{else if .is_snn4imagenet_enabled}}display:block;{{else}}display:none;{{end}}"> | |||||
| <div class="inline required field" style="{{if ((.is_benchmark_enabled) or (.is_snn4imagenet_enabled) or (.is_brainscore_enabled))}}display:block;{{else}}display:none;{{end}}"> | |||||
| <label>任务类型</label> | <label>任务类型</label> | ||||
| <select id="cloudbrain_job_type" class="ui search dropdown" placeholder="选择任务类型" style='width:385px' name="job_type"> | <select id="cloudbrain_job_type" class="ui search dropdown" placeholder="选择任务类型" style='width:385px' name="job_type"> | ||||
| <option name="job_type" value="DEBUG">DEBUG</option> | <option name="job_type" value="DEBUG">DEBUG</option> | ||||
| <option name="job_type" value="BENCHMARK">BENCHMARK</option> | |||||
| <option name="job_type" value="SNN4IMAGENET">SNN4IMAGENET</option> | |||||
| {{if .is_benchmark_enabled}} | |||||
| <option name="job_type" value="BENCHMARK">BENCHMARK</option> | |||||
| {{end}} | |||||
| {{if .is_snn4imagenet_enabled}} | |||||
| <option name="job_type" value="SNN4IMAGENET">SNN4IMAGENET</option> | |||||
| {{end}} | |||||
| {{if .is_brainscore_enabled}} | |||||
| <option name="job_type" value="BRAINSCORE">BRAINSCORE</option> | |||||
| {{end}} | |||||
| </select> | </select> | ||||
| </div> | </div> | ||||
| @@ -209,11 +220,15 @@ | |||||
| </div> | </div> | ||||
| <div class="inline required field cloudbrain_benchmark"> | <div class="inline required field cloudbrain_benchmark"> | ||||
| <label>benchmark脚本存放路径</label> | <label>benchmark脚本存放路径</label> | ||||
| <input name="benchmark_path" id="cloudbrain_benchmark_path" value="{{.benchmark_path}}" tabindex="3" autofocus required maxlength="255" readonly="readonly"> | |||||
| <input name="benchmark_path" id="cloudbrain_benchmark_path" value="{{.benchmark_path}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | |||||
| </div> | </div> | ||||
| <div class="inline required field cloudbrain_snn4imagenet"> | <div class="inline required field cloudbrain_snn4imagenet"> | ||||
| <label>snn4imagenet脚本存放路径</label> | <label>snn4imagenet脚本存放路径</label> | ||||
| <input name="snn4imagenet_path" id="cloudbrain_snn4imagenet_path" value="{{.snn4imagenet_path}}" tabindex="3" autofocus required maxlength="255" readonly="readonly"> | |||||
| <input name="snn4imagenet_path" id="cloudbrain_snn4imagenet_path" value="{{.snn4imagenet_path}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | |||||
| </div> | |||||
| <div class="inline required field cloudbrain_brainscore"> | |||||
| <label>brainscore脚本存放路径</label> | |||||
| <input name="brainscore_path" id="cloudbrain_brainscore_path" value="{{.brainscore_path}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | |||||
| </div> | </div> | ||||
| <div class="inline required field" hidden> | <div class="inline required field" hidden> | ||||
| <label>启动命令</label> | <label>启动命令</label> | ||||
| @@ -250,11 +265,11 @@ | |||||
| let value_task = $("input[name='job_name']").val() | let value_task = $("input[name='job_name']").val() | ||||
| let value_image = $("input[name='image']").val() | let value_image = $("input[name='image']").val() | ||||
| let value_data = $("input[name='attachment']").val() | let value_data = $("input[name='attachment']").val() | ||||
| let re = /^[a-z0-9][a-z0-9-_]{1,36}$/ | |||||
| let re = /^[a-z0-9][a-z0-9-_]{1,34}[a-z0-9-]$/ | |||||
| let flag = re.test(value_task) | let flag = re.test(value_task) | ||||
| if(!flag){ | if(!flag){ | ||||
| $('#messageInfo').css('display','block') | $('#messageInfo').css('display','block') | ||||
| let str = '只能以小写字母或数字开头且只包含小写字母、数字、_和-、最长36个字符。' | |||||
| let str = '只能以小写字母或数字开头且只包含小写字母、数字、_和-,不能以_结尾,最长36个字符。' | |||||
| $('#messageInfo p').text(str) | $('#messageInfo p').text(str) | ||||
| return false | return false | ||||
| } | } | ||||
| @@ -311,9 +326,12 @@ | |||||
| $(".cloudbrain_benchmark").show(); | $(".cloudbrain_benchmark").show(); | ||||
| } else if ($(this).val() == 'SNN4IMAGENET') { | } else if ($(this).val() == 'SNN4IMAGENET') { | ||||
| $(".cloudbrain_snn4imagenet").show(); | $(".cloudbrain_snn4imagenet").show(); | ||||
| } else if ($(this).val() == 'BRAINSCORE') { | |||||
| $(".cloudbrain_brainscore").show(); | |||||
| } else { | } else { | ||||
| $(".cloudbrain_benchmark").hide(); | $(".cloudbrain_benchmark").hide(); | ||||
| $(".cloudbrain_snn4imagenet").hide(); | $(".cloudbrain_snn4imagenet").hide(); | ||||
| $(".cloudbrain_brainscore").hide(); | |||||
| } | } | ||||
| }) | }) | ||||
| }) | }) | ||||
| @@ -123,7 +123,7 @@ | |||||
| <select name="pre_predict_task" id="dataset_list_auto" onchange="dataset_auto_sele_Change(this)"> | <select name="pre_predict_task" id="dataset_list_auto" onchange="dataset_auto_sele_Change(this)"> | ||||
| {{if .Attachments}} | {{if .Attachments}} | ||||
| {{range .Attachments}} | {{range .Attachments}} | ||||
| <option value="{{.UUID}}">{{.Name}}</option> | |||||
| <option value="{{.UUID}}">{{.Name}}</option> | |||||
| {{end}} | {{end}} | ||||
| {{end}} | {{end}} | ||||
| </select> | </select> | ||||
| @@ -176,13 +176,13 @@ | |||||
| <label for="CloudBrain">{{$.i18n.Tr "repo.cloudbrain_platform_selection"}}</label> | <label for="CloudBrain">{{$.i18n.Tr "repo.cloudbrain_platform_selection"}}</label> | ||||
| <div class="field"> | <div class="field"> | ||||
| <div class="ui radio checkbox"> | <div class="ui radio checkbox"> | ||||
| <input type="radio" name="CloudBrain" checked tabindex="0" class="hidden" value="0"> | |||||
| <input type="radio" name="CloudBrainSelect" checked tabindex="0" class="hidden" value="0"> | |||||
| <label>{{$.i18n.Tr "repo.cloudbrain1"}}</label> | <label>{{$.i18n.Tr "repo.cloudbrain1"}}</label> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="field"> | <div class="field"> | ||||
| <div class="ui radio checkbox"> | <div class="ui radio checkbox"> | ||||
| <input type="radio" name="CloudBrain" tabindex="0" class="hidden" value="1"> | |||||
| <input type="radio" name="CloudBrainSelect" tabindex="0" class="hidden" value="1"> | |||||
| <label>{{$.i18n.Tr "repo.cloudbrain2"}}</label> | <label>{{$.i18n.Tr "repo.cloudbrain2"}}</label> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| @@ -209,14 +209,16 @@ | |||||
| $('.ui.radio.checkbox').checkbox(); | $('.ui.radio.checkbox').checkbox(); | ||||
| var repolink = $(".cloudbrain_link").text() | var repolink = $(".cloudbrain_link").text() | ||||
| console.log(repolink) | |||||
| $(".ui.positive.right.icon.button").click(function(){ | $(".ui.positive.right.icon.button").click(function(){ | ||||
| // 声明一个变量来接收以及获取单选框选择的情况 | // 声明一个变量来接收以及获取单选框选择的情况 | ||||
| var checked_radio = $("input[type='radio']:checked").val() | |||||
| var checked_radio = $("input[name='CloudBrainSelect']:checked").val() | |||||
| console.log(checked_radio) | |||||
| if(checked_radio=='0'){ | if(checked_radio=='0'){ | ||||
| window.location.href = repolink+'/cloudbrain' | window.location.href = repolink+'/cloudbrain' | ||||
| }else if(checked_radio=='1'){ | }else if(checked_radio=='1'){ | ||||
| window.location.href = repolink+'/modelarts' | |||||
| window.location.href = repolink+'/modelarts/notebook' | |||||
| }else{ | }else{ | ||||
| return; | return; | ||||
| } | } | ||||
| @@ -202,24 +202,29 @@ | |||||
| <div class="ui container"> | <div class="ui container"> | ||||
| <!-- 中间云脑和新建任务按钮 --> | <!-- 中间云脑和新建任务按钮 --> | ||||
| <div class="ui three column stack able grid"> | |||||
| <div class="column"> | |||||
| <h2>{{.i18n.Tr "repo.cloudbrain2"}}</h2> | |||||
| </div> | |||||
| <div class="column"> | |||||
| </div> | |||||
| <div class="column right aligned"> | |||||
| {{if .Permission.CanWrite $.UnitTypeCloudBrain}} | |||||
| <a class="ui green button" href="{{.RepoLink}}/modelarts/create">{{.i18n.Tr "repo.cloudbrain.new"}}</a> {{end}} | |||||
| </div> | |||||
| </div> | |||||
| <div class="ui two column stackable grid "> | |||||
| <p>使用鹏城云脑计算资源进行调试,云脑1提供CPU / GPU资源,云脑2提供Ascend NPU资源;调试使用的数据集也需要上传到对应的环境。</p> | |||||
| <div class="ui blue mini menu selectcloudbrain"> | |||||
| <a class="item" href="{{.RepoLink}}/cloudbrain">{{svg "octicon-server" 16}} CPU / GPU</a> | |||||
| <a class="active item" href="{{.RepoLink}}/modelarts">{{svg "octicon-server" 16}} Ascend NPU</a> | |||||
| <div class="column"> | |||||
| <div class="ui blue small menu compact selectcloudbrain"> | |||||
| <a class="active item" href="{{.RepoLink}}/modelarts/notebook">调试任务</a> | |||||
| <a class="item" href="{{.RepoLink}}/modelarts/train-job">训练任务</a> | |||||
| </div> | |||||
| </div> | |||||
| <div class="column right aligned"> | |||||
| <div class="ui selection dropdown" style="min-width: 10em;min-height:2.6em;border-radius: .28571429rem;margin-right: 1em;padding: .67em 3.2em .7em 1em;"> | |||||
| {{svg "octicon-server" 16}} | |||||
| <div class="default text" style="color: rgba(0,0,0,.87);"> Ascend NPU</div> | |||||
| <i class="dropdown icon"></i> | |||||
| <div class="menu"> | |||||
| <a class="item" href="{{.RepoLink}}/cloudbrain" data-value="11">CPU / GPU</a> | |||||
| <a class="item" href="{{.RepoLink}}/modelarts" data-value="22">Ascend NPU</a> | |||||
| </div> | |||||
| </div> | |||||
| <a class="ui green button" href="{{.RepoLink}}/modelarts/create">新建调试任务</a> | |||||
| </div> | |||||
| </div> | </div> | ||||
| <!-- 中下列表展示区 --> | <!-- 中下列表展示区 --> | ||||
| @@ -410,7 +415,7 @@ | |||||
| $(".job-status").each((index, job) => { | $(".job-status").each((index, job) => { | ||||
| const jobID = job.dataset.jobid; | const jobID = job.dataset.jobid; | ||||
| const repoPath = job.dataset.repopath; | const repoPath = job.dataset.repopath; | ||||
| if (job.textContent.trim() == 'STOPPED') { | |||||
| if (job.textContent.trim() == 'STOPPED' || job.textContent.trim() == 'START_FAILED' || job.textContent.trim() == 'CREATE_FAILED') { | |||||
| return | return | ||||
| } | } | ||||
| @@ -0,0 +1,43 @@ | |||||
| <style> | |||||
| .dis { | |||||
| margin-bottom: 20px; | |||||
| } | |||||
| .disabled { | |||||
| cursor: pointer; | |||||
| pointer-events: none; | |||||
| } | |||||
| .ui.segment.bottom.attached { | |||||
| border: none; | |||||
| } | |||||
| .ui.secondary.vertical.pointing.menu{ | |||||
| border-right-width: 0px !important; | |||||
| } | |||||
| .vertical.menu .item { | |||||
| border-right-color: white !important; | |||||
| } | |||||
| .vertical.menu .activate.item { | |||||
| font-weight: 700; | |||||
| } | |||||
| </style> | |||||
| <div class="three wide column"> | |||||
| <div class="ui grid"> | |||||
| <div class="sixteen wide column ui secondary sticky pointing tabular vertical menu"> | |||||
| <a class="{{if .PageIsNotebook}}active{{end}} item" href="{{.RepoLink}}/modelarts/notebook"> | |||||
| {{svg "octicon-repo" 16}} {{.i18n.Tr "repo.modelarts.notebook"}} | |||||
| </a> | |||||
| <a class="{{if .PageIsTrainJob}}active{{end}} item" href="{{.RepoLink}}/modelarts/train-job"> | |||||
| {{svg "octicon-inbox" 16}} {{.i18n.Tr "repo.modelarts.train_job"}} | |||||
| </a> | |||||
| <!-- | |||||
| <a class="{{if .PageIsParaManage}}active{{end}} item" href="{{.RepoLink}}/modelarts/train-job/para-manage"> | |||||
| {{svg "octicon-inbox" 16}} {{.i18n.Tr "repo.modelarts.train_job_para_admin"}} | |||||
| </a> | |||||
| --> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| @@ -100,7 +100,7 @@ | |||||
| <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" .}} | ||||
| <div class="ui positive message" id="messageInfo"> | |||||
| <div class="ui negative message" id="messageInfo"> | |||||
| <p></p> | <p></p> | ||||
| </div> | </div> | ||||
| <form class="ui form" id="form_id" action="{{.Link}}" method="post"> | <form class="ui form" id="form_id" action="{{.Link}}" method="post"> | ||||
| @@ -179,11 +179,11 @@ | |||||
| let value_task = $("input[name='job_name']").val() | let value_task = $("input[name='job_name']").val() | ||||
| let re = /^[a-z0-9][a-z0-9-_]{1,36}$/ | |||||
| let re = /^[a-z0-9][a-z0-9-_]{1,34}[a-z0-9-]$/ | |||||
| let flag = re.test(value_task) | let flag = re.test(value_task) | ||||
| if(!flag){ | if(!flag){ | ||||
| $('#messageInfo').css('display','block') | $('#messageInfo').css('display','block') | ||||
| let str = '只能以小写字母或数字开头且只包含小写字母、数字、_和-、最长36个字符。' | |||||
| let str = '只能以小写字母或数字开头且只包含小写字母、数字、_和-,不能以_结尾,最长36个字符。' | |||||
| $('#messageInfo p').text(str) | $('#messageInfo p').text(str) | ||||
| return false | return false | ||||
| } | } | ||||
| @@ -0,0 +1,551 @@ | |||||
| <!-- 头部导航栏 --> | |||||
| {{template "base/head" .}} | |||||
| <style> | |||||
| .selectcloudbrain .active.item{ | |||||
| color: #0087f5 !important; | |||||
| border: 1px solid #0087f5; | |||||
| margin: -1px; | |||||
| background: #FFF !important; | |||||
| } | |||||
| #deletemodel { | |||||
| width: 100%; | |||||
| height: 100%; | |||||
| } | |||||
| /* 弹窗 */ | |||||
| #mask { | |||||
| position: fixed; | |||||
| top: 0px; | |||||
| left: 0px; | |||||
| right: 0px; | |||||
| bottom: 0px; | |||||
| filter: alpha(opacity=60); | |||||
| background-color: #777; | |||||
| z-index: 1000; | |||||
| display: none; | |||||
| opacity: 0.8; | |||||
| -moz-opacity: 0.5; | |||||
| padding-top: 100px; | |||||
| color: #000000 | |||||
| } | |||||
| #loadingPage { | |||||
| margin: 200px auto; | |||||
| width: 50px; | |||||
| height: 40px; | |||||
| text-align: center; | |||||
| font-size: 10px; | |||||
| display: block; | |||||
| } | |||||
| #loadingPage>div { | |||||
| background-color: green; | |||||
| height: 100%; | |||||
| width: 6px; | |||||
| display: inline-block; | |||||
| -webkit-animation: sk-stretchdelay 1.2s infinite ease-in-out; | |||||
| animation: sk-stretchdelay 1.2s infinite ease-in-out; | |||||
| } | |||||
| #loadingPage .rect2 { | |||||
| -webkit-animation-delay: -1.1s; | |||||
| animation-delay: -1.1s; | |||||
| } | |||||
| #loadingPage .rect3 { | |||||
| -webkit-animation-delay: -1.0s; | |||||
| animation-delay: -1.0s; | |||||
| } | |||||
| #loadingPage .rect4 { | |||||
| -webkit-animation-delay: -0.9s; | |||||
| animation-delay: -0.9s; | |||||
| } | |||||
| #loadingPage .rect5 { | |||||
| -webkit-animation-delay: -0.8s; | |||||
| animation-delay: -0.8s; | |||||
| } | |||||
| @-webkit-keyframes sk-stretchdelay { | |||||
| 0%, | |||||
| 40%, | |||||
| 100% { | |||||
| -webkit-transform: scaleY(0.4) | |||||
| } | |||||
| 20% { | |||||
| -webkit-transform: scaleY(1.0) | |||||
| } | |||||
| } | |||||
| @keyframes sk-stretchdelay { | |||||
| 0%, | |||||
| 40%, | |||||
| 100% { | |||||
| transform: scaleY(0.4); | |||||
| -webkit-transform: scaleY(0.4); | |||||
| } | |||||
| 20% { | |||||
| transform: scaleY(1.0); | |||||
| -webkit-transform: scaleY(1.0); | |||||
| } | |||||
| } | |||||
| /* 消息框 */ | |||||
| .alert { | |||||
| display: none; | |||||
| position: fixed; | |||||
| width: 100%; | |||||
| z-index: 1001; | |||||
| padding: 15px; | |||||
| border: 1px solid transparent; | |||||
| border-radius: 4px; | |||||
| text-align: center; | |||||
| font-weight: bold; | |||||
| } | |||||
| .alert-success { | |||||
| color: #3c763d; | |||||
| background-color: #dff0d8; | |||||
| border-color: #d6e9c6; | |||||
| } | |||||
| .alert-info { | |||||
| color: #31708f; | |||||
| background-color: #d9edf7; | |||||
| border-color: #bce8f1; | |||||
| } | |||||
| .alert-warning { | |||||
| color: #8a6d3b; | |||||
| background-color: #fcf8e3; | |||||
| border-color: #faebcc; | |||||
| } | |||||
| .alert-danger { | |||||
| color: #a94442; | |||||
| background-color: #f2dede; | |||||
| border-color: #ebccd1; | |||||
| } | |||||
| .pusher { | |||||
| width: calc(100% - 260px); | |||||
| box-sizing: border-box; | |||||
| } | |||||
| /* 弹窗 (background) */ | |||||
| #imageModal { | |||||
| display: none; | |||||
| position: fixed; | |||||
| z-index: 1; | |||||
| left: 0; | |||||
| top: 0; | |||||
| width: 100%; | |||||
| height: 100%; | |||||
| overflow: auto; | |||||
| background-color: rgb(0, 0, 0); | |||||
| background-color: rgba(0, 0, 0, 0.4); | |||||
| } | |||||
| /* 弹窗内容 */ | |||||
| .modal-content { | |||||
| background-color: #fefefe; | |||||
| margin: 15% auto; | |||||
| padding: 20px; | |||||
| border: 1px solid #888; | |||||
| width: 30%; | |||||
| } | |||||
| /* 关闭按钮 */ | |||||
| .close { | |||||
| color: #aaa; | |||||
| float: right; | |||||
| font-size: 28px; | |||||
| font-weight: bold; | |||||
| } | |||||
| .close:hover, | |||||
| .close:focus { | |||||
| color: black; | |||||
| text-decoration: none; | |||||
| cursor: pointer; | |||||
| } | |||||
| .dis { | |||||
| margin-bottom: 20px; | |||||
| } | |||||
| .disabled { | |||||
| cursor: pointer; | |||||
| pointer-events: none; | |||||
| } | |||||
| </style> | |||||
| <!-- 弹窗 --> | |||||
| <div id="mask"> | |||||
| <div id="loadingPage"> | |||||
| <div class="rect1"></div> | |||||
| <div class="rect2"></div> | |||||
| <div class="rect3"></div> | |||||
| <div class="rect4"></div> | |||||
| <div class="rect5"></div> | |||||
| </div> | |||||
| </div> | |||||
| <!-- 提示框 --> | |||||
| <div class="alert"></div> | |||||
| <div class="repository release dataset-list view"> | |||||
| {{template "repo/header" .}} | |||||
| <!-- 列表容器 --> | |||||
| <div class="ui container"> | |||||
| {{template "base/alert" .}} | |||||
| <!-- 中间云脑和新建任务按钮 --> | |||||
| <div class="ui two column stackable grid "> | |||||
| <div class="column"> | |||||
| <div class="ui blue small menu compact selectcloudbrain"> | |||||
| <a class="active item" href="{{.RepoLink}}/modelarts/notebook">{{$.i18n.Tr "repo.modelarts.notebook"}}</a> | |||||
| <a class="item" href="{{.RepoLink}}/modelarts/train-job">{{$.i18n.Tr "repo.modelarts.train_job"}}</a> | |||||
| </div> | |||||
| </div> | |||||
| <div class="column right aligned"> | |||||
| <div class="ui selection dropdown" style="min-width: 10em;min-height:2.6em;border-radius: .28571429rem;margin-right: 1em;padding: .67em 3.2em .7em 1em;"> | |||||
| {{svg "octicon-server" 16}} | |||||
| <div class="default text" style="color: rgba(0,0,0,.87);"> Ascend NPU</div> | |||||
| <i class="dropdown icon"></i> | |||||
| <div class="menu"> | |||||
| <a class="item" href="{{.RepoLink}}/cloudbrain" data-value="11">CPU / GPU</a> | |||||
| <a class="item" href="{{.RepoLink}}/modelarts/notebook" data-value="22">Ascend NPU</a> | |||||
| </div> | |||||
| </div> | |||||
| {{if .Permission.CanWrite $.UnitTypeCloudBrain}} | |||||
| <a class="ui green button" href="{{.RepoLink}}/modelarts/notebook/create">{{$.i18n.Tr "repo.modelarts.train_job.new_debug"}}</a>{{end}} | |||||
| </div> | |||||
| </div> | |||||
| <!-- 中下列表展示区 --> | |||||
| <div class="ui grid"> | |||||
| <div class="row"> | |||||
| <div class="ui sixteen wide column"> | |||||
| <!-- 排序区 --> | |||||
| <!-- <div class="ui sixteen wide column"> | |||||
| <div class="ui two column stackable grid"> | |||||
| <div class="column"> | |||||
| </div> | |||||
| <div class="column right aligned"> | |||||
| <div class="ui right dropdown type jump item"> | |||||
| <span class="text"> | |||||
| {{.i18n.Tr "repo.issues.filter_sort"}}<i class="dropdown icon"></i> | |||||
| </span> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> --> | |||||
| <!-- 任务展示 --> | |||||
| <div class="dataset list"> | |||||
| <!-- 表头 --> | |||||
| <div class="ui grid stackable" style="background: #f0f0f0;;"> | |||||
| <div class="row"> | |||||
| <div class="six wide column"> | |||||
| <span style="margin:0 6px">{{$.i18n.Tr "repo.cloudbrain_task"}}</span> | |||||
| </div> | |||||
| <div class="three wide column"> | |||||
| <span>{{$.i18n.Tr "repo.cloudbrain_status_createtime"}}</span> | |||||
| </div> | |||||
| <div class="two wide column text center"> | |||||
| <span>{{$.i18n.Tr "repo.cloudbrain_creator"}}</span> | |||||
| </div> | |||||
| <div class="five wide column text center"> | |||||
| <span style="margin-left: 5rem;">{{$.i18n.Tr "repo.cloudbrain_operate"}}</span> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| {{range .Tasks}} | |||||
| <div class="ui grid stackable item"> | |||||
| <div class="row"> | |||||
| <!-- 任务名 --> | |||||
| <div class="six wide column"> | |||||
| <a class="title" href="{{$.Link}}/{{.JobID}}" title="{{.JobName}}" style="font-size: 15px;"> | |||||
| <span class="fitted" style="vertical-align: middle;">{{svg "octicon-tasklist" 16}}</span> | |||||
| <span class="fitted" style="width: 90%;vertical-align: middle;margin-left: 0.4rem;">{{.JobName}}</span> | |||||
| </a> | |||||
| </div> | |||||
| <div class="three wide column"> | |||||
| <!--任务状态 --> | |||||
| <!-- <span class="ui compact button job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}"> | |||||
| {{.Status}} | |||||
| </span> --> | |||||
| <span class="job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}"> | |||||
| <span><i id="{{.JobID}}-icon" style="vertical-align: middle;" class="{{.Status}}"></i><span id="{{.JobID}}-text" style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span> | |||||
| </span> | |||||
| <!-- 任务创建时间 --> | |||||
| <span style="font-size: 12px;margin-left: 0.4rem;" class="">{{TimeSinceUnix .Cloudbrain.CreatedUnix $.Lang}}</span> | |||||
| </div> | |||||
| <div class="two wide column text center"> | |||||
| {{if .User.Name}} | |||||
| <a href="{{AppSubUrl}}/{{.User.Name}}" title="{{.User.Name}}"><img class="ui avatar image" src="{{.User.RelAvatarLink}}"></a> | |||||
| {{else}} | |||||
| <a title="Ghost"><img class="ui avatar image" src="{{AppSubUrl}}/user/avatar/Ghost/-1"></a> | |||||
| {{end}} | |||||
| </div> | |||||
| <div class="five wide column text right"> | |||||
| <div class="ui compact buttons"> | |||||
| <!-- <a class="ui basic blue button" href="{{$.Link}}/{{.JobID}}"> | |||||
| 查看 | |||||
| </a> --> | |||||
| {{if $.Permission.CanWrite $.UnitTypeCloudBrain}} | |||||
| <a id="model-debug-{{.JobID}}" style="margin-right: 2rem;" class="ui basic {{if not .CanDebug}}disabled {{else}}blue {{end}}button" href="{{$.Link}}/{{.JobID}}/debug" target="_blank"> | |||||
| {{$.i18n.Tr "repo.debug"}} | |||||
| </a> | |||||
| {{else}} | |||||
| <a style="margin-right: 2rem;" class="ui basic disabled button" href="{{$.Link}}/{{.JobID}}/debug" target="_blank"> | |||||
| {{$.i18n.Tr "repo.debug"}} | |||||
| </a> | |||||
| {{end}} | |||||
| <form id="stopForm-{{.JobID}}" action="{{$.Link}}/{{.JobID}}/stop" method="post" style="margin-left:-1px;"> | |||||
| {{$.CsrfTokenHtml}} | |||||
| {{if $.Permission.CanWrite $.UnitTypeCloudBrain}} | |||||
| <a id="stop-model-debug-{{.JobID}}" style="margin-right: 2rem;" class="ui basic {{if eq .Status "STOPPED" "FAILED" "START_FAILED" "STOPPING" "CREATING" "WAITING" "STARTING"}}disabled {{else}}blue {{end}}button" onclick="document.getElementById('stopForm-{{.JobID}}').submit();"> | |||||
| {{$.i18n.Tr "repo.stop"}} | |||||
| </a> | |||||
| {{else}} | |||||
| <a style="margin-right: 2rem;" class="ui basic disabled button" onclick="document.getElementById('stopForm-{{.JobID}}').submit();"> | |||||
| {{$.i18n.Tr "repo.stop"}} | |||||
| </a> | |||||
| {{end}} | |||||
| </form> | |||||
| </div> | |||||
| <!-- <div class="ui compact buttons" style="margin-right:10px;"> --> | |||||
| <!-- 模型下载 --> | |||||
| <!-- <a class="ui basic blue button" href="{{$.Link}}/{{.JobID}}/models" target="_blank"> | |||||
| 模型下载 | |||||
| </a> --> | |||||
| <!-- 接收结果 --> | |||||
| <!-- <iframe src="" frameborder="0" name="iframeContent" style="display: none;"></iframe> | |||||
| <a class="imageBtn ui basic {{if not .CanDebug}}disabled {{else}}blue {{end}}button" value="{{.CanDebug}}">提交镜像</a> --> | |||||
| <!-- </div> --> | |||||
| <!-- 删除任务 --> | |||||
| <form class="ui compact buttons" id="delForm-{{.JobID}}" action="{{$.Link}}/{{.JobID}}/del" method="post"> | |||||
| {{$.CsrfTokenHtml}} | |||||
| {{if $.Permission.CanWrite $.UnitTypeCloudBrain}} | |||||
| <a id="model-delete-{{.JobID}}" class="ui compact {{if eq .Status "RUNNING" "CREATING" "WAITING" "STARTING" "STOPPING" }}disabled {{else}}red {{end}}button" onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | |||||
| {{$.i18n.Tr "repo.delete"}} | |||||
| </a> | |||||
| {{else}} | |||||
| <a class="ui compact disabled button" onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | |||||
| {{$.i18n.Tr "repo.delete"}} | |||||
| </a> | |||||
| {{end}} | |||||
| </form> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| {{end}} {{template "base/paginate" .}} | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <!-- 确认模态框 --> | |||||
| <div id="deletemodel"> | |||||
| <div class="ui basic modal"> | |||||
| <div class="ui icon header"> | |||||
| <i class="trash icon"></i> 删除任务 | |||||
| </div> | |||||
| <div class="content"> | |||||
| <p>你确认删除该任务么?此任务一旦删除不可恢复。</p> | |||||
| </div> | |||||
| <div class="actions"> | |||||
| <div class="ui red basic inverted cancel button"> | |||||
| <i class="remove icon"></i> 取消操作 | |||||
| </div> | |||||
| <div class="ui green basic inverted ok button"> | |||||
| <i class="checkmark icon"></i> 确定操作 | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| {{template "base/footer" .}} | |||||
| <script> | |||||
| // 调试和评分新开窗口 | |||||
| function stop(obj) { | |||||
| if (obj.style.color != "rgb(204, 204, 204)") { | |||||
| obj.target = '_blank' | |||||
| } else { | |||||
| return | |||||
| } | |||||
| } | |||||
| // 删除时用户确认 | |||||
| function assertDelete(obj) { | |||||
| if (obj.style.color == "rgb(204, 204, 204)") { | |||||
| return | |||||
| } else { | |||||
| var delId = obj.parentNode.id | |||||
| flag = 1; | |||||
| $('.ui.basic.modal') | |||||
| .modal({ | |||||
| onDeny: function() { | |||||
| flag = false | |||||
| }, | |||||
| onApprove: function() { | |||||
| document.getElementById(delId).submit() | |||||
| flag = true | |||||
| }, | |||||
| onHidden: function() { | |||||
| if (flag == false) { | |||||
| $('.alert').html('您已取消操作').removeClass('alert-success').addClass('alert-danger').show().delay(1500).fadeOut(); | |||||
| } | |||||
| } | |||||
| }) | |||||
| .modal('show') | |||||
| } | |||||
| } | |||||
| // 加载任务状态 | |||||
| var timeid = window.setInterval(loadJobStatus, 15000); | |||||
| $(document).ready(loadJobStatus); | |||||
| function loadJobStatus() { | |||||
| $(".job-status").each((index, job) => { | |||||
| const jobID = job.dataset.jobid; | |||||
| const repoPath = job.dataset.repopath; | |||||
| if (job.textContent.trim() == 'STOPPED' || job.textContent.trim() == 'START_FAILED' || job.textContent.trim() == 'CREATE_FAILED') { | |||||
| return | |||||
| } | |||||
| $.get(`/api/v1/repos/${repoPath}/modelarts/notebook/${jobID}`, (data) => { | |||||
| const jobID = data.JobID | |||||
| const status = data.JobStatus | |||||
| if (status != job.textContent.trim()) { | |||||
| $('#' + jobID+'-icon').removeClass().addClass(status) | |||||
| $('#' + jobID+ '-text').text(status) | |||||
| //if (status == 'STOPPED') { | |||||
| // window.location.reload() | |||||
| //} | |||||
| } | |||||
| if(status==="RUNNING"){ | |||||
| $('#model-debug-'+jobID).removeClass('disabled') | |||||
| $('#model-debug-'+jobID).addClass('blue') | |||||
| } | |||||
| if(status!=="RUNNING"){ | |||||
| $('#model-debug-'+jobID).removeClass('blue') | |||||
| $('#model-debug-'+jobID).addClass('disabled') | |||||
| } | |||||
| if(status!=="STOPPED" || status!=="FAILED"){ | |||||
| // $('#stop-model-debug-'+jobID).removeClass('disabled') | |||||
| // $('#stop-model-debug-'+jobID).addClass('blue') | |||||
| // $('#model-delete-'+jobID).removeClass('red') | |||||
| // $('#model-delete-'+jobID).addClass('disabled') | |||||
| } | |||||
| if(status=="STOPPED" || status=="FAILED" ){ | |||||
| $('#stop-model-debug-'+jobID).removeClass('blue') | |||||
| $('#stop-model-debug-'+jobID).addClass('disabled') | |||||
| $('#model-delete-'+jobID).removeClass('disabled') | |||||
| $('#model-delete-'+jobID).addClass('red') | |||||
| } | |||||
| if(status=="START_FAILED"){ | |||||
| $('#stop-model-debug-'+jobID).removeClass('blue') | |||||
| $('#stop-model-debug-'+jobID).addClass('disabled') | |||||
| $('#model-delete-'+jobID).removeClass('disabled') | |||||
| $('#model-delete-'+jobID).addClass('red') | |||||
| } | |||||
| }).fail(function(err) { | |||||
| console.log(err); | |||||
| }); | |||||
| }); | |||||
| }; | |||||
| // 获取弹窗 | |||||
| var modal = document.getElementById('imageModal'); | |||||
| // 打开弹窗的按钮对象 | |||||
| var btns = document.getElementsByClassName("imageBtn"); | |||||
| // 获取 <span> 元素,用于关闭弹窗 | |||||
| var spans = document.getElementsByClassName('close'); | |||||
| // 点击按钮打开弹窗 | |||||
| for (i = 0; i < btns.length; i++) { | |||||
| btns[i].onclick = function() { | |||||
| modal.style.display = "block"; | |||||
| } | |||||
| } | |||||
| // 点击 <span> (x), 关闭弹窗 | |||||
| for (i = 0; i < spans.length; i++) { | |||||
| spans[i].onclick = function() { | |||||
| modal.style.display = "none"; | |||||
| } | |||||
| } | |||||
| // 在用户点击其他地方时,关闭弹窗 | |||||
| window.onclick = function(event) { | |||||
| if (event.target == modal) { | |||||
| modal.style.display = "none"; | |||||
| } | |||||
| } | |||||
| // 显示弹窗,弹出相应的信息 | |||||
| function showmask() { | |||||
| $('#imageModal').css('display', 'none') | |||||
| $('#mask').css('display', 'block') | |||||
| $("iframe[name=iframeContent]").on("load", function() { | |||||
| var responseText = $("iframe")[0].contentDocument.body.getElementsByTagName("pre")[0].innerHTML; | |||||
| var json1 = JSON.parse(responseText) | |||||
| $('#mask').css('display', 'none') | |||||
| parent.location.href | |||||
| if (json1.result_code === "0") { | |||||
| $('.alert').html('操作成功!').removeClass('alert-danger').addClass('alert-success').show().delay(1500).fadeOut(); | |||||
| } else { | |||||
| $('.alert').html(json1.error_msg).removeClass('alert-success').addClass('alert-danger').show().delay(5000).fadeOut(); | |||||
| } | |||||
| }) | |||||
| } | |||||
| </script> | |||||
| @@ -0,0 +1,240 @@ | |||||
| {{template "base/head" .}} | |||||
| <style> | |||||
| /* 遮罩层css效果图 */ | |||||
| #mask { | |||||
| position: fixed; | |||||
| top: 0px; | |||||
| left: 0px; | |||||
| right: 0px; | |||||
| bottom: 0px; | |||||
| filter: alpha(opacity=60); | |||||
| background-color: #777; | |||||
| z-index: 1000; | |||||
| display: none; | |||||
| opacity: 0.8; | |||||
| -moz-opacity: 0.5; | |||||
| padding-top: 100px; | |||||
| color: #000000 | |||||
| } | |||||
| /* 加载圈css效果图 */ | |||||
| #loadingPage { | |||||
| margin: 200px auto; | |||||
| width: 50px; | |||||
| height: 40px; | |||||
| text-align: center; | |||||
| font-size: 10px; | |||||
| display: block; | |||||
| } | |||||
| #loadingPage>div { | |||||
| background-color: green; | |||||
| height: 100%; | |||||
| width: 6px; | |||||
| display: inline-block; | |||||
| -webkit-animation: sk-stretchdelay 1.2s infinite ease-in-out; | |||||
| animation: sk-stretchdelay 1.2s infinite ease-in-out; | |||||
| } | |||||
| #loadingPage .rect2 { | |||||
| -webkit-animation-delay: -1.1s; | |||||
| animation-delay: -1.1s; | |||||
| } | |||||
| #loadingPage .rect3 { | |||||
| -webkit-animation-delay: -1.0s; | |||||
| animation-delay: -1.0s; | |||||
| } | |||||
| #loadingPage .rect4 { | |||||
| -webkit-animation-delay: -0.9s; | |||||
| animation-delay: -0.9s; | |||||
| } | |||||
| #loadingPage .rect5 { | |||||
| -webkit-animation-delay: -0.8s; | |||||
| animation-delay: -0.8s; | |||||
| } | |||||
| @-webkit-keyframes sk-stretchdelay { | |||||
| 0%, | |||||
| 40%, | |||||
| 100% { | |||||
| -webkit-transform: scaleY(0.4) | |||||
| } | |||||
| 20% { | |||||
| -webkit-transform: scaleY(1.0) | |||||
| } | |||||
| } | |||||
| @keyframes sk-stretchdelay { | |||||
| 0%, | |||||
| 40%, | |||||
| 100% { | |||||
| transform: scaleY(0.4); | |||||
| -webkit-transform: scaleY(0.4); | |||||
| } | |||||
| 20% { | |||||
| transform: scaleY(1.0); | |||||
| -webkit-transform: scaleY(1.0); | |||||
| } | |||||
| } | |||||
| .inline.required.field.cloudbrain_benchmark { | |||||
| display: none; | |||||
| } | |||||
| </style> | |||||
| <div id="mask"> | |||||
| <div id="loadingPage"> | |||||
| <div class="rect1"></div> | |||||
| <div class="rect2"></div> | |||||
| <div class="rect3"></div> | |||||
| <div class="rect4"></div> | |||||
| <div class="rect5"></div> | |||||
| </div> | |||||
| </div> | |||||
| <div class="repository"> | |||||
| {{template "repo/header" .}} | |||||
| <div class="repository new repo ui middle very relaxed page grid"> | |||||
| <div class="column"> | |||||
| {{template "base/alert" .}} | |||||
| <div class="ui positive message" id="messageInfo"> | |||||
| <p></p> | |||||
| </div> | |||||
| <form class="ui form" id="form_id" action="{{.Link}}" method="post"> | |||||
| {{.CsrfTokenHtml}} | |||||
| <h3 class="ui top attached header"> | |||||
| {{.i18n.Tr "repo.cloudbrain.new"}} | |||||
| </h3> | |||||
| <div class="ui attached segment"> | |||||
| <!-- <br> --> | |||||
| <div class="inline required field"> | |||||
| <label>任务名称</label> | |||||
| <input name="job_name" id="cloudbrain_job_name" placeholder="任务名称" value="{{.job_name}}" tabindex="3" autofocus required maxlength="255"> | |||||
| </div> | |||||
| <div class="inline field"> | |||||
| <label>数据集</label> | |||||
| <input type="text" list="cloudbrain_dataset" placeholder="选择数据集" name="" id="answerInput" autofocus maxlength="36"> | |||||
| <datalist id="cloudbrain_dataset" class="ui search" style='width:385px' name="attachment"> | |||||
| {{range .attachments}} | |||||
| <option name="attachment" data-value="{{.UUID}}">{{.Attachment.Name}}</option> | |||||
| {{end}} | |||||
| </datalist> | |||||
| <input type="hidden" name="attachment" id="answerInput-hidden"> | |||||
| </div> | |||||
| <div class="inline required field"> | |||||
| <label>工作环境</label> | |||||
| <input name="de" id="cloudbrain_de" value="{{.env}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | |||||
| </div> | |||||
| <div class="inline required field"> | |||||
| <label>类型</label> | |||||
| <input name="job_type" id="cloudbrain_job_type" value="{{.notebook_type}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | |||||
| </div> | |||||
| <div class="inline required field"> | |||||
| <label>规格</label> | |||||
| <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 class="inline required field"> | |||||
| <label>数据集存放路径</label> | |||||
| <input name="dataset_path" id="cloudbrain_dataset_path" value="{{.dataset_path}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | |||||
| </div> | |||||
| <div class="inline field"> | |||||
| <label>描述</label> | |||||
| <input name="description" id="cloudbrain_description" tabindex="3" autofocus maxlength="255"> | |||||
| </div> | |||||
| <div class="inline field"> | |||||
| <label></label> | |||||
| <button class="ui green button"> | |||||
| {{.i18n.Tr "repo.cloudbrain.new"}} | |||||
| </button> | |||||
| <a class="ui button" href="/">{{.i18n.Tr "repo.cloudbrain.cancel"}}</a> | |||||
| </div> | |||||
| </div> | |||||
| </form> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| {{template "base/footer" .}} | |||||
| <script> | |||||
| // 取消创建跳转 | |||||
| let url_href = window.location.pathname.split('create')[0] | |||||
| $(".ui.button").attr('href',url_href) | |||||
| // 判断必填选项是否填写正确 | |||||
| let form = document.getElementById('form_id'); | |||||
| $('#messageInfo').css('display','none') | |||||
| form.onsubmit = function(e){ | |||||
| let value_task = $("input[name='job_name']").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 | |||||
| } | |||||
| let min_value_task = value_task.toLowerCase() | |||||
| $("input[name='job_name']").attr("value",min_value_task) | |||||
| document.getElementById("mask").style.display = "block" | |||||
| } | |||||
| // 点击按钮后遮罩层显示 | |||||
| // function showmask() { | |||||
| // document.getElementById("mask").style.display = "block" | |||||
| // } | |||||
| // 页面加载完毕后遮罩层隐藏 | |||||
| document.onreadystatechange = function() { | |||||
| if (document.readyState === "complete") { | |||||
| document.getElementById("mask").style.display = "none" | |||||
| } | |||||
| } | |||||
| $('select.dropdown') | |||||
| .dropdown(); | |||||
| $(function() { | |||||
| $("#cloudbrain_job_type").change(function() { | |||||
| if ($(this).val() == 'BENCHMARK') { | |||||
| $(".cloudbrain_benchmark").show(); | |||||
| } else { | |||||
| $(".cloudbrain_benchmark").hide(); | |||||
| } | |||||
| }) | |||||
| }) | |||||
| document.querySelector('input[list]').addEventListener('input',function(e){ | |||||
| var input = e.target, | |||||
| list = input.getAttribute('list'), | |||||
| options = document.querySelectorAll('#'+list+' option'), | |||||
| hiddenInput = document.getElementById(input.getAttribute('id')+'-hidden'), | |||||
| inputValue = input.value; | |||||
| hiddenInput.value = inputValue; | |||||
| for (let i=0;i<options.length;i++){ | |||||
| var option = options[i] | |||||
| if(option.innerText===inputValue){ | |||||
| hiddenInput.value = option.getAttribute('data-value'); | |||||
| break | |||||
| } | |||||
| } | |||||
| }) | |||||
| </script> | |||||
| @@ -0,0 +1,122 @@ | |||||
| {{template "base/head" .}} | |||||
| <div class="repository"> | |||||
| {{template "repo/header" .}} | |||||
| <div class="repository new repo ui middle very relaxed page grid"> | |||||
| <div class="column"> | |||||
| {{template "base/alert" .}} | |||||
| <h4 class="ui header" id="vertical-segment"> | |||||
| <a href="javascript:window.history.back();"><i class="arrow left icon"></i>返回</a> | |||||
| </h4> | |||||
| <div> | |||||
| <div class="ui yellow segment"> | |||||
| {{with .task}} | |||||
| <p>任务名称: {{.JobName}}</p> | |||||
| {{end}} | |||||
| </div> | |||||
| <div class="ui green segment"> | |||||
| <p>任务结果:</p> | |||||
| {{with .result}} | |||||
| <table class="ui celled striped table"> | |||||
| <tbody> | |||||
| <tr> | |||||
| <td class="four wide"> 状态 </td> | |||||
| <td> {{.Status}} </td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td> 开始时间 </td> | |||||
| <td>{{.CreateTime}}</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td> 最后更新时间 </td> | |||||
| <td>{{.LatestUpdateTime}}</td> | |||||
| </tr> | |||||
| </tbody> | |||||
| </table> | |||||
| {{end}} | |||||
| </div> | |||||
| <div class="ui blue segment"> | |||||
| {{with .result}} | |||||
| <table class="ui celled striped table"> | |||||
| <thead> | |||||
| <tr> <th colspan="2"> 配置信息 </th> </tr> | |||||
| </thead> | |||||
| <tbody> | |||||
| <tr> | |||||
| <td class="four wide"> 开发环境类型 </td> | |||||
| <td>{{.Profile.DeType}}</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td> 硬件类型 </td> | |||||
| <td>{{.Profile.FlavorType}}</td> | |||||
| </tr> | |||||
| </tbody> | |||||
| </table> | |||||
| <table class="ui celled striped table"> | |||||
| <thead> | |||||
| <tr> <th colspan="2"> 机器规格详情 </th> </tr> | |||||
| </thead> | |||||
| <tbody> | |||||
| <tr> | |||||
| <td class="four wide"> 机器规格 </td> | |||||
| <td> {{.Flavor}} </td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td> 规格名称 </td> | |||||
| <td>{{.FlavorDetails.Name}}</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td> 规格销售状态 </td> | |||||
| <td>{{.FlavorDetails.Status}}</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td> 排队个数 </td> | |||||
| <td>{{.FlavorDetails.QueuingNum}}</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td> 排到队的剩余时间(秒) </td> | |||||
| <td>{{.FlavorDetails.QueueLeftTime}}</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td> 自动停止时间(秒) </td> | |||||
| <td>{{.FlavorDetails.Duration}}</td> | |||||
| </tr> | |||||
| </tbody> | |||||
| </table> | |||||
| <table class="ui celled striped table" {{if eq .QueuingInfo.RemainTime 0}}hidden{{end}}> | |||||
| <thead> | |||||
| <tr> <th colspan="2"> 排队信息 </th> </tr> | |||||
| </thead> | |||||
| <tbody> | |||||
| <tr> | |||||
| <td> 实例状态 </td> | |||||
| <td>{{.QueuingInfo.Status}}</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td> 实例排队的开始时间 </td> | |||||
| <td>{{.QueuingInfo.BeginTime}}</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td> 排到队的剩余时间(秒) </td> | |||||
| <td>{{.QueuingInfo.RemainTime}}</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td> 实例排队的预计停止时间 </td> | |||||
| <td>{{.QueuingInfo.EndTime}}</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td> 实例在队列中的排位 </td> | |||||
| <td>{{.QueuingInfo.Rank}}</td> | |||||
| </tr> | |||||
| </tbody> | |||||
| </table> | |||||
| {{end}} | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| {{template "base/footer" .}} | |||||
| @@ -0,0 +1,239 @@ | |||||
| {{template "base/head" .}} | |||||
| <div class="ui page dimmer"> | |||||
| <div class="ui text loader">{{.i18n.Tr "loading"}}</div> | |||||
| </div> | |||||
| <div class="repository"> | |||||
| {{template "repo/header" .}} | |||||
| <div class="ui container"> | |||||
| {{template "base/alert" .}} | |||||
| <h4 class="ui top attached header"> | |||||
| {{.i18n.Tr "repo.modelarts.train_job.new"}} | |||||
| </h4> | |||||
| <div class="ui attached segment"> | |||||
| <!-- equal width --> | |||||
| <form class="ui form" action="{{.Link}}" method="post"> | |||||
| {{.CsrfTokenHtml}} | |||||
| <input type="hidden" name="action" value="update"> | |||||
| <h4 class="ui dividing header">{{.i18n.Tr "repo.modelarts.train_job.basic_info"}}</h4> | |||||
| <div class="required field"> | |||||
| <label>{{.i18n.Tr "repo.modelarts.train_job.job_name"}}</label> | |||||
| <input name="job_name" id="trainjob_job_name" placeholder={{.i18n.Tr "repo.modelarts.train_job.job_name"}} value="{{.job_name}}" tabindex="3" autofocus required maxlength="255" readonly=""> | |||||
| </div> | |||||
| <div class="field"> | |||||
| <label for="description">{{.i18n.Tr "repo.modelarts.train_job.description"}}</label> | |||||
| <textarea id="description" name="description" rows="2"></textarea> | |||||
| </div> | |||||
| <h4 class="ui dividing header">{{.i18n.Tr "repo.modelarts.train_job.parameter_setting"}}</h4> | |||||
| <div class="required field"> | |||||
| <label>{{.i18n.Tr "repo.modelarts.train_job.algorithm_origin"}}</label> | |||||
| <div class="ui top attached tabular menu"> | |||||
| <a class="item active" data-tab="frame">{{svg "octicon-repo" 16}}{{.i18n.Tr "repo.modelarts.train_job.frames"}}</a> | |||||
| </div> | |||||
| <div class="ui bottom attached tab active segment" data-tab="frame"> | |||||
| <div class="required field"> | |||||
| <label>{{.i18n.Tr "repo.modelarts.train_job.AI_driver"}}</label> | |||||
| <div class="two fields"> | |||||
| <div class="field"> | |||||
| <select class="ui search dropdown" id="trainjob_engines" style='width:385px'> | |||||
| {{range .engines}} | |||||
| <option value="{{.Value}}">{{.Value}}</option> | |||||
| {{end}} | |||||
| </select> | |||||
| </div> | |||||
| <div class="field"> | |||||
| <select class="ui search dropdown" id="trainjob_engine_versions" style='width:385px' name="engine_id"> | |||||
| {{range .engine_versions}} | |||||
| <option name="engine_id" value="{{.ID}}">{{.Value}}</option> | |||||
| {{end}} | |||||
| </select> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <div class="inline required field"> | |||||
| <label>{{.i18n.Tr "repo.modelarts.train_job.start_file"}}</label> | |||||
| <input name="boot_file" id="trainjob_boot_file" value="" tabindex="3" autofocus required maxlength="255"> | |||||
| <span> | |||||
| <i class="question circle icon link" data-content={{.i18n.Tr "repo.modelarts.train_job.boot_file_helper"}} data-position="right center" data-variation="mini"></i> | |||||
| </span> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <div class="required field"> | |||||
| <label>{{.i18n.Tr "repo.modelarts.train_job.dataset"}}</label> | |||||
| <select class="ui search dropdown" id="trainjob_datasets" style='width:385px' name="attachment"> | |||||
| {{range .attachments}} | |||||
| <option name="attachment" value="{{.UUID}}">{{.Attachment.Name}}</option> | |||||
| {{end}} | |||||
| </select> | |||||
| </div> | |||||
| <div class="inline field"> | |||||
| <label>{{.i18n.Tr "repo.modelarts.train_job.run_parameter"}}</label> | |||||
| <span id="add_run_para"><i class="plus circle icon"></i>{{.i18n.Tr "repo.modelarts.train_job.add_run_parameter"}}</span> | |||||
| <input id="store_run_para" type="hidden" name="run_para_list"> | |||||
| <div class="dynamic field"> | |||||
| {{range .para}} | |||||
| <div class="two fields"> | |||||
| <div class="field"> | |||||
| <input type="text" name="shipping_first-name" placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_name"}}> | |||||
| </div> | |||||
| <div class="field"> | |||||
| <input type="text" name="shipping_last-name" placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_value"}}> | |||||
| </div> | |||||
| <span> | |||||
| <i class="trash icon"> | |||||
| </i> | |||||
| </span> | |||||
| </div> | |||||
| {{end}} | |||||
| </div> | |||||
| </div> | |||||
| <h4 class="ui dividing header">{{.i18n.Tr "repo.modelarts.train_job.resource_setting"}}</h4> | |||||
| <div class="required field"> | |||||
| <label>{{.i18n.Tr "repo.modelarts.train_job.resource_pool"}}</label> | |||||
| <select class="ui search dropdown" id="trainjob_resource_pool" style='width:385px' name="pool_id"> | |||||
| {{range .resource_pools}} | |||||
| <option value="{{.ID}}">{{.Value}}</option> | |||||
| {{end}} | |||||
| </select> | |||||
| </div> | |||||
| <div class="required grouped fields"> | |||||
| <label for="resource_type">{{.i18n.Tr "repo.modelarts.train_job.resource_type"}}</label> | |||||
| {{range .benchmark_categories}} | |||||
| <div class="field"> | |||||
| <div class="ui grid"> | |||||
| <div class="four wide column"> | |||||
| <div class="ui radio checkbox"> | |||||
| <input type="radio" name="resource_type" checked="" tabindex="0" class="hidden"> | |||||
| </div> | |||||
| </div> | |||||
| <div class="four wide column">train-private-1</div> | |||||
| <div class="four wide column">{{svg "octicon-verified" 16}} 运行中</div> | |||||
| <div class="four wide column"> CPU:192 核 2048GiB</div> | |||||
| </div> | |||||
| </div> | |||||
| {{end}} | |||||
| </div> | |||||
| <div class="required field"> | |||||
| <label>{{.i18n.Tr "repo.modelarts.train_job.standard"}}</label> | |||||
| <select class="ui search dropdown" id="trainjob-flavor" style='width:385px' name="flavor"> | |||||
| {{range .flavor_infos}} | |||||
| <option name="flavor" value="{{.Code}}">{{.Value}}</option> | |||||
| {{end}} | |||||
| </select> | |||||
| </div> | |||||
| <div class="inline required field"> | |||||
| <label>{{.i18n.Tr "repo.modelarts.train_job.amount_of_compute_node"}}</label> | |||||
| <input name="work_server_number" id="trainjob_work_server_num" tabindex="3" autofocus required maxlength="255"> | |||||
| </div> | |||||
| <div class="inline field"> | |||||
| <button class="ui green button"> | |||||
| {{.i18n.Tr "repo.modelarts.train_job_para.connfirm"}} | |||||
| </button> | |||||
| <a class="ui button" href="/">{{.i18n.Tr "repo.cloudbrain.cancel"}}</a> | |||||
| </div> | |||||
| </form> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| {{template "base/footer" .}} | |||||
| <script> | |||||
| $('select.dropdown') | |||||
| .dropdown(); | |||||
| $('.menu .item') | |||||
| .tab(); | |||||
| // 参数增加、删除、修改、保存 | |||||
| function Add_parameter(){ | |||||
| value = '<div class="two fields">' + | |||||
| '<div class="field">' + | |||||
| '<input type="text" name="shipping_first-name" placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_name"}}> ' + | |||||
| '</div> ' + | |||||
| '<div class="field"> ' + | |||||
| '<input type="text" name="shipping_last-name" placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_value"}}>' + | |||||
| '</div>'+ | |||||
| '<span>' + | |||||
| '<i class="trash icon">' + | |||||
| '</i>' + | |||||
| '</span>' + | |||||
| '</div>' | |||||
| $(".dynamic.field").append(value) | |||||
| } | |||||
| $('#add_run_para').click(function(){ | |||||
| Add_parameter() | |||||
| }); | |||||
| $(".dynamic.field").on("click",".trash.icon", function() { | |||||
| var index = $(this).parent().parent().index() | |||||
| $(this).parent().parent().remove() | |||||
| }); | |||||
| $('.question.circle.icon').hover(function(){ | |||||
| $(this).popup('show') | |||||
| }); | |||||
| $('.ui.deny.button').click(function(){ | |||||
| $('.ui.parameter.modal') | |||||
| .modal('hide'); | |||||
| }) | |||||
| function validate(){ | |||||
| $('.ui.form') | |||||
| .form({ | |||||
| on: 'blur', | |||||
| inline:true, | |||||
| fields: { | |||||
| boot_file: { | |||||
| identifier : 'boot_file', | |||||
| rules: [ | |||||
| { | |||||
| type: 'regExp[/.+\.py$/g]', | |||||
| prompt : '启动文件必须为.py结尾' | |||||
| } | |||||
| ] | |||||
| }, | |||||
| work_server_number: { | |||||
| identifier : 'work_server_number', | |||||
| rules: [ | |||||
| { | |||||
| type : 'integer[1..25]', | |||||
| prompt : '计算节点需要在1-25之间,请您键入正确的值' | |||||
| } | |||||
| ] | |||||
| } | |||||
| }, | |||||
| onSuccess: function(){ | |||||
| $('.ui.page.dimmer').dimmer('show') | |||||
| }, | |||||
| onFailure: function(e){ | |||||
| return false; | |||||
| } | |||||
| }) | |||||
| } | |||||
| function send_run_para(){ | |||||
| var run_parameters = [] | |||||
| var msg = {} | |||||
| $(".dynamic.field .two.fields").each(function(){ | |||||
| var para_name = $(this).find('input[name=shipping_first-name]').val() | |||||
| var para_value = $(this).find('input[name=shipping_last-name]').val() | |||||
| run_parameters.push({"label": para_name, "value": para_value}) | |||||
| }) | |||||
| msg["parameter"] = run_parameters | |||||
| msg = JSON.stringify(msg) | |||||
| $('#store_run_para').val(msg) | |||||
| } | |||||
| $('.ui.green.button').click(function(e) { | |||||
| send_run_para() | |||||
| validate() | |||||
| }) | |||||
| </script> | |||||
| @@ -0,0 +1,602 @@ | |||||
| <!-- 头部导航栏 --> | |||||
| {{template "base/head" .}} | |||||
| <style> | |||||
| .selectcloudbrain .active.item{ | |||||
| color: #0087f5 !important; | |||||
| border: 1px solid #0087f5; | |||||
| margin: -1px; | |||||
| background: #FFF !important; | |||||
| } | |||||
| #deletemodel { | |||||
| width: 100%; | |||||
| height: 100%; | |||||
| } | |||||
| /* 弹窗 */ | |||||
| #mask { | |||||
| position: fixed; | |||||
| top: 0px; | |||||
| left: 0px; | |||||
| right: 0px; | |||||
| bottom: 0px; | |||||
| filter: alpha(opacity=60); | |||||
| background-color: #777; | |||||
| z-index: 1000; | |||||
| display: none; | |||||
| opacity: 0.8; | |||||
| -moz-opacity: 0.5; | |||||
| padding-top: 100px; | |||||
| color: #000000 | |||||
| } | |||||
| #loadingPage { | |||||
| margin: 200px auto; | |||||
| width: 50px; | |||||
| height: 40px; | |||||
| text-align: center; | |||||
| font-size: 10px; | |||||
| display: block; | |||||
| } | |||||
| #loadingPage>div { | |||||
| background-color: green; | |||||
| height: 100%; | |||||
| width: 6px; | |||||
| display: inline-block; | |||||
| -webkit-animation: sk-stretchdelay 1.2s infinite ease-in-out; | |||||
| animation: sk-stretchdelay 1.2s infinite ease-in-out; | |||||
| } | |||||
| #loadingPage .rect2 { | |||||
| -webkit-animation-delay: -1.1s; | |||||
| animation-delay: -1.1s; | |||||
| } | |||||
| #loadingPage .rect3 { | |||||
| -webkit-animation-delay: -1.0s; | |||||
| animation-delay: -1.0s; | |||||
| } | |||||
| #loadingPage .rect4 { | |||||
| -webkit-animation-delay: -0.9s; | |||||
| animation-delay: -0.9s; | |||||
| } | |||||
| #loadingPage .rect5 { | |||||
| -webkit-animation-delay: -0.8s; | |||||
| animation-delay: -0.8s; | |||||
| } | |||||
| @-webkit-keyframes sk-stretchdelay { | |||||
| 0%, | |||||
| 40%, | |||||
| 100% { | |||||
| -webkit-transform: scaleY(0.4) | |||||
| } | |||||
| 20% { | |||||
| -webkit-transform: scaleY(1.0) | |||||
| } | |||||
| } | |||||
| @keyframes sk-stretchdelay { | |||||
| 0%, | |||||
| 40%, | |||||
| 100% { | |||||
| transform: scaleY(0.4); | |||||
| -webkit-transform: scaleY(0.4); | |||||
| } | |||||
| 20% { | |||||
| transform: scaleY(1.0); | |||||
| -webkit-transform: scaleY(1.0); | |||||
| } | |||||
| } | |||||
| /* 消息框 */ | |||||
| .alert { | |||||
| display: none; | |||||
| position: fixed; | |||||
| width: 100%; | |||||
| z-index: 1001; | |||||
| padding: 15px; | |||||
| border: 1px solid transparent; | |||||
| border-radius: 4px; | |||||
| text-align: center; | |||||
| font-weight: bold; | |||||
| } | |||||
| .alert-success { | |||||
| color: #3c763d; | |||||
| background-color: #dff0d8; | |||||
| border-color: #d6e9c6; | |||||
| } | |||||
| .alert-info { | |||||
| color: #31708f; | |||||
| background-color: #d9edf7; | |||||
| border-color: #bce8f1; | |||||
| } | |||||
| .alert-warning { | |||||
| color: #8a6d3b; | |||||
| background-color: #fcf8e3; | |||||
| border-color: #faebcc; | |||||
| } | |||||
| .alert-danger { | |||||
| color: #a94442; | |||||
| background-color: #f2dede; | |||||
| border-color: #ebccd1; | |||||
| } | |||||
| .pusher { | |||||
| width: calc(100% - 260px); | |||||
| box-sizing: border-box; | |||||
| } | |||||
| /* 弹窗 (background) */ | |||||
| #imageModal { | |||||
| display: none; | |||||
| position: fixed; | |||||
| z-index: 1; | |||||
| left: 0; | |||||
| top: 0; | |||||
| width: 100%; | |||||
| height: 100%; | |||||
| overflow: auto; | |||||
| background-color: rgb(0, 0, 0); | |||||
| background-color: rgba(0, 0, 0, 0.4); | |||||
| } | |||||
| /* 弹窗内容 */ | |||||
| .modal-content { | |||||
| background-color: #fefefe; | |||||
| margin: 15% auto; | |||||
| padding: 20px; | |||||
| border: 1px solid #888; | |||||
| width: 30%; | |||||
| } | |||||
| /* 关闭按钮 */ | |||||
| .close { | |||||
| color: #aaa; | |||||
| float: right; | |||||
| font-size: 28px; | |||||
| font-weight: bold; | |||||
| } | |||||
| .close:hover, | |||||
| .close:focus { | |||||
| color: black; | |||||
| text-decoration: none; | |||||
| cursor: pointer; | |||||
| } | |||||
| .dis { | |||||
| margin-bottom: 20px; | |||||
| } | |||||
| .disabled { | |||||
| cursor: pointer; | |||||
| pointer-events: none; | |||||
| } | |||||
| </style> | |||||
| <!-- 弹窗 --> | |||||
| <div id="mask"> | |||||
| <div id="loadingPage"> | |||||
| <div class="rect1"></div> | |||||
| <div class="rect2"></div> | |||||
| <div class="rect3"></div> | |||||
| <div class="rect4"></div> | |||||
| <div class="rect5"></div> | |||||
| </div> | |||||
| </div> | |||||
| <!-- 提示框 --> | |||||
| <div class="alert"></div> | |||||
| <div class="repository release dataset-list view"> | |||||
| {{template "repo/header" .}} | |||||
| <!-- 列表容器 --> | |||||
| <div class="ui container"> | |||||
| {{template "base/alert" .}} | |||||
| <!-- 中间云脑和新建任务按钮 --> | |||||
| <!-- <div class="ui three column stack able grid"> | |||||
| <div class="column"> | |||||
| <h2>{{.i18n.Tr "repo.cloudbrain2"}}</h2> | |||||
| </div> | |||||
| <div class="column"> | |||||
| </div> | |||||
| <div class="column right aligned"> | |||||
| {{if .Permission.CanWrite $.UnitTypeCloudBrain}} | |||||
| <a class="ui green button" href="{{.RepoLink}}/modelarts/create">{{.i18n.Tr "repo.cloudbrain.new"}}</a> {{end}} | |||||
| </div> | |||||
| <div class="column right aligned"> | |||||
| {{if .Permission.CanWrite $.UnitTypeCloudBrain}} | |||||
| <a class="ui blue button" href="{{.RepoLink}}/modelarts/train-job/create">{{.i18n.Tr "repo.cloudbrain.new"}}</a> {{end}} | |||||
| </div> | |||||
| </div> | |||||
| <p>使用鹏城云脑计算资源进行调试,云脑1提供CPU / GPU资源,云脑2提供Ascend NPU资源;调试使用的数据集也需要上传到对应的环境。</p> | |||||
| <div class="ui blue mini menu selectcloudbrain"> | |||||
| <a class="item" href="{{.RepoLink}}/cloudbrain">{{svg "octicon-server" 16}} CPU / GPU</a> | |||||
| <a class="active item" href="{{.RepoLink}}/modelarts">{{svg "octicon-server" 16}} Ascend NPU</a> | |||||
| </div> --> | |||||
| <div class="ui two column stackable grid "> | |||||
| <div class="column"> | |||||
| <div class="ui blue small menu compact selectcloudbrain"> | |||||
| <a class="item" href="{{.RepoLink}}/modelarts/notebook">{{$.i18n.Tr "repo.modelarts.notebook"}}</a> | |||||
| <a class="active item" href="{{.RepoLink}}/modelarts/train-job">{{$.i18n.Tr "repo.modelarts.train_job"}}</a> | |||||
| </div> | |||||
| </div> | |||||
| <div class="column right aligned"> | |||||
| <div class="ui selection dropdown" style="min-width: 10em;min-height:2.6em;border-radius: .28571429rem;margin-right: 1em;padding: .67em 3.2em .7em 1em;"> | |||||
| {{svg "octicon-server" 16}} | |||||
| <div class="default text" style="color: rgba(0,0,0,.87);"> Ascend NPU</div> | |||||
| <i class="dropdown icon"></i> | |||||
| <div class="menu"> | |||||
| <a class="item" href="{{.RepoLink}}/cloudbrain" data-value="11">CPU / GPU</a> | |||||
| <a class="item" href="{{.RepoLink}}/modelarts/notebook" data-value="22">Ascend NPU</a> | |||||
| </div> | |||||
| </div> | |||||
| {{if .Permission.CanWrite $.UnitTypeCloudBrain}} | |||||
| <a class="ui green button" href="{{.RepoLink}}/modelarts/train-job/create">{{$.i18n.Tr "repo.modelarts.train_job.new_train"}}</a>{{end}} | |||||
| </div> | |||||
| </div> | |||||
| <!-- 中下列表展示区 --> | |||||
| <div class="ui grid"> | |||||
| <div class="row"> | |||||
| <div class="ui sixteen wide column"> | |||||
| <!-- 排序区 --> | |||||
| <!-- <div class="ui sixteen wide column"> | |||||
| <div class="ui two column stackable grid"> | |||||
| <div class="column"> | |||||
| </div> | |||||
| <div class="column right aligned"> | |||||
| <div class="ui right dropdown type jump item"> | |||||
| <span class="text"> | |||||
| {{.i18n.Tr "repo.issues.filter_sort"}}<i class="dropdown icon"></i> | |||||
| </span> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> --> | |||||
| <!-- 任务展示 --> | |||||
| <div class="dataset list"> | |||||
| <!-- 表头 --> | |||||
| <div class="ui grid stackable" style="background: #f0f0f0;;"> | |||||
| <div class="row"> | |||||
| <div class="five wide column"> | |||||
| <span style="margin:0 6px">{{$.i18n.Tr "repo.cloudbrain_task"}}</span> | |||||
| </div> | |||||
| <div class="three wide column"> | |||||
| <span>{{$.i18n.Tr "repo.cloudbrain_status_createtime"}}</span> | |||||
| </div> | |||||
| <div class="two wide column"> | |||||
| <span>{{$.i18n.Tr "repo.cloudbrain_status_runtime"}}</span> | |||||
| </div> | |||||
| <div class="one wide column text center"> | |||||
| <span>{{$.i18n.Tr "repo.cloudbrain_creator"}}</span> | |||||
| </div> | |||||
| <div class="five wide column text center"> | |||||
| <span style="margin-left: 6rem;">{{$.i18n.Tr "repo.cloudbrain_operate"}}</span> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| {{range .Tasks}} | |||||
| <div class="ui grid stackable item"> | |||||
| <div class="row"> | |||||
| <!-- 任务名 --> | |||||
| <div class="five wide column"> | |||||
| <a class="title" href="{{$.Link}}/{{.JobID}}" title="{{.JobName}}" style="font-size: 15px;"> | |||||
| <span class="fitted" style="vertical-align: middle;">{{svg "octicon-tasklist" 16}}</span> | |||||
| <span class="fitted" style="width: 90%;vertical-align: middle;margin-left: 0.4rem;">{{.JobName}}</span> | |||||
| </a> | |||||
| </div> | |||||
| <div class="three wide column"> | |||||
| <!--任务状态 --> | |||||
| <!-- <span class="ui compact button job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}"> | |||||
| {{.Status}} | |||||
| </span> --> | |||||
| <span class="job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}"> | |||||
| <span><i id="{{.JobID}}-icon" style="vertical-align: middle;" class="{{.Status}}"></i><span id="{{.JobID}}-text" style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span> | |||||
| </span> | |||||
| <!-- 任务创建时间 --> | |||||
| <span style="font-size: 12px;margin-left: 0.4rem;" class="">{{TimeSinceUnix .Cloudbrain.CreatedUnix $.Lang}}</span> | |||||
| </div> | |||||
| <div class="two wide column"> | |||||
| <!--任务状态 --> | |||||
| <!-- <span class="ui compact button job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}"> | |||||
| {{.Status}} | |||||
| </span> --> | |||||
| <span id="duration-{{.JobID}}"></span> | |||||
| <!-- 任务创建时间 --> | |||||
| <!-- <span style="font-size: 12px;margin-left: 0.4rem;" class="">{{TimeSinceUnix .Cloudbrain.CreatedUnix $.Lang}}</span> --> | |||||
| </div> | |||||
| <div class="one wide column text center"> | |||||
| {{if .User.Name}} | |||||
| <a href="{{AppSubUrl}}/{{.User.Name}}" title="{{.User.Name}}"><img class="ui avatar image" src="{{.User.RelAvatarLink}}"></a> | |||||
| {{else}} | |||||
| <a title="Ghost"><img class="ui avatar image" src="{{AppSubUrl}}/user/avatar/Ghost/-1"></a> | |||||
| {{end}} | |||||
| </div> | |||||
| <div class="five wide column text right"> | |||||
| <div class="ui compact buttons"> | |||||
| <!-- <a class="ui basic blue button" href="{{$.Link}}/{{.JobID}}"> | |||||
| 查看 | |||||
| </a> | |||||
| <a class="ui basic {{if not .CanDebug}}disabled {{else}}blue {{end}}button" href="{{$.Link}}/{{.JobID}}/debug" target="_blank"> | |||||
| 调试 | |||||
| </a> --> | |||||
| <form id="stopForm-{{.JobID}}" action="{{$.Link}}/{{.JobID}}/stop" method="post" style="margin-left:-1px;"> | |||||
| {{$.CsrfTokenHtml}} | |||||
| {{if $.Permission.CanWrite $.UnitTypeCloudBrain}} | |||||
| <a id="stop-model-debug-{{.JobID}}" class="ui basic {{if eq .Status "KILLED" "FAILED" "START_FAILED" "KILLING" "COMPLETED"}}disabled {{else}}blue {{end}}button" onclick="document.getElementById('stopForm-{{.JobID}}').submit();"> | |||||
| {{$.i18n.Tr "repo.stop"}} | |||||
| </a> | |||||
| {{else}} | |||||
| <a class="ui basic disabled button" onclick="document.getElementById('stopForm-{{.JobID}}').submit();"> | |||||
| {{$.i18n.Tr "repo.stop"}} | |||||
| </a> | |||||
| {{end}} | |||||
| </form> | |||||
| </div> | |||||
| <div class="ui compact buttons" style="margin-right:10px;"> | |||||
| <!-- 模型下载 --> | |||||
| <a class="ui basic blue button" href="{{$.Link}}/{{.JobID}}/models" target="_blank"> | |||||
| {{$.i18n.Tr "repo.model_download"}} | |||||
| </a> | |||||
| <!-- 接收结果 --> | |||||
| <!-- <iframe src="" frameborder="0" name="iframeContent" style="display: none;"></iframe> | |||||
| <a class="imageBtn ui basic {{if not .CanDebug}}disabled {{else}}blue {{end}}button" value="{{.CanDebug}}">提交镜像</a> --> | |||||
| </div> | |||||
| <!-- 删除任务 --> | |||||
| <form class="ui compact buttons" id="delForm-{{.JobID}}" action="{{$.Link}}/{{.JobID}}/del" method="post"> | |||||
| {{$.CsrfTokenHtml}} | |||||
| {{if $.Permission.CanWrite $.UnitTypeCloudBrain}} | |||||
| <a id="model-delete-{{.JobID}}" class="ui compact {{if or (eq .Status "RUNNING") (eq .Status "INIT") (eq .Status "CREATING") (eq .Status "WAITING") }}disabled {{else}}red {{end}}button" onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | |||||
| {{$.i18n.Tr "repo.delete"}} | |||||
| </a> | |||||
| {{else}} | |||||
| <a class="ui compact disabled button" onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | |||||
| {{$.i18n.Tr "repo.delete"}} | |||||
| </a> | |||||
| {{end}} | |||||
| </form> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| {{end}} {{template "base/paginate" .}} | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <!-- 确认模态框 --> | |||||
| <div id="deletemodel"> | |||||
| <div class="ui basic modal"> | |||||
| <div class="ui icon header"> | |||||
| <i class="trash icon"></i> 删除任务 | |||||
| </div> | |||||
| <div class="content"> | |||||
| <p>你确认删除该任务么?此任务一旦删除不可恢复。</p> | |||||
| </div> | |||||
| <div class="actions"> | |||||
| <div class="ui red basic inverted cancel button"> | |||||
| <i class="remove icon"></i> 取消操作 | |||||
| </div> | |||||
| <div class="ui green basic inverted ok button"> | |||||
| <i class="checkmark icon"></i> 确定操作 | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| {{template "base/footer" .}} | |||||
| <script> | |||||
| // 调试和评分新开窗口 | |||||
| function stop(obj) { | |||||
| if (obj.style.color != "rgb(204, 204, 204)") { | |||||
| obj.target = '_blank' | |||||
| } else { | |||||
| return | |||||
| } | |||||
| } | |||||
| // 删除时用户确认 | |||||
| function assertDelete(obj) { | |||||
| if (obj.style.color == "rgb(204, 204, 204)") { | |||||
| return | |||||
| } else { | |||||
| var delId = obj.parentNode.id | |||||
| flag = 1; | |||||
| $('.ui.basic.modal') | |||||
| .modal({ | |||||
| onDeny: function() { | |||||
| flag = false | |||||
| }, | |||||
| onApprove: function() { | |||||
| document.getElementById(delId).submit() | |||||
| flag = true | |||||
| }, | |||||
| onHidden: function() { | |||||
| if (flag == false) { | |||||
| $('.alert').html('您已取消操作').removeClass('alert-success').addClass('alert-danger').show().delay(1500).fadeOut(); | |||||
| } | |||||
| } | |||||
| }) | |||||
| .modal('show') | |||||
| } | |||||
| } | |||||
| function runtime(time){ | |||||
| if(time){ | |||||
| let hours = time/3600000<10 ? "0"+parseInt(time/3600000):parseInt(time/3600000) | |||||
| let miuns = time%3600000/60000<10 ? "0"+parseInt(time%3600000/60000):parseInt(time%3600000/60000) | |||||
| let seconds = time%60000/1000<10 ? "0"+parseInt(time%60000/1000):parseInt(time%60000/1000) | |||||
| return hours + ":" + miuns + ":" + seconds | |||||
| }else{ | |||||
| return "00:00:00" | |||||
| } | |||||
| } | |||||
| function loadJobDuration() { | |||||
| $(".job-status").each((index, job) => { | |||||
| const jobID = job.dataset.jobid; | |||||
| const repoPath = job.dataset.repopath; | |||||
| $.get(`/api/v1/repos/${repoPath}/modelarts/train-job/${jobID}`, (data) => { | |||||
| const duration = data.JobDuration | |||||
| const jobID = data.JobID | |||||
| let train_duration = runtime(duration) | |||||
| $('#duration-'+jobID).text(train_duration) | |||||
| }) | |||||
| }) | |||||
| } | |||||
| $(document).ready(loadJobDuration); | |||||
| // 加载任务状态 | |||||
| var timeid = window.setInterval(loadJobStatus, 15000); | |||||
| $(document).ready(loadJobStatus); | |||||
| function loadJobStatus() { | |||||
| $(".job-status").each((index, job) => { | |||||
| const jobID = job.dataset.jobid; | |||||
| const repoPath = job.dataset.repopath; | |||||
| if (job.textContent.trim() == 'IMAGE_FAILED' || job.textContent.trim() == 'SUBMIT_FAILED' || job.textContent.trim() == 'DELETE_FAILED' | |||||
| || job.textContent.trim() == 'KILLED' || job.textContent.trim() == 'COMPLETED' || job.textContent.trim() == 'FAILED' | |||||
| || job.textContent.trim() == 'CANCELED' || job.textContent.trim() == 'LOST') { | |||||
| return | |||||
| } | |||||
| $.get(`/api/v1/repos/${repoPath}/modelarts/train-job/${jobID}`, (data) => { | |||||
| const jobID = data.JobID | |||||
| const status = data.JobStatus | |||||
| const duration = data.JobDuration | |||||
| if (status != job.textContent.trim()) { | |||||
| $('#' + jobID+'-icon').removeClass().addClass(status) | |||||
| $('#' + jobID+ '-text').text(status) | |||||
| } | |||||
| if(status==="RUNNING"){ | |||||
| $('#model-debug-'+jobID).removeClass('disabled') | |||||
| $('#model-debug-'+jobID).addClass('blue') | |||||
| let train_duration = runtime(duration) | |||||
| $('#duration-'+jobID).text(train_duration) | |||||
| } | |||||
| if(status!=="RUNNING"){ | |||||
| $('#model-debug-'+jobID).removeClass('blue') | |||||
| $('#model-debug-'+jobID).addClass('disabled') | |||||
| } | |||||
| if(status!=="KILLED" || status!=="FAILED"){ | |||||
| $('#stop-model-debug-'+jobID).removeClass('disabled') | |||||
| $('#stop-model-debug-'+jobID).addClass('blue') | |||||
| $('#model-delete-'+jobID).removeClass('red') | |||||
| $('#model-delete-'+jobID).addClass('disabled') | |||||
| } | |||||
| if(status=="KILLED" || status=="FAILED" || status=="KILLING"){ | |||||
| $('#stop-model-debug-'+jobID).removeClass('blue') | |||||
| $('#stop-model-debug-'+jobID).addClass('disabled') | |||||
| $('#model-delete-'+jobID).removeClass('disabled') | |||||
| $('#model-delete-'+jobID).addClass('red') | |||||
| } | |||||
| if(status=="START_FAILED"){ | |||||
| $('#stop-model-debug-'+jobID).removeClass('blue') | |||||
| $('#stop-model-debug-'+jobID).addClass('disabled') | |||||
| } | |||||
| }).fail(function(err) { | |||||
| console.log(err); | |||||
| }); | |||||
| }); | |||||
| }; | |||||
| // 获取弹窗 | |||||
| var modal = document.getElementById('imageModal'); | |||||
| // 打开弹窗的按钮对象 | |||||
| var btns = document.getElementsByClassName("imageBtn"); | |||||
| // 获取 <span> 元素,用于关闭弹窗 | |||||
| var spans = document.getElementsByClassName('close'); | |||||
| // 点击按钮打开弹窗 | |||||
| for (i = 0; i < btns.length; i++) { | |||||
| btns[i].onclick = function() { | |||||
| modal.style.display = "block"; | |||||
| } | |||||
| } | |||||
| // 点击 <span> (x), 关闭弹窗 | |||||
| for (i = 0; i < spans.length; i++) { | |||||
| spans[i].onclick = function() { | |||||
| modal.style.display = "none"; | |||||
| } | |||||
| } | |||||
| // 在用户点击其他地方时,关闭弹窗 | |||||
| window.onclick = function(event) { | |||||
| if (event.target == modal) { | |||||
| modal.style.display = "none"; | |||||
| } | |||||
| } | |||||
| // 显示弹窗,弹出相应的信息 | |||||
| function showmask() { | |||||
| $('#imageModal').css('display', 'none') | |||||
| $('#mask').css('display', 'block') | |||||
| $("iframe[name=iframeContent]").on("load", function() { | |||||
| var responseText = $("iframe")[0].contentDocument.body.getElementsByTagName("pre")[0].innerHTML; | |||||
| var json1 = JSON.parse(responseText) | |||||
| $('#mask').css('display', 'none') | |||||
| parent.location.href | |||||
| if (json1.result_code === "0") { | |||||
| $('.alert').html('操作成功!').removeClass('alert-danger').addClass('alert-success').show().delay(1500).fadeOut(); | |||||
| } else { | |||||
| $('.alert').html(json1.error_msg).removeClass('alert-success').addClass('alert-danger').show().delay(5000).fadeOut(); | |||||
| } | |||||
| }) | |||||
| } | |||||
| </script> | |||||
| @@ -0,0 +1,47 @@ | |||||
| {{template "base/head" .}} | |||||
| <div class="repository"> | |||||
| {{template "repo/header" .}} | |||||
| <div class="repository new repo ui middle very relaxed page grid"> | |||||
| <div class="column"> | |||||
| {{template "base/alert" .}} | |||||
| <h4 class="ui top attached header"> | |||||
| <div class="ui two column grid"> | |||||
| <div class="column"> | |||||
| {{$.i18n.Tr "repo.modelarts.version_manage"}} | |||||
| </div> | |||||
| <div class="column right aligned"> | |||||
| <a href="javascript:window.history.back();">{{svg "octicon-reply" 16}}{{$.i18n.Tr "repo.modelarts.back"}}</a> | |||||
| </div> | |||||
| </div> | |||||
| </h4> | |||||
| <div class="ui attached segment"> | |||||
| <div class="ui style accordion"> | |||||
| <div class="title active"> | |||||
| <i class="dropdown icon"></i> | |||||
| {{$.i18n.Tr "repo.modelarts.train_job.version"}} | |||||
| </div> | |||||
| <div class="content active"> | |||||
| <div class="ui list"> | |||||
| {{.ListModel}} | |||||
| <div class="item"> | |||||
| <i class="file icon"></i> | |||||
| <div class="content"> | |||||
| <div class="header">semantic.json</div> | |||||
| <div class="description">Contains build settings for gulp</div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| {{template "base/footer" .}} | |||||
| <script> | |||||
| </script> | |||||
| @@ -0,0 +1,27 @@ | |||||
| {{if .Dirs}} | |||||
| <table id="repo-files-table" class="ui single line table"> | |||||
| <tbody> | |||||
| {{range .Dirs}} | |||||
| <tr> | |||||
| <td class="name four wide"> | |||||
| <span class="truncate"> | |||||
| <span class="octicon octicon-file-directory"></span> | |||||
| <a class="title" href="{{if .IsDir}}{{$.RepoLink}}/modelarts/train-job/{{$.JobID}}/models?parentDir={{.ParenDir}}{{else}}{{$.RepoLink}}/modelarts/train-job/{{$.JobID}}/download_model?parentDir={{.ParenDir}}&fileName={{.FileName}}&jobName={{$.task.JobName}}{{end}}"> | |||||
| <span class="fitted">{{if .IsDir}} {{svg "octicon-file-directory" 16}}{{else}}{{svg "octicon-file" 16}}{{end}}</span> {{.FileName}} | |||||
| </a> | |||||
| </span> | |||||
| </td> | |||||
| <td class="message nine wide"> | |||||
| <span class="truncate has-emoji"> | |||||
| {{.Size | FileSize}} | |||||
| </span> | |||||
| </td> | |||||
| <td class="text right age three wide"> | |||||
| <span class="time-since poping up">{{.ModTime}}</span> | |||||
| </td> | |||||
| </tr> | |||||
| {{end}} | |||||
| </tbody> | |||||
| </table> | |||||
| {{end}} | |||||
| @@ -0,0 +1,29 @@ | |||||
| {{template "base/head" .}} | |||||
| <div class="repository dataset dir-list view"> | |||||
| {{template "repo/header" .}} | |||||
| <form class="ui container"> | |||||
| <div class="ui stackable grid {{if .Error}}hide{{end}}" id="dir-content"> | |||||
| <div class="row"> | |||||
| <div class="column sixteen wide"> | |||||
| <p> | |||||
| {{ range $index, $item := .Path }}<a href='{{$.Link}}/?parentDir={{if gt $index 0}}{{DatasetPathJoin $.Path $index "/"}}{{else}}{{end}}'>{{ $item }}</a><span class="directory-seperator">/</span>{{ end }} | |||||
| </p> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <div class="ui grid"> | |||||
| <div class="row"> | |||||
| <div class="ui sixteen wide column"> | |||||
| <div class="dir list"> | |||||
| {{template "repo/modelarts/trainjob/models/dir_list" .}} | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </form> | |||||
| </div> | |||||
| {{template "base/footer" .}} | |||||
| @@ -0,0 +1,606 @@ | |||||
| {{template "base/head" .}} | |||||
| <style> | |||||
| .unite{ | |||||
| font-family: SourceHanSansSC-medium !important; | |||||
| color: rgba(16, 16, 16, 100) !important; | |||||
| } | |||||
| .title{ | |||||
| font-size: 16px !important; | |||||
| padding-left: 3rem !important; | |||||
| } | |||||
| .min_title{ | |||||
| font-size: 14px !important; | |||||
| padding-left: 6rem !important; | |||||
| margin-bottom: 2rem !important; | |||||
| } | |||||
| .width{ | |||||
| width:100% !important; | |||||
| } | |||||
| .width80{ | |||||
| width: 80.7% !important; | |||||
| margin-left: 10px; | |||||
| } | |||||
| .width85{ | |||||
| width: 85% !important; | |||||
| margin-left: 4.5rem !important; | |||||
| } | |||||
| .width81{ | |||||
| margin-left: 1.5rem; | |||||
| width: 81% !important; | |||||
| } | |||||
| .add{font-size: 18px; | |||||
| padding: 0.5rem; | |||||
| border: 1px solid rgba(187, 187, 187, 100); | |||||
| border-radius: 0px 5px 5px 0px; | |||||
| line-height: 21px; | |||||
| text-align: center; | |||||
| color: #C2C7CC; | |||||
| } | |||||
| .min{ | |||||
| font-size: 18px; | |||||
| padding: 0.5rem; | |||||
| border: 1px solid rgba(187, 187, 187, 100); | |||||
| border-radius: 5px 0px 0px 5px; | |||||
| line-height: 21px; | |||||
| text-align: center; | |||||
| color: #C2C7CC;" | |||||
| } | |||||
| #mask { | |||||
| position: fixed; | |||||
| top: 0px; | |||||
| left: 0px; | |||||
| right: 0px; | |||||
| bottom: 0px; | |||||
| filter: alpha(opacity=60); | |||||
| background-color: #777; | |||||
| z-index: 1000; | |||||
| display: none; | |||||
| opacity: 0.8; | |||||
| -moz-opacity: 0.5; | |||||
| padding-top: 100px; | |||||
| color: #000000 | |||||
| } | |||||
| /* 加载圈css效果图 */ | |||||
| #loadingPage { | |||||
| margin: 200px auto; | |||||
| width: 50px; | |||||
| height: 40px; | |||||
| text-align: center; | |||||
| font-size: 10px; | |||||
| display: block; | |||||
| } | |||||
| #loadingPage>div { | |||||
| background-color: green; | |||||
| height: 100%; | |||||
| width: 6px; | |||||
| display: inline-block; | |||||
| -webkit-animation: sk-stretchdelay 1.2s infinite ease-in-out; | |||||
| animation: sk-stretchdelay 1.2s infinite ease-in-out; | |||||
| } | |||||
| #loadingPage .rect2 { | |||||
| -webkit-animation-delay: -1.1s; | |||||
| animation-delay: -1.1s; | |||||
| } | |||||
| #loadingPage .rect3 { | |||||
| -webkit-animation-delay: -1.0s; | |||||
| animation-delay: -1.0s; | |||||
| } | |||||
| #loadingPage .rect4 { | |||||
| -webkit-animation-delay: -0.9s; | |||||
| animation-delay: -0.9s; | |||||
| } | |||||
| #loadingPage .rect5 { | |||||
| -webkit-animation-delay: -0.8s; | |||||
| animation-delay: -0.8s; | |||||
| } | |||||
| @-webkit-keyframes sk-stretchdelay { | |||||
| 0%, | |||||
| 40%, | |||||
| 100% { | |||||
| -webkit-transform: scaleY(0.4) | |||||
| } | |||||
| 20% { | |||||
| -webkit-transform: scaleY(1.0) | |||||
| } | |||||
| } | |||||
| @keyframes sk-stretchdelay { | |||||
| 0%, | |||||
| 40%, | |||||
| 100% { | |||||
| transform: scaleY(0.4); | |||||
| -webkit-transform: scaleY(0.4); | |||||
| } | |||||
| 20% { | |||||
| transform: scaleY(1.0); | |||||
| -webkit-transform: scaleY(1.0); | |||||
| } | |||||
| } | |||||
| </style> | |||||
| <!-- <div class="ui page dimmer"> | |||||
| <div class="ui text loader">{{.i18n.Tr "loading"}}</div> | |||||
| </div> --> | |||||
| <div id="mask"> | |||||
| <div id="loadingPage"> | |||||
| <div class="rect1"></div> | |||||
| <div class="rect2"></div> | |||||
| <div class="rect3"></div> | |||||
| <div class="rect4"></div> | |||||
| <div class="rect5"></div> | |||||
| </div> | |||||
| </div> | |||||
| <div class="repository"> | |||||
| {{template "repo/header" .}} | |||||
| <div class="ui container"> | |||||
| {{template "base/alert" .}} | |||||
| <h4 class="ui top attached header"> | |||||
| {{.i18n.Tr "repo.modelarts.train_job.new"}} | |||||
| </h4> | |||||
| <div class="ui attached segment"> | |||||
| <!-- equal width --> | |||||
| <form class="ui form" action="{{.Link}}" method="post"> | |||||
| {{.CsrfTokenHtml}} | |||||
| <input type="hidden" name="action" value="update"> | |||||
| <h4 class="unite title ui header ">{{.i18n.Tr "repo.modelarts.train_job.basic_info"}}:</h4> | |||||
| <div class="required unite min_title inline field"> | |||||
| <label>{{.i18n.Tr "repo.modelarts.train_job.job_name"}}</label> | |||||
| <input style="width: 60%;" name="job_name" id="trainjob_job_name" placeholder={{.i18n.Tr "repo.modelarts.train_job.job_name"}} value="{{.job_name}}" tabindex="3" autofocus required maxlength="255"> | |||||
| </div> | |||||
| <div class="unite min_title inline field"> | |||||
| <label for="description">{{.i18n.Tr "repo.modelarts.train_job.description"}} </label> | |||||
| <textarea style="width: 80%;" id="description" name="description" rows="3" maxlength="255" placeholder={{.i18n.Tr "repo.modelarts.train_job.new_place"}} onchange="this.value=this.value.substring(0, 255)" onkeydown="this.value=this.value.substring(0, 255)" onkeyup="this.value=this.value.substring(0, 256)"></textarea> | |||||
| </div> | |||||
| <!-- <h4 class="ui dividing header">{{.i18n.Tr "repo.modelarts.train_job.parameter_setting"}}</h4> | |||||
| <div class="inline field"> | |||||
| <label>{{.i18n.Tr "repo.modelarts.train_job.fast_parameter_setting"}}</label> | |||||
| <span> | |||||
| {{.i18n.Tr "repo.modelarts.train_job.fast_parameter_setting_config"}} | |||||
| <a class="item active parameter_config">{{.i18n.Tr "repo.modelarts.train_job.fast_parameter_setting_config_link"}}</a> | |||||
| </span> | |||||
| </div> --> | |||||
| <div class="ui divider"></div> | |||||
| <h4 class="unite title ui header ">{{.i18n.Tr "repo.modelarts.train_job.parameter_setting"}}:</h4> | |||||
| <div class="required unite min_title inline fields" style="width: 90%;"> | |||||
| <label>{{.i18n.Tr "repo.modelarts.train_job.AI_driver"}} </label> | |||||
| <div class="field" style="flex: 1.5;"> | |||||
| <select class="ui dropdown width" id="trainjob_engines" > | |||||
| {{range .engines}} | |||||
| <option value="{{.Value}}">{{.Value}}</option> | |||||
| {{end}} | |||||
| </select> | |||||
| </div> | |||||
| <div class="field" style="flex: 2;"> | |||||
| <select class="ui dropdown width" id="trainjob_engine_versions" style='width: 100%;' name="engine_id"> | |||||
| {{range .engine_versions}} | |||||
| <option name="engine_id" value="{{.ID}}">{{.Value}}</option> | |||||
| {{end}} | |||||
| </select> | |||||
| </div> | |||||
| </div> | |||||
| <!-- <div class="required inline field"> | |||||
| <label>{{.i18n.Tr "repo.modelarts.train_job.algorithm_origin"}}</label> | |||||
| <div class="ui top attached tabular menu"> | |||||
| <a class="item active" data-tab="frame">{{svg "octicon-repo" 16}}{{.i18n.Tr "repo.modelarts.train_job.frames"}}</a> | |||||
| </div> | |||||
| <div class="ui bottom attached tab active segment" data-tab="frame"> | |||||
| <div class="required field"> | |||||
| <label>{{.i18n.Tr "repo.modelarts.train_job.AI_driver"}}</label> | |||||
| <div class="two fields"> | |||||
| <div class="field"> | |||||
| <select class="ui search dropdown" id="trainjob_engines" style='width:385px'> | |||||
| {{range .engines}} | |||||
| <option value="{{.Value}}">{{.Value}}</option> | |||||
| {{end}} | |||||
| </select> | |||||
| </div> | |||||
| <div class="field"> | |||||
| <select class="ui search dropdown" id="trainjob_engine_versions" style='width:385px' name="engine_id"> | |||||
| {{range .engine_versions}} | |||||
| <option name="engine_id" value="{{.ID}}">{{.Value}}</option> | |||||
| {{end}} | |||||
| </select> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <div class="inline required field"> | |||||
| <label>{{.i18n.Tr "repo.modelarts.train_job.start_file"}}</label> | |||||
| <input name="boot_file" id="trainjob_boot_file" value="" tabindex="3" autofocus required maxlength="255"> | |||||
| <span> | |||||
| <i class="question circle icon link" data-content={{.i18n.Tr "repo.modelarts.train_job.boot_file_helper"}} data-position="right center" data-variation="mini"></i> | |||||
| </span> | |||||
| </div> | |||||
| </div> | |||||
| </div> --> | |||||
| <div class="inline unite min_title field required"> | |||||
| <label>{{.i18n.Tr "repo.modelarts.train_job.start_file"}}</label> | |||||
| {{if .bootFile}} | |||||
| <input style="width: 33.5%;" name="boot_file" id="trainjob_boot_file" value="{{.bootFile}}" tabindex="3" autofocus required maxlength="255" > | |||||
| {{else}} | |||||
| <input style="width: 33.5%;" name="boot_file" id="trainjob_boot_file" value="" tabindex="3" autofocus required maxlength="255" > | |||||
| {{end}} | |||||
| <span> | |||||
| <i class="question circle icon link" data-content={{.i18n.Tr "repo.modelarts.train_job.boot_file_helper"}} data-position="right center" data-variation="mini"></i> | |||||
| </span> | |||||
| </div> | |||||
| <div class="required unite min_title inline field"> | |||||
| <label>{{.i18n.Tr "repo.modelarts.train_job.dataset"}}</label> | |||||
| <select class="ui dropdown width80" id="trainjob_datasets" name="attachment" placeholder="选择数据集"> | |||||
| {{if $.uuid}} | |||||
| <option name="attachment" value="{{$.uuid}}">{{$.datasetName}}</option> | |||||
| {{end}} | |||||
| {{range .attachments}} | |||||
| <option value="">选择数据集</option> | |||||
| <option name="attachment" value="{{.UUID}}">{{.Attachment.Name}}</option> | |||||
| {{end}} | |||||
| </select> | |||||
| </div> | |||||
| <div class="inline unite min_title field"> | |||||
| <label>{{.i18n.Tr "repo.modelarts.train_job.run_parameter"}}</label> | |||||
| <!-- <i class="plus square outline icon"></i> --> | |||||
| <span id="add_run_para" style="margin-left: 0.5rem;cursor:pointer;color: rgba(3, 102, 214, 100);font-size: 14px;line-height: 26px;font-family: SourceHanSansSC-medium;"><i class="plus square outline icon"></i>{{.i18n.Tr "repo.modelarts.train_job.add_run_parameter"}}</span> | |||||
| <input id="store_run_para" type="hidden" name="run_para_list"> | |||||
| <div class="dynamic field" style="margin-top: 1rem;"></div> | |||||
| <!-- <div class="dynamic field"> | |||||
| </div> --> | |||||
| </div> | |||||
| <!-- <h4 class="ui dividing header">{{.i18n.Tr "repo.modelarts.train_job.resource_setting"}}</h4> --> | |||||
| <div class="required field " style="display: none;"> | |||||
| <label>{{.i18n.Tr "repo.modelarts.train_job.resource_pool"}}</label> | |||||
| <select class="ui dropdown" id="trainjob_resource_pool" style='width:385px' name="pool_id"> | |||||
| {{range .resource_pools}} | |||||
| <option value="{{.ID}}">{{.Value}}</option> | |||||
| {{end}} | |||||
| </select> | |||||
| </div> | |||||
| <div class="required grouped fields" style="display: none;"> | |||||
| <label for="resource_type">{{.i18n.Tr "repo.modelarts.train_job.resource_type"}}</label> | |||||
| <div class="field"> | |||||
| <div class="ui grid"> | |||||
| <div class="column"> | |||||
| <div class="ui radio checkbox"> | |||||
| <input type="radio" name="resource_type" checked="" tabindex="0"> | |||||
| </div> | |||||
| </div> | |||||
| <div class="three wide column">train-private-1</div> | |||||
| <div class="three wide column">{{svg "octicon-verified" 16}} 运行中</div> | |||||
| <div class="three wide column"> CPU:192 核 2048GiB</div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <div class="required unite min_title inline field"> | |||||
| <label>{{.i18n.Tr "repo.modelarts.train_job.standard"}}</label> | |||||
| <select class="ui dropdown width81" id="trainjob-flavor" style='width:385px' name="flavor"> | |||||
| {{range .flavor_infos}} | |||||
| <option name="flavor" value="{{.Code}}">{{.Value}}</option> | |||||
| {{end}} | |||||
| </select> | |||||
| </div> | |||||
| <div class="inline required unite min_title field"> | |||||
| <label>{{.i18n.Tr "repo.modelarts.train_job.amount_of_compute_node"}}</label> | |||||
| <div class="ui labeled input" style="width: 5%;"> | |||||
| <!-- <span class="min"><i class="minus icon"></i></span> --> | |||||
| <input style="border-radius: 0;text-align: center;" name="work_server_number" id="trainjob_work_server_num" tabindex="3" autofocus required maxlength="255" value="1" readonly> | |||||
| <!-- <span class="add"><i class="plus icon"></i></span> --> | |||||
| </div> | |||||
| <!-- <input name="work_server_number" id="trainjob_work_server_num" tabindex="3" autofocus required maxlength="255"> --> | |||||
| </div> | |||||
| <!-- | |||||
| <div class="inline field"> | |||||
| <div class="ui save checkbox"> | |||||
| <input name="is_save_para" type="checkbox"> | |||||
| <label>{{.i18n.Tr "repo.modelarts.train_job.query_whether_save_parameter"}} | |||||
| <span> | |||||
| <i class="question circle icon link" data-content={{.i18n.Tr "repo.modelarts.train_job.save_helper"}} data-position="right center" data-variation="mini"></i> | |||||
| </span> | |||||
| </label> | |||||
| </div> | |||||
| </div> | |||||
| <div class="disabled field" id="save_para"> | |||||
| <div class="field"> | |||||
| <label>{{.i18n.Tr "repo.modelarts.train_job.job_parameter_name"}}</label> | |||||
| <input name="parameter_template_name" id="parameter_template_name" tabindex="3" autofocus maxlength="255"> | |||||
| </div> | |||||
| <div class="field"> | |||||
| <label for="parameter_description">{{.i18n.Tr "repo.modelarts.train_job.parameter_description"}}</label> | |||||
| <textarea id="parameter_description" name="parameter_description" rows="2"></textarea> | |||||
| </div> | |||||
| </div> | |||||
| --> | |||||
| <div class="inline unite min_title field"> | |||||
| <button class="ui create_train_job green button"> | |||||
| {{.i18n.Tr "repo.cloudbrain.new"}} | |||||
| </button> | |||||
| <a class="ui button" href="/">{{.i18n.Tr "repo.cloudbrain.cancel"}}</a> | |||||
| </div> | |||||
| <!-- 模态框 --> | |||||
| </form> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| {{template "base/footer" .}} | |||||
| <script> | |||||
| let url_href = window.location.pathname.split('create')[0] | |||||
| $(".ui.button").attr('href',url_href) | |||||
| $('select.dropdown') | |||||
| .dropdown(); | |||||
| $('.menu .item') | |||||
| .tab(); | |||||
| let sever_num = $('#trainjob_work_server_num') | |||||
| $('.add').click(function(){ | |||||
| sever_num.val(parseInt(sever_num.val())+1) | |||||
| if(sever_num.val()>=26){ | |||||
| sever_num.val(parseInt(sever_num.val())-1) | |||||
| } | |||||
| }) | |||||
| $('.min').click(function(){ | |||||
| sever_num.val(parseInt(sever_num.val())-1) | |||||
| if(sever_num.val()<=0){ | |||||
| sever_num.val(parseInt(sever_num.val())+1) | |||||
| } | |||||
| }) | |||||
| // 参数增加、删除、修改、保存 | |||||
| function Add_parameter(i){ | |||||
| value = '<div class="two fields width85" id= "para'+ i +'">' + | |||||
| '<div class="field">' + | |||||
| '<input type="text" name="shipping_first-name" required placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_name"}}> ' + | |||||
| '</div> ' + | |||||
| '<div class="field"> ' + | |||||
| '<input type="text" name="shipping_last-name" required placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_value"}}>' + | |||||
| '</div>'+ | |||||
| '<span>' + | |||||
| '<i class="trash icon">' + | |||||
| '</i>' + | |||||
| '</span>' + | |||||
| '</div>' | |||||
| $(".dynamic.field").append(value) | |||||
| } | |||||
| $('#add_run_para').click(function(){ | |||||
| var len = $(".dynamic.field .two.fields").length | |||||
| Add_parameter(len) | |||||
| }); | |||||
| $(".dynamic.field").on("click",".trash.icon", function() { | |||||
| var index = $(this).parent().parent().index() | |||||
| $(this).parent().parent().remove() | |||||
| var len = $(".dynamic.field .two.fields").length | |||||
| $(".dynamic.field .two.fields").each(function(){ | |||||
| var cur_index = $(this).index() | |||||
| $(this).attr('id', 'para' + cur_index) | |||||
| }) | |||||
| }); | |||||
| $('.ui.parameter.green.button').click(function(){ | |||||
| var parameters = []; | |||||
| $('table tr').each(function() { | |||||
| $(this).find('td:eq(1)').each(function(){ | |||||
| parameters.push($(this).text()); | |||||
| }) | |||||
| $(this).find('input').each(function(){ | |||||
| parameters.push($(this).text()) | |||||
| }) | |||||
| }); | |||||
| console.log(parameters) | |||||
| $('.ui.parameter.modal') | |||||
| .modal('hide'); | |||||
| for(var i = 2; i < parameters.length; i++){ | |||||
| switch(i) { | |||||
| // 数据集uuid待完成 | |||||
| // case (2): | |||||
| // console.log(1) | |||||
| // break; | |||||
| // $("#trainjob_datasets").val(parameters[i]); | |||||
| // console.log($("#trainjob_datasets").val()) | |||||
| case (3): | |||||
| $("input[name='boot_file']").val(parameters[i]); | |||||
| break; | |||||
| case (4): | |||||
| var para = parameters[i].split(" ") | |||||
| for(var j = 0; j < para.length; j++){ | |||||
| var para_name = para[j].split('=')[0] | |||||
| var para_value = para[j].split('=')[1] | |||||
| var len = $(".dynamic.field .two.fields").length | |||||
| Add_parameter(len) | |||||
| var pid = 'para' + len | |||||
| $(".dynamic.field"+ " #" + pid + "").find("input[name=shipping_first-name]").val(para_name) | |||||
| $(".dynamic.field"+ " #" + pid + "").find("input[name=shipping_last-name]").val(para_value) | |||||
| } | |||||
| break; | |||||
| // 数据集pool_id待完成 | |||||
| // case (5): | |||||
| // $("select[name='pool_id']").val(parameters[i]); | |||||
| // break; | |||||
| case (6): | |||||
| $("input[name='work_server_number']").val(parameters[i]); | |||||
| break; | |||||
| } | |||||
| } | |||||
| }) | |||||
| $('.ui.save.checkbox').click(function(){ | |||||
| $(this).checkbox({ | |||||
| onChange: function(){ | |||||
| if ($('.ui.save.checkbox').checkbox('is checked')){ | |||||
| $('#save_para').removeClass("disabled") | |||||
| }else{ | |||||
| $('#save_para').addClass("disabled") | |||||
| } | |||||
| } | |||||
| }); | |||||
| }) | |||||
| $('.question.circle.icon').hover(function(){ | |||||
| $(this).popup('show') | |||||
| }); | |||||
| $(".item.active.parameter_config").click(function(){ | |||||
| $('.ui.parameter.modal') | |||||
| .modal('setting', 'closable', false) | |||||
| .modal('show'); | |||||
| }) | |||||
| $('.ui.deny.button').click(function(){ | |||||
| $('.ui.parameter.modal') | |||||
| .modal('hide'); | |||||
| }) | |||||
| $('select.dropdown') | |||||
| .dropdown(); | |||||
| $('.ui.form') | |||||
| .form({ | |||||
| on: 'blur', | |||||
| inline:true, | |||||
| fields: { | |||||
| boot_file: { | |||||
| identifier : 'boot_file', | |||||
| rules: [ | |||||
| { | |||||
| type: 'regExp[/.+\.py$/g]', | |||||
| prompt : '启动文件必须为.py结尾' | |||||
| } | |||||
| ] | |||||
| }, | |||||
| job_name:{ | |||||
| identifier : 'job_name', | |||||
| rules: [ | |||||
| { | |||||
| type: 'regExp[/^[a-zA-Z0-9-_]{1,36}$/]', | |||||
| prompt : '只包含大小写字母、数字、_和-,最长36个字符。' | |||||
| } | |||||
| ] | |||||
| }, | |||||
| attachment:{ | |||||
| identifier : 'attachment', | |||||
| rules: [ | |||||
| { | |||||
| type: 'empty', | |||||
| prompt : '选择一个数据集' | |||||
| } | |||||
| ] | |||||
| }, | |||||
| work_server_number: { | |||||
| identifier : 'work_server_number', | |||||
| rules: [ | |||||
| { | |||||
| type : 'integer[1..25]', | |||||
| prompt : '计算节点需要在1-25之间,请您键入正确的值' | |||||
| } | |||||
| ] | |||||
| } | |||||
| }, | |||||
| }) | |||||
| function validate(){ | |||||
| $('.ui.form') | |||||
| .form({ | |||||
| on: 'blur', | |||||
| inline:true, | |||||
| fields: { | |||||
| boot_file: { | |||||
| identifier : 'boot_file', | |||||
| rules: [ | |||||
| { | |||||
| type: 'regExp[/.+\.py$/g]', | |||||
| prompt : '启动文件必须为.py结尾' | |||||
| } | |||||
| ] | |||||
| }, | |||||
| job_name:{ | |||||
| identifier : 'job_name', | |||||
| rules: [ | |||||
| { | |||||
| type: 'regExp[/^[a-zA-Z0-9-_]{1,36}$/]', | |||||
| prompt : '只包含大小写字母、数字、_和-,最长36个字符。' | |||||
| } | |||||
| ] | |||||
| }, | |||||
| attachment:{ | |||||
| identifier : 'attachment', | |||||
| rules: [ | |||||
| { | |||||
| type: 'empty', | |||||
| prompt : '选择一个数据集' | |||||
| } | |||||
| ] | |||||
| }, | |||||
| work_server_number: { | |||||
| identifier : 'work_server_number', | |||||
| rules: [ | |||||
| { | |||||
| type : 'integer[1..25]', | |||||
| prompt : '计算节点需要在1-25之间,请您键入正确的值' | |||||
| } | |||||
| ] | |||||
| } | |||||
| }, | |||||
| onSuccess: function(){ | |||||
| // $('.ui.page.dimmer').dimmer('show') | |||||
| document.getElementById("mask").style.display = "block" | |||||
| }, | |||||
| onFailure: function(e){ | |||||
| return false; | |||||
| } | |||||
| }) | |||||
| } | |||||
| document.onreadystatechange = function() { | |||||
| if (document.readyState === "complete") { | |||||
| document.getElementById("mask").style.display = "none" | |||||
| } | |||||
| } | |||||
| function send_run_para(){ | |||||
| var run_parameters = [] | |||||
| var msg = {} | |||||
| $(".dynamic.field .two.fields").each(function(){ | |||||
| var para_name = $(this).find('input[name=shipping_first-name]').val() | |||||
| var para_value = $(this).find('input[name=shipping_last-name]').val() | |||||
| run_parameters.push({"label": para_name, "value": para_value}) | |||||
| }) | |||||
| msg["parameter"] = run_parameters | |||||
| msg = JSON.stringify(msg) | |||||
| $('#store_run_para').val(msg) | |||||
| } | |||||
| $('.ui.create_train_job.green.button').click(function(e) { | |||||
| send_run_para() | |||||
| validate() | |||||
| }) | |||||
| </script> | |||||
| @@ -0,0 +1,154 @@ | |||||
| {{template "base/head" .}} | |||||
| <div class="modelarts"> | |||||
| <div class="repository release modelarts train_job view container"> | |||||
| {{template "repo/header" .}} | |||||
| <div class="ui container"> | |||||
| <div class="ui grid"> | |||||
| {{template "repo/modelarts/navbar" .}} | |||||
| <!-- 右侧 --> | |||||
| <div class="ui thirteen wide column"> | |||||
| <div class="ui column stackable grid"> | |||||
| <div class="column"> | |||||
| <h2>{{.i18n.Tr "repo.modelarts.train_job_para_admin"}}</h2> | |||||
| </div> | |||||
| </div> | |||||
| <div class="ui divider"></div> | |||||
| <div class="ui grid"> | |||||
| <div class="row"> | |||||
| <div class="ui sixteen wide column"> | |||||
| <div class="ui sixteen wide column"> | |||||
| <div class="ui two column stackable grid"> | |||||
| <div class="column"> | |||||
| </div> | |||||
| <div class="column right aligned"> | |||||
| <div class="ui right dropdown type jump item"> | |||||
| <span class="text"> | |||||
| {{.i18n.Tr "repo.issues.filter_sort"}}<i class="dropdown icon"></i> | |||||
| </span> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <!-- 任务展示 --> | |||||
| <div class="dataset list"> | |||||
| {{range .Tasks}} | |||||
| <div class="ui grid stackable item"> | |||||
| <div class="row"> | |||||
| <!-- 任务名 --> | |||||
| <div class="five wide column"> | |||||
| <a class="title" href="{{$.Link}}/{{.JobID}}"> | |||||
| <span class="fitted">{{svg "octicon-tasklist" 16}}</span> | |||||
| <span class="fitted">{{.JobName}}</span> | |||||
| </a> | |||||
| </div> | |||||
| <!-- 引擎类型--> | |||||
| <div class="four wide column job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}"> | |||||
| {{.Status}} | |||||
| </div> | |||||
| <!-- 创建时间 --> | |||||
| <div class="three wide column"> | |||||
| <span class="ui text center">{{svg "octicon-clock" 16}} {{TimeSinceUnix .CreatedUnix $.Lang}}</span> | |||||
| </div> | |||||
| <!-- 编辑 --> | |||||
| <div class="two wide column"> | |||||
| <a class="title" href="{{$.Link}}/{{.JobID}}/edit"> | |||||
| <span class="fitted">编辑</span> | |||||
| </a> | |||||
| </div> | |||||
| <!-- 删除 --> | |||||
| <div class="two wide column"> | |||||
| <div class="ui text center clipboard"> | |||||
| <form id="delForm-{{.JobID}}" action="{{$.Link}}/{{.JobID}}/del" method="post"> | |||||
| {{$.CsrfTokenHtml}} | |||||
| <a class="fitted" onclick="assertDelete(this)" style="font-size:16px; font-weight:bold">删除</a> | |||||
| </form> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| {{end}} {{template "base/paginate" .}} | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <!-- 确认模态框 --> | |||||
| <div id="deletemodel"> | |||||
| <div class="ui basic modal"> | |||||
| <div class="ui icon header"> | |||||
| <i class="trash icon"></i> 删除任务 | |||||
| </div> | |||||
| <div class="content"> | |||||
| <p>你确认删除该任务么?此任务一旦删除不可恢复。</p> | |||||
| </div> | |||||
| <div class="actions"> | |||||
| <div class="ui red basic inverted cancel button"> | |||||
| <i class="remove icon"></i> 取消操作 | |||||
| </div> | |||||
| <div class="ui green basic inverted ok button"> | |||||
| <i class="checkmark icon"></i> 确定操作 | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| {{template "base/footer" .}} | |||||
| <script> | |||||
| // 删除时用户确认 | |||||
| function assertDelete(obj) { | |||||
| if (obj.style.color == "rgb(204, 204, 204)") { | |||||
| return | |||||
| } else { | |||||
| var delId = obj.parentNode.id | |||||
| flag = 1; | |||||
| $('.ui.basic.modal') | |||||
| .modal({ | |||||
| onDeny: function() { | |||||
| flag = false | |||||
| }, | |||||
| onApprove: function() { | |||||
| document.getElementById(delId).submit() | |||||
| flag = true | |||||
| }, | |||||
| onHidden: function() { | |||||
| if (flag == false) { | |||||
| $('.alert').html('您已取消操作').removeClass('alert-success').addClass('alert-danger').show().delay(1500).fadeOut(); | |||||
| } | |||||
| } | |||||
| }) | |||||
| .modal('show') | |||||
| } | |||||
| } | |||||
| // 加载任务状态 | |||||
| $(document).ready(function() { | |||||
| $(".job-status").each((index, job) => { | |||||
| const jobID = job.dataset.jobid; | |||||
| const repoPath = job.dataset.repopath; | |||||
| if (job.textContent.trim() == 'STOPPED') { | |||||
| return | |||||
| } | |||||
| $.get(`/api/v1/repos/${repoPath}/modelarts/train-job/${jobID}`, (data) => { | |||||
| const jobID = data.JobID | |||||
| const status = data.JobStatus | |||||
| $('#' + jobID).text(status) | |||||
| // console.log(data) | |||||
| }).fail(function(err) { | |||||
| console.log(err); | |||||
| }); | |||||
| }); | |||||
| }); | |||||
| </script> | |||||
| @@ -0,0 +1,200 @@ | |||||
| {{template "base/head" .}} | |||||
| <div class="repository"> | |||||
| {{template "repo/header" .}} | |||||
| <div class="repository new repo ui middle very relaxed page grid"> | |||||
| <div class="column"> | |||||
| {{template "base/alert" .}} | |||||
| <h4 class="ui top attached header"> | |||||
| <div class="ui two column grid"> | |||||
| <div class="column"> | |||||
| {{$.i18n.Tr "repo.modelarts.version_manage"}} | |||||
| </div> | |||||
| <div class="column right aligned"> | |||||
| <a href="javascript:window.history.back();">{{svg "octicon-reply" 16}}{{$.i18n.Tr "repo.modelarts.back"}}</a> | |||||
| </div> | |||||
| </div> | |||||
| </h4> | |||||
| <div class="ui attached segment"> | |||||
| <div class="ui style accordion"> | |||||
| <div class="title active"> | |||||
| <i class="dropdown icon"></i> | |||||
| {{$.i18n.Tr "repo.modelarts.train_job.version"}} | |||||
| </div> | |||||
| <div class="content active"> | |||||
| <div class="ui container"> | |||||
| <div class="ui top attached tabular menu"> | |||||
| <a class="item active" data-tab="configs">{{$.i18n.Tr "repo.modelarts.train_job.config"}}</a> | |||||
| <a class="item logs" data-tab="logs">{{$.i18n.Tr "repo.modelarts.log"}}</a> | |||||
| <!-- <a class="item" data-tab="resources">资源占用情况</a> --> | |||||
| </div> | |||||
| <div class="ui bottom attached tab segment active" data-tab="configs"> | |||||
| <div> | |||||
| <div class="ui yellow segment"> | |||||
| <table class="ui celled striped table"> | |||||
| <thead> | |||||
| <tr> <th colspan="2"> {{.i18n.Tr "repo.modelarts.train_job.basic_info"}} </th> </tr> | |||||
| </thead> | |||||
| <tbody> | |||||
| <tr> | |||||
| <td class="four wide"> {{.i18n.Tr "repo.modelarts.train_job.job_name"}} </td> | |||||
| <td>{{.result.JobName}}</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="four wide"> {{.i18n.Tr "repo.modelarts.train_job.job_status"}} </td> | |||||
| <td>{{.result.Status}}</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="four wide"> {{.i18n.Tr "repo.modelarts.train_job.version"}} </td> | |||||
| <td>{{.result.VersionName}}</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="four wide"> {{.i18n.Tr "repo.modelarts.train_job.start_time"}} </td> | |||||
| <td>{{.result.CreateTime}}</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="four wide"> {{.i18n.Tr "repo.modelarts.train_job.dura_time"}} </td> | |||||
| <td>{{.result.TrainJobDuration}}</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="four wide"> {{.i18n.Tr "repo.modelarts.train_job.description"}} </td> | |||||
| <td>{{.result.Description}}</td> | |||||
| </tr> | |||||
| </tbody> | |||||
| </table> | |||||
| </div> | |||||
| <div class="ui green segment"> | |||||
| <table class="ui celled striped table"> | |||||
| <thead> | |||||
| <tr> <th colspan="2"> {{.i18n.Tr "repo.modelarts.train_job.parameter_setting_info"}} </th> </tr> | |||||
| </thead> | |||||
| <tbody> | |||||
| <tr> | |||||
| <td class="four wide"> {{.i18n.Tr "repo.modelarts.train_job.AI_driver"}} </td> | |||||
| <td>{{.result.EngineName}} | {{.result.EngineVersion}}</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="four wide"> {{.i18n.Tr "repo.modelarts.train_job.start_file"}}</td> | |||||
| <td>{{.result.BootFileUrl}}</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="four wide"> {{.i18n.Tr "repo.modelarts.train_job.dataset"}} </td> | |||||
| <td>{{.result.DatasetName}}</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="four wide"> {{.i18n.Tr "repo.modelarts.train_job.run_parameter"}} </td> | |||||
| <td>{{.result.Parameter}}</td> | |||||
| </tr> | |||||
| </tbody> | |||||
| </table> | |||||
| </div> | |||||
| <div class="ui blue segment"> | |||||
| <table class="ui celled striped table"> | |||||
| <thead> | |||||
| <tr> <th colspan="2"> {{.i18n.Tr "repo.modelarts.train_job.resource_setting_info"}} </th> </tr> | |||||
| </thead> | |||||
| <tbody> | |||||
| <tr> | |||||
| <td class="four wide"> {{.i18n.Tr "repo.modelarts.train_job.resource_pool"}} </td> | |||||
| <td>{{.result.PoolName}}</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="four wide"> {{.i18n.Tr "repo.modelarts.train_job.amount_of_compute_node"}}</td> | |||||
| <td>{{.result.WorkServerNum}}</td> | |||||
| </tr> | |||||
| <tr> | |||||
| <td class="four wide"> {{.i18n.Tr "repo.modelarts.train_job.NAS_mount_path"}} </td> | |||||
| <td>{{.result.NasMountPath}}</td> | |||||
| </tr> | |||||
| </tbody> | |||||
| </table> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <div class="ui bottom attached tab segment" data-tab="logs"> | |||||
| <div class="ui message" style="display: none;"> | |||||
| <div class="header"> | |||||
| </div> | |||||
| </div> | |||||
| <div class="ui top attached segment" style="background: #f0f0f0;"> | |||||
| <div class="center aligned"> | |||||
| <label>{{$.i18n.Tr "repo.modelarts.log"}}:</label> | |||||
| <span class="fitted file_name">{{.log_file_name}}</span> | |||||
| <input type="hidden" name="file_name" value={{.log_file_name}}> | |||||
| <input type="hidden" name="start_line" value={{.log.StartLine}}> | |||||
| <input type="hidden" name="end_line" value={{.log.EndLine}}> | |||||
| </div> | |||||
| </div> | |||||
| <div class="ui attached segment log" style="height: 300px !important; overflow: auto;"> | |||||
| <pre>{{.log.Content}}</pre> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| {{template "base/footer" .}} | |||||
| <script> | |||||
| $('.menu .item').tab() | |||||
| $('.ui.style.accordion').accordion(); | |||||
| var userName | |||||
| var repoPath | |||||
| var jobID | |||||
| $(document).ready(function(){ | |||||
| var url = window.location.href; | |||||
| var urlArr = url.split('/') | |||||
| userName = urlArr.slice(-5)[0] | |||||
| repoPath = urlArr.slice(-4)[0] | |||||
| jobID = urlArr.slice(-1)[0] | |||||
| }) | |||||
| $(".log").scroll(function () { | |||||
| var scrollTop = $(this)[0].scrollTop; // 滚动距离 | |||||
| var scrollHeight = $(this)[0].scrollHeight; // 文档高度 | |||||
| var divHeight = $(this).height(); // 可视区高度 | |||||
| var file_name = $('input[name=file_name]').val() | |||||
| if(parseInt(scrollTop) + divHeight + 29 == scrollHeight){ | |||||
| var end_line = $('input[name=end_line]').val() | |||||
| $.get(`/api/v1/repos/${userName}/${repoPath}/modelarts/train-job/${jobID}/log?file_name=${file_name}&base_line=${end_line}&order=desc`, (data) => { | |||||
| if (data.lines == 0){ | |||||
| $('.header').text('您已翻阅至日志底部') | |||||
| $('.message').css('display', 'block') | |||||
| setTimeout(function(){ | |||||
| $('.message').css('display', 'none') | |||||
| }, 1000) | |||||
| }else{ | |||||
| $('input[name=end_line]').val(data.EndLine) | |||||
| $('.log').append('<pre>' + data.Content) | |||||
| } | |||||
| }).fail(function(err) { | |||||
| console.log(err); | |||||
| }); | |||||
| } | |||||
| if(scrollTop == 0){ | |||||
| var start_line = $('input[name=start_line]').val() | |||||
| $.get(`/api/v1/repos/${userName}/${repoPath}/modelarts/train-job/${jobID}/log?file_name=${file_name}&base_line=${start_line}&order=asc`, (data) => { | |||||
| if (data.lines == 0){ | |||||
| $('.header').text('您已翻阅至日志顶部') | |||||
| $('.message').css('display', 'block') | |||||
| setTimeout(function(){ | |||||
| $('.message').css('display', 'none') | |||||
| }, 1000) | |||||
| }else{ | |||||
| $('input[name=start_line]').val(data.StartLine) //如果变动就改变所对应的值 | |||||
| $(".log").prepend('<pre>' + data.Content) | |||||
| } | |||||
| }).fail(function(err) { | |||||
| console.log(err); | |||||
| }); | |||||
| } | |||||
| }) | |||||
| </script> | |||||
| @@ -444,6 +444,7 @@ github.com/golang/protobuf/ptypes/timestamp | |||||
| # github.com/golang/snappy v0.0.1 | # github.com/golang/snappy v0.0.1 | ||||
| github.com/golang/snappy | github.com/golang/snappy | ||||
| # github.com/gomodule/redigo v2.0.0+incompatible | # github.com/gomodule/redigo v2.0.0+incompatible | ||||
| ## explicit | |||||
| github.com/gomodule/redigo/internal | github.com/gomodule/redigo/internal | ||||
| github.com/gomodule/redigo/redis | github.com/gomodule/redigo/redis | ||||
| # github.com/google/go-github/v24 v24.0.1 | # github.com/google/go-github/v24 v24.0.1 | ||||
| @@ -11,16 +11,16 @@ | |||||
| <div class="icon-wrapper"> | <div class="icon-wrapper"> | ||||
| <i style="line-height: 1.5;color: #303643;font-weight: 900;" v-if="showInitTopic[i]" class="el-icon-check" ></i> | <i style="line-height: 1.5;color: #303643;font-weight: 900;" v-if="showInitTopic[i]" class="el-icon-check" ></i> | ||||
| </div> | </div> | ||||
| <div class="text">{{arr.topic_name}} </div> | |||||
| <div class="text">{{arr.topic_name.toLowerCase()}} </div> | |||||
| </div> | </div> | ||||
| <div v-if="showInputValue" class="addition item-text" @click="postTopic"> | <div v-if="showInputValue" class="addition item-text" @click="postTopic"> | ||||
| 点击或回车添加<b class="user-add-label-text">{{input}}</b>标签 | |||||
| 点击或回车添加<b class="user-add-label-text">{{input.toLowerCase()}}</b>标签 | |||||
| </div> | </div> | ||||
| <div v-if="showAddTopic" class="item-text" @click="addPostTopic"> | <div v-if="showAddTopic" class="item-text" @click="addPostTopic"> | ||||
| <div class="icon-wrapper"> | <div class="icon-wrapper"> | ||||
| <i style="line-height: 1.5;color: #303643;font-weight: 900;" v-if="showAddFlage" class="el-icon-check" ></i> | <i style="line-height: 1.5;color: #303643;font-weight: 900;" v-if="showAddFlage" class="el-icon-check" ></i> | ||||
| </div> | </div> | ||||
| <div class="text">{{input}}</div> | |||||
| <div class="text">{{input.toLowerCase()}}</div> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| @@ -134,7 +134,7 @@ export default { | |||||
| this.showSearchTopic = true | this.showSearchTopic = true | ||||
| } | } | ||||
| else if(this.arrayTopics.indexOf(this.input)>-1){ | |||||
| else if(this.arrayTopics.indexOf(this.input.toLowerCase())>-1){ | |||||
| this.showInputValue = false | this.showInputValue = false | ||||
| this.showSearchTopic = false | this.showSearchTopic = false | ||||
| @@ -142,7 +142,7 @@ export default { | |||||
| this.showInitTopic = [] | this.showInitTopic = [] | ||||
| let timestamp=new Date().getTime() | let timestamp=new Date().getTime() | ||||
| this.params.q = this.input | |||||
| this.params.q = this.input.toLowerCase() | |||||
| this.params._ = timestamp | this.params._ = timestamp | ||||
| this.$axios.get('/api/v1/topics/search',{ | this.$axios.get('/api/v1/topics/search',{ | ||||
| params:this.params | params:this.params | ||||
| @@ -164,7 +164,7 @@ export default { | |||||
| let findelement = this.array.some((item)=>{ | let findelement = this.array.some((item)=>{ | ||||
| return item.topic_name===this.input | |||||
| return item.topic_name===this.input.toLowerCase() | |||||
| }) | }) | ||||
| this.showInputValue = !findelement | this.showInputValue = !findelement | ||||
| @@ -224,11 +224,11 @@ export default { | |||||
| return | return | ||||
| }else{ | }else{ | ||||
| let topic = this.input | let topic = this.input | ||||
| if(this.arrayTopics.includes(topic)){ | |||||
| if(this.arrayTopics.includes(topic.toLowerCase())){ | |||||
| return | return | ||||
| } | } | ||||
| else{ | else{ | ||||
| this.arrayTopics.push(topic) | |||||
| this.arrayTopics.push(topic.toLowerCase()) | |||||
| let topics = this.arrayTopics | let topics = this.arrayTopics | ||||
| let strTopics = topics.join(',') | let strTopics = topics.join(',') | ||||
| @@ -250,7 +250,10 @@ export default { | |||||
| addPostTopic(){ | addPostTopic(){ | ||||
| if(this.showAddFlage){ | if(this.showAddFlage){ | ||||
| this.arrayTopics.pop() | |||||
| // this.arrayTopics.pop() | |||||
| let cancleIndex = this.arrayTopics.indexOf(this.input) | |||||
| this.arrayTopics.splice(cancleIndex,1) | |||||
| let topics = this.arrayTopics | let topics = this.arrayTopics | ||||
| let strTopics = topics.join(',') | let strTopics = topics.join(',') | ||||
| let data = this.qs.stringify({ | let data = this.qs.stringify({ | ||||
| @@ -268,7 +271,7 @@ export default { | |||||
| } | } | ||||
| else if(!this.showAddFlage){ | else if(!this.showAddFlage){ | ||||
| let topic = this.input | let topic = this.input | ||||
| this.arrayTopics.push(topic) | |||||
| this.arrayTopics.push(topic.toLowerCase()) | |||||
| let topics = this.arrayTopics | let topics = this.arrayTopics | ||||
| let strTopics = topics.join(',') | let strTopics = topics.join(',') | ||||
| @@ -224,15 +224,17 @@ footer .column{margin-bottom:0!important; padding-bottom:0!important;} | |||||
| // icon cloudbrain | // icon cloudbrain | ||||
| .i-round{display:inline-block;width:18px;height:18px;background:url("/img/icons.svg");background-position: -496px -52px;} | .i-round{display:inline-block;width:18px;height:18px;background:url("/img/icons.svg");background-position: -496px -52px;} | ||||
| .i-bg-organ{background-position: -496px -52px;} | .i-bg-organ{background-position: -496px -52px;} | ||||
| .STOPPED{display:inline-block;width:18px;height:18px;background:url("/img/icons.svg");background-position: -496px -52px;background-position: -459px -52px;} | |||||
| .STOPPED, .KILLED{display:inline-block;width:18px;height:18px;background:url("/img/icons.svg");background-position: -496px -52px;background-position: -459px -52px;} | |||||
| .RUNNING{display:inline-block;width:18px;height:18px;background:url("/img/icons.svg");background-position: -496px -52px;background-position: -478px -52px;} | .RUNNING{display:inline-block;width:18px;height:18px;background:url("/img/icons.svg");background-position: -496px -52px;background-position: -478px -52px;} | ||||
| .i-bg-orange{background-position: -495px -51px;} | .i-bg-orange{background-position: -495px -51px;} | ||||
| .FAILED{display:inline-block;width:18px;height:18px;background:url("/img/icons.svg");background-position: -496px -52px;background-position: -532px -52px;} | |||||
| .FAILED,.START_FAILED{display:inline-block;width:18px;height:18px;background:url("/img/icons.svg");background-position: -496px -52px;background-position: -532px -52px;} | |||||
| .i-bg-green{background-position: -441px -52px;} | .i-bg-green{background-position: -441px -52px;} | ||||
| .i-bg-used{background-position: -514px -52px;} | .i-bg-used{background-position: -514px -52px;} | ||||
| .icon-bind{background-position: -550px -52px;} | .icon-bind{background-position: -550px -52px;} | ||||
| .icon-unbind{background-position: -568px -52px;} | .icon-unbind{background-position: -568px -52px;} | ||||
| .CREATING, .STOPPING, .DELETING, .STARTING, .WAITING{display:inline-block;background-image:url('/img/loading.gif');background-repeat:no-repeat;width:16px;height:16px;background-size:16px 16px;margin-right:5px;} | |||||
| .CREATING, .STOPPING, .DELETING, .STARTING, .WAITING ,.INIT,.KILLING{display:inline-block;background-image:url('/img/loading.gif');background-repeat:no-repeat;width:16px;height:16px;background-size:16px 16px;margin-right:5px;} | |||||
| .COMPLETED{display:inline-block;width:18px;height:18px;background:url("/img/icons.svg");background-position: -496px -52px;background-position: -441px -52px;} | |||||
| .text_over{ | .text_over{ | ||||
| overflow: hidden; | overflow: hidden; | ||||
| text-overflow: ellipsis; | text-overflow: ellipsis; | ||||