Browse Source

Merge remote-tracking branch 'origin/V20211018' into zouap

pull/537/head
zouap 3 years ago
parent
commit
ab50d750a7
48 changed files with 3451 additions and 411 deletions
  1. +1
    -1
      README.md
  2. +1
    -0
      go.mod
  3. +36
    -6
      models/cloudbrain.go
  4. +5
    -1
      models/repo_activity_custom.go
  5. +14
    -0
      models/repo_list.go
  6. +12
    -0
      models/user.go
  7. +112
    -1
      modules/git/repo_stats_custom.go
  8. +312
    -0
      modules/repository/elk_pagedata.go
  9. +16
    -0
      modules/setting/setting.go
  10. +13
    -2
      options/locale/locale_en-US.ini
  11. +11
    -0
      options/locale/locale_zh-CN.ini
  12. +1653
    -0
      public/img/icons.svg
  13. BIN
      public/img/loading.gif
  14. +4
    -3
      routers/home.go
  15. +26
    -3
      routers/repo/cloudbrain.go
  16. +2
    -0
      routers/repo/modelarts.go
  17. +40
    -0
      routers/repo/view.go
  18. +9
    -0
      routers/secure/user.go
  19. +1
    -1
      templates/base/head_navbar.tmpl
  20. +1
    -1
      templates/base/head_navbar_home.tmpl
  21. +2
    -2
      templates/explore/dataset_search.tmpl
  22. +11
    -11
      templates/explore/repo_left.tmpl
  23. +14
    -14
      templates/explore/repo_list.tmpl
  24. +1
    -1
      templates/explore/repo_search.tmpl
  25. +2
    -2
      templates/explore/search.tmpl
  26. +66
    -15
      templates/repo/cloudbrain/index.tmpl
  27. +35
    -46
      templates/repo/datasets/dataset_list.tmpl
  28. +3
    -3
      templates/repo/datasets/index.tmpl
  29. +95
    -26
      templates/repo/home.tmpl
  30. +18
    -8
      templates/repo/issue/labels.tmpl
  31. +2
    -2
      templates/repo/issue/list.tmpl
  32. +8
    -2
      templates/repo/issue/milestone_issues.tmpl
  33. +22
    -6
      templates/repo/issue/milestone_new.tmpl
  34. +19
    -8
      templates/repo/issue/milestones.tmpl
  35. +1
    -1
      templates/repo/issue/navbar.tmpl
  36. +15
    -3
      templates/repo/issue/new.tmpl
  37. +1
    -1
      templates/repo/issue/search.tmpl
  38. +15
    -2
      templates/repo/issue/view.tmpl
  39. +43
    -13
      templates/repo/modelarts/index.tmpl
  40. +15
    -1
      templates/repo/pulls/commits.tmpl
  41. +15
    -1
      templates/repo/pulls/files.tmpl
  42. +1
    -1
      templates/user/dashboard/issues.tmpl
  43. +477
    -0
      web_src/js/components/EditTopics.vue
  44. +4
    -5
      web_src/js/components/MinioUploader.vue
  45. +5
    -2
      web_src/js/components/ObsUploader.vue
  46. +255
    -214
      web_src/js/index.js
  47. +15
    -2
      web_src/less/_dataset.less
  48. +22
    -0
      web_src/less/openi.less

+ 1
- 1
README.md View File

@@ -2,7 +2,7 @@

<h1><img src="public/img/favicon.png" alt="logo" width="30" height="30">AiForge - 启智AI开发协作平台</h1>

[![release](https://img.shields.io/badge/release-1.21.10.1-blue)](https://git.openi.org.cn/OpenI/aiforge/releases/latest)
[![release](https://img.shields.io/badge/release-1.21.9.2-blue)](https://git.openi.org.cn/OpenI/aiforge/releases/latest)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)




+ 1
- 0
go.mod View File

@@ -52,6 +52,7 @@ require (
github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14
github.com/golang/protobuf v1.4.1 // indirect
github.com/gomodule/redigo v2.0.0+incompatible
github.com/google/go-github/v24 v24.0.1
github.com/gorilla/context v1.1.1
github.com/hashicorp/go-retryablehttp v0.6.6 // indirect


+ 36
- 6
models/cloudbrain.go View File

@@ -5,11 +5,12 @@ import (
"fmt"
"strings"
"time"
"xorm.io/builder"
"xorm.io/xorm"

"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/builder"
)

type CloudbrainStatus string
@@ -59,12 +60,18 @@ type Cloudbrain struct {
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
DeletedAt time.Time `xorm:"deleted"`
CanDebug bool `xorm:"-"`
CanDel bool `xorm:"-"`
Type int `xorm:"INDEX DEFAULT 0"`

User *User `xorm:"-"`
Repo *Repository `xorm:"-"`
}

type CloudbrainInfo struct {
Cloudbrain `xorm:"extends"`
User `xorm:"extends"`
}

type CloudBrainLoginResult struct {
Code string
Msg string
@@ -523,7 +530,7 @@ type NotebookDelResult struct {
InstanceID string `json:"instance_id"`
}

func Cloudbrains(opts *CloudbrainsOptions) ([]*Cloudbrain, int64, error) {
func Cloudbrains(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int64, error) {
sess := x.NewSession()
defer sess.Close()

@@ -583,8 +590,10 @@ func Cloudbrains(opts *CloudbrainsOptions) ([]*Cloudbrain, int64, error) {
}

sess.OrderBy("cloudbrain.created_unix DESC")
cloudbrains := make([]*Cloudbrain, 0, setting.UI.IssuePagingNum)
if err := sess.Where(cond).Find(&cloudbrains); err != nil {
cloudbrains := make([]*CloudbrainInfo, 0, setting.UI.IssuePagingNum)
if err := sess.Table(&Cloudbrain{}).Where(cond).
Join("left", "`user`", "cloudbrain.user_id = `user`.id").
Find(&cloudbrains); err != nil {
return nil, 0, fmt.Errorf("Find: %v", err)
}
sess.Close()
@@ -622,13 +631,13 @@ func GetCloudbrainByJobID(jobID string) (*Cloudbrain, error) {

func GetCloudbrainsNeededStopByUserID(userID int64) ([]*Cloudbrain, error) {
cloudBrains := make([]*Cloudbrain, 0)
err := x.Cols("job_id", "status", "type").Where("user_id=? AND (status =? OR status=?)", userID, string(JobRunning), string(JobWaiting)).Find(&cloudBrains)
err := x.Cols("job_id", "status", "type").Where("user_id=? AND status !=?", userID, string(JobStopped)).Find(&cloudBrains)
return cloudBrains, err
}

func GetCloudbrainsNeededStopByRepoID(repoID int64) ([]*Cloudbrain, error) {
cloudBrains := make([]*Cloudbrain, 0)
err := x.Cols("job_id", "status", "type").Where("repo_id=? AND (status =? OR status=?)", repoID, string(JobRunning), string(JobWaiting)).Find(&cloudBrains)
err := x.Cols("job_id", "status", "type").Where("repo_id=? AND status !=?", repoID, string(JobStopped)).Find(&cloudBrains)
return cloudBrains, err
}

@@ -662,3 +671,24 @@ func GetCloudbrainByName(jobName string) (*Cloudbrain, error) {
cb := &Cloudbrain{JobName: jobName}
return getRepoCloudBrain(cb)
}

func CanDelJob(isSigned bool, user *User, job *CloudbrainInfo) bool {
if !isSigned || job.Status != string(JobStopped) {
return false
}
repo, err := GetRepositoryByID(job.RepoID)
if err != nil {
log.Error("GetRepositoryByID failed:%v", err.Error())
return false
}
permission, _ := GetUserRepoPermission(repo, user)
if err != nil {
log.Error("GetUserRepoPermission failed:%v", err.Error())
return false
}

if user.ID == job.UserID || user.IsAdmin || permission.AccessMode >= AccessModeAdmin {
return true
}
return false
}

+ 5
- 1
models/repo_activity_custom.go View File

@@ -3,7 +3,11 @@ package models
import "code.gitea.io/gitea/modules/git"

func GetRepoKPIStats(repo *Repository) (*git.RepoKPIStats, error) {
return git.GetRepoKPIStats(repo.RepoPath())
wikiPath := ""
if repo.HasWiki() {
wikiPath = repo.WikiPath()
}
return git.GetRepoKPIStats(repo.RepoPath(), wikiPath)
}

func GetAllUserKPIStats() (map[string]*git.UserKPIStats, error) {


+ 14
- 0
models/repo_list.go View File

@@ -166,6 +166,8 @@ type SearchRepoOptions struct {
Archived util.OptionalBool
// only search topic name
TopicOnly bool
//search by Specific TopicName
TopicName string
// include description in keyword search
IncludeDescription bool
// None -> include has milestones AND has no milestone
@@ -327,6 +329,18 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
}
cond = cond.And(keywordCond)
}
if opts.TopicName != "" {
var subQueryCond = builder.NewCond()
subQueryCond = subQueryCond.Or(builder.Eq{"topic.name": opts.TopicName})
subQuery := builder.Select("repo_topic.repo_id").From("repo_topic").
Join("INNER", "topic", "topic.id = repo_topic.topic_id").
Where(subQueryCond).
GroupBy("repo_topic.repo_id")

var topicNameCond = builder.In("id", subQuery)
cond = cond.And(topicNameCond)

}

if opts.Fork != util.OptionalBoolNone {
cond = cond.And(builder.Eq{"is_fork": opts.Fork == util.OptionalBoolTrue})


+ 12
- 0
models/user.go View File

@@ -1556,6 +1556,18 @@ func GetUserByActivateEmail(email string) (*User, error) {
if len(users) >= 1 {
return &users[0],nil
}else {
// Finally, if email address is the protected email address:用户邮件地址设置为隐藏电子邮件地址
if strings.HasSuffix(email, fmt.Sprintf("@%s", setting.Service.NoReplyAddress)) {
username := strings.TrimSuffix(email, fmt.Sprintf("@%s", setting.Service.NoReplyAddress))
user := &User{LowerName: username}
has, err := ctx.e.Get(user)
if err != nil {
return nil, err
}
if has {
return user, nil
}
}
return nil, errors.New("cannot find user by email")
}
}


+ 112
- 1
modules/git/repo_stats_custom.go View File

@@ -4,18 +4,23 @@ import (
"bufio"
"bytes"
"fmt"
"net/url"
"sort"
"strconv"
"strings"
"time"

Log "code.gitea.io/gitea/modules/log"
)

type RepoKPIStats struct {
Contributors int64
KeyContributors int64
DevelopAge int64
ContributorsAdded int64
CommitsAdded int64
CommitLinesModified int64
WikiPages int64
Authors []*UserKPITypeStats
}

@@ -30,7 +35,7 @@ type UserKPITypeStats struct {
isNewContributor bool //是否是4个月内的新增贡献者
}

func GetRepoKPIStats(repoPath string) (*RepoKPIStats, error) {
func GetRepoKPIStats(repoPath string, wikiPath string) (*RepoKPIStats, error) {
stats := &RepoKPIStats{}

contributors, err := GetContributors(repoPath)
@@ -66,15 +71,42 @@ func GetRepoKPIStats(repoPath string) (*RepoKPIStats, error) {

}

err = setDevelopAge(repoPath, stats)
if err != nil {
return nil, fmt.Errorf("FillFromGit: %v", err)
}
err = setRepoKPIStats(repoPath, fourMonthAgo, stats, newContributersDict)

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

setWikiPages(wikiPath, stats)
return stats, nil

}

func setDevelopAge(repoPath string, stats *RepoKPIStats) error {
args := []string{"log", "--no-merges", "--branches=*", "--format=%cd", "--date=short"}
stdout, err := NewCommand(args...).RunInDirBytes(repoPath)
if err != nil {
return err
}
scanner := bufio.NewScanner(bytes.NewReader(stdout))
scanner.Split(bufio.ScanLines)
developMonth := make(map[string]struct{})
for scanner.Scan() {
l := strings.TrimSpace(scanner.Text())
month := l[0:strings.LastIndex(l, "-")]
if _, ok := developMonth[month]; !ok {
developMonth[month] = struct{}{}
}
}

stats.DevelopAge = int64(len(developMonth))
return nil
}

//获取一天内的用户贡献指标
func GetUserKPIStats(repoPath string) (map[string]*UserKPIStats, error) {
timeUntil := time.Now()
@@ -256,3 +288,82 @@ func getContributors(repoPath string, fromTime time.Time) ([]Contributor, error)
}
return nil, nil
}

func setWikiPages(wikiPath string, stats *RepoKPIStats) {
wikiPages := 0

if wikiPath == "" {
stats.WikiPages = int64(wikiPages)
return
}

wikiRepo, commit, err := findWikiRepoCommit(wikiPath)
if err != nil {
if !IsErrNotExist(err) {
Log.Warn("GetBranchCommit", err)
}
stats.WikiPages = int64(wikiPages)
return
}

// Get page list.
entries, err := commit.ListEntries()
if err != nil {
if wikiRepo != nil {
wikiRepo.Close()
}
Log.Warn("GetBranchCommit", err)
stats.WikiPages = int64(wikiPages)
return

}

for _, entry := range entries {
if !entry.IsRegular() {
continue
}

wikiName, err := filenameToName(entry.Name())
if err != nil || wikiName == "_Sidebar" || wikiName == "_Footer" {
continue
}

wikiPages += 1

}
//确保wikiRepo用完被关闭
defer func() {
if wikiRepo != nil {
wikiRepo.Close()
}
}()
stats.WikiPages = int64(wikiPages)
return

}

func filenameToName(filename string) (string, error) {
if !strings.HasSuffix(filename, ".md") {
return "", fmt.Errorf("invalid file")
}
basename := filename[:len(filename)-3]
unescaped, err := url.QueryUnescape(basename)
if err != nil {
return "", err
}
return strings.Replace(unescaped, "-", " ", -1), nil
}

func findWikiRepoCommit(wikiPath string) (*Repository, *Commit, error) {
wikiRepo, err := OpenRepository(wikiPath)
if err != nil {

return nil, nil, err
}

commit, err := wikiRepo.GetBranchCommit("master")
if err != nil {
return wikiRepo, nil, err
}
return wikiRepo, commit, nil
}

+ 312
- 0
modules/repository/elk_pagedata.go View File

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

import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"

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

//输入elk的json结构begin
type InputInfo struct {
Batch []Batch `json:"batch"`
}
type Fields struct {
Field string `json:"field"`
Format string `json:"format"`
}
type MatchPhrase struct {
Message string `json:"message"`
}
type Should struct {
MatchPhrase MatchPhrase `json:"match_phrase"`
}
type Bool struct {
Should []Should `json:"should"`
MinimumShouldMatch int `json:"minimum_should_match"`
}
type Timestamptest struct {
// Gte time.Time `json:"gte"`
Gte string `json:"gte"`
Lte string `json:"lte"`
Format string `json:"format"`
}
type Range struct {
Timestamptest Timestamptest `json:"@timestamptest"`
}

type FilterMatchPhrase struct {
UserName string `json:"userName.keyword,omitempty"`
ProjectName string `json:"projectName.keyword,omitempty"`
TagName string `json:"tagName.keyword,omitempty"`
}

type Filter struct {
Bool *Bool `json:"bool,omitempty"`
Range *Range `json:"range,omitempty"`
FilterMatchPhrase *FilterMatchPhrase `json:"match_phrase,omitempty"`
}
type MustNotMatchPhrase struct {
ProjectName string `json:"projectName"`
}
type MustNot struct {
MustNotMatchPhrase MustNotMatchPhrase `json:"match_phrase"`
}
type BoolIn struct {
Filter []Filter `json:"filter"`
MustNot []MustNot `json:"must_not"`
}
type Query struct {
BoolIn BoolIn `json:"bool"`
}
type Body struct {
Size int `json:"size"`
Fields []Fields `json:"fields"`
Query Query `json:"query"`
}
type Params struct {
Index string `json:"index"`
Body Body `json:"body"`
}
type Request struct {
Params Params `json:"params"`
}
type Batch struct {
Request Request `json:"request"`
}

//输入elk的json结构end

//elk输出的json结构begin
type Hits struct {
Total int `json:"total"`
}
type RawResponse struct {
Hits Hits `json:"hits"`
}
type Result struct {
RawResponse RawResponse `json:"rawResponse"`
Loaded int `json:"loaded"`
}
type ResultInfo struct {
Id int `json:"id"`
Result Result `json:"result"`
}

//elk输出的json结构end

//发送post请求到elk
func SendReqToElk(jsonStr []byte) (content string) {
ElkBase64Init := setting.ElkUser + ":" + setting.ElkPassword
ElkBase64 := base64.StdEncoding.EncodeToString([]byte(ElkBase64Init))
BasicElkBase64 := "Basic" + " " + ElkBase64
url := setting.ElkUrl
req, _ := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("kbn-version", "7.13.2")
req.Header.Set("Authorization", BasicElkBase64)

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
return string(body)
}

//处理返回的elk数据,只保留totalView,即访问量;loaded是分片载入次数,用来判断返回的数据是否准确
func GetResultFromElk(resultinfo ResultInfo, jobResult string) (loaded int, totalView int) {
var resultTest ResultInfo
errs := json.Unmarshal([]byte(jobResult), &resultTest)
fmt.Println(errs)
return resultTest.Result.Loaded, resultTest.Result.RawResponse.Hits.Total
}

//初始化传给elk的数据结构,给定用户名和项目名,查询的起止时间,返回初始化后的结构
func ProjectViewInit(User string, Project string, Gte string, Lte string) (projectViewInit InputInfo) {
var inputStruct InputInfo
inputStruct.Batch = make([]Batch, 1)
inputStruct.Batch[0].Request.Params.Index = setting.Index
inputStruct.Batch[0].Request.Params.Body.Size = 0
inputStruct.Batch[0].Request.Params.Body.Fields = make([]Fields, 1)
inputStruct.Batch[0].Request.Params.Body.Fields[0].Field = setting.TimeField
inputStruct.Batch[0].Request.Params.Body.Fields[0].Format = setting.ElkTimeFormat
inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.Filter = make([]Filter, 3)
//限定查询时间
var timeRange Range
timeRange.Timestamptest.Gte = Gte
timeRange.Timestamptest.Lte = Lte
inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.Filter[0].Range = &timeRange
//限定用户
var userName FilterMatchPhrase
userName.UserName = User
inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.Filter[1].FilterMatchPhrase = &userName
//限定项目
var projectName FilterMatchPhrase
projectName.ProjectName = Project
inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.Filter[2].FilterMatchPhrase = &projectName
return inputStruct
}

//初始化传给elk的数据结构,给定查询信息和非项目名,查询的起止时间,返回初始化后的结构
func AllProjectViewInit(MessageInfo string, NotProject string, Gte string, Lte string) (allProjectViewInit InputInfo) {
var inputStruct InputInfo
inputStruct.Batch = make([]Batch, 1)
inputStruct.Batch[0].Request.Params.Index = setting.Index
inputStruct.Batch[0].Request.Params.Body.Size = 0
inputStruct.Batch[0].Request.Params.Body.Fields = make([]Fields, 1)
inputStruct.Batch[0].Request.Params.Body.Fields[0].Field = setting.TimeField
inputStruct.Batch[0].Request.Params.Body.Fields[0].Format = setting.ElkTimeFormat
inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.Filter = make([]Filter, 2)
//限定message
var bool Bool
bool.Should = make([]Should, 1)
bool.Should[0].MatchPhrase.Message = MessageInfo
bool.MinimumShouldMatch = 1
inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.Filter[0].Bool = &bool
//限定查询时间
var timeRange Range
timeRange.Timestamptest.Gte = Gte
timeRange.Timestamptest.Lte = Lte
inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.Filter[1].Range = &timeRange
//限定非项目
// var boolIn BoolIn
inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.MustNot = make([]MustNot, 1)
inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.MustNot[0].MustNotMatchPhrase.ProjectName = NotProject
return inputStruct
}

//初始化传给elk的数据结构,给定查询信息和tagName,查询的起止时间,返回初始化后的结构
func TagNameInit(MessageInfo string, Tagname string, Gte string, Lte string) (projectViewInit InputInfo) {
var inputStruct InputInfo
inputStruct.Batch = make([]Batch, 1)
inputStruct.Batch[0].Request.Params.Index = setting.Index
inputStruct.Batch[0].Request.Params.Body.Size = 0
inputStruct.Batch[0].Request.Params.Body.Fields = make([]Fields, 1)
inputStruct.Batch[0].Request.Params.Body.Fields[0].Field = setting.TimeField
inputStruct.Batch[0].Request.Params.Body.Fields[0].Format = setting.ElkTimeFormat
inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.Filter = make([]Filter, 3)
//限定message
var bool Bool
bool.Should = make([]Should, 1)
bool.Should[0].MatchPhrase.Message = MessageInfo
bool.MinimumShouldMatch = 1
inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.Filter[0].Bool = &bool
//限定tagName
var tagName FilterMatchPhrase
tagName.TagName = Tagname
inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.Filter[1].FilterMatchPhrase = &tagName
//限定查询时间
var timeRange Range
timeRange.Timestamptest.Gte = Gte
timeRange.Timestamptest.Lte = Lte
inputStruct.Batch[0].Request.Params.Body.Query.BoolIn.Filter[2].Range = &timeRange
return inputStruct
}

//向elk发送请求,将获取的结果只保留访问量,输入是初始化后的数据结构,返回访问量
func ViewInfo(viewInfo InputInfo) (totalView int) {
jsons, errs := json.Marshal(viewInfo)
if errs != nil {
fmt.Println("errs:", errs.Error())
}
// fmt.Println("viewInfoInit:",string(jsons))
var jsonStr = []byte(jsons)
var resultInfo ResultInfo
loaded, totalView := GetResultFromElk(resultInfo, SendReqToElk(jsonStr))
time := 0
for {
if loaded == 0 {
loaded_next, totalView := GetResultFromElk(resultInfo, SendReqToElk(jsonStr))
time++
if loaded_next != 0 && time < 100 {
fmt.Println("totalView:", totalView)
return totalView
}
if time > 100 {
break
}
} else {
break
}
}
fmt.Println("loaded:", loaded)
return totalView
}

// @title ProjectView
// @description 获取指定用户和项目的访问量
// @param User string "用户名"
// @param Project string "项目名"
// @param Gte string "起始时间" 如time.Now().AddDate(0, 0, -1).Format(time.RFC3339)
// @param Lte string "结束时间" 如time.Now().Format(time.RFC3339)
// @return totalView int "访问量"
func AppointProjectView(User string, Project string, Gte string, Lte string) (totalView int) {
InitInfo := ProjectViewInit(User, Project, Gte, Lte)
return ViewInfo(InitInfo)
}

//统计项目相关页面的访问量
type ProjectInfo struct {
/* 统计所有项目中该页面的浏览情况,不需要区分项目。以aiforge项目为例 */
//地址:https://git.openi.org.cn/OpenI/aiforge/datasets?type=0
Project_dataset_type_0 int
//地址:https://git.openi.org.cn/OpenI/aiforge/datasets?type=1
Project_dataset_type_1 int
//地址:https://git.openi.org.cn/OpenI/aiforge/issues
Project_issues int
//地址:https://git.openi.org.cn/OpenI/aiforge/labels
Project_labels int
//地址:https://git.openi.org.cn/OpenI/aiforge/milestones
Project_milestones int
//地址:https://git.openi.org.cn/OpenI/aiforge/pulls
Project_pulls int
//地址:https://git.openi.org.cn/OpenI/aiforge/release
Project_release int
//地址:https://git.openi.org.cn/OpenI/aiforge/wiki
Project_wiki int
//地址:https://git.openi.org.cn/OpenI/aiforge/activity
Project_activity int
//地址:https://git.openi.org.cn/OpenI/aiforge/cloudbrain
Project_cloudbrain int
//地址:https://git.openi.org.cn/OpenI/aiforge/modelarts
Project_modelarts int
//地址:https://git.openi.org.cn/OpenI/aiforge/blockchain
Project_blockchain int
//地址:https://git.openi.org.cn/OpenI/aiforge/watchers
Project_watchers int
//地址:https://git.openi.org.cn/OpenI/aiforge/stars
Project_stars int
//地址:https://git.openi.org.cn/OpenI/aiforge/forks
Project_forks int
}

// @title AllProjectView
// @description 获取指定用户和项目的访问量
// @param Gte string "起始时间" 如time.Now().AddDate(0, 0, -1).Format(time.RFC3339)
// @param Lte string "结束时间"
// @return projectInfo ProjectInfo "统计所有项目中页面的浏览情况,不需要区分项目"
func AllProjectView(Gte string, Lte string) (projectInfo ProjectInfo) {
projectInfo.Project_dataset_type_0 = ViewInfo(AllProjectViewInit("/datasets?type=0", "%{[request][2]}", Gte, Lte))
projectInfo.Project_dataset_type_1 = ViewInfo(AllProjectViewInit("/datasets?type=1", "%{[request][2]}", Gte, Lte))
projectInfo.Project_issues = ViewInfo(AllProjectViewInit("/issues HTTP/2.0", "%{[request][2]}", Gte, Lte))
projectInfo.Project_labels = ViewInfo(TagNameInit("/labels HTTP/2.0", "labels", Gte, Lte))
projectInfo.Project_milestones = ViewInfo(AllProjectViewInit("/milestones HTTP/2.0", "%{[request][2]}", Gte, Lte))
projectInfo.Project_pulls = ViewInfo(AllProjectViewInit("/pulls HTTP/2.0", "%{[request][2]}", Gte, Lte))
projectInfo.Project_release = ViewInfo(AllProjectViewInit("/release HTTP/2.0", "%{[request][2]}", Gte, Lte))
projectInfo.Project_wiki = ViewInfo(AllProjectViewInit("/wiki HTTP/2.0", "%{[request][2]}", Gte, Lte))
projectInfo.Project_activity = ViewInfo(AllProjectViewInit("/activity HTTP/2.0", "%{[request][2]}", Gte, Lte))
projectInfo.Project_cloudbrain = ViewInfo(AllProjectViewInit("/cloudbrain HTTP/2.0", "%{[request][2]}", Gte, Lte))
projectInfo.Project_modelarts = ViewInfo(AllProjectViewInit("/modelarts HTTP/2.0", "%{[request][2]}", Gte, Lte))
projectInfo.Project_blockchain = ViewInfo(AllProjectViewInit("/blockchain HTTP/2.0", "%{[request][2]}", Gte, Lte))
projectInfo.Project_watchers = ViewInfo(AllProjectViewInit("/watchers HTTP/2.0", "%{[request][2]}", Gte, Lte))
projectInfo.Project_stars = ViewInfo(AllProjectViewInit("/stars HTTP/2.0", "%{[request][2]}", Gte, Lte))
projectInfo.Project_forks = ViewInfo(AllProjectViewInit("/forks HTTP/2.0", "%{[request][2]}", Gte, Lte))
return projectInfo
}

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

@@ -482,6 +482,14 @@ var (
PoolInfos string
Flavor string
FlavorInfos string

//elk config
ElkUrl string
ElkUser string
ElkPassword string
Index string
TimeField string
ElkTimeFormat string
)

// DateLang transforms standard language locale name to corresponding value in datetime plugin.
@@ -1201,6 +1209,14 @@ func NewContext() {
PoolInfos = sec.Key("POOL_INFOS").MustString("")
Flavor = sec.Key("FLAVOR").MustString("")
FlavorInfos = sec.Key("FLAVOR_INFOS").MustString("")

sec = Cfg.Section("elk")
ElkUrl = sec.Key("ELKURL").MustString("http://192.168.207.35:5601/internal/bsearch")
ElkUser = sec.Key("ELKUSER").MustString("Qizhi")
ElkPassword = sec.Key("ELKPASSWORD").MustString("Pcl2020")
Index = sec.Key("INDEX").MustString("filebeat-7.3.2*")
TimeField = sec.Key("TIMEFIELD").MustString(" @timestamptest")
ElkTimeFormat = sec.Key("ELKTIMEFORMAT").MustString("date_time")
}

func loadInternalToken(sec *ini.Section) string {


+ 13
- 2
options/locale/locale_en-US.ini View File

@@ -87,7 +87,7 @@ write = Write
preview = Preview
loading = Loading…

error404_index = Request forbidden by administrative rules
error404_index = Request forbidden by administrative rules
error500_index = Internal Server Error
error404 = The page you are trying to reach either <strong>does not exist</strong> or <strong>you are not authorized</strong> to view it.
error500= Sorry, the site has encountered some problems, we are trying to <strong>fix the page</strong>, please try again later.
@@ -573,7 +573,7 @@ authorized_oauth2_applications_description = You've granted access to your perso
revoke_key = Revoke
revoke_oauth2_grant = Revoke Access
revoke_oauth2_grant_description = Revoking access for this third party application will prevent this application from accessing your data. Are you sure?
revoke_oauth2_grant_success = You've revoked access successfully.
revoke_oauth2_grant_success = You have revoked access successfully.

twofa_desc = Two-factor authentication enhances the security of your account.
twofa_is_enrolled = Your account is currently <strong>enrolled</strong> in two-factor authentication.
@@ -770,6 +770,10 @@ cloudbrain_selection = select cloudbrain
cloudbrain_platform_selection = Select the cloudbrain platform you want to use:
confirm_choice = confirm
cloudbran1_tips = Only data in zip format can create cloudbrain tasks
cloudbrain_creator=Creator
cloudbrain_task = Task Name
cloudbrain_operate = Operate
cloudbrain_status_createtime = Status/Createtime

template.items = Template Items
template.git_content = Git Content (Default Branch)
@@ -831,6 +835,7 @@ fork = Fork
download_archive = Download Repository

no_desc = No Description
no_label = No labels
quick_guide = Quick Guide
clone_this_repo = Clone this repository
create_new_repo_command = Creating a new repository on the command line
@@ -845,6 +850,7 @@ filter_branch_and_tag = Filter branch or tag
branches = Branches
tags = Tags
issues = Issues
issues_detail = Detail
pulls = Pull Requests
labels = Labels
org_labels_desc = Organization level labels that can be used with <strong>all repositories</strong> under this organization
@@ -1241,6 +1247,11 @@ pulls.reject_count_1 = "%d change request"
pulls.reject_count_n = "%d change requests"
pulls.waiting_count_1 = "%d waiting review"
pulls.waiting_count_n = "%d waiting reviews"
pulls.commits_count_1=This branch is %d commit behind the upstream.
pulls.commits_count_n=This branch is %d commit behind the upstream.
pulls.fetch_upstream=Fetch upstream
pulls.upstream_up_to_date=No new commits to fetch
pulls.upstream_error=Cannot get upstream info

pulls.no_merge_desc = This pull request cannot be merged because all repository merge options are disabled.
pulls.no_merge_helper = Enable merge options in the repository settings or merge the pull request manually.


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

@@ -772,6 +772,10 @@ cloudbrain_selection=云脑选择
cloudbrain_platform_selection=选择您准备使用的云脑平台:
confirm_choice=确定
cloudbran1_tips=只有zip格式的数据集才能发起云脑任务
cloudbrain_creator=创建者
cloudbrain_task=任务名称
cloudbrain_operate=操作
cloudbrain_status_createtime=状态/创建时间

template.items=模板选项
template.git_content=Git数据(默认分支)
@@ -833,6 +837,7 @@ fork=派生
download_archive=下载此项目

no_desc=暂无描述
no_label = 暂无标签
quick_guide=快速帮助
clone_this_repo=克隆当前项目
create_new_repo_command=从命令行创建一个新的项目
@@ -847,6 +852,7 @@ filter_branch_and_tag=过滤分支或标签
branches=分支列表
tags=标签列表
issues=任务
issues_detail=详情
pulls=合并请求
labels=标签
org_labels_desc=组织级别的标签,可以被本组织下的 <strong>所有项目</strong> 使用
@@ -1243,6 +1249,11 @@ pulls.reject_count_1=%d 变更请求
pulls.reject_count_n=%d 变更请求
pulls.waiting_count_1=%d 个正在等待审核
pulls.waiting_count_n=%d 个正在等待审核
pulls.commits_count_1=当前分支落后上游分支 %d 个提交
pulls.commits_count_n=当前分支落后上游分支 %d 个提交
pulls.fetch_upstream=拉取上游更新
pulls.upstream_up_to_date=上游分支没有新的更新
pulls.upstream_error=获取上游分支信息错误

pulls.no_merge_desc=由于未启用合并选项,此合并请求无法被合并。
pulls.no_merge_helper=在项目设置中启用合并选项或者手工合并请求。


+ 1653
- 0
public/img/icons.svg
File diff suppressed because it is too large
View File


BIN
public/img/loading.gif View File

Before After
Width: 16  |  Height: 16  |  Size: 2.9 kB

+ 4
- 3
routers/home.go View File

@@ -149,8 +149,7 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {

//todo:support other topics
keyword := strings.Trim(ctx.Query("q"), " ")
topicOnly := ctx.QueryBool("topic")
ctx.Data["TopicOnly"] = topicOnly
topic := strings.Trim(ctx.Query("topic"), " ")

repos, count, err = models.SearchRepository(&models.SearchRepoOptions{
ListOptions: models.ListOptions{
@@ -164,7 +163,7 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
OwnerID: opts.OwnerID,
AllPublic: true,
AllLimited: true,
TopicOnly: topicOnly,
TopicName: topic,
IncludeDescription: setting.UI.SearchRepoDescription,
})
if err != nil {
@@ -177,6 +176,8 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
repo.Active = int64(repo.NumIssues) + int64(repo.NumPulls) + int64(repo.NumCommit)
}
ctx.Data["Keyword"] = keyword
ctx.Data["Topic"] = topic

ctx.Data["Total"] = count
ctx.Data["Repos"] = repos
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled


+ 26
- 3
routers/repo/cloudbrain.go View File

@@ -4,6 +4,7 @@ import (
"bufio"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"os"
@@ -69,11 +70,13 @@ func CloudBrainIndex(ctx *context.Context) {

timestamp := time.Now().Unix()
for i, task := range ciTasks {
if task.Status == string(models.JobRunning) && (timestamp-int64(task.CreatedUnix) > 10) {
if task.Status == string(models.JobRunning) && (timestamp-int64(task.Cloudbrain.CreatedUnix) > 10) {
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)
@@ -390,19 +393,39 @@ func StopJobs(cloudBrains []*models.Cloudbrain) {
for _, taskInfo := range cloudBrains {

if taskInfo.Type == models.TypeCloudBrainOne {
err := cloudbrain.StopJob(taskInfo.JobID)
err := retry(3, time.Second*30, func() error {
return cloudbrain.StopJob(taskInfo.JobID)
})

logErrorAndUpdateJobStatus(err, taskInfo)
} else {
param := models.NotebookAction{
Action: models.ActionStop,
}
_, err := modelarts.StopJob(taskInfo.JobID, param)
err := retry(3, time.Second*30, func() error {
_, err := modelarts.StopJob(taskInfo.JobID, param)
return err
})
logErrorAndUpdateJobStatus(err, taskInfo)
}

}
}

func retry(attempts int, sleep time.Duration, f func() error) (err error) {
for i := 0; i < attempts; i++ {
if i > 0 {
log.Warn("retrying after error:", err)
time.Sleep(sleep)
}
err = f()
if err == nil {
return nil
}
}
return fmt.Errorf("after %d attempts, last error: %s", attempts, err)
}

func logErrorAndUpdateJobStatus(err error, taskInfo *models.Cloudbrain) {
if err != nil {
log.Warn("Failed to stop cloudBrain job:"+taskInfo.JobID, err)


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

@@ -57,6 +57,8 @@ func ModelArtsIndex(ctx *context.Context) {
} 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)


+ 40
- 0
routers/repo/view.go View File

@@ -790,6 +790,46 @@ func renderCode(ctx *context.Context) {
}
}

//如果是fork的仓库
if ctx.Repo.Repository.IsFork {
//获得fetchUpstream对应的分支参数
/*
// 1. /{:baseOwner}/{:baseRepoName}/compare/{:baseBranch}...{:headBranch}
// 2. /{:baseOwner}/{:baseRepoName}/compare/{:baseBranch}...{:headOwner}:{:headBranch}
// 3. /{:baseOwner}/{:baseRepoName}/compare/{:baseBranch}...{:headOwner}/{:headRepoName}:{:headBranch}
*/
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())
ctx.Data["FetchUpstreamCnt"] = -1 // minus value indicates error
}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)
ctx.Data["UpstreamSameBranchName"] = true
}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)
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())
ctx.Data["FetchUpstreamCnt"] = compareInfo.Commits.Len()
}else{
log.Info("compareInfo nothing different")
ctx.Data["FetchUpstreamCnt"] = 0
}
}else{
ctx.Data["FetchUpstreamCnt"] = -1 // minus value indicates error
}
}
}
ctx.Data["Paths"] = paths
ctx.Data["TreeLink"] = treeLink
ctx.Data["TreeNames"] = treeNames


+ 9
- 0
routers/secure/user.go View File

@@ -7,6 +7,7 @@ package secure

import (
"net/http"
"net/mail"
"strings"

"code.gitea.io/gitea/models"
@@ -63,6 +64,14 @@ func CreateUser(ctx *context.Context, form api.CreateUserOption) {
// "422":
// "$ref": "#/responses/validationError"

_, err1 := mail.ParseAddress(form.Email)
if err1 != nil {
ctx.JSON(http.StatusBadRequest, map[string]string{
"error_msg": "Email format is wrong.",
})
return
}

u := &models.User{
Name: form.Username,
FullName: form.FullName,


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

@@ -154,7 +154,7 @@
{{svg "octicon-person" 16}} {{.i18n.Tr "register"}}
</a>
{{end}}
<a class="item{{if .PageIsSignIn}} active{{end}}" rel="nofollow" href="{{AppSubUrl}}/user/login?redirect_to={{.Link}}">
<a class="item{{if .PageIsSignIn}} active{{end}}" rel="nofollow" href="{{AppSubUrl}}/user/login">
{{svg "octicon-sign-in" 16}} {{.i18n.Tr "sign_in"}}
</a>
</div><!-- end anonymous right menu -->


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

@@ -154,7 +154,7 @@
{{svg "octicon-person" 16}} {{.i18n.Tr "register"}}
</a>
{{end}}
<a class="item{{if .PageIsSignIn}} active{{end}}" rel="nofollow" href="{{AppSubUrl}}/user/login?redirect_to={{.Link}}">
<a class="item{{if .PageIsSignIn}} active{{end}}" rel="nofollow" href="{{AppSubUrl}}/user/login">
{{svg "octicon-sign-in" 16}} {{.i18n.Tr "sign_in"}}
</a>
</div><!-- end anonymous right menu -->


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

@@ -1,12 +1,12 @@
<div class="repos--seach">
<div class="ui container">
<div class="ui two column centered grid">
<form class="mobile ten wide tablet computer column ui form ignore-dirty">
<form class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty">
<div class="ui fluid action input">
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search"}}..." autofocus>
<input type="hidden" name="tab" value="{{$.TabName}}">
<input type="hidden" name="sort" value="{{$.SortType}}">
<button class="ui blue button">{{.i18n.Tr "explore.search"}}</button>
<button class="ui green button">{{.i18n.Tr "explore.search"}}</button>
</div>
</form>
</div>


+ 11
- 11
templates/explore/repo_left.tmpl View File

@@ -6,67 +6,67 @@
</svg>
全部领域
</a>
<a class="{{if eq $.Keyword "大模型"}}active {{end}}item" href="/explore/repos?q=大模型&topic=1&sort={{.SortType}}">
<a class="{{if eq $.Topic "大模型"}}active {{end}}item" href="/explore/repos?q=&topic=大模型&sort={{.SortType}}">
<svg class="svg octicon-inbox" width="16" height="16" viewBox="0 0 24 24">
<path fill="currentColor" d="M19 3H5C3.89 3 3 3.89 3 5V19C3 20.11 3.9 21 5 21H19C20.11 21 21 20.11 21 19V5C21 3.89 20.1 3 19 3M16.1 15.9C15.07 15.9 14.09 15.5 13.35 14.76L12.71 14.12L14.13 12.71L14.76 13.34C15.12 13.7 15.6 13.9 16.11 13.9C17.15 13.9 18 13.05 18 12S17.15 10.1 16.1 10.1C15.6 10.1 15.12 10.3 14.76 10.66L10.65 14.76C9.91 15.5 8.94 15.9 7.9 15.9C5.75 15.9 4 14.15 4 12S5.75 8.1 7.9 8.1C8.94 8.1 9.91 8.5 10.65 9.24L11.29 9.88L9.87 11.3L9.24 10.66C8.88 10.3 8.4 10.1 7.9 10.1C6.85 10.1 6 10.95 6 12S6.85 13.9 7.9 13.9C8.4 13.9 8.88 13.7 9.24 13.34L13.35 9.24C14.09 8.5 15.06 8.1 16.1 8.1C18.25 8.1 20 9.85 20 12S18.25 15.9 16.1 15.9Z" />
</svg>
大模型
</a>
<a class="{{if eq $.Keyword "AI开发工具"}}active {{end}}item" href="/explore/repos?q=AI开发工具&topic=1&sort={{.SortType}}">
<a class="{{if eq $.Topic "AI开发工具"}}active {{end}}item" href="/explore/repos?q=&topic=AI开发工具&sort={{.SortType}}">
<svg class="svg octicon-inbox" width="16" height="16" viewBox="0 0 24 24">
<path fill="currentColor" d="M19 3H5C3.9 3 3 3.9 3 5V19C3 20.1 3.9 21 5 21H19C20.1 21 21 20.1 21 19V5C21 3.9 20.1 3 19 3M11 8H9V10C9 11.1 8.1 12 7 12C8.1 12 9 12.9 9 14V16H11V18H9C7.9 18 7 17.1 7 16V15C7 13.9 6.1 13 5 13V11C6.1 11 7 10.1 7 9V8C7 6.9 7.9 6 9 6H11V8M19 13C17.9 13 17 13.9 17 15V16C17 17.1 16.1 18 15 18H13V16H15V14C15 12.9 15.9 12 17 12C15.9 12 15 11.1 15 10V8H13V6H15C16.1 6 17 6.9 17 8V9C17 10.1 17.9 11 19 11V13Z" />
</svg>
AI开发工具
</a>
<a class="{{if eq $.Keyword "计算机视觉"}}active {{end}}item" href="/explore/repos?q=计算机视觉&topic=1&sort={{.SortType}}">
<a class="{{if eq $.Topic "计算机视觉"}}active {{end}}item" href="/explore/repos?q=&topic=计算机视觉&sort={{.SortType}}">
<svg class="svg octicon-inbox" width="16" height="16" viewBox="0 0 24 24">
<path fill="currentColor" d="M12,9A3,3 0 0,0 9,12A3,3 0 0,0 12,15A3,3 0 0,0 15,12A3,3 0 0,0 12,9M12,17A5,5 0 0,1 7,12A5,5 0 0,1 12,7A5,5 0 0,1 17,12A5,5 0 0,1 12,17M12,4.5C7,4.5 2.73,7.61 1,12C2.73,16.39 7,19.5 12,19.5C17,19.5 21.27,16.39 23,12C21.27,7.61 17,4.5 12,4.5Z" />
</svg>
计算机视觉
</a>
<a class="{{if eq $.Keyword "自然语言处理"}}active {{end}}item" href="/explore/repos?q=自然语言处理&topic=1&sort={{.SortType}}">
<a class="{{if eq $.Topic "自然语言处理"}}active {{end}}item" href="/explore/repos?q=&topic=自然语言处理&sort={{.SortType}}">
<svg class="svg octicon-inbox" width="16" height="16" viewBox="0 0 24 24">
<path fill="currentColor" d="M9,5A4,4 0 0,1 13,9A4,4 0 0,1 9,13A4,4 0 0,1 5,9A4,4 0 0,1 9,5M9,15C11.67,15 17,16.34 17,19V21H1V19C1,16.34 6.33,15 9,15M16.76,5.36C18.78,7.56 18.78,10.61 16.76,12.63L15.08,10.94C15.92,9.76 15.92,8.23 15.08,7.05L16.76,5.36M20.07,2C24,6.05 23.97,12.11 20.07,16L18.44,14.37C21.21,11.19 21.21,6.65 18.44,3.63L20.07,2Z" />
</svg>
自然语言处理
</a>
<a class="{{if eq $.Keyword "机器学习"}}active {{end}}item" href="/explore/repos?q=机器学习&topic=1&sort={{.SortType}}">
<a class="{{if eq $.Topic "机器学习"}}active {{end}}item" href="/explore/repos?q=&topic=机器学习&sort={{.SortType}}">
<svg class="svg octicon-inbox" width="16" height="16" viewBox="0 0 24 24">
<path fill="currentColor" d="M19,12V13.5A4,4 0 0,1 23,17.5C23,18.32 22.75,19.08 22.33,19.71L21.24,18.62C21.41,18.28 21.5,17.9 21.5,17.5A2.5,2.5 0 0,0 19,15V16.5L16.75,14.25L19,12M19,23V21.5A4,4 0 0,1 15,17.5C15,16.68 15.25,15.92 15.67,15.29L16.76,16.38C16.59,16.72 16.5,17.1 16.5,17.5A2.5,2.5 0 0,0 19,20V18.5L21.25,20.75L19,23M12,3C16.42,3 20,4.79 20,7C20,9.21 16.42,11 12,11C7.58,11 4,9.21 4,7C4,4.79 7.58,3 12,3M4,9C4,11.21 7.58,13 12,13C13.11,13 14.17,12.89 15.14,12.68C14.19,13.54 13.5,14.67 13.18,15.96L12,16C7.58,16 4,14.21 4,12V9M20,9V11H19.5L18.9,11.03C19.6,10.43 20,9.74 20,9M4,14C4,16.21 7.58,18 12,18L13,17.97C13.09,19.03 13.42,20 13.95,20.88L12,21C7.58,21 4,19.21 4,17V14Z" />
</svg>
机器学习
</a>
<a class="{{if eq $.Keyword "神经网络"}}active {{end}}item" href="/explore/repos?q=神经网络&topic=1&sort={{.SortType}}">
<a class="{{if eq $.Topic "神经网络"}}active {{end}}item" href="/explore/repos?q=&topic=神经网络&sort={{.SortType}}">
<svg class="svg octicon-inbox" width="16" height="16" viewBox="0 0 24 24">
<path fill="currentColor" d="M13 3C9.23 3 6.19 5.95 6 9.66L4.08 12.19C3.84 12.5 4.08 13 4.5 13H6V16C6 17.11 6.89 18 8 18H9V21H16V16.31C18.37 15.19 20 12.8 20 10C20 6.14 16.88 3 13 3M17.06 9.57L15.1 10.09L16.54 11.54C16.89 11.88 16.89 12.46 16.54 12.81C16.19 13.16 15.61 13.16 15.27 12.81L13.81 11.37L13.3 13.33C13.18 13.82 12.68 14.1 12.21 13.97C11.72 13.84 11.44 13.35 11.57 12.87L12.1 10.9L10.13 11.43C9.65 11.56 9.15 11.28 9.03 10.79C8.9 10.32 9.18 9.82 9.67 9.7L11.63 9.19L10.19 7.73C9.84 7.39 9.84 6.82 10.19 6.46C10.54 6.11 11.12 6.11 11.46 6.46L12.91 7.9L13.43 5.94C13.55 5.46 14.04 5.18 14.5 5.3C15 5.43 15.28 5.92 15.16 6.41L14.63 8.37L16.59 7.84C17.08 7.72 17.57 8 17.7 8.5C17.82 8.96 17.54 9.45 17.06 9.57Z" />
</svg>
神经网络
</a>
<a class="{{if eq $.Keyword "自动驾驶"}}active {{end}}item" href="/explore/repos?q=自动驾驶&topic=1&sort={{.SortType}}">
<a class="{{if eq $.Topic "自动驾驶"}}active {{end}}item" href="/explore/repos?q=&topic=自动驾驶&sort={{.SortType}}">
<svg class="svg octicon-inbox" width="16" height="16" viewBox="0 0 24 24">
<path fill="currentColor" d="M5,14H19L17.5,9.5H6.5L5,14M17.5,19A1.5,1.5 0 0,0 19,17.5A1.5,1.5 0 0,0 17.5,16A1.5,1.5 0 0,0 16,17.5A1.5,1.5 0 0,0 17.5,19M6.5,19A1.5,1.5 0 0,0 8,17.5A1.5,1.5 0 0,0 6.5,16A1.5,1.5 0 0,0 5,17.5A1.5,1.5 0 0,0 6.5,19M18.92,9L21,15V23A1,1 0 0,1 20,24H19A1,1 0 0,1 18,23V22H6V23A1,1 0 0,1 5,24H4A1,1 0 0,1 3,23V15L5.08,9C5.28,8.42 5.85,8 6.5,8H17.5C18.15,8 18.72,8.42 18.92,9M12,0C14.12,0 16.15,0.86 17.65,2.35L16.23,3.77C15.11,2.65 13.58,2 12,2C10.42,2 8.89,2.65 7.77,3.77L6.36,2.35C7.85,0.86 9.88,0 12,0M12,4C13.06,4 14.07,4.44 14.82,5.18L13.4,6.6C13.03,6.23 12.53,6 12,6C11.5,6 10.97,6.23 10.6,6.6L9.18,5.18C9.93,4.44 10.94,4 12,4Z" />
</svg>
自动驾驶
</a>
<a class="{{if eq $.Keyword "机器人"}}active {{end}}item" href="/explore/repos?q=机器人&topic=1&sort={{.SortType}}">
<a class="{{if eq $.Topic "机器人"}}active {{end}}item" href="/explore/repos?q=&topic=机器人&sort={{.SortType}}">
<svg class="svg octicon-inbox" width="16" height="16" viewBox="0 0 24 24">
<path fill="currentColor" d="M12,2A2,2 0 0,1 14,4C14,4.74 13.6,5.39 13,5.73V7H14A7,7 0 0,1 21,14H22A1,1 0 0,1 23,15V18A1,1 0 0,1 22,19H21V20A2,2 0 0,1 19,22H5A2,2 0 0,1 3,20V19H2A1,1 0 0,1 1,18V15A1,1 0 0,1 2,14H3A7,7 0 0,1 10,7H11V5.73C10.4,5.39 10,4.74 10,4A2,2 0 0,1 12,2M7.5,13A2.5,2.5 0 0,0 5,15.5A2.5,2.5 0 0,0 7.5,18A2.5,2.5 0 0,0 10,15.5A2.5,2.5 0 0,0 7.5,13M16.5,13A2.5,2.5 0 0,0 14,15.5A2.5,2.5 0 0,0 16.5,18A2.5,2.5 0 0,0 19,15.5A2.5,2.5 0 0,0 16.5,13Z" />
</svg>
机器人
</a>
<a class="{{if eq $.Keyword "联邦学习"}}active {{end}}item" href="/explore/repos?q=联邦学习&topic=1&sort={{.SortType}}">
<a class="{{if eq $.Topic "联邦学习"}}active {{end}}item" href="/explore/repos?q=&topic=联邦学习&sort={{.SortType}}">
<svg class="svg octicon-inbox" width="16" height="16" viewBox="0 0 24 24">
<path fill="currentColor" d="M3 11H11V3H3M5 5H9V9H5M13 21H21V13H13M15 15H19V19H15M3 21H11V13H3M5 15H9V19H5M13 3V11H21V3M19 9H15V5H19Z" />
</svg>
联邦学习
</a>
<a class="{{if eq $.Keyword "数据挖掘"}}active {{end}}item" href="/explore/repos?q=数据挖掘&topic=1&sort={{.SortType}}">
<a class="{{if eq $.Topic "数据挖掘"}}active {{end}}item" href="/explore/repos?q=&topic=数据挖掘&sort={{.SortType}}">
<svg class="svg octicon-inbox" width="16" height="16" viewBox="0 0 24 24">
<path fill="currentColor" d="M18.36,2.64C20,2.64 21.36,4 21.36,5.64C21.36,7.29 20,8.64 18.36,8.64C16.71,8.64 15.36,7.29 15.36,5.64C15.36,5.34 15.41,5.06 15.5,4.8C14.43,4.29 13.25,4 12,4A8,8 0 0,0 4,12L4.04,12.84L2.05,13.05L2,12A10,10 0 0,1 12,2C13.69,2 15.28,2.42 16.67,3.16C17.16,2.83 17.74,2.64 18.36,2.64M18.36,4.64A1,1 0 0,0 17.36,5.64A1,1 0 0,0 18.36,6.64C18.92,6.64 19.36,6.19 19.36,5.64C19.36,5.08 18.92,4.64 18.36,4.64M5.64,15.36C7.29,15.36 8.64,16.71 8.64,18.36C8.64,18.66 8.59,18.94 8.5,19.2C9.57,19.71 10.75,20 12,20A8,8 0 0,0 20,12L19.96,11.16L21.95,10.95L22,12A10,10 0 0,1 12,22C10.31,22 8.72,21.58 7.33,20.84C6.84,21.17 6.26,21.36 5.64,21.36C4,21.36 2.64,20 2.64,18.36C2.64,16.71 4,15.36 5.64,15.36M5.64,17.36C5.08,17.36 4.64,17.81 4.64,18.36C4.64,18.92 5.08,19.36 5.64,19.36A1,1 0 0,0 6.64,18.36A1,1 0 0,0 5.64,17.36M12,8A4,4 0 0,1 16,12A4,4 0 0,1 12,16A4,4 0 0,1 8,12A4,4 0 0,1 12,8Z" />
</svg>
数据挖掘
</a>
<a class="{{if eq $.Keyword "RISC-V"}}active {{end}}item" href="/explore/repos?q=RISC-V&topic=1&sort={{.SortType}}">
<a class="{{if eq $.Topic "RISC-V"}}active {{end}}item" href="/explore/repos?q=&topic=RISC-V&sort={{.SortType}}">
<svg class="svg octicon-inbox" width="16" height="16" viewBox="0 0 24 24">
<path fill="currentColor" d="M17,17H7V7H17M21,11V9H19V7C19,5.89 18.1,5 17,5H15V3H13V5H11V3H9V5H7C5.89,5 5,5.89 5,7V9H3V11H5V13H3V15H5V17A2,2 0 0,0 7,19H9V21H11V19H13V21H15V19H17A2,2 0 0,0 19,17V15H21V13H19V11M13,13H11V11H13M15,9H9V15H15V9Z" />
</svg>


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

@@ -40,20 +40,20 @@

<div class="ui secondary pointing tabular top attached borderless menu navbar">
{{if .PageIsExplore}}
<a class="{{if eq .SortType "hot"}}active{{end}} item" href="{{$.Link}}?sort=hot&q={{$.Keyword}}&tab={{$.TabName}}">
<a class="{{if eq .SortType "hot"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&topic={{$.Topic}}&sort=hot">
<svg class="svg octicon-repo" width="16" height="16" aria-hidden="true">
<use xlink:href="#octicon-repo" />
</svg>
热门{{.i18n.Tr "explore.repos"}}
</a>
<a class="{{if eq .SortType "active"}}active{{end}} item" href="{{$.Link}}?sort=active&q={{$.Keyword}}&tab={{$.TabName}}">
<a class="{{if eq .SortType "active"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&topic={{$.Topic}}&sort=active">
<svg class="svg octicon-inbox" width="16" height="16" aria-hidden="true">
<use xlink:href="#octicon-inbox" />
</svg>
活跃{{.i18n.Tr "explore.repos"}}
</a>
{{end}}
<a class="{{if eq .SortType "recentupdate"}}active{{end}} item" href="{{$.Link}}?sort=recentupdate&q={{$.Keyword}}&tab={{$.TabName}}">
<a class="{{if eq .SortType "recentupdate"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&topic={{$.Topic}}&sort=recentupdate">
<svg class="svg octicon-organization" width="16" height="16" aria-hidden="true">
<use xlink:href="#octicon-organization" />
</svg> {{.i18n.Tr "repo.issues.filter_sort.recentupdate"}}
@@ -67,16 +67,16 @@
<i class="dropdown icon"></i>
</span>
<div class="menu">
<a class="{{if eq .SortType "newest"}}active{{end}} item" href="{{$.Link}}?sort=newest&q={{$.Keyword}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.filter_sort.latest"}}</a>
<a class="{{if eq .SortType "oldest"}}active{{end}} item" href="{{$.Link}}?sort=oldest&q={{$.Keyword}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.filter_sort.oldest"}}</a>
<a class="{{if eq .SortType "alphabetically"}}active{{end}} item" href="{{$.Link}}?sort=alphabetically&q={{$.Keyword}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.label.filter_sort.alphabetically"}}</a>
<a class="{{if eq .SortType "reversealphabetically"}}active{{end}} item" href="{{$.Link}}?sort=reversealphabetically&q={{$.Keyword}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.label.filter_sort.reverse_alphabetically"}}</a>
<a class="{{if eq .SortType "recentupdate"}}active{{end}} item" href="{{$.Link}}?sort=recentupdate&q={{$.Keyword}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.filter_sort.recentupdate"}}</a>
<a class="{{if eq .SortType "leastupdate"}}active{{end}} item" href="{{$.Link}}?sort=leastupdate&q={{$.Keyword}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.filter_sort.leastupdate"}}</a>
<a class="{{if eq .SortType "moststars"}}active{{end}} item" href="{{$.Link}}?sort=moststars&q={{$.Keyword}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.filter_sort.moststars"}}</a>
<a class="{{if eq .SortType "feweststars"}}active{{end}} item" href="{{$.Link}}?sort=feweststars&q={{$.Keyword}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.filter_sort.feweststars"}}</a>
<a class="{{if eq .SortType "mostforks"}}active{{end}} item" href="{{$.Link}}?sort=mostforks&q={{$.Keyword}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.filter_sort.mostforks"}}</a>
<a class="{{if eq .SortType "fewestforks"}}active{{end}} item" href="{{$.Link}}?sort=fewestforks&q={{$.Keyword}}&tab={{$.TabName}}">{{.i18n.Tr "repo.issues.filter_sort.fewestforks"}}</a>
<a class="{{if eq .SortType "newest"}}active{{end}} item" href="{{$.Link}}?sort=newest&q={{$.Keyword}}&topic={{$.Topic}}">{{.i18n.Tr "repo.issues.filter_sort.latest"}}</a>
<a class="{{if eq .SortType "oldest"}}active{{end}} item" href="{{$.Link}}?sort=oldest&q={{$.Keyword}}&topic={{$.Topic}}">{{.i18n.Tr "repo.issues.filter_sort.oldest"}}</a>
<a class="{{if eq .SortType "alphabetically"}}active{{end}} item" href="{{$.Link}}?sort=alphabetically&q={{$.Keyword}}&topic={{$.Topic}}">{{.i18n.Tr "repo.issues.label.filter_sort.alphabetically"}}</a>
<a class="{{if eq .SortType "reversealphabetically"}}active{{end}} item" href="{{$.Link}}?sort=reversealphabetically&q={{$.Keyword}}&topic={{$.Topic}}">{{.i18n.Tr "repo.issues.label.filter_sort.reverse_alphabetically"}}</a>
<a class="{{if eq .SortType "recentupdate"}}active{{end}} item" href="{{$.Link}}?sort=recentupdate&q={{$.Keyword}}&topic={{$.Topic}}">{{.i18n.Tr "repo.issues.filter_sort.recentupdate"}}</a>
<a class="{{if eq .SortType "leastupdate"}}active{{end}} item" href="{{$.Link}}?sort=leastupdate&q={{$.Keyword}}&topic={{$.Topic}}">{{.i18n.Tr "repo.issues.filter_sort.leastupdate"}}</a>
<a class="{{if eq .SortType "moststars"}}active{{end}} item" href="{{$.Link}}?sort=moststars&q={{$.Keyword}}&topic={{$.Topic}}">{{.i18n.Tr "repo.issues.filter_sort.moststars"}}</a>
<a class="{{if eq .SortType "feweststars"}}active{{end}} item" href="{{$.Link}}?sort=feweststars&q={{$.Keyword}}&topic={{$.Topic}}">{{.i18n.Tr "repo.issues.filter_sort.feweststars"}}</a>
<a class="{{if eq .SortType "mostforks"}}active{{end}} item" href="{{$.Link}}?sort=mostforks&q={{$.Keyword}}&topic={{$.Topic}}">{{.i18n.Tr "repo.issues.filter_sort.mostforks"}}</a>
<a class="{{if eq .SortType "fewestforks"}}active{{end}} item" href="{{$.Link}}?sort=fewestforks&q={{$.Keyword}}&topic={{$.Topic}}">{{.i18n.Tr "repo.issues.filter_sort.fewestforks"}}</a>
</div>
</div>
</div>
@@ -143,7 +143,7 @@
{{if .Topics }}
<div class="ui tags">
{{range .Topics}}
{{if ne . "" }}<a href="{{AppSubUrl}}/explore/repos?q={{.}}&topic=1"><div class="ui small label topic">{{.}}</div></a>{{end}}
{{if ne . "" }}<a href="{{AppSubUrl}}/explore/repos?q=&topic={{.}}"><div class="ui small label topic">{{.}}</div></a>{{end}}
{{end}}
</div>
{{end}}


+ 1
- 1
templates/explore/repo_search.tmpl View File

@@ -9,7 +9,7 @@
<form class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty" style="margin-top:1.2rem">
<div class="ui fluid action input">
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search"}}..." autofocus>
<input type="hidden" name="tab" value="{{$.TabName}}">
<input type="hidden" name="topic" value="{{$.Topic}}">
<input type="hidden" name="sort" value="{{$.SortType}}">
<button class="ui green button">{{.i18n.Tr "explore.search"}}</button>
</div>


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

@@ -1,11 +1,11 @@
<div class="repos--seach">
<div class="ui container">
<div class="ui two column centered grid">
<form class="sixteen wide mobile eight fourteen tablet fourteen wide computer column ui form ignore-dirty">
<form class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty">
<div class="ui fluid action input">
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search"}}..." autofocus>
<input type="hidden" name="tab" value="{{$.TabName}}">
<button class="ui blue button">{{.i18n.Tr "explore.search"}}</button>
<button class="ui green button">{{.i18n.Tr "explore.search"}}</button>
</div>
</form>
</div>


+ 66
- 15
templates/repo/cloudbrain/index.tmpl View File

@@ -187,6 +187,12 @@
cursor: pointer;
pointer-events: none;
}
.time-show{
font-size: 10px;
margin-top: 0.4rem;
display: inline-block;
}
</style>

<!-- 弹窗 -->
@@ -235,45 +241,87 @@
<div class="ui sixteen wide column">

<!-- 排序区 -->
<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="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>
</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>{{$.i18n.Tr "repo.cloudbrain_operate"}}</span>
</div>

</div>
</div>
{{range .Tasks}}
<div class="ui grid stackable item">
<div class="row">

<!-- 任务名 -->
<div class="six wide column">
<a class="title" href="{{$.Link}}/{{.JobID}}">
<span class="fitted">{{svg "octicon-tasklist" 16}}</span>
<span class="fitted">{{.JobName}}</span>
<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 text_over" 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 class="job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}">
<!-- {{.Status}} -->
<!-- {{if eq .Status "RUNNING"}}
<span style="display:flex;position: relative; justify-content: flex-start;"><i class="i-round i-bg-stop"></i><span style="margin-left: 0.4em;font-size: 12px;">运行中</span></span>
<span style="display:flex;position: relative; justify-content: flex-start;"><i class="i-round i-bg-stop"></i><span style="margin-left: 0.4em;font-size: 12px;">运行中</span></span>
{{else}}
{{.Status}}
{{end}} -->
<span><i style="vertical-align: middle;" class="{{.Status}}"></i><span style="margin-left: 0.4em;font-size: 12px;">{{.Status}}</span></span>
</span>
<!-- <span class="job-status" id="{{.JobID}}" data-repopath="{{$.RepoRelPath}}" data-jobid="{{.JobID}}" >
{{if eq .Status "STOPPED"}}
<span style="display:flex;position: relative; justify-content: flex-start;"><i class="i-round i-bg-stop"></i><span style="margin-left: 0.4em;font-size: 12px;">已停止</span></span>
{{else if eq .Status "RUNNING"}}
<span style="display:flex;position: relative; justify-content: flex-start;"><i class="i-round i-bg-running"></i><span style="margin-left: 0.4em;font-size: 12px;">运行中</span></span>
{{else if eq .Status "FAILED"}}
<span style="display:flex;position: relative; justify-content: flex-start;"><i class="i-round i-bg-running"></i><span style="margin-left: 0.4em;font-size: 12px;">运行失败</span></span>
{{else if eq .Status "WAITING"}}
<span style="display:flex;position: relative; justify-content: flex-start;"><i class="showCircle"></i><span style="margin-left: 0.4em;font-size: 12px;">初始化等待</span></span>
{{end}}
</span> -->
<!-- 任务创建时间 -->
<span class="">{{TimeSinceUnix .CreatedUnix $.Lang}}</span>
<span style="font-size: 12px;margin-left: 0.4rem;" class="">{{TimeSinceUnix .Cloudbrain.CreatedUnix $.Lang}}</span>
</div>

<div class="one wide column">
<a href="{{AppSubUrl}}/{{.User.Name}}" title="{{.User.Name}}"><img class="ui avatar image" src="{{.User.RelAvatarLink}}"></a>
</div>
<div class="seven wide column text right">
<div class="ui compact buttons" style="margin-right:10px;">
<div class="ui compact buttons">
{{if and (ne .Status "WAITING") (ne .JobType "DEBUG")}}
<a class="ui basic button" href="{{$.Link}}/{{.JobID}}/rate" target="_blank">
评分
@@ -304,10 +352,10 @@

</div>
<!-- 删除镜像 -->
<form class="ui compact buttons" id="delForm-{{.JobID}}" action="{{if ne .Status "STOPPED"}}javascript:void(0){{else}}{{$.Link}}/{{.JobID}}/del{{end}}" method="post">
<!-- 删除任务 -->
<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 ne .Status "STOPPED"}}disabled {{else}}red {{end}}button" onclick="assertDelete(this)" style="border-radius: .28571429rem;">
<a class="ui compact {{if not .CanDel}}disabled {{else}}red {{end}}button" onclick="assertDelete(this)" style="border-radius: .28571429rem;">
删除
</a>
</form>
@@ -436,15 +484,18 @@
$(document).ready(loadJobStatus);
function loadJobStatus() {
$(".job-status").each((index, job) => {
console.log("---------",index,job)
const jobID = job.dataset.jobid;
const repoPath = job.dataset.repopath;
if (job.textContent.trim() == 'STOPPED') {
return
}

$.get(`/api/v1/repos/${repoPath}/cloudbrain/${jobID}`, (data) => {
const jobID = data.JobID
const status = data.JobStatus
console.log("status",status)
if (status != job.textContent.trim()) {
//$('#' + jobID).text(status)
//if (status == 'STOPPED') {


+ 35
- 46
templates/repo/datasets/dataset_list.tmpl View File

@@ -1,63 +1,52 @@
<style>

</style>
{{if .Attachments}}
{{range .Attachments}}
<div class="ui grid item" id="{{.UUID}}">
<div class="row">
<div class="{{if $.Permission.CanWrite $.UnitTypeDatasets}}five{{else}}nine{{end}} wide column">
<div class="eight wide column">
<span class="ui right">{{.Size | FileSize}}</span>
<a class="title" href="{{.DownloadURL}}?type={{$.Type}}">
<span class="fitted">{{svg "octicon-cloud-download" 16}}</span> {{.Name}}
{{svg "octicon-cloud-download" 16}} {{.Name}}
</a>
</div>
<div class="two wide column">
{{.Size | FileSize}}
</div>
<div class="two wide column">
<span class="ui text center" data-tooltip='{{$.i18n.Tr "dataset.download_count"}}' data-position="bottom right">{{svg "octicon-flame" 16}} {{(.DownloadCount | PrettyNumber)}}</span>
</div>

<div class="one wide column" style="{{if ne $.Type 0}}visibility: hidden;{{end}}">
<span class="ui text center clipboard" data-clipboard-text="{{.DownloadURL}}" data-tooltip='{{$.i18n.Tr "dataset.copy_url"}}' data-clipboard-action="copy">{{svg "octicon-file" 16}}</span>
</div>

<div class="one wide column">
<span class="ui text center clipboard" data-clipboard-text="{{.FileChunk.Md5}}" data-tooltip='{{$.i18n.Tr "dataset.copy_md5"}}' data-clipboard-action="copy">{{svg "octicon-file-binary" 16}}</span>
</div>

<div class="wide column one" style="{{if ne .DecompressState 1}}visibility: hidden;{{end}}">
<a class="ui text center" href="datasets/dirs/{{.UUID}}?type={{$.Type}}" data-tooltip='{{$.i18n.Tr "dataset.directory"}}'>{{svg "octicon-file-directory" 16}}</a>
</div>
{{if $.IsSigned}}
<div class="wide column one" style="{{if ne .DecompressState 1}}visibility: hidden;{{end}}">
<a class="ui text center" href="datasets/label/{{.UUID}}?type={{$.Type}}" data-tooltip='{{$.i18n.Tr "dataset.create_label_task"}}'><i class="fa fa-pencil-square-o" aria-hidden="true"></i></a>
<div class="eight wide column right aligned">
<div class="ui left mini icon buttons">
<span class="ui basic button" data-tooltip='{{$.i18n.Tr "dataset.download_count"}}' data-position="bottom right">{{svg "octicon-flame" 16}} {{(.DownloadCount | PrettyNumber)}}</span>
<span class="ui basic basic button clipboard" data-clipboard-text="{{.DownloadURL}}" data-tooltip='{{$.i18n.Tr "dataset.copy_url"}}' data-clipboard-action="copy"{{if ne $.Type 0}} style="display:none;"{{end}}>{{svg "octicon-file" 16}}</span>
<span class="ui basic basic button clipboard" data-clipboard-text="{{.FileChunk.Md5}}" data-tooltip='{{$.i18n.Tr "dataset.copy_md5"}}' data-clipboard-action="copy">{{svg "octicon-file-binary" 16}}</span>
</div>
{{end}}
{{if not .CanDel}}
<div class="two wide column">
<a class="ui button mini" disabled='true'>{{if .IsPrivate}} {{$.i18n.Tr "dataset.private"}} {{else}} {{$.i18n.Tr "dataset.public"}} {{end}}</a>
{{if ne .DecompressState 0}}
<div class="ui left mini icon buttons">
<a class="ui basic blue button" href="datasets/dirs/{{.UUID}}?type={{$.Type}}" data-tooltip='{{$.i18n.Tr "dataset.directory"}}'>{{svg "octicon-file-directory" 16}}</a>
{{if $.IsSigned}}
<a class="ui basic blue button" href="datasets/label/{{.UUID}}?type={{$.Type}}" data-tooltip='{{$.i18n.Tr "dataset.create_label_task"}}'>{{svg "octicon-pencil" 16}}</a>
{{end}}
</div>
{{else}}
{{if $.Permission.CanWrite $.UnitTypeDatasets}}
{{end}}
{{if not .CanDel}}
<a class="ui right small disabled button">{{$.i18n.Tr "dataset.delete"}}</a>
<span style="margin-right: 10px;line-height: 34px;" class="ui text{{if .IsPrivate}} red{{else}} green{{end}}">{{if .IsPrivate}} {{$.i18n.Tr "dataset.private"}} {{else}} {{$.i18n.Tr "dataset.public"}} {{end}}</span>
{{else}}
{{if $.Permission.CanWrite $.UnitTypeDatasets}}
<a class="ui right small red button" href="javascript:void(0)" data-uuid={{.UUID}} data-dataset-delete data-remove-url="{{AppSubUrl}}/attachments/delete" data-csrf="{{$.CsrfToken}}">{{$.i18n.Tr "dataset.delete"}}</a>
{{if $.Repository.IsPrivate}}
<div class="two wide column">
<a class="ui button mini" disabled='true' data-tooltip='{{$.i18n.Tr "dataset.how_to_public"}}'>{{$.i18n.Tr "dataset.private"}}</a>
</div>
{{ else }}
<div class="two wide column">
<div class="ui buttons mini">
<a class="ui button mini {{if .IsPrivate}}positive active{{end}}" href="javascript:void(0)" data-dataset-status="true-{{.UUID}}" data-csrf="{{$.CsrfToken}}" data-url="{{AppSubUrl}}/attachments/private" data-uuid={{.UUID}} data-private="true" data-is-private={{.IsPrivate}}>{{$.i18n.Tr "dataset.private"}}</a>
<span data-tooltip='{{$.i18n.Tr "dataset.how_to_public"}}' style="margin-right: 10px; line-height: 34px;" class="ui text red">{{$.i18n.Tr "dataset.private"}}</span>
{{else}}
<div class="compact small ui buttons" style="margin-right: 10px;">
<a class="ui button{{if .IsPrivate}} positive active{{end}}" href="javascript:void(0)" data-dataset-status="true-{{.UUID}}" data-csrf="{{$.CsrfToken}}" data-url="{{AppSubUrl}}/attachments/private" data-uuid={{.UUID}} data-private="true" data-is-private={{.IsPrivate}}>{{$.i18n.Tr "dataset.private"}}</a>
<div class="or"></div>
<a class="ui button mini {{if not .IsPrivate}}positive active{{end}}" href="javascript:void(0)" data-dataset-status="false-{{.UUID}}" data-csrf="{{$.CsrfToken}}" data-url="{{AppSubUrl}}/attachments/private" data-uuid={{.UUID}} data-private="false" data-is-private={{.IsPrivate}}>{{$.i18n.Tr "dataset.public"}}</a>
<a class="ui button{{if not .IsPrivate}} positive active{{end}}" href="javascript:void(0)" data-dataset-status="false-{{.UUID}}" data-csrf="{{$.CsrfToken}}" data-url="{{AppSubUrl}}/attachments/private" data-uuid={{.UUID}} data-private="false" data-is-private={{.IsPrivate}}>{{$.i18n.Tr "dataset.public"}}</a>
</div>
</div>
{{end}}
{{else}}
<a class="ui right small disabled button">{{$.i18n.Tr "dataset.delete"}}</a>
<span style="margin-right: 10px;line-height: 34px;" class="ui text{{if .IsPrivate}} red{{else}} green{{end}}">{{if .IsPrivate}} {{$.i18n.Tr "dataset.private"}} {{else}} {{$.i18n.Tr "dataset.public"}} {{end}}</span>
{{end}}
<div class="two wide column right aligned">
<a class="ui red button mini" href="javascript:void(0)" data-uuid={{.UUID}} data-dataset-delete data-remove-url="{{AppSubUrl}}/attachments/delete" data-csrf="{{$.CsrfToken}}">{{$.i18n.Tr "dataset.delete"}}</a>
</div>
{{else}}
<div class="two wide column">
<a class="ui button mini" disabled='true'>{{if .IsPrivate}} {{$.i18n.Tr "dataset.private"}} {{else}} {{$.i18n.Tr "dataset.public"}} {{end}}</a>
</div>
{{end}}
{{end}}
</div>

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


+ 3
- 3
templates/repo/datasets/index.tmpl View File

@@ -37,7 +37,7 @@
</div>
{{if .Permission.CanWrite $.UnitTypeDatasets}}
<div class="column four wide right aligned">
<a class="ui button primary" href="javascript:void(0)" id="dataset-edit">
<a class="ui green button" href="javascript:void(0)" id="dataset-edit">
{{.i18n.Tr "dataset.edit"}}
</a>
</div>
@@ -66,7 +66,7 @@
<input name="type" value="{{.Type}}" type="hidden" />
<div class="sixteen wide column">
<a class="ui button" id="cancel">{{.i18n.Tr "cancel"}}</a>
<button class="ui primary button" id="submit">{{.i18n.Tr "dataset.update_dataset"}}</button>
<button class="ui green button" id="submit">{{.i18n.Tr "dataset.update_dataset"}}</button>
</div>
</div>

@@ -80,7 +80,7 @@
<div class="ui sixteen wide column">
<div class="ui two column stackable grid">
<div class="column">
<h2>{{if eq .Type 0}}{{.i18n.Tr "repo.cloudbrain1"}}{{else}}{{.i18n.Tr "repo.cloudbrain2"}}{{end}}-{{.i18n.Tr "datasets"}}</h2>
<strong>{{if eq .Type 0}}{{.i18n.Tr "repo.cloudbrain1"}}{{else}}{{.i18n.Tr "repo.cloudbrain2"}}{{end}}-{{.i18n.Tr "datasets"}}</strong>
</div>
<div class="column right aligned" style="z-index:1">
<div class="ui right dropdown type jump item">


+ 95
- 26
templates/repo/home.tmpl View File

@@ -55,14 +55,48 @@
#contributorInfo > a.circular:nth-child(9n+8){
background-color: #bfd0aa;
}
.vue_menu {
cursor: auto;
position: absolute;
outline: none;
top: 100%;
margin: 0em;
padding: 0em 0em;
background: #fff;
font-size: 1em;
text-shadow: none;
text-align: left;
/* -webkit-box-shadow: 0px 2px 3px 0px rgb(34 36 38 / 15%); */
box-shadow: 0px 2px 3px 0px rgba(34, 36, 38, 0.15);
border: 1px solid rgba(34,36,38,0.15);
border-radius: 0.28571429rem;
-webkit-transition: opacity 0.1s ease;
transition: opacity 0.1s ease;
z-index: 11;
will-change: transform, opacity;
width: 100% !important;

-webkit-animation-iteration-count: 1;
animation-iteration-count: 1;
-webkit-animation-duration: 300ms;
animation-duration: 300ms;
-webkit-animation-timing-function: ease;
animation-timing-function: ease;
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
}




</style>
<div class="repository file list">
{{template "repo/header" .}}
<div class="ui container">
{{template "base/alert" .}}
{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}
<div class="ui repo-topic-edit grid form segment error" id="topic_edit" style="display:none">
<!-- <div class="ui repo-topic-edit grid form segment error" id="topic_edit" style="display:none">
<div class="fourteen wide column">
<div class="field">
<div class="ui fluid multiple search selection dropdown">
@@ -78,13 +112,14 @@
<a class="ui button primary" href="javascript:;" id="save_topic"
data-link="{{.RepoLink}}/topics">{{.i18n.Tr "repo.topic.done"}}</a>
</div>
</div>
</div> -->

{{end}}
<div class="hide" id="validate_prompt">
<span id="count_prompt">{{.i18n.Tr "repo.topic.count_prompt"}}</span>
<span id="format_prompt">{{.i18n.Tr "repo.topic.format_prompt"}}</span>
</div>
<div class="ui repo-description stackable grid">
<div class="ui repo-description stackable grid">

{{if .RepoSearchEnabled}}
<div class="ui repo-search four wide column">
@@ -101,7 +136,7 @@
</div>
{{end}}
</div>
{{if .Repository.IsArchived}}
<div class="ui warning message">
{{.i18n.Tr "repo.archive.title"}}
@@ -119,6 +154,21 @@
<a href="{{.BaseRepo.Link}}/compare/{{.BaseRepo.DefaultBranch | EscapePound}}...{{if ne .Repository.Owner.Name .BaseRepo.Owner.Name}}{{.Repository.Owner.Name}}:{{end}}{{.BranchName | EscapePound}}">
<button id="new-pull-request" class="ui compact basic button">{{if .PullRequestCtx.Allowed}}{{.i18n.Tr "repo.pulls.compare_changes"}}{{else}}{{.i18n.Tr "action.compare_branch"}}{{end}}</button>
</a>
{{if and .Repository.IsFork .PullRequestCtx.Allowed}}
{{if gt .FetchUpstreamCnt 0 }}
<a href="{{.Repository.Link}}/compare/{{.BranchName | EscapePound}}...{{.BaseRepo.Owner.Name}}:{{if .UpstreamSameBranchName}}{{.BranchName | EscapePound}}{{else}}{{.BaseRepo.DefaultBranch | EscapePound}}{{end}}">
<button id="new-pull-request" class="ui compact basic button" title="{{$.i18n.Tr (TrN $.i18n.Lang .FetchUpstreamCnt "repo.pulls.commits_count_1" "repo.pulls.commits_count_n") .FetchUpstreamCnt}}">{{.i18n.Tr "repo.pulls.fetch_upstream"}}</button>
</a>
{{else if lt .FetchUpstreamCnt 0}}
<a href="{{.Repository.Link}}/compare/{{.BranchName | EscapePound}}...{{.BaseRepo.Owner.Name}}:{{.BaseRepo.DefaultBranch | EscapePound}}">
<button id="new-pull-request" class="ui compact basic button" title="{{.i18n.Tr "repo.pulls.upstream_error"}}">{{.i18n.Tr "repo.pulls.fetch_upstream"}}</button>
</a>
{{else}}
<a href="{{.Repository.Link}}/compare/{{.BranchName | EscapePound}}...{{.BaseRepo.Owner.Name}}:{{if .UpstreamSameBranchName}}{{.BranchName | EscapePound}}{{else}}{{.BaseRepo.DefaultBranch | EscapePound}}{{end}}">
<button id="new-pull-request" class="ui compact basic button" title="{{.i18n.Tr "repo.pulls.upstream_up_to_date"}}">{{.i18n.Tr "repo.pulls.fetch_upstream"}}</button>
</a>
{{end}}
{{end}}
</div>
{{end}}
{{else}}
@@ -204,7 +254,7 @@
{{template "repo/view_list" .}}
{{end}}
</div>
<div class="ui six wide tablet four wide computer column">
<div class="ui six wide tablet four wide computer column">
<div id="repo-desc">
<h4 id="about-desc" class="ui header">简介
<!-- <a class="edit-icon" href="javascript:void(0)">
@@ -217,28 +267,47 @@
{{else}}
<span class="no-description text-italic">{{.i18n.Tr "repo.no_desc"}}</span>
{{end}}
</p>

</div>


{{if .Repository.Website}}
<p class="ui">
<i class="gray linkify icon"></i>
<a class="link edit-link" target="_blank" title="{{.Repository.Website}}" href="{{.Repository.Website}}">{{.Repository.Website}}</a>
</p>
{{end}}

<p class="ui" id="repo-topics">
<i class="grey bookmark icon"></i>
{{range .Topics}}<a class="ui repo-topic small label topic" href="{{AppSubUrl}}/explore/repos?q={{.Name}}&topic=1">{{.Name}}</a>{{end}}
{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}<a id="manage_topic">{{.i18n.Tr "repo.topic.manage_topics"}}</a>{{end}}
</p>
{{end}}

<div class="ui" id="repo-topics" style="display: flex;position: relative;margin-bottom: 1.0rem;">
<i class="grey bookmark icon"></i>

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

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


{{end}}
</div>
<div>
{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}<i id="manage_topic" style="cursor: pointer;" class="plus icon"></i>{{end}}
</div>
<div id="topic_edit" class="vue_menu" style="display:none">
<div id="topic_edit1">

</div>
</div>

</div>


<p class="ui">
<i class="grey code icon"></i>
{{range .LanguageStats}}
@@ -246,14 +315,14 @@
{{end}}
</p>

{{if .LICENSE}}
<p class="ui">
<i class="grey clone icon"></i>
{{.LICENSE}}
</p>
{{end}}

<div class="ui divider"></div>
@@ -269,22 +338,22 @@
{{range .ContributorInfo}}
{{if .UserInfo}}
<a href="{{AppSubUrl}}/{{.UserInfo.Name}}"><img class="ui avatar image" src="{{.UserInfo.RelAvatarLink}}"></a>
{{else if .Email}}
{{else if .Email}}
<a href="mailto:{{.Email}}" class="circular ui button">{{.Email}}</a>
{{end}}
{{end}}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

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


+ 18
- 8
templates/repo/issue/labels.tmpl View File

@@ -2,14 +2,24 @@
<div class="repository labels">
{{template "repo/header" .}}
<div class="ui container">
<div class="navbar">
{{template "repo/issue/navbar" .}}
{{if and (or .CanWriteIssues .CanWritePulls) (not .Repository.IsArchived)}}
<div class="ui right">
<div class="ui green new-label button">{{.i18n.Tr "repo.issues.new_label"}}</div>
</div>
{{end}}
</div>
<div class="ui two column stackable grid">
<div class="column" style="display: flex;align-items: center;">
<div class="ui large breadcrumb">
<a class="section" href="{{.RepoLink}}/issues">{{.i18n.Tr "repo.issues"}}</a>
<div class="divider"> / </div>
<div class="action section">{{.Title | RenderEmoji}}</div>
</div>
</div>
<div class="column right aligned">
{{template "repo/issue/navbar" .}}
{{if and (or .CanWriteIssues .CanWritePulls) (not .Repository.IsArchived)}}
<div class="ui right">
<div class="ui green new-label button">{{.i18n.Tr "repo.issues.new_label"}}</div>
</div>
{{end}}
</div>
</div>
<div class="ui divider"></div>
{{if and (or .CanWriteIssues .CanWritePulls) (not .Repository.IsArchived)}}
{{template "repo/issue/labels/label_new" .}}


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

@@ -3,14 +3,14 @@
{{template "repo/header" .}}
<div class="ui container">
<div class="ui three column stackable grid">
<div class="column">
{{template "repo/issue/navbar" .}}
<div class="column" style="display: flex;align-items: center;">
</div>
<div class="column center aligned">
{{template "repo/issue/search" .}}
</div>
{{if not .Repository.IsArchived}}
<div class="column right aligned">
{{template "repo/issue/navbar" .}}
{{if .PageIsIssueList}}
<a class="ui green button" href="{{.RepoLink}}/issues/new">{{.i18n.Tr "repo.issues.new"}}</a>
{{else}}


+ 8
- 2
templates/repo/issue/milestone_issues.tmpl View File

@@ -3,8 +3,14 @@
{{template "repo/header" .}}
<div class="ui container">
<div class="ui three column stackable grid">
<div class="column">
<h3>{{.Milestone.Name}}</h3>
<div class="column" style="display: flex;align-items: center;">
<div class="ui large 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>
<div class="divider"> / </div>
<div class="action section">{{.Milestone.Name}}</div>
</div>
</div>
<div class="column center aligned">



+ 22
- 6
templates/repo/issue/milestone_new.tmpl View File

@@ -2,13 +2,29 @@
<div class="repository new milestone">
{{template "repo/header" .}}
<div class="ui container">
<div class="navbar">
{{template "repo/issue/navbar" .}}
{{if and (or .CanWriteIssues .CanWritePulls) .PageIsEditMilestone}}
<div class="ui right floated secondary menu">
<a class="ui green button" href="{{$.RepoLink}}/milestones/new">{{.i18n.Tr "repo.milestones.new"}}</a>
<div class="ui two column stackable grid">
<div class="column" style="display: flex;align-items: center;">
<div class="ui large 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>
<div class="divider"> / </div>
{{if .PageIsEditMilestone}}
<div class="action section">{{.i18n.Tr "repo.milestones.edit"}}</div>
{{else}}
<div class="action section">{{.i18n.Tr "repo.milestones.new"}}</div>
{{end}}
</div>
</div>
{{end}}
<div class="column right aligned">
{{template "repo/issue/navbar" .}}
{{if and (or .CanWriteIssues .CanWritePulls) .PageIsEditMilestone}}
<div class="ui right floated secondary menu">
<a class="ui green button" href="{{$.RepoLink}}/milestones/new">{{.i18n.Tr "repo.milestones.new"}}</a>
</div>
{{end}}
</div>
</div>
<div class="ui divider"></div>
<h2 class="ui dividing header">


+ 19
- 8
templates/repo/issue/milestones.tmpl View File

@@ -2,14 +2,25 @@
<div class="repository milestones">
{{template "repo/header" .}}
<div class="ui container">
<div class="navbar">
{{template "repo/issue/navbar" .}}
{{if and (or .CanWriteIssues .CanWritePulls) (not .Repository.IsArchived)}}
<div class="ui right">
<a class="ui green button" href="{{$.Link}}/new">{{.i18n.Tr "repo.milestones.new"}}</a>
</div>
{{end}}
</div>
<div class="ui two column stackable grid">
<div class="column" style="display: flex;align-items: center;">
<div class="ui large breadcrumb">
<a class="section" href="{{.RepoLink}}/issues">{{.i18n.Tr "repo.issues"}}</a>
<div class="divider"> / </div>
<div class="action section">{{.Title | RenderEmoji}}</div>
</div>
</div>
{{if not .Repository.IsArchived}}
<div class="column right aligned">
{{template "repo/issue/navbar" .}}
{{if and (or .CanWriteIssues .CanWritePulls) (not .Repository.IsArchived)}}
<div class="ui right">
<a class="ui green button" href="{{$.Link}}/new">{{.i18n.Tr "repo.milestones.new"}}</a>
</div>
{{end}}
</div>
{{end}}
</div>
<div class="ui divider"></div>
{{template "base/alert" .}}
<div class="ui tiny basic buttons">


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

@@ -1,4 +1,4 @@
<div class="ui compact left small menu">
<div class="ui compact small menu" style="margin-right: 1rem;">
<a class="{{if .PageIsLabels}}active{{end}} item" href="{{.RepoLink}}/labels">{{.i18n.Tr "repo.labels"}}</a>
<a class="{{if .PageIsMilestones}}active{{end}} item" href="{{.RepoLink}}/milestones">{{.i18n.Tr "repo.milestones"}}</a>
</div>

+ 15
- 3
templates/repo/issue/new.tmpl View File

@@ -2,9 +2,21 @@
<div class="repository new issue">
{{template "repo/header" .}}
<div class="ui container">
<div class="navbar">
{{template "repo/issue/navbar" .}}
</div>
<div class="ui two column stackable grid">
<div class="column" style="display: flex;align-items: center;">
<div class="ui large 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>
</div>
</div>
<div class="column right aligned">
{{template "repo/issue/navbar" .}}
</div>
</div>
<div class="ui divider"></div>
{{template "repo/issue/new_form" .}}
</div>


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

@@ -7,7 +7,7 @@
<input type="hidden" name="assignee" value="{{$.AssigneeID}}"/>
<div class="ui search action input">
<input name="q" value="{{.Keyword}}" placeholder="{{.i18n.Tr "explore.search"}}..." autofocus>
<button class="ui blue button" type="submit">{{.i18n.Tr "explore.search"}}</button>
<button class="ui green button" type="submit">{{.i18n.Tr "explore.search"}}</button>
</div>
</div>
</form>

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

@@ -3,11 +3,24 @@
{{template "repo/header" .}}
<div class="ui container">
<div class="ui two column stackable grid">
<div class="column">
{{template "repo/issue/navbar" .}}
<div class="column" style="display: flex;align-items: center;">
{{if .PageIsIssueList}}
<div class="ui large 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">
<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>
</div>
{{end}}
</div>
{{if not .Repository.IsArchived}}
<div class="column right aligned">
{{template "repo/issue/navbar" .}}
{{if .PageIsIssueList}}
<a class="ui green button" href="{{.RepoLink}}/issues/new">{{.i18n.Tr "repo.issues.new"}}</a>
{{else}}


+ 43
- 13
templates/repo/modelarts/index.tmpl View File

@@ -228,41 +228,71 @@
<div class="ui sixteen wide column">

<!-- 排序区 -->
<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="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>
</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="six wide column">
<a class="title" href="{{$.Link}}/{{.JobID}}">
<span class="fitted">{{svg "octicon-tasklist" 16}}</span>
<span class="fitted">{{.JobName}}</span>
<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}}">
<!-- <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 class="">{{TimeSinceUnix .CreatedUnix $.Lang}}</span>
<span style="font-size: 12px;margin-left: 0.4rem;" class="">{{TimeSinceUnix .Cloudbrain.CreatedUnix $.Lang}}</span>
</div>

<div class="one wide column">
<a href="{{AppSubUrl}}/{{.User.Name}}" title="{{.User.Name}}"><img class="ui avatar image" src="{{.User.RelAvatarLink}}"></a>
</div>

<div class="seven wide column text right">
@@ -270,7 +300,7 @@
<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">
<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;">
@@ -282,9 +312,9 @@
</div>

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


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

@@ -2,11 +2,25 @@
<div class="repository view issue pull commits">
{{template "repo/header" .}}
<div class="ui container">
<div class="navbar">
<!-- <div class="navbar">
{{template "repo/issue/navbar" .}}
<div class="ui right">
<a class="ui green button {{if not .PullRequestCtx.Allowed}}disabled{{end}}" href="{{.RepoLink}}/compare/{{.BranchName | EscapePound}}...{{.PullRequestCtx.HeadInfo | EscapePound}}">{{.i18n.Tr "repo.pulls.new"}}</a>
</div>
</div> -->
<div class="ui two column stackable grid">
<div class="column" style="display: flex;align-items: center;">
<div class="ui large 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>
</div>
</div>
<div class="column right aligned">
{{template "repo/issue/navbar" .}}
<a class="ui green button {{if not .PullRequestCtx.Allowed}}disabled{{end}}" href="{{.RepoLink}}/compare/{{.BranchName | EscapePound}}...{{.PullRequestCtx.HeadInfo | EscapePound}}">{{.i18n.Tr "repo.pulls.new"}}</a>
</div>
</div>
<div class="ui divider"></div>
{{template "repo/issue/view_title" .}}


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

@@ -2,11 +2,25 @@
<div class="repository view issue pull files diff">
{{template "repo/header" .}}
<div class="ui container {{if .IsSplitStyle}}fluid padded{{end}}">
<div class="navbar">
<!-- <div class="navbar">
{{template "repo/issue/navbar" .}}
<div class="ui right">
<a class="ui green button {{if not .PullRequestCtx.Allowed}}disabled{{end}}" href="{{.RepoLink}}/compare/{{.BranchName | EscapePound}}...{{.PullRequestCtx.HeadInfo | EscapePound}}">{{.i18n.Tr "repo.pulls.new"}}</a>
</div>
</div> -->
<div class="ui two column stackable grid">
<div class="column" style="display: flex;align-items: center;">
<div class="ui large 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>
</div>
</div>
<div class="column right aligned">
{{template "repo/issue/navbar" .}}
<a class="ui green button {{if not .PullRequestCtx.Allowed}}disabled{{end}}" href="{{.RepoLink}}/compare/{{.BranchName | EscapePound}}...{{.PullRequestCtx.HeadInfo | EscapePound}}">{{.i18n.Tr "repo.pulls.new"}}</a>
</div>
</div>
<div class="ui divider"></div>
{{template "repo/issue/view_title" .}}


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

@@ -74,7 +74,7 @@
<input type="hidden" name="state" value="{{$.State}}"/>
<div class="ui search action input">
<input name="q" value="{{$.Keyword}}" placeholder="{{.i18n.Tr "explore.search"}}..." autofocus>
<button class="ui blue button" type="submit">{{.i18n.Tr "explore.search"}}</button>
<button class="ui green button" type="submit">{{.i18n.Tr "explore.search"}}</button>
</div>
</div>
</form>


+ 477
- 0
web_src/js/components/EditTopics.vue View File

@@ -0,0 +1,477 @@
<template>
<div>
<div class="input-search">
<el-input v-model="input" clearable :autofocus="true" @input="changeValue" id="topics_input" @keyup.enter.native="postTopic" placeholder="搜索或创建标签">

</el-input>
<div class="scrolling-menu">
<div v-if="showSearchTopic" class="item-text" v-for="(arr,i) in array" @click="addTopics(i,arr)">
<div class="icon-wrapper">
<i style="line-height: 1.5;color: #303643;font-weight: 900;" v-if="showInitTopic[i]" class="el-icon-check" ></i>
</div>
<div class="text">{{arr.topic_name}} </div>
</div>
<div v-if="showInputValue" class="addition item-text" @click="postTopic">
点击或回车添加<b class="user-add-label-text">{{input}}</b>标签
</div>
<div v-if="showAddTopic" class="item-text" @click="addPostTopic">
<div class="icon-wrapper">
<i style="line-height: 1.5;color: #303643;font-weight: 900;" v-if="showAddFlage" class="el-icon-check" ></i>
</div>
<div class="text">{{input}}</div>
</div>

</div>

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

<script>

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

import $ from 'jquery'


export default {
data() {
return {
input:'',
params:{},
showInputValue:false,
showFlag:-1,
array:[],
showAddTopic:false,
showAddFlage:false,
showSearchTopic:true,
postUrl:'',
arrayTopics:[],
showInitTopic:[],

};
},
methods: {

addTopics(item,array){
if(!this.showInitTopic[item]){
if(this.arrayTopics.includes(array.topic_name)){
return
}
else{
this.arrayTopics.push(array.topic_name)
let topics = this.arrayTopics
let strTopics = topics.join(',')
let data = this.qs.stringify({
_csrf:csrf,
topics:strTopics
})
this.Post(data,topics)
this.$set(this.showInitTopic,item,true)
$('#repo-topics1').children('span').remove()
}

}else{
this.arrayTopics=this.arrayTopics.filter(ele=>{
return ele !== array.topic_name

})

let topics = this.arrayTopics
let strTopics = topics.join(',')
let data = this.qs.stringify({
_csrf:csrf,
topics:strTopics
})
this.Post(data,topics)
this.$set(this.showInitTopic,item,false)
if(this.arrayTopics.length===0){
$('#repo-topics1').append('<span class="no-description text-italic">暂无标签</span>')
}else{
$('#repo-topics1').children('span').remove()
}

}
},
changeValue(){
if (this.input === ''){
this.array = this.arrayTopics
let data = []
this.showInitTopic = []
this.array.forEach((element,index) => {
let item = {}
item.topic_name = element
data.push(item)
this.showInitTopic.push(true)
});
this.array = data
this.showInputValue = false
this.showSearchTopic = true
}
else if(this.arrayTopics.indexOf(this.input)>-1){
this.showInputValue = false
this.showSearchTopic = false
}else{
this.showInitTopic = []
let timestamp=new Date().getTime()
this.params.q = this.input
this.params._ = timestamp
this.$axios.get('/api/v1/topics/search',{
params:this.params
}).then((res)=>{
this.array = res.data.topics

this.array.forEach((element,index) => {
if (this.arrayTopics.indexOf(element.topic_name)>-1){
this.showInitTopic.push(true)

}else{
this.showInitTopic.push(false)
}
});
})
this.showInputValue = true
this.showSearchTopic = true
}
this.showAddTopic = false

},
Post(data,topics){
this.$axios.post(this.postUrl,data).then(res=>{
const viewDiv = $('#repo-topics1');
viewDiv.children('.topic').remove();
if (topics.length) {
const topicArray = topics;
const last = viewDiv.children('a').last();
for (let i = 0; i < topicArray.length; i++) {
const link = $('<a class="ui repo-topic small label topic"></a>');
link.attr(
'href',
`${AppSubUrl}/explore/repos?q=${encodeURIComponent(
topicArray[i]
)}&topic=1`
);
link.text(topicArray[i]);
// link.insertBefore(last);
viewDiv.append(link)
}
}
viewDiv.show();
})
},
postTopic(){
const patter = /^[\u4e00-\u9fa5a-zA-Z0-9][\u4e00-\u9fa5a-zA-Z0-9-]{0,34}$/
let regexp = patter.test(this.input)
if(!regexp){
this.$notify({
message: '标签名必须以中文、字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符',
duration: 3000,
type:'error'
});
return
}else{
let topic = this.input
if(this.arrayTopics.includes(topic)){
return
}
else{
this.arrayTopics.push(topic)
let topics = this.arrayTopics
let strTopics = topics.join(',')
let data = this.qs.stringify({
_csrf:csrf,
topics:strTopics
})
this.Post(data,topics)
$('#repo-topics1').children('span').remove()
this.showInputValue = false
this.showAddTopic = true
this.showAddFlage = true
}
}

},
addPostTopic(){
if(this.showAddFlage){
this.arrayTopics.pop()
let topics = this.arrayTopics
let strTopics = topics.join(',')
let data = this.qs.stringify({
_csrf:csrf,
topics:strTopics
})
this.Post(data,topics)
if(this.arrayTopics.length===0){
console.log("add postTopic")
$('#repo-topics1').append('<span class="no-description text-italic">暂无标签</span>')
}else{
$('#repo-topics1').children('span').remove()
}
}
else if(!this.showAddFlage){
let topic = this.input
this.arrayTopics.push(topic)
let topics = this.arrayTopics
let strTopics = topics.join(',')
let data = this.qs.stringify({
_csrf:csrf,
topics:strTopics
})
this.Post(data,topics)
$('#repo-topics1').children('span').remove()
}
this.showAddFlage = !this.showAddFlage
},
initTopics(){
const mgrBtn = $('#manage_topic');
const editDiv = $('#topic_edit');
mgrBtn.on('click', (e) => {
// viewDiv.hide();
editDiv.css('display', ''); // show Semantic UI Grid
this.input = ''
if (this.input === ''){
this.array = this.arrayTopics
let data = []
this.showInitTopic = []
this.array.forEach((element,index) => {
let item = {}
item.topic_name = element
data.push(item)
this.showInitTopic.push(true)
});
this.array = data
this.showInputValue = false
this.showSearchTopic = true
}
stopPropagation(e);
});
$(document).bind('click',function(){
editDiv.css('display','none');

})
editDiv.click(function(e){
stopPropagation(e);
})


function stopPropagation(e) {
var ev = e || window.event;
if (ev.stopPropagation) {
ev.stopPropagation();
}
else if (window.event) {
window.event.cancelBubble = true;//兼容IE
}
}
}
},
computed:{
},
watch: {

input(newValue){
if (newValue === ''){
this.array = this.arrayTopics
let data = []
this.showInitTopic = []
this.array.forEach((element,index) => {
let item = {}
item.topic_name = element
data.push(item)
this.showInitTopic.push(true)
});
this.array = data
this.showInputValue = false
this.showSearchTopic = true
}
}
},
mounted() {
const context = this
this.postUrl = `${window.location.pathname}/topics`;
$('#repo-topics1').children('a').each(function(){
context.arrayTopics.push($(this).text())
});
if(this.arrayTopics.length===0){
$('#repo-topics1').append('<span class="no-description text-italic">暂无标签</span>')
}
this.changeValue()
} ,
created(){
this.initTopics();
this.input=''
}
};
</script>

<style scoped>
.input-search {
width: 100%;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
min-width: 10rem;
white-space: nowrap;
font-size: 1rem;
position: relative;
display: inline-block;
color: rgba(0,0,0,0.8);
padding: 8px;
}
/deep/ .el-input__inner{
border-color: #409eff;
}
.scrolling-menu{
border-top: none !important;
padding-top: 0 !important;
padding-bottom: 0 !important;
display: block;
position: static;
overflow-y: auto;
border: none;
-webkit-box-shadow: none !important;
box-shadow: none !important;
border-radius: 0 !important;
margin: 0 !important;
min-width: 100% !important;
width: auto !important;
border-top: 1px solid rgba(34,36,38,0.15);
}
.item-text{
border-top: none;
padding-right: calc(1.14285714rem + 17px ) !important;
line-height: 1.333;
padding-top: 0.7142857rem !important;
padding-bottom: 0.7142857rem !important;
position: relative;
cursor: pointer;
display: block;
border: none;
height: auto;
text-align: left;
border-top: none;
line-height: 1em;
color: rgba(0,0,0,0.87);
padding: 0.78571429rem 1.14285714rem !important;
font-size: 1rem;
text-transform: none;
font-weight: normal;
-webkit-box-shadow: none;
box-shadow: none;
-webkit-touch-callout: none;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
display: -webkit-box !important;
display: -ms-flexbox !important;
display: flex !important;
}
.icon-wrapper{
text-align: left;
width: 24px;
height: 20px;
-ms-flex-negative: 0;
flex-shrink: 0;
}
.text{
max-width: 80%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 12px;
font-weight: 400;
color: #40485b;
}
.addition{
background: #f6f6f6;
}
.user-add-label-text{
max-width: 80%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin: 0 4px;
}
</style>

+ 4
- 5
web_src/js/components/MinioUploader.vue View File

@@ -1,17 +1,16 @@
<template>
<div class="dropzone-wrapper dataset-files">
<div class="ui pointing below red basic label">
<i class="icon info circle"></i>只有zip格式的数据集才能发起云脑任务
</div>
<div
id="dataset"
class="dropzone"
/>
<p class="upload-info">
{{ file_status_text }}
<span class="success">{{ status }}</span>
<strong class="success text red">{{ status }}</strong>
</p>
<p>云脑1提供 <span class="text blue">CPU / GPU</span> 资源,云脑2提供 <span class="text blue">Ascend NPU</span> 资源;调试使用的数据集也需要上传到对应的环境。</p>
<p>说明:<br>
- 只有zip格式的数据集才能发起云脑任务;<br>
- 云脑1提供 <span class="text blue">CPU / GPU</span> 资源,云脑2提供 <span class="text blue">Ascend NPU</span> 资源;调试使用的数据集也需要上传到对应的环境。</p>
</div>
</template>



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

@@ -6,9 +6,12 @@
/>
<p class="upload-info">
{{ file_status_text }}
<span class="success">{{ status }}</span>
<strong class="success text red">{{ status }}</strong>
</p>
<p>说明:<br>
- 只有zip格式的数据集才能发起云脑任务;<br>
- 云脑1提供 <span class="text blue">CPU / GPU</span> 资源,云脑2提供 <span class="text blue">Ascend NPU</span> 资源;调试使用的数据集也需要上传到对应的环境。
</p>
<p>云脑1提供 <span class="text blue">CPU / GPU</span> 资源,云脑2提供 <span class="text blue">Ascend NPU</span> 资源;调试使用的数据集也需要上传到对应的环境。</p>
</div>
</template>



+ 255
- 214
web_src/js/index.js View File

@@ -36,6 +36,7 @@ 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'

Vue.use(ElementUI);
Vue.prototype.$axios = axios;
@@ -2967,11 +2968,13 @@ $(document).ready(async () => {
initVueUploader();
initObsUploader();
initVueEditAbout();
initVueEditTopic();
initVueImages();
initTeamSettings();
initCtrlEnterSubmit();
initNavbarContentToggle();
initTopicbar();
// initTopicbar();
// closeTopicbar();
initU2FAuth();
initU2FRegister();
initIssueList();
@@ -3666,7 +3669,19 @@ function initVueEditAbout() {
});
}


function initVueEditTopic() {
const el = document.getElementById('topic_edit1');
if (!el) {
return;
}
new Vue({
el:'#topic_edit1',
render:h=>h(EditTopics)
})
}
function initVueImages() {
const el = document.getElementById('images');
console.log("el",el)
@@ -3677,6 +3692,7 @@ function initVueImages() {

new Vue({
el: '#images',
render: h => h(Images)
});
}
@@ -3932,218 +3948,243 @@ function initNavbarContentToggle() {
});
}

function initTopicbar() {
const mgrBtn = $('#manage_topic');
const editDiv = $('#topic_edit');
const viewDiv = $('#repo-topics');
const saveBtn = $('#save_topic');
const topicDropdown = $('#topic_edit .dropdown');
const topicForm = $('#topic_edit.ui.form');
const topicPrompts = getPrompts();

mgrBtn.on('click', () => {
viewDiv.hide();
editDiv.css('display', ''); // show Semantic UI Grid
});

function getPrompts() {
const hidePrompt = $('div.hide#validate_prompt');
const prompts = {
countPrompt: hidePrompt.children('#count_prompt').text(),
formatPrompt: hidePrompt.children('#format_prompt').text()
};
hidePrompt.remove();
return prompts;
}

saveBtn.on('click', () => {
const topics = $('input[name=topics]').val();

$.post(
saveBtn.data('link'),
{
_csrf: csrf,
topics
},
(_data, _textStatus, xhr) => {
if (xhr.responseJSON.status === 'ok') {
viewDiv.children('.topic').remove();
if (topics.length) {
const topicArray = topics.split(',');

const last = viewDiv.children('a').last();
for (let i = 0; i < topicArray.length; i++) {
const link = $('<a class="ui repo-topic small label topic"></a>');
link.attr(
'href',
`${AppSubUrl}/explore/repos?q=${encodeURIComponent(
topicArray[i]
)}&topic=1`
);
link.text(topicArray[i]);
link.insertBefore(last);
}
}
editDiv.css('display', 'none');
viewDiv.show();
}
}
)
.fail((xhr) => {
if (xhr.status === 422) {
if (xhr.responseJSON.invalidTopics.length > 0) {
topicPrompts.formatPrompt = xhr.responseJSON.message;

const {invalidTopics} = xhr.responseJSON;
const topicLables = topicDropdown.children('a.ui.label');

topics.split(',').forEach((value, index) => {
for (let i = 0; i < invalidTopics.length; i++) {
if (invalidTopics[i] === value) {
topicLables
.eq(index)
.removeClass('green')
.addClass('red');
}
}
});
} else {
topicPrompts.countPrompt = xhr.responseJSON.message;
}
}
})
.always(() => {
topicForm.form('validate form');
});
});

topicDropdown.dropdown({
allowAdditions: true,
forceSelection: false,
fields: {name: 'description', value: 'data-value'},
saveRemoteData: false,
label: {
transition: 'horizontal flip',
duration: 200,
variation: false,
blue: true,
basic: true
},
className: {
label: 'ui small label'
},
apiSettings: {
url: `${AppSubUrl}/api/v1/topics/search?q={query}`,
throttle: 500,
cache: false,
onResponse(res) {
const formattedResponse = {
success: false,
results: []
};
const stripTags = function (text) {
return text.replace(/<[^>]*>?/gm, '');
};

const query = stripTags(this.urlData.query.trim());
let found_query = false;
const current_topics = [];
topicDropdown
.find('div.label.visible.topic,a.label.visible')
.each((_, e) => {
current_topics.push(e.dataset.value);
});

if (res.topics) {
let found = false;
for (let i = 0; i < res.topics.length; i++) {
// skip currently added tags
if (current_topics.includes(res.topics[i].topic_name)) {
continue;
}

if (
res.topics[i].topic_name.toLowerCase() === query.toLowerCase()
) {
found_query = true;
}
formattedResponse.results.push({
description: res.topics[i].topic_name,
'data-value': res.topics[i].topic_name
});
found = true;
}
formattedResponse.success = found;
}

if (query.length > 0 && !found_query) {
formattedResponse.success = true;
formattedResponse.results.unshift({
description: query,
'data-value': query
});
} else if (query.length > 0 && found_query) {
formattedResponse.results.sort((a, b) => {
if (a.description.toLowerCase() === query.toLowerCase()) return -1;
if (b.description.toLowerCase() === query.toLowerCase()) return 1;
if (a.description > b.description) return -1;
if (a.description < b.description) return 1;
return 0;
});
}

return formattedResponse;
}
},
onLabelCreate(value) {
value = value.toLowerCase().trim();
this.attr('data-value', value)
.contents()
.first()
.replaceWith(value);
return $(this);
},
onAdd(addedValue, _addedText, $addedChoice) {
addedValue = addedValue.toLowerCase().trim();
$($addedChoice).attr('data-value', addedValue);
$($addedChoice).attr('data-text', addedValue);
}
});

$.fn.form.settings.rules.validateTopic = function (_values, regExp) {
const topics = topicDropdown.children('a.ui.label');
const status =
topics.length === 0 || (topics.last().attr('data-value').match(regExp) !== null && topics.last().attr('data-value').length <= 35);
if (!status) {
topics
.last()
.removeClass('green')
.addClass('red');
}
return status && topicDropdown.children('a.ui.label.red').length === 0;
};

topicForm.form({
on: 'change',
inline: true,
fields: {
topics: {
identifier: 'topics',
rules: [
{
type: 'validateTopic',
value: /^[\u4e00-\u9fa5a-z0-9][\u4e00-\u9fa5a-z0-9-]{0,105}$/,
prompt: topicPrompts.formatPrompt
},
{
type: 'maxCount[25]',
prompt: topicPrompts.countPrompt
}
]
}
}
});
}
// function initTopicbar() {
// const mgrBtn = $('#manage_topic');
// const editDiv = $('#topic_edit');
// const viewDiv = $('#repo-topics');
// const saveBtn = $('#save_topic');
// const topicDropdown = $('#topic_edit .dropdown');
// const topicForm = $('#topic_edit.ui.form');
// const topicInput = $("#topics_input")
// const topicPrompts = getPrompts();
// mgrBtn.on('click', (e) => {
// // viewDiv.hide();
// editDiv.css('display', ''); // show Semantic UI Grid
// topicInput.val('')
// console.log("-----------------asdasd",$("#topics_input"),$("#topics_input").val())
// stopPropagation(e);
// });
// $(document).bind('click',function(){
// editDiv.css('display','none');

// })
// editDiv.click(function(e){
// stopPropagation(e);
// })

// function getPrompts() {
// const hidePrompt = $('div.hide#validate_prompt');
// const prompts = {
// countPrompt: hidePrompt.children('#count_prompt').text(),
// formatPrompt: hidePrompt.children('#format_prompt').text()
// };
// hidePrompt.remove();
// return prompts;
// }

// function stopPropagation(e) {
// var ev = e || window.event;
// if (ev.stopPropagation) {
// ev.stopPropagation();
// }
// else if (window.event) {
// window.event.cancelBubble = true;//兼容IE
// }
// }


// saveBtn.on('click', () => {
// const topics = $('input[name=topics]').val();

// $.post(
// saveBtn.data('link'),
// {
// _csrf: csrf,
// topics
// },
// (_data, _textStatus, xhr) => {
// if (xhr.responseJSON.status === 'ok') {
// console.log("--------saveBtn------------")
// viewDiv.children('.topic').remove();
// if (topics.length) {
// const topicArray = topics.split(',');

// const last = viewDiv.children('a').last();
// for (let i = 0; i < topicArray.length; i++) {
// const link = $('<a class="ui repo-topic small label topic"></a>');
// link.attr(
// 'href',
// `${AppSubUrl}/explore/repos?q=${encodeURIComponent(
// topicArray[i]
// )}&topic=1`
// );
// link.text(topicArray[i]);
// link.insertBefore(last);
// }
// }
// editDiv.css('display', 'none');
// viewDiv.show();
// }
// }
// )
// .fail((xhr) => {
// if (xhr.status === 422) {
// if (xhr.responseJSON.invalidTopics.length > 0) {
// topicPrompts.formatPrompt = xhr.responseJSON.message;

// const {invalidTopics} = xhr.responseJSON;
// const topicLables = topicDropdown.children('a.ui.label');

// topics.split(',').forEach((value, index) => {
// for (let i = 0; i < invalidTopics.length; i++) {
// if (invalidTopics[i] === value) {
// topicLables
// .eq(index)
// .removeClass('green')
// .addClass('red');
// }
// }
// });
// } else {
// topicPrompts.countPrompt = xhr.responseJSON.message;
// }
// }
// })
// .always(() => {
// topicForm.form('validate form');
// });
// });

// topicDropdown.dropdown({
// allowAdditions: true,
// forceSelection: false,
// fields: {name: 'description', value: 'data-value'},
// saveRemoteData: false,
// label: {
// transition: 'horizontal flip',
// duration: 200,
// variation: false,
// blue: true,
// basic: true
// },
// className: {
// label: 'ui small label'
// },
// apiSettings: {
// url: `${AppSubUrl}/api/v1/topics/search?q={query}`,
// throttle: 500,
// cache: false,
// onResponse(res) {
// const formattedResponse = {
// success: false,
// results: []
// };
// const stripTags = function (text) {
// return text.replace(/<[^>]*>?/gm, '');
// };

// const query = stripTags(this.urlData.query.trim());
// let found_query = false;
// const current_topics = [];
// topicDropdown
// .find('div.label.visible.topic,a.label.visible')
// .each((_, e) => {
// current_topics.push(e.dataset.value);
// });

// if (res.topics) {
// let found = false;
// for (let i = 0; i < res.topics.length; i++) {
// // skip currently added tags
// if (current_topics.includes(res.topics[i].topic_name)) {
// continue;
// }

// if (
// res.topics[i].topic_name.toLowerCase() === query.toLowerCase()
// ) {
// found_query = true;
// }
// formattedResponse.results.push({
// description: res.topics[i].topic_name,
// 'data-value': res.topics[i].topic_name
// });
// found = true;
// }
// formattedResponse.success = found;
// }

// if (query.length > 0 && !found_query) {
// formattedResponse.success = true;
// formattedResponse.results.unshift({
// description: query,
// 'data-value': query
// });
// } else if (query.length > 0 && found_query) {
// formattedResponse.results.sort((a, b) => {
// if (a.description.toLowerCase() === query.toLowerCase()) return -1;
// if (b.description.toLowerCase() === query.toLowerCase()) return 1;
// if (a.description > b.description) return -1;
// if (a.description < b.description) return 1;
// return 0;
// });
// }

// return formattedResponse;
// }
// },
// onLabelCreate(value) {
// value = value.toLowerCase().trim();
// this.attr('data-value', value)
// .contents()
// .first()
// .replaceWith(value);
// return $(this);
// },
// onAdd(addedValue, _addedText, $addedChoice) {
// addedValue = addedValue.toLowerCase().trim();
// $($addedChoice).attr('data-value', addedValue);
// $($addedChoice).attr('data-text', addedValue);
// }
// });

// $.fn.form.settings.rules.validateTopic = function (_values, regExp) {
// const topics = topicDropdown.children('a.ui.label');
// const status =
// topics.length === 0 || (topics.last().attr('data-value').match(regExp) !== null && topics.last().attr('data-value').length <= 35);
// if (!status) {
// topics
// .last()
// .removeClass('green')
// .addClass('red');
// }
// return status && topicDropdown.children('a.ui.label.red').length === 0;
// };

// topicForm.form({
// on: 'change',
// inline: true,
// fields: {
// topics: {
// identifier: 'topics',
// rules: [
// {
// type: 'validateTopic',
// value: /^[\u4e00-\u9fa5a-z0-9][\u4e00-\u9fa5a-z0-9-]{0,105}$/,
// prompt: topicPrompts.formatPrompt
// },
// {
// type: 'maxCount[25]',
// prompt: topicPrompts.countPrompt
// }
// ]
// }
// }
// });
// }

window.toggleDeadlineForm = function () {
$('#deadlineForm').fadeToggle(150);


+ 15
- 2
web_src/less/_dataset.less View File

@@ -140,19 +140,32 @@
border: 1px solid #ffffff;
}
}
}
}
}

.item {
border-bottom: 1px solid rgba(34,36,38,.15);
.ui.buttons {
.button {
box-shadow: none !important;
}
}
}
.ui.grid > .row {
align-items: center;
}
.title {
font-size: 16px;
font-weight: bold;
margin: 0 6px;
margin: 0 6px;
overflow: hidden;
padding-right: 15px;
white-space: nowrap;
text-overflow: ellipsis;
display: block;
}
.directory-seperator {
padding: 0 4px;


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

@@ -220,3 +220,25 @@ footer .column{margin-bottom:0!important; padding-bottom:0!important;}
.ui.vertical.menu .dropdown.item .menu {
left: 50%;
}

// icon cloudbrain
.i-round{display:inline-block;width:18px;height:18px;background:url("/img/icons.svg");background-position: -496px -52px;}
.i-bg-organ{background-position: -496px -52px;}
.STOPPED{display:inline-block;width:18px;height:18px;background:url("/img/icons.svg");background-position: -496px -52px;background-position: -459px -52px;}
.RUNNING{display:inline-block;width:18px;height:18px;background:url("/img/icons.svg");background-position: -496px -52px;background-position: -478px -52px;}
.i-bg-orange{background-position: -495px -51px;}
.FAILED{display:inline-block;width:18px;height:18px;background:url("/img/icons.svg");background-position: -496px -52px;background-position: -532px -52px;}
.i-bg-green{background-position: -441px -52px;}
.i-bg-used{background-position: -514px -52px;}
.icon-bind{background-position: -550px -52px;}
.icon-unbind{background-position: -568px -52px;}
.CREATING, .STOPPING, .DELETING, .STARTING, .WAITING{display:inline-block;background-image:url('/img/loading.gif');background-repeat:no-repeat;width:16px;height:16px;background-size:16px 16px;margin-right:5px;}
.text_over{
overflow: hidden;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
white-space: nowrap;
display: inline-block;
width: 100%;
}


Loading…
Cancel
Save