| @@ -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 | |||
| @@ -411,7 +443,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) | |||
| @@ -1061,6 +1061,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 { | |||
| @@ -1084,13 +1087,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"` | |||
| @@ -1148,6 +1156,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 { | |||
| @@ -1163,6 +1174,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 { | |||
| @@ -1178,6 +1192,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 { | |||
| @@ -1862,6 +1877,7 @@ func CreateCloudbrain(cloudbrain *Cloudbrain) (err error) { | |||
| session.Commit() | |||
| go IncreaseDatasetUseCount(cloudbrain.Uuid) | |||
| go OperateRepoAITaskNum(cloudbrain.RepoID, 1) | |||
| return nil | |||
| } | |||
| @@ -2017,9 +2033,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) | |||
| @@ -2222,7 +2258,6 @@ func RestartCloudbrain(old *Cloudbrain, new *Cloudbrain) (err error) { | |||
| } | |||
| go IncreaseDatasetUseCount(new.Uuid) | |||
| return nil | |||
| } | |||
| func CloudbrainAll(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int64, error) { | |||
| @@ -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 | |||
| @@ -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) | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -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") | |||
| } | |||
| @@ -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 | |||
| } | |||
| @@ -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 | |||
| } | |||
| @@ -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 != "" | |||
| @@ -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 | |||
| } | |||
| @@ -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 | |||
| } | |||
| @@ -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"` | |||
| } | |||
| @@ -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:"-"` | |||
| @@ -281,7 +281,7 @@ func GenerateTask(ctx *context.Context, jobName, uuid, description, flavor strin | |||
| return nil | |||
| } | |||
| func GenerateNotebook2(ctx *context.Context, displayJobName, jobName, uuid, description, imageId string, spec *models.Specification, bootFile string,autoStopDurationInMs int64) (string, error) { | |||
| func GenerateNotebook2(ctx *context.Context, displayJobName, jobName, uuid, description, imageId string, spec *models.Specification, bootFile string, autoStopDurationInMs int64) (string, error) { | |||
| if poolInfos == nil { | |||
| json.Unmarshal([]byte(setting.PoolInfos), &poolInfos) | |||
| } | |||
| @@ -379,6 +379,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 { | |||
| @@ -399,6 +402,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, | |||
| }, | |||
| }) | |||
| } | |||
| @@ -517,6 +523,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 { | |||
| @@ -536,6 +545,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) | |||
| } | |||
| @@ -972,14 +984,14 @@ func getJupyterBaseUrl(url string) string { | |||
| } | |||
| func getCookiesAndCsrf(jupyterUrl string) ([]*http.Cookie, string) { | |||
| log.Info("jupyter url:"+jupyterUrl) | |||
| log.Info("jupyter url:" + jupyterUrl) | |||
| var cookies []*http.Cookie | |||
| const retryTimes = 10 | |||
| for i := 0; i < retryTimes; i++ { | |||
| res, err := http.Get(jupyterUrl) | |||
| if err != nil { | |||
| log.Error("browser jupyterUrl failed.",err) | |||
| if i==retryTimes-1{ | |||
| log.Error("browser jupyterUrl failed.", err) | |||
| if i == retryTimes-1 { | |||
| return cookies, "" | |||
| } | |||
| @@ -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(). | |||
| @@ -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") | |||
| } | |||
| @@ -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) | |||
| @@ -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"` | |||
| } | |||
| @@ -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 | |||
| @@ -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=请您先导入 | |||
| @@ -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", | |||
| @@ -622,20 +622,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) + "&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>`; | |||
| @@ -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)) | |||
| } | |||
| @@ -543,6 +543,10 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
| m.Get("/get_multipart_url", repo.GetMultipartUploadUrl) | |||
| m.Post("/complete_multipart", repo.CompleteMultipart) | |||
| }, reqToken()) | |||
| m.Group("/pipeline", func() { | |||
| m.Post("/notification", bind(api.PipelineNotification{}), notify.PipelineNotify) | |||
| }, reqToken()) | |||
| // Notifications | |||
| @@ -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) | |||
| @@ -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) | |||
| } | |||
| @@ -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) | |||
| } | |||
| @@ -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": ""}) | |||
| } | |||
| @@ -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) | |||
| } | |||
| } | |||
| @@ -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) | |||
| } | |||
| @@ -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 | |||
| @@ -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) | |||
| } | |||
| @@ -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 | |||
| } | |||
| @@ -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) | |||
| @@ -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, | |||
| @@ -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 { | |||
| @@ -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() | |||
| @@ -2337,7 +2337,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{ | |||
| @@ -2345,10 +2345,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 | |||
| @@ -2417,7 +2419,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{ | |||
| @@ -2425,10 +2427,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 | |||
| @@ -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) | |||
| @@ -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) | |||
| } | |||
| @@ -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 = "已注销" | |||
| } | |||
| @@ -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) | |||
| @@ -1251,6 +1262,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) | |||
| @@ -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) | |||
| @@ -1382,7 +1382,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{ | |||
| @@ -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) | |||
| } | |||
| @@ -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 "" | |||
| } | |||
| @@ -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) | |||
| } | |||
| } | |||
| @@ -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}} | |||
| @@ -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}} | |||
| @@ -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}} | |||
| @@ -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}} | |||
| @@ -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"> | |||
| @@ -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" .}} | |||
| @@ -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" .}} | |||
| @@ -647,6 +647,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"}} </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" | |||
| @@ -849,6 +866,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, | |||
| @@ -679,6 +679,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"}} </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" | |||
| @@ -910,6 +927,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, | |||
| @@ -703,6 +703,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"}} </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" | |||
| @@ -930,6 +949,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, | |||
| @@ -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 | |||
| @@ -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> | |||
| @@ -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"}}  </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"}}  </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" }); | |||
| @@ -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"}}  </label> | |||
| @@ -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> <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> | |||
| @@ -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" | |||
| @@ -101,6 +90,22 @@ | |||
| <span class="text-over">{{ scope.row.computeResource }}</span> | |||
| </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" | |||
| @@ -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 | |||
| @@ -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", | |||
| @@ -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(); | |||
| @@ -0,0 +1,12 @@ | |||
| import service from '../service'; | |||
| // 获取promote配置数据 | |||
| export const getPromoteData = (filePathName) => { | |||
| return service({ | |||
| url: '/dashboard/invitation', | |||
| method: 'get', | |||
| params: { | |||
| filename: filePathName | |||
| }, | |||
| }); | |||
| } | |||
| @@ -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({ | |||
| @@ -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: {}, | |||
| }); | |||
| } | |||
| @@ -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', | |||
| }, | |||
| } | |||
| @@ -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; | |||
| @@ -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) : ''; | |||
| @@ -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> </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; | |||
| @@ -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> | |||
| @@ -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> | |||
| @@ -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> | |||
| @@ -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> | |||
| @@ -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') }} | |||
| </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> | |||
| @@ -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> | |||
| @@ -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> | |||
| @@ -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> | |||
| @@ -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> | |||
| @@ -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'); | |||
| @@ -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> | |||
| @@ -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'); | |||
| @@ -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 }); | |||
| }; | |||
| @@ -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; | |||
| @@ -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]; | |||
| @@ -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]; | |||