Reviewed-on: https://git.openi.org.cn/OpenI/aiforge/pulls/1036tags/v1.21.12.1^2
@@ -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 | |||
} | |||
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) { | |||
sess := x.NewSession() | |||
defer sess.Close() | |||
@@ -133,6 +133,7 @@ func init() { | |||
new(FileChunk), | |||
new(BlockChain), | |||
new(RecommendOrg), | |||
new(AiModelManage), | |||
) | |||
tablesStatistic = append(tablesStatistic, | |||
@@ -1114,6 +1114,12 @@ func CreateRepository(ctx DBContext, doer, u *User, repo *Repository) (err error | |||
Type: tp, | |||
Config: &BlockChainConfig{EnableBlockChain: true}, | |||
}) | |||
} else if tp == UnitTypeModelManage { | |||
units = append(units, RepoUnit{ | |||
RepoID: repo.ID, | |||
Type: tp, | |||
Config: &ModelManageConfig{EnableModelManage: true}, | |||
}) | |||
} else { | |||
units = append(units, RepoUnit{ | |||
RepoID: repo.ID, | |||
@@ -131,6 +131,20 @@ type CloudBrainConfig struct { | |||
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. | |||
func (cfg *CloudBrainConfig) FromDB(bs []byte) error { | |||
return json.Unmarshal(bs, &cfg) | |||
@@ -176,6 +190,8 @@ func (r *RepoUnit) BeforeSet(colName string, val xorm.Cell) { | |||
r.Config = new(CloudBrainConfig) | |||
case UnitTypeBlockChain: | |||
r.Config = new(BlockChainConfig) | |||
case UnitTypeModelManage: | |||
r.Config = new(ModelManageConfig) | |||
default: | |||
panic("unrecognized repo unit type: " + com.ToStr(*val)) | |||
} | |||
@@ -27,6 +27,7 @@ const ( | |||
UnitTypeDatasets UnitType = 10 // 10 Dataset | |||
UnitTypeCloudBrain UnitType = 11 // 11 CloudBrain | |||
UnitTypeBlockChain UnitType = 12 // 12 BlockChain | |||
UnitTypeModelManage UnitType = 13 // 13 ModelManage | |||
) | |||
// Value returns integer value for unit type | |||
@@ -56,6 +57,8 @@ func (u UnitType) String() string { | |||
return "UnitTypeCloudBrain" | |||
case UnitTypeBlockChain: | |||
return "UnitTypeBlockChain" | |||
case UnitTypeModelManage: | |||
return "UnitTypeModelManage" | |||
} | |||
return fmt.Sprintf("Unknown UnitType %d", u) | |||
} | |||
@@ -80,6 +83,7 @@ var ( | |||
UnitTypeDatasets, | |||
UnitTypeCloudBrain, | |||
UnitTypeBlockChain, | |||
UnitTypeModelManage, | |||
} | |||
// DefaultRepoUnits contains the default unit types | |||
@@ -92,6 +96,7 @@ var ( | |||
UnitTypeDatasets, | |||
UnitTypeCloudBrain, | |||
UnitTypeBlockChain, | |||
UnitTypeModelManage, | |||
} | |||
// NotAllowedDefaultRepoUnits contains units that can't be default | |||
@@ -281,6 +286,14 @@ var ( | |||
7, | |||
} | |||
UnitModelManage = Unit{ | |||
UnitTypeModelManage, | |||
"repo.modelmanage", | |||
"/modelmanage", | |||
"repo.modelmanage.desc", | |||
8, | |||
} | |||
// Units contains all the units | |||
Units = map[UnitType]Unit{ | |||
UnitTypeCode: UnitCode, | |||
@@ -293,6 +306,7 @@ var ( | |||
UnitTypeDatasets: UnitDataset, | |||
UnitTypeCloudBrain: UnitCloudBrain, | |||
UnitTypeBlockChain: UnitBlockChain, | |||
UnitTypeModelManage: UnitModelManage, | |||
} | |||
) | |||
@@ -122,6 +122,7 @@ type RepoSettingForm struct { | |||
// Advanced settings | |||
EnableDataset bool | |||
EnableCloudBrain bool | |||
EnableModelManager bool | |||
EnableWiki bool | |||
EnableExternalWiki bool | |||
ExternalWikiURL string | |||
@@ -821,5 +821,6 @@ func UnitTypes() macaron.Handler { | |||
ctx.Data["UnitTypeExternalWiki"] = models.UnitTypeExternalWiki | |||
ctx.Data["UnitTypeExternalTracker"] = models.UnitTypeExternalTracker | |||
ctx.Data["UnitTypeBlockChain"] = models.UnitTypeBlockChain | |||
ctx.Data["UnitTypeModelManage"] = models.UnitTypeModelManage | |||
} | |||
} |
@@ -5,6 +5,7 @@ | |||
package storage | |||
import ( | |||
"errors" | |||
"io" | |||
"net/url" | |||
"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.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) | |||
if err == nil { | |||
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) { | |||
input := &obs.GetObjectInput{} | |||
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) { | |||
input := &obs.ListObjectsInput{} | |||
input.Bucket = setting.Bucket | |||
@@ -258,27 +458,6 @@ func ObsGenMultiPartSignedUrl(uuid string, uploadId string, partNumber int, file | |||
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) { | |||
input := &obs.CreateSignedUrlInput{} | |||
input.Bucket = bucket | |||
@@ -302,7 +481,10 @@ func GetObsCreateSignedUrlByBucketAndKey(bucket, key string) (string, error) { | |||
} | |||
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) { | |||
@@ -816,6 +816,11 @@ get_repo_info_error=Can not get the information of the repository. | |||
generate_statistic_file_error=Fail to generate file. | |||
repo_stat_inspect=ProjectAnalysis | |||
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.train_job=Train 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.new=New train Task | |||
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.current_version=Current 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.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.git_content = Git Content (Default Branch) | |||
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.dataset_desc = Enable Repository Dataset | |||
settings.cloudbrain_desc = Enable Cloudbarin | |||
settings.model_desc = Enable Model Manage | |||
settings.issues_desc = Enable Repository Issue Tracker | |||
settings.use_internal_issue_tracker = Use Built-In Issue Tracker | |||
settings.use_external_issue_tracker = Use External Issue Tracker | |||
@@ -782,6 +782,9 @@ datasets=数据集 | |||
datasets.desc=数据集功能 | |||
cloudbrain_helper=使用GPU/NPU资源,开启Notebook、模型训练任务等 | |||
model_manager = 模型管理 | |||
model_noright=无权限操作 | |||
debug=调试 | |||
stop=停止 | |||
delete=删除 | |||
@@ -824,6 +827,7 @@ all=所有 | |||
modelarts.status=状态 | |||
modelarts.createtime=创建时间 | |||
modelarts.version_nums=版本数 | |||
modelarts.version=版本 | |||
modelarts.computing_resources=计算资源 | |||
modelarts.notebook=调试任务 | |||
modelarts.train_job=训练任务 | |||
@@ -831,6 +835,10 @@ modelarts.train_job.new_debug=新建调试任务 | |||
modelarts.train_job.new_train=新建训练任务 | |||
modelarts.train_job.config=配置信息 | |||
modelarts.train_job.new=新建训练任务 | |||
modelarts.train_job.new_place=描述字数不超过256个字符 | |||
modelarts.model_name=模型名称 | |||
modelarts.model_size=模型大小 | |||
modelarts.import_model=导入模型 | |||
modelarts.train_job.new_place=描述字数不超过255个字符 | |||
modelarts.modify=修改 | |||
modelarts.current_version=当前版本 | |||
@@ -887,6 +895,18 @@ modelarts.train_job_para_admin=任务参数管理 | |||
modelarts.train_job_para.edit=编辑 | |||
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.git_content=Git数据(默认分支) | |||
@@ -1566,6 +1586,7 @@ settings.external_wiki_url_error=外部百科链接无效 | |||
settings.external_wiki_url_desc=当点击任务标签时,访问者将被重定向到外部任务系统的URL。 | |||
settings.dataset_desc=启用数据集 | |||
settings.cloudbrain_desc = 启用云脑 | |||
settings.model_desc = 启用模型管理 | |||
settings.issues_desc=启用任务系统 | |||
settings.use_internal_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") | |||
} | |||
} |
@@ -344,7 +344,7 @@ func CloudBrainDebug(ctx *context.Context) { | |||
var jobID = ctx.Params(":jobid") | |||
if !ctx.IsSigned { | |||
log.Error("the user has not signed in") | |||
ctx.Error(http.StatusForbidden, "","the user has not signed in") | |||
ctx.Error(http.StatusForbidden, "", "the user has not signed in") | |||
return | |||
} | |||
task, err := models.GetCloudbrainByJobID(jobID) | |||
@@ -361,7 +361,7 @@ func CloudBrainCommitImage(ctx *context.Context, form auth.CommitImageCloudBrain | |||
var jobID = ctx.Params(":jobid") | |||
if !ctx.IsSigned { | |||
log.Error("the user has not signed in") | |||
ctx.Error(http.StatusForbidden, "","the user has not signed in") | |||
ctx.Error(http.StatusForbidden, "", "the user has not signed in") | |||
return | |||
} | |||
task, err := models.GetCloudbrainByJobID(jobID) | |||
@@ -542,10 +542,10 @@ func CloudBrainShowModels(ctx *context.Context) { | |||
} | |||
//get dirs | |||
dirs, err := getModelDirs(task.JobName, parentDir) | |||
dirs, err := GetModelDirs(task.JobName, parentDir) | |||
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 | |||
} | |||
@@ -605,7 +605,7 @@ func getImages(ctx *context.Context, imageType string) { | |||
log.Info("Get images end") | |||
} | |||
func getModelDirs(jobName string, parentDir string) (string, error) { | |||
func GetModelDirs(jobName string, parentDir string) (string, error) { | |||
var req string | |||
modelActualPath := setting.JobPath + jobName + "/model/" | |||
if parentDir == "" { | |||
@@ -12,7 +12,6 @@ import ( | |||
"code.gitea.io/gitea/modules/base" | |||
"code.gitea.io/gitea/modules/context" | |||
"code.gitea.io/gitea/modules/log" | |||
"code.gitea.io/gitea/modules/obs" | |||
"code.gitea.io/gitea/modules/setting" | |||
"code.gitea.io/gitea/modules/storage" | |||
) | |||
@@ -70,40 +69,10 @@ func DeleteAllUnzipFile(attachment *models.Attachment, parentDir string) { | |||
} | |||
} | |||
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.EnvIsDeployKey + "=false", | |||
} | |||
if !authUser.KeepEmailPrivate { | |||
environ = append(environ, models.EnvPusherEmail+"="+authUser.Email) | |||
} | |||
@@ -559,6 +558,7 @@ func serviceRPC(h serviceHandler, service string) { | |||
if service == "receive-pack" { | |||
cmd.Env = append(os.Environ(), h.environ...) | |||
} | |||
cmd.Stdout = h.w | |||
cmd.Stdin = reqBody | |||
cmd.Stderr = &stderr | |||
@@ -239,6 +239,18 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { | |||
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 !validation.IsValidExternalURL(form.ExternalWikiURL) { | |||
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) | |||
reqRepoCloudBrainReader := context.RequireRepoReader(models.UnitTypeCloudBrain) | |||
reqRepoCloudBrainWriter := context.RequireRepoWriter(models.UnitTypeCloudBrain) | |||
reqRepoModelManageReader := context.RequireRepoReader(models.UnitTypeModelManage) | |||
reqRepoModelManageWriter := context.RequireRepoWriter(models.UnitTypeModelManage) | |||
//reqRepoBlockChainReader := context.RequireRepoReader(models.UnitTypeBlockChain) | |||
//reqRepoBlockChainWriter := context.RequireRepoWriter(models.UnitTypeBlockChain) | |||
@@ -970,6 +972,23 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
m.Get("/create", reqRepoCloudBrainWriter, repo.CloudBrainNew) | |||
m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateCloudBrainForm{}), repo.CloudBrainCreate) | |||
}, 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("/modelarts", func() { | |||
m.Group("/notebook", func() { | |||
@@ -0,0 +1,575 @@ | |||
<!-- 头部导航栏 --> | |||
{{template "base/head" .}} | |||
<style> | |||
.label_after::after{ | |||
margin: -.2em 0 0 .2em; | |||
content: '\00a0'; | |||
} | |||
.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; | |||
} | |||
.time-show{ | |||
font-size: 10px; | |||
margin-top: 0.4rem; | |||
display: inline-block; | |||
} | |||
</style> | |||
<!-- 弹窗 --> | |||
<div id="mask"> | |||
<div id="loadingPage"> | |||
<div class="rect1"></div> | |||
<div class="rect2"></div> | |||
<div class="rect3"></div> | |||
<div class="rect4"></div> | |||
<div class="rect5"></div> | |||
</div> | |||
</div> | |||
<!-- 提示框 --> | |||
<div class="alert"></div> | |||
<div class="repository release dataset-list view"> | |||
{{template "repo/header" .}} | |||
<!-- 列表容器 --> | |||
<div class="ui container"> | |||
<div class="ui two column stackable grid "> | |||
<div class="column"> | |||
<div class="ui blue small menu compact selectcloudbrain"> | |||
<a class="active item" href="{{.RepoLink}}/debugjob">{{$.i18n.Tr "repo.modelarts.notebook"}}</a> | |||
<a class="item" href="{{.RepoLink}}/modelarts/train-job">{{$.i18n.Tr "repo.modelarts.train_job"}}</a> | |||
</div> | |||
</div> | |||
<div class="column right aligned"> | |||
<div class="ui selection dropdown" style="min-width: 10em;min-height:2.6em;border-radius: .28571429rem;margin-right: 1em;padding: .67em 3.2em .7em 1em;"> | |||
{{svg "octicon-server" 16}} | |||
<div class="default text" style="color: rgba(0,0,0,.87);"> 全部</div> | |||
<i class="dropdown icon"></i> | |||
<div class="menu"> | |||
<a class="active item" href="{{.RepoLink}}/debugjob" data-value="11">全部</a> | |||
<a class="item" href="{{.RepoLink}}/cloudbrain" data-value="22">CPU / GPU</a> | |||
<a class="item" href="{{.RepoLink}}/modelarts/notebook" data-value="33">Ascend NPU</a> | |||
</div> | |||
</div> | |||
{{if .Permission.CanWrite $.UnitTypeCloudBrain}} | |||
<a class="ui green button" href="{{.RepoLink}}/cloudbrain/create">{{$.i18n.Tr "repo.modelarts.train_job.new_debug"}}</a>{{end}} | |||
</div> | |||
</div> | |||
<!-- 中下列表展示区 --> | |||
<div class="ui grid"> | |||
<div class="row"> | |||
<div class="ui sixteen wide column"> | |||
<!-- 任务展示 --> | |||
<!-- 表头 --> | |||
<div class="dataset list"> | |||
<div class="ui grid stackable" style="background: #f0f0f0;;"> | |||
<div class="row"> | |||
<div class="four wide column"> | |||
<span style="margin:0 6px">{{$.i18n.Tr "repo.cloudbrain_task"}}</span> | |||
</div> | |||
<div class="two wide column text center"> | |||
<span style="margin:0 6px">{{$.i18n.Tr "repo.modelarts.status"}}</span> | |||
</div> | |||
<div class="two wide column text center"> | |||
<span>{{$.i18n.Tr "repo.modelarts.createtime"}}</span> | |||
</div> | |||
<div class="two wide column text center"> | |||
<span>{{$.i18n.Tr "repo.modelarts.computing_resources"}}</span> | |||
</div> | |||
<div class="one wide column text center"> | |||
<span>{{$.i18n.Tr "repo.cloudbrain_creator"}}</span> | |||
</div> | |||
<div class="five wide column text center"> | |||
<span>{{$.i18n.Tr "repo.cloudbrain_operate"}}</span> | |||
</div> | |||
</div> | |||
</div> | |||
{{range .Tasks}} | |||
<div class="ui grid stackable item"> | |||
<div class="row"> | |||
<!-- 任务名 --> | |||
<div class="four wide column"> | |||
<a class="title" href="{{$.Link}}/{{.JobID}}" title="{{.JobName}}" style="font-size: 14px;"> | |||
<span class="fitted text_over" style="width: 90%;vertical-align: middle;">{{.JobName}}</span> | |||
</a> | |||
</div> | |||
<div class="two wide column text center"> | |||
<!--任务状态 --> | |||
<span class="job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}" data-resource="{{.ComputeResource}}"> | |||
<span><i id="{{.JobID}}-icon" style="vertical-align: middle;" class="{{.Status}}"></i><span id="{{.JobID}}-text" style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span> | |||
</span> | |||
</div> | |||
<div class="two wide column text center"> | |||
<!-- 任务创建时间 --> | |||
<span style="font-size: 12px;margin-left: 0.4rem;" class="">{{TimeSinceUnix .Cloudbrain.CreatedUnix $.Lang}}</span> | |||
</div> | |||
<div class="two wide column text center"> | |||
<!-- 任务计算资源 --> | |||
<span style="font-size: 12px;margin-left: 0.4rem;" class="">{{.ComputeResource}}</span> | |||
</div> | |||
<div class="one wide column text center"> | |||
{{if .User.Name}} | |||
<a href="{{AppSubUrl}}/{{.User.Name}}" title="{{.User.Name}}"><img class="ui avatar image" src="{{.User.RelAvatarLink}}"></a> | |||
{{else}} | |||
<a title="Ghost"><img class="ui avatar image" src="{{AppSubUrl}}/user/avatar/Ghost/-1"></a> | |||
{{end}} | |||
</div> | |||
<div class="five wide column text center"> | |||
<div class="ui compact buttons"> | |||
{{if and (ne .Status "WAITING") (ne .JobType "DEBUG")}} | |||
<a class="ui basic button" href="{{$.Link}}/{{.JobID}}/rate" target="_blank"> | |||
评分 | |||
</a> | |||
{{end}} | |||
<!-- 调试 --> | |||
{{if $.Permission.CanWrite $.UnitTypeCloudBrain}} | |||
{{if eq .ComputeResource "CPU/GPU"}} | |||
<a id="model-debug-{{.JobID}}" class="ui basic {{if not .CanDebug}}disabled {{else}}blue {{end}}button" href="{{$.RepoLink}}/cloudbrain/{{.JobID}}/debug" target="_blank"> | |||
{{$.i18n.Tr "repo.debug"}} | |||
</a> | |||
{{else}} | |||
<a id="model-debug-{{.JobID}}" class="ui basic {{if not .CanDebug}}disabled {{else}}blue {{end}}button" href="{{$.RepoLink}}/modelarts/notebook/{{.JobID}}/debug" target="_blank"> | |||
{{$.i18n.Tr "repo.debug"}} | |||
</a> | |||
{{end}} | |||
{{else}} | |||
<a class="ui basic disabled button" href="{{$.Link}}/{{.JobID}}/debug" target="_blank"> | |||
{{$.i18n.Tr "repo.debug"}} | |||
</a> | |||
{{end}} | |||
<!-- 停止 --> | |||
<form id="stopForm-{{.JobID}}" action="{{if eq .ComputeResource "CPU/GPU"}}{{$.RepoLink}}/cloudbrain{{else}}{{$.RepoLink}}/modelarts/notebook{{end}}/{{.JobID}}/stop" method="post" style="margin-left:-1px;"> | |||
{{$.CsrfTokenHtml}} | |||
{{if $.Permission.CanWrite $.UnitTypeCloudBrain}} | |||
<a id="stop-model-debug-{{.JobID}}" class="ui basic {{if or (eq .Status "STOPPED") (eq .Status "FAILED")}}disabled {{else}}blue {{end}}button" onclick="document.getElementById('stopForm-{{.JobID}}').submit();"> | |||
{{$.i18n.Tr "repo.stop"}} | |||
</a> | |||
{{else}} | |||
<a class="ui basic disabled button" onclick="document.getElementById('stopForm-{{.JobID}}').submit();"> | |||
{{$.i18n.Tr "repo.stop"}} | |||
</a> | |||
{{end}} | |||
<input type="hidden" name="debugListType" value="all"> | |||
</form> | |||
</div> | |||
<div class="ui compact buttons"> | |||
<!-- 模型下载 --> | |||
<!-- <a class="ui basic blue button" href="{{$.Link}}/{{.JobID}}/models" target="_blank"> | |||
{{$.i18n.Tr "repo.download"}} | |||
</a> --> | |||
<!-- 接收结果 --> | |||
<iframe src="" frameborder="0" name="iframeContent" style="display: none;"></iframe> | |||
{{if $.Permission.CanWrite $.UnitTypeCloudBrain}} | |||
<a id="model-image-{{.JobID}}" style="{{if eq .ComputeResource "CPU/GPU"}} visibility: visible {{else}} visibility: hidden{{end}}" class="imageBtn ui basic {{if not .CanDebug}}disabled {{else}}blue {{end}}button" value="{{.CanDebug}}">{{$.i18n.Tr "repo.submit_image"}}</a> | |||
{{else}} | |||
<a class="imageBtn ui basic disabled button" style="{{if eq .ComputeResource "CPU/GPU"}} visibility: visible {{else}} visibility: hidden{{end}}" value="{{.CanDebug}}">{{$.i18n.Tr "repo.submit_image"}}</a> | |||
{{end}} | |||
</div> | |||
{{.CanDel}} | |||
<!-- 删除任务 --> | |||
<form class="ui compact buttons" id="delForm-{{.JobID}}" action="{{if eq .ComputeResource "CPU/GPU"}}{{$.RepoLink}}/cloudbrain{{else}}{{$.RepoLink}}/modelarts/notebook{{end}}/{{.JobID}}/del" method="post"> | |||
<input type="hidden" name="debugListType" value="all"> | |||
{{$.CsrfTokenHtml}} | |||
{{if .CanDel}} | |||
<!-- {{if not .CanDel}}disabled {{else}} blue {{end}} --> | |||
<a id="model-delete-{{.JobID}}" class="ui basic blue button " onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | |||
{{$.i18n.Tr "repo.delete"}} | |||
</a> | |||
{{else}} | |||
<a class="ui basic button disabled" onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | |||
{{$.i18n.Tr "repo.delete"}} | |||
</a> | |||
{{end}} | |||
</form> | |||
</div> | |||
<!-- 镜像列表弹窗 --> | |||
<div id="imageModal" class="modal" style="display: none;"> | |||
<div class="modal-content"> | |||
<!-- 表格 --> | |||
<div class="ui form"> | |||
<form id="commitImageForm" action="{{$.RepoLink}}/cloudbrain/{{.JobID}}/commit_image" method="post" target="iframeContent"> | |||
{{$.CsrfTokenHtml}} | |||
<div class="row"> | |||
<p style="display: inline;">提交任务镜像</p> | |||
<span class="close">×</span> | |||
</div> | |||
<div class="ui divider"></div> | |||
<div class="inline required field dis"> | |||
<label>镜像标签:</label> | |||
<input name="tag" id="image_tag" tabindex="3" autofocus required maxlength="254" style="width:75%"> | |||
</div> | |||
<div class="inline field"> | |||
<label class="label_after">镜像描述:</label> | |||
<textarea name="description" maxlength="254" rows="8" style="width:75%;margin-left: 0.2em;"></textarea> | |||
</div> | |||
<div class="ui divider"></div> | |||
<div class="inline field"> | |||
<label></label> | |||
<button class="ui green button" onclick="showmask()"> | |||
{{$.i18n.Tr "repo.cloudbrain.commit_image"}} | |||
</button> | |||
</div> | |||
</form> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
{{end}} {{template "base/paginate" .}} | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<!-- 确认模态框 --> | |||
<div id="deletemodel"> | |||
<div class="ui basic modal"> | |||
<div class="ui icon header"> | |||
<i class="trash icon"></i> 删除任务 | |||
</div> | |||
<div class="content"> | |||
<p>你确认删除该任务么?此任务一旦删除不可恢复。</p> | |||
</div> | |||
<div class="actions"> | |||
<div class="ui red basic inverted cancel button"> | |||
<i class="remove icon"></i> 取消操作 | |||
</div> | |||
<div class="ui green basic inverted ok button"> | |||
<i class="checkmark icon"></i> 确定操作 | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
{{template "base/footer" .}} | |||
<script> | |||
// 调试和评分新开窗口 | |||
console.log({{.Tasks}}) | |||
function stop(obj) { | |||
if (obj.style.color != "rgb(204, 204, 204)") { | |||
obj.target = '_blank' | |||
} else { | |||
return | |||
} | |||
} | |||
// 删除时用户确认 | |||
function assertDelete(obj) { | |||
if (obj.style.color == "rgb(204, 204, 204)") { | |||
return | |||
} else { | |||
var delId = obj.parentNode.id | |||
flag = 1; | |||
$('.ui.basic.modal') | |||
.modal({ | |||
onDeny: function() { | |||
flag = false | |||
}, | |||
onApprove: function() { | |||
document.getElementById(delId).submit() | |||
flag = true | |||
}, | |||
onHidden: function() { | |||
if (flag == false) { | |||
$('.alert').html('您已取消操作').removeClass('alert-success').addClass('alert-danger').show().delay(1500).fadeOut(); | |||
} | |||
} | |||
}) | |||
.modal('show') | |||
} | |||
} | |||
// 加载任务状态 | |||
var timeid = window.setInterval(loadJobStatus, 15000); | |||
$(document).ready(loadJobStatus); | |||
function loadJobStatus() { | |||
$(".job-status").each((index, job) => { | |||
const jobID = job.dataset.jobid; | |||
const repoPath = job.dataset.repopath; | |||
const computeResource = job.dataset.resource | |||
const initArray = ['STOPPED','FAILED','START_FAILED','CREATE_FAILED'] | |||
if (initArray.includes(job.textContent.trim())) { | |||
return | |||
} | |||
const diffResource = computeResource == "NPU" ? 'modelarts/notebook' : 'cloudbrain' | |||
$.get(`/api/v1/repos/${repoPath}/${diffResource}/${jobID}`, (data) => { | |||
const jobID = data.JobID | |||
const status = data.JobStatus | |||
if (status != job.textContent.trim()) { | |||
$('#' + jobID+'-icon').removeClass().addClass(status) | |||
$('#' + jobID+ '-text').text(status) | |||
} | |||
if(status==="RUNNING"){ | |||
$('#model-debug-'+jobID).removeClass('disabled') | |||
$('#model-debug-'+jobID).addClass('blue') | |||
$('#model-image-'+jobID).removeClass('disabled') | |||
$('#model-image-'+jobID).addClass('blue') | |||
} | |||
if(status!=="RUNNING"){ | |||
$('#model-debug-'+jobID).removeClass('blue') | |||
$('#model-debug-'+jobID).addClass('disabled') | |||
$('#model-image-'+jobID).removeClass('blue') | |||
$('#model-image-'+jobID).addClass('disabled') | |||
} | |||
if(status!=="STOPPED" || status!=="FAILED"){ | |||
$('#stop-model-debug-'+jobID).removeClass('disabled') | |||
$('#stop-model-debug-'+jobID).addClass('blue') | |||
// $('#model-delete-'+jobID).removeClass('red') | |||
// $('#model-delete-'+jobID).addClass('disabled') | |||
} | |||
if(status=="STOPPED" || status=="FAILED"){ | |||
$('#stop-model-debug-'+jobID).removeClass('blue') | |||
$('#stop-model-debug-'+jobID).addClass('disabled') | |||
$('#model-delete-'+jobID).removeClass('disabled') | |||
$('#model-delete-'+jobID).addClass('red') | |||
} | |||
}).fail(function(err) { | |||
console.log(err); | |||
}); | |||
}); | |||
}; | |||
// 获取弹窗 | |||
var modal = document.getElementById('imageModal'); | |||
// 打开弹窗的按钮对象 | |||
var btns = document.getElementsByClassName("imageBtn"); | |||
// 获取 <span> 元素,用于关闭弹窗 | |||
var spans = document.getElementsByClassName('close'); | |||
// 点击按钮打开弹窗 | |||
for (i = 0; i < btns.length; i++) { | |||
btns[i].onclick = function() { | |||
modal.style.display = "block"; | |||
} | |||
} | |||
// 点击 <span> (x), 关闭弹窗 | |||
for (i = 0; i < spans.length; i++) { | |||
spans[i].onclick = function() { | |||
modal.style.display = "none"; | |||
} | |||
} | |||
// 在用户点击其他地方时,关闭弹窗 | |||
window.onclick = function(event) { | |||
if (event.target == modal) { | |||
modal.style.display = "none"; | |||
} | |||
} | |||
// 显示弹窗,弹出相应的信息 | |||
function showmask() { | |||
var image_tag = !$('#image_tag').val() | |||
console.log("image_tag",image_tag) | |||
if(image_tag){ | |||
return | |||
} | |||
$('#imageModal').css('display', 'none') | |||
$('#mask').css('display', 'block') | |||
$("iframe[name=iframeContent]").on("load", function() { | |||
var responseText = $("iframe")[0].contentDocument.body.getElementsByTagName("pre")[0].innerHTML; | |||
var json1 = JSON.parse(responseText) | |||
$('#mask').css('display', 'none') | |||
parent.location.href | |||
if (json1.result_code === "0") { | |||
$('.alert').html('操作成功!').removeClass('alert-danger').addClass('alert-success').show().delay(1500).fadeOut(); | |||
} else { | |||
$('.alert').html(json1.error_msg).removeClass('alert-success').addClass('alert-danger').show().delay(5000).fadeOut(); | |||
} | |||
}) | |||
} | |||
</script> |
@@ -137,6 +137,12 @@ | |||
{{svg "octicon-inbox" 16}} {{.i18n.Tr "datasets"}} | |||
</a> | |||
{{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}} | |||
<a class="{{if .PageIsCloudBrain}}active{{end}} item" href="{{.RepoLink}}/cloudbrain"> | |||
<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> | |||
<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> | |||
// 点击云脑进行选择云脑平台并进入相应的界面 | |||
$('.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" .}} | |||
<style> | |||
.selectcloudbrain .active.item{ | |||
color: #0087f5 !important; | |||
border: 1px solid #0087f5; | |||
margin: -1px; | |||
background: #FFF !important; | |||
} | |||
#deletemodel { | |||
width: 100%; | |||
height: 100%; | |||
} | |||
/* 弹窗 */ | |||
#mask { | |||
position: fixed; | |||
top: 0px; | |||
left: 0px; | |||
right: 0px; | |||
bottom: 0px; | |||
filter: alpha(opacity=60); | |||
background-color: #777; | |||
z-index: 1000; | |||
display: none; | |||
opacity: 0.8; | |||
-moz-opacity: 0.5; | |||
padding-top: 100px; | |||
color: #000000 | |||
} | |||
#loadingPage { | |||
margin: 200px auto; | |||
width: 50px; | |||
height: 40px; | |||
text-align: center; | |||
font-size: 10px; | |||
display: block; | |||
} | |||
#loadingPage>div { | |||
background-color: green; | |||
height: 100%; | |||
width: 6px; | |||
display: inline-block; | |||
-webkit-animation: sk-stretchdelay 1.2s infinite ease-in-out; | |||
animation: sk-stretchdelay 1.2s infinite ease-in-out; | |||
} | |||
#loadingPage .rect2 { | |||
-webkit-animation-delay: -1.1s; | |||
animation-delay: -1.1s; | |||
} | |||
#loadingPage .rect3 { | |||
-webkit-animation-delay: -1.0s; | |||
animation-delay: -1.0s; | |||
} | |||
#loadingPage .rect4 { | |||
-webkit-animation-delay: -0.9s; | |||
animation-delay: -0.9s; | |||
} | |||
#loadingPage .rect5 { | |||
-webkit-animation-delay: -0.8s; | |||
animation-delay: -0.8s; | |||
} | |||
@-webkit-keyframes sk-stretchdelay { | |||
0%, | |||
40%, | |||
100% { | |||
-webkit-transform: scaleY(0.4) | |||
} | |||
20% { | |||
-webkit-transform: scaleY(1.0) | |||
} | |||
} | |||
@keyframes sk-stretchdelay { | |||
0%, | |||
40%, | |||
100% { | |||
transform: scaleY(0.4); | |||
-webkit-transform: scaleY(0.4); | |||
} | |||
20% { | |||
transform: scaleY(1.0); | |||
-webkit-transform: scaleY(1.0); | |||
} | |||
} | |||
/* 消息框 */ | |||
.alert { | |||
display: none; | |||
position: fixed; | |||
width: 100%; | |||
z-index: 1001; | |||
padding: 15px; | |||
border: 1px solid transparent; | |||
border-radius: 4px; | |||
text-align: center; | |||
font-weight: bold; | |||
} | |||
.alert-success { | |||
color: #3c763d; | |||
background-color: #dff0d8; | |||
border-color: #d6e9c6; | |||
} | |||
.alert-info { | |||
color: #31708f; | |||
background-color: #d9edf7; | |||
border-color: #bce8f1; | |||
} | |||
.alert-warning { | |||
color: #8a6d3b; | |||
background-color: #fcf8e3; | |||
border-color: #faebcc; | |||
} | |||
.alert-danger { | |||
color: #a94442; | |||
background-color: #f2dede; | |||
border-color: #ebccd1; | |||
} | |||
.pusher { | |||
width: calc(100% - 260px); | |||
box-sizing: border-box; | |||
} | |||
/* 弹窗 (background) */ | |||
#imageModal { | |||
display: none; | |||
position: fixed; | |||
z-index: 1; | |||
left: 0; | |||
top: 0; | |||
width: 100%; | |||
height: 100%; | |||
overflow: auto; | |||
background-color: rgb(0, 0, 0); | |||
background-color: rgba(0, 0, 0, 0.4); | |||
} | |||
/* 弹窗内容 */ | |||
.modal-content { | |||
background-color: #fefefe; | |||
margin: 15% auto; | |||
padding: 20px; | |||
border: 1px solid #888; | |||
width: 30%; | |||
} | |||
/* 关闭按钮 */ | |||
.close { | |||
color: #aaa; | |||
float: right; | |||
font-size: 28px; | |||
font-weight: bold; | |||
} | |||
.close:hover, | |||
.close:focus { | |||
color: black; | |||
text-decoration: none; | |||
cursor: pointer; | |||
} | |||
.dis { | |||
margin-bottom: 20px; | |||
} | |||
.disabled { | |||
cursor: pointer; | |||
pointer-events: none; | |||
} | |||
</style> | |||
<!-- 弹窗 --> | |||
<div id="mask"> | |||
<div id="loadingPage"> | |||
@@ -1,89 +1,8 @@ | |||
{{template "base/head" .}} | |||
<style> | |||
/* 遮罩层css效果图 */ | |||
#mask { | |||
position: fixed; | |||
top: 0px; | |||
left: 0px; | |||
right: 0px; | |||
bottom: 0px; | |||
filter: alpha(opacity=60); | |||
background-color: #777; | |||
z-index: 1000; | |||
.inline.required.field.cloudbrain_benchmark { | |||
display: none; | |||
opacity: 0.8; | |||
-moz-opacity: 0.5; | |||
padding-top: 100px; | |||
color: #000000 | |||
} | |||
/* 加载圈css效果图 */ | |||
#loadingPage { | |||
margin: 200px auto; | |||
width: 50px; | |||
height: 40px; | |||
text-align: center; | |||
font-size: 10px; | |||
display: block; | |||
} | |||
#loadingPage>div { | |||
background-color: green; | |||
height: 100%; | |||
width: 6px; | |||
display: inline-block; | |||
-webkit-animation: sk-stretchdelay 1.2s infinite ease-in-out; | |||
animation: sk-stretchdelay 1.2s infinite ease-in-out; | |||
} | |||
#loadingPage .rect2 { | |||
-webkit-animation-delay: -1.1s; | |||
animation-delay: -1.1s; | |||
} | |||
#loadingPage .rect3 { | |||
-webkit-animation-delay: -1.0s; | |||
animation-delay: -1.0s; | |||
} | |||
#loadingPage .rect4 { | |||
-webkit-animation-delay: -0.9s; | |||
animation-delay: -0.9s; | |||
} | |||
#loadingPage .rect5 { | |||
-webkit-animation-delay: -0.8s; | |||
animation-delay: -0.8s; | |||
} | |||
@-webkit-keyframes sk-stretchdelay { | |||
0%, | |||
40%, | |||
100% { | |||
-webkit-transform: scaleY(0.4) | |||
} | |||
20% { | |||
-webkit-transform: scaleY(1.0) | |||
} | |||
} | |||
@keyframes sk-stretchdelay { | |||
0%, | |||
40%, | |||
100% { | |||
transform: scaleY(0.4); | |||
-webkit-transform: scaleY(0.4); | |||
} | |||
20% { | |||
transform: scaleY(1.0); | |||
-webkit-transform: scaleY(1.0); | |||
} | |||
} | |||
.inline.required.field.cloudbrain_benchmark { | |||
display: none; | |||
} | |||
} | |||
</style> | |||
<div id="mask"> | |||
@@ -2,184 +2,6 @@ | |||
{{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; | |||
} | |||
.fontsize14{ | |||
font-size: 14px; | |||
} | |||
@@ -47,87 +47,7 @@ | |||
border-radius: 5px 0px 0px 5px; | |||
line-height: 21px; | |||
text-align: center; | |||
color: #C2C7CC;" | |||
} | |||
#mask { | |||
position: fixed; | |||
top: 0px; | |||
left: 0px; | |||
right: 0px; | |||
bottom: 0px; | |||
filter: alpha(opacity=60); | |||
background-color: #777; | |||
z-index: 1000; | |||
display: none; | |||
opacity: 0.8; | |||
-moz-opacity: 0.5; | |||
padding-top: 100px; | |||
color: #000000 | |||
} | |||
/* 加载圈css效果图 */ | |||
#loadingPage { | |||
margin: 200px auto; | |||
width: 50px; | |||
height: 40px; | |||
text-align: center; | |||
font-size: 10px; | |||
display: block; | |||
} | |||
#loadingPage>div { | |||
background-color: green; | |||
height: 100%; | |||
width: 6px; | |||
display: inline-block; | |||
-webkit-animation: sk-stretchdelay 1.2s infinite ease-in-out; | |||
animation: sk-stretchdelay 1.2s infinite ease-in-out; | |||
} | |||
#loadingPage .rect2 { | |||
-webkit-animation-delay: -1.1s; | |||
animation-delay: -1.1s; | |||
} | |||
#loadingPage .rect3 { | |||
-webkit-animation-delay: -1.0s; | |||
animation-delay: -1.0s; | |||
} | |||
#loadingPage .rect4 { | |||
-webkit-animation-delay: -0.9s; | |||
animation-delay: -0.9s; | |||
} | |||
#loadingPage .rect5 { | |||
-webkit-animation-delay: -0.8s; | |||
animation-delay: -0.8s; | |||
} | |||
.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> | |||
@@ -49,87 +49,6 @@ | |||
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); | |||
} | |||
} | |||
</style> | |||
<!-- <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> | |||
</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)}} | |||
<div class="inline field"> | |||
<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 DataAnalysis from './components/DataAnalysis.vue' | |||
import Contributors from './components/Contributors.vue' | |||
import Model from './components/Model.vue'; | |||
Vue.use(ElementUI); | |||
Vue.prototype.$axios = axios; | |||
@@ -2916,11 +2917,12 @@ $(document).ready(async () => { | |||
initVueEditTopic(); | |||
initVueContributors(); | |||
initVueImages(); | |||
initVueModel(); | |||
initVueDataAnalysis(); | |||
initTeamSettings(); | |||
initCtrlEnterSubmit(); | |||
initNavbarContentToggle(); | |||
// initTopicbar(); | |||
// initTopicbar();vim | |||
// closeTopicbar(); | |||
initU2FAuth(); | |||
initU2FRegister(); | |||
@@ -3647,7 +3649,7 @@ function initVueContributors() { | |||
function initVueImages() { | |||
const el = document.getElementById('images'); | |||
console.log("el",el) | |||
if (!el) { | |||
return; | |||
@@ -3659,6 +3661,20 @@ function initVueImages() { | |||
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() { | |||
const el = document.getElementById('data_analysis'); | |||
console.log("el",el) | |||
@@ -335,4 +335,187 @@ display: block; | |||
margin-left: 4px !important; | |||
color: #3291F8; | |||
} | |||
.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; | |||
} |