Browse Source

merge

tags/v1.22.6.2^2
ychao_1983 3 years ago
parent
commit
762d5144c1
39 changed files with 2598 additions and 1673 deletions
  1. +10
    -8
      models/action.go
  2. +46
    -1
      models/cloudbrain.go
  3. +2
    -2
      models/cloudbrain_static.go
  4. +2
    -6
      models/custom_migrations.go
  5. +2
    -7
      models/repo.go
  6. +13
    -1
      models/user.go
  7. +139
    -0
      modules/auth/wechat/auto_reply.go
  8. +39
    -2
      modules/auth/wechat/client.go
  9. +97
    -4
      modules/auth/wechat/event_handle.go
  10. +13
    -0
      modules/auth/wechat/material.go
  11. +7
    -7
      modules/grampus/grampus.go
  12. +12
    -0
      modules/setting/setting.go
  13. +3
    -1
      options/locale/locale_en-US.ini
  14. +4
    -2
      options/locale/locale_zh-CN.ini
  15. +26
    -55
      public/home/home.js
  16. +1541
    -1152
      public/home/search.js
  17. +1
    -0
      routers/api/v1/api.go
  18. +14
    -7
      routers/api/v1/repo/cloudbrain_dashboard.go
  19. +17
    -1
      routers/api/v1/repo/modelarts.go
  20. +22
    -0
      routers/authentication/wechat.go
  21. +129
    -15
      routers/authentication/wechat_event.go
  22. +14
    -45
      routers/home.go
  23. +10
    -4
      routers/repo/dataset.go
  24. +25
    -8
      routers/repo/grampus.go
  25. +2
    -7
      routers/user/home.go
  26. +0
    -5
      services/repository/repository.go
  27. +3
    -3
      templates/explore/repo_list.tmpl
  28. +1
    -1
      templates/repo/cloudbrain/trainjob/new.tmpl
  29. +1
    -1
      templates/repo/grampus/trainjob/gpu/new.tmpl
  30. +3
    -5
      templates/repo/grampus/trainjob/npu/new.tmpl
  31. +2
    -1
      templates/repo/grampus/trainjob/show.tmpl
  32. +1
    -1
      templates/repo/modelarts/trainjob/index.tmpl
  33. +269
    -239
      templates/repo/modelarts/trainjob/new.tmpl
  34. +16
    -2
      templates/repo/modelarts/trainjob/show.tmpl
  35. +13
    -11
      templates/repo/modelmanage/index.tmpl
  36. +3
    -3
      templates/user/dashboard/cloudbrains.tmpl
  37. +4
    -0
      templates/user/dashboard/feeds.tmpl
  38. +1
    -1
      web_src/js/components/Model.vue
  39. +91
    -65
      web_src/js/features/letteravatar.js

+ 10
- 8
models/action.go View File

@@ -50,14 +50,16 @@ const (
ActionRejectPullRequest // 22
ActionCommentPull // 23

ActionUploadAttachment //24
ActionCreateDebugGPUTask //25
ActionCreateDebugNPUTask //26
ActionCreateTrainTask //27
ActionCreateInferenceTask // 28
ActionCreateBenchMarkTask //29
ActionCreateNewModelTask //30
ActionCreateGPUTrainTask //31
ActionUploadAttachment //24
ActionCreateDebugGPUTask //25
ActionCreateDebugNPUTask //26
ActionCreateTrainTask //27
ActionCreateInferenceTask // 28
ActionCreateBenchMarkTask //29
ActionCreateNewModelTask //30
ActionCreateGPUTrainTask //31
ActionCreateGrampusNPUTrainTask //32
ActionCreateGrampusGPUTrainTask //33
)

// Action represents user operation type and other information to


+ 46
- 1
models/cloudbrain.go View File

@@ -107,6 +107,7 @@ const (
GrampusStatusSucceeded = "SUCCEEDED"
GrampusStatusStopped = "STOPPED"
GrampusStatusUnknown = "UNKNOWN"
GrampusStatusWaiting = "WAITING"
)

type Cloudbrain struct {
@@ -1434,6 +1435,9 @@ func QueryModelTrainJobList(repoId int64) ([]*CloudbrainInfo, int, error) {
cond = cond.And(
builder.Eq{"job_type": "TRAIN"},
)
cond = cond.And(
builder.In("type", 0, 1),
)

cloudbrains := make([]*CloudbrainInfo, 0)
if err := sess.Select("job_id,display_job_name").Table(&Cloudbrain{}).Where(cond).OrderBy("created_unix DESC").
@@ -1742,7 +1746,7 @@ func GetCloudbrainInferenceJobCountByUserID(userID int64) (int, error) {
}

func GetGrampusCountByUserID(userID int64, jobType, computeResource string) (int, error) {
count, err := x.In("status", GrampusStatusPending, GrampusStatusRunning).And("job_type = ? and user_id = ? and type = ?", jobType, userID, TypeC2Net).And("compute_resource = ?", computeResource).Count(new(Cloudbrain))
count, err := x.In("status", GrampusStatusWaiting, GrampusStatusRunning).And("job_type = ? and user_id = ? and type = ?", jobType, userID, TypeC2Net).And("compute_resource = ?", computeResource).Count(new(Cloudbrain))
return int(count), err
}

@@ -1910,3 +1914,44 @@ func CloudbrainAll(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int64, error) {

return cloudbrains, count, nil
}
func CloudbrainAllStatic(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int64, error) {
sess := x.NewSession()
defer sess.Close()

var cond = builder.NewCond()

if (opts.Type) >= 0 {
cond = cond.And(
builder.Eq{"cloudbrain.type": opts.Type},
)
}
if opts.BeginTimeUnix > 0 && opts.EndTimeUnix > 0 {
cond = cond.And(
builder.And(builder.Gte{"cloudbrain.created_unix": opts.BeginTimeUnix}, builder.Lte{"cloudbrain.created_unix": opts.EndTimeUnix}),
)
}
var count int64
var err error
count, err = sess.Unscoped().Where(cond).Count(new(Cloudbrain))

if err != nil {
return nil, 0, fmt.Errorf("Count: %v", err)
}

if opts.Page >= 0 && opts.PageSize > 0 {
var start int
if opts.Page == 0 {
start = 0
} else {
start = (opts.Page - 1) * opts.PageSize
}
sess.Limit(opts.PageSize, start)
}
sess.OrderBy("cloudbrain.created_unix DESC")
cloudbrains := make([]*CloudbrainInfo, 0, setting.UI.IssuePagingNum)
if err := sess.Cols("status", "type", "job_type", "train_job_duration", "duration", "compute_resource", "created_unix", "start_time", "end_time").Table(&Cloudbrain{}).Unscoped().Where(cond).
Find(&cloudbrains); err != nil {
return nil, 0, fmt.Errorf("Find: %v", err)
}
return cloudbrains, count, nil
}

+ 2
- 2
models/cloudbrain_static.go View File

@@ -213,7 +213,7 @@ func GetWaittingTop() ([]*CloudbrainInfo, error) {
cond = cond.And(
builder.Eq{"cloudbrain.status": string(JobWaiting)},
)
sess.OrderBy("(cloudbrain.start_time-cloudbrain.created_unix) DESC limit 10")
sess.OrderBy("cloudbrain.created_unix ASC limit 10")
cloudbrains := make([]*CloudbrainInfo, 0, 10)
if err := sess.Table(&Cloudbrain{}).Where(cond).
Find(&cloudbrains); err != nil {
@@ -228,7 +228,7 @@ func GetRunningTop() ([]*CloudbrainInfo, error) {
cond = cond.And(
builder.Eq{"cloudbrain.status": string(JobRunning)},
)
sess.OrderBy("(cloudbrain.end_time-cloudbrain.start_time) DESC limit 10")
sess.OrderBy("cloudbrain.duration DESC limit 10")
cloudbrains := make([]*CloudbrainInfo, 0, 10)
if err := sess.Table(&Cloudbrain{}).Where(cond).
Find(&cloudbrains); err != nil {


+ 2
- 6
models/custom_migrations.go View File

@@ -15,13 +15,9 @@ type CustomMigrationStatic struct {
Migrate func(*xorm.Engine, *xorm.Engine) error
}

var customMigrations = []CustomMigration{
{"Custom v1 Topic struct change to support chinese", syncTopicStruct},
}
var customMigrations []CustomMigration

var customMigrationsStatic = []CustomMigrationStatic{
{"update issue_fixed_rate to 1 if num_issues is 0 ", updateIssueFixedRate},
}
var customMigrationsStatic []CustomMigrationStatic

func MigrateCustom(x *xorm.Engine) {



+ 2
- 7
models/repo.go View File

@@ -2749,15 +2749,10 @@ func ReadLatestFileInRepo(userName, repoName, refName, treePath string) (*RepoFi
log.Error("ReadLatestFileInRepo: Close: %v", err)
}
}()

buf := make([]byte, 1024)
n, _ := reader.Read(buf)
if n >= 0 {
buf = buf[:n]
}
d, _ := ioutil.ReadAll(reader)
commitId := ""
if blob != nil {
commitId = fmt.Sprint(blob.ID)
}
return &RepoFile{CommitId: commitId, Content: buf}, nil
return &RepoFile{CommitId: commitId, Content: d}, nil
}

+ 13
- 1
models/user.go View File

@@ -6,7 +6,6 @@
package models

import (
"code.gitea.io/gitea/modules/blockchain"
"container/list"
"context"
"crypto/md5"
@@ -25,6 +24,8 @@ import (
"time"
"unicode/utf8"

"code.gitea.io/gitea/modules/blockchain"

"code.gitea.io/gitea/modules/avatar"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/generate"
@@ -1498,6 +1499,17 @@ func GetUsersByIDs(ids []int64) (UserList, error) {
return ous, err
}

func GetUsersByNames(names []string) (UserList, error) {
ous := make([]*User, 0, len(names))
if len(names) == 0 {
return ous, nil
}
err := x.In("name", names).
Asc("name").
Find(&ous)
return ous, err
}

// GetUserIDsByNames returns a slice of ids corresponds to names.
func GetUserIDsByNames(names []string, ignoreNonExistent bool) ([]int64, error) {
ids := make([]int64, 0, len(names))


+ 139
- 0
modules/auth/wechat/auto_reply.go View File

@@ -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
}

+ 39
- 2
modules/auth/wechat/client.go View File

@@ -17,7 +17,8 @@ var (
const (
GRANT_TYPE = "client_credential"
ACCESS_TOKEN_PATH = "/cgi-bin/token"
QR_CODE_Path = "/cgi-bin/qrcode/create"
QR_CODE_PATH = "/cgi-bin/qrcode/create"
GET_MATERIAL_PATH = "/cgi-bin/material/batchget_material"
ACTION_QR_STR_SCENE = "QR_STR_SCENE"

ERR_CODE_ACCESSTOKEN_EXPIRE = 42001
@@ -40,6 +41,11 @@ type QRCodeRequest struct {
Action_info ActionInfo `json:"action_info"`
Expire_seconds int `json:"expire_seconds"`
}
type MaterialRequest struct {
Type string `json:"type"`
Offset int `json:"offset"`
Count int `json:"count"`
}

type ActionInfo struct {
Scene Scene `json:"scene"`
@@ -97,7 +103,7 @@ func callQRCodeCreate(sceneStr string) (*QRCodeResponse, bool) {
SetQueryParam("access_token", GetWechatAccessToken()).
SetBody(bodyJson).
SetResult(&result).
Post(setting.WechatApiHost + QR_CODE_Path)
Post(setting.WechatApiHost + QR_CODE_PATH)
if err != nil {
log.Error("create QR code failed,e=%v", err)
return nil, false
@@ -113,6 +119,37 @@ func callQRCodeCreate(sceneStr string) (*QRCodeResponse, bool) {
return &result, false
}

//getMaterial
// api doc: https://developers.weixin.qq.com/doc/offiaccount/Asset_Management/Get_materials_list.html
func getMaterial(mType string, offset, count int) (interface{}, bool) {
client := getWechatRestyClient()

body := &MaterialRequest{
Type: mType,
Offset: offset,
Count: count,
}
bodyJson, _ := json.Marshal(body)
r, err := client.R().
SetHeader("Content-Type", "application/json").
SetQueryParam("access_token", GetWechatAccessToken()).
SetBody(bodyJson).
Post(setting.WechatApiHost + GET_MATERIAL_PATH)
if err != nil {
log.Error("create QR code failed,e=%v", err)
return nil, false
}
a := r.Body()
resultMap := make(map[string]interface{}, 0)
json.Unmarshal(a, &resultMap)
errcode := resultMap["errcode"]
if errcode == fmt.Sprint(ERR_CODE_ACCESSTOKEN_EXPIRE) || errcode == fmt.Sprint(ERR_CODE_ACCESSTOKEN_INVALID) {
return nil, true
}
log.Info("%v", r)
return &resultMap, false
}

func getErrorCodeFromResponse(r *resty.Response) int {
a := r.Body()
resultMap := make(map[string]interface{}, 0)


+ 97
- 4
modules/auth/wechat/event_handle.go View File

@@ -18,7 +18,7 @@ import (
// <EventKey><![CDATA[SCENE_VALUE]]></EventKey>
// <Ticket><![CDATA[TICKET]]></Ticket>
//</xml>
type WechatEvent struct {
type WechatMsg struct {
ToUserName string
FromUserName string
CreateTime int64
@@ -26,9 +26,13 @@ type WechatEvent struct {
Event string
EventKey string
Ticket string
Content string
MsgId string
MsgDataId string
Idx string
}

type EventReply struct {
type MsgReply struct {
XMLName xml.Name `xml:"xml"`
ToUserName string
FromUserName string
@@ -37,16 +41,97 @@ type EventReply struct {
Content string
}

type TextMsgReply struct {
XMLName xml.Name `xml:"xml"`
ToUserName string
FromUserName string
CreateTime int64
MsgType string
Content string
}
type ImageMsgReply struct {
XMLName xml.Name `xml:"xml"`
ToUserName string
FromUserName string
CreateTime int64
MsgType string
Image ImageContent
}
type VoiceMsgReply struct {
XMLName xml.Name `xml:"xml"`
ToUserName string
FromUserName string
CreateTime int64
MsgType string
Voice VoiceContent
}
type VideoMsgReply struct {
XMLName xml.Name `xml:"xml"`
ToUserName string
FromUserName string
CreateTime int64
MsgType string
Video VideoContent
}
type MusicMsgReply struct {
XMLName xml.Name `xml:"xml"`
ToUserName string
FromUserName string
CreateTime int64
MsgType string
Music MusicContent
}
type NewsMsgReply struct {
XMLName xml.Name `xml:"xml"`
ToUserName string
FromUserName string
CreateTime int64
MsgType string
ArticleCount int
Articles ArticleItem
}

type ArticleItem struct {
Item []ArticlesContent
}

type ImageContent struct {
MediaId string
}
type VoiceContent struct {
MediaId string
}
type VideoContent struct {
MediaId string
Title string
Description string
}
type MusicContent struct {
Title string
Description string
MusicUrl string
HQMusicUrl string
ThumbMediaId string
}
type ArticlesContent struct {
XMLName xml.Name `xml:"item"`
Title string
Description string
PicUrl string
Url string
}

const (
WECHAT_EVENT_SUBSCRIBE = "subscribe"
WECHAT_EVENT_SCAN = "SCAN"
)

const (
WECHAT_MSG_TYPE_TEXT = "text"
WECHAT_MSG_TYPE_TEXT = "text"
WECHAT_MSG_TYPE_EVENT = "event"
)

func HandleSubscribeEvent(we WechatEvent) string {
func HandleScanEvent(we WechatMsg) string {
eventKey := we.EventKey
if eventKey == "" {
return ""
@@ -74,3 +159,11 @@ func HandleSubscribeEvent(we WechatEvent) string {

return BIND_REPLY_SUCCESS
}

func HandleSubscribeEvent(we WechatMsg) *WechatReplyContent {
r, err := LoadReplyFromCacheAndDisk(SubscribeReply)
if err != nil || len(r) == 0 {
return nil
}
return r[0]
}

+ 13
- 0
modules/auth/wechat/material.go View File

@@ -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
}

+ 7
- 7
modules/grampus/grampus.go View File

@@ -19,8 +19,8 @@ const (
ProcessorTypeNPU = "npu.huawei.com/NPU"
ProcessorTypeGPU = "nvidia.com/gpu"

CommandPrepareScript = "pwd;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;"
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;" +
@@ -101,7 +101,7 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error
ImageUrl: req.ImageUrl,
CenterID: CenterID,
CenterName: CenterName,
ReplicaNum: 0,
ReplicaNum: 1,
},
},
})
@@ -149,9 +149,9 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error

var actionType models.ActionType
if req.ComputeResource == models.NPUResource {
actionType = models.ActionCreateTrainTask
actionType = models.ActionCreateGrampusNPUTrainTask
} else if req.ComputeResource == models.GPUResource {
actionType = models.ActionCreateGPUTrainTask
actionType = models.ActionCreateGrampusGPUTrainTask
}
notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, jobID, req.DisplayJobName, actionType)

@@ -159,8 +159,8 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error
}

func TransTrainJobStatus(status string) string {
if status == "pending" {
status = "waiting"
if status == models.GrampusStatusPending {
status = models.GrampusStatusWaiting
}

return strings.ToUpper(status)


+ 12
- 0
modules/setting/setting.go View File

@@ -554,6 +554,13 @@ var (
WechatQRCodeExpireSeconds int
WechatAuthSwitch bool

//wechat auto reply config
UserNameOfWechatReply string
RepoNameOfWechatReply string
RefNameOfWechatReply string
TreePathOfAutoMsgReply string
TreePathOfSubscribe string

//nginx proxy
PROXYURL string
RadarMap = struct {
@@ -1381,6 +1388,11 @@ func NewContext() {
WechatAppSecret = sec.Key("APP_SECRET").MustString("e48e13f315adc32749ddc7057585f198")
WechatQRCodeExpireSeconds = sec.Key("QR_CODE_EXPIRE_SECONDS").MustInt(120)
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()



+ 3
- 1
options/locale/locale_en-US.ini View File

@@ -1177,7 +1177,7 @@ model.manage.model_manage = ModelManage
model.manage.model_accuracy = Model Accuracy

grampus.train_job.ai_center = AI Center
grampus.dataset_path_rule = The code is storaged in /tmp/code;the dataset is storaged in /tmp/dataset;and please put your model into /tmp/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.no_operate_right = You have no right to do this operation.

template.items = Template Items
@@ -2939,6 +2939,8 @@ task_inferencejob=`created reasoning task <a href="%s/modelarts/inference-job/%s
task_benchmark=`created profiling task <a href="%s/cloudbrain/benchmark/%s">%s</a>`
task_createmodel=`created new model <a href="%s/modelmanage/show_model_info?name=%s">%s</a>`
task_gputrainjob=`created CPU/GPU training task<a href="%s/cloudbrain/train-job/%s">%s</a>`
task_c2netnputrainjob=`created NPU training task<a href="%s/grampus/train-job/%s">%s</a>`
task_c2netgputrainjob=`created CPU/GPU training task<a href="%s/grampus/train-job/%s">%s</a>`

[tool]
ago = %s ago


+ 4
- 2
options/locale/locale_zh-CN.ini View File

@@ -1191,7 +1191,7 @@ model.manage.model_manage = 模型管理
model.manage.model_accuracy = 模型精度

grampus.train_job.ai_center=智算中心
grampus.dataset_path_rule = 训练脚本存储在/tmp/code中,数据集存储在/tmp/dataset中,训练输出请存储在/tmp/output中以供后续下载。
grampus.dataset_path_rule = 训练脚本存储在/cache/code中,数据集存储在/cache/dataset中,训练输出请存储在/cache/output中以供后续下载。

grampus.no_operate_right = 您没有权限创建这类任务。

@@ -1425,7 +1425,7 @@ issues.label_templates.helper=选择标签模板
issues.label_templates.use=使用标签集
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.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.change_milestone_at=`%[3]s 修改了里程碑从 <b>%[1]s</b> 到 <b>%[2]s</b>`
issues.remove_milestone_at=`%[2]s 删除了里程碑 <b>%[1]s</b>`
@@ -2955,6 +2955,8 @@ task_inferencejob=`创建了推理任务 <a href="%s/modelarts/inference-job/%s"
task_benchmark=`创建了评测任务 <a href="%s/cloudbrain/benchmark/%s">%s</a>`
task_createmodel=`导入了新模型 <a href="%s/modelmanage/show_model_info?name=%s">%s</a>`
task_gputrainjob=`创建了CPU/GPU类型训练任务 <a href="%s/cloudbrain/train-job/%s">%s</a>`
task_c2netnputrainjob=`创建了NPU类型训练任务 <a href="%s/grampus/train-job/%s">%s</a>`
task_c2netgputrainjob=`创建了CPU/GPU类型训练任务 <a href="%s/grampus/train-job/%s">%s</a>`

[tool]
ago=%s前


+ 26
- 55
public/home/home.js View File

@@ -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 html =document.documentElement;
var lang = html.attributes["lang"]
var isZh = true;
if(lang != null && lang.nodeValue =="en-US" ){
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 html = "";
if (data != null){
@@ -176,7 +178,10 @@ socket.onmessage = function (e) {
output.innerHTML = html;
swiperNewMessage.updateSlides();
swiperNewMessage.updateProgress();
};
};
}



function getTaskLink(record){
var re = getRepoLink(record);
@@ -217,9 +222,9 @@ function refresh3DInfo(record){
//console.log("cloudbrain two line length=" + lines.length);
var span = $('.rotation3D__line').find("span")[1];
//console.log(span);
span.innerText =record.RefName;
//$('.rotation3D__line').find("span").eq(1).text(record.RefName)
//lines[1].find("span").text(record.RefName);
if(span != null){
span.innerText =record.RefName;
}
}

}
@@ -437,7 +442,9 @@ function getAction(opType,isZh){
}
}

queryRecommendData();




function queryRecommendData(){
$.ajax({
@@ -452,48 +459,12 @@ function queryRecommendData(){
displayOrg(json.org);
displayRepo(json.repo);
displayActivity(json.image);
displayCloudBrain(json.cloudbrain)
},
error:function(response) {
}
});

// $.ajax({
// type:"GET",
// url:"/recommend/repo",
// headers: {
// authorization:token,
// },
// dataType:"json",
// async:false,
// success:function(json){
// displayRepo(json);
// },
// error:function(response) {
// }
// });

// $.ajax({
// type:"GET",
// url:"/recommend/imageinfo",
// headers: {
// authorization:token,
// },
// dataType:"json",
// async:false,
// success:function(json){
// displayActivity(json);
// },
// error:function(response) {
// }
// });
}

function displayCloudBrain(json){
$('#completed_task').text(json.completed_task);
$('#running_task').text(json.running_task);
$('#wait_task').text(json.wait_task);
}

function displayActivity(json){
var activityDiv = document.getElementById("recommendactivity");


+ 1541
- 1152
public/home/search.js
File diff suppressed because it is too large
View File


+ 1
- 0
routers/api/v1/api.go View File

@@ -1055,6 +1055,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/prd/event", authentication.ValidEventSource)
m.Post("/prd/event", authentication.AcceptWechatEvent)
})
m.Get("/wechat/material", authentication.GetMaterial)
}, securityHeaders(), context.APIContexter(), sudo())
}



+ 14
- 7
routers/api/v1/repo/cloudbrain_dashboard.go View File

@@ -67,7 +67,7 @@ func GetAllCloudbrainsOverview(ctx *context.Context) {
pagesize := 1000
count := pagesize
for count == pagesize && count != 0 {
cloudbrains, _, err := models.CloudbrainAll(&models.CloudbrainsOptions{
cloudbrains, _, err := models.CloudbrainAllStatic(&models.CloudbrainsOptions{
ListOptions: models.ListOptions{
Page: page,
PageSize: pagesize,
@@ -121,6 +121,13 @@ func GetAllCloudbrainsOverview(ctx *context.Context) {
}
}

cloudBrainTypeList := []int{0, 1, 2}
for _, v := range cloudBrainTypeList {
if _, ok := cloudBrainNum[v]; !ok {
cloudBrainNum[v] = 0
}
}

todayRunningCount := todayStatusResult[string(models.JobRunning)]
todayCompletedCount := todayStatusResult[string(models.ModelArtsTrainJobCompleted)] + todayStatusResult[string(models.JobFailed)] +
todayStatusResult[string(models.ModelArtsStartFailed)] + todayStatusResult[string(models.JobStopped)] + todayStatusResult[string(models.JobSucceeded)] + todayStatusResult[string(models.ModelArtsTrainJobKilled)]
@@ -504,7 +511,7 @@ func GetAllCloudbrainsPeriodDistribution(ctx *context.Context) {
count := pagesize
//Each time a maximum of 1000 pieces of data are detected to the memory, batch processing
for count == pagesize && count != 0 {
cloudbrains, _, err := models.CloudbrainAll(&models.CloudbrainsOptions{
cloudbrains, _, err := models.CloudbrainAllStatic(&models.CloudbrainsOptions{
ListOptions: models.ListOptions{
Page: page,
PageSize: pagesize,
@@ -613,7 +620,7 @@ func GetCloudbrainsStatusAnalysis(ctx *context.Context) {
pagesize := 1000
count := pagesize
for count == pagesize && count != 0 {
cloudbrains, _, err := models.CloudbrainAll(&models.CloudbrainsOptions{
cloudbrains, _, err := models.CloudbrainAllStatic(&models.CloudbrainsOptions{
ListOptions: models.ListOptions{
Page: page,
PageSize: pagesize,
@@ -1021,7 +1028,7 @@ func getCloudbrainCount(beginTime time.Time, endTime time.Time, cloudbrains []*m
func getDayCloudbrainNum(beginTime time.Time, endTime time.Time) ([]DateCloudbrainNum, error) {
var endTimeTemp time.Time
endTimeTemp = beginTime.AddDate(0, 0, 1)
cloudbrains, _, err := models.CloudbrainAll(&models.CloudbrainsOptions{
cloudbrains, _, err := models.CloudbrainAllStatic(&models.CloudbrainsOptions{
Type: models.TypeCloudBrainAll,
BeginTimeUnix: beginTime.Unix(),
EndTimeUnix: endTime.Unix(),
@@ -1057,7 +1064,7 @@ func getMonthCloudbrainNum(beginTime time.Time, endTime time.Time) ([]DateCloudb
endTimeTemp = beginTime.AddDate(0, 1, 0)
endTimeTemp = time.Date(endTimeTemp.Year(), endTimeTemp.Month(), 1, 0, 0, 0, 0, now.Location())
monthCloudbrainNum := make([]DateCloudbrainNum, 0)
cloudbrains, _, err := models.CloudbrainAll(&models.CloudbrainsOptions{
cloudbrains, _, err := models.CloudbrainAllStatic(&models.CloudbrainsOptions{
Type: models.TypeCloudBrainAll,
BeginTimeUnix: beginTime.Unix(),
EndTimeUnix: endTime.Unix(),
@@ -1093,7 +1100,7 @@ func getDayCloudbrainInfo(beginTime time.Time, endTime time.Time) ([]DateCloudbr
if endTimeTemp.Equal(endTime) {
endTimeTemp = endTimeTemp.AddDate(0, 0, -1)
}
cloudbrains, _, err := models.CloudbrainAll(&models.CloudbrainsOptions{
cloudbrains, _, err := models.CloudbrainAllStatic(&models.CloudbrainsOptions{
Type: models.TypeCloudBrainAll,
BeginTimeUnix: beginTime.Unix(),
EndTimeUnix: endTime.Unix(),
@@ -1124,7 +1131,7 @@ func getMonthCloudbrainInfo(beginTime time.Time, endTime time.Time) ([]DateCloud
if endTimeTemp.Equal(endTime) {
endTimeTemp = endTimeTemp.AddDate(0, -1, 0)
}
cloudbrains, _, err := models.CloudbrainAll(&models.CloudbrainsOptions{
cloudbrains, _, err := models.CloudbrainAllStatic(&models.CloudbrainsOptions{
Type: models.TypeCloudBrainAll,
BeginTimeUnix: beginTime.Unix(),
EndTimeUnix: endTime.Unix(),


+ 17
- 1
routers/api/v1/repo/modelarts.go View File

@@ -127,7 +127,8 @@ func GetModelArtsTrainJob(ctx *context.APIContext) {

func GetModelArtsTrainJobVersion(ctx *context.APIContext) {
var (
err error
err error
aiCenterName string
)

jobID := ctx.Params(":jobid")
@@ -210,6 +211,20 @@ func GetModelArtsTrainJobVersion(ctx *context.APIContext) {
job.EndTime = job.StartTime.Add(job.Duration)
}
job.CorrectCreateUnix()

if len(job.AiCenter) == 0 {
if len(result.JobInfo.Tasks) > 0 {
if len(result.JobInfo.Tasks[0].CenterID) > 0 && len(result.JobInfo.Tasks[0].CenterName) > 0 {
job.AiCenter = result.JobInfo.Tasks[0].CenterID[0] + "+" + result.JobInfo.Tasks[0].CenterName[0]
aiCenterName = result.JobInfo.Tasks[0].CenterName[0]
}
}
} else {
temp := strings.Split(job.AiCenter, "+")
if len(temp) > 1 {
aiCenterName = temp[1]
}
}
err = models.UpdateTrainJobVersion(job)
if err != nil {
log.Error("UpdateJob failed:", err)
@@ -220,6 +235,7 @@ func GetModelArtsTrainJobVersion(ctx *context.APIContext) {
"JobID": jobID,
"JobStatus": job.Status,
"JobDuration": job.TrainJobDuration,
"AiCenter": aiCenterName,
})

}


+ 22
- 0
routers/authentication/wechat.go View File

@@ -8,9 +8,11 @@ import (
"code.gitea.io/gitea/modules/redis/redis_client"
"code.gitea.io/gitea/modules/redis/redis_key"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/routers/response"
"encoding/json"
"errors"
gouuid "github.com/satori/go.uuid"
"strconv"
"time"
)

@@ -124,3 +126,23 @@ func createQRCode4Bind(userId int64) (*QRCodeResponse, error) {
}
return result, nil
}

// GetMaterial
func GetMaterial(ctx *context.Context) {
mType := ctx.Query("type")
offsetStr := ctx.Query("offset")
countStr := ctx.Query("count")
var offset, count int
if offsetStr == "" {
offset = 0
} else {
offset, _ = strconv.Atoi(offsetStr)
}
if countStr == "" {
count = 20
} else {
count, _ = strconv.Atoi(countStr)
}
r := wechat.GetWechatMaterial(mType, offset, count)
ctx.JSON(200, response.SuccessWithData(r))
}

+ 129
- 15
routers/authentication/wechat_event.go View File

@@ -14,24 +14,48 @@ import (
// https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Passive_user_reply_message.html
func AcceptWechatEvent(ctx *context.Context) {
b, _ := ioutil.ReadAll(ctx.Req.Request.Body)
we := wechat.WechatEvent{}
we := wechat.WechatMsg{}
xml.Unmarshal(b, &we)

switch we.MsgType {
case wechat.WECHAT_MSG_TYPE_EVENT:
HandleEventMsg(ctx, we)
case wechat.WECHAT_MSG_TYPE_TEXT:
HandleTextMsg(ctx, we)
}
log.Info("accept wechat event= %+v", we)
var replyStr string
switch we.Event {
case wechat.WECHAT_EVENT_SUBSCRIBE, wechat.WECHAT_EVENT_SCAN:
replyStr = wechat.HandleSubscribeEvent(we)
break

}

// ValidEventSource
func ValidEventSource(ctx *context.Context) {
echostr := ctx.Query("echostr")
ctx.Write([]byte(echostr))
return
}

func HandleEventMsg(ctx *context.Context, msg wechat.WechatMsg) {
switch msg.Event {
case wechat.WECHAT_EVENT_SCAN:
HandleEventScan(ctx, msg)
case wechat.WECHAT_EVENT_SUBSCRIBE:
if msg.EventKey != "" {
HandleEventScan(ctx, msg)
} else {
HandleEventSubscribe(ctx, msg)
}

}
}

func HandleEventScan(ctx *context.Context, msg wechat.WechatMsg) {
replyStr := wechat.HandleScanEvent(msg)
if replyStr == "" {
log.Info("reply str is empty")
return
}
reply := &wechat.EventReply{
ToUserName: we.FromUserName,
FromUserName: we.ToUserName,
reply := &wechat.MsgReply{
ToUserName: msg.FromUserName,
FromUserName: msg.ToUserName,
CreateTime: time.Now().Unix(),
MsgType: wechat.WECHAT_MSG_TYPE_TEXT,
Content: replyStr,
@@ -39,9 +63,99 @@ func AcceptWechatEvent(ctx *context.Context) {
ctx.XML(200, reply)
}

// ValidEventSource
func ValidEventSource(ctx *context.Context) {
echostr := ctx.Query("echostr")
ctx.Write([]byte(echostr))
return
func HandleEventSubscribe(ctx *context.Context, msg wechat.WechatMsg) {
r := wechat.HandleSubscribeEvent(msg)
if r == nil {
return
}
reply := buildReplyContent(msg, r)
ctx.XML(200, reply)
}

func HandleTextMsg(ctx *context.Context, msg wechat.WechatMsg) {
r := wechat.GetAutomaticReply(msg.Content)
if r == nil {
log.Info("TextMsg reply is empty")
return
}
reply := buildReplyContent(msg, r)
ctx.XML(200, reply)
}

func buildReplyContent(msg wechat.WechatMsg, r *wechat.WechatReplyContent) interface{} {
reply := &wechat.MsgReply{
ToUserName: msg.FromUserName,
FromUserName: msg.ToUserName,
CreateTime: time.Now().Unix(),
MsgType: r.ReplyType,
}
switch r.ReplyType {
case wechat.ReplyTypeText:
return &wechat.TextMsgReply{
ToUserName: msg.FromUserName,
FromUserName: msg.ToUserName,
CreateTime: time.Now().Unix(),
MsgType: r.ReplyType,
Content: r.Reply.Content,
}

case wechat.ReplyTypeImage:
return &wechat.ImageMsgReply{
ToUserName: msg.FromUserName,
FromUserName: msg.ToUserName,
CreateTime: time.Now().Unix(),
MsgType: r.ReplyType,
Image: wechat.ImageContent{
MediaId: r.Reply.MediaId,
},
}
case wechat.ReplyTypeVoice:
return &wechat.VoiceMsgReply{
ToUserName: msg.FromUserName,
FromUserName: msg.ToUserName,
CreateTime: time.Now().Unix(),
MsgType: r.ReplyType,
Voice: wechat.VoiceContent{
MediaId: r.Reply.MediaId,
},
}
case wechat.ReplyTypeVideo:
return &wechat.VideoMsgReply{
ToUserName: msg.FromUserName,
FromUserName: msg.ToUserName,
CreateTime: time.Now().Unix(),
MsgType: r.ReplyType,
Video: wechat.VideoContent{
MediaId: r.Reply.MediaId,
Title: r.Reply.Title,
Description: r.Reply.Description,
},
}
case wechat.ReplyTypeMusic:
return &wechat.MusicMsgReply{
ToUserName: msg.FromUserName,
FromUserName: msg.ToUserName,
CreateTime: time.Now().Unix(),
MsgType: r.ReplyType,
Music: wechat.MusicContent{
Title: r.Reply.Title,
Description: r.Reply.Description,
MusicUrl: r.Reply.MusicUrl,
HQMusicUrl: r.Reply.HQMusicUrl,
ThumbMediaId: r.Reply.ThumbMediaId,
},
}
case wechat.ReplyTypeNews:
return &wechat.NewsMsgReply{
ToUserName: msg.FromUserName,
FromUserName: msg.ToUserName,
CreateTime: time.Now().Unix(),
MsgType: r.ReplyType,
ArticleCount: len(r.Reply.Articles),
Articles: wechat.ArticleItem{
Item: r.Reply.Articles},
}

}
return reply
}

+ 14
- 45
routers/home.go View File

@@ -7,7 +7,6 @@ package routers

import (
"bytes"
"fmt"
"net/http"
"strconv"
"strings"
@@ -675,10 +674,19 @@ func getRecommendOrg() ([]map[string]interface{}, error) {
if err != nil {
return nil, err
}
names := make([]string, 0)
for _, userName := range result {
names = append(names, userName)
}
users, _ := models.GetUsersByNames(names)
userMap := make(map[string]*models.User, 0)
for _, user := range users {
userMap[user.Name] = user
}
resultOrg := make([]map[string]interface{}, 0)
for _, userName := range result {
user, err := models.GetUserByName(userName)
if err == nil {
user := userMap[userName]
if user != nil {
userMap := make(map[string]interface{})
userMap["Name"] = user.Name
userMap["Description"] = user.Description
@@ -691,7 +699,7 @@ func getRecommendOrg() ([]map[string]interface{}, error) {
userMap["NumMembers"] = user.NumMembers
resultOrg = append(resultOrg, userMap)
} else {
log.Info("query user error," + err.Error())
log.Info("the user not exist," + userName)
}
}
return resultOrg, nil
@@ -760,15 +768,6 @@ func GetRankUser(index string) ([]map[string]interface{}, error) {
return resultOrg, nil
}

// func GetImageInfoFromPromote(ctx *context.Context) {
// imageInfo, err := GetImageInfo()
// if err != nil {
// ctx.ServerError("500", err)
// return
// }
// ctx.JSON(200, imageInfo)
// }

func GetUserRankFromPromote(ctx *context.Context) {
index := ctx.Params("index")
resultUserRank, err := GetRankUser(index)
@@ -792,45 +791,15 @@ func RecommendHomeInfo(ctx *context.Context) {
if err != nil {
log.Info("error." + err.Error())
}
resultCloudBrain, err := getCloudbrainNums()
if err != nil {
log.Info("error." + err.Error())
}

mapInterface := make(map[string]interface{})
mapInterface["org"] = resultOrg
mapInterface["repo"] = resultRepo
mapInterface["image"] = resultImage
mapInterface["cloudbrain"] = resultCloudBrain
//mapInterface["cloudbrain"] = resultCloudBrain
ctx.JSON(http.StatusOK, mapInterface)
}

func getCloudbrainNums() (map[string]string, error) {
result := make(map[string]string)
cloudStatusMap := models.GetAllStatusCloudBrain()
result["completed_task"] = fmt.Sprint(cloudStatusMap["COMPLETED"])
result["running_task"] = fmt.Sprint(cloudStatusMap["RUNNING"])
result["wait_task"] = fmt.Sprint(cloudStatusMap["WAITING"])
return result, nil
}

// func RecommendOrgFromPromote(ctx *context.Context) {
// resultOrg, err := GetRecommendOrg()
// if err != nil {
// ctx.ServerError("500", err)
// return
// }
// ctx.JSON(200, resultOrg)
// }

func RecommendRepoFromPromote(ctx *context.Context) {
result, err := repository.GetRecommendRepoFromPromote("projects")
if err != nil {
ctx.ServerError("500", err)
} else {
ctx.JSON(200, result)
}
}

func HomeTerm(ctx *context.Context) {
ctx.HTML(200, tplHomeTerm)
}

+ 10
- 4
routers/repo/dataset.go View File

@@ -172,8 +172,8 @@ func DatasetIndex(ctx *context.Context) {
for _, attachment := range pageAttachments {
uploader, _ := models.GetUserByID(attachment.UploaderID)
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()
if ctx.User != nil {
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) {
return true
}


+ 25
- 8
routers/repo/grampus.go View File

@@ -108,11 +108,17 @@ func grampusTrainJobNewDataPrepare(ctx *context.Context, processType string) err

ctx.Data["branchName"] = ctx.Repo.BranchName

if processType == grampus.ProcessorTypeGPU {
ctx.Data["datasetType"] = models.TypeCloudBrainOne
} else if processType == grampus.ProcessorTypeNPU {
ctx.Data["datasetType"] = models.TypeCloudBrainTwo
}

return nil
}

func grampusParamCheckCreateTrainJob(form auth.CreateGrampusTrainJobForm) error {
if !strings.HasSuffix(form.BootFile, ".py") {
if !strings.HasSuffix(strings.TrimSpace(form.BootFile), ".py") {
log.Error("the boot file(%s) must be a python file", form.BootFile)
return errors.New("启动文件必须是python文件")
}
@@ -130,7 +136,7 @@ func GrampusTrainJobGpuCreate(ctx *context.Context, form auth.CreateGrampusTrain
jobName := util.ConvertDisplayJobNameToJobName(displayJobName)
uuid := form.Attachment
description := form.Description
bootFile := form.BootFile
bootFile := strings.TrimSpace(form.BootFile)
params := form.Params
repo := ctx.Repo.Repository
codeLocalPath := setting.JobPath + jobName + cloudbrain.CodeMountPath + "/"
@@ -310,7 +316,7 @@ func GrampusTrainJobNpuCreate(ctx *context.Context, form auth.CreateGrampusTrain
jobName := util.ConvertDisplayJobNameToJobName(displayJobName)
uuid := form.Attachment
description := form.Description
bootFile := form.BootFile
bootFile := strings.TrimSpace(form.BootFile)
params := form.Params
repo := ctx.Repo.Repository
codeLocalPath := setting.JobPath + jobName + modelarts.CodePath
@@ -619,6 +625,7 @@ func GrampusTrainJobShow(ctx *context.Context) {
ctx.Data["version_list_task"] = taskList

ctx.Data["canDownload"] = cloudbrain.CanModifyJob(ctx, task)
ctx.Data["displayJobName"] = task.DisplayJobName

aiCenterInfo := strings.Split(task.AiCenter, "+")
if len(aiCenterInfo) == 2 {
@@ -665,14 +672,24 @@ func generateCommand(repoName, processorType, codeRemotePath, dataRemotePath, bo
command += commandDownload
}

//check download result
commandCheckRes := "bash -c \"[[ $? -eq 0 ]] && exit 0 || exit -1;\";"
command += commandCheckRes

//unzip code & dataset
toolUnzip := "unzip -q "
if strings.HasSuffix(datasetName, ".tar.gz") {
toolUnzip = "tar -zxvf "
}
commandUnzip := "cd /tmp/dataset;" + toolUnzip + datasetName + ";cd /tmp/code;unzip -q master.zip;"
commandUnzip := "cd /cache/code;unzip -q master.zip;echo \"start to unzip dataset\";cd /cache/dataset;" + toolUnzip + datasetName + ";"
command += commandUnzip

//check unzip result
commandCheckRes = "bash -c \"[[ $? -eq 0 ]] && exit 0 || exit -1;\";"
command += commandCheckRes

command += "echo \"unzip finished;start to exec code;\";"

//exec code
var parameters models.Parameters
var paramCode string
@@ -693,7 +710,7 @@ func generateCommand(repoName, processorType, codeRemotePath, dataRemotePath, bo
}
}

commandCode := "cd /tmp/code/" + strings.ToLower(repoName) + ";python " + bootFile + paramCode + ";"
commandCode := "cd /cache/code/" + strings.ToLower(repoName) + ";python " + bootFile + paramCode + ";"
command += commandCode

//get exec result
@@ -702,15 +719,15 @@ func generateCommand(repoName, processorType, codeRemotePath, dataRemotePath, bo

//upload models
if processorType == grampus.ProcessorTypeNPU {
commandUpload := "cd /tmp/script_for_grampus/;./uploader_for_obs " + setting.Bucket + " " + outputRemotePath + " " + "/tmp/output/;"
commandUpload := "cd /cache/script_for_grampus/;./uploader_for_obs " + setting.Bucket + " " + outputRemotePath + " " + "/cache/output/;"
command += commandUpload
} else if processorType == grampus.ProcessorTypeGPU {
commandUpload := "cd /tmp/script_for_grampus/;./uploader_for_minio " + setting.Grampus.Env + " " + outputRemotePath + " " + "/tmp/output/;"
commandUpload := "cd /cache/script_for_grampus/;./uploader_for_minio " + setting.Grampus.Env + " " + outputRemotePath + " " + "/cache/output/;"
command += commandUpload
}

//check exec result
commandCheckRes := "bash -c \"[[ $result -eq 0 ]] && exit 0 || exit -1;\""
commandCheckRes = "bash -c \"[[ $result -eq 0 ]] && exit 0 || exit -1;\""
command += commandCheckRes

return command, nil


+ 2
- 7
routers/user/home.go View File

@@ -769,12 +769,6 @@ func Cloudbrains(ctx *context.Context) {
if page <= 0 {
page = 1
}
debugType := models.TypeCloudBrainAll
if listType == models.GPUResource {
debugType = models.TypeCloudBrainOne
} else if listType == models.NPUResource {
debugType = models.TypeCloudBrainTwo
}

var jobTypes []string
jobTypeNot := false
@@ -821,7 +815,6 @@ func Cloudbrains(ctx *context.Context) {
},
Keyword: keyword,
UserID: ctxUser.ID,
Type: debugType,
JobTypeNot: jobTypeNot,
JobStatusNot: jobStatusNot,
JobStatus: jobStatuses,
@@ -829,6 +822,8 @@ func Cloudbrains(ctx *context.Context) {
NeedRepoInfo: true,
IsLatestVersion: modelarts.IsLatestVersion,
RepoIDList: repoIDList,
ComputeResource: listType,
Type: models.TypeCloudBrainAll,
})
if err != nil {
ctx.ServerError("Get job failed:", err)


+ 0
- 5
services/repository/repository.go View File

@@ -131,11 +131,6 @@ func GetRecommendRepoFromPromote(filename string) ([]map[string]interface{}, err
repoMap["ID"] = fmt.Sprint(repo.ID)
repoMap["Name"] = repo.Name
repoMap["Alias"] = repo.Alias
if repo.RepoType == models.RepoCourse {
//Load creator
repo.GetCreator()
repoMap["Creator"] = repo.Creator
}

repoMap["OwnerName"] = repo.OwnerName
repoMap["NumStars"] = repo.NumStars


+ 3
- 3
templates/explore/repo_list.tmpl View File

@@ -18,7 +18,7 @@

.ui.list .list>.item>img.image+.content,
.ui.list>.item>img.image+.content {
width: calc(100% - 30px);
width: calc(100% - 34px);
margin-left: 0;
}

@@ -107,9 +107,9 @@
{{range .Repos}}
<div class="item">
{{if .RelAvatarLink}}
<img class="ui avatar image" src="{{.RelAvatarLink}}">
<img class="ui avatar image" style="width: 28px;height: 28px;" src="{{.RelAvatarLink}}">
{{else}}
<img class="ui avatar image" avatar="{{.Owner.Name}}">
<img class="ui avatar image" style="width: 28px;height: 28px;" avatar="{{.Owner.Name}}">
{{end}}
<div class="content">
<div class="ui header">


+ 1
- 1
templates/repo/cloudbrain/trainjob/new.tmpl View File

@@ -97,7 +97,7 @@
</a>
<a class="item" href="{{.RepoLink}}/grampus/train-job/npu/create">
<svg class="svg" sxmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="none" d="M0 0h24v24H0z"></path><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-2.29-2.333A17.9 17.9 0 0 1 8.027 13H4.062a8.008 8.008 0 0 0 5.648 6.667zM10.03 13c.151 2.439.848 4.73 1.97 6.752A15.905 15.905 0 0 0 13.97 13h-3.94zm9.908 0h-3.965a17.9 17.9 0 0 1-1.683 6.667A8.008 8.008 0 0 0 19.938 13zM4.062 11h3.965A17.9 17.9 0 0 1 9.71 4.333 8.008 8.008 0 0 0 4.062 11zm5.969 0h3.938A15.905 15.905 0 0 0 12 4.248 15.905 15.905 0 0 0 10.03 11zm4.259-6.667A17.9 17.9 0 0 1 15.973 11h3.965a8.008 8.008 0 0 0-5.648-6.667z"></path></svg>
{{.i18n.Tr "cloudbrain.resource_cluster_c2net"}}
{{.i18n.Tr "cloudbrain.resource_cluster_c2net"}}(Beta)
</a>
</div>
</div>


+ 1
- 1
templates/repo/grampus/trainjob/gpu/new.tmpl View File

@@ -91,7 +91,7 @@
</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"}}
{{.i18n.Tr "cloudbrain.resource_cluster_c2net"}}(Beta)
</a>
</div>
</div>


+ 3
- 5
templates/repo/grampus/trainjob/npu/new.tmpl View File

@@ -85,10 +85,9 @@
<svg class="svg" sxmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="none" d="M0 0h24v24H0z"></path><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-2.29-2.333A17.9 17.9 0 0 1 8.027 13H4.062a8.008 8.008 0 0 0 5.648 6.667zM10.03 13c.151 2.439.848 4.73 1.97 6.752A15.905 15.905 0 0 0 13.97 13h-3.94zm9.908 0h-3.965a17.9 17.9 0 0 1-1.683 6.667A8.008 8.008 0 0 0 19.938 13zM4.062 11h3.965A17.9 17.9 0 0 1 9.71 4.333 8.008 8.008 0 0 0 4.062 11zm5.969 0h3.938A15.905 15.905 0 0 0 12 4.248 15.905 15.905 0 0 0 10.03 11zm4.259-6.667A17.9 17.9 0 0 1 15.973 11h3.965a8.008 8.008 0 0 0-5.648-6.667z"></path></svg>
{{.i18n.Tr "cloudbrain.resource_cluster_openi"}}
</a>

<a class="active item" href="{{.RepoLink}}/grampus/train-job/ {{if.NPUEnabled}}npu{{else}}gpu{{end}}/create">
<svg class="svg" sxmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="none" d="M0 0h24v24H0z"></path><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-2.29-2.333A17.9 17.9 0 0 1 8.027 13H4.062a8.008 8.008 0 0 0 5.648 6.667zM10.03 13c.151 2.439.848 4.73 1.97 6.752A15.905 15.905 0 0 0 13.97 13h-3.94zm9.908 0h-3.965a17.9 17.9 0 0 1-1.683 6.667A8.008 8.008 0 0 0 19.938 13zM4.062 11h3.965A17.9 17.9 0 0 1 9.71 4.333 8.008 8.008 0 0 0 4.062 11zm5.969 0h3.938A15.905 15.905 0 0 0 12 4.248 15.905 15.905 0 0 0 10.03 11zm4.259-6.667A17.9 17.9 0 0 1 15.973 11h3.965a8.008 8.008 0 0 0-5.648-6.667z"></path></svg>
{{.i18n.Tr "cloudbrain.resource_cluster_c2net"}}
{{.i18n.Tr "cloudbrain.resource_cluster_c2net"}}(Beta)
</a>
</div>
</div>
@@ -205,7 +204,7 @@
<button class="ui create_train_job green button">
{{.i18n.Tr "repo.cloudbrain.new"}}
</button>
<a class="ui button" href="/">{{.i18n.Tr "repo.cloudbrain.cancel"}}</a>
<a class="ui button" href="{{.RepoLink}}/modelarts/train-job">{{.i18n.Tr "repo.cloudbrain.cancel"}}</a>
</div>

<!-- 模态框 -->
@@ -217,8 +216,7 @@
{{template "base/footer" .}}

<script>
let url_href = window.location.pathname.split('create')[0]
$(".ui.button").attr('href',url_href)
$('select.dropdown')
.dropdown();



+ 2
- 1
templates/repo/grampus/trainjob/show.tmpl View File

@@ -430,7 +430,7 @@
</td>

<td class="ti-text-form-content">
<div class="text-span text-span-w">
<div class="text-span text-span-w" id="{{.VersionName}}-ai_center">
{{$.ai_center}}
</div>
</td>
@@ -679,6 +679,7 @@
// detail status and duration
$('#' + version_name + '-duration').text(data.JobDuration)
$('#' + version_name + '-status').text(data.JobStatus)
$('#' + version_name + '-ai_center').text(data.AiCenter)
loadLog(version_name)




+ 1
- 1
templates/repo/modelarts/trainjob/index.tmpl View File

@@ -157,7 +157,7 @@
{{$.i18n.Tr "repo.stop"}}
</a>
{{else}}
<a style="padding: 0.5rem 1rem;" id="ai-stop-{{.JobID}}" class="ui basic disabled button">
<a class="ui basic disabled button">
{{$.i18n.Tr "repo.stop"}}
</a>
{{end}}


+ 269
- 239
templates/repo/modelarts/trainjob/new.tmpl View File

@@ -1,67 +1,72 @@
{{template "base/head" .}}
<style>
.unite {
font-family: SourceHanSansSC-medium !important;
color: rgba(16, 16, 16, 100) !important;
}

.title {
font-size: 16px !important;
padding-left: 3rem !important;
}

.min_title {
font-size: 14px !important;
padding-left: 6rem !important;
margin-bottom: 2rem !important;

}

.width {
width: 100% !important;
}

.width80 {
width: 80.7% !important;
margin-left: 10px;
}

.unite{
font-family: SourceHanSansSC-medium !important;
color: rgba(16, 16, 16, 100) !important;
}

.title{
font-size: 16px !important;
padding-left: 3rem !important;
}
.min_title{
font-size: 14px !important;
padding-left: 6rem !important;
margin-bottom: 2rem !important;

}
.width{
width:100% !important;
}
.width80{
width: 80.7% !important;
margin-left: 10px;
}
.width85{
width: 85% !important;
margin-left: 4.5rem !important;
}
.width81{
margin-left: 1.5rem;
width: 81% !important;
}

.add{font-size: 18px;
padding: 0.5rem;
border: 1px solid rgba(187, 187, 187, 100);
border-radius: 0px 5px 5px 0px;
line-height: 21px;
text-align: center;
color: #C2C7CC;
}
.min{
font-size: 18px;
padding: 0.5rem;
border: 1px solid rgba(187, 187, 187, 100);
border-radius: 5px 0px 0px 5px;
line-height: 21px;
text-align: center;
color: #C2C7CC;
}
.width85 {
width: 85% !important;
margin-left: 4.5rem !important;
}

.width81 {
margin-left: 1.5rem;
width: 81% !important;
}

.add {
font-size: 18px;
padding: 0.5rem;
border: 1px solid rgba(187, 187, 187, 100);
border-radius: 0px 5px 5px 0px;
line-height: 21px;
text-align: center;
color: #C2C7CC;
}

.min {
font-size: 18px;
padding: 0.5rem;
border: 1px solid rgba(187, 187, 187, 100);
border-radius: 5px 0px 0px 5px;
line-height: 21px;
text-align: center;
color: #C2C7CC;
}
</style>
<!-- <div class="ui page dimmer">
<div class="ui text loader">{{.i18n.Tr "loading"}}</div>
</div> -->
<div id="mask">
<div id="loadingPage">
<div class="rect1"></div>
<div class="rect2"></div>
<div class="rect3"></div>
<div class="rect4"></div>
<div class="rect5"></div>
</div>
<div id="loadingPage">
<div class="rect1"></div>
<div class="rect2"></div>
<div class="rect3"></div>
<div class="rect4"></div>
<div class="rect5"></div>
</div>
</div>
<div class="repository">
{{template "repo/header" .}}
@@ -87,7 +92,7 @@
</a>
<a class="item" href="{{.RepoLink}}/grampus/train-job/npu/create">
<svg class="svg" sxmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="none" d="M0 0h24v24H0z"></path><path d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-2.29-2.333A17.9 17.9 0 0 1 8.027 13H4.062a8.008 8.008 0 0 0 5.648 6.667zM10.03 13c.151 2.439.848 4.73 1.97 6.752A15.905 15.905 0 0 0 13.97 13h-3.94zm9.908 0h-3.965a17.9 17.9 0 0 1-1.683 6.667A8.008 8.008 0 0 0 19.938 13zM4.062 11h3.965A17.9 17.9 0 0 1 9.71 4.333 8.008 8.008 0 0 0 4.062 11zm5.969 0h3.938A15.905 15.905 0 0 0 12 4.248 15.905 15.905 0 0 0 10.03 11zm4.259-6.667A17.9 17.9 0 0 1 15.973 11h3.965a8.008 8.008 0 0 0-5.648-6.667z"></path></svg>
{{.i18n.Tr "cloudbrain.resource_cluster_c2net"}}
{{.i18n.Tr "cloudbrain.resource_cluster_c2net"}}(Beta)
</a>
</div>
</div>
@@ -95,29 +100,41 @@
<label style="font-weight: normal;">{{.i18n.Tr "cloudbrain.compute_resource"}}</label>
<div class="ui blue mini menu compact selectcloudbrain">
<a class="item" href="{{.RepoLink}}/cloudbrain/train-job/create">
<svg class="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M3 2.992C3 2.444 3.445 2 3.993 2h16.014a1 1 0 0 1 .993.992v18.016a.993.993 0 0 1-.993.992H3.993A1 1 0 0 1 3 21.008V2.992zM19 11V4H5v7h14zm0 2H5v7h14v-7zM9 6h6v2H9V6zm0 9h6v2H9v-2z"/>
<svg class="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16"
height="16">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M3 2.992C3 2.444 3.445 2 3.993 2h16.014a1 1 0 0 1 .993.992v18.016a.993.993 0 0 1-.993.992H3.993A1 1 0 0 1 3 21.008V2.992zM19 11V4H5v7h14zm0 2H5v7h14v-7zM9 6h6v2H9V6zm0 9h6v2H9v-2z" />
</svg>
CPU/GPU
</a>
<a class="active item" href="{{.RepoLink}}/modelarts/train-job/create">
<svg class="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16">
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M3 2.992C3 2.444 3.445 2 3.993 2h16.014a1 1 0 0 1 .993.992v18.016a.993.993 0 0 1-.993.992H3.993A1 1 0 0 1 3 21.008V2.992zM19 11V4H5v7h14zm0 2H5v7h14v-7zM9 6h6v2H9V6zm0 9h6v2H9v-2z"/>
<svg class="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16"
height="16">
<path fill="none" d="M0 0h24v24H0z" />
<path
d="M3 2.992C3 2.444 3.445 2 3.993 2h16.014a1 1 0 0 1 .993.992v18.016a.993.993 0 0 1-.993.992H3.993A1 1 0 0 1 3 21.008V2.992zM19 11V4H5v7h14zm0 2H5v7h14v-7zM9 6h6v2H9V6zm0 9h6v2H9v-2z" />
</svg>
Ascend NPU</a>
</div>
</div>
<div class="required unite min_title inline field">
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.job_name"}}</label>
<input style="width: 60%;" name="display_job_name" id="display_job_name" placeholder={{.i18n.Tr "repo.modelarts.train_job.job_name"}} value="{{.display_job_name}}" tabindex="3" onkeyup="this.value=this.value.replace(/[, ]/g,'')" autofocus required maxlength="64">
<input style="width: 60%;" name="display_job_name" id="display_job_name"
placeholder={{.i18n.Tr "repo.modelarts.train_job.job_name"}} value="{{.display_job_name}}"
tabindex="3" onkeyup="this.value=this.value.replace(/[, ]/g,'')" autofocus required
maxlength="64">
<span class="tooltips" style="display: block;">{{.i18n.Tr "cloudbrain.job_name_rule"}}</span>
</div>

<div class="unite min_title inline field">
<label style="font-weight: normal;" for="description">{{.i18n.Tr "repo.modelarts.train_job.description"}}&nbsp;&nbsp;</label>
<textarea style="width: 80%;" id="description" name="description" rows="3" maxlength="255" placeholder={{.i18n.Tr "repo.modelarts.train_job.new_place"}} onchange="this.value=this.value.substring(0, 255)" onkeydown="this.value=this.value.substring(0, 255)" onkeyup="this.value=this.value.substring(0, 255)"></textarea>
<label style="font-weight: normal;"
for="description">{{.i18n.Tr "repo.modelarts.train_job.description"}}&nbsp;&nbsp;</label>
<textarea style="width: 80%;" id="description" name="description" rows="3" maxlength="255"
placeholder={{.i18n.Tr "repo.modelarts.train_job.new_place"}}
onchange="this.value=this.value.substring(0, 255)"
onkeydown="this.value=this.value.substring(0, 255)"
onkeyup="this.value=this.value.substring(0, 255)"></textarea>
</div>
<div class="ui divider"></div>

@@ -125,43 +142,45 @@


<div class="required unite min_title inline field">
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.code_version"}}</label>
<select class="ui dropdown width80 left2" id="code_version" name="branch_name">
{{if .branch_name}}
<option name="branch_name" value="{{.branch_name}}">{{.branch_name}}</option>
{{range $k, $v :=.Branches}}
{{ if ne $v $.branch_name }}
<option name="branch_name" value="{{$v}}">{{$v}}</option>
{{end}}
{{end}}
{{else}}
<option name="branch_name" value="{{.branchName}}">{{.branchName}}</option>
{{range $k, $v :=.Branches}}
{{ if ne $v $.branchName }}
<option name="branch_name" value="{{$v}}">{{$v}}</option>
{{end}}
{{end}}
{{end}}
</select>
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.code_version"}}</label>
<select class="ui dropdown width80 left2" id="code_version" name="branch_name">
{{if .branch_name}}
<option name="branch_name" value="{{.branch_name}}">{{.branch_name}}</option>
{{range $k, $v :=.Branches}}
{{ if ne $v $.branch_name }}
<option name="branch_name" value="{{$v}}">{{$v}}</option>
{{end}}
{{end}}
{{else}}
<option name="branch_name" value="{{.branchName}}">{{.branchName}}</option>
{{range $k, $v :=.Branches}}
{{ if ne $v $.branchName }}
<option name="branch_name" value="{{$v}}">{{$v}}</option>
{{end}}
{{end}}
{{end}}
</select>
</div>



<div class="required unite min_title inline fields" style="width: 90%;">
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.AI_driver"}}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</label>
<label
style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.AI_driver"}}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</label>
<div class="field" style="flex: 1.5;">
<select class="ui dropdown width" id="trainjob_engines" >
<select class="ui dropdown width" id="trainjob_engines">
{{range .engines}}
<option value="{{.Value}}">{{.Value}}</option>
<option value="{{.Value}}">{{.Value}}</option>
{{end}}
</select>
</div>

<div class="field" style="flex: 2;" id="engine_name">
<select class="ui dropdown width" id="trainjob_engine_versions" style='width: 100%;' name="engine_id">
{{range .engine_versions}}
<option name="engine_id" value="{{.ID}}">{{.Value}}</option>
{{end}}
<select class="ui dropdown width" id="trainjob_engine_versions" style='width: 100%;'
name="engine_id">
{{range .engine_versions}}
<option name="engine_id" value="{{.ID}}">{{.Value}}</option>
{{end}}
</select>

</div>
@@ -169,41 +188,49 @@
</div>

<div class="inline unite min_title field required">
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.start_file"}}</label>
{{if .bootFile}}
<input style="width: 48.5%;" name="boot_file" id="trainjob_boot_file" value="{{.bootFile}}" tabindex="3" autofocus required maxlength="255" >
{{else}}
<input style="width: 48.5%;" name="boot_file" id="trainjob_boot_file" value="" tabindex="3" autofocus required maxlength="255" >
{{end}}
<span>
<i class="question circle icon link" data-content={{.i18n.Tr "repo.modelarts.train_job.boot_file_helper"}} data-position="right center" data-variation="mini"></i>
</span>
<a href="https://git.openi.org.cn/OpenIOSSG/MINIST_Example" target="_blank">{{.i18n.Tr "cloudbrain.view_sample"}}</a>
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.start_file"}}</label>
{{if .bootFile}}
<input style="width: 48.5%;" name="boot_file" id="trainjob_boot_file" value="{{.bootFile}}"
tabindex="3" autofocus required maxlength="255">
{{else}}
<input style="width: 48.5%;" name="boot_file" id="trainjob_boot_file" value="" tabindex="3"
autofocus required maxlength="255">
{{end}}
<span>
<i class="question circle icon link"
data-content={{.i18n.Tr "repo.modelarts.train_job.boot_file_helper"}}
data-position="right center" data-variation="mini"></i>
</span>
<a href="https://git.openi.org.cn/OpenIOSSG/MINIST_Example"
target="_blank">{{.i18n.Tr "cloudbrain.view_sample"}}</a>
</div>
{{template "custom/select_dataset_train" .}}
<span class="tooltips" style="margin-left: 11.5rem;margin-bottom: 2rem;">{{.i18n.Tr "cloudbrain.dataset_path_rule"}}</span>
<span class="tooltips"
style="margin-left: 11.5rem;margin-bottom: 2rem;">{{.i18n.Tr "cloudbrain.dataset_path_rule"}}</span>
<div class="inline unite min_title field">
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.run_parameter"}}</label>
<span id="add_run_para" style="margin-left: 0.5rem;cursor:pointer;color: rgba(3, 102, 214, 100);font-size: 14px;line-height: 26px;font-family: SourceHanSansSC-medium;"><i class="plus square outline icon"></i>{{.i18n.Tr "repo.modelarts.train_job.add_run_parameter"}}</span>
<input id="store_run_para" type="hidden" name="run_para_list">
<span id="add_run_para"
style="margin-left: 0.5rem;cursor:pointer;color: rgba(3, 102, 214, 100);font-size: 14px;line-height: 26px;font-family: SourceHanSansSC-medium;"><i
class="plus square outline icon"></i>{{.i18n.Tr "repo.modelarts.train_job.add_run_parameter"}}</span>
<input id="store_run_para" type="hidden" name="run_para_list">
<div class="dynamic field" style="margin-top: 1rem;">
{{if ne 0 (len .params)}}
{{if ne 0 (len .params)}}
{{range $k ,$v := .params}}
<div class="two fields width85" id="para{{$k}}">
<div class="field">
<input type="text" name="shipping_first-name" value={{$v.Label}} required>
</div>
<div class="field">
<input type="text" name="shipping_last-name" value={{$v.Value}} required>
</div>
<span>
<i class="trash icon"></i>
</span>

<div class="two fields width85" id="para{{$k}}">
<div class="field">
<input type="text" name="shipping_first-name" value={{$v.Label}} required>
</div>
<div class="field">
<input type="text" name="shipping_last-name" value={{$v.Value}} required>
</div>
<span>
<i class="trash icon"></i>
</span>

</div>
{{end}}
{{end}}
{{end}}
</div>
</div>

@@ -212,13 +239,14 @@
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.resource_pool"}}</label>
<select class="ui dropdown" id="trainjob_resource_pool" style='width:385px' name="pool_id">
{{range .resource_pools}}
<option value="{{.ID}}">{{.Value}}</option>
<option value="{{.ID}}">{{.Value}}</option>
{{end}}
</select>
</div>

<div class="required grouped fields" style="display: none;">
<label style="font-weight: normal;" for="resource_type">{{.i18n.Tr "repo.modelarts.train_job.resource_type"}}</label>
<label style="font-weight: normal;"
for="resource_type">{{.i18n.Tr "repo.modelarts.train_job.resource_type"}}</label>
<div class="field">
<div class="ui grid">
<div class="column">
@@ -237,29 +265,31 @@
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.standard"}}</label>
<select class="ui dropdown width81" id="trainjob-flavor" style='width:385px' name="flavor">
{{range .flavor_infos}}
<option name="flavor" value="{{.Code}}">{{.Value}}</option>
<option name="flavor" value="{{.Code}}">{{.Value}}</option>
{{end}}
</select>
</div>
<div class="inline required unite min_title field">
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.amount_of_compute_node"}}</label>
<label
style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.amount_of_compute_node"}}</label>

<div class="ui labeled input" style="width: 5%;">

<input style="border-radius: 0;text-align: center;"type="hidden" name="work_server_number" id="trainjob_work_server_num" tabindex="3" autofocus required maxlength="255" value="1" readonly>
<div class="field" id="trainjob_work_server_num_select" name="work_server_number_select">
<select class="ui dropdown width" style='width: 100%;' name="work_server_id">
<option name="server_id" value="1">1</option>
<option name="server_id" value="2">2</option>
</select>
</div>
<input style="border-radius: 0;text-align: center;" type="hidden" name="work_server_number"
id="trainjob_work_server_num" tabindex="3" autofocus required maxlength="255" value="1"
readonly>
<div class="field" id="trainjob_work_server_num_select" name="work_server_number_select">
<select class="ui dropdown width" style='width: 100%;' name="work_server_id">
<option name="server_id" value="1">1</option>
</select>
</div>

</div>
</div>

<div class="inline unite min_title field">
<button class="ui create_train_job green button">
{{.i18n.Tr "repo.cloudbrain.new"}}
{{.i18n.Tr "repo.cloudbrain.new"}}
</button>
<a class="ui button" href="/">{{.i18n.Tr "repo.cloudbrain.cancel"}}</a>
</div>
@@ -274,7 +304,7 @@

<script>
let url_href = window.location.pathname.split('create')[0]
$(".ui.button").attr('href',url_href)
$(".ui.button").attr('href', url_href)
$('select.dropdown')
.dropdown();

@@ -296,182 +326,182 @@
// }
// })
// 参数增加、删除、修改、保存
function Add_parameter(i){
value = '<div class="two fields width85" id= "para'+ i +'">' +
'<div class="field">' +
'<input type="text" name="shipping_first-name" required placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_name"}}> ' +
'</div> ' +
'<div class="field"> ' +
'<input type="text" name="shipping_last-name" required placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_value"}}>' +
'</div>'+
'<span>' +
'<i class="trash icon">' +
'</i>' +
'</span>' +
'</div>'
function Add_parameter(i) {
value = '<div class="two fields width85" id= "para' + i + '">' +
'<div class="field">' +
'<input type="text" name="shipping_first-name" required placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_name"}}> ' +
'</div> ' +
'<div class="field"> ' +
'<input type="text" name="shipping_last-name" required placeholder={{.i18n.Tr "repo.modelarts.train_job.parameter_value"}}>' +
'</div>' +
'<span>' +
'<i class="trash icon">' +
'</i>' +
'</span>' +
'</div>'
$(".dynamic.field").append(value)
}

$('#add_run_para').click(function(){
$('#add_run_para').click(function () {
var len = $(".dynamic.field .two.fields").length
Add_parameter(len)
});

$(".dynamic.field").on("click",".trash.icon", function() {
$(".dynamic.field").on("click", ".trash.icon", function () {
var index = $(this).parent().parent().index()
$(this).parent().parent().remove()
var len = $(".dynamic.field .two.fields").length
$(".dynamic.field .two.fields").each(function(){
$(".dynamic.field .two.fields").each(function () {
var cur_index = $(this).index()
$(this).attr('id', 'para' + cur_index)
})
});

$('.ui.parameter.green.button').click(function(){
$('.ui.parameter.green.button').click(function () {
var parameters = [];
$('table tr').each(function() {
$(this).find('td:eq(1)').each(function(){
$('table tr').each(function () {
$(this).find('td:eq(1)').each(function () {
parameters.push($(this).text());
})
$(this).find('input').each(function(){
$(this).find('input').each(function () {
parameters.push($(this).text())
})

});
$('.ui.parameter.modal')
.modal('hide');
for(var i = 2; i < parameters.length; i++){
switch(i) {
// 数据集uuid待完成
// case (2):
// console.log(1)
// break;
// $("#trainjob_datasets").val(parameters[i]);
// console.log($("#trainjob_datasets").val())
case (3):
$("input[name='boot_file']").val(parameters[i]);
break;
case (4):
var para = parameters[i].split(" ")
for(var j = 0; j < para.length; j++){
var para_name = para[j].split('=')[0]
var para_value = para[j].split('=')[1]
var len = $(".dynamic.field .two.fields").length
Add_parameter(len)
var pid = 'para' + len
$(".dynamic.field"+ " #" + pid + "").find("input[name=shipping_first-name]").val(para_name)
$(".dynamic.field"+ " #" + pid + "").find("input[name=shipping_last-name]").val(para_value)
}
break;
// 数据集pool_id待完成
// case (5):
// $("select[name='pool_id']").val(parameters[i]);
// break;
case (6):
// $("input[name='work_server_number']").val(parameters[i]);
break;
}
for (var i = 2; i < parameters.length; i++) {
switch (i) {
// 数据集uuid待完成
// case (2):
// console.log(1)
// break;
// $("#trainjob_datasets").val(parameters[i]);
// console.log($("#trainjob_datasets").val())
case (3):
$("input[name='boot_file']").val(parameters[i]);
break;
case (4):
var para = parameters[i].split(" ")
for (var j = 0; j < para.length; j++) {
var para_name = para[j].split('=')[0]
var para_value = para[j].split('=')[1]
var len = $(".dynamic.field .two.fields").length
Add_parameter(len)
var pid = 'para' + len
$(".dynamic.field" + " #" + pid + "").find("input[name=shipping_first-name]").val(para_name)
$(".dynamic.field" + " #" + pid + "").find("input[name=shipping_last-name]").val(para_value)
}
break;
// 数据集pool_id待完成
// case (5):
// $("select[name='pool_id']").val(parameters[i]);
// break;
case (6):
// $("input[name='work_server_number']").val(parameters[i]);
break;
}
}
})

$('.ui.save.checkbox').click(function(){
$('.ui.save.checkbox').click(function () {
$(this).checkbox({
onChange: function(){
if ($('.ui.save.checkbox').checkbox('is checked')){
onChange: function () {
if ($('.ui.save.checkbox').checkbox('is checked')) {
$('#save_para').removeClass("disabled")

}else{
} else {
$('#save_para').addClass("disabled")
}
}
});
})

$('.question.circle.icon').hover(function(){
$('.question.circle.icon').hover(function () {
$(this).popup('show')
});

$(".item.active.parameter_config").click(function(){
$(".item.active.parameter_config").click(function () {
$('.ui.parameter.modal')
.modal('setting', 'closable', false)
.modal('show');
})

$('.ui.deny.button').click(function(){
$('.ui.deny.button').click(function () {
$('.ui.parameter.modal')
.modal('hide');
})
$('select.dropdown')
.dropdown();

function validate(){
function validate() {
$('.ui.form')
.form({
on: 'blur',
fields: {
boot_file: {
identifier : 'boot_file',
rules: [
{
type: 'regExp[/.+\.py$/g]',
.form({
on: 'blur',
fields: {
boot_file: {
identifier: 'boot_file',
rules: [
{
type: 'regExp[/.+\.py$/g]',
}
]
},
display_job_name: {
identifier: 'display_job_name',
rules: [
{
type: 'regExp[/^[a-zA-Z0-9-_]{1,64}[a-zA-Z0-9_]$/]',
}
]
},
attachment: {
identifier: 'attachment',
rules: [
{
type: 'empty',
}
]

},
work_server_number: {
identifier: 'work_server_number',
rules: [
{
type: 'integer[1..25]',
}
]
}
]
},
display_job_name:{
identifier : 'display_job_name',
rules: [
{
type: 'regExp[/^[a-zA-Z0-9-_]{1,64}[a-zA-Z0-9_]$/]',
}
]
onSuccess: function () {
// $('.ui.page.dimmer').dimmer('show')
document.getElementById("mask").style.display = "block"
},
attachment:{
identifier : 'attachment',
rules: [
{
type: 'empty',
}
]

},
work_server_number: {
identifier : 'work_server_number',
rules: [
{
type : 'integer[1..25]',
}
]
onFailure: function (e) {
return false;
}
},
onSuccess: function(){
// $('.ui.page.dimmer').dimmer('show')
document.getElementById("mask").style.display = "block"
},
onFailure: function(e){
return false;
}
})
})
}
document.onreadystatechange = function() {
document.onreadystatechange = function () {
if (document.readyState === "complete") {
document.getElementById("mask").style.display = "none"
}
}
function send_run_para(){
function send_run_para() {
var run_parameters = []
var msg = {}
$(".dynamic.field .two.fields").each(function(){
$(".dynamic.field .two.fields").each(function () {
var para_name = $(this).find('input[name=shipping_first-name]').val()
var para_value = $(this).find('input[name=shipping_last-name]').val()
run_parameters.push({"label": para_name, "value": para_value})
run_parameters.push({ "label": para_name, "value": para_value })
})
msg["parameter"] = run_parameters
msg = JSON.stringify(msg)
$('#store_run_para').val(msg)
}
function get_name(){
let name1=$("#engine_name .text").text()
let name2=$("#flaver_name .text").text()
function get_name() {
let name1 = $("#engine_name .text").text()
let name2 = $("#flaver_name .text").text()
$("input#ai_engine_name").val(name1)
$("input#ai_flaver_name").val(name2)

@@ -480,9 +510,9 @@
$("input#trainjob_work_server_num").val(val_server_num_select)

}
$('.ui.create_train_job.green.button').click(function(e) {
$('.ui.create_train_job.green.button').click(function (e) {
get_name()
send_run_para()
validate()
})
</script>
</script>

+ 16
- 2
templates/repo/modelarts/trainjob/show.tmpl View File

@@ -597,8 +597,12 @@
</div>
<div class="required inline field" id="verionname">
<label>模型版本</label>
<input style="width: 45%;" id="version" name="Version" value="" readonly required
maxlength="255">
<input style="width: 45%;" id="version" name="Version" value="" readonly required maxlength="255">
</div>
<div class="unite min_title inline field required">
<label>模型框架</label>
<input type="hidden" id="Engine" name="Engine" required>
<input style="width: 45%;" id="Engine_name" name="Engine_name" readonly required maxlength="255">
</div>
<div class="inline field">
<label>模型标签</label>
@@ -671,6 +675,14 @@
$('#JobName').val(obj.DisplayJobName).addClass('model_disabled')
$('input[name="JobId"]').val(obj.JobID)
$('input[name="VersionName"]').val(obj.VersionName).addClass('model_disabled')
if(obj.EngineID ==122 || obj.EngineID ==35){
$('input[name="Engine_name"]').val("MindSpore").addClass('model_disabled');
$('input[name="Engine"]').val(2);
}
if(obj.EngineID ==121){
$('input[name="Engine_name"]').val("TensorFlow").addClass('model_disabled');
$('input[name="Engine"]').val(1);
}
$('.ui.dimmer').css({ "background-color": "rgb(136, 136, 136,0.7)" })
createModelName()
},
@@ -692,6 +704,8 @@
type: 'POST',
data: data,
success: function (res) {
$('input[name="Engine_name"]').val("");
$('input[name="Engine"]').val("");
location.href = `/${userName}/${repoPath}/modelmanage/show_model`
$('.ui.modal.second').modal('hide')
},


+ 13
- 11
templates/repo/modelmanage/index.tmpl View File

@@ -266,17 +266,20 @@
$.get(`${repolink}/modelmanage/query_train_job?repoId=${repoId}`, (data) => {

const n_length = data.length
let train_html = ''
for (let i = 0; i < n_length; i++) {
train_html += `<div class="item" data-value="${data[i].JobID}">${data[i].DisplayJobName}</div>`
train_html += '</div>'
if(n_length > 0){
let train_html = ''
for (let i = 0; i < n_length; i++) {
train_html += `<div class="item" data-value="${data[i].JobID}">${data[i].DisplayJobName}</div>`
train_html += '</div>'
}
$("#job-name").append(train_html)
$(".ui.dropdown.selection.search.width83").removeClass("loading")
$('#choice_model .default.text').text(data[0].DisplayJobName)
$('#choice_model input[name="JobId"]').val(data[0].JobID)
loadTrainVersion()
}else{
$(".ui.dropdown.selection.search.width83").removeClass("loading")
}
$("#job-name").append(train_html)
$(".ui.dropdown.selection.search.width83").removeClass("loading")
$('#choice_model .default.text').text(data[0].DisplayJobName)
$('#choice_model input[name="JobId"]').val(data[0].JobID)
loadTrainVersion()

})
}
function loadTrainVersion(value) {
@@ -298,7 +301,6 @@
}
$('#choice_version .default.text').text(versionName)
$('#choice_version input[name="VersionName"]').val(versionName)
console.log("1111111111");
setEngine(data[0])
}



+ 3
- 3
templates/user/dashboard/cloudbrains.tmpl View File

@@ -94,7 +94,7 @@
</a>
{{else if eq .JobType "TRAIN"}}
<a class="title"
href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}/{{if eq .ComputeResource "NPU"}}modelarts{{else}}cloudbrain{{end}}/train-job/{{$JobID}}"
href="{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}/{{if eq .Cloudbrain.Type 1}}modelarts{{else if eq .Cloudbrain.Type 0}}cloudbrain{{else if eq .Cloudbrain.Type 2}}grampus{{end}}/train-job/{{$JobID}}"
title="{{.DisplayJobName}}" style="font-size: 14px;">
<span class="fitted"
style="width: 90%;vertical-align: middle;">{{.DisplayJobName}}</span>
@@ -186,7 +186,7 @@
{{else}}
<a style="padding: 0.5rem 1rem;" id="ai-stop-{{$JobID}}"
class='ui basic ai_stop_version {{if eq .Status "KILLED" "FAILED" "START_FAILED" "KILLING" "COMPLETED" "STOPPED" "SUCCEEDED" "CREATE_FAILED"}}disabled {{else}} blue {{end}}button'
data-repopath='{{.Repo.OwnerName}}/{{.Repo.Name}}/{{if eq .JobType "INFERENCE"}}modelarts/inference-job{{else if eq .JobType "TRAIN"}}{{if eq .ComputeResource "NPU"}}modelarts/train-job{{else}}cloudbrain/train-job{{end}}{{end}}'
data-repopath='{{.Repo.OwnerName}}/{{.Repo.Name}}/{{if eq .JobType "INFERENCE"}}modelarts/inference-job{{else if eq .JobType "TRAIN"}}{{if eq .Cloudbrain.Type 1}}modelarts/train-job{{else if eq .Cloudbrain.Type 0}}cloudbrain/train-job{{else if eq .Cloudbrain.Type 2}}grampus/train-job{{end}}{{end}}'
data-jobid="{{$JobID}}" data-version="{{.VersionName}}">
{{$.i18n.Tr "repo.stop"}}
</a>
@@ -203,7 +203,7 @@
{{end}}
<!-- 删除任务 -->
<form class="ui compact buttons" id="delForm-{{$JobID}}"
action='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .JobType "BENCHMARK"}}/cloudbrain/benchmark{{else if or (eq .JobType "SNN4IMAGENET") (eq .JobType "BRAINSCORE")}}/cloudbrain{{else if eq .JobType "DEBUG"}}{{if eq .ComputeResource "NPU"}}/modelarts/notebook{{else}}/cloudbrain{{end}}{{else if eq .JobType "TRAIN"}}{{if eq .ComputeResource "NPU"}}/modelarts/train-job{{else}}/cloudbrain/train-job{{end}}{{end}}/{{$JobID}}/del?ishomepage=true'
action='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .JobType "BENCHMARK"}}/cloudbrain/benchmark{{else if or (eq .JobType "SNN4IMAGENET") (eq .JobType "BRAINSCORE")}}/cloudbrain{{else if eq .JobType "DEBUG"}}{{if eq .ComputeResource "NPU"}}/modelarts/notebook{{else}}/cloudbrain{{end}}{{else if eq .JobType "TRAIN"}}{{if eq .Cloudbrain.Type 1}}/modelarts/train-job{{else if eq .Cloudbrain.Type 0}}/cloudbrain/train-job{{else if eq .Cloudbrain.Type 2}}/grampus/train-job{{end}}{{end}}/{{$JobID}}/del?ishomepage=true'
method="post">
{{$.CsrfTokenHtml}}
<a style="padding: 0.5rem 1rem;margin-left:0.2rem" id="ai-delete-{{$JobID}}"


+ 4
- 0
templates/user/dashboard/feeds.tmpl View File

@@ -86,6 +86,10 @@
{{$.i18n.Tr "action.task_createmodel" .GetRepoLink .RefName .RefName | Str2html}}
{{else if eq .GetOpType 31}}
{{$.i18n.Tr "action.task_gputrainjob" .GetRepoLink .Content .RefName | Str2html}}
{{else if eq .GetOpType 32}}
{{$.i18n.Tr "action.task_c2netnputrainjob" .GetRepoLink .Content .RefName | Str2html}}
{{else if eq .GetOpType 33}}
{{$.i18n.Tr "action.task_c2netgputrainjob" .GetRepoLink .Content .RefName | Str2html}}
{{end}}
</p>
{{if or (eq .GetOpType 5) (eq .GetOpType 18)}}


+ 1
- 1
web_src/js/components/Model.vue View File

@@ -353,7 +353,7 @@ export default {
return "Pytorch";
}else if(model.Engine == 1 || model.Engine == 121){
return "TensorFlow";
}else if(model.Engine == 2 || model.Engine == 122){
}else if(model.Engine == 2 || model.Engine == 122 || model.Engine == 35){
return "MindSpore";
}else{
return "Other"


+ 91
- 65
web_src/js/features/letteravatar.js View File

@@ -1,74 +1,100 @@
/**
* LetterAvatar
*
*
* Artur Heinze
* Create Letter avatar based on Initials
* based on https://gist.github.com/leecrossley/6027780
*/
(function(w, d){
function LetterAvatar (name, size, color) {
name = name || '';
size = size || 60;
var colours = [
"#1abc9c", "#2ecc71", "#3498db", "#9b59b6", "#34495e", "#16a085", "#27ae60", "#2980b9", "#8e44ad", "#2c3e50",
"#f1c40f", "#e67e22", "#e74c3c", "#00bcd4", "#95a5a6", "#f39c12", "#d35400", "#c0392b", "#bdc3c7", "#7f8c8d"
],
nameSplit = String(name).split(' '),
initials, charIndex, colourIndex, canvas, context, dataURI;
if (nameSplit.length == 1) {
initials = nameSplit[0] ? nameSplit[0].charAt(0):'?';
} else {
initials = nameSplit[0].charAt(0) + nameSplit[1].charAt(0);
}
if (w.devicePixelRatio) {
size = (size * w.devicePixelRatio);
}
charIndex = (initials == '?' ? 72 : initials.charCodeAt(0)) - 64;
colourIndex = charIndex % 20;
canvas = d.createElement('canvas');
canvas.width = size;
canvas.height = size;
context = canvas.getContext("2d");
context.fillStyle = color ? color : colours[colourIndex - 1];
context.fillRect (0, 0, canvas.width, canvas.height);
context.font = Math.round(canvas.width/2)+"px 'Microsoft Yahei'";
context.textAlign = "center";
context.fillStyle = "#FFF";
context.fillText(initials, size / 2, size / 1.5);
dataURI = canvas.toDataURL();
canvas = null;
return dataURI;
(function (w, d) {
function LetterAvatar(name, size, color) {
name = name || "";
size = size || 60;
var colours = [
"#1abc9c",
"#2ecc71",
"#3498db",
"#9b59b6",
"#34495e",
"#16a085",
"#27ae60",
"#2980b9",
"#8e44ad",
"#2c3e50",
"#f1c40f",
"#e67e22",
"#e74c3c",
"#00bcd4",
"#95a5a6",
"#f39c12",
"#d35400",
"#c0392b",
"#bdc3c7",
"#7f8c8d",
],
nameSplit = String(name).split(" "),
initials,
charIndex,
colourIndex,
canvas,
context,
dataURI;
if (nameSplit.length == 1) {
initials = nameSplit[0] ? nameSplit[0].charAt(0) : "?";
} else {
initials = nameSplit[0].charAt(0) + nameSplit[1].charAt(0);
}
let initials1 = initials.toUpperCase();
if (w.devicePixelRatio) {
size = size * w.devicePixelRatio;
}
LetterAvatar.transform = function() {
Array.prototype.forEach.call(d.querySelectorAll('img[avatar]'), function(img, name, color) {
name = img.getAttribute('avatar');
color = img.getAttribute('color');
img.src = LetterAvatar(name, img.getAttribute('width'), color);
img.removeAttribute('avatar');
img.setAttribute('alt', name);
});
};
// AMD support
if (typeof define === 'function' && define.amd) {
define(function () { return LetterAvatar; });

charIndex = (initials == "?" ? 72 : initials.charCodeAt(0)) - 64;
colourIndex = charIndex % 20;
canvas = d.createElement("canvas");
canvas.width = size;
canvas.height = size;
context = canvas.getContext("2d");

context.fillStyle = color ? color : colours[colourIndex - 1];
context.fillRect(0, 0, canvas.width, canvas.height);
context.font = Math.round(canvas.width / 2) + "px 'Microsoft Yahei'";
context.textAlign = "center";
context.fillStyle = "#FFF";
context.fillText(initials1, size / 2, size / 1.5);
dataURI = canvas.toDataURL();
canvas = null;
return dataURI;
}
LetterAvatar.transform = function () {
Array.prototype.forEach.call(
d.querySelectorAll("img[avatar]"),
function (img, name, color) {
name = img.getAttribute("avatar");
color = img.getAttribute("color");
img.src = LetterAvatar(name, img.getAttribute("width"), color);
img.removeAttribute("avatar");
img.setAttribute("alt", name);
}
);
};
// AMD support
if (typeof define === "function" && define.amd) {
define(function () {
return LetterAvatar;
});

// CommonJS and Node.js module support.
} else if (typeof exports !== 'undefined') {
// Support Node.js specific `module.exports` (which can be a function)
if (typeof module != 'undefined' && module.exports) {
exports = module.exports = LetterAvatar;
}
// But always support CommonJS module 1.1.1 spec (`exports` cannot be a function)
exports.LetterAvatar = LetterAvatar;
} else {
window.LetterAvatar = LetterAvatar;
d.addEventListener('DOMContentLoaded', function(event) {
LetterAvatar.transform();
});
} else if (typeof exports !== "undefined") {
// Support Node.js specific `module.exports` (which can be a function)
if (typeof module != "undefined" && module.exports) {
exports = module.exports = LetterAvatar;
}
})(window, document);
// But always support CommonJS module 1.1.1 spec (`exports` cannot be a function)
exports.LetterAvatar = LetterAvatar;
} else {
window.LetterAvatar = LetterAvatar;
d.addEventListener("DOMContentLoaded", function (event) {
LetterAvatar.transform();
});
}
})(window, document);

Loading…
Cancel
Save