| @@ -0,0 +1,203 @@ | |||||
| package models | |||||
| import ( | |||||
| "fmt" | |||||
| "code.gitea.io/gitea/modules/log" | |||||
| "code.gitea.io/gitea/modules/setting" | |||||
| "code.gitea.io/gitea/modules/timeutil" | |||||
| "xorm.io/builder" | |||||
| "xorm.io/xorm" | |||||
| ) | |||||
| type AiModelManage struct { | |||||
| ID string `xorm:"pk"` | |||||
| Name string `xorm:"NOT NULL"` | |||||
| Version string `xorm:"NOT NULL"` | |||||
| VersionCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| New int `xorm:"NOT NULL"` | |||||
| Type int `xorm:"NOT NULL"` | |||||
| Size int64 `xorm:"NOT NULL"` | |||||
| Description string `xorm:"varchar(2000)"` | |||||
| Label string `xorm:"varchar(1000)"` | |||||
| Path string `xorm:"varchar(400) NOT NULL"` | |||||
| DownloadCount int `xorm:"NOT NULL DEFAULT 0"` | |||||
| Engine int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
| Status int `xorm:"NOT NULL DEFAULT 0"` | |||||
| Accuracy string `xorm:"varchar(1000)"` | |||||
| AttachmentId string `xorm:"NULL"` | |||||
| RepoId int64 `xorm:"NULL"` | |||||
| CodeBranch string `xorm:"varchar(400) NULL"` | |||||
| CodeCommitID string `xorm:"NULL"` | |||||
| UserId int64 `xorm:"NOT NULL"` | |||||
| UserName string `xorm:"NULL"` | |||||
| UserRelAvatarLink string `xorm:"NULL"` | |||||
| TrainTaskInfo string `xorm:"text NULL"` | |||||
| CreatedUnix timeutil.TimeStamp `xorm:"created"` | |||||
| UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` | |||||
| IsCanOper bool | |||||
| } | |||||
| type AiModelQueryOptions struct { | |||||
| ListOptions | |||||
| RepoID int64 // include all repos if empty | |||||
| UserID int64 | |||||
| ModelID string | |||||
| SortType string | |||||
| New int | |||||
| // JobStatus CloudbrainStatus | |||||
| Type int | |||||
| } | |||||
| func SaveModelToDb(model *AiModelManage) error { | |||||
| sess := x.NewSession() | |||||
| defer sess.Close() | |||||
| re, err := sess.Insert(model) | |||||
| if err != nil { | |||||
| log.Info("insert error." + err.Error()) | |||||
| return err | |||||
| } | |||||
| log.Info("success to save db.re=" + fmt.Sprint((re))) | |||||
| return nil | |||||
| } | |||||
| func QueryModelById(id string) (*AiModelManage, error) { | |||||
| sess := x.NewSession() | |||||
| defer sess.Close() | |||||
| sess.Select("*").Table("ai_model_manage"). | |||||
| Where("id='" + id + "'") | |||||
| aiModelManageList := make([]*AiModelManage, 0) | |||||
| err := sess.Find(&aiModelManageList) | |||||
| if err == nil { | |||||
| if len(aiModelManageList) == 1 { | |||||
| return aiModelManageList[0], nil | |||||
| } | |||||
| } | |||||
| return nil, err | |||||
| } | |||||
| func DeleteModelById(id string) error { | |||||
| sess := x.NewSession() | |||||
| defer sess.Close() | |||||
| re, err := sess.Delete(&AiModelManage{ | |||||
| ID: id, | |||||
| }) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| log.Info("success to delete from db.re=" + fmt.Sprint((re))) | |||||
| return nil | |||||
| } | |||||
| func ModifyModelDescription(id string, description string) error { | |||||
| var sess *xorm.Session | |||||
| sess = x.ID(id) | |||||
| defer sess.Close() | |||||
| re, err := sess.Cols("description").Update(&AiModelManage{ | |||||
| Description: description, | |||||
| }) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| log.Info("success to update description from db.re=" + fmt.Sprint((re))) | |||||
| return nil | |||||
| } | |||||
| func ModifyModelNewProperty(id string, new int, versioncount int) error { | |||||
| var sess *xorm.Session | |||||
| sess = x.ID(id) | |||||
| defer sess.Close() | |||||
| re, err := sess.Cols("new", "version_count").Update(&AiModelManage{ | |||||
| New: new, | |||||
| VersionCount: versioncount, | |||||
| }) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| log.Info("success to update new property from db.re=" + fmt.Sprint((re))) | |||||
| return nil | |||||
| } | |||||
| func ModifyModelDownloadCount(id string) error { | |||||
| sess := x.NewSession() | |||||
| defer sess.Close() | |||||
| if _, err := sess.Exec("UPDATE `ai_model_manage` SET download_count = download_count + 1 WHERE id = ?", id); err != nil { | |||||
| return err | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func QueryModelByName(name string, repoId int64) []*AiModelManage { | |||||
| sess := x.NewSession() | |||||
| defer sess.Close() | |||||
| sess.Select("*").Table("ai_model_manage"). | |||||
| Where("name='" + name + "' and repo_id=" + fmt.Sprint(repoId)).OrderBy("version desc") | |||||
| aiModelManageList := make([]*AiModelManage, 0) | |||||
| sess.Find(&aiModelManageList) | |||||
| return aiModelManageList | |||||
| } | |||||
| func QueryModel(opts *AiModelQueryOptions) ([]*AiModelManage, int64, error) { | |||||
| sess := x.NewSession() | |||||
| defer sess.Close() | |||||
| var cond = builder.NewCond() | |||||
| if opts.RepoID > 0 { | |||||
| cond = cond.And( | |||||
| builder.Eq{"ai_model_manage.repo_id": opts.RepoID}, | |||||
| ) | |||||
| } | |||||
| if opts.UserID > 0 { | |||||
| cond = cond.And( | |||||
| builder.Eq{"ai_model_manage.user_id": opts.UserID}, | |||||
| ) | |||||
| } | |||||
| if opts.New >= 0 { | |||||
| cond = cond.And( | |||||
| builder.Eq{"ai_model_manage.new": opts.New}, | |||||
| ) | |||||
| } | |||||
| if len(opts.ModelID) > 0 { | |||||
| cond = cond.And( | |||||
| builder.Eq{"ai_model_manage.id": opts.ModelID}, | |||||
| ) | |||||
| } | |||||
| if (opts.Type) >= 0 { | |||||
| cond = cond.And( | |||||
| builder.Eq{"ai_model_manage.type": opts.Type}, | |||||
| ) | |||||
| } | |||||
| count, err := sess.Where(cond).Count(new(AiModelManage)) | |||||
| if err != nil { | |||||
| return nil, 0, fmt.Errorf("Count: %v", err) | |||||
| } | |||||
| if opts.Page >= 0 && opts.PageSize > 0 { | |||||
| var start int | |||||
| if opts.Page == 0 { | |||||
| start = 0 | |||||
| } else { | |||||
| start = (opts.Page - 1) * opts.PageSize | |||||
| } | |||||
| sess.Limit(opts.PageSize, start) | |||||
| } | |||||
| sess.OrderBy("ai_model_manage.created_unix DESC") | |||||
| aiModelManages := make([]*AiModelManage, 0, setting.UI.IssuePagingNum) | |||||
| if err := sess.Table("ai_model_manage").Where(cond). | |||||
| Find(&aiModelManages); err != nil { | |||||
| return nil, 0, fmt.Errorf("Find: %v", err) | |||||
| } | |||||
| sess.Close() | |||||
| return aiModelManages, count, nil | |||||
| } | |||||
| @@ -929,6 +929,48 @@ func Cloudbrains(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int64, error) { | |||||
| return cloudbrains, count, nil | return cloudbrains, count, nil | ||||
| } | } | ||||
| func QueryModelTrainJobVersionList(jobId string) ([]*CloudbrainInfo, int, error) { | |||||
| sess := x.NewSession() | |||||
| defer sess.Close() | |||||
| var cond = builder.NewCond() | |||||
| cond = cond.And( | |||||
| builder.Eq{"cloudbrain.job_id": jobId}, | |||||
| ) | |||||
| cond = cond.And( | |||||
| builder.Eq{"cloudbrain.Status": "COMPLETED"}, | |||||
| ) | |||||
| sess.OrderBy("cloudbrain.created_unix DESC") | |||||
| cloudbrains := make([]*CloudbrainInfo, 0) | |||||
| if err := sess.Table(&Cloudbrain{}).Where(cond). | |||||
| Find(&cloudbrains); err != nil { | |||||
| return nil, 0, fmt.Errorf("Find: %v", err) | |||||
| } | |||||
| return cloudbrains, int(len(cloudbrains)), nil | |||||
| } | |||||
| func QueryModelTrainJobList(repoId int64) ([]*CloudbrainInfo, int, error) { | |||||
| sess := x.NewSession() | |||||
| defer sess.Close() | |||||
| var cond = builder.NewCond() | |||||
| cond = cond.And( | |||||
| builder.Eq{"repo_id": repoId}, | |||||
| ) | |||||
| cond = cond.And( | |||||
| builder.Eq{"Status": "COMPLETED"}, | |||||
| ) | |||||
| sess.OrderBy("job_id DESC") | |||||
| cloudbrains := make([]*CloudbrainInfo, 0) | |||||
| if err := sess.Distinct("job_id,job_name").Table(&Cloudbrain{}).Where(cond). | |||||
| Find(&cloudbrains); err != nil { | |||||
| return nil, 0, fmt.Errorf("Find: %v", err) | |||||
| } | |||||
| return cloudbrains, int(len(cloudbrains)), nil | |||||
| } | |||||
| func CloudbrainsVersionList(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int, error) { | func CloudbrainsVersionList(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int, error) { | ||||
| sess := x.NewSession() | sess := x.NewSession() | ||||
| defer sess.Close() | defer sess.Close() | ||||
| @@ -133,6 +133,7 @@ func init() { | |||||
| new(FileChunk), | new(FileChunk), | ||||
| new(BlockChain), | new(BlockChain), | ||||
| new(RecommendOrg), | new(RecommendOrg), | ||||
| new(AiModelManage), | |||||
| ) | ) | ||||
| tablesStatistic = append(tablesStatistic, | tablesStatistic = append(tablesStatistic, | ||||
| @@ -1114,6 +1114,12 @@ func CreateRepository(ctx DBContext, doer, u *User, repo *Repository) (err error | |||||
| Type: tp, | Type: tp, | ||||
| Config: &BlockChainConfig{EnableBlockChain: true}, | Config: &BlockChainConfig{EnableBlockChain: true}, | ||||
| }) | }) | ||||
| } else if tp == UnitTypeModelManage { | |||||
| units = append(units, RepoUnit{ | |||||
| RepoID: repo.ID, | |||||
| Type: tp, | |||||
| Config: &ModelManageConfig{EnableModelManage: true}, | |||||
| }) | |||||
| } else { | } else { | ||||
| units = append(units, RepoUnit{ | units = append(units, RepoUnit{ | ||||
| RepoID: repo.ID, | RepoID: repo.ID, | ||||
| @@ -131,6 +131,20 @@ type CloudBrainConfig struct { | |||||
| EnableCloudBrain bool | EnableCloudBrain bool | ||||
| } | } | ||||
| type ModelManageConfig struct { | |||||
| EnableModelManage bool | |||||
| } | |||||
| // FromDB fills up a CloudBrainConfig from serialized format. | |||||
| func (cfg *ModelManageConfig) FromDB(bs []byte) error { | |||||
| return json.Unmarshal(bs, &cfg) | |||||
| } | |||||
| // ToDB exports a CloudBrainConfig to a serialized format. | |||||
| func (cfg *ModelManageConfig) ToDB() ([]byte, error) { | |||||
| return json.Marshal(cfg) | |||||
| } | |||||
| // FromDB fills up a CloudBrainConfig from serialized format. | // FromDB fills up a CloudBrainConfig from serialized format. | ||||
| func (cfg *CloudBrainConfig) FromDB(bs []byte) error { | func (cfg *CloudBrainConfig) FromDB(bs []byte) error { | ||||
| return json.Unmarshal(bs, &cfg) | return json.Unmarshal(bs, &cfg) | ||||
| @@ -176,6 +190,8 @@ func (r *RepoUnit) BeforeSet(colName string, val xorm.Cell) { | |||||
| r.Config = new(CloudBrainConfig) | r.Config = new(CloudBrainConfig) | ||||
| case UnitTypeBlockChain: | case UnitTypeBlockChain: | ||||
| r.Config = new(BlockChainConfig) | r.Config = new(BlockChainConfig) | ||||
| case UnitTypeModelManage: | |||||
| r.Config = new(ModelManageConfig) | |||||
| default: | default: | ||||
| panic("unrecognized repo unit type: " + com.ToStr(*val)) | panic("unrecognized repo unit type: " + com.ToStr(*val)) | ||||
| } | } | ||||
| @@ -27,6 +27,7 @@ const ( | |||||
| UnitTypeDatasets UnitType = 10 // 10 Dataset | UnitTypeDatasets UnitType = 10 // 10 Dataset | ||||
| UnitTypeCloudBrain UnitType = 11 // 11 CloudBrain | UnitTypeCloudBrain UnitType = 11 // 11 CloudBrain | ||||
| UnitTypeBlockChain UnitType = 12 // 12 BlockChain | UnitTypeBlockChain UnitType = 12 // 12 BlockChain | ||||
| UnitTypeModelManage UnitType = 13 // 13 ModelManage | |||||
| ) | ) | ||||
| // Value returns integer value for unit type | // Value returns integer value for unit type | ||||
| @@ -56,6 +57,8 @@ func (u UnitType) String() string { | |||||
| return "UnitTypeCloudBrain" | return "UnitTypeCloudBrain" | ||||
| case UnitTypeBlockChain: | case UnitTypeBlockChain: | ||||
| return "UnitTypeBlockChain" | return "UnitTypeBlockChain" | ||||
| case UnitTypeModelManage: | |||||
| return "UnitTypeModelManage" | |||||
| } | } | ||||
| return fmt.Sprintf("Unknown UnitType %d", u) | return fmt.Sprintf("Unknown UnitType %d", u) | ||||
| } | } | ||||
| @@ -80,6 +83,7 @@ var ( | |||||
| UnitTypeDatasets, | UnitTypeDatasets, | ||||
| UnitTypeCloudBrain, | UnitTypeCloudBrain, | ||||
| UnitTypeBlockChain, | UnitTypeBlockChain, | ||||
| UnitTypeModelManage, | |||||
| } | } | ||||
| // DefaultRepoUnits contains the default unit types | // DefaultRepoUnits contains the default unit types | ||||
| @@ -92,6 +96,7 @@ var ( | |||||
| UnitTypeDatasets, | UnitTypeDatasets, | ||||
| UnitTypeCloudBrain, | UnitTypeCloudBrain, | ||||
| UnitTypeBlockChain, | UnitTypeBlockChain, | ||||
| UnitTypeModelManage, | |||||
| } | } | ||||
| // NotAllowedDefaultRepoUnits contains units that can't be default | // NotAllowedDefaultRepoUnits contains units that can't be default | ||||
| @@ -281,6 +286,14 @@ var ( | |||||
| 7, | 7, | ||||
| } | } | ||||
| UnitModelManage = Unit{ | |||||
| UnitTypeModelManage, | |||||
| "repo.modelmanage", | |||||
| "/modelmanage", | |||||
| "repo.modelmanage.desc", | |||||
| 8, | |||||
| } | |||||
| // Units contains all the units | // Units contains all the units | ||||
| Units = map[UnitType]Unit{ | Units = map[UnitType]Unit{ | ||||
| UnitTypeCode: UnitCode, | UnitTypeCode: UnitCode, | ||||
| @@ -293,6 +306,7 @@ var ( | |||||
| UnitTypeDatasets: UnitDataset, | UnitTypeDatasets: UnitDataset, | ||||
| UnitTypeCloudBrain: UnitCloudBrain, | UnitTypeCloudBrain: UnitCloudBrain, | ||||
| UnitTypeBlockChain: UnitBlockChain, | UnitTypeBlockChain: UnitBlockChain, | ||||
| UnitTypeModelManage: UnitModelManage, | |||||
| } | } | ||||
| ) | ) | ||||
| @@ -122,6 +122,7 @@ type RepoSettingForm struct { | |||||
| // Advanced settings | // Advanced settings | ||||
| EnableDataset bool | EnableDataset bool | ||||
| EnableCloudBrain bool | EnableCloudBrain bool | ||||
| EnableModelManager bool | |||||
| EnableWiki bool | EnableWiki bool | ||||
| EnableExternalWiki bool | EnableExternalWiki bool | ||||
| ExternalWikiURL string | ExternalWikiURL string | ||||
| @@ -821,5 +821,6 @@ func UnitTypes() macaron.Handler { | |||||
| ctx.Data["UnitTypeExternalWiki"] = models.UnitTypeExternalWiki | ctx.Data["UnitTypeExternalWiki"] = models.UnitTypeExternalWiki | ||||
| ctx.Data["UnitTypeExternalTracker"] = models.UnitTypeExternalTracker | ctx.Data["UnitTypeExternalTracker"] = models.UnitTypeExternalTracker | ||||
| ctx.Data["UnitTypeBlockChain"] = models.UnitTypeBlockChain | ctx.Data["UnitTypeBlockChain"] = models.UnitTypeBlockChain | ||||
| ctx.Data["UnitTypeModelManage"] = models.UnitTypeModelManage | |||||
| } | } | ||||
| } | } | ||||
| @@ -5,6 +5,7 @@ | |||||
| package storage | package storage | ||||
| import ( | import ( | ||||
| "errors" | |||||
| "io" | "io" | ||||
| "net/url" | "net/url" | ||||
| "path" | "path" | ||||
| @@ -140,11 +141,51 @@ func ObsMultiPartUpload(uuid string, uploadId string, partNumber int, fileName s | |||||
| } | } | ||||
| func ObsDownload(uuid string, fileName string) (io.ReadCloser, error) { | |||||
| //delete all file under the dir path | |||||
| func ObsRemoveObject(bucket string, path string) error { | |||||
| log.Info("Bucket=" + bucket + " path=" + path) | |||||
| if len(path) == 0 { | |||||
| return errors.New("path canot be null.") | |||||
| } | |||||
| input := &obs.ListObjectsInput{} | |||||
| input.Bucket = bucket | |||||
| // 设置每页100个对象 | |||||
| input.MaxKeys = 100 | |||||
| input.Prefix = path | |||||
| index := 1 | |||||
| log.Info("prefix=" + input.Prefix) | |||||
| for { | |||||
| output, err := ObsCli.ListObjects(input) | |||||
| if err == nil { | |||||
| log.Info("Page:%d\n", index) | |||||
| index++ | |||||
| for _, val := range output.Contents { | |||||
| log.Info("delete obs file:" + val.Key) | |||||
| delObj := &obs.DeleteObjectInput{} | |||||
| delObj.Bucket = setting.Bucket | |||||
| delObj.Key = val.Key | |||||
| ObsCli.DeleteObject(delObj) | |||||
| } | |||||
| if output.IsTruncated { | |||||
| input.Marker = output.NextMarker | |||||
| } else { | |||||
| break | |||||
| } | |||||
| } else { | |||||
| if obsError, ok := err.(obs.ObsError); ok { | |||||
| log.Info("Code:%s\n", obsError.Code) | |||||
| log.Info("Message:%s\n", obsError.Message) | |||||
| } | |||||
| return err | |||||
| } | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func ObsDownloadAFile(bucket string, key string) (io.ReadCloser, error) { | |||||
| input := &obs.GetObjectInput{} | 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)), "/") | |||||
| input.Bucket = bucket | |||||
| input.Key = key | |||||
| output, err := ObsCli.GetObject(input) | output, err := ObsCli.GetObject(input) | ||||
| if err == nil { | if err == nil { | ||||
| log.Info("StorageClass:%s, ETag:%s, ContentType:%s, ContentLength:%d, LastModified:%s\n", | log.Info("StorageClass:%s, ETag:%s, ContentType:%s, ContentLength:%d, LastModified:%s\n", | ||||
| @@ -158,6 +199,11 @@ func ObsDownload(uuid string, fileName string) (io.ReadCloser, error) { | |||||
| } | } | ||||
| } | } | ||||
| func ObsDownload(uuid string, fileName string) (io.ReadCloser, error) { | |||||
| return ObsDownloadAFile(setting.Bucket, strings.TrimPrefix(path.Join(setting.BasePath, path.Join(uuid[0:1], uuid[1:2], uuid, fileName)), "/")) | |||||
| } | |||||
| func ObsModelDownload(JobName string, fileName string) (io.ReadCloser, error) { | func ObsModelDownload(JobName string, fileName string) (io.ReadCloser, error) { | ||||
| input := &obs.GetObjectInput{} | input := &obs.GetObjectInput{} | ||||
| input.Bucket = setting.Bucket | input.Bucket = setting.Bucket | ||||
| @@ -176,6 +222,160 @@ func ObsModelDownload(JobName string, fileName string) (io.ReadCloser, error) { | |||||
| } | } | ||||
| } | } | ||||
| func ObsCopyManyFile(srcBucket string, srcPath string, destBucket string, destPath string) (int64, error) { | |||||
| input := &obs.ListObjectsInput{} | |||||
| input.Bucket = srcBucket | |||||
| // 设置每页100个对象 | |||||
| input.MaxKeys = 100 | |||||
| input.Prefix = srcPath | |||||
| index := 1 | |||||
| length := len(srcPath) | |||||
| var fileTotalSize int64 | |||||
| log.Info("prefix=" + input.Prefix) | |||||
| for { | |||||
| output, err := ObsCli.ListObjects(input) | |||||
| if err == nil { | |||||
| log.Info("Page:%d\n", index) | |||||
| index++ | |||||
| for _, val := range output.Contents { | |||||
| destKey := destPath + val.Key[length:] | |||||
| obsCopyFile(srcBucket, val.Key, destBucket, destKey) | |||||
| fileTotalSize += val.Size | |||||
| } | |||||
| if output.IsTruncated { | |||||
| input.Marker = output.NextMarker | |||||
| } else { | |||||
| break | |||||
| } | |||||
| } else { | |||||
| if obsError, ok := err.(obs.ObsError); ok { | |||||
| log.Info("Code:%s\n", obsError.Code) | |||||
| log.Info("Message:%s\n", obsError.Message) | |||||
| } | |||||
| return 0, err | |||||
| } | |||||
| } | |||||
| return fileTotalSize, nil | |||||
| } | |||||
| func obsCopyFile(srcBucket string, srcKeyName string, destBucket string, destKeyName string) error { | |||||
| input := &obs.CopyObjectInput{} | |||||
| input.Bucket = destBucket | |||||
| input.Key = destKeyName | |||||
| input.CopySourceBucket = srcBucket | |||||
| input.CopySourceKey = srcKeyName | |||||
| _, err := ObsCli.CopyObject(input) | |||||
| if err == nil { | |||||
| log.Info("copy success,destBuckName:%s, destkeyname:%s", destBucket, destKeyName) | |||||
| } else { | |||||
| log.Info("copy failed,,destBuckName:%s, destkeyname:%s", destBucket, destKeyName) | |||||
| if obsError, ok := err.(obs.ObsError); ok { | |||||
| log.Info(obsError.Code) | |||||
| log.Info(obsError.Message) | |||||
| } | |||||
| return err | |||||
| } | |||||
| return nil | |||||
| } | |||||
| func GetOneLevelAllObjectUnderDir(bucket string, prefixRootPath string, relativePath string) ([]FileInfo, error) { | |||||
| input := &obs.ListObjectsInput{} | |||||
| input.Bucket = bucket | |||||
| input.Prefix = prefixRootPath + relativePath | |||||
| if !strings.HasSuffix(input.Prefix, "/") { | |||||
| input.Prefix += "/" | |||||
| } | |||||
| output, err := ObsCli.ListObjects(input) | |||||
| fileInfos := make([]FileInfo, 0) | |||||
| prefixLen := len(input.Prefix) | |||||
| if err == nil { | |||||
| for _, val := range output.Contents { | |||||
| log.Info("val key=" + val.Key) | |||||
| var isDir bool | |||||
| var fileName string | |||||
| if val.Key == input.Prefix { | |||||
| continue | |||||
| } | |||||
| if strings.Contains(val.Key[prefixLen:len(val.Key)-1], "/") { | |||||
| continue | |||||
| } | |||||
| if strings.HasSuffix(val.Key, "/") { | |||||
| isDir = true | |||||
| fileName = val.Key[prefixLen : len(val.Key)-1] | |||||
| relativePath += val.Key[prefixLen:] | |||||
| } else { | |||||
| isDir = false | |||||
| fileName = val.Key[prefixLen:] | |||||
| } | |||||
| fileInfo := FileInfo{ | |||||
| ModTime: val.LastModified.Local().Format("2006-01-02 15:04:05"), | |||||
| FileName: fileName, | |||||
| Size: val.Size, | |||||
| IsDir: isDir, | |||||
| ParenDir: relativePath, | |||||
| } | |||||
| 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 GetAllObjectByBucketAndPrefix(bucket string, prefix string) ([]FileInfo, error) { | |||||
| input := &obs.ListObjectsInput{} | |||||
| input.Bucket = bucket | |||||
| // 设置每页100个对象 | |||||
| input.MaxKeys = 100 | |||||
| input.Prefix = prefix | |||||
| index := 1 | |||||
| fileInfos := make([]FileInfo, 0) | |||||
| prefixLen := len(prefix) | |||||
| log.Info("prefix=" + input.Prefix) | |||||
| for { | |||||
| output, err := ObsCli.ListObjects(input) | |||||
| if err == nil { | |||||
| log.Info("Page:%d\n", index) | |||||
| index++ | |||||
| for _, val := range output.Contents { | |||||
| var isDir bool | |||||
| if prefixLen == len(val.Key) { | |||||
| continue | |||||
| } | |||||
| if strings.HasSuffix(val.Key, "/") { | |||||
| isDir = true | |||||
| } else { | |||||
| isDir = false | |||||
| } | |||||
| fileInfo := FileInfo{ | |||||
| ModTime: val.LastModified.Format("2006-01-02 15:04:05"), | |||||
| FileName: val.Key[prefixLen:], | |||||
| Size: val.Size, | |||||
| IsDir: isDir, | |||||
| ParenDir: "", | |||||
| } | |||||
| fileInfos = append(fileInfos, fileInfo) | |||||
| } | |||||
| if output.IsTruncated { | |||||
| input.Marker = output.NextMarker | |||||
| } else { | |||||
| break | |||||
| } | |||||
| } else { | |||||
| if obsError, ok := err.(obs.ObsError); ok { | |||||
| log.Info("Code:%s\n", obsError.Code) | |||||
| log.Info("Message:%s\n", obsError.Message) | |||||
| } | |||||
| return nil, err | |||||
| } | |||||
| } | |||||
| return fileInfos, nil | |||||
| } | |||||
| func GetObsListObject(jobName, parentDir, versionName string) ([]FileInfo, error) { | func GetObsListObject(jobName, parentDir, versionName string) ([]FileInfo, error) { | ||||
| input := &obs.ListObjectsInput{} | input := &obs.ListObjectsInput{} | ||||
| input.Bucket = setting.Bucket | input.Bucket = setting.Bucket | ||||
| @@ -258,27 +458,6 @@ 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) | |||||
| fileName = url.QueryEscape(fileName) | |||||
| 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 | |||||
| } | |||||
| log.Info("SignedUrl:%s", output.SignedUrl) | |||||
| return output.SignedUrl, nil | |||||
| } | |||||
| func GetObsCreateSignedUrlByBucketAndKey(bucket, key string) (string, error) { | func GetObsCreateSignedUrlByBucketAndKey(bucket, key string) (string, error) { | ||||
| input := &obs.CreateSignedUrlInput{} | input := &obs.CreateSignedUrlInput{} | ||||
| input.Bucket = bucket | input.Bucket = bucket | ||||
| @@ -302,7 +481,10 @@ func GetObsCreateSignedUrlByBucketAndKey(bucket, key string) (string, error) { | |||||
| } | } | ||||
| return output.SignedUrl, nil | return output.SignedUrl, nil | ||||
| } | |||||
| func GetObsCreateSignedUrl(jobName, parentDir, fileName string) (string, error) { | |||||
| return GetObsCreateSignedUrlByBucketAndKey(setting.Bucket, strings.TrimPrefix(path.Join(setting.TrainJobModelPath, jobName, setting.OutPutPath, parentDir, fileName), "/")) | |||||
| } | } | ||||
| func ObsGetPreSignedUrl(uuid, fileName string) (string, error) { | func ObsGetPreSignedUrl(uuid, fileName string) (string, error) { | ||||
| @@ -816,6 +816,11 @@ get_repo_info_error=Can not get the information of the repository. | |||||
| generate_statistic_file_error=Fail to generate file. | generate_statistic_file_error=Fail to generate file. | ||||
| repo_stat_inspect=ProjectAnalysis | repo_stat_inspect=ProjectAnalysis | ||||
| all=All | all=All | ||||
| modelarts.status=Status | |||||
| modelarts.createtime=CreateTime | |||||
| modelarts.version_nums = Version Nums | |||||
| modelarts.version = Version | |||||
| modelarts.computing_resources=compute Resources | |||||
| modelarts.notebook=Debug Task | modelarts.notebook=Debug Task | ||||
| modelarts.train_job=Train Task | modelarts.train_job=Train Task | ||||
| modelarts.train_job.new_debug= New Debug Task | modelarts.train_job.new_debug= New Debug Task | ||||
| @@ -823,6 +828,10 @@ modelarts.train_job.new_train=New Train Task | |||||
| modelarts.train_job.config=Configuration information | modelarts.train_job.config=Configuration information | ||||
| modelarts.train_job.new=New train Task | modelarts.train_job.new=New train Task | ||||
| modelarts.train_job.new_place=The description should not exceed 256 characters | modelarts.train_job.new_place=The description should not exceed 256 characters | ||||
| modelarts.model_name=Model Name | |||||
| modelarts.model_size=Model Size | |||||
| modelarts.import_model=Import Model | |||||
| modelarts.modify=Modify | modelarts.modify=Modify | ||||
| modelarts.current_version=Current version | modelarts.current_version=Current version | ||||
| modelarts.parent_version=Parent Version | modelarts.parent_version=Parent Version | ||||
| @@ -874,6 +883,20 @@ modelarts.train_job_para_admin=train_job_para_admin | |||||
| modelarts.train_job_para.edit=train_job_para.edit | modelarts.train_job_para.edit=train_job_para.edit | ||||
| modelarts.train_job_para.connfirm=train_job_para.connfirm | modelarts.train_job_para.connfirm=train_job_para.connfirm | ||||
| model.manage.import_new_model=Import New Model | |||||
| model.manage.create_error=Equal Name and Version has existed. | |||||
| model.manage.model_name = Model Name | |||||
| model.manage.version = Version | |||||
| model.manage.label = Label | |||||
| model.manage.size = Size | |||||
| model.manage.create_time = Create Time | |||||
| model.manage.Description = Description | |||||
| model.manage.Accuracy = Accuracy | |||||
| model.manage.F1 = F1 | |||||
| model.manage.Precision = Precision | |||||
| model.manage.Recall = Recall | |||||
| template.items = Template Items | template.items = Template Items | ||||
| template.git_content = Git Content (Default Branch) | template.git_content = Git Content (Default Branch) | ||||
| template.git_hooks = Git Hooks | template.git_hooks = Git Hooks | ||||
| @@ -1552,6 +1575,7 @@ settings.external_wiki_url_error = The external wiki URL is not a valid URL. | |||||
| settings.external_wiki_url_desc = Visitors are redirected to the external wiki URL when clicking the wiki tab. | settings.external_wiki_url_desc = Visitors are redirected to the external wiki URL when clicking the wiki tab. | ||||
| settings.dataset_desc = Enable Repository Dataset | settings.dataset_desc = Enable Repository Dataset | ||||
| settings.cloudbrain_desc = Enable Cloudbarin | settings.cloudbrain_desc = Enable Cloudbarin | ||||
| settings.model_desc = Enable Model Manage | |||||
| settings.issues_desc = Enable Repository Issue Tracker | settings.issues_desc = Enable Repository Issue Tracker | ||||
| settings.use_internal_issue_tracker = Use Built-In Issue Tracker | settings.use_internal_issue_tracker = Use Built-In Issue Tracker | ||||
| settings.use_external_issue_tracker = Use External Issue Tracker | settings.use_external_issue_tracker = Use External Issue Tracker | ||||
| @@ -782,6 +782,9 @@ datasets=数据集 | |||||
| datasets.desc=数据集功能 | datasets.desc=数据集功能 | ||||
| cloudbrain_helper=使用GPU/NPU资源,开启Notebook、模型训练任务等 | cloudbrain_helper=使用GPU/NPU资源,开启Notebook、模型训练任务等 | ||||
| model_manager = 模型管理 | |||||
| model_noright=无权限操作 | |||||
| debug=调试 | debug=调试 | ||||
| stop=停止 | stop=停止 | ||||
| delete=删除 | delete=删除 | ||||
| @@ -824,6 +827,7 @@ all=所有 | |||||
| modelarts.status=状态 | modelarts.status=状态 | ||||
| modelarts.createtime=创建时间 | modelarts.createtime=创建时间 | ||||
| modelarts.version_nums=版本数 | modelarts.version_nums=版本数 | ||||
| modelarts.version=版本 | |||||
| modelarts.computing_resources=计算资源 | modelarts.computing_resources=计算资源 | ||||
| modelarts.notebook=调试任务 | modelarts.notebook=调试任务 | ||||
| modelarts.train_job=训练任务 | modelarts.train_job=训练任务 | ||||
| @@ -831,6 +835,10 @@ modelarts.train_job.new_debug=新建调试任务 | |||||
| modelarts.train_job.new_train=新建训练任务 | modelarts.train_job.new_train=新建训练任务 | ||||
| modelarts.train_job.config=配置信息 | modelarts.train_job.config=配置信息 | ||||
| modelarts.train_job.new=新建训练任务 | modelarts.train_job.new=新建训练任务 | ||||
| modelarts.train_job.new_place=描述字数不超过256个字符 | |||||
| modelarts.model_name=模型名称 | |||||
| modelarts.model_size=模型大小 | |||||
| modelarts.import_model=导入模型 | |||||
| modelarts.train_job.new_place=描述字数不超过255个字符 | modelarts.train_job.new_place=描述字数不超过255个字符 | ||||
| modelarts.modify=修改 | modelarts.modify=修改 | ||||
| modelarts.current_version=当前版本 | modelarts.current_version=当前版本 | ||||
| @@ -887,6 +895,18 @@ modelarts.train_job_para_admin=任务参数管理 | |||||
| modelarts.train_job_para.edit=编辑 | modelarts.train_job_para.edit=编辑 | ||||
| modelarts.train_job_para.connfirm=确定 | modelarts.train_job_para.connfirm=确定 | ||||
| model.manage.import_new_model=导入新模型 | |||||
| model.manage.create_error=相同的名称和版本的模型已经存在。 | |||||
| model.manage.model_name = 模型名称 | |||||
| model.manage.version = 版本 | |||||
| model.manage.label = 标签 | |||||
| model.manage.size = 大小 | |||||
| model.manage.create_time = 创建时间 | |||||
| model.manage.description = 描述 | |||||
| model.manage.Accuracy = 准确率 | |||||
| model.manage.F1 = F1值 | |||||
| model.manage.Precision = 精确率 | |||||
| model.manage.Recall = 召回率 | |||||
| template.items=模板选项 | template.items=模板选项 | ||||
| template.git_content=Git数据(默认分支) | template.git_content=Git数据(默认分支) | ||||
| @@ -1566,6 +1586,7 @@ settings.external_wiki_url_error=外部百科链接无效 | |||||
| settings.external_wiki_url_desc=当点击任务标签时,访问者将被重定向到外部任务系统的URL。 | settings.external_wiki_url_desc=当点击任务标签时,访问者将被重定向到外部任务系统的URL。 | ||||
| settings.dataset_desc=启用数据集 | settings.dataset_desc=启用数据集 | ||||
| settings.cloudbrain_desc = 启用云脑 | settings.cloudbrain_desc = 启用云脑 | ||||
| settings.model_desc = 启用模型管理 | |||||
| settings.issues_desc=启用任务系统 | settings.issues_desc=启用任务系统 | ||||
| settings.use_internal_issue_tracker=使用内置的轻量级任务管理系统 | settings.use_internal_issue_tracker=使用内置的轻量级任务管理系统 | ||||
| settings.use_external_issue_tracker=使用外部的任务管理系统 | settings.use_external_issue_tracker=使用外部的任务管理系统 | ||||
| @@ -0,0 +1,513 @@ | |||||
| package repo | |||||
| import ( | |||||
| "archive/zip" | |||||
| "encoding/json" | |||||
| "errors" | |||||
| "fmt" | |||||
| "net/http" | |||||
| "path" | |||||
| "strings" | |||||
| "code.gitea.io/gitea/models" | |||||
| "code.gitea.io/gitea/modules/context" | |||||
| "code.gitea.io/gitea/modules/log" | |||||
| "code.gitea.io/gitea/modules/setting" | |||||
| "code.gitea.io/gitea/modules/storage" | |||||
| uuid "github.com/satori/go.uuid" | |||||
| ) | |||||
| const ( | |||||
| Model_prefix = "aimodels/" | |||||
| tplModelManageIndex = "repo/modelmanage/index" | |||||
| tplModelManageDownload = "repo/modelmanage/download" | |||||
| tplModelInfo = "repo/modelmanage/showinfo" | |||||
| MODEL_LATEST = 1 | |||||
| MODEL_NOT_LATEST = 0 | |||||
| ) | |||||
| func saveModelByParameters(jobId string, versionName string, name string, version string, label string, description string, ctx *context.Context) error { | |||||
| aiTask, err := models.GetCloudbrainByJobIDAndVersionName(jobId, versionName) | |||||
| //aiTask, err := models.GetCloudbrainByJobID(jobId) | |||||
| if err != nil { | |||||
| log.Info("query task error." + err.Error()) | |||||
| return err | |||||
| } | |||||
| uuid := uuid.NewV4() | |||||
| id := uuid.String() | |||||
| modelPath := id | |||||
| var lastNewModelId string | |||||
| var modelSize int64 | |||||
| cloudType := models.TypeCloudBrainTwo | |||||
| log.Info("find task name:" + aiTask.JobName) | |||||
| aimodels := models.QueryModelByName(name, aiTask.RepoID) | |||||
| if len(aimodels) > 0 { | |||||
| for _, model := range aimodels { | |||||
| if model.Version == version { | |||||
| return errors.New(ctx.Tr("repo.model.manage.create_error")) | |||||
| } | |||||
| if model.New == MODEL_LATEST { | |||||
| lastNewModelId = model.ID | |||||
| } | |||||
| } | |||||
| } | |||||
| cloudType = aiTask.Type | |||||
| //download model zip //train type | |||||
| if cloudType == models.TypeCloudBrainTwo { | |||||
| modelPath, modelSize, err = downloadModelFromCloudBrainTwo(id, aiTask.JobName, "") | |||||
| if err != nil { | |||||
| log.Info("download model from CloudBrainTwo faild." + err.Error()) | |||||
| return err | |||||
| } | |||||
| } | |||||
| accuracy := make(map[string]string) | |||||
| accuracy["F1"] = "" | |||||
| accuracy["Recall"] = "" | |||||
| accuracy["Accuracy"] = "" | |||||
| accuracy["Precision"] = "" | |||||
| accuracyJson, _ := json.Marshal(accuracy) | |||||
| log.Info("accuracyJson=" + string(accuracyJson)) | |||||
| aiTaskJson, _ := json.Marshal(aiTask) | |||||
| //taskConfigInfo,err := models.GetCloudbrainByJobIDAndVersionName(jobId,aiTask.VersionName) | |||||
| model := &models.AiModelManage{ | |||||
| ID: id, | |||||
| Version: version, | |||||
| VersionCount: len(aimodels) + 1, | |||||
| Label: label, | |||||
| Name: name, | |||||
| Description: description, | |||||
| New: MODEL_LATEST, | |||||
| Type: cloudType, | |||||
| Path: modelPath, | |||||
| Size: modelSize, | |||||
| AttachmentId: aiTask.Uuid, | |||||
| RepoId: aiTask.RepoID, | |||||
| UserId: ctx.User.ID, | |||||
| UserName: ctx.User.Name, | |||||
| UserRelAvatarLink: ctx.User.RelAvatarLink(), | |||||
| CodeBranch: aiTask.BranchName, | |||||
| CodeCommitID: aiTask.CommitID, | |||||
| Engine: aiTask.EngineID, | |||||
| TrainTaskInfo: string(aiTaskJson), | |||||
| Accuracy: string(accuracyJson), | |||||
| } | |||||
| err = models.SaveModelToDb(model) | |||||
| if err != nil { | |||||
| return err | |||||
| } | |||||
| if len(lastNewModelId) > 0 { | |||||
| //udpate status and version count | |||||
| models.ModifyModelNewProperty(lastNewModelId, MODEL_NOT_LATEST, 0) | |||||
| } | |||||
| log.Info("save model end.") | |||||
| return nil | |||||
| } | |||||
| func SaveModel(ctx *context.Context) { | |||||
| log.Info("save model start.") | |||||
| JobId := ctx.Query("JobId") | |||||
| VersionName := ctx.Query("VersionName") | |||||
| name := ctx.Query("Name") | |||||
| version := ctx.Query("Version") | |||||
| label := ctx.Query("Label") | |||||
| description := ctx.Query("Description") | |||||
| if !ctx.Repo.CanWrite(models.UnitTypeModelManage) { | |||||
| ctx.ServerError("No right.", errors.New(ctx.Tr("repo.model_noright"))) | |||||
| return | |||||
| } | |||||
| if JobId == "" || VersionName == "" { | |||||
| ctx.Error(500, fmt.Sprintf("JobId or VersionName is null.")) | |||||
| return | |||||
| } | |||||
| if name == "" || version == "" { | |||||
| ctx.Error(500, fmt.Sprintf("name or version is null.")) | |||||
| return | |||||
| } | |||||
| err := saveModelByParameters(JobId, VersionName, name, version, label, description, ctx) | |||||
| if err != nil { | |||||
| log.Info("save model error." + err.Error()) | |||||
| ctx.Error(500, fmt.Sprintf("save model error. %v", err)) | |||||
| return | |||||
| } | |||||
| log.Info("save model end.") | |||||
| } | |||||
| func downloadModelFromCloudBrainTwo(modelUUID string, jobName string, parentDir string) (string, int64, error) { | |||||
| objectkey := strings.TrimPrefix(path.Join(setting.TrainJobModelPath, jobName, setting.OutPutPath, parentDir), "/") | |||||
| modelDbResult, err := storage.GetOneLevelAllObjectUnderDir(setting.Bucket, objectkey, "") | |||||
| log.Info("bucket=" + setting.Bucket + " objectkey=" + objectkey) | |||||
| if err != nil { | |||||
| log.Info("get TrainJobListModel failed:", err) | |||||
| return "", 0, err | |||||
| } | |||||
| if len(modelDbResult) == 0 { | |||||
| return "", 0, errors.New("cannot create model, as model is empty.") | |||||
| } | |||||
| prefix := objectkey + "/" | |||||
| destKeyNamePrefix := Model_prefix + models.AttachmentRelativePath(modelUUID) + "/" | |||||
| size, err := storage.ObsCopyManyFile(setting.Bucket, prefix, setting.Bucket, destKeyNamePrefix) | |||||
| dataActualPath := setting.Bucket + "/" + destKeyNamePrefix | |||||
| return dataActualPath, size, nil | |||||
| } | |||||
| func DeleteModel(ctx *context.Context) { | |||||
| log.Info("delete model start.") | |||||
| id := ctx.Query("ID") | |||||
| err := deleteModelByID(ctx, id) | |||||
| if err != nil { | |||||
| ctx.JSON(500, err.Error()) | |||||
| } else { | |||||
| ctx.JSON(200, map[string]string{ | |||||
| "result_code": "0", | |||||
| }) | |||||
| } | |||||
| } | |||||
| func isCanDeleteOrDownload(ctx *context.Context, model *models.AiModelManage) bool { | |||||
| if ctx.User.IsAdmin || ctx.User.ID == model.UserId { | |||||
| return true | |||||
| } | |||||
| if ctx.Repo.IsOwner() { | |||||
| return true | |||||
| } | |||||
| return false | |||||
| } | |||||
| func deleteModelByID(ctx *context.Context, id string) error { | |||||
| log.Info("delete model start. id=" + id) | |||||
| model, err := models.QueryModelById(id) | |||||
| if !isCanDeleteOrDownload(ctx, model) { | |||||
| return errors.New(ctx.Tr("repo.model_noright")) | |||||
| } | |||||
| if err == nil { | |||||
| log.Info("bucket=" + setting.Bucket + " path=" + model.Path) | |||||
| if strings.HasPrefix(model.Path, setting.Bucket+"/"+Model_prefix) { | |||||
| err := storage.ObsRemoveObject(setting.Bucket, model.Path[len(setting.Bucket)+1:]) | |||||
| if err != nil { | |||||
| log.Info("Failed to delete model. id=" + id) | |||||
| return err | |||||
| } | |||||
| } | |||||
| err = models.DeleteModelById(id) | |||||
| if err == nil { //find a model to change new | |||||
| aimodels := models.QueryModelByName(model.Name, model.RepoId) | |||||
| if model.New == MODEL_LATEST { | |||||
| if len(aimodels) > 0 { | |||||
| //udpate status and version count | |||||
| models.ModifyModelNewProperty(aimodels[0].ID, MODEL_LATEST, len(aimodels)) | |||||
| } | |||||
| } else { | |||||
| for _, tmpModel := range aimodels { | |||||
| if tmpModel.New == MODEL_LATEST { | |||||
| models.ModifyModelNewProperty(tmpModel.ID, MODEL_LATEST, len(aimodels)) | |||||
| break | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| return err | |||||
| } | |||||
| func QueryModelByParameters(repoId int64, page int) ([]*models.AiModelManage, int64, error) { | |||||
| return models.QueryModel(&models.AiModelQueryOptions{ | |||||
| ListOptions: models.ListOptions{ | |||||
| Page: page, | |||||
| PageSize: setting.UI.IssuePagingNum, | |||||
| }, | |||||
| RepoID: repoId, | |||||
| Type: -1, | |||||
| New: MODEL_LATEST, | |||||
| }) | |||||
| } | |||||
| func DownloadMultiModelFile(ctx *context.Context) { | |||||
| log.Info("DownloadMultiModelFile start.") | |||||
| id := ctx.Query("ID") | |||||
| log.Info("id=" + id) | |||||
| task, err := models.QueryModelById(id) | |||||
| if err != nil { | |||||
| log.Error("no such model!", err.Error()) | |||||
| ctx.ServerError("no such model:", err) | |||||
| return | |||||
| } | |||||
| if !isCanDeleteOrDownload(ctx, task) { | |||||
| ctx.ServerError("no right.", errors.New(ctx.Tr("repo.model_noright"))) | |||||
| return | |||||
| } | |||||
| path := Model_prefix + models.AttachmentRelativePath(id) + "/" | |||||
| allFile, err := storage.GetAllObjectByBucketAndPrefix(setting.Bucket, path) | |||||
| if err == nil { | |||||
| //count++ | |||||
| models.ModifyModelDownloadCount(id) | |||||
| returnFileName := task.Name + "_" + task.Version + ".zip" | |||||
| ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+returnFileName) | |||||
| ctx.Resp.Header().Set("Content-Type", "application/octet-stream") | |||||
| w := zip.NewWriter(ctx.Resp) | |||||
| defer w.Close() | |||||
| for _, oneFile := range allFile { | |||||
| if oneFile.IsDir { | |||||
| log.Info("zip dir name:" + oneFile.FileName) | |||||
| } else { | |||||
| log.Info("zip file name:" + oneFile.FileName) | |||||
| fDest, err := w.Create(oneFile.FileName) | |||||
| if err != nil { | |||||
| log.Info("create zip entry error, download file failed: %s\n", err.Error()) | |||||
| ctx.ServerError("download file failed:", err) | |||||
| return | |||||
| } | |||||
| body, err := storage.ObsDownloadAFile(setting.Bucket, path+oneFile.FileName) | |||||
| if err != nil { | |||||
| log.Info("download file failed: %s\n", err.Error()) | |||||
| ctx.ServerError("download file failed:", err) | |||||
| return | |||||
| } else { | |||||
| defer body.Close() | |||||
| p := make([]byte, 1024) | |||||
| var readErr error | |||||
| var readCount int | |||||
| // 读取对象内容 | |||||
| for { | |||||
| readCount, readErr = body.Read(p) | |||||
| if readCount > 0 { | |||||
| fDest.Write(p[:readCount]) | |||||
| } | |||||
| if readErr != nil { | |||||
| break | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } else { | |||||
| log.Info("error,msg=" + err.Error()) | |||||
| ctx.ServerError("no file to download.", err) | |||||
| } | |||||
| } | |||||
| func QueryTrainJobVersionList(ctx *context.Context) { | |||||
| log.Info("query train job version list. start.") | |||||
| JobID := ctx.Query("JobID") | |||||
| VersionListTasks, count, err := models.QueryModelTrainJobVersionList(JobID) | |||||
| log.Info("query return count=" + fmt.Sprint(count)) | |||||
| if err != nil { | |||||
| ctx.ServerError("QueryTrainJobList:", err) | |||||
| } else { | |||||
| ctx.JSON(200, VersionListTasks) | |||||
| } | |||||
| } | |||||
| func QueryTrainJobList(ctx *context.Context) { | |||||
| log.Info("query train job list. start.") | |||||
| repoId := ctx.QueryInt64("repoId") | |||||
| VersionListTasks, count, err := models.QueryModelTrainJobList(repoId) | |||||
| log.Info("query return count=" + fmt.Sprint(count)) | |||||
| if err != nil { | |||||
| ctx.ServerError("QueryTrainJobList:", err) | |||||
| } else { | |||||
| ctx.JSON(200, VersionListTasks) | |||||
| } | |||||
| } | |||||
| func DownloadSingleModelFile(ctx *context.Context) { | |||||
| log.Info("DownloadSingleModelFile start.") | |||||
| id := ctx.Params(":ID") | |||||
| parentDir := ctx.Query("parentDir") | |||||
| fileName := ctx.Query("fileName") | |||||
| path := Model_prefix + models.AttachmentRelativePath(id) + "/" + parentDir + fileName | |||||
| if setting.PROXYURL != "" { | |||||
| body, err := storage.ObsDownloadAFile(setting.Bucket, path) | |||||
| if err != nil { | |||||
| log.Info("download error.") | |||||
| } else { | |||||
| //count++ | |||||
| models.ModifyModelDownloadCount(id) | |||||
| 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 | |||||
| } | |||||
| } | |||||
| } | |||||
| } else { | |||||
| url, err := storage.GetObsCreateSignedUrlByBucketAndKey(setting.Bucket, path) | |||||
| if err != nil { | |||||
| log.Error("GetObsCreateSignedUrl failed: %v", err.Error(), ctx.Data["msgID"]) | |||||
| ctx.ServerError("GetObsCreateSignedUrl", err) | |||||
| return | |||||
| } | |||||
| //count++ | |||||
| models.ModifyModelDownloadCount(id) | |||||
| http.Redirect(ctx.Resp, ctx.Req.Request, url, http.StatusMovedPermanently) | |||||
| } | |||||
| } | |||||
| func ShowModelInfo(ctx *context.Context) { | |||||
| ctx.Data["ID"] = ctx.Query("ID") | |||||
| ctx.Data["name"] = ctx.Query("name") | |||||
| ctx.Data["isModelManage"] = true | |||||
| ctx.HTML(200, tplModelInfo) | |||||
| } | |||||
| func ShowSingleModel(ctx *context.Context) { | |||||
| name := ctx.Query("name") | |||||
| log.Info("Show single ModelInfo start.name=" + name) | |||||
| models := models.QueryModelByName(name, ctx.Repo.Repository.ID) | |||||
| ctx.JSON(http.StatusOK, models) | |||||
| } | |||||
| func ShowOneVersionOtherModel(ctx *context.Context) { | |||||
| repoId := ctx.Repo.Repository.ID | |||||
| name := ctx.Query("name") | |||||
| aimodels := models.QueryModelByName(name, repoId) | |||||
| for _, model := range aimodels { | |||||
| log.Info("model=" + model.Name) | |||||
| log.Info("model.UserId=" + fmt.Sprint(model.UserId)) | |||||
| model.IsCanOper = isOper(ctx, model.UserId) | |||||
| } | |||||
| if len(aimodels) > 0 { | |||||
| ctx.JSON(200, aimodels[1:]) | |||||
| } else { | |||||
| ctx.JSON(200, aimodels) | |||||
| } | |||||
| } | |||||
| func ShowModelTemplate(ctx *context.Context) { | |||||
| ctx.Data["isModelManage"] = true | |||||
| ctx.HTML(200, tplModelManageIndex) | |||||
| } | |||||
| func isQueryRight(ctx *context.Context) bool { | |||||
| if ctx.Repo.Repository.IsPrivate { | |||||
| if ctx.Repo.CanRead(models.UnitTypeModelManage) || ctx.User.IsAdmin || ctx.Repo.IsAdmin() || ctx.Repo.IsOwner() { | |||||
| return true | |||||
| } | |||||
| return false | |||||
| } else { | |||||
| return true | |||||
| } | |||||
| } | |||||
| func isOper(ctx *context.Context, modelUserId int64) bool { | |||||
| if ctx.User == nil { | |||||
| return false | |||||
| } | |||||
| if ctx.User.IsAdmin || ctx.Repo.IsAdmin() || ctx.Repo.IsOwner() || ctx.User.ID == modelUserId { | |||||
| return true | |||||
| } | |||||
| return false | |||||
| } | |||||
| func ShowModelPageInfo(ctx *context.Context) { | |||||
| log.Info("ShowModelInfo start.") | |||||
| if !isQueryRight(ctx) { | |||||
| ctx.ServerError("no right.", errors.New(ctx.Tr("repo.model_noright"))) | |||||
| return | |||||
| } | |||||
| page := ctx.QueryInt("page") | |||||
| if page <= 0 { | |||||
| page = 1 | |||||
| } | |||||
| repoId := ctx.Repo.Repository.ID | |||||
| Type := -1 | |||||
| modelResult, count, err := models.QueryModel(&models.AiModelQueryOptions{ | |||||
| ListOptions: models.ListOptions{ | |||||
| Page: page, | |||||
| PageSize: setting.UI.IssuePagingNum, | |||||
| }, | |||||
| RepoID: repoId, | |||||
| Type: Type, | |||||
| New: MODEL_LATEST, | |||||
| }) | |||||
| if err != nil { | |||||
| ctx.ServerError("Cloudbrain", err) | |||||
| return | |||||
| } | |||||
| for _, model := range modelResult { | |||||
| log.Info("model=" + model.Name) | |||||
| log.Info("model.UserId=" + fmt.Sprint(model.UserId)) | |||||
| model.IsCanOper = isOper(ctx, model.UserId) | |||||
| } | |||||
| mapInterface := make(map[string]interface{}) | |||||
| mapInterface["data"] = modelResult | |||||
| mapInterface["count"] = count | |||||
| ctx.JSON(http.StatusOK, mapInterface) | |||||
| } | |||||
| func ModifyModel(id string, description string) error { | |||||
| err := models.ModifyModelDescription(id, description) | |||||
| if err == nil { | |||||
| log.Info("modify success.") | |||||
| } else { | |||||
| log.Info("Failed to modify.id=" + id + " desc=" + description + " error:" + err.Error()) | |||||
| } | |||||
| return err | |||||
| } | |||||
| func ModifyModelInfo(ctx *context.Context) { | |||||
| log.Info("modify model start.") | |||||
| id := ctx.Query("ID") | |||||
| description := ctx.Query("Description") | |||||
| task, err := models.QueryModelById(id) | |||||
| if err != nil { | |||||
| log.Error("no such model!", err.Error()) | |||||
| ctx.ServerError("no such model:", err) | |||||
| return | |||||
| } | |||||
| if !isCanDeleteOrDownload(ctx, task) { | |||||
| ctx.ServerError("no right.", errors.New(ctx.Tr("repo.model_noright"))) | |||||
| return | |||||
| } | |||||
| err = ModifyModel(id, description) | |||||
| if err != nil { | |||||
| log.Info("modify error," + err.Error()) | |||||
| ctx.ServerError("error.", err) | |||||
| } else { | |||||
| ctx.JSON(200, "success") | |||||
| } | |||||
| } | |||||
| @@ -552,10 +552,10 @@ func CloudBrainShowModels(ctx *context.Context) { | |||||
| } | } | ||||
| //get dirs | //get dirs | ||||
| dirs, err := getModelDirs(task.JobName, parentDir) | |||||
| dirs, err := GetModelDirs(task.JobName, parentDir) | |||||
| if err != nil { | if err != nil { | ||||
| log.Error("getModelDirs failed:%v", err.Error(), ctx.Data["msgID"]) | |||||
| ctx.ServerError("getModelDirs failed:", err) | |||||
| log.Error("GetModelDirs failed:%v", err.Error(), ctx.Data["msgID"]) | |||||
| ctx.ServerError("GetModelDirs failed:", err) | |||||
| return | return | ||||
| } | } | ||||
| @@ -615,7 +615,7 @@ func getImages(ctx *context.Context, imageType string) { | |||||
| log.Info("Get images end") | log.Info("Get images end") | ||||
| } | } | ||||
| func getModelDirs(jobName string, parentDir string) (string, error) { | |||||
| func GetModelDirs(jobName string, parentDir string) (string, error) { | |||||
| var req string | var req string | ||||
| modelActualPath := setting.JobPath + jobName + "/model/" | modelActualPath := setting.JobPath + jobName + "/model/" | ||||
| if parentDir == "" { | if parentDir == "" { | ||||
| @@ -12,7 +12,6 @@ import ( | |||||
| "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/log" | "code.gitea.io/gitea/modules/log" | ||||
| "code.gitea.io/gitea/modules/obs" | |||||
| "code.gitea.io/gitea/modules/setting" | "code.gitea.io/gitea/modules/setting" | ||||
| "code.gitea.io/gitea/modules/storage" | "code.gitea.io/gitea/modules/storage" | ||||
| ) | ) | ||||
| @@ -70,40 +69,10 @@ func DeleteAllUnzipFile(attachment *models.Attachment, parentDir string) { | |||||
| } | } | ||||
| } | } | ||||
| if attachment.Type == models.TypeCloudBrainTwo { | if attachment.Type == models.TypeCloudBrainTwo { | ||||
| input := &obs.ListObjectsInput{} | |||||
| input.Bucket = setting.Bucket | |||||
| // 设置每页100个对象 | |||||
| input.MaxKeys = 100 | |||||
| input.Prefix = setting.BasePath + attachment.RelativePath() + attachment.UUID | |||||
| index := 1 | |||||
| log.Info("prefix=" + input.Prefix) | |||||
| for { | |||||
| output, err := storage.ObsCli.ListObjects(input) | |||||
| if err == nil { | |||||
| log.Info("Page:%d\n", index) | |||||
| index++ | |||||
| for _, val := range output.Contents { | |||||
| log.Info("delete obs file:" + val.Key) | |||||
| delObj := &obs.DeleteObjectInput{} | |||||
| delObj.Bucket = setting.Bucket | |||||
| delObj.Key = val.Key | |||||
| storage.ObsCli.DeleteObject(delObj) | |||||
| } | |||||
| if output.IsTruncated { | |||||
| input.Marker = output.NextMarker | |||||
| } else { | |||||
| break | |||||
| } | |||||
| } else { | |||||
| if obsError, ok := err.(obs.ObsError); ok { | |||||
| log.Info("Code:%s\n", obsError.Code) | |||||
| log.Info("Message:%s\n", obsError.Message) | |||||
| } | |||||
| break | |||||
| } | |||||
| err := storage.ObsRemoveObject(setting.Bucket, setting.BasePath+attachment.RelativePath()+attachment.UUID) | |||||
| if err != nil { | |||||
| log.Info("delete file error.") | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -257,7 +257,6 @@ func HTTP(ctx *context.Context) { | |||||
| models.EnvPusherID + fmt.Sprintf("=%d", authUser.ID), | models.EnvPusherID + fmt.Sprintf("=%d", authUser.ID), | ||||
| models.EnvIsDeployKey + "=false", | models.EnvIsDeployKey + "=false", | ||||
| } | } | ||||
| if !authUser.KeepEmailPrivate { | if !authUser.KeepEmailPrivate { | ||||
| environ = append(environ, models.EnvPusherEmail+"="+authUser.Email) | environ = append(environ, models.EnvPusherEmail+"="+authUser.Email) | ||||
| } | } | ||||
| @@ -559,6 +558,7 @@ func serviceRPC(h serviceHandler, service string) { | |||||
| if service == "receive-pack" { | if service == "receive-pack" { | ||||
| cmd.Env = append(os.Environ(), h.environ...) | cmd.Env = append(os.Environ(), h.environ...) | ||||
| } | } | ||||
| cmd.Stdout = h.w | cmd.Stdout = h.w | ||||
| cmd.Stdin = reqBody | cmd.Stdin = reqBody | ||||
| cmd.Stderr = &stderr | cmd.Stderr = &stderr | ||||
| @@ -239,6 +239,18 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { | |||||
| deleteUnitTypes = append(deleteUnitTypes, models.UnitTypeCloudBrain) | deleteUnitTypes = append(deleteUnitTypes, models.UnitTypeCloudBrain) | ||||
| } | } | ||||
| if form.EnableModelManager && !models.UnitTypeModelManage.UnitGlobalDisabled() { | |||||
| units = append(units, models.RepoUnit{ | |||||
| RepoID: repo.ID, | |||||
| Type: models.UnitTypeModelManage, | |||||
| Config: &models.ModelManageConfig{ | |||||
| EnableModelManage: form.EnableModelManager, | |||||
| }, | |||||
| }) | |||||
| } else if !models.UnitTypeModelManage.UnitGlobalDisabled() { | |||||
| deleteUnitTypes = append(deleteUnitTypes, models.UnitTypeModelManage) | |||||
| } | |||||
| if form.EnableWiki && form.EnableExternalWiki && !models.UnitTypeExternalWiki.UnitGlobalDisabled() { | if form.EnableWiki && form.EnableExternalWiki && !models.UnitTypeExternalWiki.UnitGlobalDisabled() { | ||||
| if !validation.IsValidExternalURL(form.ExternalWikiURL) { | if !validation.IsValidExternalURL(form.ExternalWikiURL) { | ||||
| ctx.Flash.Error(ctx.Tr("repo.settings.external_wiki_url_error")) | ctx.Flash.Error(ctx.Tr("repo.settings.external_wiki_url_error")) | ||||
| @@ -614,6 +614,8 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| reqRepoDatasetWriter := context.RequireRepoWriter(models.UnitTypeDatasets) | reqRepoDatasetWriter := context.RequireRepoWriter(models.UnitTypeDatasets) | ||||
| reqRepoCloudBrainReader := context.RequireRepoReader(models.UnitTypeCloudBrain) | reqRepoCloudBrainReader := context.RequireRepoReader(models.UnitTypeCloudBrain) | ||||
| reqRepoCloudBrainWriter := context.RequireRepoWriter(models.UnitTypeCloudBrain) | reqRepoCloudBrainWriter := context.RequireRepoWriter(models.UnitTypeCloudBrain) | ||||
| reqRepoModelManageReader := context.RequireRepoReader(models.UnitTypeModelManage) | |||||
| reqRepoModelManageWriter := context.RequireRepoWriter(models.UnitTypeModelManage) | |||||
| //reqRepoBlockChainReader := context.RequireRepoReader(models.UnitTypeBlockChain) | //reqRepoBlockChainReader := context.RequireRepoReader(models.UnitTypeBlockChain) | ||||
| //reqRepoBlockChainWriter := context.RequireRepoWriter(models.UnitTypeBlockChain) | //reqRepoBlockChainWriter := context.RequireRepoWriter(models.UnitTypeBlockChain) | ||||
| @@ -970,6 +972,23 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
| m.Get("/create", reqRepoCloudBrainWriter, repo.CloudBrainNew) | m.Get("/create", reqRepoCloudBrainWriter, repo.CloudBrainNew) | ||||
| m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateCloudBrainForm{}), repo.CloudBrainCreate) | m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateCloudBrainForm{}), repo.CloudBrainCreate) | ||||
| }, context.RepoRef()) | }, context.RepoRef()) | ||||
| m.Group("/modelmanage", func() { | |||||
| m.Post("/create_model", reqRepoModelManageWriter, repo.SaveModel) | |||||
| m.Delete("/delete_model", repo.DeleteModel) | |||||
| m.Put("/modify_model", repo.ModifyModelInfo) | |||||
| m.Get("/show_model", reqRepoModelManageReader, repo.ShowModelTemplate) | |||||
| m.Get("/show_model_info", repo.ShowModelInfo) | |||||
| m.Get("/show_model_info_api", repo.ShowSingleModel) | |||||
| m.Get("/show_model_api", repo.ShowModelPageInfo) | |||||
| m.Get("/show_model_child_api", repo.ShowOneVersionOtherModel) | |||||
| m.Get("/query_train_job", reqRepoCloudBrainReader, repo.QueryTrainJobList) | |||||
| m.Get("/query_train_job_version", reqRepoCloudBrainReader, repo.QueryTrainJobVersionList) | |||||
| m.Group("/:ID", func() { | |||||
| m.Get("", repo.ShowSingleModel) | |||||
| m.Get("/downloadsingle", repo.DownloadSingleModelFile) | |||||
| }) | |||||
| m.Get("/downloadall", repo.DownloadMultiModelFile) | |||||
| }, context.RepoRef()) | |||||
| m.Group("/debugjob", func() { | m.Group("/debugjob", func() { | ||||
| m.Get("", reqRepoCloudBrainReader, repo.DebugJobIndex) | m.Get("", reqRepoCloudBrainReader, repo.DebugJobIndex) | ||||
| @@ -368,7 +368,10 @@ | |||||
| <a class="ui basic button disabled" onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | <a class="ui basic button disabled" onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | ||||
| {{$.i18n.Tr "repo.delete"}} | {{$.i18n.Tr "repo.delete"}} | ||||
| </a> | </a> | ||||
| <<<<<<< HEAD | |||||
| ======= | |||||
| >>>>>>> V20211213 | |||||
| {{end}} | {{end}} | ||||
| </form> | </form> | ||||
| @@ -415,7 +418,10 @@ | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <<<<<<< HEAD | |||||
| {{end}} | {{end}} | ||||
| ======= | |||||
| >>>>>>> V20211213 | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| @@ -137,6 +137,12 @@ | |||||
| {{svg "octicon-inbox" 16}} {{.i18n.Tr "datasets"}} | {{svg "octicon-inbox" 16}} {{.i18n.Tr "datasets"}} | ||||
| </a> | </a> | ||||
| {{end}} | {{end}} | ||||
| {{if .Permission.CanRead $.UnitTypeModelManage}} | |||||
| <a class="{{if .isModelManage}}active{{end}} item" href="{{.RepoLink}}/modelmanage/show_model"> | |||||
| <svg class="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="none" d="M0 0h24v24H0z"/><path d="M3.741 1.408l18.462 10.154a.5.5 0 0 1 0 .876L3.741 22.592A.5.5 0 0 1 3 22.154V1.846a.5.5 0 0 1 .741-.438zM5 13v6.617L18.85 12 5 4.383V11h5v2H5z"/></svg> | |||||
| {{.i18n.Tr "repo.model_manager"}} | |||||
| </a> | |||||
| {{end}} | |||||
| {{if .Permission.CanRead $.UnitTypeCloudBrain}} | {{if .Permission.CanRead $.UnitTypeCloudBrain}} | ||||
| <a class="{{if .PageIsCloudBrain}}active{{end}} item" href="{{.RepoLink}}/debugjob"> | <a class="{{if .PageIsCloudBrain}}active{{end}} item" href="{{.RepoLink}}/debugjob"> | ||||
| <span>{{svg "octicon-server" 16}} {{.i18n.Tr "repo.cloudbrain"}}<i class="question circle icon link cloudbrain-question" data-content={{.i18n.Tr "repo.cloudbrain_helper"}} data-position="top center" data-variation="mini"></i></span> | <span>{{svg "octicon-server" 16}} {{.i18n.Tr "repo.cloudbrain"}}<i class="question circle icon link cloudbrain-question" data-content={{.i18n.Tr "repo.cloudbrain_helper"}} data-position="top center" data-variation="mini"></i></span> | ||||
| @@ -164,86 +170,5 @@ | |||||
| <div class="ui tabs divider"></div> | <div class="ui tabs divider"></div> | ||||
| </div> | </div> | ||||
| <div class="ui select_cloudbrain modal"> | |||||
| <div class="header"> | |||||
| {{$.i18n.Tr "repo.cloudbrain_selection"}} | |||||
| </div> | |||||
| <div class="content"> | |||||
| <div class="ui form" method="post"> | |||||
| <div class="grouped fields"> | |||||
| <label for="CloudBrain">{{$.i18n.Tr "repo.cloudbrain_platform_selection"}}</label> | |||||
| <div class="field"> | |||||
| <div class="ui radio checkbox"> | |||||
| <input type="radio" name="CloudBrainSelect" checked tabindex="0" class="hidden" value="0"> | |||||
| <label>{{$.i18n.Tr "repo.cloudbrain1"}}</label> | |||||
| </div> | |||||
| </div> | |||||
| <div class="field"> | |||||
| <div class="ui radio checkbox"> | |||||
| <input type="radio" name="CloudBrainSelect" tabindex="0" class="hidden" value="1"> | |||||
| <label>{{$.i18n.Tr "repo.cloudbrain2"}}</label> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <div class="actions"> | |||||
| <div class="mb-2 ui positive right labeled icon button"> | |||||
| {{$.i18n.Tr "repo.confirm_choice"}} | |||||
| <i class="checkmark icon"></i> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <script src="https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js"></script> | <script src="https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js"></script> | ||||
| <script> | |||||
| // 点击云脑进行选择云脑平台并进入相应的界面 | |||||
| $('.item.cloudbrain').click(function(){ | |||||
| $('.ui.select_cloudbrain.modal') | |||||
| .modal('closable', false) | |||||
| .modal('show'); | |||||
| // $('.ui.select_cloudbrain.modal').modal('show'); | |||||
| $('.ui.radio.checkbox').checkbox(); | |||||
| var repolink = $(".cloudbrain_link").text() | |||||
| console.log(repolink) | |||||
| $(".ui.positive.right.icon.button").click(function(){ | |||||
| // 声明一个变量来接收以及获取单选框选择的情况 | |||||
| var checked_radio = $("input[name='CloudBrainSelect']:checked").val() | |||||
| console.log(checked_radio) | |||||
| if(checked_radio=='0'){ | |||||
| window.location.href = repolink+'/cloudbrain' | |||||
| }else if(checked_radio=='1'){ | |||||
| window.location.href = repolink+'/modelarts/notebook' | |||||
| }else{ | |||||
| return; | |||||
| } | |||||
| }) | |||||
| }) | |||||
| // 点击数据集进行选择云脑平台并进入相应的界面 | |||||
| $('.item.dataset').click(function(){ | |||||
| $('.ui.select_cloudbrain.modal') | |||||
| .modal('closable', false) | |||||
| .modal('show'); | |||||
| $('.ui.radio.checkbox').checkbox(); | |||||
| var repolink = $(".dataset_link").text() | |||||
| console.log(repolink) | |||||
| $(".ui.positive.right.icon.button").click(function(){ | |||||
| // 声明一个变量来接收以及获取单选框选择的情况 | |||||
| var checked_radio = $("input[type='radio']:checked").val() | |||||
| $('.ui.select_cloudbrain.modal') | |||||
| .modal('show'); | |||||
| // 向后端传递对象 | |||||
| window.location.href = repolink + "/datasets?type=" + checked_radio | |||||
| }) | |||||
| }) | |||||
| $('.question.circle.icon').hover(function(){ | |||||
| $(this).popup('show') | |||||
| $('.ui.popup.mini.top.center').css({"border-color":'rgba(50, 145, 248, 100)',"color":"rgba(3, 102, 214, 100)","border-radius":"5px","border-shadow":"none"}) | |||||
| }); | |||||
| </script> | |||||
| @@ -1,187 +1,6 @@ | |||||
| <!-- 头部导航栏 --> | <!-- 头部导航栏 --> | ||||
| {{template "base/head" .}} | {{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="mask"> | ||||
| <div id="loadingPage"> | <div id="loadingPage"> | ||||
| @@ -1,89 +1,8 @@ | |||||
| {{template "base/head" .}} | {{template "base/head" .}} | ||||
| <style> | <style> | ||||
| /* 遮罩层css效果图 */ | |||||
| #mask { | |||||
| position: fixed; | |||||
| top: 0px; | |||||
| left: 0px; | |||||
| right: 0px; | |||||
| bottom: 0px; | |||||
| filter: alpha(opacity=60); | |||||
| background-color: #777; | |||||
| z-index: 1000; | |||||
| .inline.required.field.cloudbrain_benchmark { | |||||
| display: none; | 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> | </style> | ||||
| <div id="mask"> | <div id="mask"> | ||||
| @@ -2,184 +2,6 @@ | |||||
| {{template "base/head" .}} | {{template "base/head" .}} | ||||
| <style> | <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; | |||||
| } | |||||
| .fontsize14{ | .fontsize14{ | ||||
| font-size: 14px; | font-size: 14px; | ||||
| } | } | ||||
| @@ -47,87 +47,7 @@ | |||||
| border-radius: 5px 0px 0px 5px; | border-radius: 5px 0px 0px 5px; | ||||
| line-height: 21px; | line-height: 21px; | ||||
| text-align: center; | 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; | |||||
| } | |||||
| .left2{ | |||||
| margin-left: -2px; | |||||
| } | |||||
| @-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); | |||||
| } | |||||
| color: #C2C7CC; | |||||
| } | } | ||||
| </style> | </style> | ||||
| @@ -49,87 +49,6 @@ | |||||
| text-align: center; | text-align: center; | ||||
| color: #C2C7CC;" | 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; | |||||
| } | |||||
| .left2{ | |||||
| margin-left: -2px; | |||||
| } | |||||
| @-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> | </style> | ||||
| <!-- <div class="ui page dimmer"> | <!-- <div class="ui page dimmer"> | ||||
| @@ -0,0 +1,299 @@ | |||||
| <!-- 头部导航栏 --> | |||||
| {{template "base/head" .}} | |||||
| <style> | |||||
| .width70{ | |||||
| width: 70% !important; | |||||
| } | |||||
| .width83{ | |||||
| width: 83% !important; | |||||
| } | |||||
| .content-padding{ | |||||
| padding: 40px !important; | |||||
| } | |||||
| </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> | |||||
| {{$repository := .Repository.ID}} | |||||
| <!-- 提示框 --> | |||||
| <div class="alert"></div> | |||||
| <div class="repository release dataset-list view"> | |||||
| {{template "repo/header" .}} | |||||
| <!-- 列表容器 --> | |||||
| <div class="ui container active loader" id="loadContainer"> | |||||
| {{template "base/alert" .}} | |||||
| <div class="ui two column stackable grid "> | |||||
| <div class="column"></div> | |||||
| <div class="column right aligned"> | |||||
| <!-- --> | |||||
| <a class="ui button {{if .Permission.CanWrite $.UnitTypeCloudBrain}} green {{else}} disabled {{end}}" onclick="showcreate(this)">{{$.i18n.Tr "repo.model.manage.import_new_model"}}</a> | |||||
| </div> | |||||
| </div> | |||||
| <!-- 中下列表展示区 --> | |||||
| <div class="ui grid"> | |||||
| <div class="row" style="padding-top: 0;"> | |||||
| <div class="ui sixteen wide column"> | |||||
| <!-- 任务展示 --> | |||||
| <div class="dataset list" id="model_list"> | |||||
| <!-- 表头 --> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <!-- div full height--> | |||||
| </div> | |||||
| <!-- 确认模态框 --> | |||||
| <div id="deletemodel"> | |||||
| <div class="ui basic modal first"> | |||||
| <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 id="newmodel"> | |||||
| <div class="ui modal second"> | |||||
| <div class="header" style="padding: 1rem;background-color: rgba(240, 240, 240, 100);"> | |||||
| <h4>导入新模型</h4> | |||||
| </div> | |||||
| <div class="content content-padding"> | |||||
| <form id="formId" method="POST" class="ui form"> | |||||
| <div class="ui error message"> | |||||
| <!-- <p>asdasdasd</p> --> | |||||
| </div> | |||||
| <input type="hidden" name="_csrf" value=""> | |||||
| <div class="two inline fields "> | |||||
| <div class="required ten wide field"> | |||||
| <label style="margin-left: -23px;">选择训练任务</label> | |||||
| <div class="ui dropdown selection search width83 loading" id="choice_model"> | |||||
| <input type="hidden" id="JobId" name="JobId" required> | |||||
| <div class="default text">选择训练任务</div> | |||||
| <i class="dropdown icon"></i> | |||||
| <div class="menu" id="job-name"> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <div class="required six widde field"> | |||||
| <label>版本</label> | |||||
| <div class="ui dropdown selection search width70" id="choice_version"> | |||||
| <input type="hidden" id="VersionName" name="VersionName" required> | |||||
| <div class="default text">选择版本</div> | |||||
| <i class="dropdown icon"></i> | |||||
| <div class="menu" id="job-version"> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| <div class="required inline field" id="modelname"> | |||||
| <label>模型名称</label> | |||||
| <input style="width: 45%;" id="name" name="Name" required maxlength="25" onkeyup="this.value=this.value.replace(/[, ]/g,'')"> | |||||
| </div> | |||||
| <div class="required inline field" id="verionname"> | |||||
| <label>模型版本</label> | |||||
| <input style="width: 45%;" id="version" name="Version" value="" readonly required maxlength="255"> | |||||
| </div> | |||||
| <div class="inline field"> | |||||
| <label>模型标签</label> | |||||
| <input style="width: 83%;margin-left: 7px;" name="Label" maxlength="255"> | |||||
| </div> | |||||
| <div class="inline field"> | |||||
| <label for="description">模型描述</label> | |||||
| <textarea style="width: 83%;margin-left: 7px;" 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> | |||||
| <div class="inline field" style="margin-left: 75px;"> | |||||
| <button id="submitId" type="button" class="ui create_train_job green button" style="position: absolute;"> | |||||
| {{.i18n.Tr "repo.cloudbrain.new"}} | |||||
| </button> | |||||
| </div> | |||||
| </form> | |||||
| <div class="actions" style="display: inline-block;margin-left: 180px;"> | |||||
| <button class="ui button cancel" >{{.i18n.Tr "repo.cloudbrain.cancel"}}</button> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| {{template "base/footer" .}} | |||||
| <script> | |||||
| let repolink = {{.RepoLink}} | |||||
| let repoId = {{$repository}} | |||||
| let url_href = window.location.pathname.split('show_model')[0] + 'create_model' | |||||
| const {_AppSubUrl, _StaticUrlPrefix, csrf} = window.config; | |||||
| $('input[name="_csrf"]').val(csrf) | |||||
| function createModelName(){ | |||||
| let repoName = location.pathname.split('/')[2] | |||||
| let modelName = repoName + '_model_' + Math.random().toString(36).substr(2, 4) | |||||
| $('#name').val(modelName) | |||||
| $('#version').val("0.0.1") | |||||
| } | |||||
| function showcreate(obj){ | |||||
| $('.ui.modal.second') | |||||
| .modal({ | |||||
| centered: false, | |||||
| onShow:function(){ | |||||
| $('.ui.dimmer').css({"background-color":"rgb(136, 136, 136,0.7)"}) | |||||
| $("#job-name").empty() | |||||
| createModelName() | |||||
| loadTrainList() | |||||
| }, | |||||
| onHide:function(){ | |||||
| document.getElementById("formId").reset(); | |||||
| $('#choice_model').dropdown('clear') | |||||
| $('#choice_version').dropdown('clear') | |||||
| $('.ui.dimmer').css({"background-color":""}) | |||||
| $('.ui.error.message').css('display','none') | |||||
| } | |||||
| }) | |||||
| .modal('show') | |||||
| } | |||||
| $(function(){ | |||||
| $('#choice_model').dropdown({ | |||||
| onChange:function(value){ | |||||
| $(".ui.dropdown.selection.search.width70").addClass("loading") | |||||
| $("#job-version").empty() | |||||
| loadTrainVersion(value) | |||||
| } | |||||
| }) | |||||
| }) | |||||
| function versionAdd(version){ | |||||
| let versionArray = version.split('.') | |||||
| if(versionArray[2]=='9'){ | |||||
| if(versionArray[1]=='9'){ | |||||
| versionArray[0] = String(Number(versionArray[1])+1) | |||||
| versionArray[1] = '0' | |||||
| }else{ | |||||
| versionArray[1]=String(Number(versionArray[1])+1) | |||||
| } | |||||
| versionArray[2]='0' | |||||
| }else{ | |||||
| versionArray[2]=String(Number(versionArray[2])+1) | |||||
| } | |||||
| return versionArray.join('.') | |||||
| } | |||||
| function loadTrainList(){ | |||||
| $.get(`${repolink}/modelmanage/query_train_job?repoId=${repoId}`, (data) => { | |||||
| const n_length = data.length | |||||
| let train_html='' | |||||
| for (let i=0;i<n_length;i++){ | |||||
| train_html += `<div class="item" data-value="${data[i].JobID}">${data[i].JobName}</div>` | |||||
| train_html += '</div>' | |||||
| } | |||||
| $("#job-name").append(train_html) | |||||
| $(".ui.dropdown.selection.search.width83").removeClass("loading") | |||||
| }) | |||||
| } | |||||
| function loadTrainVersion(value){ | |||||
| $.get(`${repolink}/modelmanage/query_train_job_version?JobID=${value}`, (data) => { | |||||
| const n_length = data.length | |||||
| let train_html='' | |||||
| for (let i=0;i<n_length;i++){ | |||||
| train_html += `<div class="item" data-value="${data[i].VersionName}">${data[i].VersionName}</div>` | |||||
| train_html += '</div>' | |||||
| } | |||||
| $("#job-version").append(train_html) | |||||
| $(".ui.dropdown.selection.search.width70").removeClass("loading") | |||||
| }) | |||||
| } | |||||
| function check(){ | |||||
| let jobid = document.getElementById("JobId").value | |||||
| let versionname = document.getElementById("VersionName").value | |||||
| let name= document.getElementById("name").value | |||||
| let version= document.getElementById("version").value | |||||
| if(jobid==""){ | |||||
| $(".required.ten.wide.field").addClass("error") | |||||
| return false | |||||
| }else{ | |||||
| $(".required.ten.wide.field").removeClass("error") | |||||
| } | |||||
| if(versionname==""){ | |||||
| $(".required.six.widde.field").addClass("error") | |||||
| return false | |||||
| }else{ | |||||
| $(".required.six.widde.field").removeClass("error") | |||||
| } | |||||
| if(name==""){ | |||||
| $("#modelname").addClass("error") | |||||
| return false | |||||
| }else{ | |||||
| $("#modelname").removeClass("error") | |||||
| } | |||||
| if(versionname==""){ | |||||
| $("#verionname").addClass("error") | |||||
| return false | |||||
| }else{ | |||||
| $("#verionname").removeClass("error") | |||||
| } | |||||
| return true | |||||
| } | |||||
| $('#submitId').click(function(){ | |||||
| let flag=check() | |||||
| if(flag){ | |||||
| let data = $("#formId").serialize() | |||||
| $("#mask").css({"display":"block","z-index":"9999"}) | |||||
| $.ajax({ | |||||
| url:url_href, | |||||
| type:'POST', | |||||
| data:data, | |||||
| success:function(res){ | |||||
| $('.ui.modal.second').modal('hide') | |||||
| window.location.href=location.href | |||||
| }, | |||||
| error: function(xhr){ | |||||
| // 隐藏 loading | |||||
| // 只有请求不正常(状态码不为200)才会执行 | |||||
| console.log("-------------",xhr) | |||||
| $('.ui.error.message').text(xhr.responseText) | |||||
| $('.ui.error.message').css('display','block') | |||||
| }, | |||||
| complete:function(xhr){ | |||||
| $("#mask").css({"display":"none","z-index":"1"}) | |||||
| } | |||||
| }) | |||||
| }else{ | |||||
| return false | |||||
| } | |||||
| }) | |||||
| </script> | |||||
| @@ -0,0 +1,218 @@ | |||||
| {{template "base/head" .}} | |||||
| <div class="repository"> | |||||
| {{template "repo/header" .}} | |||||
| <style> | |||||
| .model_header_text{ | |||||
| font-size: 14px; | |||||
| color: #101010; | |||||
| font-weight: bold; | |||||
| } | |||||
| .ti_form{ | |||||
| text-align: left; | |||||
| max-width: 100%; | |||||
| vertical-align: middle; | |||||
| } | |||||
| .ti-text-form-label { | |||||
| padding-bottom: 20px; | |||||
| padding-right: 20px; | |||||
| color: #8a8e99; | |||||
| font-size: 14px; | |||||
| white-space: nowrap !important; | |||||
| width: 80px; | |||||
| line-height: 30px; | |||||
| } | |||||
| .ti-text-form-content { | |||||
| line-height: 30px; | |||||
| padding-bottom: 20px; | |||||
| width: 100%; | |||||
| } | |||||
| .change-version{ | |||||
| min-width: auto !important; | |||||
| border: 1px solid rgba(187, 187, 187, 100) !important; | |||||
| border-radius: .38571429rem !important; | |||||
| margin-left: 1.5em; | |||||
| } | |||||
| .title-word-elipsis{ | |||||
| overflow: hidden; | |||||
| text-overflow: ellipsis; | |||||
| white-space: nowrap; | |||||
| width: 30%; | |||||
| } | |||||
| .word-elipsis{ | |||||
| overflow: hidden; | |||||
| text-overflow: ellipsis; | |||||
| white-space: nowrap; | |||||
| padding-right: 80px; | |||||
| } | |||||
| .half-table{ | |||||
| width: 50%; | |||||
| float: left; | |||||
| } | |||||
| .text-width80 { | |||||
| width: 100px; | |||||
| line-height: 30px; | |||||
| } | |||||
| .tableStyle{ | |||||
| width:100%; | |||||
| table-layout: fixed; | |||||
| } | |||||
| .iword-elipsis{ | |||||
| display: inline-block; | |||||
| width: 80%; | |||||
| overflow: hidden; | |||||
| text-overflow: ellipsis; | |||||
| white-space: nowrap; | |||||
| } | |||||
| </style> | |||||
| <div class="ui container"> | |||||
| <h4 class="ui header" id="vertical-segment"> | |||||
| <!-- <a href="javascript:window.history.back();"><i class="arrow left icon"></i>返回</a> --> | |||||
| <div class="ui breadcrumb"> | |||||
| <a class="section" href="{{$.RepoLink}}/modelmanage/show_model"> | |||||
| 模型管理 | |||||
| </a> | |||||
| <div class="divider"> / </div> | |||||
| <div class="active section">{{.name}}</div> | |||||
| </div> | |||||
| <select class="ui dropdown tiny change-version" id="dropdown" onchange="changeInfo(this.value)"> | |||||
| </select> | |||||
| </h4> | |||||
| <div id="showInfo" style="border:1px solid #e2e2e2;padding: 20px 60px;margin-top:24px"> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| {{template "base/footer" .}} | |||||
| <script> | |||||
| let url = location.href.split('show_model')[0] | |||||
| let ID = location.search.split('?name=').pop() | |||||
| $(document).ready(loadInfo); | |||||
| function changeInfo(version){ | |||||
| $.get(`${url}show_model_info_api?name=${ID}`,(data)=>{ | |||||
| let versionData = data.filter((item)=>{ | |||||
| return item.Version === version | |||||
| }) | |||||
| let initObj = transObj(versionData)[0] | |||||
| let initModelAcc = transObj(versionData)[1] | |||||
| let id= transObj(data)[2] | |||||
| $('#showInfo').empty() | |||||
| renderInfo(initObj,initModelAcc,id) | |||||
| }) | |||||
| } | |||||
| function loadInfo(){ | |||||
| $.get(`${url}show_model_info_api?name=${ID}`,(data)=>{ | |||||
| let html = '' | |||||
| for (let i=0;i<data.length;i++){ | |||||
| html += `<option value="${data[i].Version}">${data[i].Version}</option>` | |||||
| } | |||||
| $('#dropdown').append(html) | |||||
| let initObj = transObj(data)[0] | |||||
| let initModelAcc = transObj(data)[1] | |||||
| let id= transObj(data)[2] | |||||
| renderInfo(initObj,initModelAcc,id) | |||||
| }) | |||||
| } | |||||
| function transObj(data){ | |||||
| let {ID,Name,Version,Label,Size,Description,CreatedUnix,Accuracy} = data[0] | |||||
| let modelAcc = JSON.parse(Accuracy) | |||||
| let size = tranSize(Size) | |||||
| let time = transTime(CreatedUnix) | |||||
| let initObj = { | |||||
| ModelName:Name || '--', | |||||
| Version:Version, | |||||
| Label:Label || '--', | |||||
| Size:size, | |||||
| CreateTime:time, | |||||
| Description:Description || '--', | |||||
| } | |||||
| let initModelAcc = { | |||||
| Accuracy: modelAcc.Accuracy || '--', | |||||
| F1: modelAcc.F1 || '--', | |||||
| Precision:modelAcc.Precision || '--', | |||||
| Recall: modelAcc.Recall || '--' | |||||
| } | |||||
| return [initObj,initModelAcc,ID] | |||||
| } | |||||
| function transTime(time){ | |||||
| let date = new Date(time * 1000);//时间戳为10位需*1000,时间戳为13位的话不需乘1000 | |||||
| let Y = date.getFullYear() + '-'; | |||||
| let M = (date.getMonth()+1 < 10 ? '0'+(date.getMonth()+1):date.getMonth()+1) + '-'; | |||||
| let D = (date.getDate()< 10 ? '0'+date.getDate():date.getDate())+ ' '; | |||||
| let h = (date.getHours() < 10 ? '0'+date.getHours():date.getHours())+ ':'; | |||||
| let m = (date.getMinutes() < 10 ? '0'+date.getMinutes():date.getMinutes()) + ':'; | |||||
| let s = date.getSeconds() < 10 ? '0'+date.getSeconds():date.getSeconds(); | |||||
| return Y+M+D+h+m+s; | |||||
| } | |||||
| function tranSize(value){ | |||||
| if(null==value||value==''){ | |||||
| return "0 Bytes"; | |||||
| } | |||||
| var unitArr = new Array("Bytes","KB","MB","GB","TB","PB","EB","ZB","YB"); | |||||
| var index=0; | |||||
| var srcsize = parseFloat(value); | |||||
| index=Math.floor(Math.log(srcsize)/Math.log(1024)); | |||||
| var size =srcsize/Math.pow(1024,index); | |||||
| size=size.toFixed(2);//保留的小数位数 | |||||
| return size+unitArr[index]; | |||||
| } | |||||
| function editorFn(text,id){ | |||||
| $('#edit-td').replaceWith("<div id='edit-div' style='width:80%;display: inline-block;'><textarea id='textarea-value' rows='3' maxlength='255' style='width:80%;' id='edit-text'></textarea><i class='check icon' style='color: #50d4ab;' onclick='editorSure(\"" + text + "\",\"" + id + "\")'></i><i class='times icon' style='color: #f66f6a;' onclick='editorCancel(\"" + text + "\",\"" + id + "\")'></i></div>"); | |||||
| } | |||||
| function editorCancel(text,id){ | |||||
| $('#edit-div').replaceWith('<div id="edit-td" style="display:flex;"><span title="\'' + text + '\'" class="iword-elipsis">'+text+'</span><i class="pencil alternate icon" style="cursor:pointer;vertical-align: top;" id="editor" onclick="editorFn(\'' + text + '\',\'' + id + '\')"></div>') | |||||
| } | |||||
| function editorSure(text,id){ | |||||
| let description=$('#textarea-value').val() | |||||
| let data = { | |||||
| ID:id, | |||||
| Description:description | |||||
| } | |||||
| $.ajax({ | |||||
| url:`${url}modify_model`, | |||||
| type:'PUT', | |||||
| data:data | |||||
| }).done((res)=>{ | |||||
| $('#edit-div').replaceWith('<div id="edit-td" style="display:flex;"><span title="\'' + description + '\'" class="iword-elipsis">'+description+'</span><i class="pencil alternate icon" style="cursor:pointer;vertical-align: top;" id="editor" onclick="editorFn(\'' + description + '\',\'' + id + '\')"></div>') | |||||
| }) | |||||
| } | |||||
| function renderInfo(obj,accObj,id){ | |||||
| let html = '' | |||||
| html += '<div class="half-table">' | |||||
| html += '<span class="model_header_text">基本信息</span>' | |||||
| html += '<table class="tableStyle" style="margin-top:20px;">' | |||||
| html += '<tbody>' | |||||
| for(let key in obj){ | |||||
| html += '<tr style="font-size: 12px;">' | |||||
| html += `<td class="ti-text-form-label text-width80">${key}</td>` | |||||
| if(key==="Description"){ | |||||
| let description = obj[key] | |||||
| html += '<td class="ti-text-form-content" ><div id="edit-td" style="display:flex"><span title="\'' + description + '\'" class="iword-elipsis">'+description+'</span><i class="pencil alternate icon" style="cursor:pointer;vertical-align: top;" id="editor" onclick="editorFn(\'' + description + '\',\'' + id + '\')"></div></td>' | |||||
| }else{ | |||||
| html += `<td class="ti-text-form-content word-elipsis"><span title="${obj[key]}">${obj[key]}</span></td>` | |||||
| } | |||||
| html += '</tr>' | |||||
| } | |||||
| html += '</tbody>' | |||||
| html += '</table>' | |||||
| html += '</div>' | |||||
| html += '<div class="half-table">' | |||||
| html += '<span class="model_header_text">模型精度</span>' | |||||
| html += '<table class="tableStyle" style="margin-top:20px;">' | |||||
| html += '<tbody>' | |||||
| for(let key in accObj){ | |||||
| html += '<tr style="font-size: 12px;">' | |||||
| html += `<td class="ti-text-form-label text-width80">${key}</td>` | |||||
| html += `<td class="ti-text-form-content word-elipsis">${accObj[key]}</td>` | |||||
| html += '</tr>' | |||||
| } | |||||
| html += '</tbody>' | |||||
| html += '</table>' | |||||
| html += '</div>' | |||||
| html += '<div style="clear: both;"></div>' | |||||
| $('#showInfo').append(html) | |||||
| } | |||||
| </script> | |||||
| @@ -158,7 +158,14 @@ | |||||
| <label>{{.i18n.Tr "repo.settings.cloudbrain_desc"}}</label> | <label>{{.i18n.Tr "repo.settings.cloudbrain_desc"}}</label> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| {{$isModelMangeEnabled := .Repository.UnitEnabled $.UnitTypeModelManage }} | |||||
| <div class="inline field"> | |||||
| <label>{{.i18n.Tr "repo.model_manager"}}</label> | |||||
| <div class="ui checkbox"> | |||||
| <input class="enable-system" name="enable_model_manager" type="checkbox" {{if $isModelMangeEnabled}}checked{{end}}> | |||||
| <label>{{.i18n.Tr "repo.settings.model_desc"}}</label> | |||||
| </div> | |||||
| </div> | |||||
| {{$isWikiEnabled := or (.Repository.UnitEnabled $.UnitTypeWiki) (.Repository.UnitEnabled $.UnitTypeExternalWiki)}} | {{$isWikiEnabled := or (.Repository.UnitEnabled $.UnitTypeWiki) (.Repository.UnitEnabled $.UnitTypeExternalWiki)}} | ||||
| <div class="inline field"> | <div class="inline field"> | ||||
| <label>{{.i18n.Tr "repo.wiki"}}</label> | <label>{{.i18n.Tr "repo.wiki"}}</label> | ||||
| @@ -0,0 +1,427 @@ | |||||
| <template> | |||||
| <div> | |||||
| <div class="ui container" id="header"> | |||||
| <el-row style="margin-top:15px;" v-loading.fullscreen.lock="fullscreenLoading"> | |||||
| <el-table | |||||
| ref="table" | |||||
| :data="tableData" | |||||
| style="min-width: 100%" | |||||
| row-key="ID" | |||||
| lazy | |||||
| :load="load" | |||||
| :tree-props="{children: 'children', hasChildren: 'hasChildren'}" | |||||
| :header-cell-style="tableHeaderStyle" | |||||
| > | |||||
| <el-table-column | |||||
| prop="Name" | |||||
| label="模型名称" | |||||
| align="left" | |||||
| min-width="18%" | |||||
| > | |||||
| <template slot-scope="scope"> | |||||
| <div class="expand-icon" v-if="scope.row.hasChildren===false"> | |||||
| <i class="el-icon-arrow-right"></i> | |||||
| </div> | |||||
| <!-- <i class="el-icon-time"></i> --> | |||||
| <a class="text-over" :href="showinfoHref+scope.row.Name" :title="scope.row.Name">{{ scope.row.Name }}</a> | |||||
| </template> | |||||
| </el-table-column> | |||||
| <el-table-column | |||||
| prop="Version" | |||||
| label="版本" | |||||
| align="center" | |||||
| min-width="6.5%" | |||||
| > | |||||
| <template slot-scope="scope"> | |||||
| <span class="text-over" :title="scope.row.Version">{{ scope.row.Version}}</span> | |||||
| </template> | |||||
| </el-table-column> | |||||
| <el-table-column | |||||
| prop="VersionCount" | |||||
| label="版本数" | |||||
| align="center" | |||||
| min-width="7.5%" | |||||
| > | |||||
| <template slot-scope="scope"> | |||||
| <span class="text-over" :title="scope.row.VersionCount">{{ scope.row.VersionCount}}</span> | |||||
| </template> | |||||
| </el-table-column> | |||||
| <el-table-column | |||||
| prop="Size" | |||||
| label="模型大小" | |||||
| align="center" | |||||
| min-width="10.5%" | |||||
| > | |||||
| <template slot-scope="scope"> | |||||
| <span class="text-over">{{ renderSize(scope.row.Size)}}</span> | |||||
| </template> | |||||
| </el-table-column> | |||||
| <el-table-column | |||||
| prop="EngineName" | |||||
| label="AI引擎" | |||||
| align="center" | |||||
| min-width="8.5%" | |||||
| > | |||||
| <template slot-scope="scope"> | |||||
| <span class="text-over">{{ scope.row.EngineName}}</span> | |||||
| </template> | |||||
| </el-table-column> | |||||
| <el-table-column | |||||
| prop="ComputeResource" | |||||
| label="计算资源" | |||||
| align="center" | |||||
| min-width="10.5%" | |||||
| > | |||||
| <template slot-scope="scope"> | |||||
| <span class="text-over">{{ scope.row.ComputeResource}}</span> | |||||
| </template> | |||||
| </el-table-column> | |||||
| <el-table-column | |||||
| prop="CreatedUnix" | |||||
| label="创建时间" | |||||
| align="center" | |||||
| min-width="13.75%" | |||||
| > | |||||
| <template slot-scope="scope"> | |||||
| {{transTime(scope.row.CreatedUnix)}} | |||||
| </template> | |||||
| </el-table-column> | |||||
| <el-table-column | |||||
| prop="UserName" | |||||
| label="创建者" | |||||
| align="center" | |||||
| min-width="6.75%" | |||||
| > | |||||
| <template slot-scope="scope"> | |||||
| <a :href="'/'+scope.row.UserName" :title="scope.row.UserName"> | |||||
| <img class="ui avatar image" :src="scope.row.UserRelAvatarLink"> | |||||
| </a> | |||||
| </template> | |||||
| </el-table-column> | |||||
| <el-table-column label="操作" min-width="18%" align="center"> | |||||
| <template slot-scope="scope"> | |||||
| <div class="space-around"> | |||||
| <a :style="{visibility:!scope.row.Children ? 'visible':'hidden'}" :class="{'disabled':!scope.row.IsCanOper}" @click="showcreateVue(scope.row.Name,scope.row.Version)">创建新版本</a> | |||||
| <a :href="loadhref+scope.row.ID" :class="{'disabled':!scope.row.IsCanOper}">下载</a> | |||||
| <a :class="{'disabled':!scope.row.IsCanOper}" @click="deleteModel(scope.row.ID,scope.row.cName)">删除</a> | |||||
| </div> | |||||
| </template> | |||||
| </el-table-column> | |||||
| </el-table> | |||||
| </el-row> | |||||
| <div class="ui container" style="margin-top:50px;text-align:center"> | |||||
| <el-pagination | |||||
| background | |||||
| @size-change="handleSizeChange" | |||||
| @current-change="handleCurrentChange" | |||||
| :current-page="currentPage" | |||||
| :page-sizes="[10]" | |||||
| :page-size="pageSize" | |||||
| layout="total, sizes, prev, pager, next, jumper" | |||||
| :total="totalNum"> | |||||
| </el-pagination> | |||||
| </div> | |||||
| </div> | |||||
| </div> | |||||
| </template> | |||||
| <script> | |||||
| const {_AppSubUrl, _StaticUrlPrefix, csrf} = window.config; | |||||
| export default { | |||||
| components: { | |||||
| }, | |||||
| data() { | |||||
| return { | |||||
| currentPage:1, | |||||
| pageSize:10, | |||||
| totalNum:0, | |||||
| params:{page:0,pageSize:10}, | |||||
| tableData: [], | |||||
| fullscreenLoading: false, | |||||
| url:'', | |||||
| isLoading:true, | |||||
| loadNodeMap:new Map() | |||||
| }; | |||||
| }, | |||||
| methods: { | |||||
| load(tree, treeNode, resolve) { | |||||
| this.loadNodeMap.set(tree.cName, tree.ID) | |||||
| this.$axios.get(this.url+'show_model_child_api',{params:{ | |||||
| name:tree.cName | |||||
| }}).then((res)=>{ | |||||
| let TrainTaskInfo | |||||
| let tableData | |||||
| tableData= res.data | |||||
| for(let i=0;i<tableData.length;i++){ | |||||
| TrainTaskInfo = JSON.parse(tableData[i].TrainTaskInfo) | |||||
| tableData[i].EngineName = TrainTaskInfo.EngineName.split('-')[0] | |||||
| tableData[i].ComputeResource = TrainTaskInfo.ComputeResource | |||||
| tableData[i].cName=tableData[i].Name | |||||
| tableData[i].Name='' | |||||
| tableData[i].VersionCount = '' | |||||
| tableData[i].Children = true | |||||
| } | |||||
| resolve(tableData) | |||||
| }) | |||||
| }, | |||||
| tableHeaderStyle({row,column,rowIndex,columnIndex}){ | |||||
| if(rowIndex===0){ | |||||
| return 'background:#f5f5f6;color:#606266' | |||||
| } | |||||
| }, | |||||
| handleSizeChange(val){ | |||||
| this.params.size = val | |||||
| this.getModelList() | |||||
| }, | |||||
| handleCurrentChange(val){ | |||||
| this.params.page = val | |||||
| this.getModelList() | |||||
| }, | |||||
| showcreateVue(name,version){ | |||||
| $('.ui.modal.second') | |||||
| .modal({ | |||||
| centered: false, | |||||
| onShow:function(){ | |||||
| $('.ui.dimmer').css({"background-color":"rgb(136, 136, 136,0.7)"}) | |||||
| $("#job-name").empty() | |||||
| $('#name').val(name) | |||||
| let version_string = versionAdd(version) | |||||
| $('#version').val(version_string) | |||||
| loadTrainList() | |||||
| }, | |||||
| onHide:function(){ | |||||
| document.getElementById("formId").reset(); | |||||
| $('#choice_model').dropdown('clear') | |||||
| $('#choice_version').dropdown('clear') | |||||
| $('.ui.dimmer').css({"background-color":""}) | |||||
| } | |||||
| }) | |||||
| .modal('show') | |||||
| }, | |||||
| deleteModel(id,name){ | |||||
| let tree={cName:name} | |||||
| let _this = this | |||||
| let flag=1 | |||||
| $('.ui.basic.modal.first') | |||||
| .modal({ | |||||
| onDeny: function() { | |||||
| flag = false | |||||
| }, | |||||
| onApprove: function() { | |||||
| _this.$axios.delete(_this.url+'delete_model',{ | |||||
| params:{ | |||||
| ID:id | |||||
| }}).then((res)=>{ | |||||
| _this.getModelList() | |||||
| _this.loadrefresh(tree) | |||||
| }) | |||||
| flag = true | |||||
| }, | |||||
| onHidden: function() { | |||||
| if (flag == false) { | |||||
| $('.alert').html('您已取消操作').removeClass('alert-success').addClass('alert-danger').show().delay(1500).fadeOut(); | |||||
| } | |||||
| } | |||||
| }) | |||||
| .modal('show') | |||||
| }, | |||||
| loadrefresh(tree){ | |||||
| this.$axios.get(this.url+'show_model_child_api',{params:{ | |||||
| name:tree.cName | |||||
| }}).then((res)=>{ | |||||
| let TrainTaskInfo | |||||
| let tableData | |||||
| tableData= res.data | |||||
| for(let i=0;i<tableData.length;i++){ | |||||
| TrainTaskInfo = JSON.parse(tableData[i].TrainTaskInfo) | |||||
| tableData[i].EngineName = TrainTaskInfo.EngineName.split('-')[0] | |||||
| tableData[i].ComputeResource = TrainTaskInfo.ComputeResource | |||||
| tableData[i].cName=tableData[i].Name | |||||
| tableData[i].Name='' | |||||
| tableData[i].VersionCount = '' | |||||
| tableData[i].Children = true | |||||
| } | |||||
| let id = this.loadNodeMap.get(tree.cName) | |||||
| this.$set(this.$refs.table.store.states.lazyTreeNodeMap, id, tableData) | |||||
| }) | |||||
| }, | |||||
| getModelList(){ | |||||
| this.fullscreenLoading = false | |||||
| this.$axios.get(location.href+'_api',{ | |||||
| params:this.params | |||||
| }).then((res)=>{ | |||||
| $("#loadContainer").removeClass("loader") | |||||
| let TrainTaskInfo | |||||
| this.tableData = res.data.data | |||||
| for(let i=0;i<this.tableData.length;i++){ | |||||
| TrainTaskInfo = JSON.parse(this.tableData[i].TrainTaskInfo) | |||||
| this.tableData[i].cName=this.tableData[i].Name | |||||
| this.tableData[i].EngineName = TrainTaskInfo.EngineName.split('-')[0] | |||||
| this.tableData[i].ComputeResource = TrainTaskInfo.ComputeResource | |||||
| this.tableData[i].hasChildren = res.data.data[i].VersionCount===1 ? false : true | |||||
| } | |||||
| this.totalNum = res.data.count | |||||
| this.fullscreenLoading = false | |||||
| }) | |||||
| }, | |||||
| }, | |||||
| computed:{ | |||||
| loadhref(){ | |||||
| return this.url+'downloadall?ID=' | |||||
| }, | |||||
| showinfoHref(){ | |||||
| return this.url + 'show_model_info?name=' | |||||
| }, | |||||
| transTime(){ | |||||
| return function(time){ | |||||
| let date = new Date(time * 1000);//时间戳为10位需*1000,时间戳为13位的话不需乘1000 | |||||
| let Y = date.getFullYear() + '-'; | |||||
| let M = (date.getMonth()+1 < 10 ? '0'+(date.getMonth()+1):date.getMonth()+1) + '-'; | |||||
| let D = (date.getDate()< 10 ? '0'+date.getDate():date.getDate())+ ' '; | |||||
| let h = (date.getHours() < 10 ? '0'+date.getHours():date.getHours())+ ':'; | |||||
| let m = (date.getMinutes() < 10 ? '0'+date.getMinutes():date.getMinutes()) + ':'; | |||||
| let s = date.getSeconds() < 10 ? '0'+date.getSeconds():date.getSeconds(); | |||||
| return Y+M+D+h+m+s; | |||||
| } | |||||
| }, | |||||
| renderSize(){ | |||||
| return function(value){ | |||||
| if(null==value||value==''){ | |||||
| return "0 Bytes"; | |||||
| } | |||||
| var unitArr = new Array("Bytes","KB","MB","GB","TB","PB","EB","ZB","YB"); | |||||
| var index=0; | |||||
| var srcsize = parseFloat(value); | |||||
| index=Math.floor(Math.log(srcsize)/Math.log(1024)); | |||||
| var size =srcsize/Math.pow(1024,index); | |||||
| size=size.toFixed(2);//保留的小数位数 | |||||
| return size+unitArr[index]; | |||||
| } | |||||
| } | |||||
| }, | |||||
| mounted() { | |||||
| this.getModelList() | |||||
| this.url = location.href.split('show_model')[0] | |||||
| } | |||||
| }; | |||||
| </script> | |||||
| <style scoped> | |||||
| /deep/ .el-pagination.is-background .el-pager li:not(.disabled).active { | |||||
| background-color: #5bb973; | |||||
| color: #FFF; | |||||
| } | |||||
| /deep/ .el-pagination.is-background .el-pager li.active { | |||||
| color: #fff; | |||||
| cursor: default; | |||||
| } | |||||
| /deep/ .el-pagination.is-background .el-pager li:hover { | |||||
| color: #5bb973; | |||||
| } | |||||
| /deep/ .el-pagination.is-background .el-pager li:not(.disabled):hover { | |||||
| color: #5bb973; | |||||
| } | |||||
| /deep/ .el-pagination.is-background .el-pager li:not(.disabled).active:hover { | |||||
| background-color: #5bb973; | |||||
| color: #FFF; | |||||
| } | |||||
| /deep/ .el-pager li.active { | |||||
| color: #08C0B9; | |||||
| cursor: default; | |||||
| } | |||||
| /deep/ .el-pagination .el-pager li:hover { | |||||
| color: #08C0B9; | |||||
| } | |||||
| /deep/ .el-pagination .el-pager li:not(.disabled):hover { | |||||
| color: #08C0B9; | |||||
| } | |||||
| .text-over{ | |||||
| overflow: hidden; | |||||
| text-overflow: ellipsis; | |||||
| vertical-align: middle; | |||||
| white-space: nowrap; | |||||
| } | |||||
| .el-icon-arrow-right{ | |||||
| font-family: element-icons!important; | |||||
| speak: none; | |||||
| font-style: normal; | |||||
| font-weight: 400; | |||||
| font-feature-settings: normal; | |||||
| font-variant: normal; | |||||
| text-transform: none; | |||||
| line-height: 1; | |||||
| vertical-align: middle; | |||||
| display: inline-block; | |||||
| -webkit-font-smoothing: antialiased; | |||||
| -moz-osx-font-smoothing: grayscale; | |||||
| border: 1px solid #D4D4D5; | |||||
| border-radius: 50%; | |||||
| color: #D4D4D5; | |||||
| margin-right: 4px; | |||||
| } | |||||
| .el-icon-arrow-right::before{ | |||||
| content: "\e6e0"; | |||||
| } | |||||
| .expand-icon{ | |||||
| display: inline-block; | |||||
| width: 20px; | |||||
| line-height: 20px; | |||||
| height: 20px; | |||||
| text-align: center; | |||||
| margin-right: 3px; | |||||
| font-size: 12px; | |||||
| } | |||||
| /deep/ .el-table_1_column_1.is-left .cell {padding-right: 0px !important;} | |||||
| /deep/ .el-table__expand-icon .el-icon-arrow-right{ | |||||
| font-family: element-icons!important; | |||||
| speak: none; | |||||
| font-style: normal; | |||||
| font-weight: 400; | |||||
| font-feature-settings: normal; | |||||
| font-variant: normal; | |||||
| text-transform: none; | |||||
| line-height: 1; | |||||
| vertical-align: middle; | |||||
| display: inline-block; | |||||
| -webkit-font-smoothing: antialiased; | |||||
| -moz-osx-font-smoothing: grayscale; | |||||
| border: 1px solid #3291F8; | |||||
| border-radius: 50%; | |||||
| color: #3291F8; | |||||
| margin-right: 4px; | |||||
| } | |||||
| .space-around{ | |||||
| display: flex; | |||||
| justify-content: space-around; | |||||
| } | |||||
| .disabled { | |||||
| cursor: default; | |||||
| pointer-events: none; | |||||
| color: rgba(0,0,0,.6) !important; | |||||
| opacity: .45 !important; | |||||
| } | |||||
| </style> | |||||
| @@ -39,6 +39,7 @@ import Images from './components/Images.vue'; | |||||
| import EditTopics from './components/EditTopics.vue'; | import EditTopics from './components/EditTopics.vue'; | ||||
| import DataAnalysis from './components/DataAnalysis.vue' | import DataAnalysis from './components/DataAnalysis.vue' | ||||
| import Contributors from './components/Contributors.vue' | import Contributors from './components/Contributors.vue' | ||||
| import Model from './components/Model.vue'; | |||||
| Vue.use(ElementUI); | Vue.use(ElementUI); | ||||
| Vue.prototype.$axios = axios; | Vue.prototype.$axios = axios; | ||||
| @@ -2916,11 +2917,12 @@ $(document).ready(async () => { | |||||
| initVueEditTopic(); | initVueEditTopic(); | ||||
| initVueContributors(); | initVueContributors(); | ||||
| initVueImages(); | initVueImages(); | ||||
| initVueModel(); | |||||
| initVueDataAnalysis(); | initVueDataAnalysis(); | ||||
| initTeamSettings(); | initTeamSettings(); | ||||
| initCtrlEnterSubmit(); | initCtrlEnterSubmit(); | ||||
| initNavbarContentToggle(); | initNavbarContentToggle(); | ||||
| // initTopicbar(); | |||||
| // initTopicbar();vim | |||||
| // closeTopicbar(); | // closeTopicbar(); | ||||
| initU2FAuth(); | initU2FAuth(); | ||||
| initU2FRegister(); | initU2FRegister(); | ||||
| @@ -3647,7 +3649,7 @@ function initVueContributors() { | |||||
| function initVueImages() { | function initVueImages() { | ||||
| const el = document.getElementById('images'); | const el = document.getElementById('images'); | ||||
| console.log("el",el) | |||||
| if (!el) { | if (!el) { | ||||
| return; | return; | ||||
| @@ -3659,6 +3661,20 @@ function initVueImages() { | |||||
| render: h => h(Images) | render: h => h(Images) | ||||
| }); | }); | ||||
| } | } | ||||
| function initVueModel() { | |||||
| const el = document.getElementById('model_list'); | |||||
| if (!el) { | |||||
| return; | |||||
| } | |||||
| new Vue({ | |||||
| el: el, | |||||
| render: h => h(Model) | |||||
| }); | |||||
| } | |||||
| function initVueDataAnalysis() { | function initVueDataAnalysis() { | ||||
| const el = document.getElementById('data_analysis'); | const el = document.getElementById('data_analysis'); | ||||
| console.log("el",el) | console.log("el",el) | ||||
| @@ -368,4 +368,186 @@ display: block; | |||||
| color: #3F3F40; | color: #3F3F40; | ||||
| font-size: 18px; | font-size: 18px; | ||||
| margin-bottom: 1rem; | margin-bottom: 1rem; | ||||
| .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; | |||||
| } | |||||
| .letf2{ | |||||
| margin-left: -2px; | |||||
| } | } | ||||