# Conflicts: # models/action.go # modules/setting/setting.go # routers/repo/cloudbrain.gopull/2885/head
@@ -1141,3 +1141,9 @@ growth_issue=0.2 | |||
growth_contributors=0.2 | |||
growth_commit=0.2 | |||
growth_comments=0.2 | |||
[grampus] | |||
USERNAME = | |||
PASSWORD = | |||
SERVER_HOST = |
@@ -50,6 +50,16 @@ const ( | |||
ActionRejectPullRequest // 22 | |||
ActionCommentPull // 23 | |||
ActionUploadAttachment //24 | |||
ActionCreateDebugGPUTask //25 | |||
ActionCreateDebugNPUTask //26 | |||
ActionCreateTrainTask //27 | |||
ActionCreateInferenceTask // 28 | |||
ActionCreateBenchMarkTask //29 | |||
ActionCreateNewModelTask //30 | |||
ActionCreateGPUTrainTask //31 | |||
ActionCreateGrampusNPUTrainTask //32 | |||
ActionCreateGrampusGPUTrainTask //33 | |||
ActionUploadAttachment //24 | |||
ActionCreateDebugGPUTask //25 | |||
ActionCreateDebugNPUTask //26 | |||
@@ -24,7 +24,7 @@ type ModelArtsJobStatus string | |||
const ( | |||
TypeCloudBrainOne int = iota | |||
TypeCloudBrainTwo | |||
TypeIntelligentNet | |||
TypeC2Net //智算网络 | |||
TypeCloudBrainAll = -1 | |||
) | |||
@@ -99,6 +99,15 @@ const ( | |||
ModelArtsTrainJobCheckFailed ModelArtsJobStatus = "CHECK_FAILED" //审核作业失败 | |||
DURATION_STR_ZERO = "00:00:00" | |||
//grampus | |||
GrampusStatusPending = "pending" | |||
GrampusStatusRunning = "RUNNING" | |||
GrampusStatusFailed = "FAILED" | |||
GrampusStatusSucceeded = "SUCCEEDED" | |||
GrampusStatusStopped = "STOPPED" | |||
GrampusStatusUnknown = "UNKNOWN" | |||
GrampusStatusWaiting = "WAITING" | |||
) | |||
type Cloudbrain struct { | |||
@@ -138,6 +147,8 @@ type Cloudbrain struct { | |||
PreVersionName string //父版本名称 | |||
ComputeResource string //计算资源,例如npu | |||
EngineID int64 //引擎id | |||
ImageID string //grampus image_id | |||
AiCenter string //grampus ai center: center_id+center_name | |||
TrainUrl string //输出模型的obs路径 | |||
BranchName string //分支名称 | |||
@@ -238,7 +249,7 @@ func ConvertDurationToStr(duration int64) string { | |||
} | |||
func IsTrainJobTerminal(status string) bool { | |||
return status == string(ModelArtsTrainJobCompleted) || status == string(ModelArtsTrainJobFailed) || status == string(ModelArtsTrainJobKilled) | |||
return status == string(ModelArtsTrainJobCompleted) || status == string(ModelArtsTrainJobFailed) || status == string(ModelArtsTrainJobKilled) || status == GrampusStatusFailed || status == GrampusStatusStopped || status == GrampusStatusSucceeded | |||
} | |||
func IsModelArtsDebugJobTerminal(status string) bool { | |||
@@ -588,6 +599,17 @@ type FlavorInfo struct { | |||
UnitPrice int64 `json:"unitPrice"` | |||
} | |||
type SpecialPools struct { | |||
Pools []*SpecialPool `json:"pools"` | |||
} | |||
type SpecialPool struct { | |||
Org string `json:"org"` | |||
Type string `json:"type"` | |||
IsExclusive bool `json:"isExclusive"` | |||
Pool []*GpuInfo `json:"pool"` | |||
JobType []string `json:"jobType"` | |||
} | |||
type ImageInfosModelArts struct { | |||
ImageInfo []*ImageInfoModelArts `json:"image_info"` | |||
} | |||
@@ -1190,6 +1212,84 @@ type LogFile struct { | |||
Name string | |||
} | |||
//Grampus | |||
type GrampusResult struct { | |||
ErrorCode int `json:"errorCode"` | |||
ErrorMsg string `json:"errorMsg"` | |||
} | |||
type GrampusJobInfo struct { | |||
StartedAt int64 `json:"startedAt"` | |||
RunSec int64 `json:"runSec"` | |||
CompletedAt int64 `json:"completedAt"` | |||
CreatedAt int64 `json:"createdAt"` | |||
UpdatedAt int64 `json:"updatedAt"` | |||
Desc string `json:"desc"` | |||
JobID string `json:"id"` | |||
Name string `json:"name"` | |||
Status string `json:"status"` | |||
UserID string `json:"userId"` | |||
Tasks []GrampusTasks `json:"tasks"` | |||
} | |||
type GrampusSpec struct { | |||
CreatedAt int64 `json:"createdAt"` | |||
UpdatedAt int64 `json:"updatedAt"` | |||
ID string `json:"id"` | |||
Name string `json:"name"` | |||
ProcessorType string `json:"processorType"` | |||
} | |||
type GetGrampusResourceSpecsResult struct { | |||
GrampusResult | |||
Infos []GrampusSpec `json:"resourceSpecs"` | |||
} | |||
type GrampusImage struct { | |||
CreatedAt int64 `json:"createdAt"` | |||
UpdatedAt int64 `json:"updatedAt"` | |||
ID string `json:"id"` | |||
Name string `json:"name"` | |||
ProcessorType string `json:"processorType"` | |||
} | |||
type GetGrampusImagesResult struct { | |||
GrampusResult | |||
TotalSize int `json:"totalSize"` | |||
Infos []GrampusImage `json:"images"` | |||
} | |||
type CreateGrampusJobResponse struct { | |||
GrampusResult | |||
JobInfo GrampusJobInfo `json:"otJob"` | |||
} | |||
type GetGrampusJobResponse struct { | |||
GrampusResult | |||
JobInfo GrampusJobInfo `json:"otJob"` | |||
} | |||
type GrampusStopJobResponse struct { | |||
GrampusResult | |||
StoppedAt int64 `json:"stoppedAt"` | |||
} | |||
type GrampusTasks struct { | |||
Command string `json:"command"` | |||
Name string `json:"name"` | |||
ImageId string `json:"imageId"` | |||
ResourceSpecId string `json:"resourceSpecId"` | |||
ImageUrl string `json:"imageUrl"` | |||
CenterID []string `json:"centerID"` | |||
CenterName []string `json:"centerName"` | |||
ReplicaNum int `json:"replicaNum"` | |||
} | |||
type CreateGrampusJobRequest struct { | |||
Name string `json:"name"` | |||
Tasks []GrampusTasks `json:"tasks"` | |||
} | |||
type GetTrainJobMetricStatisticResult struct { | |||
TrainJobResult | |||
Interval int `json:"interval"` //查询的时间间隔,单位为分钟 | |||
@@ -1235,6 +1335,12 @@ func Cloudbrains(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int64, error) { | |||
) | |||
} | |||
if len(opts.ComputeResource) > 0 { | |||
cond = cond.And( | |||
builder.Eq{"cloudbrain.compute_resource": opts.ComputeResource}, | |||
) | |||
} | |||
if len(opts.JobTypes) > 0 { | |||
if opts.JobTypeNot { | |||
cond = cond.And( | |||
@@ -1490,6 +1596,11 @@ func GetCloudbrainByJobID(jobID string) (*Cloudbrain, error) { | |||
return getRepoCloudBrain(cb) | |||
} | |||
func GetCloudbrainByJobIDWithDeleted(jobID string) (*Cloudbrain, error) { | |||
cb := &Cloudbrain{JobID: jobID} | |||
return getRepoCloudBrainWithDeleted(cb) | |||
} | |||
func GetCloudbrainByID(id string) (*Cloudbrain, error) { | |||
idInt64, _ := strconv.ParseInt(id, 10, 64) | |||
cb := &Cloudbrain{ID: idInt64} | |||
@@ -1668,6 +1779,11 @@ func GetCloudbrainInferenceJobCountByUserID(userID int64) (int, error) { | |||
return int(count), err | |||
} | |||
func GetGrampusCountByUserID(userID int64, jobType, computeResource string) (int, error) { | |||
count, err := x.In("status", GrampusStatusWaiting, GrampusStatusRunning).And("job_type = ? and user_id = ? and type = ?", jobType, userID, TypeC2Net).And("compute_resource = ?", computeResource).Count(new(Cloudbrain)) | |||
return int(count), err | |||
} | |||
func UpdateInferenceJob(job *Cloudbrain) error { | |||
return updateInferenceJob(x, job) | |||
} | |||
@@ -68,6 +68,7 @@ type SearchImageOptions struct { | |||
IncludeCustom bool | |||
IncludeOwnerOnly bool | |||
Topics string | |||
CloudbrainType int | |||
ListOptions | |||
SearchOrderBy | |||
} | |||
@@ -411,6 +412,10 @@ func SearchImageCondition(opts *SearchImageOptions) builder.Cond { | |||
} | |||
if opts.CloudbrainType > 0 { | |||
cond = cond.And(builder.Eq{"cloudbrain_type": opts.CloudbrainType}) | |||
} | |||
return cond | |||
} | |||
@@ -15,13 +15,9 @@ type CustomMigrationStatic struct { | |||
Migrate func(*xorm.Engine, *xorm.Engine) error | |||
} | |||
var customMigrations = []CustomMigration{ | |||
{"Custom v1 Topic struct change to support chinese", syncTopicStruct}, | |||
} | |||
var customMigrations []CustomMigration | |||
var customMigrationsStatic = []CustomMigrationStatic{ | |||
{"update issue_fixed_rate to 1 if num_issues is 0 ", updateIssueFixedRate}, | |||
} | |||
var customMigrationsStatic []CustomMigrationStatic | |||
func MigrateCustom(x *xorm.Engine) { | |||
@@ -2755,15 +2755,10 @@ func ReadLatestFileInRepo(userName, repoName, refName, treePath string) (*RepoFi | |||
log.Error("ReadLatestFileInRepo: Close: %v", err) | |||
} | |||
}() | |||
buf := make([]byte, 1024) | |||
n, _ := reader.Read(buf) | |||
if n >= 0 { | |||
buf = buf[:n] | |||
} | |||
d, _ := ioutil.ReadAll(reader) | |||
commitId := "" | |||
if blob != nil { | |||
commitId = fmt.Sprint(blob.ID) | |||
} | |||
return &RepoFile{CommitId: commitId, Content: buf}, nil | |||
return &RepoFile{CommitId: commitId, Content: d}, nil | |||
} |
@@ -955,6 +955,8 @@ func CounDataByDateAndReCount(wikiCountMap map[string]int, startTime time.Time, | |||
return err | |||
} | |||
userNewAddActivity := make(map[int64]map[int64]int64) | |||
userAcitvateJsonMap := make(map[int64]map[int64]int64) | |||
userCurrentDayRegistMap := make(map[int64]map[int64]int64) | |||
ParaWeight := getParaWeight() | |||
userMetrics := make(map[string]int) | |||
var indexTotal int64 | |||
@@ -1028,7 +1030,10 @@ func CounDataByDateAndReCount(wikiCountMap map[string]int, startTime time.Time, | |||
log.Info("has activity." + userRecord.Name) | |||
addUserToMap(userNewAddActivity, userRecord.CreatedUnix, dateRecord.ID) | |||
} | |||
if userRecord.IsActive { | |||
addUserToMap(userAcitvateJsonMap, userRecord.CreatedUnix, dateRecord.ID) | |||
} | |||
addUserToMap(userCurrentDayRegistMap, userRecord.CreatedUnix, dateRecord.ID) | |||
} | |||
indexTotal += PAGE_SIZE | |||
@@ -1064,36 +1069,61 @@ func CounDataByDateAndReCount(wikiCountMap map[string]int, startTime time.Time, | |||
} | |||
statictisSess.Insert(&useMetrics) | |||
//update new user activity | |||
updateNewUserAcitivity(userNewAddActivity, statictisSess) | |||
updateNewUserAcitivity(userNewAddActivity, userAcitvateJsonMap, userCurrentDayRegistMap, statictisSess) | |||
return nil | |||
} | |||
func updateNewUserAcitivity(currentUserActivity map[int64]map[int64]int64, statictisSess *xorm.Session) { | |||
for key, value := range currentUserActivity { | |||
func updateNewUserAcitivity(currentUserActivity map[int64]map[int64]int64, userAcitvateJsonMap map[int64]map[int64]int64, userCurrentDayRegistMap map[int64]map[int64]int64, statictisSess *xorm.Session) { | |||
for key, value := range userCurrentDayRegistMap { | |||
useMetrics := &UserMetrics{CountDate: key} | |||
userAcitvateValue := userAcitvateJsonMap[key] | |||
HuodongValue := currentUserActivity[key] | |||
has, err := statictisSess.Get(useMetrics) | |||
if err == nil && has { | |||
userIdArrays := strings.Split(useMetrics.HasActivityUserJson, ",") | |||
for _, userIdStr := range userIdArrays { | |||
userIdInt, err := strconv.ParseInt(userIdStr, 10, 64) | |||
if err == nil { | |||
value[userIdInt] = userIdInt | |||
} | |||
} | |||
userIdArray := "" | |||
for _, tmpValue := range value { | |||
userIdArray += fmt.Sprint(tmpValue) + "," | |||
} | |||
useMetrics.HasActivityUser = len(value) | |||
if len(userIdArray) > 0 { | |||
useMetrics.HasActivityUserJson = userIdArray[0 : len(userIdArray)-1] | |||
} | |||
updateSql := "update public.user_metrics set has_activity_user_json='" + useMetrics.HasActivityUserJson + "',regist_activity_user=" + fmt.Sprint(useMetrics.HasActivityUser) + " where count_date=" + fmt.Sprint(key) | |||
ActivityUserArray, HuodongTotal := setUniqueUserId(useMetrics.HasActivityUserJson, HuodongValue) | |||
useMetrics.HasActivityUser = HuodongTotal | |||
useMetrics.HasActivityUserJson = ActivityUserArray | |||
useMetrics.CurrentDayRegistUser = len(value) | |||
RegistUserArray, lenRegistUser := setUniqueUserId(useMetrics.ActivityUserJson, userAcitvateValue) | |||
useMetrics.ActivityUserJson = RegistUserArray | |||
useMetrics.ActivateRegistUser = lenRegistUser | |||
updateSql := "update public.user_metrics set has_activity_user_json='" + useMetrics.HasActivityUserJson + | |||
"',regist_activity_user=" + fmt.Sprint(useMetrics.HasActivityUser) + | |||
",activity_user_json='" + useMetrics.ActivityUserJson + "'" + | |||
",activate_regist_user=" + fmt.Sprint(useMetrics.ActivateRegistUser) + | |||
",not_activate_regist_user=" + fmt.Sprint(useMetrics.CurrentDayRegistUser-useMetrics.ActivateRegistUser) + | |||
",current_day_regist_user=" + fmt.Sprint(useMetrics.CurrentDayRegistUser) + | |||
" where count_date=" + fmt.Sprint(key) | |||
statictisSess.Exec(updateSql) | |||
} | |||
} | |||
} | |||
func setUniqueUserId(jsonString string, value map[int64]int64) (string, int) { | |||
if value == nil { | |||
value = make(map[int64]int64, 0) | |||
} | |||
userIdArrays := strings.Split(jsonString, ",") | |||
for _, userIdStr := range userIdArrays { | |||
userIdInt, err := strconv.ParseInt(userIdStr, 10, 64) | |||
if err == nil { | |||
value[userIdInt] = userIdInt | |||
} | |||
} | |||
userIdArray := "" | |||
for _, tmpValue := range value { | |||
userIdArray += fmt.Sprint(tmpValue) + "," | |||
} | |||
if len(userIdArray) > 0 { | |||
return userIdArray[0 : len(userIdArray)-1], len(value) | |||
} | |||
return userIdArray, len(value) | |||
} | |||
func addUserToMap(currentUserActivity map[int64]map[int64]int64, registDate timeutil.TimeStamp, userId int64) { | |||
CountDateTime := time.Date(registDate.Year(), registDate.AsTime().Month(), registDate.AsTime().Day(), 0, 1, 0, 0, registDate.AsTime().Location()) | |||
CountDate := CountDateTime.Unix() | |||
@@ -1104,7 +1134,6 @@ func addUserToMap(currentUserActivity map[int64]map[int64]int64, registDate time | |||
} else { | |||
currentUserActivity[CountDate][userId] = userId | |||
} | |||
} | |||
func setUserMetrics(userMetrics map[string]int, user *User, start_time int64, end_time int64, dateRecord UserBusinessAnalysis) { | |||
@@ -467,11 +467,11 @@ type UserAnalysisPara struct { | |||
type UserMetrics struct { | |||
CountDate int64 `xorm:"pk"` | |||
ActivateRegistUser int `xorm:"NOT NULL DEFAULT 0"` | |||
NotActivateRegistUser int `xorm:"NOT NULL DEFAULT 0"` | |||
ActivateIndex float64 `xorm:"NOT NULL DEFAULT 0"` | |||
RegistActivityUser int `xorm:"NOT NULL DEFAULT 0"` | |||
HasActivityUser int `xorm:"NOT NULL DEFAULT 0"` | |||
ActivateRegistUser int `xorm:"NOT NULL DEFAULT 0"` //当天激活用户 | |||
NotActivateRegistUser int `xorm:"NOT NULL DEFAULT 0"` //当天未激活用户 | |||
ActivateIndex float64 `xorm:"NOT NULL DEFAULT 0"` //激活比率 | |||
RegistActivityUser int `xorm:"NOT NULL DEFAULT 0"` //当天注册激活的人中,有贡献活动的人 | |||
HasActivityUser int `xorm:"NOT NULL DEFAULT 0"` //当天有贡献活动的人 | |||
TotalUser int `xorm:"NOT NULL DEFAULT 0"` | |||
TotalRegistUser int `xorm:"-"` | |||
TotalActivateRegistUser int `xorm:"NOT NULL DEFAULT 0"` | |||
@@ -480,5 +480,7 @@ type UserMetrics struct { | |||
DisplayDate string `xorm:"-"` | |||
DataDate string `xorm:"NULL"` | |||
DaysForMonth int `xorm:"NOT NULL DEFAULT 0"` | |||
HasActivityUserJson string `xorm:"text NULL"` | |||
HasActivityUserJson string `xorm:"text NULL"` //贡献活动用户列表 | |||
ActivityUserJson string `xorm:"text NULL"` //激活用户列表 | |||
CurrentDayRegistUser int `xorm:"NOT NULL DEFAULT 0"` //当天注册用户 | |||
} |
@@ -0,0 +1,26 @@ | |||
package auth | |||
import ( | |||
"gitea.com/macaron/binding" | |||
"gitea.com/macaron/macaron" | |||
) | |||
type CreateGrampusTrainJobForm struct { | |||
DisplayJobName string `form:"display_job_name" binding:"Required"` | |||
JobName string `form:"job_name" binding:"Required"` | |||
Attachment string `form:"attachment" binding:"Required"` | |||
BootFile string `form:"boot_file" binding:"Required"` | |||
ImageID string `form:"image_id" binding:"Required"` | |||
FlavorID string `form:"flavor" binding:"Required"` | |||
Params string `form:"run_para_list" binding:"Required"` | |||
Description string `form:"description"` | |||
BranchName string `form:"branch_name" binding:"Required"` | |||
FlavorName string `form:"flavor_name" binding:"Required"` | |||
EngineName string `form:"engine_name" binding:"Required"` | |||
WorkServerNumber int `form:"work_server_number" binding:"Required"` | |||
Image string `form:"image"` | |||
} | |||
func (f *CreateGrampusTrainJobForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | |||
return validate(errs, ctx.Data, f, ctx.Locale) | |||
} |
@@ -0,0 +1,139 @@ | |||
package wechat | |||
import ( | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/modules/log" | |||
"code.gitea.io/gitea/modules/setting" | |||
"encoding/json" | |||
"github.com/patrickmn/go-cache" | |||
"strings" | |||
"time" | |||
) | |||
var WechatReplyCache = cache.New(2*time.Minute, 1*time.Minute) | |||
const ( | |||
WECHAT_REPLY_CACHE_KEY = "wechat_response" | |||
) | |||
const ( | |||
ReplyTypeText = "text" | |||
ReplyTypeImage = "image" | |||
ReplyTypeVoice = "voice" | |||
ReplyTypeVideo = "video" | |||
ReplyTypeMusic = "music" | |||
ReplyTypeNews = "news" | |||
) | |||
type ReplyConfigType string | |||
const ( | |||
SubscribeReply ReplyConfigType = "subscribe" | |||
AutoMsgReply ReplyConfigType = "autoMsg" | |||
) | |||
func (r ReplyConfigType) Name() string { | |||
switch r { | |||
case SubscribeReply: | |||
return "subscribe" | |||
case AutoMsgReply: | |||
return "autoMsg" | |||
} | |||
return "" | |||
} | |||
func (r ReplyConfigType) TreePath() string { | |||
switch r { | |||
case SubscribeReply: | |||
return setting.TreePathOfSubscribe | |||
case AutoMsgReply: | |||
return setting.TreePathOfAutoMsgReply | |||
} | |||
return "" | |||
} | |||
type WechatReplyContent struct { | |||
Reply *ReplyContent | |||
ReplyType string | |||
KeyWords []string | |||
IsFullMatch int | |||
} | |||
type ReplyContent struct { | |||
Content string | |||
MediaId string | |||
Title string | |||
Description string | |||
MusicUrl string | |||
HQMusicUrl string | |||
ThumbMediaId string | |||
Articles []ArticlesContent | |||
} | |||
func GetAutomaticReply(msg string) *WechatReplyContent { | |||
r, err := LoadReplyFromCacheAndDisk(AutoMsgReply) | |||
if err != nil { | |||
return nil | |||
} | |||
if r == nil || len(r) == 0 { | |||
return nil | |||
} | |||
for i := 0; i < len(r); i++ { | |||
if r[i].IsFullMatch == 0 { | |||
for _, v := range r[i].KeyWords { | |||
if strings.Contains(msg, v) { | |||
return r[i] | |||
} | |||
} | |||
} else if r[i].IsFullMatch > 0 { | |||
for _, v := range r[i].KeyWords { | |||
if msg == v { | |||
return r[i] | |||
} | |||
} | |||
} | |||
} | |||
return nil | |||
} | |||
func loadReplyFromDisk(replyConfig ReplyConfigType) ([]*WechatReplyContent, error) { | |||
log.Info("LoadReply from disk") | |||
repo, err := models.GetRepositoryByOwnerAndAlias(setting.UserNameOfWechatReply, setting.RepoNameOfWechatReply) | |||
if err != nil { | |||
log.Error("get AutomaticReply repo failed, error=%v", err) | |||
return nil, err | |||
} | |||
repoFile, err := models.ReadLatestFileInRepo(setting.UserNameOfWechatReply, repo.Name, setting.RefNameOfWechatReply, replyConfig.TreePath()) | |||
if err != nil { | |||
log.Error("get AutomaticReply failed, error=%v", err) | |||
return nil, err | |||
} | |||
res := make([]*WechatReplyContent, 0) | |||
json.Unmarshal(repoFile.Content, &res) | |||
if res == nil || len(res) == 0 { | |||
return nil, err | |||
} | |||
return res, nil | |||
} | |||
func LoadReplyFromCacheAndDisk(replyConfig ReplyConfigType) ([]*WechatReplyContent, error) { | |||
v, success := WechatReplyCache.Get(replyConfig.Name()) | |||
if success { | |||
log.Info("LoadReply from cache,value = %v", v) | |||
if v == nil { | |||
return nil, nil | |||
} | |||
n := v.([]*WechatReplyContent) | |||
return n, nil | |||
} | |||
content, err := loadReplyFromDisk(replyConfig) | |||
if err != nil { | |||
log.Error("LoadReply failed, error=%v", err) | |||
WechatReplyCache.Set(replyConfig.Name(), nil, 30*time.Second) | |||
return nil, err | |||
} | |||
WechatReplyCache.Set(replyConfig.Name(), content, 60*time.Second) | |||
return content, nil | |||
} |
@@ -17,7 +17,8 @@ var ( | |||
const ( | |||
GRANT_TYPE = "client_credential" | |||
ACCESS_TOKEN_PATH = "/cgi-bin/token" | |||
QR_CODE_Path = "/cgi-bin/qrcode/create" | |||
QR_CODE_PATH = "/cgi-bin/qrcode/create" | |||
GET_MATERIAL_PATH = "/cgi-bin/material/batchget_material" | |||
ACTION_QR_STR_SCENE = "QR_STR_SCENE" | |||
ERR_CODE_ACCESSTOKEN_EXPIRE = 42001 | |||
@@ -40,6 +41,11 @@ type QRCodeRequest struct { | |||
Action_info ActionInfo `json:"action_info"` | |||
Expire_seconds int `json:"expire_seconds"` | |||
} | |||
type MaterialRequest struct { | |||
Type string `json:"type"` | |||
Offset int `json:"offset"` | |||
Count int `json:"count"` | |||
} | |||
type ActionInfo struct { | |||
Scene Scene `json:"scene"` | |||
@@ -99,7 +105,7 @@ func callQRCodeCreate(sceneStr string) (*QRCodeResponse, bool) { | |||
SetQueryParam("access_token", GetWechatAccessToken()). | |||
SetBody(bodyJson). | |||
SetResult(&result). | |||
Post(setting.WechatApiHost + QR_CODE_Path) | |||
Post(setting.WechatApiHost + QR_CODE_PATH) | |||
if err != nil { | |||
log.Error("create QR code failed,e=%v", err) | |||
return nil, false | |||
@@ -115,6 +121,37 @@ func callQRCodeCreate(sceneStr string) (*QRCodeResponse, bool) { | |||
return &result, false | |||
} | |||
//getMaterial | |||
// api doc: https://developers.weixin.qq.com/doc/offiaccount/Asset_Management/Get_materials_list.html | |||
func getMaterial(mType string, offset, count int) (interface{}, bool) { | |||
client := getWechatRestyClient() | |||
body := &MaterialRequest{ | |||
Type: mType, | |||
Offset: offset, | |||
Count: count, | |||
} | |||
bodyJson, _ := json.Marshal(body) | |||
r, err := client.R(). | |||
SetHeader("Content-Type", "application/json"). | |||
SetQueryParam("access_token", GetWechatAccessToken()). | |||
SetBody(bodyJson). | |||
Post(setting.WechatApiHost + GET_MATERIAL_PATH) | |||
if err != nil { | |||
log.Error("create QR code failed,e=%v", err) | |||
return nil, false | |||
} | |||
a := r.Body() | |||
resultMap := make(map[string]interface{}, 0) | |||
json.Unmarshal(a, &resultMap) | |||
errcode := resultMap["errcode"] | |||
if errcode == fmt.Sprint(ERR_CODE_ACCESSTOKEN_EXPIRE) || errcode == fmt.Sprint(ERR_CODE_ACCESSTOKEN_INVALID) { | |||
return nil, true | |||
} | |||
log.Info("%v", r) | |||
return &resultMap, false | |||
} | |||
func getErrorCodeFromResponse(r *resty.Response) int { | |||
a := r.Body() | |||
resultMap := make(map[string]interface{}, 0) | |||
@@ -20,7 +20,7 @@ import ( | |||
// <EventKey><![CDATA[SCENE_VALUE]]></EventKey> | |||
// <Ticket><![CDATA[TICKET]]></Ticket> | |||
//</xml> | |||
type WechatEvent struct { | |||
type WechatMsg struct { | |||
ToUserName string | |||
FromUserName string | |||
CreateTime int64 | |||
@@ -28,9 +28,13 @@ type WechatEvent struct { | |||
Event string | |||
EventKey string | |||
Ticket string | |||
Content string | |||
MsgId string | |||
MsgDataId string | |||
Idx string | |||
} | |||
type EventReply struct { | |||
type MsgReply struct { | |||
XMLName xml.Name `xml:"xml"` | |||
ToUserName string | |||
FromUserName string | |||
@@ -39,16 +43,97 @@ type EventReply struct { | |||
Content string | |||
} | |||
type TextMsgReply struct { | |||
XMLName xml.Name `xml:"xml"` | |||
ToUserName string | |||
FromUserName string | |||
CreateTime int64 | |||
MsgType string | |||
Content string | |||
} | |||
type ImageMsgReply struct { | |||
XMLName xml.Name `xml:"xml"` | |||
ToUserName string | |||
FromUserName string | |||
CreateTime int64 | |||
MsgType string | |||
Image ImageContent | |||
} | |||
type VoiceMsgReply struct { | |||
XMLName xml.Name `xml:"xml"` | |||
ToUserName string | |||
FromUserName string | |||
CreateTime int64 | |||
MsgType string | |||
Voice VoiceContent | |||
} | |||
type VideoMsgReply struct { | |||
XMLName xml.Name `xml:"xml"` | |||
ToUserName string | |||
FromUserName string | |||
CreateTime int64 | |||
MsgType string | |||
Video VideoContent | |||
} | |||
type MusicMsgReply struct { | |||
XMLName xml.Name `xml:"xml"` | |||
ToUserName string | |||
FromUserName string | |||
CreateTime int64 | |||
MsgType string | |||
Music MusicContent | |||
} | |||
type NewsMsgReply struct { | |||
XMLName xml.Name `xml:"xml"` | |||
ToUserName string | |||
FromUserName string | |||
CreateTime int64 | |||
MsgType string | |||
ArticleCount int | |||
Articles ArticleItem | |||
} | |||
type ArticleItem struct { | |||
Item []ArticlesContent | |||
} | |||
type ImageContent struct { | |||
MediaId string | |||
} | |||
type VoiceContent struct { | |||
MediaId string | |||
} | |||
type VideoContent struct { | |||
MediaId string | |||
Title string | |||
Description string | |||
} | |||
type MusicContent struct { | |||
Title string | |||
Description string | |||
MusicUrl string | |||
HQMusicUrl string | |||
ThumbMediaId string | |||
} | |||
type ArticlesContent struct { | |||
XMLName xml.Name `xml:"item"` | |||
Title string | |||
Description string | |||
PicUrl string | |||
Url string | |||
} | |||
const ( | |||
WECHAT_EVENT_SUBSCRIBE = "subscribe" | |||
WECHAT_EVENT_SCAN = "SCAN" | |||
) | |||
const ( | |||
WECHAT_MSG_TYPE_TEXT = "text" | |||
WECHAT_MSG_TYPE_TEXT = "text" | |||
WECHAT_MSG_TYPE_EVENT = "event" | |||
) | |||
func HandleSubscribeEvent(we WechatEvent) string { | |||
func HandleScanEvent(we WechatMsg) string { | |||
eventKey := we.EventKey | |||
if eventKey == "" { | |||
return "" | |||
@@ -80,3 +165,11 @@ func HandleSubscribeEvent(we WechatEvent) string { | |||
return BIND_REPLY_SUCCESS | |||
} | |||
func HandleSubscribeEvent(we WechatMsg) *WechatReplyContent { | |||
r, err := LoadReplyFromCacheAndDisk(SubscribeReply) | |||
if err != nil || len(r) == 0 { | |||
return nil | |||
} | |||
return r[0] | |||
} |
@@ -0,0 +1,13 @@ | |||
package wechat | |||
import "code.gitea.io/gitea/modules/log" | |||
func GetWechatMaterial(mType string, offset, count int) interface{} { | |||
result, retryFlag := getMaterial(mType, offset, count) | |||
if retryFlag { | |||
log.Info("retryGetWechatMaterial calling") | |||
refreshAccessToken() | |||
result, _ = getMaterial(mType, offset, count) | |||
} | |||
return result | |||
} |
@@ -48,13 +48,10 @@ func isAdminOrOwnerOrJobCreater(ctx *context.Context, job *models.Cloudbrain, er | |||
if !ctx.IsSigned { | |||
return false | |||
} | |||
log.Info("is repo owner:" + strconv.FormatBool(ctx.IsUserRepoOwner())) | |||
log.Info("is user admin:" + strconv.FormatBool(ctx.IsUserSiteAdmin())) | |||
if err != nil { | |||
return ctx.IsUserRepoOwner() || ctx.IsUserSiteAdmin() | |||
} else { | |||
log.Info("is job creator:" + strconv.FormatBool(ctx.User.ID == job.UserID)) | |||
return ctx.IsUserRepoOwner() || ctx.IsUserSiteAdmin() || ctx.User.ID == job.UserID | |||
} | |||
@@ -0,0 +1,174 @@ | |||
package grampus | |||
import ( | |||
"encoding/json" | |||
"strings" | |||
"code.gitea.io/gitea/modules/setting" | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/modules/context" | |||
"code.gitea.io/gitea/modules/log" | |||
"code.gitea.io/gitea/modules/notification" | |||
"code.gitea.io/gitea/modules/timeutil" | |||
) | |||
const ( | |||
JobPath = "job/" | |||
ProcessorTypeNPU = "npu.huawei.com/NPU" | |||
ProcessorTypeGPU = "nvidia.com/gpu" | |||
GpuWorkDir = "/tmp/" | |||
NpuWorkDir = "/cache/" | |||
CommandPrepareScript = ";mkdir -p output;mkdir -p code;mkdir -p dataset;echo \"start loading script\";wget -q https://git.openi.org.cn/OpenIOSSG/script_for_grampus/archive/master.zip;" + | |||
"echo \"finish loading script\";unzip -q master.zip;cd script_for_grampus;chmod 777 downloader_for_obs uploader_for_obs downloader_for_minio uploader_for_minio;" | |||
//CommandPrepareScript = "pwd;cd /cache;mkdir -p output;mkdir -p code;mkdir -p dataset;echo \"start loading script\";wget -q https://git.openi.org.cn/OpenIOSSG/script_for_grampus/archive/master.zip;" + | |||
// "echo \"finish loading script\";unzip -q master.zip;cd script_for_grampus;chmod 777 downloader_for_obs uploader_for_obs downloader_for_minio uploader_for_minio;" | |||
CodeArchiveName = "master.zip" | |||
) | |||
var ( | |||
poolInfos *models.PoolInfos | |||
FlavorInfos *models.FlavorInfos | |||
ImageInfos *models.ImageInfosModelArts | |||
SpecialPools *models.SpecialPools | |||
) | |||
type GenerateTrainJobReq struct { | |||
JobName string | |||
Command string | |||
ResourceSpecId string | |||
ImageUrl string //与image_id二选一,都有的情况下优先image_url | |||
ImageId string | |||
DisplayJobName string | |||
Uuid string | |||
Description string | |||
CodeObsPath string | |||
BootFile string | |||
BootFileUrl string | |||
DataUrl string | |||
TrainUrl string | |||
WorkServerNumber int | |||
EngineID int64 | |||
CommitID string | |||
IsLatestVersion string | |||
BranchName string | |||
PreVersionId int64 | |||
PreVersionName string | |||
FlavorName string | |||
VersionCount int | |||
EngineName string | |||
TotalVersionCount int | |||
ComputeResource string | |||
DatasetName string | |||
Params string | |||
} | |||
func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error) { | |||
createTime := timeutil.TimeStampNow() | |||
var CenterID []string | |||
var CenterName []string | |||
if SpecialPools != nil { | |||
for _, pool := range SpecialPools.Pools { | |||
if !pool.IsExclusive && strings.Contains(req.ComputeResource, pool.Type) { | |||
org, _ := models.GetOrgByName(pool.Org) | |||
if org != nil { | |||
isOrgMember, _ := models.IsOrganizationMember(org.ID, ctx.User.ID) | |||
if isOrgMember { | |||
for _, info := range pool.Pool { | |||
CenterID = append(CenterID, info.Queue) | |||
CenterName = append(CenterName, info.Value) | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
jobResult, err := createJob(models.CreateGrampusJobRequest{ | |||
Name: req.JobName, | |||
Tasks: []models.GrampusTasks{ | |||
{ | |||
Name: req.JobName, | |||
Command: req.Command, | |||
ResourceSpecId: req.ResourceSpecId, | |||
ImageId: req.ImageId, | |||
ImageUrl: req.ImageUrl, | |||
CenterID: CenterID, | |||
CenterName: CenterName, | |||
ReplicaNum: 1, | |||
}, | |||
}, | |||
}) | |||
if err != nil { | |||
log.Error("createJob failed: %v", err.Error()) | |||
return err | |||
} | |||
jobID := jobResult.JobInfo.JobID | |||
err = models.CreateCloudbrain(&models.Cloudbrain{ | |||
Status: TransTrainJobStatus(jobResult.JobInfo.Status), | |||
UserID: ctx.User.ID, | |||
RepoID: ctx.Repo.Repository.ID, | |||
JobID: jobID, | |||
JobName: req.JobName, | |||
DisplayJobName: req.DisplayJobName, | |||
JobType: string(models.JobTypeTrain), | |||
Type: models.TypeC2Net, | |||
Uuid: req.Uuid, | |||
DatasetName: req.DatasetName, | |||
CommitID: req.CommitID, | |||
IsLatestVersion: req.IsLatestVersion, | |||
ComputeResource: req.ComputeResource, | |||
ImageID: req.ImageId, | |||
TrainUrl: req.TrainUrl, | |||
BranchName: req.BranchName, | |||
Parameters: req.Params, | |||
BootFile: req.BootFile, | |||
DataUrl: req.DataUrl, | |||
FlavorCode: req.ResourceSpecId, | |||
Description: req.Description, | |||
WorkServerNumber: req.WorkServerNumber, | |||
FlavorName: req.FlavorName, | |||
EngineName: req.EngineName, | |||
VersionCount: req.VersionCount, | |||
TotalVersionCount: req.TotalVersionCount, | |||
CreatedUnix: createTime, | |||
UpdatedUnix: createTime, | |||
}) | |||
if err != nil { | |||
log.Error("CreateCloudbrain(%s) failed:%v", req.DisplayJobName, err.Error()) | |||
return err | |||
} | |||
var actionType models.ActionType | |||
if req.ComputeResource == models.NPUResource { | |||
actionType = models.ActionCreateGrampusNPUTrainTask | |||
} else if req.ComputeResource == models.GPUResource { | |||
actionType = models.ActionCreateGrampusGPUTrainTask | |||
} | |||
notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, jobID, req.DisplayJobName, actionType) | |||
return nil | |||
} | |||
func TransTrainJobStatus(status string) string { | |||
if status == models.GrampusStatusPending { | |||
status = models.GrampusStatusWaiting | |||
} | |||
return strings.ToUpper(status) | |||
} | |||
func InitSpecialPool() { | |||
if SpecialPools == nil && setting.Grampus.SpecialPools != "" { | |||
json.Unmarshal([]byte(setting.Grampus.SpecialPools), &SpecialPools) | |||
} | |||
} |
@@ -0,0 +1,277 @@ | |||
package grampus | |||
import ( | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/modules/log" | |||
"code.gitea.io/gitea/modules/setting" | |||
"crypto/tls" | |||
"encoding/json" | |||
"fmt" | |||
"github.com/go-resty/resty/v2" | |||
"net/http" | |||
) | |||
var ( | |||
restyClient *resty.Client | |||
HOST string | |||
TOKEN string | |||
) | |||
const ( | |||
urlOpenApiV1 = "/openapi/v1/" | |||
urlGetToken = urlOpenApiV1 + "token" | |||
urlTrainJob = urlOpenApiV1 + "trainjob" | |||
urlGetResourceSpecs = urlOpenApiV1 + "resourcespec" | |||
urlGetImages = urlOpenApiV1 + "image" | |||
errorIllegalToken = 1005 | |||
) | |||
type GetTokenParams struct { | |||
UserName string `json:"username"` | |||
Password string `json:"password"` | |||
} | |||
type GetTokenResult struct { | |||
Token string `json:"token"` | |||
Expiration int64 `json:"expiration"` | |||
} | |||
func getRestyClient() *resty.Client { | |||
if restyClient == nil { | |||
restyClient = resty.New() | |||
restyClient.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true}) | |||
} | |||
return restyClient | |||
} | |||
func checkSetting() { | |||
if len(HOST) != 0 && len(TOKEN) != 0 && restyClient != nil { | |||
return | |||
} | |||
err := getToken() | |||
if err != nil { | |||
log.Error("getToken failed:%v", err) | |||
} | |||
} | |||
func getToken() error { | |||
HOST = setting.Grampus.Host | |||
client := getRestyClient() | |||
params := GetTokenParams{ | |||
UserName: setting.Grampus.UserName, | |||
Password: setting.Grampus.Password, | |||
} | |||
var result GetTokenResult | |||
res, err := client.R(). | |||
SetHeader("Content-Type", "application/json"). | |||
SetBody(params). | |||
SetResult(&result). | |||
Post(HOST + urlGetToken) | |||
if err != nil { | |||
return fmt.Errorf("resty getToken: %v", err) | |||
} | |||
if res.StatusCode() != http.StatusOK { | |||
return fmt.Errorf("getToken failed:%s", res.String()) | |||
} | |||
TOKEN = result.Token | |||
return nil | |||
} | |||
func createJob(req models.CreateGrampusJobRequest) (*models.CreateGrampusJobResponse, error) { | |||
checkSetting() | |||
client := getRestyClient() | |||
var result models.CreateGrampusJobResponse | |||
retry := 0 | |||
sendjob: | |||
_, err := client.R(). | |||
SetHeader("Content-Type", "application/json"). | |||
SetAuthToken(TOKEN). | |||
SetBody(req). | |||
SetResult(&result). | |||
Post(HOST + urlTrainJob) | |||
if err != nil { | |||
return nil, fmt.Errorf("resty CreateJob: %s", err) | |||
} | |||
if result.ErrorCode == errorIllegalToken && retry < 1 { | |||
retry++ | |||
_ = getToken() | |||
goto sendjob | |||
} | |||
if result.ErrorCode != 0 { | |||
log.Error("CreateJob failed(%d): %s", result.ErrorCode, result.ErrorMsg) | |||
return &result, fmt.Errorf("CreateJob failed(%d): %s", result.ErrorCode, result.ErrorMsg) | |||
} | |||
return &result, nil | |||
} | |||
func GetJob(jobID string) (*models.GetGrampusJobResponse, error) { | |||
checkSetting() | |||
client := getRestyClient() | |||
var result models.GetGrampusJobResponse | |||
retry := 0 | |||
sendjob: | |||
_, err := client.R(). | |||
SetAuthToken(TOKEN). | |||
SetResult(&result). | |||
Get(HOST + urlTrainJob + "/" + jobID) | |||
if err != nil { | |||
return nil, fmt.Errorf("resty GetJob: %v", err) | |||
} | |||
if result.ErrorCode == errorIllegalToken && retry < 1 { | |||
retry++ | |||
log.Info("retry get token") | |||
_ = getToken() | |||
goto sendjob | |||
} | |||
if result.ErrorCode != 0 { | |||
log.Error("GetJob failed(%d): %s", result.ErrorCode, result.ErrorMsg) | |||
return nil, fmt.Errorf("GetJob failed(%d): %s", result.ErrorCode, result.ErrorMsg) | |||
} | |||
return &result, nil | |||
} | |||
func GetResourceSpecs(processorType string) (*models.GetGrampusResourceSpecsResult, error) { | |||
checkSetting() | |||
client := getRestyClient() | |||
var result models.GetGrampusResourceSpecsResult | |||
retry := 0 | |||
sendjob: | |||
_, err := client.R(). | |||
SetAuthToken(TOKEN). | |||
SetResult(&result). | |||
Get(HOST + urlGetResourceSpecs + "?processorType=" + processorType) | |||
if err != nil { | |||
return nil, fmt.Errorf("resty GetResourceSpecs: %v", err) | |||
} | |||
if result.ErrorCode == errorIllegalToken && retry < 1 { | |||
retry++ | |||
log.Info("retry get token") | |||
_ = getToken() | |||
goto sendjob | |||
} | |||
if result.ErrorCode != 0 { | |||
log.Error("GetResourceSpecs failed(%d): %s", result.ErrorCode, result.ErrorMsg) | |||
return &result, fmt.Errorf("GetResourceSpecs failed(%d): %s", result.ErrorCode, result.ErrorMsg) | |||
} | |||
return &result, nil | |||
} | |||
func GetImages(processorType string) (*models.GetGrampusImagesResult, error) { | |||
checkSetting() | |||
client := getRestyClient() | |||
var result models.GetGrampusImagesResult | |||
retry := 0 | |||
sendjob: | |||
_, err := client.R(). | |||
SetAuthToken(TOKEN). | |||
SetResult(&result). | |||
Get(HOST + urlGetImages + "?processorType=" + processorType) | |||
if err != nil { | |||
return nil, fmt.Errorf("resty GetImages: %v", err) | |||
} | |||
if result.ErrorCode == errorIllegalToken && retry < 1 { | |||
retry++ | |||
log.Info("retry get token") | |||
_ = getToken() | |||
goto sendjob | |||
} | |||
if result.ErrorCode != 0 { | |||
log.Error("GetImages failed(%d): %s", result.ErrorCode, result.ErrorMsg) | |||
return &result, fmt.Errorf("GetImages failed(%d): %s", result.ErrorCode, result.ErrorMsg) | |||
} | |||
return &result, nil | |||
} | |||
func GetTrainJobLog(jobID string) (string, error) { | |||
checkSetting() | |||
client := getRestyClient() | |||
var logContent string | |||
res, err := client.R(). | |||
SetAuthToken(TOKEN). | |||
SetResult(&logContent). | |||
Get(HOST + urlTrainJob + "/" + jobID + "/task/0/replica/0/log") | |||
if err != nil { | |||
return logContent, fmt.Errorf("resty GetTrainJobLog: %v", err) | |||
} | |||
if res.StatusCode() != http.StatusOK { | |||
var temp models.GrampusResult | |||
if err = json.Unmarshal([]byte(res.String()), &temp); err != nil { | |||
log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error()) | |||
return logContent, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error()) | |||
} | |||
log.Error("GetTrainJobLog failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) | |||
return logContent, fmt.Errorf("GetTrainJobLog failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) | |||
} | |||
logContent = res.String() | |||
return logContent, nil | |||
} | |||
func StopJob(jobID string) (*models.GrampusStopJobResponse, error) { | |||
checkSetting() | |||
client := getRestyClient() | |||
var result models.GrampusStopJobResponse | |||
retry := 0 | |||
sendjob: | |||
_, err := client.R(). | |||
//SetHeader("Content-Type", "application/json"). | |||
SetAuthToken(TOKEN). | |||
SetResult(&result). | |||
Post(HOST + urlTrainJob + "/" + jobID + "/stop") | |||
if err != nil { | |||
return &result, fmt.Errorf("resty StopTrainJob: %v", err) | |||
} | |||
if result.ErrorCode == errorIllegalToken && retry < 1 { | |||
retry++ | |||
log.Info("retry get token") | |||
_ = getToken() | |||
goto sendjob | |||
} | |||
if result.ErrorCode != 0 { | |||
log.Error("GetJob failed(%d): %s", result.ErrorCode, result.ErrorMsg) | |||
return &result, fmt.Errorf("GetJob failed(%d): %s", result.ErrorCode, result.ErrorMsg) | |||
} | |||
return &result, nil | |||
} |
@@ -531,6 +531,15 @@ var ( | |||
FlavorInfos string | |||
TrainJobFLAVORINFOS string | |||
//grampus config | |||
Grampus = struct { | |||
Env string | |||
Host string | |||
UserName string | |||
Password string | |||
SpecialPools string | |||
}{} | |||
//elk config | |||
ElkUrl string | |||
ElkUser string | |||
@@ -553,6 +562,13 @@ var ( | |||
CloudBrainPayDelay time.Duration | |||
CloudBrainPayInterval time.Duration | |||
//wechat auto reply config | |||
UserNameOfWechatReply string | |||
RepoNameOfWechatReply string | |||
RefNameOfWechatReply string | |||
TreePathOfAutoMsgReply string | |||
TreePathOfSubscribe string | |||
//nginx proxy | |||
PROXYURL string | |||
RadarMap = struct { | |||
@@ -1380,6 +1396,12 @@ func NewContext() { | |||
WechatAppSecret = sec.Key("APP_SECRET").MustString("e48e13f315adc32749ddc7057585f198") | |||
WechatQRCodeExpireSeconds = sec.Key("QR_CODE_EXPIRE_SECONDS").MustInt(120) | |||
WechatAuthSwitch = sec.Key("AUTH_SWITCH").MustBool(false) | |||
UserNameOfWechatReply = sec.Key("AUTO_REPLY_USER_NAME").MustString("OpenIOSSG") | |||
RepoNameOfWechatReply = sec.Key("AUTO_REPLY_REPO_NAME").MustString("promote") | |||
RefNameOfWechatReply = sec.Key("AUTO_REPLY_REF_NAME").MustString("master") | |||
TreePathOfAutoMsgReply = sec.Key("AUTO_REPLY_TREE_PATH").MustString("wechat/auto_reply.json") | |||
TreePathOfSubscribe = sec.Key("SUBSCRIBE_TREE_PATH").MustString("wechat/subscribe_reply.json") | |||
WechatAuthSwitch = sec.Key("AUTH_SWITCH").MustBool(false) | |||
sec = Cfg.Section("point") | |||
CloudBrainPaySwitch = sec.Key("CLOUDBRAIN_PAY_SWITCH").MustBool(false) | |||
@@ -1395,6 +1417,18 @@ func NewContext() { | |||
Course.OrgName = sec.Key("org_name").MustString("") | |||
Course.TeamName = sec.Key("team_name").MustString("") | |||
GetGrampusConfig() | |||
} | |||
func GetGrampusConfig() { | |||
sec := Cfg.Section("grampus") | |||
Grampus.Env = sec.Key("ENV").MustString("TEST") | |||
Grampus.Host = sec.Key("SERVER_HOST").MustString("") | |||
Grampus.UserName = sec.Key("USERNAME").MustString("") | |||
Grampus.Password = sec.Key("PASSWORD").MustString("") | |||
Grampus.SpecialPools = sec.Key("SPECIAL_POOL").MustString("") | |||
} | |||
func SetRadarMapConfig() { | |||
@@ -6,6 +6,7 @@ package setting | |||
import ( | |||
"net/url" | |||
"strings" | |||
"code.gitea.io/gitea/modules/log" | |||
) | |||
@@ -13,14 +14,18 @@ import ( | |||
var ( | |||
// Webhook settings | |||
Webhook = struct { | |||
QueueLength int | |||
DeliverTimeout int | |||
SkipTLSVerify bool | |||
Types []string | |||
PagingNum int | |||
ProxyURL string | |||
ProxyURLFixed *url.URL | |||
ProxyHosts []string | |||
QueueLength int | |||
DeliverTimeout int | |||
SkipTLSVerify bool | |||
Types []string | |||
PagingNum int | |||
ProxyURL string | |||
ProxyURLFixed *url.URL | |||
ProxyHosts []string | |||
Socks5Proxy string | |||
Socks5UserName string | |||
Socks5Password string | |||
Socks5ProxyHosts []string | |||
}{ | |||
QueueLength: 1000, | |||
DeliverTimeout: 5, | |||
@@ -39,6 +44,10 @@ func newWebhookService() { | |||
Webhook.Types = []string{"gitea", "gogs", "slack", "discord", "dingtalk", "telegram", "msteams", "feishu", "matrix"} | |||
Webhook.PagingNum = sec.Key("PAGING_NUM").MustInt(10) | |||
Webhook.ProxyURL = sec.Key("PROXY_URL").MustString("") | |||
Webhook.Socks5Proxy = sec.Key("SOCKS5_PROXY_URL").MustString("") | |||
Webhook.Socks5UserName = sec.Key("SOCKS5_USER_NAME").MustString("") | |||
Webhook.Socks5Password = sec.Key("SOCKS5_PASSWORD").MustString("") | |||
Webhook.Socks5ProxyHosts = strings.Split(sec.Key("SOCKS5_PROXY_HOST").MustString(""), ";") | |||
if Webhook.ProxyURL != "" { | |||
var err error | |||
Webhook.ProxyURLFixed, err = url.Parse(Webhook.ProxyURL) | |||
@@ -31,3 +31,13 @@ func GetDirectorySize(path string) (int64, error) { | |||
}) | |||
return size, err | |||
} | |||
// check whether the path is dir | |||
func IsDir(path string) bool { | |||
s, err := os.Stat(path) | |||
if err != nil { | |||
return false | |||
} | |||
return s.IsDir() | |||
} |
@@ -115,7 +115,7 @@ func AddZero(t int64) (m string) { | |||
func ConvertDisplayJobNameToJobName(DisplayName string) (JobName string) { | |||
t := time.Now() | |||
JobName = "openi" + strings.ToLower(cutNameString(DisplayName, 15)) + "t" + t.Format("20060102150405")[4:] + strconv.Itoa(int(rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(100000))) | |||
JobName = strings.ToLower(cutNameString(DisplayName, 15)) + "t" + t.Format("20060102150405")[10:] + strconv.Itoa(int(rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(100000))) | |||
return JobName | |||
} | |||
@@ -16,6 +16,8 @@ import ( | |||
"sync" | |||
"time" | |||
"golang.org/x/net/proxy" | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/modules/graceful" | |||
"code.gitea.io/gitea/modules/log" | |||
@@ -137,8 +139,10 @@ func Deliver(t *models.HookTask) error { | |||
return | |||
} | |||
}() | |||
match := isSocks5ProxyUrlMatch(req) | |||
resp, err := makeReq(req, match) | |||
resp, err := webhookHTTPClient.Do(req) | |||
if err != nil { | |||
t.ResponseInfo.Body = fmt.Sprintf("Delivery: %v", err) | |||
return err | |||
@@ -161,6 +165,23 @@ func Deliver(t *models.HookTask) error { | |||
return nil | |||
} | |||
func makeReq(req *http.Request, proxyMatch bool) (*http.Response, error) { | |||
if proxyMatch { | |||
return webhookSocks5PoxyHTTPClient.Do(req) | |||
} | |||
return webhookHTTPClient.Do(req) | |||
} | |||
func isSocks5ProxyUrlMatch(req *http.Request) bool { | |||
for _, v := range socks5HostMatchers { | |||
if v.Match(req.URL.Host) { | |||
return true | |||
} | |||
} | |||
return false | |||
} | |||
// DeliverHooks checks and delivers undelivered hooks. | |||
// FIXME: graceful: This would likely benefit from either a worker pool with dummy queue | |||
// or a full queue. Then more hooks could be sent at same time. | |||
@@ -225,9 +246,11 @@ func DeliverHooks(ctx context.Context) { | |||
} | |||
var ( | |||
webhookHTTPClient *http.Client | |||
once sync.Once | |||
hostMatchers []glob.Glob | |||
webhookHTTPClient *http.Client | |||
once sync.Once | |||
hostMatchers []glob.Glob | |||
webhookSocks5PoxyHTTPClient *http.Client | |||
socks5HostMatchers []glob.Glob | |||
) | |||
func webhookProxy() func(req *http.Request) (*url.URL, error) { | |||
@@ -274,5 +297,31 @@ func InitDeliverHooks() { | |||
}, | |||
} | |||
if setting.Webhook.Socks5Proxy != "" { | |||
auth := proxy.Auth{ | |||
User: setting.Webhook.Socks5UserName, | |||
Password: setting.Webhook.Socks5Password, | |||
} | |||
dialSocksProxy, err := proxy.SOCKS5("tcp", setting.Webhook.Socks5Proxy, &auth, proxy.Direct) | |||
if err != nil { | |||
fmt.Println("Error connecting to proxy:", err) | |||
} | |||
tr := &http.Transport{Dial: dialSocksProxy.Dial} | |||
webhookSocks5PoxyHTTPClient = &http.Client{ | |||
Transport: tr, | |||
} | |||
for _, h := range setting.Webhook.Socks5ProxyHosts { | |||
if g, err := glob.Compile(h); err == nil { | |||
socks5HostMatchers = append(socks5HostMatchers, g) | |||
} else { | |||
log.Error("glob.Compile %s failed: %v", h, err) | |||
} | |||
} | |||
} | |||
go graceful.GetManager().RunWithShutdownContext(DeliverHooks) | |||
} |
@@ -1176,6 +1176,11 @@ model.manage.sava_model = Sava Model | |||
model.manage.model_manage = ModelManage | |||
model.manage.model_accuracy = Model Accuracy | |||
grampus.train_job.ai_center = AI Center | |||
grampus.dataset_path_rule = The code is storaged in /cache/code;the dataset is storaged in /cache/dataset;and please put your model into /cache/output, then you can download it online。 | |||
grampus.gpu_dataset_path_rule = The code is storaged in /tmp/code;the dataset is storaged in /tmp/dataset;and please put your model into /tmp/output, then you can download it online。 | |||
grampus.no_operate_right = You have no right to do this operation. | |||
template.items = Template Items | |||
template.git_content = Git Content (Default Branch) | |||
template.git_hooks = Git Hooks | |||
@@ -2935,6 +2940,8 @@ task_inferencejob=`created reasoning task <a href="%s/modelarts/inference-job/%s | |||
task_benchmark=`created profiling task <a href="%s/cloudbrain/benchmark/%s">%s</a>` | |||
task_createmodel=`created new model <a href="%s/modelmanage/show_model_info?name=%s">%s</a>` | |||
task_gputrainjob=`created CPU/GPU training task<a href="%s/cloudbrain/train-job/%s">%s</a>` | |||
task_c2netnputrainjob=`created NPU training task<a href="%s/grampus/train-job/%s">%s</a>` | |||
task_c2netgputrainjob=`created CPU/GPU training task<a href="%s/grampus/train-job/%s">%s</a>` | |||
[tool] | |||
ago = %s ago | |||
@@ -3023,6 +3030,9 @@ Platform_Tutorial = Tutorial | |||
foot.advice_feedback = Feedback | |||
[cloudbrain] | |||
resource_cluster = Resource Cluster | |||
resource_cluster_openi = OpenI Resource Cluster | |||
resource_cluster_c2net = China Computing NET | |||
compute_resource = Computing resources | |||
task_name = Task name | |||
task_type = Task type | |||
@@ -1190,6 +1190,12 @@ model.manage.sava_model = 保存模型 | |||
model.manage.model_manage = 模型管理 | |||
model.manage.model_accuracy = 模型精度 | |||
grampus.train_job.ai_center=智算中心 | |||
grampus.dataset_path_rule = 训练脚本存储在/cache/code中,数据集存储在/cache/dataset中,训练输出请存储在/cache/output中以供后续下载。 | |||
grampus.gpu_dataset_path_rule = 训练脚本存储在/tmp/code中,数据集存储在/tmp/dataset中,训练输出请存储在/tmp/output中以供后续下载。 | |||
grampus.no_operate_right = 您没有权限创建这类任务。 | |||
template.items=模板选项 | |||
template.git_content=Git数据(默认分支) | |||
template.git_hooks=Git 钩子 | |||
@@ -2950,6 +2956,8 @@ task_inferencejob=`创建了推理任务 <a href="%s/modelarts/inference-job/%s" | |||
task_benchmark=`创建了评测任务 <a href="%s/cloudbrain/benchmark/%s">%s</a>` | |||
task_createmodel=`导入了新模型 <a href="%s/modelmanage/show_model_info?name=%s">%s</a>` | |||
task_gputrainjob=`创建了CPU/GPU类型训练任务 <a href="%s/cloudbrain/train-job/%s">%s</a>` | |||
task_c2netnputrainjob=`创建了NPU类型训练任务 <a href="%s/grampus/train-job/%s">%s</a>` | |||
task_c2netgputrainjob=`创建了CPU/GPU类型训练任务 <a href="%s/grampus/train-job/%s">%s</a>` | |||
[tool] | |||
ago=%s前 | |||
@@ -3038,6 +3046,9 @@ Platform_Tutorial=新手指引 | |||
foot.advice_feedback = 意见反馈 | |||
[cloudbrain] | |||
resource_cluster = 算力集群 | |||
resource_cluster_openi = 启智集群 | |||
resource_cluster_c2net = 智算网络集群 | |||
compute_resource = 计算资源 | |||
task_name = 任务名称 | |||
task_type = 任务类型 | |||
@@ -156,7 +156,7 @@ document.onreadystatechange = function () { | |||
html += recordPrefix + actionName; | |||
html += " <a href=\"" + getRepoLink(record) + "\" rel=\"nofollow\">" + getRepotext(record) + "</a>" | |||
} | |||
else if(record.OpType == "24" || record.OpType == "26" || record.OpType == "27" || record.OpType == "28" || record.OpType == "30" || record.OpType == "31"){ | |||
else if(record.OpType == "24" || record.OpType == "26" || record.OpType == "27" || record.OpType == "28" || record.OpType == "30" || record.OpType == "31" || record.OpType == "32" || record.OpType == "33"){ | |||
html += recordPrefix + actionName; | |||
html += " <a href=\"" + getTaskLink(record) + "\" rel=\"nofollow\">" + record.RefName + "</a>" | |||
} | |||
@@ -201,6 +201,8 @@ function getTaskLink(record){ | |||
re = re + "/modelmanage/show_model_info?name=" + record.RefName; | |||
}else if(record.OpType == 31){ | |||
re = re + "/cloudbrain/train-job/" + record.Content; | |||
}else if(record.OpType == 32 || record.OpType == 33){ | |||
re = re + "/grampus/train-job/" + record.Content; | |||
} | |||
re = encodeURI(re); | |||
return re; | |||
@@ -374,7 +376,9 @@ var actionNameZH={ | |||
"28":"创建了推理任务", | |||
"29":"创建了评测任务", | |||
"30":"导入了新模型", | |||
"31":"创建了CPU/GPU类型训练任务" | |||
"31":"创建了CPU/GPU类型训练任务", | |||
"32":"创建了NPU类型训练任务", | |||
"33":"创建了CPU/GPU类型训练任务" | |||
}; | |||
var actionNameEN={ | |||
@@ -401,6 +405,8 @@ var actionNameEN={ | |||
"29":" created profiling task", | |||
"30":" created new model", | |||
"31":" created CPU/GPU type training task", | |||
"32":" created NPU type training task", | |||
"33":" created CPU/GPU type training task" | |||
}; | |||
var repoAndOrgZH={ | |||
@@ -918,6 +918,8 @@ function LetterAvatar(name, size, color) { | |||
} else { | |||
initials = nameSplit[0].charAt(0) + nameSplit[1].charAt(0); | |||
} | |||
let initials1 = initials.toUpperCase(); | |||
initials.toUpperCase(); | |||
if (w.devicePixelRatio) { | |||
size = size * w.devicePixelRatio; | |||
} | |||
@@ -934,7 +936,7 @@ function LetterAvatar(name, size, color) { | |||
context.font = Math.round(canvas.width / 2) + "px 'Microsoft Yahei'"; | |||
context.textAlign = "center"; | |||
context.fillStyle = "#FFF"; | |||
context.fillText(initials, size / 2, size / 1.5); | |||
context.fillText(initials1, size / 2, size / 1.5); | |||
dataURI = canvas.toDataURL(); | |||
canvas = null; | |||
return dataURI; | |||
@@ -1009,9 +1011,9 @@ function displayRepoResult(page, jsonResult, onlyReturnNum, keyword) { | |||
var recordMap = data[i]; | |||
html += '<div class="item">'; | |||
if (recordMap["avatar"]) { | |||
html += `<img class="ui avatar image" style="width:28px;height:28px" src="${recordMap["avatar"]}">`; | |||
html += `<img class="ui avatar image" src="${recordMap["avatar"]}">`; | |||
} else { | |||
html += `<img class="ui avatar image" style="width:28px;height:28px" avatar="${recordMap["owner_name"]}">`; | |||
html += `<img class="ui avatar image" avatar="${recordMap["owner_name"]}">`; | |||
} | |||
html += ' <div class="content">'; | |||
@@ -43,12 +43,6 @@ func CloudBrains(ctx *context.Context) { | |||
if page <= 0 { | |||
page = 1 | |||
} | |||
debugType := models.TypeCloudBrainAll | |||
if listType == models.GPUResource { | |||
debugType = models.TypeCloudBrainOne | |||
} else if listType == models.NPUResource { | |||
debugType = models.TypeCloudBrainTwo | |||
} | |||
var jobTypes []string | |||
jobTypeNot := false | |||
@@ -77,13 +71,14 @@ func CloudBrains(ctx *context.Context) { | |||
PageSize: setting.UI.IssuePagingNum, | |||
}, | |||
Keyword: keyword, | |||
Type: debugType, | |||
JobTypeNot: jobTypeNot, | |||
JobStatusNot: jobStatusNot, | |||
JobStatus: jobStatuses, | |||
JobTypes: jobTypes, | |||
NeedRepoInfo: true, | |||
IsLatestVersion: modelarts.IsLatestVersion, | |||
ComputeResource: listType, | |||
Type: models.TypeCloudBrainAll, | |||
}) | |||
if err != nil { | |||
ctx.ServerError("Get job failed:", err) | |||
@@ -947,6 +947,15 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
}) | |||
}) | |||
}, reqRepoReader(models.UnitTypeCloudBrain)) | |||
m.Group("/grampus", func() { | |||
m.Group("/train-job", func() { | |||
m.Group("/:jobid", func() { | |||
m.Get("", repo.GetModelArtsTrainJobVersion) | |||
m.Post("/stop_version", cloudbrain.AdminOrOwnerOrJobCreaterRightForTrain, repo_ext.GrampusStopJob) | |||
m.Get("/log", repo_ext.GrampusGetLog) | |||
}) | |||
}) | |||
}, reqRepoReader(models.UnitTypeCloudBrain)) | |||
}, repoAssignment()) | |||
}) | |||
@@ -1046,6 +1055,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
m.Get("/prd/event", authentication.ValidEventSource) | |||
m.Post("/prd/event", authentication.AcceptWechatEvent) | |||
}) | |||
m.Get("/wechat/material", authentication.GetMaterial) | |||
}, securityHeaders(), context.APIContexter(), sudo()) | |||
} | |||
@@ -103,7 +103,7 @@ func GetAllCloudbrainsOverview(ctx *context.Context) { | |||
if cloudbrain.Cloudbrain.Type == models.TypeCloudBrainTwo { | |||
cloudBrainTwoDuration = cloudBrainTwoDuration + cloudbrain.Cloudbrain.Duration | |||
} | |||
if cloudbrain.Cloudbrain.Type == models.TypeIntelligentNet { | |||
if cloudbrain.Cloudbrain.Type == models.TypeC2Net { | |||
intelligentNetDuration = intelligentNetDuration + cloudbrain.Cloudbrain.Duration | |||
} | |||
@@ -540,7 +540,7 @@ func GetAllCloudbrainsPeriodDistribution(ctx *context.Context) { | |||
cloudTwoJobTypeRes[cloudbrain.JobType] += 1 | |||
} | |||
} | |||
if cloudbrain.Cloudbrain.Type == models.TypeIntelligentNet { | |||
if cloudbrain.Cloudbrain.Type == models.TypeC2Net { | |||
if _, ok := intelligentNetJobTypeRes[cloudbrain.JobType]; !ok { | |||
intelligentNetJobTypeRes[cloudbrain.JobType] = 1 | |||
} else { | |||
@@ -1287,7 +1287,7 @@ func getCloudbrainType(rs *models.CloudbrainInfo, ctx *context.Context) string { | |||
return ctx.Tr("repo.cloudbrain1") | |||
} else if rs.Cloudbrain.Type == models.TypeCloudBrainTwo { | |||
return ctx.Tr("repo.cloudbrain2") | |||
} else if rs.Cloudbrain.Type == models.TypeIntelligentNet { | |||
} else if rs.Cloudbrain.Type == models.TypeC2Net { | |||
return ctx.Tr("repo.intelligent_net") | |||
} else { | |||
return ctx.Tr("repo.cloudbrain_untype") | |||
@@ -6,6 +6,8 @@ | |||
package repo | |||
import ( | |||
"code.gitea.io/gitea/modules/grampus" | |||
"encoding/json" | |||
"net/http" | |||
"strconv" | |||
"strings" | |||
@@ -125,7 +127,8 @@ func GetModelArtsTrainJob(ctx *context.APIContext) { | |||
func GetModelArtsTrainJobVersion(ctx *context.APIContext) { | |||
var ( | |||
err error | |||
err error | |||
aiCenterName string | |||
) | |||
jobID := ctx.Params(":jobid") | |||
@@ -167,7 +170,7 @@ func GetModelArtsTrainJobVersion(ctx *context.APIContext) { | |||
log.Error("UpdateJob failed:", err) | |||
} | |||
} | |||
} else { | |||
} else if job.Type == models.TypeCloudBrainTwo { | |||
result, err := modelarts.GetTrainJob(jobID, strconv.FormatInt(job.VersionID, 10)) | |||
if err != nil { | |||
ctx.NotFound(err) | |||
@@ -189,12 +192,50 @@ func GetModelArtsTrainJobVersion(ctx *context.APIContext) { | |||
if err != nil { | |||
log.Error("UpdateJob failed:", err) | |||
} | |||
} else if job.Type == models.TypeC2Net { | |||
result, err := grampus.GetJob(jobID) | |||
if err != nil { | |||
log.Error("GetJob(%s) failed:%v", job.JobName, err) | |||
ctx.NotFound(err) | |||
return | |||
} | |||
if job.StartTime == 0 && result.JobInfo.StartedAt > 0 { | |||
job.StartTime = timeutil.TimeStamp(result.JobInfo.StartedAt) | |||
} | |||
job.Status = grampus.TransTrainJobStatus(result.JobInfo.Status) | |||
job.Duration = result.JobInfo.RunSec | |||
job.TrainJobDuration = models.ConvertDurationToStr(job.Duration) | |||
if job.EndTime == 0 && models.IsTrainJobTerminal(job.Status) && job.StartTime > 0 { | |||
job.EndTime = job.StartTime.Add(job.Duration) | |||
} | |||
job.CorrectCreateUnix() | |||
if len(job.AiCenter) == 0 { | |||
if len(result.JobInfo.Tasks) > 0 { | |||
if len(result.JobInfo.Tasks[0].CenterID) > 0 && len(result.JobInfo.Tasks[0].CenterName) > 0 { | |||
job.AiCenter = result.JobInfo.Tasks[0].CenterID[0] + "+" + result.JobInfo.Tasks[0].CenterName[0] | |||
aiCenterName = result.JobInfo.Tasks[0].CenterName[0] | |||
} | |||
} | |||
} else { | |||
temp := strings.Split(job.AiCenter, "+") | |||
if len(temp) > 1 { | |||
aiCenterName = temp[1] | |||
} | |||
} | |||
err = models.UpdateTrainJobVersion(job) | |||
if err != nil { | |||
log.Error("UpdateJob failed:", err) | |||
} | |||
} | |||
ctx.JSON(http.StatusOK, map[string]interface{}{ | |||
"JobID": jobID, | |||
"JobStatus": job.Status, | |||
"JobDuration": job.TrainJobDuration, | |||
"AiCenter": aiCenterName, | |||
}) | |||
} | |||
@@ -373,11 +414,29 @@ func ModelList(ctx *context.APIContext) { | |||
log.Error("GetCloudbrainByJobID(%s) failed:%v", task.JobName, err.Error()) | |||
return | |||
} | |||
models, err := storage.GetObsListObject(task.JobName, "output/", parentDir, versionName) | |||
if err != nil { | |||
log.Info("get TrainJobListModel failed:", err) | |||
ctx.ServerError("GetObsListObject:", err) | |||
return | |||
var fileInfos []storage.FileInfo | |||
if task.ComputeResource == models.NPUResource { | |||
fileInfos, err = storage.GetObsListObject(task.JobName, "output/", parentDir, versionName) | |||
if err != nil { | |||
log.Info("get TrainJobListModel failed:", err) | |||
ctx.ServerError("GetObsListObject:", err) | |||
return | |||
} | |||
} else if task.ComputeResource == models.GPUResource { | |||
files, err := routerRepo.GetModelDirs(task.JobName, parentDir) | |||
if err != nil { | |||
log.Info("GetModelDirs failed:", err) | |||
ctx.ServerError("GetModelDirs:", err) | |||
return | |||
} | |||
err = json.Unmarshal([]byte(files), &fileInfos) | |||
if err != nil { | |||
log.Error("json.Unmarshal failed:%v", err.Error(), ctx.Data["msgID"]) | |||
ctx.ServerError("json.Unmarshal failed:", err) | |||
return | |||
} | |||
} | |||
ctx.JSON(http.StatusOK, map[string]interface{}{ | |||
@@ -385,7 +444,7 @@ func ModelList(ctx *context.APIContext) { | |||
"VersionName": versionName, | |||
"StatusOK": 0, | |||
"Path": dirArray, | |||
"Dirs": models, | |||
"Dirs": fileInfos, | |||
"task": task, | |||
"PageIsCloudBrain": true, | |||
}) | |||
@@ -887,19 +887,12 @@ func getTimePeroid(ctx *context.Context, recordBeginTime time.Time) (time.Time, | |||
if queryType == "all" { | |||
beginTime = recordBeginTimeTemp | |||
endTime = now | |||
} else if queryType == "today" { | |||
} else if queryType == "yesterday" { | |||
endTime = now | |||
beginTime = time.Date(endTime.Year(), endTime.Month(), endTime.Day(), 0, 0, 0, 0, now.Location()) | |||
} else if queryType == "yesterday" { | |||
endTime = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()) | |||
beginTime = endTime.AddDate(0, 0, -1) | |||
} else if queryType == "last_7day" { | |||
beginTime = now.AddDate(0, 0, -7) | |||
beginTime = time.Date(beginTime.Year(), beginTime.Month(), beginTime.Day(), 0, 0, 0, 0, now.Location()) | |||
endTime = now | |||
} else if queryType == "last_30day" { | |||
beginTime = now.AddDate(0, 0, -30) | |||
} else if queryType == "current_week" { | |||
beginTime = now.AddDate(0, 0, -int(time.Now().Weekday())+2) //begin from monday | |||
beginTime = time.Date(beginTime.Year(), beginTime.Month(), beginTime.Day(), 0, 0, 0, 0, now.Location()) | |||
endTime = now | |||
} else if queryType == "current_month" { | |||
@@ -8,9 +8,11 @@ import ( | |||
"code.gitea.io/gitea/modules/redis/redis_client" | |||
"code.gitea.io/gitea/modules/redis/redis_key" | |||
"code.gitea.io/gitea/modules/setting" | |||
"code.gitea.io/gitea/routers/response" | |||
"encoding/json" | |||
"errors" | |||
gouuid "github.com/satori/go.uuid" | |||
"strconv" | |||
"time" | |||
) | |||
@@ -125,3 +127,23 @@ func createQRCode4Bind(userId int64) (*QRCodeResponse, error) { | |||
} | |||
return result, nil | |||
} | |||
// GetMaterial | |||
func GetMaterial(ctx *context.Context) { | |||
mType := ctx.Query("type") | |||
offsetStr := ctx.Query("offset") | |||
countStr := ctx.Query("count") | |||
var offset, count int | |||
if offsetStr == "" { | |||
offset = 0 | |||
} else { | |||
offset, _ = strconv.Atoi(offsetStr) | |||
} | |||
if countStr == "" { | |||
count = 20 | |||
} else { | |||
count, _ = strconv.Atoi(countStr) | |||
} | |||
r := wechat.GetWechatMaterial(mType, offset, count) | |||
ctx.JSON(200, response.SuccessWithData(r)) | |||
} |
@@ -14,24 +14,48 @@ import ( | |||
// https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Passive_user_reply_message.html | |||
func AcceptWechatEvent(ctx *context.Context) { | |||
b, _ := ioutil.ReadAll(ctx.Req.Request.Body) | |||
we := wechat.WechatEvent{} | |||
we := wechat.WechatMsg{} | |||
xml.Unmarshal(b, &we) | |||
switch we.MsgType { | |||
case wechat.WECHAT_MSG_TYPE_EVENT: | |||
HandleEventMsg(ctx, we) | |||
case wechat.WECHAT_MSG_TYPE_TEXT: | |||
HandleTextMsg(ctx, we) | |||
} | |||
log.Info("accept wechat event= %+v", we) | |||
var replyStr string | |||
switch we.Event { | |||
case wechat.WECHAT_EVENT_SUBSCRIBE, wechat.WECHAT_EVENT_SCAN: | |||
replyStr = wechat.HandleSubscribeEvent(we) | |||
break | |||
} | |||
// ValidEventSource | |||
func ValidEventSource(ctx *context.Context) { | |||
echostr := ctx.Query("echostr") | |||
ctx.Write([]byte(echostr)) | |||
return | |||
} | |||
func HandleEventMsg(ctx *context.Context, msg wechat.WechatMsg) { | |||
switch msg.Event { | |||
case wechat.WECHAT_EVENT_SCAN: | |||
HandleEventScan(ctx, msg) | |||
case wechat.WECHAT_EVENT_SUBSCRIBE: | |||
if msg.EventKey != "" { | |||
HandleEventScan(ctx, msg) | |||
} else { | |||
HandleEventSubscribe(ctx, msg) | |||
} | |||
} | |||
} | |||
func HandleEventScan(ctx *context.Context, msg wechat.WechatMsg) { | |||
replyStr := wechat.HandleScanEvent(msg) | |||
if replyStr == "" { | |||
log.Info("reply str is empty") | |||
return | |||
} | |||
reply := &wechat.EventReply{ | |||
ToUserName: we.FromUserName, | |||
FromUserName: we.ToUserName, | |||
reply := &wechat.MsgReply{ | |||
ToUserName: msg.FromUserName, | |||
FromUserName: msg.ToUserName, | |||
CreateTime: time.Now().Unix(), | |||
MsgType: wechat.WECHAT_MSG_TYPE_TEXT, | |||
Content: replyStr, | |||
@@ -39,9 +63,99 @@ func AcceptWechatEvent(ctx *context.Context) { | |||
ctx.XML(200, reply) | |||
} | |||
// ValidEventSource | |||
func ValidEventSource(ctx *context.Context) { | |||
echostr := ctx.Query("echostr") | |||
ctx.Write([]byte(echostr)) | |||
return | |||
func HandleEventSubscribe(ctx *context.Context, msg wechat.WechatMsg) { | |||
r := wechat.HandleSubscribeEvent(msg) | |||
if r == nil { | |||
return | |||
} | |||
reply := buildReplyContent(msg, r) | |||
ctx.XML(200, reply) | |||
} | |||
func HandleTextMsg(ctx *context.Context, msg wechat.WechatMsg) { | |||
r := wechat.GetAutomaticReply(msg.Content) | |||
if r == nil { | |||
log.Info("TextMsg reply is empty") | |||
return | |||
} | |||
reply := buildReplyContent(msg, r) | |||
ctx.XML(200, reply) | |||
} | |||
func buildReplyContent(msg wechat.WechatMsg, r *wechat.WechatReplyContent) interface{} { | |||
reply := &wechat.MsgReply{ | |||
ToUserName: msg.FromUserName, | |||
FromUserName: msg.ToUserName, | |||
CreateTime: time.Now().Unix(), | |||
MsgType: r.ReplyType, | |||
} | |||
switch r.ReplyType { | |||
case wechat.ReplyTypeText: | |||
return &wechat.TextMsgReply{ | |||
ToUserName: msg.FromUserName, | |||
FromUserName: msg.ToUserName, | |||
CreateTime: time.Now().Unix(), | |||
MsgType: r.ReplyType, | |||
Content: r.Reply.Content, | |||
} | |||
case wechat.ReplyTypeImage: | |||
return &wechat.ImageMsgReply{ | |||
ToUserName: msg.FromUserName, | |||
FromUserName: msg.ToUserName, | |||
CreateTime: time.Now().Unix(), | |||
MsgType: r.ReplyType, | |||
Image: wechat.ImageContent{ | |||
MediaId: r.Reply.MediaId, | |||
}, | |||
} | |||
case wechat.ReplyTypeVoice: | |||
return &wechat.VoiceMsgReply{ | |||
ToUserName: msg.FromUserName, | |||
FromUserName: msg.ToUserName, | |||
CreateTime: time.Now().Unix(), | |||
MsgType: r.ReplyType, | |||
Voice: wechat.VoiceContent{ | |||
MediaId: r.Reply.MediaId, | |||
}, | |||
} | |||
case wechat.ReplyTypeVideo: | |||
return &wechat.VideoMsgReply{ | |||
ToUserName: msg.FromUserName, | |||
FromUserName: msg.ToUserName, | |||
CreateTime: time.Now().Unix(), | |||
MsgType: r.ReplyType, | |||
Video: wechat.VideoContent{ | |||
MediaId: r.Reply.MediaId, | |||
Title: r.Reply.Title, | |||
Description: r.Reply.Description, | |||
}, | |||
} | |||
case wechat.ReplyTypeMusic: | |||
return &wechat.MusicMsgReply{ | |||
ToUserName: msg.FromUserName, | |||
FromUserName: msg.ToUserName, | |||
CreateTime: time.Now().Unix(), | |||
MsgType: r.ReplyType, | |||
Music: wechat.MusicContent{ | |||
Title: r.Reply.Title, | |||
Description: r.Reply.Description, | |||
MusicUrl: r.Reply.MusicUrl, | |||
HQMusicUrl: r.Reply.HQMusicUrl, | |||
ThumbMediaId: r.Reply.ThumbMediaId, | |||
}, | |||
} | |||
case wechat.ReplyTypeNews: | |||
return &wechat.NewsMsgReply{ | |||
ToUserName: msg.FromUserName, | |||
FromUserName: msg.ToUserName, | |||
CreateTime: time.Now().Unix(), | |||
MsgType: r.ReplyType, | |||
ArticleCount: len(r.Reply.Articles), | |||
Articles: wechat.ArticleItem{ | |||
Item: r.Reply.Articles}, | |||
} | |||
} | |||
return reply | |||
} |
@@ -3,6 +3,7 @@ package repo | |||
import ( | |||
"bufio" | |||
"code.gitea.io/gitea/services/reward/point/account" | |||
"code.gitea.io/gitea/modules/grampus" | |||
"encoding/json" | |||
"errors" | |||
"fmt" | |||
@@ -187,7 +188,7 @@ func cloudBrainNewDataPrepare(ctx *context.Context) error { | |||
ctx.Data["brainscore_path"] = cloudbrain.BrainScoreMountPath | |||
ctx.Data["is_brainscore_enabled"] = setting.IsBrainScoreEnabled | |||
ctx.Data["cloudbraintype"] = models.TypeCloudBrainOne | |||
ctx.Data["datasetType"] = models.TypeCloudBrainOne | |||
ctx.Data["benchmarkMode"] = ctx.Query("benchmarkMode") | |||
@@ -1049,6 +1050,7 @@ func GetPublicImages(ctx *context.Context) { | |||
IncludeOfficialOnly: ctx.QueryBool("recommend"), | |||
SearchOrderBy: "type desc, num_stars desc,id desc", | |||
Status: models.IMAGE_STATUS_SUCCESS, | |||
CloudbrainType: ctx.QueryInt("cloudbrainType"), | |||
} | |||
getImages(ctx, &opts) | |||
@@ -1484,7 +1486,34 @@ func SyncCloudbrainStatus() { | |||
} else { | |||
log.Error("task.JobType(%s) is error:%s", task.JobName, task.JobType) | |||
} | |||
} else if task.Type == models.TypeC2Net { | |||
result, err := grampus.GetJob(task.JobID) | |||
if err != nil { | |||
log.Error("GetTrainJob(%s) failed:%v", task.JobName, err) | |||
continue | |||
} | |||
if result != nil { | |||
if len(result.JobInfo.Tasks[0].CenterID) == 1 && len(result.JobInfo.Tasks[0].CenterName) == 1 { | |||
task.AiCenter = result.JobInfo.Tasks[0].CenterID[0] + "+" + result.JobInfo.Tasks[0].CenterName[0] | |||
} | |||
task.Status = grampus.TransTrainJobStatus(result.JobInfo.Status) | |||
task.Duration = result.JobInfo.RunSec | |||
task.TrainJobDuration = models.ConvertDurationToStr(task.Duration) | |||
if task.StartTime == 0 && result.JobInfo.StartedAt > 0 { | |||
task.StartTime = timeutil.TimeStamp(result.JobInfo.StartedAt) | |||
} | |||
if task.EndTime == 0 && models.IsTrainJobTerminal(task.Status) && task.StartTime > 0 { | |||
task.EndTime = task.StartTime.Add(task.Duration) | |||
} | |||
task.CorrectCreateUnix() | |||
err = models.UpdateJob(task) | |||
if err != nil { | |||
log.Error("UpdateJob(%s) failed:%v", task.JobName, err) | |||
continue | |||
} | |||
} | |||
} else { | |||
log.Error("task.Type(%s) is error:%d", task.JobName, task.Type) | |||
} | |||
@@ -0,0 +1,786 @@ | |||
package repo | |||
import ( | |||
"code.gitea.io/gitea/modules/auth" | |||
"code.gitea.io/gitea/modules/git" | |||
"code.gitea.io/gitea/modules/grampus" | |||
"code.gitea.io/gitea/modules/modelarts" | |||
"code.gitea.io/gitea/modules/timeutil" | |||
"code.gitea.io/gitea/modules/util" | |||
"encoding/json" | |||
"errors" | |||
"github.com/unknwon/com" | |||
"io/ioutil" | |||
"net/http" | |||
"os" | |||
"path" | |||
"strconv" | |||
"strings" | |||
"time" | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/modules/base" | |||
"code.gitea.io/gitea/modules/cloudbrain" | |||
"code.gitea.io/gitea/modules/context" | |||
"code.gitea.io/gitea/modules/log" | |||
"code.gitea.io/gitea/modules/setting" | |||
) | |||
const ( | |||
tplGrampusTrainJobShow base.TplName = "repo/grampus/trainjob/show" | |||
//GPU | |||
tplGrampusTrainJobGPUNew base.TplName = "repo/grampus/trainjob/gpu/new" | |||
//NPU | |||
tplGrampusTrainJobNPUNew base.TplName = "repo/grampus/trainjob/npu/new" | |||
) | |||
func GrampusTrainJobGPUNew(ctx *context.Context) { | |||
ctx.Data["datasetType"] = models.TypeCloudBrainOne | |||
err := grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeGPU) | |||
if err != nil { | |||
ctx.ServerError("get new train-job info failed", err) | |||
return | |||
} | |||
ctx.HTML(http.StatusOK, tplGrampusTrainJobGPUNew) | |||
} | |||
func GrampusTrainJobNPUNew(ctx *context.Context) { | |||
ctx.Data["datasetType"] = models.TypeCloudBrainTwo | |||
err := grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeNPU) | |||
if err != nil { | |||
ctx.ServerError("get new train-job info failed", err) | |||
return | |||
} | |||
ctx.HTML(200, tplGrampusTrainJobNPUNew) | |||
} | |||
func grampusTrainJobNewDataPrepare(ctx *context.Context, processType string) error { | |||
ctx.Data["PageIsCloudBrain"] = true | |||
t := time.Now() | |||
var displayJobName = cutString(ctx.User.Name, 5) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:] | |||
ctx.Data["display_job_name"] = displayJobName | |||
//get valid images | |||
images, err := grampus.GetImages(processType) | |||
if err != nil { | |||
log.Error("GetImages failed:", err.Error()) | |||
} else { | |||
ctx.Data["images"] = images.Infos | |||
} | |||
grampus.InitSpecialPool() | |||
ctx.Data["GPUEnabled"] = true | |||
ctx.Data["NPUEnabled"] = true | |||
if grampus.SpecialPools != nil { | |||
for _, pool := range grampus.SpecialPools.Pools { | |||
if pool.IsExclusive { | |||
org, _ := models.GetOrgByName(pool.Org) | |||
if org != nil { | |||
isOrgMember, _ := models.IsOrganizationMember(org.ID, ctx.User.ID) | |||
if !isOrgMember { | |||
ctx.Data[pool.Type+"Enabled"] = false | |||
} | |||
} | |||
} | |||
} | |||
} | |||
//get valid resource specs | |||
specs, err := grampus.GetResourceSpecs(processType) | |||
if err != nil { | |||
log.Error("GetResourceSpecs failed:", err.Error()) | |||
} else { | |||
ctx.Data["flavor_infos"] = specs.Infos | |||
} | |||
//get branches | |||
branches, _, err := ctx.Repo.GitRepo.GetBranches(0, 0) | |||
if err != nil { | |||
log.Error("GetBranches error:", err.Error()) | |||
} else { | |||
ctx.Data["branches"] = branches | |||
} | |||
ctx.Data["branchName"] = ctx.Repo.BranchName | |||
if processType == grampus.ProcessorTypeGPU { | |||
ctx.Data["datasetType"] = models.TypeCloudBrainOne | |||
} else if processType == grampus.ProcessorTypeNPU { | |||
ctx.Data["datasetType"] = models.TypeCloudBrainTwo | |||
} | |||
return nil | |||
} | |||
func grampusParamCheckCreateTrainJob(form auth.CreateGrampusTrainJobForm) error { | |||
if !strings.HasSuffix(strings.TrimSpace(form.BootFile), ".py") { | |||
log.Error("the boot file(%s) must be a python file", form.BootFile) | |||
return errors.New("启动文件必须是python文件") | |||
} | |||
if form.BranchName == "" { | |||
log.Error("the branch must not be null!", form.BranchName) | |||
return errors.New("代码分支不能为空!") | |||
} | |||
return nil | |||
} | |||
func GrampusTrainJobGpuCreate(ctx *context.Context, form auth.CreateGrampusTrainJobForm) { | |||
displayJobName := form.DisplayJobName | |||
jobName := util.ConvertDisplayJobNameToJobName(displayJobName) | |||
uuid := form.Attachment | |||
description := form.Description | |||
bootFile := strings.TrimSpace(form.BootFile) | |||
params := form.Params | |||
repo := ctx.Repo.Repository | |||
codeLocalPath := setting.JobPath + jobName + cloudbrain.CodeMountPath + "/" | |||
codeMinioPath := setting.CBCodePathPrefix + jobName + cloudbrain.CodeMountPath + "/" | |||
dataMinioPath := setting.Attachment.Minio.BasePath + path.Join(uuid[0:1], uuid[1:2]) + "/" + uuid | |||
branchName := form.BranchName | |||
flavorName := form.FlavorName | |||
image := strings.TrimSpace(form.Image) | |||
if !jobNamePattern.MatchString(displayJobName) { | |||
grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeGPU) | |||
ctx.RenderWithErr(ctx.Tr("repo.cloudbrain_jobname_err"), tplGrampusTrainJobGPUNew, &form) | |||
return | |||
} | |||
errStr := checkSpecialPool(ctx, "GPU") | |||
if errStr != "" { | |||
grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeGPU) | |||
ctx.RenderWithErr(errStr, tplGrampusTrainJobGPUNew, &form) | |||
return | |||
} | |||
//check count limit | |||
count, err := models.GetGrampusCountByUserID(ctx.User.ID, string(models.JobTypeTrain), models.GPUResource) | |||
if err != nil { | |||
log.Error("GetGrampusCountByUserID failed:%v", err, ctx.Data["MsgID"]) | |||
grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeGPU) | |||
ctx.RenderWithErr("system error", tplGrampusTrainJobGPUNew, &form) | |||
return | |||
} else { | |||
if count >= 1 { | |||
log.Error("the user already has running or waiting task", ctx.Data["MsgID"]) | |||
grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeGPU) | |||
ctx.RenderWithErr("you have already a running or waiting task, can not create more", tplGrampusTrainJobGPUNew, &form) | |||
return | |||
} | |||
} | |||
//check param | |||
if err := grampusParamCheckCreateTrainJob(form); err != nil { | |||
log.Error("paramCheckCreateTrainJob failed:(%v)", err, ctx.Data["MsgID"]) | |||
grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeGPU) | |||
ctx.RenderWithErr(err.Error(), tplGrampusTrainJobGPUNew, &form) | |||
return | |||
} | |||
//check whether the task name in the project is duplicated | |||
tasks, err := models.GetCloudbrainsByDisplayJobName(repo.ID, string(models.JobTypeTrain), displayJobName) | |||
if err == nil { | |||
if len(tasks) != 0 { | |||
log.Error("the job name did already exist", ctx.Data["MsgID"]) | |||
grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeGPU) | |||
ctx.RenderWithErr("the job name did already exist", tplGrampusTrainJobGPUNew, &form) | |||
return | |||
} | |||
} else { | |||
if !models.IsErrJobNotExist(err) { | |||
log.Error("system error, %v", err, ctx.Data["MsgID"]) | |||
grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeGPU) | |||
ctx.RenderWithErr("system error", tplGrampusTrainJobGPUNew, &form) | |||
return | |||
} | |||
} | |||
//check dataset | |||
attachment, err := models.GetAttachmentByUUID(uuid) | |||
if err != nil { | |||
log.Error("GetAttachmentByUUID failed:", err.Error(), ctx.Data["MsgID"]) | |||
grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeGPU) | |||
ctx.RenderWithErr("dataset is not exist", tplGrampusTrainJobGPUNew, &form) | |||
return | |||
} | |||
//prepare code and out path | |||
_, err = ioutil.ReadDir(codeLocalPath) | |||
if err == nil { | |||
os.RemoveAll(codeLocalPath) | |||
} | |||
if err := downloadZipCode(ctx, codeLocalPath, branchName); err != nil { | |||
log.Error("downloadZipCode failed, server timed out: %s (%v)", repo.FullName(), err, ctx.Data["MsgID"]) | |||
grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeGPU) | |||
ctx.RenderWithErr("Create task failed, internal error", tplGrampusTrainJobGPUNew, &form) | |||
return | |||
} | |||
//todo: upload code (send to file_server todo this work?) | |||
//upload code | |||
if err := uploadCodeToMinio(codeLocalPath+"/", jobName, cloudbrain.CodeMountPath+"/"); err != nil { | |||
log.Error("Failed to uploadCodeToMinio: %s (%v)", repo.FullName(), err, ctx.Data["MsgID"]) | |||
grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeGPU) | |||
ctx.RenderWithErr("Create task failed, internal error", tplGrampusTrainJobGPUNew, &form) | |||
return | |||
} | |||
modelPath := setting.JobPath + jobName + cloudbrain.ModelMountPath + "/" | |||
if err := mkModelPath(modelPath); err != nil { | |||
log.Error("Failed to mkModelPath: %s (%v)", repo.FullName(), err, ctx.Data["MsgID"]) | |||
grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeGPU) | |||
ctx.RenderWithErr("Create task failed, internal error", tplGrampusTrainJobGPUNew, &form) | |||
return | |||
} | |||
//init model readme | |||
if err := uploadCodeToMinio(modelPath, jobName, cloudbrain.ModelMountPath+"/"); err != nil { | |||
log.Error("Failed to uploadCodeToMinio: %s (%v)", repo.FullName(), err, ctx.Data["MsgID"]) | |||
grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeGPU) | |||
ctx.RenderWithErr("Create task failed, internal error", tplGrampusTrainJobGPUNew, &form) | |||
return | |||
} | |||
//prepare command | |||
command, err := generateCommand(repo.Name, grampus.ProcessorTypeGPU, codeMinioPath+cloudbrain.DefaultBranchName+".zip", dataMinioPath, bootFile, params, setting.CBCodePathPrefix+jobName+cloudbrain.ModelMountPath+"/", attachment.Name) | |||
if err != nil { | |||
log.Error("Failed to generateCommand: %s (%v)", displayJobName, err, ctx.Data["MsgID"]) | |||
grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeGPU) | |||
ctx.RenderWithErr("Create task failed, internal error", tplGrampusTrainJobGPUNew, &form) | |||
return | |||
} | |||
commitID, _ := ctx.Repo.GitRepo.GetBranchCommitID(branchName) | |||
req := &grampus.GenerateTrainJobReq{ | |||
JobName: jobName, | |||
DisplayJobName: displayJobName, | |||
ComputeResource: models.GPUResource, | |||
Command: command, | |||
ResourceSpecId: form.FlavorID, | |||
ImageUrl: image, | |||
Description: description, | |||
BootFile: bootFile, | |||
Uuid: uuid, | |||
CommitID: commitID, | |||
BranchName: branchName, | |||
Params: form.Params, | |||
FlavorName: flavorName, | |||
EngineName: image, | |||
DatasetName: attachment.Name, | |||
IsLatestVersion: modelarts.IsLatestVersion, | |||
VersionCount: modelarts.VersionCount, | |||
WorkServerNumber: 1, | |||
} | |||
err = grampus.GenerateTrainJob(ctx, req) | |||
if err != nil { | |||
log.Error("GenerateTrainJob failed:%v", err.Error(), ctx.Data["MsgID"]) | |||
grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeGPU) | |||
ctx.RenderWithErr(err.Error(), tplGrampusTrainJobGPUNew, &form) | |||
return | |||
} | |||
ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/train-job") | |||
} | |||
func checkSpecialPool(ctx *context.Context, resourceType string) string { | |||
grampus.InitSpecialPool() | |||
if grampus.SpecialPools != nil { | |||
for _, pool := range grampus.SpecialPools.Pools { | |||
if pool.IsExclusive && pool.Type == resourceType { | |||
org, _ := models.GetOrgByName(pool.Org) | |||
if org != nil { | |||
isOrgMember, _ := models.IsOrganizationMember(org.ID, ctx.User.ID) | |||
if !isOrgMember { | |||
return ctx.Tr("repo.grampus.no_operate_right") | |||
} | |||
} | |||
} | |||
} | |||
} | |||
return "" | |||
} | |||
func GrampusTrainJobNpuCreate(ctx *context.Context, form auth.CreateGrampusTrainJobForm) { | |||
displayJobName := form.DisplayJobName | |||
jobName := util.ConvertDisplayJobNameToJobName(displayJobName) | |||
uuid := form.Attachment | |||
description := form.Description | |||
bootFile := strings.TrimSpace(form.BootFile) | |||
params := form.Params | |||
repo := ctx.Repo.Repository | |||
codeLocalPath := setting.JobPath + jobName + modelarts.CodePath | |||
codeObsPath := grampus.JobPath + jobName + modelarts.CodePath | |||
dataObsPath := setting.BasePath + path.Join(uuid[0:1], uuid[1:2]) + "/" + uuid + "/" | |||
branchName := form.BranchName | |||
isLatestVersion := modelarts.IsLatestVersion | |||
flavorName := form.FlavorName | |||
versionCount := modelarts.VersionCount | |||
engineName := form.EngineName | |||
if !jobNamePattern.MatchString(displayJobName) { | |||
grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeNPU) | |||
ctx.RenderWithErr(ctx.Tr("repo.cloudbrain_jobname_err"), tplGrampusTrainJobNPUNew, &form) | |||
return | |||
} | |||
errStr := checkSpecialPool(ctx, "NPU") | |||
if errStr != "" { | |||
grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeNPU) | |||
ctx.RenderWithErr(errStr, tplGrampusTrainJobGPUNew, &form) | |||
return | |||
} | |||
//check count limit | |||
count, err := models.GetGrampusCountByUserID(ctx.User.ID, string(models.JobTypeTrain), models.NPUResource) | |||
if err != nil { | |||
log.Error("GetGrampusCountByUserID failed:%v", err, ctx.Data["MsgID"]) | |||
grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeNPU) | |||
ctx.RenderWithErr("system error", tplGrampusTrainJobNPUNew, &form) | |||
return | |||
} else { | |||
if count >= 1 { | |||
log.Error("the user already has running or waiting task", ctx.Data["MsgID"]) | |||
grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeNPU) | |||
ctx.RenderWithErr("you have already a running or waiting task, can not create more", tplGrampusTrainJobNPUNew, &form) | |||
return | |||
} | |||
} | |||
//check param | |||
if err := grampusParamCheckCreateTrainJob(form); err != nil { | |||
log.Error("paramCheckCreateTrainJob failed:(%v)", err) | |||
grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeNPU) | |||
ctx.RenderWithErr(err.Error(), tplGrampusTrainJobNPUNew, &form) | |||
return | |||
} | |||
//check whether the task name in the project is duplicated | |||
tasks, err := models.GetCloudbrainsByDisplayJobName(repo.ID, string(models.JobTypeTrain), displayJobName) | |||
if err == nil { | |||
if len(tasks) != 0 { | |||
log.Error("the job name did already exist", ctx.Data["MsgID"]) | |||
grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeNPU) | |||
ctx.RenderWithErr("the job name did already exist", tplGrampusTrainJobNPUNew, &form) | |||
return | |||
} | |||
} else { | |||
if !models.IsErrJobNotExist(err) { | |||
log.Error("system error, %v", err, ctx.Data["MsgID"]) | |||
grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeNPU) | |||
ctx.RenderWithErr("system error", tplGrampusTrainJobNPUNew, &form) | |||
return | |||
} | |||
} | |||
//check dataset | |||
attachment, err := models.GetAttachmentByUUID(uuid) | |||
if err != nil { | |||
log.Error("GetAttachmentByUUID failed:", err.Error(), ctx.Data["MsgID"]) | |||
grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeNPU) | |||
ctx.RenderWithErr("dataset is not exist", tplGrampusTrainJobNPUNew, &form) | |||
return | |||
} | |||
//prepare code and out path | |||
_, err = ioutil.ReadDir(codeLocalPath) | |||
if err == nil { | |||
os.RemoveAll(codeLocalPath) | |||
} | |||
if err := downloadZipCode(ctx, codeLocalPath, branchName); err != nil { | |||
log.Error("downloadZipCode failed, server timed out: %s (%v)", repo.FullName(), err) | |||
grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeNPU) | |||
ctx.RenderWithErr("Create task failed, server timed out", tplGrampusTrainJobNPUNew, &form) | |||
return | |||
} | |||
//todo: upload code (send to file_server todo this work?) | |||
if err := obsMkdir(setting.CodePathPrefix + jobName + modelarts.OutputPath); err != nil { | |||
log.Error("Failed to obsMkdir_output: %s (%v)", repo.FullName(), err) | |||
grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeNPU) | |||
ctx.RenderWithErr("Failed to obsMkdir_output", tplGrampusTrainJobNPUNew, &form) | |||
return | |||
} | |||
if err := uploadCodeToObs(codeLocalPath, jobName, ""); err != nil { | |||
log.Error("Failed to uploadCodeToObs: %s (%v)", repo.FullName(), err) | |||
grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeNPU) | |||
ctx.RenderWithErr("Failed to uploadCodeToObs", tplGrampusTrainJobNPUNew, &form) | |||
return | |||
} | |||
//prepare command | |||
command, err := generateCommand(repo.Name, grampus.ProcessorTypeNPU, codeObsPath+cloudbrain.DefaultBranchName+".zip", dataObsPath+attachment.Name, bootFile, params, setting.CodePathPrefix+jobName+modelarts.OutputPath, attachment.Name) | |||
if err != nil { | |||
log.Error("Failed to generateCommand: %s (%v)", displayJobName, err, ctx.Data["MsgID"]) | |||
grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeNPU) | |||
ctx.RenderWithErr("Create task failed, internal error", tplGrampusTrainJobNPUNew, &form) | |||
return | |||
} | |||
commitID, _ := ctx.Repo.GitRepo.GetBranchCommitID(branchName) | |||
req := &grampus.GenerateTrainJobReq{ | |||
JobName: jobName, | |||
DisplayJobName: displayJobName, | |||
ComputeResource: models.NPUResource, | |||
Command: command, | |||
ResourceSpecId: form.FlavorID, | |||
ImageId: form.ImageID, | |||
DataUrl: dataObsPath, | |||
Description: description, | |||
CodeObsPath: codeObsPath, | |||
BootFileUrl: codeObsPath + bootFile, | |||
BootFile: bootFile, | |||
WorkServerNumber: form.WorkServerNumber, | |||
Uuid: uuid, | |||
CommitID: commitID, | |||
IsLatestVersion: isLatestVersion, | |||
BranchName: branchName, | |||
Params: form.Params, | |||
FlavorName: flavorName, | |||
EngineName: engineName, | |||
VersionCount: versionCount, | |||
TotalVersionCount: modelarts.TotalVersionCount, | |||
DatasetName: attachment.Name, | |||
} | |||
err = grampus.GenerateTrainJob(ctx, req) | |||
if err != nil { | |||
log.Error("GenerateTrainJob failed:%v", err.Error()) | |||
grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeNPU) | |||
ctx.RenderWithErr(err.Error(), tplGrampusTrainJobNPUNew, &form) | |||
return | |||
} | |||
ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/train-job") | |||
} | |||
func GrampusStopJob(ctx *context.Context) { | |||
var ID = ctx.Params(":jobid") | |||
var resultCode = "0" | |||
var errorMsg = "" | |||
var status = "" | |||
task := ctx.Cloudbrain | |||
for { | |||
if task.Status == string(models.GrampusStatusStopped) || task.Status == string(models.GrampusStatusFailed) || task.Status == string(models.GrampusStatusSucceeded) { | |||
log.Error("the job(%s) has been stopped", task.JobName, ctx.Data["msgID"]) | |||
resultCode = "-1" | |||
errorMsg = "system error" | |||
break | |||
} | |||
res, err := grampus.StopJob(task.JobID) | |||
if err != nil { | |||
log.Error("StopJob(%s) failed:%v", task.JobName, err, ctx.Data["msgID"]) | |||
resultCode = strconv.Itoa(res.ErrorCode) | |||
errorMsg = res.ErrorMsg | |||
break | |||
} | |||
task.Status = string(models.GrampusStatusStopped) | |||
if task.EndTime == 0 { | |||
task.EndTime = timeutil.TimeStampNow() | |||
} | |||
task.ComputeAndSetDuration() | |||
err = models.UpdateJob(task) | |||
if err != nil { | |||
log.Error("UpdateJob(%s) failed:%v", task.JobName, err, ctx.Data["msgID"]) | |||
resultCode = "-1" | |||
errorMsg = "system error" | |||
break | |||
} | |||
status = task.Status | |||
break | |||
} | |||
ctx.JSON(200, map[string]interface{}{ | |||
"result_code": resultCode, | |||
"error_msg": errorMsg, | |||
"status": status, | |||
"id": ID, | |||
"StatusOK": 0, | |||
}) | |||
} | |||
func GrampusTrainJobDel(ctx *context.Context) { | |||
var listType = ctx.Query("listType") | |||
if err := deleteGrampusJob(ctx); err != nil { | |||
log.Error("deleteGrampusJob failed: %v", err, ctx.Data["msgID"]) | |||
ctx.ServerError(err.Error(), err) | |||
return | |||
} | |||
var isAdminPage = ctx.Query("isadminpage") | |||
var isHomePage = ctx.Query("ishomepage") | |||
if ctx.IsUserSiteAdmin() && isAdminPage == "true" { | |||
ctx.Redirect(setting.AppSubURL + "/admin" + "/cloudbrains") | |||
} else if isHomePage == "true" { | |||
ctx.Redirect(setting.AppSubURL + "/cloudbrains") | |||
} else { | |||
ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/train-job?listType=" + listType) | |||
} | |||
} | |||
func deleteGrampusJob(ctx *context.Context) error { | |||
task := ctx.Cloudbrain | |||
if task.Status != string(models.GrampusStatusStopped) && task.Status != string(models.GrampusStatusSucceeded) && task.Status != string(models.GrampusStatusFailed) { | |||
log.Error("the job(%s) has not been stopped", task.JobName, ctx.Data["msgID"]) | |||
return errors.New("the job has not been stopped") | |||
} | |||
err := models.DeleteJob(task) | |||
if err != nil { | |||
log.Error("DeleteJob failed: %v", err, ctx.Data["msgID"]) | |||
return err | |||
} | |||
storageType := models.TypeCloudBrainOne | |||
if task.ComputeResource == models.NPUResource { | |||
storageType = models.TypeCloudBrainTwo | |||
} | |||
deleteJobStorage(task.JobName, storageType) | |||
return nil | |||
} | |||
func GrampusTrainJobShow(ctx *context.Context) { | |||
ctx.Data["PageIsCloudBrain"] = true | |||
var task *models.Cloudbrain | |||
task, err := models.GetCloudbrainByJobIDWithDeleted(ctx.Params(":jobid")) | |||
if err != nil { | |||
log.Error("GetCloudbrainByJobID failed:" + err.Error()) | |||
ctx.ServerError("system error", err) | |||
return | |||
} | |||
if task.DeletedAt.IsZero() { //normal record | |||
result, err := grampus.GetJob(task.JobID) | |||
if err != nil { | |||
log.Error("GetJob failed:" + err.Error()) | |||
//ctx.ServerError("GetJob failed", err) | |||
//return | |||
} | |||
if result != nil { | |||
if len(result.JobInfo.Tasks[0].CenterID) == 1 && len(result.JobInfo.Tasks[0].CenterName) == 1 { | |||
task.AiCenter = result.JobInfo.Tasks[0].CenterID[0] + "+" + result.JobInfo.Tasks[0].CenterName[0] | |||
} | |||
task.Status = grampus.TransTrainJobStatus(result.JobInfo.Status) | |||
if task.Status != result.JobInfo.Status || result.JobInfo.Status == models.GrampusStatusRunning { | |||
task.Duration = result.JobInfo.RunSec | |||
task.TrainJobDuration = models.ConvertDurationToStr(task.Duration) | |||
if task.StartTime == 0 && result.JobInfo.StartedAt > 0 { | |||
task.StartTime = timeutil.TimeStamp(result.JobInfo.StartedAt) | |||
} | |||
if task.EndTime == 0 && models.IsTrainJobTerminal(task.Status) && task.StartTime > 0 { | |||
task.EndTime = task.StartTime.Add(task.Duration) | |||
} | |||
task.CorrectCreateUnix() | |||
err = models.UpdateJob(task) | |||
if err != nil { | |||
log.Error("UpdateJob failed:" + err.Error()) | |||
} | |||
} | |||
} | |||
} | |||
if len(task.Parameters) > 0 { | |||
var parameters models.Parameters | |||
err := json.Unmarshal([]byte(task.Parameters), ¶meters) | |||
if err != nil { | |||
log.Error("Failed to Unmarshal Parameters: %s (%v)", task.Parameters, err) | |||
ctx.ServerError("system error", err) | |||
return | |||
} | |||
if len(parameters.Parameter) > 0 { | |||
paramTemp := "" | |||
for _, Parameter := range parameters.Parameter { | |||
param := Parameter.Label + " = " + Parameter.Value + "; " | |||
paramTemp = paramTemp + param | |||
} | |||
task.Parameters = paramTemp[:len(paramTemp)-2] | |||
} else { | |||
task.Parameters = "" | |||
} | |||
} | |||
taskList := make([]*models.Cloudbrain, 0) | |||
taskList = append(taskList, task) | |||
ctx.Data["version_list_task"] = taskList | |||
ctx.Data["canDownload"] = cloudbrain.CanModifyJob(ctx, task) | |||
ctx.Data["displayJobName"] = task.DisplayJobName | |||
aiCenterInfo := strings.Split(task.AiCenter, "+") | |||
if len(aiCenterInfo) == 2 { | |||
ctx.Data["ai_center"] = aiCenterInfo[1] | |||
} | |||
ctx.HTML(http.StatusOK, tplGrampusTrainJobShow) | |||
} | |||
func GrampusGetLog(ctx *context.Context) { | |||
jobID := ctx.Params(":jobid") | |||
job, err := models.GetCloudbrainByJobID(jobID) | |||
if err != nil { | |||
log.Error("GetCloudbrainByJobID failed: %v", err, ctx.Data["MsgID"]) | |||
ctx.ServerError(err.Error(), err) | |||
return | |||
} | |||
content, err := grampus.GetTrainJobLog(job.JobID) | |||
if err != nil { | |||
log.Error("GetTrainJobLog failed: %v", err, ctx.Data["MsgID"]) | |||
ctx.ServerError(err.Error(), err) | |||
return | |||
} | |||
ctx.JSON(http.StatusOK, map[string]interface{}{ | |||
"JobName": job.JobName, | |||
"Content": content, | |||
}) | |||
return | |||
} | |||
func generateCommand(repoName, processorType, codeRemotePath, dataRemotePath, bootFile, paramSrc, outputRemotePath, datasetName string) (string, error) { | |||
var command string | |||
workDir := grampus.NpuWorkDir | |||
if processorType == grampus.ProcessorTypeGPU { | |||
workDir = grampus.GpuWorkDir | |||
} | |||
command += "pwd;cd " + workDir + grampus.CommandPrepareScript | |||
//download code & dataset | |||
if processorType == grampus.ProcessorTypeNPU { | |||
commandDownload := "./downloader_for_obs " + setting.Bucket + " " + codeRemotePath + " " + grampus.CodeArchiveName + " " + dataRemotePath + " " + datasetName + ";" | |||
command += commandDownload | |||
} else if processorType == grampus.ProcessorTypeGPU { | |||
commandDownload := "./downloader_for_minio " + setting.Grampus.Env + " " + codeRemotePath + " " + grampus.CodeArchiveName + " " + dataRemotePath + " " + datasetName + ";" | |||
command += commandDownload | |||
} | |||
//check download result | |||
commandCheckRes := "bash -c \"[[ $? -eq 0 ]] && exit 0 || exit -1;\";" | |||
command += commandCheckRes | |||
//unzip code & dataset | |||
toolUnzip := "unzip -q " | |||
if strings.HasSuffix(datasetName, ".tar.gz") { | |||
toolUnzip = "tar -zxvf " | |||
} | |||
commandUnzip := "cd " + workDir + "code;unzip -q master.zip;echo \"start to unzip dataset\";cd " + workDir + "dataset;" + toolUnzip + datasetName + ";" | |||
command += commandUnzip | |||
//check unzip result | |||
commandCheckRes = "bash -c \"[[ $? -eq 0 ]] && exit 0 || exit -1;\";" | |||
command += commandCheckRes | |||
command += "echo \"unzip finished;start to exec code;\";" | |||
//exec code | |||
var parameters models.Parameters | |||
var paramCode string | |||
param := make([]models.Parameter, 0) | |||
if len(paramSrc) != 0 { | |||
err := json.Unmarshal([]byte(paramSrc), ¶meters) | |||
if err != nil { | |||
log.Error("Failed to Unmarshal params: %s (%v)", paramSrc, err) | |||
return command, err | |||
} | |||
for _, parameter := range parameters.Parameter { | |||
param = append(param, models.Parameter{ | |||
Label: parameter.Label, | |||
Value: parameter.Value, | |||
}) | |||
paramCode += " --" + parameter.Label + "=" + parameter.Value | |||
} | |||
} | |||
commandCode := "cd " + workDir + "code/" + strings.ToLower(repoName) + ";python " + bootFile + paramCode + ";" | |||
command += commandCode | |||
//get exec result | |||
commandGetRes := "result=$?;" | |||
command += commandGetRes | |||
//upload models | |||
if processorType == grampus.ProcessorTypeNPU { | |||
commandUpload := "cd " + workDir + "script_for_grampus/;./uploader_for_obs " + setting.Bucket + " " + outputRemotePath + " " + workDir + "output/;" | |||
command += commandUpload | |||
} else if processorType == grampus.ProcessorTypeGPU { | |||
commandUpload := "cd " + workDir + "script_for_grampus/;./uploader_for_minio " + setting.Grampus.Env + " " + outputRemotePath + " " + workDir + "output/;" | |||
command += commandUpload | |||
} | |||
//check exec result | |||
commandCheckRes = "bash -c \"[[ $result -eq 0 ]] && exit 0 || exit -1;\"" | |||
command += commandCheckRes | |||
return command, nil | |||
} | |||
func downloadZipCode(ctx *context.Context, codePath, branchName string) error { | |||
archiveType := git.ZIP | |||
archivePath := codePath | |||
if !com.IsDir(archivePath) { | |||
if err := os.MkdirAll(archivePath, os.ModePerm); err != nil { | |||
log.Error("MkdirAll failed:" + err.Error()) | |||
return err | |||
} | |||
} | |||
// Get corresponding commit. | |||
var ( | |||
commit *git.Commit | |||
err error | |||
) | |||
gitRepo := ctx.Repo.GitRepo | |||
if err != nil { | |||
log.Error("OpenRepository failed:" + err.Error()) | |||
return err | |||
} | |||
if gitRepo.IsBranchExist(branchName) { | |||
commit, err = gitRepo.GetBranchCommit(branchName) | |||
if err != nil { | |||
log.Error("GetBranchCommit failed:" + err.Error()) | |||
return err | |||
} | |||
} | |||
archivePath = path.Join(archivePath, grampus.CodeArchiveName) | |||
if !com.IsFile(archivePath) { | |||
if err := commit.CreateArchive(archivePath, git.CreateArchiveOpts{ | |||
Format: archiveType, | |||
Prefix: setting.Repository.PrefixArchiveFiles, | |||
}); err != nil { | |||
log.Error("CreateArchive failed:" + err.Error()) | |||
return err | |||
} | |||
} | |||
return nil | |||
} |
@@ -147,7 +147,7 @@ func notebookNewDataPrepare(ctx *context.Context) error { | |||
} | |||
ctx.Data["flavors"] = modelarts.FlavorInfos.FlavorInfo | |||
ctx.Data["cloudbraintype"] = models.TypeCloudBrainTwo | |||
ctx.Data["datasetType"] = models.TypeCloudBrainTwo | |||
return nil | |||
} | |||
@@ -573,24 +573,11 @@ func TrainJobIndex(ctx *context.Context) { | |||
} | |||
listType := ctx.Query("listType") | |||
if len(listType) == 0 { | |||
listType = models.AllResource | |||
} | |||
ctx.Data["ListType"] = listType | |||
typeCloudBrain := models.TypeCloudBrainAll | |||
if listType == models.GPUResource { | |||
typeCloudBrain = models.TypeCloudBrainOne | |||
} else if listType == models.NPUResource { | |||
typeCloudBrain = models.TypeCloudBrainTwo | |||
} else if listType == models.AllResource { | |||
typeCloudBrain = models.TypeCloudBrainAll | |||
if listType == models.AllResource { | |||
listType = "" | |||
} | |||
//else { | |||
// log.Error("listType(%s) error", listType) | |||
// ctx.ServerError("listType error", errors.New("listType error")) | |||
// return | |||
//} | |||
var jobTypes []string | |||
jobTypes = append(jobTypes, string(models.JobTypeTrain)) | |||
@@ -600,10 +587,11 @@ func TrainJobIndex(ctx *context.Context) { | |||
PageSize: setting.UI.IssuePagingNum, | |||
}, | |||
RepoID: repo.ID, | |||
Type: typeCloudBrain, | |||
JobTypeNot: false, | |||
JobTypes: jobTypes, | |||
IsLatestVersion: modelarts.IsLatestVersion, | |||
ComputeResource: listType, | |||
Type: models.TypeCloudBrainAll, | |||
}) | |||
if err != nil { | |||
ctx.ServerError("Cloudbrain", err) | |||
@@ -613,11 +601,6 @@ func TrainJobIndex(ctx *context.Context) { | |||
for i, task := range tasks { | |||
tasks[i].CanDel = cloudbrain.CanDeleteJob(ctx, &task.Cloudbrain) | |||
tasks[i].CanModify = cloudbrain.CanModifyJob(ctx, &task.Cloudbrain) | |||
if task.Cloudbrain.Type == models.TypeCloudBrainOne { | |||
tasks[i].ComputeResource = models.GPUResource | |||
} else if task.Cloudbrain.Type == models.TypeCloudBrainTwo { | |||
tasks[i].ComputeResource = models.NPUResource | |||
} | |||
} | |||
pager := context.NewPagination(int(count), setting.UI.IssuePagingNum, page, 5) | |||
@@ -704,7 +687,7 @@ func trainJobNewDataPrepare(ctx *context.Context) error { | |||
return err | |||
} | |||
ctx.Data["config_list"] = configList.ParaConfigs | |||
ctx.Data["cloudbraintype"] = models.TypeCloudBrainTwo | |||
ctx.Data["datasetType"] = models.TypeCloudBrainTwo | |||
return nil | |||
} | |||
@@ -778,7 +761,7 @@ func trainJobErrorNewDataPrepare(ctx *context.Context, form auth.CreateModelArts | |||
ctx.Data["bootFile"] = form.BootFile | |||
ctx.Data["uuid"] = form.Attachment | |||
ctx.Data["branch_name"] = form.BranchName | |||
ctx.Data["cloudbraintype"] = models.TypeCloudBrainTwo | |||
ctx.Data["datasetType"] = models.TypeCloudBrainTwo | |||
return nil | |||
} | |||
@@ -872,7 +855,7 @@ func trainJobNewVersionDataPrepare(ctx *context.Context) error { | |||
ctx.Data["uuid"] = task.Uuid | |||
ctx.Data["flavor_code"] = task.FlavorCode | |||
ctx.Data["engine_id"] = task.EngineID | |||
ctx.Data["cloudbraintype"] = models.TypeCloudBrainTwo | |||
ctx.Data["datasetType"] = models.TypeCloudBrainTwo | |||
configList, err := getConfigList(modelarts.PerPage, 1, modelarts.SortByCreateTime, "desc", "", modelarts.ConfigTypeCustom) | |||
if err != nil { | |||
@@ -969,7 +952,7 @@ func versionErrorDataPrepare(ctx *context.Context, form auth.CreateModelArtsTrai | |||
return err | |||
} | |||
ctx.Data["config_list"] = configList.ParaConfigs | |||
ctx.Data["cloudbraintype"] = models.TypeCloudBrainTwo | |||
ctx.Data["datasetType"] = models.TypeCloudBrainTwo | |||
return nil | |||
} | |||
@@ -2136,7 +2119,7 @@ func inferenceJobNewDataPrepare(ctx *context.Context) error { | |||
New: MODEL_LATEST, | |||
}) | |||
ctx.Data["MODEL_COUNT"] = model_count | |||
ctx.Data["cloudbraintype"] = models.TypeCloudBrainTwo | |||
ctx.Data["datasetType"] = models.TypeCloudBrainTwo | |||
return nil | |||
} | |||
@@ -2202,7 +2185,7 @@ func inferenceJobErrorNewDataPrepare(ctx *context.Context, form auth.CreateModel | |||
ctx.Data["model_version"] = form.ModelVersion | |||
ctx.Data["ckpt_name"] = form.CkptName | |||
ctx.Data["train_url"] = form.TrainUrl | |||
ctx.Data["cloudbraintype"] = models.TypeCloudBrainTwo | |||
ctx.Data["datasetType"] = models.TypeCloudBrainTwo | |||
return nil | |||
} | |||
@@ -2272,24 +2255,35 @@ func ModelDownload(ctx *context.Context) { | |||
err error | |||
) | |||
var jobID = ctx.Params(":jobid") | |||
jobID := ctx.Params(":jobid") | |||
versionName := ctx.Query("version_name") | |||
parentDir := ctx.Query("parent_dir") | |||
fileName := ctx.Query("file_name") | |||
task, err := models.GetCloudbrainByJobIDAndVersionName(jobID, versionName) | |||
if err != nil { | |||
log.Error("GetCloudbrainByJobID(%s) failed:%v", task.JobName, err.Error()) | |||
log.Error("GetCloudbrainByJobIDAndVersionName(%s) failed:%v", task.JobName, err.Error()) | |||
return | |||
} | |||
path := strings.TrimPrefix(path.Join(setting.TrainJobModelPath, task.JobName, setting.OutPutPath, versionName, parentDir, fileName), "/") | |||
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 | |||
var url string | |||
if task.ComputeResource == models.NPUResource { | |||
path := strings.TrimPrefix(path.Join(setting.TrainJobModelPath, task.JobName, setting.OutPutPath, versionName, parentDir, fileName), "/") | |||
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 | |||
} | |||
} else if task.ComputeResource == models.GPUResource { | |||
filePath := setting.CBCodePathPrefix + task.JobName + cloudbrain.ModelMountPath + "/" + parentDir | |||
url, err = storage.Attachments.PresignedGetURL(filePath, fileName) | |||
if err != nil { | |||
log.Error("PresignedGetURL failed: %v", err.Error(), ctx.Data["msgID"]) | |||
ctx.ServerError("PresignedGetURL", err) | |||
return | |||
} | |||
} | |||
ctx.Resp.Header().Set("Cache-Control", "max-age=0") | |||
http.Redirect(ctx.Resp, ctx.Req.Request, url, http.StatusMovedPermanently) | |||
} | |||
@@ -1103,6 +1103,24 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
m.Post("/create", reqWechatBind, reqRepoCloudBrainWriter, bindIgnErr(auth.CreateCloudBrainForm{}), repo.CloudBrainCreate) | |||
}) | |||
}, context.RepoRef()) | |||
m.Group("/grampus", func() { | |||
m.Group("/train-job", func() { | |||
m.Group("/:jobid", func() { | |||
m.Get("", reqRepoCloudBrainReader, repo.GrampusTrainJobShow) | |||
m.Post("/stop", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.GrampusStopJob) | |||
m.Post("/del", cloudbrain.AdminOrOwnerOrJobCreaterRightForTrain, repo.GrampusTrainJobDel) | |||
m.Get("/model_download", cloudbrain.AdminOrJobCreaterRightForTrain, repo.ModelDownload) | |||
}) | |||
m.Group("/gpu", func() { | |||
m.Get("/create", reqWechatBind, reqRepoCloudBrainWriter, repo.GrampusTrainJobGPUNew) | |||
m.Post("/create", reqWechatBind, reqRepoCloudBrainWriter, bindIgnErr(auth.CreateGrampusTrainJobForm{}), repo.GrampusTrainJobGpuCreate) | |||
}) | |||
m.Group("/npu", func() { | |||
m.Get("/create", reqWechatBind, reqRepoCloudBrainWriter, repo.GrampusTrainJobNPUNew) | |||
m.Post("/create", reqWechatBind, reqRepoCloudBrainWriter, bindIgnErr(auth.CreateGrampusTrainJobForm{}), repo.GrampusTrainJobNpuCreate) | |||
}) | |||
}) | |||
}, context.RepoRef()) | |||
m.Group("/modelmanage", func() { | |||
m.Post("/create_model", reqRepoModelManageWriter, repo.SaveModel) | |||
m.Post("/create_new_model", repo.SaveNewNameModel) | |||
@@ -769,12 +769,6 @@ func Cloudbrains(ctx *context.Context) { | |||
if page <= 0 { | |||
page = 1 | |||
} | |||
debugType := models.TypeCloudBrainAll | |||
if listType == models.GPUResource { | |||
debugType = models.TypeCloudBrainOne | |||
} else if listType == models.NPUResource { | |||
debugType = models.TypeCloudBrainTwo | |||
} | |||
var jobTypes []string | |||
jobTypeNot := false | |||
@@ -821,7 +815,6 @@ func Cloudbrains(ctx *context.Context) { | |||
}, | |||
Keyword: keyword, | |||
UserID: ctxUser.ID, | |||
Type: debugType, | |||
JobTypeNot: jobTypeNot, | |||
JobStatusNot: jobStatusNot, | |||
JobStatus: jobStatuses, | |||
@@ -829,6 +822,8 @@ func Cloudbrains(ctx *context.Context) { | |||
NeedRepoInfo: true, | |||
IsLatestVersion: modelarts.IsLatestVersion, | |||
RepoIDList: repoIDList, | |||
ComputeResource: listType, | |||
Type: models.TypeCloudBrainAll, | |||
}) | |||
if err != nil { | |||
ctx.ServerError("Get job failed:", err) | |||
@@ -10,7 +10,7 @@ import ( | |||
"github.com/elliotchance/orderedmap" | |||
) | |||
var opTypes = []int{1, 2, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31} | |||
var opTypes = []int{1, 2, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33} | |||
type ClientsManager struct { | |||
Clients *orderedmap.OrderedMap | |||
@@ -102,7 +102,7 @@ | |||
</a> | |||
{{else if eq .JobType "TRAIN"}} | |||
<a class="title" | |||
href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts{{end}}/train-job/{{$JobID}}" | |||
href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .Cloudbrain.Type 0}}/cloudbrain{{else if eq .Cloudbrain.Type 1}}/modelarts{{else if eq .Cloudbrain.Type 2}}/grampus{{end}}/train-job/{{$JobID}}" | |||
title="{{.DisplayJobName}}" style="font-size: 14px;"> | |||
<span class="fitted" | |||
style="width: 90%;vertical-align: middle;">{{.DisplayJobName}}</span> | |||
@@ -204,7 +204,7 @@ | |||
{{else}} | |||
<a style="padding: 0.5rem 1rem;" id="ai-stop-{{$JobID}}" | |||
class="ui basic ai_stop_version {{if eq .Status "KILLED" "FAILED" "START_FAILED" "KILLING" "COMPLETED" "SUCCEEDED" "STOPPED"}}disabled {{else}} blue {{end}}button" | |||
data-repopath="{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain/train-job{{else}}/modelarts/{{if eq .JobType "INFERENCE"}}inference-job{{else}}train-job{{end}}{{end}}" | |||
data-repopath="{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .Cloudbrain.Type 0}}/cloudbrain/train-job{{else if eq .Cloudbrain.Type 2}}/grampus/train-job{{else}}/modelarts/{{if eq .JobType "INFERENCE"}}inference-job{{else}}train-job{{end}}{{end}}" | |||
data-jobid="{{$JobID}}" data-version="{{.VersionName}}"> | |||
{{$.i18n.Tr "repo.stop"}} | |||
</a> | |||
@@ -212,7 +212,7 @@ | |||
</div> | |||
<!-- 删除任务 --> | |||
<form class="ui compact buttons" id="delForm-{{$JobID}}" | |||
action='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .JobType "BENCHMARK"}}/cloudbrain/benchmark{{else if or (eq .JobType "SNN4IMAGENET") (eq .JobType "BRAINSCORE")}}/cloudbrain{{else if eq .JobType "DEBUG"}}{{if eq .ComputeResource "NPU"}}/modelarts/notebook{{else}}/cloudbrain{{end}}{{else if eq .JobType "TRAIN"}}{{if eq .ComputeResource "NPU"}}/modelarts/notebook{{else}}/cloudbrain{{end}}/train-job{{end}}/{{$JobID}}/del?isadminpage=true' | |||
action='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .JobType "BENCHMARK"}}/cloudbrain/benchmark{{else if or (eq .JobType "SNN4IMAGENET") (eq .JobType "BRAINSCORE")}}/cloudbrain{{else if eq .JobType "DEBUG"}}{{if eq .ComputeResource "NPU"}}/modelarts/notebook{{else}}/cloudbrain{{end}}{{else if eq .JobType "TRAIN"}}{{if eq .Cloudbrain.Type 1}}/modelarts/train-job{{else if eq .Cloudbrain.Type 0}}/cloudbrain/train-job{{else if eq .Cloudbrain.Type 2}}/grampus/train-job{{end}}{{end}}/{{$JobID}}/del?isadminpage=true' | |||
method="post"> | |||
{{$.CsrfTokenHtml}} | |||
<a style="padding: 0.5rem 1rem;margin-left:0.2rem" id="ai-delete-{{$JobID}}" | |||
@@ -1,9 +1,9 @@ | |||
<div class="dataset-repolink" id="dataset-repolink-init" style="display: none;" data-repolink="{{.RepoLink}}" | |||
data-cloudranin-type="{{.cloudbraintype}}"></div> | |||
<div class="inline {{if eq .cloudbraintype 0}} required {{end}} field" id="dataset-base"> | |||
data-dataset-type="{{.datasetType}}"></div> | |||
<div class="inline {{if eq .datasetType 0}} required {{end}} field" id="dataset-base"> | |||
<label>{{.i18n.Tr "dataset.dataset"}}</label> | |||
<input type="hidden" name="attachment" :value="dataset_uuid"> | |||
{{if eq .cloudbraintype 0}} | |||
{{if eq .datasetType 0}} | |||
<input class="disabled" type="text" :value="dataset_name" placeholder="{{.i18n.Tr "cloudbrain.select_dataset"}}" | |||
required onfocus="this.blur();"> | |||
{{else}} | |||
@@ -18,7 +18,7 @@ | |||
<input type="text" placeholder="{{.i18n.Tr "dataset.search_dataset"}}" v-model="searchDataItem"> | |||
</div> | |||
<el-tabs v-model="activeName" @tab-click="handleClick('{{.RepoLink}}',activeName,{{.cloudbraintype}})"> | |||
<el-tabs v-model="activeName" @tab-click="handleClick('{{.RepoLink}}',activeName,{{.datasetType}})"> | |||
<el-tab-pane label="{{.i18n.Tr "dataset.current_project"}}" name="first"> | |||
<div style="display: flex;align-items: center;justify-content: space-between;padding: 1rem 0;border-bottom:1px solid #F5F5F5" | |||
v-for="(dataset,index) in currentRepoDataset" :key="index"> | |||
@@ -1,5 +1,5 @@ | |||
<div class="dataset-repolink" id="dataset-repolink-init" style="display: none;" data-repolink="{{.RepoLink}}" | |||
data-cloudranin-type="{{.cloudbraintype}}"></div> | |||
data-dataset-type="{{.datasetType}}"></div> | |||
<div class="inline required unite min_title field" id="dataset-base" style="margin-bottom: 0 !important;"> | |||
{{if or (.benchmarkMode) (.newInference)}} | |||
<label | |||
@@ -22,7 +22,7 @@ | |||
<input type="text" placeholder="{{.i18n.Tr "dataset.search_dataset"}}" v-model="searchDataItem"> | |||
</div> | |||
<el-tabs v-model="activeName" @tab-click="handleClick('{{.RepoLink}}',activeName,{{.cloudbraintype}})"> | |||
<el-tabs v-model="activeName" @tab-click="handleClick('{{.RepoLink}}',activeName,{{.datasetType}})"> | |||
<el-tab-pane label="{{.i18n.Tr "dataset.current_project"}}" name="first" v-loading="loadingDataIndex"> | |||
<div style="display: flex;align-items: center;justify-content: space-between;padding: 1rem 0;border-bottom:1px solid #F5F5F5" | |||
v-for="(dataset,index) in currentRepoDataset" :key="index"> | |||
@@ -89,6 +89,19 @@ | |||
<input type="hidden" id="ai_flaver_name" name="flaver_names" value=""> | |||
<h4 class="unite title ui header ">{{.i18n.Tr "repo.modelarts.train_job.basic_info"}}:</h4> | |||
<div class="required unite min_title inline field"> | |||
<label style="font-weight: normal;">{{.i18n.Tr "cloudbrain.resource_cluster"}}</label> | |||
<div class="ui blue mini menu compact selectcloudbrain"> | |||
<a class="active item" href="{{.RepoLink}}/cloudbrain/train-job/create"> | |||
<svg class="svg" sxmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="none" d="M0 0h24v24H0z"></path><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-2.29-2.333A17.9 17.9 0 0 1 8.027 13H4.062a8.008 8.008 0 0 0 5.648 6.667zM10.03 13c.151 2.439.848 4.73 1.97 6.752A15.905 15.905 0 0 0 13.97 13h-3.94zm9.908 0h-3.965a17.9 17.9 0 0 1-1.683 6.667A8.008 8.008 0 0 0 19.938 13zM4.062 11h3.965A17.9 17.9 0 0 1 9.71 4.333 8.008 8.008 0 0 0 4.062 11zm5.969 0h3.938A15.905 15.905 0 0 0 12 4.248 15.905 15.905 0 0 0 10.03 11zm4.259-6.667A17.9 17.9 0 0 1 15.973 11h3.965a8.008 8.008 0 0 0-5.648-6.667z"></path></svg> | |||
{{.i18n.Tr "cloudbrain.resource_cluster_openi"}} | |||
</a> | |||
<a class="item" href="{{.RepoLink}}/grampus/train-job/npu/create"> | |||
<svg class="svg" sxmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="none" d="M0 0h24v24H0z"></path><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-2.29-2.333A17.9 17.9 0 0 1 8.027 13H4.062a8.008 8.008 0 0 0 5.648 6.667zM10.03 13c.151 2.439.848 4.73 1.97 6.752A15.905 15.905 0 0 0 13.97 13h-3.94zm9.908 0h-3.965a17.9 17.9 0 0 1-1.683 6.667A8.008 8.008 0 0 0 19.938 13zM4.062 11h3.965A17.9 17.9 0 0 1 9.71 4.333 8.008 8.008 0 0 0 4.062 11zm5.969 0h3.938A15.905 15.905 0 0 0 12 4.248 15.905 15.905 0 0 0 10.03 11zm4.259-6.667A17.9 17.9 0 0 1 15.973 11h3.965a8.008 8.008 0 0 0-5.648-6.667z"></path></svg> | |||
{{.i18n.Tr "cloudbrain.resource_cluster_c2net"}}(Beta) | |||
</a> | |||
</div> | |||
</div> | |||
<div class="required unite min_title inline field"> | |||
<label style="font-weight: normal;">{{.i18n.Tr "cloudbrain.compute_resource"}}</label> | |||
<div class="ui blue mini menu compact selectcloudbrain"> | |||
<a class="active item" href="{{.RepoLink}}/cloudbrain/train-job/create"> | |||
@@ -0,0 +1,438 @@ | |||
{{template "base/head" .}} | |||
<style> | |||
.unite{ | |||
font-family: SourceHanSansSC-medium !important; | |||
color: rgba(16, 16, 16, 100) !important; | |||
} | |||
.title{ | |||
font-size: 16px !important; | |||
padding-left: 3rem !important; | |||
} | |||
.min_title{ | |||
font-size: 14px !important; | |||
padding-left: 6rem !important; | |||
margin-bottom: 2rem !important; | |||
} | |||
.width{ | |||
width:100% !important; | |||
} | |||
.width80{ | |||
width: 80.7% !important; | |||
margin-left: 10px; | |||
} | |||
.width806{ | |||
width: 80.6% !important; | |||
margin-left: -2px; | |||
} | |||
.width85{ | |||
width: 85% !important; | |||
margin-left: 4.5rem !important; | |||
} | |||
.width81{ | |||
margin-left: 1.5rem !important; | |||
width: 81% !important; | |||
} | |||
.add{font-size: 18px; | |||
padding: 0.5rem; | |||
border: 1px solid rgba(187, 187, 187, 100); | |||
border-radius: 0px 5px 5px 0px; | |||
line-height: 21px; | |||
text-align: center; | |||
color: #C2C7CC; | |||
} | |||
.min{ | |||
font-size: 18px; | |||
padding: 0.5rem; | |||
border: 1px solid rgba(187, 187, 187, 100); | |||
border-radius: 5px 0px 0px 5px; | |||
line-height: 21px; | |||
text-align: center; | |||
color: #C2C7CC; | |||
} | |||
</style> | |||
<!-- <div class="ui page dimmer"> | |||
<div class="ui text loader">{{.i18n.Tr "loading"}}</div> | |||
</div> --> | |||
<div id="mask"> | |||
<div id="loadingPage"> | |||
<div class="rect1"></div> | |||
<div class="rect2"></div> | |||
<div class="rect3"></div> | |||
<div class="rect4"></div> | |||
<div class="rect5"></div> | |||
</div> | |||
</div> | |||
<div class="repository"> | |||
{{template "repo/header" .}} | |||
<div class="ui container"> | |||
{{template "base/alert" .}} | |||
<h4 class="ui top attached header"> | |||
{{.i18n.Tr "repo.modelarts.train_job.new"}} | |||
</h4> | |||
<div class="ui attached segment"> | |||
<!-- equal width --> | |||
<form class="ui form" action="{{.Link}}" method="post"> | |||
{{.CsrfTokenHtml}} | |||
<input type="hidden" name="action" value="update"> | |||
<input type="hidden" id="ai_engine_name" name="engine_names" value=""> | |||
<input type="hidden" id="ai_flavor_name" name="flavor_name" value=""> | |||
<h4 class="unite title ui header ">{{.i18n.Tr "repo.modelarts.train_job.basic_info"}}:</h4> | |||
<div class="required unite min_title inline field"> | |||
<label style="font-weight: normal;">{{.i18n.Tr "cloudbrain.resource_cluster"}}</label> | |||
<div class="ui blue mini menu compact selectcloudbrain"> | |||
<a class="item" href="{{.RepoLink}}/cloudbrain/train-job/create"> | |||
<svg class="svg" sxmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="none" d="M0 0h24v24H0z"></path><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-2.29-2.333A17.9 17.9 0 0 1 8.027 13H4.062a8.008 8.008 0 0 0 5.648 6.667zM10.03 13c.151 2.439.848 4.73 1.97 6.752A15.905 15.905 0 0 0 13.97 13h-3.94zm9.908 0h-3.965a17.9 17.9 0 0 1-1.683 6.667A8.008 8.008 0 0 0 19.938 13zM4.062 11h3.965A17.9 17.9 0 0 1 9.71 4.333 8.008 8.008 0 0 0 4.062 11zm5.969 0h3.938A15.905 15.905 0 0 0 12 4.248 15.905 15.905 0 0 0 10.03 11zm4.259-6.667A17.9 17.9 0 0 1 15.973 11h3.965a8.008 8.008 0 0 0-5.648-6.667z"></path></svg> | |||
{{.i18n.Tr "cloudbrain.resource_cluster_openi"}} | |||
</a> | |||
<a class="active item" href="{{.RepoLink}}/grampus/train-job/ {{if.NPUEnabled}}npu{{else}}gpu{{end}}/create"> | |||
<svg class="svg" sxmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="none" d="M0 0h24v24H0z"></path><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-2.29-2.333A17.9 17.9 0 0 1 8.027 13H4.062a8.008 8.008 0 0 0 5.648 6.667zM10.03 13c.151 2.439.848 4.73 1.97 6.752A15.905 15.905 0 0 0 13.97 13h-3.94zm9.908 0h-3.965a17.9 17.9 0 0 1-1.683 6.667A8.008 8.008 0 0 0 19.938 13zM4.062 11h3.965A17.9 17.9 0 0 1 9.71 4.333 8.008 8.008 0 0 0 4.062 11zm5.969 0h3.938A15.905 15.905 0 0 0 12 4.248 15.905 15.905 0 0 0 10.03 11zm4.259-6.667A17.9 17.9 0 0 1 15.973 11h3.965a8.008 8.008 0 0 0-5.648-6.667z"></path></svg> | |||
{{.i18n.Tr "cloudbrain.resource_cluster_c2net"}}(Beta) | |||
</a> | |||
</div> | |||
</div> | |||
<div class="required unite min_title inline field"> | |||
<label style="font-weight: normal;">{{.i18n.Tr "cloudbrain.compute_resource"}}</label> | |||
<div class="ui blue mini menu compact selectcloudbrain"> | |||
<a {{if.GPUEnabled}}class="active item" href="{{.RepoLink}}/grampus/train-job/gpu/create"{{else}}href="javascript:return false;" class="item disabled" {{end}}> | |||
<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 2.992C3 2.444 3.445 2 3.993 2h16.014a1 1 0 0 1 .993.992v18.016a.993.993 0 0 1-.993.992H3.993A1 1 0 0 1 3 21.008V2.992zM19 11V4H5v7h14zm0 2H5v7h14v-7zM9 6h6v2H9V6zm0 9h6v2H9v-2z"/> | |||
</svg> | |||
CPU/GPU | |||
</a> | |||
<a {{if.NPUEnabled}}class="item" href="{{.RepoLink}}/grampus/train-job/npu/create"{{else}}href="javascript:return false;" class="item disabled" {{end}} > | |||
<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 2.992C3 2.444 3.445 2 3.993 2h16.014a1 1 0 0 1 .993.992v18.016a.993.993 0 0 1-.993.992H3.993A1 1 0 0 1 3 21.008V2.992zM19 11V4H5v7h14zm0 2H5v7h14v-7zM9 6h6v2H9V6zm0 9h6v2H9v-2z"/> | |||
</svg> | |||
Ascend NPU</a> | |||
</div> | |||
</div> | |||
<div class="required unite min_title inline field"> | |||
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.job_name"}}</label> | |||
<input style="width: 60%;" name="display_job_name" id="display_job_name" placeholder={{.i18n.Tr "repo.modelarts.train_job.job_name"}} value="{{.display_job_name}}" tabindex="3" onkeyup="this.value=this.value.replace(/[, ]/g,'')" autofocus required maxlength="64"> | |||
<span class="tooltips" style="display: block;">{{.i18n.Tr "cloudbrain.job_name_rule"}}</span> | |||
</div> | |||
<div class="unite min_title inline field"> | |||
<label style="font-weight: normal;" for="description">{{.i18n.Tr "repo.modelarts.train_job.description"}} </label> | |||
<textarea style="width: 80%;" id="description" name="description" rows="3" maxlength="255" placeholder={{.i18n.Tr "repo.modelarts.train_job.new_place"}} onchange="this.value=this.value.substring(0, 255)" onkeydown="this.value=this.value.substring(0, 255)" onkeyup="this.value=this.value.substring(0, 255)"></textarea> | |||
</div> | |||
<div class="ui divider"></div> | |||
<h4 class="unite title ui header ">{{.i18n.Tr "repo.modelarts.train_job.parameter_setting"}}:</h4> | |||
<div class="required unite min_title inline field"> | |||
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.code_version"}}</label> | |||
<select class="ui dropdown width80 left2" id="code_version" name="branch_name"> | |||
{{if .branch_name}} | |||
<option name="branch_name" value="{{.branch_name}}">{{.branch_name}}</option> | |||
{{range $k, $v :=.Branches}} | |||
{{ if ne $v $.branch_name }} | |||
<option name="branch_name" value="{{$v}}">{{$v}}</option> | |||
{{end}} | |||
{{end}} | |||
{{else}} | |||
<option name="branch_name" value="{{.branchName}}">{{.branchName}}</option> | |||
{{range $k, $v :=.Branches}} | |||
{{ if ne $v $.branchName }} | |||
<option name="branch_name" value="{{$v}}">{{$v}}</option> | |||
{{end}} | |||
{{end}} | |||
{{end}} | |||
</select> | |||
</div> | |||
<div id="images-new-grampus"> | |||
</div> | |||
<div class="inline unite min_title field required"> | |||
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.start_file"}}</label> | |||
{{if .bootFile}} | |||
<input style="width: 48.5%;" name="boot_file" id="trainjob_boot_file" value="{{.bootFile}}" tabindex="3" autofocus required maxlength="255" > | |||
{{else}} | |||
<input style="width: 48.5%;" name="boot_file" id="trainjob_boot_file" value="" tabindex="3" autofocus required maxlength="255" > | |||
{{end}} | |||
<span> | |||
<i class="question circle icon link" data-content={{.i18n.Tr "repo.modelarts.train_job.boot_file_helper"}} data-position="right center" data-variation="mini"></i> | |||
</span> | |||
<a href="https://git.openi.org.cn/OpenIOSSG/MNIST_PytorchExample_GPU/src/branch/master/train_for_c2net.py" target="_blank">查看样例</a> | |||
</div> | |||
{{template "custom/select_dataset_train" .}} | |||
<span class="tooltips" style="margin-left: 11.5rem;margin-bottom: 2rem;">{{.i18n.Tr "repo.grampus.gpu_dataset_path_rule"}}</span> | |||
<div class="inline unite min_title field"> | |||
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.run_parameter"}}</label> | |||
<span id="add_run_para" style="margin-left: 0.5rem;cursor:pointer;color: rgba(3, 102, 214, 100);font-size: 14px;line-height: 26px;font-family: SourceHanSansSC-medium;"><i class="plus square outline icon"></i>{{.i18n.Tr "repo.modelarts.train_job.add_run_parameter"}}</span> | |||
<input id="store_run_para" type="hidden" name="run_para_list"> | |||
<div class="dynamic field" style="margin-top: 1rem;"> | |||
{{if .params}} | |||
{{if ne 0 (len .params)}} | |||
{{range $k ,$v := .params}} | |||
<div class="two fields width85" id="para{{$k}}"> | |||
<div class="field"> | |||
<input type="text" name="shipping_first-name" value={{$v.Label}} required> | |||
</div> | |||
<div class="field"> | |||
<input type="text" name="shipping_last-name" value={{$v.Value}} required> | |||
</div> | |||
<span> | |||
<i class="trash icon"></i> | |||
</span> | |||
</div> | |||
{{end}} | |||
{{end}} | |||
{{end}} | |||
</div> | |||
</div> | |||
<div class="required unite min_title inline field" id="flavor_name"> | |||
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.standard"}}</label> | |||
<select class="ui dropdown width81" id="trainjob-flavor" style='width:385px' name="flavor"> | |||
{{range .flavor_infos}} | |||
<option name="flavor" value="{{.ID}}">{{.Name}}</option> | |||
{{end}} | |||
</select> | |||
</div> | |||
<div class="inline unite min_title field"> | |||
<button class="ui create_train_job green button"> | |||
{{.i18n.Tr "repo.cloudbrain.new"}} | |||
</button> | |||
<a class="ui button" href="{{.RepoLink}}/modelarts/train-job">{{.i18n.Tr "repo.cloudbrain.cancel"}}</a> | |||
</div> | |||
<!-- 模态框 --> | |||
</form> | |||
</div> | |||
</div> | |||
</div> | |||
{{template "base/footer" .}} | |||
<script> | |||
//let url_href = window.location.pathname.split('create')[0] | |||
//$(".ui.button").attr('href',url_href) | |||
$('select.dropdown') | |||
.dropdown(); | |||
$('.menu .item') | |||
.tab(); | |||
let sever_num = $('#trainjob_work_server_num') | |||
$('.add').click(function(){ | |||
sever_num.val(parseInt(sever_num.val())+1) | |||
if(sever_num.val()>=26){ | |||
sever_num.val(parseInt(sever_num.val())-1) | |||
} | |||
}) | |||
$('.min').click(function(){ | |||
sever_num.val(parseInt(sever_num.val())-1) | |||
if(sever_num.val()<=0){ | |||
sever_num.val(parseInt(sever_num.val())+1) | |||
} | |||
}) | |||
// 参数增加、删除、修改、保存 | |||
function Add_parameter(i){ | |||
value = '<div class="two fields width85" id= "para'+ i +'">' + | |||
'<div class="field">' + | |||
'<input type="text" name="shipping_first-name" required placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_name"}}> ' + | |||
'</div> ' + | |||
'<div class="field"> ' + | |||
'<input type="text" name="shipping_last-name" required placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_value"}}>' + | |||
'</div>'+ | |||
'<span>' + | |||
'<i class="trash icon">' + | |||
'</i>' + | |||
'</span>' + | |||
'</div>' | |||
$(".dynamic.field").append(value) | |||
} | |||
$('#add_run_para').click(function(){ | |||
var len = $(".dynamic.field .two.fields").length | |||
Add_parameter(len) | |||
}); | |||
$(".dynamic.field").on("click",".trash.icon", function() { | |||
var index = $(this).parent().parent().index() | |||
$(this).parent().parent().remove() | |||
var len = $(".dynamic.field .two.fields").length | |||
$(".dynamic.field .two.fields").each(function(){ | |||
var cur_index = $(this).index() | |||
$(this).attr('id', 'para' + cur_index) | |||
}) | |||
}); | |||
$('.ui.parameter.green.button').click(function(){ | |||
var parameters = []; | |||
$('table tr').each(function() { | |||
$(this).find('td:eq(1)').each(function(){ | |||
parameters.push($(this).text()); | |||
}) | |||
$(this).find('input').each(function(){ | |||
parameters.push($(this).text()) | |||
}) | |||
}); | |||
$('.ui.parameter.modal') | |||
.modal('hide'); | |||
for(var i = 2; i < parameters.length; i++){ | |||
switch(i) { | |||
// 数据集uuid待完成 | |||
// case (2): | |||
// console.log(1) | |||
// break; | |||
// $("#trainjob_datasets").val(parameters[i]); | |||
// console.log($("#trainjob_datasets").val()) | |||
case (3): | |||
$("input[name='boot_file']").val(parameters[i]); | |||
break; | |||
case (4): | |||
var para = parameters[i].split(" ") | |||
for(var j = 0; j < para.length; j++){ | |||
var para_name = para[j].split('=')[0] | |||
var para_value = para[j].split('=')[1] | |||
var len = $(".dynamic.field .two.fields").length | |||
Add_parameter(len) | |||
var pid = 'para' + len | |||
$(".dynamic.field"+ " #" + pid + "").find("input[name=shipping_first-name]").val(para_name) | |||
$(".dynamic.field"+ " #" + pid + "").find("input[name=shipping_last-name]").val(para_value) | |||
} | |||
break; | |||
// 数据集pool_id待完成 | |||
// case (5): | |||
// $("select[name='pool_id']").val(parameters[i]); | |||
// break; | |||
case (6): | |||
$("input[name='work_server_number']").val(parameters[i]); | |||
break; | |||
} | |||
} | |||
}) | |||
$('.ui.save.checkbox').click(function(){ | |||
$(this).checkbox({ | |||
onChange: function(){ | |||
if ($('.ui.save.checkbox').checkbox('is checked')){ | |||
$('#save_para').removeClass("disabled") | |||
}else{ | |||
$('#save_para').addClass("disabled") | |||
} | |||
} | |||
}); | |||
}) | |||
$('.question.circle.icon').hover(function(){ | |||
$(this).popup('show') | |||
}); | |||
$(".item.active.parameter_config").click(function(){ | |||
$('.ui.parameter.modal') | |||
.modal('setting', 'closable', false) | |||
.modal('show'); | |||
}) | |||
$('.ui.deny.button').click(function(){ | |||
$('.ui.parameter.modal') | |||
.modal('hide'); | |||
}) | |||
$('select.dropdown') | |||
.dropdown(); | |||
function validate(){ | |||
$('.ui.form') | |||
.form({ | |||
on: 'blur', | |||
fields: { | |||
boot_file: { | |||
identifier : 'boot_file', | |||
rules: [ | |||
{ | |||
type: 'regExp[/.+\.py$/g]', | |||
} | |||
] | |||
}, | |||
display_job_name:{ | |||
identifier : 'display_job_name', | |||
rules: [ | |||
{ | |||
type: 'regExp[/^[a-zA-Z0-9-_]{1,64}[a-zA-Z0-9_]$/]', | |||
} | |||
] | |||
}, | |||
attachment:{ | |||
identifier : 'attachment', | |||
rules: [ | |||
{ | |||
type: 'empty', | |||
} | |||
] | |||
}, | |||
flavor:{ | |||
identifier : 'flavor', | |||
rules: [ | |||
{ | |||
type: 'empty', | |||
} | |||
] | |||
}, | |||
work_server_number: { | |||
identifier : 'work_server_number', | |||
rules: [ | |||
{ | |||
type : 'integer[1..25]', | |||
} | |||
] | |||
} | |||
}, | |||
onSuccess: function(){ | |||
// $('.ui.page.dimmer').dimmer('show') | |||
document.getElementById("mask").style.display = "block" | |||
}, | |||
onFailure: function(e){ | |||
return false; | |||
} | |||
}) | |||
} | |||
document.onreadystatechange = function() { | |||
if (document.readyState === "complete") { | |||
document.getElementById("mask").style.display = "none" | |||
} | |||
} | |||
function send_run_para(){ | |||
var run_parameters = [] | |||
var msg = {} | |||
$(".dynamic.field .two.fields").each(function(){ | |||
var para_name = $(this).find('input[name=shipping_first-name]').val() | |||
var para_value = $(this).find('input[name=shipping_last-name]').val() | |||
run_parameters.push({"label": para_name, "value": para_value}) | |||
}) | |||
msg["parameter"] = run_parameters | |||
msg = JSON.stringify(msg) | |||
$('#store_run_para').val(msg) | |||
} | |||
function get_name(){ | |||
let name1=$("#engine_name .text").text() | |||
let name2=$("#flavor_name .text").text() | |||
$("input#ai_engine_name").val(name1) | |||
$("input#ai_flavor_name").val(name2) | |||
} | |||
$('.ui.create_train_job.green.button').click(function(e) { | |||
get_name() | |||
send_run_para() | |||
validate() | |||
}) | |||
</script> |
@@ -0,0 +1,430 @@ | |||
{{template "base/head" .}} | |||
<style> | |||
.unite{ | |||
font-family: SourceHanSansSC-medium !important; | |||
color: rgba(16, 16, 16, 100) !important; | |||
} | |||
.title{ | |||
font-size: 16px !important; | |||
padding-left: 3rem !important; | |||
} | |||
.min_title{ | |||
font-size: 14px !important; | |||
padding-left: 6rem !important; | |||
margin-bottom: 2rem !important; | |||
} | |||
.width{ | |||
width:100% !important; | |||
} | |||
.width80{ | |||
width: 80.7% !important; | |||
margin-left: 10px; | |||
} | |||
.width85{ | |||
width: 85% !important; | |||
margin-left: 4.5rem !important; | |||
} | |||
.width81{ | |||
margin-left: 1.5rem; | |||
width: 81% !important; | |||
} | |||
.add{font-size: 18px; | |||
padding: 0.5rem; | |||
border: 1px solid rgba(187, 187, 187, 100); | |||
border-radius: 0px 5px 5px 0px; | |||
line-height: 21px; | |||
text-align: center; | |||
color: #C2C7CC; | |||
} | |||
.min{ | |||
font-size: 18px; | |||
padding: 0.5rem; | |||
border: 1px solid rgba(187, 187, 187, 100); | |||
border-radius: 5px 0px 0px 5px; | |||
line-height: 21px; | |||
text-align: center; | |||
color: #C2C7CC; | |||
} | |||
</style> | |||
<!-- <div class="ui page dimmer"> | |||
<div class="ui text loader">{{.i18n.Tr "loading"}}</div> | |||
</div> --> | |||
<div id="mask"> | |||
<div id="loadingPage"> | |||
<div class="rect1"></div> | |||
<div class="rect2"></div> | |||
<div class="rect3"></div> | |||
<div class="rect4"></div> | |||
<div class="rect5"></div> | |||
</div> | |||
</div> | |||
<div class="repository"> | |||
{{template "repo/header" .}} | |||
<div class="ui container"> | |||
{{template "base/alert" .}} | |||
<h4 class="ui top attached header"> | |||
{{.i18n.Tr "repo.modelarts.train_job.new"}} | |||
</h4> | |||
<div class="ui attached segment"> | |||
<!-- equal width --> | |||
<form class="ui form" action="{{.Link}}" method="post"> | |||
{{.CsrfTokenHtml}} | |||
<input type="hidden" name="action" value="update"> | |||
<input type="hidden" id="ai_engine_name" name="engine_name" value=""> | |||
<input type="hidden" id="ai_flavor_name" name="flavor_name" value=""> | |||
<h4 class="unite title ui header ">{{.i18n.Tr "repo.modelarts.train_job.basic_info"}}:</h4> | |||
<div class="required unite min_title inline field"> | |||
<label style="font-weight: normal;">{{.i18n.Tr "cloudbrain.resource_cluster"}}</label> | |||
<div class="ui blue mini menu compact selectcloudbrain"> | |||
<a class="item" href="{{.RepoLink}}/cloudbrain/train-job/create"> | |||
<svg class="svg" sxmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="none" d="M0 0h24v24H0z"></path><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-2.29-2.333A17.9 17.9 0 0 1 8.027 13H4.062a8.008 8.008 0 0 0 5.648 6.667zM10.03 13c.151 2.439.848 4.73 1.97 6.752A15.905 15.905 0 0 0 13.97 13h-3.94zm9.908 0h-3.965a17.9 17.9 0 0 1-1.683 6.667A8.008 8.008 0 0 0 19.938 13zM4.062 11h3.965A17.9 17.9 0 0 1 9.71 4.333 8.008 8.008 0 0 0 4.062 11zm5.969 0h3.938A15.905 15.905 0 0 0 12 4.248 15.905 15.905 0 0 0 10.03 11zm4.259-6.667A17.9 17.9 0 0 1 15.973 11h3.965a8.008 8.008 0 0 0-5.648-6.667z"></path></svg> | |||
{{.i18n.Tr "cloudbrain.resource_cluster_openi"}} | |||
</a> | |||
<a class="active item" href="{{.RepoLink}}/grampus/train-job/ {{if.NPUEnabled}}npu{{else}}gpu{{end}}/create"> | |||
<svg class="svg" sxmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="none" d="M0 0h24v24H0z"></path><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-2.29-2.333A17.9 17.9 0 0 1 8.027 13H4.062a8.008 8.008 0 0 0 5.648 6.667zM10.03 13c.151 2.439.848 4.73 1.97 6.752A15.905 15.905 0 0 0 13.97 13h-3.94zm9.908 0h-3.965a17.9 17.9 0 0 1-1.683 6.667A8.008 8.008 0 0 0 19.938 13zM4.062 11h3.965A17.9 17.9 0 0 1 9.71 4.333 8.008 8.008 0 0 0 4.062 11zm5.969 0h3.938A15.905 15.905 0 0 0 12 4.248 15.905 15.905 0 0 0 10.03 11zm4.259-6.667A17.9 17.9 0 0 1 15.973 11h3.965a8.008 8.008 0 0 0-5.648-6.667z"></path></svg> | |||
{{.i18n.Tr "cloudbrain.resource_cluster_c2net"}}(Beta) | |||
</a> | |||
</div> | |||
</div> | |||
<div class="required unite min_title inline field"> | |||
<label style="font-weight: normal;">{{.i18n.Tr "cloudbrain.compute_resource"}}</label> | |||
<div class="ui blue mini menu compact selectcloudbrain"> | |||
<a {{if.GPUEnabled}}class="item" href="{{.RepoLink}}/grampus/train-job/gpu/create"{{else}}href="javascript:return false;" class="item disabled" {{end}}> | |||
<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 2.992C3 2.444 3.445 2 3.993 2h16.014a1 1 0 0 1 .993.992v18.016a.993.993 0 0 1-.993.992H3.993A1 1 0 0 1 3 21.008V2.992zM19 11V4H5v7h14zm0 2H5v7h14v-7zM9 6h6v2H9V6zm0 9h6v2H9v-2z"/> | |||
</svg> | |||
CPU/GPU | |||
</a> | |||
<a {{if.NPUEnabled}}class="active item" href="{{.RepoLink}}/grampus/train-job/npu/create"{{else}}href="javascript:return false;" class="item disabled" {{end}} > | |||
<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 2.992C3 2.444 3.445 2 3.993 2h16.014a1 1 0 0 1 .993.992v18.016a.993.993 0 0 1-.993.992H3.993A1 1 0 0 1 3 21.008V2.992zM19 11V4H5v7h14zm0 2H5v7h14v-7zM9 6h6v2H9V6zm0 9h6v2H9v-2z"/> | |||
</svg> | |||
Ascend NPU</a> | |||
</div> | |||
</div> | |||
<div class="required unite min_title inline field"> | |||
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.job_name"}}</label> | |||
<input style="width: 60%;" name="display_job_name" id="display_job_name" placeholder={{.i18n.Tr "repo.modelarts.train_job.job_name"}} value="{{.display_job_name}}" tabindex="3" onkeyup="this.value=this.value.replace(/[, ]/g,'')" autofocus required maxlength="64"> | |||
<span class="tooltips" style="display: block;">{{.i18n.Tr "cloudbrain.job_name_rule"}}</span> | |||
</div> | |||
<div class="unite min_title inline field"> | |||
<label style="font-weight: normal;" for="description">{{.i18n.Tr "repo.modelarts.train_job.description"}} </label> | |||
<textarea style="width: 80%;" id="description" name="description" rows="3" maxlength="255" placeholder={{.i18n.Tr "repo.modelarts.train_job.new_place"}} onchange="this.value=this.value.substring(0, 255)" onkeydown="this.value=this.value.substring(0, 255)" onkeyup="this.value=this.value.substring(0, 255)"></textarea> | |||
</div> | |||
<div class="ui divider"></div> | |||
<h4 class="unite title ui header ">{{.i18n.Tr "repo.modelarts.train_job.parameter_setting"}}:</h4> | |||
<div class="required unite min_title inline field"> | |||
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.code_version"}}</label> | |||
<select class="ui dropdown width80 left2" id="code_version" name="branch_name"> | |||
{{if .branch_name}} | |||
<option name="branch_name" value="{{.branch_name}}">{{.branch_name}}</option> | |||
{{range $k, $v :=.Branches}} | |||
{{ if ne $v $.branch_name }} | |||
<option name="branch_name" value="{{$v}}">{{$v}}</option> | |||
{{end}} | |||
{{end}} | |||
{{else}} | |||
<option name="branch_name" value="{{.branchName}}">{{.branchName}}</option> | |||
{{range $k, $v :=.Branches}} | |||
{{ if ne $v $.branchName }} | |||
<option name="branch_name" value="{{$v}}">{{$v}}</option> | |||
{{end}} | |||
{{end}} | |||
{{end}} | |||
</select> | |||
</div> | |||
<div class="required unite min_title inline field" id="engine_name"> | |||
<label style="font-weight: normal;">{{.i18n.Tr "cloudbrain.mirror"}}</label> | |||
<select class="ui dropdown width81" id="trainjob_images" name="image_id"> | |||
{{range .images}} | |||
<option name="image_id" value="{{.ID}}">{{.Name}}</option> | |||
{{end}} | |||
</select> | |||
</div> | |||
<div class="inline unite min_title field required"> | |||
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.start_file"}}</label> | |||
{{if .bootFile}} | |||
<input style="width: 48.5%;" name="boot_file" id="trainjob_boot_file" value="{{.bootFile}}" tabindex="3" autofocus required maxlength="255" > | |||
{{else}} | |||
<input style="width: 48.5%;" name="boot_file" id="trainjob_boot_file" value="" tabindex="3" autofocus required maxlength="255" > | |||
{{end}} | |||
<span> | |||
<i class="question circle icon link" data-content={{.i18n.Tr "repo.modelarts.train_job.boot_file_helper"}} data-position="right center" data-variation="mini"></i> | |||
</span> | |||
<a href="https://git.openi.org.cn/OpenIOSSG/MNIST_Example/src/branch/master/train_for_c2net.py" target="_blank">{{.i18n.Tr "cloudbrain.view_sample"}}</a> | |||
</div> | |||
{{template "custom/select_dataset_train" .}} | |||
<span class="tooltips" style="margin-left: 11.5rem;margin-bottom: 2rem;">{{.i18n.Tr "repo.grampus.dataset_path_rule"}}</span> | |||
<div class="inline unite min_title field"> | |||
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.run_parameter"}}</label> | |||
<span id="add_run_para" style="margin-left: 0.5rem;cursor:pointer;color: rgba(3, 102, 214, 100);font-size: 14px;line-height: 26px;font-family: SourceHanSansSC-medium;"><i class="plus square outline icon"></i>{{.i18n.Tr "repo.modelarts.train_job.add_run_parameter"}}</span> | |||
<input id="store_run_para" type="hidden" name="run_para_list"> | |||
<div class="dynamic field" style="margin-top: 1rem;"> | |||
</div> | |||
</div> | |||
<div class="required unite min_title inline field" id="flavor_name"> | |||
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.standard"}}</label> | |||
<select class="ui dropdown width81" id="trainjob-flavor" style='width:385px' name="flavor"> | |||
{{range .flavor_infos}} | |||
<option name="flavor" value="{{.ID}}">{{.Name}}</option> | |||
{{end}} | |||
</select> | |||
</div> | |||
<div class="inline required unite min_title field"> | |||
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.amount_of_compute_node"}}</label> | |||
<div class="ui labeled input" style="width: 5%;"> | |||
<input style="border-radius: 0;text-align: center;"type="hidden" name="work_server_number" id="trainjob_work_server_num" tabindex="3" autofocus required maxlength="255" value="1" readonly> | |||
<div class="field" id="trainjob_work_server_num_select" name="work_server_number_select"> | |||
<select class="ui dropdown width" style='width: 100%;' name="work_server_id"> | |||
<option name="server_id" value="1">1</option> | |||
</select> | |||
</div> | |||
</div> | |||
</div> | |||
<div class="inline unite min_title field"> | |||
<button class="ui create_train_job green button"> | |||
{{.i18n.Tr "repo.cloudbrain.new"}} | |||
</button> | |||
<a class="ui button" href="{{.RepoLink}}/modelarts/train-job">{{.i18n.Tr "repo.cloudbrain.cancel"}}</a> | |||
</div> | |||
<!-- 模态框 --> | |||
</form> | |||
</div> | |||
</div> | |||
</div> | |||
{{template "base/footer" .}} | |||
<script> | |||
$('select.dropdown') | |||
.dropdown(); | |||
$('.menu .item') | |||
.tab(); | |||
// let sever_num = $("#trainjob_work_server_num_select .text").text() //$('#trainjob_work_server_num') | |||
// console.log("sever_num:",sever_num) | |||
// $('.add').click(function(){ | |||
// sever_num.val(parseInt(sever_num.val())+1) | |||
// if(sever_num.val()>=26){ | |||
// sever_num.val(parseInt(sever_num.val())-1) | |||
// } | |||
// }) | |||
// $('.min').click(function(){ | |||
// sever_num.val(parseInt(sever_num.val())-1) | |||
// if(sever_num.val()<=0){ | |||
// sever_num.val(parseInt(sever_num.val())+1) | |||
// } | |||
// }) | |||
// 参数增加、删除、修改、保存 | |||
function Add_parameter(i){ | |||
value = '<div class="two fields width85" id= "para'+ i +'">' + | |||
'<div class="field">' + | |||
'<input type="text" name="shipping_first-name" required placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_name"}}> ' + | |||
'</div> ' + | |||
'<div class="field"> ' + | |||
'<input type="text" name="shipping_last-name" required placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_value"}}>' + | |||
'</div>'+ | |||
'<span>' + | |||
'<i class="trash icon">' + | |||
'</i>' + | |||
'</span>' + | |||
'</div>' | |||
$(".dynamic.field").append(value) | |||
} | |||
$('#add_run_para').click(function(){ | |||
var len = $(".dynamic.field .two.fields").length | |||
Add_parameter(len) | |||
}); | |||
$(".dynamic.field").on("click",".trash.icon", function() { | |||
var index = $(this).parent().parent().index() | |||
$(this).parent().parent().remove() | |||
var len = $(".dynamic.field .two.fields").length | |||
$(".dynamic.field .two.fields").each(function(){ | |||
var cur_index = $(this).index() | |||
$(this).attr('id', 'para' + cur_index) | |||
}) | |||
}); | |||
$('.ui.parameter.green.button').click(function(){ | |||
var parameters = []; | |||
$('table tr').each(function() { | |||
$(this).find('td:eq(1)').each(function(){ | |||
parameters.push($(this).text()); | |||
}) | |||
$(this).find('input').each(function(){ | |||
parameters.push($(this).text()) | |||
}) | |||
}); | |||
$('.ui.parameter.modal') | |||
.modal('hide'); | |||
for(var i = 2; i < parameters.length; i++){ | |||
switch(i) { | |||
// 数据集uuid待完成 | |||
// case (2): | |||
// console.log(1) | |||
// break; | |||
// $("#trainjob_datasets").val(parameters[i]); | |||
// console.log($("#trainjob_datasets").val()) | |||
case (3): | |||
$("input[name='boot_file']").val(parameters[i]); | |||
break; | |||
case (4): | |||
var para = parameters[i].split(" ") | |||
for(var j = 0; j < para.length; j++){ | |||
var para_name = para[j].split('=')[0] | |||
var para_value = para[j].split('=')[1] | |||
var len = $(".dynamic.field .two.fields").length | |||
Add_parameter(len) | |||
var pid = 'para' + len | |||
$(".dynamic.field"+ " #" + pid + "").find("input[name=shipping_first-name]").val(para_name) | |||
$(".dynamic.field"+ " #" + pid + "").find("input[name=shipping_last-name]").val(para_value) | |||
} | |||
break; | |||
// 数据集pool_id待完成 | |||
// case (5): | |||
// $("select[name='pool_id']").val(parameters[i]); | |||
// break; | |||
case (6): | |||
// $("input[name='work_server_number']").val(parameters[i]); | |||
break; | |||
} | |||
} | |||
}) | |||
$('.ui.save.checkbox').click(function(){ | |||
$(this).checkbox({ | |||
onChange: function(){ | |||
if ($('.ui.save.checkbox').checkbox('is checked')){ | |||
$('#save_para').removeClass("disabled") | |||
}else{ | |||
$('#save_para').addClass("disabled") | |||
} | |||
} | |||
}); | |||
}) | |||
$('.question.circle.icon').hover(function(){ | |||
$(this).popup('show') | |||
}); | |||
$(".item.active.parameter_config").click(function(){ | |||
$('.ui.parameter.modal') | |||
.modal('setting', 'closable', false) | |||
.modal('show'); | |||
}) | |||
$('.ui.deny.button').click(function(){ | |||
$('.ui.parameter.modal') | |||
.modal('hide'); | |||
}) | |||
$('select.dropdown') | |||
.dropdown(); | |||
function validate(){ | |||
$('.ui.form') | |||
.form({ | |||
on: 'blur', | |||
fields: { | |||
boot_file: { | |||
identifier : 'boot_file', | |||
rules: [ | |||
{ | |||
type: 'regExp[/.+\.py$/g]', | |||
} | |||
] | |||
}, | |||
display_job_name:{ | |||
identifier : 'display_job_name', | |||
rules: [ | |||
{ | |||
type: 'regExp[/^[a-zA-Z0-9-_]{1,64}[a-zA-Z0-9_]$/]', | |||
} | |||
] | |||
}, | |||
attachment:{ | |||
identifier : 'attachment', | |||
rules: [ | |||
{ | |||
type: 'empty', | |||
} | |||
] | |||
}, | |||
work_server_number: { | |||
identifier : 'work_server_number', | |||
rules: [ | |||
{ | |||
type : 'integer[1..25]', | |||
} | |||
] | |||
} | |||
}, | |||
onSuccess: function(){ | |||
// $('.ui.page.dimmer').dimmer('show') | |||
document.getElementById("mask").style.display = "block" | |||
}, | |||
onFailure: function(e){ | |||
return false; | |||
} | |||
}) | |||
} | |||
document.onreadystatechange = function() { | |||
if (document.readyState === "complete") { | |||
document.getElementById("mask").style.display = "none" | |||
} | |||
} | |||
function send_run_para(){ | |||
var run_parameters = [] | |||
var msg = {} | |||
$(".dynamic.field .two.fields").each(function(){ | |||
var para_name = $(this).find('input[name=shipping_first-name]').val() | |||
var para_value = $(this).find('input[name=shipping_last-name]').val() | |||
run_parameters.push({"label": para_name, "value": para_value}) | |||
}) | |||
msg["parameter"] = run_parameters | |||
msg = JSON.stringify(msg) | |||
$('#store_run_para').val(msg) | |||
} | |||
function get_name(){ | |||
let name1=$("#engine_name .text").text() | |||
let name2=$("#flavor_name .text").text() | |||
$("input#ai_engine_name").val(name1) | |||
$("input#ai_flavor_name").val(name2) | |||
let val_server_num_select = $("#trainjob_work_server_num_select .text").text() | |||
// console.log("val_server_num_select:",val_server_num_select) | |||
$("input#trainjob_work_server_num").val(val_server_num_select) | |||
} | |||
$('.ui.create_train_job.green.button').click(function(e) { | |||
get_name() | |||
send_run_para() | |||
validate() | |||
}) | |||
</script> |
@@ -0,0 +1,982 @@ | |||
{{template "base/head" .}} | |||
<style> | |||
.according-panel-heading { | |||
box-sizing: border-box; | |||
padding: 8px 16px; | |||
color: #252b3a; | |||
background-color: #f2f5fc; | |||
line-height: 1.5; | |||
cursor: pointer; | |||
-moz-user-select: none; | |||
-webkit-user-select: none; | |||
-ms-user-select: none; | |||
-khtml-user-select: none; | |||
user-select: none; | |||
} | |||
.accordion-panel-title { | |||
margin-top: 0; | |||
margin-bottom: 0; | |||
color: #252b3a; | |||
} | |||
.accordion-panel-title-content { | |||
vertical-align: middle; | |||
display: inline-block; | |||
width: calc(100% - 32px); | |||
cursor: default; | |||
} | |||
.acc-margin-bottom { | |||
margin-bottom: 5px; | |||
} | |||
.title_text { | |||
font-size: 12px; | |||
} | |||
.ac-display-inblock { | |||
display: inline-block; | |||
} | |||
.cti-mgRight-sm { | |||
margin-right: 8px; | |||
} | |||
.ac-text-normal { | |||
font-size: 14px; | |||
color: #575d6c; | |||
} | |||
.uc-accordionTitle-black { | |||
color: #333; | |||
} | |||
.accordion-border { | |||
border: 1px solid #cce2ff; | |||
} | |||
.padding0 { | |||
padding: 0 !important; | |||
} | |||
.content-pad { | |||
padding: 15px 35px; | |||
} | |||
.content-margin { | |||
margin: 10px 5px; | |||
} | |||
.tab_2_content { | |||
min-height: 360px; | |||
margin-left: 10px; | |||
} | |||
.ac-grid { | |||
display: block; | |||
*zoom: 1; | |||
} | |||
.ac-grid-col { | |||
float: left; | |||
width: 100%; | |||
} | |||
.ac-grid-col2 .ac-grid-col { | |||
width: 50%; | |||
} | |||
.ti-form { | |||
text-align: left; | |||
max-width: 100%; | |||
vertical-align: middle; | |||
} | |||
.ti-form>tbody { | |||
font-size: 12px; | |||
} | |||
.ti-form>tbody, | |||
.ti-form>tbody>tr { | |||
vertical-align: inherit; | |||
} | |||
.ti-text-form-label { | |||
padding-bottom: 20px; | |||
padding-right: 20px; | |||
color: #8a8e99; | |||
font-size: 12px; | |||
white-space: nowrap !important; | |||
width: 80px; | |||
line-height: 30px; | |||
} | |||
.ti-text-form-content { | |||
line-height: 30px; | |||
padding-bottom: 20px; | |||
} | |||
.ti-form>tbody>tr>td { | |||
vertical-align: top; | |||
white-space: normal; | |||
} | |||
td, | |||
th { | |||
padding: 0; | |||
} | |||
.ac-grid-col .text-span { | |||
width: 450px; | |||
overflow: hidden; | |||
text-overflow: ellipsis; | |||
white-space: nowrap; | |||
} | |||
.redo-color { | |||
color: #3291F8; | |||
} | |||
.ti-action-menu-item:not(:last-child) { | |||
margin-right: 10px; | |||
padding-right: 11px; | |||
text-decoration: none !important; | |||
color: #526ecc; | |||
cursor: pointer; | |||
display: inline-block; | |||
-moz-user-select: none; | |||
-webkit-user-select: none; | |||
-ms-user-select: none; | |||
-khtml-user-select: none; | |||
user-select: none; | |||
position: relative; | |||
} | |||
.ti-action-menu-item:not(:last-child):after { | |||
content: ""; | |||
display: inline-block; | |||
position: absolute; | |||
height: 12px; | |||
right: 0; | |||
top: 50%; | |||
-webkit-transform: translateY(-6px); | |||
-ms-transform: translateY(-6px); | |||
-o-transform: translateY(-6px); | |||
transform: translateY(-6px); | |||
border-right: 1px solid #dfe1e6; | |||
} | |||
.text-width80 { | |||
width: 100px; | |||
line-height: 30px; | |||
} | |||
.border-according { | |||
border: 1px solid #dfe1e6; | |||
} | |||
.disabled { | |||
cursor: default; | |||
pointer-events: none; | |||
color: rgba(0, 0, 0, .6) !important; | |||
opacity: .45 !important; | |||
} | |||
.pad20 { | |||
border: 0px !important; | |||
} | |||
.model_file_bread { | |||
margin-bottom: -0.5rem !important; | |||
padding-left: 1rem; | |||
padding-top: 0.5rem; | |||
} | |||
</style> | |||
<div id="mask"> | |||
<div id="loadingPage"> | |||
<div class="rect1"></div> | |||
<div class="rect2"></div> | |||
<div class="rect3"></div> | |||
<div class="rect4"></div> | |||
<div class="rect5"></div> | |||
</div> | |||
</div> | |||
<div class="repository"> | |||
{{template "repo/header" .}} | |||
<div class="ui container"> | |||
<h4 class="ui header" id="vertical-segment"> | |||
<div class="ui breadcrumb"> | |||
<a class="section" href="{{.RepoLink}}/debugjob?debugListType=all"> | |||
{{.i18n.Tr "repo.cloudbrain"}} | |||
</a> | |||
<div class="divider"> / </div> | |||
<a class="section" href="{{$.RepoLink}}/modelarts/train-job"> | |||
{{$.i18n.Tr "repo.modelarts.train_job"}} | |||
</a> | |||
<div class="divider"> / </div> | |||
<div class="active section">{{.displayJobName}}</div> | |||
</div> | |||
</h4> | |||
{{range $k ,$v := .version_list_task}} | |||
<div class="ui accordion border-according" id="accordion{{.VersionName}}" | |||
data-repopath="{{$.RepoRelPath}}/modelarts/train-job" data-jobid="{{.JobID}}" | |||
data-version="{{.VersionName}}"> | |||
<div class="{{if eq $k 0}}active{{end}} title padding0"> | |||
<div class="according-panel-heading"> | |||
<div class="accordion-panel-title"> | |||
<i class="dropdown icon"></i> | |||
<span class="accordion-panel-title-content"> | |||
<span> | |||
<div style="float: right;"> | |||
{{$.CsrfTokenHtml}} | |||
</div> | |||
<div class="ac-display-inblock title_text acc-margin-bottom"> | |||
<span class="cti-mgRight-sm"> | |||
{{if not (eq .StartTime 0)}} | |||
{{TimeSinceUnix1 .StartTime}} | |||
{{else}} | |||
{{TimeSinceUnix1 .CreatedUnix}} | |||
{{end}}</span> | |||
<span class="cti-mgRight-sm"> | |||
{{$.i18n.Tr "repo.modelarts.current_version"}}:{{.VersionName}}</span> | |||
<span class="cti-mgRight-sm"> | |||
{{$.i18n.Tr "repo.modelarts.parent_version"}}:{{.PreVersionName}}</span> | |||
<span class="cti-mgRight-sm">{{$.i18n.Tr "repo.modelarts.status"}}: | |||
<span id="{{.VersionName}}-status-span"><i id="icon" | |||
style="vertical-align: middle;" class="{{.Status}}"></i><span id="text" | |||
style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span> | |||
</span> | |||
<span | |||
class="cti-mgRight-sm">{{$.i18n.Tr "repo.modelarts.train_job.dura_time"}}:</span> | |||
<span class="cti-mgRight-sm uc-accordionTitle-black" | |||
id="{{.VersionName}}-duration-span">{{.TrainJobDuration}}</span> | |||
<span data-tooltip="刷新" style="cursor: pointer;" data-inverted="" | |||
onclick="refreshStatus({{.VersionName}})"><i | |||
class="redo icon redo-color"></i></span> | |||
</div> | |||
</span> | |||
</span> | |||
</div> | |||
</div> | |||
</div> | |||
<div class="{{if eq $k 0}}active{{end}} content"> | |||
<div class="content-pad"> | |||
<div class="ui pointing secondary menu" style="border-bottom: 1px solid rgba(34,36,38,.15);"> | |||
<a class="active item" | |||
data-tab="first{{$k}}">{{$.i18n.Tr "repo.modelarts.train_job.config"}}</a> | |||
<a class="item" data-tab="second{{$k}}" | |||
onclick="loadLog({{.VersionName}})">{{$.i18n.Tr "repo.modelarts.log"}}</a> | |||
<a class="item" data-tab="third{{$k}}" | |||
onclick="loadModelFile({{.VersionName}},'','','init')">{{$.i18n.Tr "repo.model_download"}}</a> | |||
</div> | |||
<div class="ui tab active" data-tab="first{{$k}}"> | |||
<div style="padding-top: 10px;"> | |||
<div class="tab_2_content"> | |||
<div class="ac-grid ac-grid-col2"> | |||
<div class="ac-grid-col"> | |||
<table class="ti-form"> | |||
<tbody class="ti-text-form"> | |||
<tr class="ti-no-ng-animate"> | |||
<td class="ti-no-ng-animate ti-text-form-label text-width80"> | |||
{{$.i18n.Tr "repo.cloudbrain_task"}} | |||
</td> | |||
<td class="ti-text-form-content"> | |||
<div class="text-span text-span-w"> | |||
{{.DisplayJobName}} | |||
</div> | |||
</td> | |||
</tr> | |||
<tr class="ti-no-ng-animate"> | |||
<td class="ti-no-ng-animate ti-text-form-label text-width80"> | |||
{{$.i18n.Tr "repo.modelarts.status"}} | |||
</td> | |||
<td class="ti-text-form-content"> | |||
<div class="text-span text-span-w" id="{{.VersionName}}-status"> | |||
{{.Status}} | |||
</div> | |||
</td> | |||
</tr> | |||
<tr class="ti-no-ng-animate"> | |||
<td class="ti-no-ng-animate ti-text-form-label text-width80"> | |||
{{$.i18n.Tr "repo.modelarts.run_version"}} | |||
</td> | |||
<td class="ti-text-form-content"> | |||
<div class="text-span text-span-w"> | |||
{{.VersionName}} | |||
</div> | |||
</td> | |||
</tr> | |||
<tr class="ti-no-ng-animate"> | |||
<td class="ti-no-ng-animate ti-text-form-label text-width80"> | |||
{{$.i18n.Tr "repo.modelarts.train_job.start_time"}} | |||
</td> | |||
<td class="ti-text-form-content"> | |||
<div class="text-span text-span-w"> | |||
<span style="font-size: 12px;" class=""> | |||
{{if not (eq .StartTime 0)}} | |||
{{TimeSinceUnix1 .StartTime}} | |||
{{else}} | |||
{{TimeSinceUnix1 .CreatedUnix}} | |||
{{end}}</span> | |||
</div> | |||
</td> | |||
</tr> | |||
<tr class="ti-no-ng-animate"> | |||
<td class="ti-no-ng-animate ti-text-form-label text-width80"> | |||
{{$.i18n.Tr "repo.modelarts.train_job.dura_time"}} | |||
</td> | |||
<td class="ti-text-form-content"> | |||
<div class="text-span text-span-w" | |||
id="{{.VersionName}}-duration"> | |||
{{.TrainJobDuration}} | |||
</div> | |||
</td> | |||
</tr> | |||
<tr class="ti-no-ng-animate"> | |||
<td class="ti-no-ng-animate ti-text-form-label text-width80"> | |||
{{$.i18n.Tr "repo.modelarts.train_job.standard"}} | |||
</td> | |||
<td class="ti-text-form-content"> | |||
<div class="text-span text-span-w"> | |||
{{.FlavorName}} | |||
</div> | |||
</td> | |||
</tr> | |||
<tr class="ti-no-ng-animate"> | |||
<td class="ti-no-ng-animate ti-text-form-label text-width80"> | |||
{{$.i18n.Tr "repo.modelarts.train_job.compute_node"}} | |||
</td> | |||
<td class="ti-text-form-content"> | |||
<div class="text-span text-span-w"> | |||
{{.WorkServerNumber}} | |||
</div> | |||
</td> | |||
</tr> | |||
</tbody> | |||
</table> | |||
</div> | |||
<div class="ac-grid-col"> | |||
<table class="ti-form"> | |||
<tbody class="ti-text-form"> | |||
<tr class="ti-no-ng-animate"> | |||
<td class="ti-no-ng-animate ti-text-form-label text-width80"> | |||
{{$.i18n.Tr "repo.modelarts.train_job.AI_driver"}} | |||
</td> | |||
<td class="ti-text-form-content"> | |||
<div class="text-span text-span-w"> | |||
{{.EngineName}} | |||
</div> | |||
</td> | |||
</tr> | |||
<tr class="ti-no-ng-animate"> | |||
<td class="ti-no-ng-animate ti-text-form-label text-width80"> | |||
{{$.i18n.Tr "repo.modelarts.code_version"}} | |||
</td> | |||
<td class="ti-text-form-content"> | |||
<div class="text-span text-span-w"> | |||
{{.BranchName}} | |||
</div> | |||
</td> | |||
</tr> | |||
<tr class="ti-no-ng-animate"> | |||
<td class="ti-no-ng-animate ti-text-form-label text-width80"> | |||
{{$.i18n.Tr "repo.modelarts.train_job.start_file"}} | |||
</td> | |||
<td class="ti-text-form-content"> | |||
<div class="text-span text-span-w"> | |||
{{.BootFile}} | |||
</div> | |||
</td> | |||
</tr> | |||
<tr class="ti-no-ng-animate"> | |||
<td class="ti-no-ng-animate ti-text-form-label text-width80"> | |||
{{$.i18n.Tr "repo.modelarts.train_job.train_dataset"}} | |||
</td> | |||
<td class="ti-text-form-content"> | |||
<div class="text-span text-span-w"> | |||
{{.DatasetName}} | |||
</div> | |||
</td> | |||
</tr> | |||
<tr class="ti-no-ng-animate"> | |||
<td class="ti-no-ng-animate ti-text-form-label text-width80"> | |||
{{$.i18n.Tr "repo.modelarts.train_job.run_parameter"}} | |||
</td> | |||
<td class="ti-text-form-content"> | |||
<div class="text-span text-span-w" title="{{.Parameters}}"> | |||
{{.Parameters}} | |||
</div> | |||
</td> | |||
</tr> | |||
<tr class="ti-no-ng-animate"> | |||
<td class="ti-no-ng-animate ti-text-form-label text-width80"> | |||
{{$.i18n.Tr "repo.grampus.train_job.ai_center"}} | |||
</td> | |||
<td class="ti-text-form-content"> | |||
<div class="text-span text-span-w" id="{{.VersionName}}-ai_center"> | |||
{{$.ai_center}} | |||
</div> | |||
</td> | |||
</tr> | |||
</tr> | |||
<tr class="ti-no-ng-animate"> | |||
<td class="ti-no-ng-animate ti-text-form-label text-width80"> | |||
{{$.i18n.Tr "repo.modelarts.train_job.description"}} | |||
</td> | |||
<td class="ti-text-form-content"> | |||
<div class="text-span text-span-w" | |||
title="{{.Description}}"> | |||
{{.Description}} | |||
</div> | |||
</td> | |||
</tr> | |||
</tbody> | |||
</table> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<div class="ui tab" data-tab="second{{$k}}"> | |||
<div style="position: relative;"> | |||
<div class="ui message message{{.VersionName}}" style="display: none;"> | |||
<div id="header"></div> | |||
</div> | |||
<div class="ui attached log" id="log{{.VersionName}}" | |||
style="height: 300px !important; overflow: auto;"> | |||
<input type="hidden" name="end_line" value> | |||
<input type="hidden" name="start_line" value> | |||
<pre id="log_file{{.VersionName}}"></pre> | |||
</div> | |||
</div> | |||
</div> | |||
<div class="ui tab" data-tab="third{{$k}}"> | |||
<input type="hidden" name="model{{.VersionName}}" value="-1"> | |||
<input type="hidden" name="modelback{{.VersionName}}" value="-1"> | |||
<div class='ui breadcrumb model_file_bread' id='file_breadcrumb{{.VersionName}}'> | |||
<div class="active section">{{.VersionName}}</div> | |||
<div class="divider"> / </div> | |||
</div> | |||
<div id="dir_list{{.VersionName}}"> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
{{end}} {{template "base/paginate" .}} | |||
</div> | |||
<!-- 确认模态框 --> | |||
<div id="deletemodel"> | |||
<div class="ui basic modal"> | |||
<div class="ui icon header"> | |||
<i class="trash icon"></i> {{.i18n.Tr "cloudbrain.delete_task"}} | |||
</div> | |||
<div class="content"> | |||
<p>{{.i18n.Tr "cloudbrain.task_delete_confirm"}}</p> | |||
</div> | |||
<div class="actions"> | |||
<div class="ui red basic inverted cancel button"> | |||
<i class="remove icon"></i> {{.i18n.Tr "cloudbrain.operate_cancel"}} | |||
</div> | |||
<div class="ui green basic inverted ok button"> | |||
<i class="checkmark icon"></i> {{.i18n.Tr "cloudbrain.operate_confirm"}} | |||
</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 id="model_header">导入新模型</h4> | |||
</div> | |||
<div class="content content-padding"> | |||
<form id="formId" method="POST" class="ui form"> | |||
<div class="ui error message"> | |||
</div> | |||
{{$.CsrfTokenHtml}} | |||
<input type="hidden" name="trainTaskCreate" value="true"> | |||
<div class="two inline fields "> | |||
<div class="required ten wide field"> | |||
<label style="margin-left: -23px;">选择训练任务</label> | |||
<input type="hidden" class="width83" id="JobId" name="JobId" readonly required> | |||
<input class="width83" id="JobName" readonly required> | |||
</div> | |||
<div class="required six widde field"> | |||
<label>版本</label> | |||
<input class="width70" id="VersionName" name="VersionName" readonly required> | |||
</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;" id="label" name="Label" maxlength="255" | |||
placeholder='{{.i18n.Tr "repo.modelarts.train_job.label_place"}}'> | |||
</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 onclick="createModel()" type="button" class="ui create_train_job green button" | |||
style="position: absolute;"> | |||
{{.i18n.Tr "repo.model.manage.sava_model"}} | |||
</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> | |||
</div> | |||
</div> | |||
{{template "base/footer" .}} | |||
<script> | |||
$('.menu .item').tab() | |||
$(document).ready(function () { | |||
$('.ui.accordion').accordion({ selector: { trigger: '.icon' } }); | |||
}); | |||
$(document).ready(function () { | |||
$('.secondary.menu .item').tab(); | |||
}); | |||
let userName | |||
let repoPath | |||
let jobID | |||
let downlaodFlag = {{ $.canDownload }} | |||
$(document).ready(function () { | |||
let url = window.location.href; | |||
let urlArr = url.split('/') | |||
userName = urlArr.slice(-5)[0] | |||
repoPath = urlArr.slice(-4)[0] | |||
jobID = urlArr.slice(-1)[0] | |||
}) | |||
function stopBubbling(e) { | |||
e = window.event || e; | |||
if (e.stopPropagation) { | |||
e.stopPropagation(); //阻止事件 冒泡传播 | |||
} else { | |||
e.cancelBubble = true; //ie兼容 | |||
} | |||
} | |||
function showcreate(obj) { | |||
$('.ui.modal.second') | |||
.modal({ | |||
centered: false, | |||
onShow: function () { | |||
$('input[name="Version"]').addClass('model_disabled') | |||
// $('input[name="JobId"]').text(obj.JobName) | |||
$('#JobName').val(obj.DisplayJobName).addClass('model_disabled') | |||
$('input[name="JobId"]').val(obj.JobID) | |||
$('input[name="VersionName"]').val(obj.VersionName).addClass('model_disabled') | |||
$('.ui.dimmer').css({ "background-color": "rgb(136, 136, 136,0.7)" }) | |||
createModelName() | |||
}, | |||
onHide: function () { | |||
document.getElementById("formId").reset(); | |||
$('.ui.dimmer').css({ "background-color": "" }) | |||
$('.ui.error.message').text() | |||
$('.ui.error.message').css('display', 'none') | |||
} | |||
}) | |||
.modal('show') | |||
} | |||
function createModel() { | |||
let url_href = `/${userName}/${repoPath}/modelmanage/create_new_model` | |||
let data = $("#formId").serialize() | |||
$("#mask").css({ "display": "block", "z-index": "9999" }) | |||
$.ajax({ | |||
url: url_href, | |||
type: 'POST', | |||
data: data, | |||
success: function (res) { | |||
location.href = `/${userName}/${repoPath}/modelmanage/show_model` | |||
$('.ui.modal.second').modal('hide') | |||
}, | |||
error: function (xhr) { | |||
// 隐藏 loading | |||
// 只有请求不正常(状态码不为200)才会执行 | |||
$('.ui.error.message').text(xhr.responseText) | |||
$('.ui.error.message').css('display', 'block') | |||
}, | |||
complete: function (xhr) { | |||
$("#mask").css({ "display": "none", "z-index": "1" }) | |||
} | |||
}) | |||
} | |||
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 renderSize(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(0);//保留的小数位数 | |||
return size + unitArr[index]; | |||
} | |||
function refreshStatus(version_name) { | |||
$.get(`/api/v1/repos/${userName}/${repoPath}/grampus/train-job/${jobID}?version_name=${version_name}`, (data) => { | |||
// header status and duration | |||
$(`#${version_name}-duration-span`).text(data.JobDuration) | |||
$(`#${version_name}-status-span span`).text(data.JobStatus) | |||
$(`#${version_name}-status-span i`).attr("class", data.JobStatus) | |||
// detail status and duration | |||
$('#' + version_name + '-duration').text(data.JobDuration) | |||
$('#' + version_name + '-status').text(data.JobStatus) | |||
$('#' + version_name + '-ai_center').text(data.AiCenter) | |||
loadLog(version_name) | |||
}).fail(function (err) { | |||
console.log(err); | |||
}); | |||
stopBubbling(arguments.callee.caller.arguments[0]) | |||
} | |||
function deleteVersion(version_name) { | |||
stopBubbling(arguments.callee.caller.arguments[0]) | |||
let flag = 1; | |||
$('.ui.basic.modal').modal({ | |||
onDeny: function () { | |||
flag = false | |||
}, | |||
onApprove: function () { | |||
$.post(`/api/v1/repos/${userName}/${repoPath}/modelarts/train-job/${jobID}/del_version`, { version_name: version_name }, (data) => { | |||
if (data.VersionListCount === 0) { | |||
location.href = `/${userName}/${repoPath}/modelarts/train-job` | |||
} else { | |||
$('#accordion' + version_name).remove() | |||
} | |||
}).fail(function (err) { | |||
console.log(err); | |||
}); | |||
flag = true | |||
}, | |||
onHidden: function () { | |||
if (flag == false) { | |||
$('.alert').html('您已取消操作').removeClass('alert-success').addClass('alert-danger').show().delay(1500).fadeOut(); | |||
} | |||
} | |||
}) | |||
.modal('show') | |||
} | |||
function stopVersion(version_name) { | |||
stopBubbling(arguments.callee.caller.arguments[0]) | |||
$.post(`/api/v1/repos/${userName}/${repoPath}/modelarts/train-job/${jobID}/stop_version`, { version_name: version_name }, (data) => { | |||
if (data.StatusOK === 0) { | |||
$('#' + version_name + '-stop').addClass('disabled') | |||
refreshStatus(version_name) | |||
} | |||
}).fail(function (err) { | |||
console.log(err); | |||
}); | |||
} | |||
function loadLog(version_name) { | |||
document.getElementById("mask").style.display = "block" | |||
$.get(`/api/v1/repos/${userName}/${repoPath}/grampus/train-job/${jobID}/log?version_name=${version_name}&lines=50&order=asc`, (data) => { | |||
$('input[name=end_line]').val(data.EndLine) | |||
$('input[name=start_line]').val(data.StartLine) | |||
$(`#log_file${version_name}`).text(data.Content) | |||
document.getElementById("mask").style.display = "none" | |||
}).fail(function (err) { | |||
document.getElementById("mask").style.display = "none" | |||
console.log(err); | |||
}); | |||
} | |||
function loadModelFile(version_name, parents, filename, init) { | |||
parents = parents || '' | |||
filename = filename || '' | |||
init = init || '' | |||
$.get(`/api/v1/repos/${userName}/${repoPath}/modelarts/train-job/${jobID}/model_list?version_name=${version_name}&parentDir=${parents}`, (data) => { | |||
$(`#dir_list${version_name}`).empty() | |||
renderDir(data, version_name) | |||
if (init === "init") { | |||
$(`input[name=model${version_name}]`).val("") | |||
$(`input[name=modelback${version_name}]`).val(version_name) | |||
$(`#file_breadcrumb${version_name}`).empty() | |||
let htmlBread = "" | |||
htmlBread += `<div class='active section'>${version_name}</div>` | |||
htmlBread += "<div class='divider'> / </div>" | |||
$(`#file_breadcrumb${version_name}`).append(htmlBread) | |||
} else { | |||
renderBrend(version_name, parents, filename, init) | |||
} | |||
}).fail(function (err) { | |||
console.log(err, version_name); | |||
}); | |||
} | |||
function renderBrend(version_name, parents, filename, init) { | |||
if (init == "folder") { | |||
let htmlBrend = "" | |||
let sectionName = $(`#file_breadcrumb${version_name} .active.section`).text() | |||
let parents1 = $(`input[name=model${version_name}]`).val() | |||
let filename1 = $(`input[name=modelback${version_name}]`).val() | |||
if (parents1 === "") { | |||
$(`#file_breadcrumb${version_name} .active.section`).replaceWith(`<a class='section' onclick="loadModelFile('${version_name}','${parents1}','','init')">${sectionName}</a>`) | |||
} else { | |||
$(`#file_breadcrumb${version_name} .active.section`).replaceWith(`<a class='section' onclick="loadModelFile('${version_name}','${parents1}','${filename1}')">${sectionName}</a>`) | |||
} | |||
htmlBrend += `<div class='active section'>${filename}</div>` | |||
htmlBrend += "<div class='divider'> / </div>" | |||
$(`#file_breadcrumb${version_name}`).append(htmlBrend) | |||
$(`input[name=model${version_name}]`).val(parents) | |||
$(`input[name=modelback${version_name}]`).val(filename) | |||
} else { | |||
$(`input[name=model${version_name}]`).val(parents) | |||
$(`input[name=modelback${version_name}]`).val(filename) | |||
$(`#file_breadcrumb${version_name} a.section:contains(${filename})`).nextAll().remove() | |||
$(`#file_breadcrumb${version_name} a.section:contains(${filename})`).replaceWith(`<div class='active section'>${filename}</div>`) | |||
$(`#file_breadcrumb${version_name} div.section:contains(${filename})`).append("<div class='divider'> / </div>") | |||
} | |||
} | |||
function renderDir(data, version_name) { | |||
let html = "" | |||
html += "<div class='ui grid' style='margin:0;'>" | |||
html += "<div class='row' style='padding: 0;'>" | |||
html += "<div class='ui sixteen wide column' style='padding:1rem;'>" | |||
html += "<div class='dir list'>" | |||
html += "<table id='repo-files-table' class='ui single line table pad20'>" | |||
html += '<tbody>' | |||
// html += "</tbody>" | |||
for (let i = 0; i < data.Dirs.length; i++) { | |||
let dirs_size = renderSize(data.Dirs[i].Size) | |||
html += "<tr>" | |||
html += "<td class='name six wid'>" | |||
html += "<span class='truncate'>" | |||
html += "<span class='octicon octicon-file-directory'>" | |||
html += "</span>" | |||
if (data.Dirs[i].IsDir) { | |||
html += `<a onclick="loadModelFile('${version_name}','${data.Dirs[i].ParenDir}','${data.Dirs[i].FileName}','folder')">` | |||
html += "<span class='fitted'><i class='folder icon' width='16' height='16' aria-hidden='true'></i>" + data.Dirs[i].FileName + "</span>" | |||
} else { | |||
if (downlaodFlag) { | |||
html += `<a href="${location.href}/model_download?version_name=${version_name}&file_name=${data.Dirs[i].FileName}&parent_dir=${data.Dirs[i].ParenDir}">` | |||
} | |||
else { | |||
html += `<a class="disabled">` | |||
} | |||
html += "<span class='fitted'><i class='file icon' width='16' height='16' aria-hidden='true'></i>" + data.Dirs[i].FileName + "</span>" | |||
} | |||
html += '</a>' | |||
html += "</span>" | |||
html += "</td>" | |||
html += "<td class='message seven wide'>" | |||
if (data.Dirs[i].IsDir) { | |||
html += "<span class='truncate has-emoji'></span>" | |||
} else { | |||
html += "<span class='truncate has-emoji'>" + `${dirs_size}` + "</span>" | |||
} | |||
html += "</td>" | |||
html += "<td class='text right age three wide'>" | |||
html += "<span class='truncate has-emoji'>" + data.Dirs[i].ModTime + "</span>" | |||
html += "</td>" | |||
html += "</tr>" | |||
} | |||
html += "</tbody>" | |||
html += "</table>" | |||
html += "</div>" | |||
html += "</div>" | |||
html += "</div>" | |||
html += "</div>" | |||
$(`#dir_list${version_name}`).append(html) | |||
} | |||
function debounce(fn, delay) { | |||
let timer; | |||
return (...args) => { | |||
// 判断定时器是否存在,清除定时器 | |||
if (timer) { | |||
clearTimeout(timer); | |||
} | |||
// 重新调用setTimeout | |||
timer = setTimeout(() => { | |||
fn.apply(this, args); | |||
}, delay); | |||
}; | |||
} | |||
const fn = debounce(logScroll, 500) | |||
function logScroll(version_name) { | |||
let container = document.querySelector(`#log${version_name}`) | |||
let scrollTop = container.scrollTop | |||
let scrollHeight = container.scrollHeight | |||
let clientHeight = container.clientHeight | |||
let scrollLeft = container.scrollLeft | |||
if (((parseInt(scrollTop) + clientHeight == scrollHeight || parseInt(scrollTop) + clientHeight + 1 == scrollHeight || parseInt(scrollTop) + clientHeight - 1 == scrollHeight)) && parseInt(scrollTop) !== 0 && scrollLeft == 0) { | |||
let end_line = $(`#log${version_name} input[name=end_line]`).val() | |||
$.get(`/api/v1/repos/${userName}/${repoPath}/modelarts/train-job/${jobID}/log?version_name=${version_name}&base_line=${end_line}&lines=50&order=desc`, (data) => { | |||
if (data.Lines == 0) { | |||
$(`.message${version_name} #header`).text('您已翻阅至日志底部') | |||
$(`.message${version_name}`).css('display', 'block') | |||
setTimeout(function () { | |||
$(`.message${version_name}`).css('display', 'none') | |||
}, 1000) | |||
} else { | |||
if (end_line === data.EndLine) { | |||
return | |||
} | |||
else { | |||
$(`#log${version_name} input[name=end_line]`).val(data.EndLine) | |||
$(`#log${version_name}`).append('<pre>' + data.Content) | |||
} | |||
} | |||
}).fail(function (err) { | |||
console.log(err); | |||
}); | |||
} | |||
if ([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].includes(scrollTop) && scrollLeft == 0) { | |||
let start_line = $(`#log${version_name} input[name=start_line]`).val() | |||
$.get(`/api/v1/repos/${userName}/${repoPath}/modelarts/train-job/${jobID}/log?version_name=${version_name}&base_line=${start_line}&lines=50&order=asc`, (data) => { | |||
if (data.Lines == 0) { | |||
$(`.message${version_name} #header`).text('您已翻阅至日志顶部') | |||
$(`.message${version_name}`).css('display', 'block') | |||
setTimeout(function () { | |||
$(`.message${version_name}`).css('display', 'none') | |||
}, 1000) | |||
} else { | |||
$(`#log${version_name} input[name=start_line]`).val(data.StartLine) //如果变动就改变所对应的值 | |||
$(`#log${version_name}`).prepend('<pre>' + data.Content) | |||
} | |||
}).fail(function (err) { | |||
console.log(err); | |||
}); | |||
} | |||
} | |||
function scrollAnimation(dom, currentY, targetY, currentX) { | |||
let needScrollTop = targetY - currentY; | |||
let _currentY = currentY; | |||
setTimeout(() => { | |||
// 一次调用滑动帧数,每次调用会不一样 | |||
//取总距离的十分之一 | |||
const dist = Math.ceil(needScrollTop / 10); | |||
_currentY += dist; | |||
//移动一个十分之一 | |||
dom.scrollTo(currentX || 0, _currentY, 'smooth'); | |||
// 如果移动幅度小于十个像素,直接移动,否则递归调用,实现动画效果 | |||
if (needScrollTop > 10 || needScrollTop < -10) { | |||
scrollAnimation(dom, _currentY, targetY) | |||
} else { | |||
dom.scrollTo(0, targetY, 'smooth') | |||
} | |||
}, 1) | |||
} | |||
$('.log_top').click(function () { | |||
// let logContentDom = document.querySelector('.log') | |||
// if(!logContentDom) | |||
// return | |||
// let version_name = $('.log_top').data('version') | |||
let version_name = $(this).data('version') | |||
let logContentDom = document.querySelector(`#log${version_name}`) | |||
$(`#log_file${version_name}`).siblings('pre').remove() | |||
$.get(`/api/v1/repos/${userName}/${repoPath}/grampus/train-job/${jobID}/log?version_name=${version_name}&base_line=&lines=50&order=asc`, (data) => { | |||
$(`#log${version_name} input[name=end_line]`).val(data.EndLine) //如果变动就改变所对应的值 | |||
$(`#log${version_name} input[name=start_line]`).val(data.StartLine) | |||
$(`#log${version_name}`).prepend('<pre>' + data.Content) | |||
$(`.message${version_name} #header`).text('您已翻阅至日志顶部') | |||
$(`.message${version_name}`).css('display', 'block') | |||
setTimeout(function () { | |||
$(`.message${version_name}`).css('display', 'none') | |||
}, 1000) | |||
scrollAnimation(logContentDom, logContentDom.scrollTop, 0); | |||
}) | |||
}) | |||
$('.log_bottom').click(function (e) { | |||
let version_name = $(this).data('version') | |||
let logContentDom = document.querySelector(`#log${version_name}`) | |||
$(`#log_file${version_name}`).siblings('pre').remove() | |||
$.get(`/api/v1/repos/${userName}/${repoPath}/grampus/train-job/${jobID}/log?version_name=${version_name}&base_line=&lines=50&order=desc`, (data) => { | |||
$(`#log${version_name} input[name=end_line]`).val(data.EndLine) //如果变动就改变所对应的值 | |||
$(`#log${version_name} input[name=start_line]`).val(data.StartLine) | |||
$(`#log${version_name}`).append('<pre>' + data.Content) | |||
$.get(`/api/v1/repos/${userName}/${repoPath}/grampus/train-job/${jobID}/log?version_name=${version_name}&base_line=${data.EndLine}&lines=50&order=desc`, (data) => { | |||
if (data.Lines == 0) { | |||
$(`.message${version_name} #header`).text('您已翻阅至日志底部') | |||
$(`.message${version_name}`).css('display', 'block') | |||
setTimeout(function () { | |||
$(`.message${version_name}`).css('display', 'none') | |||
}, 1000) | |||
} else { | |||
if (end_line === data.EndLine) { | |||
return | |||
} | |||
else { | |||
$(`#log${version_name} input[name=end_line]`).val(data.EndLine) | |||
$(`#log${version_name}`).append('<pre>' + data.Content) | |||
} | |||
} | |||
}).fail(function (err) { | |||
console.log(err); | |||
}); | |||
scrollAnimation(logContentDom, logContentDom.scrollTop + 1, logContentDom.scrollHeight - logContentDom.clientHeight); | |||
}) | |||
}) | |||
</script> |
@@ -112,7 +112,7 @@ | |||
<!-- 任务名 --> | |||
<div class="three wide column padding0"> | |||
<a class="title" href='{{if eq .ComputeResource "NPU" }}{{$.Link}}/{{.JobID}}{{else}}{{$.RepoLink}}/cloudbrain/train-job/{{.JobID}}{{end}}' title="{{.DisplayJobName}}" style="font-size: 14px;"> | |||
<a class="title" href='{{if eq .Cloudbrain.Type 1 }}{{$.Link}}/{{.JobID}}{{else if eq .Cloudbrain.Type 0}}{{$.RepoLink}}/cloudbrain/train-job/{{.JobID}}{{else if eq .Cloudbrain.Type 2}}{{$.RepoLink}}/grampus/train-job/{{.JobID}}{{end}}' title="{{.DisplayJobName}}" style="font-size: 14px;"> | |||
<span class="fitted" style="width: 90%;vertical-align: middle;">{{.DisplayJobName}}</span> | |||
</a> | |||
@@ -153,18 +153,18 @@ | |||
<div class="ui compact buttons"> | |||
{{$.CsrfTokenHtml}} | |||
{{if .CanDel}} | |||
<a style="padding: 0.5rem 1rem;" id="ai-stop-{{.JobID}}" class="ui basic ai_stop_version {{if eq .Status "KILLED" "FAILED" "START_FAILED" "KILLING" "COMPLETED" "SUCCEEDED" "STOPPED"}}disabled {{else}} blue {{end}}button" data-repopath='{{$.RepoRelPath}}{{if eq .ComputeResource "NPU"}}/modelarts/train-job{{else}}/cloudbrain/train-job{{end}}' data-jobid="{{.JobID}}" data-version="{{.VersionName}}"> | |||
<a style="padding: 0.5rem 1rem;" id="ai-stop-{{.JobID}}" class="ui basic ai_stop_version {{if eq .Status "KILLED" "FAILED" "START_FAILED" "KILLING" "COMPLETED" "SUCCEEDED" "STOPPED"}}disabled {{else}} blue {{end}}button" data-repopath='{{$.RepoRelPath}}{{if eq .Cloudbrain.Type 1}}/modelarts/train-job{{else if eq .Cloudbrain.Type 0}}/cloudbrain/train-job{{else if eq .Cloudbrain.Type 2}}/grampus/train-job{{end}}' data-jobid="{{.JobID}}" data-version="{{.VersionName}}"> | |||
{{$.i18n.Tr "repo.stop"}} | |||
</a> | |||
{{else}} | |||
<a style="padding: 0.5rem 1rem;" id="ai-stop-{{.JobID}}" class="ui basic disabled button"> | |||
<a class="ui basic disabled button"> | |||
{{$.i18n.Tr "repo.stop"}} | |||
</a> | |||
{{end}} | |||
</div> | |||
<!-- 删除任务 --> | |||
<form class="ui compact buttons" id="delForm-{{.JobID}}" action='{{if eq .ComputeResource "NPU" }}{{$.Link}}/{{.JobID}}{{else}}{{$.RepoLink}}/cloudbrain/train-job/{{.JobID}}{{end}}/del' method="post"> | |||
<form class="ui compact buttons" id="delForm-{{.JobID}}" action='{{if eq .Cloudbrain.Type 1}}{{$.Link}}/{{.JobID}}{{else if eq .Cloudbrain.Type 0}}{{$.RepoLink}}/cloudbrain/train-job/{{.JobID}}{{else if eq .Cloudbrain.Type 2}}{{$.RepoLink}}/grampus/train-job/{{.JobID}}{{end}}/del' method="post"> | |||
<input type="hidden" name="listType" value="{{$.ListType}}"> | |||
{{$.CsrfTokenHtml}} | |||
{{if .CanDel}} | |||
@@ -84,6 +84,19 @@ | |||
<input type="hidden" id="ai_flaver_name" name="flaver_names" value=""> | |||
<h4 class="unite title ui header ">{{.i18n.Tr "repo.modelarts.train_job.basic_info"}}:</h4> | |||
<div class="required unite min_title inline field"> | |||
<label style="font-weight: normal;">{{.i18n.Tr "cloudbrain.resource_cluster"}}</label> | |||
<div class="ui blue mini menu compact selectcloudbrain"> | |||
<a class="active item" href="{{.RepoLink}}/cloudbrain/train-job/create"> | |||
<svg class="svg" sxmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="none" d="M0 0h24v24H0z"></path><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-2.29-2.333A17.9 17.9 0 0 1 8.027 13H4.062a8.008 8.008 0 0 0 5.648 6.667zM10.03 13c.151 2.439.848 4.73 1.97 6.752A15.905 15.905 0 0 0 13.97 13h-3.94zm9.908 0h-3.965a17.9 17.9 0 0 1-1.683 6.667A8.008 8.008 0 0 0 19.938 13zM4.062 11h3.965A17.9 17.9 0 0 1 9.71 4.333 8.008 8.008 0 0 0 4.062 11zm5.969 0h3.938A15.905 15.905 0 0 0 12 4.248 15.905 15.905 0 0 0 10.03 11zm4.259-6.667A17.9 17.9 0 0 1 15.973 11h3.965a8.008 8.008 0 0 0-5.648-6.667z"></path></svg> | |||
{{.i18n.Tr "cloudbrain.resource_cluster_openi"}} | |||
</a> | |||
<a class="item" href="{{.RepoLink}}/grampus/train-job/npu/create"> | |||
<svg class="svg" sxmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="none" d="M0 0h24v24H0z"></path><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-2.29-2.333A17.9 17.9 0 0 1 8.027 13H4.062a8.008 8.008 0 0 0 5.648 6.667zM10.03 13c.151 2.439.848 4.73 1.97 6.752A15.905 15.905 0 0 0 13.97 13h-3.94zm9.908 0h-3.965a17.9 17.9 0 0 1-1.683 6.667A8.008 8.008 0 0 0 19.938 13zM4.062 11h3.965A17.9 17.9 0 0 1 9.71 4.333 8.008 8.008 0 0 0 4.062 11zm5.969 0h3.938A15.905 15.905 0 0 0 12 4.248 15.905 15.905 0 0 0 10.03 11zm4.259-6.667A17.9 17.9 0 0 1 15.973 11h3.965a8.008 8.008 0 0 0-5.648-6.667z"></path></svg> | |||
{{.i18n.Tr "cloudbrain.resource_cluster_c2net"}}(Beta) | |||
</a> | |||
</div> | |||
</div> | |||
<div class="required unite min_title inline field"> | |||
<label style="font-weight: normal;">{{.i18n.Tr "cloudbrain.compute_resource"}}</label> | |||
<div class="ui blue mini menu compact selectcloudbrain"> | |||
<a class="item" href="{{.RepoLink}}/cloudbrain/train-job/create"> | |||
@@ -94,7 +94,7 @@ | |||
</a> | |||
{{else if eq .JobType "TRAIN"}} | |||
<a class="title" | |||
href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}/{{if eq .ComputeResource "NPU"}}modelarts{{else}}cloudbrain{{end}}/train-job/{{$JobID}}" | |||
href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}/{{if eq .Cloudbrain.Type 1}}modelarts{{else if eq .Cloudbrain.Type 0}}cloudbrain{{else if eq .Cloudbrain.Type 2}}grampus{{end}}/train-job/{{$JobID}}" | |||
title="{{.DisplayJobName}}" style="font-size: 14px;"> | |||
<span class="fitted" | |||
style="width: 90%;vertical-align: middle;">{{.DisplayJobName}}</span> | |||
@@ -186,7 +186,7 @@ | |||
{{else}} | |||
<a style="padding: 0.5rem 1rem;" id="ai-stop-{{$JobID}}" | |||
class='ui basic ai_stop_version {{if eq .Status "KILLED" "FAILED" "START_FAILED" "KILLING" "COMPLETED" "STOPPED" "SUCCEEDED" "CREATE_FAILED"}}disabled {{else}} blue {{end}}button' | |||
data-repopath='{{.Repo.OwnerName}}/{{.Repo.Name}}/{{if eq .JobType "INFERENCE"}}modelarts/inference-job{{else if eq .JobType "TRAIN"}}{{if eq .ComputeResource "NPU"}}modelarts/train-job{{else}}cloudbrain/train-job{{end}}{{end}}' | |||
data-repopath='{{.Repo.OwnerName}}/{{.Repo.Name}}/{{if eq .JobType "INFERENCE"}}modelarts/inference-job{{else if eq .JobType "TRAIN"}}{{if eq .Cloudbrain.Type 1}}modelarts/train-job{{else if eq .Cloudbrain.Type 0}}cloudbrain/train-job{{else if eq .Cloudbrain.Type 2}}grampus/train-job{{end}}{{end}}' | |||
data-jobid="{{$JobID}}" data-version="{{.VersionName}}"> | |||
{{$.i18n.Tr "repo.stop"}} | |||
</a> | |||
@@ -203,7 +203,7 @@ | |||
{{end}} | |||
<!-- 删除任务 --> | |||
<form class="ui compact buttons" id="delForm-{{$JobID}}" | |||
action='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .JobType "BENCHMARK"}}/cloudbrain/benchmark{{else if or (eq .JobType "SNN4IMAGENET") (eq .JobType "BRAINSCORE")}}/cloudbrain{{else if eq .JobType "DEBUG"}}{{if eq .ComputeResource "NPU"}}/modelarts/notebook{{else}}/cloudbrain{{end}}{{else if eq .JobType "TRAIN"}}{{if eq .ComputeResource "NPU"}}/modelarts/train-job{{else}}/cloudbrain/train-job{{end}}{{end}}/{{$JobID}}/del?ishomepage=true' | |||
action='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .JobType "BENCHMARK"}}/cloudbrain/benchmark{{else if or (eq .JobType "SNN4IMAGENET") (eq .JobType "BRAINSCORE")}}/cloudbrain{{else if eq .JobType "DEBUG"}}{{if eq .ComputeResource "NPU"}}/modelarts/notebook{{else}}/cloudbrain{{end}}{{else if eq .JobType "TRAIN"}}{{if eq .Cloudbrain.Type 1}}/modelarts/train-job{{else if eq .Cloudbrain.Type 0}}/cloudbrain/train-job{{else if eq .Cloudbrain.Type 2}}/grampus/train-job{{end}}{{end}}/{{$JobID}}/del?ishomepage=true' | |||
method="post"> | |||
{{$.CsrfTokenHtml}} | |||
<a style="padding: 0.5rem 1rem;margin-left:0.2rem" id="ai-delete-{{$JobID}}" | |||
@@ -86,6 +86,10 @@ | |||
{{$.i18n.Tr "action.task_createmodel" .GetRepoLink .RefName .RefName | Str2html}} | |||
{{else if eq .GetOpType 31}} | |||
{{$.i18n.Tr "action.task_gputrainjob" .GetRepoLink .Content .RefName | Str2html}} | |||
{{else if eq .GetOpType 32}} | |||
{{$.i18n.Tr "action.task_c2netnputrainjob" .GetRepoLink .Content .RefName | Str2html}} | |||
{{else if eq .GetOpType 33}} | |||
{{$.i18n.Tr "action.task_c2netgputrainjob" .GetRepoLink .Content .RefName | Str2html}} | |||
{{end}} | |||
</p> | |||
{{if or (eq .GetOpType 5) (eq .GetOpType 18)}} | |||
@@ -672,7 +672,7 @@ export default { | |||
}, | |||
filters:{ | |||
transformType(val){ | |||
if(val==0){ | |||
if(val==0 || val==2){ | |||
return "GPU" | |||
} | |||
}, | |||
@@ -0,0 +1,216 @@ | |||
<template> | |||
<div class="inline required field" :class="{ 'unite': benchmarkNew, 'min_title': benchmarkNew}"> | |||
<label v-if="benchmarkNew" style="font-weight: normal;">镜像</label> | |||
<label v-else>镜像</label> | |||
<span v-if="benchmarkNew"> </span> | |||
<input v-if="benchmarkNew" type="text" name="image" :value="imageAddress" style="width: 48.5%;" | |||
placeholder="选择镜像或输入镜像地址"> | |||
<input v-else type="text" name="image" :value="imageAddress" placeholder="选择镜像或输入镜像地址"> | |||
<el-button type="text" @click="dialogVisible = true" icon="el-icon-plus" style="color: #0366d6;">选择镜像 | |||
</el-button> | |||
<el-dialog title="选择镜像" :visible.sync="dialogVisible" width="50%"> | |||
<div class="ui icon input" style="z-index: 9999;position: absolute;right: 50px;height:30px;"> | |||
<i class="search icon" style="cursor: pointer;pointer-events:auto"></i> | |||
<input type="text" placeholder="搜镜像Tag/描述/标签..." v-model="search"> | |||
</div> | |||
<el-tabs v-model="activeName" @tab-click="handleClick"> | |||
<el-tab-pane label="公开镜像" name="first" v-loading="loadingPublic"> | |||
<div style="display: flex;align-items: center;justify-content: space-between;padding: 1rem 0;border-bottom:1px solid #F5F5F5" | |||
v-for="(publicData,index) in tableDataPublic" :key="index"> | |||
<div style="width: 90%;"> | |||
<div style="display: flex;align-items: center;justify-content: space-between;"> | |||
<div style="display: flex;align-items: center;"> | |||
<span class="panel_dataset_name text-over" | |||
style="margin-left: 0;">{{publicData.tag}} </span> | |||
<img v-if="publicData.type==5" src="/img/jian.svg" style="margin-left: 0.5rem;"> | |||
</div> | |||
<div v-if="!!publicData.topics" class="text-over"> | |||
<span v-for="(topic,index) in publicData.topics" | |||
class="ui repo-topic label topic">{{topic}}</span> | |||
</div> | |||
</div> | |||
<div style="margin-top: 8px;display: flex;"> | |||
<a v-if="publicData.relAvatarLink||publicData.userName" :title="publicData.userName" | |||
style="cursor: default;"> | |||
<img class="ui avatar mini image" style="width: 20px;height: 20px;" | |||
:src="publicData.relAvatarLink"> | |||
</a> | |||
<a v-else><img class="ui avatar mini image" title="Ghost" src="/user/avatar/ghost/-1" | |||
style="width: 20px;height: 20px;"></a> | |||
<span class="panel_datset_desc">{{publicData.description}}</span> | |||
</div> | |||
</div> | |||
<div> | |||
<button class="ui primary basic button mini" | |||
@click.stop.prevent="selectImages(publicData.place,publicData.tag)">使用</button> | |||
</div> | |||
</div> | |||
<div class="ui container" style="margin-top:50px;text-align:center"> | |||
<el-pagination background @current-change="handleCurrentChangePublic" | |||
:current-page="currentPagePublic" :page-size="pageSizePublic" | |||
layout="total, prev, pager, next" :total="totalNumPublic"> | |||
</el-pagination> | |||
</div> | |||
</el-tab-pane> | |||
</el-tabs> | |||
</el-dialog> | |||
</div> | |||
</template> | |||
<script> | |||
const { _AppSubUrl, _StaticUrlPrefix, csrf } = window.config; | |||
export default { | |||
components: { | |||
}, | |||
data() { | |||
return { | |||
dialogVisible: false, | |||
benchmarkNew: false, | |||
imageAddress: '', | |||
activeName: 'first', | |||
search: '', | |||
checked: false, | |||
currentPagePublic: 1, | |||
pageSizePublic: 5, | |||
totalNumPublic: 0, | |||
paramsPublic: { page: 1, pageSize: 5, q: '', recommend: false,cloudbrainType: 2 }, | |||
tableDataPublic: [], | |||
loadingPublic: false, | |||
}; | |||
}, | |||
methods: { | |||
handleClick(tab, event) { | |||
this.search = '' | |||
if (tab.name == "first") { | |||
this.paramsPublic.q = '' | |||
this.getImageListPublic() | |||
} | |||
}, | |||
tableHeaderStyle({ row, column, rowIndex, columnIndex }) { | |||
if (rowIndex === 0) { | |||
return 'background:#f5f5f6;color:#606266' | |||
} | |||
}, | |||
handleCurrentChangePublic(val) { | |||
this.paramsPublic.page = val | |||
this.getImageListPublic() | |||
}, | |||
getImageListPublic() { | |||
this.loadingPublic = true | |||
this.$axios.get('/explore/images/public', { | |||
params: this.paramsPublic | |||
}).then((res) => { | |||
this.totalNumPublic = res.data.count | |||
this.tableDataPublic = res.data.images | |||
this.loadingPublic = false | |||
}) | |||
}, | |||
searchName() { | |||
if (this.activeName == 'first') { | |||
this.paramsPublic.q = this.search | |||
this.paramsPublic.page = 1 | |||
this.getImageListPublic() | |||
} | |||
}, | |||
selectImages(place) { | |||
this.imageAddress = place | |||
this.dialogVisible = false | |||
}, | |||
}, | |||
watch: { | |||
search(val) { | |||
if (this.activeName == 'first') { | |||
this.paramsPublic.q = val | |||
this.getImageListPublic() | |||
} | |||
} | |||
}, | |||
mounted() { | |||
this.getImageListPublic() | |||
if (location.href.indexOf('benchmark') !== -1 || location.href.indexOf('train-job') !== -1) { | |||
this.benchmarkNew = true | |||
} | |||
}, | |||
created() { | |||
} | |||
}; | |||
</script> | |||
<style scoped> | |||
.header-wrapper { | |||
background-color: #f5f5f6; | |||
padding-top: 15px; | |||
} | |||
.image_text { | |||
padding: 25px 0 55px 0; | |||
} | |||
#header { | |||
position: relative; | |||
top: -40px; | |||
} | |||
#success { | |||
background-color: #5bb973; | |||
color: white; | |||
} | |||
.text-over { | |||
overflow: hidden; | |||
text-overflow: ellipsis; | |||
vertical-align: middle; | |||
white-space: nowrap; | |||
} | |||
.image_title { | |||
display: inline-block; | |||
width: 80%; | |||
cursor: default; | |||
color: rgb(66, 98, 144); | |||
} | |||
.image_desc { | |||
-webkit-line-clamp: 2; | |||
-webkit-box-orient: vertical; | |||
display: -webkit-box; | |||
text-overflow: ellipsis; | |||
overflow: hidden; | |||
} | |||
.heart-stroke { | |||
stroke: #666; | |||
stroke-width: 2; | |||
fill: #fff | |||
} | |||
.stars_active { | |||
fill: #FA8C16 !important; | |||
stroke: #FA8C16 !important | |||
} | |||
</style> |
@@ -1,6 +1,7 @@ | |||
import Images from '../components/images/Images.vue'; | |||
import adminImages from '../components/images/adminImages.vue'; | |||
import selectImages from '../components/images/selectImages.vue'; | |||
import selectGrampusImages from '../components/images/selectGrampusImages.vue'; | |||
import Vue from 'vue'; | |||
export default async function initImage(){ | |||
function validate() { | |||
@@ -209,7 +210,19 @@ export default async function initImage(){ | |||
render: h => h(selectImages) | |||
}); | |||
} | |||
function initVueselectGrampusImages() { | |||
const el = document.getElementById('images-new-grampus'); | |||
if (!el) { | |||
return; | |||
} | |||
new Vue({ | |||
el:el, | |||
render: h => h(selectGrampusImages) | |||
}); | |||
} | |||
initVueImages() | |||
initVueAdminImages() | |||
initVueselectImages() | |||
initVueselectGrampusImages() | |||
} |
@@ -1,74 +1,100 @@ | |||
/** | |||
* LetterAvatar | |||
* | |||
* | |||
* Artur Heinze | |||
* Create Letter avatar based on Initials | |||
* based on https://gist.github.com/leecrossley/6027780 | |||
*/ | |||
(function(w, d){ | |||
function LetterAvatar (name, size, color) { | |||
name = name || ''; | |||
size = size || 60; | |||
var colours = [ | |||
"#1abc9c", "#2ecc71", "#3498db", "#9b59b6", "#34495e", "#16a085", "#27ae60", "#2980b9", "#8e44ad", "#2c3e50", | |||
"#f1c40f", "#e67e22", "#e74c3c", "#00bcd4", "#95a5a6", "#f39c12", "#d35400", "#c0392b", "#bdc3c7", "#7f8c8d" | |||
], | |||
nameSplit = String(name).split(' '), | |||
initials, charIndex, colourIndex, canvas, context, dataURI; | |||
if (nameSplit.length == 1) { | |||
initials = nameSplit[0] ? nameSplit[0].charAt(0):'?'; | |||
} else { | |||
initials = nameSplit[0].charAt(0) + nameSplit[1].charAt(0); | |||
} | |||
if (w.devicePixelRatio) { | |||
size = (size * w.devicePixelRatio); | |||
} | |||
charIndex = (initials == '?' ? 72 : initials.charCodeAt(0)) - 64; | |||
colourIndex = charIndex % 20; | |||
canvas = d.createElement('canvas'); | |||
canvas.width = size; | |||
canvas.height = size; | |||
context = canvas.getContext("2d"); | |||
context.fillStyle = color ? color : colours[colourIndex - 1]; | |||
context.fillRect (0, 0, canvas.width, canvas.height); | |||
context.font = Math.round(canvas.width/2)+"px 'Microsoft Yahei'"; | |||
context.textAlign = "center"; | |||
context.fillStyle = "#FFF"; | |||
context.fillText(initials, size / 2, size / 1.5); | |||
dataURI = canvas.toDataURL(); | |||
canvas = null; | |||
return dataURI; | |||
(function (w, d) { | |||
function LetterAvatar(name, size, color) { | |||
name = name || ""; | |||
size = size || 60; | |||
var colours = [ | |||
"#1abc9c", | |||
"#2ecc71", | |||
"#3498db", | |||
"#9b59b6", | |||
"#34495e", | |||
"#16a085", | |||
"#27ae60", | |||
"#2980b9", | |||
"#8e44ad", | |||
"#2c3e50", | |||
"#f1c40f", | |||
"#e67e22", | |||
"#e74c3c", | |||
"#00bcd4", | |||
"#95a5a6", | |||
"#f39c12", | |||
"#d35400", | |||
"#c0392b", | |||
"#bdc3c7", | |||
"#7f8c8d", | |||
], | |||
nameSplit = String(name).split(" "), | |||
initials, | |||
charIndex, | |||
colourIndex, | |||
canvas, | |||
context, | |||
dataURI; | |||
if (nameSplit.length == 1) { | |||
initials = nameSplit[0] ? nameSplit[0].charAt(0) : "?"; | |||
} else { | |||
initials = nameSplit[0].charAt(0) + nameSplit[1].charAt(0); | |||
} | |||
let initials1 = initials.toUpperCase(); | |||
if (w.devicePixelRatio) { | |||
size = size * w.devicePixelRatio; | |||
} | |||
LetterAvatar.transform = function() { | |||
Array.prototype.forEach.call(d.querySelectorAll('img[avatar]'), function(img, name, color) { | |||
name = img.getAttribute('avatar'); | |||
color = img.getAttribute('color'); | |||
img.src = LetterAvatar(name, img.getAttribute('width'), color); | |||
img.removeAttribute('avatar'); | |||
img.setAttribute('alt', name); | |||
}); | |||
}; | |||
// AMD support | |||
if (typeof define === 'function' && define.amd) { | |||
define(function () { return LetterAvatar; }); | |||
charIndex = (initials == "?" ? 72 : initials.charCodeAt(0)) - 64; | |||
colourIndex = charIndex % 20; | |||
canvas = d.createElement("canvas"); | |||
canvas.width = size; | |||
canvas.height = size; | |||
context = canvas.getContext("2d"); | |||
context.fillStyle = color ? color : colours[colourIndex - 1]; | |||
context.fillRect(0, 0, canvas.width, canvas.height); | |||
context.font = Math.round(canvas.width / 2) + "px 'Microsoft Yahei'"; | |||
context.textAlign = "center"; | |||
context.fillStyle = "#FFF"; | |||
context.fillText(initials1, size / 2, size / 1.5); | |||
dataURI = canvas.toDataURL(); | |||
canvas = null; | |||
return dataURI; | |||
} | |||
LetterAvatar.transform = function () { | |||
Array.prototype.forEach.call( | |||
d.querySelectorAll("img[avatar]"), | |||
function (img, name, color) { | |||
name = img.getAttribute("avatar"); | |||
color = img.getAttribute("color"); | |||
img.src = LetterAvatar(name, img.getAttribute("width"), color); | |||
img.removeAttribute("avatar"); | |||
img.setAttribute("alt", name); | |||
} | |||
); | |||
}; | |||
// AMD support | |||
if (typeof define === "function" && define.amd) { | |||
define(function () { | |||
return LetterAvatar; | |||
}); | |||
// CommonJS and Node.js module support. | |||
} else if (typeof exports !== 'undefined') { | |||
// Support Node.js specific `module.exports` (which can be a function) | |||
if (typeof module != 'undefined' && module.exports) { | |||
exports = module.exports = LetterAvatar; | |||
} | |||
// But always support CommonJS module 1.1.1 spec (`exports` cannot be a function) | |||
exports.LetterAvatar = LetterAvatar; | |||
} else { | |||
window.LetterAvatar = LetterAvatar; | |||
d.addEventListener('DOMContentLoaded', function(event) { | |||
LetterAvatar.transform(); | |||
}); | |||
} else if (typeof exports !== "undefined") { | |||
// Support Node.js specific `module.exports` (which can be a function) | |||
if (typeof module != "undefined" && module.exports) { | |||
exports = module.exports = LetterAvatar; | |||
} | |||
})(window, document); | |||
// But always support CommonJS module 1.1.1 spec (`exports` cannot be a function) | |||
exports.LetterAvatar = LetterAvatar; | |||
} else { | |||
window.LetterAvatar = LetterAvatar; | |||
d.addEventListener("DOMContentLoaded", function (event) { | |||
LetterAvatar.transform(); | |||
}); | |||
} | |||
})(window, document); |
@@ -3787,7 +3787,7 @@ function initVueDataset() { | |||
} | |||
let link = $('#square-link').data('link') | |||
let repolink = $('.dataset-repolink').data('repolink') | |||
let cloudbrainType = $('.dataset-repolink').data('cloudranin-type') | |||
let datasetType = $('.dataset-repolink').data('dataset-type') | |||
const clearBtn = document.getElementsByClassName("clear_dataset_value"); | |||
const params = new URLSearchParams(location.search) | |||
for (let i = 0; i < clearBtn.length; i++) { | |||
@@ -3872,7 +3872,7 @@ function initVueDataset() { | |||
page: 1, | |||
totalnums: 0, | |||
repolink: '', | |||
cloudbrainType: 0, | |||
datasetType: 0, | |||
dataset_uuid: '', | |||
dataset_name: '', | |||
loadingDataIndex: false, | |||
@@ -3931,8 +3931,9 @@ function initVueDataset() { | |||
this.getTypeList() | |||
if (!!document.getElementById('dataset-repolink-init')) { | |||
this.cloudbrainType = location.href.indexOf('cloudbrain') !== -1 ? 0 : 1 | |||
this.getCurrentRepoDataset(this.repolink, this.cloudbrainType) | |||
// this.datasetType = location.href.indexOf('cloudbrain') !== -1 ? 0 : 1 | |||
this.datasetType = $('#dataset-repolink-init').data("dataset-type") | |||
this.getCurrentRepoDataset(this.repolink, this.datasetType) | |||
} | |||
const params = new URLSearchParams(location.search) | |||
@@ -3959,7 +3960,7 @@ function initVueDataset() { | |||
this.licenseLists = licenseLists | |||
this.descfile = dataset_file_desc | |||
this.repolink = repolink | |||
this.cloudbrainType = cloudbrainType | |||
this.datasetType = datasetType | |||
}, | |||
methods: { | |||
copyUrl(url) { | |||
@@ -3983,17 +3984,17 @@ function initVueDataset() { | |||
this.page = val | |||
switch (this.activeName) { | |||
case 'first': | |||
this.getCurrentRepoDataset(this.repolink, this.cloudbrainType) | |||
this.getCurrentRepoDataset(this.repolink, this.datasetType) | |||
break | |||
case 'second': | |||
this.getMyDataset(this.repolink, this.cloudbrainType) | |||
this.getMyDataset(this.repolink, this.datasetType) | |||
break | |||
case 'third': | |||
this.getPublicDataset(this.repolink, this.cloudbrainType) | |||
this.getPublicDataset(this.repolink, this.datasetType) | |||
break | |||
case 'fourth': | |||
this.getStarDataset(this.repolink, this.cloudbrainType) | |||
this.getStarDataset(this.repolink, this.datasetType) | |||
break | |||
} | |||
@@ -4332,16 +4333,16 @@ function initVueDataset() { | |||
refreshStatusDataset() { | |||
switch (this.activeName) { | |||
case 'first': | |||
this.getCurrentRepoDataset(this.repolink, this.cloudbrainType) | |||
this.getCurrentRepoDataset(this.repolink, this.datasetType) | |||
break | |||
case 'second': | |||
this.getMyDataset(this.repolink, this.cloudbrainType) | |||
this.getMyDataset(this.repolink, this.datasetType) | |||
break | |||
case 'third': | |||
this.getPublicDataset(this.repolink, this.cloudbrainType) | |||
this.getPublicDataset(this.repolink, this.datasetType) | |||
break | |||
case 'fourth': | |||
this.getStarDataset(this.repolink, this.cloudbrainType) | |||
this.getStarDataset(this.repolink, this.datasetType) | |||
break | |||
} | |||
}, | |||
@@ -4443,19 +4444,19 @@ function initVueDataset() { | |||
switch (this.activeName) { | |||
case 'first': | |||
this.page = 1 | |||
this.getCurrentRepoDataset(this.repolink, this.cloudbrainType) | |||
this.getCurrentRepoDataset(this.repolink, this.datasetType) | |||
break | |||
case 'second': | |||
this.page = 1 | |||
this.getMyDataset(this.repolink, this.cloudbrainType) | |||
this.getMyDataset(this.repolink, this.datasetType) | |||
break | |||
case 'third': | |||
this.page = 1 | |||
this.getPublicDataset(this.repolink, this.cloudbrainType) | |||
this.getPublicDataset(this.repolink, this.datasetType) | |||
break | |||
case 'fourth': | |||
this.page = 1 | |||
this.getStarDataset(this.repolink, this.cloudbrainType) | |||
this.getStarDataset(this.repolink, this.datasetType) | |||
break | |||
} | |||
} | |||
@@ -4465,19 +4466,19 @@ function initVueDataset() { | |||
switch (this.activeName) { | |||
case 'first': | |||
this.page = 1 | |||
this.getCurrentRepoDataset(this.repolink, this.cloudbrainType) | |||
this.getCurrentRepoDataset(this.repolink, this.datasetType) | |||
break | |||
case 'second': | |||
this.page = 1 | |||
this.getMyDataset(this.repolink, this.cloudbrainType) | |||
this.getMyDataset(this.repolink, this.datasetType) | |||
break | |||
case 'third': | |||
this.page = 1 | |||
this.getPublicDataset(this.repolink, this.cloudbrainType) | |||
this.getPublicDataset(this.repolink, this.datasetType) | |||
break | |||
case 'fourth': | |||
this.page = 1 | |||
this.getStarDataset(this.repolink, this.cloudbrainType) | |||
this.getStarDataset(this.repolink, this.datasetType) | |||
break | |||
} | |||
} | |||