#1460 V20220125合入develop

Merged
lewis merged 337 commits from V20220125 into develop 2 years ago
  1. BIN
      custom/public/img/ranking_list.jpg
  2. +1
    -1
      integrations/pull_update_test.go
  3. +18
    -0
      models/action.go
  4. +98
    -25
      models/cloudbrain.go
  5. +179
    -42
      models/repo.go
  6. +1
    -0
      models/repo_generate.go
  7. +24
    -2
      models/repo_list.go
  8. +10
    -5
      models/repo_tag.go
  9. +4
    -1
      models/repo_watch.go
  10. +1
    -1
      models/user_business_analysis.go
  11. +2
    -0
      modules/auth/auth.go
  12. +13
    -8
      modules/auth/cloudbrain.go
  13. +15
    -0
      modules/auth/repo_form.go
  14. +5
    -0
      modules/auth/user_form.go
  15. +40
    -32
      modules/cloudbrain/cloudbrain.go
  16. +101
    -0
      modules/cloudbrain/resty.go
  17. +1
    -1
      modules/context/context.go
  18. +2
    -0
      modules/context/org.go
  19. +1
    -0
      modules/context/repo.go
  20. +16
    -0
      modules/notification/action/action.go
  21. +1
    -0
      modules/notification/base/notifier.go
  22. +4
    -0
      modules/notification/base/null.go
  23. +11
    -1
      modules/repository/create.go
  24. +2
    -1
      modules/repository/fork.go
  25. +1
    -1
      modules/repository/fork_test.go
  26. +1
    -0
      modules/repository/generate.go
  27. +1
    -1
      modules/repository/init.go
  28. +23
    -5
      modules/setting/setting.go
  29. +28
    -25
      modules/structs/repo.go
  30. +1
    -0
      modules/task/task.go
  31. +2
    -0
      modules/templates/helper.go
  32. +4
    -1
      modules/timeutil/since.go
  33. +24
    -0
      modules/validation/binding.go
  34. +57
    -8
      options/locale/locale_en-US.ini
  35. +54
    -3
      options/locale/locale_zh-CN.ini
  36. +34
    -86
      public/home/home.js
  37. +2
    -0
      routers/api/v1/api.go
  38. +57
    -0
      routers/api/v1/repo/cloudbrain.go
  39. +1
    -1
      routers/api/v1/repo/fork.go
  40. +2
    -1
      routers/api/v1/repo/modelarts.go
  41. +24
    -0
      routers/api/v1/repo/repo_dashbord.go
  42. +8
    -57
      routers/home.go
  43. +43
    -6
      routers/org/home.go
  44. +7
    -3
      routers/org/members.go
  45. +10
    -3
      routers/org/teams.go
  46. +437
    -16
      routers/repo/cloudbrain.go
  47. +196
    -0
      routers/repo/course.go
  48. +10
    -10
      routers/repo/modelarts.go
  49. +2
    -1
      routers/repo/pull.go
  50. +31
    -1
      routers/repo/repo.go
  51. +36
    -4
      routers/repo/setting.go
  52. +9
    -9
      routers/repo/user_data_analysis.go
  53. +7
    -1
      routers/repo/view.go
  54. +21
    -0
      routers/routes/routes.go
  55. +89
    -3
      services/repository/repository.go
  56. +2
    -2
      services/repository/transfer.go
  57. +1
    -0
      services/socketwrap/clientManager.go
  58. +1
    -1
      templates/admin/repo/list.tmpl
  59. +14
    -10
      templates/base/head.tmpl
  60. +209
    -0
      templates/base/head_course.tmpl
  61. +4
    -2
      templates/base/head_fluid.tmpl
  62. +4
    -2
      templates/base/head_home.tmpl
  63. +3
    -3
      templates/base/head_navbar.tmpl
  64. +4
    -4
      templates/base/head_navbar_fluid.tmpl
  65. +4
    -4
      templates/base/head_navbar_home.tmpl
  66. +4
    -4
      templates/base/head_navbar_pro.tmpl
  67. +4
    -2
      templates/base/head_pro.tmpl
  68. +2
    -2
      templates/explore/dataset_list.tmpl
  69. +7
    -7
      templates/explore/repo_list.tmpl
  70. +2
    -2
      templates/home.tmpl
  71. +137
    -0
      templates/org/course_list.tmpl
  72. +49
    -11
      templates/org/header.tmpl
  73. +32
    -0
      templates/org/header_course.tmpl
  74. +454
    -0
      templates/org/home_courses.tmpl
  75. +130
    -0
      templates/org/member/course_members.tmpl
  76. +28
    -0
      templates/org/navber_course.tmpl
  77. +2
    -2
      templates/org/repo_list.tmpl
  78. +7
    -4
      templates/org/select_pro.tmpl
  79. +56
    -0
      templates/org/team/courseTeams.tmpl
  80. +1
    -1
      templates/org/team/repositories.tmpl
  81. +387
    -0
      templates/repo/cloudbrain/benchmark/index.tmpl
  82. +233
    -0
      templates/repo/cloudbrain/benchmark/new.tmpl
  83. +458
    -0
      templates/repo/cloudbrain/benchmark/show.tmpl
  84. +1
    -4
      templates/repo/cloudbrain/new.tmpl
  85. +295
    -0
      templates/repo/courseHome.tmpl
  86. +4
    -32
      templates/repo/create.tmpl
  87. +111
    -0
      templates/repo/createCourse.tmpl
  88. +1
    -0
      templates/repo/debugjob/index.tmpl
  89. +6
    -6
      templates/repo/header.tmpl
  90. +2
    -11
      templates/repo/home.tmpl
  91. +3
    -31
      templates/repo/migrate.tmpl
  92. +1
    -0
      templates/repo/modelarts/inferencejob/index.tmpl
  93. +1
    -0
      templates/repo/modelarts/trainjob/index.tmpl
  94. +3
    -32
      templates/repo/pulls/fork.tmpl
  95. +57
    -0
      templates/repo/repo_name.tmpl
  96. +47
    -13
      templates/repo/settings/options.tmpl
  97. +1
    -0
      templates/repo/view_list.tmpl
  98. +21
    -21
      templates/user/dashboard/feeds.tmpl
  99. +4
    -1
      templates/user/dashboard/issues.tmpl
  100. +4
    -1
      templates/user/dashboard/milestones.tmpl

BIN
custom/public/img/ranking_list.jpg View File

Before After
Width: 600  |  Height: 544  |  Size: 36 KiB

+ 1
- 1
integrations/pull_update_test.go View File

@@ -58,7 +58,7 @@ func createOutdatedPR(t *testing.T, actor, forkOrg *models.User) *models.PullReq
assert.NoError(t, err) assert.NoError(t, err)
assert.NotEmpty(t, baseRepo) assert.NotEmpty(t, baseRepo)


headRepo, err := repo_module.ForkRepository(actor, forkOrg, baseRepo, "repo-pr-update", "desc")
headRepo, err := repo_module.ForkRepository(actor, forkOrg, baseRepo, "repo-pr-update", "desc", "")
assert.NoError(t, err) assert.NoError(t, err)
assert.NotEmpty(t, headRepo) assert.NotEmpty(t, headRepo)




+ 18
- 0
models/action.go View File

@@ -164,12 +164,24 @@ func (a *Action) GetRepoName() string {
return a.Repo.Name return a.Repo.Name
} }


// GetRepoName returns the name of the action repository.
func (a *Action) GetRepoDisplayName() string {
a.loadRepo()
return a.Repo.DisplayName()
}

// ShortRepoName returns the name of the action repository // ShortRepoName returns the name of the action repository
// trimmed to max 33 chars. // trimmed to max 33 chars.
func (a *Action) ShortRepoName() string { func (a *Action) ShortRepoName() string {
return base.EllipsisString(a.GetRepoName(), 33) return base.EllipsisString(a.GetRepoName(), 33)
} }


// ShortRepoName returns the name of the action repository
// trimmed to max 33 chars.
func (a *Action) ShortRepoDisplayName() string {
return base.EllipsisString(a.GetRepoDisplayName(), 33)
}

// GetRepoPath returns the virtual path to the action repository. // GetRepoPath returns the virtual path to the action repository.
func (a *Action) GetRepoPath() string { func (a *Action) GetRepoPath() string {
return path.Join(a.GetRepoUserName(), a.GetRepoName()) return path.Join(a.GetRepoUserName(), a.GetRepoName())
@@ -181,6 +193,12 @@ func (a *Action) ShortRepoPath() string {
return path.Join(a.ShortRepoUserName(), a.ShortRepoName()) return path.Join(a.ShortRepoUserName(), a.ShortRepoName())
} }


// ShortRepoPath returns the virtual path to the action repository
// trimmed to max 20 + 1 + 33 chars.
func (a *Action) ShortRepoFullDisplayName() string {
return path.Join(a.ShortRepoUserName(), a.ShortRepoDisplayName())
}

// GetRepoLink returns relative link to action repository. // GetRepoLink returns relative link to action repository.
func (a *Action) GetRepoLink() string { func (a *Action) GetRepoLink() string {
if len(setting.AppSubURL) > 0 { if len(setting.AppSubURL) > 0 {


+ 98
- 25
models/cloudbrain.go View File

@@ -78,28 +78,30 @@ const (
) )


type Cloudbrain struct { type Cloudbrain struct {
ID int64 `xorm:"pk autoincr"`
JobID string `xorm:"INDEX NOT NULL"`
JobType string `xorm:"INDEX NOT NULL DEFAULT 'DEBUG'"`
JobName string
Status string
UserID int64
RepoID int64
SubTaskName string
ContainerID string
ContainerIp string
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
Duration int64
TrainJobDuration string
Image string //GPU镜像名称
GpuQueue string //GPU类型即GPU队列
ResourceSpecId int //GPU规格id
DeletedAt time.Time `xorm:"deleted"`
CanDebug bool `xorm:"-"`
CanDel bool `xorm:"-"`
CanModify bool `xorm:"-"`
Type int
ID int64 `xorm:"pk autoincr"`
JobID string `xorm:"INDEX NOT NULL"`
JobType string `xorm:"INDEX NOT NULL DEFAULT 'DEBUG'"`
JobName string
Status string
UserID int64
RepoID int64
SubTaskName string
ContainerID string
ContainerIp string
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
Duration int64
TrainJobDuration string
Image string //GPU镜像名称
GpuQueue string //GPU类型即GPU队列
ResourceSpecId int //GPU规格id
DeletedAt time.Time `xorm:"deleted"`
CanDebug bool `xorm:"-"`
CanDel bool `xorm:"-"`
CanModify bool `xorm:"-"`
Type int
BenchmarkTypeID int
BenchmarkChildTypeID int


VersionID int64 //版本id VersionID int64 //版本id
VersionName string `xorm:"INDEX"` //当前版本 VersionName string `xorm:"INDEX"` //当前版本
@@ -214,7 +216,7 @@ type CloudbrainsOptions struct {
CloudbrainIDs []int64 CloudbrainIDs []int64
// JobStatus CloudbrainStatus // JobStatus CloudbrainStatus
Type int Type int
JobTypes []string
JobTypes []string
VersionName string VersionName string
IsLatestVersion string IsLatestVersion string
JobTypeNot bool JobTypeNot bool
@@ -387,6 +389,24 @@ type Category struct {
Value string `json:"value"` Value string `json:"value"`
} }


type BenchmarkTypes struct {
BenchmarkType []*BenchmarkType `json:"type"`
}

type BenchmarkType struct {
Id int `json:"id"`
First string `json:"first"` //一级算法类型名称
Second []*BenchmarkDataset `json:"second"`
}

type BenchmarkDataset struct {
Id int `json:"id"`
Value string `json:"value"` //二级算法类型名称
Attachment string `json:"attachment"` //数据集的uuid
Owner string `json:"owner"` //评估脚本所在仓库的拥有者
RepoName string `json:"repo_name"` //评估脚本所在仓库的名称
}

type GpuInfos struct { type GpuInfos struct {
GpuInfo []*GpuInfo `json:"gpu_type"` GpuInfo []*GpuInfo `json:"gpu_type"`
} }
@@ -442,6 +462,59 @@ type CommitImageResult struct {
Payload map[string]interface{} `json:"payload"` Payload map[string]interface{} `json:"payload"`
} }


type GetJobLogParams struct {
Size string `json:"size"`
Sort string `json:"sort"`
QueryInfo QueryInfo `json:"query"`
}

type QueryInfo struct {
MatchInfo MatchInfo `json:"match"`
}

type MatchInfo struct {
PodName string `json:"kubernetes.pod.name"`
}

type GetJobLogResult struct {
ScrollID string `json:"_scroll_id"`
Took int `json:"took"`
TimedOut bool `json:"timed_out"`
Shards struct {
Total int `json:"total"`
Successful int `json:"successful"`
Skipped int `json:"skipped"`
Failed int `json:"failed"`
} `json:"_shards"`
Hits struct {
Hits []Hits `json:"hits"`
} `json:"hits"`
}

type Hits struct {
Index string `json:"_index"`
Type string `json:"_type"`
ID string `json:"_id"`
Source struct {
Message string `json:"message"`
} `json:"_source"`
Sort []int `json:"sort"`
}

type GetAllJobLogParams struct {
Scroll string `json:"scroll"`
ScrollID string `json:"scroll_id"`
}

type DeleteJobLogTokenParams struct {
ScrollID string `json:"scroll_id"`
}

type DeleteJobLogTokenResult struct {
Succeeded bool `json:"succeeded"`
NumFreed int `json:"num_freed"`
}

type CloudBrainResult struct { type CloudBrainResult struct {
Code string `json:"code"` Code string `json:"code"`
Msg string `json:"msg"` Msg string `json:"msg"`
@@ -1221,8 +1294,8 @@ func GetCloudBrainUnStoppedJob() ([]*Cloudbrain, error) {
Find(&cloudbrains) Find(&cloudbrains)
} }


func GetCloudbrainCountByUserID(userID int64) (int, error) {
count, err := x.In("status", JobWaiting, JobRunning).And("job_type = ? and user_id = ? and type = ?", JobTypeDebug, userID, TypeCloudBrainOne).Count(new(Cloudbrain))
func GetCloudbrainCountByUserID(userID int64, jobType string) (int, error) {
count, err := x.In("status", JobWaiting, JobRunning).And("job_type = ? and user_id = ? and type = ?", jobType, userID, TypeCloudBrainOne).Count(new(Cloudbrain))
return int(count), err return int(count), err
} }




+ 179
- 42
models/repo.go View File

@@ -12,6 +12,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"html/template" "html/template"
"math/rand"
"xorm.io/xorm" "xorm.io/xorm"


"code.gitea.io/gitea/modules/blockchain" "code.gitea.io/gitea/modules/blockchain"
@@ -139,6 +140,7 @@ func NewRepoContext() {
// RepositoryStatus defines the status of repository // RepositoryStatus defines the status of repository
type RepositoryStatus int type RepositoryStatus int
type RepoBlockChainStatus int type RepoBlockChainStatus int
type RepoType int


// all kinds of RepositoryStatus // all kinds of RepositoryStatus
const ( const (
@@ -152,6 +154,11 @@ const (
RepoBlockChainFailed RepoBlockChainFailed
) )


const (
RepoNormal RepoType = iota
RepoCourse
)

// Repository represents a git repository. // Repository represents a git repository.
type Repository struct { type Repository struct {
ID int64 `xorm:"pk autoincr"` ID int64 `xorm:"pk autoincr"`
@@ -165,7 +172,8 @@ type Repository struct {
OriginalServiceType api.GitServiceType `xorm:"index"` OriginalServiceType api.GitServiceType `xorm:"index"`
OriginalURL string `xorm:"VARCHAR(2048)"` OriginalURL string `xorm:"VARCHAR(2048)"`
DefaultBranch string DefaultBranch string

CreatorID int64 `xorm:"INDEX NOT NULL DEFAULT 0"`
Creator *User `xorm:"-"`
NumWatches int NumWatches int
NumStars int NumStars int
NumForks int NumForks int
@@ -174,11 +182,12 @@ type Repository struct {
NumOpenIssues int `xorm:"-"` NumOpenIssues int `xorm:"-"`
NumPulls int NumPulls int
NumClosedPulls int NumClosedPulls int
NumOpenPulls int `xorm:"-"`
NumMilestones int `xorm:"NOT NULL DEFAULT 0"`
NumClosedMilestones int `xorm:"NOT NULL DEFAULT 0"`
NumOpenMilestones int `xorm:"-"`
NumCommit int64 `xorm:"NOT NULL DEFAULT 0"`
NumOpenPulls int `xorm:"-"`
NumMilestones int `xorm:"NOT NULL DEFAULT 0"`
NumClosedMilestones int `xorm:"NOT NULL DEFAULT 0"`
NumOpenMilestones int `xorm:"-"`
NumCommit int64 `xorm:"NOT NULL DEFAULT 0"`
RepoType RepoType `xorm:"NOT NULL DEFAULT 0"`


IsPrivate bool `xorm:"INDEX"` IsPrivate bool `xorm:"INDEX"`
IsEmpty bool `xorm:"INDEX"` IsEmpty bool `xorm:"INDEX"`
@@ -221,8 +230,10 @@ type Repository struct {
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`


Hot int64 `xorm:"-"`
Active int64 `xorm:"-"`
Hot int64 `xorm:"-"`
Active int64 `xorm:"-"`
Alias string `xorm:"INDEX"`
LowerAlias string `xorm:"INDEX"`
} }


// SanitizedOriginalURL returns a sanitized OriginalURL // SanitizedOriginalURL returns a sanitized OriginalURL
@@ -233,6 +244,14 @@ func (repo *Repository) SanitizedOriginalURL() string {
return util.SanitizeURLCredentials(repo.OriginalURL, false) return util.SanitizeURLCredentials(repo.OriginalURL, false)
} }


// GetAlias returns a sanitized OriginalURL
func (repo *Repository) DisplayName() string {
if repo.Alias == "" {
return repo.Name
}
return repo.Alias
}

// ColorFormat returns a colored string to represent this repo // ColorFormat returns a colored string to represent this repo
func (repo *Repository) ColorFormat(s fmt.State) { func (repo *Repository) ColorFormat(s fmt.State) {
var ownerName interface{} var ownerName interface{}
@@ -286,6 +305,11 @@ func (repo *Repository) FullName() string {
return repo.OwnerName + "/" + repo.Name return repo.OwnerName + "/" + repo.Name
} }


// FullDisplayName returns the repository full display name
func (repo *Repository) FullDisplayName() string {
return repo.OwnerName + "/" + repo.DisplayName()
}

// HTMLURL returns the repository HTML URL // HTMLURL returns the repository HTML URL
func (repo *Repository) HTMLURL() string { func (repo *Repository) HTMLURL() string {
return setting.AppURL + repo.FullName() return setting.AppURL + repo.FullName()
@@ -385,7 +409,9 @@ func (repo *Repository) innerAPIFormat(e Engine, mode AccessMode, isParent bool)
ID: repo.ID, ID: repo.ID,
Owner: repo.Owner.APIFormat(), Owner: repo.Owner.APIFormat(),
Name: repo.Name, Name: repo.Name,
Alias: repo.Alias,
FullName: repo.FullName(), FullName: repo.FullName(),
FullDisplayName: repo.FullDisplayName(),
Description: repo.Description, Description: repo.Description,
Private: repo.IsPrivate, Private: repo.IsPrivate,
Template: repo.IsTemplate, Template: repo.IsTemplate,
@@ -548,6 +574,19 @@ func (repo *Repository) GetOwner() error {
return repo.getOwner(x) return repo.getOwner(x)
} }


func (repo *Repository) getCreator(e Engine) (err error) {
if repo.CreatorID == 0 {
return nil
}

repo.Creator, err = getUserByID(e, repo.CreatorID)
return err
}

func (repo *Repository) GetCreator() error {
return repo.getCreator(x)
}

func (repo *Repository) mustOwner(e Engine) *User { func (repo *Repository) mustOwner(e Engine) *User {
if err := repo.getOwner(e); err != nil { if err := repo.getOwner(e); err != nil {
return &User{ return &User{
@@ -921,17 +960,50 @@ func (repo *Repository) DescriptionHTML() template.HTML {
return template.HTML(markup.Sanitize(string(desc))) return template.HTML(markup.Sanitize(string(desc)))
} }


func isRepositoryExist(e Engine, u *User, repoName string) (bool, error) {
has, err := e.Get(&Repository{
OwnerID: u.ID,
LowerName: strings.ToLower(repoName),
})
return has && com.IsDir(RepoPath(u.Name, repoName)), err
func isRepositoryExist(e Engine, u *User, repoName string, alias string) (bool, error) {
var cond = builder.NewCond()
cond = cond.And(builder.Eq{"owner_id": u.ID})
if alias != "" {
subCon := builder.NewCond()
subCon = subCon.Or(builder.Eq{"lower_alias": strings.ToLower(alias)}, builder.Eq{"lower_name": strings.ToLower(repoName)})
cond = cond.And(subCon)
} else {
cond = cond.And(builder.Eq{"lower_name": strings.ToLower(repoName)})
}
count, err := e.Where(cond).Count(&Repository{})
return count > 0 || com.IsDir(RepoPath(u.Name, repoName)), err
} }


// IsRepositoryExist returns true if the repository with given name under user has already existed. // IsRepositoryExist returns true if the repository with given name under user has already existed.
func IsRepositoryExist(u *User, repoName string) (bool, error) {
return isRepositoryExist(x, u, repoName)
func IsRepositoryExist(u *User, repoName string, alias string) (bool, error) {
return isRepositoryExist(x, u, repoName, alias)
}

// IsRepositoryAliasExist returns true if the repository with given alias under user has already existed.
func IsRepositoryAliasExist(u *User, alias string) (bool, error) {
return isRepositoryAliasExist(x, u, alias)
}

func isRepositoryAliasExist(e Engine, u *User, alias string) (bool, error) {
var cond = builder.NewCond()
cond = cond.And(builder.Eq{"owner_id": u.ID})
cond = cond.And(builder.Eq{"lower_alias": strings.ToLower(alias)})
count, err := e.Where(cond).Count(&Repository{})
return count > 0, err
}

func IsRepositoryAliasAvailable(doer *User, alias string) error {
if err := IsUsableRepoAlias(alias); err != nil {
return err
}

has, err := IsRepositoryAliasExist(doer, alias)
if err != nil {
return fmt.Errorf("IsRepositoryExist: %v", err)
} else if has {
return ErrRepoAlreadyExist{doer.Name, alias}
}
return nil
} }


// CloneLink represents different types of clone URLs of repository. // CloneLink represents different types of clone URLs of repository.
@@ -975,20 +1047,24 @@ func (repo *Repository) CloneLink() (cl *CloneLink) {
} }


// CheckCreateRepository check if could created a repository // CheckCreateRepository check if could created a repository
func CheckCreateRepository(doer, u *User, name string) error {
func CheckCreateRepository(doer, u *User, repoName, alias string) error {
if !doer.CanCreateRepo() { if !doer.CanCreateRepo() {
return ErrReachLimitOfRepo{u.MaxRepoCreation} return ErrReachLimitOfRepo{u.MaxRepoCreation}
} }


if err := IsUsableRepoName(name); err != nil {
if err := IsUsableRepoName(repoName); err != nil {
return err
}

if err := IsUsableRepoAlias(alias); err != nil {
return err return err
} }


has, err := isRepositoryExist(x, u, name)
has, err := isRepositoryExist(x, u, repoName, alias)
if err != nil { if err != nil {
return fmt.Errorf("IsRepositoryExist: %v", err) return fmt.Errorf("IsRepositoryExist: %v", err)
} else if has { } else if has {
return ErrRepoAlreadyExist{u.Name, name}
return ErrRepoAlreadyExist{u.Name, repoName}
} }
return nil return nil
} }
@@ -996,6 +1072,7 @@ func CheckCreateRepository(doer, u *User, name string) error {
// CreateRepoOptions contains the create repository options // CreateRepoOptions contains the create repository options
type CreateRepoOptions struct { type CreateRepoOptions struct {
Name string Name string
Alias string
Description string Description string
OriginalURL string OriginalURL string
GitServiceType api.GitServiceType GitServiceType api.GitServiceType
@@ -1008,6 +1085,8 @@ type CreateRepoOptions struct {
IsMirror bool IsMirror bool
AutoInit bool AutoInit bool
Status RepositoryStatus Status RepositoryStatus
IsCourse bool
Topics []string
} }


// GetRepoInitFile returns repository init files // GetRepoInitFile returns repository init files
@@ -1036,8 +1115,10 @@ func GetRepoInitFile(tp, name string) ([]byte, error) {
} }


var ( var (
reservedRepoNames = []string{".", ".."}
reservedRepoPatterns = []string{"*.git", "*.wiki"}
reservedRepoNames = []string{".", ".."}
reservedRepoPatterns = []string{"*.git", "*.wiki"}
reservedRepoAliasNames = []string{}
reservedRepoAliasPatterns = []string{}
) )


// IsUsableRepoName returns true when repository is usable // IsUsableRepoName returns true when repository is usable
@@ -1045,19 +1126,31 @@ func IsUsableRepoName(name string) error {
return isUsableName(reservedRepoNames, reservedRepoPatterns, name) return isUsableName(reservedRepoNames, reservedRepoPatterns, name)
} }


// IsUsableRepoAlias returns true when repository alias is usable
func IsUsableRepoAlias(name string) error {
return isUsableName(reservedRepoAliasNames, reservedRepoAliasPatterns, name)
}

// CreateRepository creates a repository for the user/organization. // CreateRepository creates a repository for the user/organization.
func CreateRepository(ctx DBContext, doer, u *User, repo *Repository) (err error) {
func CreateRepository(ctx DBContext, doer, u *User, repo *Repository, opts ...CreateRepoOptions) (err error) {
repo.LowerAlias = strings.ToLower(repo.Alias)
if err = IsUsableRepoName(repo.Name); err != nil { if err = IsUsableRepoName(repo.Name); err != nil {
return err return err
} }


has, err := isRepositoryExist(ctx.e, u, repo.Name)
if err := IsUsableRepoAlias(repo.Alias); err != nil {
return err
}
has, err := isRepositoryExist(ctx.e, u, repo.Name, repo.Alias)
if err != nil { if err != nil {
return fmt.Errorf("IsRepositoryExist: %v", err) return fmt.Errorf("IsRepositoryExist: %v", err)
} else if has { } else if has {
return ErrRepoAlreadyExist{u.Name, repo.Name} return ErrRepoAlreadyExist{u.Name, repo.Name}
} }

isCourse := isCourse(opts)
if isCourse {
repo.CreatorID = doer.ID
}
if _, err = ctx.e.Insert(repo); err != nil { if _, err = ctx.e.Insert(repo); err != nil {
return err return err
} }
@@ -1091,17 +1184,23 @@ func CreateRepository(ctx DBContext, doer, u *User, repo *Repository) (err error
Config: &PullRequestsConfig{AllowMerge: true, AllowRebase: true, AllowRebaseMerge: true, AllowSquash: true}, Config: &PullRequestsConfig{AllowMerge: true, AllowRebase: true, AllowRebaseMerge: true, AllowSquash: true},
}) })
} else if tp == UnitTypeDatasets { } else if tp == UnitTypeDatasets {
units = append(units, RepoUnit{
RepoID: repo.ID,
Type: tp,
Config: &DatasetConfig{EnableDataset: true},
})
if !isCourse {
units = append(units, RepoUnit{
RepoID: repo.ID,
Type: tp,
Config: &DatasetConfig{EnableDataset: true},
})
}

} else if tp == UnitTypeCloudBrain { } else if tp == UnitTypeCloudBrain {
units = append(units, RepoUnit{
RepoID: repo.ID,
Type: tp,
Config: &CloudBrainConfig{EnableCloudBrain: true},
})
if !isCourse {
units = append(units, RepoUnit{
RepoID: repo.ID,
Type: tp,
Config: &CloudBrainConfig{EnableCloudBrain: true},
})
}

} else if tp == UnitTypeBlockChain { } else if tp == UnitTypeBlockChain {
units = append(units, RepoUnit{ units = append(units, RepoUnit{
RepoID: repo.ID, RepoID: repo.ID,
@@ -1109,11 +1208,13 @@ func CreateRepository(ctx DBContext, doer, u *User, repo *Repository) (err error
Config: &BlockChainConfig{EnableBlockChain: true}, Config: &BlockChainConfig{EnableBlockChain: true},
}) })
} else if tp == UnitTypeModelManage { } else if tp == UnitTypeModelManage {
units = append(units, RepoUnit{
RepoID: repo.ID,
Type: tp,
Config: &ModelManageConfig{EnableModelManage: true},
})
if !isCourse {
units = append(units, RepoUnit{
RepoID: repo.ID,
Type: tp,
Config: &ModelManageConfig{EnableModelManage: true},
})
}
} else { } else {
units = append(units, RepoUnit{ units = append(units, RepoUnit{
RepoID: repo.ID, RepoID: repo.ID,
@@ -1183,6 +1284,14 @@ func CreateRepository(ctx DBContext, doer, u *User, repo *Repository) (err error
return nil return nil
} }


func isCourse(opts []CreateRepoOptions) bool {
var isCourse = false
if len(opts) > 0 {
isCourse = opts[0].IsCourse
}
return isCourse
}

func countRepositories(userID int64, private bool) int64 { func countRepositories(userID int64, private bool) int64 {
sess := x.Where("id > 0") sess := x.Where("id > 0")


@@ -1233,7 +1342,7 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error
} }


// Check if new owner has repository with same name. // Check if new owner has repository with same name.
has, err := IsRepositoryExist(newOwner, repo.Name)
has, err := IsRepositoryExist(newOwner, repo.Name, repo.Alias)
if err != nil { if err != nil {
return fmt.Errorf("IsRepositoryExist: %v", err) return fmt.Errorf("IsRepositoryExist: %v", err)
} else if has { } else if has {
@@ -1366,7 +1475,7 @@ func ChangeRepositoryName(doer *User, repo *Repository, newRepoName string) (err
return err return err
} }


has, err := IsRepositoryExist(repo.Owner, newRepoName)
has, err := IsRepositoryExist(repo.Owner, newRepoName, "")
if err != nil { if err != nil {
return fmt.Errorf("IsRepositoryExist: %v", err) return fmt.Errorf("IsRepositoryExist: %v", err)
} else if has { } else if has {
@@ -1848,6 +1957,26 @@ func getRepositoryByOwnerAndName(e Engine, ownerName, repoName string) (*Reposit
return &repo, nil return &repo, nil
} }


// GetRepositoryByOwnerAndAlias returns the repository by given ownername and reponame.
func GetRepositoryByOwnerAndAlias(ownerName, alias string) (*Repository, error) {
return getRepositoryByOwnerAndAlias(x, ownerName, alias)
}

func getRepositoryByOwnerAndAlias(e Engine, ownerName, alias string) (*Repository, error) {
var repo Repository
has, err := e.Table("repository").Select("repository.*").
Join("INNER", "`user`", "`user`.id = repository.owner_id").
Where("repository.lower_alias = ?", strings.ToLower(alias)).
And("`user`.lower_name = ?", strings.ToLower(ownerName)).
Get(&repo)
if err != nil {
return nil, err
} else if !has {
return nil, ErrRepoNotExist{0, 0, ownerName, alias}
}
return &repo, nil
}

// GetRepositoryByName returns the repository by given name under user if exists. // GetRepositoryByName returns the repository by given name under user if exists.
func GetRepositoryByName(ownerID int64, name string) (*Repository, error) { func GetRepositoryByName(ownerID int64, name string) (*Repository, error) {
repo := &Repository{ repo := &Repository{
@@ -2521,6 +2650,14 @@ func UpdateRepositoryCommitNum(repo *Repository) error {
return nil return nil
} }


func GenerateDefaultRepoName(ownerName string) string {
if len(ownerName) > 5 {
ownerName = ownerName[:5]
}
now := time.Now().Format("20060102150405")
return ownerName + now + fmt.Sprint(rand.Intn(10))
}

type RepoFile struct { type RepoFile struct {
CommitId string CommitId string
Content []byte Content []byte


+ 1
- 0
models/repo_generate.go View File

@@ -19,6 +19,7 @@ import (
// GenerateRepoOptions contains the template units to generate // GenerateRepoOptions contains the template units to generate
type GenerateRepoOptions struct { type GenerateRepoOptions struct {
Name string Name string
Alias string
Description string Description string
Private bool Private bool
GitContent bool GitContent bool


+ 24
- 2
models/repo_list.go View File

@@ -48,9 +48,12 @@ func (repos RepositoryList) loadAttributes(e Engine) error {


set := make(map[int64]struct{}) set := make(map[int64]struct{})
repoIDs := make([]int64, len(repos)) repoIDs := make([]int64, len(repos))
setCreator := make(map[int64]struct{})
for i := range repos { for i := range repos {
set[repos[i].OwnerID] = struct{}{} set[repos[i].OwnerID] = struct{}{}
repoIDs[i] = repos[i].ID repoIDs[i] = repos[i].ID
setCreator[repos[i].CreatorID] = struct{}{}

} }


// Load owners. // Load owners.
@@ -61,8 +64,18 @@ func (repos RepositoryList) loadAttributes(e Engine) error {
Find(&users); err != nil { Find(&users); err != nil {
return fmt.Errorf("find users: %v", err) return fmt.Errorf("find users: %v", err)
} }
//Load creator
creators := make(map[int64]*User, len(set))
if err := e.
Where("id > 0").
In("id", keysInt64(setCreator)).
Find(&creators); err != nil {
return fmt.Errorf("find create repo users: %v", err)
}

for i := range repos { for i := range repos {
repos[i].Owner = users[repos[i].OwnerID] repos[i].Owner = users[repos[i].OwnerID]
repos[i].Creator = creators[repos[i].CreatorID]
} }


// Load primary language. // Load primary language.
@@ -174,6 +187,10 @@ type SearchRepoOptions struct {
// True -> include just has milestones // True -> include just has milestones
// False -> include just has no milestone // False -> include just has no milestone
HasMilestones util.OptionalBool HasMilestones util.OptionalBool
// None -> include all repos
// True -> include just courses
// False -> include just no courses
Course util.OptionalBool
} }


//SearchOrderBy is used to sort the result //SearchOrderBy is used to sort the result
@@ -200,8 +217,8 @@ const (
SearchOrderByForks SearchOrderBy = "num_forks ASC" SearchOrderByForks SearchOrderBy = "num_forks ASC"
SearchOrderByForksReverse SearchOrderBy = "num_forks DESC" SearchOrderByForksReverse SearchOrderBy = "num_forks DESC"
SearchOrderByDownloadTimes SearchOrderBy = "download_times DESC" SearchOrderByDownloadTimes SearchOrderBy = "download_times DESC"
SearchOrderByHot SearchOrderBy = "(num_watches + num_stars + num_forks + clone_cnt) DESC"
SearchOrderByActive SearchOrderBy = "(num_issues + num_pulls + num_commit) DESC"
SearchOrderByHot SearchOrderBy = "(num_watches + num_stars + num_forks + clone_cnt) DESC"
SearchOrderByActive SearchOrderBy = "(num_issues + num_pulls + num_commit) DESC"
) )


// SearchRepositoryCondition creates a query condition according search repository options // SearchRepositoryCondition creates a query condition according search repository options
@@ -321,6 +338,7 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
var likes = builder.NewCond() var likes = builder.NewCond()
for _, v := range strings.Split(opts.Keyword, ",") { for _, v := range strings.Split(opts.Keyword, ",") {
likes = likes.Or(builder.Like{"lower_name", strings.ToLower(v)}) likes = likes.Or(builder.Like{"lower_name", strings.ToLower(v)})
likes = likes.Or(builder.Like{"alias", v})
if opts.IncludeDescription { if opts.IncludeDescription {
likes = likes.Or(builder.Like{"LOWER(description)", strings.ToLower(v)}) likes = likes.Or(builder.Like{"LOWER(description)", strings.ToLower(v)})
} }
@@ -350,6 +368,10 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
cond = cond.And(builder.Eq{"is_mirror": opts.Mirror == util.OptionalBoolTrue}) cond = cond.And(builder.Eq{"is_mirror": opts.Mirror == util.OptionalBoolTrue})
} }


if opts.Course == util.OptionalBoolTrue {
cond = cond.And(builder.Eq{"repo_type": RepoCourse})
}

if opts.Actor != nil && opts.Actor.IsRestricted { if opts.Actor != nil && opts.Actor.IsRestricted {
cond = cond.And(accessibleRepositoryCondition(opts.Actor)) cond = cond.And(accessibleRepositoryCondition(opts.Actor))
} }


+ 10
- 5
models/repo_tag.go View File

@@ -28,6 +28,7 @@ type OfficialTagRepos struct {
type TagReposBrief struct { type TagReposBrief struct {
RepoID int64 RepoID int64
RepoName string RepoName string
Alias string
TagID int64 TagID int64
} }


@@ -41,7 +42,7 @@ type TagsDetail struct {
TagId int64 TagId int64
TagName string TagName string
TagLimit int TagLimit int
RepoList []Repository
RepoList []*Repository
} }


func GetTagByID(id int64) (*OfficialTag, error) { func GetTagByID(id int64) (*OfficialTag, error) {
@@ -97,7 +98,7 @@ func UpdateTagReposByID(tagID, orgID int64, repoIdList []int64) error {


func GetTagRepos(tagID, orgID int64) ([]TagReposSelected, error) { func GetTagRepos(tagID, orgID int64) ([]TagReposSelected, error) {
t := make([]TagReposBrief, 0) t := make([]TagReposBrief, 0)
const SQLCmd = "select t1.id as repo_id,t1.name as repo_name,t2.id as tag_id from repository t1 left join official_tag_repos t2 on (t1.id = t2.repo_id and t2.tag_id = ?) where t1.owner_id = ? and t1.is_private = false order by t1.updated_unix desc"
const SQLCmd = "select t1.id as repo_id,t1.name as repo_name,t1.alias,t2.id as tag_id from repository t1 left join official_tag_repos t2 on (t1.id = t2.repo_id and t2.tag_id = ?) where t1.owner_id = ? and t1.is_private = false order by t1.updated_unix desc"


if err := x.SQL(SQLCmd, tagID, orgID).Find(&t); err != nil { if err := x.SQL(SQLCmd, tagID, orgID).Find(&t); err != nil {
return nil, err return nil, err
@@ -108,9 +109,13 @@ func GetTagRepos(tagID, orgID int64) ([]TagReposSelected, error) {
if v.TagID > 0 { if v.TagID > 0 {
selected = true selected = true
} }
repoName := v.Alias
if v.Alias == "" {
repoName = v.RepoName
}
r = append(r, TagReposSelected{ r = append(r, TagReposSelected{
RepoID: v.RepoID, RepoID: v.RepoID,
RepoName: v.RepoName,
RepoName: repoName,
Selected: selected, Selected: selected,
}) })
} }
@@ -141,8 +146,8 @@ func GetAllOfficialTagRepos(orgID int64, isOwner bool) ([]TagsDetail, error) {
return result, nil return result, nil
} }


func GetOfficialTagDetail(orgID, tagId int64) ([]Repository, error) {
t := make([]Repository, 0)
func GetOfficialTagDetail(orgID, tagId int64) ([]*Repository, error) {
t := make([]*Repository, 0)
const SQLCmd = "select t2.* from official_tag_repos t1 inner join repository t2 on t1.repo_id = t2.id where t1.org_id = ? and t1.tag_id=? order by t2.updated_unix desc" const SQLCmd = "select t2.* from official_tag_repos t1 inner join repository t2 on t1.repo_id = t2.id where t1.org_id = ? and t1.tag_id=? order by t2.updated_unix desc"


if err := x.SQL(SQLCmd, orgID, tagId).Find(&t); err != nil { if err := x.SQL(SQLCmd, orgID, tagId).Find(&t); err != nil {


+ 4
- 1
models/repo_watch.go View File

@@ -305,7 +305,10 @@ func NotifyWatchersActions(acts []*Action) error {
return err return err
} }
} }
return sess.Commit()

err := sess.Commit()
producer(acts...)
return err
} }


func watchIfAuto(e Engine, userID, repoID int64, isWrite bool) error { func watchIfAuto(e Engine, userID, repoID int64, isWrite bool) error {


+ 1
- 1
models/user_business_analysis.go View File

@@ -387,7 +387,7 @@ func refreshUserStaticTable(wikiCountMap map[string]int, CommitCodeSizeMap map[s


OpenIIndexMap := queryUserRepoOpenIIndex(startTime.Unix(), end_unix) OpenIIndexMap := queryUserRepoOpenIIndex(startTime.Unix(), end_unix)


DataDate := currentTimeNow.Format("2006-01-02")
DataDate := currentTimeNow.Format("2006-01-02") + " 00:01"


cond := "type != 1 and is_active=true" cond := "type != 1 and is_active=true"
count, err := sess.Where(cond).Count(new(User)) count, err := sess.Where(cond).Count(new(User))


+ 2
- 0
modules/auth/auth.go View File

@@ -186,6 +186,8 @@ func validate(errs binding.Errors, data map[string]interface{}, f Form, l macaro
data["ErrorMsg"] = trName + l.Tr("form.include_error", GetInclude(field)) data["ErrorMsg"] = trName + l.Tr("form.include_error", GetInclude(field))
case validation.ErrGlobPattern: case validation.ErrGlobPattern:
data["ErrorMsg"] = trName + l.Tr("form.glob_pattern_error", errs[0].Message) data["ErrorMsg"] = trName + l.Tr("form.glob_pattern_error", errs[0].Message)
case validation.ErrAlphaDashDotChinese:
data["ErrorMsg"] = trName + l.Tr("form.alpha_dash_dot_chinese_error")
default: default:
data["ErrorMsg"] = l.Tr("form.unknown_error") + " " + errs[0].Classification data["ErrorMsg"] = l.Tr("form.unknown_error") + " " + errs[0].Classification
} }


+ 13
- 8
modules/auth/cloudbrain.go View File

@@ -6,14 +6,19 @@ import (
) )


type CreateCloudBrainForm struct { type CreateCloudBrainForm struct {
JobName string `form:"job_name" binding:"Required"`
Image string `form:"image" binding:"Required"`
Command string `form:"command" binding:"Required"`
Attachment string `form:"attachment" binding:"Required"`
JobType string `form:"job_type" binding:"Required"`
BenchmarkCategory string `form:"get_benchmark_category"`
GpuType string `form:"gpu_type"`
ResourceSpecId int `form:"resource_spec_id" binding:"Required"`
JobName string `form:"job_name" binding:"Required"`
Image string `form:"image" binding:"Required"`
Command string `form:"command" binding:"Required"`
Attachment string `form:"attachment" binding:"Required"`
JobType string `form:"job_type" binding:"Required"`
BenchmarkCategory string `form:"get_benchmark_category"`
GpuType string `form:"gpu_type"`
TrainUrl string `form:"train_url"`
TestUrl string `form:"test_url"`
Description string `form:"description"`
ResourceSpecId int `form:"resource_spec_id" binding:"Required"`
BenchmarkTypeID int `form:"benchmark_types_id"`
BenchmarkChildTypeID int `form:"benchmark_child_types_id"`
} }


type CommitImageCloudBrainForm struct { type CommitImageCloudBrainForm struct {


+ 15
- 0
modules/auth/repo_form.go View File

@@ -29,6 +29,7 @@ import (
type CreateRepoForm struct { type CreateRepoForm struct {
UID int64 `binding:"Required"` UID int64 `binding:"Required"`
RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"` RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"`
Alias string `binding:"Required;MaxSize(100);AlphaDashDotChinese"`
Private bool Private bool
Description string `binding:"MaxSize(1024)"` Description string `binding:"MaxSize(1024)"`
DefaultBranch string `binding:"GitRefName;MaxSize(100)"` DefaultBranch string `binding:"GitRefName;MaxSize(100)"`
@@ -62,6 +63,7 @@ type MigrateRepoForm struct {
UID int64 `json:"uid" binding:"Required"` UID int64 `json:"uid" binding:"Required"`
// required: true // required: true
RepoName string `json:"repo_name" binding:"Required;AlphaDashDot;MaxSize(100)"` RepoName string `json:"repo_name" binding:"Required;AlphaDashDot;MaxSize(100)"`
Alias string `json:"alias" binding:"Required;AlphaDashDotChinese;MaxSize(100)"`
Mirror bool `json:"mirror"` Mirror bool `json:"mirror"`
Private bool `json:"private"` Private bool `json:"private"`
Description string `json:"description" binding:"MaxSize(255)"` Description string `json:"description" binding:"MaxSize(255)"`
@@ -109,6 +111,7 @@ func (f MigrateRepoForm) ParseRemoteAddr(user *models.User) (string, error) {
// RepoSettingForm form for changing repository settings // RepoSettingForm form for changing repository settings
type RepoSettingForm struct { type RepoSettingForm struct {
RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"` RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"`
Alias string `binding:"Required;AlphaDashDotChinese;MaxSize(100)"`
Description string `binding:"MaxSize(255)"` Description string `binding:"MaxSize(255)"`
Website string `binding:"ValidUrl;MaxSize(255)"` Website string `binding:"ValidUrl;MaxSize(255)"`
Interval string Interval string
@@ -725,3 +728,15 @@ type DeadlineForm struct {
func (f *DeadlineForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { func (f *DeadlineForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale) return validate(errs, ctx.Data, f, ctx.Locale)
} }

type CreateCourseForm struct {
RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"`
Alias string `binding:"Required;MaxSize(100);AlphaDashDotChinese"`
Topics string
Description string `binding:"MaxSize(1024)"`
}

// Validate validates the fields
func (f *CreateCourseForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}

+ 5
- 0
modules/auth/user_form.go View File

@@ -105,6 +105,11 @@ func (f RegisterForm) IsEmailDomainWhitelisted() bool {
} }


domain := strings.ToLower(f.Email[n+1:]) domain := strings.ToLower(f.Email[n+1:])
//support edu.cn
if strings.HasSuffix(domain, "edu.cn") {
return true
}


for _, v := range setting.Service.EmailDomainWhitelist { for _, v := range setting.Service.EmailDomainWhitelist {
if strings.ToLower(v) == domain { if strings.ToLower(v) == domain {


+ 40
- 32
modules/cloudbrain/cloudbrain.go View File

@@ -14,11 +14,16 @@ import (
) )


const ( const (
Command = `pip3 install jupyterlab==2.2.5 -i https://pypi.tuna.tsinghua.edu.cn/simple;service ssh stop;jupyter lab --no-browser --ip=0.0.0.0 --allow-root --notebook-dir="/code" --port=80 --LabApp.token="" --LabApp.allow_origin="self https://cloudbrain.pcl.ac.cn"`
Command = `pip3 install jupyterlab==2.2.5 -i https://pypi.tuna.tsinghua.edu.cn/simple;
service ssh stop;
jupyter lab --no-browser --ip=0.0.0.0 --allow-root --notebook-dir="/code" --port=80 --LabApp.token="" --LabApp.allow_origin="self https://cloudbrain.pcl.ac.cn"`
//CommandBenchmark = `echo "start benchmark";python /code/test.py;echo "end benchmark"`
CommandBenchmark = `echo "start benchmark";cd /benchmark && bash run_bk.sh;echo "end benchmark"`
CodeMountPath = "/code" CodeMountPath = "/code"
DataSetMountPath = "/dataset" DataSetMountPath = "/dataset"
ModelMountPath = "/model" ModelMountPath = "/model"
BenchMarkMountPath = "/benchmark" BenchMarkMountPath = "/benchmark"
BenchMarkResourceID = 1
Snn4imagenetMountPath = "/snn4imagenet" Snn4imagenetMountPath = "/snn4imagenet"
BrainScoreMountPath = "/brainscore" BrainScoreMountPath = "/brainscore"
TaskInfoName = "/taskInfo" TaskInfoName = "/taskInfo"
@@ -102,7 +107,7 @@ func AdminOrJobCreaterRight(ctx *context.Context) {


} }


func GenerateTask(ctx *context.Context, jobName, image, command, uuid, codePath, modelPath, benchmarkPath, snn4imagenetPath, brainScorePath, jobType, gpuQueue string, resourceSpecId int) error {
func GenerateTask(ctx *context.Context, jobName, image, command, uuid, codePath, modelPath, benchmarkPath, snn4imagenetPath, brainScorePath, jobType, gpuQueue, description string, benchmarkTypeID, benchmarkChildTypeID, resourceSpecId int) error {
dataActualPath := setting.Attachment.Minio.RealPath + dataActualPath := setting.Attachment.Minio.RealPath +
setting.Attachment.Minio.Bucket + "/" + setting.Attachment.Minio.Bucket + "/" +
setting.Attachment.Minio.BasePath + setting.Attachment.Minio.BasePath +
@@ -201,19 +206,22 @@ func GenerateTask(ctx *context.Context, jobName, image, command, uuid, codePath,


var jobID = jobResult.Payload["jobId"].(string) var jobID = jobResult.Payload["jobId"].(string)
err = models.CreateCloudbrain(&models.Cloudbrain{ err = models.CreateCloudbrain(&models.Cloudbrain{
Status: string(models.JobWaiting),
UserID: ctx.User.ID,
RepoID: ctx.Repo.Repository.ID,
JobID: jobID,
JobName: jobName,
SubTaskName: SubTaskName,
JobType: jobType,
Type: models.TypeCloudBrainOne,
Uuid: uuid,
Image: image,
GpuQueue: gpuQueue,
ResourceSpecId: resourceSpecId,
ComputeResource: models.GPUResource,
Status: string(models.JobWaiting),
UserID: ctx.User.ID,
RepoID: ctx.Repo.Repository.ID,
JobID: jobID,
JobName: jobName,
SubTaskName: SubTaskName,
JobType: jobType,
Type: models.TypeCloudBrainOne,
Uuid: uuid,
Image: image,
GpuQueue: gpuQueue,
ResourceSpecId: resourceSpecId,
ComputeResource: models.GPUResource,
BenchmarkTypeID: benchmarkTypeID,
BenchmarkChildTypeID: benchmarkChildTypeID,
Description: description,
}) })


if err != nil { if err != nil {
@@ -270,7 +278,7 @@ func RestartTask(ctx *context.Context, task *models.Cloudbrain, newJobID *string
Volumes: []models.Volume{ Volumes: []models.Volume{
{ {
HostPath: models.StHostPath{ HostPath: models.StHostPath{
Path: storage.GetMinioPath(jobName, CodeMountPath + "/"),
Path: storage.GetMinioPath(jobName, CodeMountPath+"/"),
MountPath: CodeMountPath, MountPath: CodeMountPath,
ReadOnly: false, ReadOnly: false,
}, },
@@ -284,28 +292,28 @@ func RestartTask(ctx *context.Context, task *models.Cloudbrain, newJobID *string
}, },
{ {
HostPath: models.StHostPath{ HostPath: models.StHostPath{
Path: storage.GetMinioPath(jobName, ModelMountPath + "/"),
Path: storage.GetMinioPath(jobName, ModelMountPath+"/"),
MountPath: ModelMountPath, MountPath: ModelMountPath,
ReadOnly: false, ReadOnly: false,
}, },
}, },
{ {
HostPath: models.StHostPath{ HostPath: models.StHostPath{
Path: storage.GetMinioPath(jobName, BenchMarkMountPath + "/"),
Path: storage.GetMinioPath(jobName, BenchMarkMountPath+"/"),
MountPath: BenchMarkMountPath, MountPath: BenchMarkMountPath,
ReadOnly: true, ReadOnly: true,
}, },
}, },
{ {
HostPath: models.StHostPath{ HostPath: models.StHostPath{
Path: storage.GetMinioPath(jobName, Snn4imagenetMountPath + "/"),
Path: storage.GetMinioPath(jobName, Snn4imagenetMountPath+"/"),
MountPath: Snn4imagenetMountPath, MountPath: Snn4imagenetMountPath,
ReadOnly: true, ReadOnly: true,
}, },
}, },
{ {
HostPath: models.StHostPath{ HostPath: models.StHostPath{
Path: storage.GetMinioPath(jobName, BrainScoreMountPath + "/"),
Path: storage.GetMinioPath(jobName, BrainScoreMountPath+"/"),
MountPath: BrainScoreMountPath, MountPath: BrainScoreMountPath,
ReadOnly: true, ReadOnly: true,
}, },
@@ -323,18 +331,18 @@ func RestartTask(ctx *context.Context, task *models.Cloudbrain, newJobID *string


var jobID = jobResult.Payload["jobId"].(string) var jobID = jobResult.Payload["jobId"].(string)
newTask := &models.Cloudbrain{ newTask := &models.Cloudbrain{
Status: string(models.JobWaiting),
UserID: task.UserID,
RepoID: task.RepoID,
JobID: jobID,
JobName: task.JobName,
SubTaskName: task.SubTaskName,
JobType: task.JobType,
Type: task.Type,
Uuid: task.Uuid,
Image: task.Image,
GpuQueue: task.GpuQueue,
ResourceSpecId: task.ResourceSpecId,
Status: string(models.JobWaiting),
UserID: task.UserID,
RepoID: task.RepoID,
JobID: jobID,
JobName: task.JobName,
SubTaskName: task.SubTaskName,
JobType: task.JobType,
Type: task.Type,
Uuid: task.Uuid,
Image: task.Image,
GpuQueue: task.GpuQueue,
ResourceSpecId: task.ResourceSpecId,
ComputeResource: task.ComputeResource, ComputeResource: task.ComputeResource,
} }




+ 101
- 0
modules/cloudbrain/resty.go View File

@@ -2,7 +2,10 @@ package cloudbrain


import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"net/http"
"strconv"
"strings" "strings"


"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
@@ -23,6 +26,8 @@ const (
JobHasBeenStopped = "S410" JobHasBeenStopped = "S410"
Public = "public" Public = "public"
Custom = "custom" Custom = "custom"
LogPageSize = 500
LogPageTokenExpired = "5m"
) )


func getRestyClient() *resty.Client { func getRestyClient() *resty.Client {
@@ -270,3 +275,99 @@ sendjob:


return nil return nil
} }

func GetJobLog(jobID string) (*models.GetJobLogResult, error) {
checkSetting()
client := getRestyClient()
var result models.GetJobLogResult
req := models.GetJobLogParams{
Size: strconv.Itoa(LogPageSize),
Sort: "log.offset",
QueryInfo: models.QueryInfo{
MatchInfo: models.MatchInfo{
PodName: jobID + "-task1-0",
},
},
}

res, err := client.R().
SetHeader("Content-Type", "application/json").
SetAuthToken(TOKEN).
SetBody(req).
SetResult(&result).
Post(HOST + "es/_search?_source=message&scroll=" + LogPageTokenExpired)

if err != nil {
log.Error("GetJobLog failed: %v", err)
return &result, fmt.Errorf("resty GetJobLog: %v, %s", err, res.String())
}

if !strings.Contains(res.Status(), strconv.Itoa(http.StatusOK)) {
log.Error("res.Status(): %s, response: %s", res.Status(), res.String())
return &result, errors.New(res.String())
}

return &result, nil
}

func GetJobAllLog(scrollID string) (*models.GetJobLogResult, error) {
checkSetting()
client := getRestyClient()
var result models.GetJobLogResult
req := models.GetAllJobLogParams{
Scroll: LogPageTokenExpired,
ScrollID: scrollID,
}

res, err := client.R().
SetHeader("Content-Type", "application/json").
SetAuthToken(TOKEN).
SetBody(req).
SetResult(&result).
Post(HOST + "es/_search/scroll")

if err != nil {
log.Error("GetJobAllLog failed: %v", err)
return &result, fmt.Errorf("resty GetJobAllLog: %v, %s", err, res.String())
}

if !strings.Contains(res.Status(), strconv.Itoa(http.StatusOK)) {
log.Error("res.Status(): %s, response: %s", res.Status(), res.String())
return &result, errors.New(res.String())
}

return &result, nil
}

func DeleteJobLogToken(scrollID string) (error) {
checkSetting()
client := getRestyClient()
var result models.DeleteJobLogTokenResult
req := models.DeleteJobLogTokenParams{
ScrollID: scrollID,
}

res, err := client.R().
SetHeader("Content-Type", "application/json").
SetAuthToken(TOKEN).
SetBody(req).
SetResult(&result).
Delete(HOST + "es/_search/scroll")

if err != nil {
log.Error("DeleteJobLogToken failed: %v", err)
return fmt.Errorf("resty DeleteJobLogToken: %v, %s", err, res.String())
}

if !strings.Contains(res.Status(), strconv.Itoa(http.StatusOK)) {
log.Error("res.Status(): %s, response: %s", res.Status(), res.String())
return errors.New(res.String())
}

if !result.Succeeded {
log.Error("DeleteJobLogToken failed")
return errors.New("DeleteJobLogToken failed")
}

return nil
}

+ 1
- 1
modules/context/context.go View File

@@ -328,7 +328,7 @@ func Contexter() macaron.Handler {
} }
} }


ctx.Resp.Header().Set(`X-Frame-Options`, `SAMEORIGIN`)
//ctx.Resp.Header().Set(`X-Frame-Options`, `SAMEORIGIN`)


ctx.Data["CsrfToken"] = html.EscapeString(x.GetToken()) ctx.Data["CsrfToken"] = html.EscapeString(x.GetToken())
ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + ctx.Data["CsrfToken"].(string) + `">`) ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + ctx.Data["CsrfToken"].(string) + `">`)


+ 2
- 0
modules/context/org.go View File

@@ -63,6 +63,8 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
org := ctx.Org.Organization org := ctx.Org.Organization
ctx.Data["Org"] = org ctx.Data["Org"] = org


ctx.Data["IsCourse"] = ctx.Org.Organization.Name == setting.Course.OrgName

// Force redirection when username is actually a user. // Force redirection when username is actually a user.
if !org.IsOrganization() { if !org.IsOrganization() {
ctx.Redirect(setting.AppSubURL + "/" + org.Name) ctx.Redirect(setting.AppSubURL + "/" + org.Name)


+ 1
- 0
modules/context/repo.go View File

@@ -402,6 +402,7 @@ func RepoAssignment() macaron.Handler {
} }
ctx.Repo.Owner = owner ctx.Repo.Owner = owner
ctx.Data["Username"] = ctx.Repo.Owner.Name ctx.Data["Username"] = ctx.Repo.Owner.Name
ctx.Data["IsCourse"] = owner.Name == setting.Course.OrgName


// Get repository. // Get repository.
repo, err := models.GetRepositoryByName(owner.ID, repoName) repo, err := models.GetRepositoryByName(owner.ID, repoName)


+ 16
- 0
modules/notification/action/action.go View File

@@ -154,6 +154,22 @@ func (a *actionNotifier) NotifyRenameRepository(doer *models.User, repo *models.
} }
} }


func (a *actionNotifier) NotifyAliasRepository(doer *models.User, repo *models.Repository, oldAlias string) {
log.Trace("action.ChangeRepositoryAlias: %s/%s", doer.Name, repo.Alias)

if err := models.NotifyWatchers(&models.Action{
ActUserID: doer.ID,
ActUser: doer,
OpType: models.ActionRenameRepo,
RepoID: repo.ID,
Repo: repo,
IsPrivate: repo.IsPrivate,
Content: oldAlias,
}); err != nil {
log.Error("NotifyWatchers: %v", err)
}
}

func (a *actionNotifier) NotifyTransferRepository(doer *models.User, repo *models.Repository, oldOwnerName string) { func (a *actionNotifier) NotifyTransferRepository(doer *models.User, repo *models.Repository, oldOwnerName string) {
if err := models.NotifyWatchers(&models.Action{ if err := models.NotifyWatchers(&models.Action{
ActUserID: doer.ID, ActUserID: doer.ID,


+ 1
- 0
modules/notification/base/notifier.go View File

@@ -18,6 +18,7 @@ type Notifier interface {
NotifyDeleteRepository(doer *models.User, repo *models.Repository) NotifyDeleteRepository(doer *models.User, repo *models.Repository)
NotifyForkRepository(doer *models.User, oldRepo, repo *models.Repository) NotifyForkRepository(doer *models.User, oldRepo, repo *models.Repository)
NotifyRenameRepository(doer *models.User, repo *models.Repository, oldRepoName string) NotifyRenameRepository(doer *models.User, repo *models.Repository, oldRepoName string)
NotifyAliasRepository(doer *models.User, repo *models.Repository, oldAlias string)
NotifyTransferRepository(doer *models.User, repo *models.Repository, oldOwnerName string) NotifyTransferRepository(doer *models.User, repo *models.Repository, oldOwnerName string)


NotifyNewIssue(*models.Issue) NotifyNewIssue(*models.Issue)


+ 4
- 0
modules/notification/base/null.go View File

@@ -135,6 +135,10 @@ func (*NullNotifier) NotifyDeleteRef(doer *models.User, repo *models.Repository,
func (*NullNotifier) NotifyRenameRepository(doer *models.User, repo *models.Repository, oldRepoName string) { func (*NullNotifier) NotifyRenameRepository(doer *models.User, repo *models.Repository, oldRepoName string) {
} }


func (a *NullNotifier) NotifyAliasRepository(doer *models.User, repo *models.Repository, oldAlias string) {

}

// NotifyTransferRepository places a place holder function // NotifyTransferRepository places a place holder function
func (*NullNotifier) NotifyTransferRepository(doer *models.User, repo *models.Repository, oldOwnerName string) { func (*NullNotifier) NotifyTransferRepository(doer *models.User, repo *models.Repository, oldOwnerName string) {
} }


+ 11
- 1
modules/repository/create.go View File

@@ -22,12 +22,17 @@ func CreateRepository(doer, u *models.User, opts models.CreateRepoOptions) (_ *m
Limit: u.MaxRepoCreation, Limit: u.MaxRepoCreation,
} }
} }
var RepoType = models.RepoNormal
if opts.IsCourse {
RepoType = models.RepoCourse
}


repo := &models.Repository{ repo := &models.Repository{
OwnerID: u.ID, OwnerID: u.ID,
Owner: u, Owner: u,
OwnerName: u.Name, OwnerName: u.Name,
Name: opts.Name, Name: opts.Name,
Alias: opts.Alias,
LowerName: strings.ToLower(opts.Name), LowerName: strings.ToLower(opts.Name),
Description: opts.Description, Description: opts.Description,
OriginalURL: opts.OriginalURL, OriginalURL: opts.OriginalURL,
@@ -37,10 +42,15 @@ func CreateRepository(doer, u *models.User, opts models.CreateRepoOptions) (_ *m
CloseIssuesViaCommitInAnyBranch: setting.Repository.DefaultCloseIssuesViaCommitsInAnyBranch, CloseIssuesViaCommitInAnyBranch: setting.Repository.DefaultCloseIssuesViaCommitsInAnyBranch,
Status: opts.Status, Status: opts.Status,
IsEmpty: !opts.AutoInit, IsEmpty: !opts.AutoInit,
RepoType: RepoType,
Topics: opts.Topics,
} }


err = models.WithTx(func(ctx models.DBContext) error { err = models.WithTx(func(ctx models.DBContext) error {
if err = models.CreateRepository(ctx, doer, u, repo); err != nil {
if err = models.CreateRepository(ctx, doer, u, repo, opts); err != nil {
return err
}
if err = models.SaveTopics(repo.ID, opts.Topics...); err != nil {
return err return err
} }




+ 2
- 1
modules/repository/fork.go View File

@@ -15,7 +15,7 @@ import (
) )


// ForkRepository forks a repository // ForkRepository forks a repository
func ForkRepository(doer, owner *models.User, oldRepo *models.Repository, name, desc string) (_ *models.Repository, err error) {
func ForkRepository(doer, owner *models.User, oldRepo *models.Repository, name, desc, alias string) (_ *models.Repository, err error) {
forkedRepo, err := oldRepo.GetUserFork(owner.ID) forkedRepo, err := oldRepo.GetUserFork(owner.ID)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -33,6 +33,7 @@ func ForkRepository(doer, owner *models.User, oldRepo *models.Repository, name,
Owner: owner, Owner: owner,
OwnerName: owner.Name, OwnerName: owner.Name,
Name: name, Name: name,
Alias: alias,
LowerName: strings.ToLower(name), LowerName: strings.ToLower(name),
Description: desc, Description: desc,
DefaultBranch: oldRepo.DefaultBranch, DefaultBranch: oldRepo.DefaultBranch,


+ 1
- 1
modules/repository/fork_test.go View File

@@ -18,7 +18,7 @@ func TestForkRepository(t *testing.T) {
user := models.AssertExistsAndLoadBean(t, &models.User{ID: 13}).(*models.User) user := models.AssertExistsAndLoadBean(t, &models.User{ID: 13}).(*models.User)
repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 10}).(*models.Repository) repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 10}).(*models.Repository)


fork, err := ForkRepository(user, user, repo, "test", "test")
fork, err := ForkRepository(user, user, repo, "test", "test", "test")
assert.Nil(t, fork) assert.Nil(t, fork)
assert.Error(t, err) assert.Error(t, err)
assert.True(t, models.IsErrForkAlreadyExist(err)) assert.True(t, models.IsErrForkAlreadyExist(err))


+ 1
- 0
modules/repository/generate.go View File

@@ -236,6 +236,7 @@ func GenerateRepository(ctx models.DBContext, doer, owner *models.User, template
Owner: owner, Owner: owner,
OwnerName: owner.Name, OwnerName: owner.Name,
Name: opts.Name, Name: opts.Name,
Alias: opts.Alias,
LowerName: strings.ToLower(opts.Name), LowerName: strings.ToLower(opts.Name),
Description: opts.Description, Description: opts.Description,
IsPrivate: opts.Private, IsPrivate: opts.Private,


+ 1
- 1
modules/repository/init.go View File

@@ -51,7 +51,7 @@ func prepareRepoCommit(ctx models.DBContext, repo *models.Repository, tmpDir, re


cloneLink := repo.CloneLink() cloneLink := repo.CloneLink()
match := map[string]string{ match := map[string]string{
"Name": repo.Name,
"Name": repo.DisplayName(),
"Description": repo.Description, "Description": repo.Description,
"CloneURL.SSH": cloneLink.SSH, "CloneURL.SSH": cloneLink.SSH,
"CloneURL.HTTPS": cloneLink.HTTPS, "CloneURL.HTTPS": cloneLink.HTTPS,


+ 23
- 5
modules/setting/setting.go View File

@@ -462,11 +462,15 @@ var (
MaxDuration int64 MaxDuration int64


//benchmark config //benchmark config
IsBenchmarkEnabled bool
BenchmarkOwner string
BenchmarkName string
BenchmarkServerHost string
BenchmarkCategory string
IsBenchmarkEnabled bool
BenchmarkOwner string
BenchmarkName string
BenchmarkServerHost string
BenchmarkCategory string
BenchmarkTypes string
BenchmarkGpuTypes string
BenchmarkResourceSpecs string
BenchmarkMaxDuration int64


//snn4imagenet config //snn4imagenet config
IsSnn4imagenetEnabled bool IsSnn4imagenetEnabled bool
@@ -568,6 +572,11 @@ var (
}{} }{}


Warn_Notify_Mails []string Warn_Notify_Mails []string

Course = struct {
OrgName string
TeamName string
}{}
) )


// DateLang transforms standard language locale name to corresponding value in datetime plugin. // DateLang transforms standard language locale name to corresponding value in datetime plugin.
@@ -1270,6 +1279,10 @@ func NewContext() {
BenchmarkName = sec.Key("NAME").MustString("") BenchmarkName = sec.Key("NAME").MustString("")
BenchmarkServerHost = sec.Key("HOST").MustString("") BenchmarkServerHost = sec.Key("HOST").MustString("")
BenchmarkCategory = sec.Key("CATEGORY").MustString("") BenchmarkCategory = sec.Key("CATEGORY").MustString("")
BenchmarkTypes = sec.Key("TYPES").MustString("")
BenchmarkGpuTypes = sec.Key("GPU_TYPES").MustString("")
BenchmarkResourceSpecs = sec.Key("RESOURCE_SPECS").MustString("")
BenchmarkMaxDuration = sec.Key("MAX_DURATION").MustInt64(14400)


sec = Cfg.Section("snn4imagenet") sec = Cfg.Section("snn4imagenet")
IsSnn4imagenetEnabled = sec.Key("ENABLED").MustBool(false) IsSnn4imagenetEnabled = sec.Key("ENABLED").MustBool(false)
@@ -1331,6 +1344,11 @@ func NewContext() {


sec = Cfg.Section("warn_mail") sec = Cfg.Section("warn_mail")
Warn_Notify_Mails = strings.Split(sec.Key("mails").MustString(""), ",") Warn_Notify_Mails = strings.Split(sec.Key("mails").MustString(""), ",")

sec = Cfg.Section("course")
Course.OrgName = sec.Key("org_name").MustString("")
Course.TeamName = sec.Key("team_name").MustString("")

} }


func SetRadarMapConfig() { func SetRadarMapConfig() {


+ 28
- 25
modules/structs/repo.go View File

@@ -46,31 +46,33 @@ type ExternalWiki struct {


// Repository represents a repository // Repository represents a repository
type Repository struct { type Repository struct {
ID int64 `json:"id"`
Owner *User `json:"owner"`
Name string `json:"name"`
FullName string `json:"full_name"`
Description string `json:"description"`
Empty bool `json:"empty"`
Private bool `json:"private"`
Fork bool `json:"fork"`
Template bool `json:"template"`
Parent *Repository `json:"parent"`
Mirror bool `json:"mirror"`
Size int `json:"size"`
HTMLURL string `json:"html_url"`
SSHURL string `json:"ssh_url"`
CloneURL string `json:"clone_url"`
OriginalURL string `json:"original_url"`
Website string `json:"website"`
Stars int `json:"stars_count"`
Forks int `json:"forks_count"`
Watchers int `json:"watchers_count"`
OpenIssues int `json:"open_issues_count"`
OpenPulls int `json:"open_pr_counter"`
Releases int `json:"release_counter"`
DefaultBranch string `json:"default_branch"`
Archived bool `json:"archived"`
ID int64 `json:"id"`
Owner *User `json:"owner"`
Name string `json:"name"`
Alias string `json:"alias"`
FullName string `json:"full_name"`
FullDisplayName string `json:"full_display_name"`
Description string `json:"description"`
Empty bool `json:"empty"`
Private bool `json:"private"`
Fork bool `json:"fork"`
Template bool `json:"template"`
Parent *Repository `json:"parent"`
Mirror bool `json:"mirror"`
Size int `json:"size"`
HTMLURL string `json:"html_url"`
SSHURL string `json:"ssh_url"`
CloneURL string `json:"clone_url"`
OriginalURL string `json:"original_url"`
Website string `json:"website"`
Stars int `json:"stars_count"`
Forks int `json:"forks_count"`
Watchers int `json:"watchers_count"`
OpenIssues int `json:"open_issues_count"`
OpenPulls int `json:"open_pr_counter"`
Releases int `json:"release_counter"`
DefaultBranch string `json:"default_branch"`
Archived bool `json:"archived"`
// swagger:strfmt date-time // swagger:strfmt date-time
Created time.Time `json:"created_at"` Created time.Time `json:"created_at"`
// swagger:strfmt date-time // swagger:strfmt date-time
@@ -217,6 +219,7 @@ type MigrateRepoOption struct {
UID int `json:"uid" binding:"Required"` UID int `json:"uid" binding:"Required"`
// required: true // required: true
RepoName string `json:"repo_name" binding:"Required"` RepoName string `json:"repo_name" binding:"Required"`
Alias string `json:"alias" binding:"Required"`
Mirror bool `json:"mirror"` Mirror bool `json:"mirror"`
Private bool `json:"private"` Private bool `json:"private"`
Description string `json:"description"` Description string `json:"description"`


+ 1
- 0
modules/task/task.go View File

@@ -84,6 +84,7 @@ func CreateMigrateTask(doer, u *models.User, opts base.MigrateOptions) (*models.


repo, err := repo_module.CreateRepository(doer, u, models.CreateRepoOptions{ repo, err := repo_module.CreateRepository(doer, u, models.CreateRepoOptions{
Name: opts.RepoName, Name: opts.RepoName,
Alias: opts.Alias,
Description: opts.Description, Description: opts.Description,
OriginalURL: opts.OriginalURL, OriginalURL: opts.OriginalURL,
GitServiceType: opts.GitServiceType, GitServiceType: opts.GitServiceType,


+ 2
- 0
modules/templates/helper.go View File

@@ -93,6 +93,7 @@ func NewFuncMap() []template.FuncMap {
"TimeSince": timeutil.TimeSince, "TimeSince": timeutil.TimeSince,
"TimeSinceUnix": timeutil.TimeSinceUnix, "TimeSinceUnix": timeutil.TimeSinceUnix,
"TimeSinceUnix1": timeutil.TimeSinceUnix1, "TimeSinceUnix1": timeutil.TimeSinceUnix1,
"TimeSinceUnixShort": timeutil.TimeSinceUnixShort,
"RawTimeSince": timeutil.RawTimeSince, "RawTimeSince": timeutil.RawTimeSince,
"FileSize": base.FileSize, "FileSize": base.FileSize,
"PrettyNumber": base.PrettyNumber, "PrettyNumber": base.PrettyNumber,
@@ -342,6 +343,7 @@ func NewTextFuncMap() []texttmpl.FuncMap {
"TimeSince": timeutil.TimeSince, "TimeSince": timeutil.TimeSince,
"TimeSinceUnix": timeutil.TimeSinceUnix, "TimeSinceUnix": timeutil.TimeSinceUnix,
"TimeSinceUnix1": timeutil.TimeSinceUnix1, "TimeSinceUnix1": timeutil.TimeSinceUnix1,
"TimeSinceUnixShort": timeutil.TimeSinceUnixShort,
"RawTimeSince": timeutil.RawTimeSince, "RawTimeSince": timeutil.RawTimeSince,
"DateFmtLong": func(t time.Time) string { "DateFmtLong": func(t time.Time) string {
return t.Format(time.RFC1123Z) return t.Format(time.RFC1123Z)


+ 4
- 1
modules/timeutil/since.go View File

@@ -165,5 +165,8 @@ func htmlTimeSinceUnix(then, now TimeStamp, lang string) template.HTML {
func TimeSinceUnix1(then TimeStamp) string { func TimeSinceUnix1(then TimeStamp) string {
format := time.Unix(int64(then), 0).Format("2006-01-02 15:04:05") format := time.Unix(int64(then), 0).Format("2006-01-02 15:04:05")
return format return format
}
func TimeSinceUnixShort(then TimeStamp) string {
format := time.Unix(int64(then), 0).Format("2006-01-02")
return format
} }

+ 24
- 0
modules/validation/binding.go View File

@@ -19,6 +19,8 @@ const (


// ErrGlobPattern is returned when glob pattern is invalid // ErrGlobPattern is returned when glob pattern is invalid
ErrGlobPattern = "GlobPattern" ErrGlobPattern = "GlobPattern"

ErrAlphaDashDotChinese = "AlphaDashDotChineseError"
) )


var ( var (
@@ -26,6 +28,8 @@ var (
// They cannot have ASCII control characters (i.e. bytes whose values are lower than \040, or \177 DEL), space, tilde ~, caret ^, or colon : anywhere. // They cannot have ASCII control characters (i.e. bytes whose values are lower than \040, or \177 DEL), space, tilde ~, caret ^, or colon : anywhere.
// They cannot have question-mark ?, asterisk *, or open bracket [ anywhere // They cannot have question-mark ?, asterisk *, or open bracket [ anywhere
GitRefNamePatternInvalid = regexp.MustCompile(`[\000-\037\177 \\~^:?*[]+`) GitRefNamePatternInvalid = regexp.MustCompile(`[\000-\037\177 \\~^:?*[]+`)

AlphaDashDotChinese = regexp.MustCompile("^[\u4e00-\u9fa5\\.\\-_A-Za-z0-9]+$")
) )


// CheckGitRefAdditionalRulesValid check name is valid on additional rules // CheckGitRefAdditionalRulesValid check name is valid on additional rules
@@ -53,6 +57,7 @@ func AddBindingRules() {
addGitRefNameBindingRule() addGitRefNameBindingRule()
addValidURLBindingRule() addValidURLBindingRule()
addGlobPatternRule() addGlobPatternRule()
addAlphaDashDotChineseRule()
} }


func addGitRefNameBindingRule() { func addGitRefNameBindingRule() {
@@ -117,6 +122,21 @@ func addGlobPatternRule() {
}) })
} }


func addAlphaDashDotChineseRule() {
binding.AddRule(&binding.Rule{
IsMatch: func(rule string) bool {
return strings.HasPrefix(rule, "AlphaDashDotChinese")
},
IsValid: func(errs binding.Errors, name string, val interface{}) (bool, binding.Errors) {
if !ValidAlphaDashDotChinese(fmt.Sprintf("%v", val)) {
errs.Add([]string{name}, ErrAlphaDashDotChinese, "ErrAlphaDashDotChinese")
return false, errs
}
return true, errs
},
})
}

func portOnly(hostport string) string { func portOnly(hostport string) string {
colon := strings.IndexByte(hostport, ':') colon := strings.IndexByte(hostport, ':')
if colon == -1 { if colon == -1 {
@@ -139,3 +159,7 @@ func validPort(p string) bool {
} }
return true return true
} }

func ValidAlphaDashDotChinese(value string) bool {
return AlphaDashDotChinese.MatchString(value)
}

+ 57
- 8
options/locale/locale_en-US.ini View File

@@ -50,6 +50,8 @@ repository = Repository
organization = Organization organization = Organization
mirror = Mirror mirror = Mirror
new_repo = New Repository new_repo = New Repository
new_course=Publish Course
course_desc = Course Description
new_migrate = New Migration new_migrate = New Migration
new_dataset = New Dataset new_dataset = New Dataset
edit_dataset = Edit Dataset edit_dataset = Edit Dataset
@@ -219,13 +221,14 @@ show_only_public = Showing only public


issues.in_your_repos = In your repositories issues.in_your_repos = In your repositories
contributors = Contributors contributors = Contributors
contributor = Contributor


page_title=Explore Better AI page_title=Explore Better AI
page_small_title=OpenI AI Development Cooperation Platform page_small_title=OpenI AI Development Cooperation Platform
page_description=The one-stop collaborative development environment for AI field provides AI development pipeline integrating code development, data management, model debugging, reasoning and evaluation page_description=The one-stop collaborative development environment for AI field provides AI development pipeline integrating code development, data management, model debugging, reasoning and evaluation
page_use=Use Now page_use=Use Now
page_only_dynamic=Only show the dynamics of open source projects page_only_dynamic=Only show the dynamics of open source projects
page_recommend_org=Recommended Organization
page_recommend_org=Recommended Organizations
page_recommend_org_desc=These excellent organizations are using the OpenI AI Collaboration Platform for collaborative development of projects. To show your organization here, page_recommend_org_desc=These excellent organizations are using the OpenI AI Collaboration Platform for collaborative development of projects. To show your organization here,
page_recommend_org_commit=Click here to submit. page_recommend_org_commit=Click here to submit.
page_recommend_org_more=More Organizations page_recommend_org_more=More Organizations
@@ -258,7 +261,9 @@ users = Users
organizations = Organizations organizations = Organizations
images = Cloudbrain Mirror images = Cloudbrain Mirror
search = Search search = Search
search_pro=Search projects
code = Code code = Code
data_analysis=Digital Bulletin Board (test)
repo_no_results = No matching repositories found. repo_no_results = No matching repositories found.
dataset_no_results = No matching datasets found. dataset_no_results = No matching datasets found.
user_no_results = No matching users found. user_no_results = No matching users found.
@@ -266,8 +271,8 @@ org_no_results = No matching organizations found.
code_no_results = No source code matching your search term found. code_no_results = No source code matching your search term found.
code_search_results = Search results for '%s' code_search_results = Search results for '%s'
code_last_indexed_at = Last indexed %s code_last_indexed_at = Last indexed %s
save=save
cancel=cancel
save=Save
cancel=Cancel


[auth] [auth]
create_new_account = Register Account create_new_account = Register Account
@@ -321,7 +326,7 @@ openid_register_title = Create new account
openid_register_desc = The chosen OpenID URI is unknown. Associate it with a new account here. openid_register_desc = The chosen OpenID URI is unknown. Associate it with a new account here.
openid_signin_desc = Enter your OpenID URI. For example: https://anne.me, bob.openid.org.cn or gnusocial.net/carry. openid_signin_desc = Enter your OpenID URI. For example: https://anne.me, bob.openid.org.cn or gnusocial.net/carry.
disable_forgot_password_mail = Account recovery is disabled. Please contact your site administrator. disable_forgot_password_mail = Account recovery is disabled. Please contact your site administrator.
email_domain_blacklisted = You cannot register with your email address.
email_domain_blacklisted = You cannot register with this kind of email address.
authorize_application = Authorize Application authorize_application = Authorize Application
authorize_redirect_notice = You will be redirected to %s if you authorize this application. authorize_redirect_notice = You will be redirected to %s if you authorize this application.
authorize_application_created_by = This application was created by %s. authorize_application_created_by = This application was created by %s.
@@ -346,7 +351,12 @@ modify = Update


[form] [form]
UserName = Username UserName = Username
RepoName = Repository name
Alias = Repository name
courseAlias = Course Name
courseAdress = Course Path
RepoPath = Repository path
RepoAdress = Repository Adress
course_Adress = Course Address
Email = Email address Email = Email address
Password = Password Password = Password
Retype = Re-Type Password Retype = Re-Type Password
@@ -370,7 +380,10 @@ SSPIDefaultLanguage = Default Language
require_error = ` cannot be empty.` require_error = ` cannot be empty.`
alpha_dash_error = ` should contain only alphanumeric, dash ('-') and underscore ('_') characters.` alpha_dash_error = ` should contain only alphanumeric, dash ('-') and underscore ('_') characters.`
alpha_dash_dot_error = ` should contain only alphanumeric, dash ('-'), underscore ('_') and dot ('.') characters.` alpha_dash_dot_error = ` should contain only alphanumeric, dash ('-'), underscore ('_') and dot ('.') characters.`
reponame_dash_dot_error=` Please enter Chinese, alphanumeric, dash ('-') ,underscore ('_') and dot ('.')characters, up to 100 characters. `
repoadd_dash_dot_error=` Path only allows input alphanumeric, dash ('-') ,underscore ('_') and dot ('.')characters, up to 100 characters. `
git_ref_name_error = ` must be a well-formed Git reference name.` git_ref_name_error = ` must be a well-formed Git reference name.`
alpha_dash_dot_chinese_error= ` should contain only alphanumeric, chinese, dash ('-') and underscore ('_') characters.`
size_error = ` must be size %s.` size_error = ` must be size %s.`
min_size_error = ` must contain at least %s characters.` min_size_error = ` must contain at least %s characters.`
max_size_error = ` must contain at most %s characters.` max_size_error = ` must contain at most %s characters.`
@@ -384,7 +397,8 @@ password_not_match = The passwords do not match.
lang_select_error = Select a language from the list. lang_select_error = Select a language from the list.


username_been_taken = The username is already taken. username_been_taken = The username is already taken.
repo_name_been_taken = The repository name is already used.
repo_name_been_taken = The repository name or path is already used.
course_name_been_taken=The course name or path is already used.
visit_rate_limit = Remote visit addressed rate limitation. visit_rate_limit = Remote visit addressed rate limitation.
2fa_auth_required = Remote visit required two factors authentication. 2fa_auth_required = Remote visit required two factors authentication.
org_name_been_taken = The organization name is already taken. org_name_been_taken = The organization name is already taken.
@@ -789,6 +803,7 @@ generate_from = Generate From
repo_desc = Description repo_desc = Description
repo_lang = Language repo_lang = Language
repo_gitignore_helper = Select .gitignore templates. repo_gitignore_helper = Select .gitignore templates.
repo_label_helpe = Press Enter to complete
issue_labels = Issue Labels issue_labels = Issue Labels
issue_labels_helper = Select an issue label set. issue_labels_helper = Select an issue label set.
license = License license = License
@@ -797,6 +812,8 @@ readme = README
readme_helper = Select a README file template. readme_helper = Select a README file template.
auto_init = Initialize Repository (Adds .gitignore, License and README) auto_init = Initialize Repository (Adds .gitignore, License and README)
create_repo = Create Repository create_repo = Create Repository
create_course = Publish Course
failed_to_create_course=Fail to publish course, please try again later.
default_branch = Default Branch default_branch = Default Branch
mirror_prune = Prune mirror_prune = Prune
mirror_prune_desc = Remove obsolete remote-tracking references mirror_prune_desc = Remove obsolete remote-tracking references
@@ -860,6 +877,11 @@ get_repo_info_error=Can not get the information of the repository.
generate_statistic_file_error=Fail to generate file. generate_statistic_file_error=Fail to generate file.
repo_stat_inspect=ProjectAnalysis repo_stat_inspect=ProjectAnalysis
all=All all=All

computing.all = All
computing.Introduction=Introduction
computing.success=Join Success

modelarts.status=Status modelarts.status=Status
modelarts.createtime=CreateTime modelarts.createtime=CreateTime
modelarts.version_nums = Version Nums modelarts.version_nums = Version Nums
@@ -897,7 +919,7 @@ modelarts.train_job.description=Description
modelarts.train_job.parameter_setting=Parameter setting modelarts.train_job.parameter_setting=Parameter setting
modelarts.train_job.parameter_setting_info=Parameter Info modelarts.train_job.parameter_setting_info=Parameter Info
modelarts.train_job.fast_parameter_setting=fast_parameter_setting modelarts.train_job.fast_parameter_setting=fast_parameter_setting
modelarts.train_job.fast_parameter_setting_config=fast_parameter_setting_config
modelarts.train_job.fast_parameter_setting_config=fast_parameter_setting_config
modelarts.train_job.fast_parameter_setting_config_link=fast_parameter_setting_config_link modelarts.train_job.fast_parameter_setting_config_link=fast_parameter_setting_config_link
modelarts.train_job.frames=frames modelarts.train_job.frames=frames
modelarts.train_job.algorithm_origin=Algorithm Origin modelarts.train_job.algorithm_origin=Algorithm Origin
@@ -931,6 +953,14 @@ modelarts.train_job_para_admin=train_job_para_admin
modelarts.train_job_para.edit=train_job_para.edit modelarts.train_job_para.edit=train_job_para.edit
modelarts.train_job_para.connfirm=train_job_para.connfirm modelarts.train_job_para.connfirm=train_job_para.connfirm



modelarts.evaluate_job=Model Evaluation
modelarts.evaluate_job.new_job=New Model Evaluation
cloudbrain.benchmark.evaluate_type=Evaluation Type
cloudbrain.benchmark.evaluate_child_type=Child Type
cloudbrain.benchmark.evaluate_mirror=Mirror
cloudbrain.benchmark.evaluate_train=Train Script
cloudbrain.benchmark.evaluate_test=Test Script
modelarts.infer_job_model = Model modelarts.infer_job_model = Model
modelarts.infer_job_model_file = Model File modelarts.infer_job_model_file = Model File
modelarts.infer_job = Inference Job modelarts.infer_job = Inference Job
@@ -964,14 +994,21 @@ template.avatar = Avatar
template.issue_labels = Issue Labels template.issue_labels = Issue Labels
template.one_item = Must select at least one template item template.one_item = Must select at least one template item
template.invalid = Must select a template repository template.invalid = Must select a template repository
template.repo_adress=Adress
template.repo_path=path
template.repo_name=Name


archive.title = This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests. archive.title = This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.
archive.issue.nocomment = This repo is archived. You cannot comment on issues. archive.issue.nocomment = This repo is archived. You cannot comment on issues.
archive.pull.nocomment = This repo is archived. You cannot comment on pull requests. archive.pull.nocomment = This repo is archived. You cannot comment on pull requests.


form.reach_limit_of_creation = You have already reached your limit of %d repositories. form.reach_limit_of_creation = You have already reached your limit of %d repositories.
form.reach_limit_of_course_creation=You have already reached your limit of %d courses or repositories.
form.name_reserved = The repository name '%s' is reserved. form.name_reserved = The repository name '%s' is reserved.
form.course_name_reserved=The course name '%s' is reserved.
form.name_pattern_not_allowed = The pattern '%s' is not allowed in a repository name. form.name_pattern_not_allowed = The pattern '%s' is not allowed in a repository name.
form.course_name_pattern_not_allowed=The pattern '%s' is not allowed in a course name.
add_course_org_fail=Fail to add organization, please try again later.


need_auth = Clone Authorization need_auth = Clone Authorization
migrate_type = Migration Type migrate_type = Migration Type
@@ -1184,7 +1221,7 @@ issues.filter_label_exclude = `Use <code>alt</code> + <code>click/enter</code> t
issues.filter_label_no_select = All labels issues.filter_label_no_select = All labels
issues.filter_milestone = Milestone issues.filter_milestone = Milestone
issues.filter_milestone_no_select = All milestones issues.filter_milestone_no_select = All milestones
issues.filter_milestone_no_add = Not add milestones
issues.filter_milestone_no_add = Not add milestones
issues.filter_assignee = Assignee issues.filter_assignee = Assignee
issues.filter_assginee_no_select = All assignees issues.filter_assginee_no_select = All assignees
issues.filter_type = Type issues.filter_type = Type
@@ -2016,6 +2053,7 @@ org_full_name_holder = Organization Full Name
org_name_helper = Organization names should be short and memorable. org_name_helper = Organization names should be short and memorable.
create_org = Create Organization create_org = Create Organization
repo_updated = Updated repo_updated = Updated
repo_released = Post
home = Home home = Home
people = People people = People
teams = Teams teams = Teams
@@ -2032,6 +2070,14 @@ team_access_desc = Repository access
team_permission_desc = Permission team_permission_desc = Permission
team_unit_desc = Allow Access to Repository Sections team_unit_desc = Allow Access to Repository Sections
team_unit_disabled = (Disabled) team_unit_disabled = (Disabled)
selected_couse=Selected Courses
release_course = Publish Course
all_keywords=All keywords
max_selectedPro= Select up to 9 public projects
custom_select_courses = Customize selected courses
recommend_remain_pro = Remain
save_fail_tips = The upper limit is exceeded
select_again = Select more than 9, please select again!


form.name_reserved = The organization name '%s' is reserved. form.name_reserved = The organization name '%s' is reserved.
form.name_pattern_not_allowed = The pattern '%s' is not allowed in an organization name. form.name_pattern_not_allowed = The pattern '%s' is not allowed in an organization name.
@@ -2075,6 +2121,8 @@ members.remove = Remove
members.leave = Leave members.leave = Leave
members.invite_desc = Add a new member to %s: members.invite_desc = Add a new member to %s:
members.invite_now = Invite Now members.invite_now = Invite Now
course_members.remove = Remove
course_members.leave = Leave


teams.join = Join teams.join = Join
teams.leave = Leave teams.leave = Leave
@@ -2117,6 +2165,7 @@ teams.all_repositories_helper = Team has access to all repositories. Selecting t
teams.all_repositories_read_permission_desc = This team grants <strong>Read</strong> access to <strong>all repositories</strong>: members can view and clone repositories. teams.all_repositories_read_permission_desc = This team grants <strong>Read</strong> access to <strong>all repositories</strong>: members can view and clone repositories.
teams.all_repositories_write_permission_desc = This team grants <strong>Write</strong> access to <strong>all repositories</strong>: members can read from and push to repositories. teams.all_repositories_write_permission_desc = This team grants <strong>Write</strong> access to <strong>all repositories</strong>: members can read from and push to repositories.
teams.all_repositories_admin_permission_desc = This team grants <strong>Admin</strong> access to <strong>all repositories</strong>: members can read from, push to and add collaborators to repositories. teams.all_repositories_admin_permission_desc = This team grants <strong>Admin</strong> access to <strong>all repositories</strong>: members can read from, push to and add collaborators to repositories.
teams.join_teams=Join in


[admin] [admin]
dashboard = Dashboard dashboard = Dashboard


+ 54
- 3
options/locale/locale_zh-CN.ini View File

@@ -50,6 +50,8 @@ repository=项目
organization=组织 organization=组织
mirror=镜像 mirror=镜像
new_repo=创建项目 new_repo=创建项目
new_course=发布课程
course_desc=课程描述
new_dataset=创建数据集 new_dataset=创建数据集
new_migrate=迁移外部项目 new_migrate=迁移外部项目
edit_dataset = Edit Dataset edit_dataset = Edit Dataset
@@ -221,6 +223,7 @@ show_only_public=只显示公开的
issues.in_your_repos=属于该用户项目的 issues.in_your_repos=属于该用户项目的


contributors=贡献者 contributors=贡献者
contributor=贡献者


page_title=探索更好的AI page_title=探索更好的AI
page_small_title=启智AI开发协作平台 page_small_title=启智AI开发协作平台
@@ -325,7 +328,7 @@ openid_register_title=创建新帐户
openid_register_desc=所选的 OpenID URI 未知。在这里关联一个新帐户。 openid_register_desc=所选的 OpenID URI 未知。在这里关联一个新帐户。
openid_signin_desc=输入您的 OpenID URI。例如: https://anne.me、bob.openid.org.cn 或 gnusocial.net/carry。 openid_signin_desc=输入您的 OpenID URI。例如: https://anne.me、bob.openid.org.cn 或 gnusocial.net/carry。
disable_forgot_password_mail=帐户恢复功能已被禁用。请与网站管理员联系。 disable_forgot_password_mail=帐户恢复功能已被禁用。请与网站管理员联系。
email_domain_blacklisted=您不能使用您的电子邮件地址注册。
email_domain_blacklisted=暂不支持此类电子邮件地址注册。
authorize_application=应用授权 authorize_application=应用授权
authorize_redirect_notice=如果您授权此应用,您将会被重定向到 %s。 authorize_redirect_notice=如果您授权此应用,您将会被重定向到 %s。
authorize_application_created_by=此应用由%s创建。 authorize_application_created_by=此应用由%s创建。
@@ -350,7 +353,13 @@ modify=更新


[form] [form]
UserName=用户名 UserName=用户名
RepoName=项目名称
RepoName=项目路径
Alias=项目名称
courseAlias=课程名称
courseAdress=课程路径
RepoPath=项目路径
RepoAdress=项目地址
course_Adress = 课程地址
Email=邮箱地址 Email=邮箱地址
Password=密码 Password=密码
Retype=重新输入密码 Retype=重新输入密码
@@ -374,7 +383,10 @@ SSPIDefaultLanguage=默认语言
require_error=不能为空。 require_error=不能为空。
alpha_dash_error=应该只包含字母数字、破折号 ('-') 和下划线 ('_') 字符。 alpha_dash_error=应该只包含字母数字、破折号 ('-') 和下划线 ('_') 字符。
alpha_dash_dot_error=应该只包含字母数字, 破折号 ('-'), 下划线 ('_') 和点 ('. ') 。 alpha_dash_dot_error=应该只包含字母数字, 破折号 ('-'), 下划线 ('_') 和点 ('. ') 。
reponame_dash_dot_error=请输入中文、字母、数字和-_ .,最多100个字符。
repoadd_dash_dot_error=路径只允许字母、数字和-_ .,最多100个字符。
git_ref_name_error=` 必须是格式良好的 git 引用名称。` git_ref_name_error=` 必须是格式良好的 git 引用名称。`
alpha_dash_dot_chinese_error=应该只包含字母数字中文, 破折号 ('-'), 下划线 ('_') 和点 ('. ') 。
size_error=长度必须为 %s。 size_error=长度必须为 %s。
min_size_error=长度最小为 %s 个字符。 min_size_error=长度最小为 %s 个字符。
max_size_error=长度最大为 %s 个字符。 max_size_error=长度最大为 %s 个字符。
@@ -388,7 +400,8 @@ password_not_match=密码不匹配。
lang_select_error=从列表中选出语言 lang_select_error=从列表中选出语言


username_been_taken=用户名已被使用。 username_been_taken=用户名已被使用。
repo_name_been_taken=项目名称已被使用。
repo_name_been_taken=项目名称或项目路径已被使用。
course_name_been_taken=课程名称或地址已被使用。
visit_rate_limit=远程访问达到速度限制。 visit_rate_limit=远程访问达到速度限制。
2fa_auth_required=远程访问需要双重验证。 2fa_auth_required=远程访问需要双重验证。
org_name_been_taken=组织名称已被使用。 org_name_been_taken=组织名称已被使用。
@@ -794,6 +807,7 @@ generate_from=生成自
repo_desc=项目描述 repo_desc=项目描述
repo_lang=项目语言 repo_lang=项目语言
repo_gitignore_helper=选择 .gitignore 模板。 repo_gitignore_helper=选择 .gitignore 模板。
repo_label_helpe=输入完成后回车键完成标签确定。
issue_labels=任务标签 issue_labels=任务标签
issue_labels_helper=选择一个任务标签集 issue_labels_helper=选择一个任务标签集
license=授权许可 license=授权许可
@@ -802,6 +816,8 @@ readme=自述
readme_helper=选择自述文件模板。 readme_helper=选择自述文件模板。
auto_init=初始化存储库 (添加. gitignore、许可证和自述文件) auto_init=初始化存储库 (添加. gitignore、许可证和自述文件)
create_repo=创建项目 create_repo=创建项目
create_course=发布课程
failed_to_create_course=发布课程失败,请稍后再试。
default_branch=默认分支 default_branch=默认分支
mirror_prune=修剪 mirror_prune=修剪
mirror_prune_desc=删除过时的远程跟踪引用 mirror_prune_desc=删除过时的远程跟踪引用
@@ -867,6 +883,10 @@ generate_statistic_file_error=生成文件失败。
repo_stat_inspect=项目分析 repo_stat_inspect=项目分析
all=所有 all=所有


computing.all=全部
computing.Introduction=简介
computing.success=加入成功

modelarts.status=状态 modelarts.status=状态
modelarts.createtime=创建时间 modelarts.createtime=创建时间
modelarts.version_nums=版本数 modelarts.version_nums=版本数
@@ -941,6 +961,14 @@ modelarts.back=返回
modelarts.train_job_para_admin=任务参数管理 modelarts.train_job_para_admin=任务参数管理
modelarts.train_job_para.edit=编辑 modelarts.train_job_para.edit=编辑
modelarts.train_job_para.connfirm=确定 modelarts.train_job_para.connfirm=确定
modelarts.evaluate_job=评测任务
modelarts.evaluate_job.new_job=新建评测任务
cloudbrain.benchmark.evaluate_type=评测类型
cloudbrain.benchmark.evaluate_child_type=子类型
cloudbrain.benchmark.evaluate_mirror=镜像
cloudbrain.benchmark.evaluate_train=训练程序
cloudbrain.benchmark.evaluate_test=测试程序



modelarts.infer_job_model = 模型名称 modelarts.infer_job_model = 模型名称
modelarts.infer_job_model_file = 模型文件 modelarts.infer_job_model_file = 模型文件
@@ -976,14 +1004,21 @@ template.avatar=头像
template.issue_labels=任务标签 template.issue_labels=任务标签
template.one_item=必须至少选择一个模板项 template.one_item=必须至少选择一个模板项
template.invalid=必须选择一个模板项目 template.invalid=必须选择一个模板项目
template.repo_adress=项目地址
template.repo_path=项目地址
template.repo_name=项目名称


archive.title=此项目已存档。您可以查看文件和克隆,但不能推送或创建任务/合并请求。 archive.title=此项目已存档。您可以查看文件和克隆,但不能推送或创建任务/合并请求。
archive.issue.nocomment=此项目已存档,您不能在此任务添加评论。 archive.issue.nocomment=此项目已存档,您不能在此任务添加评论。
archive.pull.nocomment=此项目已存档,您不能在此合并请求添加评论。 archive.pull.nocomment=此项目已存档,您不能在此合并请求添加评论。


form.reach_limit_of_creation=你已经达到了您的 %d 项目的限制。 form.reach_limit_of_creation=你已经达到了您的 %d 项目的限制。
form.reach_limit_of_course_creation=你已经达到了您的 %d 课程的限制。
form.name_reserved=项目名称 '%s' 是被保留的。 form.name_reserved=项目名称 '%s' 是被保留的。
form.course_name_reserved=课程名称 '%s' 是被保留的。
form.name_pattern_not_allowed=项目名称中不允许使用模式 "%s"。 form.name_pattern_not_allowed=项目名称中不允许使用模式 "%s"。
form.course_name_pattern_not_allowed=课程名称中不允许使用模式 "%s"。
add_course_org_fail=加入组织失败,请稍后重试。


need_auth=需要授权验证 need_auth=需要授权验证
migrate_type=迁移类型 migrate_type=迁移类型
@@ -2028,6 +2063,7 @@ org_full_name_holder=组织全名
org_name_helper=组织名字应该简单明了。 org_name_helper=组织名字应该简单明了。
create_org=创建组织 create_org=创建组织
repo_updated=最后更新于 repo_updated=最后更新于
repo_released=发布于
home=组织主页 home=组织主页
people=组织成员 people=组织成员
teams=组织团队 teams=组织团队
@@ -2044,6 +2080,14 @@ team_access_desc=项目权限
team_permission_desc=权限 team_permission_desc=权限
team_unit_desc=允许访问项目单元 team_unit_desc=允许访问项目单元
team_unit_disabled=(已禁用) team_unit_disabled=(已禁用)
selected_couse=精选课程
release_course = 发布课程
all_keywords=全部关键字
max_selectedPro= 最多可选9个公开项目
custom_select_courses = 自定义精选课程
recommend_remain_pro = 还能推荐
save_fail_tips = 最多可选9个,保存失败
select_again = 选择超过9个,请重新选择!


form.name_reserved=组织名称 '%s' 是被保留的。 form.name_reserved=组织名称 '%s' 是被保留的。
form.name_pattern_not_allowed=组织名称中不允许使用 "%s"。 form.name_pattern_not_allowed=组织名称中不允许使用 "%s"。
@@ -2087,6 +2131,8 @@ members.remove=移除成员
members.leave=离开组织 members.leave=离开组织
members.invite_desc=邀请新的用户加入 %s: members.invite_desc=邀请新的用户加入 %s:
members.invite_now=立即邀请 members.invite_now=立即邀请
course_members.remove=移除
course_members.leave=离开


teams.join=加入团队 teams.join=加入团队
teams.leave=离开团队 teams.leave=离开团队
@@ -2130,6 +2176,10 @@ teams.all_repositories_read_permission_desc=此团队授予<strong>读取</stron
teams.all_repositories_write_permission_desc=此团队授予<strong>修改</strong><strong>所有项目</strong>的访问权限: 成员可以查看和推送至项目。 teams.all_repositories_write_permission_desc=此团队授予<strong>修改</strong><strong>所有项目</strong>的访问权限: 成员可以查看和推送至项目。
teams.all_repositories_admin_permission_desc=该团队拥有 <strong>管理</strong> <strong>所有项目</strong>的权限:团队成员可以读取、克隆、推送以及添加其它项目协作者。 teams.all_repositories_admin_permission_desc=该团队拥有 <strong>管理</strong> <strong>所有项目</strong>的权限:团队成员可以读取、克隆、推送以及添加其它项目协作者。


teams.join_teams=加入该组织



[admin] [admin]
dashboard=管理面板 dashboard=管理面板
users=帐户管理 users=帐户管理
@@ -2707,6 +2757,7 @@ error.unit_not_allowed=您没有权限访问此项目单元
head.community=启智社区 head.community=启智社区
head.project=项目 head.project=项目
head.openi=启智项目 head.openi=启智项目
head.openi.repo = 启智项目
head.dataset=数据集 head.dataset=数据集
foot.council=理事会 foot.council=理事会
foot.technical_committee=技术委员会 foot.technical_committee=技术委员会


+ 34
- 86
public/home/home.js View File

@@ -4,7 +4,6 @@ if(isEmpty(token)){
var meta = $("meta[name=_uid]"); var meta = $("meta[name=_uid]");
if(!isEmpty(meta)){ if(!isEmpty(meta)){
token = meta.attr("content"); token = meta.attr("content");
console.log("token is uid:" + token);
} }
} }


@@ -33,8 +32,6 @@ var swiperRepo = new Swiper(".homepro-list", {
}); });


var output = document.getElementById("newmessage"); var output = document.getElementById("newmessage");
console.log("document.location.host="+document.location.host);
console.log("document.URL="+document.URL);
var url = "ws://" + document.location.host + "/action/notification"; var url = "ws://" + document.location.host + "/action/notification";
if(document.location.host == "git.openi.org.cn" || document.URL.startsWith("https")){ if(document.location.host == "git.openi.org.cn" || document.URL.startsWith("https")){
url = "wss://" + document.location.host + "/action/notification" url = "wss://" + document.location.host + "/action/notification"
@@ -51,18 +48,14 @@ 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" ){
console.log("the language is " + lang.nodeValue);
isZh=false; isZh=false;
}else{ }else{
console.log("default lang=zh");
} }


socket.onmessage = function (e) { socket.onmessage = function (e) {
var data =JSON.parse(e.data) var data =JSON.parse(e.data)
console.log("recevie data=" + e.data)
var html = ""; var html = "";
if (data != null){ if (data != null){
console.log("queue length=" + messageQueue.length);
if(messageQueue.length > maxSize){ if(messageQueue.length > maxSize){
delete messageQueue[0]; delete messageQueue[0];
}else{ }else{
@@ -71,38 +64,38 @@ socket.onmessage = function (e) {
var currentTime = new Date().getTime(); var currentTime = new Date().getTime();
for(var i = 0; i < messageQueue.length; i++){ for(var i = 0; i < messageQueue.length; i++){
var record = messageQueue[i]; var record = messageQueue[i];
var recordPrefix = getMsg(record); var recordPrefix = getMsg(record);
var actionName = getAction(record.OpType,isZh); var actionName = getAction(record.OpType,isZh);
if(record.OpType == "6" || record.OpType == "10" || record.OpType == "12" || record.OpType == "13"){ if(record.OpType == "6" || record.OpType == "10" || record.OpType == "12" || record.OpType == "13"){
html += recordPrefix + actionName; html += recordPrefix + actionName;
html += " <a href=\"" + getIssueLink(record) + "\" rel=\"nofollow\">" + getIssueText(record) + "</a>"
html += " <a href=\"" + getIssueLink(record) + "\" rel=\"nofollow\">" + getIssueText(record) + "</a>"
} }
else if(record.OpType == "7" || record.OpType == "11" || record.OpType == "14" || record.OpType == "15" || record.OpType == "22" else if(record.OpType == "7" || record.OpType == "11" || record.OpType == "14" || record.OpType == "15" || record.OpType == "22"
|| record.OpType == "23"){ || record.OpType == "23"){
html += recordPrefix + actionName; html += recordPrefix + actionName;
html += " <a href=\"" + getPRLink(record) + "\" rel=\"nofollow\">" + getPRText(record) + "</a>"
html += " <a href=\"" + getPRLink(record) + "\" rel=\"nofollow\">" + getPRText(record) + "</a>"
} }
else if(record.OpType == "1"){ else if(record.OpType == "1"){
html += recordPrefix + actionName; html += recordPrefix + actionName;
html += " <a href=\"" + getRepoLink(record) + "\" rel=\"nofollow\">" + getRepoLink(record) + "</a>"
html += " <a href=\"" + getRepoLink(record) + "\" rel=\"nofollow\">" +getRepotext(record) + "</a>"
} }
else if(record.OpType == "9" || record.OpType == "5"){ else if(record.OpType == "9" || record.OpType == "5"){
branch = "<a href=\"" + getRepoLink(record) + "/src/branch/" + record.RefName + "\" rel=\"nofollow\">" + record.RefName + "</a>" branch = "<a href=\"" + getRepoLink(record) + "/src/branch/" + record.RefName + "\" rel=\"nofollow\">" + record.RefName + "</a>"
actionName = actionName.replace("{branch}",branch); actionName = actionName.replace("{branch}",branch);
html += recordPrefix + actionName; html += recordPrefix + actionName;
html += " <a href=\"" + getRepoLink(record) + "\" rel=\"nofollow\">" + getRepoLink(record) + "</a>"
html += " <a href=\"" + getRepoLink(record) + "\" rel=\"nofollow\">" + getRepotext(record) + "</a>"
}else if(record.OpType == "17"){ }else if(record.OpType == "17"){
actionName = actionName.replace("{deleteBranchName}",record.RefName); actionName = actionName.replace("{deleteBranchName}",record.RefName);
var repoLink = "<a href=\"" + getRepoLink(record) + "\" rel=\"nofollow\">" + getRepoLink(record) + "</a>"
var repoLink = "<a href=\"" + getRepoLink(record) + "\" rel=\"nofollow\">" + getRepotext(record) + "</a>"
actionName = actionName.replace("{repoName}",repoLink); actionName = actionName.replace("{repoName}",repoLink);
html += recordPrefix + actionName; html += recordPrefix + actionName;
} }
else if(record.OpType == "2"){ else if(record.OpType == "2"){
actionName = actionName.replace("{oldRepoName}",record.Content); actionName = actionName.replace("{oldRepoName}",record.Content);
html += recordPrefix + actionName; html += recordPrefix + actionName;
html += " <a href=\"" + getRepoLink(record) + "\" rel=\"nofollow\">" + getRepoLink(record) + "</a>"
html += " <a href=\"" + getRepoLink(record) + "\" rel=\"nofollow\">" + getRepotext(record) + "</a>"
} }
else{ else{
continue; continue;
@@ -115,17 +108,8 @@ socket.onmessage = function (e) {
html += "</div>"; html += "</div>";
html += "</div>"; html += "</div>";
} }
/*
<div class="swiper-slide item">
<img class="ui avatar image" src="/user/avatar/zhoupzh/-1" alt="">
<div class="middle aligned content">
<a href="/zhoupzh" title="">zhoupzh</a> 合并了合并请求 <a href="/OpenI/aiforge/pulls/1168" rel="nofollow">OpenI/aiforge#1168</a><span class="time-since">22 分钟前</span>
</div>
</div>
*/


} }
console.log("html=" + html)
output.innerHTML = html; output.innerHTML = html;
swiperNewMessage.updateSlides(); swiperNewMessage.updateSlides();
swiperNewMessage.updateProgress(); swiperNewMessage.updateProgress();
@@ -136,15 +120,20 @@ function getMsg(record){
html += "<div class=\"swiper-slide item\">"; html += "<div class=\"swiper-slide item\">";
html += " <img class=\"ui avatar image\" src=\"/user/avatar/" + record.ActUser.Name + "/-1\" alt=\"\">" html += " <img class=\"ui avatar image\" src=\"/user/avatar/" + record.ActUser.Name + "/-1\" alt=\"\">"
html += " <div class=\"middle aligned content nowrap\">" html += " <div class=\"middle aligned content nowrap\">"
html += " <a href=\"/" + record.ActUser.Name + "\" title=\"\">" + record.ActUser.Name + "</a>"
html += " <a href=\"/" + record.ActUser.Name + "\" title=\"\">" + record.ActUser.Name + "</a>"
return html; return html;
} }


function getRepoLink(record){
return "/" + record.Repo.OwnerName + "/" + record.Repo.Name;
function getRepotext(record){
if(record.Repo.Alias){
return record.Repo.OwnerName + "/" + record.Repo.Alias;
}else{
return record.Repo.OwnerName + "/" + record.Repo.Name;
}
} }
function getRepoLink(record){ function getRepoLink(record){
return record.Repo.OwnerName + "/" + record.Repo.Name; return record.Repo.OwnerName + "/" + record.Repo.Name;

} }


function getTime(UpdatedUnix,currentTime){ function getTime(UpdatedUnix,currentTime){
@@ -154,8 +143,7 @@ function getTime(UpdatedUnix,currentTime){
if( timeEscSecond < 0){ if( timeEscSecond < 0){
timeEscSecond = 1; timeEscSecond = 1;
} }
console.log("currentTime=" + currentTime + " updateUnix=" + UpdatedUnix);

var hours= Math.floor(timeEscSecond / 3600); var hours= Math.floor(timeEscSecond / 3600);
//计算相差分钟数 //计算相差分钟数
var leave2 = Math.floor(timeEscSecond % (3600)); //计算小时数后剩余的秒数 var leave2 = Math.floor(timeEscSecond % (3600)); //计算小时数后剩余的秒数
@@ -179,11 +167,16 @@ function getPRLink(record){
return "/" + record.Repo.OwnerName + "/" + record.Repo.Name + "/pulls/" + getIssueId(record); return "/" + record.Repo.OwnerName + "/" + record.Repo.Name + "/pulls/" + getIssueId(record);
} }
function getPRText(record){ function getPRText(record){
return record.Repo.OwnerName + "/" + record.Repo.Name + "#" + getIssueId(record);
if(record.Repo.Alias){
return record.Repo.OwnerName + "/" + record.Repo.Alias + "#" + getIssueId(record);
}else{
return record.Repo.OwnerName + "/" + record.Repo.Name + "#" + getIssueId(record);
}

} }


function getIssueLink(record){ function getIssueLink(record){
return "/" + record.Repo.OwnerName + "/" + record.Repo.Name + "/issues/" + getIssueId(record); return "/" + record.Repo.OwnerName + "/" + record.Repo.Name + "/issues/" + getIssueId(record);
} }


@@ -204,7 +197,12 @@ function getIssueId(record){
} }


function getIssueText(record){ function getIssueText(record){
return record.Repo.OwnerName + "/" + record.Repo.Name + "#" + getIssueId(record);
if(record.Repo.Alias){
return record.Repo.OwnerName + "/" + record.Repo.Alias + "#" + getIssueId(record);
}else{
return record.Repo.OwnerName + "/" + record.Repo.Name + "#" + getIssueId(record);
}

} }


/* /*
@@ -288,8 +286,8 @@ var repoAndOrgEN={
"1":"Repository", "1":"Repository",
"2":"Member ", "2":"Member ",
"3":"Team", "3":"Team",
"11":"Repositorys",
"22":"Members ",
"11":"Repositories",
"21":"Members ",
"31":"Teams", "31":"Teams",
"4":" hour ago", "4":" hour ago",
"5":" minute ago", "5":" minute ago",
@@ -320,11 +318,9 @@ function queryRecommendData(){
dataType:"json", dataType:"json",
async:false, async:false,
success:function(json){ success:function(json){
console.log(json);
displayOrg(json); displayOrg(json);
}, },
error:function(response) { error:function(response) {
console.log(response);
} }
}); });


@@ -337,40 +333,14 @@ function queryRecommendData(){
dataType:"json", dataType:"json",
async:false, async:false,
success:function(json){ success:function(json){
console.log(json);
displayRepo(json); displayRepo(json);
}, },
error:function(response) { error:function(response) {
console.log(response);
} }
}); });


} }


/*
<div class="swiper-slide">
<div class="ui fluid card">
<div class="content">
<span class="right floated meta">
<i class="star icon"></i>276 <i class="star icon"></i>32
</span>
<img class="left floated mini ui image" src="/repo-avatars/278-a9f45e21b92b86dbf969c9f70dff1efc">
<a class="header nowrap" href="/OpenI/aiforge">aiforge </a>
<div class="description nowrap-2">
本项目是群体化方法与技术的开源实现案例,在基于Gitea的基础上,进一步支持社交化的协同开发、协同学习、协同研究等群体创新实践服务,特别是针对新一代人工智能技术特点,重点支持项目管理、git代码管理、大数据集存储管理与智能计算平台接入。
</div>
<div class="ui tags nowrap am-mt-10">
<a class="ui small label topic" href="/explore/repos?q=ai%e5%bc%80%e5%8f%91%e5%b7%a5%e5%85%b7&amp;topic=">ai开发工具</a>
<a class="ui small label topic" href="/explore/repos?q=openi&amp;topic=">openi</a>
<a class="ui small label topic" href="/explore/repos?q=golang&amp;topic=">golang</a>
<a class="ui small label topic" href="/explore/repos?q=git&amp;topic=">git</a>
<a class="ui small label topic" href="/explore/repos?q=pcl&amp;topic=">pcl</a>
</div>
</div>
</div>
</div>
*/
function displayRepo(json){ function displayRepo(json){
var orgRepo = document.getElementById("recommendrepo"); var orgRepo = document.getElementById("recommendrepo");
var html = ""; var html = "";
@@ -384,7 +354,7 @@ function displayRepo(json){
html += " <i class=\"ri-star-line\"></i>" + record["NumStars"] + "<i class=\"ri-git-branch-line am-ml-10\"></i>" + record["NumForks"]; html += " <i class=\"ri-star-line\"></i>" + record["NumStars"] + "<i class=\"ri-git-branch-line am-ml-10\"></i>" + record["NumForks"];
html += " </span>"; html += " </span>";
html += " <img class=\"left floated mini ui image\" src=\"" + record["Avatar"] + "\">"; html += " <img class=\"left floated mini ui image\" src=\"" + record["Avatar"] + "\">";
html += " <a class=\"header nowrap\" href=\"/" + record["OwnerName"] + "/" + record["Name"] + "\">" + record["Name"] +"</a>";
html += " <a class=\"header nowrap\" href=\"/" + record["OwnerName"] + "/" + record["Name"] + "\">" + record["Alias"] +"</a>";
html += " <div class=\"description nowrap-2\">" + record["Description"] + " </div>"; html += " <div class=\"description nowrap-2\">" + record["Description"] + " </div>";
html += " <div class=\"ui tags nowrap am-mt-10\">" html += " <div class=\"ui tags nowrap am-mt-10\">"
if(record["Topics"] != null){ if(record["Topics"] != null){
@@ -405,27 +375,6 @@ function displayRepo(json){
swiperRepo.updateProgress(); swiperRepo.updateProgress();
} }


/**
*
* <div class="column">
<div class="ui fluid card">
<div class="content">
<div class="ui small header">
<img class="ui image" src="/user/avatar/OpenI/-1">
<div class="content nowrap">
<a href="/OpenI">OpenI</a> 启智社区
<div class="sub header">39 项目 ・ 60 成员 ・ 23 团队</div>
</div>
</div>
</div>
</div>
</div>
*/

//var repoAndOrgZH = new Map([['1', "项目"], ['2', "成员"], ['3', "团队"]]);
//var repoAndOrgEN = new Map([['1', "Repository"], ['2', "Members"], ['3', "Teams"]]);



function getRepoOrOrg(key,isZhLang,numbers=1){ function getRepoOrOrg(key,isZhLang,numbers=1){
if(numbers > 1){ if(numbers > 1){
@@ -437,7 +386,6 @@ function getRepoOrOrg(key,isZhLang,numbers=1){
return repoAndOrgEN[key]; return repoAndOrgEN[key];
} }
} }

function displayOrg(json){ function displayOrg(json){
var orgDiv = document.getElementById("recommendorg"); var orgDiv = document.getElementById("recommendorg");
var html = ""; var html = "";
@@ -460,4 +408,4 @@ function displayOrg(json){
} }
} }
orgDiv.innerHTML = html; orgDiv.innerHTML = html;
}
}

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

@@ -534,6 +534,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/downloadAllOpenI", repo.ServeAllProjectsOpenIStatisticsFile) m.Get("/downloadAllOpenI", repo.ServeAllProjectsOpenIStatisticsFile)
m.Group("/project", func() { m.Group("/project", func() {
m.Get("", repo.GetAllProjectsPeriodStatistics) m.Get("", repo.GetAllProjectsPeriodStatistics)
m.Get("/numVisit", repo.ProjectNumVisit)


m.Group("/:id", func() { m.Group("/:id", func() {
m.Get("", repo.GetProjectLatestStatistics) m.Get("", repo.GetProjectLatestStatistics)
@@ -878,6 +879,7 @@ func RegisterRoutes(m *macaron.Macaron) {
}, reqAnyRepoReader()) }, reqAnyRepoReader())
m.Group("/cloudbrain", func() { m.Group("/cloudbrain", func() {
m.Get("/:jobid", repo.GetCloudbrainTask) m.Get("/:jobid", repo.GetCloudbrainTask)
m.Get("/:jobid/log", repo.CloudbrainGetLog)
}, reqRepoReader(models.UnitTypeCloudBrain)) }, reqRepoReader(models.UnitTypeCloudBrain))
m.Group("/modelarts", func() { m.Group("/modelarts", func() {
m.Group("/notebook", func() { m.Group("/notebook", func() {


+ 57
- 0
routers/api/v1/repo/cloudbrain.go View File

@@ -8,6 +8,7 @@ package repo
import ( import (
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"net/http" "net/http"
"sort"
"time" "time"


"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
@@ -91,3 +92,59 @@ func GetCloudbrainTask(ctx *context.APIContext) {
}) })


} }

func CloudbrainGetLog(ctx *context.Context) {
jobID := ctx.Params(":jobid")
_, err := models.GetCloudbrainByJobID(jobID)
if err != nil {
log.Error("GetCloudbrainByJobID failed: %v", err, ctx.Data["MsgID"])
ctx.ServerError(err.Error(), err)
return
}

var hits []models.Hits
result, err := cloudbrain.GetJobLog(jobID)
if err != nil{
log.Error("GetJobLog failed: %v", err, ctx.Data["MsgID"])
ctx.ServerError(err.Error(), err)
return
}
hits = result.Hits.Hits

//if the size equal page_size, then take the scroll_id to get all log and delete the scroll_id(the num of scroll_id is limited)
if len(result.Hits.Hits) >= cloudbrain.LogPageSize {
for {
resultNext, err := cloudbrain.GetJobAllLog(result.ScrollID)
if err != nil{
log.Error("GetJobAllLog failed: %v", err, ctx.Data["MsgID"])
} else {
for _, hit := range resultNext.Hits.Hits {
hits = append(hits, hit)
}
}

if len(resultNext.Hits.Hits) < cloudbrain.LogPageSize {
log.Info("get all log already")
break
}
}
}

cloudbrain.DeleteJobLogToken(result.ScrollID)

sort.Slice(hits, func(i, j int) bool {
return hits[i].Sort[0] < hits[j].Sort[0]
})

var content string
for _, log := range hits {
content += log.Source.Message + "\n"
}

ctx.JSON(http.StatusOK, map[string]interface{}{
"JobID": jobID,
"Content": content,
})

return
}

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

@@ -118,7 +118,7 @@ func CreateFork(ctx *context.APIContext, form api.CreateForkOption) {
forker = org forker = org
} }


fork, err := repo_service.ForkRepository(ctx.User, forker, repo, repo.Name, repo.Description)
fork, err := repo_service.ForkRepository(ctx.User, forker, repo, repo.Name, repo.Description, repo.Alias)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "ForkRepository", err) ctx.Error(http.StatusInternalServerError, "ForkRepository", err)
return return


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

@@ -6,11 +6,12 @@
package repo package repo


import ( import (
"code.gitea.io/gitea/modules/util"
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"


"code.gitea.io/gitea/modules/util"

"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"


+ 24
- 0
routers/api/v1/repo/repo_dashbord.go View File

@@ -12,6 +12,7 @@ import (


"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/repository"


"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
@@ -640,3 +641,26 @@ func getTotalPage(total int64, pageSize int) int {
return int(total)/pageSize + another return int(total)/pageSize + another


} }

func ProjectNumVisit(ctx *context.APIContext) {
var (
err error
)

var userName = ctx.Query("user")
var projectName = ctx.Query("project")
var beginTime = ctx.Query("begintime")
var endTime = ctx.Query("endtime")

var ProjectNumVisits int
ProjectNumVisits, err = repository.AppointProjectView(userName, projectName, beginTime, endTime) //访问量
if err != nil {
ctx.NotFound(err)
}
log.Info("ProjectNumVisits is:", ProjectNumVisits)

ctx.JSON(http.StatusOK, map[string]interface{}{
"ProjectNumVisits": ProjectNumVisits,
"StatusOK": 0,
})
}

+ 8
- 57
routers/home.go View File

@@ -7,11 +7,11 @@ package routers


import ( import (
"bytes" "bytes"
"fmt"
"io/ioutil"
"net/http" "net/http"
"strings" "strings"


"code.gitea.io/gitea/services/repository"

"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
@@ -133,6 +133,7 @@ type RepoSearchOptions struct {
Restricted bool Restricted bool
PageSize int PageSize int
TplName base.TplName TplName base.TplName
Course util.OptionalBool
} }


var ( var (
@@ -211,6 +212,7 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
AllLimited: true, AllLimited: true,
TopicName: topic, TopicName: topic,
IncludeDescription: setting.UI.SearchRepoDescription, IncludeDescription: setting.UI.SearchRepoDescription,
Course: opts.Course,
}) })
if err != nil { if err != nil {
ctx.ServerError("SearchRepository", err) ctx.ServerError("SearchRepository", err)
@@ -559,7 +561,7 @@ func NotFound(ctx *context.Context) {


func RecommendOrgFromPromote(ctx *context.Context) { func RecommendOrgFromPromote(ctx *context.Context) {
url := setting.RecommentRepoAddr + "organizations" url := setting.RecommentRepoAddr + "organizations"
result, err := recommendFromPromote(url)
result, err := repository.RecommendFromPromote(url)
if err != nil { if err != nil {
ctx.ServerError("500", err) ctx.ServerError("500", err)
return return
@@ -586,62 +588,11 @@ func RecommendOrgFromPromote(ctx *context.Context) {
ctx.JSON(200, resultOrg) ctx.JSON(200, resultOrg)
} }


func recommendFromPromote(url string) ([]string, error) {
resp, err := http.Get(url)
if err != nil || resp.StatusCode != 200 {
log.Info("Get organizations url error=" + err.Error())
return nil, err
}
bytes, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
log.Info("Get organizations url error=" + err.Error())
return nil, err
}

allLineStr := string(bytes)
lines := strings.Split(allLineStr, "\n")
result := make([]string, len(lines))
for i, line := range lines {
log.Info("i=" + fmt.Sprint(i) + " line=" + line)
result[i] = strings.Trim(line, " ")
}
return result, nil
}

func RecommendRepoFromPromote(ctx *context.Context) { func RecommendRepoFromPromote(ctx *context.Context) {
url := setting.RecommentRepoAddr + "projects"
result, err := recommendFromPromote(url)
result, err := repository.GetRecommendRepoFromPromote("projects")
if err != nil { if err != nil {
ctx.ServerError("500", err) ctx.ServerError("500", err)
return
}
resultRepo := make([]map[string]interface{}, 0)
//resultRepo := make([]*models.Repository, 0)
for _, repoName := range result {
tmpIndex := strings.Index(repoName, "/")
if tmpIndex == -1 {
log.Info("error repo name format.")
} else {
ownerName := strings.Trim(repoName[0:tmpIndex], " ")
repoName := strings.Trim(repoName[tmpIndex+1:], " ")
repo, err := models.GetRepositoryByOwnerAndName(ownerName, repoName)
if err == nil {
repoMap := make(map[string]interface{})
repoMap["ID"] = fmt.Sprint(repo.ID)
repoMap["Name"] = repo.Name
repoMap["OwnerName"] = repo.OwnerName
repoMap["NumStars"] = repo.NumStars
repoMap["NumForks"] = repo.NumForks
repoMap["Description"] = repo.Description
repoMap["NumWatchs"] = repo.NumWatches
repoMap["Topics"] = repo.Topics
repoMap["Avatar"] = repo.RelAvatarLink()
resultRepo = append(resultRepo, repoMap)
} else {
log.Info("query repo error," + err.Error())
}
}
} else {
ctx.JSON(200, result)
} }
ctx.JSON(200, resultRepo)
} }

+ 43
- 6
routers/org/home.go View File

@@ -7,6 +7,10 @@ package org
import ( import (
"strings" "strings"


"code.gitea.io/gitea/services/repository"

"code.gitea.io/gitea/modules/util"

"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
@@ -14,7 +18,8 @@ import (
) )


const ( const (
tplOrgHome base.TplName = "org/home"
tplOrgHome base.TplName = "org/home"
tplOrgCourseHome base.TplName = "org/home_courses"
) )


// Home show organization home page // Home show organization home page
@@ -59,10 +64,16 @@ func Home(ctx *context.Context) {
case "fewestforks": case "fewestforks":
orderBy = models.SearchOrderByForks orderBy = models.SearchOrderByForks
default: default:
ctx.Data["SortType"] = "recentupdate"
orderBy = models.SearchOrderByRecentUpdated
}
if setting.Course.OrgName == org.Name {
ctx.Data["SortType"] = "newest"
orderBy = models.SearchOrderByNewest


} else {
ctx.Data["SortType"] = "recentupdate"
orderBy = models.SearchOrderByRecentUpdated
}
}
orderBy = orderBy + ",id"
keyword := strings.Trim(ctx.Query("q"), " ") keyword := strings.Trim(ctx.Query("q"), " ")
ctx.Data["Keyword"] = keyword ctx.Data["Keyword"] = keyword


@@ -76,9 +87,18 @@ func Home(ctx *context.Context) {
count int64 count int64
err error err error
) )
pageSize := setting.UI.User.RepoPagingNum
var CourseOptional util.OptionalBool = util.OptionalBoolNone
if setting.Course.OrgName == org.Name {
pageSize = 15
recommendCourseKeyWords, _ := repository.GetRecommendCourseKeyWords()
ctx.Data["CoursesKeywords"] = recommendCourseKeyWords

}

repos, count, err = models.SearchRepository(&models.SearchRepoOptions{ repos, count, err = models.SearchRepository(&models.SearchRepoOptions{
ListOptions: models.ListOptions{ ListOptions: models.ListOptions{
PageSize: setting.UI.User.RepoPagingNum,
PageSize: pageSize,
Page: page, Page: page,
}, },
Keyword: keyword, Keyword: keyword,
@@ -87,6 +107,7 @@ func Home(ctx *context.Context) {
Private: ctx.IsSigned, Private: ctx.IsSigned,
Actor: ctx.User, Actor: ctx.User,
IncludeDescription: setting.UI.SearchRepoDescription, IncludeDescription: setting.UI.SearchRepoDescription,
Course: CourseOptional,
}) })
if err != nil { if err != nil {
ctx.ServerError("SearchRepository", err) ctx.ServerError("SearchRepository", err)
@@ -132,11 +153,27 @@ func Home(ctx *context.Context) {


//find org tag info //find org tag info
tags, err := models.GetAllOfficialTagRepos(org.ID, ctx.Org.IsOwner) tags, err := models.GetAllOfficialTagRepos(org.ID, ctx.Org.IsOwner)

if setting.Course.OrgName == org.Name {
for _, tag := range tags {
for _, repo := range tag.RepoList {
repo.GetCreator()
repo.GetOwner()
}
}

}

if err != nil { if err != nil {
ctx.ServerError("GetAllOfficialTagRepos", err) ctx.ServerError("GetAllOfficialTagRepos", err)
return return
} }


ctx.Data["tags"] = tags ctx.Data["tags"] = tags
ctx.HTML(200, tplOrgHome)
if setting.Course.OrgName == org.Name {
ctx.HTML(200, tplOrgCourseHome)
} else {
ctx.HTML(200, tplOrgHome)
}

} }

+ 7
- 3
routers/org/members.go View File

@@ -17,7 +17,8 @@ import (


const ( const (
// tplMembers template for organization members page // tplMembers template for organization members page
tplMembers base.TplName = "org/member/members"
tplMembers base.TplName = "org/member/members"
tplCourseMembers base.TplName = "org/member/course_members"
) )


// Members render organization users page // Members render organization users page
@@ -64,8 +65,11 @@ func Members(ctx *context.Context) {
ctx.Data["MembersIsPublicMember"] = membersIsPublic ctx.Data["MembersIsPublicMember"] = membersIsPublic
ctx.Data["MembersIsUserOrgOwner"] = members.IsUserOrgOwner(org.ID) ctx.Data["MembersIsUserOrgOwner"] = members.IsUserOrgOwner(org.ID)
ctx.Data["MembersTwoFaStatus"] = members.GetTwoFaStatus() ctx.Data["MembersTwoFaStatus"] = members.GetTwoFaStatus()

ctx.HTML(200, tplMembers)
if setting.Course.OrgName == org.Name {
ctx.HTML(200, tplCourseMembers)
} else {
ctx.HTML(200, tplMembers)
}
} }


// MembersAction response for operation to a member of organization // MembersAction response for operation to a member of organization


+ 10
- 3
routers/org/teams.go View File

@@ -10,6 +10,8 @@ import (
"path" "path"
"strings" "strings"


"code.gitea.io/gitea/modules/setting"

"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/auth"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
@@ -22,7 +24,8 @@ import (


const ( const (
// tplTeams template path for teams list page // tplTeams template path for teams list page
tplTeams base.TplName = "org/team/teams"
tplTeams base.TplName = "org/team/teams"
tplCourseTeams base.TplName = "org/team/courseTeams"
// tplTeamNew template path for create new team page // tplTeamNew template path for create new team page
tplTeamNew base.TplName = "org/team/new" tplTeamNew base.TplName = "org/team/new"
// tplTeamMembers template path for showing team members page // tplTeamMembers template path for showing team members page
@@ -44,8 +47,12 @@ func Teams(ctx *context.Context) {
} }
} }
ctx.Data["Teams"] = org.Teams ctx.Data["Teams"] = org.Teams
if setting.Course.OrgName == org.Name {
ctx.HTML(200, tplCourseTeams)
} else {
ctx.HTML(200, tplTeams)
}


ctx.HTML(200, tplTeams)
} }


// TeamsAction response for join, leave, remove, add operations to team // TeamsAction response for join, leave, remove, add operations to team
@@ -143,7 +150,7 @@ func TeamsRepoAction(ctx *context.Context) {
case "add": case "add":
repoName := path.Base(ctx.Query("repo_name")) repoName := path.Base(ctx.Query("repo_name"))
var repo *models.Repository var repo *models.Repository
repo, err = models.GetRepositoryByName(ctx.Org.Organization.ID, repoName)
repo, err = models.GetRepositoryByOwnerAndAlias(ctx.Org.Organization.Name, repoName)
if err != nil { if err != nil {
if models.IsErrRepoNotExist(err) { if models.IsErrRepoNotExist(err) {
ctx.Flash.Error(ctx.Tr("org.teams.add_nonexistent_repo")) ctx.Flash.Error(ctx.Tr("org.teams.add_nonexistent_repo"))


+ 437
- 16
routers/repo/cloudbrain.go View File

@@ -28,15 +28,21 @@ import (
) )


const ( const (
tplCloudBrainIndex base.TplName = "repo/cloudbrain/index"
tplCloudBrainNew base.TplName = "repo/cloudbrain/new" tplCloudBrainNew base.TplName = "repo/cloudbrain/new"
tplCloudBrainShow base.TplName = "repo/cloudbrain/show" tplCloudBrainShow base.TplName = "repo/cloudbrain/show"
tplCloudBrainShowModels base.TplName = "repo/cloudbrain/models/index" tplCloudBrainShowModels base.TplName = "repo/cloudbrain/models/index"

tplCloudBrainBenchmarkIndex base.TplName = "repo/cloudbrain/benchmark/index"
tplCloudBrainBenchmarkNew base.TplName = "repo/cloudbrain/benchmark/new"
tplCloudBrainBenchmarkShow base.TplName = "repo/cloudbrain/benchmark/show"
) )


var ( var (
gpuInfos *models.GpuInfos
categories *models.Categories
gpuInfos *models.GpuInfos
categories *models.Categories
benchmarkTypes *models.BenchmarkTypes
benchmarkGpuInfos *models.GpuInfos
benchmarkResourceSpecs *models.ResourceSpecs
) )


var jobNamePattern = regexp.MustCompile(`^[a-z0-9][a-z0-9-_]{1,34}[a-z0-9-]$`) var jobNamePattern = regexp.MustCompile(`^[a-z0-9][a-z0-9-_]{1,34}[a-z0-9-]$`)
@@ -124,11 +130,28 @@ func cloudBrainNewDataPrepare(ctx *context.Context) error {
} }
ctx.Data["benchmark_categories"] = categories.Category ctx.Data["benchmark_categories"] = categories.Category


if benchmarkTypes == nil {
if err := json.Unmarshal([]byte(setting.BenchmarkTypes), &benchmarkTypes); err != nil {
log.Error("json.Unmarshal BenchmarkTypes(%s) failed:%v", setting.BenchmarkTypes, err, ctx.Data["MsgID"])
}
}
ctx.Data["benchmark_types"] = benchmarkTypes.BenchmarkType

if gpuInfos == nil { if gpuInfos == nil {
json.Unmarshal([]byte(setting.GpuTypes), &gpuInfos) json.Unmarshal([]byte(setting.GpuTypes), &gpuInfos)
} }
ctx.Data["gpu_types"] = gpuInfos.GpuInfo ctx.Data["gpu_types"] = gpuInfos.GpuInfo


if benchmarkGpuInfos == nil {
json.Unmarshal([]byte(setting.BenchmarkGpuTypes), &benchmarkGpuInfos)
}
ctx.Data["benchmark_gpu_types"] = benchmarkGpuInfos.GpuInfo

if benchmarkResourceSpecs == nil {
json.Unmarshal([]byte(setting.BenchmarkResourceSpecs), &benchmarkResourceSpecs)
}
ctx.Data["benchmark_resource_specs"] = benchmarkResourceSpecs.ResourceSpec

if cloudbrain.ResourceSpecs == nil { if cloudbrain.ResourceSpecs == nil {
json.Unmarshal([]byte(setting.ResourceSpecs), &cloudbrain.ResourceSpecs) json.Unmarshal([]byte(setting.ResourceSpecs), &cloudbrain.ResourceSpecs)
} }
@@ -155,9 +178,9 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) {
ctx.Data["PageIsCloudBrain"] = true ctx.Data["PageIsCloudBrain"] = true
jobName := form.JobName jobName := form.JobName
image := form.Image image := form.Image
command := form.Command
uuid := form.Attachment uuid := form.Attachment
jobType := form.JobType jobType := form.JobType
command := cloudbrain.Command
gpuQueue := form.GpuType gpuQueue := form.GpuType
codePath := setting.JobPath + jobName + cloudbrain.CodeMountPath codePath := setting.JobPath + jobName + cloudbrain.CodeMountPath
resourceSpecId := form.ResourceSpecId resourceSpecId := form.ResourceSpecId
@@ -174,7 +197,7 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) {
return return
} }


count, err := models.GetCloudbrainCountByUserID(ctx.User.ID)
count, err := models.GetCloudbrainCountByUserID(ctx.User.ID, jobType)
if err != nil { if err != nil {
log.Error("GetCloudbrainCountByUserID failed:%v", err, ctx.Data["MsgID"]) log.Error("GetCloudbrainCountByUserID failed:%v", err, ctx.Data["MsgID"])
cloudBrainNewDataPrepare(ctx) cloudBrainNewDataPrepare(ctx)
@@ -238,12 +261,14 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) {
err = cloudbrain.GenerateTask(ctx, jobName, image, command, uuid, storage.GetMinioPath(jobName, cloudbrain.CodeMountPath+"/"), err = cloudbrain.GenerateTask(ctx, jobName, image, command, uuid, storage.GetMinioPath(jobName, cloudbrain.CodeMountPath+"/"),
storage.GetMinioPath(jobName, cloudbrain.ModelMountPath+"/"), storage.GetMinioPath(jobName, cloudbrain.ModelMountPath+"/"),
storage.GetMinioPath(jobName, cloudbrain.BenchMarkMountPath+"/"), storage.GetMinioPath(jobName, cloudbrain.Snn4imagenetMountPath+"/"), storage.GetMinioPath(jobName, cloudbrain.BenchMarkMountPath+"/"), storage.GetMinioPath(jobName, cloudbrain.Snn4imagenetMountPath+"/"),
storage.GetMinioPath(jobName, cloudbrain.BrainScoreMountPath+"/"), jobType, gpuQueue, resourceSpecId)
storage.GetMinioPath(jobName, cloudbrain.BrainScoreMountPath+"/"), jobType, gpuQueue, form.Description,
0, 0, resourceSpecId)
if err != nil { if err != nil {
cloudBrainNewDataPrepare(ctx) cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr(err.Error(), tplCloudBrainNew, &form) ctx.RenderWithErr(err.Error(), tplCloudBrainNew, &form)
return return
} }

ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/debugjob?debugListType=all") ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/debugjob?debugListType=all")
} }


@@ -276,7 +301,7 @@ func CloudBrainRestart(ctx *context.Context) {
break break
} }


count, err := models.GetCloudbrainCountByUserID(ctx.User.ID)
count, err := models.GetCloudbrainCountByUserID(ctx.User.ID, string(models.JobTypeDebug))
if err != nil { if err != nil {
log.Error("GetCloudbrainCountByUserID failed:%v", err, ctx.Data["MsgID"]) log.Error("GetCloudbrainCountByUserID failed:%v", err, ctx.Data["MsgID"])
resultCode = "-1" resultCode = "-1"
@@ -310,7 +335,20 @@ func CloudBrainRestart(ctx *context.Context) {
}) })
} }


func CloudBrainBenchMarkShow(ctx *context.Context) {
if benchmarkTypes == nil {
if err := json.Unmarshal([]byte(setting.BenchmarkTypes), &benchmarkTypes); err != nil {
log.Error("json.Unmarshal BenchmarkTypes(%s) failed:%v", setting.BenchmarkTypes, err, ctx.Data["MsgID"])
}
}
cloudBrainShow(ctx, tplCloudBrainBenchmarkShow)
}

func CloudBrainShow(ctx *context.Context) { func CloudBrainShow(ctx *context.Context) {
cloudBrainShow(ctx, tplCloudBrainShow)
}

func cloudBrainShow(ctx *context.Context, tpName base.TplName) {
ctx.Data["PageIsCloudBrain"] = true ctx.Data["PageIsCloudBrain"] = true


var jobID = ctx.Params(":jobid") var jobID = ctx.Params(":jobid")
@@ -327,6 +365,8 @@ func CloudBrainShow(ctx *context.Context) {
if result != nil { if result != nil {
jobRes, _ := models.ConvertToJobResultPayload(result.Payload) jobRes, _ := models.ConvertToJobResultPayload(result.Payload)
jobRes.Resource.Memory = strings.ReplaceAll(jobRes.Resource.Memory, "Mi", "MB") jobRes.Resource.Memory = strings.ReplaceAll(jobRes.Resource.Memory, "Mi", "MB")
spec := "GPU数:" + strconv.Itoa(jobRes.Resource.NvidiaComGpu) + ",CPU数:" + strconv.Itoa(jobRes.Resource.CPU) + ",内存(MB):" + jobRes.Resource.Memory
ctx.Data["resource_spec"] = spec
taskRoles := jobRes.TaskRoles taskRoles := jobRes.TaskRoles
if jobRes.JobStatus.State != string(models.JobFailed) { if jobRes.JobStatus.State != string(models.JobFailed) {
taskRes, _ := models.ConvertToTaskPod(taskRoles[cloudbrain.SubTaskName].(map[string]interface{})) taskRes, _ := models.ConvertToTaskPod(taskRoles[cloudbrain.SubTaskName].(map[string]interface{}))
@@ -351,11 +391,43 @@ func CloudBrainShow(ctx *context.Context) {
} }


ctx.Data["result"] = jobRes ctx.Data["result"] = jobRes
} else {
log.Info("error:" + err.Error())
}
user, err := models.GetUserByID(task.UserID)
if err == nil {
task.User = user
}

var duration int64
if task.Status == string(models.JobRunning) {
duration = time.Now().Unix() - int64(task.CreatedUnix)
} else {
duration = int64(task.UpdatedUnix) - int64(task.CreatedUnix)
}
if benchmarkTypes != nil {
for _, benchmarkType := range benchmarkTypes.BenchmarkType {
if task.BenchmarkTypeID == benchmarkType.Id {
ctx.Data["BenchmarkTypeName"] = benchmarkType.First
for _, benchmarkChildType := range benchmarkType.Second {
if task.BenchmarkChildTypeID == benchmarkChildType.Id {
ctx.Data["BenchmarkChildTypeName"] = benchmarkChildType.Value
break
}
}
break
}
}
} }


ctx.Data["duration"] = util.AddZero(duration/3600000) + ":" + util.AddZero(duration%3600000/60000) + ":" + util.AddZero(duration%60000/1000)
ctx.Data["task"] = task ctx.Data["task"] = task
ctx.Data["jobID"] = jobID ctx.Data["jobID"] = jobID
ctx.HTML(200, tplCloudBrainShow)
ctx.Data["jobName"] = task.JobName
version_list_task := make([]*models.Cloudbrain, 0)
version_list_task = append(version_list_task, task)
ctx.Data["version_list_task"] = version_list_task
ctx.HTML(200, tpName)
} }


func CloudBrainDebug(ctx *context.Context) { func CloudBrainDebug(ctx *context.Context) {
@@ -393,7 +465,7 @@ func CloudBrainStop(ctx *context.Context) {


task := ctx.Cloudbrain task := ctx.Cloudbrain
for { for {
if task.Status == string(models.JobStopped) || task.Status == string(models.JobFailed) {
if task.Status == string(models.JobStopped) || task.Status == string(models.JobFailed) || task.Status == string(models.JobSucceeded) {
log.Error("the job(%s) has been stopped", task.JobName, ctx.Data["msgID"]) log.Error("the job(%s) has been stopped", task.JobName, ctx.Data["msgID"])
resultCode = "-1" resultCode = "-1"
errorMsg = "system error" errorMsg = "system error"
@@ -510,22 +582,31 @@ func logErrorAndUpdateJobStatus(err error, taskInfo *models.Cloudbrain) {
} }


func CloudBrainDel(ctx *context.Context) { func CloudBrainDel(ctx *context.Context) {
if err := deleteCloudbrainJob(ctx); err != nil {
log.Error("deleteCloudbrainJob failed: %v", err, ctx.Data["msgID"])
ctx.ServerError(err.Error(), err)
return
}
ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/debugjob?debugListType=all")
}

func deleteCloudbrainJob(ctx *context.Context) error {
task := ctx.Cloudbrain task := ctx.Cloudbrain


if task.Status != string(models.JobStopped) && task.Status != string(models.JobFailed) {
if task.Status != string(models.JobStopped) && task.Status != string(models.JobFailed) && task.Status != string(models.JobSucceeded) {
log.Error("the job(%s) has not been stopped", task.JobName, ctx.Data["msgID"]) log.Error("the job(%s) has not been stopped", task.JobName, ctx.Data["msgID"])
ctx.ServerError("the job has not been stopped", errors.New("the job has not been stopped"))
return
return errors.New("the job has not been stopped")
} }


err := models.DeleteJob(task) err := models.DeleteJob(task)
if err != nil { if err != nil {
ctx.ServerError("DeleteJob failed", err)
return
log.Error("DeleteJob failed: %v", err, ctx.Data["msgID"])
return err
} }


deleteJobStorage(task.JobName, models.TypeCloudBrainOne) deleteJobStorage(task.JobName, models.TypeCloudBrainOne)
ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/debugjob?debugListType=all")

return nil
} }


func CloudBrainShowModels(ctx *context.Context) { func CloudBrainShowModels(ctx *context.Context) {
@@ -632,6 +713,12 @@ func CloudBrainDownloadModel(ctx *context.Context) {
} }


func GetRate(ctx *context.Context) { func GetRate(ctx *context.Context) {
isObjectDetcionAll := ctx.QueryBool("isObjectDetcionAll")
if isObjectDetcionAll {
ctx.Redirect(setting.BenchmarkServerHost + "?username=admin")
return
}

var jobID = ctx.Params(":jobid") var jobID = ctx.Params(":jobid")
job, err := models.GetCloudbrainByJobID(jobID) job, err := models.GetCloudbrainByJobID(jobID)
if err != nil { if err != nil {
@@ -640,6 +727,7 @@ func GetRate(ctx *context.Context) {
} }


if job.JobType == string(models.JobTypeBenchmark) { if job.JobType == string(models.JobTypeBenchmark) {
log.Info("url=" + setting.BenchmarkServerHost + "?username=" + ctx.User.Name)
ctx.Redirect(setting.BenchmarkServerHost + "?username=" + ctx.User.Name) ctx.Redirect(setting.BenchmarkServerHost + "?username=" + ctx.User.Name)
} else if job.JobType == string(models.JobTypeSnn4imagenet) { } else if job.JobType == string(models.JobTypeSnn4imagenet) {
ctx.Redirect(setting.Snn4imagenetServerHost) ctx.Redirect(setting.Snn4imagenetServerHost)
@@ -855,7 +943,14 @@ func SyncCloudbrainStatus() {
log.Error("UpdateJob(%s) failed:%v", task.JobName, err) log.Error("UpdateJob(%s) failed:%v", task.JobName, err)
} }


if task.Duration >= setting.MaxDuration {
var maxDuration int64
if task.JobType == string(models.JobTypeBenchmark) {
maxDuration = setting.BenchmarkMaxDuration
} else {
maxDuration = setting.MaxDuration
}

if task.Duration >= maxDuration {
log.Info("begin to stop job(%s), because of the duration", task.JobName) log.Info("begin to stop job(%s), because of the duration", task.JobName)
err = cloudbrain.StopJob(task.JobID) err = cloudbrain.StopJob(task.JobID)
if err != nil { if err != nil {
@@ -923,3 +1018,329 @@ func SyncCloudbrainStatus() {


return return
} }

func CloudBrainBenchmarkIndex(ctx *context.Context) {
MustEnableCloudbrain(ctx)
repo := ctx.Repo.Repository
page := ctx.QueryInt("page")
if page <= 0 {
page = 1
}

var jobTypes []string
jobTypes = append(jobTypes, string(models.JobTypeBenchmark))
ciTasks, count, err := models.Cloudbrains(&models.CloudbrainsOptions{
ListOptions: models.ListOptions{
Page: page,
PageSize: setting.UI.IssuePagingNum,
},
RepoID: repo.ID,
Type: models.TypeCloudBrainOne,
JobTypes: jobTypes,
})
if err != nil {
ctx.ServerError("Get debugjob faild:", err)
return
}

for i, task := range ciTasks {
ciTasks[i].CanDel = cloudbrain.CanDeleteJob(ctx, &task.Cloudbrain)
ciTasks[i].Cloudbrain.ComputeResource = task.ComputeResource
var duration int64
if task.Status == string(models.JobRunning) {
duration = time.Now().Unix() - int64(task.Cloudbrain.CreatedUnix)
} else {
duration = int64(task.Cloudbrain.UpdatedUnix) - int64(task.Cloudbrain.CreatedUnix)
}
ciTasks[i].TrainJobDuration = util.AddZero(duration/3600000) + ":" + util.AddZero(duration%3600000/60000) + ":" + util.AddZero(duration%60000/1000)
}

pager := context.NewPagination(int(count), setting.UI.IssuePagingNum, page, 5)

ctx.Data["Page"] = pager
ctx.Data["PageIsCloudBrain"] = true
ctx.Data["Tasks"] = ciTasks
ctx.Data["CanCreate"] = cloudbrain.CanCreateOrDebugJob(ctx)
ctx.Data["RepoIsEmpty"] = repo.IsEmpty
ctx.HTML(200, tplCloudBrainBenchmarkIndex)
}

func GetChildTypes(ctx *context.Context) {
benchmarkTypeID := ctx.QueryInt("benchmark_type_id")
re := make(map[string]interface{})
for {
if benchmarkTypes == nil {
if err := json.Unmarshal([]byte(setting.BenchmarkTypes), &benchmarkTypes); err != nil {
log.Error("json.Unmarshal BenchmarkTypes(%s) failed:%v", setting.BenchmarkTypes, err, ctx.Data["MsgID"])
re["errMsg"] = "system error"
break
}
}
var isExist bool
for _, benchmarkType := range benchmarkTypes.BenchmarkType {
if benchmarkTypeID == benchmarkType.Id {
isExist = true
re["child_types"] = benchmarkType.Second
re["result_code"] = "0"
break
}
}
if !isExist {
re["result_code"] = "1"
log.Error("no such benchmark_type_id", ctx.Data["MsgID"])
re["errMsg"] = "system error"
break
}
break
}
ctx.JSON(200, re)
}

func CloudBrainBenchmarkNew(ctx *context.Context) {
err := cloudBrainNewDataPrepare(ctx)
if err != nil {
ctx.ServerError("get new cloudbrain info failed", err)
return
}
ctx.HTML(200, tplCloudBrainBenchmarkNew)
}

func getBenchmarkAttachment(benchmarkTypeID, benchmarkChildTypeID int) (*models.BenchmarkDataset, error) {
var childInfo *models.BenchmarkDataset
if benchmarkTypes == nil {
if err := json.Unmarshal([]byte(setting.BenchmarkTypes), &benchmarkTypes); err != nil {
log.Error("json.Unmarshal BenchmarkTypes(%s) failed:%v", setting.BenchmarkTypes, err)
return childInfo, err
}
}

var isExist bool
for _, benchmarkType := range benchmarkTypes.BenchmarkType {
if benchmarkType.Id == benchmarkTypeID {
for _, childType := range benchmarkType.Second {
if childType.Id == benchmarkChildTypeID {
childInfo = childType
isExist = true
break
}
}
break
}
}

if !isExist {
log.Error("no such benchmark_type_id&benchmark_child_type_id")
return childInfo, errors.New("no such benchmark_type_id&benchmark_child_type_id")
}

return childInfo, nil
}

func getBenchmarkGpuQueue(gpuQueue string) (string, error) {
queue := ""
if benchmarkGpuInfos == nil {
if err := json.Unmarshal([]byte(setting.BenchmarkGpuTypes), &benchmarkGpuInfos); err != nil {
log.Error("json.Unmarshal BenchmarkGpuTypes(%s) failed:%v", setting.BenchmarkGpuTypes, err)
return queue, err
}
}

var isExist bool
for _, gpuInfo := range benchmarkGpuInfos.GpuInfo {
if gpuQueue == gpuInfo.Queue {
isExist = true
queue = gpuQueue
break
}
}

if !isExist {
log.Error("no such gpuQueue, %s", gpuQueue)
return queue, errors.New("no such gpuQueue")
}

return queue, nil
}

func getBenchmarkResourceSpec(resourceSpecID int) (int, error) {
var id int
if benchmarkResourceSpecs == nil {
if err := json.Unmarshal([]byte(setting.BenchmarkResourceSpecs), &benchmarkResourceSpecs); err != nil {
log.Error("json.Unmarshal BenchmarkResourceSpecs(%s) failed:%v", setting.BenchmarkResourceSpecs, err)
return id, err
}
}

var isExist bool
for _, resourceSpec := range benchmarkResourceSpecs.ResourceSpec {
if resourceSpecID == resourceSpec.Id {
isExist = true
id = resourceSpecID
break
}
}

if !isExist {
log.Error("no such resourceSpecID, %d", resourceSpecID)
return id, errors.New("no such resourceSpec")
}

return id, nil
}

func CloudBrainBenchmarkCreate(ctx *context.Context, form auth.CreateCloudBrainForm) {
ctx.Data["PageIsCloudBrain"] = true
jobName := form.JobName
image := form.Image
gpuQueue := form.GpuType
command := cloudbrain.CommandBenchmark
codePath := setting.JobPath + jobName + cloudbrain.CodeMountPath
resourceSpecId := cloudbrain.BenchMarkResourceID
benchmarkTypeID := form.BenchmarkTypeID
benchmarkChildTypeID := form.BenchmarkChildTypeID

if !jobNamePattern.MatchString(jobName) {
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr(ctx.Tr("repo.cloudbrain_jobname_err"), tplCloudBrainBenchmarkNew, &form)
return
}

childInfo, err := getBenchmarkAttachment(benchmarkTypeID, benchmarkChildTypeID)
if err != nil {
log.Error("getBenchmarkAttachment failed:%v", err, ctx.Data["MsgID"])
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr("benchmark type error", tplCloudBrainBenchmarkNew, &form)
return
}

_, err = getBenchmarkGpuQueue(gpuQueue)
if err != nil {
log.Error("getBenchmarkGpuQueue failed:%v", err, ctx.Data["MsgID"])
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr("gpu queue error", tplCloudBrainBenchmarkNew, &form)
return
}

_, err = getBenchmarkResourceSpec(resourceSpecId)
if err != nil {
log.Error("getBenchmarkResourceSpec failed:%v", err, ctx.Data["MsgID"])
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr("resource spec error", tplCloudBrainBenchmarkNew, &form)
return
}

count, err := models.GetCloudbrainCountByUserID(ctx.User.ID, string(models.JobTypeBenchmark))
if err != nil {
log.Error("GetCloudbrainCountByUserID failed:%v", err, ctx.Data["MsgID"])
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr("system error", tplCloudBrainBenchmarkNew, &form)
return
} else {
if count >= 1 {
log.Error("the user already has running or waiting task", ctx.Data["MsgID"])
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr("you have already a running or waiting task, can not create more", tplCloudBrainBenchmarkNew, &form)
return
}
}

_, err = models.GetCloudbrainByName(jobName)
if err == nil {
log.Error("the job name did already exist", ctx.Data["MsgID"])
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr("the job name did already exist", tplCloudBrainBenchmarkNew, &form)
return
} else {
if !models.IsErrJobNotExist(err) {
log.Error("GetCloudbrainByName failed, %v", err, ctx.Data["MsgID"])
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr("system error", tplCloudBrainBenchmarkNew, &form)
return
}
}
repo := ctx.Repo.Repository
os.RemoveAll(codePath)
if err := downloadCode(repo, codePath); err != nil {
log.Error("downloadCode failed, %v", err, ctx.Data["MsgID"])
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr("system error", tplCloudBrainBenchmarkNew, &form)
return
}

if _, err := os.Stat(codePath + "/train.py"); err != nil {
if os.IsNotExist(err) {
// file does not exist
log.Error("train.py does not exist, %v", err, ctx.Data["MsgID"])
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr("train.py does not exist", tplCloudBrainBenchmarkNew, &form)
} else {
log.Error("Stat failed, %v", err, ctx.Data["MsgID"])
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr("system error", tplCloudBrainBenchmarkNew, &form)
}
return
} else if _, err := os.Stat(codePath + "/test.py"); err != nil {
if os.IsNotExist(err) {
// file does not exist
log.Error("test.py does not exist, %v", err, ctx.Data["MsgID"])
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr("test.py does not exist", tplCloudBrainBenchmarkNew, &form)
} else {
log.Error("Stat failed, %v", err, ctx.Data["MsgID"])
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr("system error", tplCloudBrainBenchmarkNew, &form)
}
return
}

if err := uploadCodeToMinio(codePath+"/", jobName, cloudbrain.CodeMountPath+"/"); err != nil {
log.Error("uploadCodeToMinio failed, %v", err, ctx.Data["MsgID"])
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr("system error", tplCloudBrainBenchmarkNew, &form)
return
}

benchmarkPath := setting.JobPath + jobName + cloudbrain.BenchMarkMountPath
var gpuType string
for _, gpuInfo := range gpuInfos.GpuInfo {
if gpuInfo.Queue == gpuQueue {
gpuType = gpuInfo.Value
}
}

if err := downloadRateCode(repo, jobName, childInfo.Owner, childInfo.RepoName, benchmarkPath, form.BenchmarkCategory, gpuType); err != nil {
log.Error("downloadRateCode failed, %v", err, ctx.Data["MsgID"])
//cloudBrainNewDataPrepare(ctx)
//ctx.RenderWithErr("system error", tplCloudBrainBenchmarkNew, &form)
//return
}

if err := uploadCodeToMinio(benchmarkPath+"/", jobName, cloudbrain.BenchMarkMountPath+"/"); err != nil {
log.Error("uploadCodeToMinio failed, %v", err, ctx.Data["MsgID"])
//cloudBrainNewDataPrepare(ctx)
//ctx.RenderWithErr("system error", tplCloudBrainBenchmarkNew, &form)
//return
}

err = cloudbrain.GenerateTask(ctx, jobName, image, command, childInfo.Attachment, storage.GetMinioPath(jobName, cloudbrain.CodeMountPath+"/"),
storage.GetMinioPath(jobName, cloudbrain.ModelMountPath+"/"),
storage.GetMinioPath(jobName, cloudbrain.BenchMarkMountPath+"/"), storage.GetMinioPath(jobName, cloudbrain.Snn4imagenetMountPath+"/"),
storage.GetMinioPath(jobName, cloudbrain.BrainScoreMountPath+"/"), string(models.JobTypeBenchmark), gpuQueue, form.Description,
benchmarkTypeID, benchmarkChildTypeID, resourceSpecId)
if err != nil {
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr(err.Error(), tplCloudBrainBenchmarkNew, &form)
return
}

ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/cloudbrain/benchmark")
}

func BenchmarkDel(ctx *context.Context) {
if err := deleteCloudbrainJob(ctx); err != nil {
log.Error("deleteCloudbrainJob failed: %v", err, ctx.Data["msgID"])
ctx.ServerError(err.Error(), err)
return
}
ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/cloudbrain/benchmark")
}

+ 196
- 0
routers/repo/course.go View File

@@ -0,0 +1,196 @@
package repo

import (
"net/http"
"strings"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
repo_service "code.gitea.io/gitea/services/repository"
)

const (
tplCreateCourse base.TplName = "repo/createCourse"
)

func CreateCourse(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("new_course")
org, _ := models.GetUserByName(setting.Course.OrgName)

ctx.Data["Owner"] = org
ctx.Data["IsCourse"] = true

ctx.HTML(200, tplCreateCourse)

}

func CreateCoursePost(ctx *context.Context, form auth.CreateCourseForm) {
ctx.Data["Title"] = ctx.Tr("new_course")

if ctx.Written() {
return
}

org, _ := models.GetUserByName(setting.Course.OrgName)

ctx.Data["Owner"] = org
ctx.Data["IsCourse"] = true

var topics = make([]string, 0)
var topicsStr = strings.TrimSpace(form.Topics)
if len(topicsStr) > 0 {
topics = strings.Split(topicsStr, ",")
}

validTopics, invalidTopics := models.SanitizeAndValidateTopics(topics)

if len(validTopics) > 25 {
ctx.RenderWithErr(ctx.Tr("repo.topic.count_prompt"), tplCreateCourse, form)
return
}

if len(invalidTopics) > 0 {
ctx.RenderWithErr(ctx.Tr("repo.topic.format_prompt"), tplCreateCourse, form)
return
}

var repo *models.Repository
var err error

if setting.Course.OrgName == "" || setting.Course.TeamName == "" {
log.Error("then organization name or team name of course is empty.")
ctx.RenderWithErr(ctx.Tr("repo.failed_to_create_course"), tplCreateCourse, form)
return
}

org, team, err := getOrgAndTeam()

if err != nil {
log.Error("Failed to get team from db.", err)
ctx.RenderWithErr(ctx.Tr("repo.failed_to_create_course"), tplCreateCourse, form)
return
}
isInTeam, err := models.IsUserInTeams(ctx.User.ID, []int64{team.ID})
if err != nil {
log.Error("Failed to get user in team from db.")
ctx.RenderWithErr(ctx.Tr("repo.failed_to_create_course"), tplCreateCourse, form)
return
}

if !isInTeam {
err = models.AddTeamMember(team, ctx.User.ID)
if err != nil {
log.Error("Failed to add user to team.")
ctx.RenderWithErr(ctx.Tr("repo.failed_to_create_course"), tplCreateCourse, form)
return
}
}

if ctx.HasError() {
ctx.HTML(200, tplCreateCourse)
return
}

repo, err = repo_service.CreateRepository(ctx.User, org, models.CreateRepoOptions{
Name: form.RepoName,
Alias: form.Alias,
Description: form.Description,
Gitignores: "",
IssueLabels: "",
License: "",
Readme: "Default",
IsPrivate: false,
DefaultBranch: "master",
AutoInit: true,
IsCourse: true,
Topics: validTopics,
})
if err == nil {
log.Trace("Repository created [%d]: %s/%s", repo.ID, org.Name, repo.Name)
ctx.Redirect(setting.AppSubURL + "/" + org.Name + "/" + repo.Name)
return
}

handleCreateCourseError(ctx, org, err, "CreateCoursePost", tplCreateCourse, &form)
}

func AddCourseOrg(ctx *context.Context) {

_, team, err := getOrgAndTeam()

if err != nil {
log.Error("Failed to get team from db.", err)
ctx.JSON(http.StatusOK, map[string]interface{}{
"code": 1,
"message": ctx.Tr("repo.addCourseOrgFail"),
})
return
}
isInTeam, err := models.IsUserInTeams(ctx.User.ID, []int64{team.ID})
if err != nil {
log.Error("Failed to get user in team from db.", err)
ctx.JSON(http.StatusOK, map[string]interface{}{
"code": 1,
"message": ctx.Tr("repo.add_course_org_fail"),
})
return
}

if !isInTeam {
err = models.AddTeamMember(team, ctx.User.ID)
if err != nil {
log.Error("Failed to add user to team.", err)
ctx.JSON(http.StatusOK, map[string]interface{}{
"code": 1,
"message": ctx.Tr("repo.add_course_org_fail"),
})
return
}
}

ctx.JSON(http.StatusOK, map[string]interface{}{
"code": 0,
"message": "",
})

}

func getOrgAndTeam() (*models.User, *models.Team, error) {
org, err := models.GetUserByName(setting.Course.OrgName)

if err != nil {
log.Error("Failed to get organization from db.", err)
return nil, nil, err
}

team, err := models.GetTeam(org.ID, setting.Course.TeamName)

if err != nil {
log.Error("Failed to get team from db.", err)

return nil, nil, err
}
return org, team, nil
}

func handleCreateCourseError(ctx *context.Context, owner *models.User, err error, name string, tpl base.TplName, form interface{}) {
switch {
case models.IsErrReachLimitOfRepo(err):
ctx.RenderWithErr(ctx.Tr("repo.form.reach_limit_of_course_creation", owner.MaxCreationLimit()), tpl, form)
case models.IsErrRepoAlreadyExist(err):
ctx.Data["Err_RepoName"] = true
ctx.RenderWithErr(ctx.Tr("form.course_name_been_taken"), tpl, form)
case models.IsErrNameReserved(err):
ctx.Data["Err_RepoName"] = true
ctx.RenderWithErr(ctx.Tr("repo.form.course_name_reserved", err.(models.ErrNameReserved).Name), tpl, form)
case models.IsErrNamePatternNotAllowed(err):
ctx.Data["Err_RepoName"] = true
ctx.RenderWithErr(ctx.Tr("repo.form.course_name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tpl, form)
default:
ctx.ServerError(name, err)
}
}

+ 10
- 10
routers/repo/modelarts.go View File

@@ -64,7 +64,7 @@ func DebugJobIndex(ctx *context.Context) {
} }


var jobTypes []string var jobTypes []string
jobTypes = append(jobTypes, string(models.JobTypeBenchmark), string(models.JobTypeSnn4imagenet), string(models.JobTypeBrainScore), string(models.JobTypeDebug))
jobTypes = append(jobTypes, string(models.JobTypeSnn4imagenet), string(models.JobTypeBrainScore), string(models.JobTypeDebug))
ciTasks, count, err := models.Cloudbrains(&models.CloudbrainsOptions{ ciTasks, count, err := models.Cloudbrains(&models.CloudbrainsOptions{
ListOptions: models.ListOptions{ ListOptions: models.ListOptions{
Page: page, Page: page,
@@ -387,7 +387,7 @@ func TrainJobIndex(ctx *context.Context) {
RepoID: repo.ID, RepoID: repo.ID,
Type: models.TypeCloudBrainTwo, Type: models.TypeCloudBrainTwo,
JobTypeNot: false, JobTypeNot: false,
JobTypes: jobTypes,
JobTypes: jobTypes,
IsLatestVersion: modelarts.IsLatestVersion, IsLatestVersion: modelarts.IsLatestVersion,
}) })
if err != nil { if err != nil {
@@ -1321,10 +1321,10 @@ func TrainJobShow(ctx *context.Context) {
Page: page, Page: page,
PageSize: setting.UI.IssuePagingNum, PageSize: setting.UI.IssuePagingNum,
}, },
RepoID: repo.ID,
Type: models.TypeCloudBrainTwo,
RepoID: repo.ID,
Type: models.TypeCloudBrainTwo,
JobTypes: jobTypes, JobTypes: jobTypes,
JobID: jobID,
JobID: jobID,
}) })


if err != nil { if err != nil {
@@ -1438,10 +1438,10 @@ func TrainJobDel(ctx *context.Context) {
var jobTypes []string var jobTypes []string
jobTypes = append(jobTypes, string(models.JobTypeTrain)) jobTypes = append(jobTypes, string(models.JobTypeTrain))
VersionListTasks, _, err := models.CloudbrainsVersionList(&models.CloudbrainsOptions{ VersionListTasks, _, err := models.CloudbrainsVersionList(&models.CloudbrainsOptions{
RepoID: repo.ID,
Type: models.TypeCloudBrainTwo,
RepoID: repo.ID,
Type: models.TypeCloudBrainTwo,
JobTypes: jobTypes, JobTypes: jobTypes,
JobID: jobID,
JobID: jobID,
}) })
if err != nil { if err != nil {
ctx.ServerError("get VersionListTasks failed", err) ctx.ServerError("get VersionListTasks failed", err)
@@ -1747,8 +1747,8 @@ func InferenceJobIndex(ctx *context.Context) {
Page: page, Page: page,
PageSize: setting.UI.IssuePagingNum, PageSize: setting.UI.IssuePagingNum,
}, },
RepoID: repo.ID,
Type: models.TypeCloudBrainTwo,
RepoID: repo.ID,
Type: models.TypeCloudBrainTwo,
JobTypes: jobTypes, JobTypes: jobTypes,
}) })
if err != nil { if err != nil {


+ 2
- 1
routers/repo/pull.go View File

@@ -105,6 +105,7 @@ func getForkRepository(ctx *context.Context) *models.Repository {
return nil return nil
} }
ctx.Data["ForkFrom"] = forkRepo.Owner.Name + "/" + forkRepo.Name ctx.Data["ForkFrom"] = forkRepo.Owner.Name + "/" + forkRepo.Name
ctx.Data["ForkDisplayName"] = forkRepo.FullDisplayName()
ctx.Data["ForkFromOwnerID"] = forkRepo.Owner.ID ctx.Data["ForkFromOwnerID"] = forkRepo.Owner.ID


if err := ctx.User.GetOwnedOrganizations(); err != nil { if err := ctx.User.GetOwnedOrganizations(); err != nil {
@@ -221,7 +222,7 @@ func ForkPost(ctx *context.Context, form auth.CreateRepoForm) {
} }
} }


repo, err := repo_service.ForkRepository(ctx.User, ctxUser, forkRepo, form.RepoName, form.Description)
repo, err := repo_service.ForkRepository(ctx.User, ctxUser, forkRepo, form.RepoName, form.Description, form.Alias)
if err != nil { if err != nil {
ctx.Data["Err_RepoName"] = true ctx.Data["Err_RepoName"] = true
switch { switch {


+ 31
- 1
routers/repo/repo.go View File

@@ -6,11 +6,14 @@
package repo package repo


import ( import (
"code.gitea.io/gitea/modules/validation"
"fmt" "fmt"
"net/url" "net/url"
"os" "os"
"path" "path"
"regexp"
"strings" "strings"
"unicode/utf8"


"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/auth"
@@ -201,6 +204,7 @@ func CreatePost(ctx *context.Context, form auth.CreateRepoForm) {
if form.RepoTemplate > 0 { if form.RepoTemplate > 0 {
opts := models.GenerateRepoOptions{ opts := models.GenerateRepoOptions{
Name: form.RepoName, Name: form.RepoName,
Alias: form.Alias,
Description: form.Description, Description: form.Description,
Private: form.Private, Private: form.Private,
GitContent: form.GitContent, GitContent: form.GitContent,
@@ -235,6 +239,7 @@ func CreatePost(ctx *context.Context, form auth.CreateRepoForm) {
} else { } else {
repo, err = repo_service.CreateRepository(ctx.User, ctxUser, models.CreateRepoOptions{ repo, err = repo_service.CreateRepository(ctx.User, ctxUser, models.CreateRepoOptions{
Name: form.RepoName, Name: form.RepoName,
Alias: form.Alias,
Description: form.Description, Description: form.Description,
Gitignores: form.Gitignores, Gitignores: form.Gitignores,
IssueLabels: form.IssueLabels, IssueLabels: form.IssueLabels,
@@ -358,6 +363,7 @@ func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) {
GitServiceType: gitServiceType, GitServiceType: gitServiceType,
CloneAddr: remoteAddr, CloneAddr: remoteAddr,
RepoName: form.RepoName, RepoName: form.RepoName,
Alias: form.Alias,
Description: form.Description, Description: form.Description,
Private: form.Private || setting.Repository.ForcePrivate, Private: form.Private || setting.Repository.ForcePrivate,
Mirror: form.Mirror, Mirror: form.Mirror,
@@ -380,7 +386,7 @@ func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) {
opts.Releases = false opts.Releases = false
} }


err = models.CheckCreateRepository(ctx.User, ctxUser, opts.RepoName)
err = models.CheckCreateRepository(ctx.User, ctxUser, opts.RepoName, opts.Alias)
if err != nil { if err != nil {
handleMigrateError(ctx, ctxUser, err, "MigratePost", tplMigrate, &form) handleMigrateError(ctx, ctxUser, err, "MigratePost", tplMigrate, &form)
return return
@@ -552,3 +558,27 @@ func Status(ctx *context.Context) {
"err": task.Errors, "err": task.Errors,
}) })
} }

var repoNamePattern = regexp.MustCompile("^[0-9a-zA-Z\\.\\-_]{1,100}$")
var repoAliasPattern = regexp.MustCompile("^[\u4e00-\u9fa5\\.\\-_A-Za-z0-9]{1,100}$")

// CheckName returns repository's default name(by given alias)
func CheckName(ctx *context.Context) {
var r = make(map[string]string, 1)
q := ctx.Query("q")
owner := ctx.Query("owner")
if q == "" || owner == "" || utf8.RuneCountInString(q) > 100 || !validation.ValidAlphaDashDotChinese(q) {
r["name"] = ""
ctx.JSON(200, r)
return
}
if repoNamePattern.MatchString(q) {
r["name"] = q
ctx.JSON(200, r)
return
}
n := models.GenerateDefaultRepoName(owner)
r["name"] = n
ctx.JSON(200, r)
return
}

+ 36
- 4
routers/repo/setting.go View File

@@ -6,6 +6,7 @@
package repo package repo


import ( import (
"code.gitea.io/gitea/modules/notification"
"errors" "errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
@@ -70,6 +71,30 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) {
return return
} }


newAlias := form.Alias
var aliasChanged = false
// Check if repository alias has been changed.
if strings.ToLower(repo.Alias) != strings.ToLower(newAlias) {
aliasChanged = true
//check new alias is available or not
if err := models.IsRepositoryAliasAvailable(ctx.Repo.Owner, newAlias); err != nil {
ctx.Data["Err_Alias"] = true
switch {
case models.IsErrRepoAlreadyExist(err):
ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), tplSettingsOptions, &form)
case models.IsErrNameReserved(err):
ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(models.ErrNameReserved).Name), tplSettingsOptions, &form)
case models.IsErrNamePatternNotAllowed(err):
ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tplSettingsOptions, &form)
default:
ctx.ServerError("ChangeRepositoryName", err)
}
return
}

log.Trace("Repository alias changed: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Alias, newAlias)
}

newRepoName := form.RepoName newRepoName := form.RepoName
// Check if repository name has been changed. // Check if repository name has been changed.
if repo.LowerName != strings.ToLower(newRepoName) { if repo.LowerName != strings.ToLower(newRepoName) {
@@ -95,12 +120,19 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) {


log.Trace("Repository name changed: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newRepoName) log.Trace("Repository name changed: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newRepoName)
} }
//notify
if aliasChanged {
notification.NotifyRenameRepository(ctx.Repo.Owner, repo, repo.Alias)
}

// In case it's just a case change. // In case it's just a case change.
repo.Name = newRepoName repo.Name = newRepoName
repo.LowerName = strings.ToLower(newRepoName) repo.LowerName = strings.ToLower(newRepoName)
repo.Description = form.Description repo.Description = form.Description
repo.Website = form.Website repo.Website = form.Website
repo.IsTemplate = form.Template repo.IsTemplate = form.Template
repo.Alias = newAlias
repo.LowerAlias = strings.ToLower(newAlias)


// Visibility of forked repository is forced sync with base repository. // Visibility of forked repository is forced sync with base repository.
if repo.IsFork { if repo.IsFork {
@@ -380,7 +412,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) {
ctx.Error(404) ctx.Error(404)
return return
} }
if repo.Name != form.RepoName {
if repo.Alias != form.RepoName {
ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil) ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil)
return return
} }
@@ -407,7 +439,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) {
ctx.Error(404) ctx.Error(404)
return return
} }
if repo.Name != form.RepoName {
if repo.Alias != form.RepoName {
ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil) ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil)
return return
} }
@@ -445,7 +477,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) {
ctx.Error(404) ctx.Error(404)
return return
} }
if repo.Name != form.RepoName {
if repo.Alias != form.RepoName {
ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil) ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil)
return return
} }
@@ -465,7 +497,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) {
ctx.Error(404) ctx.Error(404)
return return
} }
if repo.Name != form.RepoName {
if repo.Alias != form.RepoName {
ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil) ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil)
return return
} }


+ 9
- 9
routers/repo/user_data_analysis.go View File

@@ -88,22 +88,22 @@ func queryUserDataPage(ctx *context.Context, tableName string, queryObj interfac
xlsx.SetCellValue(sheetName, "P"+rows, formatTime[0:len(formatTime)-3]) xlsx.SetCellValue(sheetName, "P"+rows, formatTime[0:len(formatTime)-3])


formatTime = userRecord.DataDate formatTime = userRecord.DataDate
xlsx.SetCellValue(sheetName, "Q"+rows, formatTime+" 00:01")
xlsx.SetCellValue(sheetName, "Q"+rows, formatTime)
} }


//设置默认打开的表单
xlsx.SetActiveSheet(index)
filename := sheetName + "_" + ctx.Tr("user.static."+tableName) + ".xlsx"
ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+url.QueryEscape(filename))
ctx.Resp.Header().Set("Content-Type", "application/octet-stream")
if _, err := xlsx.WriteTo(ctx.Resp); err != nil {
log.Info("writer exel error." + err.Error())
}
indexTotal += PAGE_SIZE indexTotal += PAGE_SIZE
if indexTotal >= count { if indexTotal >= count {
break break
} }
} }
//设置默认打开的表单
xlsx.SetActiveSheet(index)
filename := sheetName + "_" + ctx.Tr("user.static."+tableName) + ".xlsx"
ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+url.QueryEscape(filename))
ctx.Resp.Header().Set("Content-Type", "application/octet-stream")
if _, err := xlsx.WriteTo(ctx.Resp); err != nil {
log.Info("writer exel error." + err.Error())
}
} else { } else {
re, count := models.QueryUserStaticDataByTableName((page-1)*pageSize, pageSize, tableName, queryObj, userName) re, count := models.QueryUserStaticDataByTableName((page-1)*pageSize, pageSize, tableName, queryObj, userName)
mapInterface := make(map[string]interface{}) mapInterface := make(map[string]interface{})


+ 7
- 1
routers/repo/view.go View File

@@ -35,6 +35,7 @@ import (
const ( const (
tplRepoEMPTY base.TplName = "repo/empty" tplRepoEMPTY base.TplName = "repo/empty"
tplRepoHome base.TplName = "repo/home" tplRepoHome base.TplName = "repo/home"
tplCourseHome base.TplName = "repo/courseHome"
tplWatchers base.TplName = "repo/watchers" tplWatchers base.TplName = "repo/watchers"
tplForks base.TplName = "repo/forks" tplForks base.TplName = "repo/forks"
tplMigrating base.TplName = "repo/migrating" tplMigrating base.TplName = "repo/migrating"
@@ -855,7 +856,12 @@ func renderCode(ctx *context.Context) {
ctx.Data["TreeLink"] = treeLink ctx.Data["TreeLink"] = treeLink
ctx.Data["TreeNames"] = treeNames ctx.Data["TreeNames"] = treeNames
ctx.Data["BranchLink"] = branchLink ctx.Data["BranchLink"] = branchLink
ctx.HTML(200, tplRepoHome)
if ctx.Repo.Repository.RepoType == models.RepoCourse {
ctx.HTML(200, tplCourseHome)
} else {
ctx.HTML(200, tplRepoHome)
}

} }


// RenderUserCards render a page show users according the input templaet // RenderUserCards render a page show users according the input templaet


+ 21
- 0
routers/routes/routes.go View File

@@ -708,6 +708,13 @@ func RegisterRoutes(m *macaron.Macaron) {
}, reqSignIn) }, reqSignIn)
// ***** END: Organization ***** // ***** END: Organization *****


m.Group("/course", func() {
m.Get("/create", repo.CreateCourse)
m.Post("/create", bindIgnErr(auth.CreateCourseForm{}), repo.CreateCoursePost)
m.Get("/addOrg", repo.AddCourseOrg)

}, reqSignIn)

// ***** START: Repository ***** // ***** START: Repository *****
m.Group("/repo", func() { m.Group("/repo", func() {
m.Get("/create", repo.Create) m.Get("/create", repo.Create)
@@ -718,6 +725,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Combo("/:repoid").Get(repo.Fork). m.Combo("/:repoid").Get(repo.Fork).
Post(bindIgnErr(auth.CreateRepoForm{}), repo.ForkPost) Post(bindIgnErr(auth.CreateRepoForm{}), repo.ForkPost)
}, context.RepoIDAssignment(), context.UnitTypes(), reqRepoCodeReader) }, context.RepoIDAssignment(), context.UnitTypes(), reqRepoCodeReader)
m.Get("/check_name", repo.CheckName)
}, reqSignIn) }, reqSignIn)


// ***** Release Attachment Download without Signin // ***** Release Attachment Download without Signin
@@ -979,6 +987,19 @@ func RegisterRoutes(m *macaron.Macaron) {
}) })
m.Get("/create", reqRepoCloudBrainWriter, repo.CloudBrainNew) m.Get("/create", reqRepoCloudBrainWriter, repo.CloudBrainNew)
m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateCloudBrainForm{}), repo.CloudBrainCreate) m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateCloudBrainForm{}), repo.CloudBrainCreate)

m.Group("/benchmark", func() {
m.Get("", reqRepoCloudBrainReader, repo.CloudBrainBenchmarkIndex)
m.Group("/:jobid", func() {
m.Get("", reqRepoCloudBrainReader, repo.CloudBrainBenchMarkShow)
m.Post("/stop", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.CloudBrainStop)
m.Post("/del", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.BenchmarkDel)
m.Get("/rate", reqRepoCloudBrainReader, repo.GetRate)
})
m.Get("/create", reqRepoCloudBrainWriter, repo.CloudBrainBenchmarkNew)
m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateCloudBrainForm{}), repo.CloudBrainBenchmarkCreate)
m.Get("/get_child_types", repo.GetChildTypes)
})
}, context.RepoRef()) }, context.RepoRef())
m.Group("/modelmanage", func() { m.Group("/modelmanage", func() {
m.Post("/create_model", reqRepoModelManageWriter, repo.SaveModel) m.Post("/create_model", reqRepoModelManageWriter, repo.SaveModel)


+ 89
- 3
services/repository/repository.go View File

@@ -5,12 +5,17 @@
package repository package repository


import ( import (
"fmt"
"io/ioutil"
"net/http"
"strings"

"code.gitea.io/gitea/models" "code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification" "code.gitea.io/gitea/modules/notification"
repo_module "code.gitea.io/gitea/modules/repository" repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
pull_service "code.gitea.io/gitea/services/pull" pull_service "code.gitea.io/gitea/services/pull"
"fmt"
) )


// CreateRepository creates a repository for the user/organization. // CreateRepository creates a repository for the user/organization.
@@ -31,8 +36,8 @@ func CreateRepository(doer, owner *models.User, opts models.CreateRepoOptions) (
} }


// ForkRepository forks a repository // ForkRepository forks a repository
func ForkRepository(doer, u *models.User, oldRepo *models.Repository, name, desc string) (*models.Repository, error) {
repo, err := repo_module.ForkRepository(doer, u, oldRepo, name, desc)
func ForkRepository(doer, u *models.User, oldRepo *models.Repository, name, desc, alias string) (*models.Repository, error) {
repo, err := repo_module.ForkRepository(doer, u, oldRepo, name, desc, alias)
if err != nil { if err != nil {
if repo != nil { if repo != nil {
if errDelete := models.DeleteRepository(doer, u.ID, repo.ID); errDelete != nil { if errDelete := models.DeleteRepository(doer, u.ID, repo.ID); errDelete != nil {
@@ -86,3 +91,84 @@ func PushCreateRepo(authUser, owner *models.User, repoName string) (*models.Repo


return repo, nil return repo, nil
} }

func GetRecommendCourseKeyWords() ([]string, error) {

url := setting.RecommentRepoAddr + "course_keywords"
result, err := RecommendFromPromote(url)

if err != nil {
return []string{}, err
}
return result, err

}

func GetRecommendRepoFromPromote(filename string) ([]map[string]interface{}, error) {
resultRepo := make([]map[string]interface{}, 0)
url := setting.RecommentRepoAddr + filename
result, err := RecommendFromPromote(url)

if err != nil {

return resultRepo, err
}

//resultRepo := make([]*models.Repository, 0)
for _, repoName := range result {
tmpIndex := strings.Index(repoName, "/")
if tmpIndex == -1 {
log.Info("error repo name format.")
} else {
ownerName := strings.Trim(repoName[0:tmpIndex], " ")
repoName := strings.Trim(repoName[tmpIndex+1:], " ")
repo, err := models.GetRepositoryByOwnerAndAlias(ownerName, repoName)
if err == nil {
repoMap := make(map[string]interface{})
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
repoMap["NumForks"] = repo.NumForks
repoMap["Description"] = repo.Description
repoMap["NumWatchs"] = repo.NumWatches
repoMap["Topics"] = repo.Topics
repoMap["Avatar"] = repo.RelAvatarLink()
resultRepo = append(resultRepo, repoMap)
} else {
log.Info("query repo error," + err.Error())
}
}
}
return resultRepo, nil
}

func RecommendFromPromote(url string) ([]string, error) {
resp, err := http.Get(url)
if err != nil || resp.StatusCode != 200 {
log.Info("Get organizations url error=" + err.Error())
return nil, err
}
bytes, err := ioutil.ReadAll(resp.Body)
resp.Body.Close()
if err != nil {
log.Info("Get organizations url error=" + err.Error())
return nil, err
}

allLineStr := string(bytes)
lines := strings.Split(allLineStr, "\n")
result := make([]string, len(lines))
for i, line := range lines {
log.Info("i=" + fmt.Sprint(i) + " line=" + line)
result[i] = strings.Trim(line, " ")
}
return result, nil
}

+ 2
- 2
services/repository/transfer.go View File

@@ -55,7 +55,7 @@ func TransferOwnership(doer, newOwner *models.User, repo *models.Repository, tea


// ChangeRepositoryName changes all corresponding setting from old repository name to new one. // ChangeRepositoryName changes all corresponding setting from old repository name to new one.
func ChangeRepositoryName(doer *models.User, repo *models.Repository, newRepoName string) error { func ChangeRepositoryName(doer *models.User, repo *models.Repository, newRepoName string) error {
oldRepoName := repo.Name
//oldRepoName := repo.Name


// Change repository directory name. We must lock the local copy of the // Change repository directory name. We must lock the local copy of the
// repo so that we can atomically rename the repo path and updates the // repo so that we can atomically rename the repo path and updates the
@@ -68,7 +68,7 @@ func ChangeRepositoryName(doer *models.User, repo *models.Repository, newRepoNam
} }
repoWorkingPool.CheckOut(com.ToStr(repo.ID)) repoWorkingPool.CheckOut(com.ToStr(repo.ID))


notification.NotifyRenameRepository(doer, repo, oldRepoName)
//notification.NotifyRenameRepository(doer, repo, oldRepoName)


return nil return nil
} }

+ 1
- 0
services/socketwrap/clientManager.go View File

@@ -50,6 +50,7 @@ func (h *ClientsManager) Run() {
} }
case message := <-models.ActionChan: case message := <-models.ActionChan:
if isInOpTypes(opTypes, message.OpType) { if isInOpTypes(opTypes, message.OpType) {
message.Comment = nil
LastActionsQueue.Push(message) LastActionsQueue.Push(message)
for _, client := range h.Clients.Keys() { for _, client := range h.Clients.Keys() {
select { select {


+ 1
- 1
templates/admin/repo/list.tmpl View File

@@ -36,7 +36,7 @@
<span class="text gold">{{svg "octicon-lock" 16}}</span> <span class="text gold">{{svg "octicon-lock" 16}}</span>
{{end}} {{end}}
</td> </td>
<td><a href="{{AppSubUrl}}/{{.Owner.Name}}/{{.Name}}">{{.Name}}</a></td>
<td><a href="{{AppSubUrl}}/{{.Owner.Name}}/{{.Name}}">{{.Alias}}</a></td>
<td><i class="fa fa{{if .IsPrivate}}-check{{end}}-square-o"></i></td> <td><i class="fa fa{{if .IsPrivate}}-check{{end}}-square-o"></i></td>
<td>{{.NumWatches}}</td> <td>{{.NumWatches}}</td>
<td>{{.NumStars}}</td> <td>{{.NumStars}}</td>


+ 14
- 10
templates/base/head.tmpl View File

@@ -200,14 +200,16 @@ var _hmt = _hmt || [];
<div class="ui top secondary stackable main menu following bar dark"> <div class="ui top secondary stackable main menu following bar dark">
{{template "base/head_navbar" .}} {{template "base/head_navbar" .}}
</div><!-- end bar --> </div><!-- end bar -->
<div class="notic_content" id ="notic_content" >
<a href={{.notice.Link}} class="a_width">
<marquee behavior="scroll" direction="left">
{{.notice.Title}}
</marquee>
</a>
<i class="ri-close-fill x_icon" onclick="closeNoice()"></i>
</div>
<!-- {{if not .IsCourse}} -->
<div class="notic_content" id ="notic_content" style="display: none;">
<a href={{.notice.Link}} class="a_width">
<marquee behavior="scroll" direction="left">
{{.notice.Title}}
</marquee>
</a>
<i class="ri-close-fill x_icon" onclick="closeNoice()"></i>
</div>
<!-- {{end}} -->
{{end}} {{end}}
{{/* {{/*
</div> </div>
@@ -247,5 +249,7 @@ var _hmt = _hmt || [];
document.getElementById("notic_content").style.display='none' document.getElementById("notic_content").style.display='none'
} }
} }
isShowNotice();
</script>
if(!("{{.IsCourse}}" == true || "{{.IsCourse}}" =='true')) {
isShowNotice();
}
</script>

+ 209
- 0
templates/base/head_course.tmpl View File

@@ -0,0 +1,209 @@
<!DOCTYPE html>
<html lang="{{.Language}}">
<head data-suburl="{{AppSubUrl}}">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>{{if .Title}}{{.Title}} - {{end}} {{if .Repository.Name}}{{.Repository.Name}} - {{end}}{{AppName}}</title>
<link rel="manifest" href="{{AppSubUrl}}/manifest.json" crossorigin="use-credentials">
{{if UseServiceWorker}}
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('{{AppSubUrl}}/serviceworker.js').then(function(registration) {
// Registration was successful
console.info('ServiceWorker registration successful with scope: ', registration.scope);
}, function(err) {
// registration failed :(
console.info('ServiceWorker registration failed: ', err);
});
}
</script>
{{else}}
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.getRegistrations().then(function(registrations) {
registrations.forEach(function(registration) {
registration.unregister();
console.info('ServiceWorker unregistered');
});
});
}
</script>
{{end}}
<meta name="theme-color" content="{{ThemeColorMetaTag}}">
<meta name="author" content="{{if .Repository}}{{.Owner.Name}}{{else}}{{MetaAuthor}}{{end}}" />
<meta name="description" content="{{if .Repository}}{{.Repository.Name}}{{if .Repository.Description}} - {{.Repository.Description}}{{end}}{{else}}{{MetaDescription}}{{end}}" />
<meta name="keywords" content="{{MetaKeywords}}">
<meta name="referrer" content="no-referrer" />
<meta name="_csrf" content="{{.CsrfToken}}" />
{{if .IsSigned}}
<meta name="_uid" content="{{.SignedUser.ID}}" />
{{end}}
{{if .ContextUser}}
<meta name="_context_uid" content="{{.ContextUser.ID}}" />
{{end}}
{{if .SearchLimit}}
<meta name="_search_limit" content="{{.SearchLimit}}" />
{{end}}
{{if .GoGetImport}}
<meta name="go-import" content="{{.GoGetImport}} git {{.CloneLink.HTTPS}}">
<meta name="go-source" content="{{.GoGetImport}} _ {{.GoDocDirectory}} {{.GoDocFile}}">
{{end}}
<script>
{{SafeJS `/*
@licstart The following is the entire license notice for the
JavaScript code in this page.

Copyright (c) 2016 The Gitea Authors
Copyright (c) 2015 The Gogs Authors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
---
Licensing information for additional javascript libraries can be found at:
{{StaticUrlPrefix}}/vendor/librejs.html

@licend The above is the entire license notice
for the JavaScript code in this page.
*/`}}
</script>
<script>
window.config = {
AppSubUrl: '{{AppSubUrl}}',
StaticUrlPrefix: '{{StaticUrlPrefix}}',
csrf: '{{.CsrfToken}}',
HighlightJS: {{if .RequireHighlightJS}}true{{else}}false{{end}},
Minicolors: {{if .RequireMinicolors}}true{{else}}false{{end}},
SimpleMDE: {{if .RequireSimpleMDE}}true{{else}}false{{end}},
Tribute: {{if .RequireTribute}}true{{else}}false{{end}},
U2F: {{if .RequireU2F}}true{{else}}false{{end}},
Heatmap: {{if .EnableHeatmap}}true{{else}}false{{end}},
heatmapUser: {{if .HeatmapUser}}'{{.HeatmapUser}}'{{else}}null{{end}},
NotificationSettings: {
MinTimeout: {{NotificationSettings.MinTimeout}},
TimeoutStep: {{NotificationSettings.TimeoutStep}},
MaxTimeout: {{NotificationSettings.MaxTimeout}},
EventSourceUpdateTime: {{NotificationSettings.EventSourceUpdateTime}},
},
{{if .RequireTribute}}
tributeValues: [
{{ range .Assignees }}
{key: '{{.Name}} {{.FullName}}', value: '{{.Name}}',
name: '{{.Name}}', fullname: '{{.FullName}}', avatar: '{{.RelAvatarLink}}'},
{{ end }}
],
{{end}}
};
</script>
<link rel="shortcut icon" href="{{StaticUrlPrefix}}/img/favicon.png">
<link rel="mask-icon" href="{{StaticUrlPrefix}}/img/openi-safari.svg" color="#609926">
<link rel="fluid-icon" href="{{StaticUrlPrefix}}/img/gitea-lg.png" title="{{AppName}}">
<link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/assets/font-awesome/css/font-awesome.min.css">
<link rel="preload" as="font" href="{{StaticUrlPrefix}}/fomantic/themes/default/assets/fonts/icons.woff2" type="font/woff2" crossorigin="anonymous">
<link rel="preload" as="font" href="{{StaticUrlPrefix}}/fomantic/themes/default/assets/fonts/outline-icons.woff2" type="font/woff2" crossorigin="anonymous">
{{if .RequireSimpleMDE}}
<link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/plugins/simplemde/simplemde.min.css">
{{end}}

{{if .RequireTribute}}
<link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/plugins/tribute/tribute.css">
{{end}}
<link rel="stylesheet" href="{{StaticUrlPrefix}}/fomantic/semantic.min.css?v={{MD5 AppVer}}">
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/index.css?v={{MD5 AppVer}}">
<noscript>
<style>
.dropdown:hover > .menu { display: block; }
.ui.secondary.menu .dropdown.item > .menu { margin-top: 0; }
</style>
</noscript>
{{if .RequireMinicolors}}
<link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/plugins/jquery.minicolors/jquery.minicolors.css">
{{end}}
<style class="list-search-style"></style>
{{if .PageIsUserProfile}}
<meta property="og:title" content="{{.Owner.Name}}" />
<meta property="og:type" content="profile" />
<meta property="og:image" content="{{.Owner.AvatarLink}}" />
<meta property="og:url" content="{{.Owner.HTMLURL}}" />
{{if .Owner.Description}}
<meta property="og:description" content="{{.Owner.Description}}">
{{end}}
{{else if .Repository}}
{{if .Issue}}
<meta property="og:title" content="{{.Issue.Title}}" />
<meta property="og:url" content="{{.Issue.HTMLURL}}" />
{{if .Issue.Content}}
<meta property="og:description" content="{{.Issue.Content}}" />
{{end}}
{{else}}
<meta property="og:title" content="{{.Repository.Name}}" />
<meta property="og:url" content="{{.Repository.HTMLURL}}" />
{{if .Repository.Description}}
<meta property="og:description" content="{{.Repository.Description}}" />
{{end}}
{{end}}
<meta property="og:type" content="object" />
<meta property="og:image" content="{{.Repository.Owner.AvatarLink}}" />
{{else}}
<meta property="og:title" content="{{AppName}}">
<meta property="og:type" content="website" />
<meta property="og:image" content="{{StaticUrlPrefix}}/img/gitea-lg.png" />
<meta property="og:url" content="{{AppUrl}}" />
<meta property="og:description" content="{{MetaDescription}}">
{{end}}
<meta property="og:site_name" content="{{AppName}}" />
{{if .IsSigned }}
{{ if ne .SignedUser.Theme "gitea" }}
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/theme-{{.SignedUser.Theme}}.css?v={{MD5 AppVer}}">
{{end}}
{{else if ne DefaultTheme "gitea"}}
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/theme-{{DefaultTheme}}.css?v={{MD5 AppVer}}">
{{end}}
<link rel="stylesheet" href="/RemixIcon_Fonts_v2.5.0/fonts/remixicon.css">
{{template "custom/header" .}}

<script>
var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?46149a0b61fdeddfe427ff4de63794ba";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
</script>
<script src="/self/func.js" type="text/javascript"></script>
</head>
<body>
{{template "custom/body_outer_pre" .}}

<div class="full height">
<noscript>{{.i18n.Tr "enable_javascript"}}</noscript>

{{template "custom/body_inner_pre" .}}
{{if not .PageIsInstall}}
<div class="ui top secondary stackable main menu following bar dark">
{{template "base/head_navbar" .}}
</div><!-- end bar -->
{{end}}
{{/*
</div>
</body>
</html>
*/}}


+ 4
- 2
templates/base/head_fluid.tmpl View File

@@ -201,7 +201,7 @@ var _hmt = _hmt || [];
<div class="ui top secondary stackable main menu following bar dark"> <div class="ui top secondary stackable main menu following bar dark">
{{template "base/head_navbar_fluid" .}} {{template "base/head_navbar_fluid" .}}
</div><!-- end bar --> </div><!-- end bar -->
<div class="notic_content" id ="notic_content" >
<div class="notic_content" id ="notic_content" style="display: none;">
<a href={{.notice.Link}} class="a_width"> <a href={{.notice.Link}} class="a_width">
<marquee behavior="scroll" direction="left"> <marquee behavior="scroll" direction="left">
{{.notice.Title}} {{.notice.Title}}
@@ -248,5 +248,7 @@ var _hmt = _hmt || [];
document.getElementById("notic_content").style.display='none' document.getElementById("notic_content").style.display='none'
} }
} }
isShowNotice();
if(!("{{.IsCourse}}" == true || "{{.IsCourse}}" =='true')) {
isShowNotice();
}
</script> </script>

+ 4
- 2
templates/base/head_home.tmpl View File

@@ -205,7 +205,7 @@ var _hmt = _hmt || [];
<div class="ui top secondary stackable main menu following bar dark"> <div class="ui top secondary stackable main menu following bar dark">
{{template "base/head_navbar" .}} {{template "base/head_navbar" .}}
</div><!-- end bar --> </div><!-- end bar -->
<div class="notic_content" id ="notic_content" >
<div class="notic_content" id ="notic_content" style="display: none;" >
<a href={{.notice.Link}} class="a_width"> <a href={{.notice.Link}} class="a_width">
<marquee behavior="scroll" direction="left"> <marquee behavior="scroll" direction="left">
{{.notice.Title}} {{.notice.Title}}
@@ -252,5 +252,7 @@ var _hmt = _hmt || [];
document.getElementById("notic_content").style.display='none' document.getElementById("notic_content").style.display='none'
} }
} }
isShowNotice();
if(!("{{.IsCourse}}" == true || "{{.IsCourse}}" =='true')) {
isShowNotice();
}
</script> </script>

+ 3
- 3
templates/base/head_navbar.tmpl View File

@@ -25,7 +25,7 @@
<div class="dropdown-content" style="min-width: 110px;border-radius:4px"> <div class="dropdown-content" style="min-width: 110px;border-radius:4px">
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/issues">{{.i18n.Tr "issues"}}</a> <a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/issues">{{.i18n.Tr "issues"}}</a>
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/pulls">{{.i18n.Tr "pull_requests"}}</a>
<a style="border: none;color: #000; white-space: nowrap;" class=" item" href="{{AppSubUrl}}/pulls">{{.i18n.Tr "pull_requests"}}</a>
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/milestones">{{.i18n.Tr "milestones"}}</a> <a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/milestones">{{.i18n.Tr "milestones"}}</a>
</div> </div>
</div> </div>
@@ -55,7 +55,7 @@
<div class="dropdown-content" style="min-width: 110px;border-radius:4px"> <div class="dropdown-content" style="min-width: 110px;border-radius:4px">
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "issues"}}</a> <a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "issues"}}</a>
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "pull_requests"}}</a>
<a style="border: none;color: #000; white-space: nowrap;" class=" item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "pull_requests"}}</a>
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "milestones"}}</a> <a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "milestones"}}</a>
</div> </div>
</div> </div>
@@ -73,7 +73,7 @@
{{if .IsOperator}} {{if .IsOperator}}
<a class="item" href="{{AppSubUrl}}/explore/data_analysis">{{.i18n.Tr "explore.data_analysis"}}</a> <a class="item" href="{{AppSubUrl}}/explore/data_analysis">{{.i18n.Tr "explore.data_analysis"}}</a>
{{end}} {{end}}
<a class="item" href="{{AppSubUrl}}/OpenI">{{.i18n.Tr "custom.head.openi"}}</a>
<a class="item" href="{{AppSubUrl}}/OpenI">{{.i18n.Tr "custom.head.openi.repo"}}</a>
</div> </div>
</div> </div>
{{else if .IsLandingPageExplore}} {{else if .IsLandingPageExplore}}


+ 4
- 4
templates/base/head_navbar_fluid.tmpl View File

@@ -25,7 +25,7 @@
<div class="dropdown-content" style="min-width: 110px;border-radius:4px"> <div class="dropdown-content" style="min-width: 110px;border-radius:4px">
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/issues">{{.i18n.Tr "issues"}}</a> <a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/issues">{{.i18n.Tr "issues"}}</a>
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/pulls">{{.i18n.Tr "pull_requests"}}</a>
<a style="border: none;color: #000; white-space: nowrap;" class=" item" href="{{AppSubUrl}}/pulls">{{.i18n.Tr "pull_requests"}}</a>
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/milestones">{{.i18n.Tr "milestones"}}</a> <a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/milestones">{{.i18n.Tr "milestones"}}</a>
</div> </div>
</div> </div>
@@ -42,7 +42,7 @@
{{if .IsOperator}} {{if .IsOperator}}
<a class="item" href="{{AppSubUrl}}/explore/data_analysis">{{.i18n.Tr "explore.data_analysis"}}</a> <a class="item" href="{{AppSubUrl}}/explore/data_analysis">{{.i18n.Tr "explore.data_analysis"}}</a>
{{end}} {{end}}
<a class="item" href="{{AppSubUrl}}/OpenI">{{.i18n.Tr "custom.head.openi"}}</a>
<a class="item" href="{{AppSubUrl}}/OpenI">{{.i18n.Tr "custom.head.openi.repo"}}</a>
</div> </div>
</div> </div>
{{else if .IsLandingPageHome}} {{else if .IsLandingPageHome}}
@@ -54,7 +54,7 @@
<div class="dropdown-content" style="min-width: 110px;border-radius:4px"> <div class="dropdown-content" style="min-width: 110px;border-radius:4px">
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "issues"}}</a> <a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "issues"}}</a>
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "pull_requests"}}</a>
<a style="border: none;color: #000; white-space: nowrap;" class=" item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "pull_requests"}}</a>
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "milestones"}}</a> <a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "milestones"}}</a>
</div> </div>
</div> </div>
@@ -71,7 +71,7 @@
{{if .IsOperator}} {{if .IsOperator}}
<a class="item" href="{{AppSubUrl}}/explore/data_analysis">{{.i18n.Tr "explore.data_analysis"}}</a> <a class="item" href="{{AppSubUrl}}/explore/data_analysis">{{.i18n.Tr "explore.data_analysis"}}</a>
{{end}} {{end}}
<a class="item" href="{{AppSubUrl}}/OpenI">{{.i18n.Tr "custom.head.openi"}}</a>
<a class="item" href="{{AppSubUrl}}/OpenI">{{.i18n.Tr "custom.head.openi.repo"}}</a>
</div> </div>
</div> </div>
{{else if .IsLandingPageExplore}} {{else if .IsLandingPageExplore}}


+ 4
- 4
templates/base/head_navbar_home.tmpl View File

@@ -17,7 +17,7 @@
<div class="dropdown-content" style="min-width: 110px;border-radius:4px"> <div class="dropdown-content" style="min-width: 110px;border-radius:4px">
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/issues">{{.i18n.Tr "issues"}}</a> <a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/issues">{{.i18n.Tr "issues"}}</a>
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/pulls">{{.i18n.Tr "pull_requests"}}</a>
<a style="border: none;color: #000; white-space: nowrap;" class=" item" href="{{AppSubUrl}}/pulls">{{.i18n.Tr "pull_requests"}}</a>
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/milestones">{{.i18n.Tr "milestones"}}</a> <a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/milestones">{{.i18n.Tr "milestones"}}</a>
</div> </div>
</div> </div>
@@ -34,7 +34,7 @@
{{if .IsOperator}} {{if .IsOperator}}
<a class="item" href="{{AppSubUrl}}/explore/data_analysis">{{.i18n.Tr "explore.data_analysis"}}</a> <a class="item" href="{{AppSubUrl}}/explore/data_analysis">{{.i18n.Tr "explore.data_analysis"}}</a>
{{end}} {{end}}
<a class="item" href="{{AppSubUrl}}/OpenI">{{.i18n.Tr "custom.head.openi"}}</a>
<a class="item" href="{{AppSubUrl}}/OpenI">{{.i18n.Tr "custom.head.openi.repo"}}</a>
</div> </div>
</div> </div>
{{else if .IsLandingPageHome}} {{else if .IsLandingPageHome}}
@@ -46,7 +46,7 @@
<div class="dropdown-content" style="min-width: 110px;border-radius:4px"> <div class="dropdown-content" style="min-width: 110px;border-radius:4px">
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "issues"}}</a> <a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "issues"}}</a>
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "pull_requests"}}</a>
<a style="border: none;color: #000; white-space: nowrap;" class=" item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "pull_requests"}}</a>
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "milestones"}}</a> <a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "milestones"}}</a>
</div> </div>
</div> </div>
@@ -64,7 +64,7 @@
{{if .IsOperator}} {{if .IsOperator}}
<a class="item" href="{{AppSubUrl}}/explore/data_analysis">{{.i18n.Tr "explore.data_analysis"}}</a> <a class="item" href="{{AppSubUrl}}/explore/data_analysis">{{.i18n.Tr "explore.data_analysis"}}</a>
{{end}} {{end}}
<a class="item" href="{{AppSubUrl}}/OpenI">{{.i18n.Tr "custom.head.openi"}}</a>
<a class="item" href="{{AppSubUrl}}/OpenI">{{.i18n.Tr "custom.head.openi.repo"}}</a>
</div> </div>
</div> </div>
{{else if .IsLandingPageExplore}} {{else if .IsLandingPageExplore}}


+ 4
- 4
templates/base/head_navbar_pro.tmpl View File

@@ -26,7 +26,7 @@
<div class="dropdown-content" style="min-width: 110px;border-radius:4px"> <div class="dropdown-content" style="min-width: 110px;border-radius:4px">
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/issues">{{.i18n.Tr "issues"}}</a> <a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/issues">{{.i18n.Tr "issues"}}</a>
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/pulls">{{.i18n.Tr "pull_requests"}}</a>
<a style="border: none;color: #000;white-space: nowrap;" class=" item" href="{{AppSubUrl}}/pulls">{{.i18n.Tr "pull_requests"}}</a>
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/milestones">{{.i18n.Tr "milestones"}}</a> <a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/milestones">{{.i18n.Tr "milestones"}}</a>
</div> </div>
</div> </div>
@@ -44,7 +44,7 @@
{{if .IsOperator}} {{if .IsOperator}}
<a class="item" href="{{AppSubUrl}}/explore/data_analysis">{{.i18n.Tr "explore.data_analysis"}}</a> <a class="item" href="{{AppSubUrl}}/explore/data_analysis">{{.i18n.Tr "explore.data_analysis"}}</a>
{{end}} {{end}}
<a class="item" href="{{AppSubUrl}}/OpenI">{{.i18n.Tr "custom.head.openi"}}</a>
<a class="item" href="{{AppSubUrl}}/OpenI">{{.i18n.Tr "custom.head.openi.repo"}}</a>
</div> </div>
</div> </div>
{{else if .IsLandingPageHome}} {{else if .IsLandingPageHome}}
@@ -56,7 +56,7 @@
<div class="dropdown-content" style="min-width: 110px;border-radius:4px"> <div class="dropdown-content" style="min-width: 110px;border-radius:4px">
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "issues"}}</a> <a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "issues"}}</a>
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "pull_requests"}}</a>
<a style="border: none;color: #000;white-space: nowrap;" class=" item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "pull_requests"}}</a>
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "milestones"}}</a> <a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "milestones"}}</a>
</div> </div>
</div> </div>
@@ -74,7 +74,7 @@
{{if .IsOperator}} {{if .IsOperator}}
<a class="item" href="{{AppSubUrl}}/explore/data_analysis">{{.i18n.Tr "explore.data_analysis"}}</a> <a class="item" href="{{AppSubUrl}}/explore/data_analysis">{{.i18n.Tr "explore.data_analysis"}}</a>
{{end}} {{end}}
<a class="item" href="{{AppSubUrl}}/OpenI">{{.i18n.Tr "custom.head.openi"}}</a>
<a class="item" href="{{AppSubUrl}}/OpenI">{{.i18n.Tr "custom.head.openi.repo"}}</a>
</div> </div>
</div> </div>
{{else if .IsLandingPageExplore}} {{else if .IsLandingPageExplore}}


+ 4
- 2
templates/base/head_pro.tmpl View File

@@ -201,7 +201,7 @@ var _hmt = _hmt || [];
<div class="ui top secondary stackable main menu following bar dark"> <div class="ui top secondary stackable main menu following bar dark">
{{template "base/head_navbar_pro" .}} {{template "base/head_navbar_pro" .}}
</div><!-- end bar --> </div><!-- end bar -->
<div class="notic_content" id ="notic_content" >
<div class="notic_content" id ="notic_content" style="display: none;" >
<a href={{.notice.Link}} class="a_width"> <a href={{.notice.Link}} class="a_width">
<marquee behavior="scroll" direction="left"> <marquee behavior="scroll" direction="left">
{{.notice.Title}} {{.notice.Title}}
@@ -249,5 +249,7 @@ var _hmt = _hmt || [];
document.getElementById("notic_content").style.display='none' document.getElementById("notic_content").style.display='none'
} }
} }
isShowNotice();
if(!("{{.IsCourse}}" == true || "{{.IsCourse}}" =='true')) {
isShowNotice();
}
</script> </script>

+ 2
- 2
templates/explore/dataset_list.tmpl View File

@@ -26,7 +26,7 @@
<div class="item"> <div class="item">
<div class="ui header"> <div class="ui header">
<a class="name" href="{{.Repo.Link}}/datasets?type=0"> <a class="name" href="{{.Repo.Link}}/datasets?type=0">
{{.Repo.OwnerName}} / {{.Title}}
{{.Repo.OwnerName}} / {{.Repo.Alias}}
</a> </a>
<div class="ui right metas"> <div class="ui right metas">
{{if .Task}} {{if .Task}}
@@ -53,4 +53,4 @@
</div> </div>
{{end}} {{end}}
</div> </div>
</div>
</div>

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

@@ -5,7 +5,7 @@
border-radius: 0.8rem; border-radius: 0.8rem;
margin-bottom: 1.0rem; margin-bottom: 1.0rem;
padding: 1.0rem !important; padding: 1.0rem !important;
}
}
.ui.repository.list>.item .header { .ui.repository.list>.item .header {
font-size: 1.4rem !important; font-size: 1.4rem !important;
font-weight: 200; font-weight: 200;
@@ -24,7 +24,7 @@
content: ""; content: "";
height: 1px; height: 1px;
background-color: #E1E3E6; background-color: #E1E3E6;
bottom: 2.8rem;
bottom: 2.8rem;
} }
.repository .ui.mini.menu{ .repository .ui.mini.menu{
font-size: .6rem; font-size: .6rem;
@@ -43,13 +43,13 @@
<a class="{{if eq .SortType "hot"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&topic={{$.Topic}}&sort=hot&tab={{$.TabName}}"> <a class="{{if eq .SortType "hot"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&topic={{$.Topic}}&sort=hot&tab={{$.TabName}}">
<svg class="svg octicon-repo" width="16" height="16" aria-hidden="true"> <svg class="svg octicon-repo" width="16" height="16" aria-hidden="true">
<use xlink:href="#octicon-repo" /> <use xlink:href="#octicon-repo" />
</svg>
</svg>
热门{{.i18n.Tr "explore.repos"}} 热门{{.i18n.Tr "explore.repos"}}
</a> </a>
<a class="{{if eq .SortType "active"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&topic={{$.Topic}}&sort=active&tab={{$.TabName}}"> <a class="{{if eq .SortType "active"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&topic={{$.Topic}}&sort=active&tab={{$.TabName}}">
<svg class="svg octicon-inbox" width="16" height="16" aria-hidden="true"> <svg class="svg octicon-inbox" width="16" height="16" aria-hidden="true">
<use xlink:href="#octicon-inbox" /> <use xlink:href="#octicon-inbox" />
</svg>
</svg>
活跃{{.i18n.Tr "explore.repos"}} 活跃{{.i18n.Tr "explore.repos"}}
</a> </a>
{{end}} {{end}}
@@ -93,7 +93,7 @@
<div class="ui grid"> <div class="ui grid">
<div class="ui sixteen wide mobile ten wide tablet twelve wide computer column"> <div class="ui sixteen wide mobile ten wide tablet twelve wide computer column">
<a class="name" href="{{.Link}}"> <a class="name" href="{{.Link}}">
{{if or $.PageIsExplore $.PageIsProfileStarList }}{{if .Owner}}{{.Owner.Name}} <span>/</span> {{end}}{{end}}<strong>{{.Name}}</strong>
{{if or $.PageIsExplore $.PageIsProfileStarList }}{{if .Owner}}{{.Owner.Name}} <span>/</span> {{end}}{{end}}<strong>{{.DisplayName}}</strong>
{{if .IsArchived}}<i class="archive icon archived-icon"></i>{{end}} {{if .IsArchived}}<i class="archive icon archived-icon"></i>{{end}}
</a> </a>
{{if .IsPrivate}} {{if .IsPrivate}}
@@ -114,7 +114,7 @@
<a class="item"> <a class="item">
<svg class="svg octicon-inbox" width="16" height="16" viewBox="0 0 24 24"> <svg class="svg octicon-inbox" width="16" height="16" viewBox="0 0 24 24">
<path fill="currentColor" d="M17.66 11.2C17.43 10.9 17.15 10.64 16.89 10.38C16.22 9.78 15.46 9.35 14.82 8.72C13.33 7.26 13 4.85 13.95 3C13 3.23 12.17 3.75 11.46 4.32C8.87 6.4 7.85 10.07 9.07 13.22C9.11 13.32 9.15 13.42 9.15 13.55C9.15 13.77 9 13.97 8.8 14.05C8.57 14.15 8.33 14.09 8.14 13.93C8.08 13.88 8.04 13.83 8 13.76C6.87 12.33 6.69 10.28 7.45 8.64C5.78 10 4.87 12.3 5 14.47C5.06 14.97 5.12 15.47 5.29 15.97C5.43 16.57 5.7 17.17 6 17.7C7.08 19.43 8.95 20.67 10.96 20.92C13.1 21.19 15.39 20.8 17.03 19.32C18.86 17.66 19.5 15 18.56 12.72L18.43 12.46C18.22 12 17.66 11.2 17.66 11.2M14.5 17.5C14.22 17.74 13.76 18 13.4 18.1C12.28 18.5 11.16 17.94 10.5 17.28C11.69 17 12.4 16.12 12.61 15.23C12.78 14.43 12.46 13.77 12.33 13C12.21 12.26 12.23 11.63 12.5 10.94C12.69 11.32 12.89 11.7 13.13 12C13.9 13 15.11 13.44 15.37 14.8C15.41 14.94 15.43 15.08 15.43 15.23C15.46 16.05 15.1 16.95 14.5 17.5H14.5Z" /> <path fill="currentColor" d="M17.66 11.2C17.43 10.9 17.15 10.64 16.89 10.38C16.22 9.78 15.46 9.35 14.82 8.72C13.33 7.26 13 4.85 13.95 3C13 3.23 12.17 3.75 11.46 4.32C8.87 6.4 7.85 10.07 9.07 13.22C9.11 13.32 9.15 13.42 9.15 13.55C9.15 13.77 9 13.97 8.8 14.05C8.57 14.15 8.33 14.09 8.14 13.93C8.08 13.88 8.04 13.83 8 13.76C6.87 12.33 6.69 10.28 7.45 8.64C5.78 10 4.87 12.3 5 14.47C5.06 14.97 5.12 15.47 5.29 15.97C5.43 16.57 5.7 17.17 6 17.7C7.08 19.43 8.95 20.67 10.96 20.92C13.1 21.19 15.39 20.8 17.03 19.32C18.86 17.66 19.5 15 18.56 12.72L18.43 12.46C18.22 12 17.66 11.2 17.66 11.2M14.5 17.5C14.22 17.74 13.76 18 13.4 18.1C12.28 18.5 11.16 17.94 10.5 17.28C11.69 17 12.4 16.12 12.61 15.23C12.78 14.43 12.46 13.77 12.33 13C12.21 12.26 12.23 11.63 12.5 10.94C12.69 11.32 12.89 11.7 13.13 12C13.9 13 15.11 13.44 15.37 14.8C15.41 14.94 15.43 15.08 15.43 15.23C15.46 16.05 15.1 16.95 14.5 17.5H14.5Z" />
</svg>
</svg>
{{.Hot}} {{.Hot}}
</a> </a>
{{else if eq $.SortType "active"}} {{else if eq $.SortType "active"}}
@@ -130,7 +130,7 @@
<a class="item"> <a class="item">
{{svg "octicon-git-branch" 16}} {{.NumForks}} {{svg "octicon-git-branch" 16}} {{.NumForks}}
</a> </a>
{{end}}
{{end}}
<a class="item"> <a class="item">
{{svg "octicon-star" 16}} {{.NumStars}} {{svg "octicon-star" 16}} {{.NumStars}}
</a> </a>


+ 2
- 2
templates/home.tmpl View File

@@ -38,7 +38,7 @@
<div class="ui stackable grid"> <div class="ui stackable grid">
<div class="sixteen wide tablet four wide computer column homeorg-tit"> <div class="sixteen wide tablet four wide computer column homeorg-tit">
<h2>{{.page_recommend_org}}</h2> <h2>{{.page_recommend_org}}</h2>
<p><span class="ui text grey">{{.page_recommend_org_desc}}</span><a href="{{.RecommendURL}}">{{.page_recommend_org_commit}}</a></p>
<p><span class="ui text grey">{{.page_recommend_org_desc}}&nbsp;</span><a href="{{.RecommendURL}}">{{.page_recommend_org_commit}}</a></p>
<a href="{{AppSubUrl}}/explore/organizations" class="circular ui primary basic button">{{.page_recommend_org_more}} <i class="arrow circle right icon"></i></a> <a href="{{AppSubUrl}}/explore/organizations" class="circular ui primary basic button">{{.page_recommend_org_more}} <i class="arrow circle right icon"></i></a>
</div> </div>
<div class="sixteen wide tablet twelve wide computer column"> <div class="sixteen wide tablet twelve wide computer column">
@@ -57,7 +57,7 @@
<div class="leftline02-2"></div> <div class="leftline02-2"></div>
<div class="ui center homepro-tit am-mb-20"> <div class="ui center homepro-tit am-mb-20">
<h2>{{.page_recommend_repo}}</h2> <h2>{{.page_recommend_repo}}</h2>
<p><span class="ui text grey">{{.page_recommend_repo_desc}}</span><a href="{{.RecommendURL}}">{{.page_recommend_repo_commit}}</a>{{.page_recommend_repo_go}}&nbsp;<a href="{{AppSubUrl}}/explore/">{{.page_recommend_repo_more}}</a></p>
<p><span class="ui text grey">{{.page_recommend_repo_desc}}&nbsp;</span><a href="{{.RecommendURL}}">{{.page_recommend_repo_commit}}</a>{{.page_recommend_repo_go}}&nbsp;<a href="{{AppSubUrl}}/explore/">{{.page_recommend_repo_more}}</a></p>
</div> </div>


<div class="homepro-list"> <div class="homepro-list">


+ 137
- 0
templates/org/course_list.tmpl View File

@@ -0,0 +1,137 @@
<style>
.text-right{
float:right !important;
}
.header{
font-weight:bold;
font-size: 18px;
font-family: SourceHanSansSC-medium;
}
.cor{
color:#0366D6 !important;
}
.header_card{
/* color:#003A8C !important; */
color:#101010 !important;
margin: 10px 0 0px 0;
height: 25px;
font-size: 18px;
}
.marg{
margin: 0 5px !important;
}

.content_list{
max-height: 130px;
overflow: auto;
}
.Relist{
color:#0366D6 !important;
}
.descript_height{
color: #999999 !important;
margin: 10px 0;
height: 40px !important;
word-break:break-all;
line-height: 20px;
overflow: hidden;
/* overflow: hidden!important;
word-wrap:break-word!important; */


}

.tags_height{
height: 30px !important;
}
.full_height{
height: 100%;
}
.omit{
overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
}
/deep/ ui.checkbox input[type=checkbox]::after{
border: 1px solid #0366D6 !important;
}
.nowrap-2 {
/* height: 2.837em; */
/* line-height: 1.4285em; */
overflow: hidden;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.course_topic{
color:#0366D6 !important;
background-color: rgba(179, 219, 219, 0.4) !important;
font-weight:normal !important
}
.tag_text{
/* margin-top: 2px; */
/* margin-left: 0.5em; */
font-size: 14px;
}
.card{
box-shadow: 0px 4px 4px 0px rgba(232, 232, 232, 60) !important;
border-radius: 5px;
border:1px solid #E8E8E8 !important;
}
.tags{
position: relative;
overflow: hidden;
height: 30px;
line-height: 30px;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
}

</style>
<div style="width: 100%;">
<div class="ui three cards" style="margin-bottom: 10px;">
{{range .Repos}}
<div class="card " >
<div class="extra full_height cor" >
<div class="content " >
{{if .Topics }}
<div class="omit tags " style="position: relative;">
{{range .Topics}}
{{if ne . "" }}<a style="max-width:100%;margin: 5px 0;display:inline-flex;" ><span class="ui small label topic course_topic" >{{.}}</span></a>{{end}}
{{end}}
</div>
{{end}}
</div>
<div class=" header header_card omit" >
<a class="header_card image poping up " href="{{.Link}}" data-content="{{.Alias}}" data-position="top left" data-variation="tiny inverted"> {{.Alias}}</a>
</div>
<div class='content descript_height nowrap-2'>
{{.Description}}
</div>
</div>
<div class=" extra content" style="color:#888888;border-top: none !important;padding-top: 0px;margin-bottom: 15px;">
<div class="left aligned author">
<!-- <span > -->
{{if .Creator }}
<a href="{{.Creator.Name}}" title="{{.Creator.Name}}">
<img class="ui avatar image" style="width: 22px;height:22px;margin-top:-5px" src="{{.Creator.RelAvatarLink}}">
</a>
{{else}}
<a href="{{.Owner.Name}}" title="{{.Owner.Name}}">
<img class="ui avatar image" style="width: 22px;height:22px;margin-top:-5px" src="{{.Owner.RelAvatarLink}}">
</a>
{{end}}

{{$.i18n.Tr "org.repo_released"}}&nbsp;: &nbsp; {{TimeSinceUnixShort .CreatedUnix}}
<!-- </span> -->
</div>
</div>
</div>
{{end}}
</div>
</div>

+ 49
- 11
templates/org/header.tmpl View File

@@ -8,20 +8,58 @@
<img class="ui image" src="{{.SizedRelAvatarLink 100}}"> <img class="ui image" src="{{.SizedRelAvatarLink 100}}">
<span class="text thin grey"><a href="{{.HomeLink}}">{{.DisplayName}}</a></span> <span class="text thin grey"><a href="{{.HomeLink}}">{{.DisplayName}}</a></span>
{{end}} {{end}}
{{if .IsOrganizationOwner}}
<div class="ui right">
<a class="ui green button" href="{{.OrgLink}}/teams/new">{{svg "octicon-plus" 16}} {{.i18n.Tr "org.create_new_team"}}</a>
</div>
{{end}}
{{if .CanCreateOrgRepo}}
<div class="ui right">
<a class="ui green button" href="{{AppSubUrl}}/repo/create?org={{.Org.ID}}">{{svg "octicon-plus" 16}} {{.i18n.Tr "new_repo"}}</a>
</div>
{{end}}
{{if .IsCourse}}
{{if .CanCreateOrgRepo}}
<div class="ui right">
<a class="ui green button" onclick="jion_course_team()">{{svg "octicon-plus" 16}} {{.i18n.Tr "org.teams.join_teams"}}</a>
</div>
{{end}}
{{else}}
{{if .IsOrganizationOwner}}
<div class="ui right">
<a class="ui green button" href="{{.OrgLink}}/teams/new">{{svg "octicon-plus" 16}} {{.i18n.Tr "org.create_new_team"}}</a>
</div>
{{end}}
{{if .CanCreateOrgRepo}}
<div class="ui right">
<a class="ui green button" href="{{AppSubUrl}}/repo/create?org={{.Org.ID}}">{{svg "octicon-plus" 16}} {{.i18n.Tr "new_repo"}}</a>
</div>
{{end}}
{{end}}
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>


<script>

function jion_course_team(){
$.ajax({
type:"GET",
url:"/course/addOrg",
dataType:"json",
async:false,
success:function(json){
data = json;
if (data.code==0) {
$('.alert').html('{{.i18n.Tr "repo.computing.success"}}').removeClass('alert-danger').addClass('alert-success').show().delay(2000).fadeOut();
} else {
$('.alert').html(data.error_msg).removeClass('alert-success').addClass('alert-danger').show().delay(5000).fadeOut();
}
setTimeout("location.reload()",2000);
// location.reload()
// if(data.code==0){
// alert("Join success")
// location.reload()

// }else{
// alert("Join failure")
// }
},
});
}

</script>


+ 32
- 0
templates/org/header_course.tmpl View File

@@ -0,0 +1,32 @@
<div class="organization-header">
<div class="ui container">
<div class="ui vertically grid head">
<div class="column">
<div class="ui header">
{{with .Org}}
<img class="ui image" src="{{.SizedRelAvatarLink 100}}">
<span class="text thin grey"><a href="{{.HomeLink}}">{{.DisplayName}}</a></span>
{{end}}
{{if .IsCourse}}
{{if .IsOrganizationOwner}}
<div class="ui right">
<a class="ui green button" href="{{.OrgLink}}/teams/new">{{svg "octicon-plus" 16}} {{.i18n.Tr "org.create_new_team"}}</a>
</div>
{{end}}
{{else}}
{{if .IsOrganizationOwner}}
<div class="ui right">
<a class="ui green button" href="{{.OrgLink}}/teams/new">{{svg "octicon-plus" 16}} {{.i18n.Tr "org.create_new_team"}}</a>
</div>
{{end}}
{{if .CanCreateOrgRepo}}
<div class="ui right">
<a class="ui green button" href="{{AppSubUrl}}/repo/create?org={{.Org.ID}}">{{svg "octicon-plus" 16}} {{.i18n.Tr "new_repo"}}</a>
</div>
{{end}}
{{end}}
</div>
</div>
</div>
</div>
</div>

+ 454
- 0
templates/org/home_courses.tmpl View File

@@ -0,0 +1,454 @@
<style>

.organization-info_1000{
background: #F5F5F6 !important;
padding-top: 30px;
margin-bottom: 0px !important;
}
.organization-info >.container {
overflow: auto;
background: #f5f5f6 !important;
padding-top: 30px;
padding-bottom: 20px;
background-size: cover;
border-radius: 5px;
border: none !important
}
.organization.profile #org-avatar {
border:none !important
}
.desc {
font-size: 14px;
margin-bottom: 10px !important;
}
.item {
display: inline-block;
margin-right: 10px;
}
.item .icon {
margin-right: 5px;
}
.organization-info >.container {
padding-bottom:0px !important;
}
.tag_bg{
background-color: #0366D6 !important;
color:#FFFFFF !important;
}
.course{
padding:10px 0 15px !important;
}
.course_color{
color: #FA8C16;
}
.tag_lable{
border: 1px solid rgba(232, 232, 232, 100) ;
border-radius: 4px;
color: rgba(65, 80, 88, 100);
font-family: Microsoft Yahei;
font-size: 14px;
padding: 0.3em 0.5em;
height: 30px;
text-align: center;
margin: 0.2em;
}
.tag_lable_first{
border: 1px solid rgba(232, 232, 232, 100) ;
border-radius: 4px;
color: rgba(65, 80, 88, 100);
font-family: Microsoft Yahei;
font-size: 14px;
padding: 0.3em 0.5em;
height: 30px;
text-align: center;
margin: 0.2em;
margin-left: none;
}
.tag_key{
max-width:100%;
margin: 3px 3px;
display:inline-flex;
}
.bpadding{
padding:10px 40px
}
.omit{
overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
}
.noborder{
border: none !important;
}
.div_bt{
text-align: center;
margin-top: 5px;
margin-top: 10px;
}
</style>
{{template "base/head" .}}
<!-- 提示框 -->
<div class="alert"></div>
<div class="organization profile">
{{/* overflow: auto is the clearfix - this avoids the image going beyond
the container where it is supposed to stay inside. */}}
<div class="organization-info organization-info_1000">
<div class="ui center aligned container " style="overflow: auto">
<img class="ui left image" id="org-avatar" src="{{.Org.SizedRelAvatarLink 140}}"/>
<div class="content" style="text-align: left;margin-left:100px" >
<div class="ui header" >
{{.Org.DisplayName}}
</div>
<div class="description" >
{{if .Org.Description}}<p class="text grey desc">{{.Org.Description}}</p>{{end}}
</div>
<div class="meta" style="display: inline-flex;">
{{if .Org.Location}}<div class="item">{{svg "octicon-location" 16}} <span>{{.Org.Location}}</span></div>{{end}}
{{if .Org.Website}}<div class="item">{{svg "octicon-link" 16}} <a target="_blank" rel="noopener noreferrer" href="{{.Org.Website}}">{{.Org.Website}}</a></div>{{end}}
</div>
</div>
</div>
</div>
{{template "org/navber_course" .}}
<div class="ui container">
<!-- 新增 -->
<div class="ui stackable grid">
<div class="ui sixteen wide computer column">
<div class="ui mobile reversed stackable grid">
<div class="ui ten wide tablet twelve wide computer column" id='tag'>
<a class="{{if eq $.Keyword "" }} tag_bg {{end}} tag_key ui small tag_lable topic omit" href="{{$.Link}}?" >{{$.i18n.Tr "org.all_keywords"}}</span></a>
{{range .CoursesKeywords}}
{{if ne . ""}}
<a class="{{if eq $.Keyword . }} tag_bg {{end}} tag_key ui small tag_lable topic omit" href="{{$.Link}}?q={{.}}" >
{{.}}
</a>
{{end}}
{{end}}
</div>
<div class="ui sixteen wide mobile six wide tablet four wide computer column">
<div class=" ui bottom attached segment text center noborder text center" >
{{if .IsSigned}}
<a style="width: 80%;" class="ui green button bpadding" href="{{AppSubUrl}}/course/create"><i class="ri-folder-add-line" style="vertical-align: middle;"></i> &nbsp;{{.i18n.Tr "org.release_course"}} </a>
{{else}}
<a style="width: 80%;" class="ui green button bpadding" href="{{AppSubUrl}}/user/login"><i class="ri-folder-add-line" style="vertical-align: middle;"></i> &nbsp;{{.i18n.Tr "org.release_course"}} </a>
{{end}}
</div>
</div>
</div>
</div>
</div>
<!-- 全部 -->
<div class="ui stackable grid">
<div class="ui sixteen wide computer column">
<div class="ui mobile reversed stackable grid">
<div class="ui ten wide tablet twelve wide computer column">
{{template "org/course_list" .}}
{{template "base/paginate" .}}
</div>
<div class="ui sixteen wide mobile six wide tablet four wide computer column">
{{if .tags}}
<h4 class="ui top attached header noborder">
<strong>{{.i18n.Tr "org.selected_couse"}}</strong>
{{if .IsOrganizationOwner}}
<div class="ui right">
<a class="text grey" id="model" onclick="showcreate()">{{svg "octicon-gear" 16}}</a>
</div>
{{end}}
</h4>
<div class="ui attached table segment course items noborder">
{{ range .tags}}
{{if eq .TagName "精选项目"}}
{{range $i, $v := .RepoList}}
{{if gt $i 0}}
<div class="ui divider" style="margin-bottom:10px;"></div>
{{end}}
<div class="item">
<i class="large icon ri-bookmark-3-line course_color"></i>
<div class="content" style="margin-left: 10px;">
<a href="{{.Link}}"><strong class="team-name">{{.Alias}}</strong></a>
<p class="text grey">
{{if ne .CreatorID 0}}
{{$.i18n.Tr "home.contributor"}} : {{.Creator.Name}}
{{else}}
{{$.i18n.Tr "home.contributor"}}:{{.Owner.Name}}
{{end}}
</p>
</div>
</div>
{{end}}
{{end}}
{{end}}
</div>
{{end}}
<h4 class="ui top attached header noborder">
<strong>{{.i18n.Tr "org.people"}}</strong>
<div class="ui right">
<a class="text grey" href="{{.OrgLink}}/members">{{.MembersTotal}} {{svg "octicon-chevron-right" 16}}</a>
</div>
</h4>
<div class="ui attached segment members course noborder">
{{$isMember := .IsOrganizationMember}}
{{range .Members}}
{{if or $isMember (.IsPublicMember $.Org.ID)}}
<a href="{{.HomeLink}}" title="{{.Name}}{{if .FullName}} ({{.FullName}}){{end}}"><img class="ui avatar" src="{{.RelAvatarLink}}"></a>
{{end}}
{{end}}
<div class="ui bottom attached segment text center noborder">
{{if .IsSigned}}
<a class="ui blue basic button" onclick="jion_course_team()" style="width: 80%;"> <i class="ri-user-add-line"></i> {{.i18n.Tr "org.teams.join_teams"}}</a>
{{else}}
<a class="ui blue basic button" href="{{AppSubUrl}}/user/login" style="width: 80%;"> <i class="ri-user-add-line"></i> {{.i18n.Tr "org.teams.join_teams"}}</a>
{{end}}
</div>
</div>
{{if .IsOrganizationMember}}
<div class="ui top attached header noborder">
<strong>{{.i18n.Tr "org.teams"}}</strong>
<div class="ui right">
<a class="text grey" href="{{.OrgLink}}/teams"><span>{{.Org.NumTeams}}</span> {{svg "octicon-chevron-right" 16}}</a>
</div>
</div>
<div class="ui attached table segment teams noborder">
{{range .Teams}}
<div style="margin-top: 10px;">
<a href="{{$.OrgLink}}/teams/{{.LowerName}}"><strong class="team-name">{{.Name}}</strong></a>
<p class="text grey">
<a href="{{$.OrgLink}}/teams/{{.LowerName}}"><strong>{{.NumMembers}}</strong> {{$.i18n.Tr "org.lower_members"}}</a> ·
<a href="{{$.OrgLink}}/teams/{{.LowerName}}/repositories"><strong>{{.NumRepos}}</strong> {{$.i18n.Tr "org.lower_repositories"}}</a>
</p>
</div>
{{end}}
</div>
{{if .IsOrganizationOwner}}
<div class="ui bottom attached segment text center noborder">
<a class="ui blue basic button" style="width: 80%;" href="{{.OrgLink}}/teams/new">{{.i18n.Tr "org.create_new_team"}}</a>
</div>
{{end}}
{{end}}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="ui modal">
<div class="header" style="padding: 1rem;background-color: rgba(240, 240, 240, 100);">
<h4 id="model_header">{{.i18n.Tr "org.custom_select_courses"}}</h4>
</div>
<div class="content content-padding" style="color: black;">
<p>{{.i18n.Tr "org.max_selectedPro"}}</p>
<div class="ui search" >
<div class="ui input" style="width: 100%;">
<input type="text" id = 'search_selectPro' placeholder="Search ..." value = '' oninput="search()">
</div>
</div>
<div style="margin: 10px ;">
<div id ='org_list' style="margin-bottom: 20px;"class="content_list" >
</div>
</div>
<p id='recommend'></p>
<div class="inline field" style="margin-left: 37%;">
<div class="actions">
<button id="submitId" type="button" class="ui create_train_job green deny button" onclick="saveSeletedPro(1)">
{{.i18n.Tr "explore.save"}}
</button>
<button class="ui button cancel" >{{.i18n.Tr "explore.cancel"}}</button>
</div>
</div>
</div>
</div>
{{template "base/footer" .}}
<script>
var data;
var filterData=[];
var num=0;
function showcreate(obj){
document.getElementById("search_selectPro").value=''
$('.ui.modal')
.modal({
centered: false,
onShow:function(){
$("#org_list").empty()
getPro(1)
},
onHide:function(){
}
})
.modal('show')
}
function getPro(typeTag){
$.ajax({
type:"GET",
url:"/org/{{.Org.Name}}/org_tag/repo_list?tagId="+typeTag,
dataType:"json",
async:false,
success:function(json){
data = json.data;
var n_length = data.length
pro_html = getHTML(data)
$("#org_list").append(pro_html)
// console.log('原始',data)
checkedNum(0)
}
});
}
function getHTML(data){
let pro_html=''
for (let i=0;i<data.length;i++){
if (data[i].Selected==true){
console.log("data[i]:",data[i])
pro_html += `<div class="ui checkbox" style="width: 33%;margin-bottom:10px" > <input type="checkbox" id = " ${i}" checked="" onclick="checkedNum(${i})" class="Relist" name ='select_pro_name' data-repoid="${data[i].RepoID}" data-reponame="${data[i].RepoName}" data-selected=${data[i].Selected} > <label class='omit image poping up' data-content=${data[i].RepoName} data-position="top left " data-variation="mini"> ${data[i].RepoName}</label></div>`
pro_html += '</div>'
}
else{
pro_html += `<div class="ui checkbox" style="width: 33%;margin-bottom:10px" > <input type="checkbox" id = "${i}" onclick="checkedNum(${i})" class="Relist" name ='select_pro_name' data-repoid="${data[i].RepoID}" data-reponame="${data[i].RepoName}" data-selected= ${data[i].Selected}> <label class='omit image poping up' data-content=${data[i].RepoName} data-position="top left " data-variation="mini"> ${data[i].RepoName} </label></div>`
pro_html += '</div>'
}
}
return pro_html
}
function saveSeletedPro(typeTag){
var saveData=[];
$('input[name="select_pro_name"]:checked').each(function(){
// console.log('值',this.dataset.repoid)
saveData.push(parseInt(this.dataset.repoid));
})
if(saveData.length>9){
alert("{{.i18n.Tr "org.save_fail_tips"}}")
return
}
$.ajax({
type:"POST",
url:"/org/{{.Org.Name}}/org_tag/repo_submit?tagId="+typeTag,
contentType:'application/json',
dataType:"json",
async:false,
data:JSON.stringify({'repoList':saveData
}),
success:function(res){
// console.log('保存成功');
location.reload()
}
});
}
function getSelecteData(){
var selectedData=[];
$('input[name="select_pro_name"]:checked').each(function(){
// console.log(this)
// console.log('值',this.dataset.selected)
selectedData.push({"RepoID":parseInt(this.dataset.repoid),"RepoName":this.dataset.reponame,"Selected":JSON.parse(this.dataset.selected)});
})
return selectedData
}
function search(){
var selectedData = getSelecteData();
var searchValue = document.getElementById("search_selectPro").value;
filterData=[];
console.log("searchValue:",searchValue)
for (let i=0;i<data.length;i++){
var isInclude=false;
if(data[i].RepoName.toLowerCase().includes(searchValue.toLowerCase())){
filterData.push(data[i])
}
}
// console.log("选中的值:",selectedData)
// console.log("筛选包括选中的值:",filterData)
var showData=[];
for(i=0;i<selectedData.length;i++){
filterData =filterData.filter((item)=>{
return item.RepoID!=selectedData[i].RepoID
});
}
// console.log("筛选后不包括选中的值:",filterData)
$("#org_list").empty()
if(searchValue!=""){
if (filterData.length!=0){
var pro_html = getHTML(selectedData);
console.log("selectedData_pro_html:",pro_html)
$("#org_list").append(pro_html)
pro_html= getHTML(filterData);
$("#org_list").append(pro_html)
}else{
var pro_html = getHTML(selectedData);
$("#org_list").append(pro_html)
}
}else{
var pro_html = getHTML(data);
$("#org_list").append(pro_html)
}
}
function checkedNum(id){
num=0;
var inputs = document.getElementsByName("select_pro_name")
for (var i=0;i<inputs.length;i++){
if(inputs[i].checked){
num++
if(num>9){
document.getElementById(id).checked=false
alert( "{{.i18n.Tr "org.select_again"}}")
return
}
}
}
var show_num = 9-num;
let rec = "{{.i18n.Tr "org.recommend_remain_pro"}}"
document.getElementById("recommend").innerHTML=rec +" : "+ show_num
}

function jion_course_team(){
$.ajax({
type:"GET",
url:"/course/addOrg",
dataType:"json",
async:false,
success:function(json){
data = json;
if (data.code==0) {
$('.alert').html('{{.i18n.Tr "repo.computing.success"}}').removeClass('alert-danger').addClass('alert-success').show().delay(2000).fadeOut();
} else {
$('.alert').html(data.error_msg).removeClass('alert-success').addClass('alert-danger').show().delay(5000).fadeOut();
}
setTimeout("location.reload()",2000);
},

});
}
</script>

+ 130
- 0
templates/org/member/course_members.tmpl View File

@@ -0,0 +1,130 @@
<style>
.organization-header{
margin-bottom: 0px !important;
border-bottom:none !important
}
.course_header{
color: rgba(3, 102, 214, 100);
font-size: 16px;
text-align: left;
font-family: SourceHanSansSC-medium;
font-weight: bolder;
vertical-align: bottom;
}
.meb_label{
border-radius: 5px;
background-color: rgba(255, 255, 255, 100) !important;
color: rgba(255, 255, 255, 100);
font-size: 12px;
text-align: center;
font-family: Roboto;
border: 1px solid rgba(212, 212, 213, 100) !important;
margin-left: 1em !important;
}
.ui.small.label.topic {
margin-bottom: 0px !important;
}
.cor{
color:#888888 !important
}
.card_course{
padding:1em;
border: 1px solid #F5F5F6;
margin:1em;box-shadow: 0px 4px 4px 0px rgba(232, 232, 232, 60);
border-radius: 5px;border: 1px solid rgba(232, 232, 232, 100);
display: flex; width:calc(33.33333333333333% - 2em)
}
.button_leaveOrg{
position:absolute;right: -1px;top:0px;
}
.bt_mr{
margin-right: 0px !important;
font-size: 12px !important;
padding: 5px !important;
}
</style>

{{template "base/head" .}}
<!-- 提示框 -->
<div class="alert"></div>
<div class="organization members">
{{template "org/header" .}}
{{template "org/navber_course" .}}
<div class="ui container">
{{template "base/alert" .}}
<div class="ui stackable grid">
<div class="ui sixteen wide computer column list">
<div class="ui three cards" >
{{ range .Members}}
<div class="card_course" style="position: relative;" id = "{{.ID}}" onmouseover ="show_bt( {{.ID}} )" onmouseout="hide_bt({{.ID}})">
<div >
<img class="ui avatar " style="width: 45px;height:45px;margin-top: 2px;" src="{{.SizedRelAvatarLink 48}}">
<div class="meta" style="text-align: center; margin-top: 0.5em;">
{{ $isPublic := index $.MembersIsPublicMember .ID}}
{{if $isPublic}}
{{if or (eq $.SignedUser.ID .ID) $.IsOrganizationOwner}} <a class="link-action" href data-url="{{$.OrgLink}}/members/action/private?uid={{.ID}}">{{$.i18n.Tr "org.members.public_helper"}}</a> {{end}}
{{else}}
{{if or (eq $.SignedUser.ID .ID) $.IsOrganizationOwner}} <a class="link-action" href data-url="{{$.OrgLink}}/members/action/public?uid={{.ID}}">{{$.i18n.Tr "org.members.private_helper"}}</a> {{end}}
{{end}}
</div>
</div>
<div style="padding-left: 0.8em;">
<div>
<a href="{{.HomeLink}}" class="course_header"> {{.Name}}</a>
<div class="ui small label topic meb_label" >
{{if index $.MembersIsUserOrgOwner .ID}} {{$.i18n.Tr "org.members.owner"}}{{else}}{{$.i18n.Tr "org.members.member"}}{{end}}
</div>
</div>
<div class="meta" style="margin-top: 0.5em;">
{{.FullName}}
</div>

<div class="meta" style="margin-top: 0.5em;">
{{svg "octicon-mail" 16}}
<a class="cor" href="mailto:{{.Email}}" rel="nofollow"> {{.Email}}</a>
</div>
</div>
<div class="button_leaveOrg" style="display:none" >
{{if eq $.SignedUser.ID .ID}}
<form method="post" action="{{$.OrgLink}}/members/action/leave">
{{$.CsrfTokenHtml}}
<button type="submit" class="ui red basic button bt_mr" name="uid" value="{{.ID}}">{{$.i18n.Tr "org.course_members.leave"}}</button>
</form>
{{else if $.IsOrganizationOwner}}
<form method="post" action="{{$.OrgLink}}/members/action/remove">
{{$.CsrfTokenHtml}}
<button type="submit" class="ui red basic button bt_mr" name="uid" value="{{.ID}}">{{$.i18n.Tr "org.course_members.remove"}}</button>
</form>
{{end}}
</div>
</div>
{{end}}
</div>
</div>
{{template "base/paginate" .}}
</div>
</div>
</div>
{{template "base/footer" .}}

<script>
function show_bt(bt_id){
console.log(bt_id)
document.getElementById(""+bt_id).getElementsByClassName("button_leaveOrg")[0].style.display="inline-block";
}
function hide_bt(bt_id){
document.getElementById(""+bt_id).getElementsByClassName("button_leaveOrg")[0].style.display="none";
}
</script>

+ 28
- 0
templates/org/navber_course.tmpl View File

@@ -0,0 +1,28 @@
<style>
.navber_course{
background: #F5F5F6 !important;
padding-top: 30px;
margin-bottom: 20px;
}
</style>
<div class="navber_course">
<div class="ui tabs container">
<div class="ui tabular stackable menu navbar">
{{with .Org}}
<a class="{{if $.PageIsOrgHome}}active{{end}} item " href="{{.HomeLink}}">
{{svg "octicon-home" 16}}&nbsp;{{$.i18n.Tr "org.home"}}
</a>
{{end}}
<a class="{{if $.PageIsOrgMembers}}active{{end}} item" href="{{$.OrgLink}}/members">
{{svg "octicon-organization" 16}}&nbsp;{{$.i18n.Tr "org.people"}}
</a>
{{if or ($.IsOrganizationMember) ($.IsOrganizationOwner)}}
<a class="{{if $.PageIsOrgTeams}}active{{end}} item" href="{{$.OrgLink}}/teams">
{{svg "octicon-jersey" 16}}&nbsp;{{$.i18n.Tr "org.teams"}}
</a>
{{end}}
{{if .IsOrganizationOwner}}<a class="right text grey item" href="{{.OrgLink}}/settings">{{svg "octicon-gear" 16}} &nbsp;{{$.i18n.Tr "org.settings"}}</a>{{end}}
</div>

</div>
</div>

+ 2
- 2
templates/org/repo_list.tmpl View File

@@ -33,7 +33,7 @@
<img class="ui avatar image" src="{{.RelAvatarLink}}"> <img class="ui avatar image" src="{{.RelAvatarLink}}">
{{end}} {{end}}
<a class="name" href="{{.Link}}"> <a class="name" href="{{.Link}}">
{{if or $.PageIsExplore $.PageIsProfileStarList }}{{if .Owner}}{{.Owner.Name}} / {{end}}{{end}}{{.Name}}
{{if or $.PageIsExplore $.PageIsProfileStarList }}{{if .Owner}}{{.Owner.Name}} / {{end}}{{end}}{{if .Alias}}{{.Alias}}{{else}}{{.Name}}{{end}}
{{if .IsArchived}}<i class="archive icon archived-icon"></i>{{end}} {{if .IsArchived}}<i class="archive icon archived-icon"></i>{{end}}
</a> </a>
{{if .IsPrivate}} {{if .IsPrivate}}
@@ -70,4 +70,4 @@
{{$.i18n.Tr "explore.repo_no_results"}} {{$.i18n.Tr "explore.repo_no_results"}}
</div> </div>
{{end}} {{end}}
</div>
</div>

+ 7
- 4
templates/org/select_pro.tmpl View File

@@ -61,7 +61,9 @@
-webkit-line-clamp: 2; -webkit-line-clamp: 2;
-webkit-box-orient: vertical; -webkit-box-orient: vertical;
} }

.ui.cards>.card>.extra .tags > a{
margin-top: 5px;
}


</style> </style>
<div class="ui stackable grid"> <div class="ui stackable grid">
@@ -87,7 +89,7 @@
<div class="extra full_height cor" > <div class="extra full_height cor" >
<div class=" header header_card omit" > <div class=" header header_card omit" >
<a class="header_card image poping up " href="{{.Link}}" data-content="{{.Name}}" data-position="top left" data-variation="tiny inverted"> {{.Name}}</a>
<a class="header_card image poping up " href="{{.Link}}" data-content="{{if .Alias}}{{.Alias}}{{else}}{{.Name}}{{end}}" data-position="top left" data-variation="tiny inverted">{{if .Alias}}{{.Alias}}{{else}}{{.Name}}{{end}}</a>
</div> </div>
<div class='content descript_height nowrap-2'> <div class='content descript_height nowrap-2'>
@@ -99,7 +101,7 @@
{{if .Topics }} {{if .Topics }}
<div class=" tags " style="position: relative;"> <div class=" tags " style="position: relative;">
{{range .Topics}} {{range .Topics}}
{{if ne . "" }}<a style="max-width:100%;margin: 5px 0;display:inline-flex;" href="{{AppSubUrl}}/explore/repos?q={{.}}&topic={{$.Topic}}" ><span class="ui small label topic omit" >{{.}}</span></a>{{end}}
{{if ne . "" }}<a style="max-width:100%;display:inline-flex;" href="{{AppSubUrl}}/explore/repos?q={{.}}&topic={{$.Topic}}" ><span class="ui small label topic omit" >{{.}}</span></a>{{end}}
{{end}} {{end}}
</div> </div>
@@ -108,7 +110,7 @@
</div> </div>
</div> </div>
<div class=" extra " style="color:#888888;border-top: none !important">
<div class=" extra " style="color:#888888;border-top: none !important;padding-top: 0">
<div class="ui mini right compact marg" > <div class="ui mini right compact marg" >
<a class="item marg "> <a class="item marg ">
{{svg "octicon-eye" 16}} {{.NumWatches}} {{svg "octicon-eye" 16}} {{.NumWatches}}
@@ -165,6 +167,7 @@
</div> </div>


<script> <script>
console.log({{.tags}})
var data; var data;
var filterData=[]; var filterData=[];
var num=0; var num=0;


+ 56
- 0
templates/org/team/courseTeams.tmpl View File

@@ -0,0 +1,56 @@
<style>
.organization-header{
margin-bottom: 0px !important;
border-bottom:none !important
}
</style>
{{template "base/head" .}}
<div class="organization teams">
{{template "org/header_course" .}}
{{template "org/navber_course" .}}

<div class="ui container">
{{template "base/alert" .}}

<div class="ui stackable grid">
<div class="ui sixteen wide computer column list">
<div class="ui two column grid">
{{range .Teams}}
<div class="column">
<div class="ui top attached header">
<a class="text black" href="{{$.OrgLink}}/teams/{{.LowerName}}"><strong>{{.Name}}</strong></a>
<div class="ui right">
{{if .IsMember $.SignedUser.ID}}
<form method="post" action="{{$.OrgLink}}/teams/{{.LowerName}}/action/leave">
{{$.CsrfTokenHtml}}
<button type="submit" class="ui red small button" name="uid" value="{{$.SignedUser.ID}}">{{$.i18n.Tr "org.teams.leave"}}</button>
</form>
{{else if $.IsOrganizationOwner}}
<form method="post" action="{{$.OrgLink}}/teams/{{.LowerName}}/action/join">
{{$.CsrfTokenHtml}}
<button type="submit" class="ui blue small button" name="uid" value="{{$.SignedUser.ID}}">{{$.i18n.Tr "org.teams.join"}}</button>
</form>
{{end}}
</div>
</div>
<div class="ui attached segment members">
{{range .Members}}
<a href="{{.HomeLink}}" title="{{.Name}}">
<img class="ui avatar image" src="{{.RelAvatarLink}}">
</a>
{{end}}
</div>
<div class="ui bottom attached header">
<p class="team-meta">{{.NumMembers}} {{$.i18n.Tr "org.lower_members"}} · {{.NumRepos}} {{$.i18n.Tr "org.lower_repositories"}}</p>
</div>
</div>
{{end}}
</div>
</div>
</div>
</div>
</div>
{{template "base/footer" .}}

+ 1
- 1
templates/org/team/repositories.tmpl View File

@@ -50,7 +50,7 @@
{{else}} {{else}}
{{svg "octicon-repo" 16}} {{svg "octicon-repo" 16}}
{{end}} {{end}}
<strong>{{$.Org.Name}}/{{.Name}}</strong>
<strong>{{$.Org.Name}}/{{.Alias}}</strong>
</a> </a>
</div> </div>
{{else}} {{else}}


+ 387
- 0
templates/repo/cloudbrain/benchmark/index.tmpl View File

@@ -0,0 +1,387 @@
<!-- 头部导航栏 -->
{{template "base/head" .}}

<style>
.fontsize14{
font-size: 14px;
}
.padding0{
padding: 0 !important;
}
</style>

<!-- 弹窗 -->
<div id="mask">
<div id="loadingPage">
<div class="rect1"></div>
<div class="rect2"></div>
<div class="rect3"></div>
<div class="rect4"></div>
<div class="rect5"></div>
</div>
</div>

<!-- 提示框 -->
<div class="alert"></div>

<div class="repository release dataset-list view">
{{template "repo/header" .}}
<!-- 列表容器 -->
<div class="ui container">
{{template "base/alert" .}}
<div class="ui two column stackable grid ">
<div class="column">
<div class="ui blue small menu compact selectcloudbrain">
<a class="item" href="{{.RepoLink}}/debugjob?debugListType=all">{{$.i18n.Tr "repo.modelarts.notebook"}}</a>
<a class="item" href="{{.RepoLink}}/modelarts/train-job">{{$.i18n.Tr "repo.modelarts.train_job"}}</a>
<a class="item" href="{{.RepoLink}}/modelarts/inference-job">{{$.i18n.Tr "repo.modelarts.infer_job"}}</a>
<a class="active item" href="{{.RepoLink}}/cloudbrain/benchmark">{{$.i18n.Tr "repo.modelarts.evaluate_job"}}</a>
</div>
</div>
<div class="column right aligned">
<a class="ui compact orange basic icon button" href="{{$.RepoLink}}/cloudbrain/123/rate?isObjectDetcionAll=true" style="box-shadow: none;" target="_blank"><i class="large ri-trophy-fill middle aligned icon"></i>基准测试排行榜</a>
{{if .Permission.CanWrite $.UnitTypeCloudBrain}}
<a class="ui green button" href="{{.RepoLink}}/cloudbrain/benchmark/create">{{$.i18n.Tr "repo.modelarts.evaluate_job.new_job"}}</a>
{{else}}
<a class="ui disabled button" >{{$.i18n.Tr "repo.modelarts.evaluate_job.new_job"}}</a>
{{end}}
</div>
</div>
{{if eq 0 (len .Tasks)}}
<div class="ui placeholder segment bgtask-none">
<div class="ui icon header bgtask-header-pic"></div>
<div class="bgtask-content-header">未创建过评测任务</div>
<div class="bgtask-content">
{{if $.RepoIsEmpty}}
<div class="bgtask-content-txt">代码版本:您还没有初始化代码仓库,请先<a href="{{.RepoLink}}">创建代码版本;</a></div>
{{end}}
<div class="bgtask-content-txt">使用说明:可以参考启智AI协作平台<a href="https://git.openi.org.cn/zeizei/OpenI_Learning">小白训练营课程。</a></div>
</div>
</div>
{{else}}
<!-- 中下列表展示区 -->
<div class="ui grid">
<div class="row">
<div class="ui sixteen wide column">
<!-- 任务展示 -->
<div class="dataset list">

<!-- 表头 -->
<div class="ui grid stackable" style="background: #f0f0f0;;">
<div class="row">
<div class="three wide column padding0">
<span style="margin:0 6px">{{$.i18n.Tr "repo.cloudbrain_task"}}</span>
</div>
<div class="two wide column text center padding0">
<span>{{$.i18n.Tr "repo.modelarts.status"}}</span>
</div>
<div class="two wide column text center padding0">
<span>{{$.i18n.Tr "repo.modelarts.createtime"}}</span>
</div>
<div class="two wide column text center padding0">
<span>{{$.i18n.Tr "repo.cloudbrain_status_runtime"}}</span>
</div>
<div class="two wide column text center padding0">
<span>{{$.i18n.Tr "repo.modelarts.computing_resources"}}</span>
</div>
<div class="one wide column text center padding0">
<span>{{$.i18n.Tr "repo.cloudbrain_creator"}}</span>
</div>
<div class="three wide column text center padding0">
<span>{{$.i18n.Tr "repo.cloudbrain_operate"}}</span>
</div>
</div>
</div>

{{range .Tasks}}
<div class="ui grid stackable item">
<div class="row">
<!-- 任务名 -->
<div class="three wide column padding0">
<a class="title" href="{{$.Link}}/{{.JobID}}" title="{{.JobName}}" style="font-size: 14px;">
<span class="fitted" style="width: 90%;vertical-align: middle;">{{.JobName}}</span>
</a>
</div>
<!-- 任务状态 -->
<div class="two wide column padding0" style="padding-left: 2.2rem !important;">
<span class="job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}" data-version="{{.VersionName}}">
<span><i id="{{.JobID}}-icon" style="vertical-align: middle;" class="{{.Status}}"></i><span id="{{.JobID}}-text" style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span>
</span>
</div>
<!-- 任务创建时间 -->
<div class="two wide column text center padding0">
<span style="font-size: 12px;" class="">{{TimeSinceUnix .Cloudbrain.CreatedUnix $.Lang}}</span>
</div>
<!-- 任务运行时间 -->
<div class="two wide column text center padding0">
<span style="font-size: 12px;" id="duration-{{.JobID}}">{{.TrainJobDuration}}</span>
</div>
<!-- 计算资源 -->
<div class="two wide column text center padding0">
<span style="font-size: 12px;">{{.ComputeResource}}</span>
</div>
<!-- 创建者 -->
<div class="one wide column text center padding0">
{{if .User.Name}}
<a href="{{AppSubUrl}}/{{.User.Name}}" title="{{.User.Name}}"><img class="ui avatar image" src="{{.User.RelAvatarLink}}"></a>
{{else}}
<a title="Ghost"><img class="ui avatar image" src="{{AppSubUrl}}/user/avatar/Ghost/-1"></a>
{{end}}
</div>

<div class="three wide column text center padding0">
<div class="ui compact buttons" >
<!-- 停止任务 -->
<form id="stopForm-{{.JobID}}" style="margin-left:-1px;">
{{$.CsrfTokenHtml}}
{{if .CanDel}}
<a id="stop-model-debug-{{.JobID}}" class='ui basic {{if eq .Status "STOPPED" "FAILED" "START_FAILED" "STOPPING" "CREATING" "STARTING" "SUCCEEDED"}}disabled {{else}}blue {{end}}button' onclick='stopDebug("{{.JobID}}","{{$.RepoLink}}/cloudbrain/benchmark/{{.JobID}}/stop")'>
{{$.i18n.Tr "repo.stop"}}
</a>
{{else}}
<a class="ui basic disabled button">
{{$.i18n.Tr "repo.stop"}}
</a>
{{end}}
</form>

<a class="ui basic button {{if $.IsSigned}} blue{{else}} disabled{{end}}" href="{{$.RepoLink}}/cloudbrain/{{.JobID}}/rate" target="_blank">
评分
</a>

<!-- 删除任务 -->
<form id="delForm-{{.JobID}}" action="{{if eq .ComputeResource "CPU/GPU"}}{{$.RepoLink}}/cloudbrain/benchmark{{else}}{{$.RepoLink}}/modelarts/notebook{{end}}/{{.JobID}}/del" method="post">
<input type="hidden" name="debugListType" value="all">
{{$.CsrfTokenHtml}}
{{if .CanDel}}
<a id="model-delete-{{.JobID}}" class='ui basic {{if eq .Status "STOPPED" "FAILED" "START_FAILED" "SUCCEEDED"}}blue {{else}}disabled {{end}}button' onclick="assertDelete(this)" style="border-radius: .28571429rem;">
{{$.i18n.Tr "repo.delete"}}
</a>
{{else}}
<a class="ui basic button disabled" onclick="assertDelete(this)" style="border-radius: .28571429rem;">
{{$.i18n.Tr "repo.delete"}}
</a>
{{end}}
</form>
</div>
</div>
</div>
</div>
{{end}}

<!--
<div class="" style="margin-top: 3.0em;">
<img class="ui middle aligned tiny image" src="/img/ranking_list.jpg">
<a class="ui blue" href="{{$.RepoLink}}/cloudbrain/123/rate?isObjectDetcionAll=true" target="_blank">目标检测算法排行榜</a>
</div>
-->
{{template "base/paginate" .}}
</div>

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

</div>

</div>
</div>

</div>

<!-- 确认模态框 -->
<div id="deletemodel">
<div class="ui basic modal">
<div class="ui icon header">
<i class="trash icon"></i> 删除任务
</div>

<div class="content">
<p>你确认删除该任务么?此任务一旦删除不可恢复。</p>
</div>
<div class="actions">
<div class="ui red basic inverted cancel button">
<i class="remove icon"></i> 取消操作
</div>
<div class="ui green basic inverted ok button">
<i class="checkmark icon"></i> 确定操作
</div>
</div>
</div>
</div>

</div>
{{template "base/footer" .}}

<script>
console.log({{.Tasks}})
// 调试和评分新开窗口
function stop(obj) {
if (obj.style.color != "rgb(204, 204, 204)") {
obj.target = '_blank'
} else {
return
}
}

// 删除时用户确认
function assertDelete(obj) {
if (obj.style.color == "rgb(204, 204, 204)") {
return
} else {
var delId = obj.parentNode.id
flag = 1;
$('.ui.basic.modal')
.modal({
onDeny: function() {
flag = false
},
onApprove: function() {
document.getElementById(delId).submit()
flag = true
},
onHidden: function() {
if (flag == false) {
$('.alert').html('您已取消操作').removeClass('alert-success').addClass('alert-danger').show().delay(1500).fadeOut();
}
}
})
.modal('show')
}
}
function runtime(time){
if(time){
let hours = time/3600000<10 ? "0"+parseInt(time/3600000):parseInt(time/3600000)
let miuns = time%3600000/60000<10 ? "0"+parseInt(time%3600000/60000):parseInt(time%3600000/60000)
let seconds = time%60000/1000<10 ? "0"+parseInt(time%60000/1000):parseInt(time%60000/1000)
return hours + ":" + miuns + ":" + seconds
}else{
return "00:00:00"
}
}

// 加载任务状态
var timeid = window.setInterval(loadJobStatus, 15000);
$(document).ready(loadJobStatus);
function loadJobStatus() {
$(".job-status").each((index, job) => {
const jobID = job.dataset.jobid;
const repoPath = job.dataset.repopath;
const computeResource = job.dataset.resource
const initArray = ['STOPPED','FAILED','START_FAILED','CREATE_FAILED','SUCCEEDED']
if (initArray.includes(job.textContent.trim())) {
return
}
const diffResource = computeResource == "NPU" ? 'modelarts/notebook' : 'cloudbrain'
$.get(`/api/v1/repos/${repoPath}/${diffResource}/${jobID}`, (data) => {
const jobID = data.JobID
const status = data.JobStatus
if (status != job.textContent.trim()) {
$('#' + jobID+'-icon').removeClass().addClass(status)
$('#' + jobID+ '-text').text(status)
}
if(status==="RUNNING"){
$('#model-debug-'+jobID).removeClass('disabled').addClass('blue').text('调试').css("margin","0 1rem")
$('#model-image-'+jobID).removeClass('disabled').addClass('blue')
}
if(status!=="RUNNING"){
// $('#model-debug-'+jobID).removeClass('blue')
// $('#model-debug-'+jobID).addClass('disabled')
$('#model-image-'+jobID).removeClass('blue').addClass('disabled')
}
if(["CREATING","STOPPING","WAITING","STARTING"].includes(status)){
$('#model-debug-'+jobID).removeClass('blue').addClass('disabled')
}
if(['STOPPED','FAILED','START_FAILED','CREATE_FAILED','SUCCEEDED'].includes(status)){
$('#model-debug-'+jobID).removeClass('disabled').addClass('blue').text('再次调试').css("margin","0")
}
if(["RUNNING","WAITING"].includes(status)){
$('#stop-model-debug-'+jobID).removeClass('disabled').addClass('blue')
}
if(["CREATING","STOPPING","STARTING","STOPPED","FAILED","START_FAILED","SUCCEEDED"].includes(status)){
$('#stop-model-debug-'+jobID).removeClass('blue').addClass('disabled')
}
if(status==="STOPPED" || status==="FAILED"|| status==="START_FAILED"){
$('#model-delete-'+jobID).removeClass('disabled').addClass('blue')
}else{
$('#model-delete-'+jobID).removeClass('blue').addClass('disabled')
}
}).fail(function(err) {
console.log(err);
});
});
};
// 获取弹窗
var modal = document.getElementById('imageModal');

// 打开弹窗的按钮对象
var btns = document.getElementsByClassName("imageBtn");

// 获取 <span> 元素,用于关闭弹窗
var spans = document.getElementsByClassName('close');

// 点击按钮打开弹窗
for (i = 0; i < btns.length; i++) {
btns[i].onclick = function() {
modal.style.display = "block";
}
}

// 点击 <span> (x), 关闭弹窗
for (i = 0; i < spans.length; i++) {
spans[i].onclick = function() {
modal.style.display = "none";
}
}

// 在用户点击其他地方时,关闭弹窗
window.onclick = function(event) {
if (event.target == modal) {
modal.style.display = "none";
}
}
function stopDebug(JobID,stopUrl){
$.ajax({
type:"POST",
url:stopUrl,
data:$('#stopForm-'+JobID).serialize(),
success:function(res){
if(res.result_code==="0"){
$('#' + JobID+'-icon').removeClass().addClass(res.status)
$('#' + JobID+ '-text').text(res.status)
if(res.status==="STOPPED"){
$('#model-debug-'+JobID).removeClass('disabled').addClass('blue').text("再次调试").css("margin","0")
$('#model-image-'+JobID).removeClass('blue').addClass('disabled')
$('#stop-model-debug-'+JobID).removeClass('blue').addClass('disabled')
$('#model-delete-'+JobID).removeClass('disabled').addClass('blue')
}
else{
$('#model-debug-'+JobID).removeClass('blue').addClass('disabled')
$('#stop-model-debug-'+JobID).removeClass('blue').addClass('disabled')
}
}else{
$('.alert').html(res.error_msg).removeClass('alert-success').addClass('alert-danger').show().delay(2000).fadeOut();
}
},
error :function(res){
console.log(res)

}
})

}

</script>

+ 233
- 0
templates/repo/cloudbrain/benchmark/new.tmpl View File

@@ -0,0 +1,233 @@
{{template "base/head" .}}
<style>

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

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

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

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

</style>
<!-- <div class="ui page dimmer">
<div class="ui text loader">{{.i18n.Tr "loading"}}</div>
</div> -->
<div id="mask">
<div id="loadingPage">
<div class="rect1"></div>
<div class="rect2"></div>
<div class="rect3"></div>
<div class="rect4"></div>
<div class="rect5"></div>
</div>
</div>
<div class="repository">
{{template "repo/header" .}}
<div class="ui container">
{{template "base/alert" .}}
<h4 class="ui top attached header">
{{.i18n.Tr "repo.modelarts.evaluate_job.new_job"}}
</h4>
<div class="ui attached segment">
<!-- equal width -->
<form class="ui form" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}}
<input type="hidden" name="action" value="update">
<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: 80%;" name="job_name" id="trainjob_job_name" placeholder={{.i18n.Tr "repo.modelarts.train_job.job_name"}} value="{{.job_name}}" tabindex="3" autofocus required maxlength="254">
</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="254" placeholder={{.i18n.Tr "repo.modelarts.train_job.new_place"}} onchange="this.value=this.value.substring(0, 255)" onkeydown="this.value=this.value.substring(0, 255)" onkeyup="this.value=this.value.substring(0, 255)"></textarea>
</div>

<div class="required unite min_title inline field">
<label style="font-weight: normal;">GPU类型</label>
<select id="cloudbrain_gpu_type" class="ui search dropdown" placeholder="选择GPU类型" style='width:385px' name="gpu_type">
{{range .benchmark_gpu_types}}
<option value="{{.Queue}}">{{.Value}}</option>
{{end}}
</select>
</div>


<div class="required unite inline min_title fields" style="width: 90%;">
<div class="required eight wide field">
<label style="font-weight: normal;white-space: nowrap;">{{.i18n.Tr "repo.cloudbrain.benchmark.evaluate_type"}}</label>
<span>&nbsp;</span>
<select class="ui fluid selection search dropdown" id="benchmark_types_id" name="benchmark_types_id" >
{{range .benchmark_types}}
<option value="{{.Id}}">{{.First}}</option>
{{end}}
</select>
</div>
<div class="eight wide field" id="engine_name">
<label style="font-weight: normal;white-space: nowrap;">{{.i18n.Tr "repo.cloudbrain.benchmark.evaluate_child_type"}}</label>
<select class="ui fluid selection dropdown nowrap" id="benchmark_child_types_id" style='width: 100%;' name="benchmark_child_types_id">
</select>
</div>
</div>

<div class="required unite min_title inline field">
<label style="font-weight: normal;">{{.i18n.Tr "repo.cloudbrain.benchmark.evaluate_mirror"}}</label>
<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
<input type="text" list="cloudbrain_image" placeholder="选择镜像" name="image" value="{{.image}}" class="required autofocus" style='width:492px;' maxlength="254">
<i class="times circle outline icon icons" style="visibility: hidden;" onclick="clearValue()"></i>
<datalist class="ui search" id="cloudbrain_image" style='width:385px;' name="image">
{{range .images}}
<option name="image" value="{{.Place}}">{{.PlaceView}}</option>
{{end}}
{{range .public_images}}
<option name="image" value="{{.Place}}">{{.PlaceView}}</option>
{{end}}
</datalist>
</div>


<div class="required unite min_title inline field">
<label style="font-weight: normal;">资源规格</label>
<select id="cloudbrain_resource_spec" class="ui search dropdown" placeholder="选择资源规格" style='width:385px' name="resource_spec_id">
{{range .benchmark_resource_specs}}
<option name="resource_spec_id" value="{{.Id}}">GPU数:{{.GpuNum}},CPU数:{{.CpuNum}},内存(MB):{{.MemMiB}},共享内存(MB):{{.ShareMemMiB}}</option>
{{end}}
</select>
</div>

<div class="inline unite min_title field required">
<label style="font-weight: normal;">{{.i18n.Tr "repo.cloudbrain.benchmark.evaluate_train"}}</label>
<input disabled="disabled" style="width: 33.5%;" name="train_file" id="train_file" value="train.py" tabindex="3" autofocus required maxlength="254" >
<a href="https://git.openi.org.cn/CV_benchmark/CV_reID_benchmark" target="_blank">查看样例</a>
</div>

<div class="inline unite min_title field required">
<label style="font-weight: normal;">{{.i18n.Tr "repo.cloudbrain.benchmark.evaluate_test"}}</label>
<input disabled="disabled" style="width: 33.5%;" name="test_file" id="test_file" value="test.py" tabindex="3" autofocus required maxlength="254" >
<a href="https://git.openi.org.cn/CV_benchmark/CV_reID_benchmark" target="_blank">查看样例</a>
</div>

<div class="inline unite min_title field">
<button class="ui create_train_job green button">
{{.i18n.Tr "repo.cloudbrain.new"}}
</button>
<a class="ui button" href="/">{{.i18n.Tr "repo.cloudbrain.cancel"}}</a>
</div>
<!-- 模态框 -->
</form>
</div>
</div>
</div>
{{template "base/footer" .}}

<script>
let repolink = {{.RepoLink}}

let url_href = window.location.pathname.split('create')[0]
$(".ui.button").attr('href',url_href)

$('.menu .item')
.tab();

$('#benchmark_types_id').change(function(){
setChildType();
})

function setChildType(){
let type_id = $('#benchmark_types_id').val();
$.get(`${repolink}/cloudbrain/benchmark/get_child_types?benchmark_type_id=${type_id}`, (data) => {
console.log(JSON.stringify(data))
const n_length = data['child_types'].length
let html=''
for (let i=0;i<n_length;i++){
html += `<option value="${data['child_types'][i].id}">${data['child_types'][i].value}</option>`;
}
document.getElementById("benchmark_child_types_id").innerHTML=html;
})
}

document.onreadystatechange = function() {
if (document.readyState === "complete") {
setChildType();
}
}
function validate(){
$('.ui.form')
.form({
on: 'blur',
inline:true,
fields: {
image:{
identifier : 'image',
rules: [
{
type: 'empty',
prompt : '选择一个镜像'
}
]

}
},
onSuccess: function(){
// $('.ui.page.dimmer').dimmer('show')
document.getElementById("mask").style.display = "block"
},
onFailure: function(e){
return false;
}
})
}


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

+ 458
- 0
templates/repo/cloudbrain/benchmark/show.tmpl View File

@@ -0,0 +1,458 @@
{{template "base/head" .}}
<style>
.according-panel-heading{
box-sizing: border-box;
padding: 8px 16px;
color: #252b3a;
background-color: #f2f5fc;
line-height: 1.5;
cursor: pointer;
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
-khtml-user-select: none;
user-select: none;
}
.accordion-panel-title {
margin-top: 0;
margin-bottom: 0;
color: #252b3a;
}
.accordion-panel-title-content{
vertical-align: middle;
display: inline-block;
width: calc(100% - 32px);
cursor: default;
}
.acc-margin-bottom {
margin-bottom: 5px;
}
.title_text {
font-size: 12px;
}
.ac-display-inblock {
display: inline-block;
}
.cti-mgRight-sm {
margin-right: 8px;
}
.ac-text-normal {
font-size: 14px;
color: #575d6c;
}
.uc-accordionTitle-black {
color: #333;
}
.accordion-border{
border:1px solid #cce2ff;
}
.padding0{
padding: 0 !important;
}
.content-pad{
padding: 15px 35px;
}
.content-margin{
margin:10px 5px ;
}
.tab_2_content {
min-height: 360px;
margin-left: 10px;
}
.ac-grid {
display: block;
*zoom: 1;
}
.ac-grid-col {
float: left;
width: 100%;
}
.ac-grid-col2 .ac-grid-col {
width: 50%;
}
.ti-form {
text-align: left;
max-width: 100%;
vertical-align: middle;
}
.ti-form>tbody {
font-size: 12px;
}
.ti-form>tbody, .ti-form>tbody>tr {
vertical-align: inherit;
}
.ti-text-form-label {
padding-bottom: 20px;
padding-right: 20px;
color: #8a8e99;
font-size: 12px;
white-space: nowrap !important;
width: 80px;
line-height: 30px;
}
.ti-text-form-content{
line-height: 30px;
padding-bottom: 20px;
}
.ti-form>tbody>tr>td {
vertical-align: top;
white-space: normal;
}
td, th {
padding: 0;
}
.ac-grid-col .text-span {
width: 450px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.redo-color{
color: #3291F8;
}
.ti-action-menu-item:not(:last-child){
margin-right: 10px;
padding-right: 11px;
text-decoration: none!important;
color: #526ecc;
cursor: pointer;
display: inline-block;
-moz-user-select: none;
-webkit-user-select: none;
-ms-user-select: none;
-khtml-user-select: none;
user-select: none;
position: relative;
}
.ti-action-menu-item:not(:last-child):after {
content: "";
display: inline-block;
position: absolute;
height: 12px;
right: 0;
top: 50%;
-webkit-transform: translateY(-6px);
-ms-transform: translateY(-6px);
-o-transform: translateY(-6px);
transform: translateY(-6px);
border-right: 1px solid #dfe1e6;
}
.text-width80{
width: 100px;
line-height: 30px;
}
.border-according{
border: 1px solid #dfe1e6;
}
.disabled {
cursor: default;
pointer-events: none;
color: rgba(0,0,0,.6) !important;
opacity: .45 !important;
}
.pad20{
border:0px !important;
}
.model_file_bread{
margin-bottom: -0.5rem !important;
padding-left: 1rem;
padding-top: 0.5rem ;
}
</style>
<div id="mask">
<div id="loadingPage">
<div class="rect1"></div>
<div class="rect2"></div>
<div class="rect3"></div>
<div class="rect4"></div>
<div class="rect5"></div>
</div>
</div>
<div class="repository">
{{template "repo/header" .}}
<div class="ui container">
<h4 class="ui header" id="vertical-segment">
<div class="ui breadcrumb">
<a class="section" href="{{.RepoLink}}/debugjob?debugListType=all">
{{.i18n.Tr "repo.cloudbrain"}}
</a>
<div class="divider"> / </div>
<a class="section" href="{{$.RepoLink}}/cloudbrain/benchmark">
{{$.i18n.Tr "repo.modelarts.evaluate_job"}}
</a>
<div class="divider"> / </div>
<div class="active section">{{.jobName}}</div>
</div>
</h4>
{{range $k ,$v := .version_list_task}}
<div class="ui accordion border-according" id="accordion{{.VersionName}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}" data-version="{{.VersionName}}">
<div class="{{if eq $k 0}}active{{end}} title padding0">
<div class="according-panel-heading">
<div class="accordion-panel-title">
<i class="dropdown icon"></i>
<span class="accordion-panel-title-content">
<span>
<div class="ac-display-inblock title_text acc-margin-bottom">
<span class="cti-mgRight-sm">{{TimeSinceUnix1 .CreatedUnix}}</span>
<span class="cti-mgRight-sm">{{$.i18n.Tr "repo.modelarts.status"}}:
<span id="{{.VersionName}}-status-span"><i id="icon" style="vertical-align: middle;" class="{{.Status}}"></i><span id="text" style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span>
</span>
<span class="cti-mgRight-sm">{{$.i18n.Tr "repo.modelarts.train_job.dura_time"}}:</span>
<span class="cti-mgRight-sm uc-accordionTitle-black" id="{{.VersionName}}-duration-span">{{$.duration}}</span>
</div>
</span>
</span>
</div>
</div>
</div>
<div class="{{if eq $k 0}}active{{end}} content">
<div class="content-pad">
<div class="ui pointing secondary menu" style="border-bottom: 1px solid rgba(34,36,38,.15);">
<a class="active item" data-tab="first{{$k}}">{{$.i18n.Tr "repo.modelarts.train_job.config"}}</a>
<a class="item" data-tab="second{{$k}}" onclick="loadLog({{.VersionName}})">{{$.i18n.Tr "repo.modelarts.log"}}</a>
</div>
<div class="ui tab active" data-tab="first{{$k}}">
<div style="padding-top: 10px;">
<div class="tab_2_content">
<div class="ac-grid ac-grid-col2">
<div class="ac-grid-col">
<table class="ti-form">
<tbody class="ti-text-form">
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
{{$.i18n.Tr "repo.cloudbrain_task"}}
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w">
{{.JobName}}
</div>
</td>
</tr>
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
{{$.i18n.Tr "repo.modelarts.status"}}
</td>

<td class="ti-text-form-content">
<div class="text-span text-span-w" id="{{.VersionName}}-status">
{{.Status}}
</div>
</td>
</tr>
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
{{$.i18n.Tr "repo.modelarts.train_job.start_time"}}
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w">
<span style="font-size: 12px;" class="">{{TimeSinceUnix1 .CreatedUnix}}</span>
</div>
</td>
</tr>
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
{{$.i18n.Tr "repo.modelarts.train_job.dura_time"}}
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w" id="{{.VersionName}}-duration">
{{$.duration}}
</div>
</td>
</tr>
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
镜像
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w" id="{{.VersionName}}-mirror">
{{.Image}}
</div>
</td>
</tr>
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
类型
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w" id="{{.VersionName}}-BenchmarkTypeName">
{{$.BenchmarkTypeName}}
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div class="ac-grid-col">
<table class="ti-form">
<tbody class="ti-text-form">
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
训练程序
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w">
train.py
</div>
</td>
</tr>
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
测试程序
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w">
test.py
</div>
</td>
</tr>
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
{{$.i18n.Tr "repo.modelarts.train_job.description"}}
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w" title="{{.Description}}">
{{.Description}}
</div>
</td>
</tr>

<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
{{$.i18n.Tr "repo.modelarts.train_job.standard"}}
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w">
{{$.resource_spec}}
</div>
</td>
</tr>

<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
创建者
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w" id="{{.VersionName}}-mirror">
{{.User.Name}}
</div>
</td>
</tr>
<tr class="ti-no-ng-animate">
<td class="ti-no-ng-animate ti-text-form-label text-width80">
子类型
</td>
<td class="ti-text-form-content">
<div class="text-span text-span-w" id="{{.VersionName}}-BenchmarkChildTypeName">
{{$.BenchmarkChildTypeName}}
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<div class="ui tab" data-tab="second{{$k}}">
<div>
<div class="ui message message{{.VersionName}}" style="display: none;">
<div id="header"></div>
</div>
<div class="ui attached log" id="log{{.VersionName}}" style="height: 300px !important; overflow: auto;">
<input type="hidden" name="end_line" value>
<input type="hidden" name="start_line" value>
<pre id="log_file{{.VersionName}}"></pre>
</div>
</div>
</div>
</div>
</div>
</div>
{{end}} {{template "base/paginate" .}}
</div>
<!-- 确认模态框 -->
<div id="deletemodel">
<div class="ui basic modal">
<div class="ui icon header">
<i class="trash icon"></i> 删除任务
</div>
<div class="content">
<p>你确认删除该任务么?此任务一旦删除不可恢复。</p>
</div>
<div class="actions">
<div class="ui red basic inverted cancel button">
<i class="remove icon"></i> 取消操作
</div>
<div class="ui green basic inverted ok button">
<i class="checkmark icon"></i> 确定操作
</div>
</div>
</div>
</div>
</div>
{{template "base/footer" .}}

<script>
$('.menu .item').tab()

$(document).ready(function(){
$('.ui.accordion').accordion({selector:{trigger:'.icon'}});
});
$(document).ready(function(){
$('.secondary.menu .item').tab();
});
let userName
let repoPath
let jobID
$(document).ready(function(){
let url = window.location.href;
let urlArr = url.split('/')
userName = urlArr.slice(-5)[0]
repoPath = urlArr.slice(-4)[0]
jobID = urlArr.slice(-1)[0]
})


function loadLog(version_name){
document.getElementById("mask").style.display = "block"
$.get(`/api/v1/repos/${userName}/${repoPath}/cloudbrain/${jobID}/log?version_name=${version_name}&lines=50&order=asc`, (data) => {
$('input[name=end_line]').val(data.EndLine)
$('input[name=start_line]').val(data.StartLine)
$(`#log_file${version_name}`).text(data.Content)
document.getElementById("mask").style.display = "none"
}).fail(function(err) {
console.log(err);
document.getElementById("mask").style.display = "none"
});
}
</script>

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

@@ -150,13 +150,10 @@
<input name="job_name" id="cloudbrain_job_name" placeholder="任务名称" value="{{.job_name}}" tabindex="3" autofocus required maxlength="255" onkeyup="this.value=this.value.replace(/[, ]/g,'')"> <input name="job_name" id="cloudbrain_job_name" placeholder="任务名称" value="{{.job_name}}" tabindex="3" autofocus required maxlength="255" onkeyup="this.value=this.value.replace(/[, ]/g,'')">
</div> </div>


<div class="inline required field" style="{{if ((.is_benchmark_enabled) or (.is_snn4imagenet_enabled) or (.is_brainscore_enabled))}}display:block;{{else}}display:none;{{end}}">
<div class="inline required field">
<label>任务类型</label> <label>任务类型</label>
<select id="cloudbrain_job_type" class="ui search dropdown" placeholder="选择任务类型" style='width:385px' name="job_type"> <select id="cloudbrain_job_type" class="ui search dropdown" placeholder="选择任务类型" style='width:385px' name="job_type">
<option name="job_type" value="DEBUG">DEBUG</option> <option name="job_type" value="DEBUG">DEBUG</option>
{{if .is_benchmark_enabled}}
<option name="job_type" value="BENCHMARK">BENCHMARK</option>
{{end}}
{{if .is_snn4imagenet_enabled}} {{if .is_snn4imagenet_enabled}}
<option name="job_type" value="SNN4IMAGENET">SNN4IMAGENET</option> <option name="job_type" value="SNN4IMAGENET">SNN4IMAGENET</option>
{{end}} {{end}}


+ 295
- 0
templates/repo/courseHome.tmpl View File

@@ -0,0 +1,295 @@
{{template "base/head" .}}
<style>
.repository.file.list #repo-desc {
font-size: 1.0em;
margin-bottom: 1.0rem;
}
#contributorInfo > a:nth-child(n+26){
display:none;
}
#contributorInfo > a{
width: 2.0em;
float: left;
margin: .25em;
}
.edit-link{
vertical-align: top;
display: inline-block;
overflow: hidden;
word-break: keep-all;
white-space: nowrap;
text-overflow: ellipsis;
width: 16.5em;
}
#contributorInfo > a.circular{
height: 2.0em;
padding: 0;
overflow: hidden;
letter-spacing:1.0em;
text-indent: 0.6em;
line-height: 2.0em;
text-transform:capitalize;
color: #FFF;
}
#contributorInfo > a.circular:nth-child(9n+1){
background-color: #4ccdec;
}
#contributorInfo > a.circular:nth-child(9n+2){
background-color: #e0b265;
}
#contributorInfo > a.circular:nth-child(9n+3){
background-color: #d884b7;
}
#contributorInfo > a.circular:nth-child(9n+4){
background-color: #8c6bdc;
}
#contributorInfo > a.circular:nth-child(9n+5){
background-color: #3cb99f;
}
#contributorInfo > a.circular:nth-child(9n+6){
background-color: #6995b9;
}
#contributorInfo > a.circular:nth-child(9n+7){
background-color: #ab91a7;
}
#contributorInfo > a.circular:nth-child(9n+8){
background-color: #bfd0aa;
}
.vue_menu {
cursor: auto;
position: absolute;
outline: none;
margin: 0em;
padding: 0em 0em;
background: #fff;
font-size: 1em;
text-shadow: none;
text-align: left;
/* -webkit-box-shadow: 0px 2px 3px 0px rgb(34 36 38 / 15%); */
box-shadow: 0px 2px 3px 0px rgba(34, 36, 38, 0.15);
border: 1px solid rgba(34,36,38,0.15);
border-radius: 0.28571429rem;
-webkit-transition: opacity 0.1s ease;
transition: opacity 0.1s ease;
z-index: 11;
will-change: transform, opacity;
-webkit-animation-iteration-count: 1;
animation-iteration-count: 1;
-webkit-animation-duration: 300ms;
animation-duration: 300ms;
-webkit-animation-timing-function: ease;
animation-timing-function: ease;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
}
.repo-topic{
background-color: rgba(179, 219, 219, 0.4) !important;
color: #0366D6 !important;
font-weight: 200 !important;
}



</style>
<div class="repository file list">
{{template "repo/header" .}}
<div class="ui container">
{{template "base/alert" .}}
<div class="hide" id="validate_prompt">
<span id="count_prompt">{{.i18n.Tr "repo.topic.count_prompt"}}</span>
<span id="format_prompt">{{.i18n.Tr "repo.topic.format_prompt"}}</span>
</div>
{{if .Repository.IsArchived}}
<div class="ui warning message">
{{.i18n.Tr "repo.archive.title"}}
</div>
{{end}}
<div>
<span>{{.i18n.Tr "repo.computing.Introduction"}}:</span>
{{if .Repository.DescriptionHTML}}
<span class="description" style="color: #8a8e99;">{{.Repository.DescriptionHTML}}</span>
{{else}}
<span class="no-description text-italic">{{.i18n.Tr "repo.no_desc"}}</span>
{{end}}
<!-- <span style="color: #8a8e99;">生课程的教学,在全国范围形成一批开放共享的教学材料。这类材料的风格、设想既不同于教材,也不同于IEEE CSs中对知识体系的描述,而是以一定的知识内容为背景,重在教师个人在教学实践中的心得,包括对某些内容独到的理解和课堂上的处理,等等。</span> -->
</div>
<div class="ui" id="repo-topics">
<div id="repo-topics1" style="display: inline-block;margin: 0.5rem 0;">
{{range .Topics}}
<a class="ui repo-topic small label topic" href="{{AppSubUrl}}/explore/repos?q={{.Name}}&topic=">{{.Name}}</a>
{{end}}
</div>
<a style="margin-left: 0.5rem;" id="manage_topic">
{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}<i style="cursor: pointer;" class="plus square outline icon"></i>{{end}}
{{.i18n.Tr "repo.issues.new.add_labels_title"}}
</a>
<div id="topic_edit" class="vue_menu" style="display:none;">
<div id="topic_edit1">

</div>
</div>

</div>



<div class="ui mobile reversed stackable grid" style="margin-top: -1.5rem;">
{{ $n := len .TreeNames}}
{{ $l := Subtract $n 1}}
<!-- If home page, show new PR. If not, show breadcrumb -->
<div class="ui ten wide tablet twelve wide computer column text right" style="margin-top: 1rem;">
<div class="right fitted item" id="file-buttons">
<div class="ui tiny blue buttons">
{{if .Repository.CanEnableEditor}}
{{if .CanAddFile}}
<a href="{{.RepoLink}}/_new/{{EscapePound .BranchName}}/{{EscapePound .TreePath}}" class="ui button">
{{.i18n.Tr "repo.editor.new_file"}}
</a>
{{end}}
{{if .CanUploadFile}}
<a href="{{.RepoLink}}/_upload/{{EscapePound .BranchName}}/{{EscapePound .TreePath}}" class="ui button">
{{.i18n.Tr "repo.editor.upload_file"}}
</a>
{{end}}
{{end}}
{{if and (ne $n 0) (not .IsViewFile) (not .IsBlame) }}
<a href="{{.RepoLink}}/commits/{{EscapePound .BranchNameSubURL}}/{{EscapePound .TreePath}}" class="ui button">
{{.i18n.Tr "repo.file_history"}}
</a>
{{end}}
</div>
</div>
</div>
</div>
<div class="ui container">
<div class="ui mobile reversed stackable grid">
<div class="ui ten wide tablet twelve wide computer column">
{{if .IsViewFile}}
{{template "repo/view_file" .}}
{{else if .IsBlame}}
{{template "repo/blame" .}}
{{else}}
<table id="repo-files-table" class="ui single line table">
<thead>
<tr class="commit-list">
<th colspan="2">
{{if .LatestCommitUser}}
<img class="ui avatar image img-12" src="{{.LatestCommitUser.RelAvatarLink}}" />
{{if .LatestCommitUser.FullName}}
<a href="{{AppSubUrl}}/{{.LatestCommitUser.Name}}"><strong>{{.LatestCommitUser.FullName}}</strong></a>
{{else}}
<a href="{{AppSubUrl}}/{{.LatestCommitUser.Name}}"><strong>{{if .LatestCommit.Author}}{{.LatestCommit.Author.Name}}{{else}}{{.LatestCommitUser.Name}}{{end}}</strong></a>
{{end}}
{{else}}

{{if .LatestCommit.Author}}
<img class="ui avatar image img-12" src="{{AvatarLink .LatestCommit.Author.Email}}" />
<strong>{{.LatestCommit.Author.Name}}</strong>
{{end}}
{{end}}
{{if .LatestCommit.Author}}
<span style="margin: 0 0.5rem;color: #767676">{{TimeSince .LatestCommit.Author.When $.Lang}}</span>
{{end}}
{{ $commitLink:= printf "%s/commit/%s" .RepoLink .LatestCommit.ID }}
<span class="grey commit-summary" title="{{.LatestCommit.Summary}}"><span class="message-wrapper">{{RenderCommitMessageLinkSubject .LatestCommit.Message $.RepoLink $commitLink $.Repository.ComposeMetas}}</span>
{{if IsMultilineCommitMessage .LatestCommit.Message}}
<button class="basic compact mini ui icon button commit-button"><i class="ellipsis horizontal icon"></i></button>
<pre class="commit-body" style="display: none;">{{RenderCommitBody .LatestCommit.Message $.RepoLink $.Repository.ComposeMetas}}</pre>
{{end}}
</span>
</th>
</tr>
</thead>
<tbody>
{{if .HasParentPath}}
<tr class="has-parent">
<td colspan="3">{{svg "octicon-mail-reply" 16}}<a href="{{EscapePound .BranchLink}}{{.ParentPath}}">..</a></td>
</tr>
{{end}}
{{range $item := .Files}}
{{$entry := index $item 0}}
{{$commit := index $item 1}}
<tr>
{{if $entry.IsSubModule}}
<td>
<span class="truncate">
{{svg "octicon-inbox" 16}}
{{$refURL := $commit.RefURL AppUrl $.Repository.FullName}}
{{if $refURL}}
<a href="{{$refURL}}">{{$entry.Name}}</a> @ <a href="{{$refURL}}/commit/{{$commit.RefID}}">{{ShortSha $commit.RefID}}</a>
{{else}}
{{$entry.Name}} @ {{ShortSha $commit.RefID}}
{{end}}
</span>
</td>
{{else}}
<td class="name thirteen wide">
<span class="truncate">
{{if $entry.IsDir}}
{{$subJumpablePathName := $entry.GetSubJumpablePathName}}
{{$subJumpablePath := SubJumpablePath $subJumpablePathName}}
{{svg "octicon-file-directory" 16}}
<a href="{{EscapePound $.TreeLink}}/{{EscapePound $subJumpablePathName}}" title="{{$subJumpablePathName}}">
{{if eq (len $subJumpablePath) 2}}
<span class="jumpable-path">{{index $subJumpablePath 0}}</span>{{index $subJumpablePath 1}}
{{else}}
{{index $subJumpablePath 0}}
{{end}}
</a>
{{else}}
<i class="ri-file-pdf-line" style="font-size: 16px;margin-left: 3px;margin-right: 5px;vertical-align: text-top;color: #FA8C16;"></i>
<a href="{{EscapePound $.TreeLink}}/{{EscapePound $entry.Name}}" title="{{$entry.Name}}">{{$entry.Name}}</a>
{{end}}
</span>
</td>
{{end}}
<td class="text right age one wide" style="text-align: right;">{{TimeSince $commit.Committer.When $.Lang}}</td>
</tr>
{{end}}
</tbody>
</table>
{{if .ReadmeExist}}
{{template "repo/view_file" .}}
{{end}}
{{end}}
</div>
<!-- 贡献者框 -->
<div class="ui six wide tablet four wide computer column">
<div style="border-radius: 5px;border: 1px solid rgba(225, 227, 230, 100);padding: 1rem;">
<h4 class="ui header" style="border-bottom: 1px solid rgba(225, 227, 230, 100);padding: 0.5rem 0;">
{{$lenCon := len .ContributorInfo}}
{{if lt $lenCon 25 }}
<strong>{{.i18n.Tr "home.contributors"}} ({{len .ContributorInfo}})</strong>
{{else}}
<strong>{{.i18n.Tr "home.contributors"}} ({{len .ContributorInfo}}+)</strong>
{{end}}
<div class="ui right">
<a class="membersmore text grey" href="{{.RepoLink}}/contributors?type={{if .IsViewBranch}}branch{{else}}tag{{end}}&name={{.BranchName}}">{{.i18n.Tr "repo.computing.all"}} {{svg "octicon-chevron-right" 16}}</a>
</div>
</h4>
<div class="ui members" id="contributorInfo">
{{range .ContributorInfo}}
{{if .UserInfo}}
<a href="{{AppSubUrl}}/{{.UserInfo.Name}}"><img class="ui avatar image" src="{{.UserInfo.RelAvatarLink}}"></a>
{{else if .Email}}
<a href="mailto:{{.Email}}" class="circular ui button">{{.Email}}</a>
{{end}}
{{end}}
</div>
<div style="clear: both;"></div>
</div>
</div>
</div>
</div>

</div>
</div>

{{template "base/footer" .}}

+ 4
- 32
templates/repo/create.tmpl View File

@@ -2,43 +2,15 @@
<div class="repository new repo" style="margin-top: 40px;"> <div class="repository new repo" style="margin-top: 40px;">
<div class="ui middle very relaxed page grid"> <div class="ui middle very relaxed page grid">
<div class="column"> <div class="column">
<form class="ui form" action="{{.Link}}" method="post">
<form class="ui form" action="{{.Link}}" method="post" id="create_repo_form">
{{.CsrfTokenHtml}} {{.CsrfTokenHtml}}
<h3 class="ui top attached header"> <h3 class="ui top attached header">
{{.i18n.Tr "new_repo"}} {{.i18n.Tr "new_repo"}}
</h3> </h3>
<div class="ui attached segment"> <div class="ui attached segment">
{{template "base/alert" .}} {{template "base/alert" .}}
<div class="inline required field {{if .Err_Owner}}error{{end}}">
<label>{{.i18n.Tr "repo.owner"}}</label>
<div class="ui selection owner dropdown">
<input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
<span class="text" title="{{.ContextUser.Name}}">
<img class="ui mini image" src="{{.ContextUser.RelAvatarLink}}">
{{.ContextUser.ShortName 20}}
</span>
<i class="dropdown icon"></i>
<div class="menu">
<div class="item" data-value="{{.SignedUser.ID}}" title="{{.SignedUser.Name}}">
<img class="ui mini image" src="{{.SignedUser.RelAvatarLink}}">
{{.SignedUser.ShortName 20}}
</div>
{{range .Orgs}}
<div class="item" data-value="{{.ID}}" title="{{.Name}}">
<img class="ui mini image" src="{{.RelAvatarLink}}">
{{.ShortName 20}}
</div>
{{end}}
</div>
</div>
<span class="help">{{.i18n.Tr "repo.repo_owner_helper"}}</span>
</div>

<div class="inline required field {{if .Err_RepoName}}error{{end}}">
<label for="repo_name">{{.i18n.Tr "repo.repo_name"}}</label>
<input id="repo_name" name="repo_name" value="{{.repo_name}}" autofocus required>
<span class="help"></span>
</div>
{{template "repo/repo_name" .}}
<!-- <div class="js-project-full-path" id="repoAdress" ></div> -->
<div class="inline field"> <div class="inline field">
<label>{{.i18n.Tr "repo.visibility"}}</label> <label>{{.i18n.Tr "repo.visibility"}}</label>
<div class="ui checkbox"> <div class="ui checkbox">
@@ -172,7 +144,7 @@
<br/> <br/>
<div class="inline field"> <div class="inline field">
<label></label> <label></label>
<button class="ui green button">
<button class="ui green button" id="submit_reponame">
{{.i18n.Tr "repo.create_repo"}} {{.i18n.Tr "repo.create_repo"}}
</button> </button>
<a class="ui button" href="{{AppSubUrl}}/">{{.i18n.Tr "cancel"}}</a> <a class="ui button" href="{{AppSubUrl}}/">{{.i18n.Tr "cancel"}}</a>


+ 111
- 0
templates/repo/createCourse.tmpl View File

@@ -0,0 +1,111 @@
<style>
#course_label::after{
display: inline-block;
vertical-align: top;
margin: -.2em 0 0 .2em;
content: '*';
color: #db2828;
}
</style>
{{template "base/head" .}}
<div class="repository new repo" style="margin-top: 40px;">
<div class="ui middle very relaxed page grid">
<div class="column">
<form class="ui form" action="{{.Link}}" method="post" id="create_repo_form">
{{.CsrfTokenHtml}}
<h3 class="ui top attached header">
{{.i18n.Tr "new_course"}}
</h3>
<div class="ui attached segment">
{{template "base/alert" .}}
<div class="inline required field {{if .Err_RepoName}}error{{end}}" >
<label for="Alias">{{.i18n.Tr "form.courseAlias"}}</label>
<input id="alias" name="alias" value="{{.alias}}" autofocus required>
<span class="help">{{.i18n.Tr "form.reponame_dash_dot_error"}}</span>
</div>
<div class="inline required fields" style="margin-bottom: 0;">
<label id="course_label" style="text-align: right;width: 250px!important;word-wrap: break-word;">{{.i18n.Tr "form.courseAdress"}}</label>
<div class="required field {{if .Err_Owner}}error{{end}}" style="padding: 0;">
<div class="ui selection owner dropdown" id="ownerDropdown">
<input type="hidden" id="uid" name="uid" value="{{.Owner.Name}}" required>
<div class="text" title="{{.Owner.Name}}">
<img class="ui mini image" src="{{.Owner.RelAvatarLink}}">
{{$.Owner.Name}}
</div>
</div>
</div>
<div class="ui interval" style="width: 0.6em;font-size: 2rem;line-height: 0px;text-align: center;">/</div>
<div class="required field {{if .Err_RepoName}}error{{end}}">
<input style="width: 100% !important;" id="repo_name" name="repo_name" value="{{.repo_name}}" autofocus required>
</div>
</div>
<span style="display: block;margin-bottom: 1em;" class="help">{{.i18n.Tr "form.repoadd_dash_dot_error"}}</span>
<div class="inline field" id="repoAdress" style="display: none;word-break: break-all;">
<label for="">{{.i18n.Tr "form.course_Adress"}}:</label>
<span style="flex: 1;"></span>
</div>

<div class="inline field" style="display: flex;align-items:center;">
<label style="margin: .035714em 1em 0 0;">{{.i18n.Tr "repo.model.manage.label"}}</label>
<div class="ui multiple search selection dropdown" id="dropdown_container">
<input type="hidden" name="topics" value="">
<div class="default text" id="default_text">{{.i18n.Tr "repo.repo_label_helpe"}}</div>
<div class="menu" id="course_label_item">
</div>
</div>
</div>

<div class="inline field {{if .Err_Description}}error{{end}}">
<label for="description">{{.i18n.Tr "course_desc"}}</label>
<textarea id="description" name="description" maxlength="254">{{.description}}</textarea>
</div>
<div class="inline field">
<label></label>
<button class="ui green button" id="submit_reponame">
{{.i18n.Tr "new_course"}}
</button>
<a class="ui button" href="javascript:history.go(-1)">{{.i18n.Tr "cancel"}}</a>
</div>
</div>
</form>
</div>
</div>
</div>
{{template "base/footer" .}}

<script>

$(document).ready(function(){
$('.ui.multiple.search.selection.dropdown')
.dropdown({
allowAdditions: true,
onChange: function(value, text, $selectedItem) {
$('#course_label_item').empty()
}
})
let defaultText = document.getElementById("default_text").offsetHeight
defaultText = defaultText>40 ? defaultText+12 :defaultText
$("#dropdown_container").css("height",defaultText)
$('input.search').bind('input propertychange', function (event) {
$("#dropdown_container").removeAttr("style");
const query = $('input.search').val()
if(!query){
$('#course_label_item').empty()
}else{
$.get(`/api/v1/topics/search?q=${query}`,(data)=>{
console.log(data)
if(data.topics.length!==0){
let html=''
$('#course_label_item').empty()
data.topics.forEach(element => {
html += `<div class="item" data-value="${element.topic_name}">${element.topic_name}</div>`
});
$('#course_label_item').append(html)
}
})
}
});
})
console.log()
</script>

+ 1
- 0
templates/repo/debugjob/index.tmpl View File

@@ -218,6 +218,7 @@
<a class="active item" href="{{.RepoLink}}/debugjob?debugListType=all">{{$.i18n.Tr "repo.modelarts.notebook"}}</a> <a class="active item" href="{{.RepoLink}}/debugjob?debugListType=all">{{$.i18n.Tr "repo.modelarts.notebook"}}</a>
<a class="item" href="{{.RepoLink}}/modelarts/train-job">{{$.i18n.Tr "repo.modelarts.train_job"}}</a> <a class="item" href="{{.RepoLink}}/modelarts/train-job">{{$.i18n.Tr "repo.modelarts.train_job"}}</a>
<a class="item" href="{{.RepoLink}}/modelarts/inference-job">{{$.i18n.Tr "repo.modelarts.infer_job"}}</a> <a class="item" href="{{.RepoLink}}/modelarts/inference-job">{{$.i18n.Tr "repo.modelarts.infer_job"}}</a>
<a class="item" href="{{.RepoLink}}/cloudbrain/benchmark">{{$.i18n.Tr "repo.modelarts.evaluate_job"}}</a>
</div> </div>
</div> </div>
<div class="column right aligned"> <div class="column right aligned">


+ 6
- 6
templates/repo/header.tmpl View File

@@ -24,7 +24,7 @@
{{end}} {{end}}
<a href="{{AppSubUrl}}/{{.Owner.Name}}">{{.Owner.Name}}</a> <a href="{{AppSubUrl}}/{{.Owner.Name}}">{{.Owner.Name}}</a>
<div class="divider"> / </div> <div class="divider"> / </div>
<a href="{{$.RepoLink}}">{{.Name}}</a>
<a href="{{$.RepoLink}}">{{.DisplayName}}</a>
{{if .RelAvatarLink}} {{if .RelAvatarLink}}
{{if .IsTemplate}} {{if .IsTemplate}}
{{if .IsPrivate}} {{if .IsPrivate}}
@@ -46,7 +46,7 @@
{{end}} {{end}}
{{if .IsArchived}}<i class="archive icon archived-icon"></i>{{end}} {{if .IsArchived}}<i class="archive icon archived-icon"></i>{{end}}
{{if .IsMirror}}<div class="fork-flag">{{$.i18n.Tr "repo.mirror_from"}} <a target="_blank" rel="noopener noreferrer" href="{{if .SanitizedOriginalURL}}{{.SanitizedOriginalURL}}{{else}}{{MirrorAddress $.Mirror}}{{end}}">{{if .SanitizedOriginalURL}}{{.SanitizedOriginalURL}}{{else}}{{MirrorAddress $.Mirror}}{{end}}</a></div>{{end}} {{if .IsMirror}}<div class="fork-flag">{{$.i18n.Tr "repo.mirror_from"}} <a target="_blank" rel="noopener noreferrer" href="{{if .SanitizedOriginalURL}}{{.SanitizedOriginalURL}}{{else}}{{MirrorAddress $.Mirror}}{{end}}">{{if .SanitizedOriginalURL}}{{.SanitizedOriginalURL}}{{else}}{{MirrorAddress $.Mirror}}{{end}}</a></div>{{end}}
{{if .IsFork}}<div class="fork-flag">{{$.i18n.Tr "repo.forked_from"}} <a href="{{.BaseRepo.Link}}">{{SubStr .BaseRepo.RelLink 1 -1}}</a></div>{{end}}
{{if .IsFork}}<div class="fork-flag">{{$.i18n.Tr "repo.forked_from"}} <a href="{{.BaseRepo.Link}}">{{.BaseRepo.FullDisplayName}}</a></div>{{end}}
{{if .IsGenerated}}<div class="fork-flag">{{$.i18n.Tr "repo.generated_from"}} <a href="{{.TemplateRepo.Link}}">{{SubStr .TemplateRepo.RelLink 1 -1}}</a></div>{{end}} {{if .IsGenerated}}<div class="fork-flag">{{$.i18n.Tr "repo.generated_from"}} <a href="{{.TemplateRepo.Link}}">{{SubStr .TemplateRepo.RelLink 1 -1}}</a></div>{{end}}
</div> </div>
{{if not .IsBeingCreated}} {{if not .IsBeingCreated}}
@@ -90,7 +90,7 @@
{{end}} {{end}}
<div class="ui tabs container"> <div class="ui tabs container">
{{if not .Repository.IsBeingCreated}} {{if not .Repository.IsBeingCreated}}
<div class="ui tabular stackable menu navbar">
<div class="ui tabular menu navbar">
{{if .Permission.CanRead $.UnitTypeCode}} {{if .Permission.CanRead $.UnitTypeCode}}
<div class="dropdown-menu"> <div class="dropdown-menu">
<a class="{{if or .PageIsViewCode .PageIsReleaseList .PageIsWiki .PageIsActivity .PageIsViewCode}}active{{end}} item hover_active" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/{{.BranchNameSubURL | EscapePound}}{{end}}"> <a class="{{if or .PageIsViewCode .PageIsReleaseList .PageIsWiki .PageIsActivity .PageIsViewCode}}active{{end}} item hover_active" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/{{.BranchNameSubURL | EscapePound}}{{end}}">
@@ -114,9 +114,9 @@
{{end}} {{end}}
</div> </div>
</div> </div>
{{end}} {{end}}




{{if .Permission.CanRead $.UnitTypeIssues}} {{if .Permission.CanRead $.UnitTypeIssues}}
@@ -152,7 +152,7 @@
{{if .Permission.CanRead $.UnitTypeCloudBrain}} {{if .Permission.CanRead $.UnitTypeCloudBrain}}
<a class="{{if .PageIsCloudBrain}}active{{end}} item" href="{{.RepoLink}}/debugjob?debugListType=all"> <a class="{{if .PageIsCloudBrain}}active{{end}} item" href="{{.RepoLink}}/debugjob?debugListType=all">
<span> <span>
<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="M4 3h16a1 1 0 0 1 1 1v7H3V4a1 1 0 0 1 1-1zM3 13h18v7a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-7zm4 3v2h3v-2H7zM7 6v2h3V6H7z"/></svg>
<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="M4 3h16a1 1 0 0 1 1 1v7H3V4a1 1 0 0 1 1-1zM3 13h18v7a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-7zm4 3v2h3v-2H7zM7 6v2h3V6H7z"/></svg>
{{.i18n.Tr "repo.cloudbrain"}} {{.i18n.Tr "repo.cloudbrain"}}
<i class="question circle icon link cloudbrain-question" data-content={{.i18n.Tr "repo.cloudbrain_helper"}} data-position="top center" data-variation="mini"></i> <i class="question circle icon link cloudbrain-question" data-content={{.i18n.Tr "repo.cloudbrain_helper"}} data-position="top center" data-variation="mini"></i>
</span> </span>


+ 2
- 11
templates/repo/home.tmpl View File

@@ -256,11 +256,8 @@
</div> </div>
<div class="ui six wide tablet four wide computer column"> <div class="ui six wide tablet four wide computer column">
<div id="repo-desc"> <div id="repo-desc">
<h4 id="about-desc" class="ui header">简介
<!-- <a class="edit-icon" href="javascript:void(0)">
<i class="gray edit outline icon"></i>
</a> -->
</h4>
<h4 id="about-desc" class="ui header">简介</h4>
<input type="hidden" id="edit-alias" value="{{.Repository.Alias}}">
<p> <p>
{{if .Repository.DescriptionHTML}} {{if .Repository.DescriptionHTML}}
<span class="description" style="word-break:break-all">{{.Repository.DescriptionHTML}}</span> <span class="description" style="word-break:break-all">{{.Repository.DescriptionHTML}}</span>
@@ -286,14 +283,8 @@
<i class="grey bookmark icon"></i> <i class="grey bookmark icon"></i>


<div id="repo-topics1" style="flex: 1;"> <div id="repo-topics1" style="flex: 1;">
<!-- {{if not .Topics}}
<span class="no-description text-italic">{{.i18n.Tr "repo.no_desc"}}</span>
{{end}} -->
{{range .Topics}} {{range .Topics}}

<a class="ui repo-topic small label topic" href="{{AppSubUrl}}/explore/repos?q={{.Name}}&topic=">{{.Name}}</a> <a class="ui repo-topic small label topic" href="{{AppSubUrl}}/explore/repos?q={{.Name}}&topic=">{{.Name}}</a>


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


+ 3
- 31
templates/repo/migrate.tmpl View File

@@ -2,7 +2,7 @@
<div class="repository new migrate" style="margin-top: 40px;"> <div class="repository new migrate" style="margin-top: 40px;">
<div class="ui middle very relaxed page grid"> <div class="ui middle very relaxed page grid">
<div class="column"> <div class="column">
<form class="ui form" action="{{.Link}}" method="post">
<form class="ui form" action="{{.Link}}" method="post" id="create_repo_form">
{{.CsrfTokenHtml}} {{.CsrfTokenHtml}}
<h3 class="ui top attached header"> <h3 class="ui top attached header">
{{.i18n.Tr "new_migrate"}} {{.i18n.Tr "new_migrate"}}
@@ -37,35 +37,7 @@
</div> </div>


<div class="ui divider"></div> <div class="ui divider"></div>

<div class="inline required field {{if .Err_Owner}}error{{end}}">
<label>{{.i18n.Tr "repo.owner"}}</label>
<div class="ui selection owner dropdown">
<input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
<span class="text" title="{{.ContextUser.Name}}">
<img class="ui mini image" src="{{.ContextUser.RelAvatarLink}}">
{{.ContextUser.ShortName 20}}
</span>
<i class="dropdown icon"></i>
<div class="menu" title="{{.SignedUser.Name}}">
<div class="item" data-value="{{.SignedUser.ID}}">
<img class="ui mini image" src="{{.SignedUser.RelAvatarLink}}">
{{.SignedUser.ShortName 20}}
</div>
{{range .Orgs}}
<div class="item" data-value="{{.ID}}" title="{{.Name}}">
<img class="ui mini image" src="{{.RelAvatarLink}}">
{{.ShortName 20}}
</div>
{{end}}
</div>
</div>
</div>

<div class="inline required field {{if .Err_RepoName}}error{{end}}">
<label for="repo_name">{{.i18n.Tr "repo.repo_name"}}</label>
<input id="repo_name" name="repo_name" value="{{.repo_name}}" required>
</div>
{{template "repo/repo_name" .}}
<div class="inline field"> <div class="inline field">
<label>{{.i18n.Tr "repo.visibility"}}</label> <label>{{.i18n.Tr "repo.visibility"}}</label>
<div class="ui checkbox"> <div class="ui checkbox">
@@ -127,7 +99,7 @@


<div class="inline field"> <div class="inline field">
<label></label> <label></label>
<button class="ui green button">
<button class="ui green button" id="submit_reponame">
{{.i18n.Tr "repo.migrate_repo"}} {{.i18n.Tr "repo.migrate_repo"}}
</button> </button>
<a class="ui button" href="{{AppSubUrl}}/">{{.i18n.Tr "cancel"}}</a> <a class="ui button" href="{{AppSubUrl}}/">{{.i18n.Tr "cancel"}}</a>


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

@@ -35,6 +35,7 @@
<a class="item" href="{{.RepoLink}}/debugjob?debugListType=all">{{$.i18n.Tr "repo.modelarts.notebook"}}</a> <a class="item" href="{{.RepoLink}}/debugjob?debugListType=all">{{$.i18n.Tr "repo.modelarts.notebook"}}</a>
<a class="item" href="{{.RepoLink}}/modelarts/train-job">{{$.i18n.Tr "repo.modelarts.train_job"}}</a> <a class="item" href="{{.RepoLink}}/modelarts/train-job">{{$.i18n.Tr "repo.modelarts.train_job"}}</a>
<a class="active item" href="{{.RepoLink}}/modelarts/inference-job">{{$.i18n.Tr "repo.modelarts.infer_job"}}</a> <a class="active item" href="{{.RepoLink}}/modelarts/inference-job">{{$.i18n.Tr "repo.modelarts.infer_job"}}</a>
<a class="item" href="{{.RepoLink}}/cloudbrain/benchmark">{{$.i18n.Tr "repo.modelarts.evaluate_job"}}</a>
</div> </div>
</div> </div>
<div class="column right aligned"> <div class="column right aligned">


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

@@ -35,6 +35,7 @@
<a class="item" href="{{.RepoLink}}/debugjob?debugListType=all">{{$.i18n.Tr "repo.modelarts.notebook"}}</a> <a class="item" href="{{.RepoLink}}/debugjob?debugListType=all">{{$.i18n.Tr "repo.modelarts.notebook"}}</a>
<a class="active item" href="{{.RepoLink}}/modelarts/train-job">{{$.i18n.Tr "repo.modelarts.train_job"}}</a> <a class="active item" href="{{.RepoLink}}/modelarts/train-job">{{$.i18n.Tr "repo.modelarts.train_job"}}</a>
<a class="item" href="{{.RepoLink}}/modelarts/inference-job">{{$.i18n.Tr "repo.modelarts.infer_job"}}</a> <a class="item" href="{{.RepoLink}}/modelarts/inference-job">{{$.i18n.Tr "repo.modelarts.infer_job"}}</a>
<a class="item" href="{{.RepoLink}}/cloudbrain/benchmark">{{$.i18n.Tr "repo.modelarts.evaluate_job"}}</a>
</div> </div>
</div> </div>
<div class="column right aligned"> <div class="column right aligned">


+ 3
- 32
templates/repo/pulls/fork.tmpl View File

@@ -2,47 +2,18 @@
<div class="repository new fork"> <div class="repository new fork">
<div class="ui middle very relaxed page grid"> <div class="ui middle very relaxed page grid">
<div class="column"> <div class="column">
<form class="ui form" action="{{.Link}}" method="post">
<form class="ui form" action="{{.Link}}" method="post" id="create_repo_form">
{{.CsrfTokenHtml}} {{.CsrfTokenHtml}}
<h3 class="ui top attached header"> <h3 class="ui top attached header">
{{.i18n.Tr "new_fork"}} {{.i18n.Tr "new_fork"}}
</h3> </h3>
<div class="ui attached segment"> <div class="ui attached segment">
{{template "base/alert" .}} {{template "base/alert" .}}
<div class="inline required field {{if .Err_Owner}}error{{end}}">
<label>{{.i18n.Tr "repo.owner"}}</label>
<div class="ui selection owner dropdown">
<input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
<span class="text" title="{{.ContextUser.Name}}">
<img class="ui mini image" src="{{.ContextUser.RelAvatarLink}}">
{{.ContextUser.ShortName 20}}
</span>
<i class="dropdown icon"></i>
<div class="menu">
{{if .CanForkToUser}}
<div class="item" data-value="{{.SignedUser.ID}}" title="{{.SignedUser.Name}}">
<img class="ui mini image" src="{{.SignedUser.RelAvatarLink}}">
{{.SignedUser.ShortName 20}}
</div>
{{end}}
{{range .Orgs}}
<div class="item" data-value="{{.ID}}" title="{{.Name}}">
<img class="ui mini image" src="{{.RelAvatarLink}}">
{{.ShortName 20}}
</div>
{{end}}
</div>
</div>
</div>

<div class="inline field"> <div class="inline field">
<label>{{.i18n.Tr "repo.fork_from"}}</label> <label>{{.i18n.Tr "repo.fork_from"}}</label>
<a href="{{AppSubUrl}}/{{.ForkFrom}}">{{.ForkFrom}}</a>
</div>
<div class="inline required field {{if .Err_RepoName}}error{{end}}">
<label for="repo_name">{{.i18n.Tr "repo.repo_name"}}</label>
<input id="repo_name" name="repo_name" value="{{.repo_name}}" required>
<a href="{{AppSubUrl}}/{{.ForkFrom}}">{{.ForkDisplayName}}</a>
</div> </div>
{{template "repo/repo_name" .}}
<div class="inline field"> <div class="inline field">
<label>{{.i18n.Tr "repo.visibility"}}</label> <label>{{.i18n.Tr "repo.visibility"}}</label>
<div class="ui read-only checkbox"> <div class="ui read-only checkbox">


+ 57
- 0
templates/repo/repo_name.tmpl View File

@@ -0,0 +1,57 @@
<div class="inline required field {{if .Err_RepoName}}error{{end}}" >
<label for="Alias">{{.i18n.Tr "form.Alias"}}</label>
<input id="alias" name="alias" value="{{.alias}}" autofocus required maxlength="100">
<span class="help">{{.i18n.Tr "form.reponame_dash_dot_error"}}</span>
</div>

<div class="inline required fields" style="margin-bottom: 0;">
<label style="text-align: right;width: 250px!important;word-wrap: break-word;">{{.i18n.Tr "form.RepoPath"}}</label>
<div class="required field {{if .Err_Owner}}error{{end}}" style="padding: 0;">
<!-- <label>{{.i18n.Tr "repo.owner"}}</label> -->
<div class="ui selection owner dropdown" id="ownerDropdown">
<input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required>
<div class="text" title="{{.ContextUser.Name}}">
<img class="ui mini image" src="{{.ContextUser.RelAvatarLink}}">
{{.ContextUser.ShortName 20}}
</div>
<i class="dropdown icon"></i>
<div class="menu">
<div class="item" data-value="{{.SignedUser.ID}}" title="{{.SignedUser.Name}}">
<img class="ui mini image" src="{{.SignedUser.RelAvatarLink}}">
{{.SignedUser.ShortName 20}}
</div>
{{range .Orgs}}
<div class="item" data-value="{{.ID}}" title="{{.Name}}">
<img class="ui mini image" src="{{.RelAvatarLink}}">
{{.ShortName 20}}
</div>
{{end}}
</div>
</div>
<!-- <span class="help">{{.i18n.Tr "repo.repo_owner_helper"}}</span> -->
</div>
<!-- <div class="required field {{if .Err_RepoName}}error{{end}}">
<input id="repo_name" name="repo_name" value="{{.repo_name}}" autofocus required>
</div> -->
<div class="ui interval" style="width: 0.6em;font-size: 2rem;line-height: 0px;text-align: center;">/</div>
<div class="required field {{if .Err_RepoName}}error{{end}}">
<!-- <label for="repo_name">{{.i18n.Tr "repo.repo_name"}}</label> -->
<input style="width: 100% !important;" id="repo_name" name="repo_name" value="{{.repo_name}}" maxlength="100" autofocus required>
</div>
</div>
<span style="display: block;margin-bottom: 1em;" class="help">{{.i18n.Tr "form.repoadd_dash_dot_error"}}</span>
<div class="inline field" id="repoAdress" style="display: none;word-break: break-all;">
<label for="">{{.i18n.Tr "form.RepoAdress"}}:</label>
<span style="flex: 1;"></span>
</div>

<script>
console.log({{$.Err_Alias}})
</script>

+ 47
- 13
templates/repo/settings/options.tmpl View File

@@ -8,12 +8,46 @@
{{.i18n.Tr "repo.settings.basic_settings"}} {{.i18n.Tr "repo.settings.basic_settings"}}
</h4> </h4>
<div class="ui attached segment"> <div class="ui attached segment">
<form class="ui form" action="{{.Link}}" method="post">
<form class="ui form" action="{{.Link}}" method="post" id="create_repo_form">
{{.CsrfTokenHtml}} {{.CsrfTokenHtml}}
<input type="hidden" name="action" value="update"> <input type="hidden" name="action" value="update">
<div class="required field {{if .Err_RepoName}}error{{end}}">
<!-- <div class="required field {{if .Err_RepoName}}error{{end}}">
<label for="repo_name">{{.i18n.Tr "repo.repo_name"}}</label> <label for="repo_name">{{.i18n.Tr "repo.repo_name"}}</label>
<input id="repo_name" name="repo_name" value="{{.Repository.Name}}" data-repo-name="{{.Repository.Name}}" autofocus required>
<input id="repo_name" name="alias" value="{{.Repository.Alias}}" data-repo-name="{{.Repository.Alias}}" autofocus required>
<input type="hidden" name="repo_name" value="{{.Repository.Name}}" data-repo-name="{{.Repository.Name}}" autofocus required>
</div> -->
<div class="required field {{if .Err_Alias}}error{{end}}">
<label for="Alias">{{.i18n.Tr "form.Alias"}}</label>
<input name="alias" value="{{.Repository.Alias}}" data-repo-name="{{.Repository.Alias}}" autofocus required>
<span class="help">{{.i18n.Tr "form.reponame_dash_dot_error"}}</span>
</div>

<div class="required field">
<label>{{.i18n.Tr "form.RepoPath"}}</label>
<div class="fields">
<div class="eight wide required field {{if .Err_Owner}}error{{end}}">
<div class="ui selection owner dropdown" id="ownerDropdown">
<input type="hidden" id="uid" name="uid" value="{{.Owner.Name}}" required>
<div class="text" title="{{.Owner.Name}}">
<img class="ui mini image" src="{{.Owner.RelAvatarLink}}">
{{.Owner.ShortName 40}}
</div>
</div>
</div>
<div class="ui interval" style="width: 0.6em;font-size: 2rem;line-height: 38px;text-align: center;">/</div>
<div class="eight wide required field {{if .Err_Alias}}error{{end}}">
<!-- <label for="repo_name">{{.i18n.Tr "repo.repo_name"}}</label> -->
<input style="width: 100% !important;" id="repo_name" name="repo_name" value="{{.Repository.Name}}" data-repo-name="{{.Repository.Name}}" autofocus required>
<span class="help">{{.i18n.Tr "form.repoadd_dash_dot_error"}}</span>
</div>
</div>
</div>
<div class="inline field" id="repoAdress" style="display: none;">
<label for="">{{.i18n.Tr "form.RepoAdress"}}:</label>
<span></span>
</div> </div>
<div class="inline field"> <div class="inline field">
<label>{{.i18n.Tr "repo.repo_size"}}</label> <label>{{.i18n.Tr "repo.repo_size"}}</label>
@@ -41,7 +75,7 @@
{{end}} {{end}}
<div class="field {{if .Err_Description}}error{{end}}"> <div class="field {{if .Err_Description}}error{{end}}">
<label for="description">{{$.i18n.Tr "repo.repo_desc"}}</label> <label for="description">{{$.i18n.Tr "repo.repo_desc"}}</label>
<textarea id="description" name="description" rows="2" maxlength="255">{{.Repository.Description}}</textarea>
<textarea id="description" name="description" rows="2" maxlength="254">{{.Repository.Description}}</textarea>
</div> </div>
<div class="field {{if .Err_Website}}error{{end}}"> <div class="field {{if .Err_Website}}error{{end}}">
<label for="website">{{.i18n.Tr "repo.settings.site"}}</label> <label for="website">{{.i18n.Tr "repo.settings.site"}}</label>
@@ -152,7 +186,7 @@
{{$isModelMangeEnabled := .Repository.UnitEnabled $.UnitTypeModelManage }} {{$isModelMangeEnabled := .Repository.UnitEnabled $.UnitTypeModelManage }}
<div class="inline field"> <div class="inline field">
<label>{{.i18n.Tr "repo.model_manager"}}</label> <label>{{.i18n.Tr "repo.model_manager"}}</label>
<div class="ui checkbox {{if ne $.MODEL_COUNT 0}}disabled{{end}}">
<div class="ui checkbox">
<input class="enable-system" name="enable_model_manager" type="checkbox" {{if $isModelMangeEnabled}}checked{{end}}> <input class="enable-system" name="enable_model_manager" type="checkbox" {{if $isModelMangeEnabled}}checked{{end}}>
<label>{{.i18n.Tr "repo.settings.model_desc"}}</label> <label>{{.i18n.Tr "repo.settings.model_desc"}}</label>
</div> </div>
@@ -160,7 +194,7 @@
{{$isCloudBrainEnabled := .Repository.UnitEnabled $.UnitTypeCloudBrain }} {{$isCloudBrainEnabled := .Repository.UnitEnabled $.UnitTypeCloudBrain }}
<div class="inline field"> <div class="inline field">
<label>{{.i18n.Tr "repo.cloudbrain"}}</label> <label>{{.i18n.Tr "repo.cloudbrain"}}</label>
<div class="ui checkbox {{if ne $.jobCount 0}}disabled{{end}}">
<div class="ui checkbox">
<input class="enable-system" name="enable_cloud_brain" type="checkbox" {{if $isCloudBrainEnabled}}checked{{end}}> <input class="enable-system" name="enable_cloud_brain" type="checkbox" {{if $isCloudBrainEnabled}}checked{{end}}>
<label>{{.i18n.Tr "repo.settings.cloudbrain_desc"}}</label> <label>{{.i18n.Tr "repo.settings.cloudbrain_desc"}}</label>
</div> </div>
@@ -475,7 +509,7 @@
<div class="field"> <div class="field">
<label> <label>
{{.i18n.Tr "repo.settings.transfer_form_title"}} {{.i18n.Tr "repo.settings.transfer_form_title"}}
<span class="text red">{{.Repository.Name}}</span>
<span class="text red">{{.Repository.Alias}}</span>
</label> </label>
</div> </div>
<div class="required field"> <div class="required field">
@@ -507,7 +541,7 @@
<div class="field"> <div class="field">
<label> <label>
{{.i18n.Tr "repo.settings.transfer_form_title"}} {{.i18n.Tr "repo.settings.transfer_form_title"}}
<span class="text red">{{.Repository.Name}}</span>
<span class="text red">{{.Repository.Alias}}</span>
</label> </label>
</div> </div>
<div class="required field"> <div class="required field">
@@ -534,7 +568,7 @@
<div class="content"> <div class="content">
<div class="ui warning message text left"> <div class="ui warning message text left">
{{.i18n.Tr "repo.settings.delete_notices_1" | Safe}}<br> {{.i18n.Tr "repo.settings.delete_notices_1" | Safe}}<br>
{{.i18n.Tr "repo.settings.delete_notices_2" .Repository.FullName | Safe}}
{{.i18n.Tr "repo.settings.delete_notices_2" .Repository.Alias | Safe}}
{{if .Repository.NumForks}}<br> {{if .Repository.NumForks}}<br>
{{.i18n.Tr "repo.settings.delete_notices_fork_1"}} {{.i18n.Tr "repo.settings.delete_notices_fork_1"}}
{{end}} {{end}}
@@ -545,7 +579,7 @@
<div class="field"> <div class="field">
<label> <label>
{{.i18n.Tr "repo.settings.transfer_form_title"}} {{.i18n.Tr "repo.settings.transfer_form_title"}}
<span class="text red">{{.Repository.Name}}</span>
<span class="text red">{{.Repository.Alias}}</span>
</label> </label>
</div> </div>
<div class="required field"> <div class="required field">
@@ -569,7 +603,7 @@
<div class="content"> <div class="content">
<div class="ui warning message text left"> <div class="ui warning message text left">
{{.i18n.Tr "repo.settings.delete_notices_1" | Safe}}<br> {{.i18n.Tr "repo.settings.delete_notices_1" | Safe}}<br>
{{.i18n.Tr "repo.settings.wiki_delete_notices_1" .Repository.Name | Safe}}
{{.i18n.Tr "repo.settings.wiki_delete_notices_1" .Repository.Alias | Safe}}
</div> </div>
<form class="ui form" action="{{.Link}}" method="post"> <form class="ui form" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}} {{.CsrfTokenHtml}}
@@ -577,7 +611,7 @@
<div class="field"> <div class="field">
<label> <label>
{{.i18n.Tr "repo.settings.transfer_form_title"}} {{.i18n.Tr "repo.settings.transfer_form_title"}}
<span class="text red">{{.Repository.Name}}</span>
<span class="text red">{{.Repository.Alias}}</span>
</label> </label>
</div> </div>
<div class="required field"> <div class="required field">
@@ -625,4 +659,4 @@
{{end}} {{end}}
{{end}} {{end}}


{{template "base/footer" .}}
{{template "base/footer" .}}

+ 1
- 0
templates/repo/view_list.tmpl View File

@@ -93,6 +93,7 @@
</span> </span>
</td> </td>
{{end}} {{end}}
<td class="message nine wide"> <td class="message nine wide">
<span class="truncate"> <span class="truncate">
<a href="{{$.RepoLink}}/commit/{{$commit.ID}}" title="{{$commit.Summary}}">{{$commit.Summary | RenderEmoji}}</a> <a href="{{$.RepoLink}}/commit/{{$commit.ID}}" title="{{$commit.Summary}}">{{$commit.Summary | RenderEmoji}}</a>


+ 21
- 21
templates/user/dashboard/feeds.tmpl View File

@@ -13,63 +13,63 @@
{{.ShortActUserName}} {{.ShortActUserName}}
{{end}} {{end}}
{{if eq .GetOpType 1}} {{if eq .GetOpType 1}}
{{$.i18n.Tr "action.create_repo" .GetRepoLink .ShortRepoPath | Str2html}}
{{$.i18n.Tr "action.create_repo" .GetRepoLink .ShortRepoFullDisplayName | Str2html}}
{{else if eq .GetOpType 2}} {{else if eq .GetOpType 2}}
{{$.i18n.Tr "action.rename_repo" .GetContent .GetRepoLink .ShortRepoPath | Str2html}}
{{$.i18n.Tr "action.rename_repo" .GetContent .GetRepoLink .ShortRepoFullDisplayName | Str2html}}
{{else if eq .GetOpType 5}} {{else if eq .GetOpType 5}}
{{ $branchLink := .GetBranch | EscapePound | Escape}} {{ $branchLink := .GetBranch | EscapePound | Escape}}
{{$.i18n.Tr "action.commit_repo" .GetRepoLink $branchLink (Escape .GetBranch) .ShortRepoPath | Str2html}}
{{$.i18n.Tr "action.commit_repo" .GetRepoLink $branchLink (Escape .GetBranch) .ShortRepoFullDisplayName | Str2html}}
{{else if eq .GetOpType 6}} {{else if eq .GetOpType 6}}
{{ $index := index .GetIssueInfos 0}} {{ $index := index .GetIssueInfos 0}}
{{$.i18n.Tr "action.create_issue" .GetRepoLink $index .ShortRepoPath | Str2html}}
{{$.i18n.Tr "action.create_issue" .GetRepoLink $index .ShortRepoFullDisplayName | Str2html}}
{{else if eq .GetOpType 7}} {{else if eq .GetOpType 7}}
{{ $index := index .GetIssueInfos 0}} {{ $index := index .GetIssueInfos 0}}
{{$.i18n.Tr "action.create_pull_request" .GetRepoLink $index .ShortRepoPath | Str2html}}
{{$.i18n.Tr "action.create_pull_request" .GetRepoLink $index .ShortRepoFullDisplayName | Str2html}}
{{else if eq .GetOpType 8}} {{else if eq .GetOpType 8}}
{{$.i18n.Tr "action.transfer_repo" .GetContent .GetRepoLink .ShortRepoPath | Str2html}}
{{$.i18n.Tr "action.transfer_repo" .GetContent .GetRepoLink .ShortRepoFullDisplayName | Str2html}}
{{else if eq .GetOpType 9}} {{else if eq .GetOpType 9}}
{{ $branchLink := .GetBranch | EscapePound | Escape}} {{ $branchLink := .GetBranch | EscapePound | Escape}}
{{$.i18n.Tr "action.push_tag" .GetRepoLink $branchLink .ShortRepoPath | Str2html}}
{{$.i18n.Tr "action.push_tag" .GetRepoLink $branchLink .ShortRepoFullDisplayName | Str2html}}
{{else if eq .GetOpType 10}} {{else if eq .GetOpType 10}}
{{ $index := index .GetIssueInfos 0}} {{ $index := index .GetIssueInfos 0}}
{{$.i18n.Tr "action.comment_issue" .GetRepoLink $index .ShortRepoPath | Str2html}}
{{$.i18n.Tr "action.comment_issue" .GetRepoLink $index .ShortRepoFullDisplayName | Str2html}}
{{else if eq .GetOpType 11}} {{else if eq .GetOpType 11}}
{{ $index := index .GetIssueInfos 0}} {{ $index := index .GetIssueInfos 0}}
{{$.i18n.Tr "action.merge_pull_request" .GetRepoLink $index .ShortRepoPath | Str2html}}
{{$.i18n.Tr "action.merge_pull_request" .GetRepoLink $index .ShortRepoFullDisplayName | Str2html}}
{{else if eq .GetOpType 12}} {{else if eq .GetOpType 12}}
{{ $index := index .GetIssueInfos 0}} {{ $index := index .GetIssueInfos 0}}
{{$.i18n.Tr "action.close_issue" .GetRepoLink $index .ShortRepoPath | Str2html}}
{{$.i18n.Tr "action.close_issue" .GetRepoLink $index .ShortRepoFullDisplayName | Str2html}}
{{else if eq .GetOpType 13}} {{else if eq .GetOpType 13}}
{{ $index := index .GetIssueInfos 0}} {{ $index := index .GetIssueInfos 0}}
{{$.i18n.Tr "action.reopen_issue" .GetRepoLink $index .ShortRepoPath | Str2html}}
{{$.i18n.Tr "action.reopen_issue" .GetRepoLink $index .ShortRepoFullDisplayName | Str2html}}
{{else if eq .GetOpType 14}} {{else if eq .GetOpType 14}}
{{ $index := index .GetIssueInfos 0}} {{ $index := index .GetIssueInfos 0}}
{{$.i18n.Tr "action.close_pull_request" .GetRepoLink $index .ShortRepoPath | Str2html}}
{{$.i18n.Tr "action.close_pull_request" .GetRepoLink $index .ShortRepoFullDisplayName | Str2html}}
{{else if eq .GetOpType 15}} {{else if eq .GetOpType 15}}
{{ $index := index .GetIssueInfos 0}} {{ $index := index .GetIssueInfos 0}}
{{$.i18n.Tr "action.reopen_pull_request" .GetRepoLink $index .ShortRepoPath | Str2html}}
{{$.i18n.Tr "action.reopen_pull_request" .GetRepoLink $index .ShortRepoFullDisplayName | Str2html}}
{{else if eq .GetOpType 16}} {{else if eq .GetOpType 16}}
{{ $index := index .GetIssueInfos 0}} {{ $index := index .GetIssueInfos 0}}
{{$.i18n.Tr "action.delete_tag" .GetRepoLink .GetBranch .ShortRepoPath | Str2html}}
{{$.i18n.Tr "action.delete_tag" .GetRepoLink .GetBranch .ShortRepoFullDisplayName | Str2html}}
{{else if eq .GetOpType 17}} {{else if eq .GetOpType 17}}
{{ $index := index .GetIssueInfos 0}} {{ $index := index .GetIssueInfos 0}}
{{$.i18n.Tr "action.delete_branch" .GetRepoLink .GetBranch .ShortRepoPath | Str2html}}
{{$.i18n.Tr "action.delete_branch" .GetRepoLink .GetBranch .ShortRepoFullDisplayName | Str2html}}
{{else if eq .GetOpType 18}} {{else if eq .GetOpType 18}}
{{ $branchLink := .GetBranch | EscapePound}} {{ $branchLink := .GetBranch | EscapePound}}
{{$.i18n.Tr "action.mirror_sync_push" .GetRepoLink $branchLink .GetBranch .ShortRepoPath | Str2html}}
{{$.i18n.Tr "action.mirror_sync_push" .GetRepoLink $branchLink .GetBranch .ShortRepoFullDisplayName | Str2html}}
{{else if eq .GetOpType 19}} {{else if eq .GetOpType 19}}
{{$.i18n.Tr "action.mirror_sync_create" .GetRepoLink .GetBranch .ShortRepoPath | Str2html}}
{{$.i18n.Tr "action.mirror_sync_create" .GetRepoLink .GetBranch .ShortRepoFullDisplayName | Str2html}}
{{else if eq .GetOpType 20}} {{else if eq .GetOpType 20}}
{{$.i18n.Tr "action.mirror_sync_delete" .GetRepoLink .GetBranch .ShortRepoPath | Str2html}}
{{$.i18n.Tr "action.mirror_sync_delete" .GetRepoLink .GetBranch .ShortRepoFullDisplayName | Str2html}}
{{else if eq .GetOpType 21}} {{else if eq .GetOpType 21}}
{{ $index := index .GetIssueInfos 0}} {{ $index := index .GetIssueInfos 0}}
{{$.i18n.Tr "action.approve_pull_request" .GetRepoLink $index .ShortRepoPath | Str2html}}
{{$.i18n.Tr "action.approve_pull_request" .GetRepoLink $index .ShortRepoFullDisplayName | Str2html}}
{{else if eq .GetOpType 22}} {{else if eq .GetOpType 22}}
{{ $index := index .GetIssueInfos 0}} {{ $index := index .GetIssueInfos 0}}
{{$.i18n.Tr "action.reject_pull_request" .GetRepoLink $index .ShortRepoPath | Str2html}}
{{$.i18n.Tr "action.reject_pull_request" .GetRepoLink $index .ShortRepoFullDisplayName | Str2html}}
{{else if eq .GetOpType 23}} {{else if eq .GetOpType 23}}
{{ $index := index .GetIssueInfos 0}} {{ $index := index .GetIssueInfos 0}}
{{$.i18n.Tr "action.comment_pull" .GetRepoLink $index .ShortRepoPath | Str2html}}
{{$.i18n.Tr "action.comment_pull" .GetRepoLink $index .ShortRepoFullDisplayName | Str2html}}
{{end}} {{end}}
</p> </p>
{{if or (eq .GetOpType 5) (eq .GetOpType 18)}} {{if or (eq .GetOpType 5) (eq .GetOpType 18)}}


+ 4
- 1
templates/user/dashboard/issues.tmpl View File

@@ -107,7 +107,7 @@
{{ $timeStr:= TimeSinceUnix .CreatedUnix $.Lang }} {{ $timeStr:= TimeSinceUnix .CreatedUnix $.Lang }}
{{if .Repo}} {{if .Repo}}
<li class="item"> <li class="item">
<div class="ui label">{{.Repo.FullName}}#{{.Index}}</div>
<div class="ui label">{{.Repo.OwnerName}}/{{.Repo.Alias}}#{{.Index}}</div>
<a class="title" href="{{.HTMLURL}}">{{RenderEmoji .Title}}</a> <a class="title" href="{{.HTMLURL}}">{{RenderEmoji .Title}}</a>


{{if .IsPull}} {{if .IsPull}}
@@ -208,3 +208,6 @@
</div> </div>
</div> </div>
{{template "base/footer" .}} {{template "base/footer" .}}
<script>
console.log({{.Issues}})
</script>

+ 4
- 1
templates/user/dashboard/milestones.tmpl View File

@@ -65,7 +65,7 @@
<div class="milestone list"> <div class="milestone list">
{{range .Milestones}} {{range .Milestones}}
<li class="item"> <li class="item">
<div class="ui label">{{.Repo.FullName}}</div>
<div class="ui label">{{.Repo.OwnerName}}/{{.Repo.Alias}}</div>
{{svg "octicon-milestone" 16}} <a href="{{.Repo.Link }}/milestone/{{.ID}}">{{.Name}}</a> {{svg "octicon-milestone" 16}} <a href="{{.Repo.Link }}/milestone/{{.ID}}">{{.Name}}</a>
<div class="ui right green progress" data-percent="{{.Completeness}}"> <div class="ui right green progress" data-percent="{{.Completeness}}">
<div class="bar" {{if not .Completeness}}style="background-color: transparent"{{end}}> <div class="bar" {{if not .Completeness}}style="background-color: transparent"{{end}}>
@@ -117,3 +117,6 @@
</div> </div>
</div> </div>
{{template "base/footer" .}} {{template "base/footer" .}}
<script>
console.log({{.Milestones}})
</script>

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save