#3614 WIP: mlops entry and switch config

Closed
choi wants to merge 7317 commits from V20221228 into V20211228
  1. +2
    -0
      .gitignore
  2. +4
    -0
      .prettierignore
  3. +1
    -0
      .prettierrc.json
  4. +18
    -3
      Makefile
  5. +16
    -7
      README.md
  6. +16
    -0
      cmd/serv.go
  7. +11
    -0
      custom/conf/app.ini.sample
  8. +0
    -0
      custom/public/RemixIcon_Fonts_v2.5.0/fonts/.keep
  9. +0
    -0
      custom/public/RemixIcon_Fonts_v2.5.0/fonts/.keep 3
  10. +2317
    -0
      custom/public/RemixIcon_Fonts_v2.5.0/fonts/remixicon.css
  11. BIN
      custom/public/RemixIcon_Fonts_v2.5.0/fonts/remixicon.eot
  12. +1
    -0
      custom/public/RemixIcon_Fonts_v2.5.0/fonts/remixicon.glyph.json
  13. +2319
    -0
      custom/public/RemixIcon_Fonts_v2.5.0/fonts/remixicon.less
  14. +6835
    -0
      custom/public/RemixIcon_Fonts_v2.5.0/fonts/remixicon.svg
  15. +11356
    -0
      custom/public/RemixIcon_Fonts_v2.5.0/fonts/remixicon.symbol.svg
  16. BIN
      custom/public/RemixIcon_Fonts_v2.5.0/fonts/remixicon.ttf
  17. BIN
      custom/public/RemixIcon_Fonts_v2.5.0/fonts/remixicon.woff
  18. BIN
      custom/public/RemixIcon_Fonts_v2.5.0/fonts/remixicon.woff2
  19. +299
    -26
      custom/public/css/git.openi.css
  20. +1
    -0
      custom/public/css/placeholder-home.css
  21. +1
    -0
      custom/public/css/placeholder.css
  22. BIN
      custom/public/img/home-banner-01-en.jpg
  23. BIN
      custom/public/img/home-banner-01.jpg
  24. BIN
      custom/public/img/home-banner-02-1.jpg
  25. BIN
      custom/public/img/home-banner-02-2.png
  26. BIN
      custom/public/img/home-bg-ps.png
  27. BIN
      custom/public/img/i-pic-01.jpg
  28. BIN
      custom/public/img/i-pic-02.jpg
  29. BIN
      custom/public/img/i-pic-03.jpg
  30. BIN
      custom/public/img/i-pic-04.jpg
  31. +1
    -0
      custom/public/img/logo-footer.svg
  32. +45
    -0
      custom/public/img/logo-w-origin.svg
  33. +1
    -1
      custom/public/img/logo-w.svg
  34. BIN
      custom/public/img/ranking_list.jpg
  35. +1
    -0
      custom/public/js/placeholder-home.js
  36. +1
    -0
      custom/public/js/placeholder.js
  37. BIN
      custom/public/rotation3D/img/baseLogo.png
  38. +1250
    -0
      custom/public/rotation3D/img/baseLogo.svg
  39. BIN
      custom/public/rotation3D/img/baseMap.png
  40. BIN
      custom/public/rotation3D/img/baseimg.png
  41. +1
    -0
      custom/public/rotation3D/img/brain.svg
  42. BIN
      custom/public/rotation3D/img/idc-green.png
  43. BIN
      custom/public/rotation3D/img/idc-red.png
  44. BIN
      custom/public/rotation3D/img/idc-yellow.png
  45. +2
    -0
      custom/public/rotation3D/jquery-3.5.0.min.js
  46. +128
    -0
      custom/public/rotation3D/rotation3D.css
  47. +380
    -0
      custom/public/rotation3D/rotation3D.js
  48. +6
    -0
      custom/public/rotation3D/vue-2.6.10.min.js
  49. +13
    -0
      custom/public/swiper/swiper-bundle.min.css
  50. +13
    -0
      custom/public/swiper/swiper-bundle.min.js
  51. +24
    -12
      go.mod
  52. +101
    -58
      go.sum
  53. +688
    -0
      index.html
  54. +1
    -1
      integrations/pull_update_test.go
  55. +155
    -0
      models/action.go
  56. +165
    -1
      models/action_list.go
  57. +49
    -0
      models/admin_operate_log.go
  58. +387
    -36
      models/ai_model_manage.go
  59. +106
    -14
      models/attachment.go
  60. +181
    -0
      models/badge.go
  61. +94
    -0
      models/badge_category.go
  62. +159
    -0
      models/badge_user.go
  63. +37
    -0
      models/base_message.go
  64. +1743
    -96
      models/cloudbrain.go
  65. +597
    -0
      models/cloudbrain_image.go
  66. +136
    -0
      models/cloudbrain_spec.go
  67. +490
    -0
      models/cloudbrain_static.go
  68. +68
    -0
      models/cloudbrain_temp.go
  69. +11
    -25
      models/custom_migrations.go
  70. +296
    -52
      models/dataset.go
  71. +15
    -14
      models/dataset_permission.go
  72. +88
    -0
      models/dataset_reference.go
  73. +77
    -0
      models/dataset_star.go
  74. +203
    -0
      models/dbsql/dataset_foreigntable_for_es.sql
  75. +227
    -0
      models/dbsql/issue_foreigntable_for_es.sql
  76. +549
    -0
      models/dbsql/repo_foreigntable_for_es.sql
  77. +317
    -0
      models/dbsql/user_foreigntable_for_es.sql
  78. +24
    -0
      models/error.go
  79. +75
    -5
      models/file_chunk.go
  80. +22
    -0
      models/helper.go
  81. +13
    -9
      models/helper_environment.go
  82. +62
    -1
      models/issue.go
  83. +2
    -0
      models/issue_comment.go
  84. +184
    -0
      models/limit_config.go
  85. +20
    -0
      models/list_options.go
  86. +8
    -0
      models/login_source.go
  87. +43
    -0
      models/models.go
  88. +113
    -2
      models/org.go
  89. +147
    -0
      models/point_account.go
  90. +21
    -0
      models/point_account_log.go
  91. +532
    -51
      models/repo.go
  92. +46
    -3
      models/repo_activity_custom.go
  93. +14
    -0
      models/repo_collaboration.go
  94. +1
    -0
      models/repo_generate.go
  95. +60
    -19
      models/repo_list.go
  96. +85
    -36
      models/repo_statistic.go
  97. +41
    -5
      models/repo_tag.go
  98. +57
    -7
      models/repo_watch.go
  99. +351
    -0
      models/resource_queue.go
  100. +329
    -0
      models/resource_scene.go

+ 2
- 0
.gitignore View File

@@ -55,6 +55,8 @@ coverage.all
!/custom/conf/templates
/custom/conf/app.ini
!/custom/conf/app.ini.sample
/custom/public/kanban
/custom/public/annual-report
/data
/indexers
/log


+ 4
- 0
.prettierignore View File

@@ -0,0 +1,4 @@
# Ignore artifacts:
dist
build
coverage

+ 1
- 0
.prettierrc.json View File

@@ -0,0 +1 @@
{}

+ 18
- 3
Makefile View File

@@ -85,8 +85,10 @@ LDFLAGS := $(LDFLAGS) -X "main.MakeVersion=$(MAKE_VERSION)" -X "main.Version=$(G

GO_PACKAGES ?= $(filter-out code.gitea.io/gitea/integrations/migration-test,$(filter-out code.gitea.io/gitea/integrations,$(shell $(GO) list -mod=vendor ./... | grep -v /vendor/)))

WEBPACK_SOURCES := $(shell find web_src/js web_src/less -type f)
WEBPACK_SOURCES := $(shell find web_src/js web_src/less web_src/vuepages -type f)
WEBPACK_CONFIGS := webpack.config.js
WEBPACK_CONFIGS_TMP := webpack_temp.config.js
WEBPACK_CONFIGS_PRO := webpack_pro.config.js
WEBPACK_DEST := public/js/index.js public/css/index.css
WEBPACK_DEST_DIRS := public/js public/css public/fonts

@@ -290,7 +292,7 @@ lint-backend: golangci-lint revive vet swagger-check swagger-validate test-vendo

.PHONY: lint-frontend
lint-frontend: node_modules
npx eslint web_src/js webpack.config.js
npx eslint web_src/js $(WEBPACK_CONFIGS)
npx stylelint web_src/less

.PHONY: watch-frontend
@@ -504,7 +506,7 @@ install: $(wildcard *.go)
build: frontend backend

.PHONY: frontend
frontend: node-check $(FOMANTIC_DEST) $(WEBPACK_DEST)
frontend: node-check webpack_prepare $(FOMANTIC_DEST) $(WEBPACK_DEST) webpack_end

.PHONY: backend
backend: go-check generate $(EXECUTABLE)
@@ -596,6 +598,19 @@ $(FOMANTIC_DEST): $(FOMANTIC_CONFIGS) package-lock.json | node_modules

.PHONY: webpack
webpack: $(WEBPACK_DEST)
webpack_prepare:
if [ $(filter bindata,$(TAGS_SPLIT))x = "bindata"x ]; then \
echo "prepare"; \
cp $(WEBPACK_CONFIGS) $(WEBPACK_CONFIGS_TMP); \
cp $(WEBPACK_CONFIGS_PRO) $(WEBPACK_CONFIGS); \
fi

webpack_end:
if [ $(filter bindata,$(TAGS_SPLIT))x = "bindata"x ]; then \
echo "end"; \
mv $(WEBPACK_CONFIGS_TMP) $(WEBPACK_CONFIGS); \
fi

$(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json | node_modules
rm -rf $(WEBPACK_DEST_DIRS)


+ 16
- 7
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.11.1-blue)](https://git.openi.org.cn/OpenI/aiforge/releases/latest)
[![release](https://img.shields.io/badge/release-1.21.11.1-blue)](https://openi.pcl.ac.cn/OpenI/aiforge/releases/latest)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)


@@ -10,7 +10,7 @@

启智AI开发协作平台是一个在线Web应用,旨在为人工智能算法、模型开发提供在线协同工作环境,它提供了<b>代码托管、数据集管理与共享、免费云端算力资源支持(GPU/NPU)、共享镜像</b>等功能。

[启智AI开发协作平台](https://git.openi.org.cn) 是使用本项目构建的在线服务,您可以直接点击链接访问试用。
[启智AI开发协作平台](https://openi.pcl.ac.cn) 是使用本项目构建的在线服务,您可以直接点击链接访问试用。

本项目是基于[Gitea](https://github.com/go-gitea/gitea)发展而来的,我们对其进行了Fork并基于此扩展了人工智能开发中需要的功能,如数据集管理和模型训练等。对于和代码托管相关的功能,您可以参考[Gitea的文档](https://docs.gitea.io/zh-cn/)。

@@ -20,7 +20,7 @@
后端服务涵盖了AI模型开发流水线,包括代码协同开发、数据管理、模型调试、训练、推理和部署等(*目前尚未支持模型部署*)。在不同的开发阶段,我们还将提供丰富的开发工具供用户使用,如数据标注、数据筛选、模型转换、模型压缩、代码检测等。我们也欢迎社区提供更多丰富的工具接入,提高利用平台进行开发的效率。
![系统架构图](assets/架构图.png)
## 在线服务使用
本项目的在线服务平台的详细使用帮助文档,可参阅本项目[百科](https://git.openi.org.cn/OpenI/aiforge/wiki)内容。
本项目的在线服务平台的详细使用帮助文档,可参阅本项目[百科](https://openi.pcl.ac.cn/OpenI/aiforge/wiki)内容。
- 如何创建账号
- 如何创建组织及管理成员权限
- 如何创建项目仓库
@@ -39,13 +39,22 @@
[从源代码安装说明](https://docs.gitea.io/zh-cn/install-from-source/)

## 授权许可
本项目采用 MIT 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://git.openi.org.cn/OpenI/aiforge/src/branch/develop/LICENSE) 文件中。
本项目采用 MIT 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://openi.pcl.ac.cn/OpenI/aiforge/src/branch/develop/LICENSE) 文件中。


## 需要帮助?
如果您在使用或者开发过程中遇到问题,可以在以下渠道咨询:
- 点击[这里](https://git.openi.org.cn/OpenI/aiforge/issues)在线提交问题(点击页面右上角绿色按钮**创建任务**)
- 点击[这里](https://openi.pcl.ac.cn/OpenI/aiforge/issues)在线提交问题(点击页面右上角绿色按钮**创建任务**)
- 加入微信群实时交流,获得进一步的支持
<img src="https://git.openi.org.cn/OpenI/aiforge/wiki/raw/img/wechatgroup.jpg" width=200px />
<img src="https://openi.pcl.ac.cn/OpenI/aiforge/wiki/raw/img/wechatgroup.jpg" width=200px />

## 启智社区小白训练营:
- 结合案例给大家详细讲解如何使用社区平台,帮助无技术背景的小白成长为启智社区达人 (https://git.openi.org.cn/zeizei/OpenI_Learning)
- 结合案例给大家详细讲解如何使用社区平台,帮助无技术背景的小白成长为启智社区达人 (https://openi.pcl.ac.cn/zeizei/OpenI_Learning)

## 平台引用
如果本平台对您的科研工作提供了帮助,可在论文致谢中加入:
英文版:```Thanks for the support provided by OpenI Community (https://openi.pcl.ac.cn).```
中文版:```感谢启智社区提供的技术支持(https://openi.pcl.ac.cn)。```

如果您的成果中引用了本平台,也欢迎在下述开源项目中提交您的成果信息:
https://openi.pcl.ac.cn/OpenIOSSG/references

+ 16
- 0
cmd/serv.go View File

@@ -6,6 +6,7 @@
package cmd

import (
"code.gitea.io/gitea/services/repository"
"encoding/json"
"fmt"
"net/http"
@@ -208,6 +209,21 @@ func runServ(c *cli.Context) error {
os.Setenv(models.ProtectedBranchPRID, fmt.Sprintf("%d", 0))
os.Setenv(models.EnvIsDeployKey, fmt.Sprintf("%t", results.IsDeployKey))
os.Setenv(models.EnvKeyID, fmt.Sprintf("%d", results.KeyID))
//set environment for pre-receive hook script
if verb == "git-receive-pack" {
os.Setenv(models.EnvRepoMaxFileSize, fmt.Sprint(setting.Repository.Upload.FileMaxSize))
os.Setenv(models.EnvRepoMaxSize, fmt.Sprint(setting.Repository.RepoMaxSize))
os.Setenv(models.EnvPushSizeCheckFlag, fmt.Sprint(setting.Repository.Upload.ShellFlag))
if setting.Repository.Upload.ShellFlag == repository.SHELL_FLAG_ON {
env, _ := private.GetHookConfig(username, reponame)
if env != nil && len(env) > 0 {
repoSize := env[models.EnvRepoSize]
if repoSize != "" {
os.Setenv(models.EnvRepoSize, repoSize)
}
}
}
}

//LFS token authentication
if verb == lfsAuthenticateVerb {


+ 11
- 0
custom/conf/app.ini.sample View File

@@ -11,6 +11,11 @@ RUN_USER = git
; Either "dev", "prod" or "test", default is "dev"
RUN_MODE = dev

; Is show MLOps
MLOPS = true
; If the MLOPS host is not set, the default setting is AppSubUrl
MLOPS_HOST = https://dev-1-37.apulis.com.cn

[repository]
ROOT =
SCRIPT_TYPE = bash
@@ -1141,3 +1146,9 @@ growth_issue=0.2
growth_contributors=0.2
growth_commit=0.2
growth_comments=0.2


[grampus]
USERNAME =
PASSWORD =
SERVER_HOST =

+ 0
- 0
custom/public/RemixIcon_Fonts_v2.5.0/fonts/.keep View File


+ 0
- 0
custom/public/RemixIcon_Fonts_v2.5.0/fonts/.keep 3 View File


+ 2317
- 0
custom/public/RemixIcon_Fonts_v2.5.0/fonts/remixicon.css
File diff suppressed because it is too large
View File


BIN
custom/public/RemixIcon_Fonts_v2.5.0/fonts/remixicon.eot View File


+ 1
- 0
custom/public/RemixIcon_Fonts_v2.5.0/fonts/remixicon.glyph.json
File diff suppressed because it is too large
View File


+ 2319
- 0
custom/public/RemixIcon_Fonts_v2.5.0/fonts/remixicon.less
File diff suppressed because it is too large
View File


+ 6835
- 0
custom/public/RemixIcon_Fonts_v2.5.0/fonts/remixicon.svg
File diff suppressed because it is too large
View File


+ 11356
- 0
custom/public/RemixIcon_Fonts_v2.5.0/fonts/remixicon.symbol.svg
File diff suppressed because it is too large
View File


BIN
custom/public/RemixIcon_Fonts_v2.5.0/fonts/remixicon.ttf View File


BIN
custom/public/RemixIcon_Fonts_v2.5.0/fonts/remixicon.woff View File


BIN
custom/public/RemixIcon_Fonts_v2.5.0/fonts/remixicon.woff2 View File


+ 299
- 26
custom/public/css/git.openi.css View File

@@ -24,11 +24,27 @@
.am-pt-20{ padding-top: 2.0rem !important;}
.am-pt-30{ padding-top: 3.0rem !important;}
.am-pl-30{ padding-left: 3.0rem !important;}
.am-pl-50{ padding-left: 5.0em !important;}
.am-ml-10{ margin-left: 1.0rem !important;}
.am-ml-30{ margin-left: 3.0rem !important;}
.am-pr-30{ padding-right: 3.0rem !important;}
.am-mr-30{ margin-right: 3.0rem !important;}
.am-lh-18{ line-height: 1.8em;}

.nowrap{
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.nowrap-2 {
max-height: 2.837em;
line-height: 1.4285em;
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}

.opacity5{ opacity:0.5;}
.radius15{ border-radius:1.5rem !important; }
.radius10{ border-radius:1.0rem !important; }
@@ -42,16 +58,30 @@
box-shadow: 0 2px 4px 0 rgba(34,36,38,.3);
}

.ui.blue {
color: #0366d6;
}

.ui.secondary.hometop.segment{
background: #DFE9F0;
padding-top: 0;
border: none;
margin-bottom: 90px;
margin-bottom: 11em;
}
.ui.secondary.hometop.segment #navbar{
z-index: 10;
}
.ui.secondary.c2net.segment{
padding-bottom: 3em;
padding-top: 2em;
color: rgba(0,0,0,.87);
background-image: linear-gradient(to bottom left,var(--tw-gradient-stops));
--tw-gradient-from: #f5f3ff;
--tw-gradient-stops: var(--tw-gradient-from),#fff,var(--tw-gradient-to,hsla(0,0%,100%,0));
--tw-gradient-to: rgba(219,234,254,0.4);
border-top: 1px solid rgba(243,244,246,1)!important;
border-bottom: 1px solid rgba(243,244,246,1)!important;
}

.hometop .ui.secondary.menu .active.item{
color: #000;
@@ -68,6 +98,7 @@
.homebanner{
position: relative;
padding: 100px 32px 80px;
padding-bottom: 0;
z-index: 9;
}
.homebanner .ui.header .sub.header{
@@ -75,10 +106,15 @@
}
.bannerpic{
position: absolute;
right: 50px;
right: 0px;
bottom: -64px;
width: 560px;
z-index: 6;
}
.homebanner .ui.button{
font-weight: normal;
}

.ui[class*="very padded"].segment.i-code{
padding-left: 6.0rem;
}
@@ -109,53 +145,254 @@
.i-code-pic > img{
margin-bottom: -3.0rem;
}
.i-env .ui.cards>.card{
box-shadow:none;
}
.i-env .ui.cards>.card>.image{
background: none;
width: 60%;
margin: auto;
}
.i-env .ui.cards>.card>.content{
border-top: none;
}
.leftline01{

#homenews{
position: relative;
z-index: 9;
bottom: -6em;
}
#homenews > p{
color: #BBBBBB;
margin-left: 2.3em;
}
.homenews{
border-radius: 2em;
background-color: rgba(16, 16, 16, .9);
position: relative;
padding-left: 2.3em !important;
}
.homeorg, .homepro, .homemodel, .i-env{
position: relative;
padding-bottom: 3em;
}
.homenews::before{
content: '';
position: absolute;
left: 3.0rem;
left: 3em;
top: 0;
bottom: 0;
border-left: 2px solid #505559;
border-bottom: 2px solid #505559;
border-radius: 0 0 0 2.0rem;
width: 2.0rem;
background-color: rgba(105, 192, 255, .4);
width: 2px;
}
.homenews .time-since{
padding-left: 1em;
color: #888888;
}
.homenews a{
color: #69C0FF;
}
.homenews .ui.list>.item>.content{
color: #E8E8E8;
line-height: 1.8em;
width: calc(100% - 3.75em) !important;
}
.homenews .ui.list>.item{
padding: 0;
}
.newslist{
height: 260px;
overflow: hidden;
}
.leftline02{

.leftline01, .leftline03, .leftline04{
position: absolute;
width: 5em;
border-radius: 0 0 0 4.0em;
border-left: 2px solid #3291F8;
border-bottom: 2px solid #3291F8;
top: -5em;
bottom: 0;
left: 2em;
z-index: 6;
}
.leftline02, .leftline02-2 {
position: absolute;
left: 7.0em;
top: -2px;
bottom: 2px;
height: auto;
border-top: 2px solid #3291F8;
border-right: 2px solid #3291F8;
border-radius: 0 4.0em 0 0;
width: 10em;
z-index: 6;
}
.leftline02::after, .leftline02-2::after{
content: '';
position: absolute;
left: 5rem;
top: calc(-5.0rem - 2px);
border-top: 2px solid #505559;
border-right: 2px solid #505559;
border-radius: 0 2.0rem 0 0;
width: 17.5rem;
height: 6.0rem;
transform: translate(55%,50%);
right: 0;
bottom: 0;
width: 1.0em;
height: 1.0em;
background-color: #FFF;
border: 2px solid #3291F8;
border-radius: 1em;
}
.leftline03{
border-radius: 4.0em 0 0 0;
border-top: 2px solid #3291F8;
border-bottom:none;
top: -2.0em;
}
.leftline02-2{
border-color: rgba(105, 192, 255, .4);
width: 7em;
z-index: 5;
}
.leftline02-2::after{
border-color: rgba(105, 192, 255, .4);
}
.leftline04{
border-radius: 0;
border-top: none;
border-bottom: none;
}

.homeorg-tit{
padding-left: 5em !important;
}
.homeorg-tit::after{
content: '';
position: absolute;
width: 1.6em;
height: 1.6em;
background-color: #FFF;
border: 2px solid #3291F8;
left: 2.3em;
top: 1.3em;
border-radius: 1em;
z-index: 9;
}
.homeorg-list .card{
/* background-image: linear-gradient(#FFF, #FFF 60%, #DFF0EF) !important; */
box-shadow: none !important;
}
.homeorg-list .card .ui.small.header .content{
width: calc(100% - 3.75em);
}
.homepro-tit{
z-index: 9;
position: relative;
}
.homepro-list, .homeorg-list{
position: relative;
z-index: 9;
padding: 1.0em 1.0em 3.0em;
overflow: hidden;
}
.homepro-list .ui.card{
border-radius: 15px;
background-color: #FFF;
box-shadow: 0px 5px 10px 0px rgba(105, 192, 255, .3);
border: 1px solid rgba(105, 192, 255, .4);
/* min-height: 10.8em; */
}
.homepro-list .ui.card>.content>.header{
line-height: 40px !important;
}

.homepro-list .swiper-pagination-bullet-active, .homeorg-list .swiper-pagination-bullet-active{
width: 40px;
border-radius: 4px;
}
.i-env > div{
position: relative;
}
.event-list .ui.card>.image>img{
height: 146px !important;
}

@media only screen and (max-width: 767px) {
.mobile-margin-left-20 {
margin-left: 20px !important;
}
.mobile-text-align-center {
text-align: center !important;
}
.mobile-justify-content-center {
justify-content: center !important;
}
.am-mt-30{ margin-top: 1.5rem !important;}
.ui.secondary.hometop.segment{
margin-bottom: 2.0rem;
margin-bottom: 5.0rem;
}
.bannerpic, .i-code-pic{
.bannerpic{
display: none;
}
.i-code h2::before {
left: calc(-5.0rem + 6px);
#homenews{
bottom: -3em;
}
.i-code h2.am-bw::before{
left: calc(-4.0rem + 6px);
#homenews > p {
margin-left: 1.0em;
}
.homenews{
padding-left: 1.3em !important;
border-radius: 1.5em;
}
.homenews::before{
left: 2em;
}
.homepro-tit > p{
background: #FFF;
}
.homeorg{
/* padding-left: 3.5em; */
}
.homeorg-tit::after {
left: -2.3em;
}
.homeorg-list{
margin: 0 0 2.0em !important;
}
.homeorg-list > .column{
width: 3em !important;
margin-left: -0.5em;
padding: 0.5rem 0 0 !important;
}
.homeorg-list .card{
background: none !important;
}
.homeorg-list .card > .content{
padding: 0 !important;
}
.homeorg-list > .column .card .ui.header>img{
width: 3.0em;
height: 3.0em;
border-radius: 2.0em;
border: 2px solid #FFF;
}
.homeorg-list > .column .card .ui.header > .content{
display: none;
}
.leftline01{
width: calc(50% - 4.0rem);
width: 4.0em;
bottom: 4em;
border-radius: 0 0 0 3.0em;
}
.leftline02, .leftline02-2{
left: 6.0em;
top: calc(-4.0em - 2px);
border-radius: 0 3.0em 3.0em 0;
width: calc(50% - 6.0em);
}
.leftline02{
left: calc(50% - 1.0rem);
top: calc(-3.5rem - 2px);
.leftline02-2 {
width: calc(50% - 8.0em);
}
.i-env .ui.cards>.card>.content .description{
display: none;
}
}

@@ -181,4 +418,40 @@

@media only screen and (min-width: 1920px) {

}

/* rotation3D */
#app{
width: 800px;
margin: 0 auto;
}
.rotation3D-baseMap{
position: absolute; left: 0; right: 0; top: 270px; margin: auto;
width: 800px; height: 516px;
background: url("../rotation3D/img/baseMap.png") no-repeat;
background-size: cover;
}
.rotation3D-baseMap::before{
position: absolute;
margin: auto; z-index: 99;
left:50%; top: -150px;
transform:translate(-50%,0);
width: 342px; height: 470px; display: block; content: '';
background: url("../rotation3D/img/baseLogo.svg");
/*animation: 10s bounceUpDown infinite;*/
}
.rotation3D-baseMap::after{
position: absolute;
margin: auto; z-index: 100;
left:50%; top:0;
transform:translate(-50%,0);
width: 110px; height: 86px; display: block; content: '';
background: url("../rotation3D/img/brain.svg");
animation: 6s bounceUpDown infinite;
mix-blend-mode: color-dodge;
}
@keyframes bounceUpDown{
0% {transform: translate(-50%, 0px);}
50% {transform: translate(-50%, -15px);}
100% {transform: translate(-50%, 0px);}
}

+ 1
- 0
custom/public/css/placeholder-home.css View File

@@ -0,0 +1 @@
/* placeholder-home.css */

+ 1
- 0
custom/public/css/placeholder.css View File

@@ -0,0 +1 @@
/* placeholder.css */

BIN
custom/public/img/home-banner-01-en.jpg View File

Before After
Width: 1920  |  Height: 580  |  Size: 176 KiB

BIN
custom/public/img/home-banner-01.jpg View File

Before After
Width: 1920  |  Height: 580  |  Size: 170 KiB

BIN
custom/public/img/home-banner-02-1.jpg View File

Before After
Width: 1920  |  Height: 548  |  Size: 152 KiB

BIN
custom/public/img/home-banner-02-2.png View File

Before After
Width: 790  |  Height: 315  |  Size: 247 KiB

BIN
custom/public/img/home-bg-ps.png View File

Before After
Width: 681  |  Height: 800  |  Size: 60 KiB

BIN
custom/public/img/i-pic-01.jpg View File

Before After
Width: 266  |  Height: 178  |  Size: 12 KiB

BIN
custom/public/img/i-pic-02.jpg View File

Before After
Width: 266  |  Height: 178  |  Size: 11 KiB

BIN
custom/public/img/i-pic-03.jpg View File

Before After
Width: 266  |  Height: 178  |  Size: 13 KiB

BIN
custom/public/img/i-pic-04.jpg View File

Before After
Width: 266  |  Height: 178  |  Size: 16 KiB

+ 1
- 0
custom/public/img/logo-footer.svg
File diff suppressed because it is too large
View File


+ 45
- 0
custom/public/img/logo-w-origin.svg View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 26.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 160 64" style="enable-background:new 0 0 160 64;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
.st1{display:none;fill:#FFFFFF;}
</style>
<g>
<path class="st0" d="M105.3,33.3H87.1c-2.6,0.1-4,1.3-4,3.8v8.3c0.1,2.2,1.5,3.3,4,3.5h18c2.5-0.1,3.8-1.3,3.9-3.6v-8.2
c0.1-2-1.4-3.6-3.4-3.8C105.6,33.3,105.4,33.3,105.3,33.3z M104.6,43.9c-0.1,1-0.7,1.5-1.9,1.7H89.3c-1.3-0.3-1.8-0.7-1.9-1.7v-5.4
c0-1,0.8-1.9,1.8-1.9c0,0,0.1,0,0.1,0h13.1c1.1,0,2,0.8,2.1,2L104.6,43.9z"/>
<path class="st0" d="M81,25.3v-4.7c0-1.1,0.9-2.1,2.1-2.1h19c1.4-0.1,2.1,0.5,2.1,1.8v3.3c0,1.1-0.7,1.7-1.9,1.7H82.8v3.2H105
c2.3,0,3.6-1.1,3.6-3.2v-6.4c0.2-1.8-1.1-3.4-2.8-3.6c-0.3,0-0.5,0-0.8,0h-9.7v-2.6h-4.6v2.6H80.3c-2.6,0-3.9,1.2-3.9,3.9v12.3
c0,5.8-0.9,11.6-2.6,17.1l4.3,0.8c1.8-5.5,2.7-11.3,2.8-17.1v-7.2H81z"/>
<path class="st0" d="M116.2,30.4l4.4,2.4c2.6-1.9,4.4-4.6,5.1-7.6h7.8v-3.2h-7.1c0.2-1.3,0.3-2.6,0.3-3.9h6.6v-3.2h-12.3v-2h-4.6
V18h5.8c0,1.3-0.1,2.6-0.3,3.9h-6.7v3.3h5.8C120.4,27.5,118.6,29.4,116.2,30.4z"/>
<path class="st0" d="M126.5,26.7c1.2,1.8,2.1,3.8,2.9,5.8h4.9c-0.8-2-1.8-4-2.9-5.8H126.5z"/>
<path class="st0" d="M145.4,33.5h-24.7c-2.4,0.3-3.8,1.4-3.9,3.5v6.2h28.4v1.3c0,1.1-0.7,1.7-2.1,1.7h-19.7c-1.3,0-1.9-0.5-1.9-1.5
h-4.5V46c0,1.9,1.6,3.5,3.5,3.6h25.1c2.6-0.1,4-1.3,4-3.5v-9C149.2,35,147.5,33.5,145.4,33.5z M145,39.9h-23.7v-1.1
c0-1,0.8-1.9,1.8-1.9c0,0,0.1,0,0.1,0H143c1.3,0,2.1,0.7,2.1,1.9L145,39.9z"/>
<path class="st0" d="M147.3,14h-8.2c-2.2,0.1-3.5,1.1-3.8,2.9v11.7c0.1,1.6,1.4,2.9,3.1,2.9h8.7c1.9-0.1,3.1-1.1,3.1-2.9V17
C150.4,15.4,149.4,14.3,147.3,14z M146,26.2c0,1.4-0.7,2.1-1.9,2.1h-2.4c-1,0-1.8-0.8-1.8-1.8c0-0.1,0-0.1,0-0.2v-7
c0-1,0.8-1.9,1.8-1.9c0,0,0.1,0,0.1,0h2c1.1,0,2.1,0.8,2.1,1.9l0,0L146,26.2z"/>
</g>
<path class="st1" d="M67.2,44.1V20c0-2.6-1.4-5.1-3.7-6.4l-20.9-12c-2.3-1.3-5.1-1.3-7.4,0l-20.9,12c-2.3,1.3-3.7,3.8-3.7,6.4v24.1
c0,2.6,1.4,5.1,3.7,6.4l20.9,12c2.3,1.3,5.1,1.3,7.4,0l20.9-12C65.8,49.2,67.2,46.7,67.2,44.1z"/>
<path class="st0" d="M61.9,15.4L42,3.9c-1.9-1.1-4.3-1.1-6.2,0L15.9,15.4c-1.9,1.1-3.1,3.2-3.1,5.4v22.9c0,2.2,1.2,4.3,3.1,5.4
l3.8,2c0.8,0.4,0.8,1,0.8,1.9c0,0,0,0.1,0,0.1c0.1,1.6,1.8,3.5,4.2,3.5c2.3,0,4.3-1.9,4.4-4.2c0-1.6-0.8-3.1-2.3-3.9
c-0.6-0.3-1.7-0.5-2.9-0.4c-0.9,0.1-1,0.6-2.8-0.5l-2.8-1.6c-0.8-0.5-1.7-1.3-1.6-2.3V22c0-1.8,0.8-2.8,2.1-3.5L37,7.8
C38.1,7,40.2,7.2,41.5,8l16.4,9.5c2.9,1.6,3,3,3,4v19.7c0,2.3-1.5,4-2.3,4.5l-15.1,8.5C42.8,54.5,42,54,42,53.3l0-2.5
c0-0.1,0-0.3,0-0.4v-2.5c0-0.8,0.4-1.6,1.1-2l8.8-5.7c1.4-0.9,2-1.7,2-3.8L53.7,28c0-1,0.4-1.9,1.2-2.5c1.5-1.3,2-3.7,0.8-5.6
c-0.8-1.4-2.4-2.2-4-2c-1.6,0.1-2.8,1-3.5,2.4c-0.5,1-0.6,2.2-0.3,3.3c0.2,0.8,0.7,1.4,1.3,2c0.7,0.6,1.2,1.5,1.2,2.5v7.2
c0,1.1-0.5,2.1-1.5,2.7l-5.5,2.8c-0.7,0.5-1.6,0-1.6-0.8l-0.2-17.1c0-1,0.5-1.9,1.2-2.5c0.4-0.3,0.7-0.8,1-1.3
c0.7-1.3,0.7-2.9-0.1-4.3c-0.8-1.4-2.5-2.2-4.1-2.1c-2.9,0.3-4.6,3.1-3.8,5.7c0.2,0.8,0.7,1.4,1.3,2c0.7,0.6,1.2,1.5,1.2,2.5v9.9
c0,0.8-0.8,1.3-1.5,0.9L34,32.5c-0.7-0.4-1.2-1.2-1.2-2.1v-2.2c0-1,0.4-1.9,1.2-2.5c1.5-1.3,2-3.7,0.8-5.6c-0.8-1.3-2.4-2.1-4-2
c-2.9,0.2-4.7,3.1-3.8,5.7c0.2,0.8,0.7,1.5,1.3,2c0.7,0.6,1.2,1.5,1.2,2.5l0,3.5c0,1,0,2,1.6,3.1l5.5,3c0.7,0.4,1.2,1.2,1.2,2.1v4.5
c0,0.8-0.8,1.3-1.5,0.9L26.6,41c-0.7-0.4-1.2-1.2-1.2-2.1l-0.2-6.1c0-1,0.4-1.8,1.1-2.5c1.4-1.3,1.9-3.4,0.9-5.3
c-0.8-1.4-2.4-2.3-4-2.2c-2.9,0.2-4.6,3-3.8,5.6c0.2,0.7,0.7,1.4,1.2,1.9c0.7,0.6,1.1,1.5,1.1,2.5l-0.5,6.8c0,1.9,0.3,2.8,1.8,3.5
l11.8,6.3c1,0.6,1.6,1.7,1.6,2.8l0,3.1c0,3.1,4.3,5.7,8.7,3.3l16.4-9.8c1.9-1,3.2-3,3.4-5.2V20.7C65,18.5,63.8,16.5,61.9,15.4z
M24.2,50.7c1.1,0,2,0.9,2,2c0,1.1-0.9,2-2,2c-1.1,0-2-0.9-2-2C22.2,51.7,23.1,50.7,24.2,50.7z M48.7,21.5c0-1.1,0.9-2,2-2
s2,0.9,2,2c0,1.1-0.9,2-2,2S48.7,22.6,48.7,21.5z M30.6,23.8c-1.1,0-2-0.9-2-2s0.9-2,2-2c1.1,0,2,0.9,2,2S31.8,23.8,30.6,23.8z
M22.2,26.4c0-1.1,0.9-2,2-2c1.1,0,2,0.9,2,2s-0.9,2-2,2C23.1,28.4,22.2,27.5,22.2,26.4z M40,17.9c-1.1,0-2-0.9-2-2c0-1.1,0.9-2,2-2
c1.1,0,2,0.9,2,2C42,17,41.1,17.9,40,17.9z"/>
</svg>

+ 1
- 1
custom/public/img/logo-w.svg
File diff suppressed because it is too large
View File


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

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

+ 1
- 0
custom/public/js/placeholder-home.js View File

@@ -0,0 +1 @@
/* placeholder-home.js */

+ 1
- 0
custom/public/js/placeholder.js View File

@@ -0,0 +1 @@
/* placeholder.js */

BIN
custom/public/rotation3D/img/baseLogo.png View File

Before After
Width: 342  |  Height: 470  |  Size: 86 KiB

+ 1250
- 0
custom/public/rotation3D/img/baseLogo.svg View File

@@ -0,0 +1,1250 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 342 470" style="enable-background:new 0 0 342 470;" xml:space="preserve">
<style type="text/css">
.st0{fill:url(#SVGID_1_);}
.st1{fill:url(#Shape_00000122695819581411568980000009497889965072766366_);}
.st2{opacity:0.1;}
.st3{fill:none;stroke:#FFFFFF;stroke-width:0.28;}
.st4{fill:url(#Shape-3_00000108267555528354346850000003067232586542845062_);}
.st5{fill:url(#Shape-4_00000170962892825198117240000016507644302800111752_);}
.st6{fill:url(#Shape-5_00000152233876515447885360000003511306910976081557_);}
.st7{fill:url(#Shape-6_00000130648495190739671840000000046973013901678768_);}
.st8{opacity:0.3;}
.st9{fill:url(#Shape-8_00000067238065472127621960000006688745836265094844_);}
.st10{fill:url(#Shape-9_00000092456954712643526090000013987617450999243420_);}
.st11{fill:url(#Shape-10_00000071530715909169841760000001219864674538182321_);}
.st12{fill:url(#Shape-11_00000165230558524865277990000001984831809446982565_);}
.st13{fill:url(#Shape-12_00000156578567257238376930000013792221941525112461_);}
.st14{opacity:0.7;fill:url(#Shape-13_00000008126306159044654420000001950760845656686511_);enable-background:new ;}
.st15{fill:url(#Shape-14_00000016065736020663034800000004369015118147551141_);}
.st16{fill:url(#Shape-15_00000181064846143977355770000013065309429994795172_);}
.st17{fill:url(#Shape-16_00000031208842933427054440000012996998913138616501_);}
.st18{fill:url(#Shape-17_00000042004590833602562210000013828198405825221524_);}
.st19{fill:url(#Shape-18_00000162343186294817706670000009737502018486435718_);}
.st20{fill:url(#Shape-19_00000110467135021763194980000003285448470310495882_);}
.st21{fill:#FF9E00;}
.st22{fill:#FFFA00;}
.st23{fill:#FFD500;}
.st24{fill:#0670F4;}
.st25{fill:#0E36CA;}
.st26{fill:#0D46DA;}
.st27{fill:#00FCFF;}
.st28{fill:#C1FDFF;}
.st29{fill:#D5FDFF;}
.st30{fill:url(#SVGID_00000028292880454227640480000007941185209136214710_);}
.st31{fill:url(#SVGID_00000154385744779633976780000005015552716553969280_);}
.st32{fill:url(#SVGID_00000006709808785551018910000013165181589207510424_);}
.st33{fill:url(#SVGID_00000095338393378708966320000014319984612975200675_);}
.st34{fill:url(#SVGID_00000068668409858590940130000015473191643683796407_);}
.st35{fill:url(#SVGID_00000028288024236176209350000016805078944731773867_);}
.st36{opacity:0.9;fill:url(#SVGID_00000052083955581105786130000014221709174240235702_);enable-background:new ;}
.st37{opacity:0.9;fill:url(#SVGID_00000054967982296223834670000016083193669441212831_);enable-background:new ;}
.st38{opacity:0.9;fill:url(#SVGID_00000058550955804546596240000010683206481702016433_);enable-background:new ;}
.st39{opacity:0.9;fill:url(#SVGID_00000163043534529015062880000009083868386549199238_);enable-background:new ;}
.st40{opacity:0.7;}
.st41{fill:url(#SVGID_00000022541865909815407100000001966414287944324258_);}
.st42{fill:url(#SVGID_00000147900851745622378370000015873506469329059251_);}
.st43{fill:url(#SVGID_00000145055013217083447570000004918368404700113808_);}
.st44{opacity:0.9;fill:url(#SVGID_00000078738074373176931370000005247352960480890810_);enable-background:new ;}
.st45{opacity:0.9;fill:url(#SVGID_00000154427465430184299350000014008109346215955108_);enable-background:new ;}
.st46{opacity:0.9;fill:url(#SVGID_00000026130062154451319530000009591557964573573052_);enable-background:new ;}
.st47{opacity:0.9;fill:url(#SVGID_00000071535581989694575550000006424478893357759619_);enable-background:new ;}
.st48{fill:url(#SVGID_00000103264614985634555370000015439510514986295434_);}
.st49{fill:url(#SVGID_00000026846880724049010360000004318742928612435385_);}
.st50{fill:url(#SVGID_00000097491734475124487120000017280595404574408586_);}
.st51{opacity:0.9;fill:url(#SVGID_00000132797865532330196930000016589769163445796508_);enable-background:new ;}
.st52{opacity:0.9;fill:url(#SVGID_00000168112339364709499370000010561862793508189619_);enable-background:new ;}
.st53{opacity:0.9;fill:url(#SVGID_00000016776904099699711580000006027847166532311213_);enable-background:new ;}
.st54{opacity:0.9;fill:url(#SVGID_00000046319016626353319110000001507482231880905125_);enable-background:new ;}
.st55{fill:url(#SVGID_00000015347636505443995880000009639486512516552109_);}
.st56{fill:url(#SVGID_00000183235989598679666860000008675740694265734795_);}
.st57{fill:url(#SVGID_00000057145868332245323390000003648288391810730120_);}
.st58{opacity:0.9;fill:url(#SVGID_00000052089054271150011360000006380279030166986132_);enable-background:new ;}
.st59{opacity:0.9;fill:url(#SVGID_00000053524630414669161260000014167420226363617973_);enable-background:new ;}
.st60{opacity:0.9;fill:url(#SVGID_00000102509884178040304740000009453014021517178797_);enable-background:new ;}
.st61{opacity:0.9;fill:url(#SVGID_00000030477380050973659050000012784180663325317543_);enable-background:new ;}
.st62{fill:url(#SVGID_00000091715724373744771480000018381079266578924952_);}
.st63{fill:url(#SVGID_00000119802247028098950750000007807224728487343238_);}
.st64{fill:#DCCAFF;}
.st65{fill:url(#SVGID_00000168828291550228940780000008712892453049646482_);}
.st66{opacity:0.1;fill:url(#Shape-25_00000064352820151925633680000000538003253376652973_);enable-background:new ;}
.st67{opacity:0.1;fill:url(#Shape-26_00000058578484037833382700000001443243348334247842_);enable-background:new ;}
.st68{opacity:0.2;fill:url(#Shape-27_00000021087063130939724030000008785947539258868150_);enable-background:new ;}
.st69{opacity:0.4;fill:url(#Shape-28_00000139984370226791585180000007783386812744376985_);enable-background:new ;}
.st70{opacity:0.3;fill:url(#Shape-29_00000042729723539120537750000007554992902734426797_);enable-background:new ;}
.st71{opacity:0.8;fill:url(#Shape-30_00000116193469163751414410000014389267924888205229_);enable-background:new ;}
.st72{opacity:0.3;fill:url(#Shape-31_00000106850944086934364040000006754821160458469791_);enable-background:new ;}
.st73{opacity:0.9;}
.st74{fill:url(#SVGID_00000009548216845904308100000007854279399613521282_);}
.st75{fill:url(#SVGID_00000005946657920056642410000013858148092140242342_);}
.st76{fill:url(#SVGID_00000073713187741169993790000000518297220273631625_);}
.st77{fill:url(#SVGID_00000059309463566938012110000017242277441342374043_);}
.st78{opacity:0.2;fill:url(#Shape-33_00000092442636229672753120000015708534850319685025_);enable-background:new ;}
.st79{opacity:0.2;fill:url(#Shape-34_00000134958529537706583570000013531478499757925764_);enable-background:new ;}
.st80{opacity:0.3;fill:url(#Shape-35_00000183966536320522018980000015656474600764335774_);enable-background:new ;}
.st81{fill:url(#Shape-36_00000170990332344004291830000018267268661377741450_);}
.st82{fill:url(#Shape-37_00000081606422858984196810000010929270223930003892_);}
.st83{fill:url(#Shape-38_00000012458674817368296390000007645479619492313786_);}
.st84{fill:url(#Shape-39_00000116945922626526355790000011743352812616268682_);}
.st85{opacity:0.3;fill:url(#Shape-40_00000086677837877112201530000004355660159683157898_);enable-background:new ;}
.st86{fill:url(#Shape-41_00000181072257639661971490000013152471877230270889_);}
.st87{fill:url(#Shape-42_00000029013825415492016220000014598397158487279492_);}
.st88{opacity:0.2;fill:url(#Shape-43_00000057870855683888429600000006717933375613710484_);enable-background:new ;}
.st89{fill:url(#Shape-44_00000043445091589589969460000004866174366055113897_);}
.st90{fill:url(#SVGID_00000049914507342881612690000006947427049092657283_);}
.st91{fill:url(#SVGID_00000062886325047818555730000010750219106956600486_);}
.st92{fill:url(#SVGID_00000039855526018585779520000008358089142857900196_);}
.st93{fill:url(#SVGID_00000159453068813583809450000016564471048711535496_);}
.st94{fill:url(#SVGID_00000134221892980377044910000000824006706549954479_);}
.st95{fill:url(#Shape-45_00000121983145399346318480000017454074114138980021_);}
.st96{fill:url(#Shape-46_00000056413757225724440660000013227819678561858705_);}
.st97{fill:url(#SVGID_00000081614301598355860140000013115952882911281563_);}
.st98{fill:url(#SVGID_00000018926841404133431160000004363987530718928829_);}
.st99{fill:url(#SVGID_00000170973902844277814280000011391250150203830458_);}
.st100{fill:url(#SVGID_00000016781894814342093450000000425864608846005892_);}
.st101{fill:url(#SVGID_00000085245970713286330730000013740508628486946721_);}
.st102{fill:url(#SVGID_00000163055017777087957720000005454895562531643312_);}
.st103{fill:url(#SVGID_00000077302191023187935940000015456585497225833140_);}
.st104{fill:url(#SVGID_00000180339343354334441320000012833881165672081034_);}
.st105{fill:url(#SVGID_00000096033939601719065930000002030735641467470505_);}
.st106{opacity:0.4;fill:url(#Shape-48_00000094605520090581675820000009029972832137256374_);enable-background:new ;}
.st107{opacity:0.4;fill:url(#Shape-49_00000081627112398329943590000003802171881972280741_);enable-background:new ;}
.st108{opacity:0.4;}
.st109{fill:#FFFFFF;}
.st110{fill:url(#SVGID_00000023984037287111958640000000638087594766373052_);}
.st111{fill:url(#SVGID_00000065068286938501304560000001747638327604614276_);}
.st112{fill:url(#SVGID_00000118362122578489129280000008325696647302340015_);}
.st113{fill:url(#SVGID_00000063625050672048026430000010620158812897610639_);}
.st114{fill:url(#SVGID_00000160160255246035492840000011145811528394086550_);}
.st115{fill:url(#SVGID_00000137096698933832844710000001994592634361541522_);}
.st116{fill:url(#SVGID_00000028283920064565737470000017374870854956641424_);}
.st117{fill:url(#Shape-50_00000008114939600830930650000006400205404160199078_);}
.st118{fill:url(#Shape-51_00000165940789856988529460000001895047031287773092_);}
.st119{fill:url(#Shape-52_00000071536093562577904190000014204231495092591776_);}
.st120{fill:url(#SVGID_00000104667689609266550840000001590343809853163664_);}
.st121{fill:url(#SVGID_00000174565000182971844360000009855163291799870106_);}
.st122{fill:url(#SVGID_00000101801150018074089040000006999532880097025439_);}
.st123{fill:url(#SVGID_00000010285869994318200550000017907693555620668052_);}
.st124{fill:url(#SVGID_00000040553462087727254830000018074551645053105837_);}
.st125{fill:#FFCDA4;}
.st126{fill:#F2B588;}
.st127{fill:url(#SVGID_00000151537022301235923430000015311765200172483981_);}
.st128{fill:url(#SVGID_00000095318817484922321070000012112191509524194441_);}
.st129{opacity:0.3;fill:url(#SVGID_00000090282202284851546380000005436398103980201888_);enable-background:new ;}
.st130{fill:url(#SVGID_00000176044504512350597090000016535704918049643914_);}
.st131{fill:url(#SVGID_00000147205081033068325400000014263989530912263078_);}
.st132{fill:#0726A5;}
.st133{fill:#FFCBA2;}
</style>
<g id="图层_2_00000028314812550504214100000011184653534585739162_">
<g id="图层_1-2">
<radialGradient id="SVGID_1_" cx="168.6775" cy="1400.7551" r="197.2173" gradientTransform="matrix(1 0 0 1 0 -1216.3)" gradientUnits="userSpaceOnUse">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="0.4335" style="stop-color:#FFFFFF;stop-opacity:0.8"/>
<stop offset="0.81" style="stop-color:#D8E6FF;stop-opacity:0.12"/>
</radialGradient>
<path class="st0" d="M294.2,344.5l-1.8-2.3c-0.2-0.3-0.4-0.7-0.8-1c-16.1-21.4-48.4-55.7-48.4-111.1c0-68.9,4.3-157.7,24.4-231.9
H65.9c20.3,74.1,24.4,163,24.4,231.9c0,58.3-30.4,93.3-45.5,114.5h0.9c-0.3,0.9-0.4,1.8-0.4,2.7c0,16.9,55.7,30.6,124.5,30.6
s124.4-13.7,124.4-30.6c0-0.9-0.2-1.9-0.4-2.7L294.2,344.5z"/>
<g id="main_system">
<linearGradient id="Shape_00000178178190290349774510000015233560893010072992_" gradientUnits="userSpaceOnUse" x1="183.1534" y1="-517.9744" x2="328.6692" y2="-492.206" gradientTransform="matrix(1 0 0 -1 0 -136)">
<stop offset="0" style="stop-color:#91BFDB"/>
<stop offset="0.6" style="stop-color:#A8E9EB;stop-opacity:0.5"/>
<stop offset="1" style="stop-color:#EFF3F6;stop-opacity:0"/>
</linearGradient>
<path id="Shape" style="fill:url(#Shape_00000178178190290349774510000015233560893010072992_);" d="M397.1,375.7
c0,24.4-110.9,33.1-207.2,33.1S90.5,394,90.5,375.7s3.2-33.1,99.4-33.1S397.1,352.8,397.1,375.7z"/>
<g id="Group">
<g id="Shape-2" class="st2">
<line class="st3" x1="93.1" y1="395.7" x2="93.2" y2="418"/>
<line class="st3" x1="115.2" y1="401.9" x2="115.2" y2="424.1"/>
<line class="st3" x1="141" y1="405.8" x2="141" y2="428"/>
<line class="st3" x1="196.2" y1="405.9" x2="196.2" y2="428"/>
<line class="st3" x1="221.8" y1="402.1" x2="221.8" y2="424.2"/>
<line class="st3" x1="243.8" y1="395.9" x2="243.8" y2="418.1"/>
<line class="st3" x1="260.7" y1="387.9" x2="260.7" y2="409.9"/>
<line class="st3" x1="271.2" y1="378.6" x2="271.2" y2="399.4"/>
<line class="st3" x1="65.4" y1="378.4" x2="65.4" y2="399.4"/>
<line class="st3" x1="76.1" y1="387.7" x2="76.1" y2="409.7"/>
</g>
<linearGradient id="Shape-3_00000145760133362343895620000015494310892322915463_" gradientUnits="userSpaceOnUse" x1="-4695.894" y1="-1404.8131" x2="-4695.0723" y2="-1404.8131" gradientTransform="matrix(260.74 0 0 150.88 1224468.75 212327.9531)">
<stop offset="1.000000e-02" style="stop-color:#E6E6E6"/>
<stop offset="0.12" style="stop-color:#FEFEFE"/>
<stop offset="0.52" style="stop-color:#C3D0D7"/>
<stop offset="0.83" style="stop-color:#D2DDE2"/>
<stop offset="1" style="stop-color:#CDE1E4"/>
</linearGradient>
<path id="Shape-3" style="fill:url(#Shape-3_00000145760133362343895620000015494310892322915463_);" d="M274.5,367.4v-18.7
h-17.1c-4.5-2.4-9.2-4.6-14-6.2c-41.7-15.1-109.2-15.2-150.6-0.2c-4.9,1.8-9.7,3.9-14.3,6.5H61.9v19.3
c-0.1,10.5,9.4,20.9,31.3,28.9c41.7,15.1,109.1,15.2,150.6,0.2C266.2,388.9,274.7,378,274.5,367.4z"/>
<linearGradient id="Shape-4_00000071556748890164118890000017416756969037191558_" gradientUnits="userSpaceOnUse" x1="61.7996" y1="-1129.3312" x2="274.7628" y2="-1129.3312" gradientTransform="matrix(1 0 0 -1 0 -782)">
<stop offset="0" style="stop-color:#D6DCDF"/>
<stop offset="0.8" style="stop-color:#FBFBFC"/>
<stop offset="1" style="stop-color:#EEECEE"/>
</linearGradient>
<path id="Shape-4" style="fill:url(#Shape-4_00000071556748890164118890000017416756969037191558_);" d="M243.8,374.8
c-41.4,15-108.9,15-150.6-0.2s-41.9-39.6-0.4-54.7s108.9-15,150.6,0.2S285.2,359.8,243.8,374.8z"/>
<linearGradient id="Shape-5_00000180340779580305511340000000771215996181478573_" gradientUnits="userSpaceOnUse" x1="166.7489" y1="-1151.7883" x2="171.0751" y2="-1177.7562" gradientTransform="matrix(1 0 0 -1 0 -782)">
<stop offset="1.000000e-02" style="stop-color:#66EDF0"/>
<stop offset="0.52" style="stop-color:#FCFCFC"/>
<stop offset="1" style="stop-color:#FDFDFD"/>
</linearGradient>
<path id="Shape-5" style="fill:url(#Shape-5_00000180340779580305511340000000771215996181478573_);" d="M153.8,373.5
c9.7,0.7,19.5,0.5,29.2-0.2l6.4,9.1c-13.7,1.6-27.4,1.6-41.1,0L153.8,373.5z"/>
<linearGradient id="Shape-6_00000059309760233537008680000015072254621288308152_" gradientUnits="userSpaceOnUse" x1="-4691.7974" y1="-1403.4999" x2="-4691.1182" y2="-1403.5109" gradientTransform="matrix(210.65 0 0 93.82 988467.6875 132026.375)">
<stop offset="0" style="stop-color:#5BEFF1"/>
<stop offset="0.48" style="stop-color:#A8E9EB;stop-opacity:0.5"/>
<stop offset="1" style="stop-color:#EFF3F6;stop-opacity:0"/>
</linearGradient>
<path id="Shape-6" style="fill:url(#Shape-6_00000059309760233537008680000015072254621288308152_);" d="M243.8,366.8
c-33.4,9.3-87.8,9.2-121.5-0.2c-33.6-9.4-33.8-24.6-0.4-34c33.4-9.3,87.8-9.2,121.5,0.2S277.2,357.5,243.8,366.8z"/>
<g id="Shape-7" class="st8">
<polyline class="st3" points="243.4,320.2 93.1,374.7 93.2,396.8 "/>
<polyline class="st3" points="221.2,314 115.2,380.8 115.2,403.1 "/>
<polyline class="st3" points="195.5,310.1 141,384.8 141,406.9 "/>
<polyline class="st3" points="140.4,310.1 196.2,384.9 196.2,407 "/>
<polyline class="st3" points="114.7,313.9 221.8,381 221.8,403.2 "/>
<polyline class="st3" points="92.7,320 243.8,374.9 243.8,397 "/>
<polyline class="st3" points="75.8,327.9 260.7,366.9 260.7,388.8 "/>
<polyline class="st3" points="65.3,337.3 271.3,357.6 271.3,378.4 "/>
<line class="st3" x1="61.8" y1="347.3" x2="274.8" y2="347.5"/>
<polyline class="st3" points="271.1,337.6 65.4,357.3 65.4,378.4 "/>
<polyline class="st3" points="260.3,328.1 76.1,366.7 76.1,388.7 "/>
</g>
<radialGradient id="Shape-8_00000158733441478465335610000004571177335950995117_" cx="-4688.001" cy="-1394.5853" r="0.5038" gradientTransform="matrix(179.29 0 0 65.1 840676.625 91121.4297)" gradientUnits="userSpaceOnUse">
<stop offset="0.52" style="stop-color:#6A88CB"/>
<stop offset="1" style="stop-color:#C2C6CA;stop-opacity:0"/>
</radialGradient>
<path id="Shape-8" style="fill:url(#Shape-8_00000158733441478465335610000004571177335950995117_);" d="M231.2,369
c-35.3,12.8-92.7,12.7-128.1-0.1c-35.5-12.8-35.7-33.7-0.4-46.5s92.7-12.7,128.1,0.1S266.6,356.2,231.2,369z"/>
<linearGradient id="Shape-9_00000146497141956463728220000004586364667823936442_" gradientUnits="userSpaceOnUse" x1="93.7691" y1="-1127.861" x2="239.3361" y2="-1127.4558" gradientTransform="matrix(1 0 0 -1 0 -782)">
<stop offset="1.000000e-02" style="stop-color:#E6E6E6"/>
<stop offset="0.12" style="stop-color:#FEFEFE"/>
<stop offset="0.52" style="stop-color:#C3D0D7"/>
<stop offset="0.83" style="stop-color:#C8CED3"/>
<stop offset="1" style="stop-color:#C2C6CA"/>
</linearGradient>
<path id="Shape-9" style="fill:url(#Shape-9_00000146497141956463728220000004586364667823936442_);" d="M218.8,364.5
c-28.5,10.3-74.7,10.3-103.4-0.1s-28.8-27.2-0.3-37.6s74.7-10.3,103.4,0.1S247.3,354.1,218.8,364.5z"/>
<linearGradient id="Shape-10_00000031206731980968605630000001700956864725006976_" gradientUnits="userSpaceOnUse" x1="-4693.501" y1="-1404.2642" x2="-4692.6792" y2="-1404.2642" gradientTransform="matrix(227.64 0 0 131.39 1068501.75 184833.1719)">
<stop offset="1.000000e-02" style="stop-color:#E6E6E6"/>
<stop offset="0.12" style="stop-color:#FEFEFE"/>
<stop offset="0.52" style="stop-color:#C3D0D7"/>
<stop offset="0.83" style="stop-color:#D2DDE2"/>
<stop offset="1" style="stop-color:#CDE1E4"/>
</linearGradient>
<path id="Shape-10" style="fill:url(#Shape-10_00000031206731980968605630000001700956864725006976_);" d="M261.1,326l-0.2-11.9
l-10.3-2.6c-5.3-3.5-10.8-6.2-16.8-8.3c-36.4-13.1-95.1-13.3-131.1-0.1c-6,2.1-11.6,4.9-16.9,8.4L75.7,314l0.2,12.6v1.1l0,0
c0.7,8.3,9.9,16.5,27.3,22.9c36.4,13.1,95.1,13.3,131.1,0.1C253,343.9,261.9,334.9,261.1,326z"/>
<linearGradient id="Shape-11_00000085950511406540719030000013699986810275359133_" gradientUnits="userSpaceOnUse" x1="-4693.4814" y1="-1404.1786" x2="-4692.3643" y2="-1404.1786" gradientTransform="matrix(227.38 0 0 131.39 1067276 184810.1719)">
<stop offset="0" style="stop-color:#D6DCDF"/>
<stop offset="0.8" style="stop-color:#FBFBFC"/>
<stop offset="1" style="stop-color:#EEECEE"/>
</linearGradient>
<path id="Shape-11" style="fill:url(#Shape-11_00000085950511406540719030000013699986810275359133_);" d="M234,339
c-36,13.1-94.8,13-131.1-0.1s-36.5-34.5-0.4-47.6c36-13.1,94.7-13,131.1,0.1C269.9,304.5,270.1,325.8,234,339z"/>
<linearGradient id="Shape-12_00000013892298855937169510000007985557031648015280_" gradientUnits="userSpaceOnUse" x1="168.7642" y1="-1136.3141" x2="219.9114" y2="-1132.152" gradientTransform="matrix(1 0 0 -1 0 -782)">
<stop offset="1.000000e-02" style="stop-color:#66EDF0"/>
<stop offset="0.52" style="stop-color:#FCFCFC"/>
<stop offset="1" style="stop-color:#FDFDFD"/>
</linearGradient>
<path id="Shape-12" style="fill:url(#Shape-12_00000013892298855937169510000007985557031648015280_);" d="M155.3,351.9v4.9
c0,0,6.7,0.5,13,0.5c7.9,0,13-0.5,13-0.5V352C172.8,352.5,164.1,352.4,155.3,351.9z"/>
<linearGradient id="Shape-13_00000064333335148225958600000018360436247913531013_" gradientUnits="userSpaceOnUse" x1="-4398.5752" y1="-1395.9125" x2="-4398.1699" y2="-1394.8937" gradientTransform="matrix(13.71 0 0 24.9 60447.8086 35122.3555)">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="0.99" style="stop-color:#FFFFFF;stop-opacity:0"/>
</linearGradient>
<path id="Shape-13" style="opacity:0.7;fill:url(#Shape-13_00000064333335148225958600000018360436247913531013_);enable-background:new ;" d="
M137.9,358.6V370c0,0,7.2,1.1,11.2,1.4v-11.7C149.1,359.7,142.2,359.3,137.9,358.6z"/>
<linearGradient id="Shape-14_00000167393322824028187890000017452476633985115545_" gradientUnits="userSpaceOnUse" x1="-3174.8132" y1="-1394.9221" x2="-3174.7368" y2="-1394.1992" gradientTransform="matrix(2.8 0 0 22.7 9024.4199 32008.8613)">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="0.99" style="stop-color:#FFFFFF;stop-opacity:0"/>
</linearGradient>
<path id="Shape-14" style="fill:url(#Shape-14_00000167393322824028187890000017452476633985115545_);" d="M136.3,358.4v11.3
l-2.2-0.4L134,358L136.3,358.4z"/>
<linearGradient id="Shape-15_00000162352495989453854700000006277651567223056573_" gradientUnits="userSpaceOnUse" x1="-4678.0532" y1="-1402.1262" x2="-4677.2319" y2="-1402.1262" gradientTransform="matrix(128.49 0 0 74.25 601315.9375 104411.4688)">
<stop offset="1.000000e-02" style="stop-color:#55FFB5"/>
<stop offset="0.59" style="stop-color:#6DFFCB"/>
<stop offset="1" style="stop-color:#78FFD6"/>
</linearGradient>
<path id="Shape-15" style="fill:url(#Shape-15_00000162352495989453854700000006277651567223056573_);" d="M205.8,317.1
c-20.4,7.4-53.6,7.3-74.1-0.1c-20.6-7.4-20.6-19.5-0.2-26.9s53.6-7.3,74,0.1S226.2,309.6,205.8,317.1z"/>
<linearGradient id="Shape-16_00000153668204644255466410000009863565607959374214_" gradientUnits="userSpaceOnUse" x1="-4688.4077" y1="-1403.0204" x2="-4687.7285" y2="-1403.0314" gradientTransform="matrix(180.64 0 0 91.86 847058.25 129197.0469)">
<stop offset="0" style="stop-color:#C4D0D7"/>
<stop offset="1" style="stop-color:#EFF3F6;stop-opacity:0"/>
</linearGradient>
<path id="Shape-16" style="fill:url(#Shape-16_00000153668204644255466410000009863565607959374214_);" d="M231.5,332
c-28.7,9.2-75.2,9.1-104.2-0.1s-29-24.1-0.3-33.3s75.4-9.1,104.2,0.1S260.2,322.9,231.5,332z"/>
<linearGradient id="Shape-17_00000081607492502552466440000007433058223660923029_" gradientUnits="userSpaceOnUse" x1="-4685.3496" y1="-1402.9875" x2="-4684.5391" y2="-1402.9875" gradientTransform="matrix(158.96 0 0 91.86 744883 129192.7344)">
<stop offset="1.000000e-02" style="stop-color:#E6E6E6"/>
<stop offset="0.12" style="stop-color:#FEFEFE"/>
<stop offset="0.52" style="stop-color:#C3D0D7"/>
<stop offset="0.83" style="stop-color:#DAE5EA"/>
<stop offset="1" style="stop-color:#CDE1E4"/>
</linearGradient>
<path id="Shape-17" style="fill:url(#Shape-17_00000081607492502552466440000007433058223660923029_);" d="M233.4,313.6v-8.2
h-1.9c-2.7-4.1-8.5-4.5-17.3-7.7c-25.3-9.2-66.4-9.3-91.6-0.1c-8.8,3.2-14.5,3.7-17.1,7.8h-1.8v9l0,0c0.1,6,6.6,11.9,19.2,16.5
c25.4,9.2,66.5,9.3,91.7,0.1C227.7,326.2,234,319.8,233.4,313.6z"/>
<linearGradient id="Shape-18_00000000935785368065642490000001491826445201081248_" gradientUnits="userSpaceOnUse" x1="111.9444" y1="-1088.2948" x2="239.703" y2="-1085.6005" gradientTransform="matrix(1 0 0 -1 0 -782)">
<stop offset="0" style="stop-color:#E8E8E8"/>
<stop offset="0.53" style="stop-color:#FBFBFC"/>
</linearGradient>
<path id="Shape-18" style="fill:url(#Shape-18_00000000935785368065642490000001491826445201081248_);" d="M214.6,321.8
c-25.2,9.2-66.3,9.1-91.7-0.1s-25.5-24.1-0.3-33.3s66.3-9.1,91.7,0.1S239.9,312.6,214.6,321.8z"/>
<linearGradient id="Shape-19_00000159470190286743471590000007947123095774400143_" gradientUnits="userSpaceOnUse" x1="-4688.3633" y1="-1403.1714" x2="-4687.8926" y2="-1403.1821" gradientTransform="matrix(180.64 0 0 91.86 847058.25 129197.0469)">
<stop offset="0" style="stop-color:#C4D0D7"/>
<stop offset="1" style="stop-color:#EFF3F6;stop-opacity:0"/>
</linearGradient>
<path id="Shape-19" style="fill:url(#Shape-19_00000159470190286743471590000007947123095774400143_);" d="M211.9,313.6
c-20.2,6.7-52.8,6.6-73.1-0.1s-20.4-17.5-0.2-24.2s52.8-6.6,73.1,0.1S232.1,306.9,211.9,313.6z"/>
<ellipse class="st21" cx="169.8" cy="299.6" rx="46.7" ry="18.4"/>
<rect x="123.1" y="283.4" class="st21" width="93.3" height="16.3"/>
<ellipse class="st22" cx="169.8" cy="283.4" rx="46.7" ry="18.6"/>
<ellipse class="st23" cx="169.8" cy="283" rx="46.7" ry="18.4"/>
<ellipse class="st24" cx="169.8" cy="281.8" rx="39.6" ry="15.7"/>
<ellipse class="st25" cx="169.8" cy="281.8" rx="27.5" ry="10.8"/>
<ellipse class="st26" cx="169.8" cy="281.8" rx="20.9" ry="8.2"/>
<ellipse class="st27" cx="169.8" cy="281.8" rx="16" ry="6.4"/>
<path class="st28" d="M179.2,281.8c0,2.1-4.2,3.7-9.4,3.7c-5.3,0-9.4-1.6-9.4-3.7s4.2-3.7,9.4-3.7
C175,278.1,179.2,279.7,179.2,281.8z"/>
<ellipse class="st29" cx="203.1" cy="280.9" rx="2.8" ry="1.1"/>
<ellipse class="st29" cx="169" cy="268.3" rx="2.8" ry="1.1"/>
<path class="st29" d="M139.7,281.2c0,0.7-1.3,1.1-2.8,1.1s-2.8-0.5-2.8-1.1s1.3-1.1,2.8-1.1S139.7,280.6,139.7,281.2z"/>
<ellipse class="st29" cx="148.6" cy="293.1" rx="2.8" ry="1.1"/>
<ellipse class="st29" cx="189.2" cy="293.1" rx="2.8" ry="1.1"/>
<linearGradient id="SVGID_00000139277114413283346430000003203015687710406051_" gradientUnits="userSpaceOnUse" x1="203.143" y1="819.6531" x2="203.0444" y2="875.5317" gradientTransform="matrix(1 0 0 1 0 -592.21)">
<stop offset="0" style="stop-color:#0D55FF;stop-opacity:0"/>
<stop offset="0" style="stop-color:#0D55FF;stop-opacity:0"/>
<stop offset="0.65" style="stop-color:#04CEFF;stop-opacity:0.14"/>
<stop offset="1" style="stop-color:#00FCFF;stop-opacity:0.2"/>
</linearGradient>
<path style="fill:url(#SVGID_00000139277114413283346430000003203015687710406051_);" d="M206.6,277.1c0,1.2-1.5,2.2-3.5,2.2
l0,0c-2,0-3.5-1-3.5-2.2v-50.5l0,0v-0.1c0-0.8,1.5-1.4,3.5-1.4s3.5,0.7,3.5,1.4v0.1l0,0V277.1z"/>
<linearGradient id="SVGID_00000183221014587477581410000000706386369785952431_" gradientUnits="userSpaceOnUse" x1="169.1382" y1="807.0799" x2="169.0506" y2="862.9583" gradientTransform="matrix(1 0 0 1 0 -592.21)">
<stop offset="0" style="stop-color:#0D55FF;stop-opacity:0"/>
<stop offset="0" style="stop-color:#0D55FF;stop-opacity:0"/>
<stop offset="0.65" style="stop-color:#04CEFF;stop-opacity:0.14"/>
<stop offset="1" style="stop-color:#00FCFF;stop-opacity:0.2"/>
</linearGradient>
<path style="fill:url(#SVGID_00000183221014587477581410000000706386369785952431_);" d="M172.6,264.5c0,1.2-1.5,2.2-3.5,2.2
l0,0c-2,0-3.5-1-3.5-2.2V214l0,0v-0.1c0-0.8,1.5-1.4,3.5-1.4s3.5,0.7,3.5,1.4v0.1l0,0V264.5z"/>
<linearGradient id="SVGID_00000039096372971356690410000012421145682396891277_" gradientUnits="userSpaceOnUse" x1="189.4431" y1="826.3558" x2="189.3446" y2="888.3239" gradientTransform="matrix(1 0 0 1 0 -592.21)">
<stop offset="0" style="stop-color:#0D55FF;stop-opacity:0"/>
<stop offset="0" style="stop-color:#0D55FF;stop-opacity:0"/>
<stop offset="0.65" style="stop-color:#04CEFF;stop-opacity:0.14"/>
<stop offset="1" style="stop-color:#00FCFF;stop-opacity:0.2"/>
</linearGradient>
<path style="fill:url(#SVGID_00000039096372971356690410000012421145682396891277_);" d="M192.9,289.5c0,1.2-1.5,2.2-3.5,2.2
l0,0c-2,0-3.5-1-3.5-2.2v-56.4l0,0V233c0-0.8,1.5-1.4,3.5-1.4s3.5,0.7,3.5,1.4v0.1l0,0V289.5z"/>
<linearGradient id="SVGID_00000013163074585285730020000005816107648387152776_" gradientUnits="userSpaceOnUse" x1="148.6431" y1="826.3557" x2="148.5445" y2="888.3237" gradientTransform="matrix(1 0 0 1 0 -592.21)">
<stop offset="0" style="stop-color:#0D55FF;stop-opacity:0"/>
<stop offset="0" style="stop-color:#0D55FF;stop-opacity:0"/>
<stop offset="0.65" style="stop-color:#04CEFF;stop-opacity:0.14"/>
<stop offset="1" style="stop-color:#00FCFF;stop-opacity:0.2"/>
</linearGradient>
<path style="fill:url(#SVGID_00000013163074585285730020000005816107648387152776_);" d="M152.1,289.5c0,1.2-1.5,2.2-3.5,2.2
l0,0c-2,0-3.5-1-3.5-2.2v-56.4l0,0V233c0-0.8,1.5-1.4,3.5-1.4s3.5,0.7,3.5,1.4v0.1l0,0V289.5z"/>
<linearGradient id="SVGID_00000033367422727462969740000017109666248109283748_" gradientUnits="userSpaceOnUse" x1="136.9382" y1="819.653" x2="136.8506" y2="875.5316" gradientTransform="matrix(1 0 0 1 0 -592.21)">
<stop offset="0" style="stop-color:#0D55FF;stop-opacity:0"/>
<stop offset="0" style="stop-color:#0D55FF;stop-opacity:0"/>
<stop offset="0.65" style="stop-color:#04CEFF;stop-opacity:0.14"/>
<stop offset="1" style="stop-color:#00FCFF;stop-opacity:0.2"/>
</linearGradient>
<path style="fill:url(#SVGID_00000033367422727462969740000017109666248109283748_);" d="M140.4,277.1c0,1.2-1.5,2.2-3.5,2.2
l0,0c-2,0-3.5-1-3.5-2.2v-50.5l0,0v-0.1c0-0.8,1.5-1.4,3.5-1.4s3.5,0.7,3.5,1.4v0.1l0,0V277.1z"/>
</g>
<g id="Group-2">
<g id="Shape-20">
<linearGradient id="SVGID_00000066491685535550283050000010067348639462920879_" gradientUnits="userSpaceOnUse" x1="83.7" y1="-1141.8" x2="96.2" y2="-1141.8" gradientTransform="matrix(1 0 0 -1 0 -782)">
<stop offset="0" style="stop-color:#91BFDB"/>
<stop offset="0.6" style="stop-color:#A8E9EB;stop-opacity:0.5"/>
<stop offset="1" style="stop-color:#EFF3F6;stop-opacity:0"/>
</linearGradient>
<path style="fill:url(#SVGID_00000066491685535550283050000010067348639462920879_);" d="M96.2,359.8c0,1.5-4.5,2.2-8.4,2.2
s-4.1-1-4.1-2.2s0.1-2.2,4.1-2.2C91.7,357.6,96.2,358.3,96.2,359.8z"/>
<linearGradient id="SVGID_00000028319550724681881430000017225317771668268424_" gradientUnits="userSpaceOnUse" x1="89.9811" y1="-1142.4447" x2="82.3912" y2="-1140.8129" gradientTransform="matrix(1 0 0 -1 0 -782)">
<stop offset="1.000000e-02" style="stop-color:#5C6672"/>
<stop offset="0.28" style="stop-color:#5F87B2"/>
<stop offset="0.81" style="stop-color:#A7DFF9"/>
</linearGradient>
<path style="opacity:0.9;fill:url(#SVGID_00000028319550724681881430000017225317771668268424_);enable-background:new ;" d="
M90.5,359.7v-0.8h-0.1c-0.1-0.3-0.5-0.4-1-0.7c-1.6-0.9-3.6-0.9-5.3,0c-0.5,0.3-0.9,0.3-1,0.8H83v0.8l0,0
c0.1,0.7,0.4,1.2,1.1,1.5c1.6,0.9,3.6,0.9,5.3,0C89.9,361,90.4,360.3,90.5,359.7z"/>
<linearGradient id="SVGID_00000078734006496365647220000003596442759833680783_" gradientUnits="userSpaceOnUse" x1="74.6527" y1="-1139.2683" x2="91.3549" y2="-1141.5902" gradientTransform="matrix(1 0 0 -1 0 -782)">
<stop offset="1.000000e-02" style="stop-color:#5C6672"/>
<stop offset="0.28" style="stop-color:#5F87B2"/>
<stop offset="0.81" style="stop-color:#A7DFF9"/>
</linearGradient>
<path style="opacity:0.9;fill:url(#SVGID_00000078734006496365647220000003596442759833680783_);enable-background:new ;" d="
M89.4,360.5c-1.6,0.9-3.6,0.9-5.3,0c-1.4-0.9-1.4-2.2,0-3.1c1.6-0.9,3.6-0.9,5.3,0C90.8,358.2,90.8,359.6,89.4,360.5z"/>
<linearGradient id="SVGID_00000016778492564968718270000007715733383074586243_" gradientUnits="userSpaceOnUse" x1="-4461.2017" y1="-1399.1519" x2="-4461.1797" y2="-1399.8967" gradientTransform="matrix(17.18 0 0 41.2 76732.1953 58011.2617)">
<stop offset="1.000000e-02" style="stop-color:#5C6672"/>
<stop offset="0.28" style="stop-color:#5F87B2"/>
<stop offset="0.81" style="stop-color:#A7DFF9"/>
</linearGradient>
<path style="opacity:0.9;fill:url(#SVGID_00000016778492564968718270000007715733383074586243_);enable-background:new ;" d="
M92.6,339.1c-0.9-0.9-2-1.2-2.7-0.4l-3.6,3.4c-1,1-1.6,2.4-1.6,3.8l0,0v12.3l0,0c0,0.5,1,1.1,2.2,1.1s2.1-0.4,2.2-0.9v-0.1
v-12.2c0-0.3,0.1-0.5,0.3-0.9l3.6-3.5C93.7,341,93.3,340,92.6,339.1z"/>
<linearGradient id="SVGID_00000110445505960899282040000008185106537552059583_" gradientUnits="userSpaceOnUse" x1="87.1364" y1="-1123.8972" x2="94.5402" y2="-1120.8854" gradientTransform="matrix(1 0 0 -1 0 -782)">
<stop offset="1.000000e-02" style="stop-color:#5C6672"/>
<stop offset="0.28" style="stop-color:#5F87B2"/>
<stop offset="0.81" style="stop-color:#A7DFF9"/>
</linearGradient>
<path style="opacity:0.9;fill:url(#SVGID_00000110445505960899282040000008185106537552059583_);enable-background:new ;" d="
M92.8,341.8c-0.9,0.5-2.1,0.3-2.6-0.5c-0.8-0.7-0.9-1.9-0.3-2.7c0.7-0.5,1.6-0.4,2.6,0.5C93.4,340.1,93.4,341.2,92.8,341.8z"/>
<g class="st40">
<linearGradient id="SVGID_00000051370268289873400610000018298761055778287753_" gradientUnits="userSpaceOnUse" x1="-4205.8408" y1="-1377.0852" x2="-4204.7896" y2="-1376.0007" gradientTransform="matrix(8.51 0 0 9.65 35874.4414 13634.9932)">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="0.99" style="stop-color:#FFFFFF;stop-opacity:0"/>
</linearGradient>
<path style="fill:url(#SVGID_00000051370268289873400610000018298761055778287753_);" d="M82.9,347.3c1.5,2.3,4.2,3.6,6.9,3.5
v1.3c-2.5,0.4-5-0.4-6.9-2.2V347.3L82.9,347.3z"/>
<linearGradient id="SVGID_00000022539495148538625040000011656406462773614524_" gradientUnits="userSpaceOnUse" x1="-4203.5879" y1="-1360.5885" x2="-4202.3726" y2="-1359.7999" gradientTransform="matrix(8.48 0 0 6.06 35729.3945 8596.1113)">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="0.99" style="stop-color:#FFFFFF;stop-opacity:0"/>
</linearGradient>
<path style="fill:url(#SVGID_00000022539495148538625040000011656406462773614524_);" d="M82.9,350.6c0,0,1.1,2.3,6.9,2.2v0.9
c0,0-5,0.3-6.9-1.8V350.6z"/>
</g>
</g>
<g id="Shape-21">
<linearGradient id="SVGID_00000053529045596386359860000012693715221203522699_" gradientUnits="userSpaceOnUse" x1="-4537.0894" y1="-1374.7428" x2="-4536.6074" y2="-1374.7318" gradientTransform="matrix(24.44 0 0 8.38 111101.9062 11894.1025)">
<stop offset="0" style="stop-color:#91BFDB"/>
<stop offset="0.6" style="stop-color:#A8E9EB;stop-opacity:0.5"/>
<stop offset="1" style="stop-color:#EFF3F6;stop-opacity:0"/>
</linearGradient>
<path style="fill:url(#SVGID_00000053529045596386359860000012693715221203522699_);" d="M229.8,373.8c0,1.6-4.8,2.2-8.9,2.2
c-4.1,0-4.3-1-4.3-2.2s0.1-2.2,4.3-2.2C225.1,371.6,229.8,372.3,229.8,373.8z"/>
<linearGradient id="SVGID_00000016041242089640294250000005076626961659910032_" gradientUnits="userSpaceOnUse" x1="-4419.8472" y1="-1374.6202" x2="-4420.3838" y2="-1374.6202" gradientTransform="matrix(14.67 0 0 8.48 65064.1992 12030.4795)">
<stop offset="1.000000e-02" style="stop-color:#5C6672"/>
<stop offset="0.28" style="stop-color:#5F87B2"/>
<stop offset="0.81" style="stop-color:#A7DFF9"/>
</linearGradient>
<path style="opacity:0.9;fill:url(#SVGID_00000016041242089640294250000005076626961659910032_);enable-background:new ;" d="
M215.8,373.6v-0.8h0.1c0.1-0.4,0.5-0.4,1.1-0.7c1.8-0.9,3.8-0.9,5.6,0c0.5,0.3,0.9,0.3,1.1,0.8h0.1v0.9l0,0
c-0.1,0.7-0.5,1.3-1.2,1.5c-1.8,0.9-3.8,0.9-5.6,0C216.3,375,215.9,374.4,215.8,373.6z"/>
<linearGradient id="SVGID_00000034051049874645699850000001054443060867366060_" gradientUnits="userSpaceOnUse" x1="210.2652" y1="-1156.347" x2="223.9993" y2="-1154.1678" gradientTransform="matrix(1 0 0 -1 0 -782)">
<stop offset="1.000000e-02" style="stop-color:#5C6672"/>
<stop offset="0.28" style="stop-color:#5F87B2"/>
<stop offset="0.81" style="stop-color:#A7DFF9"/>
</linearGradient>
<path style="opacity:0.9;fill:url(#SVGID_00000034051049874645699850000001054443060867366060_);enable-background:new ;" d="
M216.9,374.4c1.8,0.9,3.8,0.9,5.6,0c1.5-0.9,1.5-2.2,0-3.1c-1.8-0.9-3.8-0.9-5.6,0C215.3,372.2,215.3,373.5,216.9,374.4z"/>
<linearGradient id="SVGID_00000140705939172839159610000000673615029388288165_" gradientUnits="userSpaceOnUse" x1="-4461.4771" y1="-1400.4607" x2="-4461.4556" y2="-1401.49" gradientTransform="matrix(17.1 0 0 49.17 76508.5 69238.9375)">
<stop offset="1.000000e-02" style="stop-color:#5C6672"/>
<stop offset="0.28" style="stop-color:#5F87B2"/>
<stop offset="0.81" style="stop-color:#A7DFF9"/>
</linearGradient>
<path style="opacity:0.9;fill:url(#SVGID_00000140705939172839159610000000673615029388288165_);enable-background:new ;" d="
M213.6,349.5c0.8-0.9,2-1,2.8-0.3l3.7,3.5c1.1,1,1.8,2.4,1.8,3.8v16.4l0,0c0,0.5-1,1.1-2.3,1.1s-2.3-0.4-2.3-1.1v-16.3
c0-0.3-0.1-0.7-0.4-0.9l-3.7-3.5C212.6,351.5,212.7,350.2,213.6,349.5L213.6,349.5z"/>
<linearGradient id="SVGID_00000016069627382271396290000009179125164610461070_" gradientUnits="userSpaceOnUse" x1="211.3378" y1="-1132.3972" x2="217.6791" y2="-1132.7916" gradientTransform="matrix(1 0 0 -1 0 -782)">
<stop offset="1.000000e-02" style="stop-color:#5C6672"/>
<stop offset="0.28" style="stop-color:#5F87B2"/>
<stop offset="0.81" style="stop-color:#A7DFF9"/>
</linearGradient>
<path style="opacity:0.9;fill:url(#SVGID_00000016069627382271396290000009179125164610461070_);enable-background:new ;" d="
M213.1,352.2c0.9,0.5,2.1,0.3,2.7-0.5c0.9-0.7,1-1.8,0.3-2.6c0,0,0,0,0-0.1c-0.9-0.5-2.1-0.3-2.8,0.5
C212.7,350.2,212.5,351.4,213.1,352.2C213.1,352.1,213.1,352.2,213.1,352.2z"/>
<g class="st40">
<linearGradient id="SVGID_00000023992425455839354290000003462269822397537668_" gradientUnits="userSpaceOnUse" x1="-4209.4048" y1="-1378.4917" x2="-4208.3643" y2="-1377.4075" gradientTransform="matrix(8.51 0 0 9.65 36039.0703 13662.6221)">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="0.99" style="stop-color:#FFFFFF;stop-opacity:0"/>
</linearGradient>
<path style="fill:url(#SVGID_00000023992425455839354290000003462269822397537668_);" d="M217.2,361.4
c1.5,2.3,4.2,3.6,6.9,3.5v1.3c-2.5,0.4-5-0.4-6.9-2.2V361.4L217.2,361.4z"/>
<linearGradient id="SVGID_00000161610153219861893360000018069820855094444985_" gradientUnits="userSpaceOnUse" x1="-4207.1631" y1="-1362.8098" x2="-4205.9478" y2="-1362.0103" gradientTransform="matrix(8.48 0 0 6.06 35894.0234 8623.752)">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="0.99" style="stop-color:#FFFFFF;stop-opacity:0"/>
</linearGradient>
<path style="fill:url(#SVGID_00000161610153219861893360000018069820855094444985_);" d="M217.2,364.8c0,0,1.1,2.3,6.9,2.2
v0.9c0,0-5,0.3-6.9-1.8V364.8z"/>
</g>
</g>
<g id="Shape-22">
<linearGradient id="SVGID_00000101097610753023437160000010300393479110564788_" gradientUnits="userSpaceOnUse" x1="-4537.3999" y1="-1373.2405" x2="-4536.9399" y2="-1373.2295" gradientTransform="matrix(24.44 0 0 8.38 111140.0703 11867.9131)">
<stop offset="0" style="stop-color:#91BFDB"/>
<stop offset="0.6" style="stop-color:#A8E9EB;stop-opacity:0.5"/>
<stop offset="1" style="stop-color:#EFF3F6;stop-opacity:0"/>
</linearGradient>
<path style="fill:url(#SVGID_00000101097610753023437160000010300393479110564788_);" d="M259.7,360.2c0,1.6-4.6,2.2-8.5,2.2
s-4.1-1-4.1-2.2s0.1-2.2,4.1-2.2S259.7,358.7,259.7,360.2z"/>
<linearGradient id="SVGID_00000057826871241478005110000007594934890897262214_" gradientUnits="userSpaceOnUse" x1="-4420.3882" y1="-1373.1334" x2="-4420.9033" y2="-1373.1334" gradientTransform="matrix(14.67 0 0 8.48 65102.3398 12004.2705)">
<stop offset="1.000000e-02" style="stop-color:#5C6672"/>
<stop offset="0.28" style="stop-color:#5F87B2"/>
<stop offset="0.81" style="stop-color:#A7DFF9"/>
</linearGradient>
<path style="opacity:0.9;fill:url(#SVGID_00000057826871241478005110000007594934890897262214_);enable-background:new ;" d="
M246.3,360v-0.8h0.1c0.1-0.3,0.5-0.4,1-0.7c1.6-0.9,3.7-0.9,5.4,0c0.5,0.3,0.9,0.3,1,0.8h0.1v0.9l0,0c-0.1,0.7-0.4,1.2-1.1,1.5
c-1.6,0.9-3.7,0.9-5.4,0C246.8,361.3,246.3,360.8,246.3,360z"/>
<linearGradient id="SVGID_00000034069503416819206880000007783290857966879909_" gradientUnits="userSpaceOnUse" x1="241.1007" y1="-1144.2089" x2="255.5578" y2="-1139.4556" gradientTransform="matrix(1 0 0 -1 0 -782)">
<stop offset="1.000000e-02" style="stop-color:#5C6672"/>
<stop offset="0.28" style="stop-color:#5F87B2"/>
<stop offset="0.81" style="stop-color:#A7DFF9"/>
</linearGradient>
<path style="opacity:0.9;fill:url(#SVGID_00000034069503416819206880000007783290857966879909_);enable-background:new ;" d="
M247.4,360.8c1.6,0.9,3.7,0.9,5.4,0c1.5-0.9,1.5-2.2,0-3.1c-1.6-0.9-3.7-0.9-5.4,0C245.9,358.6,245.9,359.9,247.4,360.8z"/>
<linearGradient id="SVGID_00000074402755482888099360000011871301802014376069_" gradientUnits="userSpaceOnUse" x1="-4463.228" y1="-1399.1514" x2="-4463.2495" y2="-1399.8962" gradientTransform="matrix(17.18 0 0 41.2 76926.3984 58012.0977)">
<stop offset="1.000000e-02" style="stop-color:#5C6672"/>
<stop offset="0.28" style="stop-color:#5F87B2"/>
<stop offset="0.81" style="stop-color:#A7DFF9"/>
</linearGradient>
<path style="opacity:0.9;fill:url(#SVGID_00000074402755482888099360000011871301802014376069_);enable-background:new ;" d="
M244.2,339.5c1-0.9,2-1.2,2.7-0.4l3.6,3.5c1.1,1,1.6,2.4,1.8,3.8l0,0v12.3l0,0c0,0.5-1,1.1-2.2,1.1s-2.1-0.4-2.2-0.9v-0.1
v-12.3c0-0.3-0.1-0.7-0.3-0.9l-3.6-3.5C243,341.4,243.4,340.4,244.2,339.5z"/>
<linearGradient id="SVGID_00000034800493623832042920000008020736596099291557_" gradientUnits="userSpaceOnUse" x1="235.944" y1="-1125.0896" x2="250.39" y2="-1121.2892" gradientTransform="matrix(1 0 0 -1 0 -782)">
<stop offset="1.000000e-02" style="stop-color:#5C6672"/>
<stop offset="0.28" style="stop-color:#5F87B2"/>
<stop offset="0.81" style="stop-color:#A7DFF9"/>
</linearGradient>
<path style="opacity:0.9;fill:url(#SVGID_00000034800493623832042920000008020736596099291557_);enable-background:new ;" d="
M243.9,342.2c0.9,0.5,2.1,0.3,2.6-0.5c0.8-0.7,1-1.9,0.3-2.7c-0.7-0.5-1.6-0.4-2.6,0.5C243.3,340.4,243.3,341.6,243.9,342.2z"
/>
<g class="st40">
<linearGradient id="SVGID_00000006704291500926620380000004171103234775810997_" gradientUnits="userSpaceOnUse" x1="-4208.7266" y1="-1376.4762" x2="-4207.8501" y2="-1375.5673" gradientTransform="matrix(8.47 0 0 9.55 35885.875 13484.8174)">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="0.99" style="stop-color:#FFFFFF;stop-opacity:0"/>
</linearGradient>
<path style="fill:url(#SVGID_00000006704291500926620380000004171103234775810997_);" d="M246.3,349c0,0,1.3,3.5,4.9,3.5v1.3
c-2,0.3-3.8-0.5-4.9-2.2V349z"/>
<linearGradient id="SVGID_00000177484813400042119220000011015524536336387207_" gradientUnits="userSpaceOnUse" x1="-4210.5615" y1="-1360.016" x2="-4209.5977" y2="-1359.3917" gradientTransform="matrix(8.51 0 0 6.07 36075.9102 8607.5488)">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="0.99" style="stop-color:#FFFFFF;stop-opacity:0"/>
</linearGradient>
<path style="fill:url(#SVGID_00000177484813400042119220000011015524536336387207_);" d="M246.3,352.4c0,0,0.9,2.3,4.9,2.2
v0.9c0,0-3.6,0.3-4.9-1.8V352.4z"/>
</g>
</g>
<g id="Shape-23">
<linearGradient id="SVGID_00000150793386108511341090000013432375121497242256_" gradientUnits="userSpaceOnUse" x1="111" y1="-1155.8" x2="123.6828" y2="-1155.8" gradientTransform="matrix(1 0 0 -1 0 -782)">
<stop offset="0" style="stop-color:#91BFDB"/>
<stop offset="0.6" style="stop-color:#A8E9EB;stop-opacity:0.5"/>
<stop offset="1" style="stop-color:#EFF3F6;stop-opacity:0"/>
</linearGradient>
<path style="fill:url(#SVGID_00000150793386108511341090000013432375121497242256_);" d="M123.7,373.8c0,1.5-4.6,2.2-8.5,2.2
s-4.2-1-4.2-2.2s0.1-2.2,4.2-2.2C119.1,371.6,123.7,372.3,123.7,373.8z"/>
<linearGradient id="SVGID_00000173862521492696290410000004963765814931415984_" gradientUnits="userSpaceOnUse" x1="117.3702" y1="-1156.4034" x2="109.6817" y2="-1154.7496" gradientTransform="matrix(1 0 0 -1 0 -782)">
<stop offset="1.000000e-02" style="stop-color:#5C6672"/>
<stop offset="0.28" style="stop-color:#5F87B2"/>
<stop offset="0.81" style="stop-color:#A7DFF9"/>
</linearGradient>
<path style="opacity:0.9;fill:url(#SVGID_00000173862521492696290410000004963765814931415984_);enable-background:new ;" d="
M117.9,373.6v-0.8h-0.1c-0.2-0.3-0.5-0.4-1-0.7c-1.6-0.9-3.7-0.9-5.4,0c-0.5,0.3-0.9,0.3-1,0.8h-0.1v0.9l0,0
c0.1,0.7,0.4,1.2,1.1,1.5c1.6,0.9,3.7,0.9,5.4,0C117.3,374.9,117.7,374.4,117.9,373.6z"/>
<linearGradient id="SVGID_00000020398109177578560470000006450052471999083696_" gradientUnits="userSpaceOnUse" x1="101.8376" y1="-1153.1447" x2="118.7699" y2="-1155.4994" gradientTransform="matrix(1 0 0 -1 0 -782)">
<stop offset="1.000000e-02" style="stop-color:#5C6672"/>
<stop offset="0.28" style="stop-color:#5F87B2"/>
<stop offset="0.81" style="stop-color:#A7DFF9"/>
</linearGradient>
<path style="opacity:0.9;fill:url(#SVGID_00000020398109177578560470000006450052471999083696_);enable-background:new ;" d="
M116.8,374.4c-1.6,0.9-3.7,0.9-5.4,0c-1.5-0.9-1.5-2.2,0-3.1c1.6-0.9,3.7-0.9,5.4,0C118.2,372.2,118.2,373.5,116.8,374.4z"/>
<linearGradient id="SVGID_00000146469520690687921700000001834666537324208572_" gradientUnits="userSpaceOnUse" x1="-4459.6167" y1="-1398.7903" x2="-4459.5947" y2="-1399.6775" gradientTransform="matrix(17.18 0 0 41.2 76732.1953 58011.2617)">
<stop offset="1.000000e-02" style="stop-color:#5C6672"/>
<stop offset="0.28" style="stop-color:#5F87B2"/>
<stop offset="0.81" style="stop-color:#A7DFF9"/>
</linearGradient>
<path style="opacity:0.9;fill:url(#SVGID_00000146469520690687921700000001834666537324208572_);enable-background:new ;" d="
M119.9,349.4c-0.9-0.9-2-1.2-2.7-0.4l-3.6,3.5c-1.1,1-1.6,2.4-1.8,3.8l0,0v12.3v3.9c0,0.5,1,1.1,2.2,1.1s2.1-0.4,2.2-0.9v-0.1
v-16.2c0-0.3,0.1-0.7,0.3-0.9l3.6-3.5C121.1,351.3,120.8,350.3,119.9,349.4z"/>
<linearGradient id="SVGID_00000146461411017424176850000008273474735748943006_" gradientUnits="userSpaceOnUse" x1="114.4784" y1="-1134.2108" x2="121.9807" y2="-1131.1552" gradientTransform="matrix(1 0 0 -1 0 -782)">
<stop offset="1.000000e-02" style="stop-color:#5C6672"/>
<stop offset="0.28" style="stop-color:#5F87B2"/>
<stop offset="0.81" style="stop-color:#A7DFF9"/>
</linearGradient>
<path style="opacity:0.9;fill:url(#SVGID_00000146461411017424176850000008273474735748943006_);enable-background:new ;" d="
M120.3,352.1c-0.9,0.5-2.1,0.3-2.7-0.5c-0.8-0.7-1-1.9-0.3-2.7c0.7-0.7,1.8-0.4,2.6,0.5C120.7,350.4,120.9,351.5,120.3,352.1z"
/>
<g class="st40">
<linearGradient id="SVGID_00000085959571847769126780000003905589832501502136_" gradientUnits="userSpaceOnUse" x1="-4202.5493" y1="-1376.0286" x2="-4201.4976" y2="-1374.9441" gradientTransform="matrix(8.51 0 0 9.65 35874.4414 13634.9932)">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="0.99" style="stop-color:#FFFFFF;stop-opacity:0"/>
</linearGradient>
<path style="fill:url(#SVGID_00000085959571847769126780000003905589832501502136_);" d="M111,357.6c1.5,2.3,4.2,3.6,6.9,3.5
v1.3c-2.5,0.4-5.1-0.4-6.9-2.2V357.6L111,357.6z"/>
<linearGradient id="SVGID_00000101066731764067032500000013534230380051260331_" gradientUnits="userSpaceOnUse" x1="-4200.2793" y1="-1358.8921" x2="-4199.064" y2="-1358.1035" gradientTransform="matrix(8.48 0 0 6.06 35729.3945 8596.1113)">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="0.99" style="stop-color:#FFFFFF;stop-opacity:0"/>
</linearGradient>
<path style="fill:url(#SVGID_00000101066731764067032500000013534230380051260331_);" d="M111,360.9c0,0,1.1,2.3,6.9,2.2v0.9
c0,0-5,0.3-6.9-1.8V360.9z"/>
</g>
</g>
</g>
<path id="Shape-24" class="st64" d="M101.5,377.4c0,0,2,0.7,5.5,1.5c1.8,0.4,3.9,1,6.4,1.5c2.4,0.5,5.4,1.1,8.4,1.6
c1.5,0.2,3.2,0.5,4.9,0.8l2.6,0.4l2.6,0.3c0.9,0.1,1.9,0.2,2.7,0.3l2.8,0.3c2,0.2,3.9,0.4,5.9,0.5l3.1,0.2l3.1,0.2
c2.1,0.1,4.2,0.2,6.4,0.3c1.1,0.1,2.1,0.1,3.2,0.1s2.2,0.1,3.2,0.1h3.3h1.6h1.6c2.2,0,4.3,0,6.5-0.1h3.1l3.2-0.1l3.2-0.1l3.1-0.2
c1-0.1,2.1-0.1,3.1-0.2l3.1-0.3l3-0.3l3-0.3l2.8-0.3c1-0.1,1.9-0.2,2.7-0.4l2.6-0.4l1.3-0.2l1.3-0.2l4.9-0.9
c1.5-0.3,3.1-0.5,4.5-0.9s2.7-0.5,3.9-0.9l3.5-0.8l1.5-0.3l1.4-0.3l5.5-1.4l-5.5,1.4c-0.4,0.1-0.9,0.2-1.4,0.3l-1.5,0.3l-3.5,0.8
c-1.2,0.3-2.6,0.5-3.9,0.9s-2.8,0.5-4.5,0.9l-4.9,0.9l-1.3,0.2l-1.3,0.2l-2.6,0.4c-0.9,0.1-1.9,0.2-2.7,0.4l-2.8,0.3l-3,0.3
l-3,0.3l-3.1,0.3c-1,0.1-2.1,0.1-3.1,0.2l-3.2,0.2l-3.2,0.1c-1.1,0-2.2,0.1-3.2,0.1l-3.2,0.1c-2.2,0.1-4.3,0.1-6.5,0.1H167h-1.6
h-3.3l-3.2-0.1c-1.1,0-2.2-0.1-3.2-0.1c-2.1-0.1-4.3-0.2-6.4-0.4l-3.1-0.2l-3-0.3c-2-0.1-3.9-0.4-5.9-0.7l-2.8-0.3
c-0.9-0.1-1.9-0.2-2.7-0.3l-2.7-0.4l-2.6-0.4c-1.8-0.2-3.3-0.5-4.9-0.8c-3.1-0.5-5.9-1.1-8.4-1.6s-4.6-1.1-6.4-1.5
C103.5,378,101.5,377.4,101.5,377.4z"/>
</g>
<radialGradient id="SVGID_00000152945109931711952980000000514259528298555047_" cx="164.9579" cy="-350.9658" r="213.7317" gradientTransform="matrix(1 0 0 -1 0 -136)" gradientUnits="userSpaceOnUse">
<stop offset="0" style="stop-color:#2AC6FF;stop-opacity:0.7"/>
<stop offset="0.6704" style="stop-color:#2AC6FF;stop-opacity:0.3"/>
<stop offset="0.9329" style="stop-color:#FFFFFF;stop-opacity:0"/>
</radialGradient>
<path style="fill:url(#SVGID_00000152945109931711952980000000514259528298555047_);" d="M294.2,344.5l-1.8-2.3
c-0.2-0.3-0.4-0.7-0.8-1c-16.1-21.4-48.4-55.7-48.4-111.1c0-68.9,4.3-157.7,24.4-231.9H65.9c20.3,74.1,24.4,163,24.4,231.9
c0,58.3-30.4,93.3-45.5,114.5h0.9c-0.3,0.9-0.4,1.8-0.4,2.7c0,16.9,55.7,30.6,124.5,30.6s124.4-13.7,124.4-30.6
c0-0.9-0.2-1.9-0.4-2.7L294.2,344.5z"/>
<linearGradient id="Shape-25_00000062897884857159563720000017493666428800170661_" gradientUnits="userSpaceOnUse" x1="-3138.2468" y1="-1399.6875" x2="-3138.2468" y2="-1399.1289" gradientTransform="matrix(2.13 0 0 135.17 6832.7236 189269.7344)">
<stop offset="0" style="stop-color:#4C83FF"/>
<stop offset="0.41" style="stop-color:#2AFADF"/>
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0"/>
</linearGradient>
<path id="Shape-25" style="opacity:0.1;fill:url(#Shape-25_00000062897884857159563720000017493666428800170661_);enable-background:new ;" d="
M148.3,81.3c-0.5,0-1,0.4-1,1l0,0v102.2c0,0.5,0.4,1,0.9,1s1-0.4,1-0.9l0,0V82.3C149.3,81.7,148.9,81.3,148.3,81.3L148.3,81.3z"/>
<linearGradient id="Shape-26_00000096747381355368235380000013244751307849799820_" gradientUnits="userSpaceOnUse" x1="-3141.8672" y1="-1399.7417" x2="-3141.8672" y2="-1399.1831" gradientTransform="matrix(2.13 0 0 135.17 6894.334 189301.5781)">
<stop offset="0" style="stop-color:#4C83FF"/>
<stop offset="0.41" style="stop-color:#2AFADF"/>
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0"/>
</linearGradient>
<path id="Shape-26" style="opacity:0.1;fill:url(#Shape-26_00000096747381355368235380000013244751307849799820_);enable-background:new ;" d="
M202.2,105.8c-0.5,0-1,0.4-1,1v102.1c0,0.5,0.4,1,0.9,1s1-0.4,1-0.9l0,0V106.9C203.2,106.4,202.7,105.8,202.2,105.8z"/>
<linearGradient id="Shape-27_00000119823744966612805220000010319973446294606232_" gradientUnits="userSpaceOnUse" x1="-3136.2" y1="-1399.7588" x2="-3136.2" y2="-1399.1892" gradientTransform="matrix(2.13 0 0 135.17 6791.6636 189308.4531)">
<stop offset="0" style="stop-color:#4C83FF"/>
<stop offset="0.41" style="stop-color:#2AFADF"/>
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0"/>
</linearGradient>
<path id="Shape-27" style="opacity:0.2;fill:url(#Shape-27_00000119823744966612805220000010319973446294606232_);enable-background:new ;" d="
M111.6,110.1c-0.5,0-1,0.4-1,1l0,0v102.2c0,0.5,0.4,1,0.9,1s1-0.4,1-1l0,0V111.1C112.6,110.6,112.2,110.2,111.6,110.1L111.6,110.1
z"/>
<linearGradient id="Shape-28_00000103240500498414136000000006526299530864732311_" gradientUnits="userSpaceOnUse" x1="-2292.4885" y1="-1395.9701" x2="-2292.4885" y2="-1395.4005" gradientTransform="matrix(1.38 0 0 87.55 3393.5439 122396.2578)">
<stop offset="0" style="stop-color:#4C83FF"/>
<stop offset="0.41" style="stop-color:#2AFADF"/>
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0"/>
</linearGradient>
<path id="Shape-28" style="opacity:0.4;fill:url(#Shape-28_00000103240500498414136000000006526299530864732311_);enable-background:new ;" d="
M230,172.8c-0.3,0-0.7,0.3-0.7,0.7l0,0v66.2c0,0.3,0.2,0.7,0.5,0.7l0,0c0.3,0,0.7-0.3,0.7-0.7l0,0v-66.2
C230.6,173.2,230.3,172.8,230,172.8z"/>
<linearGradient id="Shape-29_00000003076700492934946240000003238066575396519355_" gradientUnits="userSpaceOnUse" x1="-3137.2019" y1="-1396.8502" x2="-3137.2019" y2="-1396.2916" gradientTransform="matrix(2.13 0 0 94.53 6812.8838 132233.6719)">
<stop offset="0" style="stop-color:#4C83FF"/>
<stop offset="0.41" style="stop-color:#2AFADF"/>
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0"/>
</linearGradient>
<path id="Shape-29" style="opacity:0.3;fill:url(#Shape-29_00000003076700492934946240000003238066575396519355_);enable-background:new ;" d="
M130.6,191.5c-0.5,0-1,0.4-0.9,1v70.9c0,0.5,0.4,1,0.9,1s1-0.4,1-0.9l0,0v-70.9C131.5,191.9,131.1,191.5,130.6,191.5z"/>
<linearGradient id="Shape-30_00000142141291087823972360000005780631530962408337_" gradientUnits="userSpaceOnUse" x1="165.1285" y1="-976.6299" x2="164.8766" y2="-910.3356" gradientTransform="matrix(1 0 0 -1 0 -782)">
<stop offset="0" style="stop-color:#FFFFFF;stop-opacity:0.5"/>
<stop offset="0.99" style="stop-color:#12FFF7;stop-opacity:0"/>
</linearGradient>
<path id="Shape-30" style="opacity:0.8;fill:url(#Shape-30_00000142141291087823972360000005780631530962408337_);enable-background:new ;" d="
M165.3,195.1c-24.6,0-48-3.5-65.4-10s-27.4-15.2-27.4-24.4s9.5-17.9,26.9-24.2s40.5-10,65.3-10s47.9,3.5,65.4,10
s27.4,15.2,27.4,24.4s-9.5,17.7-26.9,24.2c-2.6,1-5.5,2-8.5,2.8c-11.2,3.2-22.7,5.1-34.2,6.1C180.5,194.8,172.9,195.1,165.3,195.1
z M164.7,127.1c-24.5,0-47.5,3.5-64.7,10c-17.3,6.4-26.6,14.7-26.6,23.7s9.6,17.4,26.9,23.9s40.4,10,64.9,10
c7.6,0,15.1-0.3,22.3-1c11.5-1,22.9-3.1,34-6.1c3-0.9,5.8-1.8,8.4-2.7c17.1-6.4,26.5-14.8,26.5-23.7s-9.6-17.5-26.9-23.9
C212.3,130.8,189.3,127.1,164.7,127.1z"/>
<linearGradient id="Shape-31_00000047765179999079558420000018338508616989585558_" gradientUnits="userSpaceOnUse" x1="-4556.9941" y1="-423.3402" x2="-4561.7476" y2="-464.8384" gradientTransform="matrix(77.04 0 0 111.08 359914.0938 155486.75)">
<stop offset="0" style="stop-color:#4C83FF"/>
<stop offset="1" style="stop-color:#2AFADF"/>
</linearGradient>
<path id="Shape-31" style="opacity:0.3;fill:url(#Shape-31_00000047765179999079558420000018338508616989585558_);enable-background:new ;" d="
M235.5,241.1c-6.9,2.7-14,4.9-21.2,6.6c-2.6,0.7-4.7-0.9-4.7-3.3v-28.1c0-2.4,2.1-4.7,4.7-5.4c7.2-1.6,14.3-3.8,21.2-6.6
c3.4-1.4,6.8-2.8,10-4.5c20.4-10.4,30.8-24,31.4-37.6v34.3c0.5,14.5-9.9,29-31.4,40.1C242.3,238.2,238.9,239.7,235.5,241.1z"/>
<g id="Shape-32" class="st73">
<linearGradient id="SVGID_00000083767028849843557540000003015893000703474077_" gradientUnits="userSpaceOnUse" x1="-3997.0054" y1="-1331.5676" x2="-3997.0054" y2="-1330.6367" gradientTransform="matrix(4.67 0 0 12.58 18914.2656 16962.2617)">
<stop offset="0" style="stop-color:#2AFADF"/>
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.8"/>
</linearGradient>
<polygon style="fill:url(#SVGID_00000083767028849843557540000003015893000703474077_);" points="250.3,228.9 250.3,221.8
246.2,223.9 246.2,231.6 "/>
<linearGradient id="SVGID_00000000215917149092826270000018230445423745942943_" gradientUnits="userSpaceOnUse" x1="-4006.7764" y1="-1383.0823" x2="-4006.7654" y2="-1381.5378" gradientTransform="matrix(4.74 0 0 36.55 19216.0312 50684.7617)">
<stop offset="0" style="stop-color:#2AFADF"/>
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.8"/>
</linearGradient>
<path style="fill:url(#SVGID_00000000215917149092826270000018230445423745942943_);" d="M226.1,240.6c-1.3,0.4-2.7,0.9-4.1,1.3
l-0.1-26.9c1.3-0.4,2.7-0.8,4.1-1.2L226.1,240.6z"/>
<linearGradient id="SVGID_00000127760218784342968640000002359079861741369269_" gradientUnits="userSpaceOnUse" x1="-4002.3381" y1="-1369.6538" x2="-4002.3381" y2="-1368.5916" gradientTransform="matrix(4.7 0 0 25.06 19043.0879 34513.9961)">
<stop offset="0" style="stop-color:#2AFADF"/>
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.8"/>
</linearGradient>
<path style="fill:url(#SVGID_00000127760218784342968640000002359079861741369269_);" d="M234.2,237.6l-2.2,0.9l-1.9,0.8
l-0.1-17.7h0.1l1.9-0.8c0.8-0.3,1.4-0.5,2.2-0.9V237.6z"/>
<linearGradient id="SVGID_00000025411509199572341930000014536893160322097330_" gradientUnits="userSpaceOnUse" x1="-4005.9248" y1="-1380.2017" x2="-4005.9248" y2="-1379.1831" gradientTransform="matrix(4.73 0 0 34.85 19188.4746 48291.8477)">
<stop offset="0" style="stop-color:#2AFADF"/>
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.8"/>
</linearGradient>
<path style="fill:url(#SVGID_00000025411509199572341930000014536893160322097330_);" d="M242.5,233.6l-1.3,0.7
c-0.9,0.4-1.8,0.9-2.7,1.3l-0.1-24.8l4.1-2.1V233.6z"/>
</g>
<linearGradient id="Shape-33_00000137132446951879004030000008177934085669183874_" gradientUnits="userSpaceOnUse" x1="247.2798" y1="-966.5903" x2="243.775" y2="-1045.7864" gradientTransform="matrix(1 0 0 -1 0 -782)">
<stop offset="0" style="stop-color:#30E4E5"/>
<stop offset="1" style="stop-color:#2AFADF;stop-opacity:0"/>
</linearGradient>
<path id="Shape-33" style="opacity:0.2;fill:url(#Shape-33_00000137132446951879004030000008177934085669183874_);enable-background:new ;" d="
M277.1,199.4v95.5l-67.2,39.4v-89.2c0.3,2,2.3,3.2,4.7,2.6c7.2-1.6,14.3-3.8,21.2-6.6c3.4-1.4,6.8-2.8,10-4.5
C265.8,226.3,276.2,212.8,277.1,199.4z"/>
<linearGradient id="Shape-34_00000017487326329354270790000004505123412309728175_" gradientUnits="userSpaceOnUse" x1="-4586.5684" y1="-1389.8807" x2="-4591.4204" y2="-1389.8807" gradientTransform="matrix(27.05 0 0 59.5 124361.1797 82796.8516)">
<stop offset="0" style="stop-color:#4C83FF"/>
<stop offset="1" style="stop-color:#2AFADF"/>
</linearGradient>
<path id="Shape-34" style="opacity:0.2;fill:url(#Shape-34_00000017487326329354270790000004505123412309728175_);enable-background:new ;" d="
M249,119.2c-0.7-0.3-1.4-0.8-2.2-1.1c-1.2-0.5-2.2-3-2.2-5.3V79.4c0-2.4,1-3.8,2.2-3.3c0.8,0.3,1.4,0.7,2.2,1.1
c24,12.3,25.6,31.5,4.7,44.7C252.1,120.9,250.5,120.1,249,119.2z"/>
<linearGradient id="Shape-35_00000182508484054447651870000016260184141608580785_" gradientUnits="userSpaceOnUse" x1="-4667.9175" y1="-1396.4476" x2="-4665.9023" y2="-1396.4476" gradientTransform="matrix(73.17 0 0 97.58 341653.125 136403.875)">
<stop offset="0" style="stop-color:#4C83FF"/>
<stop offset="1" style="stop-color:#2AFADF"/>
</linearGradient>
<path id="Shape-35" style="opacity:0.3;fill:url(#Shape-35_00000182508484054447651870000016260184141608580785_);enable-background:new ;" d="
M268.2,142.8c0.1,8.5-6.2,17.1-18.9,23.7c-1.9,1-3.9,1.9-6,2.7c-7.8,3.1-15.9,5-24.2,6c-3.3,0.4-6.6,0.7-10,0.9
c-2.4,0.2-4.6-1.4-4.8-3.8c0-0.1,0-0.2,0-0.3v-33.5c0-2.4,2.2-4.3,4.8-4.4c3.3-0.1,6.7-0.4,10-0.9c8.3-1,16.4-3.1,24.2-6.1
c2.1-0.9,4.2-1.8,6-2.7c12.7-6.5,18.9-15,18.9-23.5V142.8z"/>
<linearGradient id="Shape-36_00000134951791006757682060000002162209820624686987_" gradientUnits="userSpaceOnUse" x1="225.9564" y1="-904.1375" x2="222.7145" y2="-937.75" gradientTransform="matrix(1 0 0 -1 0 -782)">
<stop offset="0" style="stop-color:#2AFADF"/>
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.8"/>
</linearGradient>
<path id="Shape-36" style="fill:url(#Shape-36_00000134951791006757682060000002162209820624686987_);" d="M234.1,150.1
c0,6.4-5,14.1-11.4,17.3s-11.4,0.7-11.4-5.7s5-14.1,11.4-17.3C229,141.2,234.1,143.7,234.1,150.1z M222.7,163.8
c4.3-2.2,7.7-7.4,7.7-11.7s-3.5-6-7.8-3.9s-7.8,7.4-7.7,11.7C215,164.1,218.4,165.9,222.7,163.8L222.7,163.8z"/>
<linearGradient id="Shape-37_00000106120806841072813860000004463379643489448371_" gradientUnits="userSpaceOnUse" x1="-4573.8359" y1="-1375.6361" x2="-4572.96" y2="-1375.6361" gradientTransform="matrix(24.13 0 0 31.7 110559.8047 43764.2852)">
<stop offset="0" style="stop-color:#2AFADF"/>
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.8"/>
</linearGradient>
<path id="Shape-37" style="fill:url(#Shape-37_00000106120806841072813860000004463379643489448371_);" d="M229.4,156.3
c-1.3,3.2-3.7,5.7-6.7,7.4c-4.3,2.2-7.8,0.4-7.8-3.9c0-4.4,3.4-9.6,7.7-11.7l0,0v-3.7l0,0c-6.2,3.2-11.4,11-11.4,17.3
c0,6.4,5.1,9,11.4,5.7c4.3-2.4,7.7-6,9.7-10.5L229.4,156.3z"/>
<linearGradient id="Shape-38_00000103972781162050256340000014581144695034338738_" gradientUnits="userSpaceOnUse" x1="-4570.1318" y1="-1373.7581" x2="-4568.1167" y2="-1373.7581" gradientTransform="matrix(23.4 0 0 30.11 107153.7891 41508.875)">
<stop offset="0" style="stop-color:#2AFADF"/>
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.8"/>
</linearGradient>
<path id="Shape-38" style="fill:url(#Shape-38_00000103972781162050256340000014581144695034338738_);" d="M262,139.8
c0,5.7-4.5,12.7-10.2,15.6c-5.7,2.8-10.3,0.5-10.3-5.1s4.5-12.7,10.2-15.6C257.4,131.8,262,134.1,262,139.8z M251.8,152
c3.8-2,6.9-6.7,6.9-10.6c0-3.9-3.2-5.5-7-3.5s-6.9,6.7-6.9,10.6C244.8,152.5,248,154,251.8,152z"/>
<linearGradient id="Shape-39_00000025423177438856170250000006332888040414474649_" gradientUnits="userSpaceOnUse" x1="-4426.6255" y1="-1371.6255" x2="-4425.7603" y2="-1371.6255" gradientTransform="matrix(11.7 0 0 28.37 52017.5625 39058.5156)">
<stop offset="0" style="stop-color:#2AFADF"/>
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.8"/>
</linearGradient>
<path id="Shape-39" style="fill:url(#Shape-39_00000025423177438856170250000006332888040414474649_);" d="M251.7,134.6
c-5.7,2.8-10.2,9.9-10.2,15.6c0,3.5,1.8,5.8,4.4,6.2l1.8-3.7c-1.8-0.4-2.8-1.9-2.8-4.3c0-3.9,3.1-8.7,6.9-10.6l0,0L251.7,134.6
L251.7,134.6z"/>
<linearGradient id="Shape-40_00000021118273186128926250000015044912358265521326_" gradientUnits="userSpaceOnUse" x1="-4665.3408" y1="-1397.1777" x2="-4662.8545" y2="-1397.1777" gradientTransform="matrix(69.21 0 0 106.39 322845.875 148760.2031)">
<stop offset="0" style="stop-color:#4C83FF"/>
<stop offset="1" style="stop-color:#2AFADF"/>
</linearGradient>
<path id="Shape-40" style="opacity:0.3;fill:url(#Shape-40_00000021118273186128926250000015044912358265521326_);enable-background:new ;" d="
M121.7,107.5v44.9c0,2.4-2.1,3.7-4.6,2.8c-9.6-3.1-19.1-6.8-28.1-11.4c-8.8-4.4-17-9.7-24.4-16c-2-2-3.3-4.6-3.4-7.3V75.6
c0-2.4,1.5-2.8,3.4-1.2C72,80.6,80.3,86,89,90.4c9.1,4.6,18.5,8.3,28.1,11.4C119.6,102.5,121.5,104.8,121.7,107.5z"/>
<linearGradient id="Shape-41_00000005975336679041812930000005759237068217401271_" gradientUnits="userSpaceOnUse" x1="138.8851" y1="-878.1784" x2="67.1695" y2="-905.581" gradientTransform="matrix(1 0 0 -1 0 -782)">
<stop offset="0" style="stop-color:#2AFADF"/>
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.8"/>
</linearGradient>
<path id="Shape-41" style="fill:url(#Shape-41_00000005975336679041812930000005759237068217401271_);" d="M85.1,101.2
c8.4,4.3,15.3,14.8,15.3,23.3s-6.9,12-15.3,7.8S69.8,117.5,69.8,109S76.7,96.9,85.1,101.2z M85,127.3c5.7,3,10.4,0.5,10.4-5.3
s-4.6-12.9-10.4-15.9s-10.4-0.5-10.4,5.3S79.3,124.4,85,127.3L85,127.3z"/>
<linearGradient id="Shape-42_00000122712814098058905890000007017593085226539443_" gradientUnits="userSpaceOnUse" x1="-4521.1196" y1="-1373.542" x2="-4520.2432" y2="-1373.542" gradientTransform="matrix(17.49 0 0 30.28 79147.5156 41703.7031)">
<stop offset="0" style="stop-color:#2AFADF"/>
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0.8"/>
</linearGradient>
<path id="Shape-42" style="fill:url(#Shape-42_00000122712814098058905890000007017593085226539443_);" d="M85.1,101.2
c8.4,4.3,15.3,14.8,15.3,23.3l-4.9-2.5c0-5.8-4.6-12.9-10.4-15.9V101.2z"/>
<linearGradient id="Shape-43_00000030453719429377122740000013557076259559090617_" gradientUnits="userSpaceOnUse" x1="109.0421" y1="-913.4399" x2="80.9496" y2="-976.8318" gradientTransform="matrix(1 0 0 -1 0 -782)">
<stop offset="0" style="stop-color:#30E4E5"/>
<stop offset="0.6" style="stop-color:#2AFADF;stop-opacity:0"/>
</linearGradient>
<path id="Shape-43" style="opacity:0.2;fill:url(#Shape-43_00000030453719429377122740000013557076259559090617_);enable-background:new ;" d="
M121.7,152.3V222l-60.6-29.8v-71.7c0.1,2.7,1.4,5.4,3.4,7.3c7.4,6.2,15.7,11.6,24.4,16c9.1,4.6,18.5,8.3,28.1,11.4
C119.6,156,121.7,154.7,121.7,152.3z"/>
<linearGradient id="Shape-44_00000176727814201571328480000011766605898265965704_" gradientUnits="userSpaceOnUse" x1="249.2" y1="-1232.5" x2="287.6426" y2="-1232.5" gradientTransform="matrix(1 0 0 -1 0 -782)">
<stop offset="0" style="stop-color:#91BFDB"/>
<stop offset="0.6" style="stop-color:#A8E9EB;stop-opacity:0.5"/>
<stop offset="1" style="stop-color:#EFF3F6;stop-opacity:0"/>
</linearGradient>
<polygon id="Shape-44" style="fill:url(#Shape-44_00000176727814201571328480000011766605898265965704_);" points="249.2,460.7
271,457.3 287.6,446.1 274.3,440.3 "/>
<linearGradient id="SVGID_00000042695819913126177250000000172513830783411371_" gradientUnits="userSpaceOnUse" x1="-4654.0396" y1="-1402.9192" x2="-4653.481" y2="-1403.2366" gradientTransform="matrix(75.74 0 0 55.03 352723.9688 77637.6016)">
<stop offset="1.000000e-02" style="stop-color:#009FFC"/>
<stop offset="0.56" style="stop-color:#1595F4"/>
<stop offset="1" style="stop-color:#2090F0"/>
</linearGradient>
<polygon style="fill:url(#SVGID_00000042695819913126177250000000172513830783411371_);" points="244.6,425.2 249.2,433.2
279.7,415.2 275.4,407.6 "/>
<linearGradient id="SVGID_00000117638879148804653300000015399300247122409869_" gradientUnits="userSpaceOnUse" x1="-4644.7964" y1="-1405.0366" x2="-4644.709" y2="-1404.4231" gradientTransform="matrix(65.77 0 0 97.87 305750.0312 137920.9688)">
<stop offset="1.000000e-02" style="stop-color:#00C6FB"/>
<stop offset="0.12" style="stop-color:#00B6F8"/>
<stop offset="0.52" style="stop-color:#0085F1"/>
<stop offset="0.83" style="stop-color:#0066EC"/>
<stop offset="1" style="stop-color:#005BEA"/>
</linearGradient>
<polygon style="fill:url(#SVGID_00000117638879148804653300000015399300247122409869_);" points="249.2,433.2 249.2,460.7
279.7,440.7 279.7,415.2 "/>
<linearGradient id="SVGID_00000085224804945207263520000014360999943671216563_" gradientUnits="userSpaceOnUse" x1="-4353.3867" y1="-1395.6317" x2="-4353.4966" y2="-1396.1903" gradientTransform="matrix(12.39 0 0 17.46 54185.7148 24803.7129)">
<stop offset="1.000000e-02" style="stop-color:#55FFB5"/>
<stop offset="0.59" style="stop-color:#6DFFCB"/>
<stop offset="1" style="stop-color:#78FFD6"/>
</linearGradient>
<polyline style="fill:url(#SVGID_00000085224804945207263520000014360999943671216563_);" points="244.6,425.2 249.2,433.2
248.2,433.2 243.4,425.1 244.6,425.2 "/>
<linearGradient id="SVGID_00000057131615203601934700000015143495052675431324_" gradientUnits="userSpaceOnUse" x1="-4352.3335" y1="-1403.6678" x2="-4352.2788" y2="-1403.8651" gradientTransform="matrix(12.35 0 0 59.18 53998.4648 83513.9609)">
<stop offset="1.000000e-02" style="stop-color:#55FFB5"/>
<stop offset="0.59" style="stop-color:#6DFFCB"/>
<stop offset="1" style="stop-color:#78FFD6"/>
</linearGradient>
<polygon style="fill:url(#SVGID_00000057131615203601934700000015143495052675431324_);" points="248.2,433.2 248.2,459.4
243.5,456.5 243.5,457.3 249.2,460.7 249.2,433.2 "/>
<linearGradient id="SVGID_00000029010345686687337160000011462027730732656565_" gradientUnits="userSpaceOnUse" x1="-4281.1147" y1="-1393.7373" x2="-4280.3813" y2="-1394.3835" gradientTransform="matrix(10.3 0 0 11.87 44341.7695 17001.1699)">
<stop offset="1.000000e-02" style="stop-color:#00C6FB"/>
<stop offset="0.12" style="stop-color:#00B6F8"/>
<stop offset="0.52" style="stop-color:#0085F1"/>
<stop offset="0.83" style="stop-color:#0066EC"/>
<stop offset="1" style="stop-color:#005BEA"/>
</linearGradient>
<polygon style="fill:url(#SVGID_00000029010345686687337160000011462027730732656565_);" points="243.5,456.5 248.2,453.9
248.2,459.4 "/>
<linearGradient id="Shape-45_00000098906919044303374130000006733002538008474547_" gradientUnits="userSpaceOnUse" x1="-4647.6909" y1="-1401.2917" x2="-4647.1543" y2="-1401.2148" gradientTransform="matrix(68.73 0 0 38.94 319689.0938 54981.9297)">
<stop offset="1.000000e-02" style="stop-color:#00C6FB"/>
<stop offset="0.12" style="stop-color:#00B6F8"/>
<stop offset="0.52" style="stop-color:#0085F1"/>
<stop offset="0.83" style="stop-color:#0066EC"/>
<stop offset="1" style="stop-color:#005BEA"/>
</linearGradient>
<polygon id="Shape-45" style="fill:url(#Shape-45_00000098906919044303374130000006733002538008474547_);" points="275.4,407.6
274.7,407.1 243.4,425.1 244.6,425.2 "/>
<linearGradient id="Shape-46_00000155104190746461959110000008164066156702385284_" gradientUnits="userSpaceOnUse" x1="-4651.0366" y1="-1404.4684" x2="-4650.7954" y2="-1404.1398" gradientTransform="matrix(72.46 0 0 96.76 337260.875 136272.1094)">
<stop offset="1.000000e-02" style="stop-color:#55FFB5"/>
<stop offset="0.59" style="stop-color:#6DFFCB"/>
<stop offset="1" style="stop-color:#78FFD6"/>
</linearGradient>
<path id="Shape-46" style="fill:url(#Shape-46_00000155104190746461959110000008164066156702385284_);" d="M243.4,420.8l31.2-18.1
c0.7-0.4,1.1-1.2,1.1-2l0.3-23.9c0-0.8-0.4-1.1-1-0.8l-31.2,18.1c-0.7,0.4-1,1.2-1,2l-0.3,23.9
C242.4,420.8,242.8,421.1,243.4,420.8z"/>
<g id="Shape-47" class="st40">
<linearGradient id="SVGID_00000101076599354358181930000006343129837226329520_" gradientUnits="userSpaceOnUse" x1="-4352.7603" y1="-1398.1875" x2="-4352.8589" y2="-1397.7163" gradientTransform="matrix(12.28 0 0 25.58 53724.875 36168.9766)">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="1" style="stop-color:#FFFFFF"/>
</linearGradient>
<path style="fill:url(#SVGID_00000101076599354358181930000006343129837226329520_);" d="M275.8,393.5c0,0,0,0.9,0,2.3
c0,0.7,0,1.5,0,2.4c0,0.4,0,0.9,0,1.3s0,0.9,0,1.4c0,0.4-0.2,1-0.4,1.3c-0.1,0.2-0.3,0.3-0.5,0.5c-0.2,0.1-0.4,0.2-0.5,0.3
l-2.1,1.2l-2.1,1.1l2-1.2l2.1-1.2c0.4-0.2,0.8-0.4,1-0.8c0.2-0.3,0.4-0.8,0.4-1.2c0-0.4,0-1,0-1.4s0-0.9,0-1.3
c0-0.9,0-1.6,0.1-2.4C275.8,394.4,275.8,393.5,275.8,393.5z"/>
</g>
<linearGradient id="SVGID_00000025437517784366984770000010747534483800912309_" gradientUnits="userSpaceOnUse" x1="-4462.6445" y1="-1386.6772" x2="-4462.1733" y2="-1386.6772" gradientTransform="matrix(17.68 0 0 11.8 79164.0781 16746.4414)">
<stop offset="1.000000e-02" style="stop-color:#00C6FB"/>
<stop offset="0.12" style="stop-color:#00B6F8"/>
<stop offset="0.52" style="stop-color:#0085F1"/>
<stop offset="0.83" style="stop-color:#0066EC"/>
<stop offset="1" style="stop-color:#005BEA"/>
</linearGradient>
<polygon style="fill:url(#SVGID_00000025437517784366984770000010747534483800912309_);" points="274,381.6 265.8,386.4
265.8,385.6 274,380.9 "/>
<linearGradient id="SVGID_00000014603506014971141140000003413487988091619970_" gradientUnits="userSpaceOnUse" x1="-4392.3994" y1="-1381.9902" x2="-4391.9395" y2="-1381.9902" gradientTransform="matrix(13.8 0 0 9.56 60880.3164 13597.9766)">
<stop offset="1.000000e-02" style="stop-color:#00C6FB"/>
<stop offset="0.12" style="stop-color:#00B6F8"/>
<stop offset="0.52" style="stop-color:#0085F1"/>
<stop offset="0.83" style="stop-color:#0066EC"/>
<stop offset="1" style="stop-color:#005BEA"/>
</linearGradient>
<polygon style="fill:url(#SVGID_00000014603506014971141140000003413487988091619970_);" points="272.3,384.7 265.8,388.4
265.8,387.6 272.3,383.9 "/>
<linearGradient id="SVGID_00000057835095209813497070000006025639477437015717_" gradientUnits="userSpaceOnUse" x1="-4392.3994" y1="-1382.2275" x2="-4391.9395" y2="-1382.2275" gradientTransform="matrix(13.8 0 0 9.56 60880.3164 13602.2461)">
<stop offset="1.000000e-02" style="stop-color:#00C6FB"/>
<stop offset="0.12" style="stop-color:#00B6F8"/>
<stop offset="0.52" style="stop-color:#0085F1"/>
<stop offset="0.83" style="stop-color:#0066EC"/>
<stop offset="1" style="stop-color:#005BEA"/>
</linearGradient>
<polygon style="fill:url(#SVGID_00000057835095209813497070000006025639477437015717_);" points="272.3,386.6 265.8,390.4
265.8,389.6 272.3,385.9 "/>
<linearGradient id="SVGID_00000112619206826002184200000001160212017524668599_" gradientUnits="userSpaceOnUse" x1="-4392.3994" y1="-1382.4702" x2="-4391.9395" y2="-1382.4702" gradientTransform="matrix(13.8 0 0 9.56 60880.3164 13606.5156)">
<stop offset="1.000000e-02" style="stop-color:#00C6FB"/>
<stop offset="0.12" style="stop-color:#00B6F8"/>
<stop offset="0.52" style="stop-color:#0085F1"/>
<stop offset="0.83" style="stop-color:#0066EC"/>
<stop offset="1" style="stop-color:#005BEA"/>
</linearGradient>
<polygon style="fill:url(#SVGID_00000112619206826002184200000001160212017524668599_);" points="272.3,388.6 265.8,392.3
265.8,391.6 272.3,387.9 "/>
<linearGradient id="SVGID_00000014619903439646413060000003573965781630823350_" gradientUnits="userSpaceOnUse" x1="-4462.6445" y1="-1387.7568" x2="-4462.1733" y2="-1387.7568" gradientTransform="matrix(17.68 0 0 11.8 79164.0781 16769.0801)">
<stop offset="1.000000e-02" style="stop-color:#00C6FB"/>
<stop offset="0.12" style="stop-color:#00B6F8"/>
<stop offset="0.52" style="stop-color:#0085F1"/>
<stop offset="0.83" style="stop-color:#0066EC"/>
<stop offset="1" style="stop-color:#005BEA"/>
</linearGradient>
<polygon style="fill:url(#SVGID_00000014619903439646413060000003573965781630823350_);" points="274,391.6 265.8,396.3
265.8,395.6 274,390.8 "/>
<linearGradient id="SVGID_00000135687018638205079420000002421485263476320680_" gradientUnits="userSpaceOnUse" x1="-4392.3994" y1="-1383.1858" x2="-4391.9395" y2="-1383.1858" gradientTransform="matrix(13.8 0 0 9.56 60880.3164 13619.3066)">
<stop offset="1.000000e-02" style="stop-color:#00C6FB"/>
<stop offset="0.12" style="stop-color:#00B6F8"/>
<stop offset="0.52" style="stop-color:#0085F1"/>
<stop offset="0.83" style="stop-color:#0066EC"/>
<stop offset="1" style="stop-color:#005BEA"/>
</linearGradient>
<polygon style="fill:url(#SVGID_00000135687018638205079420000002421485263476320680_);" points="272.3,394.6 265.8,398.2
265.8,397.6 272.3,393.9 "/>
<linearGradient id="SVGID_00000033353309158504731440000001683089570208010429_" gradientUnits="userSpaceOnUse" x1="-4392.3994" y1="-1383.4285" x2="-4391.9395" y2="-1383.4285" gradientTransform="matrix(13.8 0 0 9.56 60880.3164 13623.5762)">
<stop offset="1.000000e-02" style="stop-color:#00C6FB"/>
<stop offset="0.12" style="stop-color:#00B6F8"/>
<stop offset="0.52" style="stop-color:#0085F1"/>
<stop offset="0.83" style="stop-color:#0066EC"/>
<stop offset="1" style="stop-color:#005BEA"/>
</linearGradient>
<polygon style="fill:url(#SVGID_00000033353309158504731440000001683089570208010429_);" points="272.3,396.6 265.8,400.2
265.8,399.6 272.3,395.8 "/>
<linearGradient id="SVGID_00000129923475701508823520000005680875115117715122_" gradientUnits="userSpaceOnUse" x1="-4392.3994" y1="-1383.6606" x2="-4391.9395" y2="-1383.6606" gradientTransform="matrix(13.8 0 0 9.56 60880.3164 13627.8457)">
<stop offset="1.000000e-02" style="stop-color:#00C6FB"/>
<stop offset="0.12" style="stop-color:#00B6F8"/>
<stop offset="0.52" style="stop-color:#0085F1"/>
<stop offset="0.83" style="stop-color:#0066EC"/>
<stop offset="1" style="stop-color:#005BEA"/>
</linearGradient>
<polygon style="fill:url(#SVGID_00000129923475701508823520000005680875115117715122_);" points="272.3,398.6 265.8,402.3
265.8,401.5 272.3,397.8 "/>
<linearGradient id="Shape-48_00000020372391056104230850000014324465669750481819_" gradientUnits="userSpaceOnUse" x1="-4538.2388" y1="-1403.8259" x2="-4538.3047" y2="-1403.3442" gradientTransform="matrix(25.5 0 0 71.67 115982.2188 100989.3438)">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0"/>
</linearGradient>
<polygon id="Shape-48" style="opacity:0.4;fill:url(#Shape-48_00000020372391056104230850000014324465669750481819_);enable-background:new ;" points="
259.7,385 250.1,417 252,415.9 261.9,383.7 "/>
<linearGradient id="Shape-49_00000158746474549493822570000018370522913050869146_" gradientUnits="userSpaceOnUse" x1="-4587.5259" y1="-1404.0281" x2="-4587.6245" y2="-1403.5239" gradientTransform="matrix(35.56 0 0 77.83 163397.2344 109641.4844)">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="1" style="stop-color:#FFFFFF;stop-opacity:0"/>
</linearGradient>
<polygon id="Shape-49" style="opacity:0.4;fill:url(#Shape-49_00000158746474549493822570000018370522913050869146_);enable-background:new ;" points="
263.6,382.7 253.8,414.9 260.3,411.1 270.3,378.6 "/>
<g class="st108">
<polygon class="st109" points="262.4,390.4 247.8,398.8 247.8,398.7 262.4,390.2 "/>
</g>
<g class="st108">
<polygon class="st109" points="262.4,393.4 247.8,402 247.8,401.9 262.4,393.3 "/>
</g>
<g class="st108">
<polygon class="st109" points="262.4,396.6 247.8,405.1 247.8,405 262.4,396.5 "/>
</g>
<g class="st108">
<polygon class="st109" points="262.4,399.8 247.8,408.3 247.8,408.1 262.4,399.7 "/>
</g>
<g class="st108">
<polygon class="st109" points="262.4,403 247.8,411.4 247.8,411.3 262.4,402.8 "/>
</g>
<linearGradient id="SVGID_00000076567543439310601720000004045448953772141723_" gradientUnits="userSpaceOnUse" x1="-3294.0398" y1="-1400.6531" x2="-3294.0398" y2="-1401.113" gradientTransform="matrix(3.13 0 0 37.05 10559.5449 52306.3789)">
<stop offset="1.000000e-02" style="stop-color:#00C6FB"/>
<stop offset="0.12" style="stop-color:#00B6F8"/>
<stop offset="0.52" style="stop-color:#0085F1"/>
<stop offset="0.83" style="stop-color:#0066EC"/>
<stop offset="1" style="stop-color:#005BEA"/>
</linearGradient>
<polygon style="fill:url(#SVGID_00000076567543439310601720000004045448953772141723_);" points="249.9,413.6 248.5,414.5
248.5,398 249.9,397.1 "/>
<linearGradient id="SVGID_00000079488291248167022160000006589743079914859678_" gradientUnits="userSpaceOnUse" x1="-3294.5764" y1="-1396.5031" x2="-3294.5764" y2="-1396.0323" gradientTransform="matrix(3.13 0 0 20.21 10562.6748 28634.0879)">
<stop offset="1.000000e-02" style="stop-color:#009FFC"/>
<stop offset="0.56" style="stop-color:#1595F4"/>
<stop offset="1" style="stop-color:#2090F0"/>
</linearGradient>
<polygon style="fill:url(#SVGID_00000079488291248167022160000006589743079914859678_);" points="251.4,412.7 249.9,413.6
249.9,405 251.4,404.2 "/>
<linearGradient id="SVGID_00000067926720751569274880000009097307530463908492_" gradientUnits="userSpaceOnUse" x1="-3295.7585" y1="-1398.5204" x2="-3295.7585" y2="-1398.9913" gradientTransform="matrix(3.13 0 0 26.95 10569.625 38095.2344)">
<stop offset="1.000000e-02" style="stop-color:#00C6FB"/>
<stop offset="0.12" style="stop-color:#00B6F8"/>
<stop offset="0.52" style="stop-color:#0085F1"/>
<stop offset="0.83" style="stop-color:#0066EC"/>
<stop offset="1" style="stop-color:#005BEA"/>
</linearGradient>
<polygon style="fill:url(#SVGID_00000067926720751569274880000009097307530463908492_);" points="254.7,410.8 253.1,411.7
253.1,400 254.7,399.1 "/>
<linearGradient id="SVGID_00000000212797735812424900000012527227039377536170_" gradientUnits="userSpaceOnUse" x1="-3296.2793" y1="-1401.8259" x2="-3296.2793" y2="-1401.366" gradientTransform="matrix(3.13 0 0 43.78 10572.7549 61771.4961)">
<stop offset="1.000000e-02" style="stop-color:#009FFC"/>
<stop offset="0.56" style="stop-color:#1595F4"/>
<stop offset="1" style="stop-color:#2090F0"/>
</linearGradient>
<polygon style="fill:url(#SVGID_00000000212797735812424900000012527227039377536170_);" points="256.1,410.1 254.7,410.8
254.7,391.3 256.1,390.5 "/>
<linearGradient id="SVGID_00000098184852967546321720000017918665523034777504_" gradientUnits="userSpaceOnUse" x1="-3297.3301" y1="-1390.7391" x2="-3297.3301" y2="-1391.1991" gradientTransform="matrix(3.13 0 0 13.48 10579.2441 19151.5078)">
<stop offset="1.000000e-02" style="stop-color:#00C6FB"/>
<stop offset="0.12" style="stop-color:#00B6F8"/>
<stop offset="0.52" style="stop-color:#0085F1"/>
<stop offset="0.83" style="stop-color:#0066EC"/>
<stop offset="1" style="stop-color:#005BEA"/>
</linearGradient>
<polygon style="fill:url(#SVGID_00000098184852967546321720000017918665523034777504_);" points="259.4,408.1 257.8,409
257.8,403.6 259.4,402.7 "/>
<linearGradient id="SVGID_00000099643791442660283550000003955445333089008051_" gradientUnits="userSpaceOnUse" x1="-3297.998" y1="-1400.3435" x2="-3297.998" y2="-1399.8726" gradientTransform="matrix(3.13 0 0 33.68 10582.835 47560.3516)">
<stop offset="1.000000e-02" style="stop-color:#009FFC"/>
<stop offset="0.56" style="stop-color:#1595F4"/>
<stop offset="1" style="stop-color:#2090F0"/>
</linearGradient>
<polygon style="fill:url(#SVGID_00000099643791442660283550000003955445333089008051_);" points="260.8,407.3 259.4,408.1
259.4,393.3 260.8,392.5 "/>
<linearGradient id="SVGID_00000139981804617803399270000013206585361592540318_" gradientUnits="userSpaceOnUse" x1="-4573.7271" y1="-1401.5767" x2="-4573.2671" y2="-1401.5767" gradientTransform="matrix(31.99 0 0 43.44 146560.3125 61289.4375)">
<stop offset="1.000000e-02" style="stop-color:#00C6FB"/>
<stop offset="0.12" style="stop-color:#00B6F8"/>
<stop offset="0.52" style="stop-color:#0085F1"/>
<stop offset="0.83" style="stop-color:#0066EC"/>
<stop offset="1" style="stop-color:#005BEA"/>
</linearGradient>
<polygon style="fill:url(#SVGID_00000139981804617803399270000013206585361592540318_);" points="262.4,406.5 247.5,415.1
247.5,395 247.6,394.8 247.6,414.9 262.4,406.3 "/>
<linearGradient id="Shape-50_00000140719076435951959120000006641442635192087477_" gradientUnits="userSpaceOnUse" x1="-4655.9185" y1="-1402.9375" x2="-4655.9624" y2="-1403.0032" gradientTransform="matrix(78.92 0 0 57.93 367701.5 81679.6797)">
<stop offset="0" style="stop-color:#0D55FF;stop-opacity:0"/>
<stop offset="0" style="stop-color:#0D55FF;stop-opacity:0"/>
<stop offset="0.65" style="stop-color:#04CEFF;stop-opacity:0.14"/>
<stop offset="1" style="stop-color:#00FCFF;stop-opacity:0.2"/>
</linearGradient>
<polygon id="Shape-50" style="fill:url(#Shape-50_00000140719076435951959120000006641442635192087477_);" points="244.6,425.2
243.4,425.1 240.4,419.8 277,398.2 275.4,407.6 "/>
<linearGradient id="Shape-51_00000147939181360286582920000004146921999076484486_" gradientUnits="userSpaceOnUse" x1="-4353.4653" y1="-1402.2626" x2="-4353.4541" y2="-1401.704" gradientTransform="matrix(12.39 0 0 31.04 54185.7031 43981.7422)">
<stop offset="0" style="stop-color:#FFFFFF"/>
<stop offset="1" style="stop-color:#EFF3F6;stop-opacity:0"/>
</linearGradient>
<polyline id="Shape-51" style="fill:url(#Shape-51_00000147939181360286582920000004146921999076484486_);" points="249.2,471.7
249.2,460.7 243.5,457.3 243.4,458.3 248.1,461 "/>
<linearGradient id="Shape-52_00000133514124685140248980000012016277643264497070_" gradientUnits="userSpaceOnUse" x1="-4650.8057" y1="-1400.7756" x2="-4650.313" y2="-1400.7756" gradientTransform="matrix(71.6 0 0 27.35 333266.75 38764.7188)">
<stop offset="0" style="stop-color:#91BFDB"/>
<stop offset="0.6" style="stop-color:#A8E9EB;stop-opacity:0.5"/>
<stop offset="1" style="stop-color:#EFF3F6;stop-opacity:0"/>
</linearGradient>
<path id="Shape-52" style="fill:url(#Shape-52_00000133514124685140248980000012016277643264497070_);" d="M271.6,457.3
c2.3-0.1,5.8-2.3,30.2-1.5c3.6-1.2,2.3-4.6,1.1-5.7c-1.2-1.1-12.5,0.4-16.4,1.2c-3.9,0.8-15,3.2-15.4,5.4"/>
<g id="Shape-53">
<linearGradient id="SVGID_00000054984489780984634950000007063448993696825000_" gradientUnits="userSpaceOnUse" x1="-4313.8755" y1="-1382.7407" x2="-4314.3354" y2="-1382.7407" gradientTransform="matrix(11.1 0 0 6.07 48165.1211 8850.4492)">
<stop offset="0" style="stop-color:#2A2ED0"/>
<stop offset="0.1" style="stop-color:#3133D1"/>
<stop offset="0.25" style="stop-color:#433FD6"/>
<stop offset="0.42" style="stop-color:#6255DC"/>
<stop offset="0.61" style="stop-color:#8C72E6"/>
<stop offset="0.81" style="stop-color:#C297F2"/>
<stop offset="1" style="stop-color:#FCBFFF"/>
</linearGradient>
<path style="fill:url(#SVGID_00000054984489780984634950000007063448993696825000_);" d="M272.9,457.5c0,0,0.1,0.7-0.2,1
c-0.3,0.3-1.8,0.1-2.7-0.4c-0.8-0.3-1.5-0.8-2.2-1.4c-0.1-0.3,0-0.7,0.2-0.9C268.2,455.6,272.9,457.5,272.9,457.5z"/>
<linearGradient id="SVGID_00000080918987833476237920000016819689408787064764_" gradientUnits="userSpaceOnUse" x1="-4299.2207" y1="-1380.0359" x2="-4299.0454" y2="-1379.6963" gradientTransform="matrix(10.7 0 0 5.64 46274.793 8242.5244)">
<stop offset="0.1" style="stop-color:#89F7FE"/>
<stop offset="0.28" style="stop-color:#87F2FE"/>
<stop offset="0.5" style="stop-color:#81E4FE"/>
<stop offset="0.73" style="stop-color:#76CCFF"/>
<stop offset="0.97" style="stop-color:#68ABFF"/>
<stop offset="1" style="stop-color:#66A6FF"/>
</linearGradient>
<path style="fill:url(#SVGID_00000080918987833476237920000016819689408787064764_);" d="M272.7,456.4c0,0,0.7,1.2-0.2,1.6
c-1.1,0.4-4.9-1.4-4.5-2.2c0.3-0.5,1.2-0.3,2.5-0.3C271.4,455.5,272.2,455.9,272.7,456.4z"/>
<linearGradient id="SVGID_00000160875980869004348870000016218684430427734656_" gradientUnits="userSpaceOnUse" x1="-4314.251" y1="-1382.3303" x2="-4315.248" y2="-1382.3523" gradientTransform="matrix(11.1 0 0 6.07 48178.8828 8845.9297)">
<stop offset="0" style="stop-color:#2A2ED0"/>
<stop offset="0.1" style="stop-color:#3133D1"/>
<stop offset="0.25" style="stop-color:#433FD6"/>
<stop offset="0.42" style="stop-color:#6255DC"/>
<stop offset="0.61" style="stop-color:#8C72E6"/>
<stop offset="0.81" style="stop-color:#C297F2"/>
<stop offset="1" style="stop-color:#FCBFFF"/>
</linearGradient>
<path style="fill:url(#SVGID_00000160875980869004348870000016218684430427734656_);" d="M279.3,455.3c0,0,0.1,0.7-0.2,1
c-0.3,0.3-1.8,0.1-2.7-0.4c-0.8-0.3-1.5-0.9-2.2-1.4c-0.1-0.3,0-0.7,0.2-0.9C274.6,453.4,279.3,455.3,279.3,455.3z"/>
<linearGradient id="SVGID_00000138549545104016646050000011419445232965228706_" gradientUnits="userSpaceOnUse" x1="-4300.1392" y1="-1379.9625" x2="-4300.9165" y2="-1379.5682" gradientTransform="matrix(10.7 0 0 5.64 46288.5508 8237.4941)">
<stop offset="0.1" style="stop-color:#89F7FE"/>
<stop offset="0.28" style="stop-color:#87F2FE"/>
<stop offset="0.5" style="stop-color:#81E4FE"/>
<stop offset="0.73" style="stop-color:#76CCFF"/>
<stop offset="0.97" style="stop-color:#68ABFF"/>
<stop offset="1" style="stop-color:#66A6FF"/>
</linearGradient>
<path style="fill:url(#SVGID_00000138549545104016646050000011419445232965228706_);" d="M279.1,454.2c0,0,0.7,1.2-0.2,1.6
c-1.1,0.4-4.9-1.4-4.5-2.2c0.3-0.5,1.2-0.3,2.5-0.3C277.8,453.3,278.5,453.7,279.1,454.2z"/>
<linearGradient id="SVGID_00000012439461484347364750000007568484484384575618_" gradientUnits="userSpaceOnUse" x1="-4509.1016" y1="-1403.5773" x2="-4509.123" y2="-1404.2782" gradientTransform="matrix(21.74 0 0 66.22 98302.5234 93415.1875)">
<stop offset="0" style="stop-color:#1E2275"/>
<stop offset="1" style="stop-color:#0046BC"/>
</linearGradient>
<path style="fill:url(#SVGID_00000012439461484347364750000007568484484384575618_);" d="M279,426.5c0.5,2.6,0.5,5.3,0,7.9
c-0.7,2.6,0,19.9,0,19.9s-0.7,0.9-2.3,0.1c0,0-0.8-6.5-1-11.9c-0.1-3.9-0.2-7.2-0.2-7.8l-1.5,0.7c-0.5,2.7-0.9,5.5-1.1,8.3
c0,2.7-0.1,13-0.1,13c-0.5,0.4-1.4,0.4-2,0c0,0-1.2-10.8-1.5-13.7c-0.3-2.8,1-14.5,1-14.5S276,425.1,279,426.5z"/>
<path class="st125" d="M272.3,410c0,0,0.4,2.1,0.4,2.4s1.9-0.9,2.2-1.3l-0.3-2.3C273.7,409,272.9,409.3,272.3,410z"/>
<path class="st126" d="M272.3,410l0.4,2.4c0.5-0.7,0.9-1.5,1-2.3C273.7,410,273.7,408.4,272.3,410z"/>
<linearGradient id="SVGID_00000085217106529308597890000006205264978108892050_" gradientUnits="userSpaceOnUse" x1="-4097.0649" y1="-1373.2161" x2="-4097.5254" y2="-1373.2161" gradientTransform="matrix(7.17 0 0 5.93 29652.7383 8555.3711)">
<stop offset="1.000000e-02" style="stop-color:#6A9DFF"/>
<stop offset="0.27" style="stop-color:#4872EB"/>
<stop offset="0.6" style="stop-color:#2141D3"/>
<stop offset="0.86" style="stop-color:#0922C4"/>
<stop offset="1" style="stop-color:#0017BF"/>
</linearGradient>
<path style="fill:url(#SVGID_00000085217106529308597890000006205264978108892050_);" d="M275,410.8c0.2,0.2,0.4,0.5,0.7,0.8
c-0.7,1.2-2,2-3.3,2l0.4-1.3L275,410.8z"/>
<linearGradient id="SVGID_00000020394213504040997820000013097059723123130259_" gradientUnits="userSpaceOnUse" x1="-4503.46" y1="-1402.0055" x2="-4503.3394" y2="-1401.3702" gradientTransform="matrix(21.11 0 0 39.74 95340.4531 56123.2578)">
<stop offset="1.000000e-02" style="stop-color:#FB6583"/>
<stop offset="0.13" style="stop-color:#FC7688"/>
<stop offset="0.53" style="stop-color:#FDA797"/>
<stop offset="0.83" style="stop-color:#FFC6A0"/>
<stop offset="1" style="stop-color:#FFD1A3"/>
</linearGradient>
<path style="fill:url(#SVGID_00000020394213504040997820000013097059723123130259_);" d="M275.6,411.6c1,0,1.9,0.7,2.1,1.8
c0.5,2.6,1,5.4,1.2,8.1c0,1.5,0.7,4.5,0.4,5.7c-0.2,1-2,1.4-4.2,2.4c-1.6,0.9-3.7,0.8-5.4-0.2c-0.7-0.5,0.2-3.3,0.1-4.6
c-0.1-1.3-0.1-3.7,0-4.5s-0.1-4.3,2.4-6.6C273.3,412.8,274.4,412.2,275.6,411.6z"/>
<linearGradient id="SVGID_00000181058967435089559130000006669968388613110451_" gradientUnits="userSpaceOnUse" x1="-3775.5581" y1="-1390.2045" x2="-3775.6128" y2="-1389.3502" gradientTransform="matrix(4.71 0 0 29.57 18050.4395 41817.6797)">
<stop offset="1.000000e-02" style="stop-color:#FB6583"/>
<stop offset="0.13" style="stop-color:#FC7688"/>
<stop offset="0.53" style="stop-color:#FDA797"/>
<stop offset="0.83" style="stop-color:#FFC6A0"/>
<stop offset="1" style="stop-color:#FFD1A3"/>
</linearGradient>
<path style="opacity:0.3;fill:url(#SVGID_00000181058967435089559130000006669968388613110451_);enable-background:new ;" d="
M271.6,416.2c0,0-0.5,5.9-0.7,8c-0.1,1.5,0.4,5.4,0,5.7c-0.4-0.1-0.8-0.3-1.1-0.5c-0.9-0.5-0.1-3.2,0-3.9c0.1-0.8,0-2.7,0-4.3
c0-1.9,0.3-4.7,0.7-4.9C271,416.2,271.3,416.2,271.6,416.2z"/>
<linearGradient id="SVGID_00000122710278541649656810000007312387862833113729_" gradientUnits="userSpaceOnUse" x1="-4572.3774" y1="-1397.1755" x2="-4572.0815" y2="-1396.6389" gradientTransform="matrix(31.64 0 0 21.28 144939.8594 30149.8242)">
<stop offset="1.000000e-02" style="stop-color:#FB6583"/>
<stop offset="0.13" style="stop-color:#FC7688"/>
<stop offset="0.53" style="stop-color:#FDA797"/>
<stop offset="0.83" style="stop-color:#FFC6A0"/>
<stop offset="1" style="stop-color:#FFD1A3"/>
</linearGradient>
<path style="fill:url(#SVGID_00000122710278541649656810000007312387862833113729_);" d="M273.6,414.5c0,0,0.7,0.8-0.3,1.9
s-6.9,2.5-8.2,2.3c-3.7-0.7-6-8.5-6-8.5l1.6-1.3c0,0,2.8,7.3,5.4,6.9C269.1,415,272.3,412.6,273.6,414.5z"/>
<path class="st125" d="M270.2,405.4c0,0-0.8,4.5,0,5.3s2.8-0.2,2.8-0.2s2.3-1.4,1.3-2.8C273.4,406.1,270.2,405.4,270.2,405.4z"/>
<linearGradient id="SVGID_00000166643334475131779910000005720074573264916131_" gradientUnits="userSpaceOnUse" x1="-4412.3691" y1="-1392.0507" x2="-4412.5332" y2="-1392.5873" gradientTransform="matrix(14.75 0 0 18.16 65368.8008 25736.9492)">
<stop offset="0" style="stop-color:#1E2275"/>
<stop offset="1" style="stop-color:#0046BC"/>
</linearGradient>
<path style="fill:url(#SVGID_00000166643334475131779910000005720074573264916131_);" d="M275.6,410c0,0-1.8,1-2.3,0.4
c-0.7-0.7-0.9-1.6-0.5-2.5c0.1-0.7-0.3-1.1-0.7-0.3c-0.2,0.5-0.1,0.8-0.4,0.7s-0.1-0.3-0.1-0.8c0.1-0.5-0.2-1-0.7-1.2
c-0.5-0.3-0.9-1-0.9-1.5c0-1.9,1.8-3.2,4.2-2.1c2.1,1,2.8,2.5,2.6,3.5C276.6,407.1,276.4,409.5,275.6,410z"/>
<polygon class="st132" points="275.6,434.7 276.6,434.2 275.7,439.9 "/>
<path class="st133" d="M260.3,409c-0.1-0.2-0.1-0.4-0.1-0.7c0.1-0.2,0.5-0.5,0.4-0.7c-0.1-0.1-0.7,0.4-0.7,0.3s0-1.1-0.2-1.1
s-0.2,0.7-0.2,1c0,0-1,0.4-0.8,1c0.1,0.3,0.3,0.7,0.4,1L260.3,409z"/>
</g>
</g>
</g>
</svg>

BIN
custom/public/rotation3D/img/baseMap.png View File

Before After
Width: 800  |  Height: 516  |  Size: 56 KiB

BIN
custom/public/rotation3D/img/baseimg.png View File

Before After
Width: 976  |  Height: 850  |  Size: 344 KiB

+ 1
- 0
custom/public/rotation3D/img/brain.svg
File diff suppressed because it is too large
View File


BIN
custom/public/rotation3D/img/idc-green.png View File

Before After
Width: 161  |  Height: 188  |  Size: 38 KiB

BIN
custom/public/rotation3D/img/idc-red.png View File

Before After
Width: 161  |  Height: 188  |  Size: 38 KiB

BIN
custom/public/rotation3D/img/idc-yellow.png View File

Before After
Width: 161  |  Height: 188  |  Size: 38 KiB

+ 2
- 0
custom/public/rotation3D/jquery-3.5.0.min.js
File diff suppressed because it is too large
View File


+ 128
- 0
custom/public/rotation3D/rotation3D.css View File

@@ -0,0 +1,128 @@

/*
椭圆会使内部失真 transform: rotateX(50deg);
*/
.rotation3D{
position: relative; width: 800px; height: 600px; user-select: none;
margin: 0 auto;
/* border: 1px solid white; border-radius: 100%; */
/* cursor: move; */
}
.rotation3D .center{
display: none;
position: absolute; left: 50%; top: 50%;
transform: translate(-50%, -50%);
}

.rotation3D .itemList{ position: absolute; width: 100%; height: 100%; z-index: 20; }
.rotation3D .lineList{
position: absolute; width: 100%; height: 100%; z-index: 10;
transform-style: preserve-3d;
}

/*---------------------------点样式---------------------------*/
.rotation3D__item{
position: absolute; display: block; width: 161px; height: 188px;
text-align: center; line-height: 30px; font-size: 16px; color: white;
/*background: #2292ef; border-radius: 4px;*/
/*cursor: pointer; */
}
.rotation3D__item .scale{ position: absolute; top: 0; width: 100%; height: 100%; }
.rotation3D__item .cont{ position: relative; z-index: 2; }
.rotation3D__item .cont .iconfont { font-size: 28px; margin-top: 30px; margin-bottom: 96px; display: block; height: 35px;}
.rotation3D__item .cont p{ color: #101010; }
.lineList .rotation3D__line:nth-child(5n+0) .dot{
}
.lineList .rotation3D__line:nth-child(5n+1) .dot{
animation-delay: 1s;
}
.lineList .rotation3D__line:nth-child(5n+2) .dot{
animation-delay: 3s;
}
.lineList .rotation3D__line:nth-child(5n+3) .dot{
animation-delay: 2s;
}
.lineList .rotation3D__line:nth-child(5n+3) .dot{
animation-delay: 4s;
}
.rotation3D__item.blue{ color: #01e9fc; }
.rotation3D__item.green{ color: #b4b3ca; }
.rotation3D__item.yellow{ color: #ffd200; }

/*底座*/
.rotation3D__item .baseImg{ position: absolute; width: 100%; height: 100%; z-index: 1; }
.rotation3D__item.blue .baseImg{ background: url("img/idc-red.png"); }
.rotation3D__item.green .baseImg{ background: url("img/idc-green.png"); }
.rotation3D__item.yellow .baseImg{ background: url("img/idc-yellow.png"); }

/*---------------------------
线样式
线高为总高的一般
---------------------------*/
.rotation3D__line{
position: absolute; left: 50%; top: 50%;
display: block;
width: 30px;
height: 50%;
padding-top: 60px; color: #fff; font-size: 50px;
/*background: #fff;*/
/*原点设置在中间*/
transform-origin: 50% 0;
transform-style: preserve-3d;
overflow: hidden;
}
.rotation3D__line .pos{ position: absolute; top: 0; left: 15px;}
.rotation3D__line svg { position: absolute; top: 0; }
.rotation3D__line svg path {
stroke: #fff; fill: none;
stroke-width: 2;
animation: path-animation 100s linear 0s infinite normal;
}
@keyframes path-animation {
0% { stroke-dashoffset:500; }
100% { stroke-dashoffset:0; }
}
.rotation3D__line .dot {
position: absolute; top: 0; left: 0; text-align: center;
/*width: 35px; height: 35px; font-size: 35px; */
width: 19px; height: 19px; font-size: 19px;
}
.rotation3D__line .dot1,.rotation3D__line .dot3,.rotation3D__line .dot4{
animation: svg-path-animation 6s ease-in-out 0s infinite normal;
}
.rotation3D__line .dot1{
offset-path: path("M0 400, 0 0"); offset-distance: 0%;
}
.rotation3D__line .dot2{
offset-path: path("M0 200, 0 0"); offset-distance: 0%;
background: #ffd200; border-radius: 100%;
font-size: 22px; color: #000;
}
.rotation3D__line .dot3{
offset-path: path("M20 400 S 0 200, 20 0"); offset-distance: 0%;
}
.rotation3D__line .dot4{
position: relative;
offset-path: path("M20 0 S 40 200, 20 400"); offset-distance: 0%;
}
@keyframes svg-path-animation {
from {offset-distance: 100%;}
to {offset-distance: 0%;}
}
.dot1 > span{
position: absolute;
font-size: 12px;
color: #888;
transform:scale(0.80);
transform-origin:left;
white-space: nowrap;
}

/*颜色*/
.rotation3D__line.blue { color: #07b2f9; }
.rotation3D__line.green { color: #ac94ee; }
.rotation3D__line.yellow { color: #ffd500; }

.rotation3D__line.blue svg path { stroke: #07b2f9; }
.rotation3D__line.green svg path { stroke: #ac94ee; }
.rotation3D__line.yellow svg path { stroke: #ffd500; }

+ 380
- 0
custom/public/rotation3D/rotation3D.js View File

@@ -0,0 +1,380 @@
var cancelFrame = window.cancelAnimationFrame || window.cancelRequestAnimationFrame;
var requestFrame = window.requestAnimationFrame;
var time = !window.performance || !window.performance.now ?
function () {return +new Date()}:
function () {return performance.now()};

/**
* 计算两点距离
* @param points
* @returns {number}
* distance([{x:0,y:0},{x:1,y:1}]);
*/
var distance = function(points) {
var p1=points[0];
var p2=points[1];
var a = p2.x-p1.x;
var b = p2.y-p1.y;
return Math.sqrt(a*a+b*b);
};

/**
* 圆公式
* @param rotation 弧度
* 计算公式:
* Math.PI; //圆周率
* Math.sin(); //正弦 x -左 +右
* Math.cos; //余弦 y -下 +上
*/
var circleMath = {
/**
* 根据弧度计算角度
* @param rotation 弧度
* rotation, farScale, xs, xr, ys, yr, itemWidth
*/
// parseRotate: function (rotation) {
// return (180 / Math.PI * rotation) - 180;
// },
parseRotate: function (rotation, self) {
var sin = Math.sin(rotation), cos = Math.cos(rotation);
var sin_cos = sin*cos; //得出偏移正负值,从0°向左依次 +-+-
var angle = (180 / Math.PI * rotation) - 180;
var lastAngle = angle;

// console.log('rotation',rotation)
// console.log('sin',sin)
// console.log('cos',cos)
// console.log('sin*cos',sin*cos);
// console.log('统一偏移角度',self.yr * (sin_cos/Math.PI))

lastAngle = angle + (self.yr * (sin_cos/(Math.PI+1)));

return lastAngle;
},
/**
* 计算scale,x,y
* scale 最小尺寸 + ((1 - 最小尺寸) * (sin正弦 + 1) * 0.5)
* x x起点 + (尺寸 * cos余弦 * x半径) - 元素宽度一半
* y y起点 + (尺寸 * sin正弦 * x半径) - 元素宽度一半
* farScale, xs, xr, ys, yr, itemWidth
*/
parseSXY: function (rotation, self) {
var farScale=self.farScale;
var itemWidth=self.itemWidth;
var xs=self.xs; var xr=self.xr; var ys=self.ys; var yr=self.yr;
var sin = Math.sin(rotation), cos = Math.cos(rotation);
var scale = farScale + ((1 - farScale) * (sin + 1) * 0.5); //单个尺寸

// 按设置尺寸
// var x = xs + (scale * cos * xr) - (itemWidth * 0.5);
// var y = ys + (scale * sin * yr) - (itemWidth * 0.5);
// 不使用压缩
// var x = xs + (cos * xs) - (itemWidth * 0.5);
// var y = ys + (sin * ys) - (itemWidth * 0.5);
// 使用压缩
var x = xs + (cos * xr) - (itemWidth * 0.5);
var y = ys + (sin * yr) - (itemWidth * 0.5);
var distanceNumber = distance([
{x:self.$rotation.width()/2 - self.$item.width()/2, y:self.$rotation.height()/2 - self.$item.height()/2},
{x:x,y:y}]
);

// console.log({x:self.$rotation.width()/2, y:self.$rotation.height()/2})
// console.log('x,y',x,y)
// console.log('两点距离',distanceNumber)

return {
x: x,
y: y,
scale: scale,
distanceNumber: distanceNumber,
}
},
}
/**
* 3D旋转
* @param id
*/
var Rotation3D = window.Rotation3D = function (_opts) {
var self=this;
this.$rotation = $(_opts.id)
this.$lineList = this.$rotation.find('.lineList')
this.$item = this.$rotation.find('.rotation3D__item')
this.$line = this.$rotation.find('.rotation3D__line')
this.itemWidth = this.$item.width();
this.itemHeight = this.$item.height();
this.length = this.$item.length;
// 圆计算
this.rotation = Math.PI / 2; //圆周率/2
this.destRotation = this.rotation;

var xr = this.$rotation.width() * 0.5;
var yr = this.$rotation.height() * 0.5;
var xRadius = _opts.xRadius || 0;
var yRadius = _opts.yRadius || 0;

var opts = Object.assign({
farScale: 1, // 最小尺寸
xs: xr, // x起点
ys: yr, // y起点
xr: xr - xRadius, // x半径-压缩
yr: yr - yRadius, // y半径-压缩
// 播放
autoPlay:false,
autoPlayDelay:3000,
currenIndex:-1,
fps:30,
speed:4,
},_opts)
Object.assign(this, opts)

// 遍历子元素
this.$item.each(function (index) {
$(this).click(function () {
$(this).addClass('active').siblings().removeClass('active')
self.goTo(index)
})
})
// 当前控件进入离开
this.$rotation.mouseenter(function () {
clearInterval(self.autoPlayTimer)
})
this.$rotation.mouseleave(function () {
self.onAutoPlay()
})

this.onAutoPlay()
this.onDrag()
this.render()

}
/**
* item样式
* x x起点 + (尺寸 * 余弦 * x压缩) - 元素宽度一半
* y y起点 + (尺寸 * 正弦 * y压缩) - 元素宽度一半
*/
Rotation3D.prototype.itemStyle = function($item, index, rotation) {
//console.log("itemStyle=" + rotation + " index=" + index);
var parseSXY = circleMath.parseSXY(rotation, this);
var scale = parseSXY.scale;
var x = parseSXY.x;
var y = parseSXY.y;
var $line = this.$lineList.find('.rotation3D__line').eq(index);

//设置当前子菜单的位置(left,top) = (x,y)
$item.find('.scale').css({
'transform': `scale(${scale})`,
// 'top': `${this.itemWidth * (1-scale) }`,
})
$item.css({
position: 'absolute',
display: 'inline-block',
// opacity: scale,
'z-index': parseInt(scale * 100),
'transform-origin': '0px 0px',
// 'transform': `translate(${x}px, ${y}px) scale(${scale})`,
'transform': `translate(${x}px, ${y}px)`,
});

/**
* 线样式
*/
$line.css({
height:parseSXY.distanceNumber,
})
$line.find('svg').css({
height:parseSXY.distanceNumber,
})
$line.find('.dot1').css({
'offset-path':`path("M0 ${parseSXY.distanceNumber}, 0 0")`,
})
$line.find('#path1').attr({
'd':`M0 ${parseSXY.distanceNumber}, 0 0`,
})

$line.find('.dot2').css({
'offset-path':`path("M0 ${parseSXY.distanceNumber/2}, 0 0")`,
})
$line.find('#path2').attr({
'd':`M0 ${parseSXY.distanceNumber}, 0 0`,
})

$line.find('.dot3').css({
'offset-path':`path("M20 ${parseSXY.distanceNumber} S 0 ${parseSXY.distanceNumber/2}, 20 0")`,
})
$line.find('#path3').attr({
'd':`M20 ${parseSXY.distanceNumber} S 0 ${parseSXY.distanceNumber/2}, 20 0`,
})

$line.find('.dot4').css({
'offset-path':`path("M20 0 S 40 ${parseSXY.distanceNumber/2}, 20 ${parseSXY.distanceNumber}")`,
})
$line.find('#path4').attr({
'd':`M20 0 S 40 ${parseSXY.distanceNumber/2}, 20 ${parseSXY.distanceNumber}`,
})

}
/**
* line样式
*/
Rotation3D.prototype.lineStyle = function($line, index, rotation) {
var rotate = circleMath.parseRotate(rotation, this)
//console.log("lineStyle=" + rotation + " index=" + index);

$line.css({
transform: 'rotate(' + rotate + 'deg)',
})
this.$lineList.css({
// transform: `rotateX(${this.yRadius / 3}deg)`,
})
}

/**
* 旋转至index
*/
Rotation3D.prototype.goTo = function (index) {
var self = this;
this.currenIndex = index;
//console.log('goTo currenIndex', index);
/**
* 1.计算floatIndex,用于控死amdiff
*/
var itemsRotated = this.length * ((Math.PI / 2) - this.rotation) / (2 * Math.PI);
var floatIndex = itemsRotated % this.length;
if (floatIndex < 0) { floatIndex = floatIndex + this.length; }

/**
* 2.计算diff,判断方向正反
*/
var diff = index - (floatIndex % this.length);
if (2 * Math.abs(diff) > this.length) {
diff -= (diff > 0) ? this.length : -this.length;
}
// 停止任何正在进行的旋转
this.destRotation += (2 * Math.PI / this.length) * -diff;
this.scheduleNextFrame();

}
/**
* 定时器渐近旋转
*/
Rotation3D.prototype.scheduleNextFrame = function () {
var self = this
this.lastTime = time();
// 暂停
var pause = function () {
cancelFrame ? cancelFrame(this.timer) : clearTimeout(self.timer);
self.timer = 0;
}
// 渐进播放
var playFrame = function () {
var rem = self.destRotation - self.rotation;
var now = time(), dt = (now - self.lastTime) * 0.002;
self.lastTime = now;
// console.log('rem',rem)

if (Math.abs(rem) < 0.003) {
self.rotation = self.destRotation;
pause();
} else {
// 渐近地接近目的地
self.rotation = self.destRotation - rem / (1 + (self.speed * dt));
self.scheduleNextFrame();
}
self.render();
}

this.timer = cancelFrame ?
requestFrame(playFrame) :
setTimeout(playFrame, 1000 / this.fps);
}
/**
* 更新
*/
Rotation3D.prototype.render = function () {
var self=this;
// 图形间隔:弧度
var spacing = 2 * Math.PI / this.$item.length;
var itemRotation = this.rotation;
var lineRotation = this.rotation + (Math.PI/2);

this.$item.each(function (index) {
self.itemStyle($(this), index, itemRotation)
itemRotation += spacing;
})
this.$line.each(function (index) {
self.lineStyle($(this), index, lineRotation)
lineRotation += spacing;
})
}
/**
* 自动播放
*/
Rotation3D.prototype.onAutoPlay = function () {
var self = this;

if (this.autoPlay) {
this.autoPlayTimer = setInterval(function () {
if (self.currenIndex < 0) {
self.currenIndex = self.length - 1
}
//console.log("autoPlayTimer....");
self.goTo(self.currenIndex);
self.currenIndex--; //倒叙
}, this.autoPlayDelay)
}
}
/**
* 拖拽
*/
Rotation3D.prototype.onDrag = function () {
var self = this;
var startX, startY, moveX, moveY, endX, endY;
//console.log("onDrag....");
// 拖拽:三个事件-按下 移动 抬起
//按下
this.$rotation.mousedown(function (e) {
startX = e.pageX; startY = e.pageY;
//console.log("mousedown....");
// 移动
$(document).mousemove(function (e) {
// console.log('移动');
endX = e.pageX; endY = e.pageY;
moveX = endX - startX; moveY = endY - startY;
// console.log('x,y',moveX,moveY);
})
// 抬起
$(document).mouseup(function (e) {
endX = e.pageX; endY = e.pageY;
moveX = endX - startX; moveY = endY - startY;
//console.log("mouseup....");
// 每40旋转一步
var moveIndex = parseInt(Math.abs(moveX) / 50)
//console.log('moveIndex',moveIndex)
if (moveIndex > 0) {
// console.log(moveX<0 ? '向左' : '向右')
if (moveX < 0) { //向左
self.currenIndex = self.currenIndex - moveIndex
play(moveIndex)
} else { //向右
self.currenIndex = self.currenIndex + moveIndex
play(moveIndex)
}
}

// 解绑
$(document).unbind("mousemove");
$(document).unbind("mouseup");
})

})

function play() {
if (self.currenIndex == 0) {
self.currenIndex = self.length - 1
}
self.goTo(self.currenIndex % self.length);
}

}

+ 6
- 0
custom/public/rotation3D/vue-2.6.10.min.js
File diff suppressed because it is too large
View File


+ 13
- 0
custom/public/swiper/swiper-bundle.min.css
File diff suppressed because it is too large
View File


+ 13
- 0
custom/public/swiper/swiper-bundle.min.js
File diff suppressed because it is too large
View File


+ 24
- 12
go.mod View File

@@ -22,16 +22,25 @@ require (
github.com/PuerkitoBio/goquery v1.5.0
github.com/RichardKnop/machinery v1.6.9
github.com/RoaringBitmap/roaring v0.4.23 // indirect
github.com/alecthomas/chroma v0.10.0
github.com/alibabacloud-go/darabonba-openapi v0.1.18
github.com/alibabacloud-go/dysmsapi-20170525/v2 v2.0.9
github.com/alibabacloud-go/tea v1.1.17
github.com/alibabacloud-go/tea-utils v1.4.3
github.com/alibabacloud-go/tea-xml v1.1.2 // indirect
github.com/bgentry/speakeasy v0.1.0 // indirect
github.com/blevesearch/bleve v1.0.7
github.com/clbanning/mxj/v2 v2.5.5 // indirect
github.com/couchbase/gomemcached v0.0.0-20191004160342-7b5da2ec40b2 // indirect
github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d // indirect
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 // indirect
github.com/cznic/strutil v0.0.0-20181122101858-275e90344537 // indirect
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/disintegration/imaging v1.6.2
github.com/dustin/go-humanize v1.0.0
github.com/editorconfig/editorconfig-core-go/v2 v2.1.1
github.com/elliotchance/orderedmap v1.4.0
github.com/emirpasic/gods v1.12.0
github.com/ethantkoenig/rupture v0.0.0-20180203182544-0a76f03a811a
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 // indirect
@@ -42,6 +51,7 @@ require (
github.com/go-enry/go-enry/v2 v2.3.0
github.com/go-git/go-billy/v5 v5.0.0
github.com/go-git/go-git/v5 v5.0.0
github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a
github.com/go-ini/ini v1.56.0 // indirect
github.com/go-macaron/auth v0.0.0-20161228062157-884c0e6c9b92
github.com/go-openapi/jsonreference v0.19.3 // indirect
@@ -52,9 +62,11 @@ require (
github.com/gobwas/glob v0.2.3
github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14
github.com/golang/mock v1.6.0 // indirect
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/google/uuid v1.1.1
github.com/gorilla/context v1.1.1
github.com/gorilla/websocket v1.4.0
github.com/hashicorp/go-retryablehttp v0.6.6 // indirect
@@ -87,6 +99,7 @@ require (
github.com/niklasfasching/go-org v0.1.9
github.com/oliamb/cutter v0.2.2
github.com/olivere/elastic/v7 v7.0.9
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pkg/errors v0.9.1
github.com/pquerna/otp v1.2.0
github.com/prometheus/client_golang v1.1.0
@@ -94,12 +107,11 @@ require (
github.com/prometheus/procfs v0.0.4 // indirect
github.com/quasoft/websspi v1.0.0
github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001 // indirect
github.com/robfig/cron/v3 v3.0.1
github.com/satori/go.uuid v1.2.0
github.com/sergi/go-diff v1.1.0
github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b // indirect
github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd
github.com/stretchr/testify v1.4.0
github.com/stretchr/testify v1.7.0
github.com/tecbot/gorocksdb v0.0.0-20181010114359-8752a9433481 // indirect
github.com/tinylib/msgp v1.1.2 // indirect
github.com/tstranex/u2f v1.0.0
@@ -110,25 +122,25 @@ require (
github.com/urfave/cli v1.22.1
github.com/xanzy/go-gitlab v0.31.0
github.com/yohcop/openid-go v1.0.0
github.com/yuin/goldmark v1.1.27
github.com/yuin/goldmark-meta v0.0.0-20191126180153-f0638e958b60
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79
golang.org/x/mod v0.3.0 // indirect
golang.org/x/net v0.0.0-20200513185701-a91f0712d120
github.com/yuin/goldmark v1.4.13
github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594
github.com/yuin/goldmark-meta v1.1.0
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f
golang.org/x/text v0.3.2
golang.org/x/sys v0.0.0-20210510120138-977fb7262007
golang.org/x/text v0.3.3
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect
golang.org/x/tools v0.0.0-20200515220128-d3bf790afa53
golang.org/x/tools v0.1.1
google.golang.org/appengine v1.6.5 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 // indirect
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
gopkg.in/ini.v1 v1.52.0
gopkg.in/ini.v1 v1.56.0
gopkg.in/ldap.v3 v3.0.2
gopkg.in/macaron.v1 v1.3.9 // indirect
gopkg.in/testfixtures.v2 v2.5.0
gopkg.in/yaml.v2 v2.2.8
gopkg.in/yaml.v2 v2.3.0
mvdan.cc/xurls/v2 v2.1.0
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
xorm.io/builder v0.3.7


+ 101
- 58
go.sum View File

@@ -39,11 +39,9 @@ gitea.com/macaron/inject v0.0.0-20190803172902-8375ba841591/go.mod h1:h6E4kLao1Y
gitea.com/macaron/inject v0.0.0-20190805023432-d4c86e31027a h1:aOKEXkDTnh4euoH0so/THLXeHtQuqHmDPb1xEk6Ehok=
gitea.com/macaron/inject v0.0.0-20190805023432-d4c86e31027a/go.mod h1:h6E4kLao1Yko6DOU6QDnQPcuoNzvbZqzj2mtPcEn1aM=
gitea.com/macaron/macaron v1.3.3-0.20190803174002-53e005ff4827/go.mod h1:/rvxMjIkOq4BM8uPUb+VHuU02ZfAO6R4+wD//tiCiRw=
gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb h1:amL0md6orTj1tXY16ANzVU9FmzQB+W7aJwp8pVDbrmA=
gitea.com/macaron/macaron v1.3.3-0.20190821202302-9646c0587edb/go.mod h1:0coI+mSPSwbsyAbOuFllVS38awuk9mevhLD52l50Gjs=
gitea.com/macaron/macaron v1.4.0 h1:FY1QDGqyuUzs21K6ChkbYbRUfwL7v2aUrhNEJ0IgsAw=
gitea.com/macaron/macaron v1.4.0/go.mod h1:P7hfDbQjcW22lkYkXlxdRIfWOXxH2+K4EogN4Q0UlLY=
gitea.com/macaron/session v0.0.0-20190821211443-122c47c5f705 h1:mvkQGAlON1Z6Y8pqa/+FpYIskk54mazuECUfZK5oTg0=
gitea.com/macaron/session v0.0.0-20190821211443-122c47c5f705/go.mod h1:1ujH0jD6Ca4iK9NL0Q2a7fG2chvXx5hVa7hBfABwpkA=
gitea.com/macaron/session v0.0.0-20191207215012-613cebf0674d h1:XLww3CvnFZkXVwauN67fniDaIpIqsE+9KVcxlZKlvLU=
gitea.com/macaron/session v0.0.0-20191207215012-613cebf0674d/go.mod h1:FanKy3WjWb5iw/iZBPk4ggoQT9FcM6bkBPvmDmsH6tY=
@@ -70,7 +68,6 @@ github.com/RichardKnop/machinery v1.6.9 h1:dQu1c7ENgPFrN9qWweEe7xDDvNYGSqEyprK0G
github.com/RichardKnop/machinery v1.6.9/go.mod h1:BO7MG/5tvdpgMVkOT8V94SEf8x8H8aceRzTt8Tx1IMc=
github.com/RichardKnop/redsync v1.2.0 h1:gK35hR3zZkQigHKm8wOGb9MpJ9BsrW6MzxezwjTcHP0=
github.com/RichardKnop/redsync v1.2.0/go.mod h1:9b8nBGAX3bE2uCfJGSnsDvF23mKyHTZzmvmj5FH3Tp0=
github.com/RoaringBitmap/roaring v0.4.21 h1:WJ/zIlNX4wQZ9x8Ey33O1UaD9TCTakYsdLFSBcTwH+8=
github.com/RoaringBitmap/roaring v0.4.21/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo=
github.com/RoaringBitmap/roaring v0.4.23 h1:gpyfd12QohbqhFO4NVDUdoPOCXsyahYRQhINmlHxKeo=
github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo=
@@ -79,8 +76,39 @@ github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMx
github.com/Unknwon/com v0.0.0-20190321035513-0fed4efef755/go.mod h1:voKvFVpXBJxdIPeqjoJuLK+UVcRlo/JLjeToGxPYu68=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.2/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 h1:iC9YFYKDGEy3n/FtqJnOkZsene9olVspKmkX5A2YBEo=
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=
github.com/alibabacloud-go/darabonba-openapi v0.1.14/go.mod h1:w4CosR7O/kapCtEEMBm3JsQqWBU/CnZ2o0pHorsTWDI=
github.com/alibabacloud-go/darabonba-openapi v0.1.18 h1:3eUVmAr7WCJp7fgIvmCd9ZUyuwtJYbtUqJIed5eXCmk=
github.com/alibabacloud-go/darabonba-openapi v0.1.18/go.mod h1:PB4HffMhJVmAgNKNq3wYbTUlFvPgxJpTzd1F5pTuUsc=
github.com/alibabacloud-go/darabonba-string v1.0.0/go.mod h1:93cTfV3vuPhhEwGGpKKqhVW4jLe7tDpo3LUM0i0g6mA=
github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68 h1:NqugFkGxx1TXSh/pBcU00Y6bljgDPaFdh5MUSeJ7e50=
github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68/go.mod h1:6pb/Qy8c+lqua8cFpEy7g39NRRqOWc3rOwAy8m5Y2BY=
github.com/alibabacloud-go/dysmsapi-20170525/v2 v2.0.9 h1:z+OU7LbWtQitWJ8SAn55hEQkJPCsEPJc97TvGCZV+4s=
github.com/alibabacloud-go/dysmsapi-20170525/v2 v2.0.9/go.mod h1:AT91gCNJPsemf4lHLNgWTf/RsgmpdOprWvQ3FYvtwGk=
github.com/alibabacloud-go/endpoint-util v1.1.0 h1:r/4D3VSw888XGaeNpP994zDUaxdgTSHBbVfZlzf6b5Q=
github.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE=
github.com/alibabacloud-go/openapi-util v0.0.10/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws=
github.com/alibabacloud-go/openapi-util v0.0.11 h1:iYnqOPR5hyEEnNZmebGyRMkkEJRWUEjDiiaOHZ5aNhA=
github.com/alibabacloud-go/openapi-util v0.0.11/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws=
github.com/alibabacloud-go/tea v1.1.0/go.mod h1:IkGyUSX4Ba1V+k4pCtJUc6jDpZLFph9QMy2VUPTwukg=
github.com/alibabacloud-go/tea v1.1.7/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=
github.com/alibabacloud-go/tea v1.1.8/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=
github.com/alibabacloud-go/tea v1.1.11/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=
github.com/alibabacloud-go/tea v1.1.17 h1:05R5DnaJXe9sCNIe8KUgWHC/z6w/VZIwczgUwzRnul8=
github.com/alibabacloud-go/tea v1.1.17/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=
github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE=
github.com/alibabacloud-go/tea-utils v1.4.3 h1:8SzwmmRrOnQ09Hf5a9GyfJc0d7Sjv6fmsZoF4UDbFjo=
github.com/alibabacloud-go/tea-utils v1.4.3/go.mod h1:KNcT0oXlZZxOXINnZBs6YvgOd5aYp9U67G+E3R8fcQw=
github.com/alibabacloud-go/tea-xml v1.1.2 h1:oLxa7JUXm2EDFzMg+7oRsYc+kutgCVwm+bZlhhmvW5M=
github.com/alibabacloud-go/tea-xml v1.1.2/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
github.com/aliyun/credentials-go v1.1.2 h1:qU1vwGIBb3UJ8BwunHDRFtAhS6jnQLnde/yk0+Ih2GY=
github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw=
github.com/andybalholm/cascadia v1.0.0 h1:hOCXnnZ5A+3eVDX8pvgl4kofXv2ELss0bKcqRySc45o=
github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
@@ -128,6 +156,8 @@ github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668/go.mod h1:H0wQ
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/chris-ramon/douceur v0.2.0 h1:IDMEdxlEUUBYBKE4z/mJnFyVXox+MjuEVDJNN27glkU=
github.com/chris-ramon/douceur v0.2.0/go.mod h1:wDW5xjJdeoMm1mRt4sD4c/LbF/mWdEpRXQKjTR8nIBE=
github.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9E=
github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/corbym/gocrest v1.0.3 h1:gwEdq6RkTmq+09CTuM29DfKOCtZ7G7bcyxs3IZ6EVdU=
github.com/corbym/gocrest v1.0.3/go.mod h1:maVFL5lbdS2PgfOQgGRWDYTeunSWQeiEgoNdTABShCs=
@@ -140,11 +170,9 @@ github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/couchbase/ghistogram v0.1.0/go.mod h1:s1Jhy76zqfEecpNWJfWUiKZookAFaiGOEoyzgHt9i7k=
github.com/couchbase/gomemcached v0.0.0-20190515232915-c4b4ca0eb21d h1:XMf4E1U+b9E3ElF0mjvfXZdflBRZz4gLp16nQ/QSHQM=
github.com/couchbase/gomemcached v0.0.0-20190515232915-c4b4ca0eb21d/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c=
github.com/couchbase/gomemcached v0.0.0-20191004160342-7b5da2ec40b2 h1:vZryARwW4PSFXd9arwegEywvMTvPuXL3/oa+4L5NTe8=
github.com/couchbase/gomemcached v0.0.0-20191004160342-7b5da2ec40b2/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c=
github.com/couchbase/goutils v0.0.0-20190315194238-f9d42b11473b h1:bZ9rKU2/V8sY+NulSfxDOnXTWcs1rySqdF1sVepihvo=
github.com/couchbase/goutils v0.0.0-20190315194238-f9d42b11473b/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
github.com/couchbase/goutils v0.0.0-20191018232750-b49639060d85 h1:0WMIDtuXCKEm4wtAJgAAXa/qtM5O9MariLwgHaRlYmk=
github.com/couchbase/goutils v0.0.0-20191018232750-b49639060d85/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
@@ -169,13 +197,16 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
github.com/denisenkom/go-mssqldb v0.0.0-20190924004331-208c0a498538 h1:bpWCJ5MddHsv4Xtl3azkK89mZzd/vvut32mvAnKbyUA=
github.com/denisenkom/go-mssqldb v0.0.0-20190924004331-208c0a498538/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc h1:VRRKCwnzqk8QCaRC4os14xoKDdbHqqlJtJA0oc1ZAjg=
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
@@ -185,8 +216,9 @@ github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/editorconfig/editorconfig-core-go/v2 v2.1.1 h1:mhPg/0hGebcpiiQLqJD2PWWyoHRLEdZ3sXKaEvT1EQU=
github.com/editorconfig/editorconfig-core-go/v2 v2.1.1/go.mod h1:/LuhWJiQ9Gvo1DhVpa4ssm5qeg8rrztdtI7j/iCie2k=
github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/elliotchance/orderedmap v1.4.0 h1:wZtfeEONCbx6in1CZyE6bELEt/vFayMvsxqI5SgsR+A=
github.com/elliotchance/orderedmap v1.4.0/go.mod h1:wsDwEaX5jEoyhbs7x93zk2H/qv0zwuhg4inXhDkYqys=
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/ethantkoenig/rupture v0.0.0-20180203182544-0a76f03a811a h1:M1bRpaZAn4GSsqu3hdK2R8H0AH9O6vqCTCbm2oAFGfE=
@@ -230,6 +262,8 @@ github.com/go-git/go-git-fixtures/v4 v4.0.1 h1:q+IFMfLx200Q3scvt2hN79JsEzy4AmBTp
github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
github.com/go-git/go-git/v5 v5.0.0 h1:k5RWPm4iJwYtfWoxIJy4wJX9ON7ihPeZZYC1fLYDnpg=
github.com/go-git/go-git/v5 v5.0.0/go.mod h1:oYD8y9kWsGINPFJoLdaScGCN6dlKg23blmClfZwtUVA=
github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a h1:v6zMvHuY9yue4+QkG/HQ/W67wvtQmWJ4SDo9aK/GIno=
github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a/go.mod h1:I79BieaU4fxrw4LMXby6q5OS9XnoR9UIKLOzDFjUmuw=
github.com/go-ini/ini v1.56.0 h1:6HjxSjqdmgnujDPhlzR4a44lxK3w03WPN8te0SoUSeM=
github.com/go-ini/ini v1.56.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
@@ -306,7 +340,6 @@ github.com/go-swagger/go-swagger v0.21.0 h1:AX9mdfzp6eJtUe92nFrWmbK7ocRgkCDPJs0F
github.com/go-swagger/go-swagger v0.21.0/go.mod h1:tDb8PdDVFcaE8EPXkMOsuxpL3UEPiwu1UDZar9Z/1RY=
github.com/go-swagger/scan-repo-boundary v0.0.0-20180623220736-973b3573c013 h1:l9rI6sNaZgNC0LnF3MiE+qTmyBA/tZAg1rtyrGbUMK0=
github.com/go-swagger/scan-repo-boundary v0.0.0-20180623220736-973b3573c013/go.mod h1:b65mBPzqzZWxOZGxSWrqs4GInLIn+u99Q9q7p+GKni0=
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y=
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
@@ -327,16 +360,17 @@ github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4er
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1 h1:ZFgWrT+bLgsYPirOnRfKLYJLvssAegOj/hgyMFdJZe0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
@@ -349,7 +383,6 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
@@ -375,10 +408,10 @@ github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4=
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw=
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 h1:l5lAOZEym3oK3SQ2HBHWsJUfbNBiTXJDeW2QDxw9AQ0=
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
@@ -406,7 +439,6 @@ github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVo
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-retryablehttp v0.6.4 h1:BbgctKO892xEyOXnGiaAwIoSq1QZ/SS4AhjoAh9DnfY=
github.com/hashicorp/go-retryablehttp v0.6.4/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-retryablehttp v0.6.6 h1:HJunrbHTDDbBb/ay4kxa1n+dLmttUlnP3V9oNE4hmsM=
github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
@@ -441,8 +473,9 @@ github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqx
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
@@ -458,7 +491,6 @@ github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4 h1:cTxwSmnaqLoo+
github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.9.2 h1:LfVyl+ZlLlLDeQ/d2AqfGIIH4qEDu0Ed2S5GyhCWIWY=
github.com/klauspost/compress v1.9.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.10.2 h1:Znfn6hXZAHaLPNnlqUYRrBSReFHYybslgv4PTiyz6P0=
github.com/klauspost/compress v1.10.2/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
@@ -472,7 +504,6 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
@@ -545,7 +576,6 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/mrjones/oauth v0.0.0-20180629183705-f4e24b6d100c h1:3wkDRdxK92dF+c1ke2dtj7ZzemFWBHB9plnJOtlwdFA=
github.com/mrjones/oauth v0.0.0-20180629183705-f4e24b6d100c/go.mod h1:skjdDftzkFALcuGzYSklqYd8gvat6F1gZJ4YPVbkZpM=
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae h1:VeRdUYdCw49yizlSbMEn2SZ+gT+3IUKx8BqxyQdz+BY=
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
@@ -580,6 +610,8 @@ github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFSt
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/openzipkin/zipkin-go v0.1.3/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.4.0 h1:u3Z1r+oOXJIkxqw34zVhyPgjBsm6X2wn21NWs/HfSeg=
@@ -588,7 +620,6 @@ github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ=
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -635,8 +666,6 @@ github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqn
github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001 h1:YDeskXpkNDhPdWN3REluVa46HQOVuVkjkd2sWnrABNQ=
github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
@@ -684,13 +713,15 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w=
github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
github.com/smartystreets/assertions v1.1.0 h1:MkTeG1DMwsrdH7QtLXy5W+fUxWq+vmb6cLmyJ7aRtF0=
github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8=
github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
@@ -715,12 +746,13 @@ github.com/streadway/amqp v0.0.0-20190214183023-884228600bc9 h1:wR6aLKdbJ5E8m+NZ
github.com/streadway/amqp v0.0.0-20190214183023-884228600bc9/go.mod h1:1WNBiOZtZQLpVAyu0iTduoJL9hEsMloAK5XWrtW0xdY=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08YuiTGPZLls0Wq99X9bWd0Q5ZSBesM=
github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8=
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
@@ -731,10 +763,11 @@ github.com/tecbot/gorocksdb v0.0.0-20181010114359-8752a9433481/go.mod h1:ahpPrc7
github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tinylib/msgp v1.1.0 h1:9fQd+ICuRIu/ue4vxJZu6/LzxN0HwMds2nq/0cFvxHU=
github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
github.com/tinylib/msgp v1.1.2 h1:gWmO7n0Ys2RBEb7GPYB9Ujq8Mk5p2U08lRnmMcGy6BQ=
github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
github.com/tjfoc/gmsm v1.3.2 h1:7JVkAn5bvUJ7HtU08iW6UiD+UTmJTIToHCfeFzkcCxM=
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/toqueteos/trie v1.0.0 h1:8i6pXxNUXNRAqP246iibb7w/pSFquNTQ+uNfriG7vlk=
github.com/toqueteos/trie v1.0.0/go.mod h1:Ywk48QhEqhU1+DwhMkJ2x7eeGxDHiGkAdc9+0DYcbsM=
@@ -746,7 +779,6 @@ github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGr
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/unknwon/cae v1.0.0 h1:i39lOFaBXZxhGjQOy/RNbi8uzettCs6OQxpR0xXohGU=
github.com/unknwon/cae v1.0.0/go.mod h1:QaSeRctcea9fK6piJpAMCCPKxzJ01+xFcr2k1m3WRPU=
github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e h1:GSGeB9EAKY2spCABz6xOX5DbxZEXolK+nBSvmsQwRjM=
github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs=
github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
@@ -771,13 +803,18 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yohcop/openid-go v1.0.0 h1:EciJ7ZLETHR3wOtxBvKXx9RV6eyHZpCaSZ1inbBaUXE=
github.com/yohcop/openid-go v1.0.0/go.mod h1:/408xiwkeItSPJZSTPF7+VtZxPkPrRRpRNK2vjGh6yI=
github.com/yuin/goldmark v1.1.7/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.25 h1:isv+Q6HQAmmL2Ofcmg8QauBmDPlUUnSoNhEcC940Rds=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27 h1:nqDD4MMMQA0lmWq03Z2/myGPYLQoXtmi0rGVs95ntbo=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark-meta v0.0.0-20191126180153-f0638e958b60 h1:gZucqLjL1eDzVWrXj4uiWeMbAopJlBR2mKQAsTGdPwo=
github.com/yuin/goldmark-meta v0.0.0-20191126180153-f0638e958b60/go.mod h1:i9VhcIHN2PxXMbQrKqXNueok6QNONoPjNMoj9MygVL0=
github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.5/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg=
github.com/yuin/goldmark v1.4.6/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg=
github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594 h1:yHfZyN55+5dp1wG7wDKv8HQ044moxkyGq12KFFMFDxg=
github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594/go.mod h1:U9ihbh+1ZN7fR5Se3daSPoz1CGF9IYtSvWwVQtnzGHU=
github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc=
github.com/yuin/goldmark-meta v1.1.0/go.mod h1:U4spWENafuA7Zyg+Lj5RqK/MF+ovMYtBvXi1lBb2VP0=
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
@@ -813,17 +850,18 @@ golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190907121410-71b5226ff739/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad h1:5E5raQxcv+6CZ11RrBYQe5WRbUIWpScjh0kvHZkZIrQ=
golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM=
golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 h1:IaQbIIB2X/Mp/DKctl6ROxz1KyMlKp4uyvL6+kQ7C88=
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20181217174547-8f45f776aaf1/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -832,12 +870,12 @@ golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTk
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee h1:WG0RUwxtNT4qqaXX3DPA8zHFNm/D9xaBpxzHt1WcA/E=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -865,10 +903,12 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120 h1:EZ3cVSzKOlJxAd8e8YAJ7no8nNypTxexh/YE/xW3ZEY=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/oauth2 v0.0.0-20180620175406-ef147856a6dd/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -876,7 +916,6 @@ golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAG
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190220154721-9b3c75971fc9/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -886,8 +925,11 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180824143301-4910a1d54f87/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -915,24 +957,27 @@ golang.org/x/sys v0.0.0-20190730183949-1393eb018365/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190907184412-d223b2b6db03/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47 h1:/XfQ9z7ib8eEJX2hdgFTZJ/ntt0swNk5oYBziWeTCvY=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f h1:mOhmO9WsBaJCNmaZHPtHs9wOcdqdKCjF6OPJlmDM3KI=
golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI=
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -955,17 +1000,19 @@ golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgw
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200225230052-807dcd883420 h1:4RJNOV+2rLxMEfr6QIpC7GEv9MjD6ApGXTCLrNF9+eA=
golang.org/x/tools v0.0.0-20200225230052-807dcd883420/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224 h1:azwY/v0y0K4mFHVsg5+UrTgchqALYWpqVo6vL5OmkmI=
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515220128-d3bf790afa53 h1:vmsb6v0zUdmUlXfwKaYrHPPRCV0lHq/IwNIf0ASGjyQ=
golang.org/x/tools v0.0.0-20200515220128-d3bf790afa53/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.1.1 h1:wGiQel/hW0NnEkJUk8lbzkX2gFJU6PFxf1v5OlCfuOs=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
google.golang.org/api v0.0.0-20181220000619-583d854617af/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
@@ -984,7 +1031,6 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.4 h1:WiKh4+/eMB2HaY7QhCfW/R7MuRAoA8QMCSJA6jP5/fo=
google.golang.org/appengine v1.6.4/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
@@ -1016,7 +1062,6 @@ google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLY
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0 h1:qdOKuR/EIArgaWNjetjgTzgVTAZ+S/WXVrq9HW9zimw=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0 h1:cJv5/xdbk1NnMPR1VP9+HU6gupuG9MLBoH1r6RHZ2MY=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
@@ -1027,7 +1072,6 @@ gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 h1:nn6Zav2sOQHCFJHEspya8
gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -1040,8 +1084,8 @@ gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.44.2/go.mod h1:M3Cogqpuv0QCi3ExAY5V4uOt4qb/R3xZubo9m8lK5wg=
gopkg.in/ini.v1 v1.46.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.52.0 h1:j+Lt/M1oPPejkniCg1TkWE2J3Eh1oZTsHSXzMTzUXn4=
gopkg.in/ini.v1 v1.52.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.56.0 h1:DPMeDvGTM54DXbPkVIZsp19fp/I2K7zwA/itHYHKo8Y=
gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ldap.v3 v3.0.2 h1:R6RBtabK6e1GO0eQKtkyOFbAHO73QesLzI2w2DZ6b9w=
gopkg.in/ldap.v3 v3.0.2/go.mod h1:oxD7NyBuxchC+SgJDE1Q5Od05eGt29SDQVBmV+HYbzw=
gopkg.in/macaron.v1 v1.3.9 h1:Dw+DDRYdXgQyEsPlfAfKz+UA5qVUrH3KPD7JhmZ9MFc=
@@ -1058,12 +1102,13 @@ gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
@@ -1078,13 +1123,11 @@ sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 h1:mUcz5b3FJbP5Cvdq7Khzn6J9OCUQJaBwgBkCR+MOwSs=
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:FJGmPh3vz9jSos1L/F91iAgnC/aejc0wIIrF2ZwJxdY=
xorm.io/builder v0.3.6 h1:ha28mQ2M+TFx96Hxo+iq6tQgnkC9IZkM6D8w9sKHHF8=
xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU=
xorm.io/builder v0.3.7 h1:2pETdKRK+2QG4mLX4oODHEhn5Z8j1m8sXa7jfu+/SZI=
xorm.io/builder v0.3.7/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
xorm.io/core v0.7.2 h1:mEO22A2Z7a3fPaZMk6gKL/jMD80iiyNwRrX5HOv3XLw=
xorm.io/core v0.7.2/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
xorm.io/xorm v0.8.0 h1:iALxgJrX8O00f8Jk22GbZwPmxJNgssV5Mv4uc2HL9PM=
xorm.io/xorm v0.8.0/go.mod h1:ZkJLEYLoVyg7amJK/5r779bHyzs2AU8f8VMiP6BM7uY=
xorm.io/xorm v1.0.1 h1:/lITxpJtkZauNpdzj+L9CN/3OQxZaABrbergMcJu+Cw=
xorm.io/xorm v1.0.1/go.mod h1:o4vnEsQ5V2F1/WK6w4XTwmiWJeGj82tqjAnHe44wVHY=

+ 688
- 0
index.html View File

@@ -0,0 +1,688 @@
<!DOCTYPE html>
<html lang="en-US">
<head data-suburl="">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title> OpenI</title>
<link rel="manifest" href="/manifest.json" crossorigin="use-credentials">
<script>
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/serviceworker.js').then(function(registration) {
console.info('ServiceWorker registration successful with scope: ', registration.scope);
}, function(err) {
console.info('ServiceWorker registration failed: ', err);
});
}
</script>
<meta name="theme-color" content="#6cc644">
<meta name="author" content="OpenI - open i project management" />
<meta name="description" content="Efficient code management center, you can host and review code" />
<meta name="keywords" content="OpenI,git">
<meta name="referrer" content="no-referrer" />
<meta name="_csrf" content="R3EY-tMaxCo3C6fhAmc_WpVunPc6MTY1NzcwODA3NTE2MjQ4NTgzMQ" />

<script>
/*
@licstart The following is the entire license notice for the
JavaScript code in this page.

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

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

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

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

@licend The above is the entire license notice
for the JavaScript code in this page.
*/
</script>
<script>
window.config = {
AppSubUrl: '',
StaticUrlPrefix: '',
csrf: 'R3EY-tMaxCo3C6fhAmc_WpVunPc6MTY1NzcwODA3NTE2MjQ4NTgzMQ',
HighlightJS: false,
Minicolors: false,
SimpleMDE: false,
Tribute: false,
U2F: false,
Heatmap: false,
heatmapUser: null,
NotificationSettings: {
MinTimeout: 10000 ,
TimeoutStep: 10000 ,
MaxTimeout: 60000 ,
EventSourceUpdateTime: 10000 ,
},
};
</script>
<link rel="shortcut icon" href="/img/favicon.png">
<link rel="mask-icon" href="/img/openi-safari.svg" color="#609926">
<link rel="fluid-icon" href="/img/gitea-lg.png" title="OpenI">
<link rel="stylesheet" href="/vendor/assets/font-awesome/css/font-awesome.min.css">
<link rel="preload" as="font" href="/fomantic/themes/default/assets/fonts/icons.woff2" type="font/woff2" crossorigin="anonymous">
<link rel="preload" as="font" href="/fomantic/themes/default/assets/fonts/outline-icons.woff2" type="font/woff2" crossorigin="anonymous">
<link rel="stylesheet" href="/css/git.openi.css">



<link rel="stylesheet" href="/fomantic/semantic.min.css?v=eef985e4d4b587d055fc7c3eff3f18e9">
<link rel="stylesheet" href="/css/index.css?v=eef985e4d4b587d055fc7c3eff3f18e9">
<noscript>
<style>
.dropdown:hover > .menu { display: block; }
.ui.secondary.menu .dropdown.item > .menu { margin-top: 0; }
</style>
</noscript>

<style class="list-search-style"></style>

<meta property="og:title" content="OpenI">
<meta property="og:type" content="website" />
<meta property="og:image" content="/img/gitea-lg.png" />
<meta property="og:url" content="https://openi.pcl.ac.cn/" />
<meta property="og:description" content="Efficient code management center, you can host and review code">

<meta property="og:site_name" content="OpenI" />



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

<link rel="stylesheet" href="/RemixIcon_Fonts_v2.5.0/fonts/remixicon.css">

<link rel="stylesheet" href="/swiper/swiper-bundle.min.css">
<script src="/swiper/swiper-bundle.min.js"></script>

<link rel="stylesheet" href="/rotation3D/rotation3D.css">
</head>
<body>

<div class="full height">
<noscript>This website works better with JavaScript.</noscript>


<div class="ui top secondary stackable main menu following bar dark">
<div class="ui container" id="navbar">
<div class="item brand" style="justify-content: space-between;">
<a href="https://openi.org.cn/">
<img class="ui mini image" src="/img/logo-w.svg">
</a>
<div class="ui basic icon button mobile-only" id="navbar-expand-toggle">
<i class="sidebar icon"></i>
</div>
</div>
<div style="width:1px;background:#606266;height:80%;margin:auto 0.5rem"></div>
<div class="item brand" style="margin-left: 0.9rem;">
<a href="/">
<img class="ui mini image" style="height: 1.3rem;" src="/img/git-logo.svg">
</a>
</div>


<div class="item edge">
<div class="dropdown-menu">
<a class=" item lfpd" href="/user/login">
Home <i class="dropdown icon mglf"></i>
</a>
<div class="dropdown-content" style="min-width: 110px;border-radius:4px;min-width: max-content;">
<a style="border: none;color: #000;" class=" item" href="/user/login">Issues</a>
<a style="border: none;color: #000; white-space: nowrap;" class=" item" href="/user/login">Pull Requests</a>
<a style="border: none;color: #000;" class=" item" href="/user/login">Milestones</a>
<a style="border: none;color: #000;" class=" item" href="/cloudbrains">Cloudbrain Task</a>
</div>
</div>
</div>

<a class="item" href="/explore/repos">Repositories</a>
<a class="item" href="/explore/datasets">Datasets</a>
<div class="ui simple dropdown item" id='dropdown_PageHome'>
Explore
<i class="dropdown icon"></i>
<div class="menu" >
<a class="item" href="/explore/users">Users</a>
<a class="item" href="/explore/organizations">Organizations</a>
<a class="item" href="/explore/images">Cloudbrain Mirror</a>
<a class="item" href="/OpenI">OpenI Projects</a>
</div>
</div>




<div class="right stackable menu">
<form id="searchForm" class="fourteen wide mobile ten wide tablet ten wide computer column ui form ignore-dirty" style="margin:auto" action="/all/search/" method="post">
<div class="ui fluid action input" style="background:#363840 ;border-radius: 5px;width: 200px;height:30px;border: #888888 solid 1px;">
<input name="q" value="" placeholder="Search..."
style="transition: background-color 5000s ease-in-out 0s;-webkit-text-fill-color:#888888;background:#363840 ;color:#888888;border: none;outline: none;">
<input type="hidden" name="tab" value="">
<input type="hidden" name="sort" value="hot">
<button style="border: none;background-color: #363840;outline: none;border-radius:5px"><img type = "submit" style="width: 25px; height: 25px;margin: auto;" src="/img/search.svg" >
</button>
</div>
</form>
<a class="item" href="/user/sign_up">
<svg class="svg octicon-person" width="16" height="16" aria-hidden="true"><use xlink:href="#octicon-person" /></svg> Register
</a>
<a class="item" rel="nofollow" href="/user/login">
<svg class="svg octicon-sign-in" width="16" height="16" aria-hidden="true"><use xlink:href="#octicon-sign-in" /></svg> Sign In
</a>
</div>


</div>


</div>
<div class="notic_content" id ="notic_content" style="display: block; position: relative">
<div class="ui container">
<marquee behavior="scroll" direction="left">
<a href=https://openi.org.cn/html/2022/notices_0701/636.html class="a_width" style = 'margin-left: 0px !important;' target="_blank">
<i class="ri-arrow-right-s-line"></i>
“我为开源打榜狂”上榜领奖者名单公示1周,10万奖金被瓜分,请大家自行确认&gt;&gt;&gt;
</a>
<a href=https://openi.pcl.ac.cn/OpenIOSSG/promote/src/branch/master/notice/Other_notes/RegisterMobileNumber.md class="a_width" target="_blank">
<i class="ri-arrow-right-s-line"></i>
7月中下旬登录启智AI协作平台,需登记手机号码啦&gt;&gt;&gt;
</a>
<a href=https://openi.org.cn/html/2022/dongtai_0628/634.html class="a_width" target="_blank">
<i class="ri-arrow-right-s-line"></i>
智算网络Beta版本上线,大大缩短算力排队时间,速来体验吧~&gt;&gt;&gt;
</a>
<a href=https://wj.qq.com/s2/10362208/5c0c class="a_width" target="_blank">
<i class="ri-arrow-right-s-line"></i>
启智AI协作平台问卷调查,邀请您参加&gt;&gt;&gt;
</a>
</marquee>
<div class="item right" style="position:absolute;right: 1px;top:0px;">
<i class="ri-close-fill x_icon" onclick="closeNoice()"></i>
</div>
</div>
</div>



<script>
function closeNoice(){
document.getElementById("notic_content").style.display='none'
localStorage.setItem("isCloseNotice",true)
}
function isShowNotice(){
var current_notice = localStorage.getItem("notices")

if (current_notice != "f43dc1a5866fbf7a29c92a1eef3b6a020d4a30de"){
localStorage.setItem('notices',"f43dc1a5866fbf7a29c92a1eef3b6a020d4a30de");
isNewNotice=true;
localStorage.setItem("isCloseNotice",false)
}else{
isNewNotice=false;
}
let isShowNoticeTag = false;
let notices= [{"Title":"“我为开源打榜狂”上榜领奖者名单公示1周,10万奖金被瓜分,请大家自行确认\u003e\u003e\u003e","Link":"https://openi.org.cn/html/2022/notices_0701/636.html","Visible":1},{"Title":"7月中下旬登录启智AI协作平台,需登记手机号码啦\u003e\u003e\u003e","Link":"https://openi.pcl.ac.cn/OpenIOSSG/promote/src/branch/master/notice/Other_notes/RegisterMobileNumber.md","Visible":1},{"Title":"智算网络Beta版本上线,大大缩短算力排队时间,速来体验吧~\u003e\u003e\u003e","Link":"https://openi.org.cn/html/2022/dongtai_0628/634.html","Visible":1},{"Title":"启智AI协作平台问卷调查,邀请您参加\u003e\u003e\u003e","Link":"https://wj.qq.com/s2/10362208/5c0c","Visible":1}]
if(notices != null && notices!=''){
for (i =0;i<notices.length;i++){
if (notices[i].Visible==1){
isShowNoticeTag =true;
break;
}
}
}
if (isShowNoticeTag){
if(isNewNotice){
document.getElementById("notic_content").style.display='block'
}else{
isCloseNotice = localStorage.getItem("isCloseNotice")
if (JSON.parse(isCloseNotice)){
document.getElementById("notic_content").style.display='none'
}else{
document.getElementById("notic_content").style.display='block'
}

}
}else{
if (document.getElementById("notic_content") != null){
document.getElementById("notic_content").style.display='none'
}
}
}
if(!("" == true || "" =='true')) {
isShowNotice();
}
</script>
<div class="ui vertical masthead secondary hometop segment">
<div class="ui container" style="position: relative;">
<div class="ui center homebanner">
<h1 class="ui huge header">
Explore Better AI
<div class="sub header">
OpenI AI Development Cooperation Platform
</div>
</h1>
<p class="ui am-lh-18">The one-stop collaborative development environment for AI field provides AI development pipeline integrating code development, data management, model debugging, reasoning and evaluation</p>
<a class="circular huge ui secondary button" href="/user/login">Use Now <i class="right arrow icon"></i></a>
</div>
<div class="bannerpic"><img class="ui fluid image" src="/img/gitopeni-index-01.svg"></div>
<div id="homenews">
<p>* Only show the dynamics of open source projects</p>
<div class="ui grid">
<div class="sixteen wide mobile twelve wide tablet ten wide computer column homenews">
<div class="newslist">
<div class="ui mini aligned list swiper-wrapper" id="newmessage">
</div>
</div>
</div>
</div>
</div>
</div>
</div>

<div class="ui container homeorg">
<div class="ui stackable grid">
<div class="sixteen wide tablet four wide computer column homeorg-tit">
<h2>Recommended Organizations</h2>
<p><span class="ui text grey">These excellent organizations are using the OpenI AI Collaboration Platform for collaborative development of projects. To show your organization here,&nbsp;</span><a href="/OpenIOSSG/promote/">Click here to submit.</a></p>
<a href="/explore/organizations" class="circular ui primary basic button">More Organizations <i class="arrow circle right icon"></i></a>
</div>
<div class="sixteen wide tablet twelve wide computer column">
<div class="homeorg-list">
<div class="swiper-wrapper" id="recommendorg">
</div>
<div class="swiper-pagination"></div>
</div>
</div>

<div class="sixteen wide tablet four wide computer column homeorg-tit">
<h2>Community Activities</h2>
<p><span class="ui text grey">The community has prepared a wealth of activities, waiting for you to participate!</p>
</div>
<div class="sixteen wide tablet twelve wide computer column">
<div class="event-list">
<div class="swiper-wrapper" id="recommendactivity">
</div>
<div class="swiper-pagination"></div>
</div>
</div>
</div>
<div class="leftline01"></div>
</div>
<div class="ui container homepro">
<div class="leftline02"></div>
<div class="leftline02-2"></div>
<div class="ui center homepro-tit am-mb-20">
<h2>Recommended Projects</h2>
<p><span class="ui text grey">Excellent AI projects recommendation. To show your project here,&nbsp;</span><a href="/OpenIOSSG/promote/">Click here to submit.</a>Click here to&nbsp;<a href="/explore/">explore more projects.</a></p>
</div>

<div class="homepro-list">
<div class="swiper-wrapper" id="recommendrepo">
</div>
<div class="swiper-pagination"></div>
</div>
</div>

<div class="ui vertical masthead secondary c2net segment">
<div class="ui container">
<div class="ui center am-pt-30 am-pb-30">
<h2>智算网络</h2>
<p><span class="ui text grey">人工智能算力网络推进联盟已接入10家智算中心,算力总规模1542P</p>
</div>

<div id="app" v-cloak>
<div class="rotation3D-baseMap"></div>
<div id="rotation3D" class="rotation3D">
<button class="center">中心</button>
<div class="itemList">
<div class="rotation3D__item" :class="item.type" v-for="item in itemList">
<div class="scale">
<div class="baseImg"></div>
<div class="cont">
<i class="iconfont" :class="item.icon"></i>
<p></p>
</div>
</div>
</div>
</div>
<div class="lineList">
<div class="rotation3D__line" v-for="item in itemList" :class="item.type">
<div v-if="item.type=='blue'" class="pos">
<svg width="50" height="400">
<path id="path1" d="M0 400, 0 0" stroke-dasharray="5,10"/>
</svg>
<div class="dot dot1 ri-arrow-left-s-line"><span></span></div>
</div>
<div v-if="item.type=='yellow'" class="pos">
<svg width="10" height="400">
<path id="path2" d="M0 400, 0 0" stroke-dasharray="5,10"/>
</svg>
<div class="dot dot2"><i class="el-icon-close"></i></div>
</div>
<div v-if="item.type=='green'" class="pos">
<svg width="50" height="400">
<path id="path1" d="M0 400, 0 0" stroke-dasharray="5,10"/>
</svg>
<div class="dot dot1 ri-arrow-left-s-line"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

<a name="fourth"></a>
<div class="ui container i-env">
<div class="ui center am-pb-30">
<h2>Collaborative Development Environment</h2>
<p><span class="ui text grey">Provide a collaborative development environment for AI development, which is the biggest highlight that distinguishes the OpenI AI Collaboration Platform from other traditional Git platforms.</p>
</div>
<div class="ui four doubling cards">
<div class="card">
<div class="image">
<img src="/img/i-pic-01.jpg">
</div>
<div class="content">
<h3 class="ui centered small header">Unified Management of Development Elements</h3>
<div class="description ui text grey">
The platform provides four elements of AI development: unified management of model code, data set, model and execution environment.
</div>
</div>
</div>
<div class="card">
<div class="image">
<img src="/img/i-pic-02.jpg">
</div>
<div class="content">
<h3 class="ui centered small header">Data Collaboration and Sharing</h3>
<div class="description ui text grey">
By uploading data sets in the project, many project members cooperate to complete data preprocessing. You can also establish a better model with community developers by setting the data as a public dataset.
</div>
</div>
</div>
<div class="card">
<div class="image">
<img src="/img/i-pic-03.jpg">
</div>
<div class="content">
<h3 class="ui centered small header">Model Management and Sharing</h3>
<div class="description ui text grey">
Associate the model with the code version, you can adjust the model in different ways based on the historical version of the code and save the results. The trained model can be open and shared, so that more people can use the model to test and give feedback.
</div>
</div>
</div>
<div class="card">
<div class="image">
<img src="/img/i-pic-04.jpg">
</div>
<div class="content">
<h3 class="ui centered small header">Once Configuration, Multiple Reuse</h3>
<div class="description ui text grey">
Provide execution environment sharing, Once Configuration, Multiple Reuse. Lower the threshold of model development, and avoid spending repetitive time configuring complex environments.
</div>
</div>
</div>
</div>
</div>

<a name="fifth"></a>
<div class="ui container">
<div class="ui very padded inverted segment radius15">
<div class="ui stackable grid">
<div class="six wide column">
<img class="ui centered large image" src="/img/i-yunnao.svg">
</div>
<div class="ten wide column am-pt-30">
<h2 class="ui grey inverted header">PengCheng Cloudbrain Open Source Collaboration</h2>
<p class="am-lh-18 ui text grey">
The platform has been connected with Pengcheng Cloudbrain and can use the rich computing resources of Pengcheng Cloudbrain to complete AI development tasks.<br>
Pengcheng Cloudbrain&#39;s existing AI computing power is 100p FLOPS@FP16 (billions of half precision floating-point calculations per second), the main hardware infrastructure is composed of GPU server equipped with NVIDIA Tesla V100 and Atlas 900 AI cluster equipped with Kunpeng and Ascend processors.<br>
Developers can freely choose the corresponding computing resources according to their needs, and can test the adaptability, performance, stability of the model in different hardware environments.<br>
If your model requires more computing resources, you can also apply for it separately.<br>
</p>
<a class="ui blue basic button am-mt-20" href="/user/login">Use Now</a>
<a class="ui grey basic button am-mt-20" href="mailto:aiforge@openi.org.cn">Apply Separately</a>
</div>
</div>
</div>
</div>
<div class="am-mt-30"></div>
<script src="/self/js/jquery.min.js" type="text/javascript"></script>
<script src="/home/home.js?v=eef985e4d4b587d055fc7c3eff3f18e9" type="text/javascript"></script>





</div>


<footer>
<div class="ui container">
<div class="ui grid">
<div class="sixteen wide mobile eight wide tablet eight wide computer column">
<div class="ui three column grid">
<div class="column ui vertical text menu">
<div class="header item">Community</div>
<a href="https://openi.org.cn/html/Club/2019/0227/14.html" class="item">Council</a>
<a href="https://openi.org.cn/html/Club/2019/0227/14.html" class="item">Technical Committee</a>
<a href="https://openi.org.cn/html/Club/2019/0228/17.html" class="item">Join OpenI</a>
<a href="/home/term/" class="item">Use agreement</a>
</div>
<div class="column ui vertical text menu">
<div class="header item">News</div>
<a href="https://openi.org.cn/html/news/dongtai/" class="item">Community News</a>
<a href="https://openi.org.cn/html/news/huodong/" class="item">Member news</a>
<a href="https://openi.org.cn/html/news/zixun/" class="item">Industry Advisory</a>
</div>
<div class="column ui vertical text menu">
<div class="header item">help</div>
<div class="ui language bottom floating slide up dropdown link item">
<i class="world icon"></i>
<div class="text">English</div>
<div class="menu">
<a lang="en-US" class="item active selected" href="#">English</a>
<a lang="zh-CN" class="item " href="?lang=zh-CN">简体中文</a>
</div>
</div>

<a href="https://openi.pcl.ac.cn/zeizei/OpenI_Learning" class=" item a_margin" target="_blank"><i class="ri-creative-commons-by-line footer_icon" ></i><p class="footer_icon">Tutorial</p> </a>
<a href="/api/swagger" class=" item a_margin"><i class="ri-exchange-line footer_icon" > </i><p class="footer_icon">API</p> </a>
<a href="/user/login" class=" item a_margin" ><i class="ri-mail-send-line footer_icon" ></i><p class="footer_icon">Feedback</p></a>

</div>
</div>
</div>
<div class="sixteen wide mobile eight wide tablet eight wide computer column" style=" margin:2.0rem 0">
Copyright: New Generation Artificial Intelligence Open Source Open Platform (OpenI) <a href="http://beian.miit.gov.cn/" target="_blank">京ICP备18004880号</a>
<br>
Powered_by 鹏城实验室云脑、<a href="https://www.trustie.net/" target="_blank">Trustie确实</a>、gitea
<br>
</div>
</div>
</div>
</footer>


<script src="/js/jquery.js?v=eef985e4d4b587d055fc7c3eff3f18e9"></script>










<script rel="stylesheet" src="/vendor/plugins/jquery.particleground/jquery.particleground.min.js"></script>

<script src="/fomantic/semantic.min.js?v=eef985e4d4b587d055fc7c3eff3f18e9"></script>
<script src="/js/index.js?v=eef985e4d4b587d055fc7c3eff3f18e9"></script>


<script src="/rotation3D/vue-2.6.10.min.js"></script>
<script src="/rotation3D/rotation3D.js?v=eef985e4d4b587d055fc7c3eff3f18e9"></script>
<script>
var app = new Vue({
el: "#app",
data: {
itemList: [
{ name:'鹏城云脑一号', type:'blue', icon:'', },
{ name:'鹏城云脑二号', type:'blue', icon:'', },
{ name:'北大人工智能集群系统', type:'green', icon:'', },
{ name:'合肥类脑智能开放平台', type:'green', icon:'', },
{ name:'武汉人工智能计算中心', type:'green', icon:'', },
{ name:'西安未来人工智能计算中心', type:'green', icon:'', },
{ name:'……', type:'yellow', icon:'', },
{ name:'中原人工智能计算中心', type:'green', icon:'', },
{ name:'成都人工智能计算中心', type:'green', icon:'', },
{ name:'横琴先进智能计算中心', type:'green', icon:'', },
{ name:'国家超级计算济南中心', type:'green', icon:'', },
],
},
mounted: function () {
new Rotation3D({
id: '#rotation3D',
farScale: 0.6,
xRadius: 0,
yRadius: 130,
})
},
methods: {},
});
</script>


</body>
</html>


+ 1
- 1
integrations/pull_update_test.go View File

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

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



+ 155
- 0
models/action.go View File

@@ -49,6 +49,25 @@ const (
ActionApprovePullRequest // 21
ActionRejectPullRequest // 22
ActionCommentPull // 23

ActionUploadAttachment //24
ActionCreateDebugGPUTask //25
ActionCreateDebugNPUTask //26
ActionCreateTrainTask //27
ActionCreateInferenceTask // 28
ActionCreateBenchMarkTask //29
ActionCreateNewModelTask //30
ActionCreateGPUTrainTask //31
ActionCreateGrampusNPUTrainTask //32
ActionCreateGrampusGPUTrainTask //33
ActionBindWechat //34
ActionDatasetRecommended //35
ActionCreateImage //36
ActionImageRecommend //37
ActionChangeUserAvatar //38
ActionCreateGrampusNPUDebugTask //39
ActionCreateGrampusGPUDebugTask //40
ActionCreateGrampusGCUDebugTask //41
)

// Action represents user operation type and other information to
@@ -70,6 +89,19 @@ type Action struct {
IsTransformed bool `xorm:"INDEX NOT NULL DEFAULT false"`
Content string `xorm:"TEXT"`
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
Cloudbrain *Cloudbrain `xorm:"-"`
}

type ActionShow struct {
OpType ActionType
TaskType TaskType
RepoLink string
ShortRepoFullDisplayName string
Content string
RefName string
IssueInfos []string
CommentLink string
Cloudbrain *CloudbrainShow4Action
}

// GetOpType gets the ActionType of this action.
@@ -164,12 +196,24 @@ func (a *Action) GetRepoName() string {
return a.Repo.Name
}

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

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

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

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

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

// GetRepoLink returns relative link to action repository.
func (a *Action) GetRepoLink() string {
if len(setting.AppSubURL) > 0 {
@@ -189,6 +239,40 @@ func (a *Action) GetRepoLink() string {
return "/" + a.GetRepoPath()
}

func (a *Action) ToShow() *ActionShow {
actionShow := &ActionShow{}
actionShow.OpType = a.OpType
actionShow.TaskType = GetTaskTypeFromAction(a.OpType)
actionShow.Content = a.Content
actionShow.RefName = a.RefName

if strings.Contains(a.Content, "|") && a.IsIssueAction() {
actionShow.IssueInfos = a.GetIssueInfos()
}

if a.Repo != nil {
actionShow.RepoLink = a.GetRepoLink()
actionShow.ShortRepoFullDisplayName = a.ShortRepoFullDisplayName()
}
if a.Comment != nil {
actionShow.CommentLink = a.GetCommentLink()
}

if a.Cloudbrain != nil {
c := &CloudbrainShow4Action{
ID: a.Cloudbrain.ID,
JobID: a.Cloudbrain.JobID,
Type: a.Cloudbrain.Type,
JobType: a.Cloudbrain.JobType,
DisplayJobName: a.Cloudbrain.DisplayJobName,
ComputeResource: a.Cloudbrain.ComputeResource,
}
actionShow.Cloudbrain = c
}

return actionShow
}

// GetRepositoryFromMatch returns a *Repository from a username and repo strings
func GetRepositoryFromMatch(ownerName string, repoName string) (*Repository, error) {
var err error
@@ -286,6 +370,42 @@ func (a *Action) GetIssueContent() string {
return issue.Content
}

func (a *Action) IsCloudbrainAction() bool {
switch a.OpType {
case ActionCreateDebugGPUTask,
ActionCreateDebugNPUTask,
ActionCreateTrainTask,
ActionCreateInferenceTask,
ActionCreateBenchMarkTask,
ActionCreateGPUTrainTask,
ActionCreateGrampusGPUDebugTask,
ActionCreateGrampusNPUDebugTask,
ActionCreateGrampusNPUTrainTask,
ActionCreateGrampusGPUTrainTask,
ActionCreateGrampusGCUDebugTask:
return true
}
return false
}

func (a *Action) IsIssueAction() bool {
switch a.OpType {
case ActionCreateIssue,
ActionCloseIssue,
ActionClosePullRequest,
ActionReopenIssue,
ActionReopenPullRequest,
ActionCommentPull,
ActionCommentIssue,
ActionCreatePullRequest,
ActionApprovePullRequest,
ActionRejectPullRequest,
ActionMergePullRequest:
return true
}
return false
}

// GetFeedsOptions options for retrieving feeds
type GetFeedsOptions struct {
RequestedUser *User // the user we want activity for
@@ -346,6 +466,26 @@ func GetFeeds(opts GetFeedsOptions) ([]*Action, error) {
return actions, nil
}

func GetLast20PublicFeeds(opTypes []int) ([]*Action, error) {
cond := builder.NewCond()
cond = cond.And(builder.Eq{"is_private": false})
cond = cond.And(builder.Eq{"is_deleted": false})
cond = cond.And(builder.Expr("user_id=act_user_id"))
cond = cond.And(builder.In("op_type", opTypes))

actions := make([]*Action, 0, 20)

if err := x.Limit(30).Desc("id").Where(cond).Find(&actions); err != nil {
return nil, fmt.Errorf("Find: %v", err)
}

if err := ActionList(actions).LoadAttributes(); err != nil {
return nil, fmt.Errorf("LoadAttributes: %v", err)
}

return actions, nil
}

func GetUnTransformedActions() ([]*Action, error) {
actions := make([]*Action, 0, 10)
err := x.Where("op_type = ?", ActionCommitRepo).
@@ -355,3 +495,18 @@ func GetUnTransformedActions() ([]*Action, error) {
Find(&actions)
return actions, err
}

func GetActionByIds(ids []int64) ([]*Action, error) {
if len(ids) == 0 {
return nil, nil
}
actions := make([]*Action, 0)
err := x.In("id", ids).Find(&actions)
if err != nil {
return nil, err
}
if err := ActionList(actions).LoadAllAttributes(); err != nil {
return nil, fmt.Errorf("ActionList loadAttributes: %v", err)
}
return actions, nil
}

+ 165
- 1
models/action_list.go View File

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

package models

import "fmt"
import (
"fmt"
"strconv"
"xorm.io/builder"
)

// ActionList defines a list of actions
type ActionList []*Action
@@ -26,6 +30,9 @@ func (actions ActionList) loadUsers(e Engine) ([]*User, error) {

userIDs := actions.getUserIDs()
userMaps := make(map[int64]*User, len(userIDs))
if len(userIDs) == 0 {
return make([]*User, 0), nil
}
err := e.
In("id", userIDs).
Find(&userMaps)
@@ -61,6 +68,9 @@ func (actions ActionList) loadRepositories(e Engine) ([]*Repository, error) {

repoIDs := actions.getRepoIDs()
repoMaps := make(map[int64]*Repository, len(repoIDs))
if len(repoIDs) == 0 {
return make([]*Repository, 0), nil
}
err := e.
In("id", repoIDs).
Find(&repoMaps)
@@ -79,6 +89,133 @@ func (actions ActionList) LoadRepositories() ([]*Repository, error) {
return actions.loadRepositories(x)
}

func (actions ActionList) getCommentIDs() []int64 {
commentIDs := make(map[int64]struct{}, len(actions))
for _, action := range actions {
if action.CommentID == 0 {
continue
}
if _, ok := commentIDs[action.CommentID]; !ok {
commentIDs[action.CommentID] = struct{}{}
}
}
return keysInt64(commentIDs)
}

func (actions ActionList) loadComments(e Engine) ([]*Comment, error) {
if len(actions) == 0 {
return nil, nil
}

commentIDs := actions.getCommentIDs()

commentMaps := make(map[int64]*Comment, len(commentIDs))
if len(commentIDs) == 0 {
return make([]*Comment, 0), nil
}
err := e.
In("id", commentIDs).
Find(&commentMaps)
if err != nil {
return nil, fmt.Errorf("find comment: %v", err)
}

for _, action := range actions {
if action.CommentID > 0 {
action.Comment = commentMaps[action.CommentID]
}
}
return valuesComment(commentMaps), nil
}

// LoadComments loads actions' all comments
func (actions ActionList) LoadComments() ([]*Comment, error) {
return actions.loadComments(x)
}

func (actions ActionList) getCloudbrainIDs() []int64 {
cloudbrainIDs := make(map[int64]struct{}, 0)
for _, action := range actions {
if !action.IsCloudbrainAction() {
continue
}
cloudbrainId, _ := strconv.ParseInt(action.Content, 10, 64)
if _, ok := cloudbrainIDs[cloudbrainId]; !ok {
cloudbrainIDs[cloudbrainId] = struct{}{}
}
}
return keysInt64(cloudbrainIDs)
}

func (actions ActionList) getCloudbrainJobIDs() []string {
cloudbrainJobIDs := make(map[string]struct{}, 0)
for _, action := range actions {
if !action.IsCloudbrainAction() {
continue
}
if _, ok := cloudbrainJobIDs[action.Content]; !ok {
cloudbrainJobIDs[action.Content] = struct{}{}
}
}
return keysString(cloudbrainJobIDs)
}

func (actions ActionList) loadCloudbrains(e Engine) ([]*Cloudbrain, error) {
if len(actions) == 0 {
return nil, nil
}
cloudbrainIDs := actions.getCloudbrainIDs()
cloudbrainJobIDs := actions.getCloudbrainJobIDs()

cloudbrainMaps := make(map[int64]*Cloudbrain, len(cloudbrainIDs))
if len(cloudbrainIDs) == 0 {
return make([]*Cloudbrain, 0), nil
}
//由于各个类型的云脑任务在发布action的时候,content字段保存的ID含义不同,部分取的是ID,部分取的是jobId
//所以在查询action对应的cloudbrain对象时,以这两个字段做为条件查询
cond := builder.Or(builder.In("id", cloudbrainIDs)).Or(builder.In("job_id", cloudbrainJobIDs))
err := e.
Where(cond).Unscoped().
Find(&cloudbrainMaps)
if err != nil {
return nil, fmt.Errorf("find cloudbrain: %v", err)
}

cloudBrainJobIdMap := make(map[string]*Cloudbrain, len(cloudbrainIDs))
for _, v := range cloudbrainMaps {
cloudBrainJobIdMap[v.JobID] = v
}

for _, action := range actions {
if !action.IsCloudbrainAction() {
continue
}
cloudbrainId, _ := strconv.ParseInt(action.Content, 10, 64)
if cloudbrainId > 0 {
if c, ok := cloudbrainMaps[cloudbrainId]; ok {
if c.DisplayJobName == action.RefName || c.JobName == action.RefName {
action.Cloudbrain = c
continue
}

}
}
if c, ok := cloudBrainJobIdMap[action.Content]; ok {
if c.DisplayJobName == action.RefName || c.JobName == action.RefName {
action.Cloudbrain = c
continue
}

}
}
return valuesCloudbrain(cloudbrainMaps), nil
}

// LoadComments loads actions' all comments
func (actions ActionList) LoadCloudbrains() ([]*Comment, error) {
return actions.loadComments(x)
}

// loadAttributes loads all attributes
func (actions ActionList) loadAttributes(e Engine) (err error) {
if _, err = actions.loadUsers(e); err != nil {
@@ -96,3 +233,30 @@ func (actions ActionList) loadAttributes(e Engine) (err error) {
func (actions ActionList) LoadAttributes() error {
return actions.loadAttributes(x)
}

// LoadAllAttributes loads all attributes of the actions
// compare with LoadAttributes() ,LoadAllAttributes() loads Comment and Cloudbrain attribute
func (actions ActionList) LoadAllAttributes() error {
return actions.loadAllAttributes(x)
}

// loadAllAttributes
func (actions ActionList) loadAllAttributes(e Engine) (err error) {
if _, err = actions.loadUsers(e); err != nil {
return
}

if _, err = actions.loadRepositories(e); err != nil {
return
}

if _, err = actions.loadComments(e); err != nil {
return
}

if _, err = actions.loadCloudbrains(e); err != nil {
return
}

return nil
}

+ 49
- 0
models/admin_operate_log.go View File

@@ -0,0 +1,49 @@
package models

import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/timeutil"
"encoding/json"
)

type AdminOperateLog struct {
ID int64 `xorm:"pk autoincr"`
BizType string
OperateType string
OldValue string `xorm:"TEXT"`
NewValue string `xorm:"TEXT"`
RelatedId string `xorm:"INDEX"`
Comment string
CreatedTime timeutil.TimeStamp `xorm:"created"`
CreatedBy int64
}

type LogValues struct {
Params []LogValue
}

type LogValue struct {
Key string
Val interface{}
}

func (l *LogValues) Add(key string, val interface{}) *LogValues {
l.Params = append(l.Params, LogValue{Key: key, Val: val})
return l
}

func (l *LogValues) JsonString() string {
if len(l.Params) == 0 {
return ""
}
b, err := json.Marshal(l)
if err != nil {
log.Error("LogValues JsonString error . %v", err)
return ""
}
return string(b)
}

func InsertAdminOperateLog(log AdminOperateLog) (int64, error) {
return x.Insert(&log)
}

+ 387
- 36
models/ai_model_manage.go View File

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

import (
"fmt"
"time"

"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
@@ -11,31 +12,70 @@ import (
)

type AiModelManage struct {
ID string `xorm:"pk"`
Name string `xorm:"INDEX NOT NULL"`
Version string `xorm:"NOT NULL"`
VersionCount int `xorm:"NOT NULL DEFAULT 0"`
New int `xorm:"NOT NULL"`
Type int `xorm:"NOT NULL"`
Size int64 `xorm:"NOT NULL"`
Description string `xorm:"varchar(2000)"`
Label string `xorm:"varchar(1000)"`
Path string `xorm:"varchar(400) NOT NULL"`
DownloadCount int `xorm:"NOT NULL DEFAULT 0"`
Engine int64 `xorm:"NOT NULL DEFAULT 0"`
Status int `xorm:"NOT NULL DEFAULT 0"`
Accuracy string `xorm:"varchar(1000)"`
AttachmentId string `xorm:"NULL"`
RepoId int64 `xorm:"INDEX NULL"`
CodeBranch string `xorm:"varchar(400) NULL"`
CodeCommitID string `xorm:"NULL"`
UserId int64 `xorm:"NOT NULL"`
UserName string
UserRelAvatarLink string
TrainTaskInfo string `xorm:"text NULL"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
IsCanOper bool
ID string `xorm:"pk" json:"id"`
Name string `xorm:"INDEX NOT NULL" json:"name"`
ModelType int `xorm:"NULL" json:"modelType"`
Version string `xorm:"NOT NULL" json:"version"`
VersionCount int `xorm:"NOT NULL DEFAULT 0" json:"versionCount"`
New int `xorm:"NOT NULL" json:"new"`
Type int `xorm:"NOT NULL" json:"type"`
Size int64 `xorm:"NOT NULL" json:"size"`
Description string `xorm:"varchar(2000)" json:"description"`
Label string `xorm:"varchar(1000)" json:"label"`
Path string `xorm:"varchar(400) NOT NULL" json:"path"`
DownloadCount int `xorm:"NOT NULL DEFAULT 0" json:"downloadCount"`
Engine int64 `xorm:"NOT NULL DEFAULT 0" json:"engine"`
Status int `xorm:"NOT NULL DEFAULT 0" json:"status"`
StatusDesc string `xorm:"varchar(500)" json:"statusDesc"`
Accuracy string `xorm:"varchar(1000)" json:"accuracy"`
AttachmentId string `xorm:"NULL" json:"attachmentId"`
RepoId int64 `xorm:"INDEX NULL" json:"repoId"`
CodeBranch string `xorm:"varchar(400) NULL" json:"codeBranch"`
CodeCommitID string `xorm:"NULL" json:"codeCommitID"`
UserId int64 `xorm:"NOT NULL" json:"userId"`
IsPrivate bool `xorm:"DEFAULT true" json:"isPrivate"`
UserName string `json:"userName"`
UserRelAvatarLink string `json:"userRelAvatarLink"`
TrainTaskInfo string `xorm:"text NULL" json:"trainTaskInfo"`
CreatedUnix timeutil.TimeStamp `xorm:"created" json:"createdUnix"`
UpdatedUnix timeutil.TimeStamp `xorm:"updated" json:"updatedUnix"`
IsCanOper bool `json:"isCanOper"`
IsCanDelete bool `json:"isCanDelete"`
IsCanDownload bool `json:"isCanDownload"`
}

type AiModelConvert struct {
ID string `xorm:"pk" json:"id"`
Name string `xorm:"INDEX NOT NULL" json:"name"`
Status string `xorm:"NULL" json:"status"`
StatusResult string `xorm:"NULL" json:"statusResult"`
SrcEngine int `xorm:"NOT NULL DEFAULT 0" json:"srcEngine"`
RepoId int64 `xorm:"INDEX NULL" json:"repoId"`
ModelId string `xorm:"NOT NULL" json:"modelId"`
ModelName string `xorm:"NULL" json:"modelName"`
ModelVersion string `xorm:"NOT NULL" json:"modelVersion"`
ModelPath string `xorm:"NULL" json:"modelPath"`
DestFormat int `xorm:"NOT NULL DEFAULT 0" json:"destFormat"`
NetOutputFormat int `xorm:"NULL" json:"netOutputFormat"`
UserId int64 `xorm:"NOT NULL" json:"userId"`
CloudBrainTaskId string `xorm:"NULL" json:"cloudBrainTaskId"`
ModelArtsVersionId string `xorm:"NULL" json:"modelArtsVersionId"`
ContainerID string `json:"containerID"`
ContainerIp string `json:"containerIp"`
RunTime int64 `xorm:"NULL" json:"runTime"`
TrainJobDuration string `json:"trainJobDuration"`
InputShape string `xorm:"varchar(2000)" json:"inputShape"`
InputDataFormat string `xorm:"NOT NULL" json:"inputDataFormat"`
Description string `xorm:"varchar(2000)" json:"description"`
Path string `xorm:"varchar(400) NOT NULL" json:"path"`
CreatedUnix timeutil.TimeStamp `xorm:"created" json:"createdUnix"`
UpdatedUnix timeutil.TimeStamp `xorm:"updated" json:"updatedUnix"`
StartTime timeutil.TimeStamp `json:"startTime"`
EndTime timeutil.TimeStamp `json:"endTime"`
UserName string `json:"userName"`
UserRelAvatarLink string `json:"userRelAvatarLink"`
IsCanOper bool `json:"isCanOper"`
IsCanDelete bool `json:"isCanDelete"`
}

type AiModelQueryOptions struct {
@@ -46,7 +86,126 @@ type AiModelQueryOptions struct {
SortType string
New int
// JobStatus CloudbrainStatus
Type int
Type int
Status int
IsOnlyThisRepo bool
IsQueryPrivate bool
}

func (a *AiModelConvert) IsGpuTrainTask() bool {
if a.SrcEngine == 0 || a.SrcEngine == 1 || a.SrcEngine == 4 || a.SrcEngine == 6 {
return true
}
return false
}

func ModelComputeAndSetDuration(task *AiModelConvert, result JobResultPayload) {
if task.StartTime == 0 {
task.StartTime = timeutil.TimeStamp(result.JobStatus.CreatedTime / 1000)
}
if task.EndTime == 0 {
if result.JobStatus.CompletedTime > 0 {
task.EndTime = timeutil.TimeStamp(result.JobStatus.CompletedTime / 1000)
}
}
var d int64
if task.StartTime == 0 {
d = 0
} else if task.EndTime == 0 {
d = time.Now().Unix() - task.StartTime.AsTime().Unix()
} else {
d = task.EndTime.AsTime().Unix() - task.StartTime.AsTime().Unix()
}

if d < 0 {
d = 0
}
task.RunTime = d
task.TrainJobDuration = ConvertDurationToStr(d)
}

func ModelConvertSetDuration(task *AiModelConvert) {
var d int64
if task.StartTime == 0 {
d = 0
} else if task.EndTime == 0 {
d = time.Now().Unix() - task.StartTime.AsTime().Unix()
} else {
d = task.EndTime.AsTime().Unix() - task.StartTime.AsTime().Unix()
}

if d < 0 {
d = 0
}
task.RunTime = d
task.TrainJobDuration = ConvertDurationToStr(d)
}

func UpdateModelConvertModelArts(id string, CloudBrainTaskId string, VersionId string) error {
var sess *xorm.Session
sess = x.ID(id)
defer sess.Close()
re, err := sess.Cols("cloud_brain_task_id,model_arts_version_id").Update(&AiModelConvert{
CloudBrainTaskId: CloudBrainTaskId,
ModelArtsVersionId: VersionId,
})
if err != nil {
return err
}
log.Info("success to update cloud_brain_task_id from db.re=" + fmt.Sprint((re)))
return nil
}

func UpdateModelConvertFailed(id string, status string, statusResult string) error {
var sess *xorm.Session
sess = x.ID(id)
defer sess.Close()
re, err := sess.Cols("status", "status_result").Update(&AiModelConvert{
Status: status,
StatusResult: statusResult,
})
if err != nil {
return err
}
log.Info("success to update cloud_brain_task_id from db.re=" + fmt.Sprint((re)))
return nil
}

func UpdateModelConvertCBTI(id string, CloudBrainTaskId string) error {
var sess *xorm.Session
sess = x.ID(id)
defer sess.Close()
re, err := sess.Cols("cloud_brain_task_id").Update(&AiModelConvert{
CloudBrainTaskId: CloudBrainTaskId,
})
if err != nil {
return err
}
log.Info("success to update cloud_brain_task_id from db.re=" + fmt.Sprint((re)))
return nil
}

func UpdateModelConvert(job *AiModelConvert) error {
return updateModelConvert(x, job)
}

func updateModelConvert(e Engine, job *AiModelConvert) error {
var sess *xorm.Session
sess = e.Where("id = ?", job.ID)
_, err := sess.Cols("status", "train_job_duration", "run_time", "start_time", "end_time", "updated_unix").Update(job)
return err
}

func SaveModelConvert(modelConvert *AiModelConvert) error {
sess := x.NewSession()
defer sess.Close()
re, err := sess.Insert(modelConvert)
if err != nil {
log.Info("insert modelConvert error." + err.Error())
return err
}
log.Info("success to save modelConvert db.re=" + fmt.Sprint((re)))
return nil
}

func SaveModelToDb(model *AiModelManage) error {
@@ -62,25 +221,57 @@ func SaveModelToDb(model *AiModelManage) error {
return nil
}

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

func DeleteModelById(id string) error {
func QueryModelConvertById(id string) (*AiModelConvert, error) {
sess := x.NewSession()
defer sess.Close()
re := new(AiModelConvert)
isExist, err := sess.Table(new(AiModelConvert)).ID(id).Get(re)
if err == nil && isExist {
return re, nil
}
return nil, err
}

func QueryModelById(id string) (*AiModelManage, error) {
sess := x.NewSession()
defer sess.Close()
re := new(AiModelManage)
isExist, err := sess.Table(new(AiModelManage)).ID(id).Get(re)
if err == nil && isExist {
return re, nil
}
return nil, err
}

func DeleteModelConvertById(id string) error {
sess := x.NewSession()
defer sess.Close()
re, err := sess.Delete(&AiModelConvert{
ID: id,
})
if err != nil {
return err
}
log.Info("success to delete AiModelManageConvert from db.re=" + fmt.Sprint((re)))
return nil
}

func DeleteModelById(id string) error {
sess := x.NewSession()
defer sess.Close()
re, err := sess.Delete(&AiModelManage{
ID: id,
})
@@ -89,7 +280,6 @@ func DeleteModelById(id string) error {
}
log.Info("success to delete from db.re=" + fmt.Sprint((re)))
return nil

}

func ModifyModelDescription(id string, description string) error {
@@ -106,6 +296,69 @@ func ModifyModelDescription(id string, description string) error {
return nil
}

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

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

func ModifyModelSize(id string, size int64) error {
var sess *xorm.Session
sess = x.ID(id)
defer sess.Close()
re, err := sess.Cols("size").Update(&AiModelManage{
Size: size,
})
if err != nil {
return err
}
log.Info("success to update size from db.re=" + fmt.Sprint((re)))
return nil
}

func ModifyModelStatus(id string, modelSize int64, status int, modelPath string, statusDesc string) error {
var sess *xorm.Session
sess = x.ID(id)
defer sess.Close()
re, err := sess.Cols("size", "status", "path", "status_desc").Update(&AiModelManage{
Size: modelSize,
Status: status,
Path: modelPath,
StatusDesc: statusDesc,
})
if err != nil {
return err
}
log.Info("success to update ModelStatus from db.re=" + fmt.Sprint((re)))
return nil
}

func ModifyModelNewProperty(id string, new int, versioncount int) error {
var sess *xorm.Session
sess = x.ID(id)
@@ -141,6 +394,18 @@ func QueryModelByName(name string, repoId int64) []*AiModelManage {
return aiModelManageList
}

func QueryModelByPath(path string) (*AiModelManage, error) {
modelManage := new(AiModelManage)
has, err := x.Where("path=?", path).Get(modelManage)
if err != nil {
return nil, err
}
if !has {
return nil, ErrNotExist{}
}
return modelManage, nil
}

func QueryModel(opts *AiModelQueryOptions) ([]*AiModelManage, int64, error) {
sess := x.NewSession()
defer sess.Close()
@@ -176,6 +441,16 @@ func QueryModel(opts *AiModelQueryOptions) ([]*AiModelManage, int64, error) {
)
}

if (opts.Status) >= 0 {
cond = cond.And(
builder.Eq{"ai_model_manage.status": opts.Status},
)
}
if !opts.IsQueryPrivate {
cond = cond.And(
builder.Eq{"ai_model_manage.is_private": false},
)
}
count, err := sess.Where(cond).Count(new(AiModelManage))
if err != nil {
return nil, 0, fmt.Errorf("Count: %v", err)
@@ -200,3 +475,79 @@ func QueryModel(opts *AiModelQueryOptions) ([]*AiModelManage, int64, error) {

return aiModelManages, count, nil
}

func QueryModelConvertCountByRepoID(repoId int64) int64 {
convert := new(AiModelConvert)
total, _ := x.Where("repo_id =?", repoId).Count(convert)
return total
}

func QueryModelConvertByRepoID(repoId int64) ([]*AiModelConvert, error) {
sess := x.NewSession()
defer sess.Close()
var cond = builder.NewCond()
cond = cond.And(
builder.Eq{"ai_model_convert.repo_id": repoId},
)
sess.OrderBy("ai_model_convert.created_unix DESC")
aiModelManageConvert := make([]*AiModelConvert, 0)
if err := sess.Table(new(AiModelConvert)).Where(cond).
Find(&aiModelManageConvert); err != nil {
return nil, fmt.Errorf("Find: %v", err)
}
return aiModelManageConvert, nil
}

func QueryModelConvertByUserID(userID int64) ([]*AiModelConvert, error) {
sess := x.NewSession()
defer sess.Close()
var cond = builder.NewCond()
cond = cond.And(
builder.Eq{"ai_model_convert.user_id": userID},
)
sess.OrderBy("ai_model_convert.created_unix DESC")
aiModelManageConvert := make([]*AiModelConvert, 0)
if err := sess.Table(new(AiModelConvert)).Where(cond).
Find(&aiModelManageConvert); err != nil {
return nil, fmt.Errorf("Find: %v", err)
}
return aiModelManageConvert, nil
}

func QueryModelConvert(opts *AiModelQueryOptions) ([]*AiModelConvert, int64, error) {
sess := x.NewSession()
defer sess.Close()
var cond = builder.NewCond()
if opts.RepoID > 0 {
cond = cond.And(
builder.Eq{"ai_model_convert.repo_id": opts.RepoID},
)
}
if opts.UserID > 0 {
cond = cond.And(
builder.Eq{"ai_model_convert.user_id": opts.UserID},
)
}
count, err := sess.Where(cond).Count(new(AiModelConvert))
if err != nil {
return nil, 0, fmt.Errorf("Count: %v", err)
}

if opts.Page >= 0 && opts.PageSize > 0 {
var start int
if opts.Page == 0 {
start = 0
} else {
start = (opts.Page - 1) * opts.PageSize
}
sess.Limit(opts.PageSize, start)
}
sess.OrderBy("ai_model_convert.created_unix DESC")
aiModelManageConvert := make([]*AiModelConvert, 0, setting.UI.IssuePagingNum)
if err := sess.Table(new(AiModelConvert)).Where(cond).
Find(&aiModelManageConvert); err != nil {
return nil, 0, fmt.Errorf("Find: %v", err)
}

return aiModelManageConvert, count, nil
}

+ 106
- 14
models/attachment.go View File

@@ -9,6 +9,8 @@ import (
"fmt"
"io"
"path"
"strings"
"time"

"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/obs"
@@ -18,6 +20,7 @@ import (
"code.gitea.io/gitea/modules/timeutil"

gouuid "github.com/satori/go.uuid"
"xorm.io/builder"
"xorm.io/xorm"
)

@@ -38,7 +41,9 @@ type Attachment struct {
UploaderID int64 `xorm:"INDEX DEFAULT 0"` // Notice: will be zero before this column added
CommentID int64
Name string
Description string `xorm:"TEXT"`
DownloadCount int64 `xorm:"DEFAULT 0"`
UseNumber int64 `xorm:"DEFAULT 0"`
Size int64 `xorm:"DEFAULT 0"`
IsPrivate bool `xorm:"DEFAULT false"`
DecompressState int32 `xorm:"DEFAULT 0"`
@@ -47,6 +52,8 @@ type Attachment struct {

FileChunk *FileChunk `xorm:"-"`
CanDel bool `xorm:"-"`
Uploader *User `xorm:"-"`
Md5 string `xorm:"-"`
}

type AttachmentUsername struct {
@@ -78,6 +85,30 @@ func (a *Attachment) IncreaseDownloadCount() error {
return nil
}

func IncreaseAttachmentUseNumber(uuid string) error {

uuidArray := strings.Split(uuid, ";")
for i := range uuidArray {
uuidArray[i] = "'" + uuidArray[i] + "'"
}

uuidInCondition := "(" + strings.Join(uuidArray, ",") + ")"
// Update use number.
if _, err := x.Exec("UPDATE `attachment` SET use_number=use_number+1 WHERE uuid in " + uuidInCondition); err != nil {
return fmt.Errorf("increase attachment use count: %v", err)
}

return nil
}

func (a *Attachment) UpdateDatasetUpdateUnix() error {
// Update download count.
if _, err := x.Exec("UPDATE `dataset` SET updated_unix="+fmt.Sprint(time.Now().Unix())+" WHERE id=?", a.DatasetID); err != nil {
return fmt.Errorf("UpdateDatasetUpdateUnix: %v", err)
}
return nil
}

// APIFormat converts models.Attachment to api.Attachment
func (a *Attachment) APIFormat() *api.Attachment {
return &api.Attachment{
@@ -88,12 +119,26 @@ func (a *Attachment) APIFormat() *api.Attachment {
Size: a.Size,
UUID: a.UUID,
DownloadURL: a.DownloadURL(),
S3DownloadURL: a.S3DownloadURL(),
}
}

// DownloadURL returns the download url of the attached file
func (a *Attachment) DownloadURL() string {
return fmt.Sprintf("%sattachments/%s", setting.AppURL, a.UUID)
return fmt.Sprintf("%sattachments/%s?type=%d", setting.AppURL, a.UUID, a.Type)
}

// S3DownloadURL returns the s3 download url of the attached file
func (a *Attachment) S3DownloadURL() string {
url := ""
if a.Type == TypeCloudBrainOne {
url, _ = storage.Attachments.PresignedGetURL(setting.Attachment.Minio.BasePath+AttachmentRelativePath(a.UUID), a.Name)
} else if a.Type == TypeCloudBrainTwo {
objectName := strings.TrimPrefix(path.Join(setting.BasePath, path.Join(a.UUID[0:1], a.UUID[1:2], a.UUID, a.Name)), "/")
url, _ = storage.ObsGetPreSignedUrl(objectName, a.Name)
}

return url
}

// AttachmentRelativePath returns the relative path
@@ -214,6 +259,17 @@ func GetAttachmentsByCommentID(commentID int64) ([]*Attachment, error) {
return getAttachmentsByCommentID(x, commentID)
}

func GetAttachmentByDatasetIdFileName(fileName string, datasetId int64) (*Attachment, error) {
attach := &Attachment{DatasetID: datasetId, Name: fileName}
has, err := x.Get(attach)
if err != nil {
return nil, err
} else if !has {
return nil, err
}
return attach, nil
}

func getAttachmentsByCommentID(e Engine, commentID int64) ([]*Attachment, error) {
attachments := make([]*Attachment, 0, 10)
return attachments, e.Where("comment_id=?", commentID).Find(&attachments)
@@ -313,6 +369,18 @@ func DeleteAttachmentsByComment(commentID int64, remove bool) (int, error) {
func UpdateAttachment(atta *Attachment) error {
return updateAttachment(x, atta)
}
func UpdateAttachmentDescription(atta *Attachment) error {
return updateAttachmentDescription(x, atta)
}

func updateAttachmentDescription(e Engine, atta *Attachment) error {
var sess *xorm.Session

sess = e.ID(atta.ID)

_, err := sess.Cols("description").Update(atta)
return err
}

func updateAttachment(e Engine, atta *Attachment) error {
var sess *xorm.Session
@@ -413,19 +481,6 @@ func getPrivateAttachments(e Engine, userID int64) ([]*AttachmentUsername, error
return attachments, nil
}

func getAllUserAttachments(e Engine, userID int64) ([]*AttachmentUsername, error) {
attachments := make([]*AttachmentUsername, 0, 10)
if err := e.Table("attachment").Join("LEFT", "`user`", "attachment.uploader_id "+
"= `user`.id").Where("decompress_state= ? and attachment.type = ? and (uploader_id= ? or is_private = ?)", DecompressStateDone, TypeCloudBrainOne, userID, false).Find(&attachments); err != nil {
return nil, err
}
return attachments, nil
}

func GetAllUserAttachments(userID int64) ([]*AttachmentUsername, error) {
return getAllUserAttachments(x, userID)
}

func getModelArtsUserAttachments(e Engine, userID int64) ([]*AttachmentUsername, error) {
attachments := make([]*AttachmentUsername, 0, 10)
if err := e.Table("attachment").Join("LEFT", "`user`", "attachment.uploader_id "+
@@ -487,6 +542,43 @@ func GetAttachmentSizeByDatasetID(datasetID int64) (int64, error) {
return total, nil
}

func AttachmentsByDatasetOption(datasets []int64, opts *SearchDatasetOptions) ([]*Attachment, error) {
sess := x.NewSession()
defer sess.Close()
var cond = builder.NewCond()
cond = cond.And(builder.In("attachment.dataset_id", datasets))
if opts.JustNeedZipFile {
cond = cond.And(builder.Gt{"attachment.decompress_state": 0})
}
if opts.PublicOnly {
cond = cond.And(builder.Eq{"attachment.is_private": false})
}
if opts.CloudBrainType >= 0 {
cond = cond.And(builder.Eq{"attachment.type": opts.CloudBrainType})
}
if opts.UploadAttachmentByMe {
cond = cond.And(
builder.Eq{"attachment.uploader_id": opts.User.ID},
)
}

attachments := make([]*Attachment, 0)
if err := sess.Table(&Attachment{}).Where(cond).Desc("id").
Find(&attachments); err != nil {
return nil, fmt.Errorf("Find: %v", err)
}
return attachments, nil

}

func GetAllAttachmentSize() (int64, error) {
return x.SumInt(&Attachment{}, "size")
}

func GetAllDatasetContributorByDatasetId(datasetId int64) ([]*User, error) {
r := make([]*User, 0)
if err := x.Select("distinct(public.user.*)").Table("attachment").Join("LEFT", "user", "public.user.ID = attachment.uploader_id").Where("attachment.dataset_id = ?", datasetId).Find(&r); err != nil {
return nil, err
}
return r, nil
}

+ 181
- 0
models/badge.go View File

@@ -0,0 +1,181 @@
package models

import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
"path/filepath"
"strings"
"xorm.io/builder"
)

type Badge struct {
ID int64 `xorm:"pk autoincr"`
Name string
LightedIcon string `xorm:"varchar(2048)"`
GreyedIcon string `xorm:"varchar(2048)"`
Url string `xorm:"varchar(2048)"`
CategoryId int64
CreatedUnix timeutil.TimeStamp `xorm:"created"`
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
DeletedAt timeutil.TimeStamp `xorm:"deleted"`
}

func (m *Badge) ToUserShow() *Badge4UserShow {
return &Badge4UserShow{
Name: m.Name,
LightedIcon: GetIconOuterLink(m.LightedIcon),
GreyedIcon: GetIconOuterLink(m.GreyedIcon),
Url: m.Url,
}
}

type GetBadgeOpts struct {
BadgeType BadgeType
CategoryId int64
ListOpts ListOptions
}

type BadgeAndCategory struct {
Badge Badge `xorm:"extends"`
Category BadgeCategory `xorm:"extends"`
}

func (*BadgeAndCategory) TableName() string {
return "badge"
}

func (m *BadgeAndCategory) ToShow() *Badge4AdminShow {
return &Badge4AdminShow{
ID: m.Badge.ID,
Name: m.Badge.Name,
LightedIcon: GetIconOuterLink(m.Badge.LightedIcon),
GreyedIcon: GetIconOuterLink(m.Badge.GreyedIcon),
Url: m.Badge.Url,
CategoryName: m.Category.Name,
CategoryId: m.Category.ID,
CreatedUnix: m.Badge.CreatedUnix,
UpdatedUnix: m.Badge.UpdatedUnix,
}
}

type Badge4AdminShow struct {
ID int64
Name string
LightedIcon string
GreyedIcon string
Url string
CategoryName string
CategoryId int64
CreatedUnix timeutil.TimeStamp
UpdatedUnix timeutil.TimeStamp
}

func (m Badge4AdminShow) ToDTO() Badge {
return Badge{
Name: m.Name,
LightedIcon: m.LightedIcon,
GreyedIcon: m.GreyedIcon,
Url: m.Url,
CategoryId: m.CategoryId,
}
}

type BadgeOperateReq struct {
ID int64
Name string
LightedIcon string
GreyedIcon string
Url string
CategoryId int64
}

func (m BadgeOperateReq) ToDTO() Badge {
return Badge{
Name: m.Name,
LightedIcon: m.LightedIcon,
GreyedIcon: m.GreyedIcon,
Url: m.Url,
CategoryId: m.CategoryId,
}
}

type Badge4UserShow struct {
Name string
LightedIcon string
GreyedIcon string
Url string
}

type BadgeShowWithStatus struct {
Badge *Badge4UserShow
IsLighted bool
}

type UserAllBadgeInCategory struct {
CategoryName string
CategoryId int64
LightedNum int
Badges []*BadgeShowWithStatus
}

func GetBadgeList(opts GetBadgeOpts) (int64, []*BadgeAndCategory, error) {
if opts.ListOpts.Page <= 0 {
opts.ListOpts.Page = 1
}
var cond = builder.NewCond()
if opts.BadgeType > 0 {
cond = cond.And(builder.Eq{"badge_category.type": opts.BadgeType})
}
if opts.CategoryId > 0 {
cond = cond.And(builder.Eq{"badge_category.id": opts.CategoryId})
}
n, err := x.Join("INNER", "badge_category", "badge_category.ID = badge.category_id").Where(cond).Count(&BadgeAndCategory{})
if err != nil {
return 0, nil, err
}
r := make([]*BadgeAndCategory, 0)
if err = x.Join("INNER", "badge_category", "badge_category.ID = badge.category_id").Where(cond).OrderBy("badge.created_unix desc").Limit(opts.ListOpts.PageSize, (opts.ListOpts.Page-1)*opts.ListOpts.PageSize).Find(&r); err != nil {
return 0, nil, err
}
return n, r, nil
}

func AddBadge(m Badge) (int64, error) {
return x.Insert(&m)
}

func UpdateBadgeById(id int64, param Badge) (int64, error) {
return x.ID(id).Update(&param)
}

func DelBadge(id int64) (int64, error) {
return x.ID(id).Delete(&Badge{})
}

func GetBadgeById(id int64) (*Badge, error) {
m := &Badge{}
has, err := x.ID(id).Get(m)
if err != nil {
return nil, err
} else if !has {
return nil, &ErrRecordNotExist{}
}
return m, nil
}

func GetBadgeByCategoryId(categoryId int64) ([]*Badge, error) {
r := make([]*Badge, 0)
err := x.Where("category_id = ?", categoryId).Find(&r)
return r, err
}

func GetCustomIconByHash(hash string) string {
if len(hash) == 0 {
return ""
}
return filepath.Join(setting.IconUploadPath, hash)
}

func GetIconOuterLink(hash string) string {
return strings.TrimRight(setting.AppSubURL, "/") + "/show/icon/" + hash
}

+ 94
- 0
models/badge_category.go View File

@@ -0,0 +1,94 @@
package models

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

type BadgeType int

const (
CustomizeBadge = iota + 1
SystemBadge
)

type BadgeCategory struct {
ID int64 `xorm:"pk autoincr"`
Name string
Position int64
Type BadgeType
CreatedUnix timeutil.TimeStamp `xorm:"created"`
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
DeletedAt timeutil.TimeStamp `xorm:"deleted"`
}

func (m *BadgeCategory) ToShow() *BadgeCategory4Show {
return &BadgeCategory4Show{
ID: m.ID,
Name: m.Name,
Position: m.Position,
Type: m.Type,
CreatedUnix: m.CreatedUnix,
}
}

type BadgeCategory4Show struct {
ID int64 `xorm:"pk autoincr"`
Name string
Position int64
Type BadgeType
CreatedUnix timeutil.TimeStamp `xorm:"created"`
}

func (m BadgeCategory4Show) ToDTO() BadgeCategory {
return BadgeCategory{
ID: m.ID,
Name: m.Name,
Position: m.Position,
Type: m.Type,
CreatedUnix: m.CreatedUnix,
}
}

func GetBadgeCategoryListPaging(opts ListOptions) (int64, []*BadgeCategory, error) {
n, err := x.Count(&BadgeCategory{})
if err != nil {
return 0, nil, err
}
if opts.Page <= 0 {
opts.Page = 1
}
r := make([]*BadgeCategory, 0)
if err := x.OrderBy("position asc,created_unix desc").Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).Find(&r); err != nil {
return 0, nil, err
}
return n, r, nil
}

func GetBadgeCategoryList() ([]*BadgeCategory, error) {
r := make([]*BadgeCategory, 0)
if err := x.OrderBy("position asc,created_unix desc").Find(&r); err != nil {
return nil, err
}
return r, nil
}

func AddBadgeCategory(m BadgeCategory) (int64, error) {
return x.Insert(&m)
}

func UpdateBadgeCategoryById(id int64, param BadgeCategory) (int64, error) {
return x.ID(id).Update(&param)
}

func DelBadgeCategory(id int64) (int64, error) {
return x.ID(id).Delete(&BadgeCategory{})
}

func GetBadgeCategoryById(id int64) (*BadgeCategory, error) {
m := &BadgeCategory{}
has, err := x.ID(id).Get(m)
if err != nil {
return nil, err
} else if !has {
return nil, ErrRecordNotExist{}
}
return m, nil
}

+ 159
- 0
models/badge_user.go View File

@@ -0,0 +1,159 @@
package models

import (
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/builder"
)

const (
ActionAddBadgeUser = 1
ActionDelBadgeUser = 2
)

type BadgeUser struct {
ID int64 `xorm:"pk autoincr"`
UserId int64 `xorm:"unique(user_badge)"`
BadgeId int64 `xorm:"unique(user_badge) index"`
CreatedUnix timeutil.TimeStamp `xorm:"created index"`
}

type BadgeUserLog struct {
ID int64 `xorm:"pk autoincr"`
UserId int64 `xorm:"index"`
BadgeId int64 `xorm:"index"`
Action int
CreatedUnix timeutil.TimeStamp `xorm:"created index"`
}

type BadgeUserDetail struct {
BadgeUser BadgeUser `xorm:"extends"`
User User `xorm:"extends"`
}

func (*BadgeUserDetail) TableName() string {
return "badge_user"
}

func (m *BadgeUserDetail) ToShow() *BadgeUser4SHow {
return &BadgeUser4SHow{
ID: m.BadgeUser.ID,
UserId: m.BadgeUser.UserId,
Name: m.User.Name,
Avatar: m.User.RelAvatarLink(),
Email: m.User.Email,
CreatedUnix: m.BadgeUser.CreatedUnix,
}
}

type BadgeUser4SHow struct {
ID int64
UserId int64
Name string
Avatar string
Email string
CreatedUnix timeutil.TimeStamp
}

type AddBadgeUsersReq struct {
BadgeId int64
Users string
}
type DelBadgeUserReq struct {
ID int64
}

type GetUserBadgesOpts struct {
CategoryId int64
ListOptions
}

func AddBadgeUser(m BadgeUser) (int64, error) {
sess := x.NewSession()
defer sess.Close()
sess.Begin()
n, err := sess.Insert(&m)
if err != nil || n == 0 {
return 0, err
}
_, err = sess.Insert(&BadgeUserLog{
UserId: m.UserId,
BadgeId: m.BadgeId,
Action: ActionAddBadgeUser,
})
if err != nil {
sess.Rollback()
return 0, err
}
return n, sess.Commit()
}

func DelBadgeUser(id int64) (int64, error) {
m := BadgeUser{}
has, err := x.ID(id).Get(&m)
if err != nil {
return 0, err
}
if !has {
return 0, ErrRecordNotExist{}
}
sess := x.NewSession()
defer sess.Close()
sess.Begin()
n, err := x.ID(m.ID).Delete(&BadgeUser{})
if err != nil || n == 0 {
return 0, err
}
_, err = sess.Insert(&BadgeUserLog{
UserId: m.UserId,
BadgeId: m.BadgeId,
Action: ActionDelBadgeUser,
})
if err != nil {
sess.Rollback()
return 0, err
}
return n, sess.Commit()
}

func GetBadgeUsers(badgeId int64, opts ListOptions) (int64, []BadgeUserDetail, error) {
n, err := x.Join("LEFT", "public.user", "public.user.ID = badge_user.user_id").Where("badge_user.badge_id = ?", badgeId).Count(&BadgeUserDetail{})
if err != nil {
return 0, nil, err
}
if opts.Page <= 0 {
opts.Page = 1
}
m := make([]BadgeUserDetail, 0)
err = x.Join("LEFT", "public.user", "public.user.ID = badge_user.user_id").Where("badge_user.badge_id = ?", badgeId).OrderBy("badge_user.id desc").Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).Find(&m)
if err != nil {
return 0, nil, err
}
return n, m, nil
}

func GetUserBadgesPaging(userId int64, opts GetUserBadgesOpts) ([]*Badge, error) {
cond := builder.NewCond()
cond = cond.And(builder.Eq{"badge_user.user_id": userId})
if opts.CategoryId > 0 {
cond = cond.And(builder.Eq{"badge.category_id": opts.CategoryId})
}

r := make([]*Badge, 0)
err := x.Join("INNER", "badge_user", "badge_user.badge_id = badge.id").Where(cond).OrderBy("badge_user.id desc").Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).Find(&r)
return r, err
}
func CountUserBadges(userId int64) (int64, error) {
return x.Where("user_id = ?", userId).Count(&BadgeUser{})
}

func GetUserBadges(userId, categoryId int64) ([]*Badge, error) {
cond := builder.NewCond()
cond = cond.And(builder.Eq{"badge_user.user_id": userId})
if categoryId > 0 {
cond = cond.And(builder.Eq{"badge.category_id": categoryId})
}

r := make([]*Badge, 0)
err := x.Join("INNER", "badge_user", "badge_user.badge_id = badge.id").Where(cond).OrderBy("badge_user.created_unix desc").Find(&r)
return r, err
}

+ 37
- 0
models/base_message.go View File

@@ -0,0 +1,37 @@
package models

type BaseMessage struct {
Code int
Message string
}

var BaseOKMessage = BaseMessage{
0, "",
}

func BaseErrorMessage(message string) BaseMessage {
return BaseMessage{
1, message,
}
}

type BaseMessageApi struct {
Code int `json:"code"`
Message string `json:"message"`
}

var BaseOKMessageApi = BaseMessageApi{
0, "",
}

func BaseErrorMessageApi(message string) BaseMessageApi {
return BaseMessageApi{
1, message,
}
}

type BaseMessageWithDataApi struct {
Code int `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
}

+ 1743
- 96
models/cloudbrain.go
File diff suppressed because it is too large
View File


+ 597
- 0
models/cloudbrain_image.go View File

@@ -0,0 +1,597 @@
package models

import (
"fmt"
"strings"
"unicode/utf8"

"xorm.io/builder"

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

const RECOMMOND_TYPE = 5
const NORMAL_TYPE = 0
const IMAGE_STATUS_COMMIT = 0
const IMAGE_STATUS_SUCCESS = 1
const IMAGE_STATUS_Failed = 2

type Image struct {
ID int64 `xorm:"pk autoincr" json:"id"`
Type int `xorm:"INDEX NOT NULL" json:"type"` //0 normal 5官方推荐,中间值保留为后续扩展
CloudbrainType int `xorm:"INDEX NOT NULL" json:"cloudbrainType"` //0 云脑一 1云脑二
UID int64 `xorm:"INDEX NOT NULL" json:"uid"`
IsPrivate bool `xorm:"INDEX NOT NULL" json:"isPrivate"`
Tag string `xorm:"varchar(100) UNIQUE" json:"tag"`
Description string `xorm:"varchar(765)" json:"description"`
Topics []string `xorm:"TEXT JSON" json:"topics"`
Place string `xorm:"varchar(300)" json:"place"`
NumStars int `xorm:"NOT NULL DEFAULT 0" json:"numStars"`
IsStar bool `xorm:"-" json:"isStar"`
UserName string `xorm:"-" json:"userName"`
RelAvatarLink string `xorm:"-" json:"relAvatarLink"`
Status int `xorm:"INDEX NOT NULL DEFAULT 0" json:"status"` //0代表正在提交,1提交完成,2提交失败
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created" json:"createdUnix"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated" json:"updatedUnix"`
}

type ImageList []*Image

type ImageStar struct {
ID int64 `xorm:"pk autoincr"`
UID int64 `xorm:"UNIQUE(s)"`
ImageID int64 `xorm:"UNIQUE(s)"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
}

type ImageTopic struct {
ID int64 `xorm:"pk autoincr"`
Name string `xorm:"UNIQUE VARCHAR(105)"`
ImageCount int
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}

type ImageTopicRelation struct {
ImageID int64 `xorm:"UNIQUE(s)"`
TopicID int64 `xorm:"UNIQUE(s)"`
}

type SearchImageOptions struct {
Keyword string
UID int64
Status int
IncludePublicOnly bool
IncludeOfficialOnly bool
IncludePrivateOnly bool
IncludeStarByMe bool
IncludeCustom bool
IncludeOwnerOnly bool
Topics string
CloudbrainType int
ListOptions
SearchOrderBy
}
type ErrorImageTagExist struct {
Tag string
}

type ErrorImageCommitting struct {
Tag string
}

type ImagesPageResult struct {
Count int64 `json:"count"`
Images []*Image `json:"images"`
}

func (err ErrorImageTagExist) Error() string {
return fmt.Sprintf("Image already exists [tag: %s]", err.Tag)
}

func (err ErrorImageCommitting) Error() string {
return fmt.Sprintf("Image already exists [tag: %s]", err.Tag)
}

type ErrImageNotExist struct {
ID int64
Tag string
}

func (err ErrImageNotExist) Error() string {
return fmt.Sprintf("Image does not exist [id: %d] [tag: %s]", err.ID, err.Tag)
}

func IsErrorImageCommitting(err error) bool {
_, ok := err.(ErrorImageCommitting)
return ok
}

func IsErrImageNotExist(err error) bool {
_, ok := err.(ErrImageNotExist)
return ok
}

func IsErrImageTagExist(err error) bool {
_, ok := err.(ErrorImageTagExist)
return ok
}

func IsImageExist(tag string) (bool, error) {
return x.Exist(&Image{
Tag: tag,
})
}

func IsImageExistByUser(tag string, uid int64) (bool, error) {
return x.Exist(&Image{
Tag: tag,
UID: uid,
Status: IMAGE_STATUS_SUCCESS,
})
}

type FindImageTopicOptions struct {
ListOptions
ImageID int64
Keyword string
}

func (opts *FindImageTopicOptions) toConds() builder.Cond {
var cond = builder.NewCond()
if opts.ImageID > 0 {
cond = cond.And(builder.Eq{"image_topic_relation.image_id": opts.ImageID})
}

if opts.Keyword != "" {
cond = cond.And(builder.Like{"image_topic.name", strings.ToLower(opts.Keyword)})
}

return cond
}

func GetImageByID(id int64) (*Image, error) {
rel := new(Image)
has, err := x.
ID(id).
Get(rel)
if err != nil {
return nil, err
} else if !has {
return nil, ErrImageNotExist{ID: id}
}

return rel, nil
}

func GetImageByTag(tag string) (*Image, error) {

image := &Image{Tag: tag}
has, err := x.
Get(image)
if err != nil {
return nil, err
} else if !has {
return nil, ErrImageNotExist{Tag: tag}
}

return image, nil
}

func SanitizeAndValidateImageTopics(topics []string) (validTopics []string, invalidTopics []string) {
validTopics = make([]string, 0)
mValidTopics := make(map[string]struct{})
invalidTopics = make([]string, 0)

for _, topic := range topics {
topic = strings.TrimSpace(strings.ToLower(topic))
// ignore empty string
if len(topic) == 0 {
continue
}
// ignore same topic twice
if _, ok := mValidTopics[topic]; ok {
continue
}
if utf8.RuneCountInString(topic) <= 35 {
validTopics = append(validTopics, topic)
mValidTopics[topic] = struct{}{}
} else {
invalidTopics = append(invalidTopics, topic)
}
}

return validTopics, invalidTopics
}
func FindImageTopics(opts *FindImageTopicOptions) (topics []*ImageTopic, err error) {
sess := x.Select("image_topic.*").Where(opts.toConds())
if opts.ImageID > 0 {
sess.Join("INNER", "image_topic_relation", "image_topic_relation.topic_id = image_topic.id")
}
if opts.PageSize != 0 && opts.Page != 0 {
sess = opts.setSessionPagination(sess)
}
return topics, sess.Desc("image_topic.image_count").Find(&topics)
}

func SaveImageTopics(imageID int64, topicNames ...string) error {
topics, err := FindImageTopics(&FindImageTopicOptions{
ImageID: imageID,
})
if err != nil {
return err
}

sess := x.NewSession()
defer sess.Close()

if err := sess.Begin(); err != nil {
return err
}

var addedTopicNames []string
for _, topicName := range topicNames {
if strings.TrimSpace(topicName) == "" {
continue
}

var found bool
for _, t := range topics {
if strings.EqualFold(topicName, t.Name) {
found = true
break
}
}
if !found {
addedTopicNames = append(addedTopicNames, topicName)
}
}

var removeTopics []*ImageTopic
for _, t := range topics {
var found bool
for _, topicName := range topicNames {
if strings.EqualFold(topicName, t.Name) {
found = true
break
}
}
if !found {
removeTopics = append(removeTopics, t)
}
}

for _, topicName := range addedTopicNames {
_, err := addTopicByNameToImage(sess, imageID, topicName)
if err != nil {
return err
}
}

for _, topic := range removeTopics {
err := removeTopicFromImage(sess, imageID, topic)
if err != nil {
return err
}
}

topicNames = make([]string, 0, 25)
if err := sess.Table("image_topic").Cols("name").
Join("INNER", "image_topic_relation", "image_topic_relation.topic_id = image_topic.id").
Where("image_topic_relation.image_id = ?", imageID).Desc("image_topic.image_count").Find(&topicNames); err != nil {
return err
}

if _, err := sess.ID(imageID).Cols("topics").Update(&Image{
Topics: topicNames,
}); err != nil {
return err
}

return sess.Commit()
}

func addTopicByNameToImage(e Engine, imageID int64, topicName string) (*ImageTopic, error) {
var topic ImageTopic
has, err := e.Where("name = ?", topicName).Get(&topic)
if err != nil {
return nil, err
}
if !has {
topic.Name = topicName
topic.ImageCount = 1
if _, err := e.Insert(&topic); err != nil {
return nil, err
}
} else {
topic.ImageCount++
if _, err := e.ID(topic.ID).Cols("image_count").Update(&topic); err != nil {
return nil, err
}
}

if _, err := e.Insert(&ImageTopicRelation{
ImageID: imageID,
TopicID: topic.ID,
}); err != nil {
return nil, err
}

return &topic, nil
}

func removeTopicFromImage(e Engine, imageId int64, topic *ImageTopic) error {
topic.ImageCount--
if _, err := e.ID(topic.ID).Cols("image_count").Update(topic); err != nil {
return err
}

if _, err := e.Delete(&ImageTopicRelation{
ImageID: imageId,
TopicID: topic.ID,
}); err != nil {
return err
}

return nil
}

func SearchImage(opts *SearchImageOptions) (ImageList, int64, error) {
cond := SearchImageCondition(opts)
return SearchImageByCondition(opts, cond)
}

func SearchImageCondition(opts *SearchImageOptions) builder.Cond {
var cond = builder.NewCond()

if len(opts.Keyword) > 0 {

var subQueryCond = builder.NewCond()
for _, v := range strings.Split(opts.Keyword, ",") {

subQueryCond = subQueryCond.Or(builder.Like{"LOWER(image_topic.name)", strings.ToLower(v)})

}
subQuery := builder.Select("image_topic_relation.image_id").From("image_topic_relation").
Join("INNER", "image_topic", "image_topic.id = image_topic_relation.topic_id").
Where(subQueryCond).
GroupBy("image_topic_relation.image_id")
var keywordCond = builder.In("id", subQuery)

var likes = builder.NewCond()
for _, v := range strings.Split(opts.Keyword, ",") {
likes = likes.Or(builder.Like{"LOWER(tag)", strings.ToLower(v)})

likes = likes.Or(builder.Like{"LOWER(description)", strings.ToLower(v)})

}
keywordCond = keywordCond.Or(likes)

cond = cond.And(keywordCond)

}
if len(opts.Topics) > 0 { //标签精确匹配
var subQueryCond = builder.NewCond()
for _, v := range strings.Split(opts.Keyword, ",") {

subQueryCond = subQueryCond.Or(builder.Eq{"LOWER(image_topic.name)": strings.ToLower(v)})
subQuery := builder.Select("image_topic_relation.image_id").From("image_topic_relation").
Join("INNER", "image_topic", "image_topic.id = image_topic_relation.topic_id").
Where(subQueryCond).
GroupBy("image_topic_relation.image_id")
var topicCond = builder.In("id", subQuery)
cond = cond.And(topicCond)
}
}

if opts.IncludePublicOnly {
cond = cond.And(builder.Eq{"is_private": false})
}

if opts.IncludePrivateOnly {
cond = cond.And(builder.Eq{"is_private": true})
}

if opts.IncludeOwnerOnly {

cond = cond.And(builder.Eq{"uid": opts.UID})
}
if opts.IncludeOfficialOnly {
cond = cond.And(builder.Eq{"type": RECOMMOND_TYPE})
}
if opts.Status >= 0 {
cond = cond.And(builder.Eq{"status": opts.Status})
}

if opts.IncludeStarByMe {

subQuery := builder.Select("image_id").From("image_star").
Where(builder.Eq{"uid": opts.UID})
var starCond = builder.In("id", subQuery)
cond = cond.And(starCond)

}

if opts.CloudbrainType > 0 {
cond = cond.And(builder.Eq{"cloudbrain_type": opts.CloudbrainType})
}

return cond
}

func SearchImageByCondition(opts *SearchImageOptions, cond builder.Cond) (ImageList, int64, error) {
if opts.Page <= 0 {
opts.Page = 1
}

var err error
sess := x.NewSession()
defer sess.Close()

images := make(ImageList, 0, opts.PageSize)
count, err := sess.Where(cond).Count(new(Image))

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

sess.Where(cond).OrderBy(opts.SearchOrderBy.String())

if opts.PageSize > 0 {
sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
}
if err = sess.Find(&images); err != nil {
return nil, 0, fmt.Errorf("Images: %v", err)
}

if err = images.loadAttributes(sess, opts.UID); err != nil {
return nil, 0, fmt.Errorf("LoadAttributes: %v", err)
}

return images, count, nil
}

func (images ImageList) loadAttributes(e Engine, uid int64) error {
if len(images) == 0 {
return nil
}

set := make(map[int64]struct{})

for i := range images {
set[images[i].UID] = struct{}{}
}

// Load creators.
users := make(map[int64]*User, len(set))
if err := e.Table("\"user\"").
Cols("name", "lower_name", "avatar", "email").
Where("id > 0").
In("id", keysInt64(set)).
Find(&users); err != nil {
return fmt.Errorf("find users: %v", err)
}

for i := range images {
if users[images[i].UID] != nil {
images[i].UserName = users[images[i].UID].Name
images[i].RelAvatarLink = users[images[i].UID].RelAvatarLink()
} else {
images[i].UserName = ""
images[i].RelAvatarLink = ""
}
if uid == -1 {
images[i].IsStar = false
} else {
images[i].IsStar = isImageStaring(e, uid, images[i].ID)
}
}

return nil
}

func GetCommittingImageCount() int {

total, err := x.Where("status =?", 0).Count(new(Image))

if err != nil {
return 0
}
return int(total)
}

func CreateLocalImage(image *Image) error {

_, err := x.Insert(image)
return err
}

func UpdateLocalImage(image *Image) error {

_, err := x.ID(image.ID).Cols("description", "is_private", "status").Update(image)
return err
}

func UpdateLocalImageStatus(image *Image) error {

_, err := x.ID(image.ID).Cols("status").Update(image)
return err
}

func UpdateAutoIncrementIndex() {
x.Exec("SELECT setval('image_id_seq', (SELECT MAX(id) from image))")
}

func DeleteLocalImage(id int64) error {
image := new(Image)
_, err := x.ID(id).Delete(image)
return err
}

//star or unstar Image
func StarImage(userID, imageID int64, star bool) error {
sess := x.NewSession()
defer sess.Close()

if err := sess.Begin(); err != nil {
return err
}

if star {
if isImageStaring(sess, userID, imageID) {
return nil
}

if _, err := sess.Insert(&ImageStar{UID: userID, ImageID: imageID}); err != nil {
return err
}
if _, err := sess.Exec("UPDATE `image` SET num_stars = num_stars + 1 WHERE id = ?", imageID); err != nil {
return err
}
if _, err := sess.Exec("UPDATE `user` SET num_image_stars = num_image_stars + 1 WHERE id = ?", userID); err != nil {
return err
}
} else {
if !isImageStaring(sess, userID, imageID) {
return nil
}

if _, err := sess.Delete(&ImageStar{0, userID, imageID, 0}); err != nil {
return err
}
if _, err := sess.Exec("UPDATE `image` SET num_stars = num_stars - 1 WHERE id = ?", imageID); err != nil {
return err
}
if _, err := sess.Exec("UPDATE `user` SET num_image_stars = num_image_stars - 1 WHERE id = ?", userID); err != nil {
return err
}
}

return sess.Commit()
}

func IsImageStaring(userID, datasetID int64) bool {
return isImageStaring(x, userID, datasetID)

}

func isImageStaring(e Engine, userID, imageID int64) bool {
has, _ := e.Get(&ImageStar{0, userID, imageID, 0})
return has
}
func RecommendImage(imageId int64, recommond bool) error {

image := Image{Type: GetRecommondType(recommond)}
_, err := x.ID(imageId).Cols("type").Update(image)
return err
}

func GetRecommondType(recommond bool) int {
if recommond {

return RECOMMOND_TYPE
} else {
return NORMAL_TYPE
}

}

+ 136
- 0
models/cloudbrain_spec.go View File

@@ -0,0 +1,136 @@
package models

import (
"code.gitea.io/gitea/modules/timeutil"
)

type CloudbrainSpec struct {
CloudbrainID int64 `xorm:"pk"`
SpecId int64 `xorm:"index"`
SourceSpecId string
AccCardsNum int
AccCardType string `xorm:"index"`
CpuCores int
MemGiB float32
GPUMemGiB float32
ShareMemGiB float32
ComputeResource string
UnitPrice int
QueueId int64
QueueCode string
Cluster string
AiCenterCode string `xorm:"index"`
AiCenterName string
IsExclusive bool
ExclusiveOrg string
CreatedTime timeutil.TimeStamp `xorm:"created"`
UpdatedTime timeutil.TimeStamp `xorm:"updated"`
}

func (s CloudbrainSpec) ConvertToSpecification() *Specification {
return &Specification{
ID: s.SpecId,
SourceSpecId: s.SourceSpecId,
AccCardsNum: s.AccCardsNum,
AccCardType: s.AccCardType,
CpuCores: s.CpuCores,
MemGiB: s.MemGiB,
GPUMemGiB: s.GPUMemGiB,
ShareMemGiB: s.ShareMemGiB,
ComputeResource: s.ComputeResource,
UnitPrice: s.UnitPrice,
QueueId: s.QueueId,
QueueCode: s.QueueCode,
Cluster: s.Cluster,
AiCenterCode: s.AiCenterCode,
AiCenterName: s.AiCenterName,
IsExclusive: s.IsExclusive,
ExclusiveOrg: s.ExclusiveOrg,
}
}

func NewCloudBrainSpec(cloudbrainId int64, s Specification) CloudbrainSpec {
return CloudbrainSpec{
CloudbrainID: cloudbrainId,
SpecId: s.ID,
SourceSpecId: s.SourceSpecId,
AccCardsNum: s.AccCardsNum,
AccCardType: s.AccCardType,
CpuCores: s.CpuCores,
MemGiB: s.MemGiB,
GPUMemGiB: s.GPUMemGiB,
ShareMemGiB: s.ShareMemGiB,
ComputeResource: s.ComputeResource,
UnitPrice: s.UnitPrice,
QueueId: s.QueueId,
QueueCode: s.QueueCode,
Cluster: s.Cluster,
AiCenterCode: s.AiCenterCode,
AiCenterName: s.AiCenterName,
IsExclusive: s.IsExclusive,
ExclusiveOrg: s.ExclusiveOrg,
}
}

var StatusChangeChan = make(chan *Cloudbrain, 50)

func InsertCloudbrainSpec(c CloudbrainSpec) (int64, error) {
return x.Insert(&c)
}

func GetCloudbrainSpecByID(cloudbrainId int64) (*CloudbrainSpec, error) {
r := &CloudbrainSpec{}
if has, err := x.Where("cloudbrain_id = ?", cloudbrainId).Get(r); err != nil {
return nil, err
} else if !has {
return nil, nil
}
return r, nil
}

func FindCloudbrainTask(page, pageSize int) ([]*Cloudbrain, error) {
r := make([]*Cloudbrain, 0)
err := x.Unscoped().
Limit(pageSize, (page-1)*pageSize).
OrderBy("cloudbrain.id").
Find(&r)
if err != nil {
return nil, err
}
return r, nil
}

func CountNoSpecHistoricTask() (int64, error) {
n, err := x.Unscoped().
Where(" 1=1 and not exists (select 1 from cloudbrain_spec where cloudbrain.id = cloudbrain_spec.cloudbrain_id)").
Count(&Cloudbrain{})
if err != nil {
return 0, err
}
return n, nil
}

// GetResourceSpecMapByCloudbrainIDs
func GetResourceSpecMapByCloudbrainIDs(ids []int64) (map[int64]*Specification, error) {
specs := make([]*CloudbrainSpec, 0)
if err := x.In("cloudbrain_id", ids).Find(&specs); err != nil {
return nil, err
}
r := make(map[int64]*Specification, len(ids))
for _, s := range specs {
r[s.CloudbrainID] = s.ConvertToSpecification()
}
return r, nil
}

func GetCloudbrainTaskUnitPrice(task Cloudbrain) (int, error) {
s, err := GetCloudbrainSpecByID(task.ID)
if err != nil {
return 0, err
}
var n = 1
if task.WorkServerNumber > 1 {
n = task.WorkServerNumber
}
return s.UnitPrice * n, nil
}

+ 490
- 0
models/cloudbrain_static.go View File

@@ -0,0 +1,490 @@
package models

import (
"strconv"
"time"

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

type TaskDetail struct {
ID int64 `json:"ID"`
JobID string `json:"JobID"`
JobName string `json:"JobName"`
DisplayJobName string `json:"DisplayJobName"`
Status string `json:"Status"`
JobType string `json:"JobType"`
CreatedUnix timeutil.TimeStamp `json:"CreatedUnix"`
WaitTime string `json:"WaitTime"`
RunTime string `json:"RunTime"`
StartTime timeutil.TimeStamp `json:"StartTime"`
EndTime timeutil.TimeStamp `json:"EndTime"`
ComputeResource string `json:"ComputeResource"`
Type int `json:"Type"`
UserName string `json:"UserName"`
RepoName string `json:"RepoName"`
RepoAlias string `json:"RepoAlias"`
RepoID int64 `json:"RepoID"`
IsDelete bool `json:"IsDelete"`
CardNum int `json:"CardNum"`
CardType string `json:"CardType"`
CardDuration string `json:"CardDuration"`
AiCenter string `json:"AiCenter"`
FlavorName string `json:"FlavorName"`
WorkServerNum int64 `json:"WorkServerNum"`
Spec *Specification `json:"Spec"`
}

type CloudbrainDurationStatistic struct {
ID int64 `xorm:"pk autoincr"`
Cluster string
AiCenterCode string `xorm:"INDEX"`
AiCenterName string
ComputeResource string
AccCardType string `xorm:"INDEX"`

DateTime timeutil.TimeStamp `xorm:"INDEX DEFAULT 0"`
DateTimeUnix timeutil.TimeStamp `xorm:"INDEX DEFAULT 0"`
DayTime string `xorm:"INDEX"`
HourTime int `xorm:"INDEX"`
CardsUseDuration int
CardsTotalDuration int
CardsTotalNum int

DeletedUnix timeutil.TimeStamp `xorm:"deleted"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
}
type DurationStatisticOptions struct {
BeginTime time.Time
EndTime time.Time
AiCenterCode string
}

type DurationRateStatistic struct {
AiCenterTotalDurationStat map[string]int `json:"aiCenterTotalDurationStat"`
AiCenterUsageDurationStat map[string]int `json:"aiCenterUsageDurationStat"`
UsageRate map[string]float64 `json:"UsageRate"`
}
type ResourceDetail struct {
QueueCode string
Cluster string `xorm:"notnull"`
AiCenterCode string
AiCenterName string
ComputeResource string
AccCardType string
CardsTotalNum int
IsAutomaticSync bool
}

type DateUsageStatistic struct {
Date string `json:"date"`
UsageDuration int `json:"usageDuration"`
TotalDuration int `json:"totalDuration"`
UsageRate float64 `json:"usageRate"`
}

type HourTimeStatistic struct {
HourTimeUsageDuration map[string]int `json:"hourTimeUsageDuration"`
HourTimeTotalDuration map[string]int `json:"hourTimeTotalDuration"`
HourTimeUsageRate map[string]float64 `json:"hourTimeUsageRate"`
}
type CloudbrainTypeDuration []struct {
Type int `xorm:"type"`
DurationSum int `xorm:"durationSum"`
CardDurationSum int `xorm:"cardDurationSum"`
Count int `xorm:"count"`
}
type CloudbrainAllDuration struct {
DurationSum int `xorm:"durationSum"`
CardDurationSum int `xorm:"cardDurationSum"`
Count int `xorm:"count"`
}

func GetTodayCreatorCount(beginTime time.Time, endTime time.Time) (int64, error) {
countSql := "SELECT count(distinct user_id) FROM " +
"public.cloudbrain where created_unix >=" + strconv.FormatInt(beginTime.Unix(), 10) +
" and created_unix<" + strconv.FormatInt(endTime.Unix(), 10)
return x.SQL(countSql).Count()
}
func GetTodayCloudbrainCount(beginTime time.Time, endTime time.Time) (int64, error) {
countSql := "SELECT count(*) FROM " +
"public.cloudbrain where created_unix >=" + strconv.FormatInt(beginTime.Unix(), 10) +
" and created_unix<=" + strconv.FormatInt(endTime.Unix(), 10)
return x.SQL(countSql).Count()
}
func GetTodayRunningCount(beginTime time.Time, endTime time.Time) (int64, error) {
countSql := "SELECT count(*) FROM " +
"public.cloudbrain where created_unix >=" + strconv.FormatInt(beginTime.Unix(), 10) +
" and created_unix<=" + strconv.FormatInt(endTime.Unix(), 10) + " and (status='" + string(JobRunning) + "'" +
" or status='" + string(ModelArtsTrainJobInit) + "')"
return x.SQL(countSql).Count()
}
func GetTodayWaitingCount(beginTime time.Time, endTime time.Time) (int64, error) {
countSql := "SELECT count(*) FROM " +
"public.cloudbrain where created_unix >=" + strconv.FormatInt(beginTime.Unix(), 10) +
" and created_unix<=" + strconv.FormatInt(endTime.Unix(), 10) + " and status='" + string(JobWaiting) + "'"
return x.SQL(countSql).Count()
}

func GetCreatorCount() (int64, error) {
countSql := "SELECT count(distinct user_id) FROM public.cloudbrain"
return x.SQL(countSql).Count()
}

func GetCloudbrainTypeCount() ([]map[string]string, error) {
countSql := "SELECT type,count(*) num FROM public.cloudbrain group by type order by num desc"
return x.QueryString(countSql)
}

func GetCloudbrainStatusCount() ([]map[string]string, error) {
countSql := "SELECT status,count(*) num FROM public.cloudbrain group by status order by num desc"
return x.QueryString(countSql)
}

func GetCloudbrainTpyeDurationSum() ([]map[string]string, error) {
countSql := "SELECT type,sum(duration) FROM public.cloudbrain group by type order by sum(duration) desc"
return x.QueryString(countSql)
}

func GetRecordBeginTime() ([]*CloudbrainInfo, error) {
sess := x.NewSession()
defer sess.Close()
sess.OrderBy("cloudbrain.id ASC limit 1")
cloudbrains := make([]*CloudbrainInfo, 0)
if err := sess.Table(&Cloudbrain{}).Unscoped().
Find(&cloudbrains); err != nil {
log.Info("find error.")
}
return cloudbrains, nil
}

func GetAllStatusCloudBrain() map[string]int {
sess := x.NewSession()
defer sess.Close()
cloudbrains := make([]*CloudbrainInfo, 0)
if err := sess.Table(&Cloudbrain{}).Unscoped().
Find(&cloudbrains); err != nil {
log.Info("find error.")
}
cloudBrainStatusResult := make(map[string]int)
for _, cloudbrain := range cloudbrains {
if _, ok := cloudBrainStatusResult[cloudbrain.Status]; !ok {
cloudBrainStatusResult[cloudbrain.Status] = 1
} else {
cloudBrainStatusResult[cloudbrain.Status] += 1
}
}
return cloudBrainStatusResult
}

func GetWaittingTop() ([]*CloudbrainInfo, error) {
sess := x.NewSession()
defer sess.Close()
var cond = builder.NewCond()
cond = cond.And(
builder.Eq{"cloudbrain.status": string(JobWaiting)},
)
sess.OrderBy("cloudbrain.created_unix ASC limit 10")
cloudbrains := make([]*CloudbrainInfo, 0, 10)
if err := sess.Table(&Cloudbrain{}).Where(cond).
Find(&cloudbrains); err != nil {
log.Info("find error.")
}

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

func GetRunningTop() ([]*CloudbrainInfo, error) {
sess := x.NewSession()
defer sess.Close()
var cond = builder.NewCond()
cond = cond.And(
builder.Eq{"cloudbrain.status": string(JobRunning)},
)
sess.OrderBy("cloudbrain.duration DESC limit 10")
cloudbrains := make([]*CloudbrainInfo, 0, 10)
if err := sess.Table(&Cloudbrain{}).Where(cond).
Find(&cloudbrains); err != nil {
log.Info("find error.")
}
var ids []int64
for _, task := range cloudbrains {
ids = append(ids, task.RepoID)
}
repositoryMap, err := GetRepositoriesMapByIDs(ids)
if err == nil {
for _, task := range cloudbrains {
task.Repo = repositoryMap[task.RepoID]
}
}
return cloudbrains, nil
}

func getCreatePeriodCount(dateBeginTime string, dateEndTime string, hourBeginTime string, hourEndTime string) (int64, error) {
countSql := "SELECT count(*) FROM " +
"public.cloudbrain where to_char(to_timestamp(created_unix), 'YYYY-MM-DD') >= '" + dateBeginTime +
"' and to_char(to_timestamp(created_unix), 'YYYY-MM-DD') < '" + dateEndTime +
"' and to_char(to_timestamp(created_unix), 'HH24:MI:SS') >= '" + hourBeginTime +
"' and to_char(to_timestamp(created_unix), 'HH24:MI:SS') < '" + hourEndTime + "'"
return x.SQL(countSql).Count()
}

//SELECT * FROM xxx WHERE NOT ((endTime < hourBeginTime) OR (startTime > hourEndTime))
func getRunPeriodCount(dateBeginTime string, dateEndTime string, hourBeginTime string, hourEndTime string) (int64, error) {
countSql := "SELECT count(*) FROM " +
"public.cloudbrain where not ((to_char(to_timestamp(start_time), ' HH24:MI:SS') > '" + hourEndTime +
"') or (to_char(to_timestamp(end_time), 'HH24:MI:SS') < '" + hourBeginTime + "'))" +
" and (to_char(to_timestamp(start_time), 'YYYY-MM-DD') >= '" + dateBeginTime +
"' and to_char(to_timestamp(start_time), 'YYYY-MM-DD') < '" + dateEndTime + "')"
return x.SQL(countSql).Count()

}

func GetCreateHourPeriodCount(dateBeginTime string, dateEndTime string) (map[string]interface{}, error) {
//0 to 23 for each hour,
dateHourMap := make(map[string]interface{})
var slice = []int64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}
for key, value := range slice {
hourBeginHour := util.AddZero(value) + ":00:00"
hourEndHour := util.AddZero(value+1) + ":00:00"
cout, err := getCreatePeriodCount(dateBeginTime, dateEndTime, hourBeginHour, hourEndHour)
if err != nil {
log.Error("Can not query getCreatePeriodCount.", err)
return nil, nil
}
dateHourMap[strconv.Itoa(key)] = cout
}
return dateHourMap, nil
}

func GetRunHourPeriodCount(dateBeginTime string, dateEndTime string) (map[string]interface{}, error) {
dateHourMap := make(map[string]interface{})
var slice = []int64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}
for key, value := range slice {
hourBeginHour := util.AddZero(value) + ":00:00"
hourEndHour := util.AddZero(value+1) + ":00:00"
cout, err := getRunPeriodCount(dateBeginTime, dateEndTime, hourBeginHour, hourEndHour)
if err != nil {
log.Error("Can not query getRunPeriodCount.", err)
return nil, nil
}
dateHourMap[strconv.Itoa(key)] = cout
}
return dateHourMap, nil
}

func GetCloudbrainRunning() ([]*CloudbrainInfo, error) {
sess := x.NewSession()
defer sess.Close()
var cond = builder.NewCond()
cond = cond.And(
builder.Eq{"cloudbrain.status": string(JobRunning)},
)
sess.OrderBy("cloudbrain.created_unix ASC")
cloudbrains := make([]*CloudbrainInfo, 0, 10)
if err := sess.Table(&Cloudbrain{}).Where(cond).
Find(&cloudbrains); err != nil {
log.Info("find error.")
}
return cloudbrains, nil
}

func GetCloudbrainByTime(beginTime int64, endTime int64) ([]*CloudbrainInfo, error) {
sess := x.NewSession()
defer sess.Close()
var cond = builder.NewCond()
cond = cond.Or(
builder.And(builder.Gte{"cloudbrain.end_time": beginTime}, builder.Lte{"cloudbrain.start_time": beginTime}, builder.Gt{"cloudbrain.start_time": 0}),
)
cond = cond.Or(
builder.And(builder.Gte{"cloudbrain.start_time": beginTime}, builder.Lte{"cloudbrain.start_time": endTime}, builder.Gt{"cloudbrain.start_time": 0}),
)
cond = cond.Or(
builder.And(builder.Eq{"cloudbrain.status": string(JobRunning)}, builder.Lte{"cloudbrain.start_time": beginTime}),
)
sess.OrderBy("cloudbrain.id ASC")
cloudbrains := make([]*CloudbrainInfo, 0, 10)
if err := sess.Table(&Cloudbrain{}).Unscoped().Where(cond).
Find(&cloudbrains); err != nil {
log.Error("find error.")
return nil, err
}
return cloudbrains, nil
}

func GetSpecByAiCenterCodeAndType(aiCenterCode string, accCardType string) ([]*CloudbrainSpec, error) {
sess := x.NewSession()
defer sess.Close()
var cond = builder.NewCond()
cond = cond.And(
builder.And(builder.Eq{"cloudbrain_spec.ai_center_code": aiCenterCode}, builder.Eq{"cloudbrain_spec.acc_card_type": accCardType}),
)
cloudbrainSpecs := make([]*CloudbrainSpec, 0, 10)
if err := sess.Table(&CloudbrainSpec{}).Where(cond).
Find(&cloudbrainSpecs); err != nil {
log.Error("find error.")
return nil, err
}
return cloudbrainSpecs, nil
}

func InsertCloudbrainDurationStatistic(cloudbrainDurationStatistic *CloudbrainDurationStatistic) (int64, error) {
return xStatistic.Insert(cloudbrainDurationStatistic)
}

func getDurationStatistic(cb *CloudbrainDurationStatistic) (*CloudbrainDurationStatistic, error) {
has, err := x.Get(cb)
if err != nil {
return nil, err
} else if !has {
return nil, ErrJobNotExist{}
}
return cb, nil
}

func GetCanUseCardInfo() ([]*ResourceQueue, error) {
sess := x.NewSession()
defer sess.Close()
sess.OrderBy("resource_queue.cluster DESC, resource_queue.ai_center_code ASC")
ResourceQueues := make([]*ResourceQueue, 0, 10)
if err := sess.Table(&ResourceQueue{}).Find(&ResourceQueues); err != nil {
log.Error("find error.")
return nil, err
}
return ResourceQueues, nil
}

func GetCardDurationStatistics(opts *DurationStatisticOptions) ([]*CloudbrainDurationStatistic, error) {
sess := xStatistic.NewSession()
defer sess.Close()
var cond = builder.NewCond()
if opts.BeginTime.Unix() > 0 && opts.EndTime.Unix() > 0 {
cond = cond.And(
builder.And(builder.Gte{"cloudbrain_duration_statistic.date_time_unix": opts.BeginTime.Unix()}, builder.Lt{"cloudbrain_duration_statistic.date_time_unix": opts.EndTime.Unix()}),
)
}
if opts.AiCenterCode != "" {
cond = cond.And(
builder.Eq{"cloudbrain_duration_statistic.ai_center_code": opts.AiCenterCode},
)
}
CloudbrainDurationStatistics := make([]*CloudbrainDurationStatistic, 0, 10)
if err := sess.Table(&CloudbrainDurationStatistic{}).Where(cond).
Find(&CloudbrainDurationStatistics); err != nil {
log.Error("find error.")
return nil, err
}
return CloudbrainDurationStatistics, nil
}

func GetDurationRecordBeginTime() ([]*CloudbrainDurationStatistic, error) {
sess := xStatistic.NewSession()
defer sess.Close()

var cond = builder.NewCond()

cond = cond.And(
builder.Gt{"cloudbrain_duration_statistic.date_time_unix": 0},
)

sess.OrderBy("cloudbrain_duration_statistic.date_time_unix ASC limit 1")
CloudbrainDurationStatistics := make([]*CloudbrainDurationStatistic, 0)
if err := sess.Table(&CloudbrainDurationStatistic{}).Where(cond).Find(&CloudbrainDurationStatistics); err != nil {
log.Error("find error.")
return nil, err
}
return CloudbrainDurationStatistics, nil
}

func GetDurationRecordUpdateTime() ([]*CloudbrainDurationStatistic, error) {
sess := xStatistic.NewSession()
defer sess.Close()
var cond = builder.NewCond()

cond = cond.And(
builder.Gt{"cloudbrain_duration_statistic.date_time_unix": 1577808000},
)
sess.OrderBy("cloudbrain_duration_statistic.date_time_unix DESC limit 1")
CloudbrainDurationStatistics := make([]*CloudbrainDurationStatistic, 0)
if err := sess.Table(&CloudbrainDurationStatistic{}).Where(cond).Find(&CloudbrainDurationStatistics); err != nil {
log.Error("find error.")
return nil, err
}
return CloudbrainDurationStatistics, nil
}

func DeleteCloudbrainDurationStatistic(beginTime timeutil.TimeStamp, endTime timeutil.TimeStamp) error {
sess := xStatistic.NewSession()
defer sess.Close()
if _, err := sess.Exec("DELETE FROM cloudbrain_duration_statistic WHERE cloudbrain_duration_statistic.date_time_unix BETWEEN ? AND ?", beginTime, endTime); err != nil {
log.Error("DELETE cloudbrain_duration_statistic data error.")
return err
}
return nil
}

func GetCloudbrainTypeCardDuration() (CloudbrainTypeDuration, error) {
query := `
SELECT
cloudbrain.type,
SUM(cloudbrain.duration) as durationSum,
SUM(
COALESCE(cloudbrain.duration *
CASE
WHEN cloudbrain.work_server_number = 0 THEN 1
ELSE COALESCE(cloudbrain.work_server_number, 1)
END *
COALESCE(cloudbrain_spec.acc_cards_num, 1), 0)
) as cardDurationSum,
COUNT(*) as count
FROM cloudbrain
LEFT JOIN cloudbrain_spec
ON cloudbrain.id = cloudbrain_spec.cloudbrain_id
GROUP BY cloudbrain.type
`
// 执行查询
var results CloudbrainTypeDuration
if err := x.SQL(query).Find(&results); err != nil {
panic(err)
}
return results, nil
}

func GetCloudbrainAllCardDuration() (CloudbrainAllDuration, error) {
query := `
SELECT
SUM(cloudbrain.duration) as durationSum,
SUM(
COALESCE(cloudbrain.duration *
CASE
WHEN cloudbrain.work_server_number = 0 THEN 1
ELSE COALESCE(cloudbrain.work_server_number, 1)
END *
COALESCE(cloudbrain_spec.acc_cards_num, 1), 0)
) as cardDurationSum,
COUNT(*) as count
FROM cloudbrain
LEFT JOIN cloudbrain_spec
ON cloudbrain.id = cloudbrain_spec.cloudbrain_id
`
// 执行查询
var result CloudbrainAllDuration
if _, err := x.SQL(query).Get(&result); err != nil {
panic(err)
}
return result, nil
}

+ 68
- 0
models/cloudbrain_temp.go View File

@@ -0,0 +1,68 @@
package models

import (
"time"

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

const (
TempJobId = "TEMP"
TempVersionId = TempJobId
TempJobStatus = TempJobId
)

type CloudbrainTemp struct {
ID int64 `xorm:"pk autoincr"`
JobID string `xorm:"NOT NULL DEFAULT 'TEMP'"`
VersionID string `xorm:"NOT NULL DEFAULT 'TEMP'"`
JobName string `xorm:"NOT NULL "`
Type int `xorm:"NOT NULL "`
JobType string `xorm:"INDEX NOT NULL DEFAULT 'DEBUG'"`
Status string `xorm:"INDEX NOT NULL DEFAULT 'TEMP'"`
QueryTimes int `xorm:"INDEX NOT NULL DEFAULT 0"`
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
DeletedAt time.Time `xorm:"deleted"`
}

func InsertCloudbrainTemp(temp *CloudbrainTemp) (err error) {
if _, err = x.Insert(temp); err != nil {
return err
}

return nil
}

func getCloudBrainTemp(temp *CloudbrainTemp) (*CloudbrainTemp, error) {
has, err := x.Get(temp)
if err != nil {
return nil, err
} else if !has {
return nil, ErrJobNotExist{}
}
return temp, nil
}

func GetCloudBrainTempJobs() ([]*CloudbrainTemp, error) {
jobs := make([]*CloudbrainTemp, 0, 10)
return jobs, x.In("status", TempJobStatus, string(ModelArtsStopping), string(ModelArtsTrainJobKilling)).
And("query_times < ?", setting.MaxTempQueryTimes).
Limit(100).
Find(&jobs)
}

func DeleteCloudbrainTemp(temp *CloudbrainTemp) error {
return deleteCloudbrainTemp(x, temp)
}

func deleteCloudbrainTemp(e Engine, temp *CloudbrainTemp) error {
_, err := e.ID(temp.ID).Delete(temp)
return err
}

func UpdateCloudbrainTemp(temp *CloudbrainTemp) error {
_, err := x.ID(temp.ID).AllCols().Update(temp)
return err
}

+ 11
- 25
models/custom_migrations.go View File

@@ -1,8 +1,6 @@
package models

import (
"fmt"

"code.gitea.io/gitea/modules/log"
"xorm.io/xorm"
)
@@ -18,13 +16,13 @@ type CustomMigrationStatic struct {
}

var customMigrations = []CustomMigration{
{"Custom v1 Topic struct change to support chinese", syncTopicStruct},
//手机号功能可以不启用,不启用时手机号为空串,为了避免启用时唯一性约束报错,定制唯一性约束(对null和空字符串不做唯一性检查)
{"set phone number unique index", setPhoneNumberUniqueIndex},
}

var customMigrationsStatic = []CustomMigrationStatic{
{"Delete organization user history data ", deleteNotDisplayUser},
{"update issue_fixed_rate to 1 if num_issues is 0 ", updateIssueFixedRate},
}
var customMigrationsStatic []CustomMigrationStatic



func MigrateCustom(x *xorm.Engine) {

@@ -36,44 +34,32 @@ func MigrateCustom(x *xorm.Engine) {

}
}

}

func MigrateCustomStatic(x *xorm.Engine, static *xorm.Engine) {
for _, m := range customMigrationsStatic {
log.Info("Migration: %s", m.Description)
if err := m.Migrate(x, static); err != nil {

log.Error("Migration: %v", err)

}
}
}

func syncTopicStruct(x *xorm.Engine) error {
func setPhoneNumberUniqueIndex(x *xorm.Engine) error {

query := "ALTER TABLE topic ALTER COLUMN name TYPE varchar(105);"
query := "CREATE UNIQUE INDEX IF NOT EXISTS \"UQE_user_phone_number\" ON \"user\" (phone_number) WHERE phone_number IS NOT NULL and phone_number !='';"

_, err := x.Exec(query)
return err
}

func deleteNotDisplayUser(x *xorm.Engine, static *xorm.Engine) error {

querySQL := "select id,name from public.user where type=1"
rows, err := x.Query(querySQL)
if err != nil {
log.Info("select db failed,err:", err)
return err
}
func syncTopicStruct(x *xorm.Engine) error {

for i, userRow := range rows {
log.Info("delete zuzi user, i=" + fmt.Sprint(i) + " userName=" + string(userRow["name"]))
deleteSql := "delete from user_business_analysis where id=" + string(userRow["id"]) + " and name='" + string(userRow["name"]) + "'"
static.Exec(deleteSql)
}
query := "ALTER TABLE topic ALTER COLUMN name TYPE varchar(105);"

return nil
_, err := x.Exec(query)
return err
}

func updateIssueFixedRate(x *xorm.Engine, static *xorm.Engine) error {


+ 296
- 52
models/dataset.go View File

@@ -4,6 +4,11 @@ import (
"errors"
"fmt"
"sort"
"strings"

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

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

"code.gitea.io/gitea/modules/timeutil"
"xorm.io/builder"
@@ -17,11 +22,14 @@ const (

type Dataset struct {
ID int64 `xorm:"pk autoincr"`
Title string `xorm:"INDEX NOT NULL"`
Status int32 `xorm:"INDEX"` // normal_private: 0, pulbic: 1, is_delete: 2
Title string `xorm:"INDEX NOT NULL""`
Status int32 `xorm:"INDEX""` // normal_private: 0, pulbic: 1, is_delete: 2
Category string
Description string `xorm:"TEXT"`
DownloadTimes int64
UseCount int64 `xorm:"DEFAULT 0"`
NumStars int `xorm:"INDEX NOT NULL DEFAULT 0"`
Recommend bool `xorm:"INDEX NOT NULL DEFAULT false"`
License string
Task string
ReleaseID int64 `xorm:"INDEX"`
@@ -35,6 +43,11 @@ type Dataset struct {
Attachments []*Attachment `xorm:"-"`
}

type DatasetWithStar struct {
Dataset
IsStaring bool
}

func (d *Dataset) IsPrivate() bool {
switch d.Status {
case DatasetStatusPrivate:
@@ -56,25 +69,28 @@ func (datasets DatasetList) loadAttributes(e Engine) error {
}

set := make(map[int64]struct{})
userIdSet := make(map[int64]struct{})
datasetIDs := make([]int64, len(datasets))
for i := range datasets {
set[datasets[i].UserID] = struct{}{}
userIdSet[datasets[i].UserID] = struct{}{}
set[datasets[i].RepoID] = struct{}{}
datasetIDs[i] = datasets[i].ID
}

// Load owners.
users := make(map[int64]*User, len(set))
users := make(map[int64]*User, len(userIdSet))
repos := make(map[int64]*Repository, len(set))
if err := e.
Where("id > 0").
In("id", keysInt64(set)).
In("id", keysInt64(userIdSet)).
Cols("id", "lower_name", "name", "full_name", "email").
Find(&users); err != nil {
return fmt.Errorf("find users: %v", err)
}
if err := e.
Where("id > 0").
In("id", keysInt64(set)).
Cols("id", "owner_id", "owner_name", "lower_name", "name", "description", "alias", "lower_alias", "is_private").
Find(&repos); err != nil {
return fmt.Errorf("find repos: %v", err)
}
@@ -86,38 +102,126 @@ func (datasets DatasetList) loadAttributes(e Engine) error {
return nil
}

func (datasets DatasetList) loadAttachmentAttributes(opts *SearchDatasetOptions) error {
if len(datasets) == 0 {
return nil
}
datasetIDs := make([]int64, len(datasets))
for i := range datasets {
datasetIDs[i] = datasets[i].ID
}
attachments, err := AttachmentsByDatasetOption(datasetIDs, opts)
if err != nil {
return fmt.Errorf("GetAttachmentsByDatasetIds failed error: %v", err)
}

permissionMap := make(map[int64]bool, len(datasets))

for _, attachment := range attachments {

for i := range datasets {
if attachment.DatasetID == datasets[i].ID {

if !attachment.IsPrivate {
datasets[i].Attachments = append(datasets[i].Attachments, attachment)
} else {
permission, ok := permissionMap[datasets[i].ID]
if !ok {

permission = false
datasets[i].Repo.GetOwner()
if !permission {
if datasets[i].Repo.OwnerID == opts.User.ID {
permission = true
} else {
isCollaborator, _ := datasets[i].Repo.IsCollaborator(opts.User.ID)
isInRepoTeam, _ := datasets[i].Repo.IsInRepoTeam(opts.User.ID)

if isCollaborator || isInRepoTeam {
permission = true
}
}

}

permissionMap[datasets[i].ID] = permission
}

if permission {
datasets[i].Attachments = append(datasets[i].Attachments, attachment)
}
}

}

}

}

for i := range datasets {
if datasets[i].Attachments == nil {
datasets[i].Attachments = []*Attachment{}
}
datasets[i].Repo.Owner = nil
}
return nil

}

type SearchDatasetOptions struct {
Keyword string
OwnerID int64
RepoID int64
IncludePublic bool
Keyword string
OwnerID int64
User *User
RepoID int64
IncludePublic bool
RecommendOnly bool
Category string
Task string
License string
DatasetIDs []int64
ExcludeDatasetId int64
ListOptions
SearchOrderBy
IsOwner bool
IsOwner bool
StarByMe bool
CloudBrainType int //0 cloudbrain 1 modelarts -1 all
PublicOnly bool
JustNeedZipFile bool
NeedAttachment bool
UploadAttachmentByMe bool
QueryReference bool
}

func CreateDataset(dataset *Dataset) (err error) {
if _, err = x.Insert(dataset); err != nil {

sess := x.NewSession()
defer sess.Close()

if err := sess.Begin(); err != nil {
return err
}

return nil
}

func CreateDefaultDatasetToRepo(repo *Repository) (err error) {
dataset := &Dataset{RepoID: repo.ID}
has, err := x.Get(dataset)
datasetByRepoId := &Dataset{RepoID: dataset.RepoID}
has, err := sess.Get(datasetByRepoId)
if err != nil {
return err
}
if !has {
dataset.Status = DatasetStatusPrivate
dataset.Title = repo.Name
if err = CreateDataset(dataset); err != nil {
return err
}
if has {
return fmt.Errorf("The dataset already exists.")
}
return nil

if _, err = sess.Insert(dataset); err != nil {
return err
}
return sess.Commit()

}

func RecommendDataset(dataSetId int64, recommend bool) error {

dataset := Dataset{Recommend: recommend}
_, err := x.ID(dataSetId).Cols("recommend").Update(dataset)
return err
}

func SearchDataset(opts *SearchDatasetOptions) (DatasetList, int64, error) {
@@ -129,36 +233,88 @@ func SearchDatasetCondition(opts *SearchDatasetOptions) builder.Cond {
var cond = builder.NewCond()
cond = cond.And(builder.Neq{"dataset.status": DatasetStatusDeleted})

if len(opts.Keyword) > 0 {
cond = cond.And(builder.Like{"dataset.title", opts.Keyword})
}
cond = generateFilterCond(opts, cond)

if opts.RepoID > 0 {
cond = cond.And(builder.Eq{"dataset.repo_id": opts.RepoID})
}

if opts.IncludePublic {
if opts.ExcludeDatasetId > 0 {
cond = cond.And(builder.Neq{"dataset.id": opts.ExcludeDatasetId})
}

if opts.PublicOnly {
cond = cond.And(builder.Eq{"dataset.status": DatasetStatusPublic})
cond = cond.And(builder.Eq{"attachment.is_private": false})
} else if opts.IncludePublic {
cond = cond.And(builder.Eq{"dataset.status": DatasetStatusPublic})
cond = cond.And(builder.Eq{"attachment.is_private": false})
if opts.OwnerID > 0 {
if len(opts.Keyword) == 0 {
cond = cond.Or(builder.Eq{"repository.owner_id": opts.OwnerID})
} else {
subCon := builder.NewCond()
subCon = subCon.And(builder.Eq{"repository.owner_id": opts.OwnerID}, builder.Like{"dataset.title", opts.Keyword})
cond = cond.Or(subCon)
subCon := builder.NewCond()
subCon = subCon.And(builder.Eq{"repository.owner_id": opts.OwnerID})
subCon = generateFilterCond(opts, subCon)
cond = cond.Or(subCon)

}
}
} else if opts.OwnerID > 0 {
} else if opts.OwnerID > 0 && !opts.StarByMe && !opts.UploadAttachmentByMe {
cond = cond.And(builder.Eq{"repository.owner_id": opts.OwnerID})
if !opts.IsOwner {
cond = cond.And(builder.Eq{"dataset.status": DatasetStatusPublic})
cond = cond.And(builder.Eq{"attachment.is_private": false})
}
}
if len(opts.DatasetIDs) > 0 {
if opts.StarByMe || (opts.RepoID == 0 && opts.QueryReference) {
cond = cond.And(builder.In("dataset.id", opts.DatasetIDs))
} else {
subCon := builder.NewCond()
subCon = subCon.And(builder.In("dataset.id", opts.DatasetIDs))
subCon = generateFilterCond(opts, subCon)
cond = cond.Or(subCon)
}
} else {
if opts.StarByMe {
cond = cond.And(builder.Eq{"dataset.id": -1})
}
}

return cond
}

func generateFilterCond(opts *SearchDatasetOptions, cond builder.Cond) builder.Cond {
if len(opts.Keyword) > 0 {
cond = cond.And(builder.Or(builder.Like{"LOWER(dataset.title)", strings.ToLower(opts.Keyword)}, builder.Like{"LOWER(dataset.description)", strings.ToLower(opts.Keyword)}))
}

if len(opts.Category) > 0 {
cond = cond.And(builder.Eq{"dataset.category": opts.Category})
}

if len(opts.Task) > 0 {
cond = cond.And(builder.Eq{"dataset.task": opts.Task})
}
if len(opts.License) > 0 {
cond = cond.And(builder.Eq{"dataset.license": opts.License})
}

if opts.RecommendOnly {
cond = cond.And(builder.Eq{"dataset.recommend": opts.RecommendOnly})
}

if opts.JustNeedZipFile {
cond = cond.And(builder.Gt{"attachment.decompress_state": 0})
}

if opts.CloudBrainType >= 0 {
cond = cond.And(builder.Eq{"attachment.type": opts.CloudBrainType})
}
if opts.UploadAttachmentByMe {
cond = cond.And(builder.Eq{"attachment.uploader_id": opts.User.ID})
}

return cond
}

func SearchDatasetByCondition(opts *SearchDatasetOptions, cond builder.Cond) (DatasetList, int64, error) {
if opts.Page <= 0 {
opts.Page = 1
@@ -169,18 +325,25 @@ func SearchDatasetByCondition(opts *SearchDatasetOptions, cond builder.Cond) (Da
defer sess.Close()

datasets := make(DatasetList, 0, opts.PageSize)
selectColumnsSql := "distinct dataset.id,dataset.title, dataset.status, dataset.category, dataset.description, dataset.download_times, dataset.license, dataset.task, dataset.release_id, dataset.user_id, dataset.repo_id, dataset.created_unix,dataset.updated_unix,dataset.num_stars,dataset.recommend,dataset.use_count"

count, err := sess.Join("INNER", "repository", "repository.id = dataset.repo_id").Where(cond).Count(new(Dataset))
count, err := sess.Distinct("dataset.id").Join("INNER", "repository", "repository.id = dataset.repo_id").
Join("INNER", "attachment", "attachment.dataset_id=dataset.id").
Where(cond).Count(new(Dataset))

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

sess.Select("dataset.*").Join("INNER", "repository", "repository.id = dataset.repo_id").Where(cond).OrderBy(opts.SearchOrderBy.String())
builderQuery := builder.Dialect(setting.Database.Type).Select("id", "title", "status", "category", "description", "download_times", "license", "task", "release_id", "user_id", "repo_id", "created_unix", "updated_unix", "num_stars", "recommend", "use_count").From(builder.Dialect(setting.Database.Type).Select(selectColumnsSql).From("dataset").Join("INNER", "repository", "repository.id = dataset.repo_id").
Join("INNER", "attachment", "attachment.dataset_id=dataset.id").
Where(cond), "d").OrderBy(opts.SearchOrderBy.String())

if opts.PageSize > 0 {
sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
builderQuery.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
}
if err = sess.Find(&datasets); err != nil {

if err = sess.SQL(builderQuery).Find(&datasets); err != nil {
return nil, 0, fmt.Errorf("Dataset: %v", err)
}

@@ -188,6 +351,12 @@ func SearchDatasetByCondition(opts *SearchDatasetOptions, cond builder.Cond) (Da
return nil, 0, fmt.Errorf("LoadAttributes: %v", err)
}

if opts.NeedAttachment {
if err = datasets.loadAttachmentAttributes(opts); err != nil {
return nil, 0, fmt.Errorf("LoadAttributes: %v", err)
}
}

return datasets, count, nil
}

@@ -231,13 +400,23 @@ func getDatasetAttachments(e Engine, typeCloudBrain int, isSigned bool, user *Us
sort.Sort(sortedRels)

// Select attachments
err = e.
Asc("dataset_id").
In("dataset_id", sortedRels.ID).
And("type = ?", typeCloudBrain).
Find(&attachments, Attachment{})
if err != nil {
return err
if typeCloudBrain == -1 {
err = e.
Asc("dataset_id").
In("dataset_id", sortedRels.ID).
Find(&attachments, Attachment{})
if err != nil {
return err
}
} else {
err = e.
Asc("dataset_id").
In("dataset_id", sortedRels.ID).
And("type = ?", typeCloudBrain).
Find(&attachments, Attachment{})
if err != nil {
return err
}
}

// merge join
@@ -253,7 +432,13 @@ func getDatasetAttachments(e Engine, typeCloudBrain int, isSigned bool, user *Us
if err != nil {
return err
}
attachment.FileChunk = fileChunks[0]
if len(fileChunks) > 0 {
attachment.Md5 = fileChunks[0].Md5
} else {
log.Error("has attachment record, but has no file_chunk record")
attachment.Md5 = "no_record"
}

attachment.CanDel = CanDelAttachment(isSigned, user, attachment)
sortedRels.Rel[currentIndex].Attachments = append(sortedRels.Rel[currentIndex].Attachments, attachment)
}
@@ -285,6 +470,29 @@ func UpdateDataset(ctx DBContext, rel *Dataset) error {
return err
}

func IncreaseDatasetUseCount(uuid string) {

IncreaseAttachmentUseNumber(uuid)
attachments, _ := GetAttachmentsByUUIDs(strings.Split(uuid, ";"))

countMap := make(map[int64]int)

for _, attachment := range attachments {
value, ok := countMap[attachment.DatasetID]
if ok {
countMap[attachment.DatasetID] = value + 1
} else {
countMap[attachment.DatasetID] = 1
}

}

for key, value := range countMap {
x.Exec("UPDATE `dataset` SET use_count=use_count+? WHERE id=?", value, key)
}

}

// GetDatasetByID returns Dataset with given ID.
func GetDatasetByID(id int64) (*Dataset, error) {
rel := new(Dataset)
@@ -301,9 +509,6 @@ func GetDatasetByID(id int64) (*Dataset, error) {
}

func GetDatasetByRepo(repo *Repository) (*Dataset, error) {
if err := CreateDefaultDatasetToRepo(repo); err != nil {
return nil, err
}
dataset := &Dataset{RepoID: repo.ID}
has, err := x.Get(dataset)
if err != nil {
@@ -312,10 +517,16 @@ func GetDatasetByRepo(repo *Repository) (*Dataset, error) {
if has {
return dataset, nil
} else {
return nil, errors.New("Not Found")
return nil, ErrNotExist{repo.ID}
}
}

func GetDatasetStarByUser(user *User) ([]*DatasetStar, error) {
datasetStars := make([]*DatasetStar, 0)
err := x.Cols("id", "uid", "dataset_id", "created_unix").Where("uid=?", user.ID).Find(&datasetStars)
return datasetStars, err
}

func DeleteDataset(datasetID int64, uid int64) error {
var err error
sess := x.NewSession()
@@ -367,3 +578,36 @@ func IncreaseDownloadCount(datasetID int64) error {

return nil
}

func GetCollaboratorDatasetIdsByUserID(userID int64) []int64 {
var datasets []int64
_ = x.Table("dataset").Join("INNER", "collaboration", "dataset.repo_id = collaboration.repo_id and collaboration.mode>0 and collaboration.user_id=?", userID).
Cols("dataset.id").Find(&datasets)
return datasets
}

func GetTeamDatasetIdsByUserID(userID int64) []int64 {
var datasets []int64
_ = x.Table("dataset").Join("INNER", "team_repo", "dataset.repo_id = team_repo.repo_id").
Join("INNER", "team_user", "team_repo.team_id=team_user.team_id and team_user.uid=?", userID).
Cols("dataset.id").Find(&datasets)
return datasets
}

func UpdateDatasetCreateUser(ID int64, user *User) error {
_, err := x.Where("id = ?", ID).Cols("user_id").Update(&Dataset{
UserID: user.ID,
})
if err != nil {
return err
}
return nil
}

func QueryDatasetGroupByTask() ([]map[string]interface{}, error) {
rows, err := x.QueryInterface("SELECT count(*) as total,task FROM public.dataset where task <>'' group by task order by total desc limit 7")
if err != nil {
return nil, err
}
return rows, nil
}

+ 15
- 14
models/dataset_permission.go View File

@@ -4,26 +4,27 @@

package models

import (
"code.gitea.io/gitea/modules/log"
)
import "code.gitea.io/gitea/modules/log"

// GetUserDataSetPermission returns the user permissions to the data_set
func GetUserDataSetPermission(dataSet *Dataset, user *User) (isPermit bool, err error) {
isPermit = false

switch dataSet.Status {
case DatasetStatusDeleted:
log.Error("the data_set has been deleted")
case DatasetStatusPrivate:
if !user.IsAdmin && user.ID != dataSet.UserID {
log.Error("the user is not admin nor the owner of the data_set")
if user != nil {
switch dataSet.Status {
case DatasetStatusDeleted:
log.Error("the data_set has been deleted")
case DatasetStatusPrivate:
if !user.IsAdmin && user.ID != dataSet.UserID {
log.Error("the user is not admin nor the owner of the data_set")
}
isPermit = true

case DatasetStatusPublic:
isPermit = true
default:
log.Error("the status of data_set is wrong")
}
isPermit = true
case DatasetStatusPublic:
isPermit = true
default:
log.Error("the status of data_set is wrong")
}

return isPermit, nil


+ 88
- 0
models/dataset_reference.go View File

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

import (
"strconv"
"strings"

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

type DatasetReference struct {
ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"INDEX unique"`
DatasetID string `xorm:"TEXT"`
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
}

func GetDatasetIdsByRepoID(repoID int64) []int64 {
var datasets []int64
var datasetIds []string
_ = x.Table("dataset_reference").Where("repo_id=?", repoID).
Cols("dataset_reference.dataset_id").Find(&datasetIds)
if len(datasetIds) > 0 {
for _, datasetIdStr := range strings.Split(datasetIds[0], ",") {
datasetId, err := strconv.ParseInt(datasetIdStr, 10, 64)
if err != nil {
continue
}
datasets = append(datasets, datasetId)
}
}

return datasets
}

func HasReferenceDataset(repoID int64) bool {

var datasetIds []string
_ = x.Table("dataset_reference").Where("repo_id=?", repoID).
Cols("dataset_reference.dataset_id").Find(&datasetIds)
return len(datasetIds) > 0
}

func getReferenceDatasetStr(repoID int64) string {

var datasetIds []string
_ = x.Table("dataset_reference").Where("repo_id=?", repoID).
Cols("dataset_reference.dataset_id").Find(&datasetIds)
if len(datasetIds) > 0 {
return datasetIds[0]
}
return ""
}

func DeleteReferenceDatasetIdsByRepoID(repoID int64) error {

_, err := x.Exec("delete from dataset_reference where repo_id=?", repoID)
return err
}

func NewDatasetIdsByRepoID(repoID int64, datasetIds []int64) error {
if len(datasetIds) == 0 { //关联数据集数组为空
DeleteReferenceDatasetIdsByRepoID(repoID)
}
var datasetsStrArray []string
for _, datasetId := range datasetIds {
datasetsStrArray = append(datasetsStrArray, strconv.FormatInt(datasetId, 10))
}

newDatasetStr := strings.Join(datasetsStrArray, ",")
oldDatasetStr := getReferenceDatasetStr(repoID)
if newDatasetStr == oldDatasetStr { //关联数据集无变化,不需要处理
return nil
}
if oldDatasetStr != "" { //已经存在关联数据集
_, err := x.Exec("update dataset_reference set dataset_id=? where repo_id=?", newDatasetStr, repoID)

return err
} else {
datasetReference := DatasetReference{
DatasetID: newDatasetStr,
RepoID: repoID,
}

_, err := x.Insert(datasetReference)
return err
}

}

+ 77
- 0
models/dataset_star.go View File

@@ -0,0 +1,77 @@
package models

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

type DatasetStar struct {
ID int64 `xorm:"pk autoincr"`
UID int64 `xorm:"UNIQUE(s)"`
DatasetID int64 `xorm:"UNIQUE(s)"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
}

// StarRepo or unstar repository.
func StarDataset(userID, datasetID int64, star bool) error {
sess := x.NewSession()
defer sess.Close()

if err := sess.Begin(); err != nil {
return err
}

if star {
if isDatasetStaring(sess, userID, datasetID) {
return nil
}

if _, err := sess.Insert(&DatasetStar{UID: userID, DatasetID: datasetID}); err != nil {
return err
}
if _, err := sess.Exec("UPDATE `dataset` SET num_stars = num_stars + 1 WHERE id = ?", datasetID); err != nil {
return err
}
if _, err := sess.Exec("UPDATE `user` SET num_dataset_stars = num_dataset_stars + 1 WHERE id = ?", userID); err != nil {
return err
}
} else {
if !isDatasetStaring(sess, userID, datasetID) {
return nil
}

if _, err := sess.Delete(&DatasetStar{0, userID, datasetID, 0}); err != nil {
return err
}
if _, err := sess.Exec("UPDATE `dataset` SET num_stars = num_stars - 1 WHERE id = ?", datasetID); err != nil {
return err
}
if _, err := sess.Exec("UPDATE `user` SET num_dataset_stars = num_dataset_stars - 1 WHERE id = ?", userID); err != nil {
return err
}
}

return sess.Commit()
}

func IsDatasetStaringByRepoId(userID, repoID int64) bool {
dataset, _ := GetDatasetByRepo(&Repository{ID: repoID})
if dataset == nil {
return false
}
return isDatasetStaring(x, userID, dataset.ID)
}

func IsDatasetStaring(userID, datasetID int64) bool {
return isDatasetStaring(x, userID, datasetID)

}

func isDatasetStaring(e Engine, userID, datasetID int64) bool {
has, _ := e.Get(&DatasetStar{0, userID, datasetID, 0})
return has
}

func GetDatasetIdsStarByUser(userID int64) []int64 {
var datasets []int64
_ = x.Table("dataset_star").Where("uid=?", userID).
Cols("dataset_star.dataset_id").Find(&datasets)
return datasets
}

+ 203
- 0
models/dbsql/dataset_foreigntable_for_es.sql View File

@@ -0,0 +1,203 @@
DELETE FROM public.dataset_es;
DROP FOREIGN TABLE public.dataset_es;
DROP TRIGGER IF EXISTS es_insert_dataset on public.dataset;
DROP FUNCTION public.insert_dataset_data();
DROP TRIGGER IF EXISTS es_udpate_dataset_file_name on public.attachment;
DROP FUNCTION public.udpate_dataset_file_name;

DROP TRIGGER IF EXISTS es_update_dataset on public.dataset;
DROP FUNCTION public.update_dataset;

DROP TRIGGER IF EXISTS es_delete_dataset on public.dataset;
DROP FUNCTION public.delete_dataset;


CREATE FOREIGN TABLE public.dataset_es
(
id bigint NOT NULL,
title character varying(255),
status integer,
category character varying(255),
description text,
download_times bigint,
license character varying(255),
task character varying(255),
release_id bigint,
user_id bigint,
repo_id bigint,
created_unix bigint,
updated_unix bigint,
file_name text,
file_desc text
)SERVER multicorn_es
OPTIONS
(
host '192.168.207.94',
port '9200',
index 'dataset-es-index',
rowid_column 'id',
default_sort '_id'
)
;
DELETE FROM public.dataset_es;
INSERT INTO public.dataset_es(
id,
title,
status,
category,
description,
download_times,
license, task,
release_id,
user_id,
repo_id,
created_unix,
updated_unix,
file_name,
file_desc
)
SELECT
b.id,
b.title,
b.status,
b.category,
b.description,
b.download_times,
b.license,
b.task,
b.release_id,
b.user_id,
b.repo_id,
b.created_unix,
b.updated_unix,
(select array_to_string(array_agg(name order by created_unix desc),'-#,#-') from public.attachment a where a.dataset_id=b.id and a.is_private=false),
(select array_to_string(array_agg(description order by created_unix desc),'-#,#-') from public.attachment a where a.dataset_id=b.id and a.is_private=false)
FROM public.dataset b,public.repository c where b.repo_id=c.id and c.is_private=false;


DROP TRIGGER IF EXISTS es_insert_dataset on public.dataset;

CREATE OR REPLACE FUNCTION public.insert_dataset_data() RETURNS trigger AS
$def$
DECLARE
privateValue boolean=false;
BEGIN
select into privateValue is_private from public.repository where id=NEW.repo_id;
if not privateValue then
INSERT INTO public.dataset_es(
id,
title,
status,
category,
description,
download_times,
license,
task,
release_id,
user_id,
repo_id,
created_unix,
updated_unix)
VALUES (
NEW.id,
NEW.title,
NEW.status,
NEW.category,
NEW.description,
NEW.download_times,
NEW.license,
NEW.task,
NEW.release_id,
NEW.user_id,
NEW.repo_id,
NEW.created_unix,
NEW.updated_unix
);
end if;
RETURN NEW;
END;
$def$
LANGUAGE plpgsql;



CREATE TRIGGER es_insert_dataset
AFTER INSERT ON public.dataset
FOR EACH ROW EXECUTE PROCEDURE insert_dataset_data();

ALTER TABLE public.dataset ENABLE ALWAYS TRIGGER es_insert_dataset;


DROP TRIGGER IF EXISTS es_udpate_dataset_file_name on public.attachment;

CREATE OR REPLACE FUNCTION public.udpate_dataset_file_name() RETURNS trigger AS
$def$
BEGIN
if (TG_OP = 'UPDATE') then
update public.dataset_es SET file_desc=(select array_to_string(array_agg(description order by created_unix desc),'-#,#-') from public.attachment where dataset_id=NEW.dataset_id and is_private=false) where id=NEW.dataset_id;
elsif (TG_OP = 'INSERT') then
update public.dataset_es SET file_name=(select array_to_string(array_agg(name order by created_unix desc),'-#,#-') from public.attachment where dataset_id=NEW.dataset_id and is_private=false) where id=NEW.dataset_id;
elsif (TG_OP = 'DELETE') then
update public.dataset_es SET file_name=(select array_to_string(array_agg(name order by created_unix desc),'-#,#-') from public.attachment where dataset_id=OLD.dataset_id and is_private=false) where id=OLD.dataset_id;
update public.dataset_es SET file_desc=(select array_to_string(array_agg(description order by created_unix desc),'-#,#-') from public.attachment where dataset_id=OLD.dataset_id and is_private=false) where id=OLD.dataset_id;
end if;
return NEW;
END;
$def$
LANGUAGE plpgsql;


CREATE TRIGGER es_udpate_dataset_file_name
AFTER INSERT OR UPDATE OR DELETE ON public.attachment
FOR EACH ROW EXECUTE PROCEDURE udpate_dataset_file_name();

ALTER TABLE public.attachment ENABLE ALWAYS TRIGGER es_udpate_dataset_file_name;

DROP TRIGGER IF EXISTS es_update_dataset on public.dataset;

CREATE OR REPLACE FUNCTION public.update_dataset() RETURNS trigger AS
$def$
BEGIN
if (NEW.status=0) then
delete from public.dataset_es where id=NEW.id;
elsif (NEW.status=1) then
UPDATE public.dataset_es
SET description=NEW.description,
title=NEW.title,
category=NEW.category,
task=NEW.task,
download_times=NEW.download_times,
updated_unix=NEW.updated_unix,
file_name=(select array_to_string(array_agg(name order by created_unix desc),'-#,#-') from public.attachment where dataset_id=NEW.id and is_private=false),
file_desc=(select array_to_string(array_agg(description order by created_unix desc),'-#,#-') from public.attachment where dataset_id=NEW.id and is_private=false)
where id=NEW.id;
end if;
return new;
END
$def$
LANGUAGE plpgsql;

CREATE TRIGGER es_update_dataset
AFTER UPDATE ON public.dataset
FOR EACH ROW EXECUTE PROCEDURE update_dataset();

ALTER TABLE public.dataset ENABLE ALWAYS TRIGGER es_update_dataset;

DROP TRIGGER IF EXISTS es_delete_dataset on public.dataset;

CREATE OR REPLACE FUNCTION public.delete_dataset() RETURNS trigger AS
$def$
declare
BEGIN
DELETE FROM public.dataset_es where id=OLD.id;
return new;
END
$def$
LANGUAGE plpgsql;


CREATE TRIGGER es_delete_dataset
AFTER DELETE ON public.dataset
FOR EACH ROW EXECUTE PROCEDURE delete_dataset();

ALTER TABLE public.dataset ENABLE ALWAYS TRIGGER es_delete_dataset;

+ 227
- 0
models/dbsql/issue_foreigntable_for_es.sql View File

@@ -0,0 +1,227 @@
delete from public.issue_es;
DROP FOREIGN TABLE public.issue_es;
DROP TRIGGER IF EXISTS es_insert_issue on public.issue;
DROP FUNCTION public.insert_issue_data;
DROP TRIGGER IF EXISTS es_udpate_issue_comment on public.comment;
DROP FUNCTION udpate_issue_comment;
DROP TRIGGER IF EXISTS es_update_issue on public.issue;
DROP FUNCTION public.update_issue;
DROP TRIGGER IF EXISTS es_delete_issue on public.issue;
DROP FUNCTION public.delete_issue;


CREATE FOREIGN TABLE public.issue_es
(
id bigint NOT NULL,
repo_id bigint,
index bigint,
poster_id bigint,
original_author character varying(255),
original_author_id bigint,
name character varying(255) ,
content text,
comment text,
milestone_id bigint,
priority integer,
is_closed boolean,
is_pull boolean,
pr_id bigint,
num_comments integer,
ref character varying(255),
deadline_unix bigint,
created_unix bigint,
updated_unix bigint,
closed_unix bigint,
is_locked boolean NOT NULL,
amount bigint,
is_transformed boolean NOT NULL
)SERVER multicorn_es
OPTIONS
(
host '192.168.207.94',
port '9200',
index 'issue-es-index',
rowid_column 'id',
default_sort '_id'
)
;

delete from public.issue_es;
INSERT INTO public.issue_es(
id,
repo_id,
index,
poster_id,
original_author,
original_author_id,
name,
content,
milestone_id,
priority,
is_closed,
is_pull,
num_comments,
ref,
deadline_unix,
created_unix,
updated_unix,
closed_unix,
is_locked,
amount,
is_transformed,comment,pr_id)
SELECT
b.id,
b.repo_id,
b.index,
b.poster_id,
b.original_author,
b.original_author_id,
b.name,
b.content,
b.milestone_id,
b.priority,
b.is_closed,
b.is_pull,
b.num_comments,
b.ref,
b.deadline_unix,
b.created_unix,
b.updated_unix,
b.closed_unix,
b.is_locked,
b.amount,
b.is_transformed,
(select array_to_string(array_agg(content order by created_unix desc),',') from public.comment a where a.issue_id=b.id),
(select id from public.pull_request d where b.id=d.issue_id and b.is_pull=true)
FROM public.issue b,public.repository c where b.repo_id=c.id and c.is_private=false;


CREATE OR REPLACE FUNCTION public.insert_issue_data() RETURNS trigger AS
$def$
DECLARE
privateValue boolean=false;
BEGIN
select into privateValue is_private from public.repository where id=NEW.repo_id;
if not privateValue then
INSERT INTO public.issue_es(
id,
repo_id,
index,
poster_id,
original_author,
original_author_id,
name,
content,
milestone_id,
priority,
is_closed,
is_pull,
num_comments,
ref,
deadline_unix,
created_unix,
updated_unix,
closed_unix,
is_locked,
amount,
is_transformed)
VALUES (
NEW.id,
NEW.repo_id,
NEW.index,
NEW.poster_id,
NEW.original_author,
NEW.original_author_id,
NEW.name,
NEW.content,
NEW.milestone_id,
NEW.priority,
NEW.is_closed,
NEW.is_pull,
NEW.num_comments,
NEW.ref,
NEW.deadline_unix,
NEW.created_unix,
NEW.updated_unix,
NEW.closed_unix,
NEW.is_locked,
NEW.amount,
NEW.is_transformed
);
end if;
RETURN NEW;
END;
$def$
LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS es_insert_issue on public.issue;

CREATE TRIGGER es_insert_issue
AFTER INSERT ON public.issue
FOR EACH ROW EXECUTE PROCEDURE insert_issue_data();

ALTER TABLE public.issue ENABLE ALWAYS TRIGGER es_insert_issue;

CREATE OR REPLACE FUNCTION public.udpate_issue_comment() RETURNS trigger AS
$def$
BEGIN
if (TG_OP = 'DELETE') then
update public.issue_es SET comment=(select array_to_string(array_agg(content order by created_unix desc),',') from public.comment where issue_id=OLD.issue_id) where id=OLD.issue_id;
elsif (TG_OP = 'UPDATE') then
update public.issue_es SET comment=(select array_to_string(array_agg(content order by created_unix desc),',') from public.comment where issue_id=NEW.issue_id) where id=NEW.issue_id;
end if;
return null;
END;
$def$
LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS es_udpate_issue_comment on public.comment;
CREATE TRIGGER es_udpate_issue_comment
AFTER DELETE OR UPDATE ON public.comment
FOR EACH ROW EXECUTE PROCEDURE udpate_issue_comment();

ALTER TABLE public.comment ENABLE ALWAYS TRIGGER es_udpate_issue_comment;


CREATE OR REPLACE FUNCTION public.update_issue() RETURNS trigger AS
$def$
declare
BEGIN
UPDATE public.issue_es
SET content=NEW.content,
name=NEW.name,
is_closed=NEW.is_closed,
num_comments=NEW.num_comments,
updated_unix=NEW.updated_unix,
comment=(select array_to_string(array_agg(content order by created_unix desc),',') from public.comment where issue_id=NEW.id)
where id=NEW.id;
return new;
END
$def$
LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS es_update_issue on public.issue;

CREATE TRIGGER es_update_issue
AFTER UPDATE ON public.issue
FOR EACH ROW EXECUTE PROCEDURE update_issue();

ALTER TABLE public.issue ENABLE ALWAYS TRIGGER es_update_issue;

CREATE OR REPLACE FUNCTION public.delete_issue() RETURNS trigger AS
$def$
declare
BEGIN
DELETE FROM public.issue_es where id=OLD.id;
return new;
END
$def$
LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS es_delete_issue on public.issue;
CREATE TRIGGER es_delete_issue
AFTER DELETE ON public.issue
FOR EACH ROW EXECUTE PROCEDURE delete_issue();

ALTER TABLE public.issue ENABLE ALWAYS TRIGGER es_delete_issue;

+ 549
- 0
models/dbsql/repo_foreigntable_for_es.sql View File

@@ -0,0 +1,549 @@
-- 要处理项目从私有变为公有,并且从公有变成私有的情况
DELETE FROM public.repository_es;
DROP FOREIGN table if exists public.repository_es;
DROP TRIGGER IF EXISTS es_insert_repository on public.repository;
DROP FUNCTION public.insert_repository_data;
DROP TRIGGER IF EXISTS es_update_repository on public.repository;
DROP FUNCTION public.update_repository;

DROP TRIGGER IF EXISTS es_delete_repository on public.repository;
DROP FUNCTION public.delete_repository;

DROP TRIGGER IF EXISTS es_udpate_repository_lang on public.language_stat;
DROP FUNCTION public.udpate_repository_lang;


CREATE FOREIGN TABLE public.repository_es (
id bigint NOT NULL,
owner_id bigint,
owner_name character varying(255),
lower_name character varying(255) NOT NULL,
name character varying(255) NOT NULL,
description text,
website character varying(2048),
original_service_type integer,
original_url character varying(2048),
default_branch character varying(255),
num_watches integer,
num_stars integer,
num_forks integer,
num_issues integer,
num_closed_issues integer,
num_pulls integer,
num_closed_pulls integer,
num_milestones integer DEFAULT 0 NOT NULL,
num_closed_milestones integer DEFAULT 0 NOT NULL,
is_private boolean,
is_empty boolean,
is_archived boolean,
is_mirror boolean,
status integer DEFAULT 0 NOT NULL,
is_fork boolean DEFAULT false NOT NULL,
fork_id bigint,
is_template boolean DEFAULT false NOT NULL,
template_id bigint,
size bigint DEFAULT 0 NOT NULL,
is_fsck_enabled boolean DEFAULT true NOT NULL,
close_issues_via_commit_in_any_branch boolean DEFAULT false NOT NULL,
topics text,
avatar character varying(64),
created_unix bigint,
updated_unix bigint,
contract_address character varying(255),
block_chain_status integer DEFAULT 0 NOT NULL,
balance character varying(255) DEFAULT '0'::character varying NOT NULL,
clone_cnt bigint DEFAULT 0 NOT NULL,
license character varying(100),
download_cnt bigint DEFAULT 0 NOT NULL,
num_commit bigint DEFAULT 0 NOT NULL,
git_clone_cnt bigint DEFAULT 0 NOT NULL,
creator_id bigint NOT NULL DEFAULT 0,
repo_type integer NOT NULL DEFAULT 0,
lang character varying(2048),
alias character varying(255),
lower_alias character varying(255)
) SERVER multicorn_es
OPTIONS
(
host '192.168.207.94',
port '9200',
index 'repository-es-index',
rowid_column 'id',
default_sort '_id'
)
;
delete from public.repository_es;
INSERT INTO public.repository_es (id,
owner_id,
owner_name,
lower_name,
name,
description,
website,
original_service_type,
original_url,
default_branch,
num_watches,
num_stars,
num_forks,
num_issues,
num_closed_issues,
num_pulls,
num_closed_pulls,
num_milestones,
num_closed_milestones,
is_private,
is_empty,
is_archived,
is_mirror,
status,
is_fork,
fork_id,
is_template,
template_id,
size,
is_fsck_enabled,
close_issues_via_commit_in_any_branch,
topics,
avatar,
created_unix,
updated_unix,
contract_address,
block_chain_status,
balance,
clone_cnt,
num_commit,
git_clone_cnt,
creator_id,
repo_type,
lang,
alias,
lower_alias
)
SELECT
id,
owner_id,
owner_name,
lower_name,
name,
description,
website,
original_service_type,
original_url,
default_branch,
num_watches,
num_stars,
num_forks,
num_issues,
num_closed_issues,
num_pulls,
num_closed_pulls,
num_milestones,
num_closed_milestones,
is_private,
is_empty,
is_archived,
is_mirror,
status,
is_fork,
fork_id,
is_template,
template_id,
size,
is_fsck_enabled,
close_issues_via_commit_in_any_branch,
topics,
avatar,
created_unix,
updated_unix,
contract_address,
block_chain_status,
balance,
clone_cnt,
num_commit,
git_clone_cnt,
creator_id,
repo_type,
(select array_to_string(array_agg(language order by percentage desc),',') from public.language_stat a where a.repo_id=b.id),
alias,
lower_alias
FROM public.repository b where b.is_private=false;

DROP TRIGGER IF EXISTS es_insert_repository on public.repository;

CREATE OR REPLACE FUNCTION public.insert_repository_data() RETURNS trigger AS
$def$
BEGIN
if not NEW.is_private then
INSERT INTO public.repository_es (id,
owner_id,
owner_name,
lower_name,
name,
description,
website,
original_service_type,
original_url,
default_branch,
num_watches,
num_stars,
num_forks,
num_issues,
num_closed_issues,
num_pulls,
num_closed_pulls,
num_milestones,
num_closed_milestones,
is_private,
is_empty,
is_archived,
is_mirror,
status,
is_fork,
fork_id,
is_template,
template_id,
size,
is_fsck_enabled,
close_issues_via_commit_in_any_branch,
topics,
avatar,
created_unix,
updated_unix,
contract_address,
block_chain_status,
balance,
clone_cnt,
num_commit,
git_clone_cnt,
creator_id,
repo_type,
alias,
lower_alias) VALUES
(NEW.id,
NEW.owner_id,
NEW.owner_name,
NEW.lower_name,
NEW.name,
NEW.description,
NEW.website,
NEW.original_service_type,
NEW.original_url,
NEW.default_branch,
NEW.num_watches,
NEW.num_stars,
NEW.num_forks,
NEW.num_issues,
NEW.num_closed_issues,
NEW.num_pulls,
NEW.num_closed_pulls,
NEW.num_milestones,
NEW.num_closed_milestones,
NEW.is_private,
NEW.is_empty,
NEW.is_archived,
NEW.is_mirror,
NEW.status,
NEW.is_fork,
NEW.fork_id,
NEW.is_template,
NEW.template_id,
NEW.size,
NEW.is_fsck_enabled,
NEW.close_issues_via_commit_in_any_branch,
NEW.topics,
NEW.avatar,
NEW.created_unix,
NEW.updated_unix,
NEW.contract_address,
NEW.block_chain_status,
NEW.balance,
NEW.clone_cnt,
NEW.num_commit,
NEW.git_clone_cnt,
NEW.creator_id,
NEW.repo_type,
NEW.alias,
NEW.lower_alias);
end if;
RETURN NEW;
END;
$def$
LANGUAGE plpgsql;


CREATE TRIGGER es_insert_repository
AFTER INSERT ON public.repository
FOR EACH ROW EXECUTE PROCEDURE insert_repository_data();

ALTER TABLE public.repository ENABLE ALWAYS TRIGGER es_insert_repository;

DROP TRIGGER IF EXISTS es_update_repository on public.repository;

CREATE OR REPLACE FUNCTION public.update_repository() RETURNS trigger AS
$def$
BEGIN
if OLD.is_private != NEW.is_private then
if OLD.is_private and not NEW.is_private then
--insert
INSERT INTO public.repository_es (id,
owner_id,
owner_name,
lower_name,
name,
description,
website,
original_service_type,
original_url,
default_branch,
num_watches,
num_stars,
num_forks,
num_issues,
num_closed_issues,
num_pulls,
num_closed_pulls,
num_milestones,
num_closed_milestones,
is_private,
is_empty,
is_archived,
is_mirror,
status,
is_fork,
fork_id,
is_template,
template_id,
size,
is_fsck_enabled,
close_issues_via_commit_in_any_branch,
topics,
avatar,
created_unix,
updated_unix,
contract_address,
block_chain_status,
balance,
clone_cnt,
num_commit,
git_clone_cnt,
creator_id,
repo_type,
lang,
alias,
lower_alias)
SELECT
id,
owner_id,
owner_name,
lower_name,
name,
description,
website,
original_service_type,
original_url,
default_branch,
num_watches,
num_stars,
num_forks,
num_issues,
num_closed_issues,
num_pulls,
num_closed_pulls,
num_milestones,
num_closed_milestones,
is_private,
is_empty,
is_archived,
is_mirror,
status,
is_fork,
fork_id,
is_template,
template_id,
size,
is_fsck_enabled,
close_issues_via_commit_in_any_branch,
topics,
avatar,
created_unix,
updated_unix,
contract_address,
block_chain_status,
balance,
clone_cnt,
num_commit,
git_clone_cnt,
creator_id,
repo_type,
(select array_to_string(array_agg(language order by percentage desc),',') from public.language_stat a where a.repo_id=b.id),
alias,
lower_alias
FROM public.repository b where b.id=NEW.id;
INSERT INTO public.dataset_es(
id,
title,
status,
category,
description,
download_times,
license, task,
release_id,
user_id,
repo_id,
created_unix,
updated_unix,file_name)
SELECT
b.id,
b.title,
b.status,
b.category,
b.description,
b.download_times,
b.license,
b.task,
b.release_id,
b.user_id,
b.repo_id,
b.created_unix,
b.updated_unix,(select array_to_string(array_agg(name order by created_unix desc),',') from public.attachment a where a.dataset_id=b.id and a.is_private=false)
FROM public.dataset b where b.repo_id=NEW.id;

INSERT INTO public.issue_es(
id,
repo_id,
index,
poster_id,
original_author,
original_author_id,
name,
content,
milestone_id,
priority,
is_closed,
is_pull,
num_comments,
ref,
deadline_unix,
created_unix,
updated_unix,
closed_unix,
is_locked,
amount,
is_transformed,comment,pr_id)
SELECT
b.id,
b.repo_id,
b.index,
b.poster_id,
b.original_author,
b.original_author_id,
b.name,
b.content,
b.milestone_id,
b.priority,
b.is_closed,
b.is_pull,
b.num_comments,
b.ref,
b.deadline_unix,
b.created_unix,
b.updated_unix,
b.closed_unix,
b.is_locked,
b.amount,
b.is_transformed,
(select array_to_string(array_agg(content order by created_unix desc),',') from public.comment a where a.issue_id=b.id),
(select id from public.pull_request d where d.issue_id=b.id)
FROM public.issue b where b.repo_id=NEW.id;
end if;

if not OLD.is_private and NEW.is_private then
delete from public.issue_es where repo_id=NEW.id;
-- delete from public.dataset_es where repo_id=NEW.id;
delete from public.repository_es where id=NEW.id;
end if;

end if;

if not NEW.is_private then
raise notice 'update repo,the updated_unix is %',NEW.updated_unix;
update public.repository_es SET description=NEW.description,
name=NEW.name,
lower_name=NEW.lower_name,
owner_name=NEW.owner_name,
website=NEW.website,
updated_unix=NEW.updated_unix,
num_watches=NEW.num_watches,
num_stars=NEW.num_stars,
num_forks=NEW.num_forks,
topics=NEW.topics,
alias = NEW.alias,
lower_alias = NEW.lower_alias,
avatar=NEW.avatar
where id=NEW.id;
end if;
return new;
END
$def$
LANGUAGE plpgsql;

CREATE TRIGGER es_update_repository
AFTER UPDATE ON public.repository
FOR EACH ROW EXECUTE PROCEDURE update_repository();

ALTER TABLE public.repository ENABLE ALWAYS TRIGGER es_update_repository;


DROP TRIGGER IF EXISTS es_delete_repository on public.repository;

CREATE OR REPLACE FUNCTION public.delete_repository() RETURNS trigger AS
$def$
declare
BEGIN
delete from public.issue_es where repo_id=OLD.id;
delete from public.dataset_es where repo_id=OLD.id;
DELETE FROM public.repository_es where id=OLD.id;
return new;
END
$def$
LANGUAGE plpgsql;


CREATE TRIGGER es_delete_repository
AFTER DELETE ON public.repository
FOR EACH ROW EXECUTE PROCEDURE delete_repository();

ALTER TABLE public.repository ENABLE ALWAYS TRIGGER es_delete_repository;



DROP TRIGGER IF EXISTS es_udpate_repository_lang on public.language_stat;

CREATE OR REPLACE FUNCTION public.udpate_repository_lang() RETURNS trigger AS
$def$
DECLARE
privateValue bigint;
BEGIN
if (TG_OP = 'UPDATE') then
select into privateValue updated_unix from public.repository where id=NEW.repo_id;
update public.repository_es SET updated_unix=privateValue,lang=(select array_to_string(array_agg(language order by percentage desc),',') from public.language_stat where repo_id=NEW.repo_id) where id=NEW.repo_id;
elsif (TG_OP = 'INSERT') then
select into privateValue updated_unix from public.repository where id=NEW.repo_id;
update public.repository_es SET updated_unix=privateValue,lang=(select array_to_string(array_agg(language order by percentage desc),',') from public.language_stat where repo_id=NEW.repo_id) where id=NEW.repo_id;
elsif (TG_OP = 'DELETE') then
if exists(select 1 from public.repository where id=OLD.repo_id) then
update public.repository_es SET lang=(select array_to_string(array_agg(language order by percentage desc),',') from public.language_stat where repo_id=OLD.repo_id) where id=OLD.repo_id;
end if;
end if;
return NEW;
END;
$def$
LANGUAGE plpgsql;

CREATE TRIGGER es_udpate_repository_lang
AFTER INSERT OR UPDATE OR DELETE ON public.language_stat
FOR EACH ROW EXECUTE PROCEDURE udpate_repository_lang();

ALTER TABLE public.language_stat ENABLE ALWAYS TRIGGER es_udpate_repository_lang;

+ 317
- 0
models/dbsql/user_foreigntable_for_es.sql View File

@@ -0,0 +1,317 @@
DELETE FROM public.user_es;
DROP FOREIGN table if exists public.user_es;
DROP TRIGGER IF EXISTS es_insert_user on public.user;
DROP FUNCTION public.insert_user_data;
DROP TRIGGER IF EXISTS es_update_user on public.user;
DROP FUNCTION public.update_user;

DROP TRIGGER IF EXISTS es_delete_user on public.user;
DROP FUNCTION public.delete_user;

CREATE FOREIGN TABLE public.user_es
(
id bigint NOT NULL ,
lower_name character varying(255) NULL,
name character varying(255) NULL,
full_name character varying(255),
email character varying(255),
keep_email_private boolean,
email_notifications_preference character varying(20) ,
passwd character varying(255) ,
passwd_hash_algo character varying(255) ,
must_change_password boolean NOT NULL DEFAULT false,
login_type integer,
login_source bigint NOT NULL DEFAULT 0,
login_name character varying(255) ,
type integer,
location character varying(255),
website character varying(255),
rands character varying(10),
salt character varying(10),
language character varying(5),
description character varying(255),
created_unix bigint,
updated_unix bigint,
last_login_unix bigint,
last_repo_visibility boolean,
max_repo_creation integer,
is_active boolean,
is_admin boolean,
is_restricted boolean NOT NULL DEFAULT false,
allow_git_hook boolean,
allow_import_local boolean,
allow_create_organization boolean DEFAULT true,
prohibit_login boolean NOT NULL DEFAULT false,
avatar character varying(2048) ,
avatar_email character varying(255),
use_custom_avatar boolean,
num_followers integer,
num_following integer NOT NULL DEFAULT 0,
num_stars integer,
num_repos integer,
num_teams integer,
num_members integer,
visibility integer NOT NULL DEFAULT 0,
repo_admin_change_team_access boolean NOT NULL DEFAULT false,
diff_view_style character varying(255),
theme character varying(255),
token character varying(1024) ,
public_key character varying(255),
private_key character varying(255),
is_operator boolean NOT NULL DEFAULT false,
num_dataset_stars integer NOT NULL DEFAULT 0
) SERVER multicorn_es
OPTIONS
(
host '192.168.207.94',
port '9200',
index 'user-es-index',
rowid_column 'id',
default_sort '_id'
)
;
delete from public.user_es;
INSERT INTO public.user_es(
id,
lower_name,
name,
full_name,
email,
keep_email_private,
email_notifications_preference,
must_change_password,
login_type,
login_source,
login_name,
type,
location,
website,
rands,
language,
description,
created_unix,
updated_unix,
last_login_unix,
last_repo_visibility,
max_repo_creation,
is_active,
is_restricted,
allow_git_hook,
allow_import_local,
allow_create_organization,
prohibit_login,
avatar,
avatar_email,
use_custom_avatar,
num_followers,
num_following,
num_stars,
num_repos,
num_teams,
num_members,
visibility,
repo_admin_change_team_access,
diff_view_style,
theme,
is_operator,
num_dataset_stars)
SELECT
id,
lower_name,
name,
full_name,
email,
keep_email_private,
email_notifications_preference,
must_change_password,
login_type,
login_source,
login_name,
type,
location,
website,
rands,
language,
description,
created_unix,
updated_unix,
last_login_unix,
last_repo_visibility,
max_repo_creation,
is_active,
is_restricted,
allow_git_hook,
allow_import_local,
allow_create_organization,
prohibit_login,
avatar,
avatar_email,
use_custom_avatar,
num_followers,
num_following,
num_stars,
num_repos,
num_teams,
num_members,
visibility,
repo_admin_change_team_access,
diff_view_style,
theme,
is_operator,
num_dataset_stars
FROM public.user;

DROP TRIGGER IF EXISTS es_insert_user on public.user;

CREATE OR REPLACE FUNCTION public.insert_user_data() RETURNS trigger AS
$def$
BEGIN
INSERT INTO public."user_es"(
id,
lower_name,
name,
full_name,
email,
keep_email_private,
email_notifications_preference,
must_change_password,
login_type,
login_source,
login_name,
type,
location,
website,
rands,
language,
description,
created_unix,
updated_unix,
last_login_unix,
last_repo_visibility,
max_repo_creation,
is_active,
is_restricted,
allow_git_hook,
allow_import_local,
allow_create_organization,
prohibit_login,
avatar,
avatar_email,
use_custom_avatar,
num_followers,
num_following,
num_stars,
num_repos,
num_teams,
num_members,
visibility,
repo_admin_change_team_access,
diff_view_style,
theme,
is_operator,
num_dataset_stars)
VALUES (
NEW.id,
NEW.lower_name,
NEW.name,
NEW.full_name,
NEW.email,
NEW.keep_email_private,
NEW.email_notifications_preference,
NEW.must_change_password,
NEW.login_type,
NEW.login_source,
NEW.login_name,
NEW.type,
NEW.location,
NEW.website,
NEW.rands,
NEW.language,
NEW.description,
NEW.created_unix,
NEW.updated_unix,
NEW.last_login_unix,
NEW.last_repo_visibility,
NEW.max_repo_creation,
NEW.is_active,
NEW.is_restricted,
NEW.allow_git_hook,
NEW.allow_import_local,
NEW.allow_create_organization,
NEW.prohibit_login,
NEW.avatar,
NEW.avatar_email,
NEW.use_custom_avatar,
NEW.num_followers,
NEW.num_following,
NEW.num_stars,
NEW.num_repos,
NEW.num_teams,
NEW.num_members,
NEW.visibility,
NEW.repo_admin_change_team_access,
NEW.diff_view_style,
NEW.theme,
NEW.is_operator,
NEW.num_dataset_stars
);

RETURN NEW;
END;
$def$
LANGUAGE plpgsql;



CREATE TRIGGER es_insert_user
AFTER INSERT ON public.user
FOR EACH ROW EXECUTE PROCEDURE insert_user_data();

ALTER TABLE public.user ENABLE ALWAYS TRIGGER es_insert_user;

DROP TRIGGER IF EXISTS es_update_user on public.user;

CREATE OR REPLACE FUNCTION public.update_user() RETURNS trigger AS
$def$
BEGIN
UPDATE public.user_es
SET description=NEW.description,
name=NEW.name,
full_name=NEW.full_name,
location=NEW.location,
website=NEW.website,
email=NEW.email,
num_dataset_stars=NEW.num_dataset_stars,
updated_unix=NEW.updated_unix
where id=NEW.id;
return new;
END
$def$
LANGUAGE plpgsql;



CREATE TRIGGER es_update_user
AFTER UPDATE ON public.user
FOR EACH ROW EXECUTE PROCEDURE update_user();

ALTER TABLE public.user ENABLE ALWAYS TRIGGER es_update_user;

DROP TRIGGER IF EXISTS es_delete_user on public.user;

CREATE OR REPLACE FUNCTION public.delete_user() RETURNS trigger AS
$def$
declare
BEGIN
DELETE FROM public.user_es where id=OLD.id;
return new;
END
$def$
LANGUAGE plpgsql;


CREATE TRIGGER es_delete_user
AFTER DELETE ON public.user
FOR EACH ROW EXECUTE PROCEDURE delete_user();
ALTER TABLE public.user ENABLE ALWAYS TRIGGER es_delete_user;

+ 24
- 0
models/error.go View File

@@ -2012,3 +2012,27 @@ func IsErrTagNotExist(err error) bool {
_, ok := err.(ErrTagNotExist)
return ok
}

type ErrRecordNotExist struct {
}

func IsErrRecordNotExist(err error) bool {
_, ok := err.(ErrRecordNotExist)
return ok
}

func (err ErrRecordNotExist) Error() string {
return fmt.Sprintf("record not exist in database")
}

type ErrInsufficientPointsBalance struct {
}

func IsErrInsufficientPointsBalance(err error) bool {
_, ok := err.(ErrInsufficientPointsBalance)
return ok
}

func (err ErrInsufficientPointsBalance) Error() string {
return fmt.Sprintf("Insufficient points balance")
}

+ 75
- 5
models/file_chunk.go View File

@@ -13,11 +13,6 @@ const (
FileUploaded
)

const (
TypeCloudBrainOne int = iota
TypeCloudBrainTwo
)

type FileChunk struct {
ID int64 `xorm:"pk autoincr"`
UUID string `xorm:"uuid UNIQUE"`
@@ -33,6 +28,23 @@ type FileChunk struct {
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}

type ModelFileChunk struct {
ID int64 `xorm:"pk autoincr"`
UUID string `xorm:"INDEX"`
Md5 string `xorm:"INDEX"`
ModelUUID string `xorm:"INDEX"`
ObjectName string `xorm:"DEFAULT ''"`
IsUploaded int `xorm:"DEFAULT 0"` // not uploaded: 0, uploaded: 1
UploadID string `xorm:"UNIQUE"` //minio upload id
TotalChunks int
Size int64
UserID int64 `xorm:"INDEX"`
Type int `xorm:"INDEX DEFAULT 0"`
CompletedParts []string `xorm:"DEFAULT ''"` // chunkNumber+etag eg: ,1-asqwewqe21312312.2-123hjkas
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}

// GetFileChunkByMD5 returns fileChunk by given id
func GetFileChunkByMD5(md5 string) (*FileChunk, error) {
return getFileChunkByMD5(x, md5)
@@ -54,6 +66,21 @@ func GetFileChunkByMD5AndUser(md5 string, userID int64, typeCloudBrain int) (*Fi
return getFileChunkByMD5AndUser(x, md5, userID, typeCloudBrain)
}

func GetModelFileChunkByMD5AndUser(md5 string, userID int64, typeCloudBrain int, uuid string) (*ModelFileChunk, error) {
return getModelFileChunkByMD5AndUser(x, md5, userID, typeCloudBrain, uuid)
}

func getModelFileChunkByMD5AndUser(e Engine, md5 string, userID int64, typeCloudBrain int, uuid string) (*ModelFileChunk, error) {
fileChunk := new(ModelFileChunk)

if has, err := e.Where("md5 = ? and user_id = ? and type = ? and model_uuid= ?", md5, userID, typeCloudBrain, uuid).Get(fileChunk); err != nil {
return nil, err
} else if !has {
return nil, ErrFileChunkNotExist{md5, ""}
}
return fileChunk, nil
}

func getFileChunkByMD5AndUser(e Engine, md5 string, userID int64, typeCloudBrain int) (*FileChunk, error) {
fileChunk := new(FileChunk)

@@ -81,6 +108,21 @@ func getFileChunkByUUID(e Engine, uuid string) (*FileChunk, error) {
return fileChunk, nil
}

func GetModelFileChunkByUUID(uuid string) (*ModelFileChunk, error) {
return getModelFileChunkByUUID(x, uuid)
}

func getModelFileChunkByUUID(e Engine, uuid string) (*ModelFileChunk, error) {
fileChunk := new(ModelFileChunk)

if has, err := e.Where("uuid = ?", uuid).Get(fileChunk); err != nil {
return nil, err
} else if !has {
return nil, ErrFileChunkNotExist{"", uuid}
}
return fileChunk, nil
}

// InsertFileChunk insert a record into file_chunk.
func InsertFileChunk(fileChunk *FileChunk) (_ *FileChunk, err error) {
if _, err := x.Insert(fileChunk); err != nil {
@@ -90,6 +132,14 @@ func InsertFileChunk(fileChunk *FileChunk) (_ *FileChunk, err error) {
return fileChunk, nil
}

// InsertFileChunk insert a record into file_chunk.
func InsertModelFileChunk(fileChunk *ModelFileChunk) (_ *ModelFileChunk, err error) {
if _, err := x.Insert(fileChunk); err != nil {
return nil, err
}
return fileChunk, nil
}

func DeleteFileChunkById(uuid string) (*FileChunk, error) {
return deleteFileChunkById(x, uuid)
}
@@ -111,6 +161,17 @@ func deleteFileChunkById(e Engine, uuid string) (*FileChunk, error) {
}
}

func UpdateModelFileChunk(fileChunk *ModelFileChunk) error {
return updateModelFileChunk(x, fileChunk)
}

func updateModelFileChunk(e Engine, fileChunk *ModelFileChunk) error {
var sess *xorm.Session
sess = e.Where("uuid = ?", fileChunk.UUID)
_, err := sess.Cols("is_uploaded").Update(fileChunk)
return err
}

// UpdateFileChunk updates the given file_chunk in database
func UpdateFileChunk(fileChunk *FileChunk) error {
return updateFileChunk(x, fileChunk)
@@ -132,3 +193,12 @@ func deleteFileChunk(e Engine, fileChunk *FileChunk) error {
_, err := e.ID(fileChunk.ID).Delete(fileChunk)
return err
}

func DeleteModelFileChunk(fileChunk *ModelFileChunk) error {
return deleteModelFileChunk(x, fileChunk)
}

func deleteModelFileChunk(e Engine, fileChunk *ModelFileChunk) error {
_, err := e.ID(fileChunk.ID).Delete(fileChunk)
return err
}

+ 22
- 0
models/helper.go View File

@@ -11,6 +11,13 @@ func keysInt64(m map[int64]struct{}) []int64 {
}
return keys
}
func keysString(m map[string]struct{}) []string {
var keys = make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
return keys
}

func valuesRepository(m map[int64]*Repository) []*Repository {
var values = make([]*Repository, 0, len(m))
@@ -27,3 +34,18 @@ func valuesUser(m map[int64]*User) []*User {
}
return values
}

func valuesComment(m map[int64]*Comment) []*Comment {
var values = make([]*Comment, 0, len(m))
for _, v := range m {
values = append(values, v)
}
return values
}
func valuesCloudbrain(m map[int64]*Cloudbrain) []*Cloudbrain {
var values = make([]*Cloudbrain, 0, len(m))
for _, v := range m {
values = append(values, v)
}
return values
}

+ 13
- 9
models/helper_environment.go View File

@@ -12,15 +12,19 @@ import (

// env keys for git hooks need
const (
EnvRepoName = "GITEA_REPO_NAME"
EnvRepoUsername = "GITEA_REPO_USER_NAME"
EnvRepoIsWiki = "GITEA_REPO_IS_WIKI"
EnvPusherName = "GITEA_PUSHER_NAME"
EnvPusherEmail = "GITEA_PUSHER_EMAIL"
EnvPusherID = "GITEA_PUSHER_ID"
EnvKeyID = "GITEA_KEY_ID"
EnvIsDeployKey = "GITEA_IS_DEPLOY_KEY"
EnvIsInternal = "GITEA_INTERNAL_PUSH"
EnvRepoName = "GITEA_REPO_NAME"
EnvRepoUsername = "GITEA_REPO_USER_NAME"
EnvRepoIsWiki = "GITEA_REPO_IS_WIKI"
EnvPusherName = "GITEA_PUSHER_NAME"
EnvPusherEmail = "GITEA_PUSHER_EMAIL"
EnvPusherID = "GITEA_PUSHER_ID"
EnvKeyID = "GITEA_KEY_ID"
EnvIsDeployKey = "GITEA_IS_DEPLOY_KEY"
EnvIsInternal = "GITEA_INTERNAL_PUSH"
EnvRepoSize = "REPO_CURRENT_SIZE"
EnvRepoMaxFileSize = "REPO_MAX_FILE_SIZE"
EnvRepoMaxSize = "REPO_MAX_SIZE"
EnvPushSizeCheckFlag = "PUSH_SIZE_CHECK_FLAG"
)

// InternalPushingEnvironment returns an os environment to switch off hooks on push


+ 62
- 1
models/issue.go View File

@@ -69,7 +69,8 @@ type Issue struct {

//block_chain
Amount int64
IsTransformed bool `xorm:"INDEX NOT NULL DEFAULT false"`
IsTransformed bool `xorm:"INDEX NOT NULL DEFAULT false"`
StayTop int64 `xorm:"NOT NULL DEFAULT 0"`
}

var (
@@ -775,6 +776,54 @@ func (issue *Issue) ChangeContent(doer *User, content string) (err error) {
return sess.Commit()
}

func (issue *Issue) ChangeStayTop(doer *User, stayTop int64) (err error) {
issue.StayTop = stayTop
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
if err = updateIssueCols(sess, issue, "stay_top"); err != nil {
return fmt.Errorf("UpdateIssueCols,stayTop: %v", err)
}
return sess.Commit()
}

// ChangeRef changes issue ref, as the given user.
func (issue *Issue) ChangeRef(doer *User, newRef string) (err error) {
oldRef := issue.Ref
issue.Ref = newRef
if oldRef == newRef {
return nil
}

sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}

if err = updateIssueCols(sess, issue, "ref"); err != nil {
sess.Rollback()
return fmt.Errorf("UpdateIssueCols: %v", err)
}

var opts = &CreateCommentOptions{
Type: CommentTypeRef,
Doer: doer,
Repo: issue.Repo,
Issue: issue,
OldRef: oldRef,
NewRef: newRef,
}
if _, err = createComment(sess, opts); err != nil {
sess.Rollback()
return err
}

return sess.Commit()
}

// GetTasks returns the amount of tasks in the issues content
func (issue *Issue) GetTasks() int {
return len(issueTasksPat.FindAllStringIndex(issue.Content, -1))
@@ -1081,6 +1130,7 @@ type IssuesOptions struct {
// sortIssuesSession sort an issues-related session based on the provided
// sortType string
func sortIssuesSession(sess *xorm.Session, sortType string, priorityRepoID int64) {
sess.Desc("issue.stay_top")
switch sortType {
case "oldest":
sess.Asc("issue.created_unix")
@@ -1931,3 +1981,14 @@ func UpdateReactionsMigrationsByType(gitServiceType structs.GitServiceType, orig
})
return err
}

func GetMaxStayTop(repoId int64) int64 {
re := new(Issue)
isExist, err := x.Table(new(Issue)).Where("repo_id="+fmt.Sprint(repoId)).Desc("stay_top").Limit(1, 0).Get(re)
if err == nil {
if isExist {
return re.StayTop + 1
}
}
return 1
}

+ 2
- 0
models/issue_comment.go View File

@@ -90,6 +90,8 @@ const (
CommentTypeReviewRequest
// merge pull request
CommentTypeMergePull
// Ref changed
CommentTypeRef
)

// CommentTag defines comment tag type


+ 184
- 0
models/limit_config.go View File

@@ -0,0 +1,184 @@
package models

import (
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/builder"
)

type LimitType string

const (
LimitTypeTask LimitType = "TASK"
LimitTypeRewardPoint LimitType = "REWARD_POINT"
)

func (l LimitType) Name() string {
switch l {
case LimitTypeTask:
return "TASK"
case LimitTypeRewardPoint:
return "REWARD_POINT"
default:
return ""
}
}

type LimitScope string

const (
LimitScopeAllUsers LimitScope = "ALL_USERS"
LimitScopeSingleUser LimitScope = "SINGLE_USER"
)

func (l LimitScope) Name() string {
switch l {
case LimitScopeAllUsers:
return "ALL_USERS"
case LimitScopeSingleUser:
return "SINGLE_USER"
default:
return ""
}
}

type LimiterRejectPolicy string

const (
JustReject LimiterRejectPolicy = "JUST_REJECT"
PermittedOnce LimiterRejectPolicy = "PERMITTED_ONCE"
FillUp LimiterRejectPolicy = "FillUp"
)

type LimitConfig struct {
ID int64 `xorm:"pk autoincr"`
Title string
RefreshRate string `xorm:"NOT NULL"`
Scope string `xorm:"NOT NULL"`
LimitNum int64 `xorm:"NOT NULL"`
LimitCode string
LimitType string `xorm:"NOT NULL"`
RelatedId int64 `xorm:"INDEX"`
CreatorId int64 `xorm:"NOT NULL"`
CreatorName string
DeleterId int64
DeleterName string
CreatedUnix timeutil.TimeStamp `xorm:"created"`
DeletedAt timeutil.TimeStamp `xorm:"deleted"`
}

type LimitConfigQueryOpts struct {
RefreshRate string
Scope LimitScope
LimitCode string
LimitType LimitType
}

type LimitConfigVO struct {
ID int64
Title string
RefreshRate string
Scope string
LimitNum int64
LimitCode string
LimitType string
Creator string
CreatedUnix timeutil.TimeStamp
}

func (l *LimitConfig) ToLimitConfigVO() *LimitConfigVO {
return &LimitConfigVO{
ID: l.ID,
Title: l.Title,
RefreshRate: l.RefreshRate,
Scope: l.Scope,
LimitNum: l.LimitNum,
LimitCode: l.LimitCode,
LimitType: l.LimitType,
Creator: l.CreatorName,
CreatedUnix: l.CreatedUnix,
}
}

func GetLimitConfigByLimitType(limitType LimitType) ([]LimitConfig, error) {
r := make([]LimitConfig, 0)
err := x.Where(" limit_type = ?", limitType.Name()).Find(&r)
if err != nil {
return nil, err
} else if len(r) == 0 {
return nil, ErrRecordNotExist{}
}
return r, nil
}

func GetLimitersByRelatedIdWithDeleted(limitType LimitType) ([]LimitConfig, error) {
r := make([]LimitConfig, 0)
err := x.Unscoped().Where(" = ?", limitType.Name()).Find(&r)
if err != nil {
return nil, err
} else if len(r) == 0 {
return nil, ErrRecordNotExist{}
}
return r, nil
}

func AddLimitConfig(l *LimitConfig) error {
sess := x.NewSession()
defer sess.Close()

//delete old limit config
cond := builder.NewCond()
cond = cond.And(builder.Eq{"limit_type": l.LimitType})
cond = cond.And(builder.Eq{"scope": l.Scope})
if l.LimitCode == "" {
subCond := builder.NewCond()
subCond = subCond.Or(builder.IsNull{"limit_code"})
subCond = subCond.Or(builder.Eq{"limit_code": ""})
cond = cond.And(subCond)
} else {
cond = cond.And(builder.Eq{"limit_code": l.LimitCode})
}
_, err := sess.Where(cond).Delete(&LimitConfig{})
if err != nil {
sess.Rollback()
return err
}

//add new config
_, err = sess.Insert(l)
if err != nil {
sess.Rollback()
return err
}

sess.Commit()
return nil
}

func DeleteLimitConfig(config LimitConfig, deleterId int64, deleterName string) error {
sess := x.NewSession()
defer sess.Close()

_, err := x.ID(config.ID).Update(&LimitConfig{DeleterName: deleterName, DeleterId: deleterId})
if err != nil {
sess.Rollback()
return err
}
_, err = x.ID(config.ID).Delete(&LimitConfig{})
if err != nil {
sess.Rollback()
return err
}
sess.Commit()
return nil
}

func GetLimitConfigById(id int64) (*LimitConfig, error) {
r := &LimitConfig{}
isOk, err := x.ID(id).Get(r)
if err != nil {
return nil, err
} else if !isOk {
return nil, nil
}
return r, nil
}

+ 20
- 0
models/list_options.go View File

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

type AvailablePageSize int

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

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

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

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


+ 8
- 0
models/login_source.go View File

@@ -766,6 +766,14 @@ func UserSignIn(username, password string) (*User, error) {
if err != nil {
return nil, err
}
//email和用户名方式没找到,用手机号查找
if !hasUser {
user = &User{PhoneNumber: strings.TrimSpace(username)}
hasUser, err = x.Get(user)
if err != nil {
return nil, err
}
}

if hasUser {
switch user.LoginType {


+ 43
- 0
models/models.go View File

@@ -129,13 +129,44 @@ func init() {
new(LanguageStat),
new(EmailHash),
new(Dataset),
new(DatasetStar),
new(Cloudbrain),
new(Image),
new(ImageStar),
new(ImageTopic),
new(ImageTopicRelation),
new(FileChunk),
new(ModelFileChunk),
new(BlockChain),
new(RecommendOrg),
new(AiModelManage),
new(OfficialTag),
new(OfficialTagRepos),
new(WechatBindLog),
new(OrgStatistic),
new(SearchRecord),
new(TaskConfig),
new(TaskAccomplishLog),
new(RewardOperateRecord),
new(LimitConfig),
new(RewardPeriodicTask),
new(PointAccountLog),
new(PointAccount),
new(RewardAdminLog),
new(AiModelConvert),
new(ResourceQueue),
new(ResourceSpecification),
new(ResourceScene),
new(ResourceSceneSpec),
new(AdminOperateLog),
new(CloudbrainSpec),
new(CloudbrainTemp),
new(DatasetReference),
new(ScheduleRecord),
new(BadgeCategory),
new(Badge),
new(BadgeUser),
new(BadgeUserLog),
)

tablesStatistic = append(tablesStatistic,
@@ -143,7 +174,19 @@ func init() {
new(SummaryStatistic),
new(UserBusinessAnalysis),
new(UserBusinessAnalysisAll),
new(UserBusinessAnalysisCurrentYear),
new(UserBusinessAnalysisLast30Day),
new(UserBusinessAnalysisLastMonth),
new(UserBusinessAnalysisCurrentMonth),
new(UserBusinessAnalysisCurrentWeek),
new(UserBusinessAnalysisYesterday),
new(UserBusinessAnalysisLastWeek),
new(UserLoginLog),
new(UserMetrics),
new(UserAnalysisPara),
new(Invitation),
new(CloudbrainDurationStatistic),
new(UserSummaryCurrentYear),
)

gonicNames := []string{"SSL", "UID"}


+ 113
- 2
models/org.go View File

@@ -8,6 +8,7 @@ package models
import (
"fmt"
"os"
"strconv"
"strings"

"code.gitea.io/gitea/modules/log"
@@ -19,6 +20,17 @@ import (
"xorm.io/xorm"
)

type OrgStatistic struct {
ID int64 `xorm:"pk autoincr"`
OrgID int64 `xorm:"UNIQUE"`
NumScore int `xorm:"INDEX NOT NULL DEFAULT 0"`
}

type OrgScore struct {
*User
Score string
}

// IsOwnedBy returns true if given user is in the owner team.
func (org *User) IsOwnedBy(uid int64) (bool, error) {
return IsOrganizationOwner(org.ID, uid)
@@ -135,6 +147,97 @@ func (org *User) RemoveOrgRepo(repoID int64) error {
return org.removeOrgRepo(x, repoID)
}

func UpdateOrgStatistics() {
ids, err := GetOrganizationsId()
if err != nil {
return
}
for _, id := range ids {
org := User{ID: id}
orgStat := &OrgStatistic{OrgID: id}
numScore, err := org.getOrgStatistics()
if err == nil {
has, _ := x.Get(orgStat)

orgStat.NumScore = numScore

count, err := GetPublicRepositoryCount(&org)
if err != nil || count == 0 {
x.ID(orgStat.ID).Delete(new(OrgStatistic))
} else if has {
x.ID(orgStat.ID).Cols("num_score").Update(&orgStat)
} else {
x.Insert(orgStat)
}

}
}

}

func (org *User) getOrgStatistics() (int, error) {
count, err := getRepositoryCount(x, org)
if err != nil {
return 0, err
}

err = org.GetRepositories(ListOptions{int(count), 1})

if err != nil {
return 0, err
}
var numScore = 0
for _, repo := range org.Repos {

numScore += int(getOpenIByRepoId(repo.ID))
}

return numScore, nil

}

func FindTopNStarsOrgs(n int) ([]*OrgScore, error) {
sql := "select a.id,sum(b.num_stars) score from \"user\" a ,repository b where a.id=b.owner_id and a.type=1 and a.visibility=0 group by a.id order by score desc limit " + strconv.Itoa(n)

return findTopNOrgs(sql)
}
func FindTopNMembersOrgs(n int) ([]*OrgScore, error) {
sql := "select id, count(user_id) score from" +
" (select org_id as id, uid as user_id from org_user o, \"user\" u where o.org_id=u.id and u.visibility=0 " +
"union select a.id,b.user_id from \"user\" a,collaboration b,repository c " +
"where a.type=1 and a.visibility=0 and a.id=c.owner_id and b.repo_id=c.id) d " +
"group by id order by score desc limit " + strconv.Itoa(n)

return findTopNOrgs(sql)
}

func FindTopNOpenIOrgs(n int) ([]*OrgScore, error) {
sql := "select org_id id,num_score score from org_statistic a, \"user\" b where a.org_id=b.id and b.visibility=0 order by num_score desc limit " + strconv.Itoa(n)

return findTopNOrgs(sql)
}

func findTopNOrgs(sql string) ([]*OrgScore, error) {
resutls, err := x.QueryString(sql)

if err != nil {
return nil, err
}
var orgScore []*OrgScore
for _, record := range resutls {
id, _ := strconv.ParseInt(record["id"], 10, 64)
user, err := getUserByID(x, id)
if err != nil {
continue
}
orgScore = append(orgScore, &OrgScore{user, record["score"]})

}

return orgScore, nil

}

// CreateOrganization creates record of a new organization.
func CreateOrganization(org, owner *User) (err error) {
if !owner.CanCreateOrganization() {
@@ -180,8 +283,8 @@ func CreateOrganization(org, owner *User) (err error) {

// Add initial creator to organization and owner team.
if _, err = sess.Insert(&OrgUser{
UID: owner.ID,
OrgID: org.ID,
UID: owner.ID,
OrgID: org.ID,
IsPublic: setting.Service.DefaultOrgMemberVisible,
}); err != nil {
return fmt.Errorf("insert org-user relation: %v", err)
@@ -367,6 +470,14 @@ func isOrganizationMember(e Engine, orgID, uid int64) (bool, error) {
Exist()
}

func IsOrganizationMemberByOrgName(orgName string, uid int64) (bool, error) {
org, _ := GetOrgByName(orgName)
if org != nil {
return IsOrganizationMember(org.ID, uid)
}
return false, nil
}

// IsPublicMembership returns true if given user public his/her membership.
func IsPublicMembership(orgID, uid int64) (bool, error) {
return x.


+ 147
- 0
models/point_account.go View File

@@ -0,0 +1,147 @@
package models

import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/timeutil"
)

type PointAccountStatus int

// Possible PointAccountStatus types.
const (
PointAccountNormal int = iota + 1 // 1
PointAccountFreeze // 2
PointAccountDeleted // 3
)

type PointAccount struct {
ID int64 `xorm:"pk autoincr"`
AccountCode string `xorm:"INDEX NOT NULL"`
Balance int64 `xorm:"NOT NULL DEFAULT 0"`
TotalEarned int64 `xorm:"NOT NULL DEFAULT 0"`
TotalConsumed int64 `xorm:"NOT NULL DEFAULT 0"`
UserId int64 `xorm:"INDEX NOT NULL"`
Status int `xorm:"NOT NULL"`
Version int64 `xorm:"NOT NULL"`
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
}

func (account *PointAccount) Increase(amount int64, sourceId string) error {
sess := x.NewSession()
defer sess.Close()
sql := "update point_account set balance = balance + ?,total_earned = total_earned + ? ,version = version + 1 where account_code = ? "
_, err := sess.Exec(sql, amount, amount, account.AccountCode)
if err != nil {
sess.Rollback()
return err
}
accountLog := &PointAccountLog{
AccountCode: account.AccountCode,
UserId: account.UserId,
Type: IncreaseAccountBalance,
SourceId: sourceId,
PointsAmount: amount,
BalanceBefore: account.Balance,
BalanceAfter: account.Balance + amount,
AccountVersion: account.Version,
}
_, err = sess.Insert(accountLog)
if err != nil {
sess.Rollback()
return err
}
sess.Commit()
return nil
}

func (account *PointAccount) Decrease(amount int64, sourceId string) error {
sess := x.NewSession()
defer sess.Close()
sql := "update point_account set balance = balance - ?,total_consumed = total_consumed + ? ,version = version + 1 where account_code = ? "
_, err := sess.Exec(sql, amount, amount, account.AccountCode)
if err != nil {
sess.Rollback()
return err
}
accountLog := &PointAccountLog{
AccountCode: account.AccountCode,
UserId: account.UserId,
Type: DecreaseAccountBalance,
SourceId: sourceId,
PointsAmount: amount,
BalanceBefore: account.Balance,
BalanceAfter: account.Balance - amount,
AccountVersion: account.Version,
}
_, err = sess.Insert(accountLog)
if err != nil {
sess.Rollback()
return err
}
sess.Commit()
return nil
}

func GetAccountByUserId(userId int64) (*PointAccount, error) {
p := &PointAccount{}
has, err := x.Where("user_id = ?", userId).Get(p)
if err != nil {
return nil, err
}
if !has {
return nil, ErrRecordNotExist{}
}
return p, nil
}

func InsertAccount(tl *PointAccount) (int64, error) {
return x.Insert(tl)
}

type SearchPointAccountOpts struct {
ListOptions
Keyword string
}

type SearchPointAccountResponse struct {
Records []*UserPointAccount
PageSize int
Page int
Total int64
}

type UserPointAccount struct {
UserId int64
UserName string
Email string
Balance int64
TotalEarned int64
TotalConsumed int64
}

func (UserPointAccount) TableName() string {
return "user"
}

func GetPointAccountMapByUserIds(userIds []int64) (map[int64]*PointAccount, error) {
if len(userIds) == 0 {
return make(map[int64]*PointAccount, 0), nil
}
accounts := make([]*PointAccount, 0)
err := x.In("user_id", userIds).Find(&accounts)
if err != nil {
log.Error("GetPointAccountMapByUserIds error.%v", err)
return nil, err
}
accountMap := make(map[int64]*PointAccount, 0)
for _, v := range accounts {
accountMap[v.UserId] = v
}
return accountMap, nil
}

type PointDeductCondition struct {
SpecUnitPrice int
WorkServerNumber int
}

+ 21
- 0
models/point_account_log.go View File

@@ -0,0 +1,21 @@
package models

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

const (
IncreaseAccountBalance = "increase"
DecreaseAccountBalance = "decrease"
)

type PointAccountLog struct {
ID int64 `xorm:"pk autoincr"`
AccountCode string `xorm:"INDEX NOT NULL"`
UserId int64 `xorm:"INDEX NOT NULL"`
Type string `xorm:"NOT NULL"`
SourceId string `xorm:"INDEX NOT NULL"`
PointsAmount int64 `xorm:"NOT NULL"`
BalanceBefore int64 `xorm:"NOT NULL"`
BalanceAfter int64 `xorm:"NOT NULL"`
AccountVersion int64 `xorm:"NOT NULL"`
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
}

+ 532
- 51
models/repo.go View File

@@ -11,6 +11,9 @@ import (
"errors"
"fmt"
"html/template"
"math/rand"

"code.gitea.io/gitea/modules/git"
"xorm.io/xorm"

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

// all kinds of RepositoryStatus
const (
@@ -151,6 +155,11 @@ const (
RepoBlockChainFailed
)

const (
RepoNormal RepoType = iota
RepoCourse
)

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

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

IsPrivate bool `xorm:"INDEX"`
IsEmpty bool `xorm:"INDEX"`
@@ -212,16 +223,98 @@ type Repository struct {
BlockChainStatus RepoBlockChainStatus `xorm:"NOT NULL DEFAULT 0"`

// git clone and git pull total count
CloneCnt int64 `xorm:"NOT NULL DEFAULT 0"`
CloneCnt int64 `xorm:"NOT NULL DEFAULT 0" json:"clone_cnt"`

// only git clone total count
GitCloneCnt int64 `xorm:"NOT NULL DEFAULT 0"`
GitCloneCnt int64 `xorm:"NOT NULL DEFAULT 0" json:"git_clone_cnt"`

CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`

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

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

type RepositoryShow struct {
Name string
RepoType RepoType
Alias string
}

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

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

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

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

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

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

// HTMLURL returns the repository HTML URL
func (repo *Repository) HTMLURL() string {
return setting.AppURL + repo.FullName()
@@ -384,7 +490,9 @@ func (repo *Repository) innerAPIFormat(e Engine, mode AccessMode, isParent bool)
ID: repo.ID,
Owner: repo.Owner.APIFormat(),
Name: repo.Name,
Alias: repo.Alias,
FullName: repo.FullName(),
FullDisplayName: repo.FullDisplayName(),
Description: repo.Description,
Private: repo.IsPrivate,
Template: repo.IsTemplate,
@@ -420,6 +528,7 @@ func (repo *Repository) innerAPIFormat(e Engine, mode AccessMode, isParent bool)
AllowRebaseMerge: allowRebaseMerge,
AllowSquash: allowSquash,
AvatarURL: repo.avatarLink(e),
Status: int(repo.Status),
}
}

@@ -547,6 +656,19 @@ func (repo *Repository) GetOwner() error {
return repo.getOwner(x)
}

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

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

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

func (repo *Repository) mustOwner(e Engine) *User {
if err := repo.getOwner(e); err != nil {
return &User{
@@ -632,7 +754,7 @@ func (repo *Repository) getAssignees(e Engine) (_ []*User, err error) {
userIDs[i] = accesses[i].UserID
}

if err = e.In("id", userIDs).Find(&users); err != nil {
if err = e.In("id", userIDs).OrderBy("name asc").Find(&users); err != nil {
return nil, err
}
}
@@ -920,17 +1042,50 @@ func (repo *Repository) DescriptionHTML() template.HTML {
return template.HTML(markup.Sanitize(string(desc)))
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

} else if tp == UnitTypeBlockChain {
units = append(units, RepoUnit{
RepoID: repo.ID,
@@ -1108,11 +1293,13 @@ func CreateRepository(ctx DBContext, doer, u *User, repo *Repository) (err error
Config: &BlockChainConfig{EnableBlockChain: true},
})
} else if tp == UnitTypeModelManage {
units = append(units, RepoUnit{
RepoID: repo.ID,
Type: tp,
Config: &ModelManageConfig{EnableModelManage: true},
})
if !isCourse {
units = append(units, RepoUnit{
RepoID: repo.ID,
Type: tp,
Config: &ModelManageConfig{EnableModelManage: true},
})
}
} else {
units = append(units, RepoUnit{
RepoID: repo.ID,
@@ -1166,7 +1353,7 @@ func CreateRepository(ctx DBContext, doer, u *User, repo *Repository) (err error
}

if setting.Service.AutoWatchNewRepos {
if err = watchRepo(ctx.e, doer.ID, repo.ID, true); err != nil {
if err = watchRepo(ctx.e, doer.ID, repo.ID, true, ReceiveAllNotification); err != nil {
return fmt.Errorf("watchRepo: %v", err)
}
}
@@ -1175,13 +1362,17 @@ func CreateRepository(ctx DBContext, doer, u *User, repo *Repository) (err error
return fmt.Errorf("copyDefaultWebhooksToRepo: %v", err)
}

if err = CreateDefaultDatasetToRepo(repo); err != nil {
return fmt.Errorf("models.CreateDefaultDatasetToRepo: %v", err)
}

return nil
}

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

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

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

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

has, err := IsRepositoryExist(repo.Owner, newRepoName)
has, err := IsRepositoryExist(repo.Owner, newRepoName, "")
if err != nil {
return fmt.Errorf("IsRepositoryExist: %v", err)
} else if has {
@@ -1434,6 +1625,26 @@ func GetAllRepositoriesCount() (int64, error) {
return x.Count(repo)
}

func GetAllPublicRepositoriesCount() (int64, error) {
repo := new(Repository)
return x.Where("is_private = ?", false).Count(repo)
}

func GetAllMirrorRepositoriesCount() (int64, error) {
repo := new(Repository)
return x.Where("is_mirror = ?", true).Count(repo)
}

func GetAllOrgRepositoriesCount() (int64, error) {
repo := new(Repository)
return x.Table("repository").Join("INNER", []string{"\"user\"", "u"}, "repository.owner_id = u.id and u.type=1").Count(repo)
}

func GetAllForkRepositoriesCount() (int64, error) {
repo := new(Repository)
return x.Where("is_fork = ?", true).Count(repo)
}

func GetAllRepositoriesSize() (int64, error) {
return x.SumInt(&Repository{}, "size")
}
@@ -1473,6 +1684,37 @@ func updateRepository(e Engine, repo *Repository, visibilityChanged bool) (err e
if err != nil {
return err
}

dataset, err := GetDatasetByRepo(repo)
if err != nil && !IsErrNotExist(err) {
return err
}
if dataset != nil {
_, err = e.Where("dataset_id = ?", dataset.ID).Cols("is_private").Update(&Attachment{
IsPrivate: true,
})
if err != nil {
return err
}
}

//If repo has become private, we need set dataset and dataset_file to private
_, err = e.Where("repo_id = ? and status <> 2", repo.ID).Cols("status").Update(&Dataset{
Status: 0,
})
if err != nil {
return err
}

} else {
//If repo has become public, we need set dataset to public
_, err = e.Where("repo_id = ? and status <> 2", repo.ID).Cols("status").Update(&Dataset{
Status: 1,
})
if err != nil {
return err
}

}

// Create/Remove git-daemon-export-ok for git-daemon...
@@ -1627,7 +1869,6 @@ func DeleteRepository(doer *User, uid, repoID int64) error {

// Delete dataset attachment record and remove related files
deleteDatasetAttachmentByRepoId(sess, repoID)

if err = deleteBeans(sess,
&Access{RepoID: repo.ID},
&Action{RepoID: repo.ID},
@@ -1847,6 +2088,26 @@ func getRepositoryByOwnerAndName(e Engine, ownerName, repoName string) (*Reposit
return &repo, nil
}

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

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

// GetRepositoryByName returns the repository by given name under user if exists.
func GetRepositoryByName(ownerID int64, name string) (*Repository, error) {
repo := &Repository{
@@ -2069,6 +2330,18 @@ func CheckRepoStats(ctx context.Context) error {
"UPDATE `repository` SET num_stars=(SELECT COUNT(*) FROM `star` WHERE repo_id=?) WHERE id=?",
"repository count 'num_stars'",
},
//Repository.NumIssues
{
"SELECT repo.id FROM `repository` repo WHERE repo.num_issues!=(SELECT COUNT(*) FROM `issue` WHERE repo_id=repo.id AND is_pull=false)",
"UPDATE `repository` SET num_issues=(SELECT COUNT(*) FROM `issue` WHERE repo_id=? AND is_pull=false) WHERE id=?",
"repository count 'num_issues'",
},
//Repository.NumPulls
{
"SELECT repo.id FROM `repository` repo WHERE repo.num_pulls!=(SELECT COUNT(*) FROM `issue` WHERE repo_id=repo.id AND is_pull=true)",
"UPDATE `repository` SET num_pulls=(SELECT COUNT(*) FROM `issue` WHERE repo_id=? AND is_pull=true) WHERE id=?",
"repository count 'num_pulls'",
},
// Label.NumIssues
{
"SELECT label.id FROM `label` WHERE label.num_issues!=(SELECT COUNT(*) FROM `issue_label` WHERE label_id=label.id)",
@@ -2180,6 +2453,75 @@ func CheckRepoStats(ctx context.Context) error {
}
}
// ***** END: Repository.NumForks *****

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

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

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

@@ -2519,3 +2861,142 @@ func UpdateRepositoryCommitNum(repo *Repository) error {

return nil
}

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

type RepoFile struct {
CommitId string
Content []byte
}

// ReadLatestFileInRepo read latest version of file in repository
// return a RepoFile
func ReadLatestFileInRepo(userName, repoName, refName, treePath string) (*RepoFile, error) {
var err error
repoPath := RepoPath(userName, repoName)
gitRepo, err := git.OpenRepository(repoPath)
if err != nil {
log.Error("ReadLatestFileInRepo error when OpenRepository,error=%v", err)
return nil, err
}
_, err = gitRepo.GetBranchCommitID(refName)
if err != nil {
log.Error("ReadLatestFileInRepo error when GetBranchCommitID,error=%v", err)
return nil, err
}
commit, err := gitRepo.GetBranchCommit(refName)
if err != nil {
log.Error("ReadLatestFileInRepo error when GetBranchCommit,error=%v", err)
return nil, err
}

blob, err := commit.GetBlobByPath(treePath)
if err != nil {
log.Error("ReadLatestFileInRepo error when GetBlobByPath,error=%v", err)
return nil, err
}

reader, err := blob.DataAsync()
if err != nil {
return nil, err
}
defer func() {
if err = reader.Close(); err != nil {
log.Error("ReadLatestFileInRepo: Close: %v", err)
}
}()
d, _ := ioutil.ReadAll(reader)
commitId := ""
if blob != nil {
commitId = fmt.Sprint(blob.ID)
}
return &RepoFile{CommitId: commitId, Content: d}, nil
}

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

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

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

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

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

return err
}

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

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

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

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

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

+ 46
- 3
models/repo_activity_custom.go View File

@@ -211,20 +211,63 @@ func setKeyContributerDict(contributorDistinctDict map[string]int, email string,
}
}

func GetAllUserKPIStats() (map[string]*git.UserKPIStats, error) {
func GetAllUserPublicRepoKPIStats(startTime time.Time, endTime time.Time) (map[string]*git.UserKPIStats, error) {
authors := make(map[string]*git.UserKPIStats)
repositorys, err := GetAllRepositoriesByFilterCols("owner_name", "name")
repositorys, err := GetAllRepositoriesByFilterCols("owner_name", "name", "is_private")
if err != nil {
return nil, err
}

for _, repository := range repositorys {
authorsOneRepo, err1 := git.GetUserKPIStats(repository.RepoPath())
if repository.IsPrivate {
continue
}
authorsOneRepo, err1 := git.GetUserKPIStats(repository.RepoPath(), startTime, endTime)
if err1 != nil {
log.Warn("get user kpi status err:"+repository.RepoPath(), err1.Error())
continue
}

for key, value := range authorsOneRepo {
if _, ok := authors[key]; !ok {
authors[key] = &git.UserKPIStats{

Name: value.Name,
Email: value.Email,
Commits: 0,
CommitLines: 0,
}
}
if value.Email == "1250125907@qq.com" || value.Email == "peiyongyu-34@163.com" {
log.Info("repo path=" + repository.RepoPath())
}
authors[key].Commits += value.Commits
authors[key].CommitLines += value.CommitLines

}

}
return authors, nil
}

func GetAllUserKPIStats(startTime time.Time, endTime time.Time) (map[string]*git.UserKPIStats, error) {
authors := make(map[string]*git.UserKPIStats)
repositorys, err := GetAllRepositoriesByFilterCols("owner_name", "name")
if err != nil {
return nil, err
}

for _, repository := range repositorys {
authorsOneRepo, err1 := git.GetUserKPIStats(repository.RepoPath(), startTime, endTime)
if err1 != nil {
log.Warn("get user kpi status err:"+repository.RepoPath(), err1.Error())
continue
}
// if repository.Name == "yolov5" {
// log.Info("repoName=" + repository.Name + " owner=" + repository.RepoPath())
// authorsOneRepoJson, _ := json.Marshal(authorsOneRepo)
// log.Info("authorsOneRepoJson=" + string(authorsOneRepoJson))
// }
for key, value := range authorsOneRepo {
if _, ok := authors[key]; !ok {
authors[key] = &git.UserKPIStats{


+ 14
- 0
models/repo_collaboration.go View File

@@ -130,6 +130,20 @@ func (repo *Repository) IsCollaborator(userID int64) (bool, error) {
return repo.isCollaborator(x, userID)
}

func (repo *Repository) IsInRepoTeam(userID int64) (bool, error) {
teams,err:=repo.GetRepoTeams()
if err!=nil || len(teams)==0{
return false,err
}
for _,team :=range teams{
if team.IsMember(userID){
return true,nil
}
}
return false,nil

}

func (repo *Repository) changeCollaborationAccessMode(e Engine, uid int64, mode AccessMode) error {
// Discard invalid input
if mode <= AccessModeNone || mode > AccessModeOwner {


+ 1
- 0
models/repo_generate.go View File

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


+ 60
- 19
models/repo_list.go View File

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

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

}

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

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

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

//SearchOrderBy is used to sort the result
@@ -183,31 +201,49 @@ func (s SearchOrderBy) String() string {
return string(s)
}

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

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

// SearchRepositoryCondition creates a query condition according search repository options
func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
var cond = builder.NewCond()

if opts.OnlySearchPrivate {
cond = cond.And(builder.Eq{"is_private": true})
}
if opts.Private {
if opts.Actor != nil && !opts.Actor.IsAdmin && opts.Actor.ID != opts.OwnerID {
// OK we're in the context of a User
@@ -320,7 +356,8 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
if !opts.TopicOnly {
var likes = builder.NewCond()
for _, v := range strings.Split(opts.Keyword, ",") {
likes = likes.Or(builder.Like{"lower_name", strings.ToLower(v)})
likes = likes.Or(builder.Like{"lower_alias", strings.ToLower(v)})
likes = likes.Or(builder.Like{"alias", v})
if opts.IncludeDescription {
likes = likes.Or(builder.Like{"LOWER(description)", strings.ToLower(v)})
}
@@ -350,6 +387,10 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
cond = cond.And(builder.Eq{"is_mirror": opts.Mirror == util.OptionalBoolTrue})
}

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

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


+ 85
- 36
models/repo_statistic.go View File

@@ -9,42 +9,45 @@ import (

// RepoStatistic statistic info of all repository
type RepoStatistic struct {
ID int64 `xorm:"pk autoincr" json:"-"`
RepoID int64 `xorm:"unique(s) NOT NULL" json:"repo_id"`
Name string `xorm:"INDEX" json:"name"`
OwnerName string `json:"ownerName"`
IsPrivate bool `json:"isPrivate"`
IsMirror bool `json:"isMirror"`
Date string `xorm:"unique(s) NOT NULL" json:"date"`
NumWatches int64 `xorm:"NOT NULL DEFAULT 0" json:"watch"`
NumWatchesAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
NumStars int64 `xorm:"NOT NULL DEFAULT 0" json:"star"`
NumStarsAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
NumForks int64 `xorm:"NOT NULL DEFAULT 0" json:"fork"`
NumForksAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
NumDownloads int64 `xorm:"NOT NULL DEFAULT 0" json:"download"`
NumDownloadsAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
NumComments int64 `xorm:"NOT NULL DEFAULT 0" json:"comment"`
NumCommentsAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
NumVisits int64 `xorm:"NOT NULL DEFAULT 0" json:"view"`
NumClosedIssues int64 `xorm:"NOT NULL DEFAULT 0" json:"issueClosed"`
NumClosedIssuesAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
NumVersions int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
NumDevMonths int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
RepoSize int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
DatasetSize int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
NumModels int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
NumWikiViews int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
NumCommits int64 `xorm:"NOT NULL DEFAULT 0" json:"commit"`
NumCommitsAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
NumIssues int64 `xorm:"NOT NULL DEFAULT 0" json:"issue"`
NumIssuesAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
NumPulls int64 `xorm:"NOT NULL DEFAULT 0" json:"pr"`
NumPullsAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
IssueFixedRate float32 `xorm:"NOT NULL" json:"issueClosedRatio"`
NumContributor int64 `xorm:"NOT NULL DEFAULT 0" json:"contributor"`
NumContributorAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
NumKeyContributor int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
ID int64 `xorm:"pk autoincr" json:"-"`
RepoID int64 `xorm:"unique(s) NOT NULL" json:"repo_id"`
Name string `xorm:"INDEX" json:"name"`
Alias string `xorm:"INDEX" json:"alias"`
OwnerName string `json:"ownerName"`
IsPrivate bool `json:"isPrivate"`
IsMirror bool `json:"isMirror"`
IsFork bool `json:"isFork"`
RepoCreatedUnix timeutil.TimeStamp `xorm:"NOT NULL DEFAULT 0" json:"createUnix"`
Date string `xorm:"unique(s) NOT NULL" json:"date"`
NumWatches int64 `xorm:"NOT NULL DEFAULT 0" json:"watch"`
NumWatchesAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
NumStars int64 `xorm:"NOT NULL DEFAULT 0" json:"star"`
NumStarsAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
NumForks int64 `xorm:"NOT NULL DEFAULT 0" json:"fork"`
NumForksAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
NumDownloads int64 `xorm:"NOT NULL DEFAULT 0" json:"download"`
NumDownloadsAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
NumComments int64 `xorm:"NOT NULL DEFAULT 0" json:"comment"`
NumCommentsAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
NumVisits int64 `xorm:"NOT NULL DEFAULT 0" json:"view"`
NumClosedIssues int64 `xorm:"NOT NULL DEFAULT 0" json:"issueClosed"`
NumClosedIssuesAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
NumVersions int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
NumDevMonths int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
RepoSize int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
DatasetSize int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
NumModels int64 `xorm:"NOT NULL DEFAULT 0" json:"model"`
NumWikiViews int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
NumCommits int64 `xorm:"NOT NULL DEFAULT 0" json:"commit"`
NumCommitsAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
NumIssues int64 `xorm:"NOT NULL DEFAULT 0" json:"issue"`
NumIssuesAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
NumPulls int64 `xorm:"NOT NULL DEFAULT 0" json:"pr"`
NumPullsAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
IssueFixedRate float32 `xorm:"NOT NULL" json:"issueClosedRatio"`
NumContributor int64 `xorm:"NOT NULL DEFAULT 0" json:"contributor"`
NumContributorAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
NumKeyContributor int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`

NumContributorsGrowth int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
NumCommitsGrowth int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
@@ -52,6 +55,15 @@ type RepoStatistic struct {
NumIssuesGrowth int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
NumCommentsGrowth int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`

NumDatasetFile int64 `xorm:"NOT NULL DEFAULT 0" json:"datasetFiles"`
NumCloudbrain int64 `xorm:"NOT NULL DEFAULT 0" json:"cloudbrains"`
NumModelConvert int64 `xorm:"NOT NULL DEFAULT 0" json:"modelConverts"`

NumDatasetFileAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
NumCloudbrainAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"-"`
NumModelConvertAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"- "`
NumModelsAdded int64 `xorm:"NOT NULL DEFAULT 0" json:"- "`

Impact float64 `xorm:"NOT NULL DEFAULT 0" json:"impact"`
Completeness float64 `xorm:"NOT NULL DEFAULT 0" json:"completeness"`
Liveness float64 `xorm:"NOT NULL DEFAULT 0" json:"liveness"`
@@ -63,6 +75,23 @@ type RepoStatistic struct {
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated" json:"-"`
}

func (repo *RepoStatistic) DisplayName() string {
if repo.Alias == "" {
return repo.Name
}
return repo.Alias
}

func getOpenIByRepoId(repoId int64) float64 {
repoStatistic := new(RepoStatistic)
has, err := xStatistic.Cols("radar_total").Where("repo_id=?", repoId).Desc("id").Limit(1).Get(repoStatistic)
if !has || err != nil {
return 0
}
return repoStatistic.RadarTotal

}

func DeleteRepoStatDaily(date string) error {
sess := xStatistic.NewSession()
defer sess.Close()
@@ -180,3 +209,23 @@ func UpdateRepoStatVisits(repoStat *RepoStatistic) error {
_, err := xStatistic.Exec(sql, repoStat.NumVisits, repoStat.RepoID, repoStat.Date)
return err
}

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

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

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

+ 41
- 5
models/repo_tag.go View File

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

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

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

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

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

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

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

if err := x.SQL(SQLCmd, orgID, tagId).Find(&t); err != nil {
@@ -161,3 +167,33 @@ func GetAllOfficialTags() ([]OfficialTag, error) {
}
return o, nil
}

type FindSelectedReposOpts struct {
ListOptions
OrgId int64
OnlyPublic bool
}

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

if err != nil {
return nil, err
}

return t, nil
}

+ 57
- 7
models/repo_watch.go View File

@@ -24,7 +24,16 @@ const (
RepoWatchModeAuto // 3
)

// NotifyType specifies what kind of watch the user has on a repository
type NotifyType int8

const (
RejectAllNotification NotifyType = 0
ReceiveAllNotification NotifyType = 9
)

var ActionChan = make(chan *Action, 200)
var ActionChan4Task = make(chan Action, 200)

// Watch is connection request for receiving repository notification.
type Watch struct {
@@ -33,6 +42,7 @@ type Watch struct {
RepoID int64 `xorm:"UNIQUE(watch)"`
Mode RepoWatchMode `xorm:"SMALLINT NOT NULL DEFAULT 1"`
CreatedUnix int64 `xorm:"created"`
NotifyType NotifyType `xorm:"SMALLINT NOT NULL DEFAULT 0"`
}

// getWatch gets what kind of subscription a user has on a given repository; returns dummy record if none found
@@ -59,8 +69,20 @@ func IsWatching(userID, repoID int64) bool {
return err == nil && isWatchMode(watch.Mode)
}

// GetWatchNotifyType
func GetWatchNotifyType(userID, repoID int64) NotifyType {
watch, err := getWatch(x, userID, repoID)
if err != nil {
return RejectAllNotification
}
return watch.NotifyType
}

func watchRepoMode(e Engine, watch Watch, mode RepoWatchMode) (err error) {
if watch.Mode == mode {
if _, err := e.ID(watch.ID).Cols("notify_type").Update(watch); err != nil {
return err
}
return nil
}
if mode == RepoWatchModeAuto && (watch.Mode == RepoWatchModeDont || isWatchMode(watch.Mode)) {
@@ -108,7 +130,7 @@ func WatchRepoMode(userID, repoID int64, mode RepoWatchMode) (err error) {
return watchRepoMode(x, watch, mode)
}

func watchRepo(e Engine, userID, repoID int64, doWatch bool) (err error) {
func watchRepo(e Engine, userID, repoID int64, doWatch bool, notifyTypes ...NotifyType) (err error) {
var watch Watch
if watch, err = getWatch(e, userID, repoID); err != nil {
return err
@@ -118,14 +140,19 @@ func watchRepo(e Engine, userID, repoID int64, doWatch bool) (err error) {
} else if !doWatch {
err = watchRepoMode(e, watch, RepoWatchModeNone)
} else {
notifyType := RejectAllNotification
if len(notifyTypes) > 0 {
notifyType = notifyTypes[0]
}
watch.NotifyType = notifyType
err = watchRepoMode(e, watch, RepoWatchModeNormal)
}
return err
}

// WatchRepo watch or unwatch repository.
func WatchRepo(userID, repoID int64, watch bool) (err error) {
return watchRepo(x, userID, repoID, watch)
func WatchRepo(userID, repoID int64, watch bool, notifyType ...NotifyType) (err error) {
return watchRepo(x, userID, repoID, watch, notifyType...)
}

func getWatchers(e Engine, repoID int64) ([]*Watch, error) {
@@ -155,6 +182,7 @@ func getRepoWatchersIDs(e Engine, repoID int64) ([]int64, error) {
return ids, e.Table("watch").
Where("watch.repo_id=?", repoID).
And("watch.mode<>?", RepoWatchModeDont).
And("watch.notify_type > ?", RejectAllNotification).
Select("user_id").
Find(&ids)
}
@@ -182,6 +210,7 @@ func notifyWatchers(e Engine, actions ...*Action) error {
var permCode []bool
var permIssue []bool
var permPR []bool
var permDataset []bool

for _, act := range actions {
repoChanged := repo == nil || repo.ID != act.RepoID
@@ -199,6 +228,14 @@ func notifyWatchers(e Engine, actions ...*Action) error {
if _, err = e.InsertOne(act); err != nil {
return fmt.Errorf("insert new actioner: %v", err)
}
// After InsertOne(act),the act has ID
// Send the act to task chan
ActionChan4Task <- *act

// If it has nothing to do with repo, return directly
if act.Repo == nil && act.RepoID == 0 {
return nil
}

if repoChanged {
act.loadRepo()
@@ -225,12 +262,14 @@ func notifyWatchers(e Engine, actions ...*Action) error {
permCode = make([]bool, len(watchers))
permIssue = make([]bool, len(watchers))
permPR = make([]bool, len(watchers))
permDataset = make([]bool, len(watchers))
for i, watcher := range watchers {
user, err := getUserByID(e, watcher.UserID)
if err != nil {
permCode[i] = false
permIssue[i] = false
permPR[i] = false
permDataset[i] = false
continue
}
perm, err := getUserRepoPermission(e, repo, user)
@@ -238,11 +277,13 @@ func notifyWatchers(e Engine, actions ...*Action) error {
permCode[i] = false
permIssue[i] = false
permPR[i] = false
permDataset[i] = false
continue
}
permCode[i] = perm.CanRead(UnitTypeCode)
permIssue[i] = perm.CanRead(UnitTypeIssues)
permPR[i] = perm.CanRead(UnitTypePullRequests)
permDataset[i] = perm.CanRead(UnitTypeDatasets)
}
}

@@ -267,6 +308,10 @@ func notifyWatchers(e Engine, actions ...*Action) error {
if !permPR[i] {
continue
}
case ActionDatasetRecommended:
if !permDataset[i] {
continue
}
}

if _, err = e.InsertOne(act); err != nil {
@@ -279,14 +324,16 @@ func notifyWatchers(e Engine, actions ...*Action) error {

// NotifyWatchers creates batch of actions for every watcher.
func NotifyWatchers(actions ...*Action) error {
error := notifyWatchers(x, actions...)
producer(actions...)
return notifyWatchers(x, actions...)
return error
}

func producer(actions ...*Action) {
for _, action := range actions {
ActionChan <- action
if !action.IsPrivate {
ActionChan <- action
}
}
}

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

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

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


+ 351
- 0
models/resource_queue.go View File

@@ -0,0 +1,351 @@
package models

import (
"code.gitea.io/gitea/modules/timeutil"
"errors"
"strconv"
"strings"
"xorm.io/builder"
)

type ResourceQueue struct {
ID int64 `xorm:"pk autoincr"`
QueueCode string
Cluster string `xorm:"notnull"`
AiCenterCode string
AiCenterName string
ComputeResource string
AccCardType string
CardsTotalNum int
IsAutomaticSync bool
Remark string
DeletedTime timeutil.TimeStamp `xorm:"deleted"`
CreatedTime timeutil.TimeStamp `xorm:"created"`
CreatedBy int64
UpdatedTime timeutil.TimeStamp `xorm:"updated"`
UpdatedBy int64
}

func (r ResourceQueue) ConvertToRes() *ResourceQueueRes {
return &ResourceQueueRes{
ID: r.ID,
QueueCode: r.QueueCode,
Cluster: r.Cluster,
AiCenterCode: r.AiCenterCode,
AiCenterName: r.AiCenterName,
ComputeResource: r.ComputeResource,
AccCardType: r.AccCardType,
CardsTotalNum: r.CardsTotalNum,
UpdatedTime: r.UpdatedTime,
Remark: r.Remark,
}
}

type ResourceQueueReq struct {
QueueCode string
Cluster string `binding:"Required"`
AiCenterCode string
ComputeResource string `binding:"Required"`
AccCardType string `binding:"Required"`
CardsTotalNum int
CreatorId int64
IsAutomaticSync bool
Remark string
}

func (r ResourceQueueReq) ToDTO() ResourceQueue {
q := ResourceQueue{
QueueCode: r.QueueCode,
Cluster: r.Cluster,
AiCenterCode: r.AiCenterCode,
ComputeResource: strings.ToUpper(r.ComputeResource),
AccCardType: strings.ToUpper(r.AccCardType),
CardsTotalNum: r.CardsTotalNum,
IsAutomaticSync: r.IsAutomaticSync,
Remark: r.Remark,
CreatedBy: r.CreatorId,
UpdatedBy: r.CreatorId,
}
if r.Cluster == OpenICluster {
if r.AiCenterCode == AICenterOfCloudBrainOne {
q.AiCenterName = "云脑一"
} else if r.AiCenterCode == AICenterOfCloudBrainTwo {
q.AiCenterName = "云脑二"
} else if r.AiCenterCode == AICenterOfChengdu {
q.AiCenterName = "启智成都智算"
}
}
return q
}

type SearchResourceQueueOptions struct {
ListOptions
Cluster string
AiCenterCode string
ComputeResource string
AccCardType string
}

type ResourceQueueListRes struct {
TotalSize int64
List []*ResourceQueueRes
}

type ResourceQueueCodesRes struct {
ID int64
QueueCode string
Cluster string
AiCenterCode string
AiCenterName string
}

func (ResourceQueueCodesRes) TableName() string {
return "resource_queue"
}

type ResourceAiCenterRes struct {
AiCenterCode string
AiCenterName string
}

type GetQueueCodesOptions struct {
Cluster string
}

func NewResourceQueueListRes(totalSize int64, list []ResourceQueue) *ResourceQueueListRes {
resList := make([]*ResourceQueueRes, len(list))
for i, v := range list {
resList[i] = v.ConvertToRes()
}
return &ResourceQueueListRes{
TotalSize: totalSize,
List: resList,
}
}

type ResourceQueueRes struct {
ID int64
QueueCode string
Cluster string
AiCenterCode string
AiCenterName string
ComputeResource string
AccCardType string
CardsTotalNum int
UpdatedTime timeutil.TimeStamp
Remark string
}

func InsertResourceQueue(queue ResourceQueue) (int64, error) {
return x.Insert(&queue)
}

func UpdateResourceQueueById(queueId int64, queue ResourceQueue) (int64, error) {
return x.ID(queueId).Update(&queue)
}
func UpdateResourceCardsTotalNum(queueId int64, queue ResourceQueue) (int64, error) {
return x.ID(queueId).Cols("cards_total_num", "remark").Update(&queue)
}

func SearchResourceQueue(opts SearchResourceQueueOptions) (int64, []ResourceQueue, error) {
var cond = builder.NewCond()
if opts.Page <= 0 {
opts.Page = 1
}
if opts.Cluster != "" {
cond = cond.And(builder.Eq{"cluster": opts.Cluster})
}
if opts.AiCenterCode != "" {
cond = cond.And(builder.Eq{"ai_center_code": opts.AiCenterCode})
}
if opts.ComputeResource != "" {
cond = cond.And(builder.Eq{"compute_resource": opts.ComputeResource})
}
if opts.AccCardType != "" {
cond = cond.And(builder.Eq{"acc_card_type": opts.AccCardType})
}
n, err := x.Where(cond).Unscoped().Count(&ResourceQueue{})
if err != nil {
return 0, nil, err
}

r := make([]ResourceQueue, 0)
err = x.Where(cond).Desc("id").Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).Unscoped().Find(&r)
if err != nil {
return 0, nil, err
}
return n, r, nil
}

func GetResourceQueueCodes(opts GetQueueCodesOptions) ([]*ResourceQueueCodesRes, error) {
cond := builder.NewCond()
if opts.Cluster != "" {
cond = cond.And(builder.Eq{"cluster": opts.Cluster})
}
cond = cond.And(builder.Or(builder.IsNull{"deleted_time"}, builder.Eq{"deleted_time": 0}))
r := make([]*ResourceQueueCodesRes, 0)
err := x.Where(cond).OrderBy("cluster desc,ai_center_code asc").Find(&r)
if err != nil {
return nil, err
}
return r, nil
}

func GetResourceQueue(r *ResourceQueue) (*ResourceQueue, error) {
has, err := x.Get(r)
if err != nil {
return nil, err
} else if !has {
return nil, nil
}
return r, nil
}

func ParseComputeResourceFormGrampus(grampusDeviceKind string) string {
t := strings.Split(grampusDeviceKind, "/")
if len(t) < 2 {
return ""
}
return strings.ToUpper(t[1])
}

type MemSize struct {
Sizes []string
Hex int
}

var memSize = MemSize{Sizes: []string{"K", "M", "G", "T", "P", "E"}, Hex: 1000}
var iMemSize = MemSize{Sizes: []string{"Ki", "Mi", "Gi", "Ti", "Pi", "Ei"}, Hex: 1024}

func MatchMemSize(memSize MemSize, val string) (int, float32, error) {
for i, v := range memSize.Sizes {
if strings.HasSuffix(val, v) {
s := strings.TrimSuffix(val, v)
f, err := strconv.ParseFloat(s, 32)
if err != nil {
return 0, 0, err
}
return i, float32(f), nil
}
}
return -1, 0, nil
}

//TransferMemSize transfer oldValue format from old index to new index
//eg: memSize.Sizes = []string{"M", "G", "T", "P", "E"}, oldValue = 10 , oldIndex = 1 , newIndex = 0. it means transfer 10G to 10000M
//so it returns 10000
func TransferMemSize(memSize MemSize, oldValue float32, oldIndex int, newIndex int) float32 {
diff := oldIndex - newIndex
r := oldValue
if diff > 0 {
r = oldValue * float32(diff) * float32(memSize.Hex)
} else if diff < 0 {
r = oldValue / float32(-1*diff) / float32(memSize.Hex)
}
return r
}

//ParseMemSize find the memSize which matches value's format,and parse the number from value
func ParseMemSize(value string, memSize MemSize, newIndex int) (bool, float32, error) {
index, r, err := MatchMemSize(memSize, value)
if err != nil {
return false, 0, err
}
if index < 0 {
return false, 0, nil
}
return true, TransferMemSize(memSize, r, index, newIndex), nil
}

func ParseMemSizeFromGrampus(grampusMemSize string) (float32, error) {
if grampusMemSize == "" {
return 0, nil
}
memflag, memResult, err := ParseMemSize(grampusMemSize, memSize, 2)
if err != nil {
return 0, err
}
if memflag {
return memResult, nil
}

iMemFlag, imemResult, err := ParseMemSize(grampusMemSize, iMemSize, 2)
if err != nil {
return 0, err
}
if iMemFlag {
return imemResult, nil
}
return 0, errors.New("grampus memSize format error")
}

func SyncGrampusQueues(updateList []ResourceQueue, insertList []ResourceQueue, existIds []int64) error {
sess := x.NewSession()
var err error
defer func() {
if err != nil {
sess.Rollback()
}
sess.Close()
}()

//delete queues that no longer exists
deleteQueueIds := make([]int64, 0)
queueCond := builder.NewCond()
queueCond = queueCond.And(builder.NotIn("resource_queue.id", existIds)).And(builder.Eq{"resource_queue.cluster": C2NetCluster})
if err := sess.Cols("resource_queue.id").Table("resource_queue").
Where(queueCond).Find(&deleteQueueIds); err != nil {
return err
}

if len(deleteQueueIds) > 0 {
if _, err = sess.In("id", deleteQueueIds).Update(&ResourceQueue{Remark: "自动同步时被下架"}); err != nil {
return err
}
if _, err = sess.In("id", deleteQueueIds).Delete(&ResourceQueue{}); err != nil {
return err
}

//delete specs and scene that no longer exists
deleteSpcIds := make([]int64, 0)
if err := sess.Cols("resource_specification.id").Table("resource_specification").
In("queue_id", deleteQueueIds).Find(&deleteSpcIds); err != nil {
return err
}
if len(deleteSpcIds) > 0 {
if _, err = sess.In("id", deleteSpcIds).Update(&ResourceSpecification{Status: SpecOffShelf}); err != nil {
return err
}
}

}

//update exists specs
if len(updateList) > 0 {
for _, v := range updateList {
if _, err = sess.ID(v.ID).Update(&v); err != nil {
return err
}
}

}

//insert new specs
if len(insertList) > 0 {
if _, err = sess.Insert(insertList); err != nil {
return err
}
}

return sess.Commit()
}

func GetResourceAiCenters() ([]ResourceAiCenterRes, error) {
r := make([]ResourceAiCenterRes, 0)

err := x.SQL("SELECT t.ai_center_code, t.ai_center_name FROM (SELECT DISTINCT ai_center_code, ai_center_name,cluster FROM resource_queue WHERE (deleted_time IS NULL OR deleted_time=0)) t ORDER BY cluster desc,ai_center_code asc").Find(&r)
if err != nil {
return nil, err
}
return r, nil
}

+ 329
- 0
models/resource_scene.go View File

@@ -0,0 +1,329 @@
package models

import (
"code.gitea.io/gitea/modules/timeutil"
"errors"
"xorm.io/builder"
)

const (
Exclusive = iota + 1
NotExclusive
)

type ResourceScene struct {
ID int64 `xorm:"pk autoincr"`
SceneName string
JobType string
IsExclusive bool
ExclusiveOrg string
CreatedTime timeutil.TimeStamp `xorm:"created"`
CreatedBy int64
UpdatedTime timeutil.TimeStamp `xorm:"updated"`
UpdatedBy int64
DeleteTime timeutil.TimeStamp `xorm:"deleted"`
DeletedBy int64
}

type ResourceSceneSpec struct {
ID int64 `xorm:"pk autoincr"`
SceneId int64 `xorm:"unique(idx_scene_spec)"`
SpecId int64 `xorm:"unique(idx_scene_spec)"`
CreatedTime timeutil.TimeStamp `xorm:"created"`
}

type ResourceSceneReq struct {
ID int64
SceneName string
JobType string
IsExclusive bool
ExclusiveOrg string
CreatorId int64
SpecIds []int64
}

type SearchResourceSceneOptions struct {
ListOptions
JobType string
IsExclusive int
AiCenterCode string
QueueId int64
}

type ResourceSceneListRes struct {
TotalSize int64
List []ResourceSceneRes
}

func NewResourceSceneListRes(totalSize int64, list []ResourceSceneRes) *ResourceSceneListRes {
return &ResourceSceneListRes{
TotalSize: totalSize,
List: list,
}
}

type ResourceSceneRes struct {
ID int64
SceneName string
JobType JobType
IsExclusive bool
ExclusiveOrg string
Specs []ResourceSpecWithSceneId
}

func (ResourceSceneRes) TableName() string {
return "resource_scene"
}

type ResourceSceneBriefRes struct {
ID int64
SceneName string
}

func (ResourceSceneBriefRes) TableName() string {
return "resource_scene"
}

type ResourceSpecWithSceneId struct {
ID int64
SourceSpecId string
AccCardsNum int
CpuCores int
MemGiB float32
GPUMemGiB float32
ShareMemGiB float32
UnitPrice int
Status int
UpdatedTime timeutil.TimeStamp
SceneId int64
//queue
Cluster string
AiCenterCode string
AiCenterName string
QueueCode string
QueueId int64
ComputeResource string
AccCardType string
}

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

func InsertResourceScene(r ResourceSceneReq) error {
sess := x.NewSession()
defer sess.Close()

//check
specs := make([]ResourceSpecification, 0)
cond := builder.In("id", r.SpecIds)
if err := sess.Where(cond).Find(&specs); err != nil {
return err
}
if len(specs) < len(r.SpecIds) {
return errors.New("specIds not correct")
}

rs := ResourceScene{
SceneName: r.SceneName,
JobType: r.JobType,
IsExclusive: r.IsExclusive,
ExclusiveOrg: r.ExclusiveOrg,
CreatedBy: r.CreatorId,
UpdatedBy: r.CreatorId,
}
_, err := sess.InsertOne(&rs)
if err != nil {
sess.Rollback()
return err
}

if len(r.SpecIds) == 0 {
return sess.Commit()
}
rss := make([]ResourceSceneSpec, len(r.SpecIds))
for i, v := range r.SpecIds {
rss[i] = ResourceSceneSpec{
SceneId: rs.ID,
SpecId: v,
}
}

_, err = sess.Insert(&rss)
if err != nil {
sess.Rollback()
return err
}

return sess.Commit()
}

func UpdateResourceScene(r ResourceSceneReq) error {
sess := x.NewSession()
var err error
defer func() {
if err != nil {
sess.Rollback()
}
sess.Close()
}()

// find old scene
old := ResourceScene{}
if has, _ := sess.ID(r.ID).Get(&old); !has {
return errors.New("ResourceScene not exist")
}
//check specification
specs := make([]ResourceSpecification, 0)
cond := builder.In("id", r.SpecIds)
if err := sess.Where(cond).Find(&specs); err != nil {
return err
}
if len(specs) < len(r.SpecIds) {
return errors.New("specIds not correct")
}

//update scene
rs := ResourceScene{
SceneName: r.SceneName,
IsExclusive: r.IsExclusive,
ExclusiveOrg: r.ExclusiveOrg,
}
if _, err = sess.ID(r.ID).UseBool("is_exclusive").Update(&rs); err != nil {
return err
}

//delete scene spec relation
if _, err = sess.Where("scene_id = ? ", r.ID).Delete(&ResourceSceneSpec{}); err != nil {
sess.Rollback()
return err
}

if len(r.SpecIds) == 0 {
return sess.Commit()
}
//build new scene spec relation
rss := make([]ResourceSceneSpec, len(r.SpecIds))
for i, v := range r.SpecIds {
rss[i] = ResourceSceneSpec{
SceneId: r.ID,
SpecId: v,
}
}
if _, err = sess.Insert(&rss); err != nil {
sess.Rollback()
return err
}

return sess.Commit()
}

func DeleteResourceScene(sceneId int64) error {
sess := x.NewSession()
var err error
defer func() {
if err != nil {
sess.Rollback()
}
sess.Close()
}()

if _, err = sess.ID(sceneId).Delete(&ResourceScene{}); err != nil {
return err
}
if _, err = sess.Where("scene_id = ? ", sceneId).Delete(&ResourceSceneSpec{}); err != nil {
return err
}
return sess.Commit()
}

func SearchResourceScene(opts SearchResourceSceneOptions) (int64, []ResourceSceneRes, error) {
var cond = builder.NewCond()
if opts.Page <= 0 {
opts.Page = 1
}
if opts.JobType != "" {
cond = cond.And(builder.Eq{"resource_scene.job_type": opts.JobType})
}
if opts.IsExclusive == Exclusive {
cond = cond.And(builder.Eq{"resource_scene.is_exclusive": 1})
} else if opts.IsExclusive == NotExclusive {
cond = cond.And(builder.Eq{"resource_scene.is_exclusive": 0})
}
if opts.AiCenterCode != "" {
cond = cond.And(builder.Eq{"resource_queue.ai_center_code": opts.AiCenterCode})
}
if opts.QueueId > 0 {
cond = cond.And(builder.Eq{"resource_queue.id": opts.QueueId})
}
cond = cond.And(builder.NewCond().Or(builder.Eq{"resource_scene.delete_time": 0}).Or(builder.IsNull{"resource_scene.delete_time"}))
cols := []string{"resource_scene.id", "resource_scene.scene_name", "resource_scene.job_type", "resource_scene.is_exclusive",
"resource_scene.exclusive_org"}
count, err := x.Where(cond).
Distinct("resource_scene.id").
Join("INNER", "resource_scene_spec", "resource_scene_spec.scene_id = resource_scene.id").
Join("INNER", "resource_specification", "resource_specification.id = resource_scene_spec.spec_id").
Join("INNER", "resource_queue", "resource_queue.id = resource_specification.queue_id").
Count(&ResourceSceneRes{})
if err != nil {
return 0, nil, err
}

r := make([]ResourceSceneRes, 0)
if err = x.Where(cond).Distinct(cols...).
Join("INNER", "resource_scene_spec", "resource_scene_spec.scene_id = resource_scene.id").
Join("INNER", "resource_specification", "resource_specification.id = resource_scene_spec.spec_id").
Join("INNER", "resource_queue", "resource_queue.id = resource_specification.queue_id").
Desc("resource_scene.id").
Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).
Find(&r); err != nil {
return 0, nil, err
}

if len(r) == 0 {
return 0, r, err
}
//find related specs
sceneIds := make([]int64, 0, len(r))
for _, v := range r {
sceneIds = append(sceneIds, v.ID)
}

specs := make([]ResourceSpecWithSceneId, 0)

if err := x.Cols("resource_specification.id", "resource_specification.source_spec_id",
"resource_specification.acc_cards_num", "resource_specification.cpu_cores",
"resource_specification.mem_gi_b", "resource_specification.gpu_mem_gi_b",
"resource_specification.share_mem_gi_b", "resource_specification.unit_price",
"resource_specification.status", "resource_specification.updated_time",
"resource_scene_spec.scene_id", "resource_queue.cluster",
"resource_queue.ai_center_code", "resource_queue.acc_card_type",
"resource_queue.id as queue_id", "resource_queue.compute_resource",
"resource_queue.queue_code", "resource_queue.ai_center_name",
).In("resource_scene_spec.scene_id", sceneIds).
Join("INNER", "resource_scene_spec", "resource_scene_spec.spec_id = resource_specification.id").
Join("INNER", "resource_queue", "resource_queue.ID = resource_specification.queue_id").
OrderBy("resource_specification.acc_cards_num").
Find(&specs); err != nil {
return 0, nil, err
}

specsMap := make(map[int64][]ResourceSpecWithSceneId, 0)
for _, v := range specs {
if _, ok := specsMap[v.SceneId]; !ok {
specsMap[v.SceneId] = []ResourceSpecWithSceneId{v}
} else {
specsMap[v.SceneId] = append(specsMap[v.SceneId], v)
}
}

for i, v := range r {
s := specsMap[v.ID]
if s == nil {
s = make([]ResourceSpecWithSceneId, 0)
}
r[i].Specs = s
}

return count, r, nil
}

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

Loading…
Cancel
Save