Browse Source

提交代码。

Signed-off-by: zouap <zouap@pcl.ac.cn>
pull/1036/head
zouap 3 years ago
parent
commit
7204f10526
51 changed files with 2677 additions and 19714 deletions
  1. +1
    -0
      models/cloudbrain.go
  2. +1
    -0
      modules/auth/modelarts.go
  3. +12
    -0
      modules/cron/tasks_basic.go
  4. +2
    -2
      modules/modelarts/modelarts.go
  5. +17
    -13
      modules/setting/setting.go
  6. +3
    -58
      modules/storage/obs.go
  7. +14
    -0
      options/locale/locale_en-US.ini
  8. +16
    -0
      options/locale/locale_zh-CN.ini
  9. +167
    -18485
      package-lock.json
  10. +5
    -1
      package.json
  11. BIN
      public/img/name.png
  12. BIN
      public/img/overview.png
  13. BIN
      public/img/pro.png
  14. +1
    -0
      public/img/pro.svg
  15. +2
    -1
      routers/api/v1/api.go
  16. +167
    -12
      routers/api/v1/repo/repo_dashbord.go
  17. +7
    -2
      routers/home.go
  18. +1
    -1
      routers/repo/cloudbrain.go
  19. +2
    -229
      routers/repo/modelarts.go
  20. +120
    -36
      routers/repo/view.go
  21. +3
    -0
      routers/routes/routes.go
  22. +1
    -0
      templates/base/head_navbar.tmpl
  23. +1
    -0
      templates/base/head_navbar_home.tmpl
  24. +14
    -0
      templates/explore/data_analysis.tmpl
  25. +16
    -3
      templates/home.tmpl
  26. +9
    -0
      templates/repo/contributors.tmpl
  27. +1
    -1
      templates/repo/header.tmpl
  28. +13
    -7
      templates/repo/home.tmpl
  29. +1
    -1
      templates/repo/issue/labels.tmpl
  30. +1
    -1
      templates/repo/issue/milestone_issues.tmpl
  31. +1
    -1
      templates/repo/issue/milestone_new.tmpl
  32. +1
    -1
      templates/repo/issue/milestones.tmpl
  33. +1
    -1
      templates/repo/issue/new.tmpl
  34. +2
    -2
      templates/repo/issue/view.tmpl
  35. +0
    -485
      templates/repo/modelarts/index.tmpl
  36. +0
    -240
      templates/repo/modelarts/new.tmpl
  37. +1
    -1
      templates/repo/modelarts/notebook/new.tmpl
  38. +0
    -122
      templates/repo/modelarts/show.tmpl
  39. +1
    -1
      templates/repo/pulls/commits.tmpl
  40. +1
    -1
      templates/repo/pulls/files.tmpl
  41. +112
    -0
      web_src/js/components/Contributors.vue
  42. +123
    -0
      web_src/js/components/DataAnalysis.vue
  43. +923
    -0
      web_src/js/components/ProAnalysis.vue
  44. +415
    -0
      web_src/js/components/UserAnalysis.vue
  45. +5
    -2
      web_src/js/components/basic/editDialog.vue
  46. +179
    -0
      web_src/js/excel/Blob.js
  47. +141
    -0
      web_src/js/excel/Export2Excel.js
  48. +17
    -0
      web_src/js/excel/util.js
  49. +74
    -0
      web_src/js/features/letteravatar.js
  50. +40
    -4
      web_src/js/index.js
  51. +42
    -0
      web_src/less/openi.less

+ 1
- 0
models/cloudbrain.go View File

@@ -394,6 +394,7 @@ type FlavorInfos struct {
type FlavorInfo struct {
Id int `json:"id"`
Value string `json:"value"`
Desc string `json:"desc"`
}

type PoolInfos struct {


+ 1
- 0
modules/auth/modelarts.go View File

@@ -19,6 +19,7 @@ type CreateModelArtsNotebookForm struct {
JobName string `form:"job_name" binding:"Required"`
Attachment string `form:"attachment"`
Description string `form:"description"`
Flavor string `form:"flavor"`
}

func (f *CreateModelArtsNotebookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {


+ 12
- 0
modules/cron/tasks_basic.go View File

@@ -11,6 +11,7 @@ import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/migrations"
repository_service "code.gitea.io/gitea/modules/repository"
api_repo "code.gitea.io/gitea/routers/api/v1/repo"
"code.gitea.io/gitea/routers/repo"
mirror_service "code.gitea.io/gitea/services/mirror"
)
@@ -195,6 +196,17 @@ func registerHandleUserStatistic() {
})
}

func registerHandleClearRepoStatisticFile() {
RegisterTaskFatal("handle_repo_clear_statistic_file", &BaseConfig{
Enabled: true,
RunAtStart: false,
Schedule: "@daily",
}, func(ctx context.Context, _ *models.User, _ Config) error {
api_repo.ClearUnusedStatisticsFile()
return nil
})
}

func initBasicTasks() {
registerUpdateMirrorTask()
registerRepoHealthCheck()


+ 2
- 2
modules/modelarts/modelarts.go View File

@@ -134,7 +134,7 @@ type ResourcePool struct {
} `json:"resource_pool"`
}

func GenerateTask(ctx *context.Context, jobName, uuid, description string) error {
func GenerateTask(ctx *context.Context, jobName, uuid, description, flavor string) error {
var dataActualPath string
if uuid != "" {
dataActualPath = setting.Bucket + "/" + setting.BasePath + path.Join(uuid[0:1], uuid[1:2]) + "/" + uuid + "/"
@@ -163,7 +163,7 @@ func GenerateTask(ctx *context.Context, jobName, uuid, description string) error
JobName: jobName,
Description: description,
ProfileID: setting.ProfileID,
Flavor: setting.Flavor,
Flavor: flavor,
Pool: models.Pool{
ID: poolInfos.PoolInfo[0].PoolId,
Name: poolInfos.PoolInfo[0].PoolName,


+ 17
- 13
modules/setting/setting.go View File

@@ -163,6 +163,7 @@ var (
// UI settings
UI = struct {
ExplorePagingNum int
ContributorPagingNum int
IssuePagingNum int
RepoSearchPagingNum int
MembersPagingNum int
@@ -203,19 +204,20 @@ var (
Keywords string
} `ini:"ui.meta"`
}{
ExplorePagingNum: 20,
IssuePagingNum: 10,
RepoSearchPagingNum: 10,
MembersPagingNum: 20,
FeedMaxCommitNum: 5,
GraphMaxCommitNum: 100,
CodeCommentLines: 4,
ReactionMaxUserNum: 10,
ThemeColorMetaTag: `#6cc644`,
MaxDisplayFileSize: 8388608,
DefaultTheme: `gitea`,
Themes: []string{`gitea`, `arc-green`},
Reactions: []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`},
ExplorePagingNum: 20,
ContributorPagingNum: 50,
IssuePagingNum: 10,
RepoSearchPagingNum: 10,
MembersPagingNum: 20,
FeedMaxCommitNum: 5,
GraphMaxCommitNum: 100,
CodeCommentLines: 4,
ReactionMaxUserNum: 10,
ThemeColorMetaTag: `#6cc644`,
MaxDisplayFileSize: 8388608,
DefaultTheme: `gitea`,
Themes: []string{`gitea`, `arc-green`},
Reactions: []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`},
Notification: struct {
MinTimeout time.Duration
TimeoutStep time.Duration
@@ -545,6 +547,7 @@ var (
GrowthCommit float64
GrowthComments float64
RecordBeginTime string
Path string
}{}
)

@@ -1326,6 +1329,7 @@ func SetRadarMapConfig() {
RadarMap.GrowthCommit = sec.Key("growth_commit").MustFloat64(0.2)
RadarMap.GrowthComments = sec.Key("growth_comments").MustFloat64(0.2)
RadarMap.RecordBeginTime = sec.Key("record_beigin_time").MustString("2021-11-05")
RadarMap.Path = sec.Key("PATH").MustString("data/projectborad")

}



+ 3
- 58
modules/storage/obs.go View File

@@ -11,11 +11,11 @@ import (
"strconv"
"strings"

"github.com/unknwon/com"

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

"github.com/unknwon/com"
)

type FileInfo struct {
@@ -407,64 +407,9 @@ func GetObsListObject(jobName, parentDir string) ([]FileInfo, error) {
isDir = false
nextParentDir = parentDir
}
fileInfo := FileInfo{
ModTime: val.LastModified.Format("2006-01-02 15:04:05"),
FileName: fileName,
Size: val.Size,
IsDir: isDir,
ParenDir: nextParentDir,
}
fileInfos = append(fileInfos, fileInfo)
}
return fileInfos, err
} else {
if obsError, ok := err.(obs.ObsError); ok {
log.Error("Code:%s, Message:%s", obsError.Code, obsError.Message)
}
return nil, err
}
}

func GetVersionObsListObject(jobName, parentDir string) ([]FileInfo, error) {
input := &obs.ListObjectsInput{}
input.Bucket = setting.Bucket
input.Prefix = strings.TrimPrefix(path.Join(setting.TrainJobModelPath, jobName, setting.OutPutPath, parentDir), "/")
strPrefix := strings.Split(input.Prefix, "/")
output, err := ObsCli.ListObjects(input)
fileInfos := make([]FileInfo, 0)
if err == nil {
for _, val := range output.Contents {
str1 := strings.Split(val.Key, "/")
var isDir bool
var fileName, nextParentDir string
if strings.HasSuffix(val.Key, "/") {
//dirs in next level dir
if len(str1)-len(strPrefix) > 2 {
continue
}
fileName = str1[len(str1)-2]
isDir = true
if parentDir == "" {
nextParentDir = fileName
} else {
nextParentDir = parentDir + "/" + fileName
}

if fileName == strPrefix[len(strPrefix)-1] || (fileName+"/") == setting.OutPutPath {
continue
}
} else {
//files in next level dir
if len(str1)-len(strPrefix) > 1 {
continue
}
fileName = str1[len(str1)-1]
isDir = false
nextParentDir = parentDir
}

fileInfo := FileInfo{
ModTime: val.LastModified.Format("2006-01-02 15:04:05"),
ModTime: val.LastModified.Local().Format("2006-01-02 15:04:05"),
FileName: fileName,
Size: val.Size,
IsDir: isDir,


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

@@ -218,6 +218,7 @@ show_only_private = Showing only private
show_only_public = Showing only public

issues.in_your_repos = In your repositories
contributors = Contributors

[explore]
repos = Repositories
@@ -2163,6 +2164,19 @@ repos.stars = Stars
repos.forks = Forks
repos.issues = Issues
repos.size = Size
repos.id=ID
repos.projectName=Project Name
repos.isPrivate=Private
repos.openi=OpenI
repos.visit=Visit
repos.download=Code Download
repos.pr=PR
repos.commit=Commit
repos.closedIssues=Closed Issue
repos.contributor=Contributor
repos.yes=Yes
repos.no=No


datasets.dataset_manage_panel= Dataset Manage
datasets.owner=Owner


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

@@ -220,6 +220,8 @@ show_only_public=只显示公开的

issues.in_your_repos=属于该用户项目的

contributors=贡献者

[explore]
repos=项目
users=用户
@@ -227,6 +229,7 @@ organizations=组织
images = 云脑镜像
search=搜索
code=代码
data_analysis=数字看板
repo_no_results=未找到匹配的项目。
dataset_no_results = 未找到匹配的数据集。
user_no_results=未找到匹配的用户。
@@ -2166,6 +2169,19 @@ repos.stars=点赞数
repos.forks=派生数
repos.issues=任务数
repos.size=大小
repos.id=ID
repos.projectName=项目名称
repos.isPrivate=私有
repos.openi=OpenI指数
repos.visit=浏览量
repos.download=代码下载量
repos.pr=PR数
repos.commit=Commit数
repos.closedIssues=已解决任务数
repos.contributor=贡献者数
repos.yes=是
repos.no=否


datasets.dataset_manage_panel=数据集管理
datasets.owner=所有者


+ 167
- 18485
package-lock.json
File diff suppressed because it is too large
View File


+ 5
- 1
package.json View File

@@ -19,11 +19,13 @@
"cssnano": "4.1.10",
"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-nodejs": "3.20.11",
"fast-glob": "3.2.2",
"file-loader": "6.0.0",
"file-saver": "2.0.5",
"fomantic-ui": "2.8.4",
"fs": "0.0.1-security",
"highlight.js": "10.4.1",
@@ -55,13 +57,15 @@
"webpack": "4.43.0",
"webpack-cli": "3.3.11",
"webpack-fix-style-only-entries": "0.4.0",
"worker-loader": "2.0.0"
"worker-loader": "2.0.0",
"xlsx": "0.17.3"
},
"devDependencies": {
"eslint": "6.8.0",
"eslint-config-airbnb-base": "14.1.0",
"eslint-plugin-import": "2.20.2",
"eslint-plugin-vue": "6.2.2",
"script-loader": "0.7.2",
"stylelint": "13.3.3",
"stylelint-config-standard": "20.0.0",
"updates": "10.2.11"


BIN
public/img/name.png View File

Before After
Width: 300  |  Height: 300  |  Size: 5.1 kB

BIN
public/img/overview.png View File

Before After
Width: 64  |  Height: 64  |  Size: 1.5 kB

BIN
public/img/pro.png View File

Before After
Width: 64  |  Height: 64  |  Size: 2.0 kB

+ 1
- 0
public/img/pro.svg View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1636355832057" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8359" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><defs><style type="text/css"></style></defs><path d="M1024 767.6928c0 7.168-2.8672 12.0832-8.4992 14.9504L571.2896 1021.8496 569.1392 1021.8496C567.7056 1023.2832 565.5552 1024 562.688 1024c-2.8672 0-5.0176-0.7168-6.4512-2.1504L554.1888 1021.8496 109.9776 782.6432C104.2432 779.776 101.376 774.8608 101.376 767.6928L101.376 255.1808 101.376 253.0304c0-1.3312 0.7168-2.8672 2.1504-4.3008L103.5264 246.5792l4.3008-4.3008 2.1504-2.1504L554.1888 1.024c5.7344-1.3312 11.3664-1.3312 17.1008 0l444.2112 239.2064 2.1504 2.1504 4.3008 4.3008 0 2.1504L1024 253.0304l0 2.1504L1024 767.6928zM135.5776 757.0432l410.0096 222.1056 0-474.112L135.5776 282.9312 135.5776 757.0432zM154.8288 255.1808 562.688 475.136l407.8592-219.9552L562.688 35.2256 154.8288 255.1808zM989.7984 757.0432l0-474.112L579.7888 505.0368l0 474.112L989.7984 757.0432z" p-id="8360"></path></svg>

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

@@ -528,9 +528,10 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Group("/projectboard", func() {

m.Get("/restoreFork", adminReq, repo.RestoreForkNumber)
m.Get("/downloadAll", adminReq, repo.ServeAllProjectsPeriodStatisticsFile)
m.Group("/project", func() {
m.Get("", adminReq, repo.GetAllProjectsPeriodStatistics)

m.Group("/:id", func() {
m.Get("", adminReq, repo.GetProjectLatestStatistics)
m.Get("/period", adminReq, repo.GetProjectPeriodStatistics)


+ 167
- 12
routers/api/v1/repo/repo_dashbord.go View File

@@ -1,8 +1,13 @@
package repo

import (
"encoding/csv"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"
"path"
"strconv"
"time"

@@ -103,6 +108,7 @@ func GetAllProjectsPeriodStatistics(ctx *context.Context) {
ctx.Error(http.StatusBadRequest, ctx.Tr("repo.total_count_get_error"))
return
}
sql := generateSqlByType(ctx, beginTime, endTime, latestDate, q, orderBy, page, pageSize)

projectsPeriodData := ProjectsPeriodData{
RecordBeginTime: recordBeginTime.Format(DATE_FORMAT),
@@ -110,13 +116,146 @@ func GetAllProjectsPeriodStatistics(ctx *context.Context) {
TotalPage: getTotalPage(total, pageSize),
TotalCount: total,
LastUpdatedTime: latestUpdatedTime,
PageRecords: models.GetRepoStatisticByRawSql(generatePageSql(beginTime, endTime, latestDate, q, orderBy, page, pageSize)),
PageRecords: models.GetRepoStatisticByRawSql(sql),
}

ctx.JSON(http.StatusOK, projectsPeriodData)

}

func generateSqlByType(ctx *context.Context, beginTime time.Time, endTime time.Time, latestDate string, q string, orderBy string, page int, pageSize int) string {
sql := ""
if ctx.QueryTrim("type") == "all" {
sql = generateTypeAllSql(beginTime, endTime, latestDate, q, orderBy, page, pageSize)
} else {
sql = generatePageSql(beginTime, endTime, latestDate, q, orderBy, page, pageSize)
}
return sql
}

func ServeAllProjectsPeriodStatisticsFile(ctx *context.Context) {

recordBeginTime, err := getRecordBeginTime()
if err != nil {
log.Error("Can not get record begin time", err)
ctx.Error(http.StatusBadRequest, ctx.Tr("repo.record_begintime_get_err"))
return
}
beginTime, endTime, err := getTimePeroid(ctx, recordBeginTime)
if err != nil {
log.Error("Parameter is wrong", err)
ctx.Error(http.StatusBadRequest, ctx.Tr("repo.parameter_is_wrong"))
return
}
q := ctx.QueryTrim("q")
page := ctx.QueryInt("page")
if page <= 0 {
page = 1
}
pageSize := 1000
orderBy := getOrderBy(ctx)

_, latestDate, err := models.GetRepoStatLastUpdatedTime()
if err != nil {
log.Error("Can not query the last updated time.", err)
ctx.Error(http.StatusBadRequest, ctx.Tr("repo.last_update_time_error"))
return
}

countSql := generateCountSql(beginTime, endTime, latestDate, q)
total, err := models.CountRepoStatByRawSql(countSql)
if err != nil {
log.Error("Can not query total count.", err)
ctx.Error(http.StatusBadRequest, ctx.Tr("repo.total_count_get_error"))
return
}

fileName, frontName := getFileName(ctx, beginTime, endTime)

if err := os.MkdirAll(setting.RadarMap.Path, os.ModePerm); err != nil {
ctx.Error(http.StatusBadRequest, fmt.Errorf("Failed to create dir %s: %v", setting.AvatarUploadPath, err).Error())
}

totalPage := getTotalPage(total, pageSize)

f, e := os.Create(fileName)
defer f.Close()
if e != nil {
log.Warn("Failed to create file", e)
}
writer := csv.NewWriter(f)
writer.Write(allProjectsPeroidHeader(ctx))
for i := 0; i <= totalPage; i++ {

pageRecords := models.GetRepoStatisticByRawSql(generateSqlByType(ctx, beginTime, endTime, latestDate, q, orderBy, i+1, pageSize))
for _, record := range pageRecords {
e = writer.Write(allProjectsPeroidValues(record, ctx))
if e != nil {
log.Warn("Failed to write record", e)
}
}
writer.Flush()

}

ctx.ServeFile(fileName, url.QueryEscape(frontName))

}

func getFileName(ctx *context.Context, beginTime time.Time, endTime time.Time) (string, string) {
baseName := setting.RadarMap.Path + "/项目分析_"

if ctx.QueryTrim("q") != "" {
baseName = baseName + ctx.QueryTrim("q") + "_"
}
if ctx.QueryTrim("type") == "all" {
baseName = baseName + "所有"
} else {
baseName = baseName + beginTime.AddDate(0, 0, -1).Format(DATE_FORMAT) + "_" + endTime.AddDate(0, 0, -1).Format(DATE_FORMAT)
}
frontName := baseName + ".csv"
localName := baseName + "_" + strconv.FormatInt(time.Now().Unix(), 10) + ".csv"
return localName, path.Base(frontName)
}

func ClearUnusedStatisticsFile() {
fileInfos, err := ioutil.ReadDir(setting.RadarMap.Path)
if err != nil {
log.Warn("can not read dir: "+setting.RadarMap.Path, err)
return
}

for _, fileInfo := range fileInfos {
if !fileInfo.IsDir() && fileInfo.ModTime().Before(time.Now().AddDate(0, 0, -1)) {
os.Remove(path.Join(setting.RadarMap.Path, fileInfo.Name()))
}
}

}

func allProjectsPeroidHeader(ctx *context.Context) []string {

return []string{ctx.Tr("admin.repos.id"), ctx.Tr("admin.repos.projectName"), ctx.Tr("admin.repos.isPrivate"), ctx.Tr("admin.repos.openi"), ctx.Tr("admin.repos.visit"), ctx.Tr("admin.repos.download"), ctx.Tr("admin.repos.pr"), ctx.Tr("admin.repos.commit"),
ctx.Tr("admin.repos.watches"), ctx.Tr("admin.repos.stars"), ctx.Tr("admin.repos.forks"), ctx.Tr("admin.repos.issues"), ctx.Tr("admin.repos.closedIssues"), ctx.Tr("admin.repos.contributor")}

}

func allProjectsPeroidValues(rs *models.RepoStatistic, ctx *context.Context) []string {
return []string{strconv.FormatInt(rs.RepoID, 10), rs.Name, getIsPrivateDisplay(rs.IsPrivate, ctx), strconv.FormatFloat(rs.RadarTotal, 'f', 2, 64),
strconv.FormatInt(rs.NumVisits, 10), strconv.FormatInt(rs.NumDownloads, 10), strconv.FormatInt(rs.NumPulls, 10), strconv.FormatInt(rs.NumCommits, 10),
strconv.FormatInt(rs.NumWatches, 10), strconv.FormatInt(rs.NumStars, 10), strconv.FormatInt(rs.NumForks, 10), strconv.FormatInt(rs.NumIssues, 10),
strconv.FormatInt(rs.NumClosedIssues, 10), strconv.FormatInt(rs.NumContributor, 10),
}
}

func getIsPrivateDisplay(private bool, ctx *context.Context) string {
if private {
return ctx.Tr("admin.repos.yes")
} else {
return ctx.Tr("admin.repos.no")
}
}

func GetProjectLatestStatistics(ctx *context.Context) {
repoId := ctx.Params(":id")
recordBeginTime, err := getRecordBeginTime()
@@ -235,11 +374,11 @@ func generateTargetSql(beginTime time.Time, endTime time.Time, repoId int64) str
return sql
}

func generateCountSql(beginTime time.Time, endTime time.Time, yesterday string, q string) string {
func generateCountSql(beginTime time.Time, endTime time.Time, latestDate string, q string) string {
countSql := "SELECT count(*) FROM " +
"(SELECT repo_id FROM repo_statistic where created_unix >=" + strconv.FormatInt(beginTime.Unix(), 10) +
" and created_unix<" + strconv.FormatInt(endTime.Unix(), 10) + " group by repo_id) A," +
"(SELECT repo_id,name,is_private,radar_total from public.repo_statistic where date='" + yesterday + "') B" +
"(SELECT repo_id,name,is_private,radar_total from public.repo_statistic where date='" + latestDate + "') B" +
" where A.repo_id=B.repo_id"
if q != "" {
countSql = countSql + " and B.name like '%" + q + "%'"
@@ -247,18 +386,34 @@ func generateCountSql(beginTime time.Time, endTime time.Time, yesterday string,
return countSql
}

func generatePageSql(beginTime time.Time, endTime time.Time, yesterday string, q string, orderBy string, page int, pageSize int) string {
countSql := "SELECT A.repo_id,name,is_private,radar_total,num_watches,num_visits,num_downloads,num_pulls,num_commits,num_stars,num_forks,num_issues,num_closed_issues,num_contributor FROM " +
func generateTypeAllSql(beginTime time.Time, endTime time.Time, latestDate string, q string, orderBy string, page int, pageSize int) string {
sql := "SELECT A.repo_id,name,is_private,radar_total,num_watches,num_visits,num_downloads,num_pulls,num_commits,num_stars,num_forks,num_issues,num_closed_issues,num_contributor FROM " +
"(SELECT repo_id,sum(num_visits) as num_visits " +
" FROM repo_statistic where created_unix >=" + strconv.FormatInt(beginTime.Unix(), 10) +
" and created_unix<" + strconv.FormatInt(endTime.Unix(), 10) + " group by repo_id) A," +
"(SELECT repo_id,name,is_private,radar_total,num_watches,num_downloads,num_pulls,num_commits,num_stars,num_forks,num_issues,num_closed_issues,num_contributor from public.repo_statistic where date='" + latestDate + "') B" +
" where A.repo_id=B.repo_id"

if q != "" {
sql = sql + " and name like '%" + q + "%'"
}
sql = sql + " order by " + orderBy + " desc,repo_id" + " limit " + strconv.Itoa(pageSize) + " offset " + strconv.Itoa((page-1)*pageSize)
return sql
}

func generatePageSql(beginTime time.Time, endTime time.Time, latestDate string, q string, orderBy string, page int, pageSize int) string {

sql := "SELECT A.repo_id,name,is_private,radar_total,num_watches,num_visits,num_downloads,num_pulls,num_commits,num_stars,num_forks,num_issues,num_closed_issues,num_contributor FROM " +
"(SELECT repo_id,sum(num_watches_added) as num_watches,sum(num_visits) as num_visits, sum(num_downloads_added) as num_downloads,sum(num_pulls_added) as num_pulls,sum(num_commits_added) as num_commits,sum(num_stars_added) as num_stars,sum(num_forks_added) num_forks,sum(num_issues_added) as num_issues,sum(num_closed_issues_added) as num_closed_issues,sum(num_contributor_added) as num_contributor " +
" FROM repo_statistic where created_unix >=" + strconv.FormatInt(beginTime.Unix(), 10) +
" and created_unix<" + strconv.FormatInt(endTime.Unix(), 10) + " group by repo_id) A," +
"(SELECT repo_id,name,is_private,radar_total from public.repo_statistic where date='" + yesterday + "') B" +
"(SELECT repo_id,name,is_private,radar_total from public.repo_statistic where date='" + latestDate + "') B" +
" where A.repo_id=B.repo_id"
if q != "" {
countSql = countSql + " and B.name like '%" + q + "%'"
sql = sql + " and B.name like '%" + q + "%'"
}
countSql = countSql + " order by " + orderBy + " desc,A.repo_id" + " limit " + strconv.Itoa(pageSize) + " offset " + strconv.Itoa((page-1)*pageSize)
return countSql
sql = sql + " order by " + orderBy + " desc,A.repo_id" + " limit " + strconv.Itoa(pageSize) + " offset " + strconv.Itoa((page-1)*pageSize)
return sql
}

func getOrderBy(ctx *context.Context) string {
@@ -301,7 +456,7 @@ func getTimePeroid(ctx *context.Context, recordBeginTime time.Time) (time.Time,
endTimeStr := ctx.QueryTrim("endTime")
var beginTime time.Time
var endTime time.Time
var err error
if queryType != "" {

if queryType == "all" {
@@ -346,12 +501,12 @@ func getTimePeroid(ctx *context.Context, recordBeginTime time.Time) (time.Time,

} else {

beginTime, err := time.Parse("2006-01-02", beginTimeStr)
beginTime, err = time.Parse("2006-01-02", beginTimeStr)
if err != nil {
return now, now, err
}

endTime, err := time.Parse("2006-01-02", endTimeStr)
endTime, err = time.Parse("2006-01-02", endTimeStr)
if err != nil {
return now, now, err
}


+ 7
- 2
routers/home.go View File

@@ -33,8 +33,9 @@ const (
// tplExploreOrganizations explore organizations page template
tplExploreOrganizations base.TplName = "explore/organizations"
// tplExploreCode explore code page template
tplExploreCode base.TplName = "explore/code"
tplExploreImages base.TplName = "explore/images"
tplExploreCode base.TplName = "explore/code"
tplExploreImages base.TplName = "explore/images"
tplExploreExploreDataAnalysis base.TplName = "explore/data_analysis"
)

// Home render home page
@@ -501,6 +502,10 @@ func ExploreImages(ctx *context.Context) {
ctx.HTML(200, tplExploreImages)
}

func ExploreDataAnalysis(ctx *context.Context) {
ctx.HTML(200, tplExploreExploreDataAnalysis)
}

// NotFound render 404 page
func NotFound(ctx *context.Context) {
ctx.Data["Title"] = "Page Not Found"


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

@@ -204,7 +204,7 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) {
resourceSpecId := form.ResourceSpecId

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



+ 2
- 229
routers/repo/modelarts.go View File

@@ -27,15 +27,10 @@ import (
)

const (
// tplModelArtsNotebookIndex base.TplName = "repo/modelarts/notebook/index"
tplModelArtsNotebookIndex base.TplName = "repo/modelarts/notebook/index"
tplModelArtsNotebookNew base.TplName = "repo/modelarts/notebook/new"
tplModelArtsNotebookShow base.TplName = "repo/modelarts/notebook/show"

tplModelArtsIndex base.TplName = "repo/modelarts/index"
tplModelArtsNew base.TplName = "repo/modelarts/new"
tplModelArtsShow base.TplName = "repo/modelarts/show"

tplModelArtsTrainJobIndex base.TplName = "repo/modelarts/trainjob/index"
tplModelArtsTrainJobNew base.TplName = "repo/modelarts/trainjob/new"
tplModelArtsTrainJobShow base.TplName = "repo/modelarts/trainjob/show"
@@ -51,229 +46,6 @@ func MustEnableModelArts(ctx *context.Context) {
}
}

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

ciTasks, count, err := models.Cloudbrains(&models.CloudbrainsOptions{
ListOptions: models.ListOptions{
Page: page,
PageSize: setting.UI.IssuePagingNum,
},
RepoID: repo.ID,
Type: models.TypeCloudBrainTwo,
})
if err != nil {
ctx.ServerError("Cloudbrain", err)
return
}

for i, task := range ciTasks {
if task.Status == string(models.JobRunning) {
ciTasks[i].CanDebug = true
} else {
ciTasks[i].CanDebug = false
}

ciTasks[i].CanDel = models.CanDelJob(ctx.IsSigned, ctx.User, task)
}

pager := context.NewPagination(int(count), setting.UI.IssuePagingNum, page, 5)
pager.SetDefaultParams(ctx)
ctx.Data["Page"] = pager

ctx.Data["PageIsCloudBrain"] = true
ctx.Data["Tasks"] = ciTasks
ctx.HTML(200, tplModelArtsIndex)
}

func ModelArtsNew(ctx *context.Context) {
ctx.Data["PageIsCloudBrain"] = true

t := time.Now()
var jobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:]
ctx.Data["job_name"] = jobName

attachs, err := models.GetModelArtsUserAttachments(ctx.User.ID)
if err != nil {
ctx.ServerError("GetAllUserAttachments failed:", err)
return
}

ctx.Data["attachments"] = attachs
ctx.Data["dataset_path"] = modelarts.DataSetMountPath
ctx.Data["env"] = modelarts.NotebookEnv
ctx.Data["notebook_type"] = modelarts.NotebookType
if modelarts.FlavorInfos == nil {
json.Unmarshal([]byte(setting.FlavorInfos), &modelarts.FlavorInfos)
}
ctx.Data["flavors"] = modelarts.FlavorInfos.FlavorInfo
ctx.HTML(200, tplModelArtsNew)
}

func ModelArtsCreate(ctx *context.Context, form auth.CreateModelArtsForm) {
ctx.Data["PageIsCloudBrain"] = true
jobName := form.JobName
uuid := form.Attachment
description := form.Description
//repo := ctx.Repo.Repository
if !jobNamePattern.MatchString(jobName) {
ctx.RenderWithErr(ctx.Tr("repo.cloudbrain_jobname_err"), tplModelArtsNew, &form)
return
}
err := modelarts.GenerateTask(ctx, jobName, uuid, description)
if err != nil {
ctx.RenderWithErr(err.Error(), tplModelArtsNew, &form)
return
}

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

func ModelArtsShow(ctx *context.Context) {
ctx.Data["PageIsCloudBrain"] = true

var jobID = ctx.Params(":jobid")
task, err := models.GetCloudbrainByJobID(jobID)
if err != nil {
ctx.Data["error"] = err.Error()
ctx.RenderWithErr(err.Error(), tplModelArtsIndex, nil)
return
}

result, err := modelarts.GetJob(jobID)
if err != nil {
ctx.Data["error"] = err.Error()
ctx.RenderWithErr(err.Error(), tplModelArtsIndex, nil)
return
}

if result != nil {
task.Status = result.Status
err = models.UpdateJob(task)
if err != nil {
ctx.Data["error"] = err.Error()
ctx.RenderWithErr(err.Error(), tplModelArtsIndex, nil)
return
}

createTime, _ := com.StrTo(result.CreationTimestamp).Int64()
result.CreateTime = time.Unix(int64(createTime/1000), 0).Format("2006-01-02 15:04:05")
endTime, _ := com.StrTo(result.LatestUpdateTimestamp).Int64()
result.LatestUpdateTime = time.Unix(int64(endTime/1000), 0).Format("2006-01-02 15:04:05")
result.QueuingInfo.BeginTime = time.Unix(int64(result.QueuingInfo.BeginTimestamp/1000), 0).Format("2006-01-02 15:04:05")
result.QueuingInfo.EndTime = time.Unix(int64(result.QueuingInfo.EndTimestamp/1000), 0).Format("2006-01-02 15:04:05")
}

ctx.Data["task"] = task
ctx.Data["jobID"] = jobID
ctx.Data["result"] = result
ctx.HTML(200, tplModelArtsShow)
}

func ModelArtsDebug(ctx *context.Context) {
var jobID = ctx.Params(":jobid")
_, err := models.GetCloudbrainByJobID(jobID)
if err != nil {
ctx.ServerError("GetCloudbrainByJobID failed", err)
return
}

result, err := modelarts.GetJob(jobID)
if err != nil {
ctx.RenderWithErr(err.Error(), tplModelArtsIndex, nil)
return
}

res, err := modelarts.GetJobToken(jobID)
if err != nil {
ctx.RenderWithErr(err.Error(), tplModelArtsIndex, nil)
return
}

urls := strings.Split(result.Spec.Annotations.Url, "/")
urlPrefix := result.Spec.Annotations.TargetDomain
for i, url := range urls {
if i > 2 {
urlPrefix += "/" + url
}
}

//urlPrefix := result.Spec.Annotations.TargetDomain + "/modelarts/internal/hub/notebook/user/" + task.JobID
log.Info(urlPrefix)
debugUrl := urlPrefix + "?token=" + res.Token
ctx.Redirect(debugUrl)
}

func ModelArtsStop(ctx *context.Context) {
var jobID = ctx.Params(":jobid")
log.Info(jobID)
task, err := models.GetCloudbrainByJobID(jobID)
if err != nil {
ctx.ServerError("GetCloudbrainByJobID failed", err)
return
}

if task.Status != string(models.JobRunning) {
log.Error("the job(%s) is not running", task.JobName)
ctx.ServerError("the job is not running", errors.New("the job is not running"))
return
}

param := models.NotebookAction{
Action: models.ActionStop,
}
res, err := modelarts.StopJob(jobID, param)
if err != nil {
log.Error("StopJob(%s) failed:%v", task.JobName, err.Error())
ctx.ServerError("StopJob failed", err)
return
}

task.Status = res.CurrentStatus
err = models.UpdateJob(task)
if err != nil {
ctx.ServerError("UpdateJob failed", err)
return
}

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

func ModelArtsDel(ctx *context.Context) {
var jobID = ctx.Params(":jobid")
task, err := models.GetCloudbrainByJobID(jobID)
if err != nil {
ctx.ServerError("GetCloudbrainByJobID failed", err)
return
}

if task.Status != string(models.ModelArtsCreateFailed) && task.Status != string(models.ModelArtsStartFailed) && task.Status != string(models.ModelArtsStopped) {
log.Error("the job(%s) has not been stopped", task.JobName)
ctx.ServerError("the job has not been stopped", errors.New("the job has not been stopped"))
return
}

_, err = modelarts.DelJob(jobID)
if err != nil {
log.Error("DelJob(%s) failed:%v", task.JobName, err.Error())
ctx.ServerError("DelJob failed", err)
return
}

err = models.DeleteJob(task)
if err != nil {
ctx.ServerError("DeleteJob failed", err)
return
}

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

func NotebookIndex(ctx *context.Context) {
MustEnableModelArts(ctx)
repo := ctx.Repo.Repository
@@ -343,8 +115,9 @@ func NotebookCreate(ctx *context.Context, form auth.CreateModelArtsNotebookForm)
jobName := form.JobName
uuid := form.Attachment
description := form.Description
flavor := form.Flavor

err := modelarts.GenerateTask(ctx, jobName, uuid, description)
err := modelarts.GenerateTask(ctx, jobName, uuid, description, flavor)
if err != nil {
ctx.RenderWithErr(err.Error(), tplModelArtsNotebookNew, &form)
return


+ 120
- 36
routers/repo/view.go View File

@@ -12,8 +12,10 @@ import (
"fmt"
gotemplate "html/template"
"io/ioutil"
"net/http"
"net/url"
"path"
"sort"
"strings"
"time"

@@ -31,11 +33,12 @@ import (
)

const (
tplRepoEMPTY base.TplName = "repo/empty"
tplRepoHome base.TplName = "repo/home"
tplWatchers base.TplName = "repo/watchers"
tplForks base.TplName = "repo/forks"
tplMigrating base.TplName = "repo/migrating"
tplRepoEMPTY base.TplName = "repo/empty"
tplRepoHome base.TplName = "repo/home"
tplWatchers base.TplName = "repo/watchers"
tplForks base.TplName = "repo/forks"
tplMigrating base.TplName = "repo/migrating"
tplContributors base.TplName = "repo/contributors"
)

type namedBlob struct {
@@ -575,19 +578,29 @@ func safeURL(address string) string {
}

type ContributorInfo struct {
UserInfo *models.User // nil for contributor who is not a registered user
Email string
CommitCnt int
UserInfo *models.User // nil for contributor who is not a registered user
RelAvatarLink string `json:"rel_avatar_link"`
UserName string `json:"user_name"`
Email string `json:"email"`
CommitCnt int `json:"commit_cnt"`
}

func getContributorInfo(contributorInfos []*ContributorInfo, email string) *ContributorInfo{
type GetContributorsInfo struct {
ErrorCode int `json:"error_code"`
ErrorMsg string `json:"error_msg"`
Count int `json:"count"`
ContributorInfo []*ContributorInfo `json:"contributor_info"`
}

func getContributorInfo(contributorInfos []*ContributorInfo, email string) *ContributorInfo {
for _, c := range contributorInfos {
if strings.Compare(c.Email,email) == 0 {
if strings.Compare(c.Email, email) == 0 {
return c
}
}
return nil
}

// Home render repository home page
func Home(ctx *context.Context) {
if len(ctx.Repo.Units) > 0 {
@@ -596,35 +609,41 @@ func Home(ctx *context.Context) {
if err == nil && contributors != nil {
startTime := time.Now()
var contributorInfos []*ContributorInfo
contributorInfoHash:= make(map[string]*ContributorInfo)
contributorInfoHash := make(map[string]*ContributorInfo)
count := 0
for _, c := range contributors {
if strings.Compare(c.Email,"") == 0 {
if count >= 25 {
continue
}
if strings.Compare(c.Email, "") == 0 {
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 {
if existedContributorInfo, ok := contributorInfoHash[user.Email]; ok {
// existed: same primary email, different committer name
existedContributorInfo.CommitCnt += c.CommitCnt
}else{
} else {
// new committer info
var newContributor = &ContributorInfo{
user, user.Email,c.CommitCnt,
user, user.RelAvatarLink(), user.Name, user.Email, c.CommitCnt,
}
contributorInfos = append(contributorInfos, newContributor )
count++
contributorInfos = append(contributorInfos, newContributor)
contributorInfoHash[user.Email] = newContributor
}
} else {
// committer is not system user
if existedContributorInfo,ok:=contributorInfoHash[c.Email];ok {
if existedContributorInfo, ok := contributorInfoHash[c.Email]; ok {
// existed: same primary email, different committer name
existedContributorInfo.CommitCnt += c.CommitCnt
}else{
} else {
var newContributor = &ContributorInfo{
user, c.Email,c.CommitCnt,
user, "", "",c.Email, c.CommitCnt,
}
count++
contributorInfos = append(contributorInfos, newContributor)
contributorInfoHash[c.Email] = newContributor
}
@@ -632,7 +651,7 @@ func Home(ctx *context.Context) {
}
ctx.Data["ContributorInfo"] = contributorInfos
var duration = time.Since(startTime)
log.Info("getContributorInfo cost: %v seconds",duration.Seconds())
log.Info("getContributorInfo cost: %v seconds", duration.Seconds())
}
if ctx.Repo.Repository.IsBeingCreated() {
task, err := models.GetMigratingTask(ctx.Repo.Repository.ID)
@@ -699,13 +718,13 @@ func renderLicense(ctx *context.Context) {
log.Error("failed to get license content: %v, err:%v", f, err)
continue
}
if bytes.Compare(buf,license) == 0 {
log.Info("got matched license:%v",f)
if bytes.Compare(buf, license) == 0 {
log.Info("got matched license:%v", f)
ctx.Data["LICENSE"] = f
return
}
}
log.Info("not found matched license,repo:%v",ctx.Repo.Repository.Name)
log.Info("not found matched license,repo:%v", ctx.Repo.Repository.Name)
}

func renderLanguageStats(ctx *context.Context) {
@@ -806,31 +825,31 @@ func renderCode(ctx *context.Context) {
baseGitRepo, err := git.OpenRepository(ctx.Repo.Repository.BaseRepo.RepoPath())
defer baseGitRepo.Close()
if err != nil {
log.Error("error open baseRepo:%s",ctx.Repo.Repository.BaseRepo.RepoPath())
log.Error("error open baseRepo:%s", ctx.Repo.Repository.BaseRepo.RepoPath())
ctx.Data["FetchUpstreamCnt"] = -1 // minus value indicates error
}else{
if _,error:= baseGitRepo.GetBranch(ctx.Repo.BranchName);error==nil{
} else {
if _, error := baseGitRepo.GetBranch(ctx.Repo.BranchName); error == nil {
//base repo has the same branch, then compare between current repo branch and base repo's branch
compareUrl := ctx.Repo.BranchName + "..." + ctx.Repo.Repository.BaseRepo.OwnerName + "/" + ctx.Repo.Repository.BaseRepo.Name + ":" + ctx.Repo.BranchName
ctx.SetParams("*",compareUrl)
compareUrl := ctx.Repo.BranchName + "..." + ctx.Repo.Repository.BaseRepo.OwnerName + "/" + ctx.Repo.Repository.BaseRepo.Name + ":" + ctx.Repo.BranchName
ctx.SetParams("*", compareUrl)
ctx.Data["UpstreamSameBranchName"] = true
}else{
} else {
//else, compare between current repo branch and base repo's default branch
compareUrl := ctx.Repo.BranchName + "..." + ctx.Repo.Repository.BaseRepo.OwnerName + "/" + ctx.Repo.Repository.BaseRepo.Name + ":" + ctx.Repo.Repository.BaseRepo.DefaultBranch
ctx.SetParams("*",compareUrl)
compareUrl := ctx.Repo.BranchName + "..." + ctx.Repo.Repository.BaseRepo.OwnerName + "/" + ctx.Repo.Repository.BaseRepo.Name + ":" + ctx.Repo.Repository.BaseRepo.DefaultBranch
ctx.SetParams("*", compareUrl)
ctx.Data["UpstreamSameBranchName"] = false
}
_, _, headGitRepo, compareInfo, _, _ := ParseCompareInfo(ctx)
defer headGitRepo.Close()
if compareInfo!= nil {
if compareInfo.Commits!=nil {
log.Info("compareInfoCommits数量:%d",compareInfo.Commits.Len())
if compareInfo != nil {
if compareInfo.Commits != nil {
log.Info("compareInfoCommits数量:%d", compareInfo.Commits.Len())
ctx.Data["FetchUpstreamCnt"] = compareInfo.Commits.Len()
}else{
} else {
log.Info("compareInfo nothing different")
ctx.Data["FetchUpstreamCnt"] = 0
}
}else{
} else {
ctx.Data["FetchUpstreamCnt"] = -1 // minus value indicates error
}
}
@@ -898,3 +917,68 @@ func Forks(ctx *context.Context) {

ctx.HTML(200, tplForks)
}

func Contributors(ctx *context.Context) {
ctx.Data["PageIsViewCode"] = true
ctx.HTML(http.StatusOK, tplContributors)
}

func ContributorsAPI(ctx *context.Context) {
count := 0
errorCode := 0
errorMsg := ""
contributors, err := git.GetContributors(ctx.Repo.Repository.RepoPath())
var contributorInfos []*ContributorInfo
if err == nil && contributors != nil {
contributorInfoHash := make(map[string]*ContributorInfo)
for _, c := range contributors {
if strings.Compare(c.Email, "") == 0 {
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 = &ContributorInfo{
user, user.RelAvatarLink(),user.Name, user.Email,c.CommitCnt,
}
count++
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 = &ContributorInfo{
user, "", "",c.Email,c.CommitCnt,
}
count++
contributorInfos = append(contributorInfos, newContributor)
contributorInfoHash[c.Email] = newContributor
}
}
}
sort.Slice(contributorInfos, func(i, j int) bool {
return contributorInfos[i].CommitCnt > contributorInfos[j].CommitCnt
})
} else {
log.Error("GetContributors failed: %v", err)
errorCode = -1
errorMsg = err.Error()
}

ctx.JSON(http.StatusOK, GetContributorsInfo{
ErrorCode: errorCode,
ErrorMsg: errorMsg,
Count: count,
ContributorInfo: contributorInfos,
})
}

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

@@ -325,6 +325,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/organizations", routers.ExploreOrganizations)
m.Get("/code", routers.ExploreCode)
m.Get("/images", routers.ExploreImages)
m.Get("/data_analysis", routers.ExploreDataAnalysis)
}, ignSignIn)
m.Combo("/install", routers.InstallInit).Get(routers.Install).
Post(bindIgnErr(auth.InstallForm{}), routers.InstallPost)
@@ -795,6 +796,8 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/tool/query_user_static_page", repo.QueryUserStaticDataPage)
// Grouping for those endpoints not requiring authentication
m.Group("/:username/:reponame", func() {
m.Get("/contributors", repo.Contributors)
m.Get("/contributors/list", repo.ContributorsAPI)
m.Group("/milestone", func() {
m.Get("/:id", repo.MilestoneIssuesAndPulls)
}, reqRepoIssuesOrPullsReader, context.RepoRef())


+ 1
- 0
templates/base/head_navbar.tmpl View File

@@ -37,6 +37,7 @@
<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>
<a class="item" href="{{AppSubUrl}}/explore/data_analysis">{{.i18n.Tr "explore.data_analysis"}}</a>
</div>
</div>
{{else if .IsLandingPageHome}}


+ 1
- 0
templates/base/head_navbar_home.tmpl View File

@@ -29,6 +29,7 @@
<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>
<a class="item" href="{{AppSubUrl}}/explore/data_analysis">{{.i18n.Tr "explore.data_analysis"}}</a>
</div>
</div>
{{else if .IsLandingPageHome}}


+ 14
- 0
templates/explore/data_analysis.tmpl View File

@@ -0,0 +1,14 @@
{{template "base/head" .}}
<div id="data_analysis" style="height: 100%;">

</div>

{{template "base/footer" .}}

<style>
.full.height {
flex-grow: 1;
padding-bottom: 53px;
}
</style>

+ 16
- 3
templates/home.tmpl View File

@@ -7,7 +7,11 @@
</div>
</h1>
<p class="am-lh-18">免费私有代码仓库,免费计算资源,大容量数据存储,<br>多类型硬件环境(GPU、NPU),AI开发流水线(开发-调试-训练-迭代)</p>
<a class="circular ui secondary button" href="{{AppSubUrl}}/user/sign_up">立即使用 <i class="right arrow icon"></i></a>
{{if .IsSigned}}
<a class="circular ui secondary button" href="{{AppSubUrl}}/dashboard">立即使用 <i class="right arrow icon"></i></a>
{{else}}
<a class="circular ui secondary button" href="{{AppSubUrl}}/user/login">立即使用 <i class="right arrow icon"></i></a>
{{end}}
<div class="bannerpic"><img class="ui fluid image" src="/img/gitopeni-index-01.svg"></div>
</div>
</div><!-- end segment -->
@@ -33,7 +37,11 @@
<p class="am-lh-18">在这里为你和你的团队创建项目,基于Git工具,提交记录或者回滚代码修改。<br>
不论是公开或者私有仓库,都可免费使用所有功能。<br>
尽情将你喜欢的代码都放在这里,仓库数量、存储容量不受限</p>
<a class="ui blue basic button am-mt-20" href="{{AppSubUrl}}/user/sign_up">立即使用</a>
{{if .IsSigned}}
<a class="ui blue basic button am-mt-20" href="{{AppSubUrl}}/dashboard">立即使用 </a>
{{else}}
<a class="ui blue basic button am-mt-20" href="{{AppSubUrl}}/user/login">立即使用 </a>
{{end}}
</div>
<div class="ten wide column computer only i-code-pic am-pt-30">
<img class="ui fluid rounded image am-shadow-2 am-mt-10" src="/img/i-code-pic.jpg" style="position: absolute;">
@@ -184,7 +192,12 @@
开发者可以根据使用需求,自由选择相应计算资源,可以测试模型在不同硬件环境下的适配能力、性能、稳定性等<br>
如果您的模型需要更多的计算资源,也可以单独申请<br>
</p>
<a class="ui blue basic button am-mt-20" href="{{AppSubUrl}}/user/sign_up">马上使用</a> <a class="ui grey basic button am-mt-20" href="mailto:aiforge@openi.org.cn">单独申请</a>
{{if .IsSigned}}
<a class="ui blue basic button am-mt-20" href="{{AppSubUrl}}/dashboard">立即使用 </a><a class="ui grey basic button am-mt-20" href="mailto:aiforge@openi.org.cn">单独申请</a>
{{else}}
<a class="ui blue basic button am-mt-20" href="{{AppSubUrl}}/user/login">立即使用 </a><a class="ui grey basic button am-mt-20" href="mailto:aiforge@openi.org.cn">单独申请</a>
{{end}}
</div>
</div>
</div>


+ 9
- 0
templates/repo/contributors.tmpl View File

@@ -0,0 +1,9 @@
{{template "base/head" .}}
<div class="repository watchers">
{{template "repo/header" .}}
<div class="ui container" id="Contributors">
</div>
</div>
{{template "base/footer" .}}

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

@@ -93,7 +93,7 @@
<div class="ui tabular stackable menu navbar">
{{if .Permission.CanRead $.UnitTypeCode}}
<div class="dropdown-menu">
<a class="{{if or .PageIsViewCode .PageIsReleaseList .PageIsWiki .PageIsActivity}}active{{end}} item" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/{{.BranchNameSubURL | EscapePound}}{{end}}">
<a class="{{if or .PageIsViewCode .PageIsReleaseList .PageIsWiki .PageIsActivity .PageIsViewCode}}active{{end}} item" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/{{.BranchNameSubURL | EscapePound}}{{end}}">
<span>{{svg "octicon-code" 16}} {{.i18n.Tr "repo.code"}} <i class="dropdown icon"></i></span>
</a>
<div class="dropdown-content">


+ 13
- 7
templates/repo/home.tmpl View File

@@ -4,7 +4,7 @@
font-size: 1.0em;
margin-bottom: 1.0rem;
}
#contributorInfo > a:nth-child(n+25){
#contributorInfo > a:nth-child(n+26){
display:none;
}
#contributorInfo > a{
@@ -329,9 +329,15 @@

<div>
<h4 class="ui header">
{{$lenCon := len .ContributorInfo}}
{{if lt $lenCon 25 }}
<strong>贡献者 ({{len .ContributorInfo}})</strong>
{{else}}
<strong>贡献者 ({{len .ContributorInfo}}+)</strong>
{{end}}
<div class="ui right">
<a class="membersmore text grey" href="javascript:;">全部 {{svg "octicon-chevron-right" 16}}</a>
<a class="membersmore text grey" href="{{.RepoLink}}/contributors">全部 {{svg "octicon-chevron-right" 16}}</a>
</div>
</h4>
<div class="ui members" id="contributorInfo">
@@ -353,10 +359,10 @@
</div>

<script type="text/javascript">
$(document).ready(function(){
$(".membersmore").click(function(){
$("#contributorInfo > a:nth-child(n+25)").show();
});
});
// $(document).ready(function(){
// $(".membersmore").click(function(){
// $("#contributorInfo > a:nth-child(n+25)").show();
// });
// });
</script>
{{template "base/footer" .}}

+ 1
- 1
templates/repo/issue/labels.tmpl View File

@@ -4,7 +4,7 @@
<div class="ui container">
<div class="ui two column stackable grid">
<div class="column" style="display: flex;align-items: center;">
<div class="ui large breadcrumb">
<div class="ui breadcrumb">
<a class="section" href="{{.RepoLink}}/issues">{{.i18n.Tr "repo.issues"}}</a>
<div class="divider"> / </div>
<div class="action section">{{.Title | RenderEmoji}}</div>


+ 1
- 1
templates/repo/issue/milestone_issues.tmpl View File

@@ -4,7 +4,7 @@
<div class="ui container">
<div class="ui three column stackable grid">
<div class="column" style="display: flex;align-items: center;">
<div class="ui large breadcrumb">
<div class="ui breadcrumb">
<a class="section" href="{{.RepoLink}}/issues">{{.i18n.Tr "repo.issues"}}</a>
<div class="divider"> / </div>
<a class="section" href="{{.RepoLink}}/milestones">{{.i18n.Tr "repo.milestones"}}</a>


+ 1
- 1
templates/repo/issue/milestone_new.tmpl View File

@@ -4,7 +4,7 @@
<div class="ui container">
<div class="ui two column stackable grid">
<div class="column" style="display: flex;align-items: center;">
<div class="ui large breadcrumb">
<div class="ui breadcrumb">
<a class="section" href="{{.RepoLink}}/issues">{{.i18n.Tr "repo.issues"}}</a>
<div class="divider"> / </div>
<a class="section" href="{{.RepoLink}}/milestones">{{.i18n.Tr "repo.milestones"}}</a>


+ 1
- 1
templates/repo/issue/milestones.tmpl View File

@@ -4,7 +4,7 @@
<div class="ui container">
<div class="ui two column stackable grid">
<div class="column" style="display: flex;align-items: center;">
<div class="ui large breadcrumb">
<div class="ui breadcrumb">
<a class="section" href="{{.RepoLink}}/issues">{{.i18n.Tr "repo.issues"}}</a>
<div class="divider"> / </div>
<div class="action section">{{.Title | RenderEmoji}}</div>


+ 1
- 1
templates/repo/issue/new.tmpl View File

@@ -4,7 +4,7 @@
<div class="ui container">
<div class="ui two column stackable grid">
<div class="column" style="display: flex;align-items: center;">
<div class="ui large breadcrumb">
<div class="ui breadcrumb">
<a class="section" href="{{.RepoLink}}/issues">{{.i18n.Tr "repo.issues"}}</a>
<div class="divider"> / </div>
<div class="action section">{{.i18n.Tr "repo.issues.new"}}</div>


+ 2
- 2
templates/repo/issue/view.tmpl View File

@@ -5,13 +5,13 @@
<div class="ui two column stackable grid">
<div class="column" style="display: flex;align-items: center;">
{{if .PageIsIssueList}}
<div class="ui large breadcrumb">
<div class="ui breadcrumb">
<a class="section" href="{{.RepoLink}}/issues">{{.i18n.Tr "repo.issues"}}</a>
<div class="divider"> / </div>
<div class="action section">{{.i18n.Tr "repo.issues_detail"}}</div>
</div>
{{else}}
<div class="ui large breadcrumb">
<div class="ui breadcrumb">
<a class="section" href="{{.RepoLink}}/pulls">{{.i18n.Tr "repo.pulls"}}</a>
<div class="divider"> / </div>
<div class="action section">{{.i18n.Tr "repo.issues_detail"}}</div>


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

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

<style>
.selectcloudbrain .active.item{
color: #0087f5 !important;
border: 1px solid #0087f5;
margin: -1px;
background: #FFF !important;
}
#deletemodel {
width: 100%;
height: 100%;
}
/* 弹窗 */

#mask {
position: fixed;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
filter: alpha(opacity=60);
background-color: #777;
z-index: 1000;
display: none;
opacity: 0.8;
-moz-opacity: 0.5;
padding-top: 100px;
color: #000000
}

#loadingPage {
margin: 200px auto;
width: 50px;
height: 40px;
text-align: center;
font-size: 10px;
display: block;
}

#loadingPage>div {
background-color: green;
height: 100%;
width: 6px;
display: inline-block;
-webkit-animation: sk-stretchdelay 1.2s infinite ease-in-out;
animation: sk-stretchdelay 1.2s infinite ease-in-out;
}

#loadingPage .rect2 {
-webkit-animation-delay: -1.1s;
animation-delay: -1.1s;
}

#loadingPage .rect3 {
-webkit-animation-delay: -1.0s;
animation-delay: -1.0s;
}

#loadingPage .rect4 {
-webkit-animation-delay: -0.9s;
animation-delay: -0.9s;
}

#loadingPage .rect5 {
-webkit-animation-delay: -0.8s;
animation-delay: -0.8s;
}

@-webkit-keyframes sk-stretchdelay {
0%,
40%,
100% {
-webkit-transform: scaleY(0.4)
}
20% {
-webkit-transform: scaleY(1.0)
}
}

@keyframes sk-stretchdelay {
0%,
40%,
100% {
transform: scaleY(0.4);
-webkit-transform: scaleY(0.4);
}
20% {
transform: scaleY(1.0);
-webkit-transform: scaleY(1.0);
}
}
/* 消息框 */

.alert {
display: none;
position: fixed;
width: 100%;
z-index: 1001;
padding: 15px;
border: 1px solid transparent;
border-radius: 4px;
text-align: center;
font-weight: bold;
}

.alert-success {
color: #3c763d;
background-color: #dff0d8;
border-color: #d6e9c6;
}

.alert-info {
color: #31708f;
background-color: #d9edf7;
border-color: #bce8f1;
}

.alert-warning {
color: #8a6d3b;
background-color: #fcf8e3;
border-color: #faebcc;
}

.alert-danger {
color: #a94442;
background-color: #f2dede;
border-color: #ebccd1;
}

.pusher {
width: calc(100% - 260px);
box-sizing: border-box;
}
/* 弹窗 (background) */

#imageModal {
display: none;
position: fixed;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgb(0, 0, 0);
background-color: rgba(0, 0, 0, 0.4);
}
/* 弹窗内容 */

.modal-content {
background-color: #fefefe;
margin: 15% auto;
padding: 20px;
border: 1px solid #888;
width: 30%;
}
/* 关闭按钮 */

.close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
}

.close:hover,
.close:focus {
color: black;
text-decoration: none;
cursor: pointer;
}

.dis {
margin-bottom: 20px;
}

.disabled {
cursor: pointer;
pointer-events: none;
}
</style>

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

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

<div class="repository release dataset-list view">
{{template "repo/header" .}}
<!-- 列表容器 -->
<div class="ui container">

<!-- 中间云脑和新建任务按钮 -->

<div class="ui two column stackable grid ">

<div class="column">
<div class="ui blue small menu compact selectcloudbrain">
<a class="active item" href="{{.RepoLink}}/modelarts/notebook">调试任务</a>
<a class="item" href="{{.RepoLink}}/modelarts/train-job">训练任务</a>
</div>
</div>
<div class="column right aligned">
<div class="ui selection dropdown" style="min-width: 10em;min-height:2.6em;border-radius: .28571429rem;margin-right: 1em;padding: .67em 3.2em .7em 1em;">
{{svg "octicon-server" 16}}
<div class="default text" style="color: rgba(0,0,0,.87);"> Ascend NPU</div>
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{.RepoLink}}/cloudbrain" data-value="11">CPU / GPU</a>
<a class="item" href="{{.RepoLink}}/modelarts" data-value="22">Ascend NPU</a>
</div>
</div>
<a class="ui green button" href="{{.RepoLink}}/modelarts/create">新建调试任务</a>
</div>
</div>

<!-- 中下列表展示区 -->
<div class="ui grid">
<div class="row">
<div class="ui sixteen wide column">

<!-- 排序区 -->
<!-- <div class="ui sixteen wide column">
<div class="ui two column stackable grid">
<div class="column">
</div>
<div class="column right aligned">
<div class="ui right dropdown type jump item">
<span class="text">
{{.i18n.Tr "repo.issues.filter_sort"}}<i class="dropdown icon"></i>
</span>
</div>
</div>
</div>
</div> -->

<!-- 任务展示 -->
<div class="dataset list">

<!-- 表头 -->
<div class="ui grid stackable" style="background: #f0f0f0;;">
<div class="row">
<div class="five wide column">
<span style="margin:0 6px">{{$.i18n.Tr "repo.cloudbrain_task"}}</span>
</div>
<div class="three wide column">
<span>{{$.i18n.Tr "repo.cloudbrain_status_createtime"}}</span>
</div>
<div class="one wide column">
<span>{{$.i18n.Tr "repo.cloudbrain_creator"}}</span>
</div>
<div class="seven wide column text center">
<span style="margin-left: 10rem;">{{$.i18n.Tr "repo.cloudbrain_operate"}}</span>
</div>

</div>
</div>



{{range .Tasks}}
<div class="ui grid stackable item">
<div class="row">
<!-- 任务名 -->
<div class="five wide column">
<a class="title" href="{{$.Link}}/{{.JobID}}" title="{{.JobName}}" style="font-size: 15px;">
<span class="fitted" style="vertical-align: middle;">{{svg "octicon-tasklist" 16}}</span>
<span class="fitted" style="width: 90%;vertical-align: middle;margin-left: 0.4rem;">{{.JobName}}</span>
</a>
</div>

<div class="three wide column">
<!--任务状态 -->
<!-- <span class="ui compact button job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}">
{{.Status}}
</span> -->
<span class="job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}">
<span><i style="vertical-align: middle;" class="{{.Status}}"></i><span style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span>
</span>
<!-- 任务创建时间 -->
<span style="font-size: 12px;margin-left: 0.4rem;" class="">{{TimeSinceUnix .Cloudbrain.CreatedUnix $.Lang}}</span>
</div>

<div class="one wide column">
{{if .User.Name}}
<a href="{{AppSubUrl}}/{{.User.Name}}" title="{{.User.Name}}"><img class="ui avatar image" src="{{.User.RelAvatarLink}}"></a>
{{else}}
<a title="Ghost"><img class="ui avatar image" src="{{AppSubUrl}}/user/avatar/Ghost/-1"></a>
{{end}}
</div>

<div class="seven wide column text right">
<div class="ui compact buttons" style="margin-right:10px;">
<a class="ui basic blue button" href="{{$.Link}}/{{.JobID}}">
查看
</a>
<a class="ui basic {{if not .CanDebug}}disabled {{else}}blue {{end}}button" href="{{$.Link}}/{{.JobID}}/debug" target="_blank">
调试
</a>
<form id="stopForm-{{.JobID}}" action="{{if ne .Status "RUNNING"}}javascript:void(0){{else}}{{$.Link}}/{{.JobID}}/stop{{end}}" method="post" style="margin-left:-1px;">
{{$.CsrfTokenHtml}}
<a class="ui basic {{if ne .Status "RUNNING"}}disabled {{else}}blue {{end}}button" onclick="document.getElementById('stopForm-{{.JobID}}').submit();">
停止
</a>
</form>
</div>

<!-- 删除任务 -->
<form class="ui compact buttons" id="delForm-{{.JobID}}" action="{{if not .CanDel}}javascript:void(0){{else}}{{$.Link}}/{{.JobID}}/del{{end}}" method="post">
{{$.CsrfTokenHtml}}
<a class="ui compact {{if not .CanDel}}disabled {{else}}red {{end}}button" onclick="assertDelete(this)" style="border-radius: .28571429rem;">
删除
</a>
</form>
</div>



</div>
</div>
{{end}} {{template "base/paginate" .}}
</div>

</div>
</div>
</div>

</div>

</div>
</div>

</div>

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

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

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

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

// 删除时用户确认
function assertDelete(obj) {
if (obj.style.color == "rgb(204, 204, 204)") {
return
} else {
var delId = obj.parentNode.id
flag = 1;
$('.ui.basic.modal')
.modal({
onDeny: function() {
flag = false
},
onApprove: function() {
document.getElementById(delId).submit()
flag = true
},
onHidden: function() {
if (flag == false) {
$('.alert').html('您已取消操作').removeClass('alert-success').addClass('alert-danger').show().delay(1500).fadeOut();
}
}
})
.modal('show')
}
}

// 加载任务状态
var timeid = window.setInterval(loadJobStatus, 15000);
$(document).ready(loadJobStatus);
function loadJobStatus() {
$(".job-status").each((index, job) => {
const jobID = job.dataset.jobid;
const repoPath = job.dataset.repopath;
if (job.textContent.trim() == 'STOPPED' || job.textContent.trim() == 'START_FAILED' || job.textContent.trim() == 'CREATE_FAILED') {
return
}

$.get(`/api/v1/repos/${repoPath}/modelarts/${jobID}`, (data) => {
const jobID = data.JobID
const status = data.JobStatus
if (status != job.textContent.trim()) {
//$('#' + jobID).text(status)
//if (status == 'STOPPED') {
window.location.reload()
//}
}
}).fail(function(err) {
console.log(err);
});
});
};

// 获取弹窗
var modal = document.getElementById('imageModal');

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

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

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

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

// 在用户点击其他地方时,关闭弹窗
window.onclick = function(event) {
if (event.target == modal) {
modal.style.display = "none";
}
}

// 显示弹窗,弹出相应的信息
function showmask() {
$('#imageModal').css('display', 'none')
$('#mask').css('display', 'block')

$("iframe[name=iframeContent]").on("load", function() {  
var responseText = $("iframe")[0].contentDocument.body.getElementsByTagName("pre")[0].innerHTML; 
var json1 = JSON.parse(responseText)
$('#mask').css('display', 'none')
parent.location.href

if (json1.result_code === "0") {
$('.alert').html('操作成功!').removeClass('alert-danger').addClass('alert-success').show().delay(1500).fadeOut();
} else {
$('.alert').html(json1.error_msg).removeClass('alert-success').addClass('alert-danger').show().delay(5000).fadeOut();
}
})
}
</script>

+ 0
- 240
templates/repo/modelarts/new.tmpl View File

@@ -1,240 +0,0 @@
{{template "base/head" .}}
<style>
/* 遮罩层css效果图 */
#mask {
position: fixed;
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
filter: alpha(opacity=60);
background-color: #777;
z-index: 1000;
display: none;
opacity: 0.8;
-moz-opacity: 0.5;
padding-top: 100px;
color: #000000
}
/* 加载圈css效果图 */
#loadingPage {
margin: 200px auto;
width: 50px;
height: 40px;
text-align: center;
font-size: 10px;
display: block;
}
#loadingPage>div {
background-color: green;
height: 100%;
width: 6px;
display: inline-block;
-webkit-animation: sk-stretchdelay 1.2s infinite ease-in-out;
animation: sk-stretchdelay 1.2s infinite ease-in-out;
}
#loadingPage .rect2 {
-webkit-animation-delay: -1.1s;
animation-delay: -1.1s;
}
#loadingPage .rect3 {
-webkit-animation-delay: -1.0s;
animation-delay: -1.0s;
}
#loadingPage .rect4 {
-webkit-animation-delay: -0.9s;
animation-delay: -0.9s;
}
#loadingPage .rect5 {
-webkit-animation-delay: -0.8s;
animation-delay: -0.8s;
}
@-webkit-keyframes sk-stretchdelay {
0%,
40%,
100% {
-webkit-transform: scaleY(0.4)
}
20% {
-webkit-transform: scaleY(1.0)
}
}
@keyframes sk-stretchdelay {
0%,
40%,
100% {
transform: scaleY(0.4);
-webkit-transform: scaleY(0.4);
}
20% {
transform: scaleY(1.0);
-webkit-transform: scaleY(1.0);
}
}
.inline.required.field.cloudbrain_benchmark {
display: none;
}
</style>

<div id="mask">
<div id="loadingPage">
<div class="rect1"></div>
<div class="rect2"></div>
<div class="rect3"></div>
<div class="rect4"></div>
<div class="rect5"></div>
</div>
</div>
<div class="repository">
{{template "repo/header" .}}
<div class="repository new repo ui middle very relaxed page grid">
<div class="column">
{{template "base/alert" .}}
<div class="ui negative message" id="messageInfo">
<p></p>
</div>
<form class="ui form" id="form_id" action="{{.Link}}" method="post">
{{.CsrfTokenHtml}}
<h3 class="ui top attached header">
{{.i18n.Tr "repo.cloudbrain.new"}}
</h3>
<div class="ui attached segment">
<!-- <br> -->
<div class="inline required field">
<label>任务名称</label>
<input name="job_name" id="cloudbrain_job_name" placeholder="任务名称" value="{{.job_name}}" tabindex="3" autofocus required maxlength="255">
</div>

<div class="inline field">
<label>数据集</label>
<input type="text" list="cloudbrain_dataset" placeholder="选择数据集" name="" id="answerInput" autofocus maxlength="36">
<datalist id="cloudbrain_dataset" class="ui search" style='width:385px' name="attachment">
{{range .attachments}}
<option name="attachment" data-value="{{.UUID}}">{{.Attachment.Name}}</option>
{{end}}
</datalist>
<input type="hidden" name="attachment" id="answerInput-hidden">
</div>

<div class="inline required field">
<label>工作环境</label>
<input name="de" id="cloudbrain_de" value="{{.env}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly">
</div>
<div class="inline required field">
<label>类型</label>
<input name="job_type" id="cloudbrain_job_type" value="{{.notebook_type}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly">
</div>
<div class="inline required field">
<label>规格</label>
<select id="cloudbrain_flavor" class="ui search dropdown" placeholder="选择规格" style='width:385px' name="flavor">
{{range .flavors}}
<option name="flavor" value="{{.Value}}">{{.Value}}</option>

{{end}}
</select>
</div>
<div class="inline required field">
<label>数据集存放路径</label>
<input name="dataset_path" id="cloudbrain_dataset_path" value="{{.dataset_path}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly">
</div>
<div class="inline field">
<label>描述</label>
<input name="description" id="cloudbrain_description" tabindex="3" autofocus maxlength="255">
</div>
<div class="inline field">
<label></label>
<button class="ui green button">
{{.i18n.Tr "repo.cloudbrain.new"}}
</button>
<a class="ui button" href="/">{{.i18n.Tr "repo.cloudbrain.cancel"}}</a>
</div>
</div>
</form>
</div>
</div>
</div>
{{template "base/footer" .}}

<script>
// 取消创建跳转
let url_href = window.location.pathname.split('create')[0]
$(".ui.button").attr('href',url_href)

// 判断必填选项是否填写正确
let form = document.getElementById('form_id');

$('#messageInfo').css('display','none')

form.onsubmit = function(e){
let value_task = $("input[name='job_name']").val()
let re = /^[a-z0-9][a-z0-9-_]{1,34}[a-z0-9-]$/
let flag = re.test(value_task)
if(!flag){
$('#messageInfo').css('display','block')
let str = '只能以小写字母或数字开头且只包含小写字母、数字、_和-,不能以_结尾,最长36个字符。'
$('#messageInfo p').text(str)
return false
}
let min_value_task = value_task.toLowerCase()
$("input[name='job_name']").attr("value",min_value_task)
document.getElementById("mask").style.display = "block"
}
// 点击按钮后遮罩层显示
// function showmask() {
// document.getElementById("mask").style.display = "block"
// }

// 页面加载完毕后遮罩层隐藏
document.onreadystatechange = function() {
if (document.readyState === "complete") {
document.getElementById("mask").style.display = "none"
}
}

$('select.dropdown')
.dropdown();

$(function() {
$("#cloudbrain_job_type").change(function() {
if ($(this).val() == 'BENCHMARK') {
$(".cloudbrain_benchmark").show();
} else {
$(".cloudbrain_benchmark").hide();
}
})
})
document.querySelector('input[list]').addEventListener('input',function(e){
var input = e.target,
list = input.getAttribute('list'),
options = document.querySelectorAll('#'+list+' option'),
hiddenInput = document.getElementById(input.getAttribute('id')+'-hidden'),
inputValue = input.value;
hiddenInput.value = inputValue;
for (let i=0;i<options.length;i++){
var option = options[i]
if(option.innerText===inputValue){
hiddenInput.value = option.getAttribute('data-value');
break
}
}


})
</script>

+ 1
- 1
templates/repo/modelarts/notebook/new.tmpl View File

@@ -138,7 +138,7 @@
<label>规格</label>
<select id="cloudbrain_flavor" class="ui search dropdown" placeholder="选择规格" style='width:385px' name="flavor">
{{range .flavors}}
<option name="flavor" value="{{.Value}}">{{.Value}}</option>
<option name="flavor" value="{{.Value}}">{{.Desc}}</option>

{{end}}
</select>


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

@@ -1,122 +0,0 @@
{{template "base/head" .}}
<div class="repository">
{{template "repo/header" .}}
<div class="repository new repo ui middle very relaxed page grid">
<div class="column">
{{template "base/alert" .}}

<h4 class="ui header" id="vertical-segment">
<a href="javascript:window.history.back();"><i class="arrow left icon"></i>返回</a>
</h4>
<div>
<div class="ui yellow segment">
{{with .task}}
<p>任务名称: {{.JobName}}</p>
{{end}}
</div>
<div class="ui green segment">
<p>任务结果:</p>
{{with .result}}
<table class="ui celled striped table">
<tbody>
<tr>
<td class="four wide"> 状态 </td>
<td> {{.Status}} </td>
</tr>
<tr>
<td> 开始时间 </td>
<td>{{.CreateTime}}</td>
</tr>
<tr>
<td> 最后更新时间 </td>
<td>{{.LatestUpdateTime}}</td>
</tr>
</tbody>
</table>
{{end}}
</div>
<div class="ui blue segment">
{{with .result}}
<table class="ui celled striped table">
<thead>
<tr> <th colspan="2"> 配置信息 </th> </tr>
</thead>
<tbody>
<tr>
<td class="four wide"> 开发环境类型 </td>
<td>{{.Profile.DeType}}</td>
</tr>
<tr>
<td> 硬件类型 </td>
<td>{{.Profile.FlavorType}}</td>
</tr>
</tbody>
</table>

<table class="ui celled striped table">
<thead>
<tr> <th colspan="2"> 机器规格详情 </th> </tr>
</thead>
<tbody>
<tr>
<td class="four wide"> 机器规格 </td>
<td> {{.Flavor}} </td>
</tr>
<tr>
<td> 规格名称 </td>
<td>{{.FlavorDetails.Name}}</td>
</tr>
<tr>
<td> 规格销售状态 </td>
<td>{{.FlavorDetails.Status}}</td>
</tr>
<tr>
<td> 排队个数 </td>
<td>{{.FlavorDetails.QueuingNum}}</td>
</tr>
<tr>
<td> 排到队的剩余时间(秒) </td>
<td>{{.FlavorDetails.QueueLeftTime}}</td>
</tr>
<tr>
<td> 自动停止时间(秒) </td>
<td>{{.FlavorDetails.Duration}}</td>
</tr>
</tbody>
</table>

<table class="ui celled striped table" {{if eq .QueuingInfo.RemainTime 0}}hidden{{end}}>
<thead>
<tr> <th colspan="2"> 排队信息 </th> </tr>
</thead>
<tbody>
<tr>
<td> 实例状态 </td>
<td>{{.QueuingInfo.Status}}</td>
</tr>
<tr>
<td> 实例排队的开始时间 </td>
<td>{{.QueuingInfo.BeginTime}}</td>
</tr>
<tr>
<td> 排到队的剩余时间(秒) </td>
<td>{{.QueuingInfo.RemainTime}}</td>
</tr>
<tr>
<td> 实例排队的预计停止时间 </td>
<td>{{.QueuingInfo.EndTime}}</td>
</tr>
<tr>
<td> 实例在队列中的排位 </td>
<td>{{.QueuingInfo.Rank}}</td>
</tr>
</tbody>
</table>
{{end}}
</div>
</div>

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

+ 1
- 1
templates/repo/pulls/commits.tmpl View File

@@ -10,7 +10,7 @@
</div> -->
<div class="ui two column stackable grid">
<div class="column" style="display: flex;align-items: center;">
<div class="ui large breadcrumb">
<div class="ui breadcrumb">
<a class="section" href="{{.RepoLink}}/pulls">{{.i18n.Tr "repo.pulls"}}</a>
<div class="divider"> / </div>
<div class="action section">{{.i18n.Tr "repo.issues_detail"}}</div>


+ 1
- 1
templates/repo/pulls/files.tmpl View File

@@ -10,7 +10,7 @@
</div> -->
<div class="ui two column stackable grid">
<div class="column" style="display: flex;align-items: center;">
<div class="ui large breadcrumb">
<div class="ui breadcrumb">
<a class="section" href="{{.RepoLink}}/pulls">{{.i18n.Tr "repo.pulls"}}</a>
<div class="divider"> / </div>
<div class="action section">{{.i18n.Tr "repo.issues_detail"}}</div>


+ 112
- 0
web_src/js/components/Contributors.vue View File

@@ -0,0 +1,112 @@
<template>
<div class="ui container">
<div class="row git-user-content">
<h3 class="ui header">
<div class="ui breadcrumb">
<a class="section" :href="url_code">代码</a>
<div class="divider"> / </div>
<div class="active section" >贡献者&nbsp;({{totalNum}})</div>
</div>
</h3>
<div class="ui horizontal relaxed list">
<div class="item user-list-item" v-for="(contributor,i) in contributors_list_page" >
<a v-if="contributor.user_name" :href="AppSubUrl +'/'+ contributor.user_name"><img class="ui avatar s16 image js-popover-card" :src="contributor.rel_avatar_link"></a>
<a v-else :href="'mailto:' + contributor.email "><img class="ui avatar s16 image js-popover-card" :avatar="contributor.email"></a>
<div class="content">
<div class="header" >
<a v-if="contributor.user_name" :href="AppSubUrl +'/'+ contributor.user_name">{{contributor.user_name}}</a>
<a v-else :href="'mailto:' + contributor.email ">{{contributor.email}}</a>
</div>
<span class="commit-btn">Commits: {{contributor.commit_cnt}}</span>
</div>
</div>
</div>
</div>
<div class="ui container" style="margin-top:50px;text-align:center">
<el-pagination
background
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-size="pageSize"
layout="total, prev, pager, next"
:total="totalNum">
</el-pagination>
</div>
</div>
</template>

<script>

const {AppSubUrl, StaticUrlPrefix, csrf} = window.config;

export default {
data() {
return {
url:'',
contributors_list:[],
contributors_list_page:[],
currentPage:1,
pageSize:50,
totalNum:0,
AppSubUrl:AppSubUrl
};
},
methods: {

getContributorsList(){
this.$axios.get(this.url+'/list').then((res)=>{
this.contributors_list = res.data.contributor_info
this.totalNum = this.contributors_list.length
this.contributors_list_page = this.contributors_list.slice(0,this.pageSize)
})
},
handleCurrentChange(val){
this.contributors_list_page = this.contributors_list.slice((val-1)*this.pageSize,val*this.pageSize)
},
},
computed:{
},
watch: {

},
created(){
const url = window.location.pathname;
this.url = url;
let strIndex = this.url.indexOf("contributors")
this.url_code = this.url.substr(0,strIndex)
this.getContributorsList()
},

updated(){
if(document.querySelectorAll('img[avatar]').length!==0){
window.LetterAvatar.transform()
}
}
};
</script>

<style scoped>
/deep/ .el-pagination.is-background .el-pager li:not(.disabled).active {
background-color: #5bb973;
color: #FFF;
}
/deep/ .el-pagination.is-background .el-pager li.active {
color: #fff;
cursor: default;
}
/deep/ .el-pagination.is-background .el-pager li:hover {
color: #5bb973;
}
/deep/ .el-pagination.is-background .el-pager li:not(.disabled):hover {
color: #5bb973;
}
/deep/ .el-pagination.is-background .el-pager li:not(.disabled).active:hover {
background-color: #5bb973;
color: #FFF;
}
</style>

+ 123
- 0
web_src/js/components/DataAnalysis.vue View File

@@ -0,0 +1,123 @@
<template>
<div style="height:100%">
<el-tabs tab-position="left" v-model="activeName" style="height:100%" @tab-click="handleClick" >
<el-tab-pane label="概览" name="first" >
<span slot="label">
<el-image style="width: 13px; height: 13px" src="/img/overview.png">
</el-image>
概览
</span>
<div >概览.......</div>
<div >概览.......</div>
<div >概览.......</div><div >概览.......</div>
<div >概览.......</div>
</el-tab-pane>
<el-tab-pane label="项目分析" name="second" id="second" >
<ProAnalysis ref='ProAnalysis'id="pro" v-if="isRouterAlive"></ProAnalysis>
<span slot="label">
<el-image style="width: 13px; height: 13px" src="/img/pro.svg">
</el-image>
项目分析
</span>
</el-tab-pane>
<el-tab-pane name="third" >
<span slot='label'>
<el-image style="width: 13px; height: 13px" src="/img/name.png">
</el-image>
用户分析
</span>
<UserAnalysis ref='UserAnalysis' id ="usr"></UserAnalysis>
</el-tab-pane>
</el-tabs>
</div>
</template>

<script>
import ProAnalysis from './ProAnalysis.vue'
import UserAnalysis from './UserAnalysis.vue'

export default {

components:{
'ProAnalysis':ProAnalysis,
'UserAnalysis':UserAnalysis,
},
data() {
return {
activeName:"second",
loading:true,
loading1:true,
isRouterAlive: true,
isSecond:true,
isThird:false,
}
},
methods:{
handleClick(tab, event){
if(tab.name=="second"){
this.reload()
//document.getElementById('usr').style.display="none"
//document.getElementById("pro").style.display='block'
//this.$refs.ProAnalysis.getAllProList("all",7)

this.isSecond = true
this.isThird = false
this.$refs.ProAnalysis.getAllProList("all",7)
}
if(tab.name=="third"){
// document.getElementById('usr').style.display="block"
// document.getElementById("pro").style.display='none'
//this.$refs.UserAnalysis.getUserList("all_usr",7)

this.isSecond = false
this.isThird = true
}

},
reload () {
this.isRouterAlive = false
this.$nextTick(() => (this.isRouterAlive = true))
}

},
}
</script>
<style scoped>
/deep/ .is-active{
color: #238BFC ;
background-color: #FFFF ;
}
/deep/ .ui-container{
background-color: #FFFF;
}
/deep/ .el-tabs--left .el-tabs__header.is-left{
background-color:#F5F5F6;
width: 12.43%;
}
.el-tabs--left .el-tabs__header.is-left
html,
body,
/deep/ .el-container {
padding: 0px;
margin: 0px;
height: 100%;
}
/deep/ .el-tabs--left .el-tabs__item.is-left {
text-align: left;
}
/deep/ .el-tabs__item {
padding: 0px 20px 0px 20px;
}

</style>

+ 923
- 0
web_src/js/components/ProAnalysis.vue View File

@@ -0,0 +1,923 @@
<template>
<div style="width: 100%;">
<div id = "pro_main">
<div style="margin-top: 10px;">
<b class="pro_item">项目分析</b> <span class="update_time">数据更新时间:{{lastUpdatedTime}}/{{recordBeginTime}}</span>
</div>
<bar-label :width="'95%'" :height="'500px'"></bar-label>
<div style="margin-top: 20px;">
<span class="sta_iterm">统计周期:</span>
<button type="button" class='btn' id ="yesterday" v-bind:class="{colorChange:1==dynamic}" @click="getAllProList('yesterday',1)">昨天</button>
<button type="button" class='btn' id = "current_week" v-bind:class="{colorChange:2==dynamic}" @click="getAllProList('current_week',2)">本周</button>
<button type="button" class='btn' id = "current_month" v-bind:class="{colorChange:3==dynamic}" @click="getAllProList('current_month',3)">本月</button>
<button type="button" class='btn' id = "last_month" v-bind:class="{colorChange:4==dynamic}" @click="getAllProList('last_month',4)">上月</button>
<button type="button" class='btn' id = "monthly" v-bind:class="{colorChange:5==dynamic}" @click="getAllProList('monthly',5)">近30天</button>
<button type="button" class='btn' id = "current_year" v-bind:class="{colorChange:6==dynamic}" @click="getAllProList('current_year',6)">今年</button>
<button type="button" class='btn' id = "all" v-bind:class="{colorChange:7==dynamic}" @click="getAllProList('all',7)">所有</button>
<span style="margin-left: 20px;">
<el-date-picker
v-model="value_time"
prefix-icon="el-icon-time"
@change="getAllProList('',0)"
type="daterange"
size='small'
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期">
</el-date-picker>
</span>
<span style="float:right; margin-right: 20px;">
<div style="display:inline-block;margin-left: 20px; ">
<i class="el-icon-download"></i>
<span ><a>下载报告</a> </span>
</div>
<span style="display:inline-block;margin-left: 20px; ">
<el-input size="small" placeholder="输入项目关键字搜索" v-model="search" class="input-with-select" @keyup.enter.native="searchName() "><i slot="suffix" class="el-input__icon el-icon-search"></i>
</el-input>
</span>
</span>
</div>
<div style="margin-top: 30px;">
<el-table
:data="tableData"
style="width: 100%"
:header-cell-style="tableHeaderStyle"
:cell-style='cellStyle'>
<el-table-column
label="ID"
align="center"
prop="repo_id"
stripe
>
</el-table-column>
<el-table-column
label="项目名称"
width="125px"
align="center"
prop="name"
style="color:#0366D6 100%;"
>
<template slot-scope="scope">
<a @click=goToDetailPage(scope.row.repo_id,scope.row.name)>{{scope.row.name}} </a>
</template>
</el-table-column>
<el-table-column
prop="isPrivate"
label="私有"
align="center">
<template slot-scope="scope">
{{scope.row.isPrivate|changeType}}
</template>
</el-table-column>
<el-table-column
prop="openi"
label="OpenI指数"
align="center">
<template slot-scope="scope">
{{scope.row.openi | rounding}}
</template>
</el-table-column>
<el-table-column
prop="view"
label="浏览量"
align="center">
</el-table-column>
<el-table-column
prop="download"
label="代码下载量"
align="center">
</el-table-column>
<el-table-column
prop="pr"
label="PR"
align="center">
</el-table-column>
<el-table-column
prop="commit"
label="Commit数"
align="center">
</el-table-column>
<el-table-column
prop="watch"
label="关注数"
align="center">
</el-table-column>
<el-table-column
prop="star"
label="点赞数"
align="center">
</el-table-column>
<el-table-column
prop="fork"
label="派生数"
align="center">
</el-table-column>
<el-table-column
prop="issue"
label="任务数"
align="center">
</el-table-column>
<el-table-column
prop="issueClosed"
label="已解决任务"
align="center">
</el-table-column>
<el-table-column
prop="contributor"
label="贡献者数"
align="center">
</el-table-column>
</el-table>
</div>
<div style="margin-top:50px;text-align:center">
<el-pagination
background
@current-change="handleCurrentChange"
:current-page="page"
:page-size="pageSize"
layout="prev, pager, next"
:total="totalNum">
</el-pagination>
</div>
</div>
<div id ="pro_detail" style="display:none;width: 100%;">
<div style="margin-top: 10px;">
<b class="pro_item">OpenI / {{this.pro_name}}</b> <span class="update_time">数据更新时间:{{tableDataIDTotal.lastUpdatedTime}}/{{tableDataIDTotal.recordBeginTime}}</span>
</div>
<div style="margin-top: 10px;">
项目描述:{{tableDataIDTotal.description | discriptionFun}}
</div>
<div style="margin-top:20px">
<el-row >
<el-col :span='4' class="items">
项目创建时间 </br>
{{tableDataIDTotal.creatTime}}
</el-col>
<el-col :span='4' class="items">
OPenI指数 </br>
{{tableDataIDTotal.openi | rounding}}
</el-col>
<el-col :span='4' class="items">
评论数 </br>
{{tableDataIDTotal.comment}}
</el-col>
<el-col :span='4' class="items">
浏览数 </br>
{{tableDataIDTotal.view}}
</el-col>
<el-col :span='4' class="items">
代码下载量 </br>
{{tableDataIDTotal.download}}
</el-col>
<el-col :span='4' style="text-align: center;">
任务完成比例 </br>
{{tableDataIDTotal.issueClosedRatio}}
</el-col>
</el-row>
</div>
<div style="margin-top:30px;">
<el-row :gutter="20">
<el-col :span=18 >
<div class="item_l" id="charts">
<div style="font-size:14px;color:#409eff;margin:20px 5px;">OpenI指数:{{tableDataIDTotal.openi | rounding}}</div>
<div >
<el-col :span='8' id="radar_openi" :style="{width: '400px', height: '300px'}"></el-col>
<el-col :span='16' id="line_openi" :style="{width: '600px', height: '300px',float:'right'}"></el-col>
</div>
</div>
</el-col>
<el-col :span=6 >
<div class="item_r">
<div style="font-size:14px;color:rgb(0,0,0);margin:20px 5px;">贡献者TOP10</div>
<div>
<el-table
:data="tableDataContTop10"
style="width: 100%"
stripe
:header-cell-style="tableHeaderStyle"
>
<el-table-column
label="用户名"
align="center"
prop="user">
</el-table-column>
<el-table-column
label="身份"
align="center"
prop="mode">
<template slot-scope="scope">
{{scope.row.mode | showMode}}
</template>
</el-table-column>
<el-table-column
prop="PR"
label="pr"
align="center">
</el-table-column>
<el-table-column
prop="commit"
label="commit"
align="center">
</el-table-column>
</el-table>
</div>
</div>
</el-col>
</el-row>
</div>
<div style="margin-top: 20px;">
<span class="sta_iterm">统计周期:</span>
<button type="button" class='btn' id ="yesterday_pro" v-bind:class="{colorChange:1==dynamic_pro}" @click="getOneProList(pro_id,'yesterday',true,1),getOneProList(pro_id,'yesterday',false,1)">昨天</button>
<button type="button" class='btn' id = "current_week_pro" v-bind:class="{colorChange:2==dynamic_pro}" @click="getOneProList(pro_id,'current_week',true,2),getOneProList(pro_id,'current_week',false,2)">本周</button>
<button type="button" class='btn' id = "current_month_pro" v-bind:class="{colorChange:3==dynamic_pro}" @click="getOneProList(pro_id,'current_month',true,3),getOneProList(pro_id,'current_month',false,3)">本月</button>
<button type="button" class='btn' id = "last_month_pro" v-bind:class="{colorChange:4==dynamic_pro}" @click="getOneProList(pro_id,'last_month',true,4),getOneProList(pro_id,'last_month',false,4)">上月</button>
<button type="button" class='btn' id = "monthly_pro" v-bind:class="{colorChange:5==dynamic_pro}" @click="getOneProList(pro_id,'monthly',true,5),getOneProList(pro_id,'monthly',false,5)">近30天</button>
<button type="button" class='btn' id = "current_year_pro" v-bind:class="{colorChange:6==dynamic}" @click="getOneProList(pro_id,'current_year',true,6),getOneProList(pro_id,'current_year',false,6)">今年</button>
<button type="button" class='btn' id = "all_pro" v-bind:class="{colorChange:7==dynamic_pro}" @click="getOneProList(pro_id,'all',true,7),getOneProList(pro_id,'all',false,7)">所有</button>
<span style="margin-left: 20px;">
<el-date-picker
v-model="create_time_pro"
prefix-icon="el-icon-time"
@change="getOneProList(pro_id,'',true,0),getOneProList(pro_id,'',false,0)"
type="daterange"
size='small'
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期">
</el-date-picker>
</span>
<span style="float:right; margin-right: 20px;">
<div style="display:inline-block;margin-left: 20px; ">
<i class="el-icon-download"></i>
<span ><a>下载报告</a> </span>
</div>
</span>
</div>
<div class="item_echart" id ='linecharts'>
<div style="margin-top:10px;margin-left: 5px;">
<label for="label" @change='clickCheckBox'>
<input type="checkbox" class="checkboxchart" name="checkboxchart" checked="checked" value="浏览量"/>浏览量
<input type="checkbox" class="checkboxchart" name="checkboxchart" checked="checked" value="下载量"/>下载量
<input type="checkbox" class="checkboxchart" name="checkboxchart" checked="checked" value="commit"/>commit
</label>
</div>
<div id ="selectData" style="width: 1280px;height: 300px;">

</div>
</div>
<div style="margin-top: 30px;">
<el-table
:data="tableDataID.slice((currentPage-1)*pageSize,currentPage*pageSize)"
style="width: 100%"
:header-cell-style="tableHeaderStyle"
:cell-style='cellStyle'>
<el-table-column
label="日期"
align="center"
prop="date"
stripe
>
</el-table-column>
<el-table-column
label="浏览量"
align="center"
prop="view">
</el-table-column>
<el-table-column
prop="download"
label="下载量"
align="center">
</el-table-column>
<el-table-column
prop="commit"
label="commit"
align="center">
</el-table-column>
</el-table>
</div>
<div style="margin-top:50px;text-align:center">
<el-pagination
background
@current-change="handleCurrentChangeID"
:current-page="currentPage"
:page-size="pageSize1"
layout="prev, pager, next"
:total="tableDataID.length">
</el-pagination>
</div>
</div>
</div>
</template>
<script>
// import barLabel from './basic/barLabel.vue';
export default{
name:'ProAnalysis',
components: {
// barLabel,
},
data() {
return {
recordBeginTime:'',
lastUpdatedTime:'',
page:1,
pageSize:10,
params:{type:'all',page:1,pagesize:10,beginTime:'',endTime:'',q:'',sort:'openi'},
tableData: [],
totalPage:0,
totalNum:0,
pickerOptions: {
},
value_time: '',
search:'',
dynamic:7,

//单个项目参数
currentPage:1,
pageSize1:10,
paramsID:{type:'all' ,beginTime:'',endTime:'',openi:'false'},
tableDataIDTotal: [],
tableDataID: [],
tableDataIDOpenI:[],
tableDataContTop10:[],
create_time_pro: '',
dynamic_pro:7,
pro_name:'',
pro_id:'',
radarOpenI:'',
echartsOITd:'',
echartsSelectData:'',
option:'',

};
},
methods: {
handleCurrentChange(val){
console.log(val)
this.params.page = val
switch(this.params.type){
case "yesterday":{
this.value_time=''
this.getAllProList(this.params.type,1)
break
}
case "current_week":{
this.value_time=''
this.getAllProList(this.params.type,2)
break
}
case "current_month":{
this.value_time=''
this.getAllProList(this.params.type,3)
break
}
case "last_month":{
this.value_time=''
this.getAllProList(this.params.type,4)
break
}
case "monthly":{
this.value_time=''
this.getAllProList(this.params.type,5)
break
}
case "current_year":{
this.value_time=''
this.getAllProList(this.params.type,6)
break
}
case "all":{
this.value_time=''
this.getAllProList(this.params.type,7)
break
}
case "":{
// this.value_time=''
this.getAllProList(this.params.type,0)
break
}
}
},
formatDate(myyear,mymonth,myweekday) {
// var myyear = this.date.getFullYear();
// var mymonth = this.date.getMonth() + 1;
// var myweekday = this.date.getDate();
if (mymonth < 10) {
mymonth = "0" + mymonth;
}
if (myweekday < 10) {
myweekday = "0" + myweekday;
}
return (myyear + "-" + mymonth + "-" + myweekday);
},
getAllProList(type_val,index){
console.log("类型:"+type_val)
this.dynamic = index
if (typeof type_val=="undefined" || type_val=="null" || type_val==""){
this.params.type=''
this.params.beginTime=this.formatDate(this.value_time[0].getFullYear(),this.value_time[0].getMonth() + 1,this.value_time[0].getDate())
this.params.endTime=this.formatDate(this.value_time[1].getFullYear(),this.value_time[1].getMonth() + 1,this.value_time[1].getDate())
}else{
this.params.type=type_val
this.params.beginTime=''
this.params.endTime=''
this.value_time=[]
}
this.$axios.get('../api/v1/projectboard/project',{
params:this.params
}).then((res)=>{
this.recordBeginTime=res.data.recordBeginTime
this.lastUpdatedTime=res.data.lastUpdatedTime
this.tableData = res.data.pageRecords
this.totalPage=res.data.totalPage
this.totalNum = this.totalPage*this.params.pagesize
console.log("this.totalPage:"+this.totalPage)
})
},
searchName(){
this.params.q = this.search
this.params.page = 1
this.getAllProList("all",7)
},
goToDetailPage(pro_id,pro_name){
document.getElementById("pro_main").style.display="none";
document.getElementById("pro_detail").style.display="block";
console.log(pro_id)
console.log(pro_name)
this.pro_name=pro_name;
this.pro_id=pro_id;
this.getOneProData(pro_id);
this.getOneProList(pro_id,"monthly",true,5);
this.getOneProList(pro_id,"monthly",false,5);
},
tableHeaderStyle({row,column,rowIndex,columnIndex}){
if(rowIndex===0){
return 'background:#f5f5f6;color:#606266'
}
},
cellStyle({row,column,rowIndex,columnIndex}){
if(rowIndex%2 === 1){
return 'background:#f5f5f6;color:#606266'
}
},
handleCurrentChangeID(currentPage){
this.currentPage = currentPage;
},
getOneProData(pro_id){
this.$axios.get('../api/v1/projectboard/project/'+pro_id,{
}).then((res)=>{
this.tableDataIDTotal = res.data
this.tableDataContTop10=res.data.top10
// this.drawLine()
this.drawRadarOpenI()
})
},
getOneProList(pro_id,type_val,bool_val,index){
this.dynamic_pro=index
console.log("日期类型:"+type_val)
if (typeof type_val=="undefined" || type_val=="null" || type_val==""){
this.paramsID.type=''
this.paramsID.beginTime= this.formatDate(this.create_time_pro[0].getFullYear(),this.create_time_pro[0].getMonth() + 1,this.create_time_pro[0].getDate())
this.paramsID.endTime=this.formatDate(this.create_time_pro[1].getFullYear(),this.create_time_pro[1].getMonth() + 1,this.create_time_pro[1].getDate())
}else{
this.create_time_pro=[]
this.paramsID.type=type_val
this.paramsID.beginTime=''
this.paramsID.endTime=''
}
this.paramsID.openi=bool_val
this.$axios.get('../api/v1/projectboard/project/'+pro_id+"/period",{
params:this.paramsID
}).then((res)=>{
if (bool_val){
this.tableDataIDOpenI = res.data
this.drawOpenItrend()
}else{
this.tableDataID = res.data
this.drawSelectData()
}
})
},
drawRadarOpenI(){
var ydata = [this.roundingF(this.tableDataIDTotal.impact),this.roundingF(this.tableDataIDTotal.completeness),this.roundingF(this.tableDataIDTotal.liveness),this.tableDataIDTotal.projectHealth,this.roundingF(this.tableDataIDTotal.teamHealth),this.roundingF(this.tableDataIDTotal.growth)]
console.log("ydata:",ydata)
var i = -1;
var option = {
titile:{
text:""
},
tooltip: {
trigger: 'item',
backgroundColor:'rgba(255,255,255,0.8)',
color:'black',
borderWidth:'1',
borderColor:'gray',
textStyle:{
color:'black'
},
position: 'right'
},//提示层
legend: {
data: ['name1']
},
radar: {
name: {
textStyle: {
color: 'rgb(0,0,0)', //字体颜色
borderRadius: 3, //圆角
padding: [3, 5] //padding
}
},
slpitNumber:5,
center: ['50%', '50%'],
indicator: [{
name: '社区影响力',
max: 100
},
{
name: '项目成熟度',
max: 100
},
{
name: '开发活跃度',
max: 100
},
{
name: '项目健康度',
max: 100
},
{
name: '团队健康度',
max: 100
},
{
name: '项目发展趋势',
max: 100
}
],
},
series: [{
type: 'radar',
lineStyle:{
width:2,
color: 'rgb(0,255,0)'
},
data: [{
value: ydata,
}]
}]
}
this.radarOpenI.setOption(option)
},
drawOpenItrend(){
var xdata_openI=[]
var ydata_openI=[]
for(var i =0;i<this.tableDataIDOpenI.length;i++){
xdata_openI.push(this.tableDataIDOpenI[this.tableDataIDOpenI.length-1-i].date);
ydata_openI.push(this.roundingF(this.tableDataIDOpenI[i].openi))
}
console.log("ydata_openI:"+ydata_openI)
console.log(xdata_openI)
var option = {
title : {
text: 'OpenI指数趋势',


textStyle: {
                fontSize: 12,
            },
left:'center',
top:'bottom',

subtext: '',

},
tooltip : {
trigger: 'axis',
backgroundColor:'rgba(255,255,255,0.8)',
color:'black',
borderWidth:'1',
borderColor:'gray',
textStyle:{
color:'black'
},
},
legend: {
orient: 'vertical',
top:'top',  
},
// calculable : true,
xAxis : [
{
type : 'category',
boundaryGap: false,
data : xdata_openI,
}
],
yAxis : [
{
type : 'value',
}
],

series : [
{
data: ydata_openI,
type: 'line',
areaStyle: {
color:'red',
opacity: 0.3,
origin:"start"
},
// lineStyle:{
// width:2,
// color: '#409effd6'
// },
}
]
};
this.echartsOITd.setOption(option)
},
drawSelectData(){
var xdata=[]
var ydata_view=[]
var ydata_download=[]
var ydata_commit=[]
for(var i =0;i<this.tableDataIDOpenI.length;i++){
xdata.push(this.tableDataIDOpenI[this.tableDataID.length-1-i].date);
ydata_view.push(this.roundingF(this.tableDataID[i].view))
ydata_download.push(this.roundingF(this.tableDataID[i].download))
ydata_commit.push(this.roundingF(this.tableDataID[i].commit))
}
console.log("ydata_openI:"+ydata_download)
console.log(xdata)
this.option = {
title : {
text: '',


textStyle: {
                fontSize: 12,
            },
left:'center',
top:'bottom',

subtext: '',

},
tooltip : {
trigger: 'axis',
backgroundColor:'rgba(255,255,255,0.8)',
color:'black',
borderWidth:'1',
borderColor:'gray',
textStyle:{
color:'black'
},
},
legend: {
data:['浏览量','下载量','commit'],
// orient: 'vertical',
// top:'top',  
},
toolbox: {
show : false,
feature : {
mark : {show: true},
dataView : {show: false, readOnly: false},
magicType : {show: true, type: ['line', 'bar']},
restore : {show: false},
saveAsImage : {show: true}
}
},
calculable : true,
xAxis : [
{
type : 'category',
data : xdata,
}
],
yAxis : [
{
type : 'value',
}
],
series : [
{ name:"浏览量",
data: ydata_view,
type: 'line',
areaStyle: {},
},
{
name:"下载量",
data: ydata_download,
type: 'line',
areaStyle: {},
},
{
name:"commit",
data: ydata_commit,
type: 'line',
areaStyle: {},
},

]
};
this.echartsSelectData.setOption(this.option)

// // 使用刚指定的选择项数据显示图表。
// var selectArr = this.echartsSelectData.getOption().legend[0].data;//legend所有值
// var checkboxs=document.getElementsByName('checkboxchart');
// $(".checkboxchart").click(function(){
// var obj = {};
// for(var i=0; i<checkboxs.length; i++){
// if(checkboxs[i].checked){
// obj[selectArr[i]] = true;
// }else{
// obj[selectArr[i]] = false;
// }
// }
// option.legend.selected = obj;
// this.echartsSelectData.setOption(option);
// });


},
roundingF(value){
return Number(value).toFixed(2)
},
clickCheckBox(){
// 使用刚指定的选择项数据显示图表。
var selectArr = this.echartsSelectData.getOption().legend[0].data;//legend所有值
var checkboxs=document.getElementsByName('checkboxchart');
// $(".checkboxchart").click(function(){
var obj = {};
for(var i=0; i<checkboxs.length; i++){
if(checkboxs[i].checked){
obj[selectArr[i]] = true;
}else{
obj[selectArr[i]] = false;
}
}
this.option.legend.selected = obj;
this.echartsSelectData.setOption(this.option);
// });

}
},
filters:{
rounding (value) {
return Number(value).toFixed(2)
},
changeType(value){
if(value=='false'){
return "否"
}else{
return "是"
}
},
discriptionFun(value){
if(value==''){
return "暂无描述"
}
},
showMode(value){
if(value==1){
return "可读权限"
}else if(value==2){
return "可写权限"
}else if(value==3){
return "管理员"
}else if(value==4){
return "所有者"
}else{
return "未定义"
}
}
},
mounted() {
// document.getElementById("all").style.outline="none"
// document.getElementById("all").focus()
this.getAllProList("all",7)
console.log("id:"+this.pro_id);
// document.getElementById('radar_openi').style.width = document.getElementById('charts').offsetWidth*0.4+'px'
// document.getElementById('line_openi').style.width = document.getElementById('charts').offsetWidth*0.6+'px'
// document.getElementById('selectData').style.width = document.getElementById('linecharts').offsetWidth+'px'
this.radarOpenI = this.$echarts.init(document.getElementById('radar_openi'))
this.echartsOITd = this.$echarts.init(document.getElementById('line_openi'))
this.echartsSelectData = this.$echarts.init(document.getElementById('selectData'))
// window.onresize=function(){
// this.radarOpenI.resize();
// this.echartsOITd.resize();
// this.echartsSelectData.resize();
// }
// console.log("this.radarOpenI:"+this.radarOpenI)
},
created() {
}
}
</script>
<style scoped>
.pro_item{
font-size: 16px;
color: rgba(16, 16, 16, 100);
font-family: SourceHanSansSC-bold;
}
.sta_item{
font-size: 14px;
color: rgb(0 0 0);
font-family: SourceHanSansSC-bold;
}
.update_time{
line-height: 17px;
font-size: 12px;
color:rgba(187, 187, 187, 100);
margin-left: 10px;
}
.btn{
line-height: 1.5;
margin: -3px;
border: 1px solid #409eff;
background: #FFFF;
color: #409eff;
width: 60px;
height: 30px;
border-radius:4px ;
}
.btn:focus,
.btn:active{
background-color:#409effd6 ;
}
/deep/ .el-date-picker {
width: 200px;
}
.colorChange {
background-color: #409effd6;
}
.items{
text-align: center;
border-right:2px solid rgba(219, 219, 219, 100);
}
.item_l{
margin-right: 5px;
border:1px solid rgba(219, 219, 219, 100);
height: 370px;
width: 100%;
}
.item_r{
margin-right:5px;
border:1px solid rgba(219, 219, 219, 100);
height: 370px;
}
.item_echart{
margin-top: 10px;
margin-right: 5px;
border:1px solid rgba(219, 219, 219, 100);
height: 350px;
width: 100%;
}
</style>

+ 415
- 0
web_src/js/components/UserAnalysis.vue View File

@@ -0,0 +1,415 @@
<template>
<div>
<div style="margin-top: 10px;">
<b class="pro_item">用户分析</b> <span class="update_time">数据更新时间:{{lastUpdatedTime}}/{{recordBeginTime}}</span>
</div>
<div style="margin-top: 20px;">
<span class="sta_iterm">统计周期:</span>
<button type="button" class='btn' id ="yesterday_usr" v-bind:class="{colorChange:1==dynamic}" @click="getUserList('yesterday_usr',1)">昨天</button>
<button type="button" class='btn' id = "current_week_usr" v-bind:class="{colorChange:2==dynamic}" @click="getUserList('current_week_usr',2)">本周</button>
<button type="button" class='btn' id = "current_month_usr" v-bind:class="{colorChange:3==dynamic}" @click="getUserList('current_month_usr',3)">本月</button>
<button type="button" class='btn' id = "last_month_usr" v-bind:class="{colorChange:4==dynamic}" @click="getUserList('last_month_usr',4)">上月</button>
<button type="button" class='btn' id = "monthly_usr" v-bind:class="{colorChange:5==dynamic}" @click="getUserList('monthly_usr',5)">近30天</button>
<button type="button" class='btn' id = "current_year_usr" v-bind:class="{colorChange:6==dynamic}" @click="getUserList('current_year_usr',6)">今年</button>
<button type="button" class='btn' id = "all_usr" v-bind:class="{colorChange:7==dynamic}" @click="getUserList('all_usr',7)">所有</button>
<span style="margin-left: 20px;">
<el-date-picker
v-model="value_time"
prefix-icon="el-icon-time"
@change="getUserList('',0)"
type="daterange"
size='small'
unlink-panels
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期">
</el-date-picker>
</span>
<span style="float:right; margin-right: 20px;">
<a style="display:inline-block;margin-left: 20px; " id = 'download'>
<i class="el-icon-download"></i>
<span ><a @click="exportData()">下载报告</a> </span>
</a>
<span style="display:inline-block;margin-left: 20px; ">
<el-input size="small" placeholder="输入用户名搜索" v-model="search" class="input-with-select" @keyup.enter.native="searchName() "><i slot="suffix" class="el-input__icon el-icon-search"></i>
</el-input>
</span>
</span>
</div>
<div style="margin-top: 30px;">
<el-table
:data="tableData.slice((currentPage-1)*pageSize,currentPage*pageSize)"
style="width: 100%"
:header-cell-style="tableHeaderStyle"
:cell-style='cellStyle'>
<el-table-column
label="ID"
prop="ID"
align="center"
stripe
>
</el-table-column>
<el-table-column
label="用户名"
align="center"
prop="Name"
width="100px">
</el-table-column>
<el-table-column
prop="CodeMergeCount"
label="PR数"
align="center">
</el-table-column>
<el-table-column
prop="CommitCount"
label="commit数"
align="center">
</el-table-column>
<el-table-column
prop="IssueCount"
label="提出任务数"
align="center">
</el-table-column>
<el-table-column
prop="CommentCount"
label="评论数"
align="center">
</el-table-column>
<el-table-column
prop="FocusRepoCount"
label="关注项目数"
align="center">
</el-table-column>
<el-table-column
prop="StarRepoCount"
label="点赞项目数"
align="center">
</el-table-column>

<el-table-column
prop="LoginCount"
label="登录次数"
align="center">
</el-table-column>
<el-table-column
prop="WatchedCount"
label="关注者数"
align="center">
</el-table-column>
<el-table-column
prop="CommitCodeSize"
label="commit代码行数"
width="115px"
align="center">
</el-table-column>
<el-table-column
prop="SolveIssueCount"
label="已解决任务数"
align="center">
</el-table-column>
<el-table-column
prop="EncyclopediasCount"
label="百科页面贡献次数"
width="130px"
align="center">
</el-table-column>
<el-table-column
prop="CreateRepoCount"
label="创建项目"
align="center">
</el-table-column>
<el-table-column
prop="RegistDate"
label="用户注册时间"
width="120px"
align="center">
<template slot-scope="scope">
{{scope.row.RegistDate | transformTimestamp}}
</template>
</el-table-column>
<el-table-column
prop="CountDate"
label="系统统计时间"
width="120px"
align="center">
<template slot-scope="scope">
{{scope.row.CountDate | transformTimestamp}}
</template>
</el-table-column>
</el-table>
</div>
<div style="margin-top:50px;text-align:center">
<el-pagination
background
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-size="pageSize"
layout="prev, pager, next"
:total="tableData.length">
</el-pagination>
</div>
</div>
</template>

<script>
import { export2Excel } from '../excel/util.js'
export default{
name:'UserAnalysis',
data() {
return {
type_val:'',
recordBeginTime:'',
lastUpdatedTime:'',
currentPage:1,
pageSize:10,
params:{startDate:'',endDate:''},
tableData: [],
pickerOptions: {
},
value_time: '',
search:'',
data:'',
columns: [{title: 'ID',key: 'ID'},{title: '用户名',key: 'Name'},{title: 'PR数',key: 'CommitCount'},{title: '提出任务数',key: 'IssueCount'},{title: '评论数',key: 'CommentCount'},{title: '关注项目数',key: 'FocusRepoCount'},{title: '点赞项目数',key: 'StarRepoCount'},{title: '登录次数',key: 'LoginCount'},{title:'关注者数',key:'WatchedCount'},{title:'commit代码行数',key:'CommitCodeSize'},{title:'已解决任务数',key:'SolveIssueCount'},{title:'百科页面贡献次数',key:'EncyclopediasCount'},{title:'创建项目',key:'CreateRepoCount'},{title:'加入时间',key:'RegistDate'}],
blob:'',
fileName:'',
dynamic:7,

params_pro:{type:'all',page:1,pagesize:10,beginTime:'',endTime:'',q:'',sort:'openi'},
};
},
methods: {
exportData(){
export2Excel(this.columns,this.tableData,"测试下载excel")
},
handleCurrentChange(currentPage){
this.currentPage = currentPage;
},
formatDate(myyear,mymonth,myweekday) {
// var myyear = this.date.getFullYear();
// var mymonth = this.date.getMonth() + 1;
// var myweekday = this.date.getDate();
if (mymonth < 10) {
mymonth = "0" + mymonth;
}
if (myweekday < 10) {
myweekday = "0" + myweekday;
}
return (myyear + "-" + mymonth + "-" + myweekday);
},

// 获得某月的天数
getMonthDays(nowYear,month){
let monthStartDate = new Date(nowYear, month, 1);
let monthEndDate = new Date(nowYear, month + 1, 1);
let days = (monthEndDate - monthStartDate)/(1000 * 60 * 60 * 24);
return days;
},

getUserList(type_val,index){
this.dynamic = index;
console.log("dj:"+type_val)
var now = new Date(); // 当前日期
var nowDayOfWeek = now.getDay(); // 今天本周的第几天
var nowDay = now.getDate(); // 当前日
var nowMonth = now.getMonth(); // 当前月
var nowYear = now.getFullYear(); // 当前年
var today = this.formatDate(nowYear,nowMonth+1,nowDay);

let lastMonthDate = new Date(); // 上月日期
lastMonthDate.setDate(1);
lastMonthDate.setMonth(lastMonthDate.getMonth()-1);
let lastYear = lastMonthDate.getYear();
let lastMonth = lastMonthDate.getMonth();

if (typeof type_val=="undefined" || type_val=="null" || type_val==""){
this.params.startDate= this.formatDate(this.value_time[0].getFullYear(),this.value_time[0].getMonth() + 1,this.value_time[0].getDate());
this.params.endDate = this.formatDate(this.value_time[1].getFullYear(),this.value_time[1].getMonth() + 1,this.value_time[1].getDate());
}else{
switch(type_val){
case "yesterday_usr":{
var now = new Date();
var tmp = new Date(now.setTime(now.getTime()-24*60*60*1000));
var yesterday = this.formatDate(tmp.getFullYear(),tmp.getMonth()+1,tmp.getDate());
this.params.startDate = yesterday
this.params.endDate = yesterday
this.value_time=[]
document.getElementById("yesterday_usr").style.backgroundColor="409effd6"
document.getElementById("current_week_usr")
break
}
case "current_week_usr":{
var now = new Date(); // 当前日期
var nowDayOfWeek = now.getDay(); // 今天本周的第几天
var day = nowDayOfWeek || 7;
this.params.startDate = this.formatDate(now.getFullYear(), nowMonth+1, nowDay + 1 - day);
this.params.endDate = today
this.value_time=[]
break
}
case "current_month_usr":{
this.params.startDate = this.formatDate(nowYear,nowMonth+1,1);
this.params.endDate = today
this.value_time=[]
break
}
case "last_month_usr":{
this.params.startDate=this.formatDate(nowYear, lastMonth+1, 1);
this.params.endDate=this.formatDate(nowYear, lastMonth+1, this.getMonthDays(nowYear,lastMonth));
this.value_time=[]
break

}
case "monthly_usr":{
var temp=new Date(now - 1000 * 60 * 60 * 24 * 30)
this.params.startDate = this.formatDate(temp.getFullYear(),temp.getMonth()+1,temp.getDate());
this.params.endDate = today
this.value_time=[]
break
}
case "current_year_usr":{
this.params.startDate = this.formatDate(now.getFullYear(), 1, 1);
this.params.endDate = today
this.value_time=[]
break
}
case "all_usr":{
console.log("e:"+today)
this.params.startDate = this.formatDate(2000, 1, 1); //this.recordBeginTime//
this.params.endDate = today
this.value_time=[]
break
}
}
};
this.$axios.get('../tool/query_user_static',{
params:this.params
}).then((res)=>{
this.currentPage = 1
this.tableData = res.data
})
this.$axios.get('../api/v1/projectboard/project',{
params:this.params_pro
}).then((res)=>{
this.recordBeginTime=res.data.recordBeginTime
this.lastUpdatedTime=res.data.lastUpdatedTime
})

},
searchName(){
// this.params.q = this.search
// this.params.page = 1
// this.getUserList("all_usr")
var search = this.search;
this.getUserList("all_usr",7)
this.tableData = this.tableData.filter(data => !search || data.Name.toLowerCase().includes(search.toLowerCase()))

},
goToDetailPage(pro_id,pro_name){
sessionStorage.setItem("pro_id",pro_id);
sessionStorage.setItem("pro_name",pro_name);
document.getElementById("pro_main").style.display="none";
document.getElementById("pro_detail").style.display="block";

},
tableHeaderStyle({row,column,rowIndex,columnIndex}){
if(rowIndex===0){
return 'background:#f5f5f6;color:#606266'
}
},
cellStyle({row,column,rowIndex,columnIndex}){
if(rowIndex%2 === 1){
return 'background:#f5f5f6;color:#606266'
}
},
},
filters:{
transformTimestamp(timestamp){
console.log("timestamp",timestamp)
let a = new Date(timestamp*1000);
const date = new Date(a);
const Y = date.getFullYear() + '/';
const M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '/';
const D = (date.getDate() < 10 ? '0'+date.getDate() : date.getDate()) + ' ';
const h = (date.getHours() < 10 ? '0'+date.getHours() : date.getHours()) + ':';
const m = (date.getMinutes() <10 ? '0'+date.getMinutes() : date.getMinutes());// + ':' ;
// const s = (date.getSeconds() <10 ? '0'+date.getSeconds() : date.getSeconds()) ; // 秒
const dateString = Y + M + D + h + m ;//+ s;
console.log('dateString', dateString); // > dateString 2021-07-06 14:23
return dateString;
},
// transformTimestamp(timestamp){
// var dateString= new Date(timestamp);
// return dateString.toLocaleDateString().replace(/\//g, "-") + " " + dateString.toTimeString().substr(0, 8);
// },
},
mounted() {
document.getElementById("all_usr").style.outline="none"
document.getElementById("all_usr").focus()
this.getUserList("all_usr",7)
},
created() {
},
watch:{
search(val){
if(!val){
this.getUserList("all_usr",7)
}
}
},
}
</script>

<style scoped>
.pro_item{
font-size: 16px;
color: rgba(16, 16, 16, 100);
font-family: SourceHanSansSC-bold;
}
.sta_item{
font-size: 14px;
color: rgb(0 0 0);
font-family: SourceHanSansSC-bold;
}
.update_time{
line-height: 17px;
font-size: 12px;
color:rgba(187, 187, 187, 100);
margin-left: 10px;
}
.btn{
line-height: 1.5;
margin: -3px;
border: 1px solid #409effd6;
background: #FFFF;
color: #409eff;
width: 60px;
height: 30px;
border-radius:4px ;
}

.btn:focus,
.btn:active{
background-color:#409effd6 ;
}
/deep/ .el-date-picker {
width: 200px;
}
/deep/ .el-table {
font-size: 12px;
}

.colorChange {
background-color: #409effd6;
}

</style>

+ 5
- 2
web_src/js/components/basic/editDialog.vue View File

@@ -14,8 +14,11 @@
<slot name="content"></slot>
<div slot="footer" class="dialog-footer">
<el-button size="small" @click="deleteDialog = false">{{"取消"}}</el-button>
<el-button size="small" style="background-color: #21ba45;color: #fff;" @click="deleteCallback.call(vmContext,deleteParam)">{{"确定"}}</el-button>

<button class="ui button" @click="deleteDialog = false">{{"取消"}}</button>
<button class="ui green button" @click="deleteCallback.call(vmContext,deleteParam)">{{"确定"}}</button>
<!-- <el-button size="small" style="font-size: 1rem;padding: .78571429em 1.5em .78571429em;border-radius: .28571429rem;" @click="deleteDialog = false">{{"取消"}}</el-button>
<el-button size="small" style="background-color: #21ba45;color: #fff;font-size: 1rem;padding: .78571429em 1.5em .78571429em;border-radius: .28571429rem;" @click="deleteCallback.call(vmContext,deleteParam)">{{"确定"}}</el-button> -->
</div>
</el-dialog>
</template>


+ 179
- 0
web_src/js/excel/Blob.js View File

@@ -0,0 +1,179 @@
/* eslint-disable */
/* Blob.js
* A Blob implementation.
* 2014-05-27
*
* By Eli Grey, http://eligrey.com
* By Devin Samarin, https://github.com/eboyjr
* License: X11/MIT
* See LICENSE.md
*/
/*global self, unescape */
/*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true,
plusplus: true */
/*! @source http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js */
(function (view) {
"use strict";
view.URL = view.URL || view.webkitURL;
if (view.Blob && view.URL) {
try {
new Blob;
return;
} catch (e) {}
}
// Internally we use a BlobBuilder implementation to base Blob off of
// in order to support older browsers that only have BlobBuilder
var BlobBuilder = view.BlobBuilder || view.WebKitBlobBuilder || view.MozBlobBuilder || (function(view) {
var
get_class = function(object) {
return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1];
}
, FakeBlobBuilder = function BlobBuilder() {
this.data = [];
}
, FakeBlob = function Blob(data, type, encoding) {
this.data = data;
this.size = data.length;
this.type = type;
this.encoding = encoding;
}
, FBB_proto = FakeBlobBuilder.prototype
, FB_proto = FakeBlob.prototype
, FileReaderSync = view.FileReaderSync
, FileException = function(type) {
this.code = this[this.name = type];
}
, file_ex_codes = (
"NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR "
+ "NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR"
).split(" ")
, file_ex_code = file_ex_codes.length
, real_URL = view.URL || view.webkitURL || view
, real_create_object_URL = real_URL.createObjectURL
, real_revoke_object_URL = real_URL.revokeObjectURL
, URL = real_URL
, btoa = view.btoa
, atob = view.atob
, ArrayBuffer = view.ArrayBuffer
, Uint8Array = view.Uint8Array
;
FakeBlob.fake = FB_proto.fake = true;
while (file_ex_code--) {
FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1;
}
if (!real_URL.createObjectURL) {
URL = view.URL = {};
}
URL.createObjectURL = function(blob) {
var
type = blob.type
, data_URI_header
;
if (type === null) {
type = "application/octet-stream";
}
if (blob instanceof FakeBlob) {
data_URI_header = "data:" + type;
if (blob.encoding === "base64") {
return data_URI_header + ";base64," + blob.data;
} else if (blob.encoding === "URI") {
return data_URI_header + "," + decodeURIComponent(blob.data);
} if (btoa) {
return data_URI_header + ";base64," + btoa(blob.data);
} else {
return data_URI_header + "," + encodeURIComponent(blob.data);
}
} else if (real_create_object_URL) {
return real_create_object_URL.call(real_URL, blob);
}
};
URL.revokeObjectURL = function(object_URL) {
if (object_URL.substring(0, 5) !== "data:" && real_revoke_object_URL) {
real_revoke_object_URL.call(real_URL, object_URL);
}
};
FBB_proto.append = function(data/*, endings*/) {
var bb = this.data;
// decode data to a binary string
if (Uint8Array && (data instanceof ArrayBuffer || data instanceof Uint8Array)) {
var
str = ""
, buf = new Uint8Array(data)
, i = 0
, buf_len = buf.length
;
for (; i < buf_len; i++) {
str += String.fromCharCode(buf[i]);
}
bb.push(str);
} else if (get_class(data) === "Blob" || get_class(data) === "File") {
if (FileReaderSync) {
var fr = new FileReaderSync;
bb.push(fr.readAsBinaryString(data));
} else {
// async FileReader won't work as BlobBuilder is sync
throw new FileException("NOT_READABLE_ERR");
}
} else if (data instanceof FakeBlob) {
if (data.encoding === "base64" && atob) {
bb.push(atob(data.data));
} else if (data.encoding === "URI") {
bb.push(decodeURIComponent(data.data));
} else if (data.encoding === "raw") {
bb.push(data.data);
}
} else {
if (typeof data !== "string") {
data += ""; // convert unsupported types to strings
}
// decode UTF-16 to binary string
bb.push(unescape(encodeURIComponent(data)));
}
};
FBB_proto.getBlob = function(type) {
if (!arguments.length) {
type = null;
}
return new FakeBlob(this.data.join(""), type, "raw");
};
FBB_proto.toString = function() {
return "[object BlobBuilder]";
};
FB_proto.slice = function(start, end, type) {
var args = arguments.length;
if (args < 3) {
type = null;
}
return new FakeBlob(
this.data.slice(start, args > 1 ? end : this.data.length)
, type
, this.encoding
);
};
FB_proto.toString = function() {
return "[object Blob]";
};
FB_proto.close = function() {
this.size = this.data.length = 0;
};
return FakeBlobBuilder;
}(view));
view.Blob = function Blob(blobParts, options) {
var type = options ? (options.type || "") : "";
var builder = new BlobBuilder();
if (blobParts) {
for (var i = 0, len = blobParts.length; i < len; i++) {
builder.append(blobParts[i]);
}
}
return builder.getBlob(type);
};
}(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content || this));

+ 141
- 0
web_src/js/excel/Export2Excel.js View File

@@ -0,0 +1,141 @@
/* eslint-disable */
require('script-loader!file-saver');
require('./Blob');
require('script-loader!xlsx/dist/xlsx.core.min');
function generateArray(table) {
var out = [];
var rows = table.querySelectorAll('tr');
var ranges = [];
for (var R = 0; R < rows.length; ++R) {
var outRow = [];
var row = rows[R];
var columns = row.querySelectorAll('td');
for (var C = 0; C < columns.length; ++C) {
var cell = columns[C];
var colspan = cell.getAttribute('colspan');
var rowspan = cell.getAttribute('rowspan');
var cellValue = cell.innerText;
if (cellValue !== "" && cellValue == +cellValue) cellValue = +cellValue;
//Skip ranges
ranges.forEach(function (range) {
if (R >= range.s.r && R <= range.e.r && outRow.length >= range.s.c && outRow.length <= range.e.c) {
for (var i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null);
}
});
//Handle Row Span
if (rowspan || colspan) {
rowspan = rowspan || 1;
colspan = colspan || 1;
ranges.push({s: {r: R, c: outRow.length}, e: {r: R + rowspan - 1, c: outRow.length + colspan - 1}});
}
;
//Handle Value
outRow.push(cellValue !== "" ? cellValue : null);
//Handle Colspan
if (colspan) for (var k = 0; k < colspan - 1; ++k) outRow.push(null);
}
out.push(outRow);
}
return [out, ranges];
};
function datenum(v, date1904) {
if (date1904) v += 1462;
var epoch = Date.parse(v);
return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
}
function sheet_from_array_of_arrays(data, opts) {
var ws = {};
var range = {s: {c: 10000000, r: 10000000}, e: {c: 0, r: 0}};
for (var R = 0; R != data.length; ++R) {
for (var C = 0; C != data[R].length; ++C) {
if (range.s.r > R) range.s.r = R;
if (range.s.c > C) range.s.c = C;
if (range.e.r < R) range.e.r = R;
if (range.e.c < C) range.e.c = C;
var cell = {v: data[R][C]};
if (cell.v == null) continue;
var cell_ref = XLSX.utils.encode_cell({c: C, r: R});
if (typeof cell.v === 'number') cell.t = 'n';
else if (typeof cell.v === 'boolean') cell.t = 'b';
else if (cell.v instanceof Date) {
cell.t = 'n';
cell.z = XLSX.SSF._table[14];
cell.v = datenum(cell.v);
}
else cell.t = 's';
ws[cell_ref] = cell;
}
}
if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);
return ws;
}
function Workbook() {
if (!(this instanceof Workbook)) return new Workbook();
this.SheetNames = [];
this.Sheets = {};
}
function s2ab(s) {
var buf = new ArrayBuffer(s.length);
var view = new Uint8Array(buf);
for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
return buf;
}
export function export_table_to_excel(id) {
var theTable = document.getElementById(id);
console.log('a')
var oo = generateArray(theTable);
var ranges = oo[1];
/* original data */
var data = oo[0];
var ws_name = "SheetJS";
console.log(data);
var wb = new Workbook(), ws = sheet_from_array_of_arrays(data);
/* add ranges to worksheet */
// ws['!cols'] = ['apple', 'banan'];
ws['!merges'] = ranges;
/* add worksheet to workbook */
wb.SheetNames.push(ws_name);
wb.Sheets[ws_name] = ws;
var wbout = XLSX.write(wb, {bookType: 'xlsx', bookSST: false, type: 'binary'});
saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), "test.xlsx")
}
function formatJson(jsonData) {
console.log(jsonData)
}
export function export_json_to_excel(th, jsonData, defaultTitle) {
/* original data */
var data = jsonData;
data.unshift(th);
var ws_name = "SheetJS";
var wb = new Workbook(), ws = sheet_from_array_of_arrays(data);
/* add worksheet to workbook */
wb.SheetNames.push(ws_name);
wb.Sheets[ws_name] = ws;
var wbout = XLSX.write(wb, {bookType: 'xlsx', bookSST: false, type: 'binary'});
var title = defaultTitle || '列表'
saveAs(new Blob([s2ab(wbout)], {type: "application/octet-stream"}), title + ".xlsx")
}

+ 17
- 0
web_src/js/excel/util.js View File

@@ -0,0 +1,17 @@
export function export2Excel(columns,list,filename){
require.ensure([], () => {
const { export_json_to_excel } = require('./Export2Excel');
let tHeader = []
let filterVal = []
console.log(columns)
if(!columns){
return;
}
columns.forEach(item =>{
tHeader.push(item.title)
filterVal.push(item.key)
})
const data = list.map(v => filterVal.map(j => v[j]))
export_json_to_excel(tHeader, data, filename);
})
}

+ 74
- 0
web_src/js/features/letteravatar.js View File

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

+ 40
- 4
web_src/js/index.js View File

@@ -4,7 +4,7 @@

import './publicpath.js';
import './polyfills.js';
import './features/letteravatar.js'
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
@@ -13,7 +13,7 @@ import qs from 'qs';
import 'jquery.are-you-sure';
import './vendor/semanticdropdown.js';
import {svg} from './utils.js';
import echarts from 'echarts'
import initContextPopups from './features/contextpopup.js';
import initGitGraph from './features/gitgraph.js';
import initClipboard from './features/clipboard.js';
@@ -35,14 +35,20 @@ import {createCodeEditor} from './features/codeeditor.js';
import MinioUploader from './components/MinioUploader.vue';
import ObsUploader from './components/ObsUploader.vue';
import EditAboutInfo from './components/EditAboutInfo.vue';
import Images from './components/Images.vue'
import EditTopics from './components/EditTopics.vue'
import Images from './components/Images.vue';
import EditTopics from './components/EditTopics.vue';
import DataAnalysis from './components/DataAnalysis.vue'
import Contributors from './components/Contributors.vue'

Vue.use(ElementUI);
Vue.prototype.$axios = axios;
Vue.prototype.qs = qs;
const {AppSubUrl, StaticUrlPrefix, csrf} = window.config;

Object.defineProperty(Vue.prototype, '$echarts', {
value: echarts
})

function htmlEncode(text) {
return jQuery('<div />')
.text(text)
@@ -2969,7 +2975,9 @@ $(document).ready(async () => {
initObsUploader();
initVueEditAbout();
initVueEditTopic();
initVueContributors();
initVueImages();
initVueDataAnalysis();
initTeamSettings();
initCtrlEnterSubmit();
initNavbarContentToggle();
@@ -3682,6 +3690,21 @@ function initVueEditTopic() {
render:h=>h(EditTopics)
})
}

function initVueContributors() {
const el = document.getElementById('Contributors');
if (!el) {
return;
}

new Vue({
el:'#Contributors',
render:h=>h(Contributors)
})
}


function initVueImages() {
const el = document.getElementById('images');
console.log("el",el)
@@ -3696,7 +3719,20 @@ function initVueImages() {
render: h => h(Images)
});
}
function initVueDataAnalysis() {
const el = document.getElementById('data_analysis');
console.log("el",el)
if (!el) {
return;
}

new Vue({
el: '#data_analysis',
render: h => h(DataAnalysis)
});
}
// 新增
function initObsUploader() {
const el = document.getElementById('obsUploader');


+ 42
- 0
web_src/less/openi.less View File

@@ -243,6 +243,48 @@ footer .column{margin-bottom:0!important; padding-bottom:0!important;}
display: inline-block;
width: 100%;
}
.git-user-content{
margin-bottom: 16px;
word-break: break-all;
}
.row.git-user-content .ui.avatar.s16.image {
width: 50px !important;
height: 50px !important;
vertical-align: middle;
border-radius: 500rem;}
.user-list-item {
padding: 0.3em 0px !important;
margin: 15px 0 !important;
width: 220px !important;
}
.row.git-user-content .content {
margin-left: 6px;
display: inline-block;
vertical-align: top !important;
overflow: hidden;
}
.row.git-user-content .content .header {
font-weight: bold;
}
.item.user-list-item .header {
line-height: 23px !important;
font-size: 16px !important;
text-overflow: ellipsis;
overflow: hidden;
width: 145px;
white-space:nowrap;
}
.item.user-list-item .header a {
color: #587284 !important;
}
.row.git-user-content .content .commit-btn {
margin-top: 6px;
float: left;
font-size: 11px;
color: #40485b !important;
}

.dropdown-menu {
position: relative;


Loading…
Cancel
Save