@@ -565,6 +565,17 @@ type FlavorInfo struct { | |||||
Desc string `json:"desc"` | Desc string `json:"desc"` | ||||
} | } | ||||
type SpecialPools struct { | |||||
Pools []*SpecialPool `json:"pools"` | |||||
} | |||||
type SpecialPool struct { | |||||
Org string `json:"org"` | |||||
Type string `json:"type"` | |||||
IsExclusive bool `json:"isExclusive"` | |||||
Pool []*GpuInfo `json:"pool"` | |||||
JobType []string `json:"jobType"` | |||||
} | |||||
type ImageInfosModelArts struct { | type ImageInfosModelArts struct { | ||||
ImageInfo []*ImageInfoModelArts `json:"image_info"` | ImageInfo []*ImageInfoModelArts `json:"image_info"` | ||||
} | } | ||||
@@ -15,13 +15,9 @@ type CustomMigrationStatic struct { | |||||
Migrate func(*xorm.Engine, *xorm.Engine) error | Migrate func(*xorm.Engine, *xorm.Engine) error | ||||
} | } | ||||
var customMigrations = []CustomMigration{ | |||||
{"Custom v1 Topic struct change to support chinese", syncTopicStruct}, | |||||
} | |||||
var customMigrations []CustomMigration | |||||
var customMigrationsStatic = []CustomMigrationStatic{ | |||||
{"update issue_fixed_rate to 1 if num_issues is 0 ", updateIssueFixedRate}, | |||||
} | |||||
var customMigrationsStatic []CustomMigrationStatic | |||||
func MigrateCustom(x *xorm.Engine) { | func MigrateCustom(x *xorm.Engine) { | ||||
@@ -181,6 +181,7 @@ func SearchDatasetCondition(opts *SearchDatasetOptions) builder.Cond { | |||||
if len(opts.DatasetIDs) > 0 { | if len(opts.DatasetIDs) > 0 { | ||||
subCon := builder.NewCond() | subCon := builder.NewCond() | ||||
subCon = subCon.And(builder.In("dataset.id", opts.DatasetIDs)) | subCon = subCon.And(builder.In("dataset.id", opts.DatasetIDs)) | ||||
subCon = generateFilterCond(opts, subCon) | |||||
cond = cond.Or(subCon) | cond = cond.Or(subCon) | ||||
} | } | ||||
@@ -460,5 +461,12 @@ func GetCollaboratorDatasetIdsByUserID(userID int64) []int64 { | |||||
_ = x.Table("dataset").Join("INNER", "collaboration", "dataset.repo_id = collaboration.repo_id and collaboration.mode>0 and collaboration.user_id=?", userID). | _ = x.Table("dataset").Join("INNER", "collaboration", "dataset.repo_id = collaboration.repo_id and collaboration.mode>0 and collaboration.user_id=?", userID). | ||||
Cols("dataset.id").Find(&datasets) | Cols("dataset.id").Find(&datasets) | ||||
return datasets | return datasets | ||||
} | |||||
func GetTeamDatasetIdsByUserID(userID int64) []int64 { | |||||
var datasets []int64 | |||||
_ = x.Table("dataset").Join("INNER", "team_repo", "dataset.repo_id = team_repo.repo_id"). | |||||
Join("INNER", "team_user", "team_repo.team_id=team_user.team_id and team_user.uid=?", userID). | |||||
Cols("dataset.id").Find(&datasets) | |||||
return datasets | |||||
} | } |
@@ -2749,15 +2749,10 @@ func ReadLatestFileInRepo(userName, repoName, refName, treePath string) (*RepoFi | |||||
log.Error("ReadLatestFileInRepo: Close: %v", err) | log.Error("ReadLatestFileInRepo: Close: %v", err) | ||||
} | } | ||||
}() | }() | ||||
buf := make([]byte, 1024) | |||||
n, _ := reader.Read(buf) | |||||
if n >= 0 { | |||||
buf = buf[:n] | |||||
} | |||||
d, _ := ioutil.ReadAll(reader) | |||||
commitId := "" | commitId := "" | ||||
if blob != nil { | if blob != nil { | ||||
commitId = fmt.Sprint(blob.ID) | commitId = fmt.Sprint(blob.ID) | ||||
} | } | ||||
return &RepoFile{CommitId: commitId, Content: buf}, nil | |||||
return &RepoFile{CommitId: commitId, Content: d}, nil | |||||
} | } |
@@ -955,6 +955,8 @@ func CounDataByDateAndReCount(wikiCountMap map[string]int, startTime time.Time, | |||||
return err | return err | ||||
} | } | ||||
userNewAddActivity := make(map[int64]map[int64]int64) | userNewAddActivity := make(map[int64]map[int64]int64) | ||||
userAcitvateJsonMap := make(map[int64]map[int64]int64) | |||||
userCurrentDayRegistMap := make(map[int64]map[int64]int64) | |||||
ParaWeight := getParaWeight() | ParaWeight := getParaWeight() | ||||
userMetrics := make(map[string]int) | userMetrics := make(map[string]int) | ||||
var indexTotal int64 | var indexTotal int64 | ||||
@@ -1028,7 +1030,10 @@ func CounDataByDateAndReCount(wikiCountMap map[string]int, startTime time.Time, | |||||
log.Info("has activity." + userRecord.Name) | log.Info("has activity." + userRecord.Name) | ||||
addUserToMap(userNewAddActivity, userRecord.CreatedUnix, dateRecord.ID) | addUserToMap(userNewAddActivity, userRecord.CreatedUnix, dateRecord.ID) | ||||
} | } | ||||
if userRecord.IsActive { | |||||
addUserToMap(userAcitvateJsonMap, userRecord.CreatedUnix, dateRecord.ID) | |||||
} | |||||
addUserToMap(userCurrentDayRegistMap, userRecord.CreatedUnix, dateRecord.ID) | |||||
} | } | ||||
indexTotal += PAGE_SIZE | indexTotal += PAGE_SIZE | ||||
@@ -1064,36 +1069,61 @@ func CounDataByDateAndReCount(wikiCountMap map[string]int, startTime time.Time, | |||||
} | } | ||||
statictisSess.Insert(&useMetrics) | statictisSess.Insert(&useMetrics) | ||||
//update new user activity | //update new user activity | ||||
updateNewUserAcitivity(userNewAddActivity, statictisSess) | |||||
updateNewUserAcitivity(userNewAddActivity, userAcitvateJsonMap, userCurrentDayRegistMap, statictisSess) | |||||
return nil | return nil | ||||
} | } | ||||
func updateNewUserAcitivity(currentUserActivity map[int64]map[int64]int64, statictisSess *xorm.Session) { | |||||
for key, value := range currentUserActivity { | |||||
func updateNewUserAcitivity(currentUserActivity map[int64]map[int64]int64, userAcitvateJsonMap map[int64]map[int64]int64, userCurrentDayRegistMap map[int64]map[int64]int64, statictisSess *xorm.Session) { | |||||
for key, value := range userCurrentDayRegistMap { | |||||
useMetrics := &UserMetrics{CountDate: key} | useMetrics := &UserMetrics{CountDate: key} | ||||
userAcitvateValue := userAcitvateJsonMap[key] | |||||
HuodongValue := currentUserActivity[key] | |||||
has, err := statictisSess.Get(useMetrics) | has, err := statictisSess.Get(useMetrics) | ||||
if err == nil && has { | if err == nil && has { | ||||
userIdArrays := strings.Split(useMetrics.HasActivityUserJson, ",") | |||||
for _, userIdStr := range userIdArrays { | |||||
userIdInt, err := strconv.ParseInt(userIdStr, 10, 64) | |||||
if err == nil { | |||||
value[userIdInt] = userIdInt | |||||
} | |||||
} | |||||
userIdArray := "" | |||||
for _, tmpValue := range value { | |||||
userIdArray += fmt.Sprint(tmpValue) + "," | |||||
} | |||||
useMetrics.HasActivityUser = len(value) | |||||
if len(userIdArray) > 0 { | |||||
useMetrics.HasActivityUserJson = userIdArray[0 : len(userIdArray)-1] | |||||
} | |||||
updateSql := "update public.user_metrics set has_activity_user_json='" + useMetrics.HasActivityUserJson + "',regist_activity_user=" + fmt.Sprint(useMetrics.HasActivityUser) + " where count_date=" + fmt.Sprint(key) | |||||
ActivityUserArray, HuodongTotal := setUniqueUserId(useMetrics.HasActivityUserJson, HuodongValue) | |||||
useMetrics.HasActivityUser = HuodongTotal | |||||
useMetrics.HasActivityUserJson = ActivityUserArray | |||||
useMetrics.CurrentDayRegistUser = len(value) | |||||
RegistUserArray, lenRegistUser := setUniqueUserId(useMetrics.ActivityUserJson, userAcitvateValue) | |||||
useMetrics.ActivityUserJson = RegistUserArray | |||||
useMetrics.ActivateRegistUser = lenRegistUser | |||||
updateSql := "update public.user_metrics set has_activity_user_json='" + useMetrics.HasActivityUserJson + | |||||
"',regist_activity_user=" + fmt.Sprint(useMetrics.HasActivityUser) + | |||||
",activity_user_json='" + useMetrics.ActivityUserJson + "'" + | |||||
",activate_regist_user=" + fmt.Sprint(useMetrics.ActivateRegistUser) + | |||||
",not_activate_regist_user=" + fmt.Sprint(useMetrics.CurrentDayRegistUser-useMetrics.ActivateRegistUser) + | |||||
",current_day_regist_user=" + fmt.Sprint(useMetrics.CurrentDayRegistUser) + | |||||
" where count_date=" + fmt.Sprint(key) | |||||
statictisSess.Exec(updateSql) | statictisSess.Exec(updateSql) | ||||
} | } | ||||
} | } | ||||
} | } | ||||
func setUniqueUserId(jsonString string, value map[int64]int64) (string, int) { | |||||
if value == nil { | |||||
value = make(map[int64]int64, 0) | |||||
} | |||||
userIdArrays := strings.Split(jsonString, ",") | |||||
for _, userIdStr := range userIdArrays { | |||||
userIdInt, err := strconv.ParseInt(userIdStr, 10, 64) | |||||
if err == nil { | |||||
value[userIdInt] = userIdInt | |||||
} | |||||
} | |||||
userIdArray := "" | |||||
for _, tmpValue := range value { | |||||
userIdArray += fmt.Sprint(tmpValue) + "," | |||||
} | |||||
if len(userIdArray) > 0 { | |||||
return userIdArray[0 : len(userIdArray)-1], len(value) | |||||
} | |||||
return userIdArray, len(value) | |||||
} | |||||
func addUserToMap(currentUserActivity map[int64]map[int64]int64, registDate timeutil.TimeStamp, userId int64) { | func addUserToMap(currentUserActivity map[int64]map[int64]int64, registDate timeutil.TimeStamp, userId int64) { | ||||
CountDateTime := time.Date(registDate.Year(), registDate.AsTime().Month(), registDate.AsTime().Day(), 0, 1, 0, 0, registDate.AsTime().Location()) | CountDateTime := time.Date(registDate.Year(), registDate.AsTime().Month(), registDate.AsTime().Day(), 0, 1, 0, 0, registDate.AsTime().Location()) | ||||
CountDate := CountDateTime.Unix() | CountDate := CountDateTime.Unix() | ||||
@@ -1104,7 +1134,6 @@ func addUserToMap(currentUserActivity map[int64]map[int64]int64, registDate time | |||||
} else { | } else { | ||||
currentUserActivity[CountDate][userId] = userId | currentUserActivity[CountDate][userId] = userId | ||||
} | } | ||||
} | } | ||||
func setUserMetrics(userMetrics map[string]int, user *User, start_time int64, end_time int64, dateRecord UserBusinessAnalysis) { | func setUserMetrics(userMetrics map[string]int, user *User, start_time int64, end_time int64, dateRecord UserBusinessAnalysis) { | ||||
@@ -467,11 +467,11 @@ type UserAnalysisPara struct { | |||||
type UserMetrics struct { | type UserMetrics struct { | ||||
CountDate int64 `xorm:"pk"` | CountDate int64 `xorm:"pk"` | ||||
ActivateRegistUser int `xorm:"NOT NULL DEFAULT 0"` | |||||
NotActivateRegistUser int `xorm:"NOT NULL DEFAULT 0"` | |||||
ActivateIndex float64 `xorm:"NOT NULL DEFAULT 0"` | |||||
RegistActivityUser int `xorm:"NOT NULL DEFAULT 0"` | |||||
HasActivityUser int `xorm:"NOT NULL DEFAULT 0"` | |||||
ActivateRegistUser int `xorm:"NOT NULL DEFAULT 0"` //当天激活用户 | |||||
NotActivateRegistUser int `xorm:"NOT NULL DEFAULT 0"` //当天未激活用户 | |||||
ActivateIndex float64 `xorm:"NOT NULL DEFAULT 0"` //激活比率 | |||||
RegistActivityUser int `xorm:"NOT NULL DEFAULT 0"` //当天注册激活的人中,有贡献活动的人 | |||||
HasActivityUser int `xorm:"NOT NULL DEFAULT 0"` //当天有贡献活动的人 | |||||
TotalUser int `xorm:"NOT NULL DEFAULT 0"` | TotalUser int `xorm:"NOT NULL DEFAULT 0"` | ||||
TotalRegistUser int `xorm:"-"` | TotalRegistUser int `xorm:"-"` | ||||
TotalActivateRegistUser int `xorm:"NOT NULL DEFAULT 0"` | TotalActivateRegistUser int `xorm:"NOT NULL DEFAULT 0"` | ||||
@@ -480,5 +480,7 @@ type UserMetrics struct { | |||||
DisplayDate string `xorm:"-"` | DisplayDate string `xorm:"-"` | ||||
DataDate string `xorm:"NULL"` | DataDate string `xorm:"NULL"` | ||||
DaysForMonth int `xorm:"NOT NULL DEFAULT 0"` | DaysForMonth int `xorm:"NOT NULL DEFAULT 0"` | ||||
HasActivityUserJson string `xorm:"text NULL"` | |||||
HasActivityUserJson string `xorm:"text NULL"` //贡献活动用户列表 | |||||
ActivityUserJson string `xorm:"text NULL"` //激活用户列表 | |||||
CurrentDayRegistUser int `xorm:"NOT NULL DEFAULT 0"` //当天注册用户 | |||||
} | } |
@@ -0,0 +1,139 @@ | |||||
package wechat | |||||
import ( | |||||
"code.gitea.io/gitea/models" | |||||
"code.gitea.io/gitea/modules/log" | |||||
"code.gitea.io/gitea/modules/setting" | |||||
"encoding/json" | |||||
"github.com/patrickmn/go-cache" | |||||
"strings" | |||||
"time" | |||||
) | |||||
var WechatReplyCache = cache.New(2*time.Minute, 1*time.Minute) | |||||
const ( | |||||
WECHAT_REPLY_CACHE_KEY = "wechat_response" | |||||
) | |||||
const ( | |||||
ReplyTypeText = "text" | |||||
ReplyTypeImage = "image" | |||||
ReplyTypeVoice = "voice" | |||||
ReplyTypeVideo = "video" | |||||
ReplyTypeMusic = "music" | |||||
ReplyTypeNews = "news" | |||||
) | |||||
type ReplyConfigType string | |||||
const ( | |||||
SubscribeReply ReplyConfigType = "subscribe" | |||||
AutoMsgReply ReplyConfigType = "autoMsg" | |||||
) | |||||
func (r ReplyConfigType) Name() string { | |||||
switch r { | |||||
case SubscribeReply: | |||||
return "subscribe" | |||||
case AutoMsgReply: | |||||
return "autoMsg" | |||||
} | |||||
return "" | |||||
} | |||||
func (r ReplyConfigType) TreePath() string { | |||||
switch r { | |||||
case SubscribeReply: | |||||
return setting.TreePathOfSubscribe | |||||
case AutoMsgReply: | |||||
return setting.TreePathOfAutoMsgReply | |||||
} | |||||
return "" | |||||
} | |||||
type WechatReplyContent struct { | |||||
Reply *ReplyContent | |||||
ReplyType string | |||||
KeyWords []string | |||||
IsFullMatch int | |||||
} | |||||
type ReplyContent struct { | |||||
Content string | |||||
MediaId string | |||||
Title string | |||||
Description string | |||||
MusicUrl string | |||||
HQMusicUrl string | |||||
ThumbMediaId string | |||||
Articles []ArticlesContent | |||||
} | |||||
func GetAutomaticReply(msg string) *WechatReplyContent { | |||||
r, err := LoadReplyFromCacheAndDisk(AutoMsgReply) | |||||
if err != nil { | |||||
return nil | |||||
} | |||||
if r == nil || len(r) == 0 { | |||||
return nil | |||||
} | |||||
for i := 0; i < len(r); i++ { | |||||
if r[i].IsFullMatch == 0 { | |||||
for _, v := range r[i].KeyWords { | |||||
if strings.Contains(msg, v) { | |||||
return r[i] | |||||
} | |||||
} | |||||
} else if r[i].IsFullMatch > 0 { | |||||
for _, v := range r[i].KeyWords { | |||||
if msg == v { | |||||
return r[i] | |||||
} | |||||
} | |||||
} | |||||
} | |||||
return nil | |||||
} | |||||
func loadReplyFromDisk(replyConfig ReplyConfigType) ([]*WechatReplyContent, error) { | |||||
log.Info("LoadReply from disk") | |||||
repo, err := models.GetRepositoryByOwnerAndAlias(setting.UserNameOfWechatReply, setting.RepoNameOfWechatReply) | |||||
if err != nil { | |||||
log.Error("get AutomaticReply repo failed, error=%v", err) | |||||
return nil, err | |||||
} | |||||
repoFile, err := models.ReadLatestFileInRepo(setting.UserNameOfWechatReply, repo.Name, setting.RefNameOfWechatReply, replyConfig.TreePath()) | |||||
if err != nil { | |||||
log.Error("get AutomaticReply failed, error=%v", err) | |||||
return nil, err | |||||
} | |||||
res := make([]*WechatReplyContent, 0) | |||||
json.Unmarshal(repoFile.Content, &res) | |||||
if res == nil || len(res) == 0 { | |||||
return nil, err | |||||
} | |||||
return res, nil | |||||
} | |||||
func LoadReplyFromCacheAndDisk(replyConfig ReplyConfigType) ([]*WechatReplyContent, error) { | |||||
v, success := WechatReplyCache.Get(replyConfig.Name()) | |||||
if success { | |||||
log.Info("LoadReply from cache,value = %v", v) | |||||
if v == nil { | |||||
return nil, nil | |||||
} | |||||
n := v.([]*WechatReplyContent) | |||||
return n, nil | |||||
} | |||||
content, err := loadReplyFromDisk(replyConfig) | |||||
if err != nil { | |||||
log.Error("LoadReply failed, error=%v", err) | |||||
WechatReplyCache.Set(replyConfig.Name(), nil, 30*time.Second) | |||||
return nil, err | |||||
} | |||||
WechatReplyCache.Set(replyConfig.Name(), content, 60*time.Second) | |||||
return content, nil | |||||
} |
@@ -17,7 +17,8 @@ var ( | |||||
const ( | const ( | ||||
GRANT_TYPE = "client_credential" | GRANT_TYPE = "client_credential" | ||||
ACCESS_TOKEN_PATH = "/cgi-bin/token" | ACCESS_TOKEN_PATH = "/cgi-bin/token" | ||||
QR_CODE_Path = "/cgi-bin/qrcode/create" | |||||
QR_CODE_PATH = "/cgi-bin/qrcode/create" | |||||
GET_MATERIAL_PATH = "/cgi-bin/material/batchget_material" | |||||
ACTION_QR_STR_SCENE = "QR_STR_SCENE" | ACTION_QR_STR_SCENE = "QR_STR_SCENE" | ||||
ERR_CODE_ACCESSTOKEN_EXPIRE = 42001 | ERR_CODE_ACCESSTOKEN_EXPIRE = 42001 | ||||
@@ -40,6 +41,11 @@ type QRCodeRequest struct { | |||||
Action_info ActionInfo `json:"action_info"` | Action_info ActionInfo `json:"action_info"` | ||||
Expire_seconds int `json:"expire_seconds"` | Expire_seconds int `json:"expire_seconds"` | ||||
} | } | ||||
type MaterialRequest struct { | |||||
Type string `json:"type"` | |||||
Offset int `json:"offset"` | |||||
Count int `json:"count"` | |||||
} | |||||
type ActionInfo struct { | type ActionInfo struct { | ||||
Scene Scene `json:"scene"` | Scene Scene `json:"scene"` | ||||
@@ -97,7 +103,7 @@ func callQRCodeCreate(sceneStr string) (*QRCodeResponse, bool) { | |||||
SetQueryParam("access_token", GetWechatAccessToken()). | SetQueryParam("access_token", GetWechatAccessToken()). | ||||
SetBody(bodyJson). | SetBody(bodyJson). | ||||
SetResult(&result). | SetResult(&result). | ||||
Post(setting.WechatApiHost + QR_CODE_Path) | |||||
Post(setting.WechatApiHost + QR_CODE_PATH) | |||||
if err != nil { | if err != nil { | ||||
log.Error("create QR code failed,e=%v", err) | log.Error("create QR code failed,e=%v", err) | ||||
return nil, false | return nil, false | ||||
@@ -113,6 +119,37 @@ func callQRCodeCreate(sceneStr string) (*QRCodeResponse, bool) { | |||||
return &result, false | return &result, false | ||||
} | } | ||||
//getMaterial | |||||
// api doc: https://developers.weixin.qq.com/doc/offiaccount/Asset_Management/Get_materials_list.html | |||||
func getMaterial(mType string, offset, count int) (interface{}, bool) { | |||||
client := getWechatRestyClient() | |||||
body := &MaterialRequest{ | |||||
Type: mType, | |||||
Offset: offset, | |||||
Count: count, | |||||
} | |||||
bodyJson, _ := json.Marshal(body) | |||||
r, err := client.R(). | |||||
SetHeader("Content-Type", "application/json"). | |||||
SetQueryParam("access_token", GetWechatAccessToken()). | |||||
SetBody(bodyJson). | |||||
Post(setting.WechatApiHost + GET_MATERIAL_PATH) | |||||
if err != nil { | |||||
log.Error("create QR code failed,e=%v", err) | |||||
return nil, false | |||||
} | |||||
a := r.Body() | |||||
resultMap := make(map[string]interface{}, 0) | |||||
json.Unmarshal(a, &resultMap) | |||||
errcode := resultMap["errcode"] | |||||
if errcode == fmt.Sprint(ERR_CODE_ACCESSTOKEN_EXPIRE) || errcode == fmt.Sprint(ERR_CODE_ACCESSTOKEN_INVALID) { | |||||
return nil, true | |||||
} | |||||
log.Info("%v", r) | |||||
return &resultMap, false | |||||
} | |||||
func getErrorCodeFromResponse(r *resty.Response) int { | func getErrorCodeFromResponse(r *resty.Response) int { | ||||
a := r.Body() | a := r.Body() | ||||
resultMap := make(map[string]interface{}, 0) | resultMap := make(map[string]interface{}, 0) | ||||
@@ -18,7 +18,7 @@ import ( | |||||
// <EventKey><![CDATA[SCENE_VALUE]]></EventKey> | // <EventKey><![CDATA[SCENE_VALUE]]></EventKey> | ||||
// <Ticket><![CDATA[TICKET]]></Ticket> | // <Ticket><![CDATA[TICKET]]></Ticket> | ||||
//</xml> | //</xml> | ||||
type WechatEvent struct { | |||||
type WechatMsg struct { | |||||
ToUserName string | ToUserName string | ||||
FromUserName string | FromUserName string | ||||
CreateTime int64 | CreateTime int64 | ||||
@@ -26,9 +26,13 @@ type WechatEvent struct { | |||||
Event string | Event string | ||||
EventKey string | EventKey string | ||||
Ticket string | Ticket string | ||||
Content string | |||||
MsgId string | |||||
MsgDataId string | |||||
Idx string | |||||
} | } | ||||
type EventReply struct { | |||||
type MsgReply struct { | |||||
XMLName xml.Name `xml:"xml"` | XMLName xml.Name `xml:"xml"` | ||||
ToUserName string | ToUserName string | ||||
FromUserName string | FromUserName string | ||||
@@ -37,16 +41,97 @@ type EventReply struct { | |||||
Content string | Content string | ||||
} | } | ||||
type TextMsgReply struct { | |||||
XMLName xml.Name `xml:"xml"` | |||||
ToUserName string | |||||
FromUserName string | |||||
CreateTime int64 | |||||
MsgType string | |||||
Content string | |||||
} | |||||
type ImageMsgReply struct { | |||||
XMLName xml.Name `xml:"xml"` | |||||
ToUserName string | |||||
FromUserName string | |||||
CreateTime int64 | |||||
MsgType string | |||||
Image ImageContent | |||||
} | |||||
type VoiceMsgReply struct { | |||||
XMLName xml.Name `xml:"xml"` | |||||
ToUserName string | |||||
FromUserName string | |||||
CreateTime int64 | |||||
MsgType string | |||||
Voice VoiceContent | |||||
} | |||||
type VideoMsgReply struct { | |||||
XMLName xml.Name `xml:"xml"` | |||||
ToUserName string | |||||
FromUserName string | |||||
CreateTime int64 | |||||
MsgType string | |||||
Video VideoContent | |||||
} | |||||
type MusicMsgReply struct { | |||||
XMLName xml.Name `xml:"xml"` | |||||
ToUserName string | |||||
FromUserName string | |||||
CreateTime int64 | |||||
MsgType string | |||||
Music MusicContent | |||||
} | |||||
type NewsMsgReply struct { | |||||
XMLName xml.Name `xml:"xml"` | |||||
ToUserName string | |||||
FromUserName string | |||||
CreateTime int64 | |||||
MsgType string | |||||
ArticleCount int | |||||
Articles ArticleItem | |||||
} | |||||
type ArticleItem struct { | |||||
Item []ArticlesContent | |||||
} | |||||
type ImageContent struct { | |||||
MediaId string | |||||
} | |||||
type VoiceContent struct { | |||||
MediaId string | |||||
} | |||||
type VideoContent struct { | |||||
MediaId string | |||||
Title string | |||||
Description string | |||||
} | |||||
type MusicContent struct { | |||||
Title string | |||||
Description string | |||||
MusicUrl string | |||||
HQMusicUrl string | |||||
ThumbMediaId string | |||||
} | |||||
type ArticlesContent struct { | |||||
XMLName xml.Name `xml:"item"` | |||||
Title string | |||||
Description string | |||||
PicUrl string | |||||
Url string | |||||
} | |||||
const ( | const ( | ||||
WECHAT_EVENT_SUBSCRIBE = "subscribe" | WECHAT_EVENT_SUBSCRIBE = "subscribe" | ||||
WECHAT_EVENT_SCAN = "SCAN" | WECHAT_EVENT_SCAN = "SCAN" | ||||
) | ) | ||||
const ( | const ( | ||||
WECHAT_MSG_TYPE_TEXT = "text" | |||||
WECHAT_MSG_TYPE_TEXT = "text" | |||||
WECHAT_MSG_TYPE_EVENT = "event" | |||||
) | ) | ||||
func HandleSubscribeEvent(we WechatEvent) string { | |||||
func HandleScanEvent(we WechatMsg) string { | |||||
eventKey := we.EventKey | eventKey := we.EventKey | ||||
if eventKey == "" { | if eventKey == "" { | ||||
return "" | return "" | ||||
@@ -74,3 +159,11 @@ func HandleSubscribeEvent(we WechatEvent) string { | |||||
return BIND_REPLY_SUCCESS | return BIND_REPLY_SUCCESS | ||||
} | } | ||||
func HandleSubscribeEvent(we WechatMsg) *WechatReplyContent { | |||||
r, err := LoadReplyFromCacheAndDisk(SubscribeReply) | |||||
if err != nil || len(r) == 0 { | |||||
return nil | |||||
} | |||||
return r[0] | |||||
} |
@@ -0,0 +1,13 @@ | |||||
package wechat | |||||
import "code.gitea.io/gitea/modules/log" | |||||
func GetWechatMaterial(mType string, offset, count int) interface{} { | |||||
result, retryFlag := getMaterial(mType, offset, count) | |||||
if retryFlag { | |||||
log.Info("retryGetWechatMaterial calling") | |||||
refreshAccessToken() | |||||
result, _ = getMaterial(mType, offset, count) | |||||
} | |||||
return result | |||||
} |
@@ -1,12 +1,16 @@ | |||||
package grampus | package grampus | ||||
import ( | import ( | ||||
"encoding/json" | |||||
"strings" | |||||
"code.gitea.io/gitea/modules/setting" | |||||
"code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
"code.gitea.io/gitea/modules/context" | "code.gitea.io/gitea/modules/context" | ||||
"code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
"code.gitea.io/gitea/modules/notification" | "code.gitea.io/gitea/modules/notification" | ||||
"code.gitea.io/gitea/modules/timeutil" | "code.gitea.io/gitea/modules/timeutil" | ||||
"strings" | |||||
) | ) | ||||
const ( | const ( | ||||
@@ -15,12 +19,14 @@ const ( | |||||
ProcessorTypeNPU = "npu.huawei.com/NPU" | ProcessorTypeNPU = "npu.huawei.com/NPU" | ||||
ProcessorTypeGPU = "nvidia.com/gpu" | ProcessorTypeGPU = "nvidia.com/gpu" | ||||
CommandPrepareScript = "pwd;cd /cache;mkdir -p output;mkdir -p code;mkdir -p dataset;echo \"start loading script\";wget -q https://git.openi.org.cn/OpenIOSSG/script_for_grampus/archive/master.zip;" + | |||||
GpuWorkDir = "/tmp/" | |||||
NpuWorkDir = "/cache/" | |||||
CommandPrepareScript = ";mkdir -p output;mkdir -p code;mkdir -p dataset;echo \"start loading script\";wget -q https://git.openi.org.cn/OpenIOSSG/script_for_grampus/archive/master.zip;" + | |||||
"echo \"finish loading script\";unzip -q master.zip;cd script_for_grampus;chmod 777 downloader_for_obs uploader_for_obs downloader_for_minio uploader_for_minio;" | "echo \"finish loading script\";unzip -q master.zip;cd script_for_grampus;chmod 777 downloader_for_obs uploader_for_obs downloader_for_minio uploader_for_minio;" | ||||
//CommandPrepareScript = "pwd;cd /cache;mkdir -p output;mkdir -p code;mkdir -p dataset;echo \"start loading script\";wget -q https://git.openi.org.cn/OpenIOSSG/script_for_grampus/archive/master.zip;" + | |||||
// "echo \"finish loading script\";unzip -q master.zip;cd script_for_grampus;chmod 777 downloader_for_obs uploader_for_obs downloader_for_minio uploader_for_minio;" | |||||
//CommandPrepareScript = "bash;pwd;apt-get -y update;apt-get -y upgrade;apt-get -y install wget;apt-get -y install unzip;" + | |||||
// "cd /tmp;mkdir -p output;mkdir -p code;mkdir -p dataset;wget -q https://git.openi.org.cn/OpenIOSSG/script_for_grampus/archive/master.zip;" + | |||||
// "unzip -q master.zip;cd script_for_grampus;chmod 777 downloader_for_obs uploader_for_obs downloader_for_minio uploader_for_minio;" | |||||
CodeArchiveName = "master.zip" | CodeArchiveName = "master.zip" | ||||
) | ) | ||||
@@ -28,6 +34,8 @@ var ( | |||||
poolInfos *models.PoolInfos | poolInfos *models.PoolInfos | ||||
FlavorInfos *models.FlavorInfos | FlavorInfos *models.FlavorInfos | ||||
ImageInfos *models.ImageInfosModelArts | ImageInfos *models.ImageInfosModelArts | ||||
SpecialPools *models.SpecialPools | |||||
) | ) | ||||
type GenerateTrainJobReq struct { | type GenerateTrainJobReq struct { | ||||
@@ -63,6 +71,27 @@ type GenerateTrainJobReq struct { | |||||
func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error) { | func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error) { | ||||
createTime := timeutil.TimeStampNow() | createTime := timeutil.TimeStampNow() | ||||
var CenterID []string | |||||
var CenterName []string | |||||
if SpecialPools != nil { | |||||
for _, pool := range SpecialPools.Pools { | |||||
if !pool.IsExclusive && strings.Contains(req.ComputeResource, pool.Type) { | |||||
org, _ := models.GetOrgByName(pool.Org) | |||||
if org != nil { | |||||
isOrgMember, _ := models.IsOrganizationMember(org.ID, ctx.User.ID) | |||||
if isOrgMember { | |||||
for _, info := range pool.Pool { | |||||
CenterID = append(CenterID, info.Queue) | |||||
CenterName = append(CenterName, info.Value) | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
jobResult, err := createJob(models.CreateGrampusJobRequest{ | jobResult, err := createJob(models.CreateGrampusJobRequest{ | ||||
Name: req.JobName, | Name: req.JobName, | ||||
Tasks: []models.GrampusTasks{ | Tasks: []models.GrampusTasks{ | ||||
@@ -72,6 +101,8 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error | |||||
ResourceSpecId: req.ResourceSpecId, | ResourceSpecId: req.ResourceSpecId, | ||||
ImageId: req.ImageId, | ImageId: req.ImageId, | ||||
ImageUrl: req.ImageUrl, | ImageUrl: req.ImageUrl, | ||||
CenterID: CenterID, | |||||
CenterName: CenterName, | |||||
ReplicaNum: 1, | ReplicaNum: 1, | ||||
}, | }, | ||||
}, | }, | ||||
@@ -136,3 +167,8 @@ func TransTrainJobStatus(status string) string { | |||||
return strings.ToUpper(status) | return strings.ToUpper(status) | ||||
} | } | ||||
func InitSpecialPool() { | |||||
if SpecialPools == nil && setting.Grampus.SpecialPools != "" { | |||||
json.Unmarshal([]byte(setting.Grampus.SpecialPools), &SpecialPools) | |||||
} | |||||
} |
@@ -530,10 +530,11 @@ var ( | |||||
//grampus config | //grampus config | ||||
Grampus = struct { | Grampus = struct { | ||||
Env string | |||||
Host string | |||||
UserName string | |||||
Password string | |||||
Env string | |||||
Host string | |||||
UserName string | |||||
Password string | |||||
SpecialPools string | |||||
}{} | }{} | ||||
//elk config | //elk config | ||||
@@ -553,6 +554,13 @@ var ( | |||||
WechatQRCodeExpireSeconds int | WechatQRCodeExpireSeconds int | ||||
WechatAuthSwitch bool | WechatAuthSwitch bool | ||||
//wechat auto reply config | |||||
UserNameOfWechatReply string | |||||
RepoNameOfWechatReply string | |||||
RefNameOfWechatReply string | |||||
TreePathOfAutoMsgReply string | |||||
TreePathOfSubscribe string | |||||
//nginx proxy | //nginx proxy | ||||
PROXYURL string | PROXYURL string | ||||
RadarMap = struct { | RadarMap = struct { | ||||
@@ -1380,6 +1388,11 @@ func NewContext() { | |||||
WechatAppSecret = sec.Key("APP_SECRET").MustString("e48e13f315adc32749ddc7057585f198") | WechatAppSecret = sec.Key("APP_SECRET").MustString("e48e13f315adc32749ddc7057585f198") | ||||
WechatQRCodeExpireSeconds = sec.Key("QR_CODE_EXPIRE_SECONDS").MustInt(120) | WechatQRCodeExpireSeconds = sec.Key("QR_CODE_EXPIRE_SECONDS").MustInt(120) | ||||
WechatAuthSwitch = sec.Key("AUTH_SWITCH").MustBool(true) | WechatAuthSwitch = sec.Key("AUTH_SWITCH").MustBool(true) | ||||
UserNameOfWechatReply = sec.Key("AUTO_REPLY_USER_NAME").MustString("OpenIOSSG") | |||||
RepoNameOfWechatReply = sec.Key("AUTO_REPLY_REPO_NAME").MustString("promote") | |||||
RefNameOfWechatReply = sec.Key("AUTO_REPLY_REF_NAME").MustString("master") | |||||
TreePathOfAutoMsgReply = sec.Key("AUTO_REPLY_TREE_PATH").MustString("wechat/auto_reply.json") | |||||
TreePathOfSubscribe = sec.Key("SUBSCRIBE_TREE_PATH").MustString("wechat/subscribe_reply.json") | |||||
SetRadarMapConfig() | SetRadarMapConfig() | ||||
@@ -1400,6 +1413,8 @@ func GetGrampusConfig() { | |||||
Grampus.Host = sec.Key("SERVER_HOST").MustString("") | Grampus.Host = sec.Key("SERVER_HOST").MustString("") | ||||
Grampus.UserName = sec.Key("USERNAME").MustString("") | Grampus.UserName = sec.Key("USERNAME").MustString("") | ||||
Grampus.Password = sec.Key("PASSWORD").MustString("") | Grampus.Password = sec.Key("PASSWORD").MustString("") | ||||
Grampus.SpecialPools = sec.Key("SPECIAL_POOL").MustString("") | |||||
} | } | ||||
func SetRadarMapConfig() { | func SetRadarMapConfig() { | ||||
@@ -6,6 +6,7 @@ package setting | |||||
import ( | import ( | ||||
"net/url" | "net/url" | ||||
"strings" | |||||
"code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
) | ) | ||||
@@ -13,14 +14,18 @@ import ( | |||||
var ( | var ( | ||||
// Webhook settings | // Webhook settings | ||||
Webhook = struct { | Webhook = struct { | ||||
QueueLength int | |||||
DeliverTimeout int | |||||
SkipTLSVerify bool | |||||
Types []string | |||||
PagingNum int | |||||
ProxyURL string | |||||
ProxyURLFixed *url.URL | |||||
ProxyHosts []string | |||||
QueueLength int | |||||
DeliverTimeout int | |||||
SkipTLSVerify bool | |||||
Types []string | |||||
PagingNum int | |||||
ProxyURL string | |||||
ProxyURLFixed *url.URL | |||||
ProxyHosts []string | |||||
Socks5Proxy string | |||||
Socks5UserName string | |||||
Socks5Password string | |||||
Socks5ProxyHosts []string | |||||
}{ | }{ | ||||
QueueLength: 1000, | QueueLength: 1000, | ||||
DeliverTimeout: 5, | DeliverTimeout: 5, | ||||
@@ -39,6 +44,10 @@ func newWebhookService() { | |||||
Webhook.Types = []string{"gitea", "gogs", "slack", "discord", "dingtalk", "telegram", "msteams", "feishu", "matrix"} | Webhook.Types = []string{"gitea", "gogs", "slack", "discord", "dingtalk", "telegram", "msteams", "feishu", "matrix"} | ||||
Webhook.PagingNum = sec.Key("PAGING_NUM").MustInt(10) | Webhook.PagingNum = sec.Key("PAGING_NUM").MustInt(10) | ||||
Webhook.ProxyURL = sec.Key("PROXY_URL").MustString("") | Webhook.ProxyURL = sec.Key("PROXY_URL").MustString("") | ||||
Webhook.Socks5Proxy = sec.Key("SOCKS5_PROXY_URL").MustString("") | |||||
Webhook.Socks5UserName = sec.Key("SOCKS5_USER_NAME").MustString("") | |||||
Webhook.Socks5Password = sec.Key("SOCKS5_PASSWORD").MustString("") | |||||
Webhook.Socks5ProxyHosts = strings.Split(sec.Key("SOCKS5_PROXY_HOST").MustString(""), ";") | |||||
if Webhook.ProxyURL != "" { | if Webhook.ProxyURL != "" { | ||||
var err error | var err error | ||||
Webhook.ProxyURLFixed, err = url.Parse(Webhook.ProxyURL) | Webhook.ProxyURLFixed, err = url.Parse(Webhook.ProxyURL) | ||||
@@ -16,6 +16,8 @@ import ( | |||||
"sync" | "sync" | ||||
"time" | "time" | ||||
"golang.org/x/net/proxy" | |||||
"code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
"code.gitea.io/gitea/modules/graceful" | "code.gitea.io/gitea/modules/graceful" | ||||
"code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
@@ -137,8 +139,10 @@ func Deliver(t *models.HookTask) error { | |||||
return | return | ||||
} | } | ||||
}() | }() | ||||
match := isSocks5ProxyUrlMatch(req) | |||||
resp, err := makeReq(req, match) | |||||
resp, err := webhookHTTPClient.Do(req) | |||||
if err != nil { | if err != nil { | ||||
t.ResponseInfo.Body = fmt.Sprintf("Delivery: %v", err) | t.ResponseInfo.Body = fmt.Sprintf("Delivery: %v", err) | ||||
return err | return err | ||||
@@ -161,6 +165,23 @@ func Deliver(t *models.HookTask) error { | |||||
return nil | return nil | ||||
} | } | ||||
func makeReq(req *http.Request, proxyMatch bool) (*http.Response, error) { | |||||
if proxyMatch { | |||||
return webhookSocks5PoxyHTTPClient.Do(req) | |||||
} | |||||
return webhookHTTPClient.Do(req) | |||||
} | |||||
func isSocks5ProxyUrlMatch(req *http.Request) bool { | |||||
for _, v := range socks5HostMatchers { | |||||
if v.Match(req.URL.Host) { | |||||
return true | |||||
} | |||||
} | |||||
return false | |||||
} | |||||
// DeliverHooks checks and delivers undelivered hooks. | // DeliverHooks checks and delivers undelivered hooks. | ||||
// FIXME: graceful: This would likely benefit from either a worker pool with dummy queue | // FIXME: graceful: This would likely benefit from either a worker pool with dummy queue | ||||
// or a full queue. Then more hooks could be sent at same time. | // or a full queue. Then more hooks could be sent at same time. | ||||
@@ -225,9 +246,11 @@ func DeliverHooks(ctx context.Context) { | |||||
} | } | ||||
var ( | var ( | ||||
webhookHTTPClient *http.Client | |||||
once sync.Once | |||||
hostMatchers []glob.Glob | |||||
webhookHTTPClient *http.Client | |||||
once sync.Once | |||||
hostMatchers []glob.Glob | |||||
webhookSocks5PoxyHTTPClient *http.Client | |||||
socks5HostMatchers []glob.Glob | |||||
) | ) | ||||
func webhookProxy() func(req *http.Request) (*url.URL, error) { | func webhookProxy() func(req *http.Request) (*url.URL, error) { | ||||
@@ -274,5 +297,31 @@ func InitDeliverHooks() { | |||||
}, | }, | ||||
} | } | ||||
if setting.Webhook.Socks5Proxy != "" { | |||||
auth := proxy.Auth{ | |||||
User: setting.Webhook.Socks5UserName, | |||||
Password: setting.Webhook.Socks5Password, | |||||
} | |||||
dialSocksProxy, err := proxy.SOCKS5("tcp", setting.Webhook.Socks5Proxy, &auth, proxy.Direct) | |||||
if err != nil { | |||||
fmt.Println("Error connecting to proxy:", err) | |||||
} | |||||
tr := &http.Transport{Dial: dialSocksProxy.Dial} | |||||
webhookSocks5PoxyHTTPClient = &http.Client{ | |||||
Transport: tr, | |||||
} | |||||
for _, h := range setting.Webhook.Socks5ProxyHosts { | |||||
if g, err := glob.Compile(h); err == nil { | |||||
socks5HostMatchers = append(socks5HostMatchers, g) | |||||
} else { | |||||
log.Error("glob.Compile %s failed: %v", h, err) | |||||
} | |||||
} | |||||
} | |||||
go graceful.GetManager().RunWithShutdownContext(DeliverHooks) | go graceful.GetManager().RunWithShutdownContext(DeliverHooks) | ||||
} | } |
@@ -1178,6 +1178,8 @@ model.manage.model_accuracy = Model Accuracy | |||||
grampus.train_job.ai_center = AI Center | grampus.train_job.ai_center = AI Center | ||||
grampus.dataset_path_rule = The code is storaged in /cache/code;the dataset is storaged in /cache/dataset;and please put your model into /cache/output, then you can download it online。 | grampus.dataset_path_rule = The code is storaged in /cache/code;the dataset is storaged in /cache/dataset;and please put your model into /cache/output, then you can download it online。 | ||||
grampus.gpu_dataset_path_rule = The code is storaged in /tmp/code;the dataset is storaged in /tmp/dataset;and please put your model into /tmp/output, then you can download it online。 | |||||
grampus.no_operate_right = You have no right to do this operation. | |||||
template.items = Template Items | template.items = Template Items | ||||
template.git_content = Git Content (Default Branch) | template.git_content = Git Content (Default Branch) | ||||
@@ -1192,6 +1192,9 @@ model.manage.model_accuracy = 模型精度 | |||||
grampus.train_job.ai_center=智算中心 | grampus.train_job.ai_center=智算中心 | ||||
grampus.dataset_path_rule = 训练脚本存储在/cache/code中,数据集存储在/cache/dataset中,训练输出请存储在/cache/output中以供后续下载。 | grampus.dataset_path_rule = 训练脚本存储在/cache/code中,数据集存储在/cache/dataset中,训练输出请存储在/cache/output中以供后续下载。 | ||||
grampus.gpu_dataset_path_rule = 训练脚本存储在/tmp/code中,数据集存储在/tmp/dataset中,训练输出请存储在/tmp/output中以供后续下载。 | |||||
grampus.no_operate_right = 您没有权限创建这类任务。 | |||||
template.items=模板选项 | template.items=模板选项 | ||||
template.git_content=Git数据(默认分支) | template.git_content=Git数据(默认分支) | ||||
@@ -1423,7 +1426,7 @@ issues.label_templates.helper=选择标签模板 | |||||
issues.label_templates.use=使用标签集 | issues.label_templates.use=使用标签集 | ||||
issues.label_templates.fail_to_load_file=加载标签模板文件 '%s' 时发生错误:%v | issues.label_templates.fail_to_load_file=加载标签模板文件 '%s' 时发生错误:%v | ||||
issues.add_label_at=添加了标签 <div class="ui label" style="color: %s\; background-color: %s"> %s </div> %s | issues.add_label_at=添加了标签 <div class="ui label" style="color: %s\; background-color: %s"> %s </div> %s | ||||
issues.remove_label_at=删除了 <div class="ui label" style="color: %s\; background-color: %s">%s</div> label %s 标签 | |||||
issues.remove_label_at=删除了标签 <div class="ui label" style="color: %s\; background-color: %s">%s </div> %s | |||||
issues.add_milestone_at=` %[2]s 添加了里程碑 <b>%[1]s</b>` | issues.add_milestone_at=` %[2]s 添加了里程碑 <b>%[1]s</b>` | ||||
issues.change_milestone_at=`%[3]s 修改了里程碑从 <b>%[1]s</b> 到 <b>%[2]s</b>` | issues.change_milestone_at=`%[3]s 修改了里程碑从 <b>%[1]s</b> 到 <b>%[2]s</b>` | ||||
issues.remove_milestone_at=`%[2]s 删除了里程碑 <b>%[1]s</b>` | issues.remove_milestone_at=`%[2]s 删除了里程碑 <b>%[1]s</b>` | ||||
@@ -74,28 +74,30 @@ var swiperOrg = new Swiper(".homeorg-list", { | |||||
}, | }, | ||||
}); | }); | ||||
var output = document.getElementById("newmessage"); | |||||
var url = "ws://" + document.location.host + "/action/notification"; | |||||
if(document.location.host == "git.openi.org.cn" || document.URL.startsWith("https")){ | |||||
url = "wss://" + document.location.host + "/action/notification" | |||||
} | |||||
var socket = new WebSocket(url); | |||||
socket.onopen = function () { | |||||
messageQueue = []; | |||||
console.log("message has connected."); | |||||
}; | |||||
var maxSize = 20; | var maxSize = 20; | ||||
var html =document.documentElement; | var html =document.documentElement; | ||||
var lang = html.attributes["lang"] | var lang = html.attributes["lang"] | ||||
var isZh = true; | var isZh = true; | ||||
if(lang != null && lang.nodeValue =="en-US" ){ | if(lang != null && lang.nodeValue =="en-US" ){ | ||||
isZh=false; | isZh=false; | ||||
}else{ | |||||
} | } | ||||
socket.onmessage = function (e) { | |||||
document.onreadystatechange = function () { | |||||
queryRecommendData(); | |||||
var output = document.getElementById("newmessage"); | |||||
var url = "ws://" + document.location.host + "/action/notification"; | |||||
if(document.location.host == "git.openi.org.cn" || document.URL.startsWith("https")){ | |||||
url = "wss://" + document.location.host + "/action/notification" | |||||
} | |||||
var socket = new WebSocket(url); | |||||
socket.onopen = function () { | |||||
messageQueue = []; | |||||
console.log("message has connected."); | |||||
}; | |||||
socket.onmessage = function (e) { | |||||
var data =JSON.parse(e.data) | var data =JSON.parse(e.data) | ||||
var html = ""; | var html = ""; | ||||
if (data != null){ | if (data != null){ | ||||
@@ -154,7 +156,7 @@ socket.onmessage = function (e) { | |||||
html += recordPrefix + actionName; | html += recordPrefix + actionName; | ||||
html += " <a href=\"" + getRepoLink(record) + "\" rel=\"nofollow\">" + getRepotext(record) + "</a>" | html += " <a href=\"" + getRepoLink(record) + "\" rel=\"nofollow\">" + getRepotext(record) + "</a>" | ||||
} | } | ||||
else if(record.OpType == "24" || record.OpType == "26" || record.OpType == "27" || record.OpType == "28" || record.OpType == "30" || record.OpType == "31"){ | |||||
else if(record.OpType == "24" || record.OpType == "26" || record.OpType == "27" || record.OpType == "28" || record.OpType == "30" || record.OpType == "31" || record.OpType == "32" || record.OpType == "33"){ | |||||
html += recordPrefix + actionName; | html += recordPrefix + actionName; | ||||
html += " <a href=\"" + getTaskLink(record) + "\" rel=\"nofollow\">" + record.RefName + "</a>" | html += " <a href=\"" + getTaskLink(record) + "\" rel=\"nofollow\">" + record.RefName + "</a>" | ||||
} | } | ||||
@@ -176,7 +178,10 @@ socket.onmessage = function (e) { | |||||
output.innerHTML = html; | output.innerHTML = html; | ||||
swiperNewMessage.updateSlides(); | swiperNewMessage.updateSlides(); | ||||
swiperNewMessage.updateProgress(); | swiperNewMessage.updateProgress(); | ||||
}; | |||||
}; | |||||
} | |||||
function getTaskLink(record){ | function getTaskLink(record){ | ||||
var re = getRepoLink(record); | var re = getRepoLink(record); | ||||
@@ -196,6 +201,8 @@ function getTaskLink(record){ | |||||
re = re + "/modelmanage/show_model_info?name=" + record.RefName; | re = re + "/modelmanage/show_model_info?name=" + record.RefName; | ||||
}else if(record.OpType == 31){ | }else if(record.OpType == 31){ | ||||
re = re + "/cloudbrain/train-job/" + record.Content; | re = re + "/cloudbrain/train-job/" + record.Content; | ||||
}else if(record.OpType == 32 || record.OpType == 33){ | |||||
re = re + "/grampus/train-job/" + record.Content; | |||||
} | } | ||||
re = encodeURI(re); | re = encodeURI(re); | ||||
return re; | return re; | ||||
@@ -369,7 +376,9 @@ var actionNameZH={ | |||||
"28":"创建了推理任务", | "28":"创建了推理任务", | ||||
"29":"创建了评测任务", | "29":"创建了评测任务", | ||||
"30":"导入了新模型", | "30":"导入了新模型", | ||||
"31":"创建了CPU/GPU类型训练任务" | |||||
"31":"创建了CPU/GPU类型训练任务", | |||||
"32":"创建了NPU类型训练任务", | |||||
"33":"创建了CPU/GPU类型训练任务" | |||||
}; | }; | ||||
var actionNameEN={ | var actionNameEN={ | ||||
@@ -396,6 +405,8 @@ var actionNameEN={ | |||||
"29":" created profiling task", | "29":" created profiling task", | ||||
"30":" created new model", | "30":" created new model", | ||||
"31":" created CPU/GPU type training task", | "31":" created CPU/GPU type training task", | ||||
"32":" created NPU type training task", | |||||
"33":" created CPU/GPU type training task" | |||||
}; | }; | ||||
var repoAndOrgZH={ | var repoAndOrgZH={ | ||||
@@ -437,7 +448,9 @@ function getAction(opType,isZh){ | |||||
} | } | ||||
} | } | ||||
queryRecommendData(); | |||||
function queryRecommendData(){ | function queryRecommendData(){ | ||||
$.ajax({ | $.ajax({ | ||||
@@ -43,12 +43,6 @@ func CloudBrains(ctx *context.Context) { | |||||
if page <= 0 { | if page <= 0 { | ||||
page = 1 | page = 1 | ||||
} | } | ||||
debugType := models.TypeCloudBrainAll | |||||
if listType == models.GPUResource { | |||||
debugType = models.TypeCloudBrainOne | |||||
} else if listType == models.NPUResource { | |||||
debugType = models.TypeCloudBrainTwo | |||||
} | |||||
var jobTypes []string | var jobTypes []string | ||||
jobTypeNot := false | jobTypeNot := false | ||||
@@ -77,13 +71,14 @@ func CloudBrains(ctx *context.Context) { | |||||
PageSize: setting.UI.IssuePagingNum, | PageSize: setting.UI.IssuePagingNum, | ||||
}, | }, | ||||
Keyword: keyword, | Keyword: keyword, | ||||
Type: debugType, | |||||
JobTypeNot: jobTypeNot, | JobTypeNot: jobTypeNot, | ||||
JobStatusNot: jobStatusNot, | JobStatusNot: jobStatusNot, | ||||
JobStatus: jobStatuses, | JobStatus: jobStatuses, | ||||
JobTypes: jobTypes, | JobTypes: jobTypes, | ||||
NeedRepoInfo: true, | NeedRepoInfo: true, | ||||
IsLatestVersion: modelarts.IsLatestVersion, | IsLatestVersion: modelarts.IsLatestVersion, | ||||
ComputeResource: listType, | |||||
Type: models.TypeCloudBrainAll, | |||||
}) | }) | ||||
if err != nil { | if err != nil { | ||||
ctx.ServerError("Get job failed:", err) | ctx.ServerError("Get job failed:", err) | ||||
@@ -1055,6 +1055,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
m.Get("/prd/event", authentication.ValidEventSource) | m.Get("/prd/event", authentication.ValidEventSource) | ||||
m.Post("/prd/event", authentication.AcceptWechatEvent) | m.Post("/prd/event", authentication.AcceptWechatEvent) | ||||
}) | }) | ||||
m.Get("/wechat/material", authentication.GetMaterial) | |||||
}, securityHeaders(), context.APIContexter(), sudo()) | }, securityHeaders(), context.APIContexter(), sudo()) | ||||
} | } | ||||
@@ -8,9 +8,11 @@ import ( | |||||
"code.gitea.io/gitea/modules/redis/redis_client" | "code.gitea.io/gitea/modules/redis/redis_client" | ||||
"code.gitea.io/gitea/modules/redis/redis_key" | "code.gitea.io/gitea/modules/redis/redis_key" | ||||
"code.gitea.io/gitea/modules/setting" | "code.gitea.io/gitea/modules/setting" | ||||
"code.gitea.io/gitea/routers/response" | |||||
"encoding/json" | "encoding/json" | ||||
"errors" | "errors" | ||||
gouuid "github.com/satori/go.uuid" | gouuid "github.com/satori/go.uuid" | ||||
"strconv" | |||||
"time" | "time" | ||||
) | ) | ||||
@@ -124,3 +126,23 @@ func createQRCode4Bind(userId int64) (*QRCodeResponse, error) { | |||||
} | } | ||||
return result, nil | return result, nil | ||||
} | } | ||||
// GetMaterial | |||||
func GetMaterial(ctx *context.Context) { | |||||
mType := ctx.Query("type") | |||||
offsetStr := ctx.Query("offset") | |||||
countStr := ctx.Query("count") | |||||
var offset, count int | |||||
if offsetStr == "" { | |||||
offset = 0 | |||||
} else { | |||||
offset, _ = strconv.Atoi(offsetStr) | |||||
} | |||||
if countStr == "" { | |||||
count = 20 | |||||
} else { | |||||
count, _ = strconv.Atoi(countStr) | |||||
} | |||||
r := wechat.GetWechatMaterial(mType, offset, count) | |||||
ctx.JSON(200, response.SuccessWithData(r)) | |||||
} |
@@ -14,24 +14,48 @@ import ( | |||||
// https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Passive_user_reply_message.html | // https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Passive_user_reply_message.html | ||||
func AcceptWechatEvent(ctx *context.Context) { | func AcceptWechatEvent(ctx *context.Context) { | ||||
b, _ := ioutil.ReadAll(ctx.Req.Request.Body) | b, _ := ioutil.ReadAll(ctx.Req.Request.Body) | ||||
we := wechat.WechatEvent{} | |||||
we := wechat.WechatMsg{} | |||||
xml.Unmarshal(b, &we) | xml.Unmarshal(b, &we) | ||||
switch we.MsgType { | |||||
case wechat.WECHAT_MSG_TYPE_EVENT: | |||||
HandleEventMsg(ctx, we) | |||||
case wechat.WECHAT_MSG_TYPE_TEXT: | |||||
HandleTextMsg(ctx, we) | |||||
} | |||||
log.Info("accept wechat event= %+v", we) | log.Info("accept wechat event= %+v", we) | ||||
var replyStr string | |||||
switch we.Event { | |||||
case wechat.WECHAT_EVENT_SUBSCRIBE, wechat.WECHAT_EVENT_SCAN: | |||||
replyStr = wechat.HandleSubscribeEvent(we) | |||||
break | |||||
} | |||||
// ValidEventSource | |||||
func ValidEventSource(ctx *context.Context) { | |||||
echostr := ctx.Query("echostr") | |||||
ctx.Write([]byte(echostr)) | |||||
return | |||||
} | |||||
func HandleEventMsg(ctx *context.Context, msg wechat.WechatMsg) { | |||||
switch msg.Event { | |||||
case wechat.WECHAT_EVENT_SCAN: | |||||
HandleEventScan(ctx, msg) | |||||
case wechat.WECHAT_EVENT_SUBSCRIBE: | |||||
if msg.EventKey != "" { | |||||
HandleEventScan(ctx, msg) | |||||
} else { | |||||
HandleEventSubscribe(ctx, msg) | |||||
} | |||||
} | } | ||||
} | |||||
func HandleEventScan(ctx *context.Context, msg wechat.WechatMsg) { | |||||
replyStr := wechat.HandleScanEvent(msg) | |||||
if replyStr == "" { | if replyStr == "" { | ||||
log.Info("reply str is empty") | log.Info("reply str is empty") | ||||
return | return | ||||
} | } | ||||
reply := &wechat.EventReply{ | |||||
ToUserName: we.FromUserName, | |||||
FromUserName: we.ToUserName, | |||||
reply := &wechat.MsgReply{ | |||||
ToUserName: msg.FromUserName, | |||||
FromUserName: msg.ToUserName, | |||||
CreateTime: time.Now().Unix(), | CreateTime: time.Now().Unix(), | ||||
MsgType: wechat.WECHAT_MSG_TYPE_TEXT, | MsgType: wechat.WECHAT_MSG_TYPE_TEXT, | ||||
Content: replyStr, | Content: replyStr, | ||||
@@ -39,9 +63,99 @@ func AcceptWechatEvent(ctx *context.Context) { | |||||
ctx.XML(200, reply) | ctx.XML(200, reply) | ||||
} | } | ||||
// ValidEventSource | |||||
func ValidEventSource(ctx *context.Context) { | |||||
echostr := ctx.Query("echostr") | |||||
ctx.Write([]byte(echostr)) | |||||
return | |||||
func HandleEventSubscribe(ctx *context.Context, msg wechat.WechatMsg) { | |||||
r := wechat.HandleSubscribeEvent(msg) | |||||
if r == nil { | |||||
return | |||||
} | |||||
reply := buildReplyContent(msg, r) | |||||
ctx.XML(200, reply) | |||||
} | |||||
func HandleTextMsg(ctx *context.Context, msg wechat.WechatMsg) { | |||||
r := wechat.GetAutomaticReply(msg.Content) | |||||
if r == nil { | |||||
log.Info("TextMsg reply is empty") | |||||
return | |||||
} | |||||
reply := buildReplyContent(msg, r) | |||||
ctx.XML(200, reply) | |||||
} | |||||
func buildReplyContent(msg wechat.WechatMsg, r *wechat.WechatReplyContent) interface{} { | |||||
reply := &wechat.MsgReply{ | |||||
ToUserName: msg.FromUserName, | |||||
FromUserName: msg.ToUserName, | |||||
CreateTime: time.Now().Unix(), | |||||
MsgType: r.ReplyType, | |||||
} | |||||
switch r.ReplyType { | |||||
case wechat.ReplyTypeText: | |||||
return &wechat.TextMsgReply{ | |||||
ToUserName: msg.FromUserName, | |||||
FromUserName: msg.ToUserName, | |||||
CreateTime: time.Now().Unix(), | |||||
MsgType: r.ReplyType, | |||||
Content: r.Reply.Content, | |||||
} | |||||
case wechat.ReplyTypeImage: | |||||
return &wechat.ImageMsgReply{ | |||||
ToUserName: msg.FromUserName, | |||||
FromUserName: msg.ToUserName, | |||||
CreateTime: time.Now().Unix(), | |||||
MsgType: r.ReplyType, | |||||
Image: wechat.ImageContent{ | |||||
MediaId: r.Reply.MediaId, | |||||
}, | |||||
} | |||||
case wechat.ReplyTypeVoice: | |||||
return &wechat.VoiceMsgReply{ | |||||
ToUserName: msg.FromUserName, | |||||
FromUserName: msg.ToUserName, | |||||
CreateTime: time.Now().Unix(), | |||||
MsgType: r.ReplyType, | |||||
Voice: wechat.VoiceContent{ | |||||
MediaId: r.Reply.MediaId, | |||||
}, | |||||
} | |||||
case wechat.ReplyTypeVideo: | |||||
return &wechat.VideoMsgReply{ | |||||
ToUserName: msg.FromUserName, | |||||
FromUserName: msg.ToUserName, | |||||
CreateTime: time.Now().Unix(), | |||||
MsgType: r.ReplyType, | |||||
Video: wechat.VideoContent{ | |||||
MediaId: r.Reply.MediaId, | |||||
Title: r.Reply.Title, | |||||
Description: r.Reply.Description, | |||||
}, | |||||
} | |||||
case wechat.ReplyTypeMusic: | |||||
return &wechat.MusicMsgReply{ | |||||
ToUserName: msg.FromUserName, | |||||
FromUserName: msg.ToUserName, | |||||
CreateTime: time.Now().Unix(), | |||||
MsgType: r.ReplyType, | |||||
Music: wechat.MusicContent{ | |||||
Title: r.Reply.Title, | |||||
Description: r.Reply.Description, | |||||
MusicUrl: r.Reply.MusicUrl, | |||||
HQMusicUrl: r.Reply.HQMusicUrl, | |||||
ThumbMediaId: r.Reply.ThumbMediaId, | |||||
}, | |||||
} | |||||
case wechat.ReplyTypeNews: | |||||
return &wechat.NewsMsgReply{ | |||||
ToUserName: msg.FromUserName, | |||||
FromUserName: msg.ToUserName, | |||||
CreateTime: time.Now().Unix(), | |||||
MsgType: r.ReplyType, | |||||
ArticleCount: len(r.Reply.Articles), | |||||
Articles: wechat.ArticleItem{ | |||||
Item: r.Reply.Articles}, | |||||
} | |||||
} | |||||
return reply | |||||
} | } |
@@ -345,7 +345,9 @@ func ExploreDatasets(ctx *context.Context) { | |||||
var datasetsIds []int64 | var datasetsIds []int64 | ||||
if ownerID > 0 { | if ownerID > 0 { | ||||
datasetsIds = models.GetCollaboratorDatasetIdsByUserID(ownerID) | |||||
collaboratorDatasetsIds := models.GetCollaboratorDatasetIdsByUserID(ownerID) | |||||
teamDatasetsIds := models.GetTeamDatasetIdsByUserID(ownerID) | |||||
datasetsIds = append(collaboratorDatasetsIds, teamDatasetsIds...) | |||||
} | } | ||||
opts := &models.SearchDatasetOptions{ | opts := &models.SearchDatasetOptions{ | ||||
@@ -172,8 +172,8 @@ func DatasetIndex(ctx *context.Context) { | |||||
for _, attachment := range pageAttachments { | for _, attachment := range pageAttachments { | ||||
uploader, _ := models.GetUserByID(attachment.UploaderID) | uploader, _ := models.GetUserByID(attachment.UploaderID) | ||||
attachment.Uploader = uploader | attachment.Uploader = uploader | ||||
if !strings.HasSuffix(attachment.Name, ".zip") { | |||||
attachment.DecompressState = -1 //非zip文件 | |||||
if !strings.HasSuffix(attachment.Name, ".zip") && !strings.HasSuffix(attachment.Name, ".tar.gz") { | |||||
attachment.DecompressState = -1 //非压缩文件 | |||||
} | } | ||||
} | } | ||||
@@ -616,8 +616,14 @@ func DatasetIsCollaborator(ctx *context.Context, dataset *models.Dataset) bool { | |||||
repo.GetOwner() | repo.GetOwner() | ||||
if ctx.User != nil { | if ctx.User != nil { | ||||
if repo.Owner.IsOrganization() { | if repo.Owner.IsOrganization() { | ||||
if repo.Owner.IsUserPartOfOrg(ctx.User.ID) { | |||||
for _, t := range repo.Owner.Teams { | |||||
org := repo.Owner | |||||
org.Teams, err = org.GetUserTeams(ctx.User.ID) | |||||
if err != nil { | |||||
log.Error("GetUserTeams error:", err.Error()) | |||||
return false | |||||
} | |||||
if org.IsUserPartOfOrg(ctx.User.ID) { | |||||
for _, t := range org.Teams { | |||||
if t.IsMember(ctx.User.ID) && t.HasRepository(repo.ID) { | if t.IsMember(ctx.User.ID) && t.HasRepository(repo.ID) { | ||||
return true | return true | ||||
} | } | ||||
@@ -71,6 +71,25 @@ func grampusTrainJobNewDataPrepare(ctx *context.Context, processType string) err | |||||
ctx.Data["images"] = images.Infos | ctx.Data["images"] = images.Infos | ||||
} | } | ||||
grampus.InitSpecialPool() | |||||
ctx.Data["GPUEnabled"] = true | |||||
ctx.Data["NPUEnabled"] = true | |||||
if grampus.SpecialPools != nil { | |||||
for _, pool := range grampus.SpecialPools.Pools { | |||||
if pool.IsExclusive { | |||||
org, _ := models.GetOrgByName(pool.Org) | |||||
if org != nil { | |||||
isOrgMember, _ := models.IsOrganizationMember(org.ID, ctx.User.ID) | |||||
if !isOrgMember { | |||||
ctx.Data[pool.Type+"Enabled"] = false | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
//get valid resource specs | //get valid resource specs | ||||
specs, err := grampus.GetResourceSpecs(processType) | specs, err := grampus.GetResourceSpecs(processType) | ||||
if err != nil { | if err != nil { | ||||
@@ -128,10 +147,18 @@ func GrampusTrainJobGpuCreate(ctx *context.Context, form auth.CreateGrampusTrain | |||||
image := strings.TrimSpace(form.Image) | image := strings.TrimSpace(form.Image) | ||||
if !jobNamePattern.MatchString(displayJobName) { | if !jobNamePattern.MatchString(displayJobName) { | ||||
grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeGPU) | |||||
ctx.RenderWithErr(ctx.Tr("repo.cloudbrain_jobname_err"), tplGrampusTrainJobGPUNew, &form) | ctx.RenderWithErr(ctx.Tr("repo.cloudbrain_jobname_err"), tplGrampusTrainJobGPUNew, &form) | ||||
return | return | ||||
} | } | ||||
errStr := checkSpecialPool(ctx, "GPU") | |||||
if errStr != "" { | |||||
grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeGPU) | |||||
ctx.RenderWithErr(errStr, tplGrampusTrainJobGPUNew, &form) | |||||
return | |||||
} | |||||
//check count limit | //check count limit | ||||
count, err := models.GetGrampusCountByUserID(ctx.User.ID, string(models.JobTypeTrain), models.GPUResource) | count, err := models.GetGrampusCountByUserID(ctx.User.ID, string(models.JobTypeTrain), models.GPUResource) | ||||
if err != nil { | if err != nil { | ||||
@@ -263,6 +290,28 @@ func GrampusTrainJobGpuCreate(ctx *context.Context, form auth.CreateGrampusTrain | |||||
ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/train-job") | ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/train-job") | ||||
} | } | ||||
func checkSpecialPool(ctx *context.Context, resourceType string) string { | |||||
grampus.InitSpecialPool() | |||||
if grampus.SpecialPools != nil { | |||||
for _, pool := range grampus.SpecialPools.Pools { | |||||
if pool.IsExclusive && pool.Type == resourceType { | |||||
org, _ := models.GetOrgByName(pool.Org) | |||||
if org != nil { | |||||
isOrgMember, _ := models.IsOrganizationMember(org.ID, ctx.User.ID) | |||||
if !isOrgMember { | |||||
return ctx.Tr("repo.grampus.no_operate_right") | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
return "" | |||||
} | |||||
func GrampusTrainJobNpuCreate(ctx *context.Context, form auth.CreateGrampusTrainJobForm) { | func GrampusTrainJobNpuCreate(ctx *context.Context, form auth.CreateGrampusTrainJobForm) { | ||||
displayJobName := form.DisplayJobName | displayJobName := form.DisplayJobName | ||||
jobName := util.ConvertDisplayJobNameToJobName(displayJobName) | jobName := util.ConvertDisplayJobNameToJobName(displayJobName) | ||||
@@ -281,10 +330,18 @@ func GrampusTrainJobNpuCreate(ctx *context.Context, form auth.CreateGrampusTrain | |||||
engineName := form.EngineName | engineName := form.EngineName | ||||
if !jobNamePattern.MatchString(displayJobName) { | if !jobNamePattern.MatchString(displayJobName) { | ||||
grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeNPU) | |||||
ctx.RenderWithErr(ctx.Tr("repo.cloudbrain_jobname_err"), tplGrampusTrainJobNPUNew, &form) | ctx.RenderWithErr(ctx.Tr("repo.cloudbrain_jobname_err"), tplGrampusTrainJobNPUNew, &form) | ||||
return | return | ||||
} | } | ||||
errStr := checkSpecialPool(ctx, "NPU") | |||||
if errStr != "" { | |||||
grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeNPU) | |||||
ctx.RenderWithErr(errStr, tplGrampusTrainJobGPUNew, &form) | |||||
return | |||||
} | |||||
//check count limit | //check count limit | ||||
count, err := models.GetGrampusCountByUserID(ctx.User.ID, string(models.JobTypeTrain), models.NPUResource) | count, err := models.GetGrampusCountByUserID(ctx.User.ID, string(models.JobTypeTrain), models.NPUResource) | ||||
if err != nil { | if err != nil { | ||||
@@ -607,7 +664,12 @@ func GrampusGetLog(ctx *context.Context) { | |||||
func generateCommand(repoName, processorType, codeRemotePath, dataRemotePath, bootFile, paramSrc, outputRemotePath, datasetName string) (string, error) { | func generateCommand(repoName, processorType, codeRemotePath, dataRemotePath, bootFile, paramSrc, outputRemotePath, datasetName string) (string, error) { | ||||
var command string | var command string | ||||
command += grampus.CommandPrepareScript | |||||
workDir := grampus.NpuWorkDir | |||||
if processorType == grampus.ProcessorTypeGPU { | |||||
workDir = grampus.GpuWorkDir | |||||
} | |||||
command += "pwd;cd " + workDir + grampus.CommandPrepareScript | |||||
//download code & dataset | //download code & dataset | ||||
if processorType == grampus.ProcessorTypeNPU { | if processorType == grampus.ProcessorTypeNPU { | ||||
commandDownload := "./downloader_for_obs " + setting.Bucket + " " + codeRemotePath + " " + grampus.CodeArchiveName + " " + dataRemotePath + " " + datasetName + ";" | commandDownload := "./downloader_for_obs " + setting.Bucket + " " + codeRemotePath + " " + grampus.CodeArchiveName + " " + dataRemotePath + " " + datasetName + ";" | ||||
@@ -626,7 +688,7 @@ func generateCommand(repoName, processorType, codeRemotePath, dataRemotePath, bo | |||||
if strings.HasSuffix(datasetName, ".tar.gz") { | if strings.HasSuffix(datasetName, ".tar.gz") { | ||||
toolUnzip = "tar -zxvf " | toolUnzip = "tar -zxvf " | ||||
} | } | ||||
commandUnzip := "cd /cache/code;unzip -q master.zip;echo \"start to unzip dataset\";cd /cache/dataset;" + toolUnzip + datasetName + ";" | |||||
commandUnzip := "cd " + workDir + "code;unzip -q master.zip;echo \"start to unzip dataset\";cd " + workDir + "dataset;" + toolUnzip + datasetName + ";" | |||||
command += commandUnzip | command += commandUnzip | ||||
//check unzip result | //check unzip result | ||||
@@ -655,7 +717,7 @@ func generateCommand(repoName, processorType, codeRemotePath, dataRemotePath, bo | |||||
} | } | ||||
} | } | ||||
commandCode := "cd /cache/code/" + strings.ToLower(repoName) + ";python " + bootFile + paramCode + ";" | |||||
commandCode := "cd " + workDir + "code/" + strings.ToLower(repoName) + ";python " + bootFile + paramCode + ";" | |||||
command += commandCode | command += commandCode | ||||
//get exec result | //get exec result | ||||
@@ -664,10 +726,10 @@ func generateCommand(repoName, processorType, codeRemotePath, dataRemotePath, bo | |||||
//upload models | //upload models | ||||
if processorType == grampus.ProcessorTypeNPU { | if processorType == grampus.ProcessorTypeNPU { | ||||
commandUpload := "cd /cache/script_for_grampus/;./uploader_for_obs " + setting.Bucket + " " + outputRemotePath + " " + "/cache/output/;" | |||||
commandUpload := "cd " + workDir + "script_for_grampus/;./uploader_for_obs " + setting.Bucket + " " + outputRemotePath + " " + workDir + "output/;" | |||||
command += commandUpload | command += commandUpload | ||||
} else if processorType == grampus.ProcessorTypeGPU { | } else if processorType == grampus.ProcessorTypeGPU { | ||||
commandUpload := "cd /cache/script_for_grampus/;./uploader_for_minio " + setting.Grampus.Env + " " + outputRemotePath + " " + "/cache/output/;" | |||||
commandUpload := "cd " + workDir + "script_for_grampus/;./uploader_for_minio " + setting.Grampus.Env + " " + outputRemotePath + " " + workDir + "output/;" | |||||
command += commandUpload | command += commandUpload | ||||
} | } | ||||
@@ -10,7 +10,7 @@ import ( | |||||
"github.com/elliotchance/orderedmap" | "github.com/elliotchance/orderedmap" | ||||
) | ) | ||||
var opTypes = []int{1, 2, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31} | |||||
var opTypes = []int{1, 2, 5, 6, 7, 9, 10, 11, 12, 13, 14, 15, 17, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33} | |||||
type ClientsManager struct { | type ClientsManager struct { | ||||
Clients *orderedmap.OrderedMap | Clients *orderedmap.OrderedMap | ||||
@@ -102,7 +102,7 @@ | |||||
</a> | </a> | ||||
{{else if eq .JobType "TRAIN"}} | {{else if eq .JobType "TRAIN"}} | ||||
<a class="title" | <a class="title" | ||||
href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain{{else}}/modelarts{{end}}/train-job/{{$JobID}}" | |||||
href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .Cloudbrain.Type 0}}/cloudbrain{{else if eq .Cloudbrain.Type 1}}/modelarts{{else if eq .Cloudbrain.Type 2}}/grampus{{end}}/train-job/{{$JobID}}" | |||||
title="{{.DisplayJobName}}" style="font-size: 14px;"> | title="{{.DisplayJobName}}" style="font-size: 14px;"> | ||||
<span class="fitted" | <span class="fitted" | ||||
style="width: 90%;vertical-align: middle;">{{.DisplayJobName}}</span> | style="width: 90%;vertical-align: middle;">{{.DisplayJobName}}</span> | ||||
@@ -204,7 +204,7 @@ | |||||
{{else}} | {{else}} | ||||
<a style="padding: 0.5rem 1rem;" id="ai-stop-{{$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" "SUCCEEDED" "STOPPED"}}disabled {{else}} blue {{end}}button" | class="ui basic ai_stop_version {{if eq .Status "KILLED" "FAILED" "START_FAILED" "KILLING" "COMPLETED" "SUCCEEDED" "STOPPED"}}disabled {{else}} blue {{end}}button" | ||||
data-repopath="{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .ComputeResource "CPU/GPU"}}/cloudbrain/train-job{{else}}/modelarts/{{if eq .JobType "INFERENCE"}}inference-job{{else}}train-job{{end}}{{end}}" | |||||
data-repopath="{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .Cloudbrain.Type 0}}/cloudbrain/train-job{{else if eq .Cloudbrain.Type 2}}/grampus/train-job{{else}}/modelarts/{{if eq .JobType "INFERENCE"}}inference-job{{else}}train-job{{end}}{{end}}" | |||||
data-jobid="{{$JobID}}" data-version="{{.VersionName}}"> | data-jobid="{{$JobID}}" data-version="{{.VersionName}}"> | ||||
{{$.i18n.Tr "repo.stop"}} | {{$.i18n.Tr "repo.stop"}} | ||||
</a> | </a> | ||||
@@ -212,7 +212,7 @@ | |||||
</div> | </div> | ||||
<!-- 删除任务 --> | <!-- 删除任务 --> | ||||
<form class="ui compact buttons" id="delForm-{{$JobID}}" | <form class="ui compact buttons" id="delForm-{{$JobID}}" | ||||
action='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .JobType "BENCHMARK"}}/cloudbrain/benchmark{{else if or (eq .JobType "SNN4IMAGENET") (eq .JobType "BRAINSCORE")}}/cloudbrain{{else if eq .JobType "DEBUG"}}{{if eq .ComputeResource "NPU"}}/modelarts/notebook{{else}}/cloudbrain{{end}}{{else if eq .JobType "TRAIN"}}{{if eq .ComputeResource "NPU"}}/modelarts/notebook{{else}}/cloudbrain{{end}}/train-job{{end}}/{{$JobID}}/del?isadminpage=true' | |||||
action='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .JobType "BENCHMARK"}}/cloudbrain/benchmark{{else if or (eq .JobType "SNN4IMAGENET") (eq .JobType "BRAINSCORE")}}/cloudbrain{{else if eq .JobType "DEBUG"}}{{if eq .ComputeResource "NPU"}}/modelarts/notebook{{else}}/cloudbrain{{end}}{{else if eq .JobType "TRAIN"}}{{if eq .Cloudbrain.Type 1}}/modelarts/train-job{{else if eq .Cloudbrain.Type 0}}/cloudbrain/train-job{{else if eq .Cloudbrain.Type 2}}/grampus/train-job{{end}}{{end}}/{{$JobID}}/del?isadminpage=true' | |||||
method="post"> | method="post"> | ||||
{{$.CsrfTokenHtml}} | {{$.CsrfTokenHtml}} | ||||
<a style="padding: 0.5rem 1rem;margin-left:0.2rem" id="ai-delete-{{$JobID}}" | <a style="padding: 0.5rem 1rem;margin-left:0.2rem" id="ai-delete-{{$JobID}}" | ||||
@@ -18,7 +18,7 @@ | |||||
.ui.list .list>.item>img.image+.content, | .ui.list .list>.item>img.image+.content, | ||||
.ui.list>.item>img.image+.content { | .ui.list>.item>img.image+.content { | ||||
width: calc(100% - 30px); | |||||
width: calc(100% - 34px); | |||||
margin-left: 0; | margin-left: 0; | ||||
} | } | ||||
@@ -107,9 +107,9 @@ | |||||
{{range .Repos}} | {{range .Repos}} | ||||
<div class="item"> | <div class="item"> | ||||
{{if .RelAvatarLink}} | {{if .RelAvatarLink}} | ||||
<img class="ui avatar image" src="{{.RelAvatarLink}}"> | |||||
<img class="ui avatar image" style="width: 28px;height: 28px;" src="{{.RelAvatarLink}}"> | |||||
{{else}} | {{else}} | ||||
<img class="ui avatar image" avatar="{{.Owner.Name}}"> | |||||
<img class="ui avatar image" style="width: 28px;height: 28px;" avatar="{{.Owner.Name}}"> | |||||
{{end}} | {{end}} | ||||
<div class="content"> | <div class="content"> | ||||
<div class="ui header"> | <div class="ui header"> | ||||
@@ -89,23 +89,23 @@ | |||||
<svg class="svg" sxmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="none" d="M0 0h24v24H0z"></path><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-2.29-2.333A17.9 17.9 0 0 1 8.027 13H4.062a8.008 8.008 0 0 0 5.648 6.667zM10.03 13c.151 2.439.848 4.73 1.97 6.752A15.905 15.905 0 0 0 13.97 13h-3.94zm9.908 0h-3.965a17.9 17.9 0 0 1-1.683 6.667A8.008 8.008 0 0 0 19.938 13zM4.062 11h3.965A17.9 17.9 0 0 1 9.71 4.333 8.008 8.008 0 0 0 4.062 11zm5.969 0h3.938A15.905 15.905 0 0 0 12 4.248 15.905 15.905 0 0 0 10.03 11zm4.259-6.667A17.9 17.9 0 0 1 15.973 11h3.965a8.008 8.008 0 0 0-5.648-6.667z"></path></svg> | <svg class="svg" sxmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="none" d="M0 0h24v24H0z"></path><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-2.29-2.333A17.9 17.9 0 0 1 8.027 13H4.062a8.008 8.008 0 0 0 5.648 6.667zM10.03 13c.151 2.439.848 4.73 1.97 6.752A15.905 15.905 0 0 0 13.97 13h-3.94zm9.908 0h-3.965a17.9 17.9 0 0 1-1.683 6.667A8.008 8.008 0 0 0 19.938 13zM4.062 11h3.965A17.9 17.9 0 0 1 9.71 4.333 8.008 8.008 0 0 0 4.062 11zm5.969 0h3.938A15.905 15.905 0 0 0 12 4.248 15.905 15.905 0 0 0 10.03 11zm4.259-6.667A17.9 17.9 0 0 1 15.973 11h3.965a8.008 8.008 0 0 0-5.648-6.667z"></path></svg> | ||||
{{.i18n.Tr "cloudbrain.resource_cluster_openi"}} | {{.i18n.Tr "cloudbrain.resource_cluster_openi"}} | ||||
</a> | </a> | ||||
<a class="active item" href="{{.RepoLink}}/grampus/train-job/npu/create"> | |||||
<svg class="svg" sxmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="none" d="M0 0h24v24H0z"></path><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-2.29-2.333A17.9 17.9 0 0 1 8.027 13H4.062a8.008 8.008 0 0 0 5.648 6.667zM10.03 13c.151 2.439.848 4.73 1.97 6.752A15.905 15.905 0 0 0 13.97 13h-3.94zm9.908 0h-3.965a17.9 17.9 0 0 1-1.683 6.667A8.008 8.008 0 0 0 19.938 13zM4.062 11h3.965A17.9 17.9 0 0 1 9.71 4.333 8.008 8.008 0 0 0 4.062 11zm5.969 0h3.938A15.905 15.905 0 0 0 12 4.248 15.905 15.905 0 0 0 10.03 11zm4.259-6.667A17.9 17.9 0 0 1 15.973 11h3.965a8.008 8.008 0 0 0-5.648-6.667z"></path></svg> | |||||
{{.i18n.Tr "cloudbrain.resource_cluster_c2net"}} | |||||
</a> | |||||
<a class="active item" href="{{.RepoLink}}/grampus/train-job/ {{if.NPUEnabled}}npu{{else}}gpu{{end}}/create"> | |||||
<svg class="svg" sxmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="none" d="M0 0h24v24H0z"></path><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-2.29-2.333A17.9 17.9 0 0 1 8.027 13H4.062a8.008 8.008 0 0 0 5.648 6.667zM10.03 13c.151 2.439.848 4.73 1.97 6.752A15.905 15.905 0 0 0 13.97 13h-3.94zm9.908 0h-3.965a17.9 17.9 0 0 1-1.683 6.667A8.008 8.008 0 0 0 19.938 13zM4.062 11h3.965A17.9 17.9 0 0 1 9.71 4.333 8.008 8.008 0 0 0 4.062 11zm5.969 0h3.938A15.905 15.905 0 0 0 12 4.248 15.905 15.905 0 0 0 10.03 11zm4.259-6.667A17.9 17.9 0 0 1 15.973 11h3.965a8.008 8.008 0 0 0-5.648-6.667z"></path></svg> | |||||
{{.i18n.Tr "cloudbrain.resource_cluster_c2net"}}(Beta) | |||||
</a> | |||||
</div> | </div> | ||||
</div> | </div> | ||||
<div class="required unite min_title inline field"> | <div class="required unite min_title inline field"> | ||||
<label style="font-weight: normal;">{{.i18n.Tr "cloudbrain.compute_resource"}}</label> | <label style="font-weight: normal;">{{.i18n.Tr "cloudbrain.compute_resource"}}</label> | ||||
<div class="ui blue mini menu compact selectcloudbrain"> | <div class="ui blue mini menu compact selectcloudbrain"> | ||||
<a class="active item" href="{{.RepoLink}}/grampus/train-job/gpu/create"> | |||||
<a {{if.GPUEnabled}}class="active item" href="{{.RepoLink}}/grampus/train-job/gpu/create"{{else}}href="javascript:return false;" class="item disabled" {{end}}> | |||||
<svg class="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"> | <svg class="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"> | ||||
<path fill="none" d="M0 0h24v24H0z"/> | <path fill="none" d="M0 0h24v24H0z"/> | ||||
<path d="M3 2.992C3 2.444 3.445 2 3.993 2h16.014a1 1 0 0 1 .993.992v18.016a.993.993 0 0 1-.993.992H3.993A1 1 0 0 1 3 21.008V2.992zM19 11V4H5v7h14zm0 2H5v7h14v-7zM9 6h6v2H9V6zm0 9h6v2H9v-2z"/> | <path d="M3 2.992C3 2.444 3.445 2 3.993 2h16.014a1 1 0 0 1 .993.992v18.016a.993.993 0 0 1-.993.992H3.993A1 1 0 0 1 3 21.008V2.992zM19 11V4H5v7h14zm0 2H5v7h14v-7zM9 6h6v2H9V6zm0 9h6v2H9v-2z"/> | ||||
</svg> | </svg> | ||||
CPU/GPU | CPU/GPU | ||||
</a> | </a> | ||||
<a class="item" href="{{.RepoLink}}/grampus/train-job/npu/create"> | |||||
<a {{if.NPUEnabled}}class="item" href="{{.RepoLink}}/grampus/train-job/npu/create"{{else}}href="javascript:return false;" class="item disabled" {{end}} > | |||||
<svg class="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"> | <svg class="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"> | ||||
<path fill="none" d="M0 0h24v24H0z"/> | <path fill="none" d="M0 0h24v24H0z"/> | ||||
<path d="M3 2.992C3 2.444 3.445 2 3.993 2h16.014a1 1 0 0 1 .993.992v18.016a.993.993 0 0 1-.993.992H3.993A1 1 0 0 1 3 21.008V2.992zM19 11V4H5v7h14zm0 2H5v7h14v-7zM9 6h6v2H9V6zm0 9h6v2H9v-2z"/> | <path d="M3 2.992C3 2.444 3.445 2 3.993 2h16.014a1 1 0 0 1 .993.992v18.016a.993.993 0 0 1-.993.992H3.993A1 1 0 0 1 3 21.008V2.992zM19 11V4H5v7h14zm0 2H5v7h14v-7zM9 6h6v2H9V6zm0 9h6v2H9v-2z"/> | ||||
@@ -167,7 +167,7 @@ | |||||
{{template "custom/select_dataset_train" .}} | {{template "custom/select_dataset_train" .}} | ||||
<span class="tooltips" style="margin-left: 11.5rem;margin-bottom: 2rem;">{{.i18n.Tr "repo.grampus.dataset_path_rule"}}</span> | |||||
<span class="tooltips" style="margin-left: 11.5rem;margin-bottom: 2rem;">{{.i18n.Tr "repo.grampus.gpu_dataset_path_rule"}}</span> | |||||
<div class="inline unite min_title field"> | <div class="inline unite min_title field"> | ||||
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.run_parameter"}}</label> | <label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.run_parameter"}}</label> | ||||
<span id="add_run_para" style="margin-left: 0.5rem;cursor:pointer;color: rgba(3, 102, 214, 100);font-size: 14px;line-height: 26px;font-family: SourceHanSansSC-medium;"><i class="plus square outline icon"></i>{{.i18n.Tr "repo.modelarts.train_job.add_run_parameter"}}</span> | <span id="add_run_para" style="margin-left: 0.5rem;cursor:pointer;color: rgba(3, 102, 214, 100);font-size: 14px;line-height: 26px;font-family: SourceHanSansSC-medium;"><i class="plus square outline icon"></i>{{.i18n.Tr "repo.modelarts.train_job.add_run_parameter"}}</span> | ||||
@@ -85,23 +85,23 @@ | |||||
<svg class="svg" sxmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="none" d="M0 0h24v24H0z"></path><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-2.29-2.333A17.9 17.9 0 0 1 8.027 13H4.062a8.008 8.008 0 0 0 5.648 6.667zM10.03 13c.151 2.439.848 4.73 1.97 6.752A15.905 15.905 0 0 0 13.97 13h-3.94zm9.908 0h-3.965a17.9 17.9 0 0 1-1.683 6.667A8.008 8.008 0 0 0 19.938 13zM4.062 11h3.965A17.9 17.9 0 0 1 9.71 4.333 8.008 8.008 0 0 0 4.062 11zm5.969 0h3.938A15.905 15.905 0 0 0 12 4.248 15.905 15.905 0 0 0 10.03 11zm4.259-6.667A17.9 17.9 0 0 1 15.973 11h3.965a8.008 8.008 0 0 0-5.648-6.667z"></path></svg> | <svg class="svg" sxmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="none" d="M0 0h24v24H0z"></path><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-2.29-2.333A17.9 17.9 0 0 1 8.027 13H4.062a8.008 8.008 0 0 0 5.648 6.667zM10.03 13c.151 2.439.848 4.73 1.97 6.752A15.905 15.905 0 0 0 13.97 13h-3.94zm9.908 0h-3.965a17.9 17.9 0 0 1-1.683 6.667A8.008 8.008 0 0 0 19.938 13zM4.062 11h3.965A17.9 17.9 0 0 1 9.71 4.333 8.008 8.008 0 0 0 4.062 11zm5.969 0h3.938A15.905 15.905 0 0 0 12 4.248 15.905 15.905 0 0 0 10.03 11zm4.259-6.667A17.9 17.9 0 0 1 15.973 11h3.965a8.008 8.008 0 0 0-5.648-6.667z"></path></svg> | ||||
{{.i18n.Tr "cloudbrain.resource_cluster_openi"}} | {{.i18n.Tr "cloudbrain.resource_cluster_openi"}} | ||||
</a> | </a> | ||||
<a class="active item" href="{{.RepoLink}}/grampus/train-job/npu/create"> | |||||
<svg class="svg" sxmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="none" d="M0 0h24v24H0z"></path><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-2.29-2.333A17.9 17.9 0 0 1 8.027 13H4.062a8.008 8.008 0 0 0 5.648 6.667zM10.03 13c.151 2.439.848 4.73 1.97 6.752A15.905 15.905 0 0 0 13.97 13h-3.94zm9.908 0h-3.965a17.9 17.9 0 0 1-1.683 6.667A8.008 8.008 0 0 0 19.938 13zM4.062 11h3.965A17.9 17.9 0 0 1 9.71 4.333 8.008 8.008 0 0 0 4.062 11zm5.969 0h3.938A15.905 15.905 0 0 0 12 4.248 15.905 15.905 0 0 0 10.03 11zm4.259-6.667A17.9 17.9 0 0 1 15.973 11h3.965a8.008 8.008 0 0 0-5.648-6.667z"></path></svg> | |||||
{{.i18n.Tr "cloudbrain.resource_cluster_c2net"}}(Beta) | |||||
<a class="active item" href="{{.RepoLink}}/grampus/train-job/ {{if.NPUEnabled}}npu{{else}}gpu{{end}}/create"> | |||||
<svg class="svg" sxmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="none" d="M0 0h24v24H0z"></path><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-2.29-2.333A17.9 17.9 0 0 1 8.027 13H4.062a8.008 8.008 0 0 0 5.648 6.667zM10.03 13c.151 2.439.848 4.73 1.97 6.752A15.905 15.905 0 0 0 13.97 13h-3.94zm9.908 0h-3.965a17.9 17.9 0 0 1-1.683 6.667A8.008 8.008 0 0 0 19.938 13zM4.062 11h3.965A17.9 17.9 0 0 1 9.71 4.333 8.008 8.008 0 0 0 4.062 11zm5.969 0h3.938A15.905 15.905 0 0 0 12 4.248 15.905 15.905 0 0 0 10.03 11zm4.259-6.667A17.9 17.9 0 0 1 15.973 11h3.965a8.008 8.008 0 0 0-5.648-6.667z"></path></svg> | |||||
{{.i18n.Tr "cloudbrain.resource_cluster_c2net"}}(Beta) | |||||
</a> | </a> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
<div class="required unite min_title inline field"> | <div class="required unite min_title inline field"> | ||||
<label style="font-weight: normal;">{{.i18n.Tr "cloudbrain.compute_resource"}}</label> | <label style="font-weight: normal;">{{.i18n.Tr "cloudbrain.compute_resource"}}</label> | ||||
<div class="ui blue mini menu compact selectcloudbrain"> | <div class="ui blue mini menu compact selectcloudbrain"> | ||||
<!--a class="item" href="{{.RepoLink}}/grampus/train-job/gpu/create" type="hidden"> | |||||
<a {{if.GPUEnabled}}class="item" href="{{.RepoLink}}/grampus/train-job/gpu/create"{{else}}href="javascript:return false;" class="item disabled" {{end}}> | |||||
<svg class="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"> | <svg class="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"> | ||||
<path fill="none" d="M0 0h24v24H0z"/> | <path fill="none" d="M0 0h24v24H0z"/> | ||||
<path d="M3 2.992C3 2.444 3.445 2 3.993 2h16.014a1 1 0 0 1 .993.992v18.016a.993.993 0 0 1-.993.992H3.993A1 1 0 0 1 3 21.008V2.992zM19 11V4H5v7h14zm0 2H5v7h14v-7zM9 6h6v2H9V6zm0 9h6v2H9v-2z"/> | <path d="M3 2.992C3 2.444 3.445 2 3.993 2h16.014a1 1 0 0 1 .993.992v18.016a.993.993 0 0 1-.993.992H3.993A1 1 0 0 1 3 21.008V2.992zM19 11V4H5v7h14zm0 2H5v7h14v-7zM9 6h6v2H9V6zm0 9h6v2H9v-2z"/> | ||||
</svg> | </svg> | ||||
CPU/GPU | CPU/GPU | ||||
</a--> | |||||
<a class="active item" href="{{.RepoLink}}/grampus/train-job/npu/create"> | |||||
</a> | |||||
<a {{if.NPUEnabled}}class="active item" href="{{.RepoLink}}/grampus/train-job/npu/create"{{else}}href="javascript:return false;" class="item disabled" {{end}} > | |||||
<svg class="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"> | <svg class="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"> | ||||
<path fill="none" d="M0 0h24v24H0z"/> | <path fill="none" d="M0 0h24v24H0z"/> | ||||
<path d="M3 2.992C3 2.444 3.445 2 3.993 2h16.014a1 1 0 0 1 .993.992v18.016a.993.993 0 0 1-.993.992H3.993A1 1 0 0 1 3 21.008V2.992zM19 11V4H5v7h14zm0 2H5v7h14v-7zM9 6h6v2H9V6zm0 9h6v2H9v-2z"/> | <path d="M3 2.992C3 2.444 3.445 2 3.993 2h16.014a1 1 0 0 1 .993.992v18.016a.993.993 0 0 1-.993.992H3.993A1 1 0 0 1 3 21.008V2.992zM19 11V4H5v7h14zm0 2H5v7h14v-7zM9 6h6v2H9V6zm0 9h6v2H9v-2z"/> | ||||
@@ -1,74 +1,100 @@ | |||||
/** | /** | ||||
* LetterAvatar | * LetterAvatar | ||||
* | |||||
* | |||||
* Artur Heinze | * Artur Heinze | ||||
* Create Letter avatar based on Initials | * Create Letter avatar based on Initials | ||||
* based on https://gist.github.com/leecrossley/6027780 | * based on https://gist.github.com/leecrossley/6027780 | ||||
*/ | */ | ||||
(function(w, d){ | |||||
function LetterAvatar (name, size, color) { | |||||
name = name || ''; | |||||
size = size || 60; | |||||
var colours = [ | |||||
"#1abc9c", "#2ecc71", "#3498db", "#9b59b6", "#34495e", "#16a085", "#27ae60", "#2980b9", "#8e44ad", "#2c3e50", | |||||
"#f1c40f", "#e67e22", "#e74c3c", "#00bcd4", "#95a5a6", "#f39c12", "#d35400", "#c0392b", "#bdc3c7", "#7f8c8d" | |||||
], | |||||
nameSplit = String(name).split(' '), | |||||
initials, charIndex, colourIndex, canvas, context, dataURI; | |||||
if (nameSplit.length == 1) { | |||||
initials = nameSplit[0] ? nameSplit[0].charAt(0):'?'; | |||||
} else { | |||||
initials = nameSplit[0].charAt(0) + nameSplit[1].charAt(0); | |||||
} | |||||
if (w.devicePixelRatio) { | |||||
size = (size * w.devicePixelRatio); | |||||
} | |||||
charIndex = (initials == '?' ? 72 : initials.charCodeAt(0)) - 64; | |||||
colourIndex = charIndex % 20; | |||||
canvas = d.createElement('canvas'); | |||||
canvas.width = size; | |||||
canvas.height = size; | |||||
context = canvas.getContext("2d"); | |||||
context.fillStyle = color ? color : colours[colourIndex - 1]; | |||||
context.fillRect (0, 0, canvas.width, canvas.height); | |||||
context.font = Math.round(canvas.width/2)+"px 'Microsoft Yahei'"; | |||||
context.textAlign = "center"; | |||||
context.fillStyle = "#FFF"; | |||||
context.fillText(initials, size / 2, size / 1.5); | |||||
dataURI = canvas.toDataURL(); | |||||
canvas = null; | |||||
return dataURI; | |||||
(function (w, d) { | |||||
function LetterAvatar(name, size, color) { | |||||
name = name || ""; | |||||
size = size || 60; | |||||
var colours = [ | |||||
"#1abc9c", | |||||
"#2ecc71", | |||||
"#3498db", | |||||
"#9b59b6", | |||||
"#34495e", | |||||
"#16a085", | |||||
"#27ae60", | |||||
"#2980b9", | |||||
"#8e44ad", | |||||
"#2c3e50", | |||||
"#f1c40f", | |||||
"#e67e22", | |||||
"#e74c3c", | |||||
"#00bcd4", | |||||
"#95a5a6", | |||||
"#f39c12", | |||||
"#d35400", | |||||
"#c0392b", | |||||
"#bdc3c7", | |||||
"#7f8c8d", | |||||
], | |||||
nameSplit = String(name).split(" "), | |||||
initials, | |||||
charIndex, | |||||
colourIndex, | |||||
canvas, | |||||
context, | |||||
dataURI; | |||||
if (nameSplit.length == 1) { | |||||
initials = nameSplit[0] ? nameSplit[0].charAt(0) : "?"; | |||||
} else { | |||||
initials = nameSplit[0].charAt(0) + nameSplit[1].charAt(0); | |||||
} | |||||
let initials1 = initials.toUpperCase(); | |||||
if (w.devicePixelRatio) { | |||||
size = size * w.devicePixelRatio; | |||||
} | } | ||||
LetterAvatar.transform = function() { | |||||
Array.prototype.forEach.call(d.querySelectorAll('img[avatar]'), function(img, name, color) { | |||||
name = img.getAttribute('avatar'); | |||||
color = img.getAttribute('color'); | |||||
img.src = LetterAvatar(name, img.getAttribute('width'), color); | |||||
img.removeAttribute('avatar'); | |||||
img.setAttribute('alt', name); | |||||
}); | |||||
}; | |||||
// AMD support | |||||
if (typeof define === 'function' && define.amd) { | |||||
define(function () { return LetterAvatar; }); | |||||
charIndex = (initials == "?" ? 72 : initials.charCodeAt(0)) - 64; | |||||
colourIndex = charIndex % 20; | |||||
canvas = d.createElement("canvas"); | |||||
canvas.width = size; | |||||
canvas.height = size; | |||||
context = canvas.getContext("2d"); | |||||
context.fillStyle = color ? color : colours[colourIndex - 1]; | |||||
context.fillRect(0, 0, canvas.width, canvas.height); | |||||
context.font = Math.round(canvas.width / 2) + "px 'Microsoft Yahei'"; | |||||
context.textAlign = "center"; | |||||
context.fillStyle = "#FFF"; | |||||
context.fillText(initials1, size / 2, size / 1.5); | |||||
dataURI = canvas.toDataURL(); | |||||
canvas = null; | |||||
return dataURI; | |||||
} | |||||
LetterAvatar.transform = function () { | |||||
Array.prototype.forEach.call( | |||||
d.querySelectorAll("img[avatar]"), | |||||
function (img, name, color) { | |||||
name = img.getAttribute("avatar"); | |||||
color = img.getAttribute("color"); | |||||
img.src = LetterAvatar(name, img.getAttribute("width"), color); | |||||
img.removeAttribute("avatar"); | |||||
img.setAttribute("alt", name); | |||||
} | |||||
); | |||||
}; | |||||
// AMD support | |||||
if (typeof define === "function" && define.amd) { | |||||
define(function () { | |||||
return LetterAvatar; | |||||
}); | |||||
// CommonJS and Node.js module support. | // CommonJS and Node.js module support. | ||||
} else if (typeof exports !== 'undefined') { | |||||
// Support Node.js specific `module.exports` (which can be a function) | |||||
if (typeof module != 'undefined' && module.exports) { | |||||
exports = module.exports = LetterAvatar; | |||||
} | |||||
// But always support CommonJS module 1.1.1 spec (`exports` cannot be a function) | |||||
exports.LetterAvatar = LetterAvatar; | |||||
} else { | |||||
window.LetterAvatar = LetterAvatar; | |||||
d.addEventListener('DOMContentLoaded', function(event) { | |||||
LetterAvatar.transform(); | |||||
}); | |||||
} else if (typeof exports !== "undefined") { | |||||
// Support Node.js specific `module.exports` (which can be a function) | |||||
if (typeof module != "undefined" && module.exports) { | |||||
exports = module.exports = LetterAvatar; | |||||
} | } | ||||
})(window, document); | |||||
// But always support CommonJS module 1.1.1 spec (`exports` cannot be a function) | |||||
exports.LetterAvatar = LetterAvatar; | |||||
} else { | |||||
window.LetterAvatar = LetterAvatar; | |||||
d.addEventListener("DOMContentLoaded", function (event) { | |||||
LetterAvatar.transform(); | |||||
}); | |||||
} | |||||
})(window, document); |