Browse Source

merge

pull/3418/head
ychao_1983 2 years ago
parent
commit
99284430a8
93 changed files with 4484 additions and 306 deletions
  1. +41
    -5
      models/ai_model_manage.go
  2. +36
    -1
      models/cloudbrain.go
  3. +21
    -0
      models/cloudbrain_static.go
  4. +20
    -0
      models/list_options.go
  5. +229
    -4
      models/repo.go
  6. +33
    -21
      models/repo_list.go
  7. +20
    -0
      models/repo_statistic.go
  8. +31
    -0
      models/repo_tag.go
  9. +93
    -0
      models/resource_specification.go
  10. +14
    -0
      models/topic.go
  11. +34
    -0
      models/user.go
  12. +17
    -0
      models/user_analysis_for_activity.go
  13. +197
    -49
      models/user_business_analysis.go
  14. +9
    -0
      models/user_business_struct.go
  15. +1
    -0
      models/user_invitation.go
  16. +1
    -4
      modules/grampus/grampus.go
  17. +12
    -0
      modules/modelarts/modelarts.go
  18. +3
    -1
      modules/modelarts/resty.go
  19. +9
    -0
      modules/redis/redis_key/repo_redis_key.go
  20. +14
    -0
      modules/setting/setting.go
  21. +23
    -0
      modules/structs/pipeline.go
  22. +5
    -0
      options/locale/locale_en-US.ini
  23. +5
    -0
      options/locale/locale_zh-CN.ini
  24. +2
    -1
      package.json
  25. +3
    -11
      public/home/home.js
  26. +34
    -0
      routers/admin/resources.go
  27. +10
    -0
      routers/api/v1/api.go
  28. +15
    -0
      routers/api/v1/notify/pipeline.go
  29. +26
    -0
      routers/api/v1/repo/cloudbrain.go
  30. +15
    -0
      routers/api/v1/repo/cloudbrain_dashboard.go
  31. +14
    -0
      routers/api/v1/repo/mlops.go
  32. +20
    -0
      routers/api/v1/repo/modelmanage.go
  33. +14
    -2
      routers/api/v1/repo/topic.go
  34. +106
    -0
      routers/home.go
  35. +3
    -0
      routers/private/internal.go
  36. +38
    -7
      routers/repo/ai_model_convert.go
  37. +170
    -46
      routers/repo/ai_model_manage.go
  38. +4
    -1
      routers/repo/aisafety.go
  39. +3
    -0
      routers/repo/attachment.go
  40. +7
    -1
      routers/repo/cloudbrain_statistic.go
  41. +14
    -10
      routers/repo/modelarts.go
  42. +2
    -0
      routers/repo/repo_statistic.go
  43. +18
    -0
      routers/repo/user_data_analysis.go
  44. +6
    -3
      routers/repo/user_invitation.go
  45. +13
    -1
      routers/routes/routes.go
  46. +2
    -1
      routers/user/Invitation.go
  47. +1
    -1
      routers/user/auth.go
  48. +7
    -2
      services/cloudbrain/clear.go
  49. +2
    -46
      services/cloudbrain/resource/resource_specification.go
  50. +88
    -0
      services/repository/contributor.go
  51. +51
    -6
      services/repository/repository.go
  52. +315
    -0
      services/repository/square.go
  53. +5
    -5
      templates/base/head_navbar.tmpl
  54. +5
    -5
      templates/base/head_navbar_fluid.tmpl
  55. +5
    -5
      templates/base/head_navbar_home.tmpl
  56. +4
    -4
      templates/base/head_navbar_pro.tmpl
  57. +2
    -2
      templates/explore/navbar.tmpl
  58. +8
    -0
      templates/explore/repos/search.tmpl
  59. +16
    -0
      templates/explore/repos/square.tmpl
  60. +21
    -0
      templates/repo/cloudbrain/trainjob/show.tmpl
  61. +21
    -0
      templates/repo/grampus/trainjob/show.tmpl
  62. +23
    -0
      templates/repo/modelarts/trainjob/show.tmpl
  63. +1
    -1
      templates/repo/modelmanage/convertIndex.tmpl
  64. +1
    -0
      templates/repo/modelmanage/create_local_1.tmpl
  65. +23
    -0
      templates/repo/modelmanage/create_online.tmpl
  66. +3
    -1
      templates/repo/modelmanage/index.tmpl
  67. +1
    -1
      templates/user/dashboard/dashboard.tmpl
  68. +59
    -29
      web_src/js/components/Model.vue
  69. +10
    -0
      web_src/js/features/i18nVue.js
  70. +2
    -2
      web_src/js/index.js
  71. +12
    -0
      web_src/vuepages/apis/modules/common.js
  72. +10
    -0
      web_src/vuepages/apis/modules/modelmanage.js
  73. +69
    -0
      web_src/vuepages/apis/modules/repos.js
  74. +71
    -15
      web_src/vuepages/langs/config/en-US.js
  75. +74
    -4
      web_src/vuepages/langs/config/zh-CN.js
  76. +11
    -2
      web_src/vuepages/pages/modelmanage/common/modelmanage-common-detail.vue
  77. +45
    -1
      web_src/vuepages/pages/modelmanage/local/modelmanage-local-create-1.vue
  78. +123
    -0
      web_src/vuepages/pages/repos/components/ActiveOrgs.vue
  79. +156
    -0
      web_src/vuepages/pages/repos/components/ActiveUsers.vue
  80. +151
    -0
      web_src/vuepages/pages/repos/components/RecommendRepos.vue
  81. +115
    -0
      web_src/vuepages/pages/repos/components/ReposFilters.vue
  82. +316
    -0
      web_src/vuepages/pages/repos/components/ReposItem.vue
  83. +161
    -0
      web_src/vuepages/pages/repos/components/ReposList.vue
  84. +255
    -0
      web_src/vuepages/pages/repos/components/SearchBar.vue
  85. +363
    -0
      web_src/vuepages/pages/repos/components/SquareTop.vue
  86. +123
    -0
      web_src/vuepages/pages/repos/search/index.vue
  87. +17
    -0
      web_src/vuepages/pages/repos/search/vp-repos-search.js
  88. +146
    -0
      web_src/vuepages/pages/repos/square/index.vue
  89. +17
    -0
      web_src/vuepages/pages/repos/square/vp-repos-square.js
  90. +89
    -1
      web_src/vuepages/utils/index.js
  91. +75
    -0
      web_src/vuepages/utils/letteravatar.js
  92. +2
    -2
      webpack.config.js
  93. +2
    -2
      webpack_pro.config.js

+ 41
- 5
models/ai_model_manage.go View File

@@ -33,6 +33,7 @@ type AiModelManage struct {
CodeBranch string `xorm:"varchar(400) NULL" json:"codeBranch"`
CodeCommitID string `xorm:"NULL" json:"codeCommitID"`
UserId int64 `xorm:"NOT NULL" json:"userId"`
IsPrivate bool `xorm:"DEFAULT true" json:"isPrivate"`
UserName string `json:"userName"`
UserRelAvatarLink string `json:"userRelAvatarLink"`
TrainTaskInfo string `xorm:"text NULL" json:"trainTaskInfo"`
@@ -40,6 +41,7 @@ type AiModelManage struct {
UpdatedUnix timeutil.TimeStamp `xorm:"updated" json:"updatedUnix"`
IsCanOper bool `json:"isCanOper"`
IsCanDelete bool `json:"isCanDelete"`
IsCanDownload bool `json:"isCanDownload"`
}

type AiModelConvert struct {
@@ -84,8 +86,10 @@ type AiModelQueryOptions struct {
SortType string
New int
// JobStatus CloudbrainStatus
Type int
Status int
Type int
Status int
IsOnlyThisRepo bool
IsQueryPrivate bool
}

func (a *AiModelConvert) IsGpuTrainTask() bool {
@@ -217,6 +221,19 @@ func SaveModelToDb(model *AiModelManage) error {
return nil
}

func QueryModelConvertByName(name string, repoId int64) ([]*AiModelConvert, error) {
sess := x.NewSession()
defer sess.Close()
sess.Select("*").Table(new(AiModelConvert)).
Where("name='" + name + "' and repo_id=" + fmt.Sprint(repoId)).OrderBy("created_unix desc")
aiModelManageConvertList := make([]*AiModelConvert, 0)
err := sess.Find(&aiModelManageConvertList)
if err == nil {
return aiModelManageConvertList, nil
}
return nil, err
}

func QueryModelConvertById(id string) (*AiModelConvert, error) {
sess := x.NewSession()
defer sess.Close()
@@ -288,15 +305,30 @@ func ModifyModelDescription(id string, description string) error {
return nil
}

func ModifyLocalModel(id string, name, label, description string, engine int) error {
func ModifyModelPrivate(id string, isPrivate bool) error {
var sess *xorm.Session
sess = x.ID(id)
defer sess.Close()
re, err := sess.Cols("is_private").Update(&AiModelManage{
IsPrivate: isPrivate,
})
if err != nil {
return err
}
log.Info("success to update isPrivate from db.re=" + fmt.Sprint((re)))
return nil
}

func ModifyLocalModel(id string, name, label, description string, engine int, isPrivate bool) error {
var sess *xorm.Session
sess = x.ID(id)
defer sess.Close()
re, err := sess.Cols("name", "label", "description", "engine").Update(&AiModelManage{
re, err := sess.Cols("name", "label", "description", "engine", "is_private").Update(&AiModelManage{
Description: description,
Name: name,
Label: label,
Engine: int64(engine),
IsPrivate: isPrivate,
})
if err != nil {
return err
@@ -423,7 +455,11 @@ func QueryModel(opts *AiModelQueryOptions) ([]*AiModelManage, int64, error) {
builder.Eq{"ai_model_manage.status": opts.Status},
)
}

if !opts.IsQueryPrivate {
cond = cond.And(
builder.Eq{"ai_model_manage.is_private": false},
)
}
count, err := sess.Where(cond).Count(new(AiModelManage))
if err != nil {
return nil, 0, fmt.Errorf("Count: %v", err)


+ 36
- 1
models/cloudbrain.go View File

@@ -1068,6 +1068,9 @@ type UserImageConfig struct {
CreateVersion bool `json:"create_version"`
Flavor Flavor `json:"flavor"`
PoolID string `json:"pool_id"`
ShareAddr string `json:"nas_share_addr"`
MountPath string `json:"nas_mount_path"`
NasType string `json:"nas_type"`
}

type CreateTrainJobParams struct {
@@ -1091,13 +1094,18 @@ type Config struct {
CreateVersion bool `json:"create_version"`
Flavor Flavor `json:"flavor"`
PoolID string `json:"pool_id"`
ShareAddr string `json:"nas_share_addr"`
MountPath string `json:"nas_mount_path"`
NasType string `json:"nas_type"`
}

type CreateInferenceJobParams struct {
JobName string `json:"job_name"`
Description string `json:"job_desc"`
InfConfig InfConfig `json:"config"`
WorkspaceID string `json:"workspace_id"`
}

type CreateInfUserImageParams struct {
JobName string `json:"job_name"`
Description string `json:"job_desc"`
@@ -1155,6 +1163,9 @@ type TrainJobVersionConfig struct {
Flavor Flavor `json:"flavor"`
PoolID string `json:"pool_id"`
PreVersionId int64 `json:"pre_version_id"`
ShareAddr string `json:"nas_share_addr"`
MountPath string `json:"nas_mount_path"`
NasType string `json:"nas_type"`
}

type TrainJobVersionUserImageConfig struct {
@@ -1170,6 +1181,9 @@ type TrainJobVersionUserImageConfig struct {
PreVersionId int64 `json:"pre_version_id"`
UserImageUrl string `json:"user_image_url"`
UserCommand string `json:"user_command"`
ShareAddr string `json:"nas_share_addr"`
MountPath string `json:"nas_mount_path"`
NasType string `json:"nas_type"`
}

type CreateConfigParams struct {
@@ -1185,6 +1199,7 @@ type CreateConfigParams struct {
LogUrl string `json:"log_url"`
Flavor Flavor `json:"flavor"`
PoolID string `json:"pool_id"`
Volumes []Volumes `json:"volumes"`
}

type Parameter struct {
@@ -1928,6 +1943,7 @@ func CreateCloudbrain(cloudbrain *Cloudbrain) (err error) {
session.Commit()

go IncreaseDatasetUseCount(cloudbrain.Uuid)
go OperateRepoAITaskNum(cloudbrain.RepoID, 1)
return nil
}

@@ -2083,9 +2099,29 @@ func DeleteJob(job *Cloudbrain) error {

func deleteJob(e Engine, job *Cloudbrain) error {
_, err := e.ID(job.ID).Delete(job)
if err == nil {
go updateAITaskNumWhenDeleteJob(job)
}
return err
}

func updateAITaskNumWhenDeleteJob(job *Cloudbrain) {
repoId := job.RepoID
if repoId == 0 {
t := &Cloudbrain{}
_, tempErr := x.ID(job.ID).Unscoped().Get(t)
if tempErr != nil {
log.Error("updateAITaskNumWhenDeleteJob error.%v", tempErr)
return
}
repoId = t.RepoID
}

if repoId > 0 {
go OperateRepoAITaskNum(repoId, -1)
}
}

func GetCloudbrainByName(jobName string) (*Cloudbrain, error) {
cb := &Cloudbrain{JobName: jobName}
return getRepoCloudBrain(cb)
@@ -2288,7 +2324,6 @@ func RestartCloudbrain(old *Cloudbrain, new *Cloudbrain) (err error) {
}

go IncreaseDatasetUseCount(new.Uuid)

return nil
}
func CloudbrainAll(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int64, error) {


+ 21
- 0
models/cloudbrain_static.go View File

@@ -183,6 +183,17 @@ func GetWaittingTop() ([]*CloudbrainInfo, error) {
Find(&cloudbrains); err != nil {
log.Info("find error.")
}

var ids []int64
for _, task := range cloudbrains {
ids = append(ids, task.RepoID)
}
repositoryMap, err := GetRepositoriesMapByIDs(ids)
if err == nil {
for _, task := range cloudbrains {
task.Repo = repositoryMap[task.RepoID]
}
}
return cloudbrains, nil
}

@@ -199,6 +210,16 @@ func GetRunningTop() ([]*CloudbrainInfo, error) {
Find(&cloudbrains); err != nil {
log.Info("find error.")
}
var ids []int64
for _, task := range cloudbrains {
ids = append(ids, task.RepoID)
}
repositoryMap, err := GetRepositoriesMapByIDs(ids)
if err == nil {
for _, task := range cloudbrains {
task.Repo = repositoryMap[task.RepoID]
}
}
return cloudbrains, nil
}



+ 20
- 0
models/list_options.go View File

@@ -10,6 +10,26 @@ import (
"xorm.io/xorm"
)

type AvailablePageSize int

const (
PageSize15 AvailablePageSize = 15
PageSize30 AvailablePageSize = 30
PageSize50 AvailablePageSize = 50
)

func (s AvailablePageSize) IsLegal() bool {
switch s {
case PageSize30, PageSize50, PageSize15:
return true
}
return false
}

func (s AvailablePageSize) Int() int {
return int(s)
}

// ListOptions options to paginate results
type ListOptions struct {
PageSize int


+ 229
- 4
models/repo.go View File

@@ -231,10 +231,43 @@ type Repository struct {
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`

Hot int64 `xorm:"-"`
Active int64 `xorm:"-"`
Alias string `xorm:"INDEX"`
LowerAlias string `xorm:"INDEX"`
Hot int64 `xorm:"-"`
Active int64 `xorm:"-"`
Alias string `xorm:"INDEX"`
LowerAlias string `xorm:"INDEX"`
AiTaskCnt int64 `xorm:"NOT NULL DEFAULT 0"`
ModelCnt int64 `xorm:"NOT NULL DEFAULT 0"`
DatasetCnt int64 `xorm:"NOT NULL DEFAULT 0"`
LastMonthVisits int64 `xorm:"NOT NULL DEFAULT 0"`
LastFourMonthCommits int64 `xorm:"NOT NULL DEFAULT 0"`
}

// Repository4Card format for front display
type Repository4Card struct {
ID int64
OwnerID int64
OwnerName string
LowerName string
Name string
Alias string
NumWatches int
NumStars int
NumForks int
Description string
Topics []string
AiTaskCnt int64
ModelCnt int64
DatasetCnt int64
CreatedUnix timeutil.TimeStamp
UpdatedUnix timeutil.TimeStamp
PrimaryLanguage *LanguageStat
RelAvatarLink string
Contributors []*ContributorInfo
IsPrivate bool
IsFork bool
IsMirror bool
IsOwnerPrivate bool
IsArchived bool
}

type RepositoryShow struct {
@@ -243,6 +276,47 @@ type RepositoryShow struct {
Alias string
}

func (repo *Repository) ToCardFormat() *Repository4Card {
link := repo.RelAvatarLink()
var isOwnerPrivate bool
if repo.Owner != nil && repo.Owner.Visibility.IsPrivate() {
isOwnerPrivate = true
}
result := &Repository4Card{
ID: repo.ID,
OwnerID: repo.OwnerID,
OwnerName: repo.OwnerName,
LowerName: repo.LowerName,
Name: repo.Name,
NumWatches: repo.NumWatches,
NumStars: repo.NumStars,
NumForks: repo.NumForks,
Description: repo.Description,
Topics: repo.Topics,
AiTaskCnt: repo.AiTaskCnt,
ModelCnt: repo.ModelCnt,
DatasetCnt: repo.DatasetCnt,
CreatedUnix: repo.CreatedUnix,
UpdatedUnix: repo.UpdatedUnix,
PrimaryLanguage: repo.PrimaryLanguage,
RelAvatarLink: link,
Alias: repo.Alias,
IsPrivate: repo.IsPrivate,
IsFork: repo.IsFork,
IsMirror: repo.IsMirror,
IsOwnerPrivate: isOwnerPrivate,
IsArchived: repo.IsArchived,
}
return result
}

type ContributorInfo struct {
RelAvatarLink string
UserName string
Email string
CommitCnt int
}

// SanitizedOriginalURL returns a sanitized OriginalURL
func (repo *Repository) SanitizedOriginalURL() string {
if repo.OriginalURL == "" {
@@ -2379,6 +2453,75 @@ func CheckRepoStats(ctx context.Context) error {
}
}
// ***** END: Repository.NumForks *****

// ***** START: Repository.DatasetCnt *****
desc = "repository count 'dataset_cnt'"
results, err = x.Query("SELECT repository.id FROM `repository` WHERE repository.dataset_cnt!=(select count(1) from attachment inner join dataset on attachment.dataset_id = dataset.id where dataset.repo_id = repository.id)")
if err != nil {
log.Error("Select %s: %v", desc, err)
} else {
for _, result := range results {
id := com.StrTo(result["id"]).MustInt64()
select {
case <-ctx.Done():
log.Warn("CheckRepoStats: Cancelled")
return ErrCancelledf("during %s for repo ID %d", desc, id)
default:
}
log.Trace("Updating %s: %d", desc, id)
err = ResetRepoDatasetNum(id)
if err != nil {
log.Error("Update %s[%d]: %v", desc, id, err)
}
}
}
// ***** END: Repository.DatasetCnt *****

// ***** START: Repository.ModelCnt *****
desc = "repository count 'model_cnt'"
results, err = x.Query("SELECT repository.id FROM `repository` WHERE repository.model_cnt!=(select count(1) from ai_model_manage where repository.id = ai_model_manage.repo_id and ai_model_manage.size > 0 )")
if err != nil {
log.Error("Select %s: %v", desc, err)
} else {
for _, result := range results {
id := com.StrTo(result["id"]).MustInt64()
select {
case <-ctx.Done():
log.Warn("CheckRepoStats: Cancelled")
return ErrCancelledf("during %s for repo ID %d", desc, id)
default:
}
log.Trace("Updating %s: %d", desc, id)
err = ResetRepoModelNum(id)
if err != nil {
log.Error("Update %s[%d]: %v", desc, id, err)
}
}
}
// ***** END: Repository.ModelCnt *****

// ***** START: Repository.AiTaskCnt *****
desc = "repository count 'ai_task_cnt'"
results, err = x.Query("SELECT repository.id FROM `repository` WHERE repository.ai_task_cnt!=(select count(1) from cloudbrain where repository.id = cloudbrain.repo_id and (cloudbrain.deleted_at is null or cloudbrain.deleted_at = '0001-01-01 00:00:00') )")
if err != nil {
log.Error("Select %s: %v", desc, err)
} else {
for _, result := range results {
id := com.StrTo(result["id"]).MustInt64()
select {
case <-ctx.Done():
log.Warn("CheckRepoStats: Cancelled")
return ErrCancelledf("during %s for repo ID %d", desc, id)
default:
}
log.Trace("Updating %s: %d", desc, id)
err = ResetRepoAITaskNum(id)
if err != nil {
log.Error("Update %s[%d]: %v", desc, id, err)
}
}
}
// ***** END: Repository.AiTaskCnt *****
return nil
}

@@ -2775,3 +2918,85 @@ func ReadLatestFileInRepo(userName, repoName, refName, treePath string) (*RepoFi
}
return &RepoFile{CommitId: commitId, Content: d}, nil
}

func ResetRepoAITaskNum(repoId int64) error {
n, err := x.Where("repo_id = ? ", repoId).Count(&Cloudbrain{})
if err != nil {
return err
}
r := Repository{
AiTaskCnt: n,
}
_, err = x.Cols("ai_task_cnt").Where("id = ?", repoId).Update(&r)
return err
}

func ResetRepoDatasetNum(repoId int64) error {
n, err := x.Table("attachment").Join("inner", "dataset", "attachment.dataset_id = dataset.id").Where("dataset.repo_id = ?", repoId).Count()
if err != nil {
return err
}
r := Repository{
DatasetCnt: n,
}
_, err = x.Cols("dataset_cnt").Where("id = ?", repoId).Update(&r)
return err
}

func ResetRepoModelNum(repoId int64) error {
_, err := x.Exec("update repository set model_cnt = (select count(1) from ai_model_manage where ai_model_manage.repo_id = ? and size > 0) where id = ?", repoId, repoId)
return err
}

func operateRepoCol(repoId int64, colName string, amount int64, engines ...*xorm.Engine) error {
var err error

if amount == 0 {
return nil
}
var ee *xorm.Engine
if len(engines) == 0 {
ee = x
} else {
ee = engines[0]
}
if amount > 0 {
_, err = ee.Exec(fmt.Sprintf("update repository set %s = %s + ? where id = ?", colName, colName), amount, repoId)
} else {
_, err = ee.Exec(fmt.Sprintf("update repository set %s = %s - ? where id = ?", colName, colName), -1*amount, repoId)
}

return err
}

func OperateRepoDatasetNum(repoId int64, amount int64, engines ...*xorm.Engine) error {
return operateRepoCol(repoId, "dataset_cnt", amount, engines...)
}

func OperateRepoModelNum(repoId int64, amount int64, engines ...*xorm.Engine) error {
return operateRepoCol(repoId, "model_cnt", amount, engines...)
}

func OperateRepoAITaskNum(repoId int64, amount int64, engines ...*xorm.Engine) error {
return operateRepoCol(repoId, "ai_task_cnt", amount, engines...)
}

func UpdateRepositoryLastFourMonthCommits(repoID int64, amount int64) error {
_, err := x.Exec("update repository set last_four_month_commits = ? where id = ?", amount, repoID)
return err
}
func UpdateRepositoryLastMonthVisits(repoID int64, amount int64) error {
_, err := x.Exec("update repository set last_month_visits = ? where id = ?", amount, repoID)
return err
}

func SyncStatDataToRepo(repo *Repository) {
//Save the visit number of repository in the last month
if lv, err := SumLastMonthNumVisits(repo.ID); err == nil {
UpdateRepositoryLastMonthVisits(repo.ID, lv)
}
//Save the commits number of repository in the last four month
if lc, err := SumLastFourMonthNumCommits(repo.ID); err == nil {
UpdateRepositoryLastFourMonthCommits(repo.ID, lc)
}
}

+ 33
- 21
models/repo_list.go View File

@@ -201,29 +201,41 @@ func (s SearchOrderBy) String() string {
return string(s)
}

type FindReposResponse struct {
Repos []*Repository4Card
Page int
PageSize int
Total int64
}

// Strings for sorting result
const (
SearchOrderByAlphabetically SearchOrderBy = "name ASC"
SearchOrderByAlphabeticallyReverse SearchOrderBy = "name DESC"
SearchOrderByLeastUpdated SearchOrderBy = "updated_unix ASC"
SearchOrderByRecentUpdated SearchOrderBy = "updated_unix DESC"
SearchOrderByOldest SearchOrderBy = "created_unix ASC"
SearchOrderByNewest SearchOrderBy = "created_unix DESC"
SearchOrderBySize SearchOrderBy = "size ASC"
SearchOrderBySizeReverse SearchOrderBy = "size DESC"
SearchOrderByID SearchOrderBy = "id ASC"
SearchOrderByIDReverse SearchOrderBy = "id DESC"
SearchOrderByStars SearchOrderBy = "num_stars ASC"
SearchOrderByStarsReverse SearchOrderBy = "num_stars DESC"
SearchOrderByForks SearchOrderBy = "num_forks ASC"
SearchOrderByForksReverse SearchOrderBy = "num_forks DESC"
SearchOrderByDownloadTimes SearchOrderBy = "download_times DESC"
SearchOrderByUseCount SearchOrderBy = "use_count ASC"
SearchOrderByUseCountReverse SearchOrderBy = "use_count DESC"
SearchOrderByHot SearchOrderBy = "(num_watches + num_stars + num_forks + clone_cnt) DESC"
SearchOrderByActive SearchOrderBy = "(num_issues + num_pulls + num_commit) DESC"
SearchOrderByWatches SearchOrderBy = "num_watches DESC"
SearchOrderByDefault SearchOrderBy = "recommend desc,num_stars DESC,updated_unix DESC"
SearchOrderByAlphabetically SearchOrderBy = "name ASC"
SearchOrderByAlphabeticallyReverse SearchOrderBy = "name DESC"
SearchOrderByLeastUpdated SearchOrderBy = "updated_unix ASC"
SearchOrderByRecentUpdated SearchOrderBy = "updated_unix DESC"
SearchOrderByOldest SearchOrderBy = "created_unix ASC"
SearchOrderByNewest SearchOrderBy = "created_unix DESC"
SearchOrderBySize SearchOrderBy = "size ASC"
SearchOrderBySizeReverse SearchOrderBy = "size DESC"
SearchOrderByID SearchOrderBy = "id ASC"
SearchOrderByIDReverse SearchOrderBy = "id DESC"
SearchOrderByStars SearchOrderBy = "num_stars ASC"
SearchOrderByStarsReverse SearchOrderBy = "num_stars DESC"
SearchOrderByForks SearchOrderBy = "num_forks ASC"
SearchOrderByForksReverse SearchOrderBy = "num_forks DESC"
SearchOrderByDownloadTimes SearchOrderBy = "download_times DESC"
SearchOrderByUseCount SearchOrderBy = "use_count ASC"
SearchOrderByUseCountReverse SearchOrderBy = "use_count DESC"
SearchOrderByHot SearchOrderBy = "(num_watches + num_stars + num_forks + clone_cnt) DESC"
SearchOrderByActive SearchOrderBy = "(num_issues + num_pulls + num_commit) DESC"
SearchOrderByWatches SearchOrderBy = "num_watches DESC"
SearchOrderByDefault SearchOrderBy = "recommend desc,num_stars DESC,updated_unix DESC"
SearchOrderByAiTaskCntReverse SearchOrderBy = "ai_task_cnt desc"
SearchOrderByModelCntReverse SearchOrderBy = "model_cnt desc"
SearchOrderByDatasetCntReverse SearchOrderBy = "dataset_cnt desc"
SearchOrderByLastMonthVisitsReverse SearchOrderBy = "last_month_visits desc"
SearchOrderByLastFourMonthCommitsReverse SearchOrderBy = "last_four_month_commits desc"
)

// SearchRepositoryCondition creates a query condition according search repository options


+ 20
- 0
models/repo_statistic.go View File

@@ -200,3 +200,23 @@ func UpdateRepoStatVisits(repoStat *RepoStatistic) error {
_, err := xStatistic.Exec(sql, repoStat.NumVisits, repoStat.RepoID, repoStat.Date)
return err
}

func SumRepoStatColumn(begin, end time.Time, repoId int64, columnName string) (int64, error) {
res, err := xStatistic.Where("created_unix <= ? and created_unix >= ? and repo_id = ? ", end.Unix(), begin.Unix(), repoId).Sum(&RepoStatistic{}, columnName)
if err != nil {
return 0, err
}
return int64(res), nil
}

func SumLastMonthNumVisits(repoId int64) (int64, error) {
end := time.Now()
begin := end.AddDate(0, 0, -30)
return SumRepoStatColumn(begin, end, repoId, "num_visits")
}

func SumLastFourMonthNumCommits(repoId int64) (int64, error) {
end := time.Now()
begin := end.AddDate(0, 0, -120)
return SumRepoStatColumn(begin, end, repoId, "num_commits_added")
}

+ 31
- 0
models/repo_tag.go View File

@@ -4,6 +4,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/timeutil"
"fmt"
"xorm.io/builder"
)

type OfficialTag struct {
@@ -166,3 +167,33 @@ func GetAllOfficialTags() ([]OfficialTag, error) {
}
return o, nil
}

type FindSelectedReposOpts struct {
ListOptions
OrgId int64
OnlyPublic bool
}

func GetSelectedRepos(opts FindSelectedReposOpts) ([]*Repository, error) {
if opts.Page < 1 {
opts.Page = 1
}
var cond = builder.NewCond()
cond = cond.And(builder.Eq{"official_tag.code": "selected"})
if opts.OrgId > 0 {
cond = cond.And(builder.Eq{"official_tag_repos.org_id": opts.OrgId})
}
if opts.OnlyPublic {
cond = cond.And(builder.Eq{"repository.is_private": false})
}
t := make([]*Repository, 0)
err := x.Join("inner", "official_tag_repos", "repository.id = official_tag_repos.repo_id").
Join("inner", "official_tag", "official_tag.id = official_tag_repos.tag_id").
Where(cond).OrderBy("repository.updated_unix desc").Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).Find(&t)

if err != nil {
return nil, err
}

return t, nil
}

+ 93
- 0
models/resource_specification.go View File

@@ -3,6 +3,7 @@ package models
import (
"code.gitea.io/gitea/modules/timeutil"
"fmt"
"strings"
"xorm.io/builder"
)

@@ -197,12 +198,104 @@ type Specification struct {
AiCenterName string
IsExclusive bool
ExclusiveOrg string
//specs that have the same sourceSpecId, computeResource and cluster as current spec
RelatedSpecs []*Specification
}

func (Specification) TableName() string {
return "resource_specification"
}

func (s *Specification) loadRelatedSpecs() {
if s.RelatedSpecs != nil {
return
}
defaultSpecs := make([]*Specification, 0)
if s.SourceSpecId == "" {
s.RelatedSpecs = defaultSpecs
return
}
r, err := FindSpecs(FindSpecsOptions{
ComputeResource: s.ComputeResource,
Cluster: s.Cluster,
SourceSpecId: s.SourceSpecId,
RequestAll: true,
SpecStatus: SpecOnShelf,
})
if err != nil {
s.RelatedSpecs = defaultSpecs
return
}
s.RelatedSpecs = r
}
func (s *Specification) GetAvailableCenterIds(userIds ...int64) []string {
s.loadRelatedSpecs()

if len(s.RelatedSpecs) == 0 {
return make([]string, 0)
}

var uId int64
if len(userIds) > 0 {
uId = userIds[0]
}
//filter exclusive specs
specs := FilterExclusiveSpecs(s.RelatedSpecs, uId)

centerIds := make([]string, len(specs))
for i, v := range specs {
centerIds[i] = v.AiCenterCode
}
return centerIds
}

func FilterExclusiveSpecs(r []*Specification, userId int64) []*Specification {
if userId == 0 {
return r
}
specs := make([]*Specification, 0, len(r))
specMap := make(map[int64]string, 0)
for i := 0; i < len(r); i++ {
spec := r[i]
if _, has := specMap[spec.ID]; has {
continue
}
if !spec.IsExclusive {
specs = append(specs, spec)
specMap[spec.ID] = ""
continue
}
orgs := strings.Split(spec.ExclusiveOrg, ";")
for _, org := range orgs {
isMember, _ := IsOrganizationMemberByOrgName(org, userId)
if isMember {
specs = append(specs, spec)
specMap[spec.ID] = ""
break
}
}
}
return specs
}

func DistinctSpecs(r []*Specification) []*Specification {
specs := make([]*Specification, 0, len(r))
sourceSpecIdMap := make(map[string]string, 0)
for i := 0; i < len(r); i++ {
spec := r[i]
if spec.SourceSpecId == "" {
specs = append(specs, spec)
continue
}
if _, has := sourceSpecIdMap[spec.SourceSpecId]; has {
continue
}
specs = append(specs, spec)
sourceSpecIdMap[spec.SourceSpecId] = ""
}
return specs
}

func InsertResourceSpecification(r ResourceSpecification) (int64, error) {
return x.Insert(&r)
}


+ 14
- 0
models/topic.go View File

@@ -9,6 +9,7 @@ import (
"regexp"
"strings"
"unicode/utf8"
"xorm.io/xorm"

"code.gitea.io/gitea/modules/timeutil"

@@ -337,3 +338,16 @@ func GetOrgTopics(orgId int64) ([]Topic, error) {

return result, nil
}

func UpdateRepoTopics(repoID int64, topicNames []string, sess ...*xorm.Engine) error {
e := x
if len(sess) > 0 {
e = sess[0]
}
if _, err := e.ID(repoID).Cols("topics").Update(&Repository{
Topics: topicNames,
}); err != nil {
return err
}
return nil
}

+ 34
- 0
models/user.go View File

@@ -198,6 +198,40 @@ type SearchOrganizationsOptions struct {
All bool
}

type User4Front struct {
ID int64
LowerName string `xorm:"UNIQUE NOT NULL"`
Name string `xorm:"UNIQUE NOT NULL"`
FullName string
Email string `xorm:"NOT NULL"`
Language string `xorm:"VARCHAR(5)"`
Description string
RelAvatarLink string
NumMembers int
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}

func (u *User) ToFrontFormat() *User4Front {
uf := &User4Front{
ID: u.ID,
LowerName: u.LowerName,
Name: u.Name,
FullName: u.FullName,
Email: u.Email,
Language: u.Language,
Description: u.Description,
CreatedUnix: u.CreatedUnix,
UpdatedUnix: u.UpdatedUnix,
NumMembers: u.NumMembers,
}
if !u.KeepEmailPrivate {
uf.Email = u.Email
}
uf.RelAvatarLink = u.RelAvatarLink()
return uf
}

// GenerateRandomAvatar generates a random avatar for user.
func (u *User) IsBindWechat() bool {
return u.WechatOpenId != ""


+ 17
- 0
models/user_analysis_for_activity.go View File

@@ -449,3 +449,20 @@ func QueryUserLoginInfo(userIds []int64) []*UserLoginLog {

return loginList
}

func QueryUserAnnualReport(userId int64) *UserSummaryCurrentYear {
statictisSess := xStatistic.NewSession()
defer statictisSess.Close()
log.Info("userId=" + fmt.Sprint(userId))

reList := make([]*UserSummaryCurrentYear, 0)
err := statictisSess.Select("*").Table(new(UserSummaryCurrentYear)).Where("id=" + fmt.Sprint(userId)).Find(&reList)
if err == nil {
if len(reList) > 0 {
return reList[0]
}
} else {
log.Info("error:=" + err.Error())
}
return nil
}

+ 197
- 49
models/user_business_analysis.go View File

@@ -132,11 +132,17 @@ func makeResultForMonth(allUserInfo []*UserMetrics, count int) []*UserMetrics {
if count > 0 {
for _, userMetrics := range allUserInfo {
dateTime := time.Unix(userMetrics.CountDate, 0)
month := fmt.Sprint(dateTime.Year()) + "-" + fmt.Sprint(int(dateTime.Month()))
mInt := int(dateTime.Month())
mString := fmt.Sprint(mInt)
if mInt < 10 {
mString = "0" + mString
}
month := fmt.Sprint(dateTime.Year()) + "-" + mString
if _, ok := monthMap[month]; !ok {
monthUserMetrics := &UserMetrics{
DisplayDate: month,
ActivateRegistUser: userMetrics.ActivateRegistUser,
RegistActivityUser: userMetrics.RegistActivityUser,
NotActivateRegistUser: userMetrics.NotActivateRegistUser,
TotalUser: userMetrics.TotalUser,
TotalNotActivateRegistUser: userMetrics.TotalUser - userMetrics.TotalActivateRegistUser,
@@ -152,6 +158,7 @@ func makeResultForMonth(allUserInfo []*UserMetrics, count int) []*UserMetrics {
value.ActivateRegistUser += userMetrics.ActivateRegistUser
value.NotActivateRegistUser += userMetrics.NotActivateRegistUser
value.HasActivityUser += userMetrics.HasActivityUser
value.RegistActivityUser += userMetrics.RegistActivityUser
value.TotalRegistUser += userMetrics.ActivateRegistUser + userMetrics.NotActivateRegistUser
value.ActivateIndex = float64(value.ActivateRegistUser) / float64(value.TotalRegistUser)
value.DaysForMonth += 1
@@ -348,6 +355,7 @@ func QueryUserStaticDataForUserDefine(opts *UserBusinessAnalysisQueryOptions, wi
OpenIIndexMap := queryUserRepoOpenIIndex(start_unix, end_unix)
CloudBrainTaskMap, CloudBrainTaskItemMap := queryCloudBrainTask(start_unix, end_unix)
AiModelManageMap := queryUserModel(start_unix, end_unix)
AiModelConvertMap := queryUserModelConvert(start_unix, end_unix)

CollectDataset, CollectedDataset := queryDatasetStars(start_unix, end_unix)
RecommendDataset, _ := queryRecommedDataSet(start_unix, end_unix)
@@ -420,6 +428,7 @@ func QueryUserStaticDataForUserDefine(opts *UserBusinessAnalysisQueryOptions, wi
dateRecord.GpuBenchMarkJob = getMapKeyStringValue(fmt.Sprint(dateRecord.ID)+"_GpuBenchMarkJob", CloudBrainTaskItemMap)
dateRecord.CloudBrainRunTime = getMapKeyStringValue(fmt.Sprint(dateRecord.ID)+"_CloudBrainRunTime", CloudBrainTaskItemMap)
dateRecord.CommitModelCount = getMapValue(dateRecord.ID, AiModelManageMap)
dateRecord.ModelConvertCount = getMapValue(dateRecord.ID, AiModelConvertMap)

dateRecord.CollectDataset = getMapValue(dateRecord.ID, CollectDataset)
dateRecord.CollectedDataset = getMapValue(dateRecord.ID, CollectedDataset)
@@ -539,6 +548,7 @@ func QueryUserStaticDataPage(opts *UserBusinessAnalysisQueryOptions) ([]*UserBus
resultMap[userRecord.ID].CommitDatasetSize += userRecord.CommitDatasetSize
resultMap[userRecord.ID].CommitDatasetNum += userRecord.CommitDatasetNum
resultMap[userRecord.ID].CommitModelCount += userRecord.CommitModelCount
resultMap[userRecord.ID].ModelConvertCount += userRecord.ModelConvertCount
resultMap[userRecord.ID].SolveIssueCount += userRecord.SolveIssueCount
resultMap[userRecord.ID].EncyclopediasCount += userRecord.EncyclopediasCount
resultMap[userRecord.ID].CreateRepoCount += userRecord.CreateRepoCount
@@ -576,7 +586,7 @@ func refreshUserStaticTable(wikiCountMap map[string]int, tableName string, pageS
startTime := currentTimeNow.AddDate(0, 0, -1)

CodeMergeCountMap := queryPullRequest(start_unix, end_unix)
CommitCountMap, mostActiveMap := queryCommitAction(start_unix, end_unix, 5)
CommitCountMap, _ := queryCommitAction(start_unix, end_unix, 5)
IssueCountMap := queryCreateIssue(start_unix, end_unix)

CommentCountMap := queryComment(start_unix, end_unix)
@@ -592,29 +602,25 @@ func refreshUserStaticTable(wikiCountMap map[string]int, tableName string, pageS
//log.Info("CommitCodeSizeMapJson=" + string(CommitCodeSizeMapJson))
}
//CommitCodeSizeMap := queryCommitCodeSize(StartTimeNextDay.Unix(), EndTimeNextDay.Unix())
CommitDatasetSizeMap, CommitDatasetNumMap, dataSetDownloadMap := queryDatasetSize(start_unix, end_unix)
CommitDatasetSizeMap, CommitDatasetNumMap, _ := queryDatasetSize(start_unix, end_unix)
SolveIssueCountMap := querySolveIssue(start_unix, end_unix)
CreateRepoCountMap, DetailInfoMap, MostDownloadMap := queryUserCreateRepo(start_unix, end_unix)
CreateRepoCountMap, _, _ := queryUserCreateRepo(start_unix, end_unix)
LoginCountMap := queryLoginCount(start_unix, end_unix)

OpenIIndexMap := queryUserRepoOpenIIndex(startTime.Unix(), end_unix)
CloudBrainTaskMap, CloudBrainTaskItemMap := queryCloudBrainTask(start_unix, end_unix)
AiModelManageMap := queryUserModel(start_unix, end_unix)
AiModelConvertMap := queryUserModelConvert(start_unix, end_unix)

CollectDataset, CollectedDataset := queryDatasetStars(start_unix, end_unix)
RecommendDataset, CreatedDataset := queryRecommedDataSet(start_unix, end_unix)
RecommendDataset, _ := queryRecommedDataSet(start_unix, end_unix)
CollectImage, CollectedImage := queryImageStars(start_unix, end_unix)
RecommendImage := queryRecommedImage(start_unix, end_unix)

InvitationMap := queryUserInvitationCount(start_unix, end_unix)

DataDate := currentTimeNow.Format("2006-01-02") + " 00:01"
bonusMap := make(map[string]map[string]int)
if tableName == "user_business_analysis_current_year" {
bonusMap = getBonusMap()
log.Info("truncate all data from table:user_summary_current_year ")
statictisSess.Exec("TRUNCATE TABLE user_summary_current_year")
}

cond := "type != 1 and is_active=true"
count, err := sess.Where(cond).Count(new(User))
if err != nil {
@@ -680,6 +686,7 @@ func refreshUserStaticTable(wikiCountMap map[string]int, tableName string, pageS
dateRecordAll.GpuBenchMarkJob = getMapKeyStringValue(fmt.Sprint(dateRecordAll.ID)+"_GpuBenchMarkJob", CloudBrainTaskItemMap)
dateRecordAll.CloudBrainRunTime = getMapKeyStringValue(fmt.Sprint(dateRecordAll.ID)+"_CloudBrainRunTime", CloudBrainTaskItemMap)
dateRecordAll.CommitModelCount = getMapValue(dateRecordAll.ID, AiModelManageMap)
dateRecordAll.ModelConvertCount = getMapValue(dateRecordAll.ID, AiModelConvertMap)
dateRecordAll.CollectDataset = getMapValue(dateRecordAll.ID, CollectDataset)
dateRecordAll.CollectedDataset = getMapValue(dateRecordAll.ID, CollectedDataset)
dateRecordAll.RecommendDataset = getMapValue(dateRecordAll.ID, RecommendDataset)
@@ -712,37 +719,6 @@ func refreshUserStaticTable(wikiCountMap map[string]int, tableName string, pageS
userMetrics["TotalHasActivityUser"] = getMapKeyStringValue("TotalHasActivityUser", userMetrics) + 1
}
}
if tableName == "user_business_analysis_current_year" {
//年度数据
subTime := time.Now().UTC().Sub(dateRecordAll.RegistDate.AsTime().UTC())
mostActiveDay := ""
if userInfo, ok := mostActiveMap[dateRecordAll.ID]; ok {
mostActiveDay = getMostActiveJson(userInfo)
}
scoreMap := make(map[string]float64)
repoInfo := getRepoDetailInfo(DetailInfoMap, dateRecordAll.ID, MostDownloadMap)
dataSetInfo, datasetscore := getDataSetInfo(dateRecordAll.ID, CreatedDataset, dataSetDownloadMap, CommitDatasetNumMap, CollectedDataset)
scoreMap["datasetscore"] = datasetscore
codeInfo, codescore := getCodeInfo(dateRecordAll)
scoreMap["codescore"] = codescore
cloudBrainInfo := getCloudBrainInfo(dateRecordAll, CloudBrainTaskItemMap, scoreMap)
playARoll := getPlayARoll(bonusMap, dateRecordAll.Name, scoreMap)
re := &UserSummaryCurrentYear{
ID: dateRecordAll.ID,
Name: dateRecordAll.Name,
Email: dateRecordAll.Email,
Phone: dateRecordAll.Phone,
RegistDate: dateRecordAll.RegistDate,
DateCount: int(subTime.Hours()) / 24,
MostActiveDay: mostActiveDay,
RepoInfo: repoInfo,
DataSetInfo: dataSetInfo,
CodeInfo: codeInfo,
CloudBrainInfo: cloudBrainInfo,
PlayARoll: playARoll,
}
statictisSess.Insert(re)
}
}
if len(dateRecordBatch) > 0 {
err := insertTable(dateRecordBatch, tableName, statictisSess)
@@ -772,6 +748,138 @@ func refreshUserStaticTable(wikiCountMap map[string]int, tableName string, pageS
log.Info("refresh data finished.tableName=" + tableName + " total record:" + fmt.Sprint(insertCount))
}

func RefreshUserYearTable(pageStartTime time.Time, pageEndTime time.Time) {
sess := x.NewSession()
defer sess.Close()
log.Info("RefreshUserYearTable start....")
statictisSess := xStatistic.NewSession()
defer statictisSess.Close()

log.Info("UserYear StartTime:" + pageStartTime.Format("2006-01-02 15:04:05"))
log.Info("UserYear EndTime time:" + pageEndTime.Format("2006-01-02 15:04:05"))

start_unix := pageStartTime.Unix()
end_unix := pageEndTime.Unix()

CodeMergeCountMap := queryPullRequest(start_unix, end_unix)
CommitCountMap, mostActiveMap := queryCommitAction(start_unix, end_unix, 5)
IssueCountMap := queryCreateIssue(start_unix, end_unix)

CommentCountMap := queryComment(start_unix, end_unix)

CommitCodeSizeMap, err := GetAllUserKPIStats(pageStartTime, pageEndTime)
if err != nil {
log.Info("query commit code errr.")
} else {
log.Info("query commit code size, len=" + fmt.Sprint(len(CommitCodeSizeMap)))
}
CommitDatasetSizeMap, CommitDatasetNumMap, dataSetDownloadMap := queryDatasetSize(start_unix, end_unix)
SolveIssueCountMap := querySolveIssue(start_unix, end_unix)
CreateRepoCountMap, DetailInfoMap, MostDownloadMap := queryUserCreateRepo(start_unix, end_unix)

CloudBrainTaskMap, CloudBrainTaskItemMap := queryCloudBrainTask(start_unix, end_unix)

_, CollectedDataset := queryDatasetStars(start_unix, end_unix)
_, CreatedDataset := queryRecommedDataSet(start_unix, end_unix)

bonusMap := getBonusMap()
log.Info("truncate all data from table:user_summary_current_year ")
statictisSess.Exec("TRUNCATE TABLE user_summary_current_year")

cond := "type != 1 and is_active=true"
count, err := sess.Where(cond).Count(new(User))
if err != nil {
log.Info("query user error. return.")
return
}
var indexTotal int64
indexTotal = 0
for {
sess.Select("`user`.*").Table("user").Where(cond).OrderBy("id asc").Limit(PAGE_SIZE, int(indexTotal))
userList := make([]*User, 0)
sess.Find(&userList)
for _, userRecord := range userList {
var dateRecordAll UserBusinessAnalysisAll
dateRecordAll.ID = userRecord.ID
dateRecordAll.Email = userRecord.Email
dateRecordAll.Phone = userRecord.PhoneNumber
dateRecordAll.RegistDate = userRecord.CreatedUnix
dateRecordAll.Name = userRecord.Name

dateRecordAll.CodeMergeCount = getMapValue(dateRecordAll.ID, CodeMergeCountMap)
dateRecordAll.CommitCount = getMapValue(dateRecordAll.ID, CommitCountMap)
dateRecordAll.IssueCount = getMapValue(dateRecordAll.ID, IssueCountMap)
dateRecordAll.CommentCount = getMapValue(dateRecordAll.ID, CommentCountMap)

if _, ok := CommitCodeSizeMap[dateRecordAll.Email]; !ok {
dateRecordAll.CommitCodeSize = 0
} else {
dateRecordAll.CommitCodeSize = int(CommitCodeSizeMap[dateRecordAll.Email].CommitLines)
}
//dateRecordAll.CommitCodeSize = getMapValue(dateRecordAll.ID, CommitCodeSizeMap)
dateRecordAll.CommitDatasetSize = getMapValue(dateRecordAll.ID, CommitDatasetSizeMap)
dateRecordAll.CommitDatasetNum = getMapValue(dateRecordAll.ID, CommitDatasetNumMap)
dateRecordAll.SolveIssueCount = getMapValue(dateRecordAll.ID, SolveIssueCountMap)
dateRecordAll.CreateRepoCount = getMapValue(dateRecordAll.ID, CreateRepoCountMap)

dateRecordAll.CloudBrainTaskNum = getMapValue(dateRecordAll.ID, CloudBrainTaskMap)
dateRecordAll.GpuDebugJob = getMapKeyStringValue(fmt.Sprint(dateRecordAll.ID)+"_GpuDebugJob", CloudBrainTaskItemMap)
dateRecordAll.NpuDebugJob = getMapKeyStringValue(fmt.Sprint(dateRecordAll.ID)+"_NpuDebugJob", CloudBrainTaskItemMap)
dateRecordAll.GpuTrainJob = getMapKeyStringValue(fmt.Sprint(dateRecordAll.ID)+"_GpuTrainJob", CloudBrainTaskItemMap)
dateRecordAll.NpuTrainJob = getMapKeyStringValue(fmt.Sprint(dateRecordAll.ID)+"_NpuTrainJob", CloudBrainTaskItemMap)
dateRecordAll.NpuInferenceJob = getMapKeyStringValue(fmt.Sprint(dateRecordAll.ID)+"_NpuInferenceJob", CloudBrainTaskItemMap)
dateRecordAll.GpuBenchMarkJob = getMapKeyStringValue(fmt.Sprint(dateRecordAll.ID)+"_GpuBenchMarkJob", CloudBrainTaskItemMap)
dateRecordAll.CloudBrainRunTime = getMapKeyStringValue(fmt.Sprint(dateRecordAll.ID)+"_CloudBrainRunTime", CloudBrainTaskItemMap)

//年度数据
subTime := time.Now().UTC().Sub(dateRecordAll.RegistDate.AsTime().UTC())
mostActiveDay := ""
if userInfo, ok := mostActiveMap[dateRecordAll.ID]; ok {
mostActiveDay = getMostActiveJson(userInfo)
}
scoreMap := make(map[string]float64)
repoInfo := getRepoDetailInfo(DetailInfoMap, dateRecordAll.ID, MostDownloadMap)
dataSetInfo, datasetscore := getDataSetInfo(dateRecordAll.ID, CreatedDataset, dataSetDownloadMap, CommitDatasetNumMap, CollectedDataset)
scoreMap["datasetscore"] = datasetscore
codeInfo, codescore := getCodeInfo(dateRecordAll)
scoreMap["codescore"] = codescore
cloudBrainInfo := getCloudBrainInfo(dateRecordAll, CloudBrainTaskItemMap, scoreMap)
playARoll := getPlayARoll(bonusMap, dateRecordAll.Name, scoreMap)
re := &UserSummaryCurrentYear{
ID: dateRecordAll.ID,
Name: dateRecordAll.Name,
Email: dateRecordAll.Email,
Phone: dateRecordAll.Phone,
RegistDate: dateRecordAll.RegistDate,
DateCount: int(subTime.Hours()) / 24,
MostActiveDay: mostActiveDay,
RepoInfo: repoInfo,
DataSetInfo: dataSetInfo,
CodeInfo: codeInfo,
CloudBrainInfo: cloudBrainInfo,
PlayARoll: playARoll,
}
statictisSess.Insert(re)
}
indexTotal += PAGE_SIZE
if indexTotal >= count {
break
}
}
log.Info("update user year data finished. ")
}

func isUserYearData(tableName string) bool {
if tableName == "user_business_analysis_current_year" {
currentTimeNow := time.Now()
if currentTimeNow.Year() >= 2023 {
return false
}
return true
}
return false
}

func getBonusMap() map[string]map[string]int {
bonusMap := make(map[string]map[string]int)
url := setting.RecommentRepoAddr + "bonus/record.txt"
@@ -794,6 +902,7 @@ func getBonusMap() map[string]map[string]int {
record, ok := bonusMap[userName]
if !ok {
record = make(map[string]int)
bonusMap[userName] = record
}
record["times"] = getMapKeyStringValue("times", record) + getIntValue(aLine[3])
record["total_bonus"] = getMapKeyStringValue("total_bonus", record) + getIntValue(aLine[4])
@@ -979,7 +1088,7 @@ func insertTable(dateRecords []UserBusinessAnalysisAll, tableName string, static

insertBatchSql := "INSERT INTO public." + tableName +
"(id, count_date, code_merge_count, commit_count, issue_count, comment_count, focus_repo_count, star_repo_count, watched_count, gitea_age_month, commit_code_size, commit_dataset_size, " +
"commit_model_count, solve_issue_count, encyclopedias_count, regist_date, create_repo_count, login_count, open_i_index, email, name, data_date,cloud_brain_task_num,gpu_debug_job,npu_debug_job,gpu_train_job,npu_train_job,npu_inference_job,gpu_bench_mark_job,cloud_brain_run_time,commit_dataset_num,user_index,user_location,focus_other_user,collect_dataset,collected_dataset,recommend_dataset,collect_image,collected_image,recommend_image,user_index_primitive,phone,invitation_user_num) " +
"commit_model_count, solve_issue_count, encyclopedias_count, regist_date, create_repo_count, login_count, open_i_index, email, name, data_date,cloud_brain_task_num,gpu_debug_job,npu_debug_job,gpu_train_job,npu_train_job,npu_inference_job,gpu_bench_mark_job,cloud_brain_run_time,commit_dataset_num,user_index,user_location,focus_other_user,collect_dataset,collected_dataset,recommend_dataset,collect_image,collected_image,recommend_image,user_index_primitive,phone,invitation_user_num,model_convert_count) " +
"VALUES"

for i, record := range dateRecords {
@@ -988,7 +1097,7 @@ func insertTable(dateRecords []UserBusinessAnalysisAll, tableName string, static
", " + fmt.Sprint(record.WatchedCount) + ", " + fmt.Sprint(record.GiteaAgeMonth) + ", " + fmt.Sprint(record.CommitCodeSize) + ", " + fmt.Sprint(record.CommitDatasetSize) +
", " + fmt.Sprint(record.CommitModelCount) + ", " + fmt.Sprint(record.SolveIssueCount) + ", " + fmt.Sprint(record.EncyclopediasCount) + ", " + fmt.Sprint(record.RegistDate) +
", " + fmt.Sprint(record.CreateRepoCount) + ", " + fmt.Sprint(record.LoginCount) + ", " + fmt.Sprint(record.OpenIIndex) + ", '" + record.Email + "', '" + record.Name + "', '" + record.DataDate + "'," + fmt.Sprint(record.CloudBrainTaskNum) + "," + fmt.Sprint(record.GpuDebugJob) + "," + fmt.Sprint(record.NpuDebugJob) + "," + fmt.Sprint(record.GpuTrainJob) + "," + fmt.Sprint(record.NpuTrainJob) + "," + fmt.Sprint(record.NpuInferenceJob) + "," + fmt.Sprint(record.GpuBenchMarkJob) + "," + fmt.Sprint(record.CloudBrainRunTime) + "," + fmt.Sprint(record.CommitDatasetNum) + "," + fmt.Sprint(record.UserIndex) + ",'" + record.UserLocation + "'," +
fmt.Sprint(record.FocusOtherUser) + "," + fmt.Sprint(record.CollectDataset) + "," + fmt.Sprint(record.CollectedDataset) + "," + fmt.Sprint(record.RecommendDataset) + "," + fmt.Sprint(record.CollectImage) + "," + fmt.Sprint(record.CollectedImage) + "," + fmt.Sprint(record.RecommendImage) + "," + fmt.Sprint(record.UserIndexPrimitive) + ",'" + record.Phone + "'" + "," + fmt.Sprint(record.InvitationUserNum) + ")"
fmt.Sprint(record.FocusOtherUser) + "," + fmt.Sprint(record.CollectDataset) + "," + fmt.Sprint(record.CollectedDataset) + "," + fmt.Sprint(record.RecommendDataset) + "," + fmt.Sprint(record.CollectImage) + "," + fmt.Sprint(record.CollectedImage) + "," + fmt.Sprint(record.RecommendImage) + "," + fmt.Sprint(record.UserIndexPrimitive) + ",'" + record.Phone + "'" + "," + fmt.Sprint(record.InvitationUserNum) + "," + fmt.Sprint(record.ModelConvertCount) + ")"
if i < (len(dateRecords) - 1) {
insertBatchSql += ","
}
@@ -1079,6 +1188,7 @@ func CounDataByDateAndReCount(wikiCountMap map[string]int, startTime time.Time,
OpenIIndexMap := queryUserRepoOpenIIndex(start_unix, end_unix)
CloudBrainTaskMap, CloudBrainTaskItemMap := queryCloudBrainTask(start_unix, end_unix)
AiModelManageMap := queryUserModel(start_unix, end_unix)
AiModelConvertMap := queryUserModelConvert(start_unix, end_unix)

CollectDataset, CollectedDataset := queryDatasetStars(start_unix, end_unix)
RecommendDataset, _ := queryRecommedDataSet(start_unix, end_unix)
@@ -1160,7 +1270,7 @@ func CounDataByDateAndReCount(wikiCountMap map[string]int, startTime time.Time,
dateRecord.GpuBenchMarkJob = getMapKeyStringValue(fmt.Sprint(dateRecord.ID)+"_GpuBenchMarkJob", CloudBrainTaskItemMap)
dateRecord.CloudBrainRunTime = getMapKeyStringValue(fmt.Sprint(dateRecord.ID)+"_CloudBrainRunTime", CloudBrainTaskItemMap)
dateRecord.CommitModelCount = getMapValue(dateRecord.ID, AiModelManageMap)
dateRecord.ModelConvertCount = getMapValue(dateRecord.ID, AiModelConvertMap)
dateRecord.CollectDataset = getMapValue(dateRecord.ID, CollectDataset)
dateRecord.CollectedDataset = getMapValue(dateRecord.ID, CollectedDataset)
dateRecord.RecommendDataset = getMapValue(dateRecord.ID, RecommendDataset)
@@ -1349,6 +1459,7 @@ func getUserIndexFromAnalysisAll(dateRecord UserBusinessAnalysisAll, ParaWeight
result += float64(dateRecord.CreateRepoCount) * getParaWeightValue("CreateRepoCount", ParaWeight, 0.05)
result += float64(dateRecord.CloudBrainTaskNum) * getParaWeightValue("CloudBrainTaskNum", ParaWeight, 0.3)
result += float64(dateRecord.CommitModelCount) * getParaWeightValue("CommitModelCount", ParaWeight, 0.2)
result += float64(dateRecord.ModelConvertCount) * getParaWeightValue("ModelConvertCount", ParaWeight, 0.2)
result += dateRecord.OpenIIndex * getParaWeightValue("OpenIIndex", ParaWeight, 0.1)

result += float64(dateRecord.CollectDataset) * getParaWeightValue("CollectDataset", ParaWeight, 0.1)
@@ -1374,6 +1485,7 @@ func getUserActivateAll(dateRecord UserBusinessAnalysisAll) int {
result += dateRecord.CreateRepoCount
result += dateRecord.CloudBrainTaskNum
result += dateRecord.CommitModelCount
result += dateRecord.ModelConvertCount
result += dateRecord.CommitDatasetNum
result += dateRecord.FocusOtherUser
result += dateRecord.CollectDataset
@@ -1395,6 +1507,7 @@ func getUserActivate(dateRecord UserBusinessAnalysis) int {
result += dateRecord.CreateRepoCount
result += dateRecord.CloudBrainTaskNum
result += dateRecord.CommitModelCount
result += dateRecord.ModelConvertCount
result += dateRecord.CommitDatasetNum
result += dateRecord.FocusOtherUser
result += dateRecord.CollectDataset
@@ -1431,6 +1544,7 @@ func getUserIndex(dateRecord UserBusinessAnalysis, ParaWeight map[string]float64
result += float64(dateRecord.CreateRepoCount) * getParaWeightValue("CreateRepoCount", ParaWeight, 0.05)
result += float64(dateRecord.CloudBrainTaskNum) * getParaWeightValue("CloudBrainTaskNum", ParaWeight, 0.3)
result += float64(dateRecord.CommitModelCount) * getParaWeightValue("CommitModelCount", ParaWeight, 0.2)
result += float64(dateRecord.ModelConvertCount) * getParaWeightValue("ModelConvertCount", ParaWeight, 0.2)
result += dateRecord.OpenIIndex * getParaWeightValue("OpenIIndex", ParaWeight, 0.1)

result += float64(dateRecord.CollectDataset) * getParaWeightValue("CollectDataset", ParaWeight, 0.1)
@@ -1475,10 +1589,6 @@ func getInt(str string) int {
return int(re)
}

func CounDataByDate(wikiCountMap map[string]int, startTime time.Time, endTime time.Time) {
CounDataByDateAndReCount(wikiCountMap, startTime, endTime, false)
}

func querySolveIssue(start_unix int64, end_unix int64) map[int64]int {
sess := x.NewSession()
defer sess.Close()
@@ -2259,6 +2369,38 @@ func queryUserModel(start_unix int64, end_unix int64) map[int64]int {
return resultMap
}

func queryUserModelConvert(start_unix int64, end_unix int64) map[int64]int {
sess := x.NewSession()
defer sess.Close()
resultMap := make(map[int64]int)
cond := " created_unix>=" + fmt.Sprint(start_unix) + " and created_unix<=" + fmt.Sprint(end_unix)
count, err := sess.Where(cond).Count(new(AiModelConvert))
if err != nil {
log.Info("query AiModelConvert error. return.")
return resultMap
}
var indexTotal int64
indexTotal = 0
for {
sess.Select("id,user_id").Table("ai_model_convert").Where(cond).OrderBy("id asc").Limit(PAGE_SIZE, int(indexTotal))
aiModelList := make([]*AiModelConvert, 0)
sess.Find(&aiModelList)
log.Info("query AiModelConvert size=" + fmt.Sprint(len(aiModelList)))
for _, aiModelRecord := range aiModelList {
if _, ok := resultMap[aiModelRecord.UserId]; !ok {
resultMap[aiModelRecord.UserId] = 1
} else {
resultMap[aiModelRecord.UserId] += 1
}
}
indexTotal += PAGE_SIZE
if indexTotal >= count {
break
}
}
return resultMap
}

func queryCloudBrainTask(start_unix int64, end_unix int64) (map[int64]int, map[string]int) {
sess := x.NewSession()
defer sess.Close()
@@ -2424,3 +2566,9 @@ func GetContentFromPromote(url string) (string, error) {
allLineStr := string(bytes)
return allLineStr, nil
}

func QueryLast30DaysHighestIndexUsers(size int) ([]int64, error) {
userIds := make([]int64, 0)
err := xStatistic.Table("user_business_analysis_last30_day").Cols("id").OrderBy("user_index desc").Limit(size).Find(&userIds)
return userIds, err
}

+ 9
- 0
models/user_business_struct.go View File

@@ -89,6 +89,7 @@ type UserBusinessAnalysisCurrentYear struct {

Phone string `xorm:"NULL"`
InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"`
ModelConvertCount int `xorm:"NOT NULL DEFAULT 0"`
}

type UserBusinessAnalysisLast30Day struct {
@@ -157,6 +158,7 @@ type UserBusinessAnalysisLast30Day struct {

Phone string `xorm:"NULL"`
InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"`
ModelConvertCount int `xorm:"NOT NULL DEFAULT 0"`
}

type UserBusinessAnalysisLastMonth struct {
@@ -225,6 +227,7 @@ type UserBusinessAnalysisLastMonth struct {

Phone string `xorm:"NULL"`
InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"`
ModelConvertCount int `xorm:"NOT NULL DEFAULT 0"`
}

type UserBusinessAnalysisCurrentMonth struct {
@@ -293,6 +296,7 @@ type UserBusinessAnalysisCurrentMonth struct {

Phone string `xorm:"NULL"`
InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"`
ModelConvertCount int `xorm:"NOT NULL DEFAULT 0"`
}

type UserBusinessAnalysisCurrentWeek struct {
@@ -362,6 +366,7 @@ type UserBusinessAnalysisCurrentWeek struct {

Phone string `xorm:"NULL"`
InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"`
ModelConvertCount int `xorm:"NOT NULL DEFAULT 0"`
}

type UserBusinessAnalysisYesterday struct {
@@ -431,6 +436,7 @@ type UserBusinessAnalysisYesterday struct {

Phone string `xorm:"NULL"`
InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"`
ModelConvertCount int `xorm:"NOT NULL DEFAULT 0"`
}

type UserBusinessAnalysisLastWeek struct {
@@ -500,6 +506,7 @@ type UserBusinessAnalysisLastWeek struct {

Phone string `xorm:"NULL"`
InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"`
ModelConvertCount int `xorm:"NOT NULL DEFAULT 0"`
}

type UserAnalysisPara struct {
@@ -616,6 +623,7 @@ type UserBusinessAnalysisAll struct {

Phone string `xorm:"NULL"`
InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"`
ModelConvertCount int `xorm:"NOT NULL DEFAULT 0"`
}

type UserBusinessAnalysis struct {
@@ -704,4 +712,5 @@ type UserBusinessAnalysis struct {

Phone string `xorm:"NULL"`
InvitationUserNum int `xorm:"NOT NULL DEFAULT 0"`
ModelConvertCount int `xorm:"NOT NULL DEFAULT 0"`
}

+ 1
- 0
models/user_invitation.go View File

@@ -13,6 +13,7 @@ type Invitation struct {
SrcUserID int64 `xorm:"NOT NULL DEFAULT 0"`
UserID int64 `xorm:"NOT NULL DEFAULT 0"`
Phone string `xorm:"INDEX"`
Email string `xorm:"-"`
Avatar string `xorm:"-"`
Name string `xorm:"-"`
InvitationUserNum int `xorm:"-"`


+ 1
- 4
modules/grampus/grampus.go View File

@@ -279,8 +279,6 @@ func GenerateNotebookJob(ctx *context.Context, req *GenerateNotebookJobReq) (job
func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (jobId string, err error) {
createTime := timeutil.TimeStampNow()

centerID, centerName := getCentersParamter(ctx, req)

var datasetGrampus, modelGrampus []models.GrampusDataset
var codeGrampus models.GrampusDataset
if ProcessorTypeNPU == req.ProcessType {
@@ -312,8 +310,7 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (jobId str
ResourceSpecId: req.Spec.SourceSpecId,
ImageId: req.ImageId,
ImageUrl: req.ImageUrl,
CenterID: centerID,
CenterName: centerName,
CenterID: req.Spec.GetAvailableCenterIds(ctx.User.ID),
ReplicaNum: 1,
Datasets: datasetGrampus,
Models: modelGrampus,


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

@@ -283,6 +283,9 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (jobId str
Parameter: req.Parameters,
UserImageUrl: req.UserImageUrl,
UserCommand: req.UserCommand,
ShareAddr: setting.ModelArtsShareAddr,
MountPath: setting.ModelArtsMountPath,
NasType: setting.ModelArtsNasType,
},
})
} else {
@@ -303,6 +306,9 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (jobId str
Code: req.Spec.SourceSpecId,
},
Parameter: req.Parameters,
ShareAddr: setting.ModelArtsShareAddr,
MountPath: setting.ModelArtsMountPath,
NasType: setting.ModelArtsNasType,
},
})
}
@@ -421,6 +427,9 @@ func GenerateTrainJobVersion(ctx *context.Context, req *GenerateTrainJobReq, job
PreVersionId: req.PreVersionId,
UserImageUrl: req.UserImageUrl,
UserCommand: req.UserCommand,
ShareAddr: setting.ModelArtsShareAddr,
MountPath: setting.ModelArtsMountPath,
NasType: setting.ModelArtsNasType,
},
}, jobId)
} else {
@@ -440,6 +449,9 @@ func GenerateTrainJobVersion(ctx *context.Context, req *GenerateTrainJobReq, job
},
Parameter: req.Parameters,
PreVersionId: req.PreVersionId,
ShareAddr: setting.ModelArtsShareAddr,
MountPath: setting.ModelArtsMountPath,
NasType: setting.ModelArtsNasType,
},
}, jobId)
}


+ 3
- 1
modules/modelarts/resty.go View File

@@ -497,7 +497,7 @@ sendjob:
}

req, _ := json.Marshal(createJobParams)
log.Info("%s", req)
log.Info("postapi json: %s", req)

if res.StatusCode() == http.StatusUnauthorized && retry < 1 {
retry++
@@ -543,6 +543,8 @@ func createTrainJob(createJobParams models.CreateTrainJobParams) (*models.Create
var result models.CreateTrainJobResult

retry := 0
req, _ := json.Marshal(createJobParams)
log.Info("postapi json: %s", req)

sendjob:
res, err := client.R().


+ 9
- 0
modules/redis/redis_key/repo_redis_key.go View File

@@ -0,0 +1,9 @@
package redis_key

import "fmt"

const REPO_PREFIX = "repo"

func RepoTopNContributors(repoId int64, N int) string {
return KeyJoin(REPO_PREFIX, fmt.Sprint(repoId), fmt.Sprint(N), "top_n_contributor")
}

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

@@ -585,6 +585,9 @@ var (
TrainJobFLAVORINFOS string
ModelArtsSpecialPools string
ModelArtsMultiNode string
ModelArtsShareAddr string
ModelArtsMountPath string
ModelArtsNasType string
//kanban
IsCloudbrainTimingEnabled bool

@@ -677,6 +680,10 @@ var (
CloudbrainStoppedTitle string
CloudbrainStoppedRemark string

//repo square config
IncubationSourceOrgName string
PaperRepoTopicName string

//nginx proxy
PROXYURL string
RadarMap = struct {
@@ -1553,6 +1560,9 @@ func NewContext() {
TrainJobFLAVORINFOS = sec.Key("TrainJob_FLAVOR_INFOS").MustString("")
ModelArtsSpecialPools = sec.Key("SPECIAL_POOL").MustString("")
ModelArtsMultiNode = sec.Key("MULTI_NODE").MustString("")
ModelArtsShareAddr = sec.Key("ModelArts_Share_Addr").MustString("192.168.0.30:/")
ModelArtsMountPath = sec.Key("ModelArts_Mount_Path").MustString("/cache/sfs")
ModelArtsNasType = sec.Key("ModelArts_Nas_Type").MustString("nfs")

sec = Cfg.Section("elk")
ElkUrl = sec.Key("ELKURL").MustString("")
@@ -1585,6 +1595,10 @@ func NewContext() {
CloudbrainStoppedTitle = sec.Key("CLOUDBRAIN_STOPPED_TITLE").MustString("您好,您申请的算力资源已结束使用,任务已完成运行,状态为%s,请您关注运行结果")
CloudbrainStoppedRemark = sec.Key("CLOUDBRAIN_STOPPED_REMARK").MustString("感谢您的耐心等待。")

sec = Cfg.Section("repo-square")
IncubationSourceOrgName = sec.Key("INCUBATION_ORG_NAME").MustString("OpenI")
PaperRepoTopicName = sec.Key("PAPER_REPO_TOPIC_NAME").MustString("openi-paper")

sec = Cfg.Section("point")
CloudBrainPaySwitch = sec.Key("CLOUDBRAIN_PAY_SWITCH").MustBool(false)
CloudBrainPayDelay = sec.Key("CLOUDBRAIN_PAY_DELAY").MustDuration(30 * time.Minute)


+ 23
- 0
modules/structs/pipeline.go View File

@@ -0,0 +1,23 @@
package structs

type Pipeline struct {
ID int64 `json:"id"`
Name string `json:"name"`
Status string `json:"status"`
}
type NodeInfo struct {
Name string `json:"name"`
Status string `json:"status"`
Code string `json:"code"`
Message string `json:"message"`
}

type PipelineNotification struct {
Type int `json:"type"`
Username string `json:"username"`
Reponame string `json:"reponame"`
Pipeline Pipeline `json:"pipeline"`
PipelineRunId string `json:"pipeline_run_id"`
Node NodeInfo `json:"node"`
OccurTime int64 `json:"occur_time"`
}

+ 5
- 0
options/locale/locale_en-US.ini View File

@@ -1307,6 +1307,11 @@ model.manage.select.engine=Select model engine
model.manage.modelfile=Model file
model.manage.modellabel=Model label
model.manage.modeldesc=Model description
model.manage.modelaccess=Model Access
model.manage.modelaccess.public=Public
model.manage.modelaccess.private=Private
model.manage.modelaccess.setpublic=Set Public
model.manage.modelaccess.setprivate=Set Private
model.manage.baseinfo=Base Information
modelconvert.notcreate=No model conversion task has been created.
modelconvert.importfirst1=Please import the


+ 5
- 0
options/locale/locale_zh-CN.ini View File

@@ -1320,6 +1320,11 @@ model.manage.select.engine=选择模型框架
model.manage.modelfile=模型文件
model.manage.modellabel=模型标签
model.manage.modeldesc=模型描述
model.manage.modelaccess=模型权限
model.manage.modelaccess.public=公开
model.manage.modelaccess.private=私有
model.manage.modelaccess.setpublic=设为公开
model.manage.modelaccess.setprivate=设为私有
model.manage.baseinfo=基本信息
modelconvert.notcreate=未创建过模型转换任务
modelconvert.importfirst1=请您先导入


+ 2
- 1
package.json View File

@@ -17,11 +17,12 @@
"core-js": "3.6.5",
"css-loader": "3.5.3",
"cssnano": "4.1.10",
"dayjs": "1.10.7",
"domino": "2.1.5",
"dropzone": "5.7.2",
"echarts": "3.8.5",
"element-ui": "2.15.5",
"esdk-obs-browserjs": "3.20.7",
"esdk-obs-browserjs": "3.22.3",
"esdk-obs-nodejs": "3.20.11",
"fast-glob": "3.2.2",
"file-loader": "6.0.0",


+ 3
- 11
public/home/home.js View File

@@ -629,20 +629,12 @@ function displayRepo(json){
for (var i = 0, iLen = repos.length; i < iLen; i++) {
if (i >= 4) break;
var repo = repos[i];
// <i class="ri-star-line"></i>${repo["NumStars"]}<i class="ri-git-branch-line am-ml-10"></i>${repo["NumForks"]}</span> <div class="ui tags nowrap am-mt-10"></div>
html += `<div class="ui fluid card" style="border-radius:6px;">
<div class="content">
${repo["Avatar"] ? `<img class="left floated mini ui image" src="${repo["Avatar"]}">` : `<img class="left floated mini ui image" avatar="${repo["OwnerName"]}">`}
<div class="content" style="position:relative;">
${repo["Avatar"] ? `<img style="border-radius:100%;" class="left floated mini ui image" src="${repo["Avatar"]}">` : `<img style="border-radius:100%;" class="left floated mini ui image" avatar="${repo["OwnerName"]}">`}
<a class="header nowrap" style="color:rgb(50, 145, 248);font-size:14px;" href="/${repo["OwnerName"]}/${repo["Name"]}" title="${repo["Alias"]}">${repo["Alias"]}</a>
<div class="description nowrap-2" style="rgba(136,136,136,1);;font-size:12px;" title="${repo["Description"]}">${repo["Description"]}</div>
`;
// if (repo["Topics"] != null) {
// for(var j = 0; j < repo["Topics"].length; j++){
// var topic = repo["Topics"][j];
// var url = "/explore/repos?q=" + (topic) + "&amp;topic="
// html += `<a class="ui small label topic" href=" ${url}">${topic}</a>`;
// }
// }
<a href="/${repo["OwnerName"]}/${repo["Name"]}" style="height:100%;width:100%;position:absolute;left:0;top:0"></a>`;
html += `
</div>
</div>`;


+ 34
- 0
routers/admin/resources.go View File

@@ -307,3 +307,37 @@ func RefreshHistorySpec(ctx *context.Context) {
r["total"] = total
ctx.JSON(http.StatusOK, response.SuccessWithData(r))
}

func RefreshReposHistoryCnt(ctx *context.Context) {
scope := ctx.Query("scope")
list := ctx.Query("list")

var scopeAll = false
if scope == "all" {
scopeAll = true
}
var ids = make([]int64, 0)
if list != "" {
strs := strings.Split(list, "|")
for _, s := range strs {
i, err := strconv.ParseInt(s, 10, 64)
if err != nil {
ctx.JSON(http.StatusOK, response.ServerError(err.Error()))
return
}
ids = append(ids, i)
}

}

total, success, err := resource.RefreshHistorySpec(scopeAll, ids)
if err != nil {
log.Error("RefreshHistorySpec error. %v", err)
ctx.JSON(http.StatusOK, response.ServerError(err.Error()))
return
}
r := make(map[string]interface{}, 0)
r["success"] = success
r["total"] = total
ctx.JSON(http.StatusOK, response.SuccessWithData(r))
}

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

@@ -544,6 +544,10 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/complete_multipart", repo.CompleteMultipart)

}, reqToken())
m.Group("/pipeline", func() {
m.Post("/notification", bind(api.PipelineNotification{}), notify.PipelineNotify)

}, reqToken())

// Notifications
m.Group("/notifications", func() {
@@ -610,6 +614,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/query_invitation_yesterday", operationReq, repo_ext.QueryInvitationYesterday)
m.Get("/query_invitation_all", operationReq, repo_ext.QueryInvitationAll)
m.Get("/query_invitation_userdefine", operationReq, repo_ext.QueryUserDefineInvitationPage)
m.Get("/query_user_annual_report", repo_ext.QueryUserAnnualReport)

m.Get("/download_invitation_detail", operationReq, repo_ext.DownloadInvitationDetail)

@@ -758,6 +763,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Group("/:username/:reponame", func() {
m.Get("/right", reqToken(), repo.GetRight)
m.Get("/tagger", reqToken(), repo.ListTagger)
m.Get("/cloudBrainJobId", repo.GetCloudBrainJobId)
m.Combo("").Get(reqAnyRepoReader(), repo.Get).
Delete(reqToken(), reqOwner(), repo.Delete).
Patch(reqToken(), reqAdmin(), bind(api.EditRepoOption{}), context.RepoRef(), repo.Edit)
@@ -994,6 +1000,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/detail", reqToken(), reqRepoReader(models.UnitTypeCloudBrain), repo.CloudBrainShow)
m.Get("/model_list", repo.CloudBrainModelList)
m.Post("/stop_version", cloudbrain.AdminOrOwnerOrJobCreaterRightForTrain, repo_ext.CloudBrainStop)
m.Put("/stop", cloudbrain.AdminOrOwnerOrJobCreaterRightForTrain, repo.GeneralCloudBrainJobStop)
})
})
m.Group("/inference-job", func() {
@@ -1014,12 +1021,15 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Delete("/delete_model", repo.DeleteModel)
m.Get("/downloadall", repo.DownloadModel)
m.Get("/query_model_byId", repo.QueryModelById)
m.Get("/query_model_byName", repo.QueryModelByName)
m.Get("/query_model_for_predict", repo.QueryModelListForPredict)
m.Get("/query_modelfile_for_predict", repo.QueryModelFileForPredict)
m.Get("/query_train_model", repo.QueryTrainModelList)
m.Post("/create_model_convert", repo.CreateModelConvert)
m.Post("/convert_stop", repo.StopModelConvert)
m.Get("/show_model_convert_page", repo.ShowModelConvertPage)
m.Get("/query_model_convert_byId", repo.QueryModelConvertById)
m.Get("/query_model_convert_byName", repo.QueryModelConvertByName)

m.Get("/:id", repo.GetCloudbrainModelConvertTask)
m.Get("/:id/log", repo.CloudbrainForModelConvertGetLog)


+ 15
- 0
routers/api/v1/notify/pipeline.go View File

@@ -0,0 +1,15 @@
package notify

import (
"net/http"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
)

func PipelineNotify(ctx *context.APIContext, form api.PipelineNotification) {

ctx.JSON(http.StatusOK, models.BaseOKMessageApi)

}

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

@@ -17,6 +17,8 @@ import (
"strings"
"time"

"code.gitea.io/gitea/modules/grampus"

cloudbrainService "code.gitea.io/gitea/services/cloudbrain"

"code.gitea.io/gitea/modules/convert"
@@ -80,6 +82,30 @@ func CloudBrainShow(ctx *context.APIContext) {
ctx.JSON(http.StatusOK, models.BaseMessageWithDataApi{Code: 0, Message: "", Data: convert.ToCloudBrain(task)})

}
func GeneralCloudBrainJobStop(ctx *context.APIContext) {
task := ctx.Cloudbrain
if task.IsTerminal() {
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi("cloudbrain.Already_stopped"))
return
}
var err error

if ctx.Cloudbrain.Type == models.TypeCloudBrainOne {
err = cloudbrain.StopJob(task.JobID)
} else if ctx.Cloudbrain.Type == models.TypeCloudBrainTwo {
_, err = modelarts.StopTrainJob(task.JobID, strconv.FormatInt(task.VersionID, 10))
} else {
_, err = grampus.StopJob(task.JobID)
}

if err != nil {
log.Warn("cloud brain stopped failed.", err)
ctx.JSON(http.StatusOK, models.BaseErrorMessageApi("cloudbrain.Stopped_failed"))
return
}

ctx.JSON(http.StatusOK, models.BaseOKMessageApi)
}
func CreateFileNoteBook(ctx *context.APIContext, option api.CreateFileNotebookJobOption) {
cloudbrainTask.FileNotebookCreate(ctx.Context, option)
}


+ 15
- 0
routers/api/v1/repo/cloudbrain_dashboard.go View File

@@ -968,6 +968,8 @@ func GetWaittingTop(ctx *context.Context) {
taskDetail.RepoID = ciTasks[i].RepoID
if ciTasks[i].Repo != nil {
taskDetail.RepoName = ciTasks[i].Repo.OwnerName + "/" + ciTasks[i].Repo.Name
} else {
taskDetail.RepoName = ""
}
WaitTimeInt := time.Now().Unix() - ciTasks[i].Cloudbrain.CreatedUnix.AsTime().Unix()
taskDetail.WaitTime = models.ConvertDurationToStr(WaitTimeInt)
@@ -975,6 +977,13 @@ func GetWaittingTop(ctx *context.Context) {
if WaitTimeInt < 0 {
taskDetail.WaitTime = "00:00:00"
}

taskDetail.ID = ciTasks[i].Cloudbrain.ID
taskDetail.ComputeResource = ciTasks[i].Cloudbrain.ComputeResource
taskDetail.JobType = ciTasks[i].Cloudbrain.JobType
taskDetail.JobID = ciTasks[i].Cloudbrain.JobID
taskDetail.Type = ciTasks[i].Cloudbrain.Type

tasks = append(tasks, taskDetail)
}
ctx.JSON(http.StatusOK, map[string]interface{}{
@@ -1001,6 +1010,12 @@ func GetRunningTop(ctx *context.Context) {
taskDetail.RepoName = ciTasks[i].Repo.OwnerName + "/" + ciTasks[i].Repo.Name
}

taskDetail.ID = ciTasks[i].Cloudbrain.ID
taskDetail.ComputeResource = ciTasks[i].Cloudbrain.ComputeResource
taskDetail.JobType = ciTasks[i].Cloudbrain.JobType
taskDetail.JobID = ciTasks[i].Cloudbrain.JobID
taskDetail.Type = ciTasks[i].Cloudbrain.Type

tasks = append(tasks, taskDetail)
}
ctx.JSON(http.StatusOK, map[string]interface{}{


+ 14
- 0
routers/api/v1/repo/mlops.go View File

@@ -69,3 +69,17 @@ func GetRight(ctx *context.APIContext) {
})

}

func GetCloudBrainJobId(ctx *context.APIContext) {
cloudbrains, err := models.GetCloudbrainsByDisplayJobName(ctx.Repo.Repository.ID, ctx.Query("jobType"), ctx.Query("name"))
if err != nil {
log.Warn("get cloudbrain by display name failed", err)
ctx.JSON(http.StatusOK, map[string]string{"jobId": ""})
return
}
if len(cloudbrains) > 0 {
ctx.JSON(http.StatusOK, map[string]string{"jobId": cloudbrains[0].JobID})
return
}
ctx.JSON(http.StatusOK, map[string]string{"jobId": ""})
}

+ 20
- 0
routers/api/v1/repo/modelmanage.go View File

@@ -43,8 +43,14 @@ func QueryModelById(ctx *context.APIContext) {
routerRepo.QueryModelById(ctx.Context)
}

func QueryModelByName(ctx *context.APIContext) {
log.Info("QueryModelByName by api.")
routerRepo.ShowSingleModel(ctx.Context)
}

func QueryModelListForPredict(ctx *context.APIContext) {
log.Info("QueryModelListForPredict by api.")
ctx.Context.SetParams("isOnlyThisRepo", "true")
routerRepo.QueryModelListForPredict(ctx.Context)
}

@@ -88,6 +94,11 @@ func CreateModelConvert(ctx *context.APIContext) {
routerRepo.SaveModelConvert(ctx.Context)
}

func StopModelConvert(ctx *context.APIContext) {
log.Info("StopModelConvert by api.")
routerRepo.StopModelConvertApi(ctx.Context)
}

func ShowModelConvertPage(ctx *context.APIContext) {
log.Info("ShowModelConvertPage by api.")
modelResult, count, err := routerRepo.GetModelConvertPageData(ctx.Context)
@@ -113,3 +124,12 @@ func QueryModelConvertById(ctx *context.APIContext) {
ctx.JSON(http.StatusOK, nil)
}
}

func QueryModelConvertByName(ctx *context.APIContext) {
modelResult, err := routerRepo.GetModelConvertByName(ctx.Context)
if err == nil {
ctx.JSON(http.StatusOK, modelResult)
} else {
ctx.JSON(http.StatusOK, nil)
}
}

+ 14
- 2
routers/api/v1/repo/topic.go View File

@@ -177,13 +177,25 @@ func AddTopic(ctx *context.APIContext) {
return
}

_, err = models.AddTopic(ctx.Repo.Repository.ID, topicName)
topic, err := models.AddTopic(ctx.Repo.Repository.ID, topicName)
if err != nil {
log.Error("AddTopic failed: %v", err)
ctx.InternalServerError(err)
return
}

found := false
topicNames := make([]string, len(topics))
for i, t := range topics {
topicNames[i] = t.Name
if strings.EqualFold(topic.Name, t.Name) {
found = true
break
}
}
if !found && topic.Name != "" {
topicNames = append(topicNames, topic.Name)
}
models.UpdateRepoTopics(ctx.Repo.Repository.ID, topicNames)
ctx.Status(http.StatusNoContent)
}



+ 106
- 0
routers/home.go View File

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

import (
"bytes"
"code.gitea.io/gitea/routers/response"
"encoding/json"
"net/http"
"strconv"
@@ -43,6 +44,8 @@ const (
tplHomeTerm base.TplName = "terms"
tplHomePrivacy base.TplName = "privacy"
tplResoruceDesc base.TplName = "resource_desc"
tplRepoSquare base.TplName = "explore/repos/square"
tplRepoSearch base.TplName = "explore/repos/search"
)

// Home render home page
@@ -296,6 +299,109 @@ func ExploreRepos(ctx *context.Context) {
})
}

func GetRepoSquarePage(ctx *context.Context) {
ctx.Data["SquareBanners"] = repository.GetBanners()
ctx.Data["SquareTopics"] = repository.GetTopics()
ctx.Data["SquareRecommendRepos"] = repository.GetRecommendRepos()

repos, _ := repository.GetPreferredRepos()
ctx.Data["SquarePreferredRepos"] = repos
ctx.HTML(200, tplRepoSquare)
}
func GetRepoSearchPage(ctx *context.Context) {
ctx.Data["SquareTopics"] = repository.GetTopics()
ctx.HTML(200, tplRepoSearch)
}

func RepoSquare(ctx *context.Context) {
var result []*models.Repository4Card
var err error
switch ctx.Query("type") {
case "preferred":
result, err = repository.GetPreferredRepos()
case "incubation":
result, err = repository.GetIncubationRepos()
case "hot-paper":
result, err = repository.GetHotPaperRepos()
default:
result, err = repository.GetPreferredRepos()
}
if err != nil {
ctx.JSON(http.StatusOK, response.ResponseError(err))
return
}
resultMap := make(map[string]interface{}, 0)
resultMap["Repos"] = result
ctx.JSON(http.StatusOK, response.SuccessWithData(resultMap))
}

func ActiveUser(ctx *context.Context) {
var err error
var currentUserId int64
if ctx.User != nil {
currentUserId = ctx.User.ID
}
result, err := repository.GetActiveUser4Square(currentUserId)
if err != nil {
log.Error("ActiveUser err. %v", err)
ctx.JSON(http.StatusOK, response.Success())
return
}
resultMap := make(map[string]interface{}, 0)
resultMap["Users"] = result
ctx.JSON(http.StatusOK, response.SuccessWithData(resultMap))
}
func ActiveOrg(ctx *context.Context) {
result, err := repository.GetActiveOrgs()
if err != nil {
log.Error("ActiveOrg err. %v", err)
ctx.JSON(http.StatusOK, response.Success())
return
}
resultMap := make(map[string]interface{}, 0)
resultMap["Orgs"] = result
ctx.JSON(http.StatusOK, response.SuccessWithData(resultMap))
}

func RepoFind(ctx *context.Context) {
keyword := strings.Trim(ctx.Query("q"), " ")
topic := strings.Trim(ctx.Query("topic"), " ")
sort := strings.Trim(ctx.Query("sort"), " ")
page := ctx.QueryInt("page")
pageSize := ctx.QueryInt("pageSize")
if pageSize == 0 {
pageSize = 15
}
if pageSize > 100 {
ctx.JSON(http.StatusOK, response.ServerError("pageSize illegal"))
return
}
if page <= 0 {
page = 1
}

var ownerID int64
if ctx.User != nil && !ctx.User.IsAdmin {
ownerID = ctx.User.ID
}

result, err := repository.FindRepos(repository.FindReposOptions{
ListOptions: models.ListOptions{Page: page, PageSize: pageSize},
Actor: ctx.User,
Sort: sort,
Keyword: keyword,
Topic: topic,
Private: ctx.User != nil,
OwnerID: ownerID,
})
if err != nil {
log.Error("RepoFind error. %v", err)
ctx.JSON(http.StatusOK, response.ResponseError(err))
return
}
ctx.JSON(http.StatusOK, response.SuccessWithData(result))
}

func ExploreDatasets(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("explore")
ctx.Data["PageIsExplore"] = true


+ 3
- 0
routers/private/internal.go View File

@@ -6,6 +6,7 @@
package private

import (
"code.gitea.io/gitea/services/repository"
"strings"

"code.gitea.io/gitea/routers/admin"
@@ -55,7 +56,9 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/task/history_handle/duration", repo.HandleTaskWithNoDuration)
m.Post("/task/history_handle/aicenter", repo.HandleTaskWithAiCenter)
m.Post("/resources/specification/handle_historical_task", admin.RefreshHistorySpec)
m.Post("/repos/cnt_stat/handle_historical_task", admin.RefreshHistorySpec)
m.Post("/duration_statisctic/history_handle", repo.CloudbrainUpdateHistoryData)
m.Post("/square/repo/stat/refresh", repository.RefreshRepoStatData)

}, CheckInternalToken)
}

+ 38
- 7
routers/repo/ai_model_convert.go View File

@@ -573,13 +573,10 @@ func deleteCloudBrainTask(task *models.AiModelConvert) {
}
}

func StopModelConvert(ctx *context.Context) {
id := ctx.Params(":id")
log.Info("stop model convert start.id=" + id)
func stopModelConvert(id string) error {
job, err := models.QueryModelConvertById(id)
if err != nil {
ctx.ServerError("Not found task.", err)
return
return err
}
if job.IsGpuTrainTask() {
err = cloudbrain.StopJob(job.CloudBrainTaskId)
@@ -600,6 +597,35 @@ func StopModelConvert(ctx *context.Context) {
err = models.UpdateModelConvert(job)
if err != nil {
log.Error("UpdateModelConvert failed:", err)
return err
}
return nil
}

func StopModelConvertApi(ctx *context.Context) {
id := ctx.Query("id")
log.Info("stop model convert start.id=" + id)
err := stopModelConvert(id)
if err == nil {
ctx.JSON(200, map[string]string{
"code": "0",
"msg": "succeed",
})
} else {
ctx.JSON(200, map[string]string{
"code": "1",
"msg": err.Error(),
})
}
}

func StopModelConvert(ctx *context.Context) {
id := ctx.Params(":id")
log.Info("stop model convert start.id=" + id)
err := stopModelConvert(id)
if err != nil {
ctx.ServerError("Not found task.", err)
return
}
ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelmanage/convert_model")
}
@@ -620,7 +646,7 @@ func ShowModelConvertInfo(ctx *context.Context) {
return
}
ctx.Data["Name"] = job.Name
ctx.Data["canDownload"] = isOper(ctx, job.UserId)
ctx.Data["canDownload"] = isOperModifyOrDelete(ctx, job.UserId)
user, err := models.GetUserByID(job.UserId)
if err == nil {
job.UserName = user.Name
@@ -732,6 +758,11 @@ func GetModelConvertById(ctx *context.Context) (*models.AiModelConvert, error) {
return models.QueryModelConvertById(id)
}

func GetModelConvertByName(ctx *context.Context) ([]*models.AiModelConvert, error) {
name := ctx.Query("name")
return models.QueryModelConvertByName(name, ctx.Repo.Repository.ID)
}

func GetModelConvertPageData(ctx *context.Context) ([]*models.AiModelConvert, int64, error) {
page := ctx.QueryInt("page")
if page <= 0 {
@@ -755,7 +786,7 @@ func GetModelConvertPageData(ctx *context.Context) ([]*models.AiModelConvert, in
}
userIds := make([]int64, len(modelResult))
for i, model := range modelResult {
model.IsCanOper = isOper(ctx, model.UserId)
model.IsCanOper = isOperModifyOrDelete(ctx, model.UserId)
model.IsCanDelete = isCanDelete(ctx, model.UserId)
userIds[i] = model.UserId
}


+ 170
- 46
routers/repo/ai_model_manage.go View File

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

import (
"archive/zip"
"code.gitea.io/gitea/services/repository"
"encoding/json"
"errors"
"fmt"
@@ -93,7 +94,7 @@ func saveModelByParameters(jobId string, versionName string, name string, versio
log.Info("accuracyJson=" + string(accuracyJson))
aiTask.ContainerIp = ""
aiTaskJson, _ := json.Marshal(aiTask)
isPrivate := ctx.QueryBool("isPrivate")
model := &models.AiModelManage{
ID: id,
Version: version,
@@ -114,6 +115,7 @@ func saveModelByParameters(jobId string, versionName string, name string, versio
TrainTaskInfo: string(aiTaskJson),
Accuracy: string(accuracyJson),
Status: STATUS_COPY_MODEL,
IsPrivate: isPrivate,
}

err = models.SaveModelToDb(model)
@@ -169,10 +171,17 @@ func updateStatus(id string, modelSize int64, status int, modelPath string, stat
if len(statusDesc) > 400 {
statusDesc = statusDesc[0:400]
}
m, _ := models.QueryModelById(id)
err := models.ModifyModelStatus(id, modelSize, status, modelPath, statusDesc)
if err != nil {
log.Info("update status error." + err.Error())
}
if m != nil {
if modelSize > 0 && m.Size == 0 {
go repository.ResetRepoModelNum(m.RepoId)
}
}

}

func SaveNewNameModel(ctx *context.Context) {
@@ -216,6 +225,7 @@ func SaveLocalModel(ctx *context.Context) {
description := ctx.Query("description")
engine := ctx.QueryInt("engine")
taskType := ctx.QueryInt("type")
isPrivate := ctx.QueryBool("isPrivate")
modelActualPath := ""
if taskType == models.TypeCloudBrainOne {
destKeyNamePrefix := Model_prefix + models.AttachmentRelativePath(id) + "/"
@@ -262,6 +272,7 @@ func SaveLocalModel(ctx *context.Context) {
TrainTaskInfo: "",
Accuracy: "",
Status: STATUS_FINISHED,
IsPrivate: isPrivate,
}

err := models.SaveModelToDb(model)
@@ -305,13 +316,14 @@ func getSize(files []storage.FileInfo) int64 {
func UpdateModelSize(modeluuid string) {
model, err := models.QueryModelById(modeluuid)
if err == nil {
var size int64
if model.Type == models.TypeCloudBrainOne {
if strings.HasPrefix(model.Path, setting.Attachment.Minio.Bucket+"/"+Model_prefix) {
files, err := storage.GetAllObjectByBucketAndPrefixMinio(setting.Attachment.Minio.Bucket, model.Path[len(setting.Attachment.Minio.Bucket)+1:])
if err != nil {
log.Info("Failed to query model size from minio. id=" + modeluuid)
}
size := getSize(files)
size = getSize(files)
models.ModifyModelSize(modeluuid, size)
}
} else if model.Type == models.TypeCloudBrainTwo {
@@ -320,10 +332,13 @@ func UpdateModelSize(modeluuid string) {
if err != nil {
log.Info("Failed to query model size from obs. id=" + modeluuid)
}
size := getSize(files)
size = getSize(files)
models.ModifyModelSize(modeluuid, size)
}
}
if model.Size == 0 && size > 0 {
go repository.ResetRepoModelNum(model.RepoId)
}
} else {
log.Info("not found model,uuid=" + modeluuid)
}
@@ -438,13 +453,14 @@ func DeleteModelFile(ctx *context.Context) {
fileName := ctx.Query("fileName")
model, err := models.QueryModelById(id)
if err == nil {
var totalSize int64
if model.ModelType == MODEL_LOCAL_TYPE {
if model.Type == models.TypeCloudBrainOne {
bucketName := setting.Attachment.Minio.Bucket
objectName := model.Path[len(bucketName)+1:] + fileName
log.Info("delete bucket=" + bucketName + " path=" + objectName)
if strings.HasPrefix(model.Path, bucketName+"/"+Model_prefix) {
totalSize := storage.MinioGetFilesSize(bucketName, []string{objectName})
totalSize = storage.MinioGetFilesSize(bucketName, []string{objectName})
err := storage.Attachments.DeleteDir(objectName)
if err != nil {
log.Info("Failed to delete model. id=" + id)
@@ -464,7 +480,7 @@ func DeleteModelFile(ctx *context.Context) {
objectName := model.Path[len(setting.Bucket)+1:] + fileName
log.Info("delete bucket=" + setting.Bucket + " path=" + objectName)
if strings.HasPrefix(model.Path, bucketName+"/"+Model_prefix) {
totalSize := storage.ObsGetFilesSize(bucketName, []string{objectName})
totalSize = storage.ObsGetFilesSize(bucketName, []string{objectName})
err := storage.ObsRemoveObject(bucketName, objectName)
if err != nil {
log.Info("Failed to delete model. id=" + id)
@@ -481,6 +497,9 @@ func DeleteModelFile(ctx *context.Context) {
}
}
}
if (model.Size - totalSize) <= 0 {
go repository.ResetRepoModelNum(model.RepoId)
}
}
ctx.JSON(200, map[string]string{
"code": "0",
@@ -549,25 +568,14 @@ func deleteModelByID(ctx *context.Context, id string) error {
}
}
}
if model.Size > 0 {
go repository.ResetRepoModelNum(model.RepoId)
}
}
}
return err
}

func QueryModelByParameters(repoId int64, page int) ([]*models.AiModelManage, int64, error) {

return models.QueryModel(&models.AiModelQueryOptions{
ListOptions: models.ListOptions{
Page: page,
PageSize: setting.UI.IssuePagingNum,
},
RepoID: repoId,
Type: -1,
New: MODEL_LATEST,
Status: -1,
})
}

func DownloadMultiModelFile(ctx *context.Context) {
log.Info("DownloadMultiModelFile start.")
id := ctx.Query("id")
@@ -578,7 +586,7 @@ func DownloadMultiModelFile(ctx *context.Context) {
ctx.ServerError("no such model:", err)
return
}
if !isOper(ctx, task.UserId) {
if !isCanDownload(ctx, task) {
ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
return
}
@@ -806,7 +814,7 @@ func DownloadSingleModelFile(ctx *context.Context) {
ctx.ServerError("no such model:", err)
return
}
if !isOper(ctx, task.UserId) {
if !isCanDownload(ctx, task) {
ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
return
}
@@ -874,8 +882,9 @@ func QueryModelById(ctx *context.Context) {
id := ctx.Query("id")
model, err := models.QueryModelById(id)
if err == nil {
model.IsCanOper = isOper(ctx, model.UserId)
model.IsCanOper = isOperModifyOrDelete(ctx, model.UserId)
model.IsCanDelete = isCanDelete(ctx, model.UserId)
model.IsCanDownload = isCanDownload(ctx, model)
removeIpInfo(model)
ctx.JSON(http.StatusOK, model)
} else {
@@ -891,7 +900,8 @@ func ShowSingleModel(ctx *context.Context) {

userIds := make([]int64, len(models))
for i, model := range models {
model.IsCanOper = isOper(ctx, model.UserId)
model.IsCanOper = isOperModifyOrDelete(ctx, model.UserId)
model.IsCanDownload = isCanDownload(ctx, model)
model.IsCanDelete = isCanDelete(ctx, model.UserId)
userIds[i] = model.UserId
}
@@ -941,7 +951,8 @@ func ShowOneVersionOtherModel(ctx *context.Context) {

userIds := make([]int64, len(aimodels))
for i, model := range aimodels {
model.IsCanOper = isOper(ctx, model.UserId)
model.IsCanOper = isOperModifyOrDelete(ctx, model.UserId)
model.IsCanDownload = isCanDownload(ctx, model)
model.IsCanDelete = isCanDelete(ctx, model.UserId)
userIds[i] = model.UserId
}
@@ -964,6 +975,7 @@ func ShowOneVersionOtherModel(ctx *context.Context) {
}

func SetModelCount(ctx *context.Context) {
isQueryPrivate := isQueryPrivateModel(ctx)
repoId := ctx.Repo.Repository.ID
Type := -1
_, count, _ := models.QueryModel(&models.AiModelQueryOptions{
@@ -971,10 +983,12 @@ func SetModelCount(ctx *context.Context) {
Page: 1,
PageSize: 2,
},
RepoID: repoId,
Type: Type,
New: MODEL_LATEST,
Status: -1,
RepoID: repoId,
Type: Type,
New: MODEL_LATEST,
IsOnlyThisRepo: true,
Status: -1,
IsQueryPrivate: isQueryPrivate,
})
ctx.Data["MODEL_COUNT"] = count
}
@@ -1001,27 +1015,87 @@ func isQueryRight(ctx *context.Context) bool {
}
}

func isCanDownload(ctx *context.Context, task *models.AiModelManage) bool {
if ctx.User == nil {
return false
}
isCollaborator, err := ctx.Repo.Repository.IsCollaborator(ctx.User.ID)
if err != nil {
log.Info("query error.")
}
isTeamMember, err := ctx.Repo.Repository.IsInRepoTeam(ctx.User.ID)
if err != nil {
log.Info("query IsInRepoTeam error." + err.Error())
}
if ctx.User.IsAdmin || ctx.User.ID == task.UserId || isCollaborator || isTeamMember {
return true
}
if ctx.Repo.IsOwner() {
return true
}
if !task.IsPrivate {
return true
}
return false
}

func isQueryPrivateModel(ctx *context.Context) bool {
if ctx.User == nil {
return false
}
isCollaborator, err := ctx.Repo.Repository.IsCollaborator(ctx.User.ID)
if err != nil {
log.Info("query IsCollaborator error." + err.Error())
}
isTeamMember, err := ctx.Repo.Repository.IsInRepoTeam(ctx.User.ID)
if err != nil {
log.Info("query IsInRepoTeam error." + err.Error())
}
if ctx.User.IsAdmin || isCollaborator || isTeamMember {
return true
}
if ctx.Repo.IsOwner() {
return true
}
return false
}

func isCanDelete(ctx *context.Context, modelUserId int64) bool {
if ctx.User == nil {
return false
}
if ctx.User.IsAdmin || ctx.User.ID == modelUserId {
if ctx.User.ID == modelUserId {
return true
}
return isAdminRight(ctx)
}

func isAdminRight(ctx *context.Context) bool {
if ctx.User.IsAdmin {
return true
}
if ctx.Repo.IsOwner() {
return true
}
permission, err := models.GetUserRepoPermission(ctx.Repo.Repository, ctx.User)
if err != nil {
log.Error("GetUserRepoPermission failed:%v", err.Error())
return false
}
if permission.AccessMode >= models.AccessModeAdmin {
return true
}
return false
}

func isOper(ctx *context.Context, modelUserId int64) bool {
func isOperModifyOrDelete(ctx *context.Context, modelUserId int64) bool {
if ctx.User == nil {
return false
}
if ctx.User.IsAdmin || ctx.User.ID == modelUserId {
return true
}
return false
return isAdminRight(ctx)
}

func ShowModelPageInfo(ctx *context.Context) {
@@ -1038,6 +1112,7 @@ func ShowModelPageInfo(ctx *context.Context) {
if pageSize <= 0 {
pageSize = setting.UI.IssuePagingNum
}
isQueryPrivate := isQueryPrivateModel(ctx)
repoId := ctx.Repo.Repository.ID
Type := -1
modelResult, count, err := models.QueryModel(&models.AiModelQueryOptions{
@@ -1045,10 +1120,12 @@ func ShowModelPageInfo(ctx *context.Context) {
Page: page,
PageSize: pageSize,
},
RepoID: repoId,
Type: Type,
New: MODEL_LATEST,
Status: -1,
RepoID: repoId,
Type: Type,
New: MODEL_LATEST,
IsOnlyThisRepo: true,
Status: -1,
IsQueryPrivate: isQueryPrivate,
})
if err != nil {
ctx.ServerError("Cloudbrain", err)
@@ -1057,8 +1134,9 @@ func ShowModelPageInfo(ctx *context.Context) {

userIds := make([]int64, len(modelResult))
for i, model := range modelResult {
model.IsCanOper = isOper(ctx, model.UserId)
model.IsCanOper = isOperModifyOrDelete(ctx, model.UserId)
model.IsCanDelete = isCanDelete(ctx, model.UserId)
model.IsCanDownload = isCanDownload(ctx, model)
userIds[i] = model.UserId
}

@@ -1089,6 +1167,37 @@ func ModifyModel(id string, description string) error {
return err
}

func ModifyModelPrivate(ctx *context.Context) {
id := ctx.Query("id")
isPrivate := ctx.QueryBool("isPrivate")
re := map[string]string{
"code": "-1",
}
task, err := models.QueryModelById(id)
if err != nil || task == nil {
re["msg"] = err.Error()
log.Error("no such model!", err.Error())
ctx.JSON(200, re)
return
}
if !isOperModifyOrDelete(ctx, task.UserId) {
re["msg"] = "No right to operation."
ctx.JSON(200, re)
return
}
err = models.ModifyModelPrivate(id, isPrivate)
if err == nil {
re["code"] = "0"
ctx.JSON(200, re)
log.Info("modify success.")
} else {
re["msg"] = err.Error()
ctx.JSON(200, re)
log.Info("Failed to modify.id=" + id + " isprivate=" + fmt.Sprint(isPrivate) + " error:" + err.Error())
}

}

func ModifyModelInfo(ctx *context.Context) {
log.Info("modify model start.")
id := ctx.Query("id")
@@ -1102,7 +1211,7 @@ func ModifyModelInfo(ctx *context.Context) {
ctx.JSON(200, re)
return
}
if !isOper(ctx, task.UserId) {
if !isOperModifyOrDelete(ctx, task.UserId) {
re["msg"] = "No right to operation."
ctx.JSON(200, re)
return
@@ -1112,6 +1221,7 @@ func ModifyModelInfo(ctx *context.Context) {
label := ctx.Query("label")
description := ctx.Query("description")
engine := ctx.QueryInt("engine")
isPrivate := ctx.QueryBool("isPrivate")
aimodels := models.QueryModelByName(name, task.RepoId)
if aimodels != nil && len(aimodels) > 0 {
if len(aimodels) == 1 {
@@ -1126,14 +1236,14 @@ func ModifyModelInfo(ctx *context.Context) {
return
}
}
err = models.ModifyLocalModel(id, name, label, description, engine)
err = models.ModifyLocalModel(id, name, label, description, engine, isPrivate)

} else {
label := ctx.Query("label")
description := ctx.Query("description")
engine := task.Engine
name := task.Name
err = models.ModifyLocalModel(id, name, label, description, int(engine))
err = models.ModifyLocalModel(id, name, label, description, int(engine), task.IsPrivate)
}

if err != nil {
@@ -1148,15 +1258,27 @@ func ModifyModelInfo(ctx *context.Context) {

func QueryModelListForPredict(ctx *context.Context) {
repoId := ctx.Repo.Repository.ID
page := ctx.QueryInt("page")
if page <= 0 {
page = -1
}
pageSize := ctx.QueryInt("pageSize")
if pageSize <= 0 {
pageSize = -1
}
isQueryPrivate := isQueryPrivateModel(ctx)
//IsOnlyThisRepo := ctx.QueryBool("isOnlyThisRepo")
modelResult, count, err := models.QueryModel(&models.AiModelQueryOptions{
ListOptions: models.ListOptions{
Page: -1,
PageSize: -1,
Page: page,
PageSize: pageSize,
},
RepoID: repoId,
Type: ctx.QueryInt("type"),
New: -1,
Status: 0,
RepoID: repoId,
Type: ctx.QueryInt("type"),
New: -1,
Status: 0,
IsOnlyThisRepo: true,
IsQueryPrivate: isQueryPrivate,
})
if err != nil {
ctx.ServerError("Cloudbrain", err)
@@ -1168,7 +1290,9 @@ func QueryModelListForPredict(ctx *context.Context) {

nameMap := make(map[string][]*models.AiModelManage)
for _, model := range modelResult {
removeIpInfo(model)
model.TrainTaskInfo = ""
model.Accuracy = ""
//removeIpInfo(model)
if _, value := nameMap[model.Name]; !value {
models := make([]*models.AiModelManage, 0)
models = append(models, model)


+ 4
- 1
routers/repo/aisafety.go View File

@@ -847,6 +847,9 @@ func createForGPU(ctx *context.Context, jobName string) error {
codePath := setting.JobPath + jobName + cloudbrain.CodeMountPath
os.RemoveAll(codePath)

gitRepo, _ := git.OpenRepository(repo.RepoPath())
commitID, _ := gitRepo.GetBranchCommitID(cloudbrain.DefaultBranchName)

if err := downloadCode(repo, codePath, cloudbrain.DefaultBranchName); err != nil {
log.Error("downloadCode failed, %v", err, ctx.Data["MsgID"])
return errors.New("system error")
@@ -891,7 +894,7 @@ func createForGPU(ctx *context.Context, jobName string) error {
BranchName: cloudbrain.DefaultBranchName,
BootFile: BootFile,
Params: Params,
CommitID: "",
CommitID: commitID,
ModelName: modelName,
ModelVersion: modelVersion,
CkptName: CkptName,


+ 3
- 0
routers/repo/attachment.go View File

@@ -29,6 +29,7 @@ import (
"code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/modules/upload"
"code.gitea.io/gitea/modules/worker"
repo_service "code.gitea.io/gitea/services/repository"
gouuid "github.com/satori/go.uuid"
)

@@ -180,6 +181,7 @@ func DeleteAttachment(ctx *context.Context) {
ctx.Error(500, fmt.Sprintf("DeleteAttachment: %v", err))
return
}
go repo_service.DecreaseRepoDatasetNum(attach.DatasetID)

attachjson, _ := json.Marshal(attach)
labelmsg.SendDeleteAttachToLabelSys(string(attachjson))
@@ -894,6 +896,7 @@ func CompleteMultipart(ctx *context.Context) {
return
}
attachment.UpdateDatasetUpdateUnix()
go repo_service.IncreaseRepoDatasetNum(dataset.ID)
repository, _ := models.GetRepositoryByID(dataset.RepoID)
notification.NotifyOtherTask(ctx.User, repository, fmt.Sprint(repository.IsPrivate, attachment.IsPrivate), attachment.Name, models.ActionUploadAttachment)
if attachment.DatasetID != 0 {


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

@@ -14,7 +14,13 @@ import (
)

func CloudbrainDurationStatisticHour() {
if setting.IsCloudbrainTimingEnabled {
defer func() {
err := recover()
if err == nil {
return
}
}()
if setting.IsCloudbrainTimingEnabled {
var statisticTime time.Time
var count int64
recordDurationUpdateTime, err := models.GetDurationRecordUpdateTime()


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

@@ -2408,7 +2408,7 @@ func InferenceJobIndex(ctx *context.Context) {
tasks[i].ComputeResource = models.NPUResource
}
}
isQueryPrivate := isQueryPrivateModel(ctx)
repoId := ctx.Repo.Repository.ID
Type := -1
_, model_count, _ := models.QueryModel(&models.AiModelQueryOptions{
@@ -2416,10 +2416,12 @@ func InferenceJobIndex(ctx *context.Context) {
Page: 1,
PageSize: 2,
},
RepoID: repoId,
Type: Type,
New: MODEL_LATEST,
Status: 0,
RepoID: repoId,
Type: Type,
New: MODEL_LATEST,
IsOnlyThisRepo: true,
Status: 0,
IsQueryPrivate: isQueryPrivate,
})
ctx.Data["MODEL_COUNT"] = model_count

@@ -2488,7 +2490,7 @@ func inferenceJobNewDataPrepare(ctx *context.Context) error {
return err
}
ctx.Data["config_list"] = configList.ParaConfigs
isQueryPrivate := isQueryPrivateModel(ctx)
repoId := ctx.Repo.Repository.ID
Type := -1
_, model_count, _ := models.QueryModel(&models.AiModelQueryOptions{
@@ -2496,10 +2498,12 @@ func inferenceJobNewDataPrepare(ctx *context.Context) error {
Page: 1,
PageSize: 2,
},
RepoID: repoId,
Type: Type,
New: MODEL_LATEST,
Status: 0,
RepoID: repoId,
Type: Type,
New: MODEL_LATEST,
IsOnlyThisRepo: true,
Status: 0,
IsQueryPrivate: isQueryPrivate,
})
ctx.Data["MODEL_COUNT"] = model_count
ctx.Data["datasetType"] = models.TypeCloudBrainTwo


+ 2
- 0
routers/repo/repo_statistic.go View File

@@ -166,6 +166,8 @@ func RepoStatisticDaily(date string) {
repoStat.NumIssuesGrowth = repoStat.NumIssues - repoStatisticFourMonthsAgo.NumIssues
}

models.SyncStatDataToRepo(repo)

if _, err = models.InsertRepoStat(&repoStat); err != nil {
log.Error("InsertRepoStat failed(%s): %v", projectName, err)
log.Error("failed statistic: %s", projectName)


+ 18
- 0
routers/repo/user_data_analysis.go View File

@@ -21,6 +21,7 @@ import (
const (
PAGE_SIZE = 2000
Excel_File_Path = "/useranalysis/"
USER_YEAR = 2022
)

func getUserMetricsExcelHeader(ctx *context.Context) map[string]string {
@@ -104,6 +105,7 @@ func getExcelHeader(ctx *context.Context) map[string]string {
excelHeader = append(excelHeader, ctx.Tr("user.static.CloudBrainRunTime"))
excelHeader = append(excelHeader, ctx.Tr("user.static.CommitDatasetNum"))
excelHeader = append(excelHeader, ctx.Tr("user.static.CommitModelCount"))
excelHeader = append(excelHeader, ctx.Tr("user.static.ModelConvertCount"))

excelHeader = append(excelHeader, ctx.Tr("user.static.FocusOtherUser"))
excelHeader = append(excelHeader, ctx.Tr("user.static.CollectDataset"))
@@ -178,6 +180,8 @@ func writeExcel(row int, xlsx *excelize.File, sheetName string, userRecord *mode
tmp = tmp + 1
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.CommitModelCount)
tmp = tmp + 1
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.ModelConvertCount)
tmp = tmp + 1
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.FocusOtherUser)
tmp = tmp + 1
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.CollectDataset)
@@ -256,6 +260,8 @@ func writeExcelPage(row int, xlsx *excelize.File, sheetName string, userRecord *
tmp = tmp + 1
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.CommitModelCount)
tmp = tmp + 1
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.ModelConvertCount)
tmp = tmp + 1
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.FocusOtherUser)
tmp = tmp + 1
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.CollectDataset)
@@ -714,6 +720,12 @@ func TimingCountDataByDateAndReCount(date string, isReCount bool) {
log.Info("startTime time:" + startTime.Format("2006-01-02 15:04:05"))
log.Info("endTime time:" + endTime.Format("2006-01-02 15:04:05"))
warnEmailMessage := "用户统计信息入库失败,请尽快定位。"

startYear := time.Date(USER_YEAR, 1, 1, 0, 0, 0, 1, t.Location())
endYear := startYear.AddDate(1, 0, 0)

models.RefreshUserYearTable(startYear, endYear)

//query wiki data
log.Info("start to time count data")
wikiMap, err := queryWikiCountMap(startTime, endTime)
@@ -907,3 +919,9 @@ func QueryUserLoginInfo(ctx *context.Context) {
log.Info("writer exel error." + err.Error())
}
}

func QueryUserAnnualReport(ctx *context.Context) {
log.Info("start to QueryUserAnnualReport ")
result := models.QueryUserAnnualReport(ctx.User.ID)
ctx.JSON(http.StatusOK, result)
}

+ 6
- 3
routers/repo/user_invitation.go View File

@@ -49,9 +49,10 @@ func getInvitationDetailExcelHeader(ctx *context.Context) map[string]string {
excelHeader := make([]string, 0)
excelHeader = append(excelHeader, ctx.Tr("user.static.id"))
excelHeader = append(excelHeader, ctx.Tr("user.static.name"))
excelHeader = append(excelHeader, ctx.Tr("user.static.srcUserId"))
excelHeader = append(excelHeader, ctx.Tr("user.static.email"))
excelHeader = append(excelHeader, ctx.Tr("user.static.phone"))
excelHeader = append(excelHeader, ctx.Tr("user.static.registdate"))
excelHeader = append(excelHeader, ctx.Tr("user.static.srcUserId"))

excelHeaderMap := make(map[string]string, 0)
var i byte
@@ -92,8 +93,7 @@ func writeInvitationDetailExcel(row int, xlsx *excelize.File, sheetName string,
tmp = tmp + 1
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.Name)
tmp = tmp + 1

xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.SrcUserID)
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.Email)
tmp = tmp + 1

xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.Phone)
@@ -101,7 +101,9 @@ func writeInvitationDetailExcel(row int, xlsx *excelize.File, sheetName string,

formatTime := userRecord.CreatedUnix.Format("2006-01-02 15:04:05")
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, formatTime[0:len(formatTime)-3])
tmp = tmp + 1

xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.SrcUserID)
}

func DownloadInvitationDetail(ctx *context.Context) {
@@ -413,6 +415,7 @@ func queryData(ctx *context.Context, startTime time.Time, endTime time.Time) {
invi.Name = tmpUser.Name
invi.Phone = tmpUser.PhoneNumber
invi.CreatedUnix = tmpUser.CreatedUnix
invi.Email = tmpUser.Email
} else {
invi.Name = "已注销"
}


+ 13
- 1
routers/routes/routes.go View File

@@ -371,7 +371,18 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/images/custom", repo.GetCustomImages)
m.Get("/images/star", repo.GetStarImages)

m.Get("/repos", routers.ExploreRepos)
m.Group("/repos", func() {
//m.Get("", routers.ExploreRepos)
m.Get("", routers.GetRepoSearchPage)
m.Group("/square", func() {
m.Get("", routers.GetRepoSquarePage)
m.Get("/tab", routers.RepoSquare)
m.Get("/active-user", routers.ActiveUser)
m.Get("/active-org", routers.ActiveOrg)
})

m.Get("/search", routers.RepoFind)
})
m.Get("/datasets", routers.ExploreDatasets)
m.Get("/users", routers.ExploreUsers)
m.Get("/organizations", routers.ExploreOrganizations)
@@ -1264,6 +1275,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/delete_model_convert/:id", repo.DeleteModelConvert)
m.Post("/convert_stop/:id", repo.StopModelConvert)
m.Put("/modify_model", repo.ModifyModelInfo)
m.Put("/modify_model_status", repo.ModifyModelPrivate)
m.Get("/show_model", reqRepoModelManageReader, repo.ShowModelTemplate)
m.Get("/convert_model", reqRepoModelManageReader, repo.ConvertModelTemplate)
m.Get("/show_model_info", repo.ShowModelInfo)


+ 2
- 1
routers/user/Invitation.go View File

@@ -63,7 +63,7 @@ func InviationTpl(ctx *context.Context) {
ctx.HTML(200, tplInvitation)
}

func RegisteUserByInvitaionCode(invitationcode string, newUserId int64, newPhoneNumber string) error {
func RegisteUserByInvitaionCode(invitationcode string, newUserId int64, newPhoneNumber string, email string) error {
user := parseInvitaionCode(invitationcode)
if user == nil {
return errors.New("The invitated user not existed.")
@@ -85,6 +85,7 @@ func RegisteUserByInvitaionCode(invitationcode string, newUserId int64, newPhone
SrcUserID: user.ID,
UserID: newUserId,
Phone: newPhoneNumber,
Email: email,
}

err := models.InsertInvitaion(invitation)


+ 1
- 1
routers/user/auth.go View File

@@ -1368,7 +1368,7 @@ func SignUpPost(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterFo

log.Info("enter here, and form.InvitaionCode =" + invitationCode)
if invitationCode != "" {
RegisteUserByInvitaionCode(invitationCode, u.ID, u.PhoneNumber)
RegisteUserByInvitaionCode(invitationCode, u.ID, u.PhoneNumber, u.Email)
}

err := models.AddEmailAddress(&models.EmailAddress{


+ 7
- 2
services/cloudbrain/clear.go View File

@@ -69,7 +69,8 @@ func clearMinioHistoryTrashFile() {
SortModTimeAscend(miniofiles)
for _, file := range miniofiles {

if file.ModTime().Before(time.Now().AddDate(0, 0, -setting.ClearStrategy.TrashSaveDays)) {
if file.Name()!="" && file.ModTime().Before(time.Now().AddDate(0, 0, -setting.ClearStrategy.TrashSaveDays)) {

has,err:=models.IsCloudbrainExistByJobName(file.Name())
if err==nil && !has {
dirPath := setting.CBCodePathPrefix + file.Name() + "/"
@@ -98,7 +99,7 @@ func clearLocalHistoryTrashFile() {
SortModTimeAscend(files)
for _, file := range files {
//清理n天前的历史垃圾数据,清理job目录
if file.ModTime().Before(time.Now().AddDate(0, 0, -setting.ClearStrategy.TrashSaveDays)) {
if file.Name()!="" && file.ModTime().Before(time.Now().AddDate(0, 0, -setting.ClearStrategy.TrashSaveDays)) {
has,err:=models.IsCloudbrainExistByJobName(file.Name())
if err==nil && !has{
os.RemoveAll(setting.JobPath + file.Name())
@@ -125,6 +126,10 @@ func SortModTimeAscend(files []os.FileInfo) {
}

func DeleteCloudbrainOneJobStorage(jobName string) error {

if jobName==""{
return nil
}
//delete local
localJobPath := setting.JobPath + jobName
err := os.RemoveAll(localJobPath)


+ 2
- 46
services/cloudbrain/resource/resource_specification.go View File

@@ -246,10 +246,10 @@ func FindAvailableSpecs(userId int64, opts models.FindSpecsOptions) ([]*models.S
return nil, err
}
//filter exclusive specs
specs := filterExclusiveSpecs(r, userId)
specs := models.FilterExclusiveSpecs(r, userId)

//distinct by sourceSpecId
specs = distinctSpecs(specs)
specs = models.DistinctSpecs(specs)
return specs, err
}

@@ -265,50 +265,6 @@ func FindAvailableSpecs4Show(userId int64, opts models.FindSpecsOptions) ([]*api
return result, nil
}

func filterExclusiveSpecs(r []*models.Specification, userId int64) []*models.Specification {
specs := make([]*models.Specification, 0, len(r))
specMap := make(map[int64]string, 0)
for i := 0; i < len(r); i++ {
spec := r[i]
if _, has := specMap[spec.ID]; has {
continue
}
if !spec.IsExclusive {
specs = append(specs, spec)
specMap[spec.ID] = ""
continue
}
orgs := strings.Split(spec.ExclusiveOrg, ";")
for _, org := range orgs {
isMember, _ := models.IsOrganizationMemberByOrgName(org, userId)
if isMember {
specs = append(specs, spec)
specMap[spec.ID] = ""
break
}
}
}
return specs
}

func distinctSpecs(r []*models.Specification) []*models.Specification {
specs := make([]*models.Specification, 0, len(r))
sourceSpecIdMap := make(map[string]string, 0)
for i := 0; i < len(r); i++ {
spec := r[i]
if spec.SourceSpecId == "" {
specs = append(specs, spec)
continue
}
if _, has := sourceSpecIdMap[spec.SourceSpecId]; has {
continue
}
specs = append(specs, spec)
sourceSpecIdMap[spec.SourceSpecId] = ""
}
return specs
}

func GetAndCheckSpec(userId int64, specId int64, opts models.FindSpecsOptions) (*models.Specification, error) {
if specId == 0 {
return nil, nil


+ 88
- 0
services/repository/contributor.go View File

@@ -0,0 +1,88 @@
package repository

import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/redis/redis_client"
"code.gitea.io/gitea/modules/redis/redis_key"
"encoding/json"
"github.com/patrickmn/go-cache"
"time"
)

var repoContributorCache = cache.New(5*time.Minute, 1*time.Minute)

type ContributorCacheVal struct {
Contributors []*models.ContributorInfo
Total int
}

func GetRepoTopNContributors(repo *models.Repository, N int) ([]*models.ContributorInfo, int) {
val, _ := redis_client.Get(redis_key.RepoTopNContributors(repo.ID, N))
if val != "" {
log.Debug("Get RepoTopNContributors from redis,repo.ID = %d value = %v", repo.ID, val)
temp := &ContributorCacheVal{}
json.Unmarshal([]byte(val), temp)
return temp.Contributors, temp.Total
}

contributorInfos, total := getRepoTopNContributorsFromDisk(repo, N)
log.Debug("Get RepoTopNContributors from disk,repo.ID = %d ", repo.ID)
jsonVal, err := json.Marshal(&ContributorCacheVal{Contributors: contributorInfos, Total: total})
if err == nil {
redis_client.Setex(redis_key.RepoTopNContributors(repo.ID, N), string(jsonVal), 2*time.Minute)
}
return contributorInfos, total
}

func getRepoTopNContributorsFromDisk(repo *models.Repository, N int) ([]*models.ContributorInfo, int) {
contributorInfos := make([]*models.ContributorInfo, 0)

branchName := GetDefaultBranchName(repo)
if branchName == "" {
return contributorInfos, 0
}

contributors, err := git.GetContributors(repo.RepoPath(), branchName)
if err == nil && contributors != nil {
contributorInfoHash := make(map[string]*models.ContributorInfo)
for _, c := range contributors {
if len(contributorInfos) >= N {
break
}
if c.Email == "" {
continue
}
// get user info from committer email
user, err := models.GetUserByActivateEmail(c.Email)
if err == nil {
// committer is system user, get info through user's primary email
if existedContributorInfo, ok := contributorInfoHash[user.Email]; ok {
// existed: same primary email, different committer name
existedContributorInfo.CommitCnt += c.CommitCnt
} else {
// new committer info
var newContributor = &models.ContributorInfo{
user.RelAvatarLink(), user.Name, user.Email, c.CommitCnt,
}
contributorInfos = append(contributorInfos, newContributor)
contributorInfoHash[user.Email] = newContributor
}
} else {
// committer is not system user
if existedContributorInfo, ok := contributorInfoHash[c.Email]; ok {
// existed: same primary email, different committer name
existedContributorInfo.CommitCnt += c.CommitCnt
} else {
var newContributor = &models.ContributorInfo{
"", "", c.Email, c.CommitCnt,
}
contributorInfos = append(contributorInfos, newContributor)
contributorInfoHash[c.Email] = newContributor
}
}
}
}
return contributorInfos, len(contributors)
}

+ 51
- 6
services/repository/repository.go View File

@@ -5,18 +5,19 @@
package repository

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

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification"
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
pull_service "code.gitea.io/gitea/services/pull"
"fmt"
"io/ioutil"
"net/http"
"os"
"strings"
"xorm.io/xorm"
)

const SHELL_FLAG_ON = 1
@@ -328,3 +329,47 @@ func IsUploadFileInvalidErr(err error) bool {
_, ok := err.(UploadFileInvalidErr)
return ok
}

func IncreaseRepoDatasetNum(datasetID int64, engines ...*xorm.Engine) error {
dataset, err := models.GetDatasetByID(datasetID)
if err != nil {
return err
}
return models.OperateRepoDatasetNum(dataset.RepoID, 1, engines...)
}

func IncreaseRepoModelNum(repoId int64, engines ...*xorm.Engine) error {
return models.OperateRepoModelNum(repoId, 1, engines...)
}

func ResetRepoModelNum(repoId int64) error {
return models.ResetRepoModelNum(repoId)
}

func DecreaseRepoDatasetNum(datasetID int64, engines ...*xorm.Engine) error {
dataset, err := models.GetDatasetByID(datasetID)
if err != nil {
return err
}
return models.OperateRepoDatasetNum(dataset.RepoID, -1, engines...)
}

func DecreaseRepoModelNum(repoId int64, engines ...*xorm.Engine) error {
return models.OperateRepoModelNum(repoId, -1, engines...)
}

func GetDefaultBranchName(repo *models.Repository) string {
gitRepo, err := git.OpenRepository(repo.RepoPath())
if err != nil {
return ""
}
defer gitRepo.Close()
if len(repo.DefaultBranch) > 0 && gitRepo.IsBranchExist(repo.DefaultBranch) {
return repo.DefaultBranch
}
brs, _, err := gitRepo.GetBranches(0, 0)
if len(brs) > 0 {
return brs[0]
}
return ""
}

+ 315
- 0
services/repository/square.go View File

@@ -0,0 +1,315 @@
package repository

import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"encoding/json"
"github.com/patrickmn/go-cache"
"time"
)

var repoSquareCache = cache.New(2*time.Minute, 1*time.Minute)

const (
RREFERED_CACHE = "PreferredRepos"
REPO_BANNER_CACHE = "RepoBanner"
TOPICS_CACHE = "RepoTopics"
RECOMMEND_CACHE = "RecommendRepos"
)

func GetBanners() []map[string]string {
v, success := repoSquareCache.Get(REPO_BANNER_CACHE)
if success {
log.Debug("GetBanners from cache,value = %v", v)
if v == nil {
return nil
}
r := v.([]map[string]string)
return r
}
repoMap := getMapContent("repos/square_banner")
repoSquareCache.Set(REPO_BANNER_CACHE, repoMap, 1*time.Minute)
return repoMap
}

func GetTopics() []string {
v, success := repoSquareCache.Get(TOPICS_CACHE)
if success {
log.Debug("GetTopics from cache,value = %v", v)
if v == nil {
return nil
}
r := v.([]string)
return r
}
topics := getArrayContent("repos/recommend_topics")
repoSquareCache.Set(TOPICS_CACHE, topics, 1*time.Minute)
return topics
}

func getMapContent(fileName string) []map[string]string {
url := setting.RecommentRepoAddr + fileName
result, err := RecommendContentFromPromote(url)
remap := make([]map[string]string, 0)
if err == nil {
json.Unmarshal([]byte(result), &remap)
}
return remap
}

func getArrayContent(fileName string) []string {
url := setting.RecommentRepoAddr + fileName
result, err := RecommendContentFromPromote(url)
r := make([]string, 0)
if err == nil {
json.Unmarshal([]byte(result), &r)
}
return r
}

func GetRecommendRepos() []map[string]interface{} {
v, success := repoSquareCache.Get(RECOMMEND_CACHE)
if success {
log.Debug("GetRecommendRepos from cache,value = %v", v)
if v == nil {
return nil
}
r := v.([]map[string]interface{})
return r
}
repoMap := getMapContent("home/projects")
r, _ := GetRecommendRepoFromPromote(repoMap)
repoSquareCache.Set(RECOMMEND_CACHE, r, 1*time.Minute)
return r
}

func GetPreferredRepos() ([]*models.Repository4Card, error) {
v, success := repoSquareCache.Get(RREFERED_CACHE)
if success {
log.Debug("GetPreferredRepos from cache,value = %v", v)
if v == nil {
return nil, nil
}
r := v.([]*models.Repository4Card)
return r, nil
}

repos, err := models.GetSelectedRepos(models.FindSelectedReposOpts{
ListOptions: models.ListOptions{
PageSize: 10,
Page: 1,
},
OnlyPublic: true,
})
if err != nil {
return nil, err
}
result := make([]*models.Repository4Card, len(repos))
for i, r := range repos {
result[i] = r.ToCardFormat()
}

repoSquareCache.Set(RREFERED_CACHE, result, 1*time.Minute)
return result, nil
}

func GetIncubationRepos() ([]*models.Repository4Card, error) {
org, err := models.GetOrgByName(setting.IncubationSourceOrgName)
if models.IsErrOrgNotExist(err) {
return make([]*models.Repository4Card, 0), nil
}
if err != nil {
return nil, err
}
repos, err := models.GetSelectedRepos(models.FindSelectedReposOpts{
ListOptions: models.ListOptions{
PageSize: 10,
Page: 1,
},
OrgId: org.ID,
OnlyPublic: true,
})
if err != nil {
return nil, err
}
result := make([]*models.Repository4Card, len(repos))
for i, r := range repos {
result[i] = r.ToCardFormat()
}
return result, nil
}

func GetHotPaperRepos() ([]*models.Repository4Card, error) {
rlist, _, err := models.SearchRepository(&models.SearchRepoOptions{
ListOptions: models.ListOptions{
Page: 1,
PageSize: 10,
},
OrderBy: models.SearchOrderByLastMonthVisitsReverse + "," + models.SearchOrderByRecentUpdated,
TopicOnly: true,
TopicName: setting.PaperRepoTopicName,
AllPublic: true,
})
if err != nil {
return nil, err
}
result := make([]*models.Repository4Card, len(rlist))
for i, r := range rlist {
result[i] = r.ToCardFormat()
}
return result, nil
}

type FindReposOptions struct {
models.ListOptions
Actor *models.User
Sort string
Keyword string
Topic string
Private bool
OwnerID int64
}

func FindRepos(opts FindReposOptions) (*models.FindReposResponse, error) {

var (
repos []*models.Repository
count int64
err error
orderBy models.SearchOrderBy
)

switch opts.Sort {
//1.近期热门:按最近1个月浏览量倒序排序,最近1个月浏览量>最近更新>项目名称升序
case "mostpopular":
orderBy = models.SearchOrderByLastMonthVisitsReverse + "," + models.SearchOrderByRecentUpdated + "," + models.SearchOrderByAlphabetically
//2.近期活跃:按提交增长量(最近4个月commit数)倒序排序,提交增长量>最近更新>项目名称升序。
case "mostactive":
orderBy = models.SearchOrderByLastFourMonthCommitsReverse + "," + models.SearchOrderByRecentUpdated + "," + models.SearchOrderByAlphabetically
//3.最近更新:按最近更新>项目名称升序排序。
case "recentupdate":
orderBy = models.SearchOrderByRecentUpdated + "," + models.SearchOrderByAlphabetically
//4.最近创建:按项目创建时间排序,最近的排前面。最近创建>项目名称升序。
case "newest":
orderBy = models.SearchOrderByNewest + "," + models.SearchOrderByAlphabetically
//5.点赞最多:按点赞数倒序排序。点赞数>最近更新>项目名称升序。
case "moststars":
orderBy = models.SearchOrderByStarsReverse + "," + models.SearchOrderByRecentUpdated + "," + models.SearchOrderByAlphabetically
//6.派生最多:按派生数倒序排序。派生数>最近更新>项目名称升序。
case "mostforks":
orderBy = models.SearchOrderByForksReverse + "," + models.SearchOrderByRecentUpdated + "," + models.SearchOrderByAlphabetically
//7.数据集最多:按项目包含的数据集文件数量倒序排序,数据集文件数>最近更新>项目名称升序。
case "mostdatasets":
orderBy = models.SearchOrderByDatasetCntReverse + "," + models.SearchOrderByRecentUpdated + "," + models.SearchOrderByAlphabetically
//8.AI任务最多:按项目包含的AI任务数量倒序排序,AI任务数>最近更新>项目名称升序。
case "mostaitasks":
orderBy = models.SearchOrderByAiTaskCntReverse + "," + models.SearchOrderByRecentUpdated + "," + models.SearchOrderByAlphabetically
//9.模型最多:按项目包含的模型数量倒序排序,模型大小为0则不统计。模型数>最近更新>项目名称升序。
case "mostmodels":
orderBy = models.SearchOrderByModelCntReverse + "," + models.SearchOrderByRecentUpdated + "," + models.SearchOrderByAlphabetically

default:
orderBy = models.SearchOrderByLastMonthVisitsReverse + "," + models.SearchOrderByRecentUpdated + "," + models.SearchOrderByAlphabetically
}

repos, count, err = models.SearchRepository(&models.SearchRepoOptions{
ListOptions: opts.ListOptions,
Actor: opts.Actor,
OrderBy: orderBy,
Private: opts.Private,
Keyword: opts.Keyword,
OwnerID: opts.OwnerID,
AllPublic: true,
AllLimited: true,
TopicName: opts.Topic,
IncludeDescription: setting.UI.SearchRepoDescription,
})
if err != nil {
log.Error("FindRepos error when SearchRepository.%v", err)
return nil, err
}
result := make([]*models.Repository4Card, len(repos))
for i, r := range repos {
t := r.ToCardFormat()
contributors, _ := GetRepoTopNContributors(r, 6)
t.Contributors = contributors
result[i] = t
}

return &models.FindReposResponse{
Repos: result,
Total: count,
Page: opts.Page,
PageSize: opts.PageSize,
}, nil
}

type ActiveUser struct {
User *models.User4Front
Followed bool
ShowButton bool
}

func GetActiveUser4Square(currentUserId int64) ([]*ActiveUser, error) {
result := make([]*ActiveUser, 0)
userIds, err := models.QueryLast30DaysHighestIndexUsers(5)
if err != nil {
log.Error("ActiveUser err. %v", err)
return result, err
}
if len(userIds) == 0 {
return result, nil
}

users, err := models.GetUsersByIDs(userIds)
if err != nil {
return result, nil
}
usersMap := make(map[int64]*models.User)
for _, v := range users {
usersMap[v.ID] = v
}

for i := 0; i < len(userIds); i++ {
userId := userIds[i]
user := usersMap[userId]
if user == nil {
continue
}
isFollowed := false
if currentUserId != 0 {
isFollowed = models.IsFollowing(currentUserId, userId)
}
a := &ActiveUser{
Followed: isFollowed,
User: user.ToFrontFormat(),
ShowButton: currentUserId != userId,
}
result = append(result, a)
}
return result, nil
}

func GetActiveOrgs() ([]*models.User4Front, error) {
orgScores, err := models.FindTopNOpenIOrgs(5)
if err != nil {
return nil, err
}
orgs := make([]*models.User4Front, len(orgScores))
for i, v := range orgScores {
orgs[i] = v.ToFrontFormat()
}
return orgs, nil
}

func RefreshRepoStatData() {
repos, err := models.GetAllRepositories()
if err != nil {
log.Error("RefreshRepoStatData GetAllRepositories failed: %v", err.Error())
return
}
for _, repo := range repos {
models.SyncStatDataToRepo(repo)
}
}

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

@@ -35,7 +35,7 @@
</div>
</div>
</div>
<a class="item" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "custom.head.project"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/repos/square">{{.i18n.Tr "custom.head.project"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "custom.head.dataset"}}</a>
<div class="ui simple dropdown item" >
{{.i18n.Tr "repo.model_manager"}}
@@ -48,7 +48,7 @@
{{.i18n.Tr "explore"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a>
<!--<a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a>-->
<a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/images">{{.i18n.Tr "explore.images"}}</a>
{{if .IsOperator}}
@@ -75,7 +75,7 @@
</div>
</div>

<a class="item" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "custom.head.project"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/repos/square">{{.i18n.Tr "custom.head.project"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "custom.head.dataset"}}</a>
<div class="ui simple dropdown item" >
{{.i18n.Tr "repo.model_manager"}}
@@ -89,7 +89,7 @@
{{.i18n.Tr "explore"}}
<i class="dropdown icon"></i>
<div class="menu" >
<a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a>
<!--<a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a>-->
<a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/images">{{.i18n.Tr "explore.images"}}</a>
{{if .IsOperator}}
@@ -100,7 +100,7 @@
</div>
</div>
{{else if .IsLandingPageExplore}}
<a class="item {{if .PageIsExplore}}active{{end}}" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "home"}}</a>
<a class="item {{if .PageIsExplore}}active{{end}}" href="{{AppSubUrl}}/explore/repos/square">{{.i18n.Tr "home"}}</a>
{{else if .IsLandingPageOrganizations}}
<a class="item {{if .PageIsExplore}}active{{end}}" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "home"}}</a>
{{end}}


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

@@ -32,7 +32,7 @@
</div>
</div>
</div>
<a class="item" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "custom.head.project"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/repos/square">{{.i18n.Tr "custom.head.project"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "custom.head.dataset"}}</a>
<div class="ui simple dropdown item" >
{{.i18n.Tr "repo.model_manager"}}
@@ -45,7 +45,7 @@
{{.i18n.Tr "explore"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a>
<!--<a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a>-->
<a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/images">{{.i18n.Tr "explore.images"}}</a>
{{if .IsOperator}}
@@ -71,7 +71,7 @@
</div>
</div>
</div>
<a class="item" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "custom.head.project"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/repos/square">{{.i18n.Tr "custom.head.project"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "custom.head.dataset"}}</a>
<div class="ui simple dropdown item" >
{{.i18n.Tr "repo.model_manager"}}
@@ -84,7 +84,7 @@
{{.i18n.Tr "explore"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a>
<!--<a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a>-->
<a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/images">{{.i18n.Tr "explore.images"}}</a>
{{if .IsOperator}}
@@ -95,7 +95,7 @@
</div>
</div>
{{else if .IsLandingPageExplore}}
<a class="item {{if .PageIsExplore}}active{{end}}" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "home"}}</a>
<a class="item {{if .PageIsExplore}}active{{end}}" href="{{AppSubUrl}}/explore/repos/square">{{.i18n.Tr "home"}}</a>
{{else if .IsLandingPageOrganizations}}
<a class="item {{if .PageIsExplore}}active{{end}}" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "home"}}</a>
{{end}}


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

@@ -24,7 +24,7 @@
</div>
</div>
</div>
<a class="item" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "custom.head.project"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/repos/square">{{.i18n.Tr "custom.head.project"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "custom.head.dataset"}}</a>
<div class="ui simple dropdown item" >
{{.i18n.Tr "repo.model_manager"}}
@@ -37,7 +37,7 @@
{{.i18n.Tr "explore"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a>
<!--<a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a>-->
<a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/images">{{.i18n.Tr "explore.images"}}</a>
{{if .IsOperator}}
@@ -64,7 +64,7 @@
</div>
</div>

<a class="item" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "custom.head.project"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/repos/square">{{.i18n.Tr "custom.head.project"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "custom.head.dataset"}}</a>
<div class="ui simple dropdown item" >
{{.i18n.Tr "repo.model_manager"}}
@@ -77,7 +77,7 @@
{{.i18n.Tr "explore"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a>
<!--<a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a>-->
<a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/images">{{.i18n.Tr "explore.images"}}</a>
{{if .IsOperator}}
@@ -88,7 +88,7 @@
</div>
</div>
{{else if .IsLandingPageExplore}}
<a class="item {{if .PageIsExplore}}active{{end}}" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "home"}}</a>
<a class="item {{if .PageIsExplore}}active{{end}}" href="{{AppSubUrl}}/explore/repos/square">{{.i18n.Tr "home"}}</a>
{{else if .IsLandingPageOrganizations}}
<a class="item {{if .PageIsExplore}}active{{end}}" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "home"}}</a>
{{end}}


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

@@ -34,7 +34,7 @@
</div>
</div>

<a class="item" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "custom.head.project"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/repos/square">{{.i18n.Tr "custom.head.project"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "custom.head.dataset"}}</a>
<div class="ui simple dropdown item" >
{{.i18n.Tr "repo.model_manager"}}
@@ -47,7 +47,7 @@
{{.i18n.Tr "explore"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a>
<!--<a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a>-->
<a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/images">{{.i18n.Tr "explore.images"}}</a>
{{if .IsOperator}}
@@ -87,7 +87,7 @@
{{.i18n.Tr "explore"}}
<i class="dropdown icon"></i>
<div class="menu" >
<a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a>
<!--<a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a>-->
<a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/images">{{.i18n.Tr "explore.images"}}</a>
{{if .IsOperator}}
@@ -98,7 +98,7 @@
</div>
</div>
{{else if .IsLandingPageExplore}}
<a class="item {{if .PageIsExplore}}active{{end}}" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "home"}}</a>
<a class="item {{if .PageIsExplore}}active{{end}}" href="{{AppSubUrl}}/explore/repos/square">{{.i18n.Tr "home"}}</a>
{{else if .IsLandingPageOrganizations}}
<a class="item {{if .PageIsExplore}}active{{end}}" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "home"}}</a>
{{end}}


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

@@ -1,6 +1,6 @@
<div class="tablet only mobile only sixteen wide mobile sixteen wide tablet column row">
<div class="ui secondary pointing tabular top attached borderless menu navbar">
<a class="{{if .PageIsExploreRepositories}}active{{end}} item" href="{{AppSubUrl}}/explore/repos">
<a class="{{if .PageIsExploreRepositories}}active{{end}} item" href="{{AppSubUrl}}/explore/repos/square">
{{svg "octicon-repo" 16}} {{.i18n.Tr "explore.repos"}}
</a>
<a class="{{if .PageIsDatasets}}active{{end}} item" href="{{AppSubUrl}}/explore/datasets">
@@ -24,7 +24,7 @@
<div class="computer only three wide computer column">
<div class="ui grid">
<div class="sixteen wide column ui secondary sticky pointing tabular vertical menu">
<a class="{{if .PageIsExploreRepositories}}active{{end}} item" href="{{AppSubUrl}}/explore/repos">
<a class="{{if .PageIsExploreRepositories}}active{{end}} item" href="{{AppSubUrl}}/explore/repos/square">
{{svg "octicon-repo" 16}} {{.i18n.Tr "explore.repos"}}
</a>
<a class="{{if .PageIsDatasets}}active{{end}} item" href="{{AppSubUrl}}/explore/datasets">


+ 8
- 0
templates/explore/repos/search.tmpl View File

@@ -0,0 +1,8 @@
{{template "base/head_home" .}}
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/vp-repos-search.css?v={{MD5 AppVer}}" />
<script>
var staticSquareTopics = {{ .SquareTopics }};
</script>
<div id="__vue-root"></div>
<script src="{{StaticUrlPrefix}}/js/vp-repos-search.js?v={{MD5 AppVer}}"></script>
{{template "base/footer" .}}

+ 16
- 0
templates/explore/repos/square.tmpl View File

@@ -0,0 +1,16 @@
{{template "base/head_home" .}}
{{ if .SquareBanners }}
{{ range .SquareBanners }}
<img preload style="height:0;width:0;position:absolute;left:-2000px;" src="{{.src}}" />
{{ end }}
{{ end }}
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/vp-repos-square.css?v={{MD5 AppVer}}" />
<script>
var staticSquareBanners = {{ .SquareBanners }};
var staticSquarePreferredRepos = {{ .SquarePreferredRepos }};
var staticSquareTopics = {{ .SquareTopics }};
var staticSquareRecommendRepos = {{ .SquareRecommendRepos }};
</script>
<div id="__vue-root"></div>
<script src="{{StaticUrlPrefix}}/js/vp-repos-square.js?v={{MD5 AppVer}}"></script>
{{template "base/footer" .}}

+ 21
- 0
templates/repo/cloudbrain/trainjob/show.tmpl View File

@@ -454,6 +454,23 @@
<input style="width: 83%;margin-left: 7px;" id="label" name="label" maxlength="255"
placeholder='{{.i18n.Tr "repo.modelarts.train_job.label_place"}}'>
</div>
<div class="inline fields">
<label>{{.i18n.Tr "repo.model.manage.modelaccess"}}&nbsp;&nbsp;&nbsp;</label>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="isPrivate" checked="checked" value="false">
<label>{{.i18n.Tr "repo.model.manage.modelaccess.public"}}</label>
</div>
</div>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="isPrivate" value="true">
<label>{{.i18n.Tr "repo.model.manage.modelaccess.private"}}</label>
</div>
</div>
</div>
<div class="inline field">
<label for="description">{{.i18n.Tr "repo.model.manage.modeldesc"}}</label>
<textarea style="width: 83%;margin-left: 7px;" id="description" name="description" rows="3"
@@ -656,6 +673,10 @@
}
let url_href = `/${userName}/${repoPath}/modelmanage/create_new_model`
let data = $("#formId").serialize()
var radio = document.getElementsByName("isPrivate");
if(radio == null || radio.length == 0){
data +="&isPrivate=true";
}
$("#mask").css({ "display": "block", "z-index": "9999" })
$.ajax({
url: url_href,


+ 21
- 0
templates/repo/grampus/trainjob/show.tmpl View File

@@ -490,6 +490,23 @@
<input style="width: 83%;margin-left: 7px;" id="label" name="label" maxlength="255"
placeholder='{{.i18n.Tr "repo.modelarts.train_job.label_place"}}'>
</div>
<div class="inline fields">
<label>{{.i18n.Tr "repo.model.manage.modelaccess"}}&nbsp;&nbsp;&nbsp;</label>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="isPrivate" checked="checked" value="false">
<label>{{.i18n.Tr "repo.model.manage.modelaccess.public"}}</label>
</div>
</div>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="isPrivate" value="true">
<label>{{.i18n.Tr "repo.model.manage.modelaccess.private"}}</label>
</div>
</div>
</div>
<div class="inline field">
<label for="description">{{.i18n.Tr "repo.model.manage.modeldesc"}}</label>
<textarea style="width: 83%;margin-left: 7px;" id="description" name="description" rows="3"
@@ -721,6 +738,10 @@
}
let url_href = `/${userName}/${repoPath}/modelmanage/create_new_model`
let data = $("#formId").serialize()
var radio = document.getElementsByName("isPrivate");
if(radio == null || radio.length == 0){
data +="&isPrivate=true";
}
$("#mask").css({ "display": "block", "z-index": "9999" })
$.ajax({
url: url_href,


+ 23
- 0
templates/repo/modelarts/trainjob/show.tmpl View File

@@ -508,6 +508,25 @@
<input style="width: 83%;margin-left: 7px;" id="label" name="label" maxlength="255"
placeholder='{{.i18n.Tr "repo.modelarts.train_job.label_place"}}'>
</div>
{{if eq $.Repository.IsPrivate false}}
<div class="inline fields">
<label>{{.i18n.Tr "repo.model.manage.modelaccess"}}&nbsp;&nbsp;&nbsp;</label>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="isPrivate" checked="checked" value="false">
<label>{{.i18n.Tr "repo.model.manage.modelaccess.public"}}</label>
</div>
</div>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="isPrivate" value="true">
<label>{{.i18n.Tr "repo.model.manage.modelaccess.private"}}</label>
</div>
</div>
</div>
{{end}}
<div class="inline field">
<label for="description">{{.i18n.Tr "repo.model.manage.modeldesc"}}</label>
<textarea style="width: 83%;margin-left: 7px;" id="description" name="description" rows="3"
@@ -735,6 +754,10 @@
}
let url_href = `/${userName}/${repoPath}/modelmanage/create_new_model`
let data = $("#formId").serialize()
var radio = document.getElementsByName("isPrivate");
if(radio == null || radio.length == 0){
data +="&isPrivate=true";
}
$("#mask").css({ "display": "block", "z-index": "9999" })
$.ajax({
url: url_href,


+ 1
- 1
templates/repo/modelmanage/convertIndex.tmpl View File

@@ -487,7 +487,7 @@
}
function loadModelList(){
$.get(`${repolink}/modelmanage/query_model_for_predict?repoId=${repoId}&type=-1`, (data) => {
$.get(`${repolink}/modelmanage/query_model_for_predict?repoId=${repoId}&type=-1&isOnlyThisRepo=true`, (data) => {
modelData = data
let nameList = data.nameList
const n_length = nameList.length


+ 1
- 0
templates/repo/modelmanage/create_local_1.tmpl View File

@@ -2,6 +2,7 @@
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/vp-modelmanage-local-create-1.css?v={{MD5 AppVer}}" />
<div class="repository release dataset-list view">
{{template "repo/header" .}}
<script>var REPO_IS_PRIVATE = {{$.Repository.IsPrivate}};</script>
<div class="ui container">
<div id="__vue-root"></div>
</div>


+ 23
- 0
templates/repo/modelmanage/create_online.tmpl View File

@@ -135,6 +135,25 @@
<input class="ays-ignore" id="label" name="label" maxlength="255" placeholder='{{.i18n.Tr "repo.modelarts.train_job.label_place"}}'>
</div>
</div>
{{if eq $.Repository.IsPrivate false}}
<div class="inline fields">
<div class="two wide field right aligned">
<label>{{.i18n.Tr "repo.model.manage.modelaccess"}} &nbsp</label>
</div>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="isPrivate" checked="checked" value="false">
<label>{{.i18n.Tr "repo.model.manage.modelaccess.public"}}</label>
</div>
</div>
<div class="field">
<div class="ui radio checkbox">
<input type="radio" name="isPrivate" value="true">
<label>{{.i18n.Tr "repo.model.manage.modelaccess.private"}}</label>
</div>
</div>
</div>
{{end}}
<div class="inline fields">
<div class="two wide field right aligned">
<label for="description">{{.i18n.Tr "repo.model.manage.modeldesc"}} &nbsp</label>
@@ -549,6 +568,10 @@
let cName = $("input[name='name']").val();
let version = $("input[name='version']").val();
let data = $("#formId").serialize();
var radio = document.getElementsByName("isPrivate");
if(radio == null || radio.length == 0){
data +="&isPrivate=true";
}
const initModel = $("input[name='initModel']").val();
let url_href = location.href.split("create_online_model")[0] + 'create_new_model';
$("#mask").css({ display: "block", "z-index": "9999" });


+ 3
- 1
templates/repo/modelmanage/index.tmpl View File

@@ -44,7 +44,7 @@
}
</style>
<link rel="stylesheet" href="/self/ztree/css/zTreeStyle/zTreeStyle.css" type="text/css">
<script>var REPO_IS_PRIVATE = {{.Repository.IsPrivate}};</script>

<!-- 弹窗 -->
<div id="mask">
@@ -57,6 +57,7 @@
</div>
</div>
{{$repository := .Repository.ID}}

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

@@ -234,6 +235,7 @@
<input id="label" name="label" maxlength="255" placeholder='{{.i18n.Tr "repo.modelarts.train_job.label_place"}}'>
</div>
</div>
<div class="inline fields">
<div class="two wide field right aligned">
<label for="description">{{.i18n.Tr "repo.model.manage.modeldesc"}} &nbsp</label>


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

@@ -10,7 +10,7 @@
{{.i18n.Tr "home.wecome_AI_plt"}}
</div>
<div class="content">
<p class="ui text grey">{{.i18n.Tr "home.explore_AI"}} <a href="{{AppSubUrl}}/explore/repos"> {{.i18n.Tr "home.repositories"}}</a> {{.i18n.Tr "home.or_t"}} <a href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "home.datasets"}}</a></p>
<p class="ui text grey">{{.i18n.Tr "home.explore_AI"}} <a href="{{AppSubUrl}}/explore/repos/square"> {{.i18n.Tr "home.repositories"}}</a> {{.i18n.Tr "home.or_t"}} <a href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "home.datasets"}}</a></p>
<p><span class="ui text grey">{{.i18n.Tr "home.use_plt__fuction"}}</span>&nbsp;<a class="mini ui blue button" href="{{AppSubUrl}}/repo/create{{if .ContextUser.IsOrganization}}?org={{.ContextUser.ID}}{{end}}" >{{.i18n.Tr "repo.create_repo"}}</a></p>
<p class="ui text grey">{{.i18n.Tr "home.provide_resoure"}}</p>
</div>


+ 59
- 29
web_src/js/components/Model.vue View File

@@ -32,18 +32,7 @@
>
</template>
</el-table-column>
<el-table-column
prop="status"
:label="i18n.model_status"
align="center"
min-width="6.5%"
>
<template slot-scope="scope">
<span class="text-over" :title="scope.row.status_title">
<i style="vertical-align: middle" :class="scope.row.status"></i
></span>
</template>
</el-table-column>
<el-table-column
prop="version"
:label="i18n.model_version"
@@ -102,6 +91,22 @@
</template>
</el-table-column>
<el-table-column
prop="isPrivate"
:label="i18n.model_status"
align="center"
min-width="6.75%"
>
<template slot-scope="scope">
<span class="text-over" :title="scope.row.status_title">
<i style="vertical-align: middle" :class="scope.row.status"></i
></span>
<span style="color: #fa8c16;" v-if="scope.row.isPrivate">{{ i18n.modelaccess_private }}</span>
<span style="color: #13c28d;" v-else="!scope.row.isPrivate">{{ i18n.modelaccess_public }}</span>
</template>
</el-table-column>

<el-table-column
prop="createdUnix"
:label="i18n.model_create_time"
align="center"
@@ -137,29 +142,23 @@
>
<template slot-scope="scope">
<div class="space-around" >
<!--<a
:style="{
visibility: !scope.row.Children ? 'visible' : 'hidden',
}"
:class="{ disabled: !scope.row.isCanOper }"
@click="
showcreateVue(
scope.row.name,
scope.row.version,
scope.row.label
)
"
>{{ i18n.model_create_new_ver }}</a
>-->
<a class="op-btn"
v-show="scope.row.modelType == 1"
:href="url + 'create_local_model_1?type=1&name=' + encodeURIComponent(scope.row.name) + '&id=' + scope.row.id"
:class="{ disabled: !scope.row.isCanOper }"
>{{ i18n.modify }}</a>
<a class="op-btn" v-show="scope.row.modelType != 1" style="color:transparent;cursor:default;" >{{ i18n.modify }}</a>
<a class="op-btn" v-show="scope.row.modelType != 1" style="color:transparent;cursor:default;" >{{ i18n.modify }}</a>
<a class="op-btn" style="color: #13c28d;" v-show="repoIsPrivate == false && scope.row.isPrivate==true && scope.row.isCanOper" @click="
modifyModelStatus(scope.row.id, scope.row.cName, scope.row.rowKey,false)
">{{ i18n.modelaccess_setpublic }}</a>
<a class="op-btn" style="color: #fa8c16;" v-show="repoIsPrivate == false && scope.row.isPrivate==false && scope.row.isCanOper" @click="
modifyModelStatus(scope.row.id, scope.row.cName, scope.row.rowKey,true)
">{{ i18n.modelaccess_setprivate }}</a>
<a class="op-btn"
:href="loadhref + scope.row.id"
:class="{ disabled: !scope.row.isCanOper }"
:class="{ disabled: !scope.row.isCanDownload }"
>{{ i18n.model_download }}</a>
<a class="op-btn"
:class="{ disabled: !scope.row.isCanDelete }"
@@ -190,8 +189,9 @@
</template>

<script>
import { modifyModelStatus } from '~/apis/modules/modelmanage';
const { _AppSubUrl, _StaticUrlPrefix, csrf } = window.config;
const REPOISPRIVATE = window.REPO_IS_PRIVATE;
export default {
components: {},
data() {
@@ -206,11 +206,13 @@ export default {
isLoading: true,
loadNodeMap: new Map(),
submitId: {},
repo: location.pathname.split('/').slice(0, 3).join('/'),
defaultAvatar: "/user/avatar/Ghost/-1",
defaultAvatarName: "Ghost",
data: "",
timer: null,
timerFlag: false,
repoIsPrivate: REPOISPRIVATE,
};
},
methods: {
@@ -403,6 +405,26 @@ export default {
}
}
},
modifyModelStatus(id, name, rowKey,isPrivate) {
let data = {'id':id,'isPrivate':isPrivate,'repo':this.repo};
modifyModelStatus(data).then(res => {
res = res.data;
if (res && res.code == '0') {
this.getModelList();
} else {
this.$message({
type: 'error',
message: this.$t('modelManage.infoModificationFailed'),
});
}
}).catch(err => {
console.log(err);
this.$message({
type: 'error',
message: this.$t('modelManage.infoModificationFailed'),
});
});
},
deleteModel(id, name, rowKey) {
let row = { cName: name, id: id, rowKey: rowKey };
let _this = this;
@@ -542,6 +564,14 @@ export default {
showinfoHref() {
return this.url + "show_model_info?name=";
},
transStatus(){
return function (state) {
if(state){
return this.i18n.modelaccess_private;
}
return this.i18n.modelaccess_public;
}
},
transTime() {
return function (time) {
let date = new Date(time * 1000); //时间戳为10位需*1000,时间戳为13位的话不需乘1000


+ 10
- 0
web_src/js/features/i18nVue.js View File

@@ -92,6 +92,11 @@ export const i18nVue = {
model_create_time: "创建时间",
model_creator: "创建者",
model_operation: "操作",
model_access: "权限",
modelaccess_public:"公开",
modelaccess_private:"私有",
modelaccess_setpublic:"设为公开",
modelaccess_setprivate:"设为私有",
model_create_new_ver: "创建新版本",
model_download: "下载",
model_delete: "删除",
@@ -208,6 +213,11 @@ export const i18nVue = {
model_create_time: "Created Time",
model_creator: "Creator",
model_operation: "Operation",
model_access: "Access",
modelaccess_public:"Public",
modelaccess_private:"Private",
modelaccess_setpublic:"Set Public",
modelaccess_setprivate:"Set Private",
model_create_new_ver: "New Version",
model_download: "Download",
model_delete: "Delete",


+ 2
- 2
web_src/js/index.js View File

@@ -50,7 +50,7 @@ import initImage from "./features/images.js";
import selectDataset from "./components/dataset/selectDataset.vue";
import referenceDataset from "./components/dataset/referenceDataset.vue";
// import $ from 'jquery.js'
import router from "./router/index.js";
// import router from "./router/index.js";
import { Message } from "element-ui";

import { i18nVue } from "./features/i18nVue.js";
@@ -5214,7 +5214,7 @@ function initTopToHome() {
$(window).scroll(function (e) {
const scrollTop = $(document).scrollTop();
const winHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
if (scrollTop > winHeight * 1.2) {
if (scrollTop > winHeight * 0.5) {
topToHomeEl.fadeIn();
} else {
topToHomeEl.fadeOut();


+ 12
- 0
web_src/vuepages/apis/modules/common.js View File

@@ -0,0 +1,12 @@
import service from '../service';

// 获取promote配置数据
export const getPromoteData = (filePathName) => {
return service({
url: '/dashboard/invitation',
method: 'get',
params: {
filename: filePathName
},
});
}

+ 10
- 0
web_src/vuepages/apis/modules/modelmanage.js View File

@@ -24,6 +24,16 @@ export const modifyModel = (data) => {
});
};

export const modifyModelStatus = (data) => {
return service({
url: `${data.repo}/modelmanage/modify_model_status`,
method: 'put',
headers: { 'Content-type': 'application/x-www-form-urlencoded' },
params: {},
data: Qs.stringify(data),
});
};

// 求模型信息
export const getModelInfoByName = (params) => {
return service({


+ 69
- 0
web_src/vuepages/apis/modules/repos.js View File

@@ -0,0 +1,69 @@
import service from '../service';

// 获取首页数据
export const getHomePageData = () => {
return service({
url: '/recommend/home',
method: 'get',
params: {},
});
}

// 获取项目广场上方tab数据 tab=preferred 项目优选|incubation 启智孵化管道|hot-paper 热门论文项目
export const getReposSquareTabData = (tab) => {
return service({
url: '/explore/repos/square/tab',
method: 'get',
params: {
type: tab
},
});
}

// 搜索项目
// q string 否 关键词
// topic string 否 标签名
// sort string 是 mostpopular 近期热门 | mostactive 近期活跃 | recentupdate 最近更新 | newest 最近创建
// moststars 点赞最多 | mostforks 派生最多 | mostdatasets 数据集最多 | mostaitasks AI任务最多 | mostmodels 模型最多
// pageSize int 是 每页大小,可选值为15 | 30 | 50
// page int 是 页码
export const getReposListData = (params) => {
return service({
url: '/explore/repos/search',
method: 'get',
params: {
q: params.q || '',
topic: params.topic || '',
sort: params.sort || 'mostpopular',
pageSize: params.pageSize || 15,
page: params.page || 1,
},
});
}

// 获取活跃用户列表
export const getActiveUsers = () => {
return service({
url: '/explore/repos/square/active-user',
method: 'get',
params: {},
});
}

// 关注用户
export const followingUsers = (userName, isFollowing) => {
return service({
url: `/api/v1/user/following/${userName}`,
method: isFollowing ? 'put' : 'delete',
params: {},
});
}

// 获取活跃组织列表
export const getActiveOrgs = () => {
return service({
url: '/explore/repos/square/active-org',
method: 'get',
params: {},
});
}

+ 71
- 15
web_src/vuepages/langs/config/en-US.js View File

@@ -177,21 +177,21 @@ const en = {
Activated: 'Activated',
notActive: 'Not active',
},
tranformImageFailed:'Picture desensitization failed',
originPicture:'Origin picture',
desensitizationPicture:'Desensitization picture',
desensitizationObject:'Desensitization object',
example:'Example',
startDesensitization:'Start desensitization',
all:'All',
onlyFace:'Only face',
onlyLicensePlate:'Only license plate',
dragThePictureHere:'Drag the picture here',
or:' or ',
clickUpload:'Click upload',
dataDesensitizationModelExperience:'Data desensitization model experience',
dataDesensitizationModelDesc:'Use AI technology to desensitize the face and license plate number in the picture. For more information about this model, please visit the project',
limitFilesUpload:'Only jpg/jpeg/png files can be uploaded',
tranformImageFailed: 'Picture desensitization failed',
originPicture: 'Origin picture',
desensitizationPicture: 'Desensitization picture',
desensitizationObject: 'Desensitization object',
example: 'Example',
startDesensitization: 'Start desensitization',
all: 'All',
onlyFace: 'Only face',
onlyLicensePlate: 'Only license plate',
dragThePictureHere: 'Drag the picture here',
or: ' or ',
clickUpload: 'Click upload',
dataDesensitizationModelExperience: 'Data desensitization model experience',
dataDesensitizationModelDesc: 'Use AI technology to desensitize the face and license plate number in the picture. For more information about this model, please visit the project',
limitFilesUpload: 'Only jpg/jpeg/png files can be uploaded',
limitSizeUpload: 'The size of the uploaded file cannot exceed 20M!',
notebook: {
createNewNotebook: "Create new notebook debug task",
@@ -281,6 +281,62 @@ const en = {
infoModificationFailed: 'Information modify failed',
deleteModelFileConfirmTips: 'Are you sure you want to delete the current model file?',
modelFileDeleteFailed: 'Model file delete failed',
modelAccess:'Model Access',
modelAccessPublic:'Public',
modelAccessPrivate:'Private',
},
repos: {
activeOrganization: 'Active Organization',
activeUsers: 'Active Users',
follow: 'Follow',
unFollow: 'Unfollow',
selectedFields: 'Recommend Repositories',
mostPopular: 'Most Popular',
mostActive: 'Most Active',
newest: 'Newest',
recentlyUpdated: 'Recently Updated',
mostStars: 'Most Stars',
mostForks: 'Most Forks',
mostDatasets: 'Most Datasets',
mostAiTasks: 'Most AI Tasks',
mostModels: 'Most Models',
dataset: 'Datasets',
model: 'Models',
aiTask: 'AI Tasks',
updated: 'Updated',
contributors: 'Contributors',
searchRepositories: 'Search Repositories',
search: 'Search',
allFields: 'All Fields',
preferred: 'Preferred',
openIIncubation: 'OpenI Incubation',
hotPapers: 'Hot Papers',
watch: 'Watch',
star: 'Star',
fork: 'Fork',
noReposfound: 'No matching repositories found.',
},
timeObj: {
ago: '{msg} ago',
from_now: '{msg} from now',
now: 'now',
future: 'future',
'1s': '1 second',
'1m': '1 minute',
'1h': '1 hour',
'1d': '1 day',
'1w': '1 week',
'1mon': '1 month',
'1y': '1 year',
seconds: '{msg} seconds',
minutes: '{msg} minutes',
hours: '{msg} hours',
days: '{msg} days',
weeks: '{msg} weeks',
months: '{msg} months',
years: '{msg} years',
raw_seconds: 'seconds',
raw_minutes: 'minutes',
},
}



+ 74
- 4
web_src/vuepages/langs/config/zh-CN.js View File

@@ -220,8 +220,24 @@ const zh = {
graphicMemory: "显存",
memory: "内存",
sharedMemory: "共享内存",
tips:'本次新建的调试任务会放在您名下项目openi-notebook中,如果没有该项目系统会自动新建一个。'
tips: '本次新建的调试任务会放在您名下项目openi-notebook中,如果没有该项目系统会自动新建一个。'
},
tranformImageFailed: '图片脱敏失败',
originPicture: '原始图片',
desensitizationPicture: '脱敏图片',
desensitizationObject: '脱敏对象',
example: '示例',
startDesensitization: '开始处理',
all: '全部',
onlyFace: '仅人脸',
onlyLicensePlate: '仅车牌',
dragThePictureHere: '拖动图片到这里',
or: '或',
clickUpload: '点击上传',
dataDesensitizationModelExperience: '数据脱敏模型体验',
dataDesensitizationModelDesc: '利用人工智能AI技术,把图片中的人脸、车牌号码进行脱敏处理。该模型更多信息请访问项目',
limitFilesUpload: '只能上传 jpg/jpeg/png 格式的文件',
limitSizeUpload: '上传文件大小不能超过 20M !',
modelManage: {
modelManage: '模型管理',
modelName: '模型名称',
@@ -282,9 +298,63 @@ const zh = {
infoModificationFailed: '信息修改失败',
deleteModelFileConfirmTips: '请确认是否删除当前模型文件?',
modelFileDeleteFailed: '模型文件删除失败',
modelAccess:'模型权限',
modelAccessPublic:'公开',
modelAccessPrivate:'私有',
},
};


repos: {
activeOrganization: '活跃组织',
activeUsers: '活跃用户',
follow: '关注',
unFollow: '取消关注',
selectedFields: '领域精选',
mostPopular: '近期热门',
mostActive: '近期活跃',
newest: '最近创建',
recentlyUpdated: '最近更新',
mostStars: '点赞最多',
mostForks: '派生最多',
mostDatasets: '数据集最多',
mostAiTasks: 'AI任务最多',
mostModels: '模型最多',
dataset: '数据集',
model: '模型',
aiTask: 'AI任务',
updated: '最后更新于',
contributors: '贡献者',
searchRepositories: '搜项目',
search: '搜索',
allFields: '全部领域',
preferred: '项目优选',
openIIncubation: '启智孵化管道',
hotPapers: '热门论文项目',
watch: '关注',
star: '点赞',
fork: '派生',
noReposfound: '未找到匹配的项目。',
},
timeObj: {
ago: '{msg}前',
from_now: '{msg} 之后',
now: '现在',
future: '将来',
'1s': '1 秒',
'1m': '1 分钟',
'1h': '1 小时',
'1d': '1 天',
'1w': '1 周',
'1mon': '1 个月',
'1y': '1 年',
seconds: '{msg} 秒',
minutes: '{msg} 分钟',
hours: '{msg} 小时',
days: '{msg} 天',
weeks: '{msg} 周',
months: '{msg} 个月',
years: '{msg} 年',
raw_seconds: '秒',
raw_minutes: '分钟',
},
}

export default zh;

+ 11
- 2
web_src/vuepages/pages/modelmanage/common/modelmanage-common-detail.vue View File

@@ -51,6 +51,12 @@
</div>
</div>
</div>
<div class="row">
<div class="tit">{{ $t('modelManage.modelAccess') }}:</div>
<div class="val">
<div class="txt-wrap">{{ state.isPrivate }}</div>
</div>
</div>
</div>
<div class="area">
<div class="row">
@@ -176,8 +182,8 @@
<span>{{ scope.row.FileName }}</span>
</div>
</a>
<a v-else :class="!canOperate ? 'disabled-download' : ''"
:href="canOperate ? `${repo}/modelmanage/${state.id}/downloadsingle?parentDir=${filePath.length > 1 ? encodeURIComponent(filePath.map(item => item.path).join('/').slice(1) + '/') : ''}&fileName=${scope.row.FileName}` : 'javascript:;'">
<a v-else :class="!canDownload ? 'disabled-download' : ''"
:href="canDownload ? `${repo}/modelmanage/${state.id}/downloadsingle?parentDir=${filePath.length > 1 ? encodeURIComponent(filePath.map(item => item.path).join('/').slice(1) + '/') : ''}&fileName=${scope.row.FileName}` : 'javascript:;'">
<div class="fitted" :title="scope.row.FileName">
<i class="icon file" width="16" height="16" aria-hidden="true"></i>
<span>{{ scope.row.FileName }}</span>
@@ -222,6 +228,7 @@ export default {
return {
modelType: '0', // 1-本地, 0-线上
canOperate: false,
canDownload:false,
canDelete: false,
isExpanded: false,
loading: false,
@@ -285,6 +292,7 @@ export default {
const data = this.modelList.filter((model) => model.version == version)[0];
this.modelType = data.modelType;
this.canOperate = data.isCanOper;
this.canDownload = data.isCanDownload;
this.canDelete = data.isCanDelete;
this.state.type = data.type;
this.state.typeStr = data.type == 0 ? 'CPU/GPU' : data.type == 1 ? 'NPU' : '';
@@ -298,6 +306,7 @@ export default {
this.state._label = data.label;
this.state.description = data.description || '--';
this.state._description = data.description;
this.state.isPrivate= (data.isPrivate == true ? this.$t('modelManage.modelAccessPrivate'):this.$t('modelManage.modelAccessPublic'));
this.state.createTime = formatDate(new Date(data.createdUnix * 1000), 'yyyy-MM-dd HH:mm:ss');

const trainTaskInfo = data.trainTaskInfo ? JSON.parse(data.trainTaskInfo) : '';


+ 45
- 1
web_src/vuepages/pages/modelmanage/local/modelmanage-local-create-1.vue View File

@@ -85,6 +85,28 @@
:placeholder="$t('modelManage.modelLabelInputTips')" @input="labelInput"></el-input>
</div>
</div>

<div class="row" v-if="repoIsPrivate==false">
<div class="r-title"><label>{{ $t('modelManage.modelAccess') }}</label></div>
<div class="field">
<div class="ui radio checkbox">
<input id="isPrivate_false" type="radio" name="isPrivate" checked="checked" value="false">
<label>{{ $t('modelManage.modelAccessPublic') }}</label>
</div>
</div>
<div class="field">
<label>&nbsp;&nbsp;</label>
</div>
<div class="field">
<div class="ui radio checkbox">
<input id="isPrivate_true" type="radio" name="isPrivate" value="true">
<label>{{ $t('modelManage.modelAccessPrivate') }}</label>
</div>
</div>
</div>

<div class="row" style="align-items:flex-start;">
<div class="r-title"><label>{{ $t('modelManage.modelDescr') }}</label></div>
<div class="r-content">
@@ -93,6 +115,7 @@
</el-input>
</div>
</div>

<div class="row" style="margin-top:20px">
<div class="r-title"><label></label></div>
<div class="r-content">
@@ -116,7 +139,7 @@ import { MODEL_ENGINES } from '~/const'

const REPO_NAME = location.pathname.split('/')[2];
const MAX_LABEL_COUNT = 5;
const REPOISPRIVATE = window.REPO_IS_PRIVATE;
export default {
data() {
return {
@@ -129,10 +152,12 @@ export default {
engine: '0',
label: '',
description: '',
isPrivate : false,
},
nameErr: false,
isShowVersion: false,
engineList: MODEL_ENGINES,
repoIsPrivate: REPOISPRIVATE,
};
},
components: {},
@@ -145,6 +170,7 @@ export default {
const hasEndSpace = this.state.label[this.state.label.length - 1] == ' ';
const list = this.state.label.trim().split(' ').filter(label => label != '');
this.state.label = list.slice(0, MAX_LABEL_COUNT).join(' ') + (hasEndSpace && list.length < MAX_LABEL_COUNT ? ' ' : '');
},
submit() {
if (!this.checkName()) {
@@ -154,6 +180,16 @@ export default {
// });
return;
}
var radio = document.getElementsByName("isPrivate");
if(radio != null && radio.length > 0){
for (var i=0; i<radio.length; i++) {
if (radio[i].checked) {
this.state.isPrivate=radio[i].value;
}
}
}else{
this.state.isPrivate = true;
}
const submintApi = this.type == '1' ? modifyModel : saveLocalModel;
submintApi({
repo: location.pathname.split('/').slice(0, 3).join('/'),
@@ -220,6 +256,14 @@ export default {
this.state.engine = data.engine.toString();
this.state.label = data.label;
this.state.description = data.description;
this.state.isPrivate = data.isPrivate;
if(data.isPrivate){
$('#isPrivate_true').attr("checked",true);
$('#isPrivate_false').attr("checked",false);
}else{
$('#isPrivate_true').attr("checked",false);
$('#isPrivate_false').attr("checked",true);
}
}
}).catch(err => {
this.loading = false;


+ 123
- 0
web_src/vuepages/pages/repos/components/ActiveOrgs.vue View File

@@ -0,0 +1,123 @@
<template>
<div>
<div class="container">
<div class="title">
<i style="margin-left:10px;margin-right:8px;font-size:20px;" class="ri-blaze-line"></i>
<span>{{ $t('repos.activeOrganization') }}</span>
</div>
<div class="content">
<div class="item" v-for="(item, index) in list" :key="index">
<div class="item-l">
<a class="name" :href="`/${item.Name}`" :title="item.Name">
<img class="avatar" :src="item.RelAvatarLink">
<div class="name-c"><span>{{ item.Name }}</span></div>
</a>
</div>
<div class="item-r">
<i class="ri-user-2-line" style="color:rgb(250, 140, 22);margin-right:4px;"></i>
<span>{{ item.NumMembers }}</span>
</div>
</div>
</div>
</div>
</div>
</template>

<script>
import { getActiveOrgs } from '~/apis/modules/repos';

export default {
name: "ActiveOrgs",
props: {},
components: {},
data() {
return {
list: [],
};
},
methods: {},
mounted() {
getActiveOrgs().then(res => {
res = res.data;
if (res.Code == 0) {
this.list = res.Data.Orgs || [];
} else {
this.list = [];
}
}).catch(err => {
console.log(err);
this.list = [];
});
},
};
</script>

<style scoped lang="less">
.title {
height: 43px;
border-color: rgba(16, 16, 16, 0.05);
border-width: 1px 0px;
border-style: solid;
display: flex;
align-items: center;
font-size: 18px;
color: rgba(47, 9, 69, 0.74);
background: url("data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20version%3D%221.1%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%221%22%20x1%3D%220%22%20x2%3D%221%22%20y1%3D%220%22%20y2%3D%220%22%20gradientTransform%3D%22matrix(-1.007%2C%200.0010000000000001494%2C%20-0.000018400023883213844%2C%20-1.007%2C%201.003%2C%200.008)%22%3E%3Cstop%20stop-color%3D%22%23eee9da%22%20stop-opacity%3D%220%22%20offset%3D%220%22%3E%3C%2Fstop%3E%3Cstop%20stop-color%3D%22%23f3e7f7%22%20stop-opacity%3D%220.26%22%20offset%3D%220.29%22%3E%3C%2Fstop%3E%3Cstop%20stop-color%3D%22%23d0e7ff%22%20stop-opacity%3D%220.3%22%20offset%3D%221%22%3E%3C%2Fstop%3E%3C%2FlinearGradient%3E%3C%2Fdefs%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22url(%231)%22%3E%3C%2Frect%3E%3C%2Fsvg%3E");
}

.content {
padding: 6px 0;
}

.item {
display: flex;
align-items: center;
height: 48px;
padding: 0 8px;
font-size: 14px;
color: rgba(16, 16, 16, 1);
}

.item>div {
display: flex;
align-items: center;
}

.item-l {
flex: 1;
overflow: hidden;
a {
display: flex;
align-items: center;
overflow: hidden;
}
}

.item-r {
width: 80px;
justify-content: flex-end;
}

.item .avatar {
width: 32px;
height: 32px;
border-radius: 50%;
margin-right: 10px;
}

.item .name-c {
flex: 1;
overflow: hidden;
width: 100%;
text-overflow: ellipsis;
white-space: nowrap;
}

.item .name {
color: rgba(16, 16, 16, 1);

&:hover {
opacity: 0.8;
}
}
</style>

+ 156
- 0
web_src/vuepages/pages/repos/components/ActiveUsers.vue View File

@@ -0,0 +1,156 @@
<template>
<div>
<div class="container">
<div class="title">
<i style="margin-left:10px;margin-right:8px;font-size:20px;" class="ri-account-pin-circle-line"></i>
<span>{{ $t('repos.activeUsers') }}</span>
</div>
<div class="content">
<div class="item" v-for="(item, index) in list" :key="index">
<div class="item-l">
<a class="name" :href="`/${item.User.Name}`" :title="(item.User.FullName || item.User.Name)">
<img class="avatar" :src="item.User.RelAvatarLink">
<div class="name-c">
{{ item.User.FullName || item.User.Name }}
</div>
</a>
</div>
<div class="item-r">
<template v-if="item.ShowButton">
<a class="op-btn" v-if="!item.Followed" href="javascript:;" @click="following(item, index, true)">
{{ $t('repos.follow') }}</a>
<a class="op-btn" v-if="item.Followed" href="javascript:;" @click="following(item, index, false)">
{{ $t('repos.unFollow') }}</a>
</template>
</div>
</div>
</div>
</div>
</div>
</template>

<script>
import { getActiveUsers, followingUsers } from '~/apis/modules/repos';

export default {
name: "ActiveUsers",
props: {},
components: {},
data() {
return {
list: [],
};
},
methods: {
following(userInfo, index, followingOrNot) {
followingUsers(userInfo.User.Name, followingOrNot).then(res => {
if (res.status == 204) { // 成功
userInfo.Followed = !userInfo.Followed;
} else {
console.log(res);
}
}).catch(err => {
if (err.response.status == 401) { // 未登陆
window.location.href = `/user/login?redirect_to=${encodeURIComponent(window.location.href)}`;
} else {
console.log(err);
}
});
}
},
mounted() {
getActiveUsers().then(res => {
res = res.data;
if (res.Code == 0) {
this.list = res.Data.Users || [];
} else {
this.list = [];
}
}).catch(err => {
console.log(err);
this.list = [];
});
},
};
</script>

<style scoped lang="less">
.title {
height: 43px;
border-color: rgba(16, 16, 16, 0.05);
border-width: 1px 0px;
border-style: solid;
display: flex;
align-items: center;
font-size: 18px;
color: rgba(47, 9, 69, 0.74);
background: url("data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20version%3D%221.1%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%221%22%20x1%3D%220%22%20x2%3D%221%22%20y1%3D%220%22%20y2%3D%220%22%20gradientTransform%3D%22matrix(-1.007%2C%200.0010000000000001494%2C%20-0.000018400023883213844%2C%20-1.007%2C%201.003%2C%200.008)%22%3E%3Cstop%20stop-color%3D%22%23eee9da%22%20stop-opacity%3D%220%22%20offset%3D%220%22%3E%3C%2Fstop%3E%3Cstop%20stop-color%3D%22%23f3e7f7%22%20stop-opacity%3D%220.26%22%20offset%3D%220.29%22%3E%3C%2Fstop%3E%3Cstop%20stop-color%3D%22%23d0e7ff%22%20stop-opacity%3D%220.3%22%20offset%3D%221%22%3E%3C%2Fstop%3E%3C%2FlinearGradient%3E%3C%2Fdefs%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22url(%231)%22%3E%3C%2Frect%3E%3C%2Fsvg%3E");
}

.content {
padding: 6px 0;
}

.item {
display: flex;
align-items: center;
height: 48px;
padding: 0 8px;
font-size: 14px;
color: rgba(16, 16, 16, 1);
}

.item>div {
display: flex;
align-items: center;
}

.item-l {
flex: 1;
overflow: hidden;

a {
display: flex;
align-items: center;
overflow: hidden;
}
}

.item-r {
width: 80px;
justify-content: flex-end;
}

.item .avatar {
width: 32px;
height: 32px;
border-radius: 50%;
margin-right: 10px;
}

.item .name-c {
flex: 1;
overflow: hidden;
width: 100%;
text-overflow: ellipsis;
white-space: nowrap;
}

.item .name {
color: rgba(16, 16, 16, 1);

&:hover {
opacity: 0.8;
}
}

.item .op-btn {
border-color: rgb(50, 145, 248);
border-width: 1px;
border-style: solid;
color: rgb(50, 145, 248);
font-size: 12px;
padding: 0px 6px;
border-radius: 3px;
}
</style>

+ 151
- 0
web_src/vuepages/pages/repos/components/RecommendRepos.vue View File

@@ -0,0 +1,151 @@
<template>
<div class="repo-selected-bg">
<div class="ui container _repo_container _repo-selected-container" style="padding-top:3rem;padding-bottom:3rem;">
<div class="_repo_title"><span>{{ $t('repos.selectedFields') }}</span></div>
<div class="_repo-selected-list">
<div class="swiper-wrapper" id="_repo-selected"></div>
<div class="swiper-pagination _repo-selected-swiper-pagination"></div>
</div>
</div>
</div>
</template>

<script>
import { getHomePageData } from '~/apis/modules/repos';
import LetterAvatar from '~/utils/letteravatar';

export default {
name: "RecommendRepos",
props: {
static: { type: Boolean, default: false },
staticSwiperData: { type: Array, default: () => [] },
},
components: {},
data() {
return {
swiperHandler: null,
};
},
methods: {
renderRepos(json) {
var selectedRepoEl = document.getElementById("_repo-selected");
var html = "";
if (json != null && json.length > 0) {
var repoMap = {};
for (var i = 0, iLen = json.length; i < iLen; i++) {
var repo = json[i];
var label = repo.Label;
if (repoMap[label]) {
repoMap[label].push(repo);
} else {
repoMap[label] = [repo];
}
}
for (var label in repoMap) {
var repos = repoMap[label];
var labelSearch = repos[0].Label;
html += `<div class="swiper-slide"><div><a style="color:rgb(50, 145, 248);font-size:16px;font-weight:550;" href="/explore/repos?q=&topic=${labelSearch}&sort=hot"># ${label}</a></div>`;
for (var i = 0, iLen = repos.length; i < iLen; i++) {
if (i >= 4) break;
var repo = repos[i];
html += `<div class="ui fluid card">
<div class="content">
${repo["Avatar"] ? `<img style="border-radius: 100%;" class="left floated mini ui image" src="${repo["Avatar"]}">` : `<img style="border-radius: 100%;" class="left floated mini ui image" avatar="${repo["OwnerName"]}">`}
<span class="header nowrap" style="color:rgb(50, 145, 248);font-size:14px;" href="javascript:;" title="${repo["Alias"]}">${repo["Alias"]}</span>
<div class="description nowrap-2" style="rgba(136,136,136,1);;font-size:12px;" title="${repo["Description"]}">${repo["Description"]}</div>
</div>
<a style="position:absolute;height:100%;width:100%;" href="/${repo["OwnerName"]}/${repo["Name"]}"></a>
</div>`;
}
html += '</div>'
}
this.swiperHandler = new Swiper("._repo-selected-list", {
slidesPerView: 1,
spaceBetween: 25,
pagination: {
el: "._repo-selected-swiper-pagination",
clickable: true,
},
autoplay: {
delay: 4500,
disableOnInteraction: false,
},
breakpoints: {
768: {
slidesPerView: 3,
},
1024: {
slidesPerView: 4,
},
1200: {
slidesPerView: 4,
},
1600: {
slidesPerView: 4,
}
},
});
selectedRepoEl.innerHTML = html;
this.swiperHandler.updateSlides();
this.swiperHandler.updateProgress();
LetterAvatar.transform();
}
}
},
mounted() {
if (this.static) {
this.renderRepos(this.staticSwiperData);
} else {
getHomePageData().then(res => {
this.renderRepos(res.data.repo);
}).catch(err => {
console.log(err);
});
}
},
};
</script>

<style scoped lang="less">
.repo-selected-bg {
background: url("data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20version%3D%221.1%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%221%22%20x1%3D%220%22%20x2%3D%221%22%20y1%3D%220%22%20y2%3D%220%22%20gradientTransform%3D%22matrix(0.11899999999999993%2C%201.217%2C%20-0.24039506172839506%2C%200.11899999999999993%2C%200.269%2C%20-0.22)%22%3E%3Cstop%20stop-color%3D%22%23ffffff%22%20stop-opacity%3D%220.47%22%20offset%3D%220%22%3E%3C%2Fstop%3E%3Cstop%20stop-color%3D%22%23e5e7eb%22%20stop-opacity%3D%220.3%22%20offset%3D%221%22%3E%3C%2Fstop%3E%3C%2FlinearGradient%3E%3C%2Fdefs%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22url(%231)%22%3E%3C%2Frect%3E%3C%2Fsvg%3E");
}

._repo_title {
font-size: 18px;
color: rgb(16, 16, 16);
text-align: center;
margin-bottom: 1em;
font-weight: bold;
}

._repo-selected-list {
overflow: hidden;
padding: 1em 1em 3em 1em;
text-align: left;
position: relative;
}

/deep/._repo-selected-swiper-pagination .swiper-pagination-bullet {
width: 8px;
height: 8px;
border-radius: 100%;
background: #76cbed;
}

/deep/._repo-selected-swiper-pagination .swiper-pagination-bullet-active {
width: 40px;
border-radius: 4px;
}

/deep/ ._repo-selected-list .card {
border-radius: 6px;
background-color: #FFF;
box-shadow: 0px 5px 10px 0px rgba(105, 192, 255, .3);
border: 1px solid rgba(105, 192, 255, .4);
}

/deep/ ._repo-selected-list .header {
line-height: 40px !important;
}
</style>

+ 115
- 0
web_src/vuepages/pages/repos/components/ReposFilters.vue View File

@@ -0,0 +1,115 @@
<template>
<div>
<div class="item" :class="(focusIndex == index) ? 'item-focus' : ''" v-for="(item, index) in list" :key="item.key">
<a href="javascript:;" @click="changeFilters(item, index)">{{ item.label }}</a>
</div>
</div>
</template>

<script>
export default {
name: "ReposFilters",
props: {
defaultsort: { type: String, default: 'mostpopular' },
},
components: {},
data() {
return {
focusIndex: 0,
list: [{
key: 'mostpopular',
label: this.$t('repos.mostPopular'),
}, {
key: 'mostactive',
label: this.$t('repos.mostActive'),
}, {
key: 'recentupdate',
label: this.$t('repos.recentlyUpdated'),
}, {
key: 'newest',
label: this.$t('repos.newest'),
}, {
key: 'moststars',
label: this.$t('repos.mostStars'),
}, {
key: 'mostforks',
label: this.$t('repos.mostForks'),
}, {
key: 'mostdatasets',
label: this.$t('repos.mostDatasets'),
}, {
key: 'mostaitasks',
label: this.$t('repos.mostAiTasks'),
}, {
key: 'mostmodels',
label: this.$t('repos.mostModels'),
}]
};
},
methods: {
changeFilters(item, index) {
this.focusIndex = index;
this.$emit('change', this.list[this.focusIndex]);
},
setDefaultFilter(sort) {
const index = this.list.findIndex((item) => item.key == sort);
this.focusIndex = index >= 0 ? index : 0;
}
},
mounted() {
},
};
</script>

<style scoped lang="less">
.item {
height: 40px;
border-color: rgba(16, 16, 16, 0.05);
border-width: 0px 0px 1px;
border-style: solid;
color: rgba(16, 16, 16, 0.8);
font-size: 14px;
padding: 0px;
display: flex;
align-items: center;
justify-content: flex-end;
position: relative;
}

.item a {
color: inherit;
height: 100%;
display: flex;
align-items: center;

&:hover {
opacity: 0.8;
}
}

.item-focus {
font-weight: bold;
border-color: rgba(0, 108, 205, 0.3);
color: rgb(50, 145, 248);

a {
cursor: default;

&:hover {
opacity: 1;
}
}

}

.item-focus:before {
content: "";
position: absolute;
width: 7px;
height: 7px;
bottom: -4px;
background: rgb(178, 211, 240);
left: 0;
border-radius: 100%;
}
</style>

+ 316
- 0
web_src/vuepages/pages/repos/components/ReposItem.vue View File

@@ -0,0 +1,316 @@
<template>
<div>
<div class="item">
<div class="item-top">
<img v-if="data.RelAvatarLink" class="avatar" :src="data.RelAvatarLink" />
<img v-else class="avatar" :avatar="data.OwnerName" />
<div class="content">
<div class="title">
<div class="title-l">
<a :href="`/${data.OwnerName}/${data.Name}`" :title="`${data.OwnerName}/${data.Name}`">
<span class="title-1">{{ data.OwnerName }}</span>
<span class="title-1"> / </span>
<span class="title-2" v-html="data.NameShow"></span>
</a>
<i v-if="data.IsArchived" class="archive icon archived-icon"></i>
<svg v-if="data.IsFork" class="svg octicon-repo-forked" width="15" height="15" aria-hidden="true">
<use xlink:href="#octicon-repo-forked"></use>
</svg>
<svg v-if="data.IsMirror" class="svg octicon-repo-clone" width="15" height="15" aria-hidden="true">
<use xlink:href="#octicon-repo-clone"></use>
</svg>
<svg v-if="(data.IsPrivate || data.IsOwnerPrivate)" style="color:#a1882b!important" class="svg octicon-lock" width="15" height="15"
aria-hidden="true">
<use xlink:href="#octicon-lock"></use>
</svg>
</div>
<span class="title-r">
<span class="t-item" :title="$t('repos.watch')">
<i class="ri-eye-line"></i>
<span>{{ data.NumWatches }}</span>
</span>
<span class="t-item" :title="$t('repos.star')">
<i class="ri-star-line"></i>
<span>{{ data.NumStars }}</span>
</span>
<span class="t-item" :title="$t('repos.fork')">
<svg class="svg octicon-repo-forked" width="13" height="13" aria-hidden="true">
<use xlink:href="#octicon-repo-forked"></use>
</svg>
<span>{{ data.NumForks }}</span></span>
</span>
</div>
<div class="descr" v-show="data.DescriptionShow" v-html="data.DescriptionShow"></div>
<div class="tags" v-show="data.Topics && data.Topics.length">
<a v-for="(item, index) in data.TopicsShow" :key="index" class="tag"
:class="(item.topic.toLocaleLowerCase() == topic.toLocaleLowerCase() ? 'tag-focus' : '')"
:href="`/explore/repos?q=&topic=${item.topic}&sort=hot`" v-html="item.topicShow"></a>
</div>
<div class="repo-datas" v-show="(data.DatasetCnt > 0) || (data.ModelCnt > 0) || (data.AiTaskCnt > 0)">
<span class="repo-datas-item" v-show="(data.DatasetCnt > 0)">
<i class="ri-stack-line"></i>
<span class="label">{{ $t('repos.dataset') }}:</span>
<span class="value">{{ data.DatasetCnt }}</span>
</span>
<span class="repo-datas-item" v-show="(data.ModelCnt > 0)">
<i class="ri-send-plane-2-line"></i>
<span class="label">{{ $t('repos.model') }}:</span>
<span class="value">{{ data.ModelCnt }}</span>
</span>
<span class="repo-datas-item" v-show="(data.AiTaskCnt > 0)">
<i class="ri-order-play-line"></i>
<span class="label">{{ $t('repos.aiTask') }}:</span>
<span class="value">{{ data.AiTaskCnt }}</span>
</span>
</div>
</div>
</div>
<div class="item-bottom">
<div>
<span>{{ $t('repos.updated') }}</span>
<el-tooltip effect="dark" :content="dateFormat(data.UpdatedUnix)" placement="top-start">
<span>{{ calcFromNow(data.UpdatedUnix) }}</span>
</el-tooltip>
<span style="margin-left:8px;" v-if="data.PrimaryLanguage"><i class="color-icon"
:style="{ backgroundColor: data.PrimaryLanguage.Color }"></i>{{ data.PrimaryLanguage.Language }}</span>
</div>
<div class="contributors">
<span class="contributors-count" v-show="data.Contributors && data.Contributors.length">
{{ $t('repos.contributors') }}&nbsp;
</span>
<span class="contributors-avatar">
<a :href="item.UserName ? `/${item.UserName}` : `mailto:${item.Email}`" class="avatar-c"
v-for="(item, index) in data.Contributors" :key="index">
<img class="avatar" v-show="item.UserName" :src="item.RelAvatarLink">
<span class="avatar" v-show="!item.UserName" :style="{ backgroundColor: item.bgColor }">
{{ item.Email[0].toLocaleUpperCase() }}</span>
</a>
</span>
</div>
</div>
</div>
</div>
</template>

<script>
import relativeTime from 'dayjs/plugin/relativeTime';
import localizedFormat from 'dayjs/plugin/localizedFormat';
import 'dayjs/locale/zh-cn';
import 'dayjs/locale/en';
import dayjs from 'dayjs';
import { lang } from '~/langs';
import { timeSinceUnix } from '~/utils';

dayjs.locale(lang == 'zh-CN' ? 'zh-cn' : 'en');
dayjs.extend(relativeTime);
dayjs.extend(localizedFormat);

export default {
name: "ReposItem",
props: {
data: { type: Object, default: () => ({}) },
topic: { type: String, default: '' }
},
components: {},
data() {
return {
contributors: [],
};
},
methods: {
calcFromNow(unix) {
// return dayjs(unix * 1000).fromNow();
return timeSinceUnix(unix, Date.now() / 1000);
},
dateFormat(unix) {
return lang == 'zh-CN' ? dayjs(unix * 1000).format('YYYY年MM月DD日 HH时mm分ss秒') :
dayjs(unix * 1000).format('ddd, D MMM YYYY HH:mm:ss [CST]');
}
},
mounted() { },
};
</script>

<style scoped lang="less">
.item {
width: 100%;
border-color: rgba(157, 197, 226, 0.4);
border-width: 1px;
border-style: solid;
box-shadow: rgb(157 197 226 / 20%) 0px 5px 10px 0px;
border-radius: 15px;
font-size: 14px;
padding: 20px 26px 10px 26px;
margin-bottom: 40px;
}

.item-top {
display: flex;
}

.item-top .avatar {
width: 38px;
height: 38px;
margin-right: 10px;
border-radius: 100%;
}

.content {
flex: 1;
overflow: hidden;
}

.content .title {
display: flex;
align-items: center;
height: 30px;
margin: 4px 0 8px;
}

.content .title-l {
flex: 1;
overflow: hidden;
width: 100%;
text-overflow: ellipsis;
white-space: nowrap;
}

.content .title-1 {
font-size: 18px;
color: rgba(16, 16, 16, 0.6);
}

.content .title-2 {
font-size: 18px;
color: rgba(16, 16, 16, 1);
font-weight: bold;
margin-right: 3px;
}

.content .title-r {
display: flex;
align-items: center;
font-weight: 400;
font-size: 12px;
color: rgba(26, 40, 51, 1);
justify-content: flex-end;
}

.content .t-item {
margin-left: 12px;
display: flex;
align-items: center;
}

.content .t-item i {
margin-right: 4px;
}

.content .descr {
font-weight: 300;
font-size: 14px;
color: rgba(16, 16, 16, 0.8);
margin-bottom: 16px;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 6;
max-height: 120px;
white-space: break-spaces;
}

.content .tags {
margin-bottom: 16px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}

.content .tag {
color: rgba(16, 16, 16, 0.8);
border-radius: 4px;
font-size: 14px;
background: rgba(232, 232, 232, 0.6);
padding: 2px 6px;
margin-right: 8px;

&.tag-focus {
color: red;
}
}

.content .repo-datas {
display: flex;
align-items: center;
margin-top: 20px;
margin-bottom: 10px;
}

.content .repo-datas-item {
display: flex;
align-items: center;
margin-right: 24px;
}

.content .repo-datas-item i {
color: rgba(2, 107, 251, 0.54);
margin-right: 4px;
font-size: 16px;
}

.content .repo-datas-item .label {
color: rgba(2, 107, 251, 0.54);
margin-right: 4px;
}

.content .repo-datas-item .value {
font-weight: bold;
}

.item-bottom {
display: flex;
align-items: center;
justify-content: space-between;
border-top: 1px solid rgba(157, 197, 226, 0.2);
margin-top: 10px;
padding-top: 10px;
font-size: 12px;
color: rgba(16, 16, 16, 0.6);
}

.item-bottom .contributors {
display: flex;
align-items: center;
}

.item-bottom .contributors-avatar {
display: flex;
align-items: center;
margin-left: 16px;

.avatar-c {

img[src=""],
img:not([src]) {
// opacity: 0;
}
}
}

.item-bottom .avatar {
display: block;
width: 25px;
height: 25px;
margin-left: -6px;
border-radius: 100%;
border: 1px solid white;
font-size: 16px;
line-height: 24px;
text-align: center;
color: white;
background-color: white;
font-weight: bold;
}
</style>

+ 161
- 0
web_src/vuepages/pages/repos/components/ReposList.vue View File

@@ -0,0 +1,161 @@
<template>
<div class="list-container">
<div style="min-height:540px;" v-loading="loading">
<div class="repos-item-container" v-for="(item, index) in list" :key="item.ID">
<ReposItem :data="item" :topic="topic"></ReposItem>
</div>
<div v-show="(!list.length && !loading)" class="repos-no-data">{{ $t('repos.noReposfound') }}</div>
</div>
<div class="center">
<el-pagination ref="paginationRef" background @current-change="currentChange" @size-change="sizeChange"
:current-page.sync="iPage" :page-sizes="iPageSizes" :page-size.sync="iPageSize"
layout="total, sizes, prev, pager, next, jumper" :total="total">
</el-pagination>
</div>
</div>
</template>

<script>
import ReposItem from '../components/ReposItem.vue';
import { getReposListData } from '~/apis/modules/repos';
import LetterAvatar from '~/utils/letteravatar';

export default {
name: "ReposList",
props: {
q: { type: String, default: '' },
sort: { type: String, default: 'mostpopular' },
topic: { type: String, default: '' },
page: { type: Number, default: 1 },
pageSize: { type: Number, default: 15 },
pageSizes: { type: Array, default: () => [15, 30, 50] }
},
components: { ReposItem },
data() {
return {
loading: false,
list: [],
iPageSizes: [15, 30, 50],
iPageSize: 15,
iPage: 1,
total: 0,
};
},
methods: {
getListData() {
this.loading = true;
getReposListData({
q: this.q || '',
topic: this.topic || '',
sort: this.sort || 'mostpopular',
pageSize: this.iPageSize || 15,
page: this.iPage || 1,
}).then(res => {
res = res.data;
this.loading = false;
if (res.Code == 0) {
const list = res.Data.Repos || [];
this.list = list.map((item) => {
item.Contributors = (item.Contributors || []).map((_item) => {
return {
..._item,
bgColor: this.randomColor(_item.Email[0].toLocaleUpperCase()),
}
});
const contributors = item.Contributors || [];
return {
...item,
NameShow: this.handlerSearchStr(item.Alias, this.q),
DescriptionShow: this.handlerSearchStr(item.Description, this.q),
TopicsShow: (item.Topics || []).map((_item) => {
return {
topic: _item,
topicShow: this.handlerSearchStr(_item, this.q)
}
}),
}
});
this.total = res.Data.Total;
this.iPage = this.iPage;
this.iPageSize = this.iPageSize;
this.$nextTick(() => {
LetterAvatar.transform();
});
} else {
this.list = [];
this.total = 0;
this.iPage = this.iPage;
this.iPageSize = this.iPageSize;
}
}).catch(err => {
console.log(err);
this.loading = false;
this.list = [];
this.total = 0;
this.iPage = this.iPage;
this.iPageSize = this.iPageSize;
});
},
search() {
this.getListData();
},
currentChange(page) {
this.iPage = page;
this.$emit('current-change', {
page: this.iPage,
pageSize: this.iPageSize,
});
},
sizeChange(pageSize) {
this.iPageSize = pageSize;
this.$emit('size-change', {
page: this.iPage,
pageSize: this.iPageSize,
});
},
handlerSearchStr(oStr, searchKey) {
if (!searchKey) return oStr;
return oStr.replace(new RegExp(`(${searchKey})`, 'ig'), `<font color="red">$1</font>`);
},
randomColor(t) {
const tIndex = t.charCodeAt(0);
const colorList = ["#1abc9c", "#2ecc71", "#3498db", "#9b59b6", "#34495e", "#16a085", "#27ae60", "#2980b9", "#8e44ad",
"#2c3e50", "#f1c40f", "#e67e22", "#e74c3c", "#00bcd4", "#95a5a6", "#f39c12", "#d35400", "#c0392b", "#bdc3c7", "#7f8c8d"];
return colorList[tIndex % colorList.length];
}
},
watch: {
page: {
handler(val) {
this.iPage = val;
},
immediate: true,
},
pageSize: {
handler(val) {
this.iPageSize = val;
},
immediate: true,
}
},
mounted() { },
};
</script>

<style scoped lang="less">
.list-container {
margin-left: 12px;
margin-right: 12px;
}

.center {
text-align: center;
}

.repos-no-data {
height: 60px;
display: flex;
justify-content: center;
align-items: center;
}
</style>

+ 255
- 0
web_src/vuepages/pages/repos/components/SearchBar.vue View File

@@ -0,0 +1,255 @@
<template>
<div>
<div class="_repo_search">
<div class="_repo_search_input_c">
<div class="_repo_search_input">
<input type="text" v-model="searchInputValue" :placeholder="$t('repos.searchRepositories')" autocomplete="off"
@keyup.enter="search" />
</div>
<div class="_repo_search_btn" @click="search">
<svg xmlns="http://www.w3.org/2000/svg"
class="styles__StyledSVGIconPathComponent-sc-16fsqc8-0 kdvdTY svg-icon-path-icon fill" viewBox="0 0 32 32"
width="24" height="24">
<defs data-reactroot="">
<linearGradient id="ilac9fwnydq3lcx1,1,rs,1,f0dwf0xj,ezu9f0dw,f000,00lwrsktrs,rs1bhv8urs" x1="0" x2="100%"
y1="0" y2="0"
gradientTransform="matrix(-0.7069999999999999, -0.707, 0.707, -0.7069999999999999, 16, 38.624)"
gradientUnits="userSpaceOnUse">
<stop stop-color="#c9ffbf" stop-opacity="1" offset="0"></stop>
<stop stop-color="#0ca451" stop-opacity="1" offset="1"></stop>
</linearGradient>
</defs>
<g>
<path fill="url(#ilac9fwnydq3lcx1,1,rs,1,f0dwf0xj,ezu9f0dw,f000,00lwrsktrs,rs1bhv8urs)"
d="M14.667 2.667c6.624 0 12 5.376 12 12s-5.376 12-12 12-12-5.376-12-12 5.376-12 12-12zM14.667 24c5.156 0 9.333-4.177 9.333-9.333 0-5.157-4.177-9.333-9.333-9.333-5.157 0-9.333 4.176-9.333 9.333 0 5.156 4.176 9.333 9.333 9.333zM25.98 24.095l3.772 3.771-1.887 1.887-3.771-3.772 1.885-1.885z">
</path>
</g>
</svg>
<span style="margin-left:10px;">{{ $t('repos.search') }}</span>
</div>
</div>
<div class="_repo_search_label_c">
<div class="_repo_search_label">
<a v-if="type == 'square'" :href="`/explore/repos?q=${searchInputValue.trim()}&topic=${item.v}&sort=${sort}`"
:style="{ backgroundColor: topicColors[index % topicColors.length] }" v-for="(item, index) in topics"
:key="index">{{ item.v }}</a>
<a v-if="type == 'search'" href="javascript:;" @click="changeTopic({ k: '', v: '' })"
style="font-weight:bold;"
:style="{ backgroundColor: selectTopic == '' ? selectedColor : defaultColor, color: selectTopic == '' ? 'white' : '#40485b' }">{{
$t('repos.allFields')
}}</a>
<a v-if="type == 'search'" href="javascript:;" @click="changeTopic(item)"
:style="{ backgroundColor: selectTopic.toLocaleLowerCase() == item.k ? selectedColor : defaultColor, color: selectTopic.toLocaleLowerCase() == item.k ? 'white' : '#40485b' }"
v-for="(item, index) in topics" :key="index">{{ item.v }}</a>
</div>
</div>
</div>
</div>
</template>

<script>
import { getPromoteData } from '~/apis/modules/common';
const COLOR_LIST = [
'rgb(255, 104, 104)',
'rgb(22, 132, 252)',
'rgb(2, 202, 253)',
'rgb(164, 145, 215)',
'rgb(232, 64, 247)',
'rgb(245, 182, 110)',
'rgb(54, 187, 166)',
'rgb(123, 50, 178)'
];
export default {
name: "SearchBar",
props: {
type: { type: String, default: 'square' }, // square|search
searchValue: { type: String, default: '' },
topic: { type: String, default: '' },
sort: { type: String, default: '' },
static: { type: Boolean, default: false },
staticTopicsData: { type: String, default: '[]' },
},
components: {},
data() {
return {
searchInputValue: '',
topicColors: COLOR_LIST,
defaultColor: '#F6F6F6',
selectedColor: '',
selectTopic: '',
topicOri: [],
topics: [],
};
},
methods: {
setDefaultSearch(params) {
this.searchInputValue = params.q || '';
this.selectTopic = params.topic || '';
this.selectTopic && this.changeTopic({
k: this.selectTopic.toLocaleLowerCase(),
v: this.selectTopic,
}, true);
},
changeTopic(topicItem, noSearch) {
const index_ori = this.topicOri.findIndex((item) => {
return item.k == this.selectTopic.toLocaleLowerCase();
});
if (index_ori < 0 && this.selectTopic) {
const index = this.topics.findIndex((item) => {
return item.k == this.selectTopic.toLocaleLowerCase();
});
if (index > -1) {
this.topics.splice(index, 1);
}
}
this.selectTopic = topicItem.v;
if (this.selectTopic && this.topics.indexOf(this.selectTopic) < 0) {
const index = this.topics.findIndex(item => {
return item.k == this.selectTopic.toLocaleLowerCase();
})
if (index < 0) {
this.topics.push({
k: this.selectTopic.toLocaleLowerCase(),
v: this.selectTopic,
});
}
}
!noSearch && this.search();
},
handlerTopicsData(data) {
try {
const topicsData = JSON.parse(data);
const topics = topicsData.map((item) => {
return {
k: item.trim().toLocaleLowerCase(),
v: item.trim(),
}
});
this.topicOri = JSON.parse(JSON.stringify(topics));
this.topics = topics;
const selectTopic_key = this.selectTopic.toLocaleLowerCase();
if (selectTopic_key) {
const index = this.topics.findIndex((item) => {
return item.k == selectTopic_key;
});
if (index < 0) {
this.topics.push({
k: this.selectTopic.toLocaleLowerCase(),
v: this.selectTopic,
});
}
}
} catch (err) {
console.log(err);
}
},
search() {
this.searchInputValue = this.searchInputValue.trim();
if (this.type == 'square') {
window.location.href = `/explore/repos?q=${this.searchInputValue}&sort=${this.sort}&topic=${this.selectTopic}`;
} else {
this.$emit('change', {
q: this.searchInputValue,
topic: this.selectTopic,
});
}
}
},
mounted() {
if (this.static) {
try {
this.handlerTopicsData(this.staticTopicsData);
} catch (err) {
console.log(err);
}
} else {
getPromoteData('/repos/recommend_topics').then(res => {
const data = res.data;
this.handlerTopicsData(data);
}).catch(err => {
console.log(err);
this.handlerTopicsData('[]');
});
}
},
};
</script>

<style scoped lang="less">
._repo_search {
margin: 54px 0;
}

._repo_search_input_c {
margin: 0 0 35px;
display: flex;
justify-content: center;
align-items: center;
}

._repo_search_input {
display: flex;
align-items: center;
width: 437px;
height: 41px;
border-color: rgba(47, 9, 69, 0.64);
border-width: 1px;
border-style: solid;
color: rgba(16, 16, 16, 0.5);
border-radius: 20px;
font-size: 14px;
padding: 20px;
text-align: left;
line-height: 20px;
background: url("data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20version%3D%221.1%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%221%22%20x1%3D%220%22%20x2%3D%221%22%20y1%3D%220%22%20y2%3D%220%22%20gradientTransform%3D%22matrix(6.123233995736765e-17%2C%20-0.9999999999999999%2C%200.008802475794500678%2C%206.123233995736765e-17%2C%201%2C%201.003)%22%3E%3Cstop%20stop-color%3D%22%23eeeade%22%20stop-opacity%3D%220.2%22%20offset%3D%220.76%22%3E%3C%2Fstop%3E%3Cstop%20stop-color%3D%22%23ece0e9%22%20stop-opacity%3D%221%22%20offset%3D%221%22%3E%3C%2Fstop%3E%3C%2FlinearGradient%3E%3C%2Fdefs%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22url(%231)%22%3E%3C%2Frect%3E%3C%2Fsvg%3E");
}

._repo_search_input input {
width: 100%;
height: 30px;
border: none;
background: transparent;
outline: none;
}

._repo_search_btn {
margin-left: 10px;
width: 110px;
height: 40px;
border-style: none;
border-color: unset;
color: rgb(255, 255, 255);
border-radius: 21px;
font-size: 14px;
text-align: center;
font-weight: normal;
font-style: normal;
background: url("data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20version%3D%221.1%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%221%22%20x1%3D%220%22%20x2%3D%221%22%20y1%3D%220%22%20y2%3D%220%22%20gradientTransform%3D%22matrix(6.123233995736766e-17%2C%201%2C%20-0.17728531855955676%2C%206.123233995736766e-17%2C%201%2C%200)%22%3E%3Cstop%20stop-color%3D%22%232f0945%22%20stop-opacity%3D%221%22%20offset%3D%220%22%3E%3C%2Fstop%3E%3Cstop%20stop-color%3D%22%23341a7b%22%20stop-opacity%3D%221%22%20offset%3D%221%22%3E%3C%2Fstop%3E%3C%2FlinearGradient%3E%3C%2Fdefs%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22url(%231)%22%3E%3C%2Frect%3E%3C%2Fsvg%3E");
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}

._repo_search_label_c {
display: flex;
justify-content: center;
}

._repo_search_label {
display: flex;
justify-content: center;
flex-wrap: wrap;
max-width: 1200px;
}

._repo_search_label a {
background-color: rgb(2, 202, 253);
color: rgb(255, 255, 255);
border-radius: 5px;
margin: 0 5px 10px 5px;
padding: 5px 10px;
cursor: pointer;
font-size: 12px;
}
</style>

+ 363
- 0
web_src/vuepages/pages/repos/components/SquareTop.vue View File

@@ -0,0 +1,363 @@
<template>
<div class="ui _repo_container_bg">
<div class="ui container _repo_container _content_container">
<div class="_repo_top_left">
<a :href="bannerData[0] ? bannerData[0].url : ''">
<div class="_repo_top_left_img">
<img :src="bannerData[0] ? bannerData[0].src : ''">
</div>
</a>
</div>
<div class="_repo_top_middle">
<div class="_repo_top_middle_header">
<div class="_repo_top_mid_item" :class="(tabIndex == index) ? '_foucs' : ''" v-for="(item, index) in tabs"
:key="index" @click="changeTab(item, index)">
<i :class="item.icon"></i>
<span>{{ item.label }}</span>
</div>
</div>
<div class="_repo_top_middle_content">
<div class="_repo_top_mid_repo_list">
<div class="swiper-wrapper" id="_repo_top_mid_repo"></div>
<div class="swiper-pagination _repo_top-swiper-pagination"></div>
</div>
</div>
</div>
<div class="_repo_top_right">
<a :href="bannerData[1] ? bannerData[1].url : ''">
<div class="_repo_top_right_img">
<img :src="bannerData[1] ? bannerData[1].src : ''">
</div>
</a>
</div>
</div>
</div>
</template>

<script>
import { getPromoteData } from '~/apis/modules/common';
import { getReposSquareTabData } from '~/apis/modules/repos';
import LetterAvatar from '~/utils/letteravatar';

export default {
name: "SquareTop",
props: {
static: { type: Boolean, default: false },
staticSwiperData: { type: Array, default: () => [] },
staticBannerData: { type: String, default: '[]' },
},
components: {},
data() {
return {
swiperHandler: null,
tabIndex: 0,
tabs: [{
key: 'preferred',
icon: 'ri-fire-line',
label: this.$t('repos.preferred'),
}, {
key: 'incubation',
icon: 'ri-award-line',
label: this.$t('repos.openIIncubation'),
}, {
key: 'hot-paper',
icon: 'ri-file-damage-line',
label: this.$t('repos.hotPapers'),
}],
bannerData: [],
};
},
methods: {
initSwiper() {
this.swiperHandler = new Swiper("._repo_top_mid_repo_list", {
slidesPerView: 1,
spaceBetween: 20,
pagination: {
el: "._repo_top-swiper-pagination",
clickable: true,
},
autoplay: {
delay: 4500,
disableOnInteraction: false,
},
breakpoints: {
768: {
slidesPerView: 2,
},
1024: {
slidesPerView: 2,
},
1200: {
slidesPerView: 3,
},
},
});
},
renderSwiper(data) {
const swiperEl = document.getElementById("_repo_top_mid_repo");
let html = '';
const width = swiperEl.parentNode.clientWidth;
for (let i = 0, iLen = data.length; i < iLen; i++) {
html += `<div class="swiper-slide">`;
for (let j = i; j < i + 2; j++) {
let dataJ = data[j];
if (dataJ === undefined) break;
html += `<div class="_repo_sw_card"><div style="display:flex;">
${dataJ["RelAvatarLink"] ? `<img style="border-radius:100%;width:35px;height:35px;margin-bottom:0.6em;" class="left floated mini ui image" src="${dataJ["RelAvatarLink"]}">`
: `<img style="border-radius:100%;width:35px;height:35px;margin-bottom:0.6em;" class="left floated mini ui image" avatar="${dataJ["OwnerName"]}">`}
<span class="header nowrap" style="color:rgb(50, 145, 248);font-size:14px;height:35px;line-height:35px;" href="javascript:;" title="${dataJ["Alias"]}">${dataJ["Alias"]}</span>
</div><div class="_repo_sw_card_descr _repo_nowrap_line_3" title="${dataJ.Description}">${dataJ.Description}</div>
<a href="/${dataJ.OwnerName}/${dataJ.Name}" style="position:absolute;width:100%;height:100%;top:0;left:0;"></a>
</div>`;
}
html += `</div>`;
i++;
}
this.swiperHandler.removeAllSlides();
swiperEl.innerHTML = html;
this.swiperHandler.updateSlides();
this.swiperHandler.updateProgress();
LetterAvatar.transform();
},
getBannerData() {
getPromoteData('/repos/square_banner').then(res => {
const data = res.data;
try {
const list = JSON.parse(data);
this.bannerData = list;
} catch (err) {
console.log(err);
}
}).catch(err => {
this.bannerData = [];
console.log(err);
});
},
getTabData() {
getReposSquareTabData(this.tabs[this.tabIndex].key).then(res => {
res = res.data;
if (res.Code == 0) {
const data = res.Data.Repos || [];
this.renderSwiper(data);
} else {
this.renderSwiper([]);
}
}).catch(err => {
console.log(err);
this.renderSwiper([]);
});
},
changeTab(item, index) {
this.tabIndex = index;
this.getTabData();
},
},
mounted() {
this.initSwiper();
if (this.static) {
try {
this.bannerData = JSON.parse(this.staticBannerData);
} catch (err) {
console.log(err);
}
this.renderSwiper(this.staticSwiperData);
} else {
this.getBannerData();
this.getTabData();
}
},
};
</script>

<style scoped lang="less">
._repo_container_bg {
width: 100%;
height: 450px;
background: url("data:image/svg+xml;charset=utf8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20version%3D%221.1%22%3E%3Cdefs%3E%3ClinearGradient%20id%3D%221%22%20x1%3D%220%22%20x2%3D%221%22%20y1%3D%220%22%20y2%3D%220%22%20gradientTransform%3D%22matrix(0.4999999999999999%2C%20-0.8660000000000001%2C%200.07722000385802469%2C%200.4999999999999999%2C%20-0.183%2C%200.683)%22%3E%3Cstop%20stop-color%3D%22%239aceec%22%20stop-opacity%3D%221%22%20offset%3D%220%22%3E%3C%2Fstop%3E%3Cstop%20stop-color%3D%22%23eeeeee%22%20stop-opacity%3D%221%22%20offset%3D%220.99%22%3E%3C%2Fstop%3E%3C%2FlinearGradient%3E%3C%2Fdefs%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20fill%3D%22url(%231)%22%3E%3C%2Frect%3E%3C%2Fsvg%3E");
position: relative;
}

._content_container {
display: flex !important;
margin: 0 auto !important;
height: 100% !important;
}

._repo_top_left {
width: 243px;
height: 100%;
position: relative;
}

._repo_top_left_img {
width: 100%;
height: 346px;
position: absolute;
bottom: 0;
border-top-left-radius: 10px;
overflow: hidden;
}

._repo_top_left_img img {
height: 100%;
width: 100%;
}

._content_container img[src=""],
img:not([src]) {
opacity: 0;
}

._repo_top_right {
width: 243px;
height: 100%;
position: relative;
}

._repo_top_right_img {
width: 100%;
height: 346px;
position: absolute;
bottom: 0;
border-top-right-radius: 10px;
overflow: hidden;
}

._repo_top_right_img img {
height: 100%;
width: 100%;
}

._repo_top_middle {
flex: 1;
height: 100%;
position: relative;

}

._repo_top_middle_header {
width: 100%;
height: 42px;
position: absolute;
bottom: 346px;
display: flex;
align-items: center;
background: raba(0, 0, 255, 0.3);
padding: 0 20px;
}

._repo_top_mid_item {
padding: 0px 16px;
font-size: 18px;
color: rgba(47, 9, 69, 0.64);
height: 100%;
display: flex;
align-items: center;
cursor: pointer;
box-sizing: border-box;
border-bottom: 3px solid transparent;
}

._repo_top_mid_item i {
margin-right: 5px;
}

._repo_top_mid_item._foucs {
background-color: rgba(255, 255, 255, 0.3);
color: rgb(16, 16, 16);
cursor: default;
border-bottom: 3px solid rgba(16, 16, 16, 0.8);
}

._repo_top_middle_content {
width: 100%;
height: 346px;
position: absolute;
bottom: 0;
background: rgba(255, 255, 255, 0.3);
padding: 20px 20px;
overflow: hidden;
}

._repo_top_mid_repo_list {
overflow: hidden;
}

/deep/._repo_sw_card {
height: 128px;
border-color: rgb(255, 255, 255);
border-width: 1px;
border-style: solid;
font-size: 14px;
padding: 12px;
background: rgba(255, 255, 255, 0.6);
margin-bottom: 20px;
box-sizing: border-box;
position: relative;
}

/deep/._repo_nowrap {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}

/deep/._repo_nowrap_line_3 {
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
font-size: 12px;
color: rgb(136, 136, 136);
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
max-height: 65px;
white-space: break-spaces;
}

/deep/._repo_sw_card a {
color: inherit;
}

/deep/._repo_sw_card_title {
font-weight: 700;
font-size: 14px;
color: rgba(26, 40, 51, 1);
margin-bottom: 10px;
}

/deep/._repo_sw_card_descr {
font-size: 12px;
color: rgba(80, 85, 89, 1);
margin-bottom: 10px;
min-height: 42px;
}

/deep/._repo_sw_card_label {
color: rgb(26, 40, 51);
font-size: 14px;
display: flex;
width: 100%;
position: relative;
}

/deep/._repo_sw_card_label span {
border-radius: 4px;
background: rgba(232, 232, 232, 0.6);
padding: 0px 6px 2px;
margin-right: 10px;
max-width: 50%;
}

/deep/._repo_top_mid_repo_list .swiper-pagination-bullet {
width: 8px;
height: 8px;
border-radius: 100%;
background: #76cbed;
}

/deep/._repo_top_mid_repo_list .swiper-pagination-bullet-active {
width: 40px;
border-radius: 4px;
}
</style>

+ 123
- 0
web_src/vuepages/pages/repos/search/index.vue View File

@@ -0,0 +1,123 @@
<template>
<div>
<div class="ui container">
<SearchBar ref="searchBarRef" type="search" :static="true" :staticTopicsData="staticSquareTopics"
:sort="reposListSortType" :searchValue="reposListQurey" :topic="reposListTopic" @change="searchBarChange">
</SearchBar>
</div>
<div class="ui container">
<div class="ui grid">
<div class="computer only ui two wide computer column">
<ReposFilters ref="reposFiltersRef" @change="filtersChange"></ReposFilters>
</div>
<div class="ui sixteen wide mobile twelve wide tablet ten wide computer column">
<ReposList ref="reposListRef" :sort="reposListSortType" :q="reposListQurey" :topic="reposListTopic"
:page="page" :pageSize="pageSize" :pageSizes="pageSizes" @current-change="currentChange"
@size-change="sizeChange">
</ReposList>
</div>
<div class="computer only ui four wide computer column">
<div>
<ActiveUsers></ActiveUsers>
</div>
<div class="active-org-c">
<ActiveOrgs></ActiveOrgs>
</div>
</div>
</div>
</div>
</div>
</template>

<script>
import SearchBar from '../components/SearchBar.vue';
import ReposFilters from '../components/ReposFilters.vue';
import ReposList from '../components/ReposList.vue';
import ActiveUsers from '../components/ActiveUsers.vue';
import ActiveOrgs from '../components/ActiveOrgs.vue';
import { getUrlSearchParams } from '~/utils';

const staticSquareTopics = JSON.stringify(window.staticSquareTopics || []);

export default {
data() {
return {
reposListSortType: 'mostpopular',
reposListQurey: '',
reposListTopic: '',

page: 1,
pageSize: 15,
pageSizes: [15, 30, 50],

staticSquareTopics: staticSquareTopics,
};
},
components: {
SearchBar,
ReposFilters,
ReposList,
ActiveUsers,
ActiveOrgs,
},
methods: {
filtersChange(condition) {
this.page = 1;
this.reposListSortType = condition.key;
this.search();
},
searchBarChange(params) {
this.page = 1;
this.reposListQurey = params.q || '';
this.reposListTopic = params.topic || '';
this.search();
},
currentChange({ page, pageSize }) {
this.page = page;
this.search();
},
sizeChange({ page, pageSize }) {
this.page = 1;
this.pageSize = pageSize;
this.search();
},
search() {
window.location.href = `/explore/repos?q=${this.reposListQurey.trim()}&sort=${this.reposListSortType}&topic=${this.reposListTopic.trim()}&page=${this.page}&pageSize=${this.pageSize}`;
}
},
beforeMount() {
const urlParams = getUrlSearchParams();
this.reposListQurey = urlParams.q || '';
this.reposListTopic = urlParams.topic || '';
this.reposListSortType = urlParams.sort || 'mostpopular';
this.page = Number(urlParams.page) || 1;
this.pageSize = this.pageSizes.indexOf(Number(urlParams.pageSize)) >= 0 ? Number(urlParams.pageSize) : 15;
},
mounted() {
this.$nextTick(() => {
this.$refs.reposFiltersRef.setDefaultFilter(this.reposListSortType);
this.$refs.searchBarRef.setDefaultSearch({
q: this.reposListQurey,
topic: this.reposListTopic,
});
this.$refs.reposListRef.search();
});
window.addEventListener('pageshow', function (e) {
if (e.persisted) {
window.location.reload();
}
}, false);
},
beforeDestroy() { },
};
</script>

<style scoped lang="less">
.recommend-repos-c {
margin: 0 0 54px;
}

.active-org-c {
margin-top: 32px;
}
</style>

+ 17
- 0
web_src/vuepages/pages/repos/search/vp-repos-search.js View File

@@ -0,0 +1,17 @@
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import localeEn from 'element-ui/lib/locale/lang/en';
import localeZh from 'element-ui/lib/locale/lang/zh-CN';
import { i18n, lang } from '~/langs';
import App from './index.vue';

Vue.use(ElementUI, {
locale: lang === 'zh-CN' ? localeZh : localeEn,
size: 'small',
});

new Vue({
i18n,
render: (h) => h(App),
}).$mount('#__vue-root');

+ 146
- 0
web_src/vuepages/pages/repos/square/index.vue View File

@@ -0,0 +1,146 @@
<template>
<div>
<div>
<SquareTop :static="true" :staticBannerData="staticSquareBanners" :staticSwiperData="staticSquarePreferredRepos">
</SquareTop>
</div>
<div class="ui container">
<SearchBar :static="true" :staticTopicsData="staticSquareTopics" ref="searchBarRef" type="square" :sort="``"
:searchValue="reposListQurey" :topic="``" @change="searchBarChange"></SearchBar>
</div>
<div class="recommend-repos-c">
<RecommendRepos :static="true" :staticSwiperData="staticSquareRecommendRepos"></RecommendRepos>
<a name="search"></a>
</div>
<div class="ui container">
<div class="ui grid">
<div class="computer only ui two wide computer column">
<ReposFilters ref="reposFiltersRef" @change="filtersChange"></ReposFilters>
</div>
<div class="ui sixteen wide mobile twelve wide tablet ten wide computer column">
<ReposList ref="reposListRef" :sort="reposListSortType" :q="reposListQurey" :topic="reposListTopic"
:page="page" :pageSize="pageSize" :pageSizes="pageSizes" @current-change="currentChange"
@size-change="sizeChange">
</ReposList>
</div>
<div class="computer only ui four wide computer column">
<div>
<ActiveUsers></ActiveUsers>
</div>
<div class="active-org-c">
<ActiveOrgs></ActiveOrgs>
</div>
</div>
</div>
</div>
</div>
</template>

<script>
import SquareTop from '../components/SquareTop.vue';
import SearchBar from '../components/SearchBar.vue';
import RecommendRepos from '../components/RecommendRepos.vue';
import ReposFilters from '../components/ReposFilters.vue';
import ReposList from '../components/ReposList.vue';
import ActiveUsers from '../components/ActiveUsers.vue';
import ActiveOrgs from '../components/ActiveOrgs.vue';
import { getUrlSearchParams } from '~/utils';

const staticSquareBanners = JSON.stringify(window.staticSquareBanners || []);
const staticSquarePreferredRepos = window.staticSquarePreferredRepos || [];
const staticSquareTopics = JSON.stringify(window.staticSquareTopics || []);
const staticSquareRecommendRepos = window.staticSquareRecommendRepos || [];

export default {
data() {
return {
reposListSortType: 'mostpopular',
reposListQurey: '',
reposListTopic: '',

page: 1,
pageSize: 15,
pageSizes: [15, 30, 50],

staticSquareBanners: staticSquareBanners,
staticSquarePreferredRepos: staticSquarePreferredRepos,
staticSquareTopics: staticSquareTopics,
staticSquareRecommendRepos: staticSquareRecommendRepos,
};
},
components: {
SquareTop,
SearchBar,
RecommendRepos,
ReposFilters,
ReposList,
ActiveUsers,
ActiveOrgs,
},
methods: {
filtersChange(condition) {
this.page = 1;
this.reposListSortType = condition.key;
this.search();
},
searchBarChange(params) {
this.page = 1;
this.reposListQurey = params.q || '';
this.reposListTopic = params.topic || '';
this.search();
},
currentChange({ page, pageSize }) {
this.page = page;
this.search();
},
sizeChange({ page, pageSize }) {
this.page = 1;
this.pageSize = pageSize;
this.search();
},
search() {
window.location.href = `/explore/repos/square?q=${this.reposListQurey.trim()}&sort=${this.reposListSortType}&topic=${this.reposListTopic.trim()}&page=${this.page}&pageSize=${this.pageSize}`;
}
},
beforeMount() {
const urlParams = getUrlSearchParams();
this.reposListQurey = urlParams.q || '';
this.reposListTopic = urlParams.topic || '';
this.reposListSortType = urlParams.sort || 'mostpopular';
this.page = Number(urlParams.page) || 1;
this.pageSize = this.pageSizes.indexOf(Number(urlParams.pageSize)) >= 0 ? Number(urlParams.pageSize) : 15;
},
mounted() {
this.$nextTick(() => {
this.$refs.reposFiltersRef.setDefaultFilter(this.reposListSortType);
this.$refs.searchBarRef.setDefaultSearch({
q: this.reposListQurey,
topic: this.reposListTopic,
});
const urlParams = getUrlSearchParams();
const page = Number(urlParams.page) || 1;
const reposListSortType = urlParams.sort;
if (page != 1 || reposListSortType) {
window.location.href = '#search';
}
this.$refs.reposListRef.search();
});
window.addEventListener('pageshow', function (e) {
if (e.persisted) {
window.location.reload();
}
}, false);
},
beforeDestroy() { },
};
</script>

<style scoped lang="less">
.recommend-repos-c {
margin: 0 0 54px;
}

.active-org-c {
margin-top: 32px;
}
</style>

+ 17
- 0
web_src/vuepages/pages/repos/square/vp-repos-square.js View File

@@ -0,0 +1,17 @@
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import localeEn from 'element-ui/lib/locale/lang/en';
import localeZh from 'element-ui/lib/locale/lang/zh-CN';
import { i18n, lang } from '~/langs';
import App from './index.vue';

Vue.use(ElementUI, {
locale: lang === 'zh-CN' ? localeZh : localeEn,
size: 'small',
});

new Vue({
i18n,
render: (h) => h(App),
}).$mount('#__vue-root');

+ 89
- 1
web_src/vuepages/utils/index.js View File

@@ -27,7 +27,7 @@ export const transFileSize = (srcSize) => {
srcSize = parseFloat(srcSize);
const index = Math.floor(Math.log(srcSize) / Math.log(1024));
const size = (srcSize / Math.pow(1024, index)).toFixed(2);
return size + ' ' + unitArr[index];
return size + ' ' + unitArr[index];
};

export const renderSpecStr = (spec, showPoint) => {
@@ -39,3 +39,91 @@ export const renderSpecStr = (spec, showPoint) => {
var specStr = `${ngpu}, CPU: ${spec.CpuCores}, ${gpuMemStr}${i18n.t('resourcesManagement.mem')}: ${spec.MemGiB}GB${sharedMemStr}${pointStr}`;
return specStr;
};

const Minute = 60;
const Hour = 60 * Minute;
const Day = 24 * Hour;
const Week = 7 * Day;
const Month = 30 * Day;
const Year = 12 * Month;

const computeTimeDiff = (diff) => {
let diffStr = '';
switch (true) {
case diff <= 0:
diff = 0;
diffStr = i18n.t('timeObj.now');
break;
case diff < 2:
diff = 0;
diffStr = i18n.t('timeObj.1s');
break;
case diff < 1 * Minute:
diffStr = i18n.t('timeObj.seconds', { msg: Math.floor(diff) });
diff = 0;
break;
case diff < 2 * Minute:
diff -= 1 * Minute;
diffStr = i18n.t('timeObj.1m');
break;
case diff < 1 * Hour:
diffStr = i18n.t('timeObj.minutes', { msg: Math.floor(diff / Minute) });
diff -= diff / Minute * Minute;
break;
case diff < 2 * Hour:
diff -= 1 * Hour;
diffStr = i18n.t('timeObj.1h');
break;
case diff < 1 * Day:
diffStr = i18n.t('timeObj.hours', { msg: Math.floor(diff / Hour) });
diff -= diff / Hour * Hour;
break;
case diff < 2 * Day:
diff -= 1 * Day;
diffStr = i18n.t('timeObj.1d');
break;
case diff < 1 * Week:
diffStr = i18n.t('timeObj.days', { msg: Math.floor(diff / Day) });
diff -= diff / Day * Day;
break;
case diff < 2 * Week:
diff -= 1 * Week;
diffStr = i18n.t('timeObj.1w');
break;
case diff < 1 * Month:
diffStr = i18n.t('timeObj.weeks', { msg: Math.floor(diff / Week) });
diff -= diff / Week * Week;
break;
case diff < 2 * Month:
diff -= 1 * Month;
diffStr = i18n.t('timeObj.1mon');
break;
case diff < 1 * Year:
diffStr = i18n.t('timeObj.months', { msg: Math.floor(diff / Month) });
diff -= diff / Month * Month;
break;
case diff < 2 * Year:
diff -= 1 * Year;
diffStr = i18n.t('timeObj.1y');
break;
default:
diffStr = i18n.t('timeObj.years', { msg: Math.floor(diff / Year) });
diff -= (diff / Year) * Year;
break;
}
return { diff, diffStr };
};

export const timeSinceUnix = (then, now) => {
let lbl = 'timeObj.ago';
let diff = now - then;
if (then > now) {
lbl = 'timeObj.from_now';
diff = then - now;
}
if (diff <= 0) {
return i18n.t('timeObj.now');
}
const out = computeTimeDiff(diff);
return i18n.t(lbl, { msg: out.diffStr });
};

+ 75
- 0
web_src/vuepages/utils/letteravatar.js View File

@@ -0,0 +1,75 @@

function LetterAvatar(name, size, color) {
name = name || "";
size = size || 60;
var colours = [
"#1abc9c",
"#2ecc71",
"#3498db",
"#9b59b6",
"#34495e",
"#16a085",
"#27ae60",
"#2980b9",
"#8e44ad",
"#2c3e50",
"#f1c40f",
"#e67e22",
"#e74c3c",
"#00bcd4",
"#95a5a6",
"#f39c12",
"#d35400",
"#c0392b",
"#bdc3c7",
"#7f8c8d",
],
nameSplit = String(name).split(" "),
initials,
charIndex,
colourIndex,
canvas,
context,
dataURI;
if (nameSplit.length == 1) {
initials = nameSplit[0] ? nameSplit[0].charAt(0) : "?";
} else {
initials = nameSplit[0].charAt(0) + nameSplit[1].charAt(0);
}
let initials1 = initials.toUpperCase();
if (window.devicePixelRatio) {
size = size * window.devicePixelRatio;
}

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

context.fillStyle = color ? color : colours[colourIndex - 1];
context.fillRect(0, 0, canvas.width, canvas.height);
context.font = Math.round(canvas.width / 2) + "px 'Microsoft Yahei'";
context.textAlign = "center";
context.fillStyle = "#FFF";
context.fillText(initials1, size / 2, size / 1.5);
dataURI = canvas.toDataURL();
canvas = null;
return dataURI;
}

LetterAvatar.transform = function () {
Array.prototype.forEach.call(
document.querySelectorAll("img[avatar]"),
function (img, name, color) {
name = img.getAttribute("avatar");
color = img.getAttribute("color");
img.src = LetterAvatar(name, img.getAttribute("width"), color);
img.removeAttribute("avatar");
img.setAttribute("alt", name);
}
);
};

export default LetterAvatar;

+ 2
- 2
webpack.config.js View File

@@ -22,8 +22,8 @@ for (const path of glob('web_src/less/themes/*.less')) {

const standalone = {};
const stadalonePaths = [
...glob('web_src/js/standalone/*.js'),
...glob('web_src/less/standalone/*.less'),
...glob('web_src/js/standalone/**/*.js'),
...glob('web_src/less/standalone/**/*.less'),
];
for (const path of stadalonePaths) {
standalone[parse(path).name] = [path];


+ 2
- 2
webpack_pro.config.js View File

@@ -22,8 +22,8 @@ for (const path of glob('web_src/less/themes/*.less')) {

const standalone = {};
const stadalonePaths = [
...glob('web_src/js/standalone/*.js'),
...glob('web_src/less/standalone/*.less'),
...glob('web_src/js/standalone/**/*.js'),
...glob('web_src/less/standalone/**/*.less'),
];
for (const path of stadalonePaths) {
standalone[parse(path).name] = [path];


Loading…
Cancel
Save