Reviewed-on: https://git.openi.org.cn/OpenI/aiforge/pulls/1598tags/v1.22.2.2
@@ -44,6 +44,12 @@ | |||
-webkit-line-clamp: 2; | |||
-webkit-box-orient: vertical; | |||
} | |||
.ui.label{ | |||
font-weight: normal; | |||
} | |||
.active { | |||
color: #0366D6 !important; | |||
} | |||
.opacity5{ opacity:0.5;} | |||
.radius15{ border-radius:1.5rem !important; } | |||
@@ -179,7 +185,7 @@ | |||
.homenews .ui.list>.item>.content{ | |||
color: #E8E8E8; | |||
line-height: 1.8em; | |||
width: calc(100% - 3.25em) !important; | |||
width: calc(100% - 3.75em) !important; | |||
} | |||
.homenews .ui.list>.item{ | |||
padding: 0; | |||
@@ -250,9 +256,13 @@ | |||
box-shadow: none !important; | |||
} | |||
.homeorg-list .card .ui.small.header .content{ | |||
width: calc(100% - 3.25em); | |||
width: calc(100% - 3.75em); | |||
} | |||
.homepro-list{ | |||
.homepro-tit{ | |||
z-index: 9; | |||
position: relative; | |||
} | |||
.homepro-list, .homeorg-list{ | |||
position: relative; | |||
z-index: 9; | |||
padding: 1.0em 1.0em 3.0em; | |||
@@ -261,42 +271,156 @@ | |||
.homepro-list .ui.card{ | |||
border-radius: 15px; | |||
background-color: #FFF; | |||
box-shadow: 0px 5px 10px 0px rgba(105, 192, 255, 30); | |||
border: 1px solid rgba(105, 192, 255, 40); | |||
box-shadow: 0px 5px 10px 0px rgba(105, 192, 255, .3); | |||
border: 1px solid rgba(105, 192, 255, .4); | |||
min-height: 10.8em; | |||
} | |||
.homepro-list .ui.card>.content>.header{ | |||
line-height: 40px !important; | |||
} | |||
.homepro-list .swiper-pagination-bullet-active{ | |||
.homepro-list .swiper-pagination-bullet-active, .homeorg-list .swiper-pagination-bullet-active{ | |||
width: 40px; | |||
border-radius: 4px; | |||
border-radius: 4px; | |||
} | |||
.i-env > div{ | |||
position: relative; | |||
} | |||
/**seach**/ | |||
/**搜索导航条适配窄屏**/ | |||
.seachnav{ | |||
overflow-x: auto; | |||
overflow-y: hidden; | |||
scrollbar-width: none; /* firefox */ | |||
-ms-overflow-style: none; /* IE 10+ */ | |||
} | |||
.seachnav::-webkit-scrollbar { | |||
display: none; /* Chrome Safari */ | |||
} | |||
.ui.green.button, .ui.green.buttons .button{ | |||
background-color: #5BB973; | |||
} | |||
.seach .repos--seach{ | |||
padding-bottom: 0; | |||
border-bottom: none; | |||
} | |||
.seach .ui.secondary.pointing.menu{ | |||
border-bottom: none; | |||
} | |||
.seach .ui.secondary.pointing.menu .item > i{ | |||
margin-right: 5px; | |||
} | |||
.seach .ui.secondary.pointing.menu .active.item{ | |||
border-bottom-width: 2px; | |||
margin: 0 0 -1px; | |||
} | |||
.seach .ui.menu .active.item>.label { | |||
background: #1684FC; | |||
color: #FFF; | |||
} | |||
.seach .ui.menu .item>.label:not(.active.item>.label) { | |||
background: #e8e8e8; | |||
color: rgba(0,0,0,.6); | |||
} | |||
.highlight{ | |||
color: red; | |||
} | |||
.ui.list .list>.item>img.image+.content, .ui.list>.item>img.image+.content { | |||
width: calc(100% - 3.0em); | |||
margin-left: 0; | |||
} | |||
.seach .ui.list .list>.item .header, .seach .ui.list>.item .header{ | |||
margin-bottom: 0.5em; | |||
font-size: 1.4rem !important; | |||
font-weight: normal; | |||
} | |||
.seach .time, .seach .time a{ | |||
font-size: 12px; | |||
color: grey; | |||
} | |||
.seach .list .item.members .ui.avatar.image { | |||
width: 3.2em; | |||
height: 3.2em; | |||
} | |||
.ui.list .list>.item.members>img.image+.content, .ui.list>.item.members>img.image+.content { | |||
width: calc(100% - 4.0em); | |||
margin-left: 0; | |||
} | |||
@media only screen and (max-width: 767px) { | |||
.am-mt-30{ margin-top: 1.5rem !important;} | |||
.ui.secondary.hometop.segment{ | |||
margin-bottom: 2.0rem; | |||
margin-bottom: 5.0rem; | |||
} | |||
.bannerpic, .i-code-pic{ | |||
.bannerpic{ | |||
display: none; | |||
} | |||
.i-code h2::before { | |||
left: calc(-5.0rem + 6px); | |||
#homenews{ | |||
bottom: -3em; | |||
} | |||
.i-code h2.am-bw::before{ | |||
left: calc(-4.0rem + 6px); | |||
#homenews > p { | |||
margin-left: 1.0em; | |||
} | |||
.homenews{ | |||
padding-left: 1.3em !important; | |||
border-radius: 1.5em; | |||
} | |||
.homenews::before{ | |||
left: 2em; | |||
} | |||
.homepro-tit > p{ | |||
background: #FFF; | |||
} | |||
.homeorg{ | |||
padding-left: 3.5em; | |||
} | |||
.homeorg-tit::after { | |||
left: -2.3em; | |||
} | |||
.homeorg-list{ | |||
margin: 0 0 2.0em !important; | |||
} | |||
.homeorg-list > .column{ | |||
width: 3em !important; | |||
margin-left: -0.5em; | |||
padding: 0.5rem 0 0 !important; | |||
} | |||
.homeorg-list .card{ | |||
background: none !important; | |||
} | |||
.homeorg-list .card > .content{ | |||
padding: 0 !important; | |||
} | |||
.homeorg-list > .column .card .ui.header>img{ | |||
width: 3.0em; | |||
height: 3.0em; | |||
border-radius: 2.0em; | |||
border: 2px solid #FFF; | |||
} | |||
.homeorg-list > .column .card .ui.header > .content{ | |||
display: none; | |||
} | |||
.leftline01{ | |||
width: calc(50% - 4.0rem); | |||
width: 4.0em; | |||
bottom: 4em; | |||
border-radius: 0 0 0 3.0em; | |||
} | |||
.leftline02{ | |||
left: calc(50% - 1.0rem); | |||
top: calc(-3.5rem - 2px); | |||
.leftline02, .leftline02-2{ | |||
left: 6.0em; | |||
top: calc(-4.0em - 2px); | |||
border-radius: 0 3.0em 3.0em 0; | |||
width: calc(50% - 6.0em); | |||
} | |||
.leftline02-2 { | |||
width: calc(50% - 8.0em); | |||
} | |||
.i-env .ui.cards>.card>.content .description{ | |||
display: none; | |||
} | |||
} | |||
@@ -49,6 +49,14 @@ const ( | |||
ActionApprovePullRequest // 21 | |||
ActionRejectPullRequest // 22 | |||
ActionCommentPull // 23 | |||
ActionUploadAttachment //24 | |||
ActionCreateDebugGPUTask //25 | |||
ActionCreateDebugNPUTask //26 | |||
ActionCreateTrainTask //27 | |||
ActionCreateInferenceTask // 28 | |||
ActionCreateBenchMarkTask //29 | |||
ActionCreateNewModelTask //30 | |||
) | |||
// Action represents user operation type and other information to | |||
@@ -88,12 +88,25 @@ func (a *Attachment) APIFormat() *api.Attachment { | |||
Size: a.Size, | |||
UUID: a.UUID, | |||
DownloadURL: a.DownloadURL(), | |||
S3DownloadURL: a.S3DownloadURL(), | |||
} | |||
} | |||
// DownloadURL returns the download url of the attached file | |||
func (a *Attachment) DownloadURL() string { | |||
return fmt.Sprintf("%sattachments/%s", setting.AppURL, a.UUID) | |||
return fmt.Sprintf("%sattachments/%s?type=%d", setting.AppURL, a.UUID, a.Type) | |||
} | |||
// S3DownloadURL returns the s3 download url of the attached file | |||
func (a *Attachment) S3DownloadURL() string { | |||
url := "" | |||
if a.Type == TypeCloudBrainOne { | |||
url, _ = storage.Attachments.PresignedGetURL(setting.Attachment.Minio.BasePath+AttachmentRelativePath(a.UUID), a.Name) | |||
} else if a.Type == TypeCloudBrainTwo { | |||
url, _ = storage.ObsGetPreSignedUrl(a.UUID, a.Name) | |||
} | |||
return url | |||
} | |||
// AttachmentRelativePath returns the relative path | |||
@@ -102,7 +102,7 @@ type Cloudbrain struct { | |||
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` | |||
Duration int64 | |||
TrainJobDuration string | |||
Image string //GPU镜像名称 | |||
Image string //镜像名称 | |||
GpuQueue string //GPU类型即GPU队列 | |||
ResourceSpecId int //GPU规格id | |||
DeletedAt time.Time `xorm:"deleted"` | |||
@@ -219,17 +219,20 @@ type GetImagesPayload struct { | |||
type CloudbrainsOptions struct { | |||
ListOptions | |||
RepoID int64 // include all repos if empty | |||
UserID int64 | |||
JobID string | |||
SortType string | |||
CloudbrainIDs []int64 | |||
// JobStatus CloudbrainStatus | |||
RepoID int64 // include all repos if empty | |||
UserID int64 | |||
JobID string | |||
SortType string | |||
CloudbrainIDs []int64 | |||
JobStatus []string | |||
JobStatusNot bool | |||
Keyword string | |||
Type int | |||
JobTypes []string | |||
VersionName string | |||
IsLatestVersion string | |||
JobTypeNot bool | |||
NeedRepoInfo bool | |||
} | |||
type TaskPod struct { | |||
@@ -449,6 +452,16 @@ type FlavorInfo struct { | |||
Desc string `json:"desc"` | |||
} | |||
type ImageInfosModelArts struct { | |||
ImageInfo []*ImageInfoModelArts `json:"image_info"` | |||
} | |||
type ImageInfoModelArts struct { | |||
Id string `json:"id"` | |||
Value string `json:"value"` | |||
Desc string `json:"desc"` | |||
} | |||
type PoolInfos struct { | |||
PoolInfo []*PoolInfo `json:"pool_info"` | |||
} | |||
@@ -1072,16 +1085,39 @@ func Cloudbrains(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int64, error) { | |||
} | |||
if (opts.IsLatestVersion) != "" { | |||
cond = cond.And( | |||
builder.Eq{"cloudbrain.is_latest_version": opts.IsLatestVersion}, | |||
) | |||
cond = cond.And(builder.Or(builder.And(builder.Eq{"cloudbrain.is_latest_version": opts.IsLatestVersion}, builder.Eq{"cloudbrain.job_type": "TRAIN"}), builder.Neq{"cloudbrain.job_type": "TRAIN"})) | |||
} | |||
if len(opts.CloudbrainIDs) > 0 { | |||
cond = cond.And(builder.In("cloudbrain.id", opts.CloudbrainIDs)) | |||
} | |||
count, err := sess.Where(cond).Count(new(Cloudbrain)) | |||
if len(opts.JobStatus) > 0 { | |||
if opts.JobStatusNot { | |||
cond = cond.And( | |||
builder.NotIn("cloudbrain.status", opts.JobStatus), | |||
) | |||
} else { | |||
cond = cond.And( | |||
builder.In("cloudbrain.status", opts.JobStatus), | |||
) | |||
} | |||
} | |||
var count int64 | |||
var err error | |||
condition := "cloudbrain.user_id = `user`.id" | |||
if len(opts.Keyword) == 0 { | |||
count, err = sess.Where(cond).Count(new(Cloudbrain)) | |||
} else { | |||
lowerKeyWord := strings.ToLower(opts.Keyword) | |||
cond = cond.And(builder.Or(builder.Like{"LOWER(cloudbrain.job_name)", lowerKeyWord}, builder.Like{"`user`.lower_name", lowerKeyWord})) | |||
count, err = sess.Table(&Cloudbrain{}).Where(cond). | |||
Join("left", "`user`", condition).Count(new(CloudbrainInfo)) | |||
} | |||
if err != nil { | |||
return nil, 0, fmt.Errorf("Count: %v", err) | |||
} | |||
@@ -1099,11 +1135,25 @@ func Cloudbrains(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int64, error) { | |||
sess.OrderBy("cloudbrain.created_unix DESC") | |||
cloudbrains := make([]*CloudbrainInfo, 0, setting.UI.IssuePagingNum) | |||
if err := sess.Table(&Cloudbrain{}).Where(cond). | |||
Join("left", "`user`", "cloudbrain.user_id = `user`.id"). | |||
Join("left", "`user`", condition). | |||
Find(&cloudbrains); err != nil { | |||
return nil, 0, fmt.Errorf("Find: %v", err) | |||
} | |||
if opts.NeedRepoInfo { | |||
var ids []int64 | |||
for _, task := range cloudbrains { | |||
ids = append(ids, task.RepoID) | |||
} | |||
repositoryMap, err := GetRepositoriesMapByIDs(ids) | |||
if err == nil { | |||
for _, task := range cloudbrains { | |||
task.Repo = repositoryMap[task.RepoID] | |||
} | |||
} | |||
} | |||
return cloudbrains, count, nil | |||
} | |||
@@ -136,6 +136,7 @@ func init() { | |||
new(AiModelManage), | |||
new(OfficialTag), | |||
new(OfficialTagRepos), | |||
new(WechatBindLog), | |||
) | |||
tablesStatistic = append(tablesStatistic, | |||
@@ -12,6 +12,7 @@ type RepoStatistic struct { | |||
ID int64 `xorm:"pk autoincr" json:"-"` | |||
RepoID int64 `xorm:"unique(s) NOT NULL" json:"repo_id"` | |||
Name string `xorm:"INDEX" json:"name"` | |||
Alias string `xorm:"INDEX" json:"alias"` | |||
OwnerName string `json:"ownerName"` | |||
IsPrivate bool `json:"isPrivate"` | |||
IsMirror bool `json:"isMirror"` | |||
@@ -63,6 +64,13 @@ type RepoStatistic struct { | |||
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated" json:"-"` | |||
} | |||
func (repo *RepoStatistic) DisplayName() string { | |||
if repo.Alias == "" { | |||
return repo.Name | |||
} | |||
return repo.Alias | |||
} | |||
func DeleteRepoStatDaily(date string) error { | |||
sess := xStatistic.NewSession() | |||
defer sess.Close() | |||
@@ -177,6 +177,10 @@ type User struct { | |||
//BlockChain | |||
PublicKey string `xorm:"INDEX"` | |||
PrivateKey string `xorm:"INDEX"` | |||
WechatOpenId string `xorm:"INDEX"` | |||
WechatBindUnix timeutil.TimeStamp | |||
} | |||
// SearchOrganizationsOptions options to filter organizations | |||
@@ -185,6 +189,11 @@ type SearchOrganizationsOptions struct { | |||
All bool | |||
} | |||
// GenerateRandomAvatar generates a random avatar for user. | |||
func (u *User) IsBindWechat() bool { | |||
return u.WechatOpenId != "" | |||
} | |||
// ColorFormat writes a colored string to identify this struct | |||
func (u *User) ColorFormat(s fmt.State) { | |||
log.ColorFprintf(s, "%d:%s", | |||
@@ -0,0 +1,98 @@ | |||
package models | |||
import ( | |||
"code.gitea.io/gitea/modules/log" | |||
"code.gitea.io/gitea/modules/timeutil" | |||
"time" | |||
) | |||
type WechatBindAction int | |||
const ( | |||
WECHAT_BIND WechatBindAction = iota + 1 | |||
WECHAT_UNBIND | |||
) | |||
type WechatBindLog struct { | |||
ID int64 `xorm:"pk autoincr"` | |||
UserID int64 `xorm:"INDEX"` | |||
WechatOpenId string `xorm:"INDEX"` | |||
Action int | |||
CreateTime time.Time `xorm:"INDEX created"` | |||
} | |||
func BindWechatOpenId(userId int64, wechatOpenId string) error { | |||
sess := x.NewSession() | |||
defer sess.Close() | |||
if err := sess.Begin(); err != nil { | |||
return err | |||
} | |||
param := &User{WechatOpenId: wechatOpenId, WechatBindUnix: timeutil.TimeStampNow()} | |||
n, err := sess.Where("ID = ?", userId).Update(param) | |||
if err != nil { | |||
log.Error("update wechat_open_id failed,e=%v", err) | |||
if e := sess.Rollback(); e != nil { | |||
log.Error("BindWechatOpenId: sess.Rollback: %v", e) | |||
} | |||
return err | |||
} | |||
if n == 0 { | |||
log.Error("update wechat_open_id failed,user not exist,userId=%d", userId) | |||
if e := sess.Rollback(); e != nil { | |||
log.Error("BindWechatOpenId: sess.Rollback: %v", e) | |||
} | |||
return nil | |||
} | |||
logParam := &WechatBindLog{ | |||
UserID: userId, | |||
WechatOpenId: wechatOpenId, | |||
Action: int(WECHAT_BIND), | |||
} | |||
sess.Insert(logParam) | |||
return sess.Commit() | |||
} | |||
func GetUserWechatOpenId(userId int64) string { | |||
param := &User{} | |||
x.Cols("wechat_open_id").Where("ID =?", userId).Get(param) | |||
return param.WechatOpenId | |||
} | |||
func GetUserByWechatOpenId(wechatOpenId string) *User { | |||
user := &User{} | |||
x.Where("wechat_open_id = ?", wechatOpenId).Get(user) | |||
return user | |||
} | |||
func UnbindWechatOpenId(userId int64, oldWechatOpenID string) error { | |||
sess := x.NewSession() | |||
defer sess.Close() | |||
if err := sess.Begin(); err != nil { | |||
return err | |||
} | |||
n, err := x.Table(new(User)).Where("ID = ? AND wechat_open_id =?", userId, oldWechatOpenID).Update(map[string]interface{}{"wechat_open_id": "", "wechat_bind_unix": nil}) | |||
if err != nil { | |||
log.Error("update wechat_open_id failed,e=%v", err) | |||
if e := sess.Rollback(); e != nil { | |||
log.Error("UnbindWechatOpenId: sess.Rollback: %v", e) | |||
} | |||
return err | |||
} | |||
if n == 0 { | |||
log.Error("update wechat_open_id failed,user not exist,userId=%d", userId) | |||
if e := sess.Rollback(); e != nil { | |||
log.Error("UnbindWechatOpenId: sess.Rollback: %v", e) | |||
} | |||
return nil | |||
} | |||
logParam := &WechatBindLog{ | |||
UserID: userId, | |||
WechatOpenId: oldWechatOpenID, | |||
Action: int(WECHAT_UNBIND), | |||
} | |||
sess.Insert(logParam) | |||
return sess.Commit() | |||
} |
@@ -19,7 +19,8 @@ type CreateModelArtsNotebookForm struct { | |||
JobName string `form:"job_name" binding:"Required"` | |||
Attachment string `form:"attachment"` | |||
Description string `form:"description"` | |||
Flavor string `form:"flavor"` | |||
Flavor string `form:"flavor" binding:"Required"` | |||
ImageId string `form:"image_id" binding:"Required"` | |||
} | |||
func (f *CreateModelArtsNotebookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | |||
@@ -0,0 +1,67 @@ | |||
package wechat | |||
import ( | |||
"code.gitea.io/gitea/modules/redis/redis_client" | |||
"code.gitea.io/gitea/modules/redis/redis_key" | |||
"code.gitea.io/gitea/modules/redis/redis_lock" | |||
"time" | |||
) | |||
const EMPTY_REDIS_VAL = "Nil" | |||
var accessTokenLock = redis_lock.NewDistributeLock() | |||
func GetWechatAccessToken() string { | |||
token, _ := redis_client.Get(redis_key.WechatAccessTokenKey()) | |||
if token != "" { | |||
if token == EMPTY_REDIS_VAL { | |||
return "" | |||
} | |||
live, _ := redis_client.TTL(redis_key.WechatAccessTokenKey()) | |||
//refresh wechat access token when expire time less than 5 minutes | |||
if live > 0 && live < 300 { | |||
refreshAccessToken() | |||
} | |||
return token | |||
} | |||
return refreshAndGetAccessToken() | |||
} | |||
func refreshAccessToken() { | |||
if ok := accessTokenLock.Lock(redis_key.AccessTokenLockKey(), 3*time.Second); ok { | |||
defer accessTokenLock.UnLock(redis_key.AccessTokenLockKey()) | |||
callAccessTokenAndUpdateCache() | |||
} | |||
} | |||
func refreshAndGetAccessToken() string { | |||
if ok := accessTokenLock.LockWithWait(redis_key.AccessTokenLockKey(), 3*time.Second, 3*time.Second); ok { | |||
defer accessTokenLock.UnLock(redis_key.AccessTokenLockKey()) | |||
token, _ := redis_client.Get(redis_key.WechatAccessTokenKey()) | |||
if token != "" { | |||
if token == EMPTY_REDIS_VAL { | |||
return "" | |||
} | |||
return token | |||
} | |||
return callAccessTokenAndUpdateCache() | |||
} | |||
return "" | |||
} | |||
func callAccessTokenAndUpdateCache() string { | |||
r := callAccessToken() | |||
var token string | |||
if r != nil { | |||
token = r.Access_token | |||
} | |||
if token == "" { | |||
redis_client.Setex(redis_key.WechatAccessTokenKey(), EMPTY_REDIS_VAL, 10*time.Second) | |||
return "" | |||
} | |||
redis_client.Setex(redis_key.WechatAccessTokenKey(), token, time.Duration(r.Expires_in)*time.Second) | |||
return token | |||
} |
@@ -0,0 +1,71 @@ | |||
package wechat | |||
import ( | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/modules/log" | |||
"fmt" | |||
) | |||
type QRCode4BindCache struct { | |||
UserId int64 | |||
Status int | |||
} | |||
const ( | |||
BIND_STATUS_UNBIND = 0 | |||
BIND_STATUS_SCANNED = 1 | |||
BIND_STATUS_BOUND = 2 | |||
BIND_STATUS_EXPIRED = 9 | |||
) | |||
const ( | |||
BIND_REPLY_SUCCESS = "扫码成功,您可以使用OpenI启智社区算力环境。" | |||
BIND_REPLY_WECHAT_ACCOUNT_USED = "认证失败,您的微信号已绑定其他启智账号" | |||
BIND_REPLY_OPENI_ACCOUNT_USED = "认证失败,您待认证的启智账号已绑定其他微信号" | |||
BIND_REPLY_FAILED_DEFAULT = "微信认证失败" | |||
) | |||
type WechatBindError struct { | |||
Reply string | |||
} | |||
func NewWechatBindError(reply string) WechatBindError { | |||
return WechatBindError{Reply: reply} | |||
} | |||
func (err WechatBindError) Error() string { | |||
return fmt.Sprint("wechat bind error,reply=%s", err.Reply) | |||
} | |||
func BindWechat(userId int64, wechatOpenId string) error { | |||
if !IsWechatAccountAvailable(userId, wechatOpenId) { | |||
log.Error("bind wechat failed, because user use wrong wechat account to bind,userId=%d wechatOpenId=%s", userId, wechatOpenId) | |||
return NewWechatBindError(BIND_REPLY_WECHAT_ACCOUNT_USED) | |||
} | |||
if !IsUserAvailableForWechatBind(userId, wechatOpenId) { | |||
log.Error("openI account has been used,userId=%d wechatOpenId=%s", userId, wechatOpenId) | |||
return NewWechatBindError(BIND_REPLY_OPENI_ACCOUNT_USED) | |||
} | |||
return models.BindWechatOpenId(userId, wechatOpenId) | |||
} | |||
func UnbindWechat(userId int64, oldWechatOpenId string) error { | |||
return models.UnbindWechatOpenId(userId, oldWechatOpenId) | |||
} | |||
//IsUserAvailableForWechatBind if user has bound wechat and the bound openId is not the given wechatOpenId,return false | |||
//otherwise,return true | |||
func IsUserAvailableForWechatBind(userId int64, wechatOpenId string) bool { | |||
currentOpenId := models.GetUserWechatOpenId(userId) | |||
return currentOpenId == "" || currentOpenId == wechatOpenId | |||
} | |||
//IsWechatAccountAvailable if wechat account used by another account,return false | |||
//if wechat account not used or used by the given user,return true | |||
func IsWechatAccountAvailable(userId int64, wechatOpenId string) bool { | |||
user := models.GetUserByWechatOpenId(wechatOpenId) | |||
if user != nil && user.WechatOpenId != "" && user.ID != userId { | |||
return false | |||
} | |||
return true | |||
} |
@@ -0,0 +1,5 @@ | |||
package wechat | |||
type WechatCall interface { | |||
call() | |||
} |
@@ -0,0 +1,126 @@ | |||
package wechat | |||
import ( | |||
"code.gitea.io/gitea/modules/log" | |||
"code.gitea.io/gitea/modules/setting" | |||
"encoding/json" | |||
"fmt" | |||
"github.com/go-resty/resty/v2" | |||
"strconv" | |||
"time" | |||
) | |||
var ( | |||
client *resty.Client | |||
) | |||
const ( | |||
GRANT_TYPE = "client_credential" | |||
ACCESS_TOKEN_PATH = "/cgi-bin/token" | |||
QR_CODE_Path = "/cgi-bin/qrcode/create" | |||
ACTION_QR_STR_SCENE = "QR_STR_SCENE" | |||
ERR_CODE_ACCESSTOKEN_EXPIRE = 42001 | |||
ERR_CODE_ACCESSTOKEN_INVALID = 40001 | |||
) | |||
type AccessTokenResponse struct { | |||
Access_token string | |||
Expires_in int | |||
} | |||
type QRCodeResponse struct { | |||
Ticket string `json:"ticket"` | |||
Expire_Seconds int `json:"expire_seconds"` | |||
Url string `json:"url"` | |||
} | |||
type QRCodeRequest struct { | |||
Action_name string `json:"action_name"` | |||
Action_info ActionInfo `json:"action_info"` | |||
Expire_seconds int `json:"expire_seconds"` | |||
} | |||
type ActionInfo struct { | |||
Scene Scene `json:"scene"` | |||
} | |||
type Scene struct { | |||
Scene_str string `json:"scene_str"` | |||
} | |||
type ErrorResponse struct { | |||
Errcode int | |||
Errmsg string | |||
} | |||
func getWechatRestyClient() *resty.Client { | |||
if client == nil { | |||
client = resty.New() | |||
client.SetTimeout(time.Duration(setting.WechatApiTimeoutSeconds) * time.Second) | |||
} | |||
return client | |||
} | |||
// api doc:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html | |||
func callAccessToken() *AccessTokenResponse { | |||
client := getWechatRestyClient() | |||
var result AccessTokenResponse | |||
_, err := client.R(). | |||
SetQueryParam("grant_type", GRANT_TYPE). | |||
SetQueryParam("appid", setting.WechatAppId). | |||
SetQueryParam("secret", setting.WechatAppSecret). | |||
SetResult(&result). | |||
Get(setting.WechatApiHost + ACCESS_TOKEN_PATH) | |||
if err != nil { | |||
log.Error("get wechat access token failed,e=%v", err) | |||
return nil | |||
} | |||
return &result | |||
} | |||
//callQRCodeCreate call the wechat api to create qr-code, | |||
// api doc: https://developers.weixin.qq.com/doc/offiaccount/Account_Management/Generating_a_Parametric_QR_Code.html | |||
func callQRCodeCreate(sceneStr string) (*QRCodeResponse, bool) { | |||
client := getWechatRestyClient() | |||
body := &QRCodeRequest{ | |||
Action_name: ACTION_QR_STR_SCENE, | |||
Action_info: ActionInfo{Scene: Scene{Scene_str: sceneStr}}, | |||
Expire_seconds: setting.WechatQRCodeExpireSeconds, | |||
} | |||
bodyJson, _ := json.Marshal(body) | |||
var result QRCodeResponse | |||
r, err := client.R(). | |||
SetHeader("Content-Type", "application/json"). | |||
SetQueryParam("access_token", GetWechatAccessToken()). | |||
SetBody(bodyJson). | |||
SetResult(&result). | |||
Post(setting.WechatApiHost + QR_CODE_Path) | |||
if err != nil { | |||
log.Error("create QR code failed,e=%v", err) | |||
return nil, false | |||
} | |||
errCode := getErrorCodeFromResponse(r) | |||
if errCode == ERR_CODE_ACCESSTOKEN_EXPIRE || errCode == ERR_CODE_ACCESSTOKEN_INVALID { | |||
return nil, true | |||
} | |||
if result.Url == "" { | |||
return nil, false | |||
} | |||
log.Info("%v", r) | |||
return &result, false | |||
} | |||
func getErrorCodeFromResponse(r *resty.Response) int { | |||
a := r.Body() | |||
resultMap := make(map[string]interface{}, 0) | |||
json.Unmarshal(a, &resultMap) | |||
code := resultMap["errcode"] | |||
if code == nil { | |||
return -1 | |||
} | |||
c, _ := strconv.Atoi(fmt.Sprint(code)) | |||
return c | |||
} |
@@ -0,0 +1,76 @@ | |||
package wechat | |||
import ( | |||
"code.gitea.io/gitea/modules/redis/redis_client" | |||
"code.gitea.io/gitea/modules/redis/redis_key" | |||
"encoding/json" | |||
"encoding/xml" | |||
"strings" | |||
"time" | |||
) | |||
//<xml> | |||
// <ToUserName><![CDATA[toUser]]></ToUserName> | |||
// <FromUserName><![CDATA[FromUser]]></FromUserName> | |||
// <CreateTime>123456789</CreateTime> | |||
// <MsgType><![CDATA[event]]></MsgType> | |||
// <Event><![CDATA[SCAN]]></Event> | |||
// <EventKey><![CDATA[SCENE_VALUE]]></EventKey> | |||
// <Ticket><![CDATA[TICKET]]></Ticket> | |||
//</xml> | |||
type WechatEvent struct { | |||
ToUserName string | |||
FromUserName string | |||
CreateTime int64 | |||
MsgType string | |||
Event string | |||
EventKey string | |||
Ticket string | |||
} | |||
type EventReply struct { | |||
XMLName xml.Name `xml:"xml"` | |||
ToUserName string | |||
FromUserName string | |||
CreateTime int64 | |||
MsgType string | |||
Content string | |||
} | |||
const ( | |||
WECHAT_EVENT_SUBSCRIBE = "subscribe" | |||
WECHAT_EVENT_SCAN = "SCAN" | |||
) | |||
const ( | |||
WECHAT_MSG_TYPE_TEXT = "text" | |||
) | |||
func HandleSubscribeEvent(we WechatEvent) string { | |||
eventKey := we.EventKey | |||
if eventKey == "" { | |||
return "" | |||
} | |||
sceneStr := strings.TrimPrefix(eventKey, "qrscene_") | |||
key := redis_key.WechatBindingUserIdKey(sceneStr) | |||
val, _ := redis_client.Get(key) | |||
if val == "" { | |||
return "" | |||
} | |||
qrCache := new(QRCode4BindCache) | |||
json.Unmarshal([]byte(val), qrCache) | |||
if qrCache.Status == BIND_STATUS_UNBIND { | |||
err := BindWechat(qrCache.UserId, we.FromUserName) | |||
if err != nil { | |||
if err, ok := err.(WechatBindError); ok { | |||
return err.Reply | |||
} | |||
return BIND_REPLY_FAILED_DEFAULT | |||
} | |||
qrCache.Status = BIND_STATUS_BOUND | |||
jsonStr, _ := json.Marshal(qrCache) | |||
redis_client.Setex(redis_key.WechatBindingUserIdKey(sceneStr), string(jsonStr), 60*time.Second) | |||
} | |||
return BIND_REPLY_SUCCESS | |||
} |
@@ -0,0 +1,13 @@ | |||
package wechat | |||
import "code.gitea.io/gitea/modules/log" | |||
func GetWechatQRCode4Bind(sceneStr string) *QRCodeResponse { | |||
result, retryFlag := callQRCodeCreate(sceneStr) | |||
if retryFlag { | |||
log.Info("retry wechat qr-code calling,sceneStr=%s", sceneStr) | |||
refreshAccessToken() | |||
result, _ = callQRCodeCreate(sceneStr) | |||
} | |||
return result | |||
} |
@@ -1,16 +1,17 @@ | |||
package cloudbrain | |||
import ( | |||
"code.gitea.io/gitea/modules/storage" | |||
"encoding/json" | |||
"errors" | |||
"strconv" | |||
"code.gitea.io/gitea/modules/setting" | |||
"code.gitea.io/gitea/modules/storage" | |||
"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/setting" | |||
) | |||
const ( | |||
@@ -221,13 +222,19 @@ func GenerateTask(ctx *context.Context, jobName, image, command, uuid, codePath, | |||
ComputeResource: models.GPUResource, | |||
BenchmarkTypeID: benchmarkTypeID, | |||
BenchmarkChildTypeID: benchmarkChildTypeID, | |||
Description: description, | |||
Description: description, | |||
}) | |||
if err != nil { | |||
return err | |||
} | |||
if string(models.JobTypeBenchmark) == jobType { | |||
notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, jobID, jobName, models.ActionCreateBenchMarkTask) | |||
} else { | |||
notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, jobID, jobName, models.ActionCreateDebugGPUTask) | |||
} | |||
return nil | |||
} | |||
@@ -6,12 +6,14 @@ | |||
package context | |||
import ( | |||
"encoding/base64" | |||
"net/http" | |||
"strings" | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/modules/auth" | |||
"code.gitea.io/gitea/modules/log" | |||
"code.gitea.io/gitea/modules/setting" | |||
"encoding/base64" | |||
"net/http" | |||
"gitea.com/macaron/csrf" | |||
"gitea.com/macaron/macaron" | |||
@@ -21,12 +23,14 @@ import ( | |||
// ToggleOptions contains required or check options | |||
type ToggleOptions struct { | |||
SignInRequired bool | |||
SignOutRequired bool | |||
AdminRequired bool | |||
DisableCSRF bool | |||
BasicAuthRequired bool | |||
OperationRequired bool | |||
SignInRequired bool | |||
SignOutRequired bool | |||
AdminRequired bool | |||
DisableCSRF bool | |||
BasicAuthRequired bool | |||
OperationRequired bool | |||
WechatAuthRequired bool | |||
WechatAuthRequiredForAPI bool | |||
} | |||
// Toggle returns toggle options as middleware | |||
@@ -94,7 +98,14 @@ func Toggle(options *ToggleOptions) macaron.Handler { | |||
return | |||
} | |||
ctx.SetCookie("redirect_to", setting.AppSubURL+ctx.Req.URL.RequestURI(), 0, setting.AppSubURL) | |||
tempUrl := ctx.Req.URL.RequestURI() | |||
if strings.Contains(tempUrl, "action/star?") || strings.Contains(tempUrl, "action/watch?") { | |||
redirectForStarAndWatch(ctx, tempUrl) | |||
} else { | |||
ctx.SetCookie("redirect_to", setting.AppSubURL+ctx.Req.URL.RequestURI(), 0, setting.AppSubURL) | |||
} | |||
ctx.Redirect(setting.AppSubURL + "/user/login") | |||
return | |||
} else if !ctx.User.IsActive && setting.Service.RegisterEmailConfirm { | |||
@@ -126,6 +137,36 @@ func Toggle(options *ToggleOptions) macaron.Handler { | |||
} | |||
} | |||
if setting.WechatAuthSwitch && options.WechatAuthRequired { | |||
if !ctx.IsSigned { | |||
ctx.SetCookie("redirect_to", setting.AppSubURL+ctx.Req.URL.RequestURI(), 0, setting.AppSubURL) | |||
ctx.Redirect(setting.AppSubURL + "/user/login") | |||
return | |||
} | |||
if ctx.User.WechatOpenId == "" { | |||
ctx.SetCookie("redirect_to", setting.AppSubURL+ctx.Req.URL.RequestURI(), 0, setting.AppSubURL) | |||
ctx.Redirect(setting.AppSubURL + "/authentication/wechat/bind") | |||
} | |||
} | |||
if setting.WechatAuthSwitch && options.WechatAuthRequiredForAPI { | |||
if !ctx.IsSigned { | |||
ctx.SetCookie("redirect_to", setting.AppSubURL+ctx.Req.URL.RequestURI(), 0, setting.AppSubURL) | |||
ctx.Redirect(setting.AppSubURL + "/user/login") | |||
return | |||
} | |||
if ctx.User.WechatOpenId == "" { | |||
redirectUrl := ctx.Query("redirect_to") | |||
if redirectUrl == "" { | |||
redirectUrl = ctx.Req.URL.RequestURI() | |||
} | |||
ctx.SetCookie("redirect_to", setting.AppSubURL+redirectUrl, 0, setting.AppSubURL) | |||
ctx.JSON(200, map[string]string{ | |||
"WechatRedirectUrl": setting.AppSubURL + "/authentication/wechat/bind", | |||
}) | |||
} | |||
} | |||
// Redirect to log in page if auto-signin info is provided and has not signed in. | |||
if !options.SignOutRequired && !ctx.IsSigned && !auth.IsAPIPath(ctx.Req.URL.Path) && | |||
len(ctx.GetCookie(setting.CookieUserName)) > 0 { | |||
@@ -159,6 +200,17 @@ func Toggle(options *ToggleOptions) macaron.Handler { | |||
} | |||
} | |||
func redirectForStarAndWatch(ctx *Context, tempUrl string) { | |||
splits := strings.Split(tempUrl, "?") | |||
if len(splits) > 1 { | |||
redirectArguments := strings.Split(splits[1], "=") | |||
if len(redirectArguments) > 0 && redirectArguments[0] == "redirect_to" { | |||
ctx.SetCookie("redirect_to", setting.AppSubURL+strings.Replace(redirectArguments[1], "%2f", "/", -1), 0, setting.AppSubURL) | |||
} | |||
} | |||
} | |||
func basicAuth(ctx *Context) bool { | |||
var siteAuth = base64.StdEncoding.EncodeToString([]byte(setting.CBAuthUser + ":" + setting.CBAuthPassword)) | |||
auth := ctx.Req.Header.Get("Authorization") | |||
@@ -354,3 +354,18 @@ func Contexter() macaron.Handler { | |||
c.Map(ctx) | |||
} | |||
} | |||
// CheckWechatBind | |||
func (ctx *Context) CheckWechatBind() { | |||
if !setting.WechatAuthSwitch || ctx.User.IsBindWechat() { | |||
return | |||
} | |||
redirectUrl := ctx.Query("redirect_to") | |||
if redirectUrl == "" { | |||
redirectUrl = ctx.Req.URL.RequestURI() | |||
} | |||
ctx.SetCookie("redirect_to", setting.AppSubURL+redirectUrl, 0, setting.AppSubURL) | |||
ctx.JSON(200, map[string]string{ | |||
"WechatRedirectUrl": setting.AppSubURL + "/authentication/wechat/bind", | |||
}) | |||
} |
@@ -2,6 +2,7 @@ package modelarts | |||
import ( | |||
"encoding/json" | |||
"errors" | |||
"fmt" | |||
"path" | |||
"strconv" | |||
@@ -9,14 +10,15 @@ import ( | |||
"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/setting" | |||
"code.gitea.io/gitea/modules/storage" | |||
) | |||
const ( | |||
//notebook | |||
storageTypeOBS = "obs" | |||
autoStopDuration = 4 * 60 * 60 | |||
storageTypeOBS = "obs" | |||
autoStopDuration = 4 * 60 * 60 | |||
autoStopDurationMs = 4 * 60 * 60 * 1000 | |||
DataSetMountPath = "/home/ma-user/work" | |||
@@ -63,6 +65,7 @@ const ( | |||
var ( | |||
poolInfos *models.PoolInfos | |||
FlavorInfos *models.FlavorInfos | |||
ImageInfos *models.ImageInfosModelArts | |||
) | |||
type GenerateTrainJobReq struct { | |||
@@ -259,35 +262,42 @@ func GenerateTask(ctx *context.Context, jobName, uuid, description, flavor strin | |||
if err != nil { | |||
return err | |||
} | |||
notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, jobResult.ID, jobName, models.ActionCreateDebugNPUTask) | |||
return nil | |||
} | |||
func GenerateNotebook2(ctx *context.Context, jobName, uuid, description, flavor string) error { | |||
func GenerateNotebook2(ctx *context.Context, jobName, uuid, description, flavor, imageId string) error { | |||
if poolInfos == nil { | |||
json.Unmarshal([]byte(setting.PoolInfos), &poolInfos) | |||
} | |||
imageName, err := GetNotebookImageName(imageId) | |||
if err != nil { | |||
log.Error("GetNotebookImageName failed: %v", err.Error()) | |||
return err | |||
} | |||
jobResult, err := createNotebook2(models.CreateNotebook2Params{ | |||
JobName: jobName, | |||
Description: description, | |||
Flavor: flavor, | |||
Duration: autoStopDurationMs, | |||
ImageID: "59a6e9f5-93c0-44dd-85b0-82f390c5d53a", | |||
ImageID: imageId, | |||
PoolID: poolInfos.PoolInfo[0].PoolId, | |||
Feature: models.NotebookFeature, | |||
Volume: models.VolumeReq{ | |||
Capacity: 100, | |||
Category: models.EVSCategory, | |||
Ownership: models.ManagedOwnership, | |||
Volume: models.VolumeReq{ | |||
Capacity: setting.Capacity, | |||
Category: models.EVSCategory, | |||
Ownership: models.ManagedOwnership, | |||
}, | |||
WorkspaceID: "0", | |||
WorkspaceID: "0", | |||
}) | |||
if err != nil { | |||
log.Error("createNotebook2 failed: %v", err.Error()) | |||
return err | |||
} | |||
err = models.CreateCloudbrain(&models.Cloudbrain{ | |||
Status: string(models.JobWaiting), | |||
Status: jobResult.Status, | |||
UserID: ctx.User.ID, | |||
RepoID: ctx.Repo.Repository.ID, | |||
JobID: jobResult.ID, | |||
@@ -296,12 +306,14 @@ func GenerateNotebook2(ctx *context.Context, jobName, uuid, description, flavor | |||
Type: models.TypeCloudBrainTwo, | |||
Uuid: uuid, | |||
ComputeResource: models.NPUResource, | |||
Image: imageName, | |||
Description: description, | |||
}) | |||
if err != nil { | |||
return err | |||
} | |||
notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, jobResult.ID, jobName, models.ActionCreateDebugNPUTask) | |||
return nil | |||
} | |||
@@ -335,12 +347,12 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error | |||
log.Error("GetAttachmentByUUID(%s) failed:%v", strconv.FormatInt(jobResult.JobID, 10), err.Error()) | |||
return err | |||
} | |||
jobId := strconv.FormatInt(jobResult.JobID, 10) | |||
err = models.CreateCloudbrain(&models.Cloudbrain{ | |||
Status: TransTrainJobStatus(jobResult.Status), | |||
UserID: ctx.User.ID, | |||
RepoID: ctx.Repo.Repository.ID, | |||
JobID: strconv.FormatInt(jobResult.JobID, 10), | |||
JobID: jobId, | |||
JobName: req.JobName, | |||
JobType: string(models.JobTypeTrain), | |||
Type: models.TypeCloudBrainTwo, | |||
@@ -371,7 +383,7 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error | |||
log.Error("CreateCloudbrain(%s) failed:%v", req.JobName, err.Error()) | |||
return err | |||
} | |||
notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, jobId, req.JobName, models.ActionCreateTrainTask) | |||
return nil | |||
} | |||
@@ -555,12 +567,12 @@ func GenerateInferenceJob(ctx *context.Context, req *GenerateInferenceJobReq) (e | |||
log.Error("GetAttachmentByUUID(%s) failed:%v", strconv.FormatInt(jobResult.JobID, 10), err.Error()) | |||
return err | |||
} | |||
jobID := strconv.FormatInt(jobResult.JobID, 10) | |||
err = models.CreateCloudbrain(&models.Cloudbrain{ | |||
Status: TransTrainJobStatus(jobResult.Status), | |||
UserID: ctx.User.ID, | |||
RepoID: ctx.Repo.Repository.ID, | |||
JobID: strconv.FormatInt(jobResult.JobID, 10), | |||
JobID: jobID, | |||
JobName: req.JobName, | |||
JobType: string(models.JobTypeInference), | |||
Type: models.TypeCloudBrainTwo, | |||
@@ -583,6 +595,7 @@ func GenerateInferenceJob(ctx *context.Context, req *GenerateInferenceJobReq) (e | |||
EngineName: req.EngineName, | |||
LabelName: req.LabelName, | |||
IsLatestVersion: req.IsLatestVersion, | |||
ComputeResource: models.NPUResource, | |||
VersionCount: req.VersionCount, | |||
TotalVersionCount: req.TotalVersionCount, | |||
ModelName: req.ModelName, | |||
@@ -595,6 +608,29 @@ func GenerateInferenceJob(ctx *context.Context, req *GenerateInferenceJobReq) (e | |||
log.Error("CreateCloudbrain(%s) failed:%v", req.JobName, err.Error()) | |||
return err | |||
} | |||
notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, jobID, req.JobName, models.ActionCreateInferenceTask) | |||
return nil | |||
} | |||
func GetNotebookImageName(imageId string) (string, error) { | |||
var validImage = false | |||
var imageName = "" | |||
if ImageInfos == nil { | |||
json.Unmarshal([]byte(setting.ImageInfos), &ImageInfos) | |||
} | |||
for _, imageInfo := range ImageInfos.ImageInfo { | |||
if imageInfo.Id == imageId { | |||
validImage = true | |||
imageName = imageInfo.Value | |||
} | |||
} | |||
if !validImage { | |||
log.Error("the image id(%s) is invalid", imageId) | |||
return imageName, errors.New("the image id is invalid") | |||
} | |||
return imageName, nil | |||
} |
@@ -30,9 +30,12 @@ const ( | |||
errorCodeExceedLimit = "ModelArts.0118" | |||
//notebook 2.0 | |||
urlNotebook2 = "/notebooks" | |||
urlNotebook2 = "/notebooks" | |||
//error code | |||
modelartsIllegalToken = "ModelArts.6401" | |||
NotebookNotFound = "ModelArts.6404" | |||
NotebookNoPermission = "ModelArts.6403" | |||
) | |||
func getRestyClient() *resty.Client { | |||
@@ -330,3 +330,18 @@ func (a *actionNotifier) NotifySyncDeleteRef(doer *models.User, repo *models.Rep | |||
log.Error("notifyWatchers: %v", err) | |||
} | |||
} | |||
func (a *actionNotifier) NotifyOtherTask(doer *models.User, repo *models.Repository, id string, name string, optype models.ActionType) { | |||
if err := models.NotifyWatchers(&models.Action{ | |||
ActUserID: doer.ID, | |||
ActUser: doer, | |||
OpType: optype, | |||
RepoID: repo.ID, | |||
Repo: repo, | |||
IsPrivate: repo.IsPrivate, | |||
RefName: name, | |||
Content: id, | |||
}); err != nil { | |||
log.Error("notifyWatchers: %v", err) | |||
} | |||
} |
@@ -54,4 +54,6 @@ type Notifier interface { | |||
NotifySyncPushCommits(pusher *models.User, repo *models.Repository, refName, oldCommitID, newCommitID string, commits *repository.PushCommits) | |||
NotifySyncCreateRef(doer *models.User, repo *models.Repository, refType, refFullName string) | |||
NotifySyncDeleteRef(doer *models.User, repo *models.Repository, refType, refFullName string) | |||
NotifyOtherTask(doer *models.User, repo *models.Repository, id string, name string, optype models.ActionType) | |||
} |
@@ -154,3 +154,7 @@ func (*NullNotifier) NotifySyncCreateRef(doer *models.User, repo *models.Reposit | |||
// NotifySyncDeleteRef places a place holder function | |||
func (*NullNotifier) NotifySyncDeleteRef(doer *models.User, repo *models.Repository, refType, refFullName string) { | |||
} | |||
func (*NullNotifier) NotifyOtherTask(doer *models.User, repo *models.Repository, id string, name string, optype models.ActionType) { | |||
} |
@@ -37,6 +37,13 @@ func NewContext() { | |||
RegisterNotifier(action.NewNotifier()) | |||
} | |||
// NotifyUploadAttachment notifies attachment upload message to notifiers | |||
func NotifyOtherTask(doer *models.User, repo *models.Repository, id string, name string, optype models.ActionType) { | |||
for _, notifier := range notifiers { | |||
notifier.NotifyOtherTask(doer, repo, id, name, optype) | |||
} | |||
} | |||
// NotifyCreateIssueComment notifies issue comment related message to notifiers | |||
func NotifyCreateIssueComment(doer *models.User, repo *models.Repository, | |||
issue *models.Issue, comment *models.Comment) { | |||
@@ -0,0 +1,87 @@ | |||
package redis_client | |||
import ( | |||
"code.gitea.io/gitea/modules/labelmsg" | |||
"fmt" | |||
"github.com/gomodule/redigo/redis" | |||
"math" | |||
"strconv" | |||
"time" | |||
) | |||
func Setex(key, value string, timeout time.Duration) (bool, error) { | |||
redisClient := labelmsg.Get() | |||
defer redisClient.Close() | |||
seconds := int(math.Floor(timeout.Seconds())) | |||
reply, err := redisClient.Do("SETEX", key, seconds, value) | |||
if err != nil { | |||
return false, err | |||
} | |||
if reply != "OK" { | |||
return false, nil | |||
} | |||
return true, nil | |||
} | |||
func Setnx(key, value string, timeout time.Duration) (bool, error) { | |||
redisClient := labelmsg.Get() | |||
defer redisClient.Close() | |||
seconds := int(math.Floor(timeout.Seconds())) | |||
reply, err := redisClient.Do("SET", key, value, "NX", "EX", seconds) | |||
if err != nil { | |||
return false, err | |||
} | |||
if reply != "OK" { | |||
return false, nil | |||
} | |||
return true, nil | |||
} | |||
func Get(key string) (string, error) { | |||
redisClient := labelmsg.Get() | |||
defer redisClient.Close() | |||
reply, err := redisClient.Do("GET", key) | |||
if err != nil { | |||
return "", err | |||
} | |||
if reply == nil { | |||
return "", err | |||
} | |||
s, _ := redis.String(reply, nil) | |||
return s, nil | |||
} | |||
func Del(key string) (int, error) { | |||
redisClient := labelmsg.Get() | |||
defer redisClient.Close() | |||
reply, err := redisClient.Do("DEL", key) | |||
if err != nil { | |||
return 0, err | |||
} | |||
if reply == nil { | |||
return 0, err | |||
} | |||
s, _ := redis.Int(reply, nil) | |||
return s, nil | |||
} | |||
func TTL(key string) (int, error) { | |||
redisClient := labelmsg.Get() | |||
defer redisClient.Close() | |||
reply, err := redisClient.Do("TTL", key) | |||
if err != nil { | |||
return 0, err | |||
} | |||
n, _ := strconv.Atoi(fmt.Sprint(reply)) | |||
return n, nil | |||
} |
@@ -0,0 +1,16 @@ | |||
package redis_key | |||
import "strings" | |||
const KEY_SEPARATE = ":" | |||
func KeyJoin(keys ...string) string { | |||
var build strings.Builder | |||
for _, v := range keys { | |||
build.WriteString(v) | |||
build.WriteString(KEY_SEPARATE) | |||
} | |||
s := build.String() | |||
s = strings.TrimSuffix(s, KEY_SEPARATE) | |||
return s | |||
} |
@@ -0,0 +1,14 @@ | |||
package redis_key | |||
const PREFIX = "wechat" | |||
func WechatBindingUserIdKey(sceneStr string) string { | |||
return KeyJoin(PREFIX, sceneStr, "scene_userId") | |||
} | |||
func WechatAccessTokenKey() string { | |||
return KeyJoin(PREFIX, "access_token") | |||
} | |||
func AccessTokenLockKey() string { | |||
return KeyJoin(PREFIX, "access_token_lock") | |||
} |
@@ -0,0 +1,40 @@ | |||
package redis_lock | |||
import ( | |||
"code.gitea.io/gitea/modules/redis/redis_client" | |||
"time" | |||
) | |||
type DistributeLock struct { | |||
} | |||
func NewDistributeLock() *DistributeLock { | |||
return &DistributeLock{} | |||
} | |||
func (lock *DistributeLock) Lock(lockKey string, expireTime time.Duration) bool { | |||
isOk, _ := redis_client.Setnx(lockKey, "", expireTime) | |||
return isOk | |||
} | |||
func (lock *DistributeLock) LockWithWait(lockKey string, expireTime time.Duration, waitTime time.Duration) bool { | |||
start := time.Now().Unix() * 1000 | |||
duration := waitTime.Milliseconds() | |||
for { | |||
isOk, _ := redis_client.Setnx(lockKey, "", expireTime) | |||
if isOk { | |||
return true | |||
} | |||
if time.Now().Unix()*1000-start > duration { | |||
return false | |||
} | |||
time.Sleep(50 * time.Millisecond) | |||
} | |||
return false | |||
} | |||
func (lock *DistributeLock) UnLock(lockKey string) error { | |||
_, err := redis_client.Del(lockKey) | |||
return err | |||
} |
@@ -470,7 +470,7 @@ var ( | |||
BenchmarkTypes string | |||
BenchmarkGpuTypes string | |||
BenchmarkResourceSpecs string | |||
BenchmarkMaxDuration int64 | |||
BenchmarkMaxDuration int64 | |||
//snn4imagenet config | |||
IsSnn4imagenetEnabled bool | |||
@@ -513,6 +513,8 @@ var ( | |||
PoolInfos string | |||
Flavor string | |||
DebugHost string | |||
ImageInfos string | |||
Capacity int | |||
//train-job | |||
ResourcePools string | |||
Engines string | |||
@@ -529,6 +531,14 @@ var ( | |||
ElkTimeFormat string | |||
PROJECT_LIMIT_PAGES []string | |||
//wechat config | |||
WechatApiHost string | |||
WechatApiTimeoutSeconds int | |||
WechatAppId string | |||
WechatAppSecret string | |||
WechatQRCodeExpireSeconds int | |||
WechatAuthSwitch bool | |||
//nginx proxy | |||
PROXYURL string | |||
RadarMap = struct { | |||
@@ -1326,7 +1336,8 @@ func NewContext() { | |||
ProfileID = sec.Key("PROFILE_ID").MustString("") | |||
PoolInfos = sec.Key("POOL_INFOS").MustString("") | |||
Flavor = sec.Key("FLAVOR").MustString("") | |||
DebugHost = sec.Key("DEBUG_SERVER_HOST").MustString("http://192.168.202.73") | |||
ImageInfos = sec.Key("IMAGE_INFOS").MustString("") | |||
Capacity = sec.Key("IMAGE_INFOS").MustInt(100) | |||
ResourcePools = sec.Key("Resource_Pools").MustString("") | |||
Engines = sec.Key("Engines").MustString("") | |||
EngineVersions = sec.Key("Engine_Versions").MustString("") | |||
@@ -1342,6 +1353,14 @@ func NewContext() { | |||
ElkTimeFormat = sec.Key("ELKTIMEFORMAT").MustString("date_time") | |||
PROJECT_LIMIT_PAGES = strings.Split(sec.Key("project_limit_pages").MustString(""), ",") | |||
sec = Cfg.Section("wechat") | |||
WechatApiHost = sec.Key("HOST").MustString("https://api.weixin.qq.com") | |||
WechatApiTimeoutSeconds = sec.Key("TIMEOUT_SECONDS").MustInt(3) | |||
WechatAppId = sec.Key("APP_ID").MustString("wxba77b915a305a57d") | |||
WechatAppSecret = sec.Key("APP_SECRET").MustString("e48e13f315adc32749ddc7057585f198") | |||
WechatQRCodeExpireSeconds = sec.Key("QR_CODE_EXPIRE_SECONDS").MustInt(120) | |||
WechatAuthSwitch = sec.Key("AUTH_SWITCH").MustBool(true) | |||
SetRadarMapConfig() | |||
sec = Cfg.Section("warn_mail") | |||
@@ -16,9 +16,10 @@ type Attachment struct { | |||
Size int64 `json:"size"` | |||
DownloadCount int64 `json:"download_count"` | |||
// swagger:strfmt date-time | |||
Created time.Time `json:"created_at"` | |||
UUID string `json:"uuid"` | |||
DownloadURL string `json:"browser_download_url"` | |||
Created time.Time `json:"created_at"` | |||
UUID string `json:"uuid"` | |||
DownloadURL string `json:"browser_download_url"` | |||
S3DownloadURL string | |||
} | |||
// EditAttachmentOptions options for editing attachments | |||
@@ -493,6 +493,14 @@ account_link = Linked Accounts | |||
organization = Organizations | |||
uid = Uid | |||
u2f = Security Keys | |||
bind_wechat = Bind WeChat | |||
wechat_bind = WeChat Binding | |||
bind_account_information = Bind account information | |||
bind_time = Bind Time | |||
wechat = Wechat | |||
unbind_wc = Unbind | |||
unbind_wechat = Are you sure you want to unbind WeChat? | |||
unbind_computing = After unbundling, the qizhi computing power environment will not be available | |||
public_profile = Public Profile | |||
profile_desc = Your email address will be used for notifications and other operations. | |||
@@ -864,9 +872,13 @@ confirm_choice = confirm | |||
cloudbran1_tips = Only data in zip format can create cloudbrain tasks | |||
cloudbrain_creator=Creator | |||
cloudbrain_task = Task Name | |||
cloudbrain_task_type = Task Type | |||
cloudbrain_task_name=Cloud Brain Task Name | |||
cloudbrain_operate = Operate | |||
cloudbrain_status_createtime = Status/Createtime | |||
cloudbrain_status_runtime = Running Time | |||
cloudbrain_jobname_err=Name must start with a lowercase letter or number,can include lowercase letter,number,_ and -,can not end with _, and can be up to 36 characters long. | |||
cloudbrain_query_fail=Failed to query cloudbrain information. | |||
record_begintime_get_err=Can not get the record begin time. | |||
parameter_is_wrong=The input parameter is wrong. | |||
@@ -2059,6 +2071,9 @@ people = People | |||
teams = Teams | |||
lower_members = members | |||
lower_repositories = repositories | |||
lower_member=member | |||
lower_repository = repository | |||
create_new_team = New Team | |||
create_team = Create Team | |||
org_desc = Description | |||
@@ -2078,6 +2093,9 @@ custom_select_courses = Customize selected courses | |||
recommend_remain_pro = Remain | |||
save_fail_tips = The upper limit is exceeded | |||
select_again = Select more than 9, please select again! | |||
custom_select_projects = Customize selected projects | |||
customize = Customize | |||
selected_project=Selected Projects | |||
form.name_reserved = The organization name '%s' is reserved. | |||
form.name_pattern_not_allowed = The pattern '%s' is not allowed in an organization name. | |||
@@ -2338,6 +2356,13 @@ datasets.owner=Owner | |||
datasets.name=name | |||
datasets.private=Private | |||
cloudbrain.all_task_types=All Task Types | |||
cloudbrain.all_computing_resources=All Computing Resources | |||
cloudbrain.all_status=All Status | |||
cloudbrain.download_report=Download Report | |||
cloudbrain.cloudbrain_name=Cloudbrain Name | |||
cloudbrain.search = Seach Task Name/Creter | |||
hooks.desc = Webhooks automatically make HTTP POST requests to a server when certain openi events trigger. Webhooks defined here are defaults and will be copied into all new repositories. Read more in the <a target="_blank" rel="noopener" href="https://docs.openi.io/en-us/webhooks/">webhooks guide</a>. | |||
hooks.add_webhook = Add Default Webhook | |||
hooks.update_webhook = Update Default Webhook | |||
@@ -2677,6 +2702,13 @@ mirror_sync_create = synced new reference <a href="%s/src/%s">%[2]s</a> to <a hr | |||
mirror_sync_delete = synced and deleted reference <code>%[2]s</code> at <a href="%[1]s">%[3]s</a> from mirror | |||
approve_pull_request = `approved <a href="%s/pulls/%s">%s#%[2]s</a>` | |||
reject_pull_request = `suggested changes for <a href="%s/pulls/%s">%s#%[2]s</a>` | |||
upload_dataset=`upload dataset <a href="%s/datasets?type=%s">%s</a>` | |||
task_gpudebugjob=`created CPU/GPU type debugging task<a href="%s/cloudbrain/%s">%s</a>` | |||
task_npudebugjob=`created NPU type debugging task <a href="%s/modelarts/notebook/%s">%s</a>` | |||
task_trainjob=`created training task<a href="%s/modelarts/train-job/%s">%s</a>` | |||
task_inferencejob=`created reasoning task <a href="%s/modelarts/inference-job/%s">%s</a>` | |||
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>` | |||
[tool] | |||
ago = %s ago | |||
@@ -2751,6 +2783,7 @@ head.dataset = Datasets | |||
foot.council = Council | |||
foot.technical_committee = Technical Committee | |||
foot.join = Join OpenI | |||
foot.agreement=Use agreement | |||
foot.news = News | |||
foot.community_news = Community News | |||
foot.member_news = Member news | |||
@@ -496,6 +496,14 @@ account_link=已绑定帐户 | |||
organization=组织 | |||
uid=用户 ID | |||
u2f=安全密钥 | |||
wechat_bind = 微信绑定 | |||
bind_wechat = 绑定微信 | |||
bind_account_information = 绑定账号信息 | |||
bind_time = 绑定时间 | |||
wechat = 微信 | |||
unbind_wc = 解除绑定 | |||
unbind_wechat = 确定要解绑微信? | |||
unbind_computing = 解绑后将无法使用启智算力环境 | |||
public_profile=公开信息 | |||
profile_desc=您的电子邮件地址将用于通知和其他操作。 | |||
@@ -868,10 +876,13 @@ confirm_choice=确定 | |||
cloudbran1_tips=只有zip格式的数据集才能发起云脑任务 | |||
cloudbrain_creator=创建者 | |||
cloudbrain_task=任务名称 | |||
cloudbrain_task_type=任务类型 | |||
cloudbrain_task_name=云脑侧任务名称 | |||
cloudbrain_operate=操作 | |||
cloudbrain_status_createtime=状态/创建时间 | |||
cloudbrain_status_runtime = 运行时长 | |||
cloudbrain_jobname_err=只能以小写字母或数字开头且只包含小写字母、数字、_和-,不能以_结尾,最长36个字符。 | |||
cloudbrain_query_fail=查询云脑任务失败。 | |||
record_begintime_get_err=无法获取统计开始时间。 | |||
parameter_is_wrong=输入参数错误,请检查输入参数。 | |||
@@ -2069,6 +2080,8 @@ people=组织成员 | |||
teams=组织团队 | |||
lower_members=名成员 | |||
lower_repositories=个项目 | |||
lower_member=名成员 | |||
lower_repository=个项目 | |||
create_new_team=新建团队 | |||
create_team=创建团队 | |||
org_desc=组织描述 | |||
@@ -2088,6 +2101,9 @@ custom_select_courses = 自定义精选课程 | |||
recommend_remain_pro = 还能推荐 | |||
save_fail_tips = 最多可选9个,保存失败 | |||
select_again = 选择超过9个,请重新选择! | |||
custom_select_projects = 自定义精选项目 | |||
customize = 自定义 | |||
selected_project=精选项目 | |||
form.name_reserved=组织名称 '%s' 是被保留的。 | |||
form.name_pattern_not_allowed=组织名称中不允许使用 "%s"。 | |||
@@ -2349,6 +2365,13 @@ datasets.owner=所有者 | |||
datasets.name=名称 | |||
datasets.private=私有 | |||
cloudbrain.all_task_types=全部任务类型 | |||
cloudbrain.all_computing_resources=全部计算资源 | |||
cloudbrain.all_status=全部状态 | |||
cloudbrain.download_report=下载此报告 | |||
cloudbrain.cloudbrain_name=云脑侧名称 | |||
cloudbrain.search = 搜索任务名称/创建者 | |||
hooks.desc=当某些 openi 事件触发时, Web 钩子会自动向服务器发出 HTTP POST 请求。此处定义的 Web 钩子是默认值, 将复制到所有新建项目中。参阅 <a target="_blank" rel="noopener" href="https://docs.openi.io/en-us/webhooks/">Web钩子指南</a> 获取更多内容。 | |||
hooks.add_webhook=新增默认Web钩子 | |||
hooks.update_webhook=更新默认Web钩子 | |||
@@ -2415,7 +2438,7 @@ auths.sspi_auto_activate_users_helper=允许 SSPI 认证自动激活新用户 | |||
auths.sspi_strip_domain_names=从用户名中删除域名部分 | |||
auths.sspi_strip_domain_names_helper=如果选中此项,域名将从登录名中删除(例如,"DOMAIN\user"和"user@example.org",两者都将变成只是“用户”)。 | |||
auths.sspi_separator_replacement=要使用的分隔符代替\, / 和 @ | |||
auths.sspi_separator_replacement_helper=用于替换下级登录名称分隔符的字符 (例如) "DOMAIN\user") 中的 \ 和用户主名字(如"user@example.org中的 @ )。 | |||
auths.sspi_separator_replacement_helper=用于替换下级登录名称分隔符的字符 (例如) "DOMAIN\user") 中的 \ 和用户主名字(如"user@example.org"中的 @ )。 | |||
auths.sspi_default_language=默认语言 | |||
auths.sspi_default_language_helper=SSPI 认证方法为用户自动创建的默认语言。如果您想要自动检测到语言,请留空。 | |||
auths.tips=帮助提示 | |||
@@ -2688,6 +2711,13 @@ mirror_sync_create=从镜像同步了新的引用 <a href="%s/src/%s">%[2]s</a> | |||
mirror_sync_delete=从镜像同步并从 <a href="%[1]s">%[3]s</a> 删除了引用 <code>%[2]s</code> | |||
approve_pull_request=`同意了 <a href="%s/pulls/%s">%s#%[2]s</a>` | |||
reject_pull_request=`建议变更 <a href="%s/pulls/%s">%s#%[2]s</a>` | |||
upload_dataset=`上传了数据集文件 <a href="%s/datasets?type=%s">%s</a>` | |||
task_gpudebugjob=`创建了CPU/GPU类型调试任务 <a href="%s/cloudbrain/%s">%s</a>` | |||
task_npudebugjob=`创建了NPU类型调试任务 <a href="%s/modelarts/notebook/%s">%s</a>` | |||
task_trainjob=`创建了训练任务 <a href="%s/modelarts/train-job/%s">%s</a>` | |||
task_inferencejob=`创建了推理任务 <a href="%s/modelarts/inference-job/%s">%s</a>` | |||
task_benchmark=`创建了评测任务 <a href="%s/cloudbrain/benchmark/%s">%s</a>` | |||
task_createmodel=`导入了新模型 <a href="%s/modelmanage/show_model_info?name=%s">%s</a>` | |||
[tool] | |||
ago=%s前 | |||
@@ -2762,6 +2792,7 @@ head.dataset=数据集 | |||
foot.council=理事会 | |||
foot.technical_committee=技术委员会 | |||
foot.join=加入启智 | |||
foot.agreement=使用协议 | |||
foot.news=动态 | |||
foot.community_news=社区动态 | |||
foot.member_news=成员动态 | |||
@@ -7787,6 +7787,11 @@ | |||
} | |||
} | |||
}, | |||
"js-cookie": { | |||
"version": "3.0.1", | |||
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.1.tgz", | |||
"integrity": "sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw==" | |||
}, | |||
"js-file-download": { | |||
"version": "0.4.12", | |||
"resolved": "https://registry.npmjs.org/js-file-download/-/js-file-download-0.4.12.tgz", | |||
@@ -11147,6 +11152,11 @@ | |||
"resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", | |||
"integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" | |||
}, | |||
"qrcodejs2": { | |||
"version": "0.0.2", | |||
"resolved": "https://registry.npmjs.org/qrcodejs2/-/qrcodejs2-0.0.2.tgz", | |||
"integrity": "sha1-Rlr+Xjnxn6zsuTLBH3oYYQkUauE=" | |||
}, | |||
"qs": { | |||
"version": "6.9.4", | |||
"resolved": "https://registry.npm.taobao.org/qs/download/qs-6.9.4.tgz", | |||
@@ -13869,6 +13879,130 @@ | |||
"integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==", | |||
"dev": true | |||
}, | |||
"ts-loader": { | |||
"version": "4.0.0", | |||
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-4.0.0.tgz", | |||
"integrity": "sha512-iissbnuJkqbB3YAmnWyEbmdNcGcoiiXopKHKyqdoCrFQVi9pnplXeveQDXJnQOCYNNcb2pjT2zzSYTX6c9QtAA==", | |||
"dev": true, | |||
"requires": { | |||
"chalk": "^2.3.0", | |||
"enhanced-resolve": "^4.0.0", | |||
"loader-utils": "^1.0.2", | |||
"micromatch": "^3.1.4", | |||
"semver": "^5.0.1" | |||
}, | |||
"dependencies": { | |||
"braces": { | |||
"version": "2.3.2", | |||
"resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", | |||
"integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", | |||
"dev": true, | |||
"requires": { | |||
"arr-flatten": "^1.1.0", | |||
"array-unique": "^0.3.2", | |||
"extend-shallow": "^2.0.1", | |||
"fill-range": "^4.0.0", | |||
"isobject": "^3.0.1", | |||
"repeat-element": "^1.1.2", | |||
"snapdragon": "^0.8.1", | |||
"snapdragon-node": "^2.0.1", | |||
"split-string": "^3.0.2", | |||
"to-regex": "^3.0.1" | |||
}, | |||
"dependencies": { | |||
"extend-shallow": { | |||
"version": "2.0.1", | |||
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", | |||
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", | |||
"dev": true, | |||
"requires": { | |||
"is-extendable": "^0.1.0" | |||
} | |||
} | |||
} | |||
}, | |||
"fill-range": { | |||
"version": "4.0.0", | |||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", | |||
"integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", | |||
"dev": true, | |||
"requires": { | |||
"extend-shallow": "^2.0.1", | |||
"is-number": "^3.0.0", | |||
"repeat-string": "^1.6.1", | |||
"to-regex-range": "^2.1.0" | |||
}, | |||
"dependencies": { | |||
"extend-shallow": { | |||
"version": "2.0.1", | |||
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", | |||
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", | |||
"dev": true, | |||
"requires": { | |||
"is-extendable": "^0.1.0" | |||
} | |||
} | |||
} | |||
}, | |||
"is-number": { | |||
"version": "3.0.0", | |||
"resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", | |||
"integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", | |||
"dev": true, | |||
"requires": { | |||
"kind-of": "^3.0.2" | |||
}, | |||
"dependencies": { | |||
"kind-of": { | |||
"version": "3.2.2", | |||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", | |||
"integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", | |||
"dev": true, | |||
"requires": { | |||
"is-buffer": "^1.1.5" | |||
} | |||
} | |||
} | |||
}, | |||
"isobject": { | |||
"version": "3.0.1", | |||
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", | |||
"integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", | |||
"dev": true | |||
}, | |||
"micromatch": { | |||
"version": "3.1.10", | |||
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", | |||
"integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", | |||
"dev": true, | |||
"requires": { | |||
"arr-diff": "^4.0.0", | |||
"array-unique": "^0.3.2", | |||
"braces": "^2.3.1", | |||
"define-property": "^2.0.2", | |||
"extend-shallow": "^3.0.2", | |||
"extglob": "^2.0.4", | |||
"fragment-cache": "^0.2.1", | |||
"kind-of": "^6.0.2", | |||
"nanomatch": "^1.2.9", | |||
"object.pick": "^1.3.0", | |||
"regex-not": "^1.0.0", | |||
"snapdragon": "^0.8.1", | |||
"to-regex": "^3.0.2" | |||
} | |||
}, | |||
"to-regex-range": { | |||
"version": "2.1.1", | |||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", | |||
"integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", | |||
"dev": true, | |||
"requires": { | |||
"is-number": "^3.0.0", | |||
"repeat-string": "^1.6.1" | |||
} | |||
} | |||
} | |||
}, | |||
"tslib": { | |||
"version": "1.13.0", | |||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", | |||
@@ -13928,6 +14062,12 @@ | |||
"is-typedarray": "^1.0.0" | |||
} | |||
}, | |||
"typescript": { | |||
"version": "4.5.5", | |||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz", | |||
"integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", | |||
"dev": true | |||
}, | |||
"ua-parser-js": { | |||
"version": "0.7.21", | |||
"resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.21.tgz", | |||
@@ -33,6 +33,7 @@ | |||
"jquery": "3.5.1", | |||
"jquery-datetimepicker": "2.5.21", | |||
"jquery.are-you-sure": "1.9.0", | |||
"js-cookie": "3.0.1", | |||
"less-loader": "6.1.0", | |||
"mini-css-extract-plugin": "0.9.0", | |||
"monaco-editor": "0.20.0", | |||
@@ -41,6 +42,7 @@ | |||
"postcss-loader": "3.0.0", | |||
"postcss-preset-env": "6.7.0", | |||
"postcss-safe-parser": "4.0.2", | |||
"qrcodejs2": "0.0.2", | |||
"qs": "6.9.4", | |||
"remixicon": "2.5.0", | |||
"spark-md5": "3.0.1", | |||
@@ -69,6 +71,8 @@ | |||
"script-loader": "0.7.2", | |||
"stylelint": "13.3.3", | |||
"stylelint-config-standard": "20.0.0", | |||
"ts-loader": "4.0.0", | |||
"typescript": "4.5.5", | |||
"updates": "10.2.11" | |||
}, | |||
"browserslist": [ | |||
@@ -6,7 +6,6 @@ if(isEmpty(token)){ | |||
token = meta.attr("content"); | |||
} | |||
} | |||
var swiperNewMessage = new Swiper(".newslist", { | |||
direction: "vertical", | |||
slidesPerView: 10, | |||
@@ -17,7 +16,7 @@ var swiperNewMessage = new Swiper(".newslist", { | |||
}, | |||
}); | |||
var swiperRepo = new Swiper(".homepro-list", { | |||
slidesPerView: 3, | |||
slidesPerView: 1, | |||
slidesPerColumn: 2, | |||
slidesPerColumnFill:'row', | |||
spaceBetween: 30, | |||
@@ -29,6 +28,37 @@ var swiperRepo = new Swiper(".homepro-list", { | |||
delay: 2500, | |||
disableOnInteraction: false, | |||
}, | |||
breakpoints: { | |||
768: { | |||
slidesPerView: 2, | |||
}, | |||
1024: { | |||
slidesPerView: 3, | |||
}, | |||
}, | |||
}); | |||
var swiperOrg = new Swiper(".homeorg-list", { | |||
slidesPerView: 1, | |||
slidesPerColumn: 4, | |||
slidesPerColumnFill:'row', | |||
spaceBetween: 15, | |||
pagination: { | |||
el: ".swiper-pagination", | |||
clickable: true, | |||
}, | |||
autoplay: { | |||
delay: 4500, | |||
disableOnInteraction: false, | |||
}, | |||
breakpoints: { | |||
768: { | |||
slidesPerView: 2, | |||
}, | |||
1024: { | |||
slidesPerView: 3, | |||
}, | |||
}, | |||
}); | |||
var output = document.getElementById("newmessage"); | |||
@@ -64,10 +94,12 @@ socket.onmessage = function (e) { | |||
var currentTime = new Date().getTime(); | |||
for(var i = 0; i < messageQueue.length; i++){ | |||
var record = messageQueue[i]; | |||
var recordPrefix = getMsg(record); | |||
var actionName = getAction(record.OpType,isZh); | |||
if(record.ActUser == null){ | |||
console.log("receive action type=" + record.OpType + " name=" + actionName + " but user is null."); | |||
continue; | |||
} | |||
var recordPrefix = getMsg(record); | |||
if(record.OpType == "6" || record.OpType == "10" || record.OpType == "12" || record.OpType == "13"){ | |||
html += recordPrefix + actionName; | |||
html += " <a href=\"" + getIssueLink(record) + "\" rel=\"nofollow\">" + getIssueText(record) + "</a>" | |||
@@ -96,11 +128,14 @@ socket.onmessage = function (e) { | |||
actionName = actionName.replace("{oldRepoName}",record.Content); | |||
html += recordPrefix + actionName; | |||
html += " <a href=\"" + getRepoLink(record) + "\" rel=\"nofollow\">" + getRepotext(record) + "</a>" | |||
} | |||
else if(record.OpType == "24" || record.OpType == "25" || record.OpType == "26" || record.OpType == "27" || record.OpType == "28" || record.OpType == "29" || record.OpType == "30"){ | |||
html += recordPrefix + actionName; | |||
html += " <a href=\"" + getTaskLink(record) + "\" rel=\"nofollow\">" + record.RefName + "</a>" | |||
} | |||
else{ | |||
continue; | |||
} | |||
if(record.Repo != null){ | |||
var time = getTime(record.CreatedUnix,currentTime); | |||
html += " " + time; | |||
@@ -108,19 +143,44 @@ socket.onmessage = function (e) { | |||
html += "</div>"; | |||
html += "</div>"; | |||
} | |||
} | |||
output.innerHTML = html; | |||
swiperNewMessage.updateSlides(); | |||
swiperNewMessage.updateProgress(); | |||
}; | |||
function getTaskLink(record){ | |||
var re = getRepoLink(record); | |||
if(record.OpType == 24){ | |||
return re + "/datasets?type=" + record.Content; | |||
}else if(record.OpType == 25){ | |||
return re + "/cloudbrain/" + record.RefName; | |||
}else if(record.OpType == 26){ | |||
return re + "/modelarts/notebook/" + record.Content; | |||
}else if(record.OpType == 27){ | |||
return re + "/modelarts/train-job/" + record.Content; | |||
}else if(record.OpType == 28){ | |||
return re + "/modelarts/inference-job/" + record.Content; | |||
}else if(record.OpType == 29){ | |||
return re + "/cloudbrain/benchmark/" + record.RefName; | |||
}else if(record.OpType == 30){ | |||
return re + "/modelmanage/show_model_info?name=" + record.RefName; | |||
} | |||
return re; | |||
} | |||
function getMsg(record){ | |||
var html =""; | |||
html += "<div class=\"swiper-slide item\">"; | |||
html += " <img class=\"ui avatar image\" src=\"/user/avatar/" + record.ActUser.Name + "/-1\" alt=\"\">" | |||
var name = ""; | |||
if(record.ActUser != null){ | |||
name = record.ActUser.Name; | |||
}else{ | |||
console.log("act user is null."); | |||
} | |||
html += " <img class=\"ui avatar image\" src=\"/user/avatar/" + name + "/-1\" alt=\"\">" | |||
html += " <div class=\"middle aligned content nowrap\">" | |||
html += " <a href=\"/" + record.ActUser.Name + "\" title=\"\">" + record.ActUser.Name + "</a>" | |||
html += " <a href=\"/" + name + "\" title=\"\">" + name + "</a>" | |||
return html; | |||
} | |||
@@ -246,7 +306,14 @@ var actionNameZH={ | |||
"15":"重新开启了合并请求", | |||
"17":"从 {repoName} 删除分支 {deleteBranchName}", | |||
"22":"建议变更", | |||
"23":"评论了合并请求" | |||
"23":"评论了合并请求", | |||
"24":"上传了数据集文件", | |||
"25":"创建了CPU/GPU类型调试任务", | |||
"26":"创建了NPU类型调试任务", | |||
"27":"创建了训练任务", | |||
"28":"创建了推理任务", | |||
"29":"创建了评测任务", | |||
"30":"导入了新模型" | |||
}; | |||
var actionNameEN={ | |||
@@ -264,7 +331,14 @@ var actionNameEN={ | |||
"15":" reopened pull request", | |||
"17":" deleted branch {deleteBranchName} from {repoName}", | |||
"22":" proposed changes", | |||
"23":" commented on pull request" | |||
"23":" commented on pull request", | |||
"24":" upload dataset ", | |||
"25":" created CPU/GPU type debugging task ", | |||
"26":" created NPU type debugging task ", | |||
"27":" created training task", | |||
"28":" created reasoning task", | |||
"29":" created profiling task", | |||
"30":" created new model" | |||
}; | |||
var repoAndOrgZH={ | |||
@@ -392,7 +466,7 @@ function displayOrg(json){ | |||
if (json != null && json.length > 0){ | |||
for(var i = 0; i < json.length;i++){ | |||
var record = json[i] | |||
html += "<div class=\"column\">"; | |||
html += "<div class=\"swiper-slide\">"; | |||
html += " <a href=\"/" + record["Name"] + "\" class=\"ui fluid card\">"; | |||
html += " <div class=\"content\">"; | |||
html += " <div class=\"ui small header\">"; | |||
@@ -408,4 +482,5 @@ function displayOrg(json){ | |||
} | |||
} | |||
orgDiv.innerHTML = html; | |||
swiperOrg.updateSlides(); | |||
} |
@@ -0,0 +1,225 @@ | |||
package admin | |||
import ( | |||
"net/http" | |||
"net/url" | |||
"strconv" | |||
"strings" | |||
"time" | |||
"github.com/360EntSecGroup-Skylar/excelize/v2" | |||
"code.gitea.io/gitea/modules/modelarts" | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/modules/base" | |||
"code.gitea.io/gitea/modules/context" | |||
"code.gitea.io/gitea/modules/log" | |||
"code.gitea.io/gitea/modules/setting" | |||
) | |||
const ( | |||
tplCloudBrains base.TplName = "admin/cloudbrain/list" | |||
EXCEL_DATE_FORMAT = "20060102150405" | |||
CREATE_TIME_FORMAT = "2006/01/02 15:04:05" | |||
) | |||
func CloudBrains(ctx *context.Context) { | |||
ctx.Data["Title"] = ctx.Tr("admin.cloudBrains") | |||
ctx.Data["PageIsAdmin"] = true | |||
ctx.Data["PageIsAdminCloudBrains"] = true | |||
listType := ctx.Query("listType") | |||
jobType := ctx.Query("jobType") | |||
jobStatus := ctx.Query("jobStatus") | |||
ctx.Data["ListType"] = listType | |||
ctx.Data["JobType"] = jobType | |||
ctx.Data["JobStatus"] = jobStatus | |||
page := ctx.QueryInt("page") | |||
if page <= 0 { | |||
page = 1 | |||
} | |||
debugType := modelarts.DebugType | |||
if listType == models.GPUResource { | |||
debugType = models.TypeCloudBrainOne | |||
} else if listType == models.NPUResource { | |||
debugType = models.TypeCloudBrainTwo | |||
} | |||
var jobTypes []string | |||
jobTypeNot := false | |||
if jobType == string(models.JobTypeDebug) { | |||
jobTypes = append(jobTypes, string(models.JobTypeSnn4imagenet), string(models.JobTypeBrainScore), string(models.JobTypeDebug)) | |||
} else if jobType != "all" && jobType != "" { | |||
jobTypes = append(jobTypes, jobType) | |||
} | |||
var jobStatuses []string | |||
jobStatusNot := false | |||
if jobStatus == "other" { | |||
jobStatusNot = true | |||
jobStatuses = append(jobStatuses, string(models.ModelArtsTrainJobWaiting), string(models.ModelArtsTrainJobFailed), string(models.ModelArtsRunning), string(models.ModelArtsTrainJobCompleted), | |||
string(models.ModelArtsStarting), string(models.ModelArtsRestarting), string(models.ModelArtsStartFailed), | |||
string(models.ModelArtsStopping), string(models.ModelArtsStopped), string(models.JobSucceeded)) | |||
} else if jobStatus != "all" && jobStatus != "" { | |||
jobStatuses = append(jobStatuses, jobStatus) | |||
} | |||
keyword := strings.Trim(ctx.Query("q"), " ") | |||
ciTasks, count, err := models.Cloudbrains(&models.CloudbrainsOptions{ | |||
ListOptions: models.ListOptions{ | |||
Page: page, | |||
PageSize: setting.UI.IssuePagingNum, | |||
}, | |||
Keyword: keyword, | |||
Type: debugType, | |||
JobTypeNot: jobTypeNot, | |||
JobStatusNot: jobStatusNot, | |||
JobStatus: jobStatuses, | |||
JobTypes: jobTypes, | |||
NeedRepoInfo: true, | |||
IsLatestVersion: modelarts.IsLatestVersion, | |||
}) | |||
if err != nil { | |||
ctx.ServerError("Get job failed:", err) | |||
return | |||
} | |||
for i, task := range ciTasks { | |||
ciTasks[i].CanDebug = true | |||
ciTasks[i].CanDel = true | |||
ciTasks[i].Cloudbrain.ComputeResource = task.ComputeResource | |||
} | |||
pager := context.NewPagination(int(count), setting.UI.IssuePagingNum, page, getTotalPage(count, setting.UI.IssuePagingNum)) | |||
pager.SetDefaultParams(ctx) | |||
pager.AddParam(ctx, "listType", "ListType") | |||
ctx.Data["Page"] = pager | |||
ctx.Data["PageIsCloudBrain"] = true | |||
ctx.Data["Tasks"] = ciTasks | |||
ctx.Data["CanCreate"] = true | |||
ctx.Data["Keyword"] = keyword | |||
ctx.HTML(200, tplCloudBrains) | |||
} | |||
func DownloadCloudBrains(ctx *context.Context) { | |||
page := 1 | |||
pageSize := 300 | |||
var cloudBrain = ctx.Tr("repo.cloudbrain") | |||
fileName := getFileName(cloudBrain) | |||
_, total, err := models.Cloudbrains(&models.CloudbrainsOptions{ | |||
ListOptions: models.ListOptions{ | |||
Page: page, | |||
PageSize: 1, | |||
}, | |||
Type: modelarts.DebugType, | |||
NeedRepoInfo: false, | |||
IsLatestVersion: modelarts.IsLatestVersion, | |||
}) | |||
if err != nil { | |||
log.Warn("Can not get cloud brain info", err) | |||
ctx.Error(http.StatusBadRequest, ctx.Tr("repo.cloudbrain_query_fail")) | |||
return | |||
} | |||
totalPage := getTotalPage(total, pageSize) | |||
f := excelize.NewFile() | |||
index := f.NewSheet(cloudBrain) | |||
f.DeleteSheet("Sheet1") | |||
for k, v := range allHeader(ctx) { | |||
f.SetCellValue(cloudBrain, k, v) | |||
} | |||
var row = 2 | |||
for i := 0; i < totalPage; i++ { | |||
pageRecords, _, err := models.Cloudbrains(&models.CloudbrainsOptions{ | |||
ListOptions: models.ListOptions{ | |||
Page: page, | |||
PageSize: pageSize, | |||
}, | |||
Type: modelarts.DebugType, | |||
NeedRepoInfo: true, | |||
IsLatestVersion: modelarts.IsLatestVersion, | |||
}) | |||
if err != nil { | |||
log.Warn("Can not get cloud brain info", err) | |||
continue | |||
} | |||
for _, record := range pageRecords { | |||
for k, v := range allValues(row, record, ctx) { | |||
f.SetCellValue(cloudBrain, k, v) | |||
} | |||
row++ | |||
} | |||
page++ | |||
} | |||
f.SetActiveSheet(index) | |||
ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+url.QueryEscape(fileName)) | |||
ctx.Resp.Header().Set("Content-Type", "application/octet-stream") | |||
f.WriteTo(ctx.Resp) | |||
} | |||
func allValues(row int, rs *models.CloudbrainInfo, ctx *context.Context) map[string]string { | |||
return map[string]string{getCellName("A", row): rs.JobName, getCellName("B", row): rs.JobType, getCellName("C", row): rs.Status, getCellName("D", row): time.Unix(int64(rs.Cloudbrain.CreatedUnix), 0).Format(CREATE_TIME_FORMAT), getCellName("E", row): getDurationTime(rs), | |||
getCellName("F", row): rs.ComputeResource, getCellName("G", row): rs.Name, getCellName("H", row): getRepoPathName(rs), getCellName("I", row): rs.JobName, | |||
} | |||
} | |||
func getRepoPathName(rs *models.CloudbrainInfo) string { | |||
if rs.Repo != nil { | |||
return rs.Repo.OwnerName + "/" + rs.Repo.Alias | |||
} | |||
return "" | |||
} | |||
func getDurationTime(rs *models.CloudbrainInfo) string { | |||
if rs.JobType == "TRAIN" || rs.JobType == "INFERENCE" { | |||
return rs.TrainJobDuration | |||
} else { | |||
return "-" | |||
} | |||
} | |||
func getFileName(baseName string) string { | |||
return baseName + "_" + time.Now().Format(EXCEL_DATE_FORMAT) + ".xlsx" | |||
} | |||
func getTotalPage(total int64, pageSize int) int { | |||
another := 0 | |||
if int(total)%pageSize != 0 { | |||
another = 1 | |||
} | |||
return int(total)/pageSize + another | |||
} | |||
func allHeader(ctx *context.Context) map[string]string { | |||
return map[string]string{"A1": ctx.Tr("repo.cloudbrain_task"), "B1": ctx.Tr("repo.cloudbrain_task_type"), "C1": ctx.Tr("repo.modelarts.status"), "D1": ctx.Tr("repo.modelarts.createtime"), "E1": ctx.Tr("repo.modelarts.train_job.dura_time"), "F1": ctx.Tr("repo.modelarts.computing_resources"), "G1": ctx.Tr("repo.cloudbrain_creator"), "H1": ctx.Tr("repo.repo_name"), "I1": ctx.Tr("repo.cloudbrain_task_name")} | |||
} | |||
func getCellName(col string, row int) string { | |||
return col + strconv.Itoa(row) | |||
} |
@@ -62,6 +62,8 @@ import ( | |||
"net/http" | |||
"strings" | |||
"code.gitea.io/gitea/routers/authentication" | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/modules/auth" | |||
"code.gitea.io/gitea/modules/context" | |||
@@ -879,7 +881,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
}, reqAnyRepoReader()) | |||
m.Group("/cloudbrain", func() { | |||
m.Get("/:jobid", repo.GetCloudbrainTask) | |||
m.Get("/:jobid/log", repo.CloudbrainGetLog) | |||
m.Get("/:jobname/log", repo.CloudbrainGetLog) | |||
}, reqRepoReader(models.UnitTypeCloudBrain)) | |||
m.Group("/modelarts", func() { | |||
m.Group("/notebook", func() { | |||
@@ -995,6 +997,12 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
m.Group("/topics", func() { | |||
m.Get("/search", repo.TopicSearch) | |||
}) | |||
m.Group("/from_wechat", func() { | |||
m.Get("/event", authentication.ValidEventSource) | |||
m.Post("/event", authentication.AcceptWechatEvent) | |||
m.Get("/prd/event", authentication.ValidEventSource) | |||
m.Post("/prd/event", authentication.AcceptWechatEvent) | |||
}) | |||
}, securityHeaders(), context.APIContexter(), sudo()) | |||
} | |||
@@ -6,11 +6,12 @@ | |||
package repo | |||
import ( | |||
"code.gitea.io/gitea/modules/log" | |||
"net/http" | |||
"sort" | |||
"time" | |||
"code.gitea.io/gitea/modules/log" | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/modules/cloudbrain" | |||
"code.gitea.io/gitea/modules/context" | |||
@@ -48,14 +49,15 @@ func GetCloudbrainTask(ctx *context.APIContext) { | |||
err error | |||
) | |||
// jobName := ctx.Params(":jobname") | |||
// job, err := models.GetCloudbrainByName(jobName) | |||
jobID := ctx.Params(":jobid") | |||
repoID := ctx.Repo.Repository.ID | |||
job, err := models.GetRepoCloudBrainByJobID(repoID, jobID) | |||
if err != nil { | |||
ctx.NotFound(err) | |||
return | |||
ctx.Data["error"] = err.Error() | |||
} | |||
jobResult, err := cloudbrain.GetJob(jobID) | |||
jobResult, err := cloudbrain.GetJob(job.JobID) | |||
if err != nil { | |||
ctx.NotFound(err) | |||
return | |||
@@ -85,6 +87,7 @@ func GetCloudbrainTask(ctx *context.APIContext) { | |||
ctx.JSON(http.StatusOK, map[string]interface{}{ | |||
"JobID": result.Config.JobID, | |||
"JobName": result.Config.JobName, | |||
"JobStatus": result.JobStatus.State, | |||
"SubState": result.JobStatus.SubState, | |||
"CreatedTime": time.Unix(result.JobStatus.CreatedTime/1000, 0).Format("2006-01-02 15:04:05"), | |||
@@ -94,17 +97,17 @@ func GetCloudbrainTask(ctx *context.APIContext) { | |||
} | |||
func CloudbrainGetLog(ctx *context.Context) { | |||
jobID := ctx.Params(":jobid") | |||
_, err := models.GetCloudbrainByJobID(jobID) | |||
jobName := ctx.Params(":jobname") | |||
job, err := models.GetCloudbrainByName(jobName) | |||
if err != nil { | |||
log.Error("GetCloudbrainByJobID failed: %v", err, ctx.Data["MsgID"]) | |||
log.Error("GetCloudbrainByJobName failed: %v", err, ctx.Data["MsgID"]) | |||
ctx.ServerError(err.Error(), err) | |||
return | |||
} | |||
var hits []models.Hits | |||
result, err := cloudbrain.GetJobLog(jobID) | |||
if err != nil{ | |||
result, err := cloudbrain.GetJobLog(job.JobID) | |||
if err != nil { | |||
log.Error("GetJobLog failed: %v", err, ctx.Data["MsgID"]) | |||
ctx.ServerError(err.Error(), err) | |||
return | |||
@@ -115,7 +118,7 @@ func CloudbrainGetLog(ctx *context.Context) { | |||
if len(result.Hits.Hits) >= cloudbrain.LogPageSize { | |||
for { | |||
resultNext, err := cloudbrain.GetJobAllLog(result.ScrollID) | |||
if err != nil{ | |||
if err != nil { | |||
log.Error("GetJobAllLog failed: %v", err, ctx.Data["MsgID"]) | |||
} else { | |||
for _, hit := range resultNext.Hits.Hits { | |||
@@ -142,8 +145,8 @@ func CloudbrainGetLog(ctx *context.Context) { | |||
} | |||
ctx.JSON(http.StatusOK, map[string]interface{}{ | |||
"JobID": jobID, | |||
"Content": content, | |||
"JobName": jobName, | |||
"Content": content, | |||
}) | |||
return | |||
@@ -77,6 +77,7 @@ func GetModelArtsNotebook2(ctx *context.APIContext) { | |||
ctx.JSON(http.StatusOK, map[string]interface{}{ | |||
"JobID": jobID, | |||
"JobName": job.JobName, | |||
"JobStatus": result.Status, | |||
}) | |||
@@ -297,7 +297,7 @@ func allProjectsPeroidHeader(ctx *context.Context) map[string]string { | |||
} | |||
func allProjectsPeroidValues(row int, rs *models.RepoStatistic, ctx *context.Context) map[string]string { | |||
return map[string]string{getCellName("A", row): strconv.FormatInt(rs.RepoID, 10), getCellName("B", row): rs.Name, getCellName("C", row): rs.OwnerName, getCellName("D", row): getIsPrivateDisplay(rs.IsPrivate, ctx), getCellName("E", row): strconv.FormatFloat(rs.RadarTotal, 'f', 2, 64), | |||
return map[string]string{getCellName("A", row): strconv.FormatInt(rs.RepoID, 10), getCellName("B", row): rs.DisplayName(), getCellName("C", row): rs.OwnerName, getCellName("D", row): getIsPrivateDisplay(rs.IsPrivate, ctx), getCellName("E", row): strconv.FormatFloat(rs.RadarTotal, 'f', 2, 64), | |||
getCellName("F", row): strconv.FormatInt(rs.NumVisits, 10), getCellName("G", row): strconv.FormatInt(rs.NumDownloads, 10), getCellName("H", row): strconv.FormatInt(rs.NumPulls, 10), getCellName("I", row): strconv.FormatInt(rs.NumCommits, 10), | |||
getCellName("J", row): strconv.FormatInt(rs.NumWatches, 10), getCellName("K", row): strconv.FormatInt(rs.NumStars, 10), getCellName("L", row): strconv.FormatInt(rs.NumForks, 10), getCellName("M", row): strconv.FormatInt(rs.NumIssues, 10), | |||
getCellName("N", row): strconv.FormatInt(rs.NumClosedIssues, 10), getCellName("O", row): strconv.FormatInt(rs.NumContributor, 10), | |||
@@ -317,7 +317,7 @@ func allProjectsOpenIHeader() map[string]string { | |||
func allProjectsOpenIValues(row int, rs *models.RepoStatistic, ctx *context.Context) map[string]string { | |||
return map[string]string{getCellName("A", row): strconv.FormatInt(rs.RepoID, 10), getCellName("B", row): rs.Name, getCellName("C", row): rs.OwnerName, getCellName("D", row): getIsPrivateDisplay(rs.IsPrivate, ctx), getCellName("E", row): strconv.FormatFloat(rs.RadarTotal, 'f', 2, 64), | |||
return map[string]string{getCellName("A", row): strconv.FormatInt(rs.RepoID, 10), getCellName("B", row): rs.DisplayName(), getCellName("C", row): rs.OwnerName, getCellName("D", row): getIsPrivateDisplay(rs.IsPrivate, ctx), getCellName("E", row): strconv.FormatFloat(rs.RadarTotal, 'f', 2, 64), | |||
getCellName("F", row): strconv.FormatFloat(rs.Impact, 'f', 2, 64), getCellName("G", row): strconv.FormatFloat(rs.Completeness, 'f', 2, 64), getCellName("H", row): strconv.FormatFloat(rs.Liveness, 'f', 2, 64), getCellName("I", row): strconv.FormatFloat(rs.ProjectHealth, 'f', 2, 64), getCellName("J", row): strconv.FormatFloat(rs.TeamHealth, 'f', 2, 64), getCellName("K", row): strconv.FormatFloat(rs.Growth, 'f', 2, 64), | |||
getCellName("L", row): strconv.FormatInt(rs.NumWatches, 10), getCellName("M", row): strconv.FormatInt(rs.NumStars, 10), getCellName("N", row): strconv.FormatInt(rs.NumForks, 10), getCellName("O", row): strconv.FormatInt(rs.NumDownloads, 10), | |||
@@ -466,10 +466,10 @@ func generateCountSql(beginTime time.Time, endTime time.Time, latestDate string, | |||
countSql := "SELECT count(*) FROM " + | |||
"(SELECT repo_id FROM repo_statistic where created_unix >=" + strconv.FormatInt(beginTime.Unix(), 10) + | |||
" and created_unix<" + strconv.FormatInt(endTime.Unix(), 10) + " group by repo_id) A," + | |||
"(SELECT repo_id,name,is_private,radar_total from public.repo_statistic where date='" + latestDate + "') B" + | |||
"(SELECT repo_id,name,alias,is_private,radar_total from public.repo_statistic where date='" + latestDate + "') B" + | |||
" where A.repo_id=B.repo_id" | |||
if q != "" { | |||
countSql = countSql + " and LOWER(B.name) like '%" + strings.ToLower(q) + "%'" | |||
countSql = countSql + " and LOWER(B.alias) like '%" + strings.ToLower(q) + "%'" | |||
} | |||
return countSql | |||
} | |||
@@ -482,22 +482,22 @@ func generateOpenICountSql(latestDate string) string { | |||
} | |||
func generateTypeAllSql(beginTime time.Time, endTime time.Time, latestDate string, q string, orderBy string, page int, pageSize int) string { | |||
sql := "SELECT A.repo_id,name,owner_name,is_private,radar_total,num_watches,num_visits,num_downloads,num_pulls,num_commits,num_stars,num_forks,num_issues,num_closed_issues,num_contributor FROM " + | |||
sql := "SELECT A.repo_id,name,alias,owner_name,is_private,radar_total,num_watches,num_visits,num_downloads,num_pulls,num_commits,num_stars,num_forks,num_issues,num_closed_issues,num_contributor FROM " + | |||
"(SELECT repo_id,sum(num_visits) as num_visits " + | |||
" FROM repo_statistic where created_unix >=" + strconv.FormatInt(beginTime.Unix(), 10) + | |||
" and created_unix<" + strconv.FormatInt(endTime.Unix(), 10) + " group by repo_id) A," + | |||
"(SELECT repo_id,name,owner_name,is_private,radar_total,num_watches,num_downloads,num_pulls,num_commits,num_stars,num_forks,num_issues,num_closed_issues,num_contributor from public.repo_statistic where date='" + latestDate + "') B" + | |||
"(SELECT repo_id,name,alias,owner_name,is_private,radar_total,num_watches,num_downloads,num_pulls,num_commits,num_stars,num_forks,num_issues,num_closed_issues,num_contributor from public.repo_statistic where date='" + latestDate + "') B" + | |||
" where A.repo_id=B.repo_id" | |||
if q != "" { | |||
sql = sql + " and LOWER(name) like '%" + strings.ToLower(q) + "%'" | |||
sql = sql + " and LOWER(alias) like '%" + strings.ToLower(q) + "%'" | |||
} | |||
sql = sql + " order by " + orderBy + " desc,repo_id" + " limit " + strconv.Itoa(pageSize) + " offset " + strconv.Itoa((page-1)*pageSize) | |||
return sql | |||
} | |||
func generateTypeAllOpenISql(latestDate string, page int, pageSize int) string { | |||
sql := "SELECT id, repo_id, date, num_watches, num_stars, num_forks, num_downloads, num_comments, num_visits, num_closed_issues, num_versions, num_dev_months, repo_size, dataset_size, num_models, num_wiki_views, num_commits, num_issues, num_pulls, issue_fixed_rate, num_contributor, num_key_contributor, num_contributors_growth, num_commits_growth, num_commit_lines_growth, num_issues_growth, num_comments_growth, impact, completeness, liveness, project_health, team_health, growth, radar_total, name, is_private, owner_name FROM " + | |||
sql := "SELECT id, repo_id, date, num_watches, num_stars, num_forks, num_downloads, num_comments, num_visits, num_closed_issues, num_versions, num_dev_months, repo_size, dataset_size, num_models, num_wiki_views, num_commits, num_issues, num_pulls, issue_fixed_rate, num_contributor, num_key_contributor, num_contributors_growth, num_commits_growth, num_commit_lines_growth, num_issues_growth, num_comments_growth, impact, completeness, liveness, project_health, team_health, growth, radar_total, name,alias, is_private, owner_name FROM " + | |||
" public.repo_statistic where date='" + latestDate + "'" | |||
sql = sql + " order by radar_total desc,repo_id" + " limit " + strconv.Itoa(pageSize) + " offset " + strconv.Itoa((page-1)*pageSize) | |||
@@ -506,14 +506,14 @@ func generateTypeAllOpenISql(latestDate string, page int, pageSize int) string { | |||
func generatePageSql(beginTime time.Time, endTime time.Time, latestDate string, q string, orderBy string, page int, pageSize int) string { | |||
sql := "SELECT A.repo_id,name,owner_name,is_private,radar_total,num_watches,num_visits,num_downloads,num_pulls,num_commits,num_stars,num_forks,num_issues,num_closed_issues,num_contributor FROM " + | |||
sql := "SELECT A.repo_id,name,alias,owner_name,is_private,radar_total,num_watches,num_visits,num_downloads,num_pulls,num_commits,num_stars,num_forks,num_issues,num_closed_issues,num_contributor FROM " + | |||
"(SELECT repo_id,sum(num_watches_added) as num_watches,sum(num_visits) as num_visits, sum(num_downloads_added) as num_downloads,sum(num_pulls_added) as num_pulls,sum(num_commits_added) as num_commits,sum(num_stars_added) as num_stars,sum(num_forks_added) num_forks,sum(num_issues_added) as num_issues,sum(num_closed_issues_added) as num_closed_issues,sum(num_contributor_added) as num_contributor " + | |||
" FROM repo_statistic where created_unix >=" + strconv.FormatInt(beginTime.Unix(), 10) + | |||
" and created_unix<" + strconv.FormatInt(endTime.Unix(), 10) + " group by repo_id) A," + | |||
"(SELECT repo_id,name,owner_name,is_private,radar_total from public.repo_statistic where date='" + latestDate + "') B" + | |||
"(SELECT repo_id,name,alias,owner_name,is_private,radar_total from public.repo_statistic where date='" + latestDate + "') B" + | |||
" where A.repo_id=B.repo_id" | |||
if q != "" { | |||
sql = sql + " and LOWER(B.name) like '%" + strings.ToLower(q) + "%'" | |||
sql = sql + " and LOWER(B.alias) like '%" + strings.ToLower(q) + "%'" | |||
} | |||
sql = sql + " order by " + orderBy + " desc,A.repo_id" + " limit " + strconv.Itoa(pageSize) + " offset " + strconv.Itoa((page-1)*pageSize) | |||
return sql | |||
@@ -0,0 +1,126 @@ | |||
package authentication | |||
import ( | |||
"code.gitea.io/gitea/modules/auth/wechat" | |||
"code.gitea.io/gitea/modules/base" | |||
"code.gitea.io/gitea/modules/context" | |||
"code.gitea.io/gitea/modules/log" | |||
"code.gitea.io/gitea/modules/redis/redis_client" | |||
"code.gitea.io/gitea/modules/redis/redis_key" | |||
"code.gitea.io/gitea/modules/setting" | |||
"encoding/json" | |||
"errors" | |||
gouuid "github.com/satori/go.uuid" | |||
"time" | |||
) | |||
const tplBindPage base.TplName = "repo/wx_autorize" | |||
type QRCodeResponse struct { | |||
Url string | |||
Ticket string | |||
SceneStr string | |||
ExpireSeconds int | |||
} | |||
// GetQRCode4Bind get QR code for wechat binding | |||
func GetQRCode4Bind(ctx *context.Context) { | |||
userId := ctx.User.ID | |||
r, err := createQRCode4Bind(userId) | |||
if err != nil { | |||
ctx.JSON(200, map[string]interface{}{ | |||
"code": "9999", | |||
"msg": "Get QR code failed", | |||
}) | |||
return | |||
} | |||
ctx.JSON(200, map[string]interface{}{ | |||
"code": "00", | |||
"msg": "success", | |||
"data": r, | |||
}) | |||
} | |||
// GetBindStatus the web page will poll the service to get bind status | |||
func GetBindStatus(ctx *context.Context) { | |||
sceneStr := ctx.Query("sceneStr") | |||
val, _ := redis_client.Get(redis_key.WechatBindingUserIdKey(sceneStr)) | |||
if val == "" { | |||
ctx.JSON(200, map[string]interface{}{ | |||
"code": "00", | |||
"msg": "QR code expired", | |||
"data": map[string]interface{}{ | |||
"status": wechat.BIND_STATUS_EXPIRED, | |||
}, | |||
}) | |||
return | |||
} | |||
qrCache := new(wechat.QRCode4BindCache) | |||
json.Unmarshal([]byte(val), qrCache) | |||
ctx.JSON(200, map[string]interface{}{ | |||
"code": "00", | |||
"msg": "success", | |||
"data": map[string]interface{}{ | |||
"status": qrCache.Status, | |||
}, | |||
}) | |||
} | |||
// UnbindWechat | |||
func UnbindWechat(ctx *context.Context) { | |||
if ctx.User.WechatOpenId != "" { | |||
wechat.UnbindWechat(ctx.User.ID, ctx.User.WechatOpenId) | |||
} | |||
ctx.JSON(200, map[string]interface{}{ | |||
"code": "00", | |||
"msg": "success", | |||
}) | |||
} | |||
// GetBindPage | |||
func GetBindPage(ctx *context.Context) { | |||
userId := ctx.User.ID | |||
r, _ := createQRCode4Bind(userId) | |||
if r != nil { | |||
ctx.Data["qrcode"] = r | |||
} | |||
redirectUrl := ctx.Query("redirect_to") | |||
if redirectUrl != "" { | |||
ctx.SetCookie("redirect_to", setting.AppSubURL+redirectUrl, 0, setting.AppSubURL) | |||
} | |||
ctx.HTML(200, tplBindPage) | |||
} | |||
func createQRCode4Bind(userId int64) (*QRCodeResponse, error) { | |||
log.Info("start to create qr-code for binding.userId=%d", userId) | |||
sceneStr := gouuid.NewV4().String() | |||
r := wechat.GetWechatQRCode4Bind(sceneStr) | |||
if r == nil { | |||
return nil, errors.New("createQRCode4Bind failed") | |||
} | |||
jsonStr, _ := json.Marshal(&wechat.QRCode4BindCache{ | |||
UserId: userId, | |||
Status: wechat.BIND_STATUS_UNBIND, | |||
}) | |||
isOk, err := redis_client.Setex(redis_key.WechatBindingUserIdKey(sceneStr), string(jsonStr), time.Duration(setting.WechatQRCodeExpireSeconds)*time.Second) | |||
if err != nil { | |||
log.Error("createQRCode4Bind failed.e=%+v", err) | |||
return nil, err | |||
} | |||
if !isOk { | |||
log.Error("createQRCode4Bind failed.redis reply is not ok") | |||
return nil, errors.New("reply is not ok when set WechatBindingUserIdKey") | |||
} | |||
result := &QRCodeResponse{ | |||
Url: r.Url, | |||
Ticket: r.Ticket, | |||
SceneStr: sceneStr, | |||
ExpireSeconds: setting.WechatQRCodeExpireSeconds, | |||
} | |||
return result, nil | |||
} |
@@ -0,0 +1,47 @@ | |||
package authentication | |||
import ( | |||
"code.gitea.io/gitea/modules/auth/wechat" | |||
"code.gitea.io/gitea/modules/context" | |||
"code.gitea.io/gitea/modules/log" | |||
"encoding/xml" | |||
"io/ioutil" | |||
"time" | |||
) | |||
// AcceptWechatEvent | |||
// https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_event_pushes.html | |||
// 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{} | |||
xml.Unmarshal(b, &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 | |||
} | |||
if replyStr == "" { | |||
log.Info("reply str is empty") | |||
return | |||
} | |||
reply := &wechat.EventReply{ | |||
ToUserName: we.FromUserName, | |||
FromUserName: we.ToUserName, | |||
CreateTime: time.Now().Unix(), | |||
MsgType: wechat.WECHAT_MSG_TYPE_TEXT, | |||
Content: replyStr, | |||
} | |||
ctx.XML(200, reply) | |||
} | |||
// ValidEventSource | |||
func ValidEventSource(ctx *context.Context) { | |||
echostr := ctx.Query("echostr") | |||
ctx.Write([]byte(echostr)) | |||
return | |||
} |
@@ -38,6 +38,7 @@ const ( | |||
tplExploreCode base.TplName = "explore/code" | |||
tplExploreImages base.TplName = "explore/images" | |||
tplExploreExploreDataAnalysis base.TplName = "explore/data_analysis" | |||
tplHomeTerm base.TplName = "terms" | |||
) | |||
// Home render home page | |||
@@ -596,3 +597,7 @@ func RecommendRepoFromPromote(ctx *context.Context) { | |||
ctx.JSON(200, result) | |||
} | |||
} | |||
func HomeTerm(ctx *context.Context) { | |||
ctx.HTML(200, tplHomeTerm) | |||
} |
@@ -12,6 +12,7 @@ import ( | |||
"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/setting" | |||
"code.gitea.io/gitea/modules/storage" | |||
uuid "github.com/satori/go.uuid" | |||
@@ -113,7 +114,7 @@ func saveModelByParameters(jobId string, versionName string, name string, versio | |||
models.UpdateRepositoryUnits(ctx.Repo.Repository, units, deleteUnitTypes) | |||
log.Info("save model end.") | |||
notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, id, name, models.ActionCreateNewModelTask) | |||
return nil | |||
} | |||
@@ -20,11 +20,11 @@ import ( | |||
"code.gitea.io/gitea/modules/labelmsg" | |||
"code.gitea.io/gitea/modules/log" | |||
"code.gitea.io/gitea/modules/minio_ext" | |||
"code.gitea.io/gitea/modules/notification" | |||
"code.gitea.io/gitea/modules/setting" | |||
"code.gitea.io/gitea/modules/storage" | |||
"code.gitea.io/gitea/modules/upload" | |||
"code.gitea.io/gitea/modules/worker" | |||
gouuid "github.com/satori/go.uuid" | |||
) | |||
@@ -845,6 +845,9 @@ func CompleteMultipart(ctx *context.Context) { | |||
ctx.Error(500, fmt.Sprintf("InsertAttachment: %v", err)) | |||
return | |||
} | |||
dataset, _ := models.GetDatasetByID(attachment.DatasetID) | |||
repository, _ := models.GetRepositoryByID(dataset.RepoID) | |||
notification.NotifyOtherTask(ctx.User, repository, fmt.Sprint(attachment.Type), attachment.Name, models.ActionUploadAttachment) | |||
if attachment.DatasetID != 0 { | |||
if isCanDecompress(attachment.Name) { | |||
@@ -865,7 +868,6 @@ func CompleteMultipart(ctx *context.Context) { | |||
labelmsg.SendDecompressAttachToLabelOBS(string(attachjson)) | |||
} | |||
} else { | |||
dataset, _ := models.GetDatasetByID(attachment.DatasetID) | |||
var labelMap map[string]string | |||
labelMap = make(map[string]string) | |||
labelMap["UUID"] = uuid | |||
@@ -350,18 +350,16 @@ func CloudBrainShow(ctx *context.Context) { | |||
func cloudBrainShow(ctx *context.Context, tpName base.TplName) { | |||
ctx.Data["PageIsCloudBrain"] = true | |||
var jobID = ctx.Params(":jobid") | |||
task, err := models.GetCloudbrainByJobID(jobID) | |||
var jobName = ctx.Params(":jobname") | |||
debugListType := ctx.Query("debugListType") | |||
task, err := models.GetCloudbrainByName(jobName) | |||
if err != nil { | |||
ctx.Data["error"] = err.Error() | |||
} | |||
result, err := cloudbrain.GetJob(jobID) | |||
result, err := cloudbrain.GetJob(task.JobID) | |||
if err != nil { | |||
ctx.Data["error"] = err.Error() | |||
} | |||
if result != nil { | |||
jobRes, _ := models.ConvertToJobResultPayload(result.Payload) | |||
jobRes.Resource.Memory = strings.ReplaceAll(jobRes.Resource.Memory, "Mi", "MB") | |||
@@ -369,6 +367,7 @@ func cloudBrainShow(ctx *context.Context, tpName base.TplName) { | |||
ctx.Data["resource_spec"] = spec | |||
taskRoles := jobRes.TaskRoles | |||
if jobRes.JobStatus.State != string(models.JobFailed) { | |||
taskRes, _ := models.ConvertToTaskPod(taskRoles[cloudbrain.SubTaskName].(map[string]interface{})) | |||
ctx.Data["taskRes"] = taskRes | |||
task.Status = taskRes.TaskStatuses[0].State | |||
@@ -422,11 +421,12 @@ func cloudBrainShow(ctx *context.Context, tpName base.TplName) { | |||
ctx.Data["duration"] = util.AddZero(duration/3600000) + ":" + util.AddZero(duration%3600000/60000) + ":" + util.AddZero(duration%60000/1000) | |||
ctx.Data["task"] = task | |||
ctx.Data["jobID"] = jobID | |||
// ctx.Data["jobID"] = task.JobID | |||
ctx.Data["jobName"] = task.JobName | |||
version_list_task := make([]*models.Cloudbrain, 0) | |||
version_list_task = append(version_list_task, task) | |||
ctx.Data["version_list_task"] = version_list_task | |||
ctx.Data["debugListType"] = debugListType | |||
ctx.HTML(200, tpName) | |||
} | |||
@@ -582,12 +582,19 @@ func logErrorAndUpdateJobStatus(err error, taskInfo *models.Cloudbrain) { | |||
} | |||
func CloudBrainDel(ctx *context.Context) { | |||
var listType = ctx.Query("debugListType") | |||
if err := deleteCloudbrainJob(ctx); err != nil { | |||
log.Error("deleteCloudbrainJob failed: %v", err, ctx.Data["msgID"]) | |||
ctx.ServerError(err.Error(), err) | |||
return | |||
} | |||
ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/debugjob?debugListType=all") | |||
var isAdminPage = ctx.Query("isadminpage") | |||
if ctx.IsUserSiteAdmin() && isAdminPage == "true" { | |||
ctx.Redirect(setting.AppSubURL + "/admin" + "/cloudbrains") | |||
} else { | |||
ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/debugjob?debugListType=" + listType) | |||
} | |||
} | |||
func deleteCloudbrainJob(ctx *context.Context) error { | |||
@@ -1098,6 +1105,9 @@ func GetChildTypes(ctx *context.Context) { | |||
} | |||
func CloudBrainBenchmarkNew(ctx *context.Context) { | |||
ctx.Data["description"] = "" | |||
ctx.Data["benchmarkTypeID"] = -1 | |||
ctx.Data["benchmark_child_types_id_hidden"] = -1 | |||
err := cloudBrainNewDataPrepare(ctx) | |||
if err != nil { | |||
ctx.ServerError("get new cloudbrain info failed", err) | |||
@@ -1200,6 +1210,9 @@ func CloudBrainBenchmarkCreate(ctx *context.Context, form auth.CreateCloudBrainF | |||
benchmarkTypeID := form.BenchmarkTypeID | |||
benchmarkChildTypeID := form.BenchmarkChildTypeID | |||
ctx.Data["description"] = form.Description | |||
ctx.Data["benchmarkTypeID"] = benchmarkTypeID | |||
ctx.Data["benchmark_child_types_id_hidden"] = benchmarkChildTypeID | |||
if !jobNamePattern.MatchString(jobName) { | |||
cloudBrainNewDataPrepare(ctx) | |||
ctx.RenderWithErr(ctx.Tr("repo.cloudbrain_jobname_err"), tplCloudBrainBenchmarkNew, &form) | |||
@@ -1343,5 +1356,11 @@ func BenchmarkDel(ctx *context.Context) { | |||
ctx.ServerError(err.Error(), err) | |||
return | |||
} | |||
ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/cloudbrain/benchmark") | |||
var isAdminPage = ctx.Query("isadminpage") | |||
if ctx.IsUserSiteAdmin() && isAdminPage == "true" { | |||
ctx.Redirect(setting.AppSubURL + "/admin" + "/cloudbrains") | |||
} else { | |||
ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/cloudbrain/benchmark") | |||
} | |||
} |
@@ -28,7 +28,8 @@ func ServeData(ctx *context.Context, name string, reader io.Reader) error { | |||
buf = buf[:n] | |||
} | |||
ctx.Resp.Header().Set("Cache-Control", "public,max-age=86400") | |||
//ctx.Resp.Header().Set("Cache-Control", "public,max-age=86400") | |||
ctx.Resp.Header().Set("Cache-Control", "max-age=0") | |||
name = path.Base(name) | |||
// Google Chrome dislike commas in filenames, so let's change it to a space | |||
@@ -91,6 +91,7 @@ func DebugJobIndex(ctx *context.Context) { | |||
ctx.Data["Tasks"] = ciTasks | |||
ctx.Data["CanCreate"] = cloudbrain.CanCreateOrDebugJob(ctx) | |||
ctx.Data["RepoIsEmpty"] = repo.IsEmpty | |||
ctx.Data["debugListType"] = debugListType | |||
ctx.HTML(200, tplDebugJobIndex) | |||
} | |||
@@ -103,8 +104,13 @@ func MustEnableModelArts(ctx *context.Context) { | |||
} | |||
func NotebookNew(ctx *context.Context) { | |||
ctx.Data["PageIsCloudBrain"] = true | |||
notebookNewDataPrepare(ctx) | |||
ctx.HTML(200, tplModelArtsNotebookNew) | |||
} | |||
func notebookNewDataPrepare(ctx *context.Context) error { | |||
ctx.Data["PageIsCloudBrain"] = true | |||
t := time.Now() | |||
var jobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:] | |||
ctx.Data["job_name"] = jobName | |||
@@ -112,26 +118,14 @@ func NotebookNew(ctx *context.Context) { | |||
attachs, err := models.GetModelArtsUserAttachments(ctx.User.ID) | |||
if err != nil { | |||
ctx.ServerError("GetAllUserAttachments failed:", err) | |||
return | |||
return err | |||
} | |||
ctx.Data["attachments"] = attachs | |||
ctx.Data["dataset_path"] = modelarts.DataSetMountPath | |||
ctx.Data["env"] = modelarts.NotebookEnv | |||
ctx.Data["notebook_type"] = modelarts.NotebookType | |||
if modelarts.FlavorInfos == nil { | |||
json.Unmarshal([]byte(setting.FlavorInfos), &modelarts.FlavorInfos) | |||
} | |||
ctx.Data["flavors"] = modelarts.FlavorInfos.FlavorInfo | |||
ctx.HTML(200, tplModelArtsNotebookNew) | |||
} | |||
func notebookNewDataPrepare(ctx *context.Context) error { | |||
ctx.Data["PageIsCloudBrain"] = true | |||
t := time.Now() | |||
var jobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:] | |||
ctx.Data["job_name"] = jobName | |||
if modelarts.ImageInfos == nil { | |||
json.Unmarshal([]byte(setting.ImageInfos), &modelarts.ImageInfos) | |||
} | |||
ctx.Data["images"] = modelarts.ImageInfos.ImageInfo | |||
if modelarts.FlavorInfos == nil { | |||
json.Unmarshal([]byte(setting.FlavorInfos), &modelarts.FlavorInfos) | |||
@@ -191,8 +185,7 @@ func Notebook2Create(ctx *context.Context, form auth.CreateModelArtsNotebookForm | |||
uuid := form.Attachment | |||
description := form.Description | |||
flavor := form.Flavor | |||
flavor = "modelarts.bm.910.arm.public.1" | |||
imageId := form.ImageId | |||
count, err := models.GetCloudbrainNotebookCountByUserID(ctx.User.ID) | |||
if err != nil { | |||
@@ -223,7 +216,7 @@ func Notebook2Create(ctx *context.Context, form auth.CreateModelArtsNotebookForm | |||
} | |||
} | |||
err = modelarts.GenerateNotebook2(ctx, jobName, uuid, description, flavor) | |||
err = modelarts.GenerateNotebook2(ctx, jobName, uuid, description, flavor, imageId) | |||
if err != nil { | |||
log.Error("GenerateNotebook2 failed, %v", err, ctx.Data["MsgID"]) | |||
notebookNewDataPrepare(ctx) | |||
@@ -235,6 +228,7 @@ func Notebook2Create(ctx *context.Context, form auth.CreateModelArtsNotebookForm | |||
func NotebookShow(ctx *context.Context) { | |||
ctx.Data["PageIsCloudBrain"] = true | |||
debugListType := ctx.Query("debugListType") | |||
var jobID = ctx.Params(":jobid") | |||
task, err := models.GetCloudbrainByJobID(jobID) | |||
@@ -262,13 +256,24 @@ func NotebookShow(ctx *context.Context) { | |||
result.CreateTime = time.Unix(int64(result.CreateAt/1000), 0).Format("2006-01-02 15:04:05") | |||
result.LatestUpdateTime = time.Unix(int64(result.UpdateAt/1000), 0).Format("2006-01-02 15:04:05") | |||
//result.QueuingInfo.BeginTime = time.Unix(int64(result.QueuingInfo.BeginTimestamp/1000), 0).Format("2006-01-02 15:04:05") | |||
//result.QueuingInfo.EndTime = time.Unix(int64(result.QueuingInfo.EndTimestamp/1000), 0).Format("2006-01-02 15:04:05") | |||
} | |||
datasetDownloadLink := "-" | |||
if ctx.IsSigned { | |||
if task.Uuid != "" && task.UserID == ctx.User.ID { | |||
attachment, err := models.GetAttachmentByUUID(task.Uuid) | |||
if err == nil { | |||
datasetDownloadLink = attachment.S3DownloadURL() | |||
} | |||
} | |||
} | |||
ctx.Data["datasetDownloadLink"] = datasetDownloadLink | |||
ctx.Data["task"] = task | |||
ctx.Data["jobID"] = jobID | |||
ctx.Data["jobName"] = task.JobName | |||
ctx.Data["result"] = result | |||
ctx.Data["debugListType"] = debugListType | |||
ctx.HTML(200, tplModelArtsNotebookShow) | |||
} | |||
@@ -307,7 +312,7 @@ func NotebookDebug2(ctx *context.Context) { | |||
ctx.RenderWithErr(err.Error(), tplModelArtsNotebookIndex, nil) | |||
return | |||
} | |||
ctx.Redirect(result.Url) | |||
} | |||
@@ -342,6 +347,10 @@ func NotebookManage(ctx *context.Context) { | |||
break | |||
} | |||
} else if action == models.ActionRestart { | |||
ctx.CheckWechatBind() | |||
if ctx.Written() { | |||
return | |||
} | |||
if task.Status != string(models.ModelArtsStopped) && task.Status != string(models.ModelArtsStartFailed) && task.Status != string(models.ModelArtsCreateFailed) { | |||
log.Error("the job(%s) is not stopped", task.JobName, ctx.Data["MsgID"]) | |||
resultCode = "-1" | |||
@@ -387,7 +396,7 @@ func NotebookManage(ctx *context.Context) { | |||
log.Error("ManageNotebook2(%s) failed:%v", task.JobName, err.Error(), ctx.Data["MsgID"]) | |||
resultCode = "-1" | |||
errorMsg = err.Error() | |||
if strings.Contains(err.Error(), "ModelArts.6404") { | |||
if strings.Contains(err.Error(), modelarts.NotebookNotFound) { | |||
errorMsg = "the job's version is too old and can not be restarted" | |||
} | |||
break | |||
@@ -417,6 +426,7 @@ func NotebookManage(ctx *context.Context) { | |||
func NotebookDel(ctx *context.Context) { | |||
var jobID = ctx.Params(":jobid") | |||
var listType = ctx.Query("debugListType") | |||
task := ctx.Cloudbrain | |||
if task.Status != string(models.ModelArtsCreateFailed) && task.Status != string(models.ModelArtsStartFailed) && task.Status != string(models.ModelArtsStopped) { | |||
@@ -428,8 +438,12 @@ func NotebookDel(ctx *context.Context) { | |||
_, err := modelarts.DelNotebook2(jobID) | |||
if err != nil { | |||
log.Error("DelNotebook2(%s) failed:%v", task.JobName, err.Error()) | |||
ctx.ServerError("DelNotebook2 failed", err) | |||
return | |||
if strings.Contains(err.Error(), modelarts.NotebookNotFound) || strings.Contains(err.Error(), modelarts.NotebookNoPermission) { | |||
log.Info("old notebook version") | |||
} else { | |||
ctx.ServerError("DelNotebook2 failed", err) | |||
return | |||
} | |||
} | |||
err = models.DeleteJob(task) | |||
@@ -438,7 +452,12 @@ func NotebookDel(ctx *context.Context) { | |||
return | |||
} | |||
ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/debugjob?debugListType=all") | |||
var isAdminPage = ctx.Query("isadminpage") | |||
if ctx.IsUserSiteAdmin() && isAdminPage == "true" { | |||
ctx.Redirect(setting.AppSubURL + "/admin" + "/cloudbrains") | |||
} else { | |||
ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/debugjob?debugListType=" + listType) | |||
} | |||
} | |||
func TrainJobIndex(ctx *context.Context) { | |||
@@ -1447,6 +1466,7 @@ func TrainJobShow(ctx *context.Context) { | |||
ctx.Data["jobName"] = VersionListTasks[0].JobName | |||
ctx.Data["version_list_task"] = VersionListTasks | |||
ctx.Data["version_list_count"] = VersionListCount | |||
ctx.Data["canDownload"] = cloudbrain.CanDeleteJob(ctx, &VersionListTasks[0].Cloudbrain) | |||
ctx.HTML(http.StatusOK, tplModelArtsTrainJobShow) | |||
} | |||
@@ -1543,7 +1563,12 @@ func TrainJobDel(ctx *context.Context) { | |||
DeleteJobStorage(VersionListTasks[0].JobName) | |||
} | |||
ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/train-job") | |||
var isAdminPage = ctx.Query("isadminpage") | |||
if ctx.IsUserSiteAdmin() && isAdminPage == "true" { | |||
ctx.Redirect(setting.AppSubURL + "/admin" + "/cloudbrains") | |||
} else { | |||
ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/train-job") | |||
} | |||
} | |||
func TrainJobStop(ctx *context.Context) { | |||
@@ -2046,6 +2071,7 @@ func InferenceJobShow(ctx *context.Context) { | |||
ctx.Data["jobID"] = jobID | |||
ctx.Data["jobName"] = task.JobName | |||
ctx.Data["task"] = task | |||
ctx.Data["canDownload"] = cloudbrain.CanDeleteJob(ctx, task) | |||
tempUids := []int64{} | |||
tempUids = append(tempUids, task.UserID) | |||
@@ -107,6 +107,7 @@ func RepoStatisticDaily(date string) { | |||
RepoID: repo.ID, | |||
Date: date, | |||
Name: repo.Name, | |||
Alias: repo.Alias, | |||
IsPrivate: repo.IsPrivate, | |||
IsMirror: repo.IsMirror, | |||
OwnerName: repo.OwnerName, | |||
@@ -282,7 +283,7 @@ func RepoStatisticDaily(date string) { | |||
} | |||
func getDistinctProjectName(repo *models.Repository) string { | |||
return repo.OwnerName + "/" + repo.Name | |||
return repo.OwnerName + "/" + repo.Alias | |||
} | |||
func getDatasetSize(repo *models.Repository) (int64, error) { | |||
@@ -63,11 +63,13 @@ func queryUserDataPage(ctx *context.Context, tableName string, queryObj interfac | |||
_, count := models.QueryUserStaticDataByTableName(1, 1, tableName, queryObj, userName) | |||
var indexTotal int64 | |||
indexTotal = 0 | |||
row := 1 | |||
for { | |||
re, _ := models.QueryUserStaticDataByTableName(int(indexTotal), PAGE_SIZE, tableName, queryObj, "") | |||
log.Info("return count=" + fmt.Sprint(count)) | |||
for i, userRecord := range re { | |||
rows := fmt.Sprint(i + 2) | |||
for _, userRecord := range re { | |||
row++ | |||
rows := fmt.Sprint(row) | |||
xlsx.SetCellValue(sheetName, "A"+rows, userRecord.ID) | |||
xlsx.SetCellValue(sheetName, "B"+rows, userRecord.Name) | |||
xlsx.SetCellValue(sheetName, "C"+rows, userRecord.CodeMergeCount) | |||
@@ -6,6 +6,7 @@ package routes | |||
import ( | |||
"bytes" | |||
"code.gitea.io/gitea/routers/authentication" | |||
"encoding/gob" | |||
"net/http" | |||
"path" | |||
@@ -274,6 +275,8 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
ignSignInAndCsrf := context.Toggle(&context.ToggleOptions{DisableCSRF: true}) | |||
reqSignOut := context.Toggle(&context.ToggleOptions{SignOutRequired: true}) | |||
reqBasicAuth := context.Toggle(&context.ToggleOptions{BasicAuthRequired: true, DisableCSRF: true}) | |||
reqWechatBind := context.Toggle(&context.ToggleOptions{WechatAuthRequired: true}) | |||
reqWechatBindForApi := context.Toggle(&context.ToggleOptions{WechatAuthRequiredForAPI: true}) | |||
bindIgnErr := binding.BindIgnErr | |||
validation.AddBindingRules() | |||
@@ -319,6 +322,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
m.Get("/action/notification", routers.ActionNotification) | |||
m.Get("/recommend/org", routers.RecommendOrgFromPromote) | |||
m.Get("/recommend/repo", routers.RecommendRepoFromPromote) | |||
m.Get("/home/term", routers.HomeTerm) | |||
m.Group("/explore", func() { | |||
m.Get("", func(ctx *context.Context) { | |||
ctx.Redirect(setting.AppSubURL + "/explore/repos") | |||
@@ -391,6 +395,13 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
}, ignSignInAndCsrf, reqSignIn) | |||
m.Post("/login/oauth/access_token", bindIgnErr(auth.AccessTokenForm{}), ignSignInAndCsrf, user.AccessTokenOAuth) | |||
m.Group("/authentication/wechat", func() { | |||
m.Get("/qrCode4Bind", authentication.GetQRCode4Bind) | |||
m.Get("/bindStatus", authentication.GetBindStatus) | |||
m.Post("/unbind", authentication.UnbindWechat) | |||
m.Get("/bind", authentication.GetBindPage) | |||
}, reqSignIn) | |||
m.Group("/user/settings", func() { | |||
m.Get("", userSetting.Profile) | |||
m.Post("", bindIgnErr(auth.UpdateProfileForm{}), userSetting.ProfilePost) | |||
@@ -507,6 +518,10 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
m.Get("", admin.Datasets) | |||
// m.Post("/delete", admin.DeleteDataset) | |||
}) | |||
m.Group("/cloudbrains", func() { | |||
m.Get("", admin.CloudBrains) | |||
m.Get("/download", admin.DownloadCloudBrains) | |||
}) | |||
m.Group("/^:configType(hooks|system-hooks)$", func() { | |||
m.Get("", admin.DefaultOrSystemWebhooks) | |||
@@ -974,30 +989,34 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
}, context.RepoRef()) | |||
m.Group("/cloudbrain", func() { | |||
m.Group("/:jobid", func() { | |||
m.Group("/:jobname", func() { | |||
m.Get("", reqRepoCloudBrainReader, repo.CloudBrainShow) | |||
}) | |||
m.Group("/:jobid", func() { | |||
m.Get("/debug", cloudbrain.AdminOrJobCreaterRight, repo.CloudBrainDebug) | |||
m.Post("/commit_image", cloudbrain.AdminOrJobCreaterRight, bindIgnErr(auth.CommitImageCloudBrainForm{}), repo.CloudBrainCommitImage) | |||
m.Post("/stop", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.CloudBrainStop) | |||
m.Post("/del", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.CloudBrainDel) | |||
m.Post("/restart", cloudbrain.AdminOrJobCreaterRight, repo.CloudBrainRestart) | |||
m.Post("/restart", reqWechatBindForApi, cloudbrain.AdminOrJobCreaterRight, repo.CloudBrainRestart) | |||
m.Get("/rate", reqRepoCloudBrainReader, repo.GetRate) | |||
m.Get("/models", reqRepoCloudBrainReader, repo.CloudBrainShowModels) | |||
m.Get("/download_model", cloudbrain.AdminOrJobCreaterRight, repo.CloudBrainDownloadModel) | |||
}) | |||
m.Get("/create", reqRepoCloudBrainWriter, repo.CloudBrainNew) | |||
m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateCloudBrainForm{}), repo.CloudBrainCreate) | |||
m.Get("/create", reqWechatBind, reqRepoCloudBrainWriter, repo.CloudBrainNew) | |||
m.Post("/create", reqWechatBind, reqRepoCloudBrainWriter, bindIgnErr(auth.CreateCloudBrainForm{}), repo.CloudBrainCreate) | |||
m.Group("/benchmark", func() { | |||
m.Get("", reqRepoCloudBrainReader, repo.CloudBrainBenchmarkIndex) | |||
m.Group("/:jobid", func() { | |||
m.Group("/:jobname", func() { | |||
m.Get("", reqRepoCloudBrainReader, repo.CloudBrainBenchMarkShow) | |||
}) | |||
m.Group("/:jobid", func() { | |||
m.Post("/stop", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.CloudBrainStop) | |||
m.Post("/del", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.BenchmarkDel) | |||
m.Get("/rate", reqRepoCloudBrainReader, repo.GetRate) | |||
}) | |||
m.Get("/create", reqRepoCloudBrainWriter, repo.CloudBrainBenchmarkNew) | |||
m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateCloudBrainForm{}), repo.CloudBrainBenchmarkCreate) | |||
m.Get("/create", reqWechatBind, reqRepoCloudBrainWriter, repo.CloudBrainBenchmarkNew) | |||
m.Post("/create", reqWechatBind, reqRepoCloudBrainWriter, bindIgnErr(auth.CreateCloudBrainForm{}), repo.CloudBrainBenchmarkCreate) | |||
m.Get("/get_child_types", repo.GetChildTypes) | |||
}) | |||
}, context.RepoRef()) | |||
@@ -1045,8 +1064,8 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
m.Post("/:action", reqRepoCloudBrainWriter, repo.NotebookManage) | |||
m.Post("/del", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.NotebookDel) | |||
}) | |||
m.Get("/create", reqRepoCloudBrainWriter, repo.NotebookNew) | |||
m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateModelArtsNotebookForm{}), repo.Notebook2Create) | |||
m.Get("/create", reqWechatBind, reqRepoCloudBrainWriter, repo.NotebookNew) | |||
m.Post("/create", reqWechatBind, reqRepoCloudBrainWriter, bindIgnErr(auth.CreateModelArtsNotebookForm{}), repo.Notebook2Create) | |||
}) | |||
m.Group("/train-job", func() { | |||
@@ -1056,11 +1075,11 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
m.Post("/stop", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.TrainJobStop) | |||
m.Post("/del", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.TrainJobDel) | |||
m.Get("/model_download", cloudbrain.AdminOrJobCreaterRight, repo.ModelDownload) | |||
m.Get("/create_version", cloudbrain.AdminOrJobCreaterRight, repo.TrainJobNewVersion) | |||
m.Post("/create_version", cloudbrain.AdminOrJobCreaterRight, bindIgnErr(auth.CreateModelArtsTrainJobForm{}), repo.TrainJobCreateVersion) | |||
m.Get("/create_version", reqWechatBind, cloudbrain.AdminOrJobCreaterRight, repo.TrainJobNewVersion) | |||
m.Post("/create_version", reqWechatBind, cloudbrain.AdminOrJobCreaterRight, bindIgnErr(auth.CreateModelArtsTrainJobForm{}), repo.TrainJobCreateVersion) | |||
}) | |||
m.Get("/create", reqRepoCloudBrainWriter, repo.TrainJobNew) | |||
m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateModelArtsTrainJobForm{}), repo.TrainJobCreate) | |||
m.Get("/create", reqWechatBind, reqRepoCloudBrainWriter, repo.TrainJobNew) | |||
m.Post("/create", reqWechatBind, reqRepoCloudBrainWriter, bindIgnErr(auth.CreateModelArtsTrainJobForm{}), repo.TrainJobCreate) | |||
m.Get("/para-config-list", reqRepoCloudBrainReader, repo.TrainJobGetConfigList) | |||
}) | |||
@@ -1072,8 +1091,8 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
m.Get("/result_download", cloudbrain.AdminOrJobCreaterRight, repo.ResultDownload) | |||
m.Get("/downloadall", repo.DownloadMultiResultFile) | |||
}) | |||
m.Get("/create", reqRepoCloudBrainWriter, repo.InferenceJobNew) | |||
m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateModelArtsInferenceJobForm{}), repo.InferenceJobCreate) | |||
m.Get("/create", reqWechatBind, reqRepoCloudBrainWriter, repo.InferenceJobNew) | |||
m.Post("/create", reqWechatBind, reqRepoCloudBrainWriter, bindIgnErr(auth.CreateModelArtsInferenceJobForm{}), repo.InferenceJobCreate) | |||
}) | |||
}, context.RepoRef()) | |||
@@ -132,7 +132,7 @@ func getNotifications(c *context.Context) { | |||
} | |||
c.Data["Title"] = c.Tr("notifications") | |||
c.Data["Keyword"] = keyword | |||
//c.Data["Keyword"] = keyword | |||
c.Data["Status"] = status | |||
c.Data["Notifications"] = notifications | |||
@@ -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} | |||
var opTypes = []int{1, 2, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 22, 23, 25, 26, 27, 28, 29, 30} | |||
type ClientsManager struct { | |||
Clients *orderedmap.OrderedMap | |||
@@ -0,0 +1,316 @@ | |||
{{template "base/head" .}} | |||
<!-- 弹窗 --> | |||
<div id="mask"> | |||
<div id="loadingPage"> | |||
<div class="rect1"></div> | |||
<div class="rect2"></div> | |||
<div class="rect3"></div> | |||
<div class="rect4"></div> | |||
<div class="rect5"></div> | |||
</div> | |||
</div> | |||
<!-- 提示框 --> | |||
<div class="alert"></div> | |||
<div class="admin user"> | |||
{{template "admin/navbar" .}} | |||
<div class="ui container" style="width: 80%;"> | |||
{{template "base/alert" .}} | |||
<div class="ui grid" > | |||
<div class="row" style="border: 1px solid #d4d4d5;margin-top: 15px;padding-top: 0;"> | |||
{{template "admin/cloudbrain/search" .}} | |||
<div class="ui ten wide column right aligned" style="margin: 1rem 0;"> | |||
<a class="ui compact blue basic icon button" style="box-shadow: none !important; padding: 0.8em;" href="/admin/cloudbrains/download"><i class="ri-download-line middle aligned icon"></i>{{.i18n.Tr "admin.cloudbrain.download_report"}}</a> | |||
</div> | |||
<div class="ui sixteen wide column"> | |||
<!-- 任务展示 --> | |||
<div class="dataset list"> | |||
<!-- 表头 --> | |||
<div class="ui grid stackable" style="background: #f0f0f0;;"> | |||
<div class="row"> | |||
<div class="two wide column nowrap"> | |||
<span style="margin:0 6px">{{$.i18n.Tr "repo.cloudbrain_task"}}</span> | |||
</div> | |||
<div class="one wide column text center nowrap"> | |||
<span style="margin:0 6px">{{$.i18n.Tr "repo.cloudbrain_task_type"}}</span> | |||
</div> | |||
<div class="two wide column text center nowrap" style="width: 10% !important;"> | |||
<span>{{$.i18n.Tr "repo.modelarts.status"}}</span> | |||
</div> | |||
<div class="two wide column text center nowrap" style="width: 10% !important;"> | |||
<span>{{$.i18n.Tr "repo.modelarts.createtime"}}</span> | |||
</div> | |||
<div class="one wide column text center nowrap"> | |||
<span>{{$.i18n.Tr "repo.cloudbrain_status_runtime"}}</span> | |||
</div> | |||
<div class="one wide column text center nowrap"> | |||
<span>{{$.i18n.Tr "repo.modelarts.computing_resources"}}</span> | |||
</div> | |||
<div class="one wide column text center nowrap"> | |||
<span>{{$.i18n.Tr "repo.cloudbrain_creator"}}</span> | |||
</div> | |||
<div class="two wide column text center nowrap"> | |||
<span>{{$.i18n.Tr "repository"}}</span> | |||
</div> | |||
<div class="two wide column text center nowrap"> | |||
<span>{{.i18n.Tr "admin.cloudbrain.cloudbrain_name"}}</span> | |||
</div> | |||
<div class="two wide column text center nowrap" style="width: 17.5%!important;"> | |||
<span>{{$.i18n.Tr "repo.cloudbrain_operate"}}</span> | |||
</div> | |||
</div> | |||
</div> | |||
{{range .Tasks}} | |||
{{if .Repo}} | |||
<div class="ui grid stackable item"> | |||
<div class="row"> | |||
<!-- 任务名 --> | |||
<div class="two wide column nowrap"> | |||
{{if or (eq .JobType "DEBUG") (eq .JobType "SNN4IMAGENET") (eq .JobType "BRAINSCORE")}} | |||
<a class="title" href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain/{{.JobName}}{{else}}/modelarts/notebook/{{.JobID}}{{end}}" title="{{.JobName}}" style="font-size: 14px;"> | |||
<span class="fitted" style="width: 90%;vertical-align: middle;">{{.JobName}}</span> | |||
</a> | |||
{{else if eq .JobType "INFERENCE"}} | |||
<a class="title" href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}/modelarts/inference-job/{{.JobID}}" title="{{.JobName}}" style="font-size: 14px;"> | |||
<span class="fitted" style="width: 90%;vertical-align: middle;">{{.JobName}}</span> | |||
</a> | |||
{{else if eq .JobType "TRAIN"}} | |||
<a class="title" href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}/modelarts/train-job/{{.JobID}}" title="{{.JobName}}" style="font-size: 14px;"> | |||
<span class="fitted" style="width: 90%;vertical-align: middle;">{{.JobName}}</span> | |||
</a> | |||
{{else if eq .JobType "BENCHMARK"}} | |||
<a class="title" href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}/cloudbrain/benchmark/{{.JobName}}" title="{{.JobName}}" style="font-size: 14px;"> | |||
<span class="fitted" style="width: 90%;vertical-align: middle;">{{.JobName}}</span> | |||
</a> | |||
{{end}} | |||
</div> | |||
<!-- 任务类型 --> | |||
<div class="one wide column text center nowrap"> | |||
<span style="font-size: 12px;">{{.JobType}} </span> | |||
</div> | |||
<!-- 任务状态 --> | |||
<div class="two wide column text center nowrap" style="padding-left: 2.2rem !important; width: 10% !important;"> | |||
<span class="job-status" id="{{.JobID}}" data-repopath='{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .JobType "DEBUG"}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}{{else if eq .JobType "INFERENCE"}}/modelarts/inference-job{{else if eq .JobType "TRAIN"}}/modelarts/train-job{{else if eq .JobType "BENCHMARK"}}/cloudbrain{{end}}' data-jobid="{{.JobID}}" data-version="{{.VersionName}}"> | |||
<span><i id="{{.JobID}}-icon" style="vertical-align: middle;" class="{{.Status}}"></i><span id="{{.JobID}}-text" style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span> | |||
</span> | |||
</div> | |||
<!-- 任务创建时间 --> | |||
<div class="two wide column text center nowrap" style="width: 10% !important;"> | |||
<span style="font-size: 12px;" class="">{{TimeSinceUnix1 .Cloudbrain.CreatedUnix}}</span> | |||
</div> | |||
<!-- 任务运行时间 --> | |||
<div class="one wide column text center nowrap"> | |||
<span style="font-size: 12px;" id="duration-{{.JobID}}">{{if .TrainJobDuration}}{{.TrainJobDuration}}{{else}}--{{end}}</span> | |||
</div> | |||
<!-- 计算资源 --> | |||
<div class="one wide column text center nowrap"> | |||
<span style="font-size: 12px;">{{if .ComputeResource}}{{.ComputeResource}}{{else}}--{{end}}</span> | |||
</div> | |||
<!-- 创建者 --> | |||
<div class="one wide column text center nowrap"> | |||
{{if .User.Name}} | |||
<a href="{{AppSubUrl}}/{{.User.Name}}" title="{{.User.Name}}"><img class="ui avatar image" src="{{.User.RelAvatarLink}}"></a> | |||
{{else}} | |||
<a title="Ghost"><img class="ui avatar image" src="{{AppSubUrl}}/user/avatar/Ghost/-1"></a> | |||
{{end}} | |||
</div> | |||
<!-- 项目 --> | |||
<div class="two wide column text center nowrap"> | |||
<a href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}" title="{{.Repo.OwnerName}}/{{.Repo.Alias}}">{{.Repo.OwnerName}}/{{.Repo.Alias}}</a> | |||
</div> | |||
<!-- 云脑侧名称 --> | |||
<div class="two wide column text center nowrap" style="overflow: hidden;text-overflow:ellipsis;"> | |||
<span class="fitted">{{.JobName}}</span> | |||
</div> | |||
<div class="two wide column text center nowrap" style="width: 17.5%!important;"> | |||
{{if eq .JobType "DEBUG" "SNN4IMAGENET" "BRAINSCORE"}} | |||
<div class="ui compact buttons"> | |||
<form id="debugAgainForm-{{.JobID}}"> | |||
{{$.CsrfTokenHtml}} | |||
{{if eq .Status "RUNNING" "WAITING" "CREATING" "STARTING"}} | |||
<a style="margin: 0 1rem;" id="ai-debug-{{.JobID}}" class='ui basic ai_debug {{if eq .Status "CREATING" "STOPPING" "WAITING" "STARTING"}}disabled {{else}}blue {{end}}button' data-jobid="{{.JobID}}" data-repopath='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}/{{.JobID}}/'> | |||
{{$.i18n.Tr "repo.debug"}} | |||
</a> | |||
{{else}} | |||
<a id="ai-debug-{{.JobID}}" class='ui basic ai_debug {{if eq .Status "CREATING" "STOPPING" "WAITING" "STARTING"}} disabled {{else}}blue {{end}}button' data-jobid="{{.JobID}}" data-repopath='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}/{{.JobID}}/'> | |||
{{$.i18n.Tr "repo.debug_again"}} | |||
</a> | |||
{{end}} | |||
</form> | |||
</div> | |||
{{end}} | |||
<!-- 停止任务 --> | |||
<div class="ui compact buttons"> | |||
{{if eq .JobType "DEBUG" "BENCHMARK" "SNN4IMAGENET" "BRAINSCORE"}} | |||
<form id="stopForm-{{.JobID}}" style="margin-left:-1px;"> | |||
{{$.CsrfTokenHtml}} | |||
<a style="padding: 0.5rem 1rem;" id="ai-stop-{{.JobID}}" class='ui basic ai_stop {{if eq .Status "KILLED" "FAILED" "START_FAILED" "KILLING" "COMPLETED" "SUCCEEDED" "STOPPED" "STOPPING"}}disabled {{else}} blue {{end}}button' data-repopath='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else if eq .JobType "BENCHMARK" }}/cloudbrain/benchmark{{else if eq .ComputeResource "NPU" }}/modelarts/notebook{{end}}/{{.JobID}}/stop' data-jobid="{{.JobID}}"> | |||
{{$.i18n.Tr "repo.stop"}} | |||
</a> | |||
</form> | |||
{{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"}}disabled {{else}} blue {{end}}button" data-repopath="{{.Repo.OwnerName}}/{{.Repo.Name}}/modelarts/{{if eq .JobType "INFERENCE"}}inference-job{{else}}train-job{{end}}" data-jobid="{{.JobID}}" data-version="{{.VersionName}}" > | |||
{{$.i18n.Tr "repo.stop"}} | |||
</a> | |||
{{end}} | |||
</div> | |||
<!-- 删除任务 --> | |||
<form class="ui compact buttons" id="delForm-{{.JobID}}" action='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .JobType "BENCHMARK"}}/cloudbrain/benchmark{{else if eq .JobType "DEBUG"}}{{if eq .ComputeResource "NPU"}}/modelarts/notebook{{else}}/cloudbrain{{end}}{{else if eq .JobType "TRAIN"}}/modelarts/train-job{{end}}/{{.JobID}}/del?isadminpage=true' method="post"> | |||
{{$.CsrfTokenHtml}} | |||
<a style="padding: 0.5rem 1rem;margin-left:0.2rem" id="ai-delete-{{.JobID}}" data-repopath="{{.Repo.OwnerName}}/{{.Repo.Name}}/modelarts/inference-job/{{.JobID}}/del_version?isadminpage=true" data-version="{{.VersionName}}" class="ui basic ai_delete blue button" style="border-radius: .28571429rem;"> | |||
{{$.i18n.Tr "repo.delete"}} | |||
</a> | |||
</form> | |||
</div> | |||
</div> | |||
</div> | |||
{{else}} | |||
<div class="ui grid stackable item"> | |||
<div class="row"> | |||
<!-- 任务名 --> | |||
<div class="two wide column nowrap"> | |||
{{if eq .JobType "DEBUG"}} | |||
<a class="title" href="" title="{{.JobName}}" style="font-size: 14px;"> | |||
<span class="fitted" style="width: 90%;vertical-align: middle;">{{.JobName}}</span> | |||
</a> | |||
{{else if eq .JobType "INFERENCE"}} | |||
<a class="title" href="" title="{{.JobName}}" style="font-size: 14px;"> | |||
<span class="fitted" style="width: 90%;vertical-align: middle;">{{.JobName}}</span> | |||
</a> | |||
{{else if eq .JobType "TRAIN"}} | |||
<a class="title" href="" title="{{.JobName}}" style="font-size: 14px;"> | |||
<span class="fitted" style="width: 90%;vertical-align: middle;">{{.JobName}}</span> | |||
</a> | |||
{{else if eq .JobType "BENCHMARK"}} | |||
<a class="title" href="" title="{{.JobName}}" style="font-size: 14px;"> | |||
<span class="fitted" style="width: 90%;vertical-align: middle;">{{.JobName}}</span> | |||
</a> | |||
{{end}} | |||
</div> | |||
<!-- 任务类型 --> | |||
<div class="one wide column text center nowrap"> | |||
<span style="font-size: 12px;">{{.JobType}} </span> | |||
</div> | |||
<!-- 任务状态 --> | |||
<div class="two wide column text center nowrap" style="padding-left: 2.2rem !important; width: 10% !important;"> | |||
<span class="job-status" id="{{.JobID}}" data-jobid="{{.JobID}}" data-version="{{.VersionName}}"> | |||
<span><i id="{{.JobID}}-icon" style="vertical-align: middle;" class="{{.Status}}"></i><span id="{{.JobID}}-text" style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span> | |||
</span> | |||
</div> | |||
<!-- 任务创建时间 --> | |||
<div class="two wide column text center nowrap" style="width: 10% !important;"> | |||
<span style="font-size: 12px;" class="">{{TimeSinceUnix1 .Cloudbrain.CreatedUnix}}</span> | |||
</div> | |||
<!-- 任务运行时间 --> | |||
<div class="one wide column text center nowrap"> | |||
<span style="font-size: 12px;" id="duration-{{.JobID}}">{{if .TrainJobDuration}}{{.TrainJobDuration}}{{else}}--{{end}}</span> | |||
</div> | |||
<!-- 计算资源 --> | |||
<div class="one wide column text center nowrap"> | |||
<span style="font-size: 12px;">{{if .ComputeResource}}{{.ComputeResource}}{{else}}--{{end}}</span> | |||
</div> | |||
<!-- 创建者 --> | |||
<div class="one wide column text center nowrap"> | |||
{{if .User.Name}} | |||
<a href="{{AppSubUrl}}/{{.User.Name}}" title="{{.User.Name}}"><img class="ui avatar image" src="{{.User.RelAvatarLink}}"></a> | |||
{{else}} | |||
<a title="Ghost"><img class="ui avatar image" src="{{AppSubUrl}}/user/avatar/Ghost/-1"></a> | |||
{{end}} | |||
</div> | |||
<!-- 项目 --> | |||
<div class="two wide column text center nowrap"> | |||
<a href="" title="">--</a> | |||
</div> | |||
<!-- 云脑侧名称 --> | |||
<div class="two wide column text center nowrap" style="overflow: hidden;text-overflow:ellipsis;"> | |||
<span class="fitted">{{.JobName}}</span> | |||
</div> | |||
<div class="two wide column text center nowrap" style="width: 17.5%!important;"> | |||
{{if eq .JobType "DEBUG"}} | |||
<div class="ui compact buttons"> | |||
<form id="debugAgainForm-{{.JobID}}"> | |||
{{$.CsrfTokenHtml}} | |||
{{if eq .Status "RUNNING" "WAITING" "CREATING" "STARTING"}} | |||
<a style="margin: 0 1rem;" id="ai-debug-{{.JobID}}" class='ui basic disabled button' > | |||
{{$.i18n.Tr "repo.debug"}} | |||
</a> | |||
{{else}} | |||
<a id="ai-debug-{{.JobID}}" class='ui basic disabled button' > | |||
{{$.i18n.Tr "repo.debug_again"}} | |||
</a> | |||
{{end}} | |||
</form> | |||
</div> | |||
{{end}} | |||
<!-- 停止任务 --> | |||
<div class="ui compact buttons"> | |||
<a style="padding: 0.5rem 1rem;" id="ai-stop-{{.JobID}}" class="ui basic disabled button" data-jobid="{{.JobID}}" data-version="{{.VersionName}}" > | |||
{{$.i18n.Tr "repo.stop"}} | |||
</a> | |||
</div> | |||
<!-- 删除任务 --> | |||
<form class="ui compact buttons" id="delForm-{{.JobID}}" action='' method="post"> | |||
{{$.CsrfTokenHtml}} | |||
<a style="padding: 0.5rem 1rem;margin-left:0.2rem" id="ai-delete-{{.JobID}}" class="ui basic disabled button" style="border-radius: .28571429rem;"> | |||
{{$.i18n.Tr "repo.delete"}} | |||
</a> | |||
</form> | |||
</div> | |||
</div> | |||
</div> | |||
{{end}} | |||
{{end}} | |||
<div id="app" style="margin-top: 2rem;"> | |||
<div class="center"> | |||
<el-pagination | |||
background | |||
@current-change="handleCurrentChange" | |||
:current-page="page" | |||
:page-sizes="[10]" | |||
:page-size="10" | |||
layout="total, sizes, prev, pager, next, jumper" | |||
:total="{{.Page.Paginater.Total}}"> | |||
</el-pagination> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<!-- 确认模态框 --> | |||
<div id="deletemodel"> | |||
<div class="ui basic modal"> | |||
<div class="ui icon header"> | |||
<i class="trash icon"></i> 删除任务 | |||
</div> | |||
<div class="content"> | |||
<p>你确认删除该任务么?此任务一旦删除不可恢复。</p> | |||
</div> | |||
<div class="actions"> | |||
<div class="ui red basic inverted cancel button"> | |||
<i class="remove icon"></i> 取消操作 | |||
</div> | |||
<div class="ui green basic inverted ok button"> | |||
<i class="checkmark icon"></i> 确定操作 | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
{{template "base/footer" .}} | |||
<script> | |||
function getParams(){ | |||
const params = new URLSearchParams(window.location.search) | |||
let jobType = !params.get('jobType')? '{{.i18n.Tr "admin.cloudbrain.all_task_types"}}' : params.get('jobType') | |||
let listType = !params.get('listType')? '{{.i18n.Tr "admin.cloudbrain.all_computing_resources"}}' : params.get('listType') | |||
let jobStatus = !params.get('jobStatus')? '{{.i18n.Tr "admin.cloudbrain.all_status"}}' : params.get('jobStatus').toUpperCase() | |||
const dropdownValueArray = [jobType,listType,jobStatus] | |||
$('#adminCloud .default.text ').each(function(index,e){ | |||
$(e).text(dropdownValueArray[index]) | |||
}) | |||
} | |||
getParams() | |||
</script> |
@@ -0,0 +1,51 @@ | |||
<div class="ui attached segment"> | |||
<form class="ui form ignore-dirty" style="max-width: 90%"> | |||
<div class="ui fluid action input"> | |||
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "admin.cloudbrain.search"}}..." autofocus> | |||
<button class="ui blue button">{{.i18n.Tr "explore.search"}}</button> | |||
</div> | |||
</form> | |||
</div> | |||
<div class="ui six wide column" style="margin: 1rem 0;" id="adminCloud"> | |||
<div class="ui selection dropdown" style="min-width: 10em;min-height:2.6em;border-radius: .28571429rem;margin-right: 1em;padding: .67em 3.2em .7em 1em;"> | |||
<div class="default text" style="color: rgba(0,0,0,.87);">{{.i18n.Tr "admin.cloudbrain.all_task_types"}}</div> | |||
<i class="dropdown icon"></i> | |||
<div class="menu"> | |||
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&jobType=&listType={{$.ListType}}&jobStatus={{$.JobStatus}}" data-value='{{.i18n.Tr "admin.cloudbrain.all_task_types"}}'>{{.i18n.Tr "admin.cloudbrain.all_task_types"}}</a> | |||
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&jobType=DEBUG&listType={{$.ListType}}&jobStatus={{$.JobStatus}}" data-value="DEBUG">DEBUG</a> | |||
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&jobType=TRAIN&listType={{$.ListType}}&jobStatus={{$.JobStatus}}" data-value="TRAIN">TRAIN</a> | |||
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&jobType=INFERENCE&listType={{$.ListType}}&jobStatus={{$.JobStatus}}" data-value="INFERENCE">INFERENCE</a> | |||
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&jobType=BENCHMARK&listType={{$.ListType}}&jobStatus={{$.JobStatus}}" data-value="BENCHMARK">BENCHMARK</a> | |||
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&jobType=SNN4IMAGENET&listType={{$.ListType}}&jobStatus={{$.JobStatus}}" data-value="BENCHMARK">SNN4IMAGENET</a> | |||
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&jobType=BRAINSCORE&listType={{$.ListType}}&jobStatus={{$.JobStatus}}" data-value="BENCHMARK">BRAINSCORE</a> | |||
</div> | |||
</div> | |||
<div class="ui selection dropdown" style="min-width: 10em;min-height:2.6em;border-radius: .28571429rem;margin-right: 1em;padding: .67em 3.2em .7em 1em;"> | |||
<div class="default text" style="color: rgba(0,0,0,.87);">{{.i18n.Tr "admin.cloudbrain.all_computing_resources"}}</div> | |||
<i class="dropdown icon"></i> | |||
<div class="menu"> | |||
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&jobType={{$.JobType}}&listType=&jobStatus={{$.JobStatus}}" data-value='{{.i18n.Tr "admin.cloudbrain.all_computing_resources"}}'>{{.i18n.Tr "admin.cloudbrain.all_computing_resources"}}</a> | |||
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&jobType={{$.JobType}}&listType=CPU/GPU&jobStatus={{$.JobStatus}}" data-value="CPU/GPU">CPU/GPU</a> | |||
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&jobType={{$.JobType}}&listType=NPU&jobStatus={{$.JobStatus}}" data-value="NPU">NPU</a> | |||
</div> | |||
</div> | |||
<div class="ui selection dropdown" style="min-width: 10em;min-height:2.6em;border-radius: .28571429rem;margin-right: 1em;padding: .67em 3.2em .7em 1em;"> | |||
<div class="default text" style="color: rgba(0,0,0,.87);">{{.i18n.Tr "admin.cloudbrain.all_status"}}</div> | |||
<i class="dropdown icon"></i> | |||
<div class="menu"> | |||
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&jobType={{$.JobType}}&listType={{$.ListType}}&jobStatus=" data-value='{{.i18n.Tr "admin.cloudbrain.all_status"}}'>{{.i18n.Tr "admin.cloudbrain.all_status"}}</a> | |||
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&jobType={{$.JobType}}&listType={{$.ListType}}&jobStatus=STARTING" data-value="STARTING">STARTING</a> | |||
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&jobType={{$.JobType}}&listType={{$.ListType}}&jobStatus=RUNNING" data-value="RUNNING">RUNNING</a> | |||
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&jobType={{$.JobType}}&listType={{$.ListType}}&jobStatus=RESTARTING" data-value="RESTARTING">RESTARTING </a> | |||
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&jobType={{$.JobType}}&listType={{$.ListType}}&jobStatus=START_FAILED" data-value="START_FAILED">START_FAILED</a> | |||
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&jobType={{$.JobType}}&listType={{$.ListType}}&jobStatus=STOPPING" data-value="STOPPING">STOPPING</a> | |||
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&jobType={{$.JobType}}&listType={{$.ListType}}&jobStatus=STOPPED" data-value="STOPPED">STOPPED</a> | |||
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&jobType={{$.JobType}}&listType={{$.ListType}}&jobStatus=WAITING" data-value="WAITING">WAITING</a> | |||
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&jobType={{$.JobType}}&listType={{$.ListType}}&jobStatus=COMPLETED" data-value="COMPLETED">COMPLETED</a> | |||
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&jobType={{$.JobType}}&listType={{$.ListType}}&jobStatus=SUCCEEDED" data-value="SUCCEEDED">SUCCEEDED</a> | |||
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&jobType={{$.JobType}}&listType={{$.ListType}}&jobStatus=FAILED" data-value="FAILED">FAILED </a> | |||
<a class="item" href="{{$.Link}}?q={{$.Keyword}}&jobType={{$.JobType}}&listType={{$.ListType}}&jobStatus=other" data-value="OTHER">OTHER</a> | |||
</div> | |||
</div> | |||
</div> |
@@ -14,6 +14,9 @@ | |||
<a class="{{if .PageIsAdminDatasets}}active{{end}} item" href="{{AppSubUrl}}/admin/datasets"> | |||
{{.i18n.Tr "admin.datasets"}} | |||
</a> | |||
<a class="{{if .PageIsAdminCloudBrains}}active{{end}} item" href="{{AppSubUrl}}/admin/cloudbrains"> | |||
云脑任务 | |||
</a> | |||
<a class="{{if .PageIsAdminHooks}}active{{end}} item" href="{{AppSubUrl}}/admin/hooks"> | |||
{{.i18n.Tr "admin.hooks"}} | |||
</a> | |||
@@ -8,6 +8,8 @@ | |||
<a href="https://openi.org.cn/html/Club/2019/0227/14.html" class="item">{{.i18n.Tr "custom.foot.council"}}</a> | |||
<a href="https://openi.org.cn/html/Club/2019/0227/14.html" class="item">{{.i18n.Tr "custom.foot.technical_committee"}}</a> | |||
<a href="https://openi.org.cn/html/Club/2019/0228/17.html" class="item">{{.i18n.Tr "custom.foot.join"}}</a> | |||
<a href="{{AppSubUrl}}/home/term/" class="item">{{.i18n.Tr "custom.foot.agreement"}}</a> | |||
</div> | |||
<div class="column ui vertical text menu"> | |||
<div class="header item">{{.i18n.Tr "custom.foot.news"}}</div> | |||
@@ -226,10 +226,12 @@ var _hmt = _hmt || []; | |||
} | |||
let isShowNoticeTag = false; | |||
let notices= {{.notices.Notices}} | |||
for (i =0;i<notices.length;i++){ | |||
if (notices[i].Visible==1){ | |||
isShowNoticeTag =true; | |||
break; | |||
if(notices != null && notices!=''){ | |||
for (i =0;i<notices.length;i++){ | |||
if (notices[i].Visible==1){ | |||
isShowNoticeTag =true; | |||
break; | |||
} | |||
} | |||
} | |||
if (isShowNoticeTag){ | |||
@@ -245,7 +247,9 @@ var _hmt = _hmt || []; | |||
} | |||
}else{ | |||
document.getElementById("notic_content").style.display='none' | |||
if (document.getElementById("notic_content") != null){ | |||
document.getElementById("notic_content").style.display='none' | |||
} | |||
} | |||
} | |||
@@ -227,10 +227,12 @@ var _hmt = _hmt || []; | |||
} | |||
let isShowNoticeTag = false; | |||
let notices= {{.notices.Notices}} | |||
for (i =0;i<notices.length;i++){ | |||
if (notices[i].Visible==1){ | |||
isShowNoticeTag =true; | |||
break; | |||
if(notices != null && notices!=''){ | |||
for (i =0;i<notices.length;i++){ | |||
if (notices[i].Visible==1){ | |||
isShowNoticeTag =true; | |||
break; | |||
} | |||
} | |||
} | |||
if (isShowNoticeTag){ | |||
@@ -246,7 +248,9 @@ var _hmt = _hmt || []; | |||
} | |||
}else{ | |||
document.getElementById("notic_content").style.display='none' | |||
if (document.getElementById("notic_content") != null){ | |||
document.getElementById("notic_content").style.display='none' | |||
} | |||
} | |||
} | |||
@@ -231,10 +231,12 @@ var _hmt = _hmt || []; | |||
} | |||
let isShowNoticeTag = false; | |||
let notices= {{.notices.Notices}} | |||
for (i =0;i<notices.length;i++){ | |||
if (notices[i].Visible==1){ | |||
isShowNoticeTag =true; | |||
break; | |||
if(notices != null && notices!=''){ | |||
for (i =0;i<notices.length;i++){ | |||
if (notices[i].Visible==1){ | |||
isShowNoticeTag =true; | |||
break; | |||
} | |||
} | |||
} | |||
if (isShowNoticeTag){ | |||
@@ -250,7 +252,9 @@ var _hmt = _hmt || []; | |||
} | |||
}else{ | |||
document.getElementById("notic_content").style.display='none' | |||
if (document.getElementById("notic_content") != null){ | |||
document.getElementById("notic_content").style.display='none' | |||
} | |||
} | |||
} | |||
@@ -228,10 +228,12 @@ var _hmt = _hmt || []; | |||
} | |||
let isShowNoticeTag = false; | |||
let notices= {{.notices.Notices}} | |||
for (i =0;i<notices.length;i++){ | |||
if (notices[i].Visible==1){ | |||
isShowNoticeTag =true; | |||
break; | |||
if(notices != null && notices!=''){ | |||
for (i =0;i<notices.length;i++){ | |||
if (notices[i].Visible==1){ | |||
isShowNoticeTag =true; | |||
break; | |||
} | |||
} | |||
} | |||
if (isShowNoticeTag){ | |||
@@ -247,7 +249,9 @@ var _hmt = _hmt || []; | |||
} | |||
}else{ | |||
document.getElementById("notic_content").style.display='none' | |||
if (document.getElementById("notic_content") != null){ | |||
document.getElementById("notic_content").style.display='none' | |||
} | |||
} | |||
} | |||
@@ -20,7 +20,7 @@ | |||
<div id="homenews" class="ui container"> | |||
<p>* {{.page_only_dynamic}}</p> | |||
<div class="ui grid"> | |||
<div class="twelve wide tablet ten wide computer column homenews"> | |||
<div class="sixteen wide mobile twelve wide tablet ten wide computer column homenews"> | |||
<div class="newslist"> | |||
<div class="ui mini aligned list swiper-wrapper" id="newmessage"> | |||
@@ -42,7 +42,11 @@ | |||
<a href="{{AppSubUrl}}/explore/organizations" class="circular ui primary basic button">{{.page_recommend_org_more}} <i class="arrow circle right icon"></i></a> | |||
</div> | |||
<div class="sixteen wide tablet twelve wide computer column"> | |||
<div class="ui stackable three column grid homeorg-list" id="recommendorg"> | |||
<div class="homeorg-list"> | |||
<div class="swiper-wrapper" id="recommendorg"> | |||
</div> | |||
<div class="swiper-pagination"></div> | |||
</div> | |||
</div> | |||
@@ -78,7 +82,7 @@ | |||
<h2>{{.page_dev_env}}</h2> | |||
<p><span class="ui text grey">{{.page_dev_env_desc}}</p> | |||
</div> | |||
<div class="ui four stackable cards"> | |||
<div class="ui four doubling cards"> | |||
<div class="card"> | |||
<div class="image"> | |||
<img src="/img/i-pic-01.svg"> | |||
@@ -158,4 +162,4 @@ | |||
<script src="/home/home.js?v={{MD5 AppVer}}" type="text/javascript"></script> | |||
{{template "base/footer" .}} | |||
{{template "base/footer" .}} |
@@ -67,8 +67,20 @@ | |||
<div class="item"> | |||
<a href="{{$.OrgLink}}/teams/{{.LowerName}}"><strong class="team-name">{{.Name}}</strong></a> | |||
<p class="text grey"> | |||
<a href="{{$.OrgLink}}/teams/{{.LowerName}}"><strong>{{.NumMembers}}</strong> {{$.i18n.Tr "org.lower_members"}}</a> · | |||
<a href="{{$.OrgLink}}/teams/{{.LowerName}}/repositories"><strong>{{.NumRepos}}</strong> {{$.i18n.Tr "org.lower_repositories"}}</a> | |||
<a href="{{$.OrgLink}}/teams/{{.LowerName}}"><strong>{{.NumMembers}}</strong> | |||
{{if le .NumMembers 1}} | |||
{{$.i18n.Tr "org.lower_member"}} | |||
{{else}} | |||
{{$.i18n.Tr "org.lower_members"}} | |||
{{end}} | |||
</a> · | |||
<a href="{{$.OrgLink}}/teams/{{.LowerName}}/repositories"><strong>{{.NumRepos}}</strong> | |||
{{if le .NumRepos 1}} | |||
{{$.i18n.Tr "org.lower_repository"}} | |||
{{else}} | |||
{{$.i18n.Tr "org.lower_repositories"}} | |||
{{end}} | |||
</a> | |||
</p> | |||
</div> | |||
{{end}} | |||
@@ -221,8 +221,20 @@ | |||
<div style="margin-top: 10px;"> | |||
<a href="{{$.OrgLink}}/teams/{{.LowerName}}"><strong class="team-name">{{.Name}}</strong></a> | |||
<p class="text grey"> | |||
<a href="{{$.OrgLink}}/teams/{{.LowerName}}"><strong>{{.NumMembers}}</strong> {{$.i18n.Tr "org.lower_members"}}</a> · | |||
<a href="{{$.OrgLink}}/teams/{{.LowerName}}/repositories"><strong>{{.NumRepos}}</strong> {{$.i18n.Tr "org.lower_repositories"}}</a> | |||
<a href="{{$.OrgLink}}/teams/{{.LowerName}}"><strong>{{.NumMembers}}</strong> | |||
{{if le .NumMembers 1}} | |||
{{$.i18n.Tr "org.lower_member"}} | |||
{{else}} | |||
{{$.i18n.Tr "org.lower_members"}} | |||
{{end}} | |||
</a> · | |||
<a href="{{$.OrgLink}}/teams/{{.LowerName}}/repositories"><strong>{{.NumRepos}}</strong> | |||
{{if le .NumRepos 1}} | |||
{{$.i18n.Tr "org.lower_repository"}} | |||
{{else}} | |||
{{$.i18n.Tr "org.lower_repositories"}} | |||
{{end}} | |||
</a> | |||
</p> | |||
</div> | |||
{{end}} | |||
@@ -120,7 +120,6 @@ | |||
<script> | |||
function show_bt(bt_id){ | |||
console.log(bt_id) | |||
document.getElementById(""+bt_id).getElementsByClassName("button_leaveOrg")[0].style.display="inline-block"; | |||
} | |||
@@ -70,11 +70,11 @@ | |||
<div style="width: 100%;margin:15px 0;"> | |||
{{if .tags}} | |||
<span class="header"> | |||
精选项目 | |||
{{.i18n.Tr "org.selected_project"}} | |||
</span> | |||
<!-- {{.IsOrganizationOwner}} --> | |||
{{if .IsOrganizationOwner}} | |||
<a class="text-right" id="model" onclick="showcreate()" >{{svg "octicon-gear" 16}}自定义</a> | |||
<a class="text-right" id="model" onclick="showcreate()" >{{svg "octicon-gear" 16}}{{.i18n.Tr "org.customize"}}</a> | |||
{{end}} | |||
{{end}} | |||
@@ -137,10 +137,10 @@ | |||
<div class="ui modal"> | |||
<div class="header" style="padding: 1rem;background-color: rgba(240, 240, 240, 100);"> | |||
<h4 id="model_header">自定义精选项目</h4> | |||
<h4 id="model_header">{{.i18n.Tr "org.custom_select_projects"}}</h4> | |||
</div> | |||
<div class="content content-padding" style="color: black;"> | |||
<p>最多可选9个公开项目</p> | |||
<p>{{.i18n.Tr "org.max_selectedPro"}}</p> | |||
<div class="ui search" > | |||
<div class="ui input" style="width: 100%;"> | |||
<input type="text" id = 'search_selectPro' placeholder="Search ..." value = '' oninput="search()"> | |||
@@ -223,12 +223,12 @@ | |||
function saveSeletedPro(typeTag){ | |||
var saveData=[]; | |||
$('input[name="select_pro_name"]:checked').each(function(){ | |||
console.log('值',this.dataset.repoid) | |||
// console.log('值',this.dataset.repoid) | |||
saveData.push(parseInt(this.dataset.repoid)); | |||
}) | |||
if(saveData.length>9){ | |||
alert("最多可选9个,保存失败") | |||
alert("{{.i18n.Tr "org.save_fail_tips"}}") | |||
return | |||
} | |||
// saveData = getSelecteDataID(); | |||
@@ -242,7 +242,7 @@ | |||
data:JSON.stringify({'repoList':saveData | |||
}), | |||
success:function(res){ | |||
console.log('保存成功'); | |||
// console.log('保存成功'); | |||
location.reload() | |||
} | |||
@@ -270,15 +270,15 @@ | |||
filterData.push(data[i]) | |||
} | |||
} | |||
console.log("选中的值:",selectedData) | |||
console.log("筛选包括选中的值:",filterData) | |||
// console.log("选中的值:",selectedData) | |||
// console.log("筛选包括选中的值:",filterData) | |||
var showData=[]; | |||
for(i=0;i<selectedData.length;i++){ | |||
filterData =filterData.filter((item)=>{ | |||
return item.RepoID!=selectedData[i].RepoID | |||
}); | |||
} | |||
console.log("筛选后不包括选中的值:",filterData) | |||
// console.log("筛选后不包括选中的值:",filterData) | |||
$("#org_list").empty() | |||
if(searchValue!=""){ | |||
if (filterData.length!=0){ | |||
@@ -307,7 +307,7 @@ | |||
num++ | |||
if(num>9){ | |||
document.getElementById(id).checked=false | |||
alert("选择超过9个,请重新选择!") | |||
alert("{{.i18n.Tr "org.select_again"}}") | |||
return | |||
} | |||
} | |||
@@ -315,7 +315,8 @@ | |||
} | |||
var show_num = 9-num; | |||
document.getElementById("recommend").innerHTML="还能推荐"+show_num+"个" | |||
let rec = "{{.i18n.Tr "org.recommend_remain_pro"}}" | |||
document.getElementById("recommend").innerHTML=rec +" : "+ show_num | |||
} | |||
@@ -44,7 +44,19 @@ | |||
{{end}} | |||
</div> | |||
<div class="ui bottom attached header"> | |||
<p class="team-meta">{{.NumMembers}} {{$.i18n.Tr "org.lower_members"}} · {{.NumRepos}} {{$.i18n.Tr "org.lower_repositories"}}</p> | |||
<p class="team-meta">{{.NumMembers}} | |||
{{if le .NumMembers 1}} | |||
{{$.i18n.Tr "org.lower_member"}} | |||
{{else}} | |||
{{$.i18n.Tr "org.lower_members"}} | |||
{{end}} | |||
· {{.NumRepos}} | |||
{{if le .NumRepos 1}} | |||
{{$.i18n.Tr "org.lower_repository"}} | |||
{{else}} | |||
{{$.i18n.Tr "org.lower_repositories"}} | |||
{{end}} | |||
</p> | |||
</div> | |||
</div> | |||
{{end}} | |||
@@ -36,7 +36,19 @@ | |||
{{end}} | |||
</div> | |||
<div class="ui bottom attached header"> | |||
<p class="team-meta">{{.NumMembers}} {{$.i18n.Tr "org.lower_members"}} · {{.NumRepos}} {{$.i18n.Tr "org.lower_repositories"}}</p> | |||
<p class="team-meta">{{.NumMembers}} | |||
{{if le .NumMembers 1}} | |||
{{$.i18n.Tr "org.lower_member"}} | |||
{{else}} | |||
{{$.i18n.Tr "org.lower_members"}} | |||
{{end}} | |||
· {{.NumRepos}} | |||
{{if le .NumRepos 1}} | |||
{{$.i18n.Tr "org.lower_repository"}} | |||
{{else}} | |||
{{$.i18n.Tr "org.lower_repositories"}} | |||
{{end}} | |||
</p> | |||
</div> | |||
</div> | |||
{{end}} | |||
@@ -99,14 +99,13 @@ | |||
<!-- 任务名 --> | |||
<div class="three wide column padding0"> | |||
<a class="title" href="{{$.Link}}/{{.JobID}}" title="{{.JobName}}" style="font-size: 14px;"> | |||
<a class="title" href="{{$.Link}}/{{.JobName}}" title="{{.JobName}}" style="font-size: 14px;"> | |||
<span class="fitted" style="width: 90%;vertical-align: middle;">{{.JobName}}</span> | |||
</a> | |||
</div> | |||
<!-- 任务状态 --> | |||
<div class="two wide column padding0" style="padding-left: 2.2rem !important;"> | |||
<span class="job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}" data-version="{{.VersionName}}"> | |||
<span class="job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}" data-jobid="{{.JobID}}" data-version="{{.VersionName}}"> | |||
<span><i id="{{.JobID}}-icon" style="vertical-align: middle;" class="{{.Status}}"></i><span id="{{.JobID}}-text" style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span> | |||
</span> | |||
</div> | |||
@@ -137,10 +136,9 @@ | |||
<form id="stopForm-{{.JobID}}" style="margin-left:-1px;"> | |||
{{$.CsrfTokenHtml}} | |||
{{if .CanDel}} | |||
<a id="stop-model-debug-{{.JobID}}" class='ui basic {{if eq .Status "STOPPED" "FAILED" "START_FAILED" "STOPPING" "CREATING" "STARTING" "SUCCEEDED"}}disabled {{else}}blue {{end}}button' onclick='stopDebug("{{.JobID}}","{{$.RepoLink}}/cloudbrain/benchmark/{{.JobID}}/stop")'> | |||
{{$.i18n.Tr "repo.stop"}} | |||
</a> | |||
<a id="ai-stop-{{.JobID}}" class='ui basic ai_stop {{if eq .Status "STOPPED" "FAILED" "START_FAILED" "STOPPING" "CREATING" "STARTING" "SUCCEEDED"}}disabled {{else}}blue {{end}}button' data-repopath="{{$.RepoLink}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}/{{.JobID}}/stop" data-jobid="{{.JobID}}"> | |||
{{$.i18n.Tr "repo.stop"}} | |||
</a> | |||
{{else}} | |||
<a class="ui basic disabled button"> | |||
@@ -148,26 +146,21 @@ | |||
</a> | |||
{{end}} | |||
</form> | |||
<a class="ui basic button {{if $.IsSigned}} blue{{else}} disabled{{end}}" href="{{$.RepoLink}}/cloudbrain/{{.JobID}}/rate" target="_blank"> | |||
评分 | |||
</a> | |||
<!-- 删除任务 --> | |||
<form id="delForm-{{.JobID}}" action="{{if eq .ComputeResource "CPU/GPU"}}{{$.RepoLink}}/cloudbrain/benchmark{{else}}{{$.RepoLink}}/modelarts/notebook{{end}}/{{.JobID}}/del" method="post"> | |||
<input type="hidden" name="debugListType" value="all"> | |||
{{$.CsrfTokenHtml}} | |||
{{if .CanDel}} | |||
<a id="model-delete-{{.JobID}}" class='ui basic {{if eq .Status "STOPPED" "FAILED" "START_FAILED" "SUCCEEDED"}}blue {{else}}disabled {{end}}button' onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | |||
<a id="ai-delete-{{.JobID}}" class='ui basic ai_delete {{if eq .Status "STOPPED" "FAILED" "START_FAILED" "SUCCEEDED"}}blue {{else}}disabled {{end}}button' style="border-radius: .28571429rem;"> | |||
{{$.i18n.Tr "repo.delete"}} | |||
</a> | |||
{{else}} | |||
<a class="ui basic button disabled" onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | |||
<a class="ui basic button disabled" style="border-radius: .28571429rem;"> | |||
{{$.i18n.Tr "repo.delete"}} | |||
</a> | |||
{{end}} | |||
</form> | |||
</div> | |||
@@ -221,167 +214,3 @@ | |||
</div> | |||
{{template "base/footer" .}} | |||
<script> | |||
console.log({{.Tasks}}) | |||
// 调试和评分新开窗口 | |||
function stop(obj) { | |||
if (obj.style.color != "rgb(204, 204, 204)") { | |||
obj.target = '_blank' | |||
} else { | |||
return | |||
} | |||
} | |||
// 删除时用户确认 | |||
function assertDelete(obj) { | |||
if (obj.style.color == "rgb(204, 204, 204)") { | |||
return | |||
} else { | |||
var delId = obj.parentNode.id | |||
flag = 1; | |||
$('.ui.basic.modal') | |||
.modal({ | |||
onDeny: function() { | |||
flag = false | |||
}, | |||
onApprove: function() { | |||
document.getElementById(delId).submit() | |||
flag = true | |||
}, | |||
onHidden: function() { | |||
if (flag == false) { | |||
$('.alert').html('您已取消操作').removeClass('alert-success').addClass('alert-danger').show().delay(1500).fadeOut(); | |||
} | |||
} | |||
}) | |||
.modal('show') | |||
} | |||
} | |||
function runtime(time){ | |||
if(time){ | |||
let hours = time/3600000<10 ? "0"+parseInt(time/3600000):parseInt(time/3600000) | |||
let miuns = time%3600000/60000<10 ? "0"+parseInt(time%3600000/60000):parseInt(time%3600000/60000) | |||
let seconds = time%60000/1000<10 ? "0"+parseInt(time%60000/1000):parseInt(time%60000/1000) | |||
return hours + ":" + miuns + ":" + seconds | |||
}else{ | |||
return "00:00:00" | |||
} | |||
} | |||
// 加载任务状态 | |||
var timeid = window.setInterval(loadJobStatus, 15000); | |||
$(document).ready(loadJobStatus); | |||
function loadJobStatus() { | |||
$(".job-status").each((index, job) => { | |||
const jobID = job.dataset.jobid; | |||
const repoPath = job.dataset.repopath; | |||
const computeResource = job.dataset.resource | |||
const initArray = ['STOPPED','FAILED','START_FAILED','CREATE_FAILED','SUCCEEDED'] | |||
if (initArray.includes(job.textContent.trim())) { | |||
return | |||
} | |||
const diffResource = computeResource == "NPU" ? 'modelarts/notebook' : 'cloudbrain' | |||
$.get(`/api/v1/repos/${repoPath}/${diffResource}/${jobID}`, (data) => { | |||
const jobID = data.JobID | |||
const status = data.JobStatus | |||
if (status != job.textContent.trim()) { | |||
$('#' + jobID+'-icon').removeClass().addClass(status) | |||
$('#' + jobID+ '-text').text(status) | |||
} | |||
if(status==="RUNNING"){ | |||
$('#model-debug-'+jobID).removeClass('disabled').addClass('blue').text('调试').css("margin","0 1rem") | |||
$('#model-image-'+jobID).removeClass('disabled').addClass('blue') | |||
} | |||
if(status!=="RUNNING"){ | |||
// $('#model-debug-'+jobID).removeClass('blue') | |||
// $('#model-debug-'+jobID).addClass('disabled') | |||
$('#model-image-'+jobID).removeClass('blue').addClass('disabled') | |||
} | |||
if(["CREATING","STOPPING","WAITING","STARTING"].includes(status)){ | |||
$('#model-debug-'+jobID).removeClass('blue').addClass('disabled') | |||
} | |||
if(['STOPPED','FAILED','START_FAILED','CREATE_FAILED','SUCCEEDED'].includes(status)){ | |||
$('#model-debug-'+jobID).removeClass('disabled').addClass('blue').text('再次调试').css("margin","0") | |||
} | |||
if(["RUNNING","WAITING"].includes(status)){ | |||
$('#stop-model-debug-'+jobID).removeClass('disabled').addClass('blue') | |||
} | |||
if(["CREATING","STOPPING","STARTING","STOPPED","FAILED","START_FAILED","SUCCEEDED"].includes(status)){ | |||
$('#stop-model-debug-'+jobID).removeClass('blue').addClass('disabled') | |||
} | |||
if(status==="STOPPED" || status==="FAILED"|| status==="START_FAILED"){ | |||
$('#model-delete-'+jobID).removeClass('disabled').addClass('blue') | |||
}else{ | |||
$('#model-delete-'+jobID).removeClass('blue').addClass('disabled') | |||
} | |||
}).fail(function(err) { | |||
console.log(err); | |||
}); | |||
}); | |||
}; | |||
// 获取弹窗 | |||
var modal = document.getElementById('imageModal'); | |||
// 打开弹窗的按钮对象 | |||
var btns = document.getElementsByClassName("imageBtn"); | |||
// 获取 <span> 元素,用于关闭弹窗 | |||
var spans = document.getElementsByClassName('close'); | |||
// 点击按钮打开弹窗 | |||
for (i = 0; i < btns.length; i++) { | |||
btns[i].onclick = function() { | |||
modal.style.display = "block"; | |||
} | |||
} | |||
// 点击 <span> (x), 关闭弹窗 | |||
for (i = 0; i < spans.length; i++) { | |||
spans[i].onclick = function() { | |||
modal.style.display = "none"; | |||
} | |||
} | |||
// 在用户点击其他地方时,关闭弹窗 | |||
window.onclick = function(event) { | |||
if (event.target == modal) { | |||
modal.style.display = "none"; | |||
} | |||
} | |||
function stopDebug(JobID,stopUrl){ | |||
$.ajax({ | |||
type:"POST", | |||
url:stopUrl, | |||
data:$('#stopForm-'+JobID).serialize(), | |||
success:function(res){ | |||
if(res.result_code==="0"){ | |||
$('#' + JobID+'-icon').removeClass().addClass(res.status) | |||
$('#' + JobID+ '-text').text(res.status) | |||
if(res.status==="STOPPED"){ | |||
$('#model-debug-'+JobID).removeClass('disabled').addClass('blue').text("再次调试").css("margin","0") | |||
$('#model-image-'+JobID).removeClass('blue').addClass('disabled') | |||
$('#stop-model-debug-'+JobID).removeClass('blue').addClass('disabled') | |||
$('#model-delete-'+JobID).removeClass('disabled').addClass('blue') | |||
} | |||
else{ | |||
$('#model-debug-'+JobID).removeClass('blue').addClass('disabled') | |||
$('#stop-model-debug-'+JobID).removeClass('blue').addClass('disabled') | |||
} | |||
}else{ | |||
$('.alert').html(res.error_msg).removeClass('alert-success').addClass('alert-danger').show().delay(2000).fadeOut(); | |||
} | |||
}, | |||
error :function(res){ | |||
console.log(res) | |||
} | |||
}) | |||
} | |||
</script> |
@@ -49,6 +49,9 @@ | |||
text-align: center; | |||
color: #C2C7CC; | |||
} | |||
.nowrapx { | |||
white-space: nowrap !important; | |||
} | |||
</style> | |||
<!-- <div class="ui page dimmer"> | |||
@@ -83,7 +86,7 @@ | |||
</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="254" 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> | |||
<textarea style="width: 80%;" id="description" name="description" rows="3" maxlength="254" 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)">{{.description}}</textarea> | |||
</div> | |||
<div class="required unite min_title inline field"> | |||
@@ -102,13 +105,18 @@ | |||
<span> </span> | |||
<select class="ui fluid selection search dropdown" id="benchmark_types_id" name="benchmark_types_id" > | |||
{{range .benchmark_types}} | |||
<option value="{{.Id}}">{{.First}}</option> | |||
{{if eq .Id $.benchmarkTypeID}} | |||
<option value="{{.Id}}" selected="true">{{.First}}</option> | |||
{{else}} | |||
<option value="{{.Id}}">{{.First}}</option> | |||
{{end}} | |||
{{end}} | |||
</select> | |||
</div> | |||
<div class="eight wide field" id="engine_name"> | |||
<input type="hidden" id="benchmark_child_types_id_hidden" name="benchmark_child_types_id_hidden" value="{{.benchmark_child_types_id_hidden}}"> | |||
<label style="font-weight: normal;white-space: nowrap;">{{.i18n.Tr "repo.cloudbrain.benchmark.evaluate_child_type"}}</label> | |||
<select class="ui fluid selection dropdown nowrap" id="benchmark_child_types_id" style='width: 100%;' name="benchmark_child_types_id"> | |||
<select class="ui fluid selection dropdown nowrapx" id="benchmark_child_types_id" style='width: 100%;' name="benchmark_child_types_id"> | |||
</select> | |||
</div> | |||
</div> | |||
@@ -182,12 +190,16 @@ | |||
function setChildType(){ | |||
let type_id = $('#benchmark_types_id').val(); | |||
let child_selected_id = $('#benchmark_child_types_id_hidden').val(); | |||
$.get(`${repolink}/cloudbrain/benchmark/get_child_types?benchmark_type_id=${type_id}`, (data) => { | |||
console.log(JSON.stringify(data)) | |||
const n_length = data['child_types'].length | |||
let html='' | |||
for (let i=0;i<n_length;i++){ | |||
html += `<option value="${data['child_types'][i].id}">${data['child_types'][i].value}</option>`; | |||
if(child_selected_id == data['child_types'][i].id){ | |||
html += `<option value="${data['child_types'][i].id}" selected="true">${data['child_types'][i].value}</option>`; | |||
}else{ | |||
html += `<option value="${data['child_types'][i].id}">${data['child_types'][i].value}</option>`; | |||
} | |||
} | |||
document.getElementById("benchmark_child_types_id").innerHTML=html; | |||
}) | |||
@@ -188,6 +188,7 @@ td, th { | |||
</h4> | |||
{{range $k ,$v := .version_list_task}} | |||
<div class="ui accordion border-according" id="accordion{{.VersionName}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}" data-version="{{.VersionName}}"> | |||
<input type="hidden" id="jobId_input" name="jobId_input" value="{{.JobID}}"> | |||
<div class="{{if eq $k 0}}active{{end}} title padding0"> | |||
<div class="according-panel-heading"> | |||
<div class="accordion-panel-title"> | |||
@@ -432,19 +433,18 @@ td, th { | |||
let userName | |||
let repoPath | |||
let jobID | |||
let jobName | |||
$(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] | |||
jobName = urlArr.slice(-1)[0] | |||
}) | |||
function loadLog(version_name){ | |||
document.getElementById("mask").style.display = "block" | |||
$.get(`/api/v1/repos/${userName}/${repoPath}/cloudbrain/${jobID}/log?version_name=${version_name}&lines=50&order=asc`, (data) => { | |||
$.get(`/api/v1/repos/${userName}/${repoPath}/cloudbrain/${jobName}/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) | |||
@@ -254,7 +254,7 @@ | |||
<button class="ui green button" > | |||
{{.i18n.Tr "repo.cloudbrain.new"}} | |||
</button> | |||
<a class="ui button cancel" href="">{{.i18n.Tr "repo.cloudbrain.cancel"}}</a> | |||
<a class="ui button cancel" href="{{.RepoLink}}/debugjob?debugListType=all">{{.i18n.Tr "repo.cloudbrain.cancel"}}</a> | |||
</div> | |||
</div> | |||
</form> | |||
@@ -11,7 +11,7 @@ | |||
{{.i18n.Tr "repo.cloudbrain"}} | |||
</a> | |||
<div class="divider"> / </div> | |||
<a class="section backTodeBug" href="{{.RepoLink}}/debugjob?debugListType=CPU/GPU"> | |||
<a class="section backTodeBug" href="{{.RepoLink}}/debugjob?debugListType={{if eq $.debugListType "NPU"}}NPU{{else if eq $.debugListType "CPU/GPU"}}CPU/GPU{{else}}all{{end}}"> | |||
{{$.i18n.Tr "repo.modelarts.notebook"}} | |||
</a> | |||
<div class="divider"> / </div> | |||
@@ -98,4 +98,3 @@ | |||
</div> | |||
</div> | |||
{{template "base/footer" .}} | |||
@@ -121,8 +121,8 @@ | |||
</div> | |||
<a style="margin-left: 0.5rem;" id="manage_topic"> | |||
{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}<i style="cursor: pointer;" class="plus square outline icon"></i>{{end}} | |||
{{.i18n.Tr "repo.issues.new.add_labels_title"}} | |||
{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}<i style="cursor: pointer;" class="plus square outline icon"></i>{{.i18n.Tr "repo.issues.new.add_labels_title"}}{{end}} | |||
</a> | |||
<div id="topic_edit" class="vue_menu" style="display:none;"> | |||
@@ -94,7 +94,6 @@ $(document).ready(function(){ | |||
$('#course_label_item').empty() | |||
}else{ | |||
$.get(`/api/v1/topics/search?q=${query}`,(data)=>{ | |||
console.log(data) | |||
if(data.topics.length!==0){ | |||
let html='' | |||
$('#course_label_item').empty() | |||
@@ -107,5 +106,4 @@ $(document).ready(function(){ | |||
} | |||
}); | |||
}) | |||
console.log() | |||
</script> |
@@ -7,14 +7,14 @@ | |||
<div class="row"> | |||
<div class="eight wide column" data-tooltip="{{.Name}}"> | |||
<span class="ui right">{{.Size | FileSize}}</span> | |||
<a class="title" href="{{.DownloadURL}}?type={{$.Type}}"> | |||
<a class="title" href="{{.DownloadURL}}"> | |||
{{svg "octicon-cloud-download" 16}} {{.Name}} | |||
</a> | |||
</div> | |||
<div class="eight wide column right aligned"> | |||
<div class="ui left mini icon buttons"> | |||
<span class="ui basic button text left" data-tooltip='{{$.i18n.Tr "dataset.download_count"}}' data-position="bottom right" style="width: 60px; padding-left: 0;">{{svg "octicon-flame" 16}} {{(.DownloadCount | PrettyNumber)}}</span> | |||
<span class="ui basic basic button clipboard" data-clipboard-text="{{.DownloadURL}}" data-tooltip='{{$.i18n.Tr "dataset.copy_url"}}' data-clipboard-action="copy"{{if ne $.Type 0}} style="display:none;"{{end}}>{{svg "octicon-file" 16}}</span> | |||
<span class="ui basic basic button clipboard" data-clipboard-text="{{.DownloadURL}}" data-tooltip='{{$.i18n.Tr "dataset.copy_url"}}' data-clipboard-action="copy">{{svg "octicon-file" 16}}</span> | |||
<span class="ui basic basic button clipboard" data-clipboard-text="{{.FileChunk.Md5}}" data-tooltip='{{$.i18n.Tr "dataset.copy_md5"}}' data-clipboard-action="copy">{{svg "octicon-file-binary" 16}}</span> | |||
</div> | |||
{{if eq .DecompressState 1}} | |||
@@ -11,9 +11,8 @@ | |||
{{template "repo/header" .}} | |||
<script> | |||
$(document).ready(function() { | |||
url = window.location.href | |||
type = url.split('?type=')[1] | |||
if (type == 0){ | |||
const params = new URLSearchParams(window.location.search); | |||
if (params.get('type') == 0){ | |||
$('.contorl_component').attr("id", 'minioUploader') | |||
}else{ | |||
$('.contorl_component').attr("id", 'obsUploader') | |||
@@ -287,13 +287,13 @@ | |||
<div class="row"> | |||
<!-- 任务名 --> | |||
<div class="four wide column"> | |||
<a class="title" href='{{if eq .ComputeResource "CPU/GPU"}}{{$.RepoLink}}/cloudbrain{{else}}{{$.RepoLink}}/modelarts/notebook{{end}}/{{.JobID}}' title="{{.JobName}}" style="font-size: 14px;"> | |||
<a class="title" href='{{if eq .ComputeResource "CPU/GPU"}}{{$.RepoLink}}/cloudbrain/{{.JobName}}{{else}}{{$.RepoLink}}/modelarts/notebook/{{.JobID}}{{end}}?debugListType={{$.debugListType}}' title="{{.JobName}}" style="font-size: 14px;"> | |||
<span class="fitted text_over" style="width: 90%;vertical-align: middle;">{{.JobName}}</span> | |||
</a> | |||
</div> | |||
<div class="two wide column text center"> | |||
<!--任务状态 --> | |||
<span class="job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}" data-resource="{{.ComputeResource}}"> | |||
<span class="job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}" data-jobid="{{.JobID}}" data-resource="{{.ComputeResource}}"> | |||
<span><i id="{{.JobID}}-icon" style="vertical-align: middle;" class="{{.Status}}"></i><span id="{{.JobID}}-text" style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span> | |||
</span> | |||
</div> | |||
@@ -324,11 +324,11 @@ | |||
{{$.CsrfTokenHtml}} | |||
{{if .CanDebug}} | |||
{{if eq .Status "RUNNING" "WAITING" "CREATING" "STARTING"}} | |||
<a style="margin: 0 1rem;" id="model-debug-{{.JobID}}" class='ui basic {{if eq .Status "CREATING" "STOPPING" "WAITING" "STARTING"}}disabled {{else}}blue {{end}}button' onclick='debugAgain("{{.JobID}}","{{if eq .ComputeResource "CPU/GPU"}}{{$.RepoLink}}/cloudbrain{{else}}{{$.RepoLink}}/modelarts/notebook{{end}}/{{.JobID}}/")'> | |||
<a style="margin: 0 1rem;" id="ai-debug-{{.JobID}}" class='ui basic ai_debug {{if eq .Status "CREATING" "STOPPING" "WAITING" "STARTING"}}disabled {{else}}blue {{end}}button' data-jobid="{{.JobID}}" data-repopath='{{$.RepoLink}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}/{{.JobID}}/'> | |||
{{$.i18n.Tr "repo.debug"}} | |||
</a> | |||
{{else}} | |||
<a id="model-debug-{{.JobID}}" class='ui basic {{if eq .Status "CREATING" "STOPPING" "WAITING" "STARTING"}} disabled {{else}}blue {{end}}button' onclick='debugAgain("{{.JobID}}","{{if eq .ComputeResource "CPU/GPU"}}{{$.RepoLink}}/cloudbrain{{else}}{{$.RepoLink}}/modelarts/notebook{{end}}/{{.JobID}}/")'> | |||
<a id="ai-debug-{{.JobID}}" class='ui basic ai_debug {{if eq .Status "CREATING" "STOPPING" "WAITING" "STARTING"}} disabled {{else}}blue {{end}}button' data-jobid="{{.JobID}}" data-repopath='{{$.RepoLink}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}/{{.JobID}}/' data-linkpath='{{$.Link}}'> | |||
{{$.i18n.Tr "repo.debug_again"}} | |||
</a> | |||
{{end}} | |||
@@ -349,15 +349,9 @@ | |||
<form id="stopForm-{{.JobID}}" style="margin-left:-1px;"> | |||
{{$.CsrfTokenHtml}} | |||
{{if .CanDel}} | |||
{{if eq .ComputeResource "CPU/GPU" }} | |||
<a id="stop-model-debug-{{.JobID}}" class='ui basic {{if eq .Status "STOPPED" "FAILED" "START_FAILED" "STOPPING" "CREATING" "STARTING" "SUCCEEDED"}}disabled {{else}}blue {{end}}button' onclick='stopDebug("{{.JobID}}","{{$.RepoLink}}/cloudbrain/{{.JobID}}/stop")'> | |||
{{$.i18n.Tr "repo.stop"}} | |||
</a> | |||
{{else}} | |||
<a id="stop-model-debug-{{.JobID}}" class='ui basic {{if eq .Status "STOPPED" "FAILED" "START_FAILED" "STOPPING" "CREATING" "STARTING" "SUCCEEDED"}}disabled {{else}}blue {{end}}button' onclick='stopDebug("{{.JobID}}","{{$.RepoLink}}/modelarts/notebook/{{.JobID}}/stop")'> | |||
{{$.i18n.Tr "repo.stop"}} | |||
</a> | |||
{{end}} | |||
<a id="ai-stop-{{.JobID}}" class='ui basic ai_stop {{if eq .Status "STOPPED" "FAILED" "START_FAILED" "STOPPING" "CREATING" "STARTING" "SUCCEEDED"}}disabled {{else}}blue {{end}}button' data-repopath="{{$.RepoLink}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts/notebook{{end}}/{{.JobID}}/stop" data-jobid="{{.JobID}}"> | |||
{{$.i18n.Tr "repo.stop"}} | |||
</a> | |||
{{else}} | |||
<a class="ui basic disabled button"> | |||
{{$.i18n.Tr "repo.stop"}} | |||
@@ -366,22 +360,19 @@ | |||
</form> | |||
<!-- 删除 --> | |||
<form id="delForm-{{.JobID}}" action="{{if eq .ComputeResource "CPU/GPU"}}{{$.RepoLink}}/cloudbrain{{else}}{{$.RepoLink}}/modelarts/notebook{{end}}/{{.JobID}}/del" method="post"> | |||
<input type="hidden" name="debugListType" value="all"> | |||
<input type="hidden" name="debugListType" value="{{$.ListType}}"> | |||
{{$.CsrfTokenHtml}} | |||
{{if .CanDel}} | |||
<a id="model-delete-{{.JobID}}" class='ui basic {{if eq .Status "STOPPED" "FAILED" "START_FAILED"}}blue {{else}}disabled {{end}}button' onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | |||
<a id="ai-delete-{{.JobID}}" class='ui basic ai_delete {{if eq .Status "STOPPED" "FAILED" "START_FAILED"}}blue {{else}}disabled {{end}}button' style="border-radius: .28571429rem;"> | |||
{{$.i18n.Tr "repo.delete"}} | |||
</a> | |||
{{else}} | |||
<a class="ui basic button disabled" onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | |||
{{$.i18n.Tr "repo.delete"}} | |||
<a class="ui basic button disabled" style="border-radius: .28571429rem;"> | |||
{{$.i18n.Tr "repo.delete"}} | |||
</a> | |||
{{end}} | |||
</form> | |||
</div> | |||
<!-- 删除任务 --> | |||
</div> | |||
<div class="ui compact buttons" style="{{if eq .ComputeResource "CPU/GPU"}} visibility: visible {{else}} visibility: hidden{{end}}"> | |||
<div class="ui dropdown" id="model_more" style="padding: .58928571em 1.125em .58928571em;"> | |||
<div class="text">更多</div> | |||
@@ -411,7 +402,6 @@ | |||
</a> | |||
</div> | |||
{{end}} | |||
</div> | |||
</div> | |||
</div> | |||
@@ -487,15 +477,15 @@ | |||
</div> | |||
{{template "base/footer" .}} | |||
<script> | |||
// 调试和评分新开窗口 | |||
console.log({{.Tasks}}) | |||
const {AppSubUrl, StaticUrlPrefix, csrf} = window.config; | |||
let url={{.RepoLink}} | |||
let redirect_to = {{$.Link}} | |||
let getParam=getQueryVariable('debugListType') | |||
let dropdownValue = getParam==='all'||getParam==='' ? '全部' : getParam | |||
localStorage.setItem('all',location.href) | |||
let dropdownValue = ['all','',false].includes(getParam)? '全部' : getParam | |||
// localStorage.setItem('all',location.href) | |||
function getQueryVariable(variable) | |||
{ | |||
let query = window.location.search.substring(1); | |||
@@ -506,153 +496,6 @@ | |||
} | |||
return(false); | |||
} | |||
function stop(obj) { | |||
if (obj.style.color != "rgb(204, 204, 204)") { | |||
obj.target = '_blank' | |||
} else { | |||
return | |||
} | |||
} | |||
// 删除时用户确认 | |||
function assertDelete(obj) { | |||
if (obj.style.color == "rgb(204, 204, 204)") { | |||
return | |||
} else { | |||
var delId = obj.parentNode.id | |||
flag = 1; | |||
$('.ui.basic.modal') | |||
.modal({ | |||
onDeny: function() { | |||
flag = false | |||
}, | |||
onApprove: function() { | |||
document.getElementById(delId).submit() | |||
flag = true | |||
$('.alert').html('操作成功!').removeClass('alert-danger').addClass('alert-success').show().delay(1500).fadeOut(); | |||
}, | |||
onHidden: function() { | |||
if (flag == false) { | |||
$('.alert').html('您已取消操作').removeClass('alert-success').addClass('alert-danger').show().delay(1500).fadeOut(); | |||
} | |||
} | |||
}) | |||
.modal('show') | |||
} | |||
} | |||
function debugAgain(JobID,debugUrl){ | |||
if($('#' + JobID+ '-text').text()==="RUNNING"){ | |||
window.open(debugUrl+'debug') | |||
}else{ | |||
$.ajax({ | |||
type:"POST", | |||
url:debugUrl+'restart', | |||
data:$('#debugAgainForm-'+JobID).serialize(), | |||
success:function(res){ | |||
if(res.result_code==="0"){ | |||
if(res.job_id!==JobID){ | |||
location.reload() | |||
}else{ | |||
$('#' + JobID+'-icon').removeClass().addClass(res.status) | |||
$('#' + JobID+ '-text').text(res.status) | |||
$('#model-debug-'+JobID).removeClass('blue').addClass('disabled') | |||
$('#model-delete-'+JobID).removeClass('blue').addClass('disabled') | |||
$('#model-debug-'+JobID).text("调试").css("margin","0 1rem") | |||
} | |||
}else{ | |||
$('.alert').html(res.error_msg).removeClass('alert-success').addClass('alert-danger').show().delay(2000).fadeOut(); | |||
} | |||
}, | |||
error :function(res){ | |||
console.log(res) | |||
} | |||
}) | |||
} | |||
} | |||
function stopDebug(JobID,stopUrl){ | |||
$.ajax({ | |||
type:"POST", | |||
url:stopUrl, | |||
data:$('#stopForm-'+JobID).serialize(), | |||
success:function(res){ | |||
if(res.result_code==="0"){ | |||
$('#' + JobID+'-icon').removeClass().addClass(res.status) | |||
$('#' + JobID+ '-text').text(res.status) | |||
if(res.status==="STOPPED"){ | |||
$('#model-debug-'+JobID).removeClass('disabled').addClass('blue').text("再次调试").css("margin","0") | |||
$('#model-image-'+JobID).removeClass('blue').addClass('disabled') | |||
$('#stop-model-debug-'+JobID).removeClass('blue').addClass('disabled') | |||
$('#model-delete-'+JobID).removeClass('disabled').addClass('blue') | |||
} | |||
else{ | |||
$('#model-debug-'+JobID).removeClass('blue').addClass('disabled') | |||
$('#stop-model-debug-'+JobID).removeClass('blue').addClass('disabled') | |||
} | |||
}else{ | |||
$('.alert').html(res.error_msg).removeClass('alert-success').addClass('alert-danger').show().delay(2000).fadeOut(); | |||
} | |||
}, | |||
error :function(res){ | |||
console.log(res) | |||
} | |||
}) | |||
} | |||
// 加载任务状态 | |||
var timeid = window.setInterval(loadJobStatus, 15000); | |||
$(document).ready(loadJobStatus); | |||
function loadJobStatus() { | |||
$(".job-status").each((index, job) => { | |||
const jobID = job.dataset.jobid; | |||
const repoPath = job.dataset.repopath; | |||
const computeResource = job.dataset.resource | |||
const initArray = ['STOPPED','FAILED','START_FAILED','CREATE_FAILED','SUCCEEDED','UNAVAILABLE','DELETED','RESIZE_FAILED'] | |||
if (initArray.includes(job.textContent.trim())) { | |||
return | |||
} | |||
const diffResource = computeResource == "NPU" ? 'modelarts/notebook' : 'cloudbrain' | |||
$.get(`/api/v1/repos/${repoPath}/${diffResource}/${jobID}`, (data) => { | |||
const jobID = data.JobID | |||
const status = data.JobStatus | |||
if (status != job.textContent.trim()) { | |||
$('#' + jobID+'-icon').removeClass().addClass(status) | |||
$('#' + jobID+ '-text').text(status) | |||
} | |||
if(status==="RUNNING"){ | |||
$('#model-debug-'+jobID).removeClass('disabled').addClass('blue').text('调试').css("margin","0 1rem") | |||
$('#model-image-'+jobID).removeClass('disabled').addClass('blue') | |||
} | |||
if(status!=="RUNNING"){ | |||
// $('#model-debug-'+jobID).removeClass('blue') | |||
// $('#model-debug-'+jobID).addClass('disabled') | |||
$('#model-image-'+jobID).removeClass('blue').addClass('disabled') | |||
} | |||
if(["CREATING","STOPPING","WAITING","STARTING"].includes(status)){ | |||
$('#model-debug-'+jobID).removeClass('blue').addClass('disabled') | |||
} | |||
if(['STOPPED','FAILED','START_FAILED','CREATE_FAILED','SUCCEEDED'].includes(status)){ | |||
$('#model-debug-'+jobID).removeClass('disabled').addClass('blue').text('再次调试').css("margin","0") | |||
} | |||
if(["RUNNING","WAITING"].includes(status)){ | |||
$('#stop-model-debug-'+jobID).removeClass('disabled').addClass('blue') | |||
} | |||
if(["CREATING","STOPPING","STARTING","STOPPED","FAILED","START_FAILED","SUCCEEDED"].includes(status)){ | |||
$('#stop-model-debug-'+jobID).removeClass('blue').addClass('disabled') | |||
} | |||
if(status==="STOPPED" || status==="FAILED"|| status==="START_FAILED"){ | |||
$('#model-delete-'+jobID).removeClass('disabled').addClass('blue') | |||
}else{ | |||
$('#model-delete-'+jobID).removeClass('blue').addClass('disabled') | |||
} | |||
}).fail(function(err) { | |||
console.log(err); | |||
}); | |||
}); | |||
}; | |||
$(document).ready(function(){ | |||
dropdownValue = dropdownValue==="CPU%2FGPU"? 'CPU/GPU' : dropdownValue | |||
$('.default.text').text(dropdownValue) | |||
@@ -662,6 +505,7 @@ | |||
}) | |||
$('.ui.selection.dropdown').dropdown({ | |||
onChange:function(value){ | |||
location.href = `${url}/debugjob?debugListType=${value}` | |||
} | |||
}) | |||
@@ -672,9 +516,6 @@ | |||
.transition('fade') | |||
}) | |||
}) | |||
// 获取弹窗 | |||
var modal = document.getElementById('imageModal'); | |||
@@ -704,7 +545,6 @@ | |||
modal.style.display = "none"; | |||
} | |||
} | |||
// 显示弹窗,弹出相应的信息 | |||
function showmask() { | |||
var image_tag = !$('#image_tag').val() | |||
@@ -132,7 +132,7 @@ | |||
{{end}} --> | |||
{{if and .Repository.CanEnablePulls (.Permission.CanRead $.UnitTypePullRequests)}} | |||
<a class="{{if .PageIsPullList}}active{{end}} item" href="{{.RepoLink}}/pulls"> | |||
<a class="{{if or .PageIsComparePull .PageIsPullList}}active{{end}} item" href="{{.RepoLink}}/pulls"> | |||
{{svg "octicon-git-pull-request" 16}} {{.i18n.Tr "repo.pulls"}} <span class="ui {{if not .Repository.NumOpenPulls}}gray{{else}}blue{{end}} small label">{{.Repository.NumOpenPulls}}</span> | |||
</a> | |||
{{end}} | |||
@@ -255,9 +255,9 @@ | |||
{{end}} | |||
</div> | |||
<div class="ui six wide tablet four wide computer column"> | |||
<div id="repo-desc"> | |||
<h4 id="about-desc" class="ui header">简介</h4> | |||
<input type="hidden" id="edit-alias" value="{{.Repository.Alias}}"> | |||
<div id="repo-desc" data-IsAdmin= "{{.Permission.IsAdmin}}" data-IsArchived="{{.Repository.IsArchived}}" > | |||
<h4 id="about-desc" class="ui header">简介</h4> | |||
<input type="hidden" id="edit-alias" value="{{.Repository.Alias}}"> | |||
<p> | |||
{{if .Repository.DescriptionHTML}} | |||
<span class="description" style="word-break:break-all">{{.Repository.DescriptionHTML}}</span> | |||
@@ -185,40 +185,6 @@ | |||
</div> | |||
</div> | |||
</form> | |||
<script src="https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js"></script> | |||
<script> | |||
// $(document).ready(function(){ | |||
// var reward_value = $('.ui.form').form('get value', 'dog') | |||
// var reward_value = $('form').form('get value', 'dog') | |||
// console.log(reward_value) | |||
// alert(reward_value) | |||
// $('.ui.green.button').click(function(){ | |||
// $('.ui.form') | |||
// .form({ | |||
// // on: 'blur', | |||
// inline: true, | |||
// fields: { | |||
// dog: { | |||
// identifier: 'dog', | |||
// rules: [ | |||
// { | |||
// type: 'empty', | |||
// prompt: '请您输入项目奖励' | |||
// }, | |||
// { | |||
// type : 'integer[0..100]', | |||
// prompt : '项目奖励必须为整数,请您输入有效奖励金额' | |||
// } | |||
// ] | |||
// } | |||
// }, | |||
// onFailure: function(e){ | |||
// return false; | |||
// } | |||
// }); | |||
// }); | |||
// </script> | |||
{{if .PageIsComparePull}} | |||
<script>window.wipPrefixes = {{.PullRequestWorkInProgressPrefixes}}</script> | |||
{{end}} |
@@ -448,8 +448,8 @@ | |||
</div> | |||
</div> | |||
<script src="https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js"></script> | |||
<script> | |||
<!-- <script src="https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js"></script> --> | |||
<!-- <script> | |||
$(document) | |||
.ready(function() { | |||
@@ -477,4 +477,4 @@ | |||
}); | |||
}); | |||
}) | |||
</script> | |||
</script> --> |
@@ -113,11 +113,11 @@ | |||
<!-- 模型版本 --> | |||
<!-- href="{{$.RepoLink}}/modelmanage/show_model_info?name={{.ModelName}}" --> | |||
<div class="three wide column text center padding0"> | |||
<a id="{{.JobName}}" href="javascript:void(0);" data-variation="inverted" data-position="top center" data-content="{{$.i18n.Tr "repo.modelarts.infer_job.tooltip"}}" onclick="getModelInfo({{.ModelName}},{{.ModelVersion}},{{.JobName}})">{{.ModelName}} </a> <span style="font-size: 12px;">{{.ModelVersion}} </span> | |||
<a id="{{.JobName}}" class="goto_modelmanage" href="javascript:void(0);" data-variation="inverted" data-position="top center" data-content="{{$.i18n.Tr "repo.modelarts.infer_job.tooltip"}}" data-jobname={{.JobName}} data-modelname={{.ModelName}} data-version={{.ModelVersion}} data-repopath="{{$.RepoLink}}">{{.ModelName}} </a> <span style="font-size: 12px;">{{.ModelVersion}}</span> | |||
</div> | |||
<!-- 任务状态 --> | |||
<div class="two wide column text center padding0" > | |||
<span class="job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}" data-version="{{.VersionName}}"> | |||
<span class="job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}/modelarts/inference-job" data-jobid="{{.JobID}}" data-version="{{.VersionName}}"> | |||
<span><i id="{{.JobID}}-icon" style="vertical-align: middle;" class="{{.Status}}"></i><span id="{{.JobID}}-text" style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span> | |||
</span> | |||
</div> | |||
@@ -147,11 +147,11 @@ | |||
<div class="ui compact buttons"> | |||
{{$.CsrfTokenHtml}} | |||
{{if .CanDel}} | |||
<a style="padding: 0.5rem 1rem;" id="{{.JobID}}-stop" class="ui basic {{if eq .Status "KILLED" "FAILED" "START_FAILED" "KILLING" "COMPLETED"}}disabled {{else}} blue {{end}}button" onclick="stopVersion({{.VersionName}},{{.JobID}})"> | |||
<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"}}disabled {{else}} blue {{end}}button" data-repopath="{{$.RepoRelPath}}/modelarts/inference-job" data-jobid="{{.JobID}}" data-version="{{.VersionName}}"> | |||
{{$.i18n.Tr "repo.stop"}} | |||
</a> | |||
{{else}} | |||
<a style="padding: 0.5rem 1rem;" id="{{.JobID}}-stop" class="ui basic disabled button"> | |||
<a style="padding: 0.5rem 1rem;" id="ai-stop-{{.JobID}}" class="ui basic disabled button"> | |||
{{$.i18n.Tr "repo.stop"}} | |||
</a> | |||
{{end}} | |||
@@ -174,7 +174,7 @@ | |||
<div class="ui compact buttons"> | |||
{{$.CsrfTokenHtml}} | |||
{{if .CanDel}} | |||
<a style="padding: 0.5rem 1rem;margin-left:0.2rem" id="model-delete-{{.JobID}}" class="ui basic blue button" onclick="assertDelete(this,{{.VersionName}},{{.JobID}})" style="border-radius: .28571429rem;"> | |||
<a style="padding: 0.5rem 1rem;margin-left:0.2rem" id="model-delete-{{.JobID}}" class="ui basic ai_delete blue button" data-repopath="{{$.RepoRelPath}}/modelarts/inference-job/{{.JobID}}/del_version" data-version="{{.VersionName}}" style="border-radius: .28571429rem;"> | |||
{{$.i18n.Tr "repo.delete"}} | |||
</a> | |||
{{else}} | |||
@@ -221,118 +221,6 @@ | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
{{template "base/footer" .}} | |||
<script> | |||
// 加载任务状态 | |||
var timeid = window.setInterval(loadJobStatus, 15000); | |||
$(document).ready(loadJobStatus); | |||
function loadJobStatus() { | |||
$(".job-status").each((index, job) => { | |||
const jobID = job.dataset.jobid | |||
const repoPath = job.dataset.repopath | |||
const versionname = job.dataset.version | |||
const status_text = $(`#${jobID}-text`).text() | |||
const finalState = ['IMAGE_FAILED','SUBMIT_FAILED','DELETE_FAILED','KILLED','COMPLETED','FAILED','CANCELED','LOST','START_FAILED','SUBMIT_MODEL_FAILED','DEPLOY_SERVICE_FAILED','CHECK_FAILED'] | |||
if(finalState.includes(status_text)){ | |||
return | |||
} | |||
$.get(`/api/v1/repos/${repoPath}/modelarts/inference-job/${jobID}?version_name=${versionname}`, (data) => { | |||
const jobID = data.JobID | |||
const status = data.JobStatus | |||
const duration = data.JobDuration | |||
$('#duration-'+jobID).text(duration) | |||
if (status != job.textContent.trim()) { | |||
$('#' + jobID+'-icon').removeClass().addClass(status) | |||
$('#' + jobID+ '-text').text(status) | |||
finalState.includes(status) && $('#' + jobID + '-stop').removeClass('blue').addClass('disabled') | |||
} | |||
}).fail(function(err) { | |||
console.log(err); | |||
}); | |||
}); | |||
}; | |||
function getModelInfo(ID,version,JobName){ | |||
$.get("{{$.RepoLink}}/modelmanage/show_model_info_api?name="+ID,(data)=>{ | |||
if(data.length===0){ | |||
$(`#${JobName}`).popup('toggle') | |||
}else{ | |||
let versionData = data.filter((item)=>{ | |||
return item.Version === version | |||
}) | |||
if(versionData.length==0){ | |||
$(`#${JobName}`).popup('toggle') | |||
} | |||
else{ | |||
location.href = "{{$.RepoLink}}/modelmanage/show_model_info?name="+ID | |||
} | |||
} | |||
}) | |||
} | |||
function deleteVersion(version_name,jobID){ | |||
const url = '/api/v1/repos/{{$.RepoRelPath}}/modelarts/inference-job/'+jobID+'/del_version' | |||
$.post(url,{version_name:version_name},(data)=>{ | |||
if(data.StatusOK===0){ | |||
location.reload() | |||
} | |||
}).fail(function(err) { | |||
console.log(err); | |||
}); | |||
} | |||
function stopVersion(version_name,jobID){ | |||
const url = '/api/v1/repos/{{$.RepoRelPath}}/modelarts/inference-job/'+jobID+'/stop_version' | |||
$.post(url,{version_name:version_name},(data)=>{ | |||
if(data.StatusOK===0){ | |||
$('#'+version_name+'-stop').removeClass('blue') | |||
$('#'+version_name+'-stop').addClass('disabled') | |||
refreshStatus(version_name,jobID) | |||
} | |||
}).fail(function(err) { | |||
console.log(err); | |||
}); | |||
} | |||
function refreshStatus(version_name,jobID){ | |||
const url = '/api/v1/repos/{{$.RepoRelPath}}/modelarts/inference-job/'+jobID+'?version_name='+version_name | |||
$.get(url,(data)=>{ | |||
$(`#${jobID}-icon`).attr("class",data.JobStatus) | |||
// detail status and duration | |||
$(`#${jobID}-text`).text(data.JobStatus) | |||
}).fail(function(err) { | |||
console.log(err); | |||
}); | |||
} | |||
function assertDelete(obj,version_name,jobID) { | |||
if (obj.style.color == "rgb(204, 204, 204)") { | |||
return | |||
} else { | |||
// var delId = obj.parentNode.id | |||
flag = 1; | |||
$('.ui.basic.modal') | |||
.modal({ | |||
onDeny: function() { | |||
flag = false | |||
}, | |||
onApprove: function() { | |||
// document.getElementById(delId).submit() | |||
deleteVersion(version_name,jobID) | |||
flag = true | |||
}, | |||
onHidden: function() { | |||
if (flag == false) { | |||
$('.alert').html('您已取消操作').removeClass('alert-success').addClass('alert-danger').show().delay(1500).fadeOut(); | |||
} | |||
} | |||
}) | |||
.modal('show') | |||
} | |||
} | |||
</script> |
@@ -27,7 +27,7 @@ | |||
} | |||
.nowrap { | |||
.nowrapx { | |||
white-space: nowrap !important; | |||
} | |||
</style> | |||
@@ -140,7 +140,7 @@ | |||
</select> | |||
</div> | |||
<div class="eight wide field" id="engine_name"> | |||
<select class="ui fluid selection dropdown nowrap" id="trainjob_engine_versions" name="engine_id" style="white-space: nowrap;"> | |||
<select class="ui fluid selection dropdown nowrapx" id="trainjob_engine_versions" name="engine_id" style="white-space: nowrap;!"> | |||
{{range .engine_versions}} | |||
<option name="engine_id" value="{{.ID}}">{{.Value}}</option> | |||
{{end}} | |||
@@ -469,13 +469,13 @@ td, th { | |||
</div> | |||
{{template "base/footer" .}} | |||
<script> | |||
console.log({{.task}}) | |||
$(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('/') | |||
@@ -621,7 +621,12 @@ function renderDir(data,version_name){ | |||
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{ | |||
html += `<a href="${location.href}/result_download?version_name=${version_name}&file_name=${data.Dirs[i].FileName}&parent_dir=${data.Dirs[i].ParenDir}">` | |||
if(downlaodFlag){ | |||
html += `<a href="${location.href}/result_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>' | |||
@@ -19,7 +19,7 @@ | |||
<div class="repository new repo ui middle very relaxed page grid"> | |||
<div class="column"> | |||
{{template "base/alert" .}} | |||
<div class="ui positive message" id="messageInfo"> | |||
<div class="ui negative message" id="messageInfo"> | |||
<p></p> | |||
</div> | |||
<form class="ui form" id="form_id" action="{{.Link}}" method="post"> | |||
@@ -51,7 +51,16 @@ | |||
<input name="job_name" id="cloudbrain_job_name" placeholder="任务名称" value="{{.job_name}}" tabindex="3" autofocus required maxlength="255" onkeyup="this.value=this.value.replace(/[, ]/g,'')"> | |||
</div> | |||
<!-- <div class="inline field"> | |||
<div class="inline required field"> | |||
<label>镜像</label> | |||
<select id="cloudbrain_image" class="ui search dropdown" placeholder="选择镜像" style='width:385px' name="image_id"> | |||
{{range .images}} | |||
<option name="image_id" value="{{.Id}}">{{.Value}}</option> | |||
{{end}} | |||
</select> | |||
</div> | |||
<div class="inline field"> | |||
<label>数据集</label> | |||
<input type="text" list="cloudbrain_dataset" placeholder="选择数据集" name="" id="answerInput" autofocus maxlength="36"> | |||
<datalist id="cloudbrain_dataset" class="ui search" style='width:385px' name="attachment"> | |||
@@ -62,7 +71,7 @@ | |||
<input type="hidden" name="attachment" id="answerInput-hidden"> | |||
</div> | |||
<div class="inline required field"> | |||
<!--<div class="inline required field"> | |||
<label>工作环境</label> | |||
<input name="de" id="cloudbrain_de" value="{{.env}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | |||
</div> | |||
@@ -92,7 +101,7 @@ | |||
<button class="ui green button"> | |||
{{.i18n.Tr "repo.cloudbrain.new"}} | |||
</button> | |||
<a class="ui button cancel" href="">{{.i18n.Tr "repo.cloudbrain.cancel"}}</a> | |||
<a class="ui button cancel" href="{{.RepoLink}}/debugjob?debugListType=all">{{.i18n.Tr "repo.cloudbrain.cancel"}}</a> | |||
</div> | |||
</div> | |||
</form> | |||
@@ -162,11 +171,8 @@ | |||
if(option.innerText===inputValue){ | |||
hiddenInput.value = option.getAttribute('data-value'); | |||
break | |||
break | |||
} | |||
} | |||
}) | |||
</script> |
@@ -11,7 +11,7 @@ | |||
{{.i18n.Tr "repo.cloudbrain"}} | |||
</a> | |||
<div class="divider"> / </div> | |||
<a class="section backTodeBug" href="{{.RepoLink}}/debugjob?debugListType=NPU"> | |||
<a class="section backTodeBug" href="{{.RepoLink}}/debugjob?debugListType={{if eq $.debugListType "NPU"}}NPU{{else if eq $.debugListType "CPU/GPU"}}CPU/GPU{{else}}all{{end}}"> | |||
{{$.i18n.Tr "repo.modelarts.notebook"}} | |||
</a> | |||
<div class="divider"> / </div> | |||
@@ -27,7 +27,7 @@ | |||
{{end}} | |||
</div> | |||
<div class="ui green segment"> | |||
<p>任务结果:</p> | |||
<p>任务详情:</p> | |||
{{with .result}} | |||
<table class="ui celled striped table"> | |||
<tbody> | |||
@@ -35,6 +35,18 @@ | |||
<td class="four wide"> 状态 </td> | |||
<td> {{.Status}} </td> | |||
</tr> | |||
<tr> | |||
<td> 描述 </td> | |||
<td style="max-width: 480px; word-wrap:break-word">{{$.task.Description}}</td> | |||
</tr> | |||
<tr> | |||
<td> 镜像名称 </td> | |||
<td>{{$.task.Image}}</td> | |||
</tr> | |||
<tr> | |||
<td> 数据集下载地址 </td> | |||
<td style="max-width: 480px; word-wrap:break-word">{{$.datasetDownloadLink}}</td> | |||
</tr> | |||
<tr> | |||
<td> 开始时间 </td> | |||
<td>{{.CreateTime}}</td> | |||
@@ -113,7 +113,7 @@ | |||
</div> | |||
<!-- 任务状态 --> | |||
<div class="two wide column padding0" style="padding-left: 2.2rem !important;"> | |||
<span class="job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}" data-version="{{.VersionName}}"> | |||
<span class="job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}/modelarts/train-job" data-jobid="{{.JobID}}" data-version="{{.VersionName}}"> | |||
<span><i id="{{.JobID}}-icon" style="vertical-align: middle;" class="{{.Status}}"></i><span id="{{.JobID}}-text" style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span> | |||
</span> | |||
</div> | |||
@@ -143,11 +143,11 @@ | |||
<div class="ui compact buttons"> | |||
{{$.CsrfTokenHtml}} | |||
{{if .CanDel}} | |||
<a style="padding: 0.5rem 1rem;" id="{{.JobID}}-stop" class="ui basic {{if eq .Status "KILLED" "FAILED" "START_FAILED" "KILLING" "COMPLETED"}}disabled {{else}} blue {{end}}button" onclick="stopVersion({{.VersionName}},{{.JobID}})"> | |||
<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"}}disabled {{else}} blue {{end}}button" data-repopath="{{$.RepoRelPath}}/modelarts/train-job" data-jobid="{{.JobID}}" data-version="{{.VersionName}}"> | |||
{{$.i18n.Tr "repo.stop"}} | |||
</a> | |||
{{else}} | |||
<a style="padding: 0.5rem 1rem;" id="{{.JobID}}-stop" class="ui basic disabled button"> | |||
<a style="padding: 0.5rem 1rem;" id="ai-stop-{{.JobID}}" class="ui basic disabled button"> | |||
{{$.i18n.Tr "repo.stop"}} | |||
</a> | |||
{{end}} | |||
@@ -157,7 +157,7 @@ | |||
<form class="ui compact buttons" id="delForm-{{.JobID}}" action="{{$.Link}}/{{.JobID}}/del" method="post"> | |||
{{$.CsrfTokenHtml}} | |||
{{if .CanDel}} | |||
<a style="padding: 0.5rem 1rem;margin-left:0.2rem" id="model-delete-{{.JobID}}" class="ui basic blue button" onclick="assertDelete(this)" style="border-radius: .28571429rem;"> | |||
<a style="padding: 0.5rem 1rem;margin-left:0.2rem" id="model-delete-{{.JobID}}" class="ui basic ai_delete blue button" style="border-radius: .28571429rem;"> | |||
{{$.i18n.Tr "repo.delete"}} | |||
</a> | |||
{{else}} | |||
@@ -204,162 +204,5 @@ | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
{{template "base/footer" .}} | |||
<script> | |||
console.log({{.Tasks}}) | |||
// 调试和评分新开窗口 | |||
function stop(obj) { | |||
if (obj.style.color != "rgb(204, 204, 204)") { | |||
obj.target = '_blank' | |||
} else { | |||
return | |||
} | |||
} | |||
// 删除时用户确认 | |||
function assertDelete(obj) { | |||
if (obj.style.color == "rgb(204, 204, 204)") { | |||
return | |||
} else { | |||
var delId = obj.parentNode.id | |||
flag = 1; | |||
$('.ui.basic.modal') | |||
.modal({ | |||
onDeny: function() { | |||
flag = false | |||
}, | |||
onApprove: function() { | |||
document.getElementById(delId).submit() | |||
flag = true | |||
}, | |||
onHidden: function() { | |||
if (flag == false) { | |||
$('.alert').html('您已取消操作').removeClass('alert-success').addClass('alert-danger').show().delay(1500).fadeOut(); | |||
} | |||
} | |||
}) | |||
.modal('show') | |||
} | |||
} | |||
function runtime(time){ | |||
if(time){ | |||
let hours = time/3600000<10 ? "0"+parseInt(time/3600000):parseInt(time/3600000) | |||
let miuns = time%3600000/60000<10 ? "0"+parseInt(time%3600000/60000):parseInt(time%3600000/60000) | |||
let seconds = time%60000/1000<10 ? "0"+parseInt(time%60000/1000):parseInt(time%60000/1000) | |||
return hours + ":" + miuns + ":" + seconds | |||
}else{ | |||
return "00:00:00" | |||
} | |||
} | |||
// 加载任务状态 | |||
var timeid = window.setInterval(loadJobStatus, 15000); | |||
$(document).ready(loadJobStatus); | |||
function loadJobStatus() { | |||
$(".job-status").each((index, job) => { | |||
const jobID = job.dataset.jobid | |||
const repoPath = job.dataset.repopath | |||
const versionname = job.dataset.version | |||
const status_text = $(`#${jobID}-text`).text() | |||
const finalState = ['IMAGE_FAILED','SUBMIT_FAILED','DELETE_FAILED','KILLED','COMPLETED','FAILED','CANCELED','LOST','START_FAILED','SUBMIT_MODEL_FAILED','DEPLOY_SERVICE_FAILED','CHECK_FAILED'] | |||
if(finalState.includes(status_text)){ | |||
return | |||
} | |||
$.get(`/api/v1/repos/${repoPath}/modelarts/train-job/${jobID}?version_name=${versionname}`, (data) => { | |||
const jobID = data.JobID | |||
const status = data.JobStatus | |||
const duration = data.JobDuration | |||
$('#duration-'+jobID).text(duration) | |||
if (status != job.textContent.trim()) { | |||
$('#' + jobID+'-icon').removeClass().addClass(status) | |||
$('#' + jobID+ '-text').text(status) | |||
finalState.includes(status) && $('#' + jobID + '-stop').removeClass('blue').addClass('disabled') | |||
} | |||
}).fail(function(err) { | |||
console.log(err); | |||
}); | |||
}); | |||
}; | |||
// 获取弹窗 | |||
var modal = document.getElementById('imageModal'); | |||
// 打开弹窗的按钮对象 | |||
var btns = document.getElementsByClassName("imageBtn"); | |||
// 获取 <span> 元素,用于关闭弹窗 | |||
var spans = document.getElementsByClassName('close'); | |||
// 点击按钮打开弹窗 | |||
for (i = 0; i < btns.length; i++) { | |||
btns[i].onclick = function() { | |||
modal.style.display = "block"; | |||
} | |||
} | |||
// 点击 <span> (x), 关闭弹窗 | |||
for (i = 0; i < spans.length; i++) { | |||
spans[i].onclick = function() { | |||
modal.style.display = "none"; | |||
} | |||
} | |||
// 在用户点击其他地方时,关闭弹窗 | |||
window.onclick = function(event) { | |||
if (event.target == modal) { | |||
modal.style.display = "none"; | |||
} | |||
} | |||
function stopVersion(version_name,jobID){ | |||
const url = '/api/v1/repos/{{$.RepoRelPath}}/modelarts/train-job/'+jobID+'/stop_version' | |||
$.post(url,{version_name:version_name},(data)=>{ | |||
if(data.StatusOK===0){ | |||
$('#'+version_name+'-stop').removeClass('blue') | |||
$('#'+version_name+'-stop').addClass('disabled') | |||
refreshStatus(version_name,jobID) | |||
} | |||
}).fail(function(err) { | |||
console.log(err); | |||
}); | |||
} | |||
function refreshStatus(version_name,jobID){ | |||
const url = '/api/v1/repos/{{$.RepoRelPath}}/modelarts/train-job/'+jobID+'?version_name='+version_name | |||
$.get(url,(data)=>{ | |||
$(`#${jobID}-icon`).attr("class",data.JobStatus) | |||
// detail status and duration | |||
$(`#${jobID}-text`).text(data.JobStatus) | |||
}).fail(function(err) { | |||
console.log(err); | |||
}); | |||
} | |||
// 显示弹窗,弹出相应的信息 | |||
function showmask() { | |||
$('#imageModal').css('display', 'none') | |||
$('#mask').css('display', 'block') | |||
$("iframe[name=iframeContent]").on("load", function() { | |||
var responseText = $("iframe")[0].contentDocument.body.getElementsByTagName("pre")[0].innerHTML; | |||
var json1 = JSON.parse(responseText) | |||
$('#mask').css('display', 'none') | |||
parent.location.href | |||
if (json1.result_code === "0") { | |||
$('.alert').html('操作成功!').removeClass('alert-danger').addClass('alert-success').show().delay(1500).fadeOut(); | |||
} else { | |||
$('.alert').html(json1.error_msg).removeClass('alert-success').addClass('alert-danger').show().delay(5000).fadeOut(); | |||
} | |||
}) | |||
} | |||
</script> | |||
{{template "base/footer" .}} |
@@ -518,6 +518,7 @@ td, th { | |||
</div> | |||
</div> | |||
</div> | |||
{{template "base/footer" .}} | |||
<script> | |||
@@ -532,6 +533,7 @@ td, th { | |||
let userName | |||
let repoPath | |||
let jobID | |||
let downlaodFlag = {{$.canDownload }} | |||
$(document).ready(function(){ | |||
let url = window.location.href; | |||
let urlArr = url.split('/') | |||
@@ -560,16 +562,13 @@ td, th { | |||
$('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() | |||
createModelName() | |||
}, | |||
onHide:function(){ | |||
document.getElementById("formId").reset(); | |||
$('.ui.dimmer').css({"background-color":""}) | |||
$('.ui.error.message').text() | |||
$('.ui.error.message').css('display','none') | |||
} | |||
}) | |||
.modal('show') | |||
@@ -787,7 +786,12 @@ td, th { | |||
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{ | |||
html += `<a href="${location.href}/model_download?version_name=${version_name}&file_name=${data.Dirs[i].FileName}&parent_dir=${data.Dirs[i].ParenDir}">` | |||
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>' | |||
@@ -186,7 +186,6 @@ | |||
<div class='ui breadcrumb model_file_bread' id='file_breadcrumb'> | |||
<div class="active section"></div> | |||
<div class="divider"> / </div> | |||
</div> | |||
<div id="dir_list"> | |||
@@ -52,6 +52,4 @@ | |||
</div> | |||
<script> | |||
console.log({{$.Err_Alias}}) | |||
</script> | |||
@@ -0,0 +1,15 @@ | |||
<!-- 头部导航栏 --> | |||
{{template "base/head" .}} | |||
<div class="alert" style="top: 0;"></div> | |||
<div class="repository release dataset-list view"> | |||
{{template "repo/header" .}} | |||
{{template "base/alert" .}} | |||
<input id="WxAutorize-qrcode" type="hidden" value="{{.qrcode.Ticket}}"> | |||
<div id="WxAutorize"> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
{{template "base/footer" .}} | |||
@@ -0,0 +1,70 @@ | |||
{{template "base/head_home" .}} | |||
<div class="ui container"> | |||
<h1 class="ui center am-pt-30 am-pb-20">OpenI启智社区AI协作平台使用协议</h1> | |||
<div class="ui divider am-pb-10"></div> | |||
<p> | |||
OpenI启智社区AI协作平台作为新一代人工智能领域开源开放开发协作平台,不仅为用户提供代码托管与数据集管理等服务,同时提供开发者所需的计算算力资源,一个良好的开发环境对用户、组织和项目都尤为重要。 | |||
</p> | |||
<p> | |||
为了保障用户权益,维护平台正常秩序,实现平台规范化运营,给开发者提供一个良好的开发环境,您不得在本平台进行恶意攻击、挖矿等任何违法或扰乱平台秩序的行为。<strong>一经发现,平台有权变更、暂停或终止您对平台服务的使用而无须事先声明,包括但不限于限制您使用平台服务的次数与资源、账号永久封闭等。</strong> | |||
</p> | |||
<h2> | |||
AI协作平台使用协议及违规处罚 | |||
</h2> | |||
<p> | |||
1. 您需保证对平台服务的使用不违反国家各项法律法规的规定,且不侵害任何第三方权益,如因此造成任何后果及损失,由您自行承担全部责任。 | |||
</p> | |||
<p> | |||
<strong>2. 禁止行为</strong> | |||
</p> | |||
<p> | |||
您充分理解并同意,您在使用平台服务时,应当遵守所有中华人民共和国的法律、法规、规章制度、规范、政策、行政命令、强制标准及行业标准等(统称为“法律法规”)。除非法律法规允许且启智社区事先书面许可,您不得从事以下活动,也不得同意、授权或指示任何第三人从事包括但不限于以下内容的活动: | |||
<br> | |||
(1) 对平台服务进行挖矿、逆向工程、反编辑等恶意行为损坏平台服务相关内容与数据,或不正当手段获取原始数据和其他数据等;<br> | |||
(2) 对平台服务或者平台服务运行过程中释放出的任何数据或其他数据及平台服务运行过程中的交互数据进行复制、更改、修改等操作,包括但不限于使用插件、外挂或非经授权的第三方工具或服务接入平台服务和相关系统; | |||
<br> | |||
(3) 宣扬或提供关于非法行为的说明信息、宣扬针对任何团体或个人的人身伤害或传播任何病毒、蠕虫、缺陷、特洛伊木马或其他具有破坏性的内容等;<br> | |||
(4) 删除本平台服务中包含的任何版权声明、商标声明或其他所有权声明;包括但不限于任何有损本平台一切相关知识产权的行为;<br> | |||
(5) 创造任何网站或应用程序以重现或复制平台服务或本平台。 | |||
</p> | |||
<p> | |||
<strong>3. 遵守法律规范</strong> | |||
</p> | |||
<p> | |||
您不得利用平台服务上传、上载、发布、发表、传播任何违法内容,包括但不限于您制作、复制、发布、传播下列信息: | |||
</p> | |||
<p> | |||
(1)煽动抗拒、破坏宪法和法律、行政法规实施<br> | |||
(2)煽动颠覆国家政权,推翻社会主义制度<br> | |||
(3)煽动分裂国家、破坏国家统一<br> | |||
(4)煽动民族仇恨、民族歧视、破坏民族团结<br> | |||
(5)捏造或者歪曲事实、散布谣言,扰乱社会秩序<br> | |||
(6)宣扬封建迷信、淫秽、色情、赌博、暴力、教唆犯罪<br> | |||
(7)公然侮辱他人或者捏造事实诽谤他人<br> | |||
(8)损害国家机关信誉或有损国家领导人荣誉<br> | |||
(9)其他违反宪法和法律、行政法规<br> | |||
(10)含有虚假、有害、胁迫、侵害他人隐私、骚扰、侵害、中伤、粗俗、猥亵或其他道德上令人反感的内容<br> | |||
(11)含有中国法律、法规、规章、条例以及任何具有法律效力之规范所限制或禁止的其他内容 | |||
</p> | |||
<p> | |||
一经发现,平台将按照国家有关规定,及时删除网站中含有上述内容的地址、目录,并保留原始记录,在二十四小时之内向公安机关报告。 | |||
</p> | |||
<p> | |||
<strong>4. 保障网络信息安全</strong> | |||
</p> | |||
<p> | |||
您不得利用平台服务从事以下危害计算机网络信息安全的活动:<br> | |||
(1) 未经允许,进入计算机信息网络或者使用计算机信息网络资源的;<br> | |||
(2) 未经允许,对计算机信息网络功能进行删除、修改或者增加的;<br> | |||
(3) 未经允许,对进入计算机信息网络中存储、处理或者传输的数据和应用程序进行删除、修改或者增加的;<br> | |||
(4) 故意制作、传播计算机病毒等破坏性程序的;<br> | |||
(5) 其他危害计算机信息网络安全的行为。 | |||
</p> | |||
<p> | |||
5. 您充分理解并同意,您对自己使用平台服务的一切行为及由此产生的一切结果负责,包括但不限于您所发表的任何内容、提供的任何服务以及由此产生的任何后果。您应对平台服务的内容应自行判断并决定是否使用,并承担因使用平台服务及其相关内容而引起的所有风险,包括因对平台服务及其内容的真实性、完整性、准确性、及时性及实用性的依赖而产生的风险。平台不对此提供任何担保和保证,不对因前述风险而导致的任何后果或损失对您承担责任。 | |||
</p> | |||
<p> | |||
<strong>如果您违反了法律法规或本协议,OpenI启智社区有权依据合理判断对违反法律法规或本协议的行为作出处理,并保留对该违反行为采取法律所能提供的所有补救手段的权利。OpenI启智社区有权对违反法律法规及本协议的任何用户调查并采取适当的法律行动,包括但不限于民事诉讼等。OpenI启智社区有权根据违法违规行为的严重程度,将上述违法违规行为的线索和您的个人信息报告司法机关或其他执法机关,并配合司法机关或其他执法机关进行的调查、听证、起诉等。您应当自行承担由此产生的任何法律责任。</strong> | |||
</p> | |||
</div> | |||
{{template "base/footer" .}} |
@@ -69,7 +69,21 @@ | |||
{{$.i18n.Tr "action.reject_pull_request" .GetRepoLink $index .ShortRepoFullDisplayName | Str2html}} | |||
{{else if eq .GetOpType 23}} | |||
{{ $index := index .GetIssueInfos 0}} | |||
{{$.i18n.Tr "action.comment_pull" .GetRepoLink $index .ShortRepoFullDisplayName | Str2html}} | |||
{{$.i18n.Tr "action.comment_pull" .GetRepoLink $index .ShortRepoPath | Str2html}} | |||
{{else if eq .GetOpType 24}} | |||
{{$.i18n.Tr "action.upload_dataset" .GetRepoLink .Content .RefName | Str2html}} | |||
{{else if eq .GetOpType 25}} | |||
{{$.i18n.Tr "action.task_gpudebugjob" .GetRepoLink .RefName .RefName | Str2html}} | |||
{{else if eq .GetOpType 26}} | |||
{{$.i18n.Tr "action.task_npudebugjob" .GetRepoLink .Content .RefName | Str2html}} | |||
{{else if eq .GetOpType 27}} | |||
{{$.i18n.Tr "action.task_trainjob" .GetRepoLink .Content .RefName | Str2html}} | |||
{{else if eq .GetOpType 28}} | |||
{{$.i18n.Tr "action.task_inferencejob" .GetRepoLink .Content .RefName | Str2html}} | |||
{{else if eq .GetOpType 29}} | |||
{{$.i18n.Tr "action.task_benchmark" .GetRepoLink .RefName .RefName | Str2html}} | |||
{{else if eq .GetOpType 30}} | |||
{{$.i18n.Tr "action.task_createmodel" .GetRepoLink .RefName .RefName | Str2html}} | |||
{{end}} | |||
</p> | |||
{{if or (eq .GetOpType 5) (eq .GetOpType 18)}} | |||
@@ -101,7 +115,23 @@ | |||
</div> | |||
</div> | |||
<div class="ui two wide right aligned column"> | |||
<span class="text grey">{{svg (printf "octicon-%s" (ActionIcon .GetOpType)) 32}}</span> | |||
{{if eq .GetOpType 24}} | |||
<span class="text grey"><i class="ri-uninstall-line icon big"></i></span> | |||
{{else if eq .GetOpType 25}} | |||
<span class="text grey"><i class="ri-voice-recognition-line icon big"></i></span> | |||
{{else if eq .GetOpType 26}} | |||
<span class="text grey"><i class="ri-voice-recognition-line icon big"></i></span> | |||
{{else if eq .GetOpType 27}} | |||
<span class="text grey"><i class="ri-character-recognition-line icon big"></i></span> | |||
{{else if eq .GetOpType 28}} | |||
<span class="text grey"><i class="ri-haze-2-line icon big"></i></span> | |||
{{else if eq .GetOpType 29}} | |||
<span class="text grey"><i class="ri-vip-crown-line icon big"></i></span> | |||
{{else if eq .GetOpType 30}} | |||
<span class="text grey"><i class="ri-picture-in-picture-exit-line icon big"></i></span> | |||
{{else}} | |||
<span class="text grey">{{svg (printf "octicon-%s" (ActionIcon .GetOpType)) 32}}</span> | |||
{{end}} | |||
</div> | |||
</div> | |||
<div class="ui divider"></div> | |||
@@ -208,6 +208,3 @@ | |||
</div> | |||
</div> | |||
{{template "base/footer" .}} | |||
<script> | |||
console.log({{.Issues}}) | |||
</script> |
@@ -117,6 +117,3 @@ | |||
</div> | |||
</div> | |||
{{template "base/footer" .}} | |||
<script> | |||
console.log({{.Milestones}}) | |||
</script> |
@@ -141,6 +141,7 @@ | |||
{{if eq .TabName "activity"}} | |||
{{if .EnableHeatmap}} | |||
<div id="user-heatmap" style="padding-right: 40px"> | |||
<activity-heatmap :locale="locale" :suburl="suburl" :user="heatmapUser"> | |||
<div slot="loading"> | |||
@@ -1,6 +1,7 @@ | |||
{{template "base/head" .}} | |||
<div class="user settings profile"> | |||
{{template "user/settings/navbar" .}} | |||
<div class="alert" style="top: 0;"></div> | |||
<div class="ui container"> | |||
{{template "base/alert" .}} | |||
<h4 class="ui top attached header"> | |||
@@ -102,6 +103,71 @@ | |||
</div> | |||
</form> | |||
</div> | |||
<h4 class="ui top attached header"> | |||
{{$.i18n.Tr "settings.wechat_bind"}} | |||
</h4> | |||
{{if not .SignedUser.IsBindWechat}} | |||
<div class="ui attached segment"> | |||
<a href="/authentication/wechat/bind?redirect_to=/user/settings" class="ui green button">{{$.i18n.Tr "settings.bind_wechat"}}</a> | |||
</div> | |||
{{else}} | |||
<div class="ui attached segment"> | |||
<table class="ui celled striped table provider titleless"> | |||
<thead> | |||
<th class="center aligned"> | |||
{{$.i18n.Tr "settings.bind_account_information"}} | |||
</th> | |||
<th class="center aligned"> | |||
{{$.i18n.Tr "settings.bind_time"}} | |||
</th> | |||
<th class="center aligned"> | |||
{{$.i18n.Tr "repo.cloudbrain_operate"}} | |||
</th> | |||
</thead> | |||
<tbody> | |||
<td class="center aligned"> | |||
{{$.i18n.Tr "settings.wechat"}} | |||
</td> | |||
<td class="center aligned"> | |||
{{TimeSinceUnix1 .SignedUser.WechatBindUnix}} | |||
<td class="center aligned"> | |||
<div> | |||
<a class="ui inverted orange button " onclick="showcreate(this)" href="javascript: void(0)">{{$.i18n.Tr "settings.unbind_wc"}}</a> | |||
</div> | |||
</td> | |||
</tbody> | |||
</table> | |||
</div> | |||
{{end}} | |||
</div> | |||
<div class="ui mini modal wx-unbind"> | |||
<div class="header">{{$.i18n.Tr "settings.unbind_wechat"}}</div> | |||
<div class="content"> | |||
<p>{{$.i18n.Tr "settings.unbind_computing"}}</p> | |||
</div> | |||
<div class="actions"> | |||
<div class="ui blank cancel button">{{$.i18n.Tr "cancel"}}</div> | |||
<div class="ui green approve button">{{$.i18n.Tr "repo.confirm_choice"}}</div> | |||
</div> | |||
</div> | |||
</div> | |||
{{template "base/footer" .}} | |||
<script> | |||
function showcreate(obj){ | |||
const {csrf} = window.config | |||
$('.ui.modal.wx-unbind') | |||
.modal({ | |||
onShow:function(){ | |||
}, | |||
onHide:function(){ | |||
}, | |||
onApprove:function($element){ | |||
$.post('/authentication/wechat/unbind',{_csrf:csrf},(data)=>{ | |||
$('.alert').html('解绑成功!').removeClass('alert-danger').addClass('alert-success').show().delay(1500).fadeOut(); | |||
window.location.href = '/user/settings' | |||
}) | |||
} | |||
}) | |||
.modal('show') | |||
} | |||
</script> |