#3 develop

Merged
avadesian merged 11037 commits from OpenI/aiforge:develop into develop 2 months ago
  1. +2
    -32
      .github/issue_template.md
  2. +8
    -2
      .gitignore
  3. +4
    -0
      .prettierignore
  4. +1
    -0
      .prettierrc.json
  5. +19
    -4
      Makefile
  6. +229
    -16
      README.md
  7. +13
    -0
      assets/abandon.svg
  8. +11
    -0
      assets/code1.svg
  9. +11
    -0
      assets/code2.svg
  10. +13
    -0
      assets/codeLog.svg
  11. +11
    -0
      assets/drag.svg
  12. BIN
      assets/drag2.png
  13. +9
    -0
      assets/more.svg
  14. +11
    -0
      assets/submit.svg
  15. +13
    -0
      assets/submit2.svg
  16. +11
    -0
      assets/warehouse.svg
  17. +16
    -0
      cmd/serv.go
  18. +1
    -1
      cmd/web.go
  19. +15
    -4
      custom/conf/app.ini.sample
  20. +158
    -25
      custom/public/css/git.openi.css
  21. +1
    -0
      custom/public/css/placeholder-home.css
  22. +1
    -0
      custom/public/css/placeholder.css
  23. BIN
      custom/public/img/home-banner-01-en.jpg
  24. BIN
      custom/public/img/home-banner-01.jpg
  25. BIN
      custom/public/img/home-banner-02-1.jpg
  26. BIN
      custom/public/img/home-banner-02-2.png
  27. BIN
      custom/public/img/home-bg-ps.png
  28. BIN
      custom/public/img/i-pic-01.jpg
  29. BIN
      custom/public/img/i-pic-02.jpg
  30. BIN
      custom/public/img/i-pic-03.jpg
  31. BIN
      custom/public/img/i-pic-04.jpg
  32. +183
    -0
      custom/public/img/logo-footer.svg
  33. +149
    -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. +0
    -1
      custom/public/swiper/swiper-bundle.min.js
  50. +0
    -0
      docs/持续集成操作指南.md
  51. +501
    -0
      entity/ai_task.go
  52. +371
    -0
      entity/cluster.go
  53. +56
    -0
      entity/command.go
  54. +64
    -0
      entity/container.go
  55. +93
    -0
      entity/creation.go
  56. +13
    -0
      entity/err_code.go
  57. +8
    -0
      entity/file.go
  58. +22
    -0
      entity/file_download.go
  59. +3
    -0
      entity/grampus_err_code.go
  60. +14
    -0
      entity/images.go
  61. +35
    -0
      entity/operation_profile.go
  62. +11
    -0
      entity/resource_usage.go
  63. +21
    -0
      entity/storage.go
  64. +44
    -0
      entity/user.go
  65. +181
    -47
      go.mod
  66. +180
    -89
      go.sum
  67. BIN
      go_build_code_gitea_io_gitea
  68. +1
    -1
      integrations/pull_update_test.go
  69. +1527
    -0
      manager/client/cloudbrain_two/resty.go
  70. +233
    -0
      manager/client/cloudbrain_two_cd/resty.go
  71. +674
    -0
      manager/client/grampus/grampus.go
  72. +232
    -4
      models/action.go
  73. +165
    -1
      models/action_list.go
  74. +49
    -0
      models/admin_operate_log.go
  75. +752
    -55
      models/ai_model_manage.go
  76. +107
    -15
      models/attachment.go
  77. +181
    -0
      models/badge.go
  78. +94
    -0
      models/badge_category.go
  79. +159
    -0
      models/badge_user.go
  80. +37
    -0
      models/base_message.go
  81. +442
    -0
      models/card_request.go
  82. +2652
    -109
      models/cloudbrain.go
  83. +35
    -0
      models/cloudbrain_config.go
  84. +674
    -0
      models/cloudbrain_image.go
  85. +178
    -0
      models/cloudbrain_spec.go
  86. +623
    -0
      models/cloudbrain_static.go
  87. +68
    -0
      models/cloudbrain_temp.go
  88. +11
    -25
      models/custom_migrations.go
  89. +296
    -52
      models/dataset.go
  90. +15
    -14
      models/dataset_permission.go
  91. +88
    -0
      models/dataset_reference.go
  92. +77
    -0
      models/dataset_star.go
  93. +203
    -0
      models/dbsql/dataset_foreigntable_for_es.sql
  94. +227
    -0
      models/dbsql/issue_foreigntable_for_es.sql
  95. +549
    -0
      models/dbsql/repo_foreigntable_for_es.sql
  96. +317
    -0
      models/dbsql/user_foreigntable_for_es.sql
  97. +82
    -0
      models/error.go
  98. +117
    -5
      models/file_chunk.go
  99. +22
    -0
      models/helper.go
  100. +13
    -9
      models/helper_environment.go

+ 2
- 32
.github/issue_template.md View File

@@ -1,33 +1,3 @@
<!-- NOTE: If your issue is a security concern, please send an email to security@gitea.io instead of opening a public issue -->
## 场景描述

<!--
1. Please speak English, this is the language all maintainers can speak and write.
2. Please ask questions or configuration/deploy problems on our Discord
server (https://discord.gg/gitea) or forum (https://discourse.gitea.io).
3. Please take a moment to check that your issue doesn't already exist.
4. Please give all relevant information below for bug reports, because
incomplete details will be handled as an invalid report.
-->

- Gitea version (or commit ref):
- Git version:
- Operating system:
- Database (use `[x]`):
- [ ] PostgreSQL
- [ ] MySQL
- [ ] MSSQL
- [ ] SQLite
- Can you reproduce the bug at https://try.gitea.io:
- [ ] Yes (provide example URL)
- [ ] No
- [ ] Not relevant
- Log gist:

## Description

...


## Screenshots

<!-- **If this issue involves the Web Interface, please include a screenshot** -->
## 预期效果

+ 8
- 2
.gitignore View File

@@ -55,6 +55,9 @@ coverage.all
!/custom/conf/templates
/custom/conf/app.ini
!/custom/conf/app.ini.sample
/custom/conf/privatekey.pem
/custom/public/kanban
/custom/public/annual-report
/data
/indexers
/log
@@ -75,7 +78,6 @@ coverage.all
/integrations/pgsql.ini
/integrations/mssql.ini
/node_modules
/yarn.lock
/public/js
/public/css
/public/fonts
@@ -83,6 +85,7 @@ coverage.all
/public/img/svg
/VERSION


# Snapcraft
snap/.snapcraft/
parts/
@@ -97,4 +100,7 @@ prime/
/.make_evidence

/templates/home_bak.tmpl
/==bak
/==bak

#ROS-hmci-frontend-link
web_src/vuepages/pages/ros-hmci/views/links.js

+ 4
- 0
.prettierignore View File

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

+ 1
- 0
.prettierrc.json View File

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

+ 19
- 4
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,10 +598,23 @@ $(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)
npx webpack --hide-modules --display-entrypoints=false
npx webpack --hide-modules --display-entrypoints=false --progress
@touch $(WEBPACK_DEST)

.PHONY: update-translations


+ 229
- 16
README.md View File

@@ -2,50 +2,263 @@

<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)


## AiForge

启智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/)。

### 系统总体架构

下图展示了系统总体架构,本项目分为Web前端和服务后端,Web页面面向算法开发者、应用开发者、科研工作者、学生等用户群体,通过统一的Web页面入口,使用系统提供的系统服务。

后端服务涵盖了AI模型开发流水线,包括代码协同开发、数据管理、模型调试、训练、推理和部署等(*目前尚未支持模型部署*)。在不同的开发阶段,我们还将提供丰富的开发工具供用户使用,如数据标注、数据筛选、模型转换、模型压缩、代码检测等。我们也欢迎社区提供更多丰富的工具接入,提高利用平台进行开发的效率。
![系统架构图](assets/架构图.png)

## 在线服务使用
本项目的在线服务平台的详细使用帮助文档,可参阅本项目[百科](https://git.openi.org.cn/OpenI/aiforge/wiki)内容。

本项目的在线服务平台的详细使用帮助文档,可参阅本项目[百科](https://openi.pcl.ac.cn/OpenI/aiforge/wiki)内容。

- 如何创建账号

- 如何创建组织及管理成员权限

- 如何创建项目仓库

- 如何使用数据集功能

- 如何使用计算资源进行模型调试和训练

- 使用小技巧

- 常见问题(FAQ)
## 安装
您也可以基于本项目代码,在本地环境安装部署服务。
### 数据库准备
[数据库准备说明](https://docs.gitea.io/zh-cn/database-prep/)
### 从源代码安装
## 安装
您也可以基于本项目代码,在本地环境安装部署服务。
### 数据库准备
[数据库准备说明](https://docs.gitea.io/zh-cn/database-prep/)
### 从源代码安装

- node版本 >= v10.13.0
- golang版本 >= 1.13.3

- golang版本 >= 1.18.10

[从源代码安装说明](https://docs.gitea.io/zh-cn/install-from-source/)

## 开发者指南

#### Linux下通过Docker快速搭建开发环境:

前提条件:已安装Docker,了解Docker的基本操作;熟悉git的基本操作(拉取代码,提交代码,合并代码,创建分支)。

1. 拉取镜像:
aiforge-postgres是数据库镜像,初始化了数据库;
aiforge-dev是开发环境镜像,安装了go,nodejs,openssh等依赖。
如果执行命令提示没有权限,在命令前加sudo
```
docker pull swr.cn-north-4.myhuaweicloud.com/openi/aiforge-postgres:v1
docker pull swr.cn-north-4.myhuaweicloud.com/openi/aiforge-dev:v2
```

2. 启动镜像:
注意:由于linux下的回车符和windows不一致,直接拷贝命令执行可能失败。 如果失败:去掉'\\',把命令改成一行命令再执行。
```
docker run --name postgres-openi \
-e POSTGRES_PASSWORD=openi \
-e PGDATA=/var/lib/postgresql/data/pgdata \
-p 5432:5432 \
-v /home/openi/postgresql/data:/var/lib/postgresql/data \
-d swr.cn-north-4.myhuaweicloud.com/openi/aiforge-postgres:v1
```
```
docker run --name aiforgedev \
-p 8787:3000 \
-p 2222:22 \
-v /home/openi/data:/data \
-d swr.cn-north-4.myhuaweicloud.com/openi/aiforge-dev:v2
```

3. 进入开发容器并下载代码:
- 执行sudo docker ps,找到名称为aiforgedev的容器id(CONTAINER ID )
- 执行docker exec -it {开发容器id} /bin/bash
- 执行cd /data
- 执行git clone {aiforge项目地址或派生项目地址,例如https://openi.pcl.ac.cn/OpenI/aiforge.git,后续说明已此地址为例}

4. 设置配置文件:
- 在/data/aiforge/custom/conf目录下创建app.ini
- 文件内容如下:
注意:需要将文件中的两个Local_IP替换为本机IP地址
> APP_NAME = aiforge
> RUN_MODE = prod
>
> [repository]
> ROOT = /data/git/repositories
>
> [repository.local]
> LOCAL_COPY_PATH = /data/gitea/tmp/local-repo
>
> [repository.upload]
> TEMP_PATH = /data/gitea/uploads
>
> [server]
> SSH_DOMAIN = 0.0.0.0
> DOMAIN = 0.0.0.0
> HTTP_PORT = 3000
> ROOT_URL = http://0.0.0.0:3000
> DISABLE_SSH = false
> SSH_PORT = 22
> LFS_START_SERVER = true
> LFS_CONTENT_PATH = /data/git/lfs
> LFS_JWT_SECRET = tqVLRkZYpP4UlAoZtZcdX2paFZ6G7FN_Y47It6PfJAE
> OFFLINE_MODE = false
>
> [database]
> DB_TYPE = postgres
> HOST = Local_IP:5432
> NAME = gitea
> USER = gitea
> PASSWD = gitea
> SCHEMA =
> SSL_MODE = disable
> CHARSET = utf8
> PATH = /data/gitea/gitea.db
>
> [database_statistic]
> DB_TYPE = postgres
> HOST = Local_IP:5432
> NAME = statistic
> USER = gitea
> PASSWD = gitea
> SCHEMA =
> SSL_MODE = disable
> CHARSET = utf8
> PATH = /data/gitea/statistic.db
>
> [indexer]
> ISSUE_INDEXER_PATH = /data/gitea/indexers/issues.bleve
>
> [session]
> PROVIDER_CONFIG = /data/gitea/sessions
>
> [picture]
> AVATAR_UPLOAD_PATH = /data/gitea/avatars
> REPOSITORY_AVATAR_UPLOAD_PATH = /data/gitea/repo-avatars
>
> [attachment]
> PATH = /data/gitea/attachments
>
> ENABLED = true
> MAX_SIZE = 1048576
> ALLOWED_TYPES = */*
> MAX_FILES = 10
> STORE_TYPE = minio
> MINIO_ENDPOINT =
>
> MINIO_ACCESS_KEY_ID =
> MINIO_SECRET_ACCESS_KEY =
> MINIO_BUCKET =
> MINIO_LOCATION =
> MINIO_BASE_PATH = attachment/
> MINIO_USE_SSL = true
> MINIO_REAL_PATH =
>
> [log]
> MODE = file
> LEVEL = info
> ROOT_PATH = /data/gitea/log
>
> [security]
> INSTALL_LOCK = true
> SECRET_KEY = sdfsagg453535
>
> [service]
> DISABLE_REGISTRATION = false
> REQUIRE_SIGNIN_VIEW = false
>
> [obs]
>
> ENDPOINT =
>
> ACCESS_KEY_ID =
> SECRET_ACCESS_KEY =
> BUCKET =
> LOCATION =
> BASE_PATH =
> CODE_PATH_PREFIX =
> Output_Path =
> TrainJobModel_Path =
>
> [picture]
> DISABLE_GRAVATAR = true
> ENABLE_FEDERATED_AVATAR = false

5. 编译:
```
cd /data/aiforge
make build
```

6. 运行:
```
./opendata web
```

7. 访问:http://本机ip:8787
注意:代码提交到aiforge请参考[代码提交](https://openi.pcl.ac.cn/docs/index.html#/repo/code.md),[合并请求](https://openi.pcl.ac.cn/docs/index.html#/repo/pr?id=%e5%90%88%e5%b9%b6%e8%af%b7%e6%b1%82)
**特别说明:** 目前无法给开发者提供云脑和智算网络的开发环境,开发者暂时只能参与代码管理相关的任务开发。

## 授权许可
本项目采用 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)在线提交问题(点击页面右上角绿色按钮**创建任务**)
- 加入微信群实时交流,获得进一步的支持
<img src="https://git.openi.org.cn/OpenI/aiforge/wiki/raw/img/wechatgroup.jpg" width=200px />

- 点击[这里](https://openi.pcl.ac.cn/OpenI/aiforge/issues)在线提交问题(点击页面右上角绿色按钮**创建任务**)

- 加入微信群实时交流,获得进一步的支持
<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

+ 13
- 0
assets/abandon.svg View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="14px" height="14px" viewBox="0 0 14 14" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>放弃更改</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Manaco-提交" transform="translate(-414.000000, -114.000000)" fill="#9096A3" fill-rule="nonzero">
<g id="编组-8" transform="translate(90.000000, 114.000000)">
<g id="放弃更改" transform="translate(324.000000, 0.000000)">
<path d="M5.64465565,9.44839769 C3.70098527,7.81473925 1.87574642,6.2803543 0.00696656051,4.7093949 C1.86355494,3.14888535 3.69053543,1.61624204 5.60633957,0.00696656051 L5.60633957,2.18575836 C6.64958201,2.31638137 7.63535032,2.37385549 8.58976911,2.57240247 C10.5734972,2.98342954 12.1549064,3.94655653 12.8445959,5.99472532 C13.8408141,8.95028861 12.6599821,12.2263137 10.0057225,13.8564889 C9.96914809,13.8791302 9.92386545,13.8913217 9.82981688,13.9296377 C10.1851115,13.4959693 10.5282146,13.1267416 10.8173268,12.7174562 C11.3885848,11.9128185 11.7212381,11.0176154 11.7595541,10.0213973 C11.7943869,9.12271099 11.3955514,8.46959594 10.6431628,8.01328623 C9.76363455,7.47860271 8.77786624,7.28876393 7.77816481,7.20516521 C7.08499204,7.14769108 6.38485271,7.19471537 5.64465565,7.19471537 L5.64465565,9.44839769 Z" id="路径"></path>
</g>
</g>
</g>
</g>
</svg>

+ 11
- 0
assets/code1.svg View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="22px" height="24px" viewBox="0 0 22 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>代码1</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Manaco-代码" transform="translate(-24.000000, -54.000000)" fill="#FFFFFF" fill-rule="nonzero">
<g id="编组-2" transform="translate(23.000000, 54.000000)">
<path d="M18.691358,20.6530612 C19.3209877,20.6530612 19.8024691,21.4013605 19.8148148,22.3265306 C19.8148148,23.2380952 19.308642,24 18.7037037,24 L5.2962963,24 C4.67901235,24 4.18518519,23.2517007 4.18518519,22.3265306 C4.18518519,21.414966 4.69135802,20.6530612 5.2962963,20.6530612 L18.691358,20.6530612 Z M21.4320988,1.24344979e-14 C22.2962963,1.24344979e-14 23,0.74829932 23,1.67346939 L23,16.707483 L22.9876543,16.707483 C22.9876543,17.6190476 22.2839506,18.3809524 21.4197531,18.3809524 L2.56790123,18.3809524 C1.7037037,18.3809524 1,17.6326531 1,16.707483 L1,1.67346939 C1,0.761904762 1.7037037,1.24344979e-14 2.56790123,1.24344979e-14 L21.4320988,1.24344979e-14 Z M7.40275426,5.59798965 L4.42406571,7.86899844 C4.2208615,8.02209757 4.09361684,8.24213052 4.07053819,8.48032233 C4.04570362,8.87774887 4.05154704,9.27649576 4.08952935,9.67260195 C4.11436392,9.92346921 4.25168453,10.1518904 4.4693523,10.3024108 L7.45096256,12.4215789 C7.80850744,12.6781677 8.30844535,12.6529526 8.6334608,12.3784779 L8.71752605,12.2974656 L8.71752605,12.2974656 C9.05638834,11.8920818 8.97886013,11.3169104 8.54222314,10.996917 L5.87323641,9.10088868 L8.58750971,7.02925332 C8.99780991,6.70122471 9.06310176,6.14433333 8.73797805,5.74586928 C8.58953291,5.5575319 8.36035163,5.43424637 8.10640394,5.40612098 C7.85245625,5.37799559 7.59712068,5.44761958 7.40275426,5.59798965 Z M13.2750251,5.43601868 C13.041601,5.50337484 12.8462407,5.65606881 12.7322875,5.86022349 L9.67682551,11.2791811 C9.51225291,11.5570107 9.51537327,11.896483 9.68502809,12.1715632 C9.85162755,12.4442956 10.1628838,12.6086547 10.4957167,12.5996479 C10.8286146,12.587652 11.1280084,12.4046164 11.2763291,12.1224175 L14.3331583,6.70475316 C14.4492093,6.49817249 14.4756869,6.25696469 14.4069815,6.03223337 C14.340046,5.80948374 14.182814,5.62076308 13.9695104,5.50715059 L13.9681433,5.50715059 C13.7568457,5.39554401 13.5067057,5.36987315 13.2750251,5.43601868 Z M15.8922987,5.40611327 C15.6385591,5.43425376 15.4096876,5.55776951 15.2618761,5.74633526 C14.9356259,6.14558659 15.0029615,6.70437583 15.4166517,7.0307509 L18.129604,9.10060055 L15.4619162,10.9975227 C15.0590628,11.2928349 14.9620467,11.8055279 15.2158996,12.20143 L15.2866986,12.297779 L15.2866986,12.297779 C15.6035504,12.6462741 16.1627866,12.7017165 16.5526457,12.4205444 L19.5328049,10.3044927 C19.750759,10.1515096 19.8883325,9.92333042 19.912443,9.67482542 C19.9489467,9.27880827 19.9547872,8.87883095 19.9270444,8.48149375 L19.9270444,8.48017369 C19.903977,8.24203541 19.7767943,8.02205192 19.573689,7.86898721 L16.5949899,5.59848887 C16.4011465,5.4478137 16.1460382,5.37797278 15.8922987,5.40611327 Z" id="代码1"></path>
</g>
</g>
</g>
</svg>

+ 11
- 0
assets/code2.svg View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="22px" height="24px" viewBox="0 0 22 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>代码</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Manaco-提交" transform="translate(-24.000000, -54.000000)" fill="#000000" fill-rule="nonzero">
<g id="编组-2" transform="translate(23.000000, 54.000000)">
<path d="M18.691358,20.6530612 C19.3209877,20.6530612 19.8024691,21.4013605 19.8148148,22.3265306 C19.8148148,23.2380952 19.308642,24 18.7037037,24 L5.2962963,24 C4.67901235,24 4.18518519,23.2517007 4.18518519,22.3265306 C4.18518519,21.414966 4.69135802,20.6530612 5.2962963,20.6530612 L18.691358,20.6530612 Z M21.4320988,1.24344979e-14 C22.2962963,1.24344979e-14 23,0.74829932 23,1.67346939 L23,16.707483 L22.9876543,16.707483 C22.9876543,17.6190476 22.2839506,18.3809524 21.4197531,18.3809524 L2.56790123,18.3809524 C1.7037037,18.3809524 1,17.6326531 1,16.707483 L1,1.67346939 C1,0.761904762 1.7037037,1.24344979e-14 2.56790123,1.24344979e-14 L21.4320988,1.24344979e-14 Z M7.40275426,5.59798965 L4.42406571,7.86899844 C4.2208615,8.02209757 4.09361684,8.24213052 4.07053819,8.48032233 C4.04570362,8.87774887 4.05154704,9.27649576 4.08952935,9.67260195 C4.11436392,9.92346921 4.25168453,10.1518904 4.4693523,10.3024108 L7.45096256,12.4215789 C7.80850744,12.6781677 8.30844535,12.6529526 8.6334608,12.3784779 L8.71752605,12.2974656 L8.71752605,12.2974656 C9.05638834,11.8920818 8.97886013,11.3169104 8.54222314,10.996917 L5.87323641,9.10088868 L8.58750971,7.02925332 C8.99780991,6.70122471 9.06310176,6.14433333 8.73797805,5.74586928 C8.58953291,5.5575319 8.36035163,5.43424637 8.10640394,5.40612098 C7.85245625,5.37799559 7.59712068,5.44761958 7.40275426,5.59798965 Z M13.2750251,5.43601868 C13.041601,5.50337484 12.8462407,5.65606881 12.7322875,5.86022349 L9.67682551,11.2791811 C9.51225291,11.5570107 9.51537327,11.896483 9.68502809,12.1715632 C9.85162755,12.4442956 10.1628838,12.6086547 10.4957167,12.5996479 C10.8286146,12.587652 11.1280084,12.4046164 11.2763291,12.1224175 L14.3331583,6.70475316 C14.4492093,6.49817249 14.4756869,6.25696469 14.4069815,6.03223337 C14.340046,5.80948374 14.182814,5.62076308 13.9695104,5.50715059 L13.9681433,5.50715059 C13.7568457,5.39554401 13.5067057,5.36987315 13.2750251,5.43601868 Z M15.8922987,5.40611327 C15.6385591,5.43425376 15.4096876,5.55776951 15.2618761,5.74633526 C14.9356259,6.14558659 15.0029615,6.70437583 15.4166517,7.0307509 L18.129604,9.10060055 L15.4619162,10.9975227 C15.0590628,11.2928349 14.9620467,11.8055279 15.2158996,12.20143 L15.2866986,12.297779 L15.2866986,12.297779 C15.6035504,12.6462741 16.1627866,12.7017165 16.5526457,12.4205444 L19.5328049,10.3044927 C19.750759,10.1515096 19.8883325,9.92333042 19.912443,9.67482542 C19.9489467,9.27880827 19.9547872,8.87883095 19.9270444,8.48149375 L19.9270444,8.48017369 C19.903977,8.24203541 19.7767943,8.02205192 19.573689,7.86898721 L16.5949899,5.59848887 C16.4011465,5.4478137 16.1460382,5.37797278 15.8922987,5.40611327 Z" id="代码"></path>
</g>
</g>
</g>
</svg>

+ 13
- 0
assets/codeLog.svg View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="14px" height="16px" viewBox="0 0 14 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>代码文件</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Manaco-提交" transform="translate(-90.000000, -169.000000)" fill="#6F6E6E" fill-rule="nonzero">
<g id="编组-8" transform="translate(90.000000, 114.000000)">
<g id="编组-5" transform="translate(0.000000, 55.000000)">
<path d="M9.72883333,0 C10.0462727,0 10.3500145,0.136417787 10.57,0.377846154 L13.6733333,3.78092308 C13.882631,4.01000077 13.9997061,4.31568263 14,4.63384615 L14,14.7692308 C14,15.4489658 13.4776655,16 12.8333333,16 L1.16666667,16 C0.522334459,16 0,15.4489658 0,14.7692308 L0,1.23076923 C0,0.551034154 0.522334459,0 1.16666667,0 L9.72883333,0 Z M6.36548862,5.17542359 C6.11720692,4.93565488 5.72153765,4.94252988 5.48173828,5.19078174 L5.48173828,5.19078174 L3.18299167,7.48923751 C2.93900277,7.73326904 2.93900277,8.1288445 3.18299173,8.37287604 L3.18299173,8.37287604 L5.48173833,10.6750818 C5.72394113,10.908979 6.1079261,10.908979 6.3501289,10.6750818 C6.59841088,10.4353128 6.60528832,10.0396952 6.36548895,9.79144331 L6.36548895,9.79144331 L4.50923605,7.93043799 L6.3808487,6.05906212 C6.62064776,5.81081183 6.61377188,5.41519262 6.36548862,5.17542359 Z M11.009075,5.01869617 C10.6741417,4.93523201 10.3349555,5.13905256 10.2514799,5.47394202 L10.2514799,5.47394202 L9.02147908,10.0608692 C8.9320786,10.3942696 9.12991167,10.7370085 9.46335425,10.8263983 C9.79679682,10.9157874 10.139579,10.7179794 10.2289801,10.384579 L10.2289801,10.384579 L11.4577308,5.79640184 L11.4589816,5.79640005 L11.4643785,5.77619549 C11.5478532,5.44130448 11.3440069,5.10216127 11.009075,5.01869617 Z" id="代码文件"></path>
</g>
</g>
</g>
</g>
</svg>

+ 11
- 0
assets/drag.svg View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="22px" height="10px" viewBox="0 0 22 10" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>左右拖动</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Manaco-代码" transform="translate(-440.000000, -546.000000)" fill="#6F6E6E" fill-rule="nonzero">
<g id="左右拖动" transform="translate(440.000000, 546.000000)">
<path d="M5.89500984,9.90606823 L0.154266811,5.3158823 C0.0566144941,5.23813069 0,5.12195442 0,4.99931775 C0,4.87668107 0.0566144941,4.76050481 0.154266811,4.6827532 L5.89500984,0.0925672631 C6.02101948,-0.00758660268 6.19515807,-0.0282927778 6.34232468,0.0393787107 C6.48949128,0.107050199 6.58336056,0.250994188 6.58344941,0.409131811 L6.58344941,2.76972089 C6.58344941,2.99579905 6.77215853,3.1790716 7.00494302,3.1790716 L14.996462,3.1790716 C15.2292464,3.1790716 15.4179556,2.99579905 15.4179556,2.76972089 L15.4179556,0.410496313 C15.4180444,0.252358691 15.5119137,0.108414701 15.6590803,0.040743213 C15.8062469,-0.0269282754 15.9803855,-0.00622210032 16.1063951,0.0939317654 L21.8457332,4.6841177 C21.9433855,4.76186931 22,4.87804558 22,5.00068225 C22,5.12331893 21.9433855,5.23949519 21.8457332,5.3172468 L16.1063951,9.90743274 C15.9803855,10.0075866 15.8062469,10.0282928 15.6590803,9.96062129 C15.5119137,9.8929498 15.4180444,9.74900581 15.4179556,9.59086819 L15.4179556,7.23027911 C15.4179556,7.00420095 15.2292464,6.8209284 14.996462,6.8209284 L7.00494302,6.8209284 C6.77215853,6.8209284 6.58344941,7.00420095 6.58344941,7.23027911 L6.58344941,9.59086819 C6.58336056,9.74900581 6.48949128,9.8929498 6.34232468,9.96062129 C6.19515807,10.0282928 6.02101948,10.0075866 5.89500984,9.90743274 L5.89500984,9.90606823 Z" id="路径"></path>
</g>
</g>
</g>
</svg>

BIN
assets/drag2.png View File

Before After
Width: 28  |  Height: 28  |  Size: 801 B

+ 9
- 0
assets/more.svg View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>更多</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Manaco-代码" transform="translate(-412.000000, -59.000000)" fill="#9FA5B0">
<path d="M426,59 C427.104569,59 428,59.8954305 428,61 L428,73 C428,74.1045695 427.104569,75 426,75 L414,75 C412.895431,75 412,74.1045695 412,73 L412,61 C412,59.8954305 412.895431,59 414,59 L426,59 Z M416,66 C415.447715,66 415,66.4477153 415,67 C415,67.5522847 415.447715,68 416,68 C416.552285,68 417,67.5522847 417,67 C417,66.4477153 416.552285,66 416,66 Z M420,66 C419.447715,66 419,66.4477153 419,67 C419,67.5522847 419.447715,68 420,68 C420.552285,68 421,67.5522847 421,67 C421,66.4477153 420.552285,66 420,66 Z M424,66 C423.447715,66 423,66.4477153 423,67 C423,67.5522847 423.447715,68 424,68 C424.552285,68 425,67.5522847 425,67 C425,66.4477153 424.552285,66 424,66 Z" id="更多"></path>
</g>
</g>
</svg>

+ 11
- 0
assets/submit.svg View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>提交</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Manaco-代码" transform="translate(-23.000000, -124.000000)" fill="#000000" fill-rule="nonzero">
<g id="编组-3" transform="translate(23.000000, 124.000000)">
<path d="M20.1289472,24 L7.67629341,24 C5.45869754,24 3.92343885,22.0588235 3.92343885,19.7647059 C3.92343885,17.4705882 5.79986613,15.8823529 8.01746202,15.8823529 L8.1880463,15.8823529 C8.35863061,14.2941177 9.5527207,12.882353 11.2585637,13.0588235 C11.7703166,13.0588235 12.1114852,13.2352941 12.6232381,13.4117647 C13.3055753,12 14.6702497,11.117647 16.3760926,11.117647 C18.9348571,11.117647 21.152453,13.2352941 21.152453,15.8823529 L21.152453,16.0588235 C23.1994646,16.5882353 24.3935547,18.8823529 23.8818017,21 C23.3700488,22.7647059 21.8347902,23.8235294 20.1289472,24 Z M16.3760926,9.35294117 C14.4996653,9.35294117 12.7938224,10.2352941 11.5997323,11.6470588 L10.7468108,11.6470588 C9.38213639,11.6470588 8.1880463,12.5294118 7.50570911,13.7647059 L0,13.7647059 L0,7.76470589 L22.5171273,7.76470589 L22.5171273,14.2941176 C21.6642058,12 20.1289472,9.17647058 16.3760926,9.35294117 Z M4.09402315,9.1764706 C3.24110165,9.1764706 2.55876448,9.88235296 2.55876448,10.7647059 C2.55876448,11.6470588 3.24110167,12.3529412 4.09402315,12.3529412 C4.94694463,12.3529412 5.62928185,11.6470588 5.62928185,10.7647059 C5.62928185,9.88235296 4.94694465,9.1764706 4.09402315,9.1764706 Z M1.70584298,-5.32907052e-15 L20.8112844,-5.32907052e-15 C21.8347902,-5.32907052e-15 22.5171273,0.705882356 22.5171273,1.7647059 L22.5171273,6 L0,6 L0,1.7647059 C0,0.882352951 0.682337196,-5.32907052e-15 1.70584298,-5.32907052e-15 Z M4.09402315,4.5882353 C4.94694465,4.5882353 5.62928185,3.88235294 5.62928185,3.00000002 C5.62928185,2.11764709 4.94694465,1.41176471 4.09402315,1.41176471 C3.24110165,1.41176471 2.55876448,2.11764707 2.55876448,3.00000002 C2.55876448,3.88235297 3.24110167,4.5882353 4.09402315,4.5882353 L4.09402315,4.5882353 Z M2.55876448,19.9411765 C2.55876448,21.5294117 3.24110167,23.117647 4.43519176,24 L1.70584298,24 C0.682337196,24 0,23.2941176 0,22.2352941 L0,15.3529412 L5.11752893,15.3529412 C3.58227026,16.2352941 2.55876446,18 2.55876448,19.9411765 Z" id="提交"></path>
</g>
</g>
</g>
</svg>

+ 13
- 0
assets/submit2.svg View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="14px" height="16px" viewBox="0 0 14 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>提交</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Manaco-提交" transform="translate(-91.000000, -59.000000)" fill="#000000" fill-rule="nonzero">
<g id="编组-17" transform="translate(91.000000, 59.000000)">
<g id="提交" transform="translate(-0.000000, 0.000000)">
<path d="M13.4778367,15.988073 L9.55310265,15.988073 L9.55310265,15.43347 L9.55310265,15.0458442 L12.972039,15.0458442 L12.972039,0.954155797 L6.14056884,0.954155797 L6.14056884,4.72903466 C6.14056884,4.98922102 5.91411232,5.20014909 5.63477113,5.20014909 C5.62509697,5.20014909 5.61549961,5.19983898 5.60595988,5.19933805 C5.59642015,5.19983898 5.5868228,5.20014909 5.57714861,5.20014909 L1.01856687,5.20014909 L1.01856687,15.0458442 L4.43110069,15.0458442 L4.43110069,15.43347 L4.43110069,15.988073 L0.512769174,15.988073 C0.233427991,15.988073 0.00697148198,15.777145 0.00697148198,15.5169586 L0.00697148198,4.52031308 C0.00697148198,4.40469325 0.0517633795,4.29885948 0.125968379,4.21687961 C0.160010492,4.16720388 0.201492303,4.11953783 0.250183335,4.07557511 L4.51104873,0.228985469 C4.60098467,0.0984748438 4.75803806,0.0119269531 4.93689838,0.0119269531 L13.4778367,0.0119269531 C13.7571778,0.0119269531 13.9836343,0.222855016 13.9836343,0.483041375 L13.9836343,15.5169586 C13.9836343,15.777145 13.7571778,15.988073 13.4778367,15.988073 Z M5.12897344,0.983311219 L1.50169969,4.25792023 L5.12897344,4.25792023 L5.12897344,0.983311219 Z M2.5954584,10.7580947 L2.59340321,9.61971525 L11.8174309,9.61904138 L11.8194925,10.7574268 L2.5954584,10.7580947 Z M2.59340321,6.38751248 L11.8174309,6.38683861 L11.8194925,7.525224 L2.5954584,7.52589191 L2.59340321,6.38751248 Z M2.5954584,9.1419933 L2.59340321,8.00361386 L11.8174309,8.00294 L11.8194925,9.14132537 L2.5954584,9.1419933 Z M7.04972419,11.4171182 L8.63383774,12.8930302 L7.63185892,12.8930302 L7.63239672,14.8444696 L6.41020382,14.845054 L6.40966602,12.8930302 L5.46561062,12.8930302 L7.04972419,11.4171182 Z" id="形状"></path>
</g>
</g>
</g>
</g>
</svg>

+ 11
- 0
assets/warehouse.svg View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="14px" height="16px" viewBox="0 0 14 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>代码仓库</title>
<g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="Manaco-代码" transform="translate(-90.000000, -59.000000)" fill="#000000" fill-rule="nonzero">
<g id="代码仓库" transform="translate(90.000000, 59.000000)">
<path d="M13.5574941,6.11252215 L7.7074972,0.29324975 L7.70749726,0.293249807 C7.31718961,-0.0974428356 6.68402439,-0.0977943985 6.29328388,0.29246314 C6.29302151,0.292725179 6.2927593,0.292987362 6.29249722,0.293249694 L0.442500285,6.11252209 L0.442500249,6.11252213 C0.159278149,6.3940287 0,6.77683852 0,7.17613743 L0,14.5002084 L0,14.5002082 C0,15.3285311 0.671573248,16.0000193 1.5,16.0000193 L12.5,16.0000193 L12.5,16.0000193 C13.3284268,16.0000193 14,15.3285311 14,14.5002082 C14,14.5002082 14,14.5002082 14,14.5002082 L14,7.17613718 L14,7.17619816 C14,6.77689769 13.8407531,6.39408162 13.5575435,6.11256411 L13.5574941,6.11252215 Z M6.36499767,11.8617945 L6.36499768,11.8617945 C6.60479705,12.1100463 6.5979196,12.505664 6.34963762,12.745433 C6.10743482,12.9793301 5.72344986,12.9793301 5.48124706,12.745433 L3.18250042,10.4432272 L3.18250045,10.4432272 C2.93851149,10.1991957 2.93851149,9.80362021 3.18250039,9.55958868 L5.48124703,7.26113288 L5.481247,7.2611329 C5.72104637,7.01288105 6.11671564,7.00600604 6.36499734,7.24577476 C6.6132806,7.48554379 6.62015648,7.881163 6.38035743,8.12941329 C6.37532678,8.13462123 6.37020601,8.13974136 6.3649974,8.14477137 L4.50874477,10.0007892 L6.36499767,11.8617945 Z M10.978743,7.86855544 L9.74999237,12.4567327 L9.74999239,12.4567326 C9.66059128,12.790133 9.31780908,12.987941 8.98436651,12.8985518 C8.65092393,12.8091621 8.45309086,12.4664232 8.54249134,12.1330228 L9.77249213,7.54609562 L9.77249214,7.54609561 C9.8559678,7.21120616 10.1951539,7.00738561 10.5300873,7.09084977 C10.8650191,7.17431487 11.0688655,7.51345807 10.9853908,7.84834909 C10.9837046,7.85511356 10.9819054,7.86184935 10.9799938,7.86855364 L10.978743,7.86855544 Z" id="形状"></path>
</g>
</g>
</g>
</svg>

+ 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 {


+ 1
- 1
cmd/web.go View File

@@ -167,7 +167,7 @@ func runWeb(ctx *cli.Context) error {
if setting.EnablePprof {
go func() {
log.Info("Starting pprof server on localhost:6060")
log.Info("%v", http.ListenAndServe("localhost:6060", nil))
log.Info("%v", http.ListenAndServe("192.168.207.34:6060", nil))
}()
}



+ 15
- 4
custom/conf/app.ini.sample View File

@@ -5,12 +5,17 @@
; see https://docs.gitea.io/en-us/config-cheat-sheet/ for additional documentation.

; App name that shows in every page title
APP_NAME = Gitea: Git with a cup of tea
APP_NAME = OpenI-启智AI开源社区提供普惠算力!
; Change it if you run locally
RUN_USER = git
; Either "dev", "prod" or "test", default is "dev"
RUN_MODE = dev

; Is show MLOps
MLOPS = false
; 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
@@ -389,12 +394,12 @@ MAX_OPEN_CONNS = 0
DB_TYPE = postgres
HOST = 127.0.0.1:5432
NAME = statistic
USER =
PASSWD =
USER =
PASSWD =
SCHEMA =
SSL_MODE = disable
CHARSET = utf8
PATH =
PATH =

[indexer]
; Issue indexer type, currently support: bleve, db or elasticsearch, default is bleve
@@ -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 =

+ 158
- 25
custom/public/css/git.openi.css View File

@@ -71,6 +71,17 @@
.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;
@@ -134,8 +145,13 @@
.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;
@@ -158,7 +174,7 @@
}
.homeorg, .homepro, .homemodel, .i-env{
position: relative;
padding-bottom: 5em;
padding-bottom: 3em;
}
.homenews::before{
content: '';
@@ -168,7 +184,7 @@
bottom: 0;
background-color: rgba(105, 192, 255, .4);
width: 2px;
}
}
.homenews .time-since{
padding-left: 1em;
color: #888888;
@@ -179,13 +195,13 @@
.homenews .ui.list>.item>.content{
color: #E8E8E8;
line-height: 1.8em;
width: calc(100% - 3.25em) !important;
width: calc(100% - 3.75em) !important;
}
.homenews .ui.list>.item{
padding: 0;
}
.newslist{
height: 325px;
height: 260px;
overflow: hidden;
}

@@ -208,14 +224,25 @@
height: auto;
border-top: 2px solid #3291F8;
border-right: 2px solid #3291F8;
border-bottom: 2px solid #3291F8;
border-radius: 0 4.0em 4.0em 0;
border-radius: 0 4.0em 0 0;
width: 10em;
z-index: 6;
}
.leftline02::after, .leftline02-2::after{
content: '';
position: absolute;
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-top: 2px solid #3291F8;
border-bottom:none;
top: -2.0em;
}
@@ -224,6 +251,9 @@
width: 7em;
z-index: 5;
}
.leftline02-2::after{
border-color: rgba(105, 192, 255, .4);
}
.leftline04{
border-radius: 0;
border-top: none;
@@ -246,13 +276,17 @@
z-index: 9;
}
.homeorg-list .card{
background-image: linear-gradient(#FFF, #FFF 60%, #DFF0EF) !important;
/* background-image: linear-gradient(#FFF, #FFF 60%, #DFF0EF) !important; */
box-shadow: none !important;
}
.homeorg-list .card .ui.small.header .content{
width: calc(100% - 3.25em);
width: calc(100% - 3.75em);
}
.homepro-tit{
z-index: 9;
position: relative;
}
.homepro-list{
.homepro-list, .homeorg-list{
position: relative;
z-index: 9;
padding: 1.0em 1.0em 3.0em;
@@ -261,42 +295,104 @@
.homepro-list .ui.card{
border-radius: 15px;
background-color: #FFF;
box-shadow: 0px 5px 10px 0px rgba(105, 192, 255, 30);
border: 1px solid rgba(105, 192, 255, 40);
min-height: 10.8em;
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{
.homepro-list .swiper-pagination-bullet-active, .homeorg-list .swiper-pagination-bullet-active{
width: 40px;
border-radius: 4px;
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;
}
}

@@ -322,4 +418,41 @@

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

}
/* rotation3D */
#app{
position: relative;
width: 800px;
margin: 0 auto;
z-index: 4;
}
.rotation3D-baseMap{
position: absolute; left: 0; right: 0; top: 104px; 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

+ 183
- 0
custom/public/img/logo-footer.svg View File

@@ -0,0 +1,183 @@
<?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 850.4 425.2" style="enable-background:new 0 0 850.4 425.2;" xml:space="preserve">
<style type="text/css">
.st0{font-family:'MicrosoftYaHei';}
.st1{font-size:61.6522px;}
</style>
<g>
<path d="M519.1,275.2h-20.7c-1.6,0-2.9-1.3-2.9-2.9V162.8c0-1.6,1.3-2.9,2.9-2.9s2.9,1.3,2.9,2.9v106.6h14.9V162.8
c0-1.6,1.3-2.9,2.9-2.9c1.6,0,2.9,1.3,2.9,2.9v109.5C522,273.9,520.7,275.2,519.1,275.2"/>
<path d="M475.8,181.5c0-0.1,0-0.2,0-0.3c-1.1-12.2-11.1-21.4-23.4-21.4c-1.6,0-2.9,1.3-2.9,2.9c0,1.6,1.3,2.9,2.9,2.9
c9.2,0,16.7,6.8,17.6,15.9l0.1,87.8h-15v-85.9v-0.2c0-11.6-8.3-21.3-19.7-23.2c-1.5-0.2-38.1-0.3-38.5-0.3c-1.6,0-2.9,1.3-2.9,2.9
v109.5c0,1.6,1.3,2.9,2.9,2.9h20.7c1.6,0,2.9-1.3,2.9-2.9v-85.9h20.8c1.6,0,2.9-1.3,2.9-2.9s-1.3-2.9-2.9-2.9h-23.7
c-1.6,0-2.9,1.3-2.9,2.9v85.9h-14.9V165.7c8.7,0,32.5,0.1,34.7,0.2c8.6,1.4,14.8,8.7,14.8,17.4v0.2v88.8c0,1.6,1.3,2.9,2.9,2.9H473
c0.8,0,1.5-0.3,2.1-0.9c0.5-0.5,0.9-1.3,0.9-2.1L475.8,181.5z"/>
<path d="M566.3,38.1l-50-29c-4.4-2.5-9.8-2.5-14.2,0l-50.1,29c-4.4,2.5-7.1,7.2-7.1,12.3v57.8c0,5,2.7,9.8,7.1,12.3l12.3,7.1
c-0.1,0.6-0.2,1.2-0.2,1.9c0,5.2,4.2,9.4,9.4,9.4c5.2,0,9.4-4.2,9.4-9.4s-4.2-9.4-9.4-9.4c-2.4,0-4.7,0.9-6.3,2.5l-12.2-7.1
c-2.6-1.5-4.2-4.3-4.2-7.2v-58c0-3,1.6-5.8,4.2-7.2l50-28.9c2.6-1.5,5.8-1.5,8.4,0l50.1,28.9c2.6,1.5,4.2,4.3,4.2,7.2v57.8
c0,3-1.6,5.8-4.2,7.2l-50.1,28.9c-0.5,0.3-1.1,0.5-1.6,0.7v-18.1c0-0.3,0-0.6,0-0.9v-10.4c0.1-0.1,0.2-0.1,0.3-0.2L540.9,96
c0.8-0.5,1.3-1.4,1.3-2.4V63.3c3.9-1.2,6.7-4.8,6.7-9c0-5.2-4.2-9.4-9.4-9.4s-9.4,4.2-9.4,9.4c0,4.1,2.6,7.6,6.3,8.9v28.9
l-24.6,16.5V50.3c3.8-1.2,6.6-4.8,6.6-9c0-5.2-4.2-9.4-9.4-9.4c-5.2,0-9.4,4.2-9.4,9.4c0,4.1,2.7,7.7,6.4,8.9v38.3L489.7,79V63.9
c3.8-1.2,6.5-4.8,6.5-8.9c0-5.2-4.2-9.4-9.4-9.4c-5.2,0-9.4,4.2-9.4,9.4c0,4.2,2.7,7.7,6.5,8.9v16.8c0,1,0.6,2,1.4,2.5l20.6,12
v26.1l-35.5-20.5V75.5c3.7-1.2,6.5-4.8,6.5-8.9c0-5.2-4.2-9.4-9.4-9.4c-5.2,0-9.4,4.2-9.4,9.4c0,4.2,2.7,7.7,6.5,9v26.9
c0,1,0.6,2,1.5,2.5l39.8,23.1v20.3c0,0.1,0,0.2,0,0.3s0,0.2,0,0.3s0.1,0.2,0.1,0.3s0,0.1,0.1,0.2c0,0.1,0.1,0.2,0.2,0.3
c0,0.1,0.1,0.1,0.1,0.2c0.1,0.1,0.2,0.2,0.2,0.3l0.1,0.1c0.1,0.1,0.2,0.2,0.3,0.3l0.1,0.1c0.1,0.1,0.3,0.2,0.4,0.2
c0,0,0.1,0,0.1,0.1c0.1,0.1,0.3,0.1,0.4,0.2h0.1c0.2,0,0.3,0.1,0.5,0.1h0.1l0,0c0,0,0,0,0.1,0l0,0c0.1,0,0.2,0,0.3,0
c2.5,0,4.9-0.7,7.1-1.9l50.1-28.9c4.4-2.5,7.1-7.2,7.1-12.3v-58C573.4,45.3,570.7,40.6,566.3,38.1 M473.5,125.8
c2,0,3.6,1.6,3.6,3.6c0,2-1.6,3.6-3.6,3.6s-3.6-1.6-3.6-3.6C469.9,127.4,471.5,125.8,473.5,125.8 M539.4,50.7c2,0,3.6,1.6,3.6,3.6
s-1.6,3.6-3.6,3.6s-3.6-1.6-3.6-3.6S537.4,50.7,539.4,50.7 M486.7,51.3c2,0,3.6,1.6,3.6,3.6s-1.6,3.6-3.6,3.6s-3.6-1.6-3.6-3.6
S484.8,51.3,486.7,51.3 M467.5,63c2,0,3.6,1.6,3.6,3.6s-1.6,3.6-3.6,3.6s-3.6-1.6-3.6-3.6S465.5,63,467.5,63 M508.9,37.7
c2,0,3.6,1.6,3.6,3.6s-1.6,3.6-3.6,3.6s-3.6-1.6-3.6-3.6S506.9,37.7,508.9,37.7"/>
<path d="M371.3,248.7h-59.1c-6.1,0-11-5-11-11v-40c0-6.1,5-11,11-11h35.4v17.5h-26V195c0-1.6-1.3-2.9-2.9-2.9s-2.9,1.3-2.9,2.9v12
c0,1.6,1.3,2.9,2.9,2.9h31.8c1.6,0,2.9-1.3,2.9-2.9v-23.3c0-1.6-1.3-2.9-2.9-2.9h-38.3c-9.3,0-16.9,7.6-16.9,16.9v40
c0,9.3,7.6,16.9,16.9,16.9h56.2v14.9l-52.6-0.1c-8.9,0-16.4-6.7-17.5-15.5c-0.2-1.6-1.7-2.7-3.2-2.5c-1.6,0.2-2.7,1.6-2.5,3.2
c1.4,11.8,11.4,20.7,23.3,20.7l55.5,0.1c1.6,0,2.9-1.3,2.9-2.9v-20.7C374.3,250,372.9,248.7,371.3,248.7"/>
<path d="M295.2,184c1.6,0.2,3-1,3.2-2.6c1-8.9,8.5-15.7,17.5-15.7h34.9c9.1,0,16.8,7.1,17.6,16.1v43h-49.6c-1.6,0-2.9,1.3-2.9,2.9
v12.6c0,1.6,1.3,2.9,2.9,2.9s2.9-1.3,2.9-2.9v-9.7h49.6c1.6,0,2.9-1.3,2.9-2.9v-46c0-0.1,0-0.1,0-0.2
c-0.9-12.1-11.2-21.6-23.4-21.6h-35c-12,0-22,9-23.3,20.9C292.4,182.4,293.6,183.8,295.2,184"/>
<path d="M249.5,159.9c-1.6,0-2.9,1.3-2.9,2.9c0,1.6,1.3,2.9,2.9,2.9c9.2,0,16.7,6.8,17.6,15.9V254c-1,8.9-8.5,15.6-17.5,15.6h-31.7
v-83.2h19.3c1.6,0,2.9-1.3,2.9-2.9s-1.3-2.9-2.9-2.9H215c-1.6,0-2.9,1.3-2.9,2.9v89v42h-14.9V165.7c13.4,0,31.4,0.1,33.2,0.2
c8.5,1.4,14.7,8.7,14.7,17.4v65.9h-19.3c-1.6,0-2.9,1.3-2.9,2.9c0,1.6,1.3,2.9,2.9,2.9H248c1.6,0,2.9-1.3,2.9-2.9v-68.8
c0-11.6-8.3-21.3-19.7-23.2c-0.9-0.1-13.3-0.2-37-0.3l0,0c-0.8,0-1.5,0.3-2.1,0.9c-0.5,0.5-0.9,1.3-0.9,2.1v154.6
c0,1.6,1.3,2.9,2.9,2.9h20.7c1.6,0,2.9-1.3,2.9-2.9v-42h31.7c12,0,22-9,23.3-20.9c0-0.1,0-0.2,0-0.3v-72.7c0-0.1,0-0.2,0-0.3
C271.8,169,261.7,159.9,249.5,159.9"/>
<path d="M98.4,155.4c-20,0-37.6,12.8-43.8,31.8c-0.5,1.5,0.3,3.2,1.9,3.7c1.5,0.5,3.2-0.3,3.7-1.9c5.4-16.6,20.8-27.8,38.3-27.8
c18.4,0,34.4,12.4,39,30.2c0.3,1.3,1.5,2.2,2.8,2.2h28.4c0.9,0,1.7-0.4,2.2-1c0.6-0.6,0.8-1.5,0.7-2.3
c-5.5-35.7-36.9-62.7-73.1-62.7s-67.6,26.9-73.1,62.7l0,0c-0.6,3.7-0.9,7.5-0.9,11.3c0,1.6,1.3,2.9,2.9,2.9h25
c0.2,2.9,0.6,5.8,1.4,8.6l0,0c5.3,20.3,23.6,34.5,44.6,34.5c20.3,0,38-13.2,44-32.3h22.7c-6.4,31.2-34.5,54.4-66.7,54.4
c-32.7,0-60.8-23.2-66.9-55.3c-0.3-1.6-1.8-2.6-3.4-2.3s-2.6,1.8-2.3,3.4c6.6,34.8,37.2,60,72.6,60c36.2,0,67.6-26.9,73.1-62.7
c0.1-0.8-0.1-1.7-0.7-2.3c-0.6-0.6-1.4-1-2.2-1h-28.4c-1.3,0-2.5,0.9-2.8,2.2c-4.6,17.8-20.6,30.2-39,30.2s-34.4-12.4-39-30.2l0,0
c-0.8-3.3-1.3-6.7-1.3-10.1c0-1.6-1.3-2.9-2.9-2.9H30.3c0.1-2.5,0.3-5,0.7-7.5l0,0c5-32.9,34-57.7,67.3-57.7
c32.2,0,60.3,23.1,66.7,54.4h-22.7C136.4,168.6,118.7,155.4,98.4,155.4"/>
<g>
<g>
<g>
<path d="M679.9,221.1h-59c-8.4,0.4-12.8,4.4-13.2,12.1V260c0.3,7,4.7,10.7,13.2,11.1h58.4c8.1-0.4,12.3-4.2,12.6-11.6v-26.3
C691.6,225.5,687.6,221.4,679.9,221.1z M677.8,255.3c-0.4,3.2-2.5,4.9-6.3,5.3h-43.2c-3.9-0.7-6-2.5-6.3-5.3v-17.4
c0.3-3.9,2.5-6,6.3-6.3H671c4.2,0.4,6.5,2.5,6.8,6.3V255.3z"/>
<path d="M601.4,194.8v-1.2v-13.7c0.3-4.2,2.6-6.5,6.8-6.8h61.6c4.6-0.3,6.8,1.6,6.8,5.8v10.7c0,3.5-2.1,5.3-6.3,5.3h-62.7v10.5
h71.6c7.7,0,11.6-3.5,11.6-10.5v-20.5c0-7.7-3.9-11.6-11.6-11.6H648v-8.4h-14.8v8.4h-33.5c-8.4,0-12.6,4.2-12.6,12.6v40
c0,18.3-2.8,36.7-8.4,55.3l13.7,2.6c6-18.6,9-37,9-55.3v-7.8v-4.8L601.4,194.8L601.4,194.8z"/>
</g>
<g>
<path d="M715.5,211.6l14.4,7.4c8.6-6.1,13.9-14.3,16.8-24.5h25V184h-22.8c0.5-3,0.8-8.7,0.8-12.6h21v-10.5h-39.5v-6.6h-14.8
l-0.2,17.2H735c0,5.6-0.3,9.9-1,12.6h-21.7v10.5h19C729.7,200.7,721.8,209.4,715.5,211.6z"/>
<path d="M748.3,199.3c4.1,7.2,6.6,10.7,9.4,18.7h15.5c-2.6-6.9-6-13.7-9.3-18.7H748.3z"/>
<path d="M809.6,221.6h-80c-7.7,0.7-11.9,4.4-12.6,11.1v20h14.2h3.8h73v4.7c0,3.5-2.3,5.3-6.8,5.3h-63.7c-4.1,0-6.2-1.7-6.3-5
H717v4.5c0.7,6.7,4.4,10.4,11.1,11.1h81.1c8.4-0.4,12.8-4,13.2-11.1v-29C821.5,226.2,817.3,222.3,809.6,221.6z M808,242.1h-76.9
v-3.7c0.3-3.9,2.5-6,6.3-6.3h63.7c4.6,0,6.8,2.1,6.8,6.3v3.7H808z"/>
<path d="M815.9,158.9h-26.3c-7.4,0.4-11.4,3.5-12.1,9.5v37.9c0.7,5.6,4,8.8,10,9.5h28.4c6.3-0.3,9.7-3.5,10-9.5v-37.9
C825.6,162.8,822.3,159.6,815.9,158.9z M811.5,198.1c0,4.6-2.1,6.8-6.3,6.8h-7.6c-3.9-0.3-5.8-2.6-5.8-6.8v-22.4
c0.3-3.9,2.5-6,6.3-6.3h6.6c4.2,0.4,6.5,2.5,6.8,6.3V198.1z"/>
</g>
</g>
</g>
<text transform="matrix(1 0 0 1 24.4574 402.2025)" class="st0 st1">新一代人工智能开源开放平台</text>
</g>
<g>
<path d="M49.3,59.3c3.1,0,5.1,0.3,6.2,0.9c1.1,0.6,1.8,1.3,2,1.9c0.5,0.7,0.3,1.5-0.6,2.4c-0.8,0.6-1.7,1-2.8,1
c-1,0-2.2,0.9-3.4,2.6c-1.8,2.7-2.8,4.2-2.8,4.5c-0.8,1.1-1.1,1.8-1.1,2c0,0.4,1.2,0.4,3.6,0c1.1-0.2,3.1-0.3,5.9-0.3
c1.6,0.3,2.8,0.8,3.4,1.5c0.7,0.8,1.1,1.5,1.1,2.1c0,0.8-0.6,2.4-1.9,4.6c-1.5,1.6-2.4,2.9-2.9,3.9c-0.1,0.3-0.1,0.4-0.1,0.3
c0.1-0.1,0.4-0.3,1-0.6c0.3-0.1,0.8-0.4,1.6-0.9s1.4-0.9,1.8-1.1c1.2-0.6,2.2-0.8,2.8-0.6c0.4,0.5,0.5,1.1,0.2,1.8
c-0.5,1.1-1.6,2.3-3.2,3.4c-0.5,0.3-2.8,1.8-7.1,4.5c-2.8,1.6-4.8,2.4-6.2,2.4c-1.5-0.3-2.6-0.7-3.2-1.3c-0.6-0.6-1.1-2.9-1.5-6.7
L42,82.3l-1.8,4.4c-1.4,3.1-2.2,5.4-2.6,7.1c-0.5,1.5-1.1,2.5-1.5,3c-0.9,0.3-1.6,0.1-2.3-0.6c-0.6-0.5-0.9-0.9-0.9-1.3
s0.2-1.1,0.6-2c0-0.2,1.7-4,5-11.4c3-6.1,5.4-10.5,7.3-13.5c2-3.1,3-4.7,3-4.8C47.8,63,46.5,63,45,63c-3.2,0-5.6,0.3-7.4,0.8
c-2.4,0.5-3.8,0.6-4.1,0.3c-0.4-0.2-0.6-0.7-0.6-1.5c0.2-0.4,1.3-0.9,3.2-1.7c1.8-0.6,3.7-1.1,5.6-1.5L49.3,59.3L49.3,59.3z
M51.3,78.1c-2-0.3-3.5-0.2-4.5,0.2c-0.5,0.3-0.6,2.5-0.3,6.5c0.1,3.4,0.4,5.1,0.7,5.2s1-0.5,2-1.7c1.7-1.7,3.2-3.7,4.3-5.9
c0.3-0.6,0.6-1.3,0.9-1.9c0.3-0.6,0.5-1,0.5-1.3c0.1-0.3,0-0.5-0.1-0.6C54.9,78.3,53.7,78.1,51.3,78.1z M73.4,43.6
c0.5,0,0.8,0.2,0.8,0.5c0.1,0.4,0,1.2-0.1,2.6C74,48.1,73.9,49,74,49.4c0.1,0.4,0.5,0.8,1.2,1.2c0.8,0.5,2.9,0.7,6.4,0.6
c3.4-0.3,5.2-0.1,5.6,0.6c0.5,0.5,0.5,1.1,0,1.9c-0.3,0.8-2.1,2.6-5.6,5.4c-3,2.4-5.6,4.3-7.9,5.6c-0.6,0.4-1.2,0.9-1.7,1.5
s-0.7,1-0.7,1.3c-0.2,0.4-0.5,2.1-0.9,5l-0.2,4c1.3,0,2.9-0.1,4.7-0.2c2.2,0,3.5-0.2,3.9-0.7s0.6-2,0.6-4.6c0-3.5,0.3-5.4,0.9-5.7
c0.8-0.3,1.3-0.1,1.7,0.6c0.5,0.9,0.8,2.8,0.8,5.8c0,2.4,0.1,3.9,0.3,4.3c0.2,0.5,0.8,0.9,1.9,1.2c1.6,0.3,2.6,0.7,2.8,1.2
c0.4,0.6,0.3,1.6-0.2,2.9c-0.2,0.2-0.5,0.4-0.7,0.9c-0.3,0.4-0.6,0.9-1,1.4s-0.7,0.9-0.9,1.1c-1,0.9-1.4,1.5-1.3,1.9
c0,0.4,0.7,0,2.2-1.1c0.9-0.5,2.2-1.1,4-1.7c1.2-0.3,2-0.2,2.2,0.3c0,0.4-0.3,1.1-0.9,1.9c-1,1.2-2.1,2.1-3.3,2.6
c-1.5,0.7-2.3,1.1-2.3,1.3s0.8,1.7,2.3,4.3c1.5,2.7,2.6,4.4,3,5.3c0.5,0.9,0.9,1.3,1,1.3s0.3-0.4,0.4-1.1c0.2-0.5,0.5-3.4,0.9-8.4
c0.3-3.7,0.2-7.6-0.3-11.5c-0.3-2.6-0.9-4.9-1.9-7.1c-0.8-1.3-2.6-3.3-5.3-6s-3.9-4.3-3.6-4.6c0.4-0.4,0.8-0.6,1.1-0.6
c0.6,0,1.8,0.5,3.3,1.5c1.4,0.8,3.2,2.4,5.3,5c1.9,1.9,3.4,3.9,4.3,6.2c0.9,2,1.4,5.1,1.6,9.2c0.3,3.1,0.2,7.8-0.3,14.1
c-0.3,4.8-0.6,7.8-1,9c-0.2,0.2-0.4,0.4-0.7,0.7s-0.6,0.6-1,0.9c-0.8,0-1.7-0.4-2.6-1.3c-0.8-0.8-1.9-2.3-3.3-4.6
c-1.5-2.5-2.6-4.7-3.4-6.7c-0.8-1.6-1.2-2.4-1.3-2.4c-0.2-0.1-0.4,0.6-0.5,2.2l-0.2,2.5c-0.4,4.2-0.4,7.9,0,10.9
c0,3.5-0.2,5.5-0.6,5.9c-0.3,0.5-0.8,0.8-1.7,0.8c-0.8,0-1.3-0.4-1.7-1.1c-0.5-0.3-0.9-1.4-1.3-3.3c0-2.1,0.1-3.4,0.2-3.9
c0.2-0.5,0.4-3,0.8-7.3l0.3-6.2L74.9,96c-1.7,0.8-3.7,1.9-5.8,3.5c-0.6,0.9-1.3,1.4-2,1.4c-0.7,0-1.2-0.4-1.6-1.1
c-0.5-0.9-0.6-4-0.2-9.3c0-3.1,0.3-7.5,0.8-13.2c0.3-3,0.7-5.4,1.1-7.1c0.4-1.2,0.9-2.5,1.7-3.7c0.8-0.8,1.1-1.5,1.1-2.2
c0-0.6,0.8-1.4,2.3-2.3c1.1-0.6,2.7-1.9,4.8-3.9c1.7-1.5,2.4-2.3,2-2.4l-1.2-0.2c-1.6,0-3.2-0.6-4.9-1.9c-1.6-0.8-2.6-1.8-3-3.1
c-0.4-1-0.4-2.1,0-3.2c0.4-1,0.9-1.8,1.5-2.4C71.9,44,72.6,43.6,73.4,43.6z M78.7,80.5c-0.4-0.2-1.9-0.4-4.5-0.4h-5v6.4
c0,4.3,0.2,6.5,0.6,6.5c0.6,0,2.8-1.6,6.7-4.7c1.2-1.4,2-2.3,2.3-2.8s0.4-1.4,0.4-2.5C79.3,81.5,79.1,80.7,78.7,80.5z"/>
<path d="M125.2,58.3c0.2,0,0.6,0,1.3,0.1s1.2,0.1,1.6,0.1c1.3,0.4,1.9,0.8,1.9,1.3c0.1,0.2,0.1,0.5,0,0.8c-0.1,0.3-0.3,0.5-0.6,0.6
c-0.5,0.5-0.7,1-0.7,1.6s0.2,2.2,0.5,4.9c0.2,2.2,0.5,5.3,1.1,9.3l1,3.2c0.2,0.2,0.5,0.1,0.9-0.2c0.4-0.4,1.4-0.6,3.1-0.6
s2.7-0.2,3.1-0.6c0-0.4-0.9-1.3-2.8-2.5c-2.1-1.4-3.4-2.4-3.7-3.1c-0.4-1.2-0.5-1.9-0.2-2c0-0.5,0.3-0.7,0.8-0.7
c0.2,0,2,0.9,5.4,2.8c2.4,1.4,4.2,2.8,5.6,4.1c0.3,1,0.1,2-0.6,2.8c-0.5,0.8-1.4,1.4-2.8,1.8c-1,0.1-1.5,0.2-1.5,0.3
s0.5,0.6,1.5,1.2c1.6,1.1,2.4,2.1,2.4,2.8c0.3,0.9,0.3,1.6,0,2.4c0,0.5-0.6,0.7-1.8,0.7c-0.9,0-2.7-1.3-5.3-3.9
c-2.3-2.6-3.7-3.7-4.1-3.3c0,0.3-0.4,0.6-1.3,1c-0.8,0.4-1.5,0.4-2.3,0c-0.6-0.4-1.1-1.1-1.5-1.9c-0.4-0.9-0.8-3.1-1.1-6.7
c-0.1-1.1-0.3-3-0.6-5.5s-0.4-4.1-0.4-4.8c-0.4-3.2-0.5-5-0.3-5.4C123.9,58.6,124.3,58.3,125.2,58.3z M161.6,59.8
c3.2,0,5.5,0.4,6.7,1.1c1.4,0.6,2,1.4,2,2.3c0,0.9-0.7,1.7-2,2.1c-2.6,1.8-5.8,4.1-9.5,6.7c-3.9,3-6.2,4.8-6.8,5.4s-0.6,1.3,0,2
c0.8,1,1.1,2.3,1.1,3.8c-0.4,1.6-0.4,2.4,0,2.4s1.4-0.3,3-0.9c1.7-0.6,2.6-1.2,2.6-1.6s0.3-0.8,0.9-1.1c1,0,2,0.1,3,0.2
c1.6,0.3,2.8,0.7,3.4,1.3c0.4,0.5,0.4,1.2,0.2,2c-0.6,0.8-1.3,1.1-1.9,1.1c-1.8,0.6-4.2,1.2-7.1,1.7c-3.6,0.9-5.7,1.8-6.2,2.8
c-0.1,0.2-0.3,0.5-0.6,0.9s-0.5,0.8-0.8,1.2c-0.2,0.4-0.6,0.9-0.9,1.5c-0.4,0.6-0.7,1.1-1,1.5c-2.4,3.2-4.4,5.7-5.9,7.3
c-3.6,3-5.4,4.5-5.6,4.7c-1.8,1.1-3.3,1.8-4.3,2c-1.4,0.5-2.5,0.6-3.3,0.3c-1-0.4-2-1.2-2.8-2.6c-1-1.6-1.4-2.5-1.1-2.8
c0.4-0.4,0.8-0.6,1.1-0.6c0.5,0,1,0.5,1.7,1.4c0.6,0.9,1.3,1.4,2.1,1.4c0.9,0,1.9-0.2,3-0.7c0.5-0.3,2.2-1.7,5-4.3
c2.6-2.5,4.4-4.6,5.3-6.5c0.5-0.5,0.9-1,1.3-1.5s0.6-0.9,0.7-1.3s0.1-0.5,0-0.5c0-0.1-0.8,0.4-2.4,1.3s-5.5,3.2-11.6,6.9
c-1.1,0.7-2.1,1.1-3,1.1c-0.9,0-1.7-0.3-2.3-0.9c-0.8-0.6-1.1-1.4-1.1-2.1c0-0.8,0.2-1.4,0.6-1.7c0.4-0.4,1.1-0.6,2.3-0.6
c0.9,0.3,3.2-0.5,7.1-2.4c5.5-3.1,9.7-5.3,12.6-6.4c0.6,0,1-1,1.3-2.9c0.4-1.6,0.4-2.9,0-3.9c-0.1-0.5-0.2-0.8-0.4-1
c-0.1-0.2-0.3-0.2-0.4-0.1c-0.4,0.4-0.8,0.2-1.1-0.6c-0.3-0.5-0.3-0.9-0.2-1.1s0.7-0.8,1.7-1.7c0.2-0.2,0.5-0.4,0.9-0.8
s0.9-0.7,1.3-1.1c0.4-0.4,0.9-0.7,1.4-1.1s0.9-0.7,1.1-1c1.9-1.9,3.9-3.6,5.9-5c2-1.6,2.8-2.5,2.5-2.8l-1.1-0.2
c-1-0.4-3.1-0.4-6.2,0c-1.5,0.2-4.6,0.7-9.3,1.5c-2.3,0.5-4.9,1.3-8,2.4c-2,0.9-3.2,1.1-3.7,0.6c-0.3-0.3-0.3-0.8,0-1.5
c0-0.4,1.1-1.1,3.4-2.3c1.6-1,4-1.8,7.2-2.4c0.9-0.2,2.4-0.6,4.5-1.1s3.6-0.8,4.5-1C157.5,60.1,160.6,59.8,161.6,59.8z M139.9,44.7
c0.8,0,2.1,0.8,4.1,2.4c1.4,1,3.1,2.3,5.3,4.1c1.2,1.1,1.8,2,1.8,2.5s-0.6,1.5-1.9,2.8s-2.7,2.3-4.2,3c-1,0.5-2.6,1-4.8,1.4
c-2,0.4-3.2,0.5-3.6,0.3c-0.8-0.4-1.1-0.8-1.1-1.1c0-0.5,0.9-1,2.7-1.6c0.2-0.1,1.9-0.8,5-2c1.7-0.8,2.6-1.5,2.6-1.9
c0.3-0.5,0.1-1-0.6-1.6c-1.1-0.5-2.5-1.7-4.1-3.4c-1.9-1.9-2.8-3.2-2.6-3.9C138.4,45,138.9,44.7,139.9,44.7z M165.6,92.4
c0.4,0,0.9,0.6,1.6,1.7c0.9,1.2,1.6,2.8,2.3,4.8c0.3,0.5,0.8,2.2,1.5,5c0.5,1.9,0.9,3.9,1.1,6.2c0.4,1.7,0.6,2.9,0.6,3.4
s-0.2,1-0.6,1.3c-0.8,0.8-1.5,1.1-2.3,1.1c-1,0-1.6-0.4-2-1.1c-0.7-0.4-1.2-2.6-1.5-6.7c0-3-0.4-6.3-1.3-10.1
c-0.8-2.8-0.9-4.3-0.6-4.7C164.8,92.7,165.1,92.4,165.6,92.4z"/>
<path d="M209,48.4c1.2,0.6,2.4,1.6,3.3,2.8c1.5,1.8,2.3,2.9,2.3,3.3c0,0.8-0.6,2.1-1.7,4c-1,1.8-3.2,4.5-6.7,8
c-0.5,0.6-1,1.2-1.6,1.8s-1,1.1-1.4,1.5s-0.7,0.7-1,1.1s-0.6,0.7-0.8,0.9c1.2,0.4,2.5,0.8,4.1,1.1c2.2,0.6,3.9,1.2,5,1.7
c0.6,1.1,0.9,1.8,0.9,2c0,1-0.5,2.3-1.5,3.9c-1.2,2.4-2.8,4.4-4.7,6.2c-1.8,2-2.6,3.1-2.2,3.2c0.2,0.1,0.8,0.2,1.9,0.2
c1.6,0,2.8,0.2,3.6,0.6c0.5,0.3,1.2,1,2,2.3c0.3,0.8,0.6,1,1.1,0.7c0.6-0.5,2.3-2.2,5-5.2s4.5-4.9,5.3-5.6s1.8-1.2,3.1-1.5
c1.8-0.3,2.9-0.2,3.4,0.3c0.5,0.3,0.9,1.2,1.3,2.8c0.1,0.4,0.4,1.9,0.9,4.7c0.2,1.1,0.3,1.9,0.5,2.1c0.2,0.3,0.5,0.3,0.9,0.1
c0.4,0,2.3-0.5,5.6-1.6s6.4-1.6,9.4-1.6c2-0.2,3.3-0.3,3.9-0.2s1.2,0.5,2,1.1c0.5,0.6,0.8,1.1,0.8,1.4c0.1,0.3-0.1,0.9-0.5,1.8
c-0.8,1.1-2.1,2.6-4.1,4.5c-0.6,0.5-1.6,1.3-2.7,2.3c-1.2,1-1.8,1.5-2,1.7c-0.7,0.5-1.1,0.8-1.1,0.8c0,0.1,0.5-0.1,1.6-0.3
c0.4,0,1.1-0.1,2.1-0.2c1.1-0.1,1.8-0.2,2.3-0.2c1.5,0,2.6,0.3,3.2,0.9c0.4,0.6,0.1,1.5-0.9,2.6c-1.1,1.1-2.7,1.7-4.7,1.7
c-2.7,0-4.6,0.3-5.6,0.8c-1.1,0.2-3.1,1.1-6.1,2.8c-2.6,1.5-4.1,2.3-4.5,2.3s-0.9-0.2-1.5-0.6c-0.4-0.8-0.6-1.4-0.6-1.9
c0-0.8,0.3-1.6,1-2.4c0.8-0.6,1.1-1.2,1.1-1.5c0-0.4-0.4-1.4-1.1-2.9c-1.4-3.4-2.1-5.1-2.1-5.3c-0.8-3-1.4-5.6-1.8-7.9
c-0.2-0.9-0.4-1.8-0.6-2.8s-0.4-1.8-0.6-2.4c-0.1-0.6-0.2-0.9-0.2-1h-0.6c0,0.1-0.2,0.5-0.6,1.1S222,86,221,87.2s-1.8,2.4-2.6,3.5
c-3.6,4.4-6.3,8.1-8,11.2c-0.6,0.9-1.5,2-2.6,3.5s-1.7,2.4-2.1,2.6c-0.9,1.5-1.8,2.3-2.7,2.3c-1,0-1.7-0.3-2.3-0.9
s-0.9-1.3-0.9-2.3s0.9-2.8,2.8-5.3c1.7-2.8,2.7-4.7,3-5.6c0.3-1.1,0.2-1.7-0.2-1.9c-0.1,0-1.5-0.2-4.2-0.6
c-2.1-0.3-3.5-0.9-4.4-1.8c-0.8-0.8-1.2-1.4-1.2-1.9c0.3-0.6,1.5-1.5,3.5-2.8c2-1.5,3.7-2.9,5-4.5c1-1.4,1.7-2.7,2.1-4
c0.2-0.7,0.2-1.2,0-1.4s-0.7-0.5-1.6-0.8c-0.5-0.1-2.4-0.4-5.6-0.9c-2.4-0.4-3.7-0.8-4.1-1.4c-0.5-0.3-0.6-0.9-0.2-1.8
c0.4-0.8,1.3-1.6,2.8-2.4c1.3-0.9,3.3-2.9,5.9-6.2c2.9-3.7,4.4-6,4.7-6.7c0.6-0.9,0.9-1.5,0.9-1.9s-0.2-1-0.6-1.8
c-0.8-1-1.2-1.6-1.5-1.9c-0.4-0.4-0.6-0.9-0.6-1.4c0-0.6,0.2-1,0.6-1.4C207.5,48.6,208.1,48.4,209,48.4z M235.9,44.7
c0.2,0,0.9,0.6,2,1.8c1,1.1,1.4,2.4,1.1,3.8l-1.1,4.5c-0.6,1.5-0.9,2.4-0.9,2.9c0,0.4,0.3,0.6,0.9,0.6s2.2,0.6,4.7,1.9
c4.6,2.6,7.1,4.1,7.4,4.3c2,1.4,4.8,3.7,8.4,6.9c3,3,4.5,4.6,4.5,4.9l-1.1,2.1c-0.8,1.2-1.5,1.6-2.3,1.2c-0.7-0.4-2.1-1.6-4.2-3.6
c-2.2-2.2-3-3.4-2.4-3.7c0.1-0.1,0.2-0.1,0.2-0.2c0-0.1-0.1-0.3-0.4-0.6c-0.2-0.3-0.5-0.6-0.8-0.8c-0.3-0.2-0.7-0.6-1.3-1.1
c-0.5-0.5-1.1-1-1.8-1.5s-1.4-1-2.1-1.5s-1.2-0.9-1.4-1.1c-0.3-0.1-1.1-0.6-2.6-1.4c-1.5-0.8-2.7-1.5-3.5-1.9
c-2.6-1.1-4-1.6-4.1-1.4c-0.1,0.1-0.2,0.2-0.3,0.4c-0.1,0.1-0.2,0.3-0.4,0.6s-0.4,0.6-0.6,0.9c-0.2,0.3-0.5,0.6-0.7,1
c-0.3,0.4-0.6,0.8-0.9,1.1c-0.8,1.1-3.3,4-7.5,8.7c-3.7,3.6-6.5,6.1-8.4,7.5c-2,1.6-3.2,2.4-3.7,2.4c-0.4,0-0.8-0.2-1.3-0.6
c-0.6-0.7-0.8-1.2-0.6-1.5c0.6-0.6,1.7-1.5,3.3-2.6c1.8-1,4.2-3.3,7.3-6.7c2.8-2.8,5.5-5.8,8.2-9.2c2-2.7,3.5-5,4.5-6.8
c0.5-0.9,0.9-2.3,1.4-4.1c0.2-1.1,0.1-2.4-0.2-3.7c-0.3-0.8-0.3-1.6,0-2.4C235.4,45,235.6,44.7,235.9,44.7z M246.6,73
c1.1,0.4,1.7,0.7,1.9,0.9c0.6,0.5,0.9,1,0.9,1.4c0,0.6-0.7,1.6-2.2,3c-1.4,1.7-2.4,2.4-3.2,2c-0.5,0-0.8-0.6-0.8-1.7
c0-0.6-0.2-1-0.6-1.1s-1.8-0.1-4.2,0c-3.5,0.3-6.2,0.8-8.2,1.7c-2.1,1-3.6,1.5-4.5,1.5c-0.8,0-1.1-0.4-1.1-1.1
c0-0.9,0.5-1.6,1.5-2.4c1.2-0.8,3-1.5,5.3-2.1c3-0.9,5.9-1.4,8.8-1.7C242,73.1,244.1,73,246.6,73z M245.8,91.9
c-0.9,0-2.4,0.2-4.3,0.6c-0.5,0.1-2.6,0.7-6.2,1.7c-1.6,0.7-2.6,1.2-2.8,1.5s-0.3,0.9-0.2,1.9c0,1,0.4,2.4,1.1,4.1l1.5,2.6
c0.4,0,2.1-1.3,5-3.9c3-2.6,4.9-4.6,5.8-5.8c1-1,1.5-1.8,1.5-2.4C246.8,91.9,246.4,91.9,245.8,91.9z"/>
<path d="M314.6,41.3c0.2,0,1,0.6,2.6,1.7c1,1,1.6,2,2,3.2c0.2,0.6-0.4,3.2-1.7,7.8c-1.1,3.5-2.3,7.1-3.6,10.7
c-0.3,0.6-0.6,1.3-0.9,2c-0.3,0.7-0.5,1.3-0.6,1.6c-0.1,0.3-0.2,0.5-0.2,0.6c0,0.3,2.4,0.3,7.1,0c5,0,8.1,0.3,9.5,0.8
c1.5,0.6,3,1.6,4.5,3c1.2,1.7,2,3.1,2.3,4.1s0.1,3-0.6,6.2c-0.9,3.4-1.8,6-2.8,8c-1.6,3.4-2.7,5.4-3.3,6.2
c-0.6,1.2-2.4,3.2-5.3,6.2l-5.4,5.4c-1.1,0.8-2,1.1-2.6,1.1s-2.3-0.6-5.3-1.7c-2.5-1.1-4.5-2.1-5.9-3c-0.8-0.8-1.1-1.4-1.1-1.8
c0.4-0.3,1.8-0.1,4.3,0.6c2.5,0.6,4.5,0.8,6.2,0.6c1.2-0.2,2.2-0.5,3-1c0.7-0.4,2-1.6,3.7-3.5c2.6-2.7,4.7-5.5,6.2-8.5
c1.4-2.5,2.5-5,3.4-7.5c0.6-1.7,0.9-3.4,0.9-5c0-1.5-0.3-2.5-0.9-3.1c-0.5-0.9-1.8-1.7-3.7-2.3c-1.9-0.6-4.9-0.9-8.9-0.9l-6.4,0.3
l-3,8.2c-1.4,3.5-3,7.4-4.9,11.8c-0.4,0.8-1.1,2.2-2.3,4.2c-1.1,2.1-1.8,3.3-2.1,3.8c-3.4,5.1-5.3,8-5.9,8.8
c-2,2.7-3.3,4.2-3.9,4.5c-0.5,0.4-1.2,0.4-2.3,0c-0.8-0.4-1.1-0.9-1.1-1.5c0-1,0.3-2,0.8-3.2c0.6-1.5,1.2-2.1,1.8-1.8
c0.3,0.2,0.7,0,1.3-0.8c0.6-0.7,1.5-2,2.9-3.9c2.6-4.2,4.8-8.1,6.7-11.8c3.6-7.7,5.4-11.8,5.6-12.3c1.5-3.9,2.1-6,2-6.2
c-2.3,0-4.7,0.2-7.1,0.6c-1,0.1-2.3,0.1-4.1,0.2c-1.8,0.1-3.3,0.2-4.7,0.3c-1.4,0.1-2.5,0.2-3.3,0.3c-2.5,0.2-4,0.3-4.5,0.2
s-0.8-0.4-0.8-1c0-0.7,0.7-1.2,2-1.5c6.7-1.1,13-1.8,18.9-2.2c2.4-0.3,4-0.7,4.6-1.3s1.6-2.5,2.8-5.7c0.3-0.9,0.6-1.9,0.9-3
s0.7-2.1,1-3c0.4-0.9,0.7-1.8,1-2.8s0.6-1.7,0.8-2.2c0.6-2.4,1-3.9,1-4.4c0-0.6-0.3-1.4-1-2.6c-0.7-1.3-0.9-2.2-0.6-2.8
C314,41.5,314.5,41.3,314.6,41.3z"/>
<path d="M391.5,52.1c0.6,0,1.2,0.2,1.6,0.6c0.1,0,0.2,0,0.3,0.1s0.3,0.2,0.4,0.4s0.1,0.3,0.1,0.4c0.2,0.4,0.1,3.6-0.2,9.6
c-0.8,6.3-1.2,12.5-1.2,18.8c-0.3,7-0.6,10.8-1,11.4c-0.3,0.6-0.8,1.2-1.7,1.6c-0.9,0-1.4-0.2-1.7-0.7c-0.6-0.9-0.9-3-0.9-6.5
c0-2.5,0.4-8.9,1.1-19.4c0.2-1.4,0.4-2.8,0.5-4.4s0.3-2.9,0.4-4.1c0.1-1.2,0.2-2.3,0.4-3.3c0.1-1,0.2-1.9,0.3-2.5
c0.1-0.6,0.1-1,0.2-1.3C390.4,52.4,390.9,52.1,391.5,52.1z M391.1,99.5c1.2,0.9,1.9,1.6,1.9,2.1c0.3,0.6,0.1,1.4-0.6,2.4
c-0.5,0.9-0.9,1.4-1.2,1.4c-0.2,0-0.9-0.2-2-0.6c-0.1-0.1-0.3-0.3-0.6-0.5c-0.3-0.2-0.5-0.4-0.6-0.6s-0.2-0.3-0.2-0.4
c-0.2-0.2-0.2-0.8-0.2-1.9c0.4-1,0.8-1.6,1.4-1.9C389.4,99,390.1,99,391.1,99.5z"/>
</g>
</svg>

+ 149
- 1
custom/public/img/logo-w.svg View File

@@ -1 +1,149 @@
<svg id="图层_1" data-name="图层 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 120 50"><defs><style>.cls-1{fill:#5bb973;}.cls-2{fill:#fff;}</style></defs><title>logo-w</title><path class="cls-1" d="M42.08,12.13l-15.65-9a4.44,4.44,0,0,0-4.43,0l-15.65,9A4.44,4.44,0,0,0,4.13,16V34a4.44,4.44,0,0,0,2.22,3.84l3.83,2.21a2.88,2.88,0,0,0-.06.59,2.95,2.95,0,1,0,1-2.17L7.26,36.3A2.63,2.63,0,0,1,6,34V16A2.62,2.62,0,0,1,7.26,13.7l15.65-9a2.59,2.59,0,0,1,2.61,0l15.65,9A2.63,2.63,0,0,1,42.47,16V34a2.64,2.64,0,0,1-1.3,2.27l-15.65,9a3.16,3.16,0,0,1-.5.22V39.88a1.31,1.31,0,0,0,0-.28V36.35l.1,0,9-6a.92.92,0,0,0,.41-.75V20a3,3,0,1,0-1.82,0v9L25,34.18V15.94a3,3,0,1,0-1.82,0v12l-5.08-2.95V20.19a3,3,0,1,0-1.82,0v5.26a.9.9,0,0,0,.45.79L23.2,30v8.16L12.11,31.73V23.84a2.93,2.93,0,1,0-1.82,0v8.41a.94.94,0,0,0,.46.79l12.45,7.2v6.34a.22.22,0,0,0,0,.08v.09l0,.1s0,0,0,.07l.06.1s0,0,0,0l.08.1,0,0a.39.39,0,0,0,.1.08l0,0,.12.07,0,0,.14,0h0a.39.39,0,0,0,.15,0h.16a4.44,4.44,0,0,0,2.22-.59l15.65-9A4.46,4.46,0,0,0,44.29,34V16a4.45,4.45,0,0,0-2.21-3.83m-29,27.42a1.12,1.12,0,1,1-1.12,1.12,1.12,1.12,0,0,1,1.12-1.12m20.6-23.46a1.12,1.12,0,1,1-1.12,1.12,1.12,1.12,0,0,1,1.12-1.12m-16.45.19a1.12,1.12,0,1,1-1.12,1.12,1.12,1.12,0,0,1,1.12-1.12m-6,3.65a1.12,1.12,0,1,1-1.12,1.12,1.12,1.12,0,0,1,1.12-1.12M24.13,12A1.12,1.12,0,1,1,23,13.14,1.12,1.12,0,0,1,24.13,12"/><path class="cls-2" d="M81.68,26.69H67.86c-2,.1-3.06,1-3.06,2.85v6.33c.1,1.68,1.16,2.53,3.06,2.63H81.57c1.9-.1,2.85-1,3-2.74V29.54A2.71,2.71,0,0,0,81.68,26.69Zm-.53,8c-.11.73-.53,1.16-1.48,1.26H69.54c-1-.21-1.37-.53-1.47-1.26V30.59a1.44,1.44,0,0,1,1.47-1.48h10a1.54,1.54,0,0,1,1.58,1.48Z"/><path class="cls-2" d="M63.21,20.57V17A1.63,1.63,0,0,1,64.8,15.4H79.25c1.06-.11,1.58.42,1.58,1.37V19.3c0,.85-.52,1.27-1.47,1.27H64.59V23H81.47c1.79,0,2.74-.85,2.74-2.43V15.72A2.45,2.45,0,0,0,81.47,13H74.08V11H70.6v2H62.69c-2,0-3,.95-3,3v9.39a42.65,42.65,0,0,1-2,13l3.27.64a43.43,43.43,0,0,0,2.11-13V20.57Z"/><path class="cls-2" d="M90,24.47l3.38,1.8a10.1,10.1,0,0,0,3.9-5.81h5.91V18H97.82a25.57,25.57,0,0,0,.21-3h5V12.55H93.7V11H90.22v4h4.43a17.38,17.38,0,0,1-.21,2.95H89.38v2.53h4.43A7.05,7.05,0,0,1,90,24.47Z"/><path class="cls-2" d="M97.82,21.62A21.82,21.82,0,0,1,100,26.05h3.7a25.8,25.8,0,0,0-2.22-4.43Z"/><path class="cls-2" d="M112.17,26.79H93.39c-1.8.21-2.85,1.06-3,2.64v4.75H112v1c0,.85-.52,1.27-1.58,1.27h-15c-1,0-1.48-.42-1.48-1.16H90.54v1A2.75,2.75,0,0,0,93.18,39h19.09c2-.1,3.06-1,3.06-2.64V29.54A3.21,3.21,0,0,0,112.17,26.79Zm-.32,4.86h-18V30.8a1.43,1.43,0,0,1,1.48-1.47h15c1,0,1.58.52,1.58,1.47Z"/><path class="cls-2" d="M113.64,12h-6.22q-2.53.17-2.85,2.22V23.1a2.33,2.33,0,0,0,2.32,2.22h6.65c1.47-.11,2.32-.85,2.32-2.22V14.24C116,13,115.23,12.23,113.64,12Zm-1,9.29c0,1.05-.53,1.58-1.48,1.58h-1.79A1.39,1.39,0,0,1,108,21.31V16a1.42,1.42,0,0,1,1.47-1.47H111A1.53,1.53,0,0,1,112.59,16Z"/></svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 27.9.0, 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,42H87.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,42,105.4,42,105.3,42z M104.6,52.6c-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.9h0.1h13.1c1.1,0,2,0.8,2.1,2L104.6,52.6z"/>
<path class="st0" d="M81,34v-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.6V24H80.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.2H81V34z"/>
<path class="st0" d="M116.2,39.1l4.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.2H121v-2h-4.6v5.1
h5.8c0,1.3-0.1,2.6-0.3,3.9h-6.7v3.3h5.8C120.4,36.2,118.6,38.1,116.2,39.1z"/>
<path class="st0" d="M126.5,35.4c1.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,42.2h-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
H117v1.3c0,1.9,1.6,3.5,3.5,3.6h25.1c2.6-0.1,4-1.3,4-3.5v-9C149.2,43.7,147.5,42.2,145.4,42.2z M145,48.6h-23.7v-1.1
c0-1,0.8-1.9,1.8-1.9h0.1H143c1.3,0,2.1,0.7,2.1,1.9L145,48.6z"/>
<path class="st0" d="M147.3,22.7h-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.9V25.7
C150.4,24.1,149.4,23,147.3,22.7z M146,34.9c0,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.9h0.1h2c1.1,0,2.1,0.8,2.1,1.9l0,0L146,34.9z"/>
</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.9v0.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.9c-0.6-0.3-1.7-0.5-2.9-0.4
c-0.9,0.1-1,0.6-2.8-0.5L18.3,46c-0.8-0.5-1.7-1.3-1.6-2.3V22c0-1.8,0.8-2.8,2.1-3.5L37,7.8C38.1,7,40.2,7.2,41.5,8l16.4,9.5
c2.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.3v-2.5c0-0.1,0-0.3,0-0.4v-2.5c0-0.8,0.4-1.6,1.1-2
l8.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.6c-0.8-1.4-2.4-2.2-4-2c-1.6,0.1-2.8,1-3.5,2.4
c-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.2c0,1.1-0.5,2.1-1.5,2.7l-5.5,2.8c-0.7,0.5-1.6,0-1.6-0.8
l-0.2-17.1c0-1,0.5-1.9,1.2-2.5c0.4-0.3,0.7-0.8,1-1.3c0.7-1.3,0.7-2.9-0.1-4.3s-2.5-2.2-4.1-2.1c-2.9,0.3-4.6,3.1-3.8,5.7
c0.2,0.8,0.7,1.4,1.3,2c0.7,0.6,1.2,1.5,1.2,2.5v9.9c0,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.2
c0-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-2c-2.9,0.2-4.7,3.1-3.8,5.7c0.2,0.8,0.7,1.5,1.3,2
c0.7,0.6,1.2,1.5,1.2,2.5v3.5c0,1,0,2,1.6,3.1l5.5,3c0.7,0.4,1.2,1.2,1.2,2.1v4.5c0,0.8-0.8,1.3-1.5,0.9L26.6,41
c-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.3c-0.8-1.4-2.4-2.3-4-2.2c-2.9,0.2-4.6,3-3.8,5.6
c0.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.5l11.8,6.3c1,0.6,1.6,1.7,1.6,2.8v3.1
c0,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,2s-0.9,2-2,2
s-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-2s2,0.9,2,2s-0.9,2-2,2S48.7,22.6,48.7,21.5z M30.6,23.8
c-1.1,0-2-0.9-2-2s0.9-2,2-2s2,0.9,2,2S31.8,23.8,30.6,23.8z M22.2,26.4c0-1.1,0.9-2,2-2s2,0.9,2,2s-0.9,2-2,2S22.2,27.5,22.2,26.4z
M40,17.9c-1.1,0-2-0.9-2-2s0.9-2,2-2s2,0.9,2,2S41.1,17.9,40,17.9z"/>
<g>
<path class="st0" d="M75.7,5.4c0.7,0,1.2,0.1,1.5,0.2c0.3,0.1,0.4,0.3,0.5,0.5c0.1,0.2,0.1,0.4-0.1,0.6c-0.2,0.1-0.4,0.2-0.7,0.2
c-0.2,0-0.5,0.2-0.8,0.6c-0.4,0.6-0.7,1-0.7,1.1c-0.2,0.3-0.3,0.4-0.3,0.5c0,0.1,0.3,0.1,0.9,0C76.3,9,76.8,9,77.4,9
c0.4,0.1,0.7,0.2,0.8,0.4c0.2,0.2,0.3,0.4,0.3,0.5c0,0.2-0.1,0.6-0.5,1.1c-0.4,0.4-0.6,0.7-0.7,0.9c0,0.1,0,0.1,0,0.1
c0,0,0.1-0.1,0.2-0.1c0.1,0,0.2-0.1,0.4-0.2c0.2-0.1,0.3-0.2,0.4-0.3c0.3-0.1,0.5-0.2,0.7-0.1c0.1,0.1,0.1,0.3,0,0.4
c-0.1,0.3-0.4,0.6-0.8,0.8c-0.1,0.1-0.7,0.4-1.7,1.1c-0.7,0.4-1.2,0.6-1.5,0.6c-0.4-0.1-0.6-0.2-0.8-0.3c-0.1-0.1-0.3-0.7-0.4-1.6
l0-1.3L73.6,12c-0.3,0.7-0.5,1.3-0.6,1.7c-0.1,0.4-0.3,0.6-0.4,0.7c-0.2,0.1-0.4,0-0.6-0.1c-0.1-0.1-0.2-0.2-0.2-0.3
c0-0.1,0-0.3,0.1-0.5c0,0,0.4-1,1.2-2.7c0.7-1.5,1.3-2.5,1.8-3.2c0.5-0.7,0.7-1.1,0.7-1.2c-0.2,0-0.6,0-0.9,0
c-0.8,0-1.3,0.1-1.8,0.2c-0.6,0.1-0.9,0.1-1,0.1c-0.1,0-0.1-0.2-0.1-0.4c0-0.1,0.3-0.2,0.8-0.4c0.4-0.1,0.9-0.3,1.3-0.4L75.7,5.4
L75.7,5.4z M76.2,9.9c-0.5-0.1-0.8,0-1.1,0C75,10,75,10.5,75.1,11.5c0,0.8,0.1,1.2,0.2,1.2s0.2-0.1,0.5-0.4c0.4-0.4,0.8-0.9,1-1.4
c0.1-0.1,0.1-0.3,0.2-0.5c0.1-0.1,0.1-0.2,0.1-0.3c0-0.1,0-0.1,0-0.1C77.1,9.9,76.8,9.9,76.2,9.9z M81.5,1.6c0.1,0,0.2,0,0.2,0.1
c0,0.1,0,0.3,0,0.6c0,0.3,0,0.6,0,0.6c0,0.1,0.1,0.2,0.3,0.3c0.2,0.1,0.7,0.2,1.5,0.1c0.8-0.1,1.2,0,1.3,0.1C85,3.7,85,3.9,84.8,4
c-0.1,0.2-0.5,0.6-1.3,1.3c-0.7,0.6-1.3,1-1.9,1.3c-0.1,0.1-0.3,0.2-0.4,0.4S81,7.3,81,7.4c0,0.1-0.1,0.5-0.2,1.2l0,1
c0.3,0,0.7,0,1.1,0c0.5,0,0.8,0,0.9-0.2C82.9,9.2,83,8.8,83,8.2c0-0.8,0.1-1.3,0.2-1.4c0.2-0.1,0.3,0,0.4,0.1
c0.1,0.2,0.2,0.7,0.2,1.4c0,0.6,0,0.9,0.1,1c0,0.1,0.2,0.2,0.5,0.3c0.4,0.1,0.6,0.2,0.7,0.3c0.1,0.1,0.1,0.4,0,0.7
c0,0-0.1,0.1-0.2,0.2c-0.1,0.1-0.1,0.2-0.2,0.3c-0.1,0.1-0.2,0.2-0.2,0.3c-0.2,0.2-0.3,0.4-0.3,0.5c0,0.1,0.2,0,0.5-0.3
c0.2-0.1,0.5-0.3,1-0.4c0.3-0.1,0.5,0,0.5,0.1c0,0.1-0.1,0.3-0.2,0.5c-0.2,0.3-0.5,0.5-0.8,0.6c-0.4,0.2-0.6,0.3-0.6,0.3
s0.2,0.4,0.6,1c0.4,0.6,0.6,1.1,0.7,1.3c0.1,0.2,0.2,0.3,0.2,0.3s0.1-0.1,0.1-0.3c0-0.1,0.1-0.8,0.2-2c0.1-0.9,0-1.8-0.1-2.8
c-0.1-0.6-0.2-1.2-0.5-1.7c-0.2-0.3-0.6-0.8-1.3-1.4s-0.9-1-0.9-1.1c0.1-0.1,0.2-0.1,0.3-0.1c0.1,0,0.4,0.1,0.8,0.4
c0.3,0.2,0.8,0.6,1.3,1.2c0.5,0.5,0.8,0.9,1,1.5c0.2,0.5,0.3,1.2,0.4,2.2c0.1,0.7,0,1.9-0.1,3.4c-0.1,1.2-0.1,1.9-0.2,2.2
c0,0-0.1,0.1-0.2,0.2c-0.1,0.1-0.1,0.1-0.2,0.2c-0.2,0-0.4-0.1-0.6-0.3c-0.2-0.2-0.5-0.6-0.8-1.1c-0.4-0.6-0.6-1.1-0.8-1.6
c-0.2-0.4-0.3-0.6-0.3-0.6c0,0-0.1,0.1-0.1,0.5l0,0.6c-0.1,1-0.1,1.9,0,2.6c0,0.8,0,1.3-0.1,1.4c-0.1,0.1-0.2,0.2-0.4,0.2
c-0.2,0-0.3-0.1-0.4-0.3c-0.1-0.1-0.2-0.3-0.3-0.8c0-0.5,0-0.8,0-0.9c0-0.1,0.1-0.7,0.2-1.8l0.1-1.5l-1.1,0.6
c-0.4,0.2-0.9,0.5-1.4,0.8c-0.1,0.2-0.3,0.3-0.5,0.3s-0.3-0.1-0.4-0.3c-0.1-0.2-0.1-1,0-2.2c0-0.7,0.1-1.8,0.2-3.2
C79.8,9,79.9,8.4,80,8c0.1-0.3,0.2-0.6,0.4-0.9c0.2-0.2,0.3-0.4,0.3-0.5c0-0.1,0.2-0.3,0.6-0.6c0.3-0.1,0.6-0.5,1.2-0.9
c0.4-0.4,0.6-0.6,0.5-0.6l-0.3,0c-0.4,0-0.8-0.1-1.2-0.5c-0.4-0.2-0.6-0.4-0.7-0.7c-0.1-0.2-0.1-0.5,0-0.8c0.1-0.2,0.2-0.4,0.4-0.6
C81.2,1.7,81.3,1.6,81.5,1.6z M82.8,10.5c-0.1,0-0.5-0.1-1.1-0.1h-1.2v1.5c0,1,0,1.6,0.1,1.6c0.1,0,0.7-0.4,1.6-1.1
c0.3-0.3,0.5-0.6,0.6-0.7c0.1-0.1,0.1-0.3,0.1-0.6C82.9,10.7,82.9,10.5,82.8,10.5z"/>
<path class="st0" d="M93.9,5.2c0,0,0.1,0,0.3,0c0.2,0,0.3,0,0.4,0c0.3,0.1,0.5,0.2,0.5,0.3c0,0,0,0.1,0,0.2c0,0.1-0.1,0.1-0.1,0.1
c-0.1,0.1-0.2,0.2-0.2,0.4s0,0.5,0.1,1.2c0,0.5,0.1,1.3,0.3,2.2l0.2,0.8c0,0,0.1,0,0.2,0c0.1-0.1,0.3-0.1,0.7-0.1s0.6,0,0.7-0.1
c0-0.1-0.2-0.3-0.7-0.6c-0.5-0.3-0.8-0.6-0.9-0.7c-0.1-0.3-0.1-0.5,0-0.5c0-0.1,0.1-0.2,0.2-0.2c0,0,0.5,0.2,1.3,0.7
c0.6,0.3,1,0.7,1.3,1c0.1,0.2,0,0.5-0.1,0.7c-0.1,0.2-0.3,0.3-0.7,0.4c-0.2,0-0.4,0-0.4,0.1s0.1,0.1,0.4,0.3
c0.4,0.3,0.6,0.5,0.6,0.7c0.1,0.2,0.1,0.4,0,0.6c0,0.1-0.1,0.2-0.4,0.2c-0.2,0-0.6-0.3-1.3-0.9c-0.6-0.6-0.9-0.9-1-0.8
c0,0.1-0.1,0.1-0.3,0.2c-0.2,0.1-0.4,0.1-0.6,0c-0.1-0.1-0.3-0.3-0.4-0.5c-0.1-0.2-0.2-0.7-0.3-1.6c0-0.3-0.1-0.7-0.1-1.3
c-0.1-0.6-0.1-1-0.1-1.2c-0.1-0.8-0.1-1.2-0.1-1.3C93.6,5.2,93.7,5.2,93.9,5.2z M102.7,5.5c0.8,0,1.3,0.1,1.6,0.3
c0.3,0.1,0.5,0.3,0.5,0.6c0,0.2-0.2,0.4-0.5,0.5c-0.6,0.4-1.4,1-2.3,1.6c-0.9,0.7-1.5,1.2-1.6,1.3c-0.1,0.1-0.1,0.3,0,0.5
c0.2,0.2,0.3,0.6,0.3,0.9c-0.1,0.4-0.1,0.6,0,0.6s0.3-0.1,0.7-0.2c0.4-0.1,0.6-0.3,0.6-0.4c0-0.1,0.1-0.2,0.2-0.3
c0.2,0,0.5,0,0.7,0c0.4,0.1,0.7,0.2,0.8,0.3c0.1,0.1,0.1,0.3,0,0.5c-0.1,0.2-0.3,0.3-0.5,0.3c-0.4,0.1-1,0.3-1.7,0.4
c-0.9,0.2-1.4,0.4-1.5,0.7c0,0-0.1,0.1-0.1,0.2c-0.1,0.1-0.1,0.2-0.2,0.3c0,0.1-0.1,0.2-0.2,0.4c-0.1,0.1-0.2,0.3-0.2,0.4
c-0.6,0.8-1.1,1.4-1.4,1.8c-0.9,0.7-1.3,1.1-1.3,1.1c-0.4,0.3-0.8,0.4-1,0.5c-0.3,0.1-0.6,0.1-0.8,0.1c-0.2-0.1-0.5-0.3-0.7-0.6
c-0.2-0.4-0.3-0.6-0.3-0.7c0.1-0.1,0.2-0.1,0.3-0.1c0.1,0,0.2,0.1,0.4,0.3c0.1,0.2,0.3,0.3,0.5,0.3c0.2,0,0.5,0,0.7-0.2
c0.1-0.1,0.5-0.4,1.2-1c0.6-0.6,1.1-1.1,1.3-1.6c0.1-0.1,0.2-0.2,0.3-0.4c0.1-0.1,0.1-0.2,0.2-0.3c0-0.1,0-0.1,0-0.1
c0,0-0.2,0.1-0.6,0.3s-1.3,0.8-2.8,1.7c-0.3,0.2-0.5,0.3-0.7,0.3c-0.2,0-0.4-0.1-0.6-0.2c-0.2-0.1-0.3-0.3-0.3-0.5
c0-0.2,0-0.3,0.1-0.4c0.1-0.1,0.3-0.1,0.6-0.1c0.2,0.1,0.8-0.1,1.7-0.6c1.3-0.7,2.3-1.3,3-1.5c0.1,0,0.2-0.2,0.3-0.7
c0.1-0.4,0.1-0.7,0-0.9c0-0.1,0-0.2-0.1-0.2c0,0-0.1,0-0.1,0c-0.1,0.1-0.2,0-0.3-0.1C99,10,99,9.9,99,9.9s0.2-0.2,0.4-0.4
c0,0,0.1-0.1,0.2-0.2c0.1-0.1,0.2-0.2,0.3-0.3c0.1-0.1,0.2-0.2,0.3-0.3c0.1-0.1,0.2-0.2,0.3-0.2c0.5-0.5,0.9-0.9,1.4-1.2
c0.5-0.4,0.7-0.6,0.6-0.7l-0.3,0c-0.2-0.1-0.7-0.1-1.5,0c-0.4,0-1.1,0.2-2.2,0.4c-0.6,0.1-1.2,0.3-1.9,0.6
c-0.5,0.2-0.8,0.3-0.9,0.1c-0.1-0.1-0.1-0.2,0-0.4c0-0.1,0.3-0.3,0.8-0.6c0.4-0.2,1-0.4,1.7-0.6c0.2,0,0.6-0.1,1.1-0.3
s0.9-0.2,1.1-0.2C101.7,5.6,102.4,5.5,102.7,5.5z M97.5,1.9c0.2,0,0.5,0.2,1,0.6c0.3,0.2,0.7,0.6,1.3,1c0.3,0.3,0.4,0.5,0.4,0.6
c0,0.1-0.1,0.4-0.5,0.7c-0.3,0.3-0.6,0.6-1,0.7c-0.2,0.1-0.6,0.2-1.2,0.3c-0.5,0.1-0.8,0.1-0.9,0.1c-0.2-0.1-0.3-0.2-0.3-0.3
c0-0.1,0.2-0.2,0.6-0.4c0,0,0.5-0.2,1.2-0.5c0.4-0.2,0.6-0.4,0.6-0.5c0.1-0.1,0-0.2-0.1-0.4c-0.3-0.1-0.6-0.4-1-0.8
c-0.5-0.5-0.7-0.8-0.6-0.9C97.1,2,97.2,1.9,97.5,1.9z M103.6,13.3c0.1,0,0.2,0.1,0.4,0.4c0.2,0.3,0.4,0.7,0.6,1.2
c0.1,0.1,0.2,0.5,0.4,1.2c0.1,0.5,0.2,0.9,0.3,1.5c0.1,0.4,0.1,0.7,0.1,0.8s0,0.2-0.1,0.3c-0.2,0.2-0.4,0.3-0.6,0.3
c-0.2,0-0.4-0.1-0.5-0.3c-0.2-0.1-0.3-0.6-0.4-1.6c0-0.7-0.1-1.5-0.3-2.4c-0.2-0.7-0.2-1-0.1-1.1C103.4,13.4,103.5,13.3,103.6,13.3
z"/>
<path class="st0" d="M114,2.8c0.3,0.1,0.6,0.4,0.8,0.7c0.4,0.4,0.6,0.7,0.6,0.8c0,0.2-0.1,0.5-0.4,1c-0.2,0.4-0.8,1.1-1.6,1.9
c-0.1,0.1-0.2,0.3-0.4,0.4c-0.1,0.1-0.2,0.3-0.3,0.4c-0.1,0.1-0.2,0.2-0.2,0.3c-0.1,0.1-0.1,0.2-0.2,0.2c0.3,0.1,0.6,0.2,1,0.3
c0.5,0.1,0.9,0.3,1.2,0.4c0.1,0.3,0.2,0.4,0.2,0.5c0,0.2-0.1,0.6-0.4,0.9c-0.3,0.6-0.7,1.1-1.1,1.5c-0.4,0.5-0.6,0.7-0.5,0.8
c0,0,0.2,0,0.5,0c0.4,0,0.7,0,0.9,0.1c0.1,0.1,0.3,0.2,0.5,0.6c0.1,0.2,0.1,0.2,0.3,0.2c0.1-0.1,0.6-0.5,1.2-1.2s1.1-1.2,1.3-1.3
c0.2-0.2,0.4-0.3,0.7-0.4c0.4-0.1,0.7,0,0.8,0.1c0.1,0.1,0.2,0.3,0.3,0.7c0,0.1,0.1,0.5,0.2,1.1c0,0.3,0.1,0.5,0.1,0.5
c0,0.1,0.1,0.1,0.2,0c0.1,0,0.6-0.1,1.3-0.4c0.8-0.3,1.5-0.4,2.3-0.4c0.5,0,0.8-0.1,0.9,0c0.1,0,0.3,0.1,0.5,0.3
c0.1,0.1,0.2,0.3,0.2,0.3c0,0.1,0,0.2-0.1,0.4c-0.2,0.3-0.5,0.6-1,1.1c-0.1,0.1-0.4,0.3-0.6,0.6c-0.3,0.2-0.4,0.4-0.5,0.4
c-0.2,0.1-0.3,0.2-0.3,0.2c0,0,0.1,0,0.4-0.1c0.1,0,0.3,0,0.5,0c0.3,0,0.4,0,0.6,0c0.4,0,0.6,0.1,0.8,0.2c0.1,0.1,0,0.4-0.2,0.6
c-0.3,0.3-0.6,0.4-1.1,0.4c-0.6,0-1.1,0.1-1.3,0.2c-0.3,0-0.7,0.3-1.5,0.7c-0.6,0.4-1,0.6-1.1,0.6s-0.2,0-0.4-0.1
c-0.1-0.2-0.1-0.3-0.1-0.5c0-0.2,0.1-0.4,0.2-0.6c0.2-0.1,0.3-0.3,0.3-0.4c0-0.1-0.1-0.3-0.3-0.7c-0.3-0.8-0.5-1.2-0.5-1.3
c-0.2-0.7-0.3-1.3-0.4-1.9c0-0.2-0.1-0.4-0.1-0.7c0-0.2-0.1-0.4-0.1-0.6c0-0.1,0-0.2,0-0.2h-0.1c0,0,0,0.1-0.1,0.3
c-0.1,0.1-0.3,0.4-0.5,0.7c-0.2,0.3-0.4,0.6-0.6,0.8c-0.9,1.1-1.5,1.9-1.9,2.7c-0.1,0.2-0.4,0.5-0.6,0.8c-0.3,0.4-0.4,0.6-0.5,0.6
c-0.2,0.4-0.4,0.6-0.6,0.6c-0.2,0-0.4-0.1-0.6-0.2c-0.1-0.1-0.2-0.3-0.2-0.6c0-0.2,0.2-0.7,0.7-1.3c0.4-0.7,0.6-1.1,0.7-1.3
c0.1-0.3,0-0.4,0-0.5c0,0-0.4,0-1-0.1c-0.5-0.1-0.8-0.2-1.1-0.4c-0.2-0.2-0.3-0.3-0.3-0.5c0.1-0.1,0.4-0.4,0.8-0.7
c0.5-0.4,0.9-0.7,1.2-1.1c0.2-0.3,0.4-0.6,0.5-1c0-0.2,0-0.3,0-0.3s-0.2-0.1-0.4-0.2c-0.1,0-0.6-0.1-1.3-0.2
c-0.6-0.1-0.9-0.2-1-0.3c-0.1-0.1-0.1-0.2,0-0.4c0.1-0.2,0.3-0.4,0.7-0.6c0.3-0.2,0.8-0.7,1.4-1.5c0.7-0.9,1.1-1.4,1.1-1.6
c0.1-0.2,0.2-0.4,0.2-0.5s0-0.2-0.1-0.4c-0.2-0.2-0.3-0.4-0.4-0.5c-0.1-0.1-0.1-0.2-0.1-0.3c0-0.1,0-0.2,0.1-0.3
C113.7,2.8,113.8,2.8,114,2.8z M120.5,1.9c0,0,0.2,0.1,0.5,0.4c0.2,0.3,0.3,0.6,0.3,0.9L121,4.3c-0.1,0.4-0.2,0.6-0.2,0.7
c0,0.1,0.1,0.1,0.2,0.1s0.5,0.1,1.1,0.5c1.1,0.6,1.7,1,1.8,1c0.5,0.3,1.2,0.9,2,1.7c0.7,0.7,1.1,1.1,1.1,1.2l-0.3,0.5
c-0.2,0.3-0.4,0.4-0.6,0.3c-0.2-0.1-0.5-0.4-1-0.9c-0.5-0.5-0.7-0.8-0.6-0.9c0,0,0,0,0,0s0-0.1-0.1-0.1c0-0.1-0.1-0.1-0.2-0.2
c-0.1,0-0.2-0.1-0.3-0.3c-0.1-0.1-0.3-0.2-0.4-0.4s-0.3-0.2-0.5-0.4s-0.3-0.2-0.3-0.3c-0.1,0-0.3-0.1-0.6-0.3
c-0.4-0.2-0.6-0.4-0.8-0.5c-0.6-0.3-1-0.4-1-0.3c0,0,0,0-0.1,0.1c0,0,0,0.1-0.1,0.1c0,0.1-0.1,0.1-0.1,0.2c0,0.1-0.1,0.1-0.2,0.2
c-0.1,0.1-0.1,0.2-0.2,0.3c-0.2,0.3-0.8,1-1.8,2.1c-0.9,0.9-1.6,1.5-2,1.8c-0.5,0.4-0.8,0.6-0.9,0.6c-0.1,0-0.2,0-0.3-0.1
c-0.1-0.2-0.2-0.3-0.1-0.4c0.1-0.1,0.4-0.4,0.8-0.6c0.4-0.2,1-0.8,1.8-1.6c0.7-0.7,1.3-1.4,2-2.2c0.5-0.6,0.8-1.2,1.1-1.6
c0.1-0.2,0.2-0.6,0.3-1c0-0.3,0-0.6,0-0.9c-0.1-0.2-0.1-0.4,0-0.6C120.4,2,120.4,1.9,120.5,1.9z M123,8.7c0.3,0.1,0.4,0.2,0.5,0.2
c0.1,0.1,0.2,0.2,0.2,0.3c0,0.1-0.2,0.4-0.5,0.7c-0.3,0.4-0.6,0.6-0.8,0.5c-0.1,0-0.2-0.1-0.2-0.4c0-0.1,0-0.2-0.1-0.3
c-0.1,0-0.4,0-1,0c-0.8,0.1-1.5,0.2-2,0.4c-0.5,0.2-0.9,0.4-1.1,0.4c-0.2,0-0.3-0.1-0.3-0.3c0-0.2,0.1-0.4,0.4-0.6
c0.3-0.2,0.7-0.4,1.3-0.5c0.7-0.2,1.4-0.3,2.1-0.4C121.9,8.7,122.4,8.7,123,8.7z M122.9,13.2c-0.2,0-0.6,0-1,0.1
c-0.1,0-0.6,0.2-1.5,0.4c-0.4,0.2-0.6,0.3-0.7,0.4s-0.1,0.2,0,0.5c0,0.2,0.1,0.6,0.3,1l0.4,0.6c0.1,0,0.5-0.3,1.2-0.9
c0.7-0.6,1.2-1.1,1.4-1.4c0.2-0.2,0.4-0.4,0.4-0.6C123.1,13.2,123,13.2,122.9,13.2z"/>
<path class="st0" d="M139.4,1.1c0,0,0.2,0.1,0.6,0.4c0.2,0.2,0.4,0.5,0.5,0.8c0,0.1-0.1,0.8-0.4,1.9c-0.3,0.8-0.6,1.7-0.9,2.6
C139.1,6.8,139,7,139,7.2c-0.1,0.2-0.1,0.3-0.1,0.4c0,0.1,0,0.1,0,0.1c0,0.1,0.6,0.1,1.7,0c1.2,0,1.9,0.1,2.3,0.2
c0.4,0.1,0.7,0.4,1.1,0.7c0.3,0.4,0.5,0.7,0.6,1c0.1,0.2,0,0.7-0.1,1.5c-0.2,0.8-0.4,1.4-0.7,1.9c-0.4,0.8-0.6,1.3-0.8,1.5
c-0.1,0.3-0.6,0.8-1.3,1.5l-1.3,1.3c-0.3,0.2-0.5,0.3-0.6,0.3s-0.6-0.1-1.3-0.4c-0.6-0.3-1.1-0.5-1.4-0.7c-0.2-0.2-0.3-0.3-0.3-0.4
c0.1-0.1,0.4,0,1,0.1c0.6,0.1,1.1,0.2,1.5,0.1c0.3,0,0.5-0.1,0.7-0.2c0.2-0.1,0.5-0.4,0.9-0.8c0.6-0.6,1.1-1.3,1.5-2
c0.3-0.6,0.6-1.2,0.8-1.8c0.1-0.4,0.2-0.8,0.2-1.2c0-0.4-0.1-0.6-0.2-0.7c-0.1-0.2-0.4-0.4-0.9-0.6c-0.5-0.1-1.2-0.2-2.1-0.2
l-1.5,0.1l-0.7,2c-0.3,0.8-0.7,1.8-1.2,2.8c-0.1,0.2-0.3,0.5-0.6,1c-0.3,0.5-0.4,0.8-0.5,0.9c-0.8,1.2-1.3,1.9-1.4,2.1
c-0.5,0.6-0.8,1-0.9,1.1c-0.1,0.1-0.3,0.1-0.6,0c-0.2-0.1-0.3-0.2-0.3-0.4c0-0.2,0.1-0.5,0.2-0.8c0.1-0.4,0.3-0.5,0.4-0.4
c0.1,0,0.2,0,0.3-0.2c0.1-0.2,0.4-0.5,0.7-0.9c0.6-1,1.2-1.9,1.6-2.8c0.9-1.8,1.3-2.8,1.3-2.9c0.4-0.9,0.5-1.4,0.5-1.5
c-0.6,0-1.1,0-1.7,0.1c-0.2,0-0.6,0-1,0c-0.4,0-0.8,0-1.1,0.1c-0.3,0-0.6,0-0.8,0.1c-0.6,0-1,0.1-1.1,0c-0.1,0-0.2-0.1-0.2-0.2
c0-0.2,0.2-0.3,0.5-0.4c1.6-0.3,3.1-0.4,4.5-0.5c0.6-0.1,1-0.2,1.1-0.3c0.1-0.1,0.4-0.6,0.7-1.4c0.1-0.2,0.1-0.5,0.2-0.7
c0.1-0.3,0.2-0.5,0.2-0.7c0.1-0.2,0.2-0.4,0.2-0.7c0.1-0.2,0.1-0.4,0.2-0.5c0.1-0.6,0.2-0.9,0.2-1.1c0-0.1-0.1-0.3-0.2-0.6
c-0.2-0.3-0.2-0.5-0.1-0.7C139.2,1.1,139.3,1.1,139.4,1.1z"/>
<path class="st0" d="M157.8,3.7c0.1,0,0.3,0,0.4,0.1c0,0,0,0,0.1,0c0,0,0.1,0,0.1,0.1c0,0,0,0.1,0,0.1c0,0.1,0,0.9,0,2.3
c-0.2,1.5-0.3,3-0.3,4.5c-0.1,1.7-0.1,2.6-0.2,2.7c-0.1,0.1-0.2,0.3-0.4,0.4c-0.2,0-0.3,0-0.4-0.2c-0.1-0.2-0.2-0.7-0.2-1.6
c0-0.6,0.1-2.1,0.3-4.7c0-0.3,0.1-0.7,0.1-1.1c0-0.4,0.1-0.7,0.1-1c0-0.3,0-0.6,0.1-0.8c0-0.2,0-0.5,0.1-0.6c0-0.1,0-0.2,0-0.3
C157.5,3.7,157.6,3.7,157.8,3.7z M157.7,15c0.3,0.2,0.5,0.4,0.5,0.5c0.1,0.1,0,0.3-0.1,0.6c-0.1,0.2-0.2,0.3-0.3,0.3
c0,0-0.2,0-0.5-0.1c0,0-0.1-0.1-0.1-0.1c-0.1,0-0.1-0.1-0.1-0.1c0,0,0-0.1,0-0.1c0,0,0-0.2,0-0.5c0.1-0.2,0.2-0.4,0.3-0.5
C157.3,14.9,157.5,14.9,157.7,15z"/>
</g>
</svg>

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


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


vendor/github.com/yuin/goldmark/go.sum → docs/持续集成操作指南.md View File


+ 501
- 0
entity/ai_task.go View File

@@ -0,0 +1,501 @@
package entity

import (
"archive/zip"
"encoding/json"
"fmt"
"io"
"reflect"
"strings"
"sync"

"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/services/role"

"code.gitea.io/gitea/models"

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

// todo 暂时保留之前各种云脑属性的定义
type CreateReq struct {
JobType models.JobType `json:"job_type" binding:"Required"`
DisplayJobName string `json:"display_job_name" binding:"Required"`
JobName string `json:"job_name"`
SpecId int64 `json:"spec_id" binding:"Required"`
ComputeSourceStr string `json:"compute_source" binding:"Required"`
Cluster ClusterType `json:"cluster" binding:"Required"`
WorkServerNumber int `json:"work_server_number"`
BranchName string `json:"branch_name"`
ImageUrl string `json:"image_url"`
ImageID string `json:"image_id"`
ImageName string `json:"image_name"`
PretrainModelName string `json:"pretrain_model_name"`
PretrainModelId string `json:"pretrain_model_id_str"`
Description string `json:"description"`
LabelName string `json:"label_names"`
DatasetUUIDStr string `json:"dataset_uuid_str"`
Params string `json:"params"`
BootFile string `json:"boot_file"`
PoolId string `json:"pool_id"`
IsContinueRequest bool `json:"is_continue"`
SourceCloudbrainId int64 `json:"source_cloudbrain_id"`
AppName string `json:"app_name"`
HasInternet models.SpecInternetQuery `json:"has_internet"` //0 all;1 no internet;2 has internet
ParamArray models.Parameters
ComputeSource *models.ComputeSource
ReqCommitID string
IsFileNoteBookRequest bool
FileRepository *models.Repository
FileBranchName string
IsRestartRequest bool
DatasetNames string
ModelNames string
}

type CreationResponse struct {
Error error
JobID string
Status string //todo 考虑统一状态
CreateTime timeutil.TimeStamp
VersionID int64
VersionName string
}
type JobIdAndVersionId struct {
JobID string
VersionID int64
}

type QueryAITaskRes struct {
Task *AITaskDetailInfo `json:"task"`
EarlyVersionList []*AITaskDetailInfo `json:"early_version_list"`
CanCreateVersion bool `json:"can_create_version"`
CanDownload bool `json:"can_download"`
}

func (r *QueryAITaskRes) TryToRemoveDatasetAndModelInfo(currentUser *models.User) {
if r.Task != nil {
r.Task.TryToRemoveDatasets(currentUser)
r.Task.TryToRemovePretrainModelList(currentUser)
r.Task.TryToRemoveSDKCode(currentUser)
}
if r.EarlyVersionList != nil {
for _, t := range r.EarlyVersionList {
t.TryToRemoveDatasets(currentUser)
t.TryToRemovePretrainModelList(currentUser)
t.TryToRemoveSDKCode(currentUser)
}
}
}
func (r *QueryAITaskRes) Tr(language string) {
if r.Task != nil {
r.Task.Tr(language)
}
if r.EarlyVersionList != nil {
for _, t := range r.EarlyVersionList {
t.Tr(language)
}
}
}

type AITaskDetailInfo struct {
ID int64 `json:"id"`
JobID string `json:"job_id"`
Status string `json:"status"`
DetailedStatus string `json:"detailed_status"`
JobType string `json:"job_type"`
Cluster string `json:"cluster"`
DisplayJobName string `json:"display_job_name"`
FormattedDuration string `json:"formatted_duration"`
ComputeSource string `json:"compute_source"`
AICenter string `json:"ai_center"`
BootFile string `json:"boot_file"`
PreVersionName string `json:"pre_version_name"`
CurrentVersionName string `json:"current_version_name"`
WorkServerNumber int `json:"work_server_number"`
Spec *structs.SpecificationShow `json:"spec"`
DatasetList []*models.DatasetDownload `json:"dataset_list"`
PretrainModelList []*models.Model4Show `json:"pretrain_model_list"`
SDKCode string `json:"sdk_code"`
Parameters *models.Parameters `json:"parameters"`
CreatedUnix timeutil.TimeStamp `json:"created_unix"`
CodePath string `json:"code_path"`
DatasetPath string `json:"dataset_path"`
PretrainModelPath string `json:"pretrain_model_path"`
PretrainModelUrl string `json:"pretrain_model_url"`
OutputPath string `json:"output_path"`
CodeUrl string `json:"code_url"`
PretrainModelName string `json:"pretrain_model_name"`
PretrainModelId string `json:"pretrain_model_id"`
StartTime timeutil.TimeStamp `json:"start_time"`
EndTime timeutil.TimeStamp `json:"end_time"`
Description string `json:"description"`
CommitID string `json:"commit_id"`
BranchName string `json:"branch_name"`
ImageUrl string `json:"image_url"`
ImageID string `json:"image_id"`
ImageName string `json:"image_name"`
CreatorName string `json:"creator_name"`
EngineName string `json:"engine_name"`
FailedReason string `json:"failed_reason"`
UserId int64 `json:"-"`
AppName string `json:"app_name"`
HasInternet int `json:"has_internet"`
}

func (a *AITaskDetailInfo) Tr(language string) {
a.AICenter = getAiCenterShow(a.AICenter, language)
}

func (a *AITaskDetailInfo) TryToRemoveDatasets(currentUser *models.User) {
if currentUser == nil || a.UserId == 0 || (!currentUser.IsAdmin && currentUser.ID != a.UserId && !role.UserHasRole(currentUser.ID, models.MonitorAdmin)) {
a.DatasetList = []*models.DatasetDownload{}
}
}
func (a *AITaskDetailInfo) TryToRemovePretrainModelList(currentUser *models.User) {
if currentUser == nil || a.UserId == 0 || (!currentUser.IsAdmin && currentUser.ID != a.UserId) {
a.PretrainModelList = []*models.Model4Show{}
}
}
func (a *AITaskDetailInfo) TryToRemoveSDKCode(currentUser *models.User) {
if currentUser == nil || a.UserId == 0 || (!currentUser.IsAdmin && currentUser.ID != a.UserId) {
a.SDKCode = ""
}
}

func getAiCenterShow(aiCenter string, language string) string {
aiCenterInfo := strings.Split(aiCenter, "+")
if len(aiCenterInfo) == 2 {
if setting.AiCenterCodeAndNameAndLocMapInfo != nil {
if info, ok := setting.AiCenterCodeAndNameAndLocMapInfo[aiCenterInfo[0]]; ok {
if language == defaultLanguage {
return info.Content
} else {
return info.ContentEN
}
} else {
return aiCenterInfo[1]
}
} else {
return aiCenterInfo[1]
}
}
return ""
}

var defaultLanguage = "zh-CN"

type CreateTaskRes struct {
ID int64 `json:"id"`
Status string `json:"status"`
}

type GetAITaskCreationInfoReq struct {
User *models.User
JobType models.JobType
ClusterType ClusterType
ComputeSource *models.ComputeSource
Repo *models.Repository
GitRepo *git.Repository
IsOnlineType bool
}

type GetAITaskCreationImageInfoReq struct {
JobType models.JobType
ClusterType ClusterType
ComputeSource *models.ComputeSource
Spec *models.Specification
UserID int64
}

type AITaskBriefInfo struct {
ID int64 `json:"id"`
JobType string `json:"job_type"`
Status string `json:"status"`
DetailedStatus string `json:"detailed_status"`
DisplayJobName string `json:"display_job_name"`
CreatedUnix timeutil.TimeStamp `json:"created_unix"`
StartTime timeutil.TimeStamp `json:"start_time"`
EndTime timeutil.TimeStamp `json:"end_time"`
FormattedDuration string `json:"formatted_duration"`
Cluster string `json:"cluster"`
ComputeSource string `json:"compute_source"`
AICenter string `json:"ai_center"`
IsFileNotebook bool `json:"is_file_notebook"`
IsFineTuneTask bool `json:"is_fine_tune_task"`
APPName string `json:"app_name"`
}

func (a *AITaskBriefInfo) Tr(language string) {
a.AICenter = getAiCenterShow(a.AICenter, language)
}

type AITaskListRes struct {
Tasks []*AITaskInfo4List `json:"tasks"`
Total int64 `json:"total"`
PageSize int `json:"page_size"`
Page int `json:"page"`
CanCreateTask bool `json:"can_create_task"`
IsRepoEmpty bool `json:"is_repo_empty"`
}
type AITaskInfo4List struct {
Task *AITaskBriefInfo `json:"task"`
Creator UserBriefInfo `json:"creator"`
CanModify bool `json:"can_modify"`
CanDelete bool `json:"can_delete"`
}

func ConvertCloudbrainToAITaskBriefInfo(task *models.Cloudbrain) *AITaskBriefInfo {
return &AITaskBriefInfo{
ID: task.ID,
JobType: task.JobType,
Status: task.Status,
DetailedStatus: task.DetailedStatus,
DisplayJobName: task.DisplayJobName,
CreatedUnix: task.CreatedUnix,
FormattedDuration: task.TrainJobDuration,
Cluster: GetClusterTypeFromCloudbrainType(task.Type).GetParentCluster(),
ComputeSource: task.GetStandardComputeSource(),
StartTime: task.StartTime,
EndTime: task.EndTime,
AICenter: task.AiCenter,
IsFileNotebook: task.IsFileNoteBookTask(),
IsFineTuneTask: task.FineTune,
APPName: task.AppName,
}
}

type NotebookDataset struct {
DatasetUrl string `json:"dataset_url"`
DatasetName string `json:"dataset_name"`
ContainerPath string `json:"containerPath"`
ReadOnly bool `json:"readOnly"`
}

type QueryLogOpts struct {
CloudbrainId int64
BaseLine int64
Lines int64
Order Direction
UserId int64
NodeId int
LogFileName string
}

type GetLogDownloadInfoReq struct {
CloudbrainId int64
NodeId int
LogFileName string
}
type GetAllOutputReq struct {
CloudbrainId int64
Suffix []string
}

type DownloadAllFileReq struct {
CloudbrainId int64
FileName string
ParentDir string
ZIPWriter *zip.Writer
}

type GetSingleDownloadInfoReq struct {
CloudbrainId int64
FileName string
ParentDir string
}

type Direction string

const (
UP Direction = "up"
DOWN Direction = "down"
)

func (o Direction) Reverse() Direction {
switch o {
case DOWN:
return UP
case UP:
return DOWN
}
return ""
}

type FileReader struct {
Reader io.ReadCloser
Name string
}

type GetTaskListReq struct {
models.ListOptions
ComputeSource *models.ComputeSource
JobTypes []string
RepoID int64
Operator *models.User
IsRepoOwner bool
}

type AITaskBaseConfig struct {
ContainerSteps map[ContainerDataType]*ContainerBuildOpts `json:"container_configs"`
ActionType models.ActionType `json:"action_type"`
IsActionUseJobId bool `json:"is_action_use_job_id"`
DatasetsLimitSizeGB int
DatasetsMaxNum int
ModelMaxNum int
ModelLimitSizeGB int
}

func GetAITaskConfigFromCloudbrainConfig(config *models.CloudbrainConfig) *AITaskBaseConfig {
if config == nil {
return nil
}
s := config.ConfigurationSnapshot
c := &AITaskBaseConfig{}
err := json.Unmarshal([]byte(s), c)
if err != nil {
log.Error("GetAITaskConfigFromCloudbrain err,config=%+v err=&v", config, err)
return nil
}
return c
}

type AITaskDetailConfigInfo struct {
BaseConfig *AITaskBaseConfig
OutputObjectPrefix string
OutputStorageType StorageType
LogObjectPrefix string
LogStorageType StorageType
}

func BuildAITaskDetailConfigInfo(config *models.CloudbrainConfig) *AITaskDetailConfigInfo {
c := &AITaskBaseConfig{}
json.Unmarshal([]byte(config.ConfigurationSnapshot), c)
return &AITaskDetailConfigInfo{
BaseConfig: c,
OutputObjectPrefix: config.OutputObjectPrefix,
OutputStorageType: StorageType(config.OutputStorageType),
LogObjectPrefix: config.LogObjectPrefix,
LogStorageType: StorageType(config.LogStorageType),
}
}

type AITaskConfigKey struct {
ComputeSource string
IsFileNoteBookRequest bool
}

func (opts AITaskConfigKey) GetKey() string {
v := reflect.ValueOf(opts)
t := v.Type()
b := strings.Builder{}
for i := 0; i < v.NumField(); i++ {
field := v.Field(i)
fieldName := t.Field(i).Name
fieldValue := field.Interface()
if !field.IsZero() {
b.WriteString(fmt.Sprintf("%s:%v;", fieldName, fieldValue))
}
}
return b.String()
}

func (c *AITaskBaseConfig) GetContainerConfig(containerDataType ContainerDataType) *ContainerBuildOpts {
containerConfigs := c.ContainerSteps
if containerConfigs != nil {
return containerConfigs[containerDataType]
}
return nil

}
func (c *AITaskBaseConfig) GetContainerPath(containerDataType ContainerDataType) string {
config := c.GetContainerConfig(containerDataType)
if config == nil {
return ""
}
return config.ContainerPath

}

type AITaskConfigMap struct {
mu sync.RWMutex
ConfigMap map[string]*AITaskBaseConfig
}

func (h *AITaskConfigMap) Add(opts AITaskConfigKey, config *AITaskBaseConfig) *AITaskConfigMap {
h.mu.Lock()
defer h.mu.Unlock()

if h.ConfigMap == nil {
h.ConfigMap = make(map[string]*AITaskBaseConfig, 0)
}
h.ConfigMap[opts.GetKey()] = config
return h
}
func (h *AITaskConfigMap) Default(config *AITaskBaseConfig) *AITaskConfigMap {
h.mu.Lock()
defer h.mu.Unlock()

if h.ConfigMap == nil {
h.ConfigMap = make(map[string]*AITaskBaseConfig, 0)
}
h.ConfigMap[AITaskConfigKey{}.GetKey()] = config
return h
}

func (h AITaskConfigMap) Get(opts AITaskConfigKey) *AITaskBaseConfig {
h.mu.RLock()
defer h.mu.RUnlock()

if h.ConfigMap == nil {
return nil
}
key := opts.GetKey()
if _, isOk := h.ConfigMap[key]; isOk {
return h.ConfigMap[key]
}
return nil
}

func (h AITaskConfigMap) IsEmpty() bool {
return h.ConfigMap == nil || len(h.ConfigMap) == 0
}

type AITaskOutput struct {
Status models.ModelMigrateStatus `json:"status"`
Path string `json:"path"`
FileList []storage.FileInfo `json:"file_list"`
IsTaskTerminal bool `json:"is_task_terminal"`
CanReschedule bool `json:"can_reschedule"`
CanDownload bool `json:"can_download"`
}
type AllAITaskOutput struct {
FileList []storage.FileInfo `json:"file_list"`
}

type GetResourceUsageOpts struct {
CloudbrainId int64
NodeId int
LogFileName string
}
type GetSpecOpts struct {
UserId int64
ComputeSource models.ComputeSource
JobType models.JobType
HasInternet models.SpecInternetQuery //0 all;1 no internet;2 has internet
}

type AITaskNodeInfo struct {
ID int `json:"id"`
LogFileName string `json:"log_file_name"`
}

type StorageObjectInfo struct {
ObjectKey string
StorageType StorageType
Bucket string
}

+ 371
- 0
entity/cluster.go View File

@@ -0,0 +1,371 @@
package entity

import (
"archive/zip"
"encoding/json"
"fmt"
"strconv"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/cloudbrain"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/modules/timeutil"
)

type CreateNoteBookTaskRequest struct {
Name string
Description string
Tasks []NoteBookTask
PrimitiveDatasetName string
RepoName string
}

type NoteBookTask struct {
AutoStopDuration int64
Name string
Capacity int
Queues []models.ResourceQueue
Code []ContainerData
Datasets []ContainerData
PreTrainModel []ContainerData
OutPut []ContainerData
ImageId string
ImageUrl string
ResourceSpecId string
BootFile string
Spec *models.Specification
EnvVariables models.GrampusEnvVarReq
}

type CreateNoteBookTaskResponse struct {
StartedAt int64
RunSec int64
CompletedAt int64
CreatedAt int64
UpdatedAt int64
Desc string
JobID string
Name string
Status string
UserID string
}

type RestartNoteBookTaskResponse struct {
JobId string `json:"newId"`
Status string `json:"status"`
}

type CreateTrainTaskRequest struct {
Name string
DisplayJobName string
Description string
TaskConfig *AITaskBaseConfig
Tasks []TrainTask
}

type QueryTaskResponse struct {
StartedAt timeutil.TimeStamp `json:"started_at"`
CompletedAt timeutil.TimeStamp `json:"completed_at"`
JobId string `json:"job_id"`
Status string `json:"status"`
DetailedStatus string `json:"detailed_status"`
Url string `json:"url"`
Token string `json:"token"`
CenterId string `json:"center_id"`
CenterName string `json:"center_name"`
QueueCode string `json:"queue_code"`
CodeUrl string `json:"code_url"`
DataUrl string `json:"data_url"`
ContainerIP string `json:"container_ip"`
ContainerID string `json:"container_id"`
VersionId int64 `json:"version_id"`
}

func ConvertGrampusNotebookResponse(job models.GrampusNotebookInfo) *QueryTaskResponse {
if len(job.Tasks) == 0 {
return nil
}
task := job.Tasks[0]
centerId := ""
if len(task.CenterID) > 0 {
centerId = task.CenterID[0]
}
centerName := ""
if len(task.CenterName) > 0 {
centerName = task.CenterName[0]
}
var url, token, codeUrl, dataUrl string
if len(job.Tasks) > 0 {
t := job.Tasks[0]
url = t.Url
token = t.Token
codeUrl = t.CodeUrl
dataUrl = t.DataUrl
}
return &QueryTaskResponse{
StartedAt: timeutil.TimeStamp(job.StartedAt),
CompletedAt: timeutil.TimeStamp(job.CompletedAt),
Status: job.Status,
DetailedStatus: job.DetailedStatus,
CenterId: centerId,
CenterName: centerName,
QueueCode: task.PoolId,
Url: url,
Token: token,
JobId: job.JobID,
CodeUrl: codeUrl,
DataUrl: dataUrl,
}
}
func ConvertGrampusTrainResponse(job models.GrampusJobInfo) *QueryTaskResponse {
if len(job.Tasks) == 0 {
return nil
}
task := job.Tasks[0]
centerId := ""
if len(task.CenterID) > 0 {
centerId = task.CenterID[0]
}
centerName := ""
if len(task.CenterName) > 0 {
centerName = task.CenterName[0]
}
return &QueryTaskResponse{
StartedAt: timeutil.TimeStamp(job.StartedAt),
CompletedAt: timeutil.TimeStamp(job.CompletedAt),
Status: job.Status,
CenterId: centerId,
CenterName: centerName,
JobId: job.JobID,
}
}

func ConvertCloudbrainOneQueryNotebookByNameResponse(result models.JobResultInListPayload) *QueryTaskResponse {
if result.State == "" {
return nil
}
return &QueryTaskResponse{
StartedAt: timeutil.TimeStamp(result.CreatedTime / 1000),
CompletedAt: timeutil.TimeStamp(result.CompletedTime / 1000),
Status: result.State,
JobId: result.Id,
}
}

func ConvertCloudbrainOneNotebookResponse(input map[string]interface{}) (*QueryTaskResponse, error) {
data, _ := json.Marshal(input)
var jobResultPayload models.JobResultPayload
err := json.Unmarshal(data, &jobResultPayload)
if err != nil {
log.Error("parse cloudbrain one result err,result=%+v err=%v", input, err)
return nil, err
}
if jobResultPayload.JobStatus.State == "" {
return nil, nil
}

startTime := jobResultPayload.JobStatus.AppLaunchedTime / 1000
var endTime int64
switch jobResultPayload.JobStatus.AppCompletedTime.(type) {
case float64:
f := jobResultPayload.JobStatus.AppCompletedTime.(float64)
s := fmt.Sprintf("%.0f", f)
i, err := strconv.ParseInt(s, 10, 64)
if err == nil {
endTime = i / 1000
}
}

if jobResultPayload.JobStatus.State == string(models.JobWaiting) {
startTime = 0
endTime = 0
}
var containerIP, containerID string
taskRoles := jobResultPayload.TaskRoles
if taskRoles != nil && len(taskRoles) > 0 {
subTask := taskRoles[cloudbrain.SubTaskName]
if subTask != nil {
taskRes, _ := models.ConvertToTaskPod(taskRoles[cloudbrain.SubTaskName].(map[string]interface{}))
if taskRes.TaskStatuses != nil && len(taskRes.TaskStatuses) > 0 {
containerIP = taskRes.TaskStatuses[0].ContainerIP
containerID = taskRes.TaskStatuses[0].ContainerID
}
}
}

res := &QueryTaskResponse{
StartedAt: timeutil.TimeStamp(startTime),
CompletedAt: timeutil.TimeStamp(endTime),
Status: jobResultPayload.JobStatus.State,
JobId: jobResultPayload.ID,
ContainerIP: containerIP,
ContainerID: containerID,
}
return res, nil
}

type ClusterLog struct {
Content string `json:"content"`
CanLogDownload bool `json:"can_log_download"`
//云脑二返回的startline和baseline在前端会丢失精度。因此改为string类型
StartLine string `json:"start_line"`
EndLine string `json:"end_line"`
Lines int64 `json:"lines"`
}

type TrainTask struct {
Command string `json:"command"`
Name string `json:"name"`
ImageId string `json:"imageId"`
ImageUrl string `json:"imageUrl"`
ResourceSpecId string `json:"resourceSpecId"`
Queues []models.ResourceQueue `json:"centerID"`
ReplicaNum int `json:"replicaNum"`
Datasets []ContainerData `json:"datasets"`
PreTrainModel []ContainerData `json:"models"`
Code []ContainerData `json:"code"`
BootFile string `json:"bootFile"`
OutPut []ContainerData `json:"output"`
LogPath []ContainerData `json:"logPath"`
PoolId string `json:"poolId"`
Params models.Parameters
Spec *models.Specification
RepoName string
WorkServerNumber int
}

type CreateTrainTaskResponse struct {
StartedAt int64 `json:"startedAt"`
RunSec int64 `json:"runSec"`
CompletedAt int64 `json:"completedAt"`
CreatedAt int64 `json:"createdAt"`
UpdatedAt int64 `json:"updatedAt"`
Desc string `json:"desc"`
JobID string `json:"id"`
Name string `json:"name"`
Status string `json:"status"`
UserID string `json:"userId"`
VersionID int64 `json:"versionID"`
VersionName string `json:"versionName"` //当前版本
}

type ClusterType string

const (
OpenICloudbrainOne ClusterType = "OpenICloudbrainOne"
OpenICloudbrainTwo ClusterType = "OpenICloudbrainTwo"
C2Net ClusterType = "C2Net"
)

func (t ClusterType) GetParentCluster() string {
switch t {
case OpenICloudbrainTwo, OpenICloudbrainOne:
return models.OpenICluster
case C2Net:
return models.C2NetCluster
}
return ""
}

func (t ClusterType) GetCloudbrainType() int {
switch t {
case OpenICloudbrainOne:
return models.TypeCloudBrainOne
case OpenICloudbrainTwo:
return models.TypeCloudBrainTwo
case C2Net:
return models.TypeC2Net
}
return -1
}

func GetClusterTypeFromCloudbrainType(t int) ClusterType {
switch t {
case models.TypeCloudBrainOne:
return OpenICloudbrainOne
case models.TypeCloudBrainTwo:
return OpenICloudbrainTwo
case models.TypeC2Net:
return C2Net
case models.TypeCDCenter:
return OpenICloudbrainTwo
}
return ""
}

type ClusterLogOpts struct {
JobId string
JobType string
JobName string
BaseLine int64
Lines int64
Direction Direction
ObjectKeyPrefix string
StorageType StorageType
VersionID int64
NodeId int
LogFileName string
WorkServerNum int
}

func (opts ClusterLogOpts) IsBottomRequest() bool {
return opts.BaseLine == 0 && opts.Direction == UP
}
func (opts ClusterLogOpts) IsHeadRequest() bool {
return opts.BaseLine == 0 && opts.Direction == DOWN
}

type ClusterLogDownloadInfoOpts struct {
JobId string
JobType string
ObjectKeyPrefix string
StorageType StorageType
NodeId int
LogFileName string
WorkServerNum int
JobName string
DisplayJobName string
}

type DownloadOutputOpts struct {
JobId string
Path string
JobName string
StorageType StorageType
ZIPWriter *zip.Writer
}
type ClusterSingleOutputDownloadInfoOpts struct {
JobId string
Path string
JobName string
StorageType StorageType
}
type ClusterNodeInfoOpts struct {
JobId string
WorkServerNum int
VersionId int64
}
type ClusterResourceUsageOpts struct {
JobId string
NodeId int
LogFileName string
VersionID int64
StartTime int64
EndTime int64
ComputeSource string
WorkServerNumber int
}

type ClusterOutputOpts struct {
JobId string
ObjectKeyPrefix string
StorageType StorageType
ParentDir string
}

type ClusterAITaskOutput struct {
Status models.ModelMigrateStatus
Path string
FileList []storage.FileInfo
}

+ 56
- 0
entity/command.go View File

@@ -0,0 +1,56 @@
package entity

import "strings"

type Command struct {
CommandStr string
}

func NewCommand(s ...string) *Command {
var builder strings.Builder
for i := 0; i < len(s); i++ {
builder.WriteString(s[i] + " ")
}
r := strings.TrimSuffix(builder.String(), " ")
return &Command{
CommandStr: r,
}
}

func (c *Command) ToString() string {
return c.CommandStr
}

type CommandBuilder struct {
Commands []*Command
}

func (b *CommandBuilder) ToString() string {
var builder strings.Builder
for i := 0; i < len(b.Commands); i++ {
builder.WriteString(b.Commands[i].ToString() + ";")
}
return builder.String()
}

func (b *CommandBuilder) Next(c *Command) *CommandBuilder {
if b.Commands == nil {
b.Commands = make([]*Command, 0)
}
b.Commands = append(b.Commands, c)
return b
}

func (b *CommandBuilder) Add(another *CommandBuilder) *CommandBuilder {
if b.Commands == nil {
b.Commands = make([]*Command, 0)
}
if another == nil {
return b
}
if another.Commands == nil {
return b
}
b.Commands = append(b.Commands, another.Commands...)
return b
}

+ 64
- 0
entity/container.go View File

@@ -0,0 +1,64 @@
package entity

type TaskData struct {
Code ContainerData
Dataset []ContainerData
PreTrainModel ContainerData
OutPutPath ContainerData
}

type ContainerData struct {
Name string `json:"name"`
Bucket string `json:"bucket"`
EndPoint string `json:"endPoint"`
ObjectKey string `json:"objectKey"`
ContainerPath string `json:"containerPath"`
RealPath string `json:"realPath"`
ReadOnly bool `json:"readOnly"`
IsDir bool `json:"isDir"`
GetBackEndpoint string `json:"getBackEndpoint"`
S3DownloadUrl string `json:"s3DownloadUrl"`
Size int64 `json:"size"`
IsOverwrite bool `json:"isOverwrite"`
IsNeedUnzip bool `json:"isNeedUnzip"`
StorageType StorageType
}

type ContainerDataType string

const (
ContainerCode ContainerDataType = "code"
ContainerDataset ContainerDataType = "dataset"
ContainerPreTrainModel ContainerDataType = "pre_train_model"
ContainerOutPutPath ContainerDataType = "output"
ContainerLogPath ContainerDataType = "log"
ContainerFileNoteBookCode ContainerDataType = "file_note_book_code"
)

type ContainerBuildOpts struct {
Disable bool
//容器内路径
ContainerPath string
//在aiforge存储上基于云脑存储目录的相对路径
StorageRelativePath string
ReadOnly bool
AcceptStorageType []StorageType
Uncompressed bool
MKDIR bool
VolumeFolder bool
}

func (opts ContainerBuildOpts) IsStorageTypeIn(storageType StorageType) bool {
for _, s := range opts.AcceptStorageType {
if string(s) == string(storageType) {
return true
}
}
return false
}
func (opts ContainerBuildOpts) GetLocalPath() string {
if opts.StorageRelativePath != "" {
return opts.StorageRelativePath
}
return opts.ContainerPath
}

+ 93
- 0
entity/creation.go View File

@@ -0,0 +1,93 @@
package entity

import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/structs"
)

type CreationRequiredInfo struct {
//排队信息、代码分支信息、查询是否有正在运行的任务、查询镜像列表、查询资源规格(积分余额,开关)
Specs map[string][]*structs.SpecificationShow `json:"specs"`
Images []ClusterImage `json:"images"`
CanUseAllImages bool `json:"can_use_all_images"`
Branches []string `json:"branches"`
DefaultBranch string `json:"default_branch"`
WaitCount int64 `json:"wait_count"`
NotStopTaskCount int `json:"not_stop_task_count"`
DisplayJobName string `json:"display_job_name"`
PointAccount *PointAccountInfo `json:"point_account"`
PaySwitch bool `json:"pay_switch"`
Config AITaskCreationConfig `json:"config"`
AllowedWorkerNum []int `json:"allowed_worker_num"`
}

type ImageRequiredInfo struct {
Images []ClusterImage `json:"images"`
CanUseAllImages bool `json:"can_use_all_images"`
}

type AITaskCreationConfig struct {
DatasetMaxSize int `json:"dataset_max_size"`
DatasetsMaxNum int `json:"dataset_max_num"`
ModelMaxSize int `json:"model_max_size"`
ModelMaxNum int `json:"model_max_num"`
}

type SpecificationInfo struct {
ID int64 `json:"id"`
SourceSpecId string `json:"source_spec_id"`
AccCardsNum int `json:"acc_cards_num"`
AccCardType string `json:"acc_card_type"`
CpuCores int `json:"cpu_cores"`
MemGiB float32 `json:"mem_gi_b"`
GPUMemGiB float32 `json:"gpu_mem_gi_b"`
ShareMemGiB float32 `json:"share_mem_gi_b"`
ComputeResource string `json:"compute_resource"`
UnitPrice int `json:"unit_price"`
QueueId int64 `json:"queue_id"`
QueueCode string `json:"queue_code"`
Cluster string `json:"cluster"`
AiCenterCode string `json:"ai_center_code"`
AiCenterName string `json:"ai_center_name"`
IsExclusive bool `json:"is_exclusive"`
ExclusiveOrg string `json:"exclusive_org"`
}

func ParseSpecificationInfo(s *models.Specification) *SpecificationInfo {
return &SpecificationInfo{
ID: 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,
}
}

type PointAccountInfo struct {
AccountCode string `json:"account_code"`
Balance int64 `json:"balance"`
TotalEarned int64 `json:"total_earned"`
TotalConsumed int64 `json:"total_consumed"`
}

func ParsePointAccountInfo(p *models.PointAccount) *PointAccountInfo {
return &PointAccountInfo{
AccountCode: p.AccountCode,
Balance: p.Balance,
TotalEarned: p.TotalEarned,
TotalConsumed: p.TotalConsumed,
}

}

+ 13
- 0
entity/err_code.go View File

@@ -0,0 +1,13 @@
package entity

import "fmt"

type ErrCode struct {
CodeVal string
CodeMsg string
CodeTrCode string
}

func (e *ErrCode) IsMatch(code interface{}) bool {
return fmt.Sprint(code) == e.CodeVal
}

+ 8
- 0
entity/file.go View File

@@ -0,0 +1,8 @@
package entity

type FileType string

const (
FileTypeTXT FileType = "txt"
FileTypeZIP FileType = "zip"
)

+ 22
- 0
entity/file_download.go View File

@@ -0,0 +1,22 @@
package entity

type FileDownloadInfo struct {
Readers []FileReader
ResultType FileType
ResultFileName string
DownloadUrl string
}

func (f *FileDownloadInfo) IsEmpty() bool {
return (f.Readers == nil || len(f.Readers) == 0) && f.DownloadUrl == ""
}

func (f *FileDownloadInfo) Close() {
if f.Readers != nil && len(f.Readers) > 0 {
for _, r := range f.Readers {
if r.Reader != nil {
r.Reader.Close()
}
}
}
}

+ 3
- 0
entity/grampus_err_code.go View File

@@ -0,0 +1,3 @@
package entity

var GrampusJobCanNotRestart = &ErrCode{CodeVal: "5005", CodeMsg: "Job can not restart", CodeTrCode: "ai_task.can_not_restart"}

+ 14
- 0
entity/images.go View File

@@ -0,0 +1,14 @@
package entity

import "code.gitea.io/gitea/models"

type ClusterImage struct {
ImageId string `json:"image_id"`
ImageName string `json:"image_name"`
ImageUrl string `json:"image_url"`
}

type GetImageReq struct {
ComputeSource models.ComputeSource
JobType models.JobType
}

+ 35
- 0
entity/operation_profile.go View File

@@ -0,0 +1,35 @@
package entity

type OperationProfile struct {
Events []ProfileEvent `json:"events"`
}

type ProfileEvent struct {
Message string `json:"message"`
Name string `json:"name"`
Reason string `json:"reason"`
Timestamp string `json:"timestamp"`
Action string `json:"action"`
}

type CloudbrainOneAppExitDiagnostics struct {
PodRoleName struct {
Task10 string `json:"task1-0"`
} `json:"podRoleName"`
PodEvents struct {
Task10 []struct {
Uid string `json:"uid"`
Reason string `json:"reason"`
Message string `json:"message"`
ReportingController string `json:"reportingController"`
Action string `json:"action"`
} `json:"task1-0"`
} `json:"podEvents"`
Extras []struct {
Uid string `json:"uid"`
Reason string `json:"reason"`
Message string `json:"message"`
ReportingController string `json:"reportingController"`
Action string `json:"action"`
} `json:"extras"`
}

+ 11
- 0
entity/resource_usage.go View File

@@ -0,0 +1,11 @@
package entity

type ResourceUsage struct {
Interval int `json:"interval"`
MetricsInfo []MetricsInfo `json:"metrics_info"`
}

type MetricsInfo struct {
Name string `json:"name"`
Value []float32 `json:"value"`
}

+ 21
- 0
entity/storage.go View File

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

import "code.gitea.io/gitea/models"

type StorageType string

const (
MINIO StorageType = "MINIO"
OBS StorageType = "OBS"
)

func GetStorageTypeFromCloudbrainType(cloudbrainType int) StorageType {
switch cloudbrainType {
case models.TypeCloudBrainOne:
return MINIO
case models.TypeCloudBrainTwo:
return OBS

}
return ""
}

+ 44
- 0
entity/user.go View File

@@ -0,0 +1,44 @@
package entity

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

type UserBriefInfo struct {
ID int64 `json:"id"`
LowerName string `json:"lower_name"`
Name string `json:"name"`
FullName string `json:"full_name"`
Email string `json:"email"`
Language string `json:"language"`
Description string `json:"description"`
RelAvatarLink string `json:"rel_avatar_link"`
NumMembers int `json:"num_members"`
CreatedUnix timeutil.TimeStamp `json:"created_unix"`
UpdatedUnix timeutil.TimeStamp `json:"updated_unix"`
}

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

+ 181
- 47
go.mod View File

@@ -1,9 +1,8 @@
module code.gitea.io/gitea

go 1.14
go 1.18

require (
cloud.google.com/go v0.45.0 // indirect
gitea.com/jolheiser/gitea-vet v0.1.0
gitea.com/lunny/levelqueue v0.3.0
gitea.com/macaron/binding v0.0.0-20190822013154-a5f53841ed2b
@@ -21,31 +20,26 @@ require (
github.com/BurntSushi/toml v0.3.1
github.com/PuerkitoBio/goquery v1.5.0
github.com/RichardKnop/machinery v1.6.9
github.com/RoaringBitmap/roaring v0.4.23 // indirect
github.com/bgentry/speakeasy v0.1.0 // 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/blevesearch/bleve v1.0.7
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/emirpasic/gods v1.18.1
github.com/ethantkoenig/rupture v0.0.0-20180203182544-0a76f03a811a
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 // indirect
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 // indirect
github.com/gliderlabs/ssh v0.2.2
github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a // indirect
github.com/gliderlabs/ssh v0.3.5
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-ini/ini v1.56.0 // indirect
github.com/go-git/go-billy/v5 v5.5.0
github.com/go-git/go-git/v5 v5.7.0
github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a
github.com/go-macaron/auth v0.0.0-20161228062157-884c0e6c9b92
github.com/go-openapi/jsonreference v0.19.3 // indirect
github.com/go-redis/redis v6.15.2+incompatible
github.com/go-resty/resty/v2 v2.3.0
github.com/go-sql-driver/mysql v1.4.1
@@ -53,28 +47,22 @@ 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/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
github.com/huandu/xstrings v1.3.0
github.com/issue9/assert v1.3.2 // indirect
github.com/issue9/identicon v1.0.1
github.com/jaytaylor/html2text v0.0.0-20160923191438-8fb95d837f7d
github.com/jmhodges/levigo v1.0.0 // indirect
github.com/joho/godotenv v1.3.0 // indirect
github.com/kballard/go-shellquote v0.0.0-20170619183022-cd60e84ee657
github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4
github.com/klauspost/compress v1.10.2
github.com/lafriks/xormstore v1.3.2
github.com/lib/pq v1.2.0
github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
github.com/mailru/easyjson v0.7.0 // indirect
github.com/markbates/goth v1.61.2
github.com/mattn/go-isatty v0.0.11
github.com/mattn/go-oci8 v0.0.0-20190320171441-14ba190cf52d // indirect
github.com/mattn/go-sqlite3 v1.11.0
github.com/mcuadros/go-version v0.0.0-20190308113854-92cdf37c5b75
github.com/mgechev/dots v0.0.0-20190921121421-c36f7dcfbb81
@@ -92,18 +80,11 @@ require (
github.com/pkg/errors v0.9.1
github.com/pquerna/otp v1.2.0
github.com/prometheus/client_golang v1.1.0
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 // indirect
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.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
github.com/unknwon/cae v1.0.0
github.com/unknwon/com v1.0.1
@@ -112,27 +93,180 @@ 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.16.0
golang.org/x/exp v0.0.0-20231127185646-65229373498e
golang.org/x/net v0.19.0
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/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect
golang.org/x/tools v0.0.0-20200515220128-d3bf790afa53
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
golang.org/x/sys v0.15.0
golang.org/x/text v0.14.0
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1
golang.org/x/tools v0.16.0
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.4.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
xorm.io/xorm v1.0.1
)

require (
cloud.google.com/go v0.45.0 // indirect
github.com/Microsoft/go-winio v0.5.2 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/RichardKnop/logging v0.0.0-20181101035820-b1d5d44c82d6 // indirect
github.com/RichardKnop/redsync v1.2.0 // indirect
github.com/RoaringBitmap/roaring v0.4.23 // indirect
github.com/acomagu/bufpipe v1.0.4 // indirect
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 // indirect
github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68 // indirect
github.com/alibabacloud-go/endpoint-util v1.1.0 // indirect
github.com/alibabacloud-go/openapi-util v0.0.11 // indirect
github.com/alibabacloud-go/tea-xml v1.1.2 // indirect
github.com/aliyun/credentials-go v1.1.2 // indirect
github.com/andybalholm/cascadia v1.0.0 // indirect
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect
github.com/aws/aws-sdk-go v1.25.25 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bgentry/speakeasy v0.1.0 // indirect
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
github.com/blevesearch/mmap-go v1.0.2 // indirect
github.com/blevesearch/segment v0.9.0 // indirect
github.com/blevesearch/snowballstem v0.9.0 // indirect
github.com/blevesearch/zap/v11 v11.0.7 // indirect
github.com/blevesearch/zap/v12 v12.0.7 // indirect
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668 // indirect
github.com/chris-ramon/douceur v0.2.0 // indirect
github.com/clbanning/mxj/v2 v2.5.5 // indirect
github.com/cloudflare/circl v1.3.3 // indirect
github.com/couchbase/gomemcached v0.0.0-20191004160342-7b5da2ec40b2 // indirect
github.com/couchbase/goutils v0.0.0-20191018232750-b49639060d85 // indirect
github.com/couchbase/vellum v1.0.1 // indirect
github.com/couchbaselabs/go-couchbase v0.0.0-20190708161019-23e7ca2ce2b7 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dlclark/regexp2 v1.4.0 // indirect
github.com/fatih/color v1.9.0 // indirect
github.com/fatih/structtag v1.2.0 // indirect
github.com/fsnotify/fsnotify v1.4.7 // indirect
github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a // indirect
github.com/go-enry/go-oniguruma v1.2.0 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-ini/ini v1.56.0 // indirect
github.com/go-macaron/inject v0.0.0-20160627170012-d8a0b8677191 // indirect
github.com/go-openapi/analysis v0.19.5 // indirect
github.com/go-openapi/errors v0.19.2 // indirect
github.com/go-openapi/inflect v0.19.0 // indirect
github.com/go-openapi/jsonpointer v0.19.3 // indirect
github.com/go-openapi/jsonreference v0.19.3 // indirect
github.com/go-openapi/loads v0.19.3 // indirect
github.com/go-openapi/runtime v0.19.5 // indirect
github.com/go-openapi/spec v0.19.3 // indirect
github.com/go-openapi/strfmt v0.19.3 // indirect
github.com/go-openapi/swag v0.19.5 // indirect
github.com/go-openapi/validate v0.19.3 // indirect
github.com/go-stack/stack v1.8.0 // indirect
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.3.2 // indirect
github.com/golang/snappy v0.0.1 // indirect
github.com/google/go-querystring v1.0.0 // indirect
github.com/googleapis/gax-go/v2 v2.0.5 // indirect
github.com/gorilla/css v1.0.0 // indirect
github.com/gorilla/handlers v1.4.2 // indirect
github.com/gorilla/mux v1.6.2 // indirect
github.com/gorilla/securecookie v1.1.1 // indirect
github.com/gorilla/sessions v1.2.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.1 // indirect
github.com/hashicorp/go-retryablehttp v0.6.6 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/imdario/mergo v0.3.15 // indirect
github.com/issue9/assert v1.3.2 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jessevdk/go-flags v1.5.0 // indirect
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af // indirect
github.com/jmhodges/levigo v1.0.0 // indirect
github.com/joho/godotenv v1.3.0 // indirect
github.com/json-iterator/go v1.1.10 // indirect
github.com/kelseyhightower/envconfig v1.3.0 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/cpuid v1.2.3 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de // indirect
github.com/lunny/nodb v0.0.0-20160621015157-fc1ef06ad4af // indirect
github.com/magiconair/properties v1.8.1 // indirect
github.com/mailru/easyjson v0.7.0 // indirect
github.com/mattn/go-colorable v0.1.4 // indirect
github.com/mattn/go-oci8 v0.0.0-20190320171441-14ba190cf52d // indirect
github.com/mattn/go-runewidth v0.0.7 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/minio/md5-simd v1.1.0 // indirect
github.com/minio/sha256-simd v0.1.1 // indirect
github.com/mitchellh/mapstructure v1.1.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/mrjones/oauth v0.0.0-20180629183705-f4e24b6d100c // indirect
github.com/mschoch/smat v0.2.0 // indirect
github.com/olekukonko/tablewriter v0.0.4 // indirect
github.com/opentracing/opentracing-go v1.1.0 // indirect
github.com/pelletier/go-toml v1.4.0 // indirect
github.com/philhofer/fwd v1.0.0 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 // indirect
github.com/prometheus/common v0.6.0 // indirect
github.com/prometheus/procfs v0.0.4 // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/russross/blackfriday/v2 v2.0.1 // indirect
github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b // indirect
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d // indirect
github.com/skeema/knownhosts v1.1.1 // indirect
github.com/spf13/afero v1.2.2 // indirect
github.com/spf13/cast v1.3.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.3 // indirect
github.com/spf13/viper v1.4.0 // indirect
github.com/steveyen/gtreap v0.1.0 // indirect
github.com/streadway/amqp v0.0.0-20190214183023-884228600bc9 // indirect
github.com/syndtr/goleveldb v1.0.0 // indirect
github.com/tecbot/gorocksdb v0.0.0-20181010114359-8752a9433481 // indirect
github.com/tinylib/msgp v1.1.2 // indirect
github.com/tjfoc/gmsm v1.3.2 // indirect
github.com/toqueteos/trie v1.0.0 // indirect
github.com/toqueteos/webbrowser v1.2.0 // indirect
github.com/willf/bitset v1.1.10 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c // indirect
github.com/xdg/stringprep v1.0.0 // indirect
go.etcd.io/bbolt v1.3.4 // indirect
go.mongodb.org/mongo-driver v1.1.1 // indirect
go.opencensus.io v0.22.1 // indirect
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/sync v0.5.0 // indirect
google.golang.org/api v0.9.0 // indirect
google.golang.org/appengine v1.6.5 // indirect
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 // indirect
google.golang.org/grpc v1.21.1 // 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/macaron.v1 v1.3.9 // indirect
gopkg.in/toqueteos/substring.v1 v1.0.2 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

+ 180
- 89
go.sum View File

@@ -54,7 +54,11 @@ github.com/360EntSecGroup-Skylar/excelize/v2 v2.0.2/go.mod h1:EfRHD2k+Kd7ijnqlwO
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903 h1:ZK3C5DtzV2nVAQTx5S5jQvMeDqWtD1By5mOoyY/xJek=
github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903/go.mod h1:8TI4H3IbrackdNgv+92dI+rhpCaLqM0IfpgCgenFvRE=
github.com/PuerkitoBio/goquery v1.5.0 h1:uGvmFXOA73IKluu/F84Xd1tt/z07GYm8X49XKHP7EJk=
github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg=
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
@@ -74,18 +78,49 @@ github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
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/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ=
github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
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=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
@@ -122,10 +157,16 @@ github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBT
github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668 h1:U/lr3Dgy4WK+hNk4tyD+nuGjpVLPEHuJSFXMw11/HPA=
github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
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/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs=
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/corbym/gocrest v1.0.3 h1:gwEdq6RkTmq+09CTuM29DfKOCtZ7G7bcyxs3IZ6EVdU=
github.com/corbym/gocrest v1.0.3/go.mod h1:maVFL5lbdS2PgfOQgGRWDYTeunSWQeiEgoNdTABShCs=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
@@ -148,18 +189,15 @@ github.com/couchbase/vellum v1.0.1 h1:qrj9ohvZedvc51S5KzPfJ6P6z0Vqzv7Lx7k3mVc2WO
github.com/couchbase/vellum v1.0.1/go.mod h1:FcwrEivFpNi24R3jLOs3n+fs5RnuQnQqCLBJ1uAg1W4=
github.com/couchbaselabs/go-couchbase v0.0.0-20190708161019-23e7ca2ce2b7 h1:1XjEY/gnjQ+AfXef2U6dxCquhiRzkEpxZuWqs+QxTL8=
github.com/couchbaselabs/go-couchbase v0.0.0-20190708161019-23e7ca2ce2b7/go.mod h1:mby/05p8HE5yHEAKiIH/555NoblMs7PtW6NrYshDruc=
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY=
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d h1:SwD98825d6bdB+pEuTxWOXiSjBrHdOl/UVp75eI7JT8=
github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8=
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 h1:iwZdTE0PVqJCos1vaoKsclOGD3ADKpshg3SRtYBbwso=
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM=
github.com/cznic/strutil v0.0.0-20181122101858-275e90344537 h1:MZRmHqDBd0vxNwenEbKSQqRVT24d3C05ft8kduSwlqM=
github.com/cznic/strutil v0.0.0-20181122101858-275e90344537/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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=
@@ -170,6 +208,10 @@ github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xb
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=
@@ -180,23 +222,17 @@ github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFP
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/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819 h1:RIB4cRk+lBqKK3Oy0r2gRX4ui7tuhiZq2SuTtTCi0/0=
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/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/ethantkoenig/rupture v0.0.0-20180203182544-0a76f03a811a h1:M1bRpaZAn4GSsqu3hdK2R8H0AH9O6vqCTCbm2oAFGfE=
github.com/ethantkoenig/rupture v0.0.0-20180203182544-0a76f03a811a/go.mod h1:MkKY/CB98aVE4VxO63X5vTQKUgcn+3XP15LMASe3lYs=
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ=
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A=
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 h1:E2s37DuLxFhQDg5gKsWoLBOB0n+ZW8s599zru8FJ2/Y=
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
@@ -204,8 +240,8 @@ github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
@@ -217,14 +253,15 @@ github.com/go-enry/go-enry/v2 v2.3.0 h1:o8KwgY6uSplysrIpj+Y42J/xGPp90ogVpxE2Z3s8
github.com/go-enry/go-enry/v2 v2.3.0/go.mod h1:+xFJwbqWi15bvqFHb2ELUWVRKFQtwB61+sDrkvvxxGI=
github.com/go-enry/go-oniguruma v1.2.0 h1:oBO9XC1IDT9+AoWW5oFsa/7gFeOPacEqDbyXZKWXuDs=
github.com/go-enry/go-oniguruma v1.2.0/go.mod h1:bWDhYP+S6xZQgiRL7wlTScFYBe023B6ilRZbCAD5Hf4=
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM=
github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
github.com/go-git/go-git-fixtures/v4 v4.0.1 h1:q+IFMfLx200Q3scvt2hN79JsEzy4AmBTp/pqnefH+Bc=
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-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8=
github.com/go-git/go-git/v5 v5.7.0 h1:t9AudWVLmqzlo+4bqdf7GY+46SUuRsx59SboFxkq2aE=
github.com/go-git/go-git/v5 v5.7.0/go.mod h1:coJHKEOk5kUClpsNlXrUvPrDxY3w3gjHvhcZd8Fodw8=
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=
@@ -316,22 +353,17 @@ github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2V
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
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/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
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/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=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@@ -342,9 +374,7 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ
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/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=
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-github/v24 v24.0.1 h1:KCt1LjMJEey1qvPXxa9SjaWxwTsCWSq6p2Ju57UR4Q4=
github.com/google/go-github/v24 v24.0.1/go.mod h1:CRqaW1Uns1TCkP0wqTpxYyRxRjxwvKU/XSS44u6X74M=
@@ -358,7 +388,6 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go v2.0.0+incompatible h1:j0GKcs05QVmm7yesiZq2+9cxHkNK9YM6zKx4D2qucQU=
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
@@ -367,8 +396,9 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m
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/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=
@@ -407,6 +437,8 @@ github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huandu/xstrings v1.3.0 h1:gvV6jG9dTgFEncxo+AF7PH6MZXi/vZl25owA/8Dg8Wo=
github.com/huandu/xstrings v1.3.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM=
github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/issue9/assert v1.3.1/go.mod h1:9Ger+iz8X7r1zMYYwEhh++2wMGWcNN2oVI+zIQXxcio=
github.com/issue9/assert v1.3.2 h1:IaTa37u4m1fUuTH9K9ldO5IONKVDXjLiUO1T9vj0OF0=
@@ -419,8 +451,9 @@ github.com/jaytaylor/html2text v0.0.0-20160923191438-8fb95d837f7d/go.mod h1:CVKl
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc=
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U=
@@ -430,8 +463,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=
@@ -441,8 +475,8 @@ github.com/kballard/go-shellquote v0.0.0-20170619183022-cd60e84ee657 h1:vE7J1m7c
github.com/kballard/go-shellquote v0.0.0-20170619183022-cd60e84ee657/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kelseyhightower/envconfig v1.3.0 h1:IvRS4f2VcIQy6j4ORGIf9145T/AsUB+oY8LyvN8BXNM=
github.com/kelseyhightower/envconfig v1.3.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4 h1:cTxwSmnaqLoo+4tLukHoB9iqHOu3LmLhRmgUxZo6Vp4=
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=
@@ -455,8 +489,9 @@ github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgo
github.com/kljensen/snowball v0.6.0/go.mod h1:27N7E8fVU5H68RlUmnWwZCfxgt4POBJfENGMvNRhldw=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
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=
@@ -487,6 +522,8 @@ github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7
github.com/markbates/going v1.0.0/go.mod h1:I6mnB4BPnEeqo85ynXIx1ZFLLbtiLHNXVgWeFO9OGOA=
github.com/markbates/goth v1.61.2 h1:jDowrUH5qw8KGuQdKwFhLzkXkTYCIPfz3LHADJsiPIs=
github.com/markbates/goth v1.61.2/go.mod h1:qh2QfwZoWRucQ+DR5KVKC6dUGkNCToWh4vS45GIzFsY=
github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
@@ -542,7 +579,6 @@ github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJE
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5 h1:BvoENQQU+fZ9uukda/RzCAL/191HHwJA5b13R6diVlY=
github.com/nfnt/resize v0.0.0-20160724205520-891127d8d1b5/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/niklasfasching/go-org v0.1.9 h1:Toz8WMIt+qJb52uYEk1YD/muLuOOmRt1CfkV+bKVMkI=
github.com/niklasfasching/go-org v0.1.9/go.mod h1:AsLD6X7djzRIz4/RFZu8vwRL0VGjUvGZCCH1Nz0VdrU=
@@ -558,8 +594,8 @@ github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
@@ -575,6 +611,9 @@ github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUr
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/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -620,12 +659,10 @@ github.com/quasoft/websspi v1.0.0 h1:5nDgdM5xSur9s+B5w2xQ5kxf5nUGqgFgU4W0aDLZ8Mw
github.com/quasoft/websspi v1.0.0/go.mod h1:HmVdl939dQ0WIXZhyik+ARdI03M6bQzaSEKcgpFmewk=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
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/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -669,15 +706,20 @@ github.com/siddontang/ledisdb v0.0.0-20190202134119-8ceb77e66a92/go.mod h1:mF1Dp
github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/skeema/knownhosts v1.1.1 h1:MTk78x9FPgDFVFkDLTrsnnfCJl7g1C/nnKvePgrIngE=
github.com/skeema/knownhosts v1.1.1/go.mod h1:g4fPeYpque7P0xefxtGzV81ihjC8sX2IqpAoNkjxbMo=
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=
@@ -705,8 +747,8 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
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=
@@ -722,6 +764,8 @@ github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhV
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=
@@ -747,8 +791,8 @@ github.com/willf/bitset v1.1.10 h1:NotGKqX0KwQ72NUzqrjZq5ipPNDQex9lo3WpaS8L2sc=
github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
github.com/xanzy/go-gitlab v0.31.0 h1:+nHztQuCXGSMluKe5Q9IRaPdz6tO8O0gMkQ0vqGpiBk=
github.com/xanzy/go-gitlab v0.31.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk=
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
github.com/xdg/stringprep v1.0.0 h1:d9X0esnoa3dFsV0FG35rAT0RIhYFlPq7MiP+DW89La0=
@@ -757,12 +801,16 @@ 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/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.4.5/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=
@@ -800,14 +848,22 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190907121410-71b5226ff739/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
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/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-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
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/exp v0.0.0-20231127185646-65229373498e h1:Gvh4YaCaXNs6dKTlfgismwWZKyjVZXwOPfIyUaqU3No=
golang.org/x/exp v0.0.0-20231127185646-65229373498e/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a h1:gHevYm0pO4QUbwy8Dmdr01R5r1BuKtfYqRqF0h/Cbh0=
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=
@@ -818,8 +874,10 @@ golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHl
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/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
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=
@@ -847,9 +905,16 @@ 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/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120 h1:EZ3cVSzKOlJxAd8e8YAJ7no8nNypTxexh/YE/xW3ZEY=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
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=
@@ -866,8 +931,12 @@ 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/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
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=
@@ -898,14 +967,39 @@ golang.org/x/sys v0.0.0-20190907184412-d223b2b6db03/go.mod h1:h1NjWce9XRLGQEsW7w
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/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
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/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
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=
@@ -933,11 +1027,13 @@ golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200225230052-807dcd883420/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
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.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM=
golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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=
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=
@@ -984,13 +1080,6 @@ google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiq
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
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/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=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
@@ -999,8 +1088,8 @@ gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175/go.mod h1:cuepJuh7vyXfUy
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/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=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
@@ -1010,8 +1099,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=
@@ -1030,10 +1119,12 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
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.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/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=


BIN
go_build_code_gitea_io_gitea View File


+ 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)



+ 1527
- 0
manager/client/cloudbrain_two/resty.go View File

@@ -0,0 +1,1527 @@
package cloudbrain_two

import (
"crypto/tls"
"encoding/json"
"fmt"
"net/http"
"strconv"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"github.com/go-resty/resty/v2"
)

var (
restyClient *resty.Client
HOST string
TOKEN string
AutoStopDurationMs = 4 * 60 * 60 * 1000
)

const (
methodPassword = "password"

urlGetToken = "/v3/auth/tokens"
urlNotebook = "/demanager/instances"
urlTrainJob = "/training-jobs"
urlResourceSpecs = "/job/resource-specs"
urlTrainJobConfig = "/training-job-configs"
errorCodeExceedLimit = "ModelArts.0118"

//notebook 2.0
urlNotebook2 = "/notebooks"

//error code
modelartsIllegalToken = "ModelArts.6401"
NotebookNotFound = "ModelArts.6404"
NotebookNoPermission = "ModelArts.6407"
NotebookInvalid = "ModelArts.6400"
UnknownErrorPrefix = "UNKNOWN:"

ModelArtsJobInTargetState = "ModelArts.6357"
ModelArtsJobNotExists = "ModelArts.0102"
ModelArtsJobInternalError = "ModelArts.0010"
)

func getRestyClient() *resty.Client {
if restyClient == nil {
restyClient = resty.New()
restyClient.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
}
return restyClient
}

func checkSetting() {
if len(HOST) != 0 && len(TOKEN) != 0 && restyClient != nil {
return
}

err := getToken()
if err != nil {
log.Error("getToken failed:%v", err)
}
}

func getToken() error {
HOST = setting.ModelArtsHost

client := getRestyClient()
params := models.GetTokenParams{
Auth: models.Auth{
Identity: models.Identity{
Methods: []string{methodPassword},
Password: models.Password{
User: models.NotebookUser{
Name: setting.ModelArtsUsername,
Password: setting.ModelArtsPassword,
Domain: models.Domain{
Name: setting.ModelArtsDomain,
},
},
},
},
Scope: models.Scope{
Project: models.Project{
Name: setting.ProjectName,
},
},
},
}

res, err := client.R().
SetHeader("Content-Type", "application/json").
SetBody(params).
Post(setting.IamHost + urlGetToken)
if err != nil {
return fmt.Errorf("resty getToken: %v", err)
}

if res.StatusCode() != http.StatusCreated {
return fmt.Errorf("getToken failed:%s", res.String())
}

TOKEN = res.Header().Get("X-Subject-Token")

return nil
}

func CreateJob(createJobParams models.CreateNotebookParams) (*models.CreateNotebookResult, error) {
checkSetting()
client := getRestyClient()
var result models.CreateNotebookResult

retry := 0

sendjob:
res, err := client.R().
SetHeader("Content-Type", "application/json").
SetAuthToken(TOKEN).
SetBody(createJobParams).
SetResult(&result).
Post(HOST + "/v1/" + setting.ProjectID + urlNotebook)

if err != nil {
return nil, fmt.Errorf("resty create notebook: %s", err)
}

if res.StatusCode() == http.StatusUnauthorized && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

var response models.NotebookResult
err = json.Unmarshal(res.Body(), &response)
if err != nil {
log.Error("json.Unmarshal failed: %s", err.Error())
return &result, fmt.Errorf("son.Unmarshal failed: %s", err.Error())
}

if len(response.ErrorCode) != 0 {
log.Error("createNotebook failed(%s): %s", response.ErrorCode, response.ErrorMsg)
if response.ErrorCode == errorCodeExceedLimit {
response.ErrorMsg = "所选规格使用数量已超过最大配额限制。"
}
return &result, fmt.Errorf("createNotebook failed(%s): %s", response.ErrorCode, response.ErrorMsg)
}

return &result, nil
}

func GetJob(jobID string) (*models.GetNotebookResult, error) {
checkSetting()
client := getRestyClient()
var result models.GetNotebookResult

retry := 0

sendjob:
res, err := client.R().
SetHeader("Content-Type", "application/json").
SetAuthToken(TOKEN).
SetResult(&result).
Get(HOST + "/v1/" + setting.ProjectID + urlNotebook + "/" + jobID)

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

if res.StatusCode() == http.StatusUnauthorized && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

var response models.NotebookResult
err = json.Unmarshal(res.Body(), &response)
if err != nil {
log.Error("json.Unmarshal failed: %s", err.Error())
return &result, fmt.Errorf("son.Unmarshal failed: %s", err.Error())
}

if len(response.ErrorCode) != 0 {
log.Error("GetJob failed(%s): %s", response.ErrorCode, response.ErrorMsg)
return &result, fmt.Errorf("GetJob failed(%s): %s", response.ErrorCode, response.ErrorMsg)
}

return &result, nil
}

func GetNotebook2(jobID string) (*models.GetNotebook2Result, error) {
checkSetting()
client := getRestyClient()
var result models.GetNotebook2Result

retry := 0

sendjob:
res, err := client.R().
SetHeader("Content-Type", "application/json").
SetAuthToken(TOKEN).
SetResult(&result).
Get(HOST + "/v1/" + setting.ProjectID + urlNotebook2 + "/" + jobID)

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

if res.StatusCode() == http.StatusUnauthorized && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

var response models.NotebookResult
err = json.Unmarshal(res.Body(), &response)
if err != nil {
log.Error("json.Unmarshal failed: %s", err.Error())
return &result, fmt.Errorf("son.Unmarshal failed: %s", err.Error())
}

if len(response.ErrorCode) != 0 {
log.Error("GetJob failed(%s): %s", response.ErrorCode, response.ErrorMsg)
if response.ErrorCode == modelartsIllegalToken && retry < 1 {
retry++
_ = getToken()
goto sendjob
}
return &result, fmt.Errorf("GetJob failed(%s): %s", response.ErrorCode, response.ErrorMsg)
}

return &result, nil
}

func ManageNotebook(jobID string, param models.NotebookAction) (*models.NotebookActionResult, error) {
checkSetting()
client := getRestyClient()
var result models.NotebookActionResult

retry := 0

sendjob:
res, err := client.R().
SetHeader("Content-Type", "application/json").
SetBody(param).
SetAuthToken(TOKEN).
SetResult(&result).
Post(HOST + "/v1/" + setting.ProjectID + urlNotebook + "/" + jobID + "/action")

if err != nil {
return &result, fmt.Errorf("resty StopJob: %v", err)
}

if res.StatusCode() == http.StatusUnauthorized && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

var response models.NotebookResult
err = json.Unmarshal(res.Body(), &response)
if err != nil {
log.Error("json.Unmarshal failed: %s", err.Error())
return &result, fmt.Errorf("son.Unmarshal failed: %s", err.Error())
}

if len(response.ErrorCode) != 0 {
log.Error("ManageNotebook failed(%s): %s", response.ErrorCode, response.ErrorMsg)
return &result, fmt.Errorf("ManageNotebook failed(%s): %s", response.ErrorCode, response.ErrorMsg)
}

return &result, nil
}

func ManageNotebook2(jobID string, param models.NotebookAction) (*models.NotebookActionResult, error) {
checkSetting()
client := getRestyClient()
var result models.NotebookActionResult

retry := 0

sendjob:
res, err := client.R().
SetHeader("Content-Type", "application/json").
SetAuthToken(TOKEN).
SetResult(&result).
Post(HOST + "/v1/" + setting.ProjectID + urlNotebook2 + "/" + jobID + "/" + param.Action + "?duration=" + strconv.Itoa(AutoStopDurationMs))

if err != nil {
return &result, fmt.Errorf("resty ManageNotebook2: %v", err)
}

if res.StatusCode() == http.StatusUnauthorized && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

var response models.NotebookResult
err = json.Unmarshal(res.Body(), &response)
if err != nil {
log.Error("json.Unmarshal failed: %s", err.Error())
return &result, fmt.Errorf("son.Unmarshal failed: %s", err.Error())
}

if res.StatusCode() == http.StatusBadGateway {
return &result, fmt.Errorf(UnknownErrorPrefix+"createNotebook2 failed(%s): %s", response.ErrorCode, response.ErrorMsg)
}

if len(response.ErrorCode) != 0 {
log.Error("ManageNotebook2 failed(%s): %s", response.ErrorCode, response.ErrorMsg)
if response.ErrorCode == modelartsIllegalToken && retry < 1 {
retry++
_ = getToken()
goto sendjob
}
return &result, fmt.Errorf("ManageNotebook2 failed(%s): %s", response.ErrorCode, response.ErrorMsg)
}

return &result, nil
}

func DelNotebook(jobID string) (*models.NotebookDelResult, error) {
checkSetting()
client := getRestyClient()
var result models.NotebookDelResult

retry := 0

sendjob:
res, err := client.R().
SetHeader("Content-Type", "application/json").
SetAuthToken(TOKEN).
SetResult(&result).
Delete(HOST + "/v1/" + setting.ProjectID + urlNotebook + "/" + jobID)

if err != nil {
return &result, fmt.Errorf("resty DelJob: %v", err)
}

if res.StatusCode() == http.StatusUnauthorized && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

var response models.NotebookResult
err = json.Unmarshal(res.Body(), &response)
if err != nil {
log.Error("json.Unmarshal failed: %s", err.Error())
return &result, fmt.Errorf("son.Unmarshal failed: %s", err.Error())
}

if len(response.ErrorCode) != 0 {
log.Error("DelJob failed(%s): %s", response.ErrorCode, response.ErrorMsg)
return &result, fmt.Errorf("DelJob failed(%s): %s", response.ErrorCode, response.ErrorMsg)
}

return &result, nil
}

func DelNotebook2(jobID string) (*models.NotebookDelResult, error) {
checkSetting()
client := getRestyClient()
var result models.NotebookDelResult

retry := 0

sendjob:
res, err := client.R().
SetHeader("Content-Type", "application/json").
SetAuthToken(TOKEN).
SetResult(&result).
Delete(HOST + "/v1/" + setting.ProjectID + urlNotebook2 + "/" + jobID)

if err != nil {
return &result, fmt.Errorf("resty DelJob: %v", err)
}

if res.StatusCode() == http.StatusUnauthorized && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

var response models.NotebookResult
err = json.Unmarshal(res.Body(), &response)
if err != nil {
log.Error("json.Unmarshal failed: %s", err.Error())
return &result, fmt.Errorf("son.Unmarshal failed: %s", err.Error())
}

if len(response.ErrorCode) != 0 {
log.Error("DelNotebook2 failed(%s): %s", response.ErrorCode, response.ErrorMsg)
if response.ErrorCode == modelartsIllegalToken && retry < 1 {
retry++
_ = getToken()
goto sendjob
}
if response.ErrorCode == ModelArtsJobNotExists || response.ErrorCode == ModelArtsJobInTargetState {
//任务不存在或者已经处于被删除的状态,此时认为删除成功
return &models.NotebookDelResult{}, nil
}
if result.ErrorCode == ModelArtsJobInternalError {
log.Error("ModelArt internal error when del job,jobId=%s", jobID)
return &models.NotebookDelResult{}, nil
}
return &result, fmt.Errorf("DelNotebook2 failed(%s): %s", response.ErrorCode, response.ErrorMsg)
}

return &result, nil
}

func DelJob(jobID string) (*models.NotebookDelResult, error) {
checkSetting()
client := getRestyClient()
var result models.NotebookDelResult

retry := 0

sendjob:
res, err := client.R().
SetHeader("Content-Type", "application/json").
SetAuthToken(TOKEN).
SetResult(&result).
Delete(HOST + "/v1/" + setting.ProjectID + urlNotebook + "/" + jobID)

if err != nil {
return &result, fmt.Errorf("resty DelJob: %v", err)
}

if res.StatusCode() == http.StatusUnauthorized && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

var response models.NotebookResult
err = json.Unmarshal(res.Body(), &response)
if err != nil {
log.Error("json.Unmarshal failed: %s", err.Error())
return &result, fmt.Errorf("son.Unmarshal failed: %s", err.Error())
}

if len(response.ErrorCode) != 0 {
log.Error("DelJob failed(%s): %s", response.ErrorCode, response.ErrorMsg)
return &result, fmt.Errorf("DelJob failed(%s): %s", response.ErrorCode, response.ErrorMsg)
}

return &result, nil
}

func GetJobToken(jobID string) (*models.NotebookGetJobTokenResult, error) {
checkSetting()
client := getRestyClient()
var result models.NotebookGetJobTokenResult

retry := 0

sendjob:
res, err := client.R().
SetHeader("Content-Type", "application/json").
SetAuthToken(TOKEN).
SetResult(&result).
Get(HOST + "/v1/" + setting.ProjectID + urlNotebook + "/" + jobID + "/token")

if err != nil {
return &result, fmt.Errorf("resty GetJobToken: %v", err)
}

if res.StatusCode() == http.StatusUnauthorized && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

var response models.NotebookResult
err = json.Unmarshal(res.Body(), &response)
if err != nil {
log.Error("json.Unmarshal failed: %s", err.Error())
return &result, fmt.Errorf("son.Unmarshal failed: %s", err.Error())
}

if len(response.ErrorCode) != 0 {
log.Error("GetJobToken failed(%s): %s", response.ErrorCode, response.ErrorMsg)
return &result, fmt.Errorf("GetJobToken failed(%s): %s", response.ErrorCode, response.ErrorMsg)
}

return &result, nil
}

func createTrainJobUserImage(createJobParams models.CreateUserImageTrainJobParams) (*models.CreateTrainJobResult, error) {
checkSetting()
client := getRestyClient()
var result models.CreateTrainJobResult

retry := 0

sendjob:
res, err := client.R().
SetHeader("Content-Type", "application/json").
SetAuthToken(TOKEN).
SetBody(createJobParams).
SetResult(&result).
Post(HOST + "/v1/" + setting.ProjectID + urlTrainJob)

if err != nil {
return nil, fmt.Errorf("resty create train-job: %s", err)
}

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

if res.StatusCode() == http.StatusUnauthorized && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

if res.StatusCode() != http.StatusOK {
var temp models.ErrorResult
if err = json.Unmarshal([]byte(res.String()), &temp); err != nil {
log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error())
return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error())
}
log.Error("createTrainJobUserImage failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
bootFileErrorMsg := "Invalid OBS path '" + createJobParams.Config.BootFileUrl + "'."
dataSetErrorMsg := "Invalid OBS path '" + createJobParams.Config.DataUrl + "'."
if temp.ErrorMsg == bootFileErrorMsg {
log.Error("启动文件错误!createTrainJobUserImage failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
return &result, fmt.Errorf("启动文件错误!")
}
if temp.ErrorMsg == dataSetErrorMsg {
log.Error("数据集错误!createTrainJobUserImage failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
return &result, fmt.Errorf("数据集错误!")
}
if res.StatusCode() == http.StatusBadGateway {
return &result, fmt.Errorf(UnknownErrorPrefix+"createTrainJobUserImage failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
} else {
return &result, fmt.Errorf("createTrainJobUserImage failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
}
}

if !result.IsSuccess {
log.Error("createTrainJobUserImage failed(%s): %s", result.ErrorCode, result.ErrorMsg)
return &result, fmt.Errorf("createTrainJobUserImage failed(%s): %s", result.ErrorCode, result.ErrorMsg)
}

return &result, nil
}

func createTrainJob(createJobParams models.CreateTrainJobParams) (*models.CreateTrainJobResult, error) {
checkSetting()
client := getRestyClient()
var result models.CreateTrainJobResult

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

sendjob:
res, err := client.R().
SetHeader("Content-Type", "application/json").
SetAuthToken(TOKEN).
SetBody(createJobParams).
SetResult(&result).
Post(HOST + "/v1/" + setting.ProjectID + urlTrainJob)

if err != nil {
return nil, fmt.Errorf("resty create train-job: %s", err)
}

if res.StatusCode() == http.StatusUnauthorized && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

if res.StatusCode() != http.StatusOK {
var temp models.ErrorResult
if err = json.Unmarshal([]byte(res.String()), &temp); err != nil {
log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error())
return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error())
}
log.Error("createTrainJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
bootFileErrorMsg := "Invalid OBS path '" + createJobParams.Config.BootFileUrl + "'."
dataSetErrorMsg := "Invalid OBS path '" + createJobParams.Config.DataUrl + "'."
if temp.ErrorMsg == bootFileErrorMsg {
log.Error("启动文件错误!createTrainJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
return &result, fmt.Errorf("启动文件错误!")
}
if temp.ErrorMsg == dataSetErrorMsg {
log.Error("数据集错误!createTrainJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
return &result, fmt.Errorf("数据集错误!")
}
if res.StatusCode() == http.StatusBadGateway {
return &result, fmt.Errorf(UnknownErrorPrefix+"createTrainJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
} else {
return &result, fmt.Errorf("createTrainJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
}
}

if !result.IsSuccess {
log.Error("createTrainJob failed(%s): %s", result.ErrorCode, result.ErrorMsg)
return &result, fmt.Errorf("createTrainJob failed(%s): %s", result.ErrorCode, result.ErrorMsg)
}

return &result, nil
}

func createTrainJobVersion(createJobVersionParams models.CreateTrainJobVersionParams, jobID string) (*models.CreateTrainJobResult, error) {
checkSetting()
client := getRestyClient()
var result models.CreateTrainJobResult

retry := 0

sendjob:
res, err := client.R().
SetHeader("Content-Type", "application/json").
SetAuthToken(TOKEN).
SetBody(createJobVersionParams).
SetResult(&result).
Post(HOST + "/v1/" + setting.ProjectID + urlTrainJob + "/" + jobID + "/versions")

if err != nil {
return nil, fmt.Errorf("resty create train-job version: %s", err)
}

if res.StatusCode() == http.StatusUnauthorized && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

if res.StatusCode() != http.StatusOK {
var temp models.ErrorResult
if err = json.Unmarshal([]byte(res.String()), &temp); err != nil {
log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error())
return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error())
}

log.Error("createTrainJobVersion failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
bootFileErrorMsg := "Invalid OBS path '" + createJobVersionParams.Config.BootFileUrl + "'."
dataSetErrorMsg := "Invalid OBS path '" + createJobVersionParams.Config.DataUrl + "'."
if temp.ErrorMsg == bootFileErrorMsg {
log.Error("启动文件错误!createTrainJobVersion failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
return &result, fmt.Errorf("启动文件错误!")
}
if temp.ErrorMsg == dataSetErrorMsg {
log.Error("数据集错误!createTrainJobVersion failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
return &result, fmt.Errorf("数据集错误!")
}
if res.StatusCode() == http.StatusBadGateway {
return &result, fmt.Errorf(UnknownErrorPrefix+"createTrainJobVersion failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
} else {
return &result, fmt.Errorf("createTrainJobVersion failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
}
}

if !result.IsSuccess {
log.Error("createTrainJobVersion failed(%s): %s", result.ErrorCode, result.ErrorMsg)
return &result, fmt.Errorf("createTrainJobVersion failed(%s): %s", result.ErrorCode, result.ErrorMsg)
}

return &result, nil
}

func createTrainJobVersionUserImage(createJobVersionParams models.CreateTrainJobVersionUserImageParams, jobID string) (*models.CreateTrainJobResult, error) {
checkSetting()
client := getRestyClient()
var result models.CreateTrainJobResult

retry := 0

sendjob:
res, err := client.R().
SetHeader("Content-Type", "application/json").
SetAuthToken(TOKEN).
SetBody(createJobVersionParams).
SetResult(&result).
Post(HOST + "/v1/" + setting.ProjectID + urlTrainJob + "/" + jobID + "/versions")

if err != nil {
return nil, fmt.Errorf("resty create train-job version: %s", err)
}

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

if res.StatusCode() == http.StatusUnauthorized && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

if res.StatusCode() != http.StatusOK {
var temp models.ErrorResult
if err = json.Unmarshal([]byte(res.String()), &temp); err != nil {
log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error())
return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error())
}
BootFileErrorMsg := "Invalid OBS path '" + createJobVersionParams.Config.BootFileUrl + "'."
DataSetErrorMsg := "Invalid OBS path '" + createJobVersionParams.Config.DataUrl + "'."
if temp.ErrorMsg == BootFileErrorMsg {
log.Error("启动文件错误!createTrainJobVersion failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
return &result, fmt.Errorf("启动文件错误!")
}
if temp.ErrorMsg == DataSetErrorMsg {
log.Error("数据集错误!createTrainJobVersion failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
return &result, fmt.Errorf("数据集错误!")
}
return &result, fmt.Errorf("createTrainJobVersion failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
}

if !result.IsSuccess {
log.Error("createTrainJobVersion failed(%s): %s", result.ErrorCode, result.ErrorMsg)
return &result, fmt.Errorf("createTrainJobVersion failed(%s): %s", result.ErrorCode, result.ErrorMsg)
}

return &result, nil
}

func GetResourceSpecs() (*models.GetResourceSpecsResult, error) {
checkSetting()
client := getRestyClient()
var result models.GetResourceSpecsResult

retry := 0

sendjob:
res, err := client.R().
SetHeader("Content-Type", "application/json").
SetAuthToken(TOKEN).
SetResult(&result).
Get(HOST + "/v1/" + setting.ProjectID + urlResourceSpecs)

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

if res.StatusCode() == http.StatusUnauthorized && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

if res.StatusCode() != http.StatusOK {
var temp models.ErrorResult
if err = json.Unmarshal([]byte(res.String()), &temp); err != nil {
log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error())
return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error())
}
log.Error("GetResourceSpecs failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
return &result, fmt.Errorf("GetResourceSpecs failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
}

if !result.IsSuccess {
log.Error("GetResourceSpecs failed(%s): %s", result.ErrorCode, result.ErrorMsg)
return &result, fmt.Errorf("GetResourceSpecs failed(%s): %s", result.ErrorCode, result.ErrorMsg)
}

return &result, nil
}

func CreateTrainJobConfig(req models.CreateConfigParams) (*models.CreateTrainJobConfigResult, error) {
checkSetting()
client := getRestyClient()
var result models.CreateTrainJobConfigResult

retry := 0

sendjob:
res, err := client.R().
SetHeader("Content-Type", "application/json").
SetAuthToken(TOKEN).
SetBody(req).
SetResult(&result).
Post(HOST + "/v1/" + setting.ProjectID + urlTrainJobConfig)

if err != nil {
return nil, fmt.Errorf("resty CreateTrainJobConfig: %s", err)
}

if res.StatusCode() == http.StatusUnauthorized && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

if res.StatusCode() != http.StatusOK {
var temp models.ErrorResult
if err = json.Unmarshal([]byte(res.String()), &temp); err != nil {
log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error())
return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error())
}
log.Error("CreateTrainJobConfig failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
return &result, fmt.Errorf("CreateTrainJobConfig failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
}

if !result.IsSuccess {
log.Error("CreateTrainJobConfig failed(%s): %s", result.ErrorCode, result.ErrorMsg)
return &result, fmt.Errorf("CreateTrainJobConfig failed(%s): %s", result.ErrorCode, result.ErrorMsg)
}

return &result, nil
}

func GetConfigList(perPage, page int, sortBy, order, searchContent, configType string) (*models.GetConfigListResult, error) {
checkSetting()
client := getRestyClient()
var result models.GetConfigListResult

retry := 0

sendjob:
res, err := client.R().
SetQueryParams(map[string]string{
"per_page": strconv.Itoa(perPage),
"page": strconv.Itoa(page),
"sortBy": sortBy,
"order": order,
"search_content": searchContent,
"config_type": configType,
}).
SetAuthToken(TOKEN).
SetResult(&result).
Get(HOST + "/v1/" + setting.ProjectID + urlTrainJobConfig)

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

if res.StatusCode() == http.StatusUnauthorized && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

if res.StatusCode() != http.StatusOK {
var temp models.ErrorResult
if err = json.Unmarshal([]byte(res.String()), &temp); err != nil {
log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error())
return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error())
}
log.Error("GetConfigList failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
return &result, fmt.Errorf("获取参数配置列表失败(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
}

if !result.IsSuccess {
log.Error("GetConfigList failed(%s): %s", result.ErrorCode, result.ErrorMsg)
return &result, fmt.Errorf("获取参数配置列表失败(%s): %s", result.ErrorCode, result.ErrorMsg)
}

return &result, nil
}

func GetParaConfig(configName, configType string) (models.GetConfigResult, error) {
checkSetting()
client := getRestyClient()
var result models.GetConfigResult

retry := 0

sendjob:
res, err := client.R().
SetQueryParams(map[string]string{
"config_type": configType,
}).
SetAuthToken(TOKEN).
SetResult(&result).
Get(HOST + "/v1/" + setting.ProjectID + urlTrainJobConfig + "/" + configName)

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

if res.StatusCode() == http.StatusUnauthorized && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

if res.StatusCode() != http.StatusOK {
var temp models.ErrorResult
if err = json.Unmarshal([]byte(res.String()), &temp); err != nil {
log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error())
return result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error())
}
log.Error("GetParaConfig failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
return result, fmt.Errorf("获取参数配置详情失败(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
}

if !result.IsSuccess {
log.Error("GetParaConfig failed(%s): %s", result.ErrorCode, result.ErrorMsg)
return result, fmt.Errorf("获取参数配置详情失败(%s): %s", result.ErrorCode, result.ErrorMsg)
}

return result, nil
}

func GetTrainJob(jobID, versionID string) (*models.GetTrainJobResult, error) {
checkSetting()
client := getRestyClient()
var result models.GetTrainJobResult

retry := 0

sendjob:
res, err := client.R().
SetAuthToken(TOKEN).
SetResult(&result).
Get(HOST + "/v1/" + setting.ProjectID + urlTrainJob + "/" + jobID + "/versions/" + versionID)

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

if res.StatusCode() == http.StatusUnauthorized && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

if res.StatusCode() != http.StatusOK {
var temp models.ErrorResult
if err = json.Unmarshal([]byte(res.String()), &temp); err != nil {
log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error())
return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error())
}
log.Error("GetTrainJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
return &result, fmt.Errorf("获取作业详情失败(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
}

if !result.IsSuccess {
log.Error("GetTrainJob(%s) failed", jobID)
return &result, fmt.Errorf("获取作业详情失败")
}

return &result, nil
}

func GetTrainJobLog(jobID, versionID, baseLine, logFile, order string, lines int) (*models.GetTrainJobLogResult, error) {
checkSetting()
client := getRestyClient()
var result models.GetTrainJobLogResult

retry := 0

sendjob:
res, err := client.R().
SetQueryParams(map[string]string{
"base_line": baseLine,
"lines": strconv.Itoa(lines),
"log_file": logFile,
"order": order,
}).
SetAuthToken(TOKEN).
SetResult(&result).
Get(HOST + "/v1/" + setting.ProjectID + urlTrainJob + "/" + jobID + "/versions/" + versionID + "/aom-log")

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

if res.StatusCode() == http.StatusUnauthorized && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

if res.StatusCode() != http.StatusOK {
var temp models.ErrorResult
if err = json.Unmarshal([]byte(res.String()), &temp); err != nil {
log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error())
return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error())
}
log.Error("GetTrainJobLog failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
return &result, fmt.Errorf("获取作业日志失败(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
}

if !result.IsSuccess {
log.Error("GetTrainJobLog(%s) failed", jobID)
return &result, fmt.Errorf("获取作业日志失败:%s", result.ErrorMsg)
}

return &result, nil
}

func GetTrainJobLogFileNames(jobID, versionID string) (*models.GetTrainJobLogFileNamesResult, error) {
checkSetting()
client := getRestyClient()
var result models.GetTrainJobLogFileNamesResult

retry := 0

sendjob:
res, err := client.R().
SetAuthToken(TOKEN).
SetResult(&result).
Get(HOST + "/v1/" + setting.ProjectID + urlTrainJob + "/" + jobID + "/versions/" + versionID + "/log/file-names")

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

if res.StatusCode() == http.StatusUnauthorized && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

if res.StatusCode() != http.StatusOK {
var temp models.ErrorResult
if err = json.Unmarshal([]byte(res.String()), &temp); err != nil {
log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error())
return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error())
}
log.Error("GetTrainJobLogFileNames failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
return &result, fmt.Errorf("GetTrainJobLogFileNames failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
}

if !result.IsSuccess {
log.Error("GetTrainJobLogFileNames(%s) failed", jobID)
return &result, fmt.Errorf("获取作业日志文件失败:%s", result.ErrorMsg)
}

return &result, nil
}

func DelTrainJob(jobID string) (*models.TrainJobResult, error) {
checkSetting()
client := getRestyClient()
var result models.TrainJobResult

retry := 0

sendjob:
res, err := client.R().
SetAuthToken(TOKEN).
SetResult(&result).
Delete(HOST + "/v1/" + setting.ProjectID + urlTrainJob + "/" + jobID)

if err != nil {
return &result, fmt.Errorf("resty DelTrainJob: %v", err)
}

if res.StatusCode() == http.StatusUnauthorized && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

if res.StatusCode() != http.StatusOK {
var temp models.ErrorResult
if err = json.Unmarshal([]byte(res.String()), &temp); err != nil {
log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error())
return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error())
}
log.Error("DelTrainJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
if temp.ErrorCode == ModelArtsJobNotExists || temp.ErrorCode == ModelArtsJobInTargetState {
//任务不存在或者已经处于被删除的状态,此时认为删除成功
return &models.TrainJobResult{IsSuccess: true}, nil
}
if result.ErrorCode == ModelArtsJobInternalError {
log.Error("ModelArt internal error when del job,jobId=%s", jobID)
return &models.TrainJobResult{IsSuccess: true}, nil
}
return &result, fmt.Errorf("删除训练作业失败(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
}

if !result.IsSuccess {
log.Error("DelTrainJob(%s) failed", jobID)
if result.ErrorCode == ModelArtsJobNotExists || result.ErrorCode == ModelArtsJobInTargetState {
//任务不存在或者已经处于被删除的状态,此时认为删除成功
return &models.TrainJobResult{IsSuccess: true}, nil
}
if result.ErrorCode == ModelArtsJobInternalError {
log.Error("ModelArt internal error when del job,jobId=%s", jobID)
return &models.TrainJobResult{IsSuccess: true}, nil
}
return &result, fmt.Errorf("删除训练作业失败:%s", result.ErrorMsg)
}

return &result, nil
}

func StopTrainJob(jobID, versionID string) (*models.TrainJobResult, error) {
checkSetting()
client := getRestyClient()
var result models.TrainJobResult

retry := 0

sendjob:
res, err := client.R().
SetAuthToken(TOKEN).
SetResult(&result).
Post(HOST + "/v1/" + setting.ProjectID + urlTrainJob + "/" + jobID + "/versions/" + versionID + "/stop")

if err != nil {
return &result, fmt.Errorf("resty StopTrainJob: %v", err)
}

if res.StatusCode() == http.StatusUnauthorized && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

if res.StatusCode() != http.StatusOK {
var temp models.ErrorResult
if err = json.Unmarshal([]byte(res.String()), &temp); err != nil {
log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error())
return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error())
}
log.Error("StopTrainJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
return &result, fmt.Errorf("停止训练作业失败(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
}

if !result.IsSuccess {
log.Error("StopTrainJob(%s) failed", jobID)
return &result, fmt.Errorf("停止训练作业失败:%s", result.ErrorMsg)
}

return &result, nil
}

func DelTrainJobVersion(jobID string, versionID string) (*models.TrainJobResult, error) {
checkSetting()
client := getRestyClient()
var result models.TrainJobResult

retry := 0

sendjob:
res, err := client.R().
SetAuthToken(TOKEN).
SetResult(&result).
Delete(HOST + "/v1/" + setting.ProjectID + urlTrainJob + "/" + jobID + "/versions/" + versionID)

if err != nil {
return &result, fmt.Errorf("resty DelTrainJobVersion: %v", err)
}

if res.StatusCode() == http.StatusUnauthorized && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

if res.StatusCode() != http.StatusOK {
var temp models.ErrorResult
if err = json.Unmarshal([]byte(res.String()), &temp); err != nil {
log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error())
return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error())
}

if temp.ErrorCode == ModelArtsJobNotExists || temp.ErrorCode == ModelArtsJobInTargetState {
//任务不存在或者已经处于被删除的状态,此时认为删除成功
return &models.TrainJobResult{IsSuccess: true}, nil
}
if result.ErrorCode == ModelArtsJobInternalError {
log.Error("ModelArt internal error when del job,jobId=%s", jobID)
return &models.TrainJobResult{IsSuccess: true}, nil
}
log.Error("DelTrainJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
return &result, fmt.Errorf("删除训练作业版本失败(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
}

if !result.IsSuccess {
log.Error("DelTrainJob(%s) failed", jobID)
return &result, fmt.Errorf("删除训练作业版本失败:%s", result.ErrorMsg)
}

return &result, nil
}

func createInferenceJob(createJobParams models.CreateInferenceJobParams) (*models.CreateTrainJobResult, error) {
checkSetting()
client := getRestyClient()
var result models.CreateTrainJobResult

retry := 0

sendjob:
res, err := client.R().
SetHeader("Content-Type", "application/json").
SetAuthToken(TOKEN).
SetBody(createJobParams).
SetResult(&result).
Post(HOST + "/v1/" + setting.ProjectID + urlTrainJob)

if err != nil {
return nil, fmt.Errorf("resty create inference-job: %s", err)
}

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

if res.StatusCode() == http.StatusUnauthorized && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

if res.StatusCode() != http.StatusOK {
var temp models.ErrorResult
if err = json.Unmarshal([]byte(res.String()), &temp); err != nil {
log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error())
return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error())
}
log.Error("createInferenceJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
BootFileErrorMsg := "Invalid OBS path '" + createJobParams.InfConfig.BootFileUrl + "'."
DataSetErrorMsg := "Invalid OBS path '" + createJobParams.InfConfig.DataUrl + "'."
if temp.ErrorMsg == BootFileErrorMsg {
log.Error("启动文件错误!createInferenceJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
return &result, fmt.Errorf("启动文件错误!")
}
if temp.ErrorMsg == DataSetErrorMsg {
log.Error("数据集错误!createInferenceJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
return &result, fmt.Errorf("数据集错误!")
}
if res.StatusCode() == http.StatusBadGateway {
return &result, fmt.Errorf(UnknownErrorPrefix+"createInferenceJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
} else {
return &result, fmt.Errorf("createInferenceJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
}
}

if !result.IsSuccess {
log.Error("createInferenceJob failed(%s): %s", result.ErrorCode, result.ErrorMsg)
return &result, fmt.Errorf("createInferenceJob failed(%s): %s", result.ErrorCode, result.ErrorMsg)
}

return &result, nil
}

func createInferenceJobUserImage(createJobParams models.CreateInfUserImageParams) (*models.CreateTrainJobResult, error) {
checkSetting()
client := getRestyClient()
var result models.CreateTrainJobResult

retry := 0

sendjob:
res, err := client.R().
SetHeader("Content-Type", "application/json").
SetAuthToken(TOKEN).
SetBody(createJobParams).
SetResult(&result).
Post(HOST + "/v1/" + setting.ProjectID + urlTrainJob)

if err != nil {
return nil, fmt.Errorf("resty create train-job: %s", err)
}

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

if res.StatusCode() == http.StatusUnauthorized && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

if res.StatusCode() != http.StatusOK {
var temp models.ErrorResult
if err = json.Unmarshal([]byte(res.String()), &temp); err != nil {
log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error())
return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error())
}
log.Error("createInferenceJobUserImage failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
bootFileErrorMsg := "Invalid OBS path '" + createJobParams.Config.BootFileUrl + "'."
dataSetErrorMsg := "Invalid OBS path '" + createJobParams.Config.DataUrl + "'."
if temp.ErrorMsg == bootFileErrorMsg {
log.Error("启动文件错误!createInferenceJobUserImage failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
return &result, fmt.Errorf("启动文件错误!")
}
if temp.ErrorMsg == dataSetErrorMsg {
log.Error("数据集错误!createInferenceJobUserImage failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
return &result, fmt.Errorf("数据集错误!")
}
if res.StatusCode() == http.StatusBadGateway {
return &result, fmt.Errorf(UnknownErrorPrefix+"createInferenceJobUserImage failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
} else {
return &result, fmt.Errorf("createInferenceJobUserImage failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
}
}

if !result.IsSuccess {
log.Error("createInferenceJobUserImage failed(%s): %s", result.ErrorCode, result.ErrorMsg)
return &result, fmt.Errorf("createInferenceJobUserImage failed(%s): %s", result.ErrorCode, result.ErrorMsg)
}

return &result, nil
}

func CreateNotebook2(createJobParams models.CreateNotebook2Params) (*models.CreateNotebookResult, error) {
checkSetting()
client := getRestyClient()
var result models.CreateNotebookResult

retry := 0

sendjob:
res, err := client.R().
SetHeader("Content-Type", "application/json").
SetAuthToken(TOKEN).
SetBody(createJobParams).
SetResult(&result).
Post(HOST + "/v1/" + setting.ProjectID + urlNotebook2)

if err != nil {
return nil, fmt.Errorf("resty create notebook2: %s", err)
}

if res.StatusCode() == http.StatusUnauthorized && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

var response models.NotebookResult
err = json.Unmarshal(res.Body(), &response)
if err != nil {
log.Error("json.Unmarshal failed: %s", err.Error())
return &result, fmt.Errorf("json.Unmarshal failed: %s", err.Error())
}

if res.StatusCode() == http.StatusBadGateway {
return &result, fmt.Errorf(UnknownErrorPrefix+"createNotebook2 failed(%s): %s", response.ErrorCode, response.ErrorMsg)
}

if len(response.ErrorCode) != 0 {
log.Error("createNotebook2 failed(%s): %s", response.ErrorCode, response.ErrorMsg)
if response.ErrorCode == errorCodeExceedLimit {
response.ErrorMsg = "所选规格使用数量已超过最大配额限制。"
}
if response.ErrorCode == modelartsIllegalToken && retry < 1 {
retry++
_ = getToken()
goto sendjob
}
return &result, fmt.Errorf("createNotebook2 failed(%s): %s", response.ErrorCode, response.ErrorMsg)
}

return &result, nil
}

func GetTrainJobMetricStatistic(jobID, versionID, podName string) (*models.GetTrainJobMetricStatisticResult, error) {
checkSetting()
client := getRestyClient()
var result models.GetTrainJobMetricStatisticResult

retry := 0

sendjob:
res, err := client.R().
SetAuthToken(TOKEN).
SetResult(&result).
Get(HOST + "/v1/" + setting.ProjectID + urlTrainJob + "/" + jobID + "/versions/" + versionID + "/pod/" + podName + "/metric-statistic?statistic_type=each")

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

if res.StatusCode() == http.StatusUnauthorized && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

if res.StatusCode() != http.StatusOK {
var temp models.ErrorResult
if err = json.Unmarshal([]byte(res.String()), &temp); err != nil {
log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error())
return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error())
}
log.Error("GetTrainJobMetricStatistic failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
return &result, fmt.Errorf("GetTrainJobMetricStatistic failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
}

if !result.IsSuccess {
log.Error("GetTrainJobMetricStatistic(%s) failed", jobID)
return &result, fmt.Errorf("获取任务资源占用情况失败:%s", result.ErrorMsg)
}

return &result, nil
}

func GetTrainJobList(perPage, page int, sortBy, order, searchContent string) (*models.GetTrainJobListResult, error) {
checkSetting()
client := getRestyClient()
var result models.GetTrainJobListResult

retry := 0

sendjob:
res, err := client.R().
SetQueryParams(map[string]string{
"per_page": strconv.Itoa(perPage),
"page": strconv.Itoa(page),
"sortBy": sortBy,
"order": order,
"search_content": searchContent,
}).
SetAuthToken(TOKEN).
SetResult(&result).
Get(HOST + "/v1/" + setting.ProjectID + urlTrainJob)

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

if res.StatusCode() == http.StatusUnauthorized && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

if res.StatusCode() != http.StatusOK {
var temp models.ErrorResult
if err = json.Unmarshal([]byte(res.String()), &temp); err != nil {
log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error())
return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error())
}
log.Error("GetTrainJobList failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
return &result, fmt.Errorf(temp.ErrorMsg)
}

if !result.IsSuccess {
log.Error("GetTrainJobList failed(%s): %s", result.ErrorCode, result.ErrorMsg)
return &result, fmt.Errorf(result.ErrorMsg)
}

return &result, nil
}

func GetTrainJobVersionList(perPage, page int, jobID string) (*models.GetTrainJobVersionListResult, error) {
checkSetting()
client := getRestyClient()
var result models.GetTrainJobVersionListResult

retry := 0

sendjob:
res, err := client.R().
SetQueryParams(map[string]string{
"per_page": strconv.Itoa(perPage),
"page": strconv.Itoa(page),
}).
SetAuthToken(TOKEN).
SetResult(&result).
Get(HOST + "/v1/" + setting.ProjectID + urlTrainJob + "/" + jobID + "/versions")

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

if res.StatusCode() == http.StatusUnauthorized && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

if res.StatusCode() != http.StatusOK {
var temp models.ErrorResult
if err = json.Unmarshal([]byte(res.String()), &temp); err != nil {
log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error())
return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error())
}
log.Error("GetTrainJobVersionList failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
return &result, fmt.Errorf(temp.ErrorMsg)
}

if !result.IsSuccess {
log.Error("GetTrainJobVersionList failed(%s): %s", result.ErrorCode, result.ErrorMsg)
return &result, fmt.Errorf(result.ErrorMsg)
}

return &result, nil
}

func GetNotebookList(limit, offset int, sortBy, order, searchContent string) (*models.GetNotebookListResult, error) {
checkSetting()
client := getRestyClient()
var result models.GetNotebookListResult

retry := 0

sendjob:
res, err := client.R().
SetQueryParams(map[string]string{
"limit": strconv.Itoa(limit),
"offset": strconv.Itoa(offset),
"name": searchContent,
"sort_key": sortBy,
"sort_dir": order,
}).
SetAuthToken(TOKEN).
SetResult(&result).
Get(HOST + "/v1/" + setting.ProjectID + urlNotebook2)

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

if res.StatusCode() == http.StatusUnauthorized && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

if res.StatusCode() != http.StatusOK {
var temp models.ErrorResult
if err = json.Unmarshal([]byte(res.String()), &temp); err != nil {
log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error())
return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error())
}
log.Error("GetNotebookList failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
return &result, fmt.Errorf(temp.ErrorMsg)
}

return &result, nil
}

+ 233
- 0
manager/client/cloudbrain_two_cd/resty.go View File

@@ -0,0 +1,233 @@
package cloudbrain_two_cd

import (
"bytes"
"code.gitea.io/gitea/modules/modelarts_gateway/core"
"crypto/tls"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strconv"
"time"

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

var (
httpClient *http.Client
HOST string
TOKEN string
autoStopDurationMs = 4 * 60 * 60 * 1000
)

const (
errorCodeExceedLimit = "ModelArts.0118"

//notebook 2.0
urlNotebook2 = "/notebooks"

//error code
modelartsIllegalToken = "ModelArts.6401"
NotebookNotFound = "ModelArts.6404"
NotebookNoPermission = "ModelArts.6407"
NotebookInvalid = "ModelArts.6400"
UnknownErrorPrefix = "UNKNOWN:"
ModelArtsJobNotExists = "ModelArts.0102"
ModelArtsJobInTargetState = "ModelArts.6357"
ModelArtsJobInternalError = "ModelArts.0010"
)

func getHttpClient() *http.Client {
if httpClient == nil {
httpClient = &http.Client{
Timeout: 30 * time.Second,
Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}},
}
}
return httpClient
}

func GetNotebook(jobID string) (*models.GetNotebook2Result, error) {
var result models.GetNotebook2Result

client := getHttpClient()
s := core.Signer{
Key: setting.ModelartsCD.AccessKey,
Secret: setting.ModelartsCD.SecretKey,
}
r, _ := http.NewRequest(http.MethodGet,
setting.ModelartsCD.EndPoint+"/v1/"+setting.ModelartsCD.ProjectID+urlNotebook2+"/"+jobID,
nil)

r.Header.Add("content-type", "application/json")
s.Sign(r)

resp, err := client.Do(r)
if err != nil {
log.Error("client.Do failed: %s", err.Error())
return &result, fmt.Errorf("client.Do failed: %s", err.Error())
}

defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Error("ioutil.ReadAll failed: %s", err.Error())
return &result, fmt.Errorf("ioutil.ReadAll failed: %s", err.Error())
}

err = json.Unmarshal(body, &result)
if err != nil {
log.Error("json.Unmarshal failed: %s", err.Error())
return &result, fmt.Errorf("son.Unmarshal failed: %s", err.Error())
}

if len(result.ErrorCode) != 0 {
log.Error("GetNotebook failed(%s): %s", result.ErrorCode, result.ErrorMsg)
return &result, fmt.Errorf("GetNotebook failed(%s): %s", result.ErrorCode, result.ErrorMsg)
}

return &result, nil
}

func ManageNotebook(jobID string, param models.NotebookAction) (*models.NotebookActionResult, error) {
var result models.NotebookActionResult

client := getHttpClient()
s := core.Signer{
Key: setting.ModelartsCD.AccessKey,
Secret: setting.ModelartsCD.SecretKey,
}
r, _ := http.NewRequest(http.MethodPost,
setting.ModelartsCD.EndPoint+"/v1/"+setting.ModelartsCD.ProjectID+urlNotebook2+"/"+jobID+"/"+param.Action+"?duration="+strconv.Itoa(autoStopDurationMs),
nil)

r.Header.Add("content-type", "application/json")
s.Sign(r)

resp, err := client.Do(r)
if err != nil {
log.Error("client.Do failed: %s", err.Error())
return &result, fmt.Errorf("client.Do failed: %s", err.Error())
}

defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Error("ioutil.ReadAll failed: %s", err.Error())
return &result, fmt.Errorf("ioutil.ReadAll failed: %s", err.Error())
}

err = json.Unmarshal(body, &result)
if err != nil {
log.Error("json.Unmarshal failed: %s", err.Error())
return &result, fmt.Errorf("son.Unmarshal failed: %s", err.Error())
}

if len(result.ErrorCode) != 0 {
log.Error("ManageNotebook2 failed(%s): %s", result.ErrorCode, result.ErrorMsg)
return &result, fmt.Errorf("ManageNotebook failed(%s): %s", result.ErrorCode, result.ErrorMsg)
}

return &result, nil
}

func DelNotebook(jobID string) (*models.NotebookDelResult, error) {
var result models.NotebookDelResult

client := getHttpClient()
s := core.Signer{
Key: setting.ModelartsCD.AccessKey,
Secret: setting.ModelartsCD.SecretKey,
}

r, _ := http.NewRequest(http.MethodDelete,
setting.ModelartsCD.EndPoint+"/v1/"+setting.ModelartsCD.ProjectID+urlNotebook2+"/"+jobID,
nil)

r.Header.Add("content-type", "application/json")
s.Sign(r)

resp, err := client.Do(r)
if err != nil {
log.Error("client.Do failed: %s", err.Error())
return &result, fmt.Errorf("client.Do failed: %s", err.Error())
}

defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Error("ioutil.ReadAll failed: %s", err.Error())
return &result, fmt.Errorf("ioutil.ReadAll failed: %s", err.Error())
}

err = json.Unmarshal(body, &result)
if err != nil {
log.Error("json.Unmarshal failed: %s", err.Error())
return &result, fmt.Errorf("son.Unmarshal failed: %s", err.Error())
}

if len(result.ErrorCode) != 0 {
log.Error("DelNotebook2 failed(%s): %s", result.ErrorCode, result.ErrorMsg)
if result.ErrorCode == ModelArtsJobNotExists || result.ErrorCode == ModelArtsJobInTargetState {
//任务不存在或者已经处于被删除的状态,此时认为删除成功
return &models.NotebookDelResult{}, nil
}

if result.ErrorCode == ModelArtsJobInternalError {
log.Error("ModelArt internal error when del job,jobId=%s", jobID)
return &models.NotebookDelResult{}, nil
}
return &result, fmt.Errorf("DelNotebook2 failed(%s): %s", result.ErrorCode, result.ErrorMsg)
}

return &result, nil
}

func CreateNotebook(createJobParams models.CreateNotebookWithoutPoolParams) (*models.CreateNotebookResult, error) {
var result models.CreateNotebookResult
client := getHttpClient()
s := core.Signer{
Key: setting.ModelartsCD.AccessKey,
Secret: setting.ModelartsCD.SecretKey,
}

req, _ := json.Marshal(createJobParams)
r, _ := http.NewRequest(http.MethodPost,
setting.ModelartsCD.EndPoint+"/v1/"+setting.ModelartsCD.ProjectID+urlNotebook2,
ioutil.NopCloser(bytes.NewBuffer(req)))

r.Header.Add("content-type", "application/json")
s.Sign(r)

resp, err := client.Do(r)
if err != nil {
log.Error("client.Do failed: %s", err.Error())
return &result, fmt.Errorf("client.Do failed: %s", err.Error())
}

defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Error("ioutil.ReadAll failed: %s", err.Error())
return &result, fmt.Errorf("ioutil.ReadAll failed: %s", err.Error())
}

err = json.Unmarshal(body, &result)
if err != nil {
log.Error("json.Unmarshal failed: %s", err.Error())
return &result, fmt.Errorf("json.Unmarshal failed: %s", err.Error())
}

if len(result.ErrorCode) != 0 {
log.Error("createNotebook failed(%s): %s", result.ErrorCode, result.ErrorMsg)
if result.ErrorCode == errorCodeExceedLimit {
result.ErrorMsg = "所选规格使用数量已超过最大配额限制。"
}
return &result, fmt.Errorf("createNotebook failed(%s): %s", result.ErrorCode, result.ErrorMsg)
}

return &result, nil
}

+ 674
- 0
manager/client/grampus/grampus.go View File

@@ -0,0 +1,674 @@
package grampus

import (
"crypto/tls"
"encoding/json"
"fmt"
"math"
"net/http"
"strconv"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"github.com/go-resty/resty/v2"
"github.com/gorilla/websocket"
)

var (
restyClient *resty.Client
HOST string
TOKEN string
)

const (
urlOpenApiV1 = "/openapi/v1/"

urlGetToken = urlOpenApiV1 + "token"
urlTrainJob = urlOpenApiV1 + "trainjob"
urlGetResourceSpecs = urlOpenApiV1 + "resourcespec"
urlGetAiCenter = urlOpenApiV1 + "sharescreen/aicenter"
urlGetImages = urlOpenApiV1 + "image"
urlNotebookJob = urlOpenApiV1 + "notebook"
urlInferenceJob = urlOpenApiV1 + "inference"

errorIllegalToken = 1005
errorCannotStopCreatingJob = 5008
MAX_MATRICS_SIZE = 1000
)

type GetTokenParams struct {
UserName string `json:"username"`
Password string `json:"password"`
}

type GetTokenResult struct {
Token string `json:"token"`
Expiration int64 `json:"expiration"`
}

func getRestyClient() *resty.Client {
if restyClient == nil {
restyClient = resty.New()
restyClient.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
}
return restyClient
}

func checkSetting() {
if len(HOST) != 0 && len(TOKEN) != 0 && restyClient != nil {
return
}

err := getToken()
if err != nil {
log.Error("getToken failed:%v", err)
}
}

func getToken() error {
HOST = setting.Grampus.Host

client := getRestyClient()
params := GetTokenParams{
UserName: setting.Grampus.UserName,
Password: setting.Grampus.Password,
}

var result GetTokenResult
res, err := client.R().
SetHeader("Content-Type", "application/json").
SetBody(params).
SetResult(&result).
Post(HOST + urlGetToken)
if err != nil {
return fmt.Errorf("resty getToken: %v", err)
}

if res.StatusCode() != http.StatusOK {
return fmt.Errorf("getToken failed:%s", res.String())
}

TOKEN = result.Token

return nil
}

func CreateInferenceJob(req models.CreateGrampusInferenceRequest) (*models.GrampusNotebookResponse, error) {
checkSetting()
client := getRestyClient()
var result models.GrampusNotebookResponse
reqJson, _ := json.Marshal(req)
log.Info("Online infer REQ:" + string(reqJson))
retry := 0

sendjob:
_, err := client.R().
SetHeader("Content-Type", "application/json").
SetAuthToken(TOKEN).
SetBody(req).
SetResult(&result).
Post(HOST + urlInferenceJob)

if err != nil {
log.Error("resty CreateInferenceJob: %v", err)
return nil, models.NetworkError{}
}

if result.ErrorCode == errorIllegalToken && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

if result.ErrorCode != 0 {
log.Error("CreateInferenceJob failed(%d): %s", result.ErrorCode, result.ErrorMsg)
return &result, fmt.Errorf("CreateNotebookJob failed(%d): %s", result.ErrorCode, result.ErrorMsg)
}
log.Info("CreateInferenceJob success.req.JobName = %s ,result=%+v", req.Name, result)
return &result, nil
}

func CreateNotebookJob(req models.CreateGrampusNotebookRequest) (*models.GrampusNotebookResponse, error) {
checkSetting()
client := getRestyClient()
var result models.GrampusNotebookResponse
reqJson, _ := json.Marshal(req)
log.Info("Online infer REQ:" + string(reqJson))
retry := 0

sendjob:
_, err := client.R().
SetHeader("Content-Type", "application/json").
SetAuthToken(TOKEN).
SetBody(req).
SetResult(&result).
Post(HOST + urlNotebookJob)

if err != nil {
log.Error("resty CreateNotebookJob: %v", err)
return nil, models.NetworkError{}
}

if result.ErrorCode == errorIllegalToken && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

if result.ErrorCode != 0 {
log.Error("CreateNotebookJob failed(%d): %s", result.ErrorCode, result.ErrorMsg)
return &result, fmt.Errorf("CreateNotebookJob failed(%d): %s", result.ErrorCode, result.ErrorMsg)
}
log.Info("CreateNotebookJob success.req.JobName = %s ,result=%+v", req.Name, result)
return &result, nil
}

func CreateJob(req models.CreateGrampusJobRequest) (*models.CreateGrampusJobResponse, error) {
checkSetting()
client := getRestyClient()
var result models.CreateGrampusJobResponse

retry := 0

sendjob:
_, err := client.R().
SetHeader("Content-Type", "application/json").
SetAuthToken(TOKEN).
SetBody(req).
SetResult(&result).
Post(HOST + urlTrainJob)

if err != nil {
return nil, fmt.Errorf("resty CreateJob: %s", err)
}

if result.ErrorCode == errorIllegalToken && retry < 1 {
retry++
_ = getToken()
goto sendjob
}

if result.ErrorCode != 0 {
log.Error("CreateJob failed(%d): %s", result.ErrorCode, result.ErrorMsg)
return &result, fmt.Errorf("CreateJob failed(%d): %s", result.ErrorCode, result.ErrorMsg)
}

return &result, nil
}

func GetNotebookJob(jobID string) (*models.GrampusNotebookResponse, error) {
checkSetting()
client := getRestyClient()
var result models.GrampusNotebookResponse

retry := 0

sendjob:
body, err := client.R().
SetAuthToken(TOKEN).
SetResult(&result).
Get(HOST + urlNotebookJob + "/" + jobID)

if err != nil {
return nil, fmt.Errorf("resty GetNotebookJob: %v", err)
}
log.Info("%+v", body)
if result.ErrorCode == errorIllegalToken && retry < 1 {
retry++
log.Info("retry get token")
_ = getToken()
goto sendjob
}

if result.ErrorCode != 0 {
log.Error("GetNotebookJob failed(%d): %s", result.ErrorCode, result.ErrorMsg)
return nil, fmt.Errorf("GetNotebookJob failed(%d): %s", result.ErrorCode, result.ErrorMsg)
}

return &result, nil
}

func GetJob(jobID string) (*models.GetGrampusJobResponse, error) {
checkSetting()
client := getRestyClient()
var result models.GetGrampusJobResponse

retry := 0

sendjob:
_, err := client.R().
SetAuthToken(TOKEN).
SetResult(&result).
Get(HOST + urlTrainJob + "/" + jobID)
if err != nil {
return nil, fmt.Errorf("resty GetJob: %v", err)
}

if result.ErrorCode == errorIllegalToken && retry < 1 {
retry++
log.Info("retry get token")
_ = getToken()
goto sendjob
}

if result.ErrorCode != 0 {
log.Error("GetJob failed(%d): %s", result.ErrorCode, result.ErrorMsg)
return nil, fmt.Errorf("GetJob failed(%d): %s", result.ErrorCode, result.ErrorMsg)
}

return &result, nil
}

func GetJobListByJobName(jobName string) (*models.GetGrampusJobListResponse, error) {
checkSetting()
client := getRestyClient()
var result models.GetGrampusJobListResponse

retry := 0

sendjob:
_, err := client.R().
SetAuthToken(TOKEN).
SetResult(&result).
Get(HOST + urlTrainJob + "?pageIndex=1&pageSize=20&searchKey=" + jobName)
if err != nil {
return nil, fmt.Errorf("resty GetJobListByJobName: %v", err)
}

if result.ErrorCode == errorIllegalToken && retry < 1 {
retry++
log.Info("retry get token")
_ = getToken()
goto sendjob
}

if result.ErrorCode != 0 {
log.Error("GetJob failed(%d): %s", result.ErrorCode, result.ErrorMsg)
return nil, fmt.Errorf("GetJobListByJobName failed(%d): %s", result.ErrorCode, result.ErrorMsg)
}

return &result, nil
}

func GetResourceSpecs(processorType string) (*models.GetGrampusResourceSpecsResult, error) {
checkSetting()
client := getRestyClient()
var result models.GetGrampusResourceSpecsResult

retry := 0

sendjob:
_, err := client.R().
SetAuthToken(TOKEN).
SetResult(&result).
Get(HOST + urlGetResourceSpecs + "?processorType=" + processorType)

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

if result.ErrorCode == errorIllegalToken && retry < 1 {
retry++
log.Info("retry get token")
_ = getToken()
goto sendjob
}

if result.ErrorCode != 0 {
log.Error("GetResourceSpecs failed(%d): %s", result.ErrorCode, result.ErrorMsg)
return &result, fmt.Errorf("GetResourceSpecs failed(%d): %s", result.ErrorCode, result.ErrorMsg)
}

return &result, nil
}

func GetImages(processorType string, jobType string) (*models.GetGrampusImagesResult, error) {
checkSetting()
client := getRestyClient()
var result models.GetGrampusImagesResult

retry := 0

queryType := "TrainJob"
if jobType == string(models.JobTypeDebug) {
queryType = "Notebook"
}

sendjob:
_, err := client.R().
SetAuthToken(TOKEN).
SetResult(&result).
Get(HOST + urlGetImages + "?processorType=" + processorType + "&trainType=" + queryType)

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

if result.ErrorCode == errorIllegalToken && retry < 1 {
retry++
log.Info("retry get token")
_ = getToken()
goto sendjob
}

if result.ErrorCode != 0 {
log.Error("GetImages failed(%d): %s", result.ErrorCode, result.ErrorMsg)
return &result, fmt.Errorf("GetImages failed(%d): %s", result.ErrorCode, result.ErrorMsg)
}

return &result, nil
}

func GetTrainJobLog(jobID string, nodeId ...int) (string, error) {
checkSetting()
client := getRestyClient()
var logContent string

url := HOST + urlTrainJob + "/" + jobID + "/task/0/replica/0/log"
if len(nodeId) > 0 {
url = HOST + urlTrainJob + "/" + jobID + "/task/0/replica/0/log/node/" + strconv.Itoa(nodeId[0])
}

res, err := client.R().
SetAuthToken(TOKEN).
SetResult(&logContent).
Get(url)

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

if res.StatusCode() != http.StatusOK {
var temp models.GrampusResult
if err = json.Unmarshal([]byte(res.String()), &temp); err != nil {
log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error())
return logContent, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error())
}
log.Error("GetTrainJobLog failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
return logContent, fmt.Errorf("GetTrainJobLog failed(%d):%d(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg)
}

logContent = res.String()

return logContent, nil
}

func GetGrampusMetrics(jobID string, startTime int64, endTime int64, nodeId ...int) (models.NewModelArtsMetricStatisticResult, error) {
checkSetting()
client := getRestyClient()
var result models.NewModelArtsMetricStatisticResult
url := HOST + urlTrainJob + "/" + jobID + "/task/0/replica/0/metrics"
if len(nodeId) > 0 {
url = HOST + urlTrainJob + "/" + jobID + "/task/0/replica/0/metrics/node/" + strconv.Itoa(nodeId[0])
}
if startTime > 0 {
var step int64 = 60

size := int64(math.Ceil(float64(endTime-startTime)/float64(step))) + 1

if size > MAX_MATRICS_SIZE {
step = int64(math.Ceil(float64(size*step) / float64(MAX_MATRICS_SIZE)))
size = MAX_MATRICS_SIZE
}

url = url + "?startTime=" + strconv.FormatInt(startTime, 10) + "&step=" + strconv.FormatInt(step, 10) + "&size=" + strconv.FormatInt(size, 10)
}
res, err := client.R().
SetAuthToken(TOKEN).
Get(url)

if err != nil {
return result, fmt.Errorf("resty GetTrainJobLog: %v", err)
}
if err = json.Unmarshal([]byte(res.String()), &result); err != nil {
log.Error("GetGrampusMetrics json.Unmarshal failed(%s): %v", res.String(), err.Error())
return result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error())
}
if res.StatusCode() != http.StatusOK {
return result, fmt.Errorf("Call GrampusMetrics failed(%d)", res.StatusCode())
}
return result, nil
}

func StopJob(jobID string, jobType ...string) (*models.GrampusStopJobResponse, error) {
checkSetting()
client := getRestyClient()
var result models.GrampusStopJobResponse

retry := 0

url := urlTrainJob
if len(jobType) > 0 {
if jobType[0] == string(models.JobTypeDebug) {
url = urlNotebookJob
}
}

sendjob:
_, err := client.R().
//SetHeader("Content-Type", "application/json").
SetAuthToken(TOKEN).
SetResult(&result).
Post(HOST + url + "/" + jobID + "/stop")

if err != nil {
return &result, fmt.Errorf("resty StopTrainJob: %v", err)
}

if result.ErrorCode == errorIllegalToken && retry < 1 {
retry++
log.Info("retry get token")
_ = getToken()
goto sendjob
}

if result.ErrorCode != 0 {
log.Error("GetJob failed(%d): %s", result.ErrorCode, result.ErrorMsg)
if result.ErrorCode == errorCannotStopCreatingJob {
return &result, &models.ErrCannotStopCreatingGrampusJob{}
}
return &result, fmt.Errorf("GetJob failed(%d): %s", result.ErrorCode, result.ErrorMsg)
}

return &result, nil
}

func GetAiCenters(pageIndex, pageSize int) (*models.GetGrampusAiCentersResult, error) {
checkSetting()
client := getRestyClient()
var result models.GetGrampusAiCentersResult

retry := 0

sendjob:
_, err := client.R().
SetAuthToken(TOKEN).
SetResult(&result).
Get(HOST + urlGetAiCenter + "?pageIndex=" + fmt.Sprint(pageIndex) + "&pageSize=" + fmt.Sprint(pageSize))

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

if result.ErrorCode == errorIllegalToken && retry < 1 {
retry++
log.Info("retry get token")
_ = getToken()
goto sendjob
}

if result.ErrorCode != 0 {
log.Error("GetAiCenters failed(%d): %s", result.ErrorCode, result.ErrorMsg)
return &result, fmt.Errorf("GetAiCenters failed(%d): %s", result.ErrorCode, result.ErrorMsg)
}

return &result, nil
}

func RestartNotebookJob(jobID string) (*models.GrampusNotebookRestartResponse, error) {
checkSetting()
client := getRestyClient()
var restartResponse *models.GrampusNotebookRestartResponse
retry := 0

sendjob:
res, err := client.R().
SetAuthToken(TOKEN).
SetResult(&restartResponse).
Post(HOST + urlNotebookJob + "/" + jobID + "/start")

if err != nil {
return nil, fmt.Errorf("resty grampus restart note book job: %v", err)
}
if restartResponse.ErrorCode == errorIllegalToken && retry < 1 {
retry++
log.Info("retry get token")
_ = getToken()
goto sendjob
}

if res.StatusCode() != http.StatusOK {
log.Error("resty grampus restart note book job failed(%s): %v", res.String(), err.Error())
return nil, fmt.Errorf("resty grampus restart note book job failed: %v", err)
}
log.Info("RestartNotebookJob success.jobId = %s ,result=%+v", jobID, restartResponse)

return restartResponse, nil
}

func GetDebugJobEvents(jobID string) (*models.GetGrampusDebugJobEventsResponse, error) {
checkSetting()
client := getRestyClient()
var result models.GetGrampusDebugJobEventsResponse

retry := 0

sendjob:
res, err := client.R().
SetAuthToken(TOKEN).
SetResult(&result).
Get(HOST + urlNotebookJob + "/" + jobID + "/events")
log.Info("res=%v", res)
if err != nil {
return nil, fmt.Errorf("resty GetDebugJobEvents: %v", err)
}

if result.ErrorCode == errorIllegalToken && retry < 1 {
retry++
log.Info("retry get token")
_ = getToken()
goto sendjob
}

if result.ErrorCode != 0 {
log.Error("GetDebugJobEvents failed(%d): %s", result.ErrorCode, result.ErrorMsg)
return nil, fmt.Errorf("GetDebugJobEvents failed(%d): %s", result.ErrorCode, result.ErrorMsg)
}

return &result, nil
}

func GetTrainJobEvents(jobID string) (*models.GetGrampusJobEventsResponse, error) {
checkSetting()
client := getRestyClient()
var result models.GetGrampusJobEventsResponse

retry := 0

sendjob:
res, err := client.R().
SetAuthToken(TOKEN).
SetResult(&result).
Get(HOST + urlTrainJob + "/" + jobID + "/events")
if err != nil {
return nil, fmt.Errorf("resty GetTrainJobEvents: %v", err)
}
log.Info("res=%+v", res)

if result.ErrorCode == errorIllegalToken && retry < 1 {
retry++
log.Info("retry get token")
_ = getToken()
goto sendjob
}

if result.ErrorCode != 0 {
log.Error("GetTrainJobEvents failed(%d): %s", result.ErrorCode, result.ErrorMsg)
return nil, fmt.Errorf("GetTrainJobEvents failed(%d): %s", result.ErrorCode, result.ErrorMsg)
}

return &result, nil
}

func DeleteJob(jobID string, jobType ...string) (*models.GrampusDeleteJobResponse, error) {
checkSetting()
client := getRestyClient()
var result models.GrampusDeleteJobResponse

retry := 0

url := urlTrainJob
if len(jobType) > 0 {
if jobType[0] == string(models.JobTypeDebug) {
url = urlNotebookJob
}
}

sendjob:
_, err := client.R().
//SetHeader("Content-Type", "application/json").
SetAuthToken(TOKEN).
SetResult(&result).
Delete(HOST + url + "/" + jobID)

if err != nil {
return &result, fmt.Errorf("resty StopTrainJob: %v", err)
}

if result.ErrorCode == errorIllegalToken && retry < 1 {
retry++
log.Info("retry get token")
_ = getToken()
goto sendjob
}

if result.ErrorCode != 0 {
log.Error("Delete Job failed(%d): %s", result.ErrorCode, result.ErrorMsg)
return &result, fmt.Errorf("Delete Job failed(%d): %s", result.ErrorCode, result.ErrorMsg)
}
log.Info("delete grampus task, re=" + result.Info + " grampus jobId=" + jobID)
return &result, nil
}

func ClickOnce(url string) {
client := getRestyClient()
log.Info("click url=" + url)
res, err := client.R().
SetHeader("Content-Type", "application/json").
Get(url)
if err != nil {
log.Info("error=" + err.Error())
} else {
if res != nil {
jsonstr, _ := json.Marshal(res)
log.Info("job resp:", jsonstr)
}
}
}

func SendMsgToWebsocket(wsurl string, msg string) {
//wsurl := strings.Replace(tmpurl, "http", "ws", 1)
log.Info("wsurl =" + wsurl)
conn, _, err := websocket.DefaultDialer.Dial(wsurl, nil)
if err == nil {
defer conn.Close()
err1 := conn.WriteJSON(msg)
//err1 := conn.WriteJSON("{\"msg\":\"send_hash\"}")
if err1 != nil {
log.Info("websocket send msg error=" + err1.Error())
}
} else {
log.Info("websocket error=" + err.Error())
}
}

+ 232
- 4
models/action.go View File

@@ -49,6 +49,36 @@ 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
ActionCreateGrampusGCUTrainTask //42
ActionCreateGrampusMLUDebugTask //43
ActionCreateGrampusMLUTrainTask //44
ActionCreateGrampusGPUOnlineInferTask //45
ActionCreateGrampusDCUDebugTask //46
ActionCreateSuperComputeTask //47
ActionCreateGrampusILUVATARDebugTask //48
ActionCreateGrampusMETAXDebugTask //49
ActionCreateGrampusGPUInferenceTask //50
ActionCreateGrampusILUVATARInferenceTask //51
ActionInviteFriendRegister //52
)

// Action represents user operation type and other information to
@@ -70,6 +100,28 @@ 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
Data map[string]interface{}
}

func (a *ActionShow) AddData(key string, val interface{}) {
if a.Data == nil {
a.Data = map[string]interface{}{key: val}
} else {
a.Data[key] = val
}
}

// GetOpType gets the ActionType of this action.
@@ -92,6 +144,20 @@ func (a *Action) loadActUser() {
}
}

func (a *Action) FilterCloudbrainInfo() {
if a.Cloudbrain == nil {
return
}

if a.Cloudbrain.DeletedAt.IsZero() {
newCloudbrain := &Cloudbrain{}
newCloudbrain.ID = a.Cloudbrain.ID
a.Cloudbrain = newCloudbrain
} else {
a.Cloudbrain = nil
}
}

func (a *Action) loadRepo() {
if a.Repo != nil {
return
@@ -102,6 +168,26 @@ func (a *Action) loadRepo() {
log.Error("GetRepositoryByID(%d): %v", a.RepoID, err)
}
}
func (a *Action) loadCloudbrain() {
if !a.IsCloudbrainAction() {
return
}
cloudbrain := &Cloudbrain{}
cloudbrainId, _ := strconv.ParseInt(a.Content, 10, 64)
jobId := a.Content

//由于各个类型的云脑任务在发布action的时候,content字段保存的ID含义不同,部分取的是ID,部分取的是jobId
//所以在查询action对应的cloudbrain对象时,以这两个字段做为条件查询
if has, err := x.
Where(builder.Or(builder.Eq{"id": cloudbrainId}).Or(builder.Eq{"job_id": jobId})).Unscoped().
Get(cloudbrain); err != nil || !has {
return
}
if cloudbrain.DisplayJobName == a.RefName || cloudbrain.JobName == a.RefName {
a.Cloudbrain = cloudbrain
}

}

// GetActFullName gets the action's user full name.
func (a *Action) GetActFullName() string {
@@ -164,12 +250,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 +279,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 +293,61 @@ 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 strings.Contains(a.Content, "|") && a.IsInviteAction() {
ids := strings.Split(a.Content, "|")
if len(ids) >= 2 {
var invitedId int64
var invitedName string
if len(ids) >= 4 {
invitedName = ids[3]
}
invitedId, _ = strconv.ParseInt(ids[1], 10, 64)
if invitedId > 0 {
invitedUser, _ := GetUserByID(invitedId)
if invitedUser != nil {
actionShow.AddData("InvitedUserName", invitedUser.Name)
actionShow.AddData("InvitedUserNotExists", false)
} else {
actionShow.AddData("InvitedUserName", invitedName)
actionShow.AddData("InvitedUserNotExists", true)
}
}
}
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 +445,59 @@ func (a *Action) GetIssueContent() string {
return issue.Content
}

func (a *Action) IsCloudbrainAction() bool {
switch a.OpType {
case ActionCreateDebugGPUTask,
ActionCreateDebugNPUTask,
ActionCreateTrainTask,
ActionCreateInferenceTask,
ActionCreateBenchMarkTask,
ActionCreateGPUTrainTask,
ActionCreateGrampusGPUDebugTask,
ActionCreateGrampusGPUOnlineInferTask,
ActionCreateGrampusNPUDebugTask,
ActionCreateGrampusNPUTrainTask,
ActionCreateGrampusGPUTrainTask,
ActionCreateGrampusGCUTrainTask,
ActionCreateGrampusGCUDebugTask,
ActionCreateGrampusDCUDebugTask,
ActionCreateGrampusMLUDebugTask,
ActionCreateGrampusILUVATARDebugTask,
ActionCreateGrampusMETAXDebugTask,
ActionCreateSuperComputeTask,
ActionCreateGrampusILUVATARInferenceTask,
ActionCreateGrampusGPUInferenceTask:
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
}

func (a *Action) IsInviteAction() bool {
switch a.OpType {
case ActionInviteFriendRegister:
return true
}
return false
}

// GetFeedsOptions options for retrieving feeds
type GetFeedsOptions struct {
RequestedUser *User // the user we want activity for
@@ -339,18 +551,19 @@ func GetFeeds(opts GetFeedsOptions) ([]*Action, error) {
return nil, fmt.Errorf("Find: %v", err)
}

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

return actions, nil
}

func GetLast20PublicFeeds() ([]*Action, error) {
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)

@@ -358,7 +571,7 @@ func GetLast20PublicFeeds() ([]*Action, error) {
return nil, fmt.Errorf("Find: %v", err)
}

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

@@ -374,3 +587,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)
}

+ 752
- 55
models/ai_model_manage.go View File

@@ -2,40 +2,113 @@ package models

import (
"fmt"
"time"

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

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"`
ComputeResource string `json:"computeResource"`
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"`
Recommend int `xorm:"NOT NULL DEFAULT 0" json:"recommend"`
UserId int64 `xorm:"NOT NULL" json:"userId"`
IsPrivate bool `xorm:"DEFAULT true" json:"isPrivate"`
UserName string `xorm:"-" json:"userName"`
UserRelAvatarLink string `xorm:"-" 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 `xorm:"-" json:"isCanOper"`
IsCanDelete bool `xorm:"-" json:"isCanDelete"`
IsCanDownload bool `xorm:"-" json:"isCanDownload"`
IsCollected bool `xorm:"-" json:"isCollected"`
RepoName string `xorm:"-" json:"repoName"`
RepoDisplayName string `xorm:"-" json:"repoDisplayName"`
RepoOwnerName string `xorm:"-" json:"repoOwnerName"`
DatasetInfo []*DatasetDownload `xorm:"-" json:"datasetInfo"`
ReferenceCount int `xorm:"NOT NULL DEFAULT 0" json:"referenceCount"`
CollectedCount int `xorm:"NOT NULL DEFAULT 0" json:"collectedCount"`
ModelFileList []storage.FileInfo `xorm:"-" json:"modelFileList"`
OnlineInfo []map[string]interface{} `xorm:"-" json:"onlineInfo"`
UsedCloudbrain []map[string]interface{} `xorm:"-" json:"usedCloudbrain"`
HasOnlineUrl int `xorm:"NOT NULL DEFAULT 0" json:"hasOnlineUrl"`
License string `xorm:"NULL" json:"license"`
}

type AiModelFile struct {
ID int64 `xorm:"pk autoincr"`
ModelID string `xorm:"UNIQUE(s)"`
Name string `xorm:"varchar(400) UNIQUE(s)"`
Path string `xorm:"varchar(400) NULL"`
Description string `xorm:"varchar(400) NULL"`
DownloadCount int64 `xorm:"DEFAULT 0"`
Size int64 `xorm:"DEFAULT 0"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
}

type AiModelCollect struct {
ID int64 `xorm:"pk autoincr"`
ModelID string `xorm:"UNIQUE(s)"`
UserId int64 `xorm:"UNIQUE(s)"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
}

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 `xorm:"-" json:"userName"`
UserRelAvatarLink string `xorm:"-" json:"userRelAvatarLink"`
IsCanOper bool `xorm:"-" json:"isCanOper"`
IsCanDelete bool `xorm:"-" json:"isCanDelete"`
}

type AiModelQueryOptions struct {
@@ -46,7 +119,149 @@ type AiModelQueryOptions struct {
SortType string
New int
// JobStatus CloudbrainStatus
Type int
Type int
Status int
IsOnlyThisRepo bool
IsQueryPrivate bool
IsRecommend bool
IsCollected bool
CollectedUserId int64
Namelike string
LabelFilter string
FrameFilter int
ComputeResourceFilter string
NotNeedEmpty bool
HasOnlineUrl int
}

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 UpdateResultMigrateFlag(id string, resultMigrateFlag string) error {
var sess *xorm.Session
sess = x.ID(id)
defer sess.Close()
re, err := sess.Cols("status_result").Update(&AiModelConvert{
StatusResult: resultMigrateFlag,
})
if err != nil {
return err
}
log.Info("success to update resultMigrateFlag 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 +277,59 @@ 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 {
return nil, err
} else if !isExist {
return nil, ErrPretrainModelNotExist{}
}
return re, nil
}

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 +338,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 +354,112 @@ func ModifyModelDescription(id string, description string) error {
return nil
}

func ModifyModelHashOnlineUrl(id string, hasOnlineUrl int) error {
var sess *xorm.Session
sess = x.ID(id)
defer sess.Close()
re, err := sess.Cols("has_online_url").Update(&AiModelManage{
HasOnlineUrl: hasOnlineUrl,
})
if err != nil {
return err
}
log.Info("success to update hasOnlineUrl from db.re=" + fmt.Sprint((re)))
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 ModifyModelRecommend(id string, recommend int) error {
var sess *xorm.Session
sess = x.ID(id)
defer sess.Close()
re, err := sess.Cols("recommend").Update(&AiModelManage{
Recommend: recommend,
})
if err != nil {
return err
}
log.Info("success to update recommend from db.re=" + fmt.Sprint((re)))
return nil
}

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

func ModifyLocalModel(id string, name, label, description string, engine int, isPrivate bool, license string) error {
var sess *xorm.Session
sess = x.ID(id)
defer sess.Close()
re, err := sess.Cols("name", "label", "description", "engine", "is_private", "license").Update(&AiModelManage{
Description: description,
Name: name,
Label: label,
Engine: int64(engine),
IsPrivate: isPrivate,
License: license,
})
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,42 +495,197 @@ func QueryModelByName(name string, repoId int64) []*AiModelManage {
return aiModelManageList
}

func QueryModel(opts *AiModelQueryOptions) ([]*AiModelManage, int64, error) {
func QueryModelByRepoId(repoId int64) []*AiModelManage {
sess := x.NewSession()
defer sess.Close()
sess.Select("*").Table("ai_model_manage").
Where("repo_id=?", repoId)
aiModelManageList := make([]*AiModelManage, 0)
sess.Find(&aiModelManageList)
return aiModelManageList
}

var cond = builder.NewCond()
func DeleteModelByRepoId(repoId int64) error {
sess := x.NewSession()
defer sess.Close()
re, err := sess.Delete(&AiModelManage{
RepoId: repoId,
})
if err != nil {
return err
}
log.Info("success to delete DeleteModelByRepoId from db.re=" + fmt.Sprint((re)))
return nil
}

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()
var where string
where += " ai_model_manage.user_id > 0 "
if opts.RepoID > 0 {
cond = cond.And(
builder.Eq{"ai_model_manage.repo_id": opts.RepoID},
)
where += " and ai_model_manage.repo_id= " + fmt.Sprint(opts.RepoID)
}

if opts.UserID > 0 {
cond = cond.And(
builder.Eq{"ai_model_manage.user_id": opts.UserID},
)
where += " and ai_model_manage.user_id=" + fmt.Sprint(opts.UserID)
}

if opts.New >= 0 {
cond = cond.And(
builder.Eq{"ai_model_manage.new": opts.New},
)
where += " and ai_model_manage.new=" + fmt.Sprint(opts.New)
}

if len(opts.ModelID) > 0 {
cond = cond.And(
builder.Eq{"ai_model_manage.id": opts.ModelID},
)
where += " and ai_model_manage.id='" + fmt.Sprint(opts.ModelID) + "'"
}

if (opts.Type) >= 0 {
where += " and ai_model_manage.type=" + fmt.Sprint(opts.Type)
}

if (opts.Status) >= 0 {
where += " and ai_model_manage.status=" + fmt.Sprint(opts.Status)
}
if !opts.IsQueryPrivate {
where += " and ai_model_manage.is_private=false"
}
if opts.IsRecommend {
where += " and ai_model_manage.recommend=1"
}
if opts.FrameFilter >= 0 {
if opts.FrameFilter == 2 {
where += " and ai_model_manage.engine in (2,121,122)"
} else {
where += " and ai_model_manage.engine=" + fmt.Sprint(opts.FrameFilter)
}
}
if opts.LabelFilter != "" {
where += " and ai_model_manage.label ILIKE '%" + opts.LabelFilter + "%'"
}
if opts.ComputeResourceFilter != "" {
where += " and ai_model_manage.compute_resource ILIKE '%" + opts.ComputeResourceFilter + "%'"
}
if opts.Namelike != "" {
where += " and ( ai_model_manage.name ILIKE '%" + opts.Namelike + "%'"
where += " or ai_model_manage.description ILIKE '%" + opts.Namelike + "%'"
where += " or ai_model_manage.label ILIKE '%" + opts.Namelike + "%')"
}
if opts.NotNeedEmpty {
where += " and ai_model_manage.size > 0 "
}
if opts.HasOnlineUrl > 0 {
where += " and ai_model_manage.has_online_url =1 "
}
var count int64
var err error
if opts.IsCollected {
where += " and ai_model_collect.user_id=" + fmt.Sprint(opts.CollectedUserId)

count, err = sess.Join("INNER", "ai_model_collect", "ai_model_manage.id = ai_model_collect.model_id").Where(where).Count(new(AiModelManage))
if err != nil {
log.Info("error=" + err.Error())
return nil, 0, fmt.Errorf("Count: %v", err)
}
} else {
count, err = sess.Where(where).Count(new(AiModelManage))
if err != nil {
log.Info("error=" + err.Error())
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)
}
if opts.IsCollected {
sess.Join("INNER", "ai_model_collect", "ai_model_manage.id = ai_model_collect.model_id")
}
orderby := "ai_model_manage.created_unix desc"
if opts.SortType != "" {
orderby = opts.SortType
}
sess.OrderBy(orderby)
aiModelManages := make([]*AiModelManage, 0, setting.UI.IssuePagingNum)
if err := sess.Table("ai_model_manage").Where(where).
Find(&aiModelManages); err != nil {
log.Info("error=" + err.Error())
return nil, 0, fmt.Errorf("Find: %v", err)
}

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_manage.type": opts.Type},
builder.Eq{"ai_model_convert.repo_id": opts.RepoID},
)
}

count, err := sess.Where(cond).Count(new(AiModelManage))
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)
}
@@ -190,13 +699,201 @@ func QueryModel(opts *AiModelQueryOptions) ([]*AiModelManage, int64, error) {
}
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
}

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

func DeleteModelCollect(modelCollect *AiModelCollect) error {
sess := x.NewSession()
defer sess.Close()
re, err := sess.Delete(modelCollect)
if err != nil {
log.Info("delete AiModelCollect error." + err.Error())
return err
}
log.Info("success to delete AiModelCollect db.re=" + fmt.Sprint((re)))
return nil
}

func QueryModelCollectNum(modelId string) int {
sess := x.NewSession()
defer sess.Close()
modelCollects := make([]*AiModelCollect, 0)
err := sess.Table(new(AiModelCollect)).Where("model_id=?", modelId).Find(&modelCollects)
if err == nil {
return len(modelCollects)
}
return 0
}
func QueryModelCollectByUserId(modelId string, userId int64) []*AiModelCollect {
sess := x.NewSession()
defer sess.Close()
modelCollects := make([]*AiModelCollect, 0)
err := sess.Table(new(AiModelCollect)).Where("model_id=? and user_id=?", modelId, userId).Find(&modelCollects)
if err == nil {
return modelCollects
}
return nil
}

sess.OrderBy("ai_model_manage.created_unix DESC")
func QueryModelCollectedStatus(modelIds []string, userId int64) map[string]*AiModelCollect {
sess := x.NewSession()
defer sess.Close()
modelCollects := make([]*AiModelCollect, 0)
var cond = builder.NewCond()
cond = cond.And(
builder.In("model_id", modelIds),
)
cond = cond.And(
builder.Eq{"user_id": userId},
)
result := make(map[string]*AiModelCollect, 0)
err := sess.Table(new(AiModelCollect)).Where(cond).Find(&modelCollects)
if err == nil {
for _, v := range modelCollects {
result[v.ModelID] = v
}
}
return result
}

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

func DeleteModelFile(modelFile *AiModelFile) error {
sess := x.NewSession()
defer sess.Close()
re, err := sess.Delete(modelFile)
if err != nil {
log.Info("delete modelFile error." + err.Error())
return err
}
log.Info("success to delete modelFile db.re=" + fmt.Sprint((re)))
return nil
}

func QueryModelFileByModelId(modelId string) []*AiModelFile {
sess := x.NewSession()
defer sess.Close()
var cond = builder.NewCond()
cond = cond.And(
builder.Eq{"model_id": modelId},
)
result := make([]*AiModelFile, 0)
err := sess.Table(new(AiModelFile)).Where(cond).Find(&result)
if err != nil {
log.Info("query AiModelFile failed, err=" + err.Error())
}
return result
}

func QueryModelForSearch(opts *AiModelQueryOptions) ([]*AiModelManage, int64, error) {
sess := x.NewSession()
defer sess.Close()
var where string
where += "ai_model_manage.user_id=" + fmt.Sprint(opts.UserID)
where += " and ai_model_manage.is_private=true"
if opts.Namelike != "" {
where += " and ( ai_model_manage.name ILIKE '%" + opts.Namelike + "%'"
where += " or ai_model_manage.description ILIKE '%" + opts.Namelike + "%'"
where += " or ai_model_manage.label ILIKE '%" + opts.Namelike + "%'"
where += " or ai_model_file.name ILIKE '%" + opts.Namelike + "%')"
}

var count int64
var err error
count, err = sess.Join("LEFT", "ai_model_file", "ai_model_manage.id = ai_model_file.model_id").Select("count(distinct(ai_model_manage.id))").Where(where).Count(new(AiModelManage))
if err != nil {
log.Info("error=" + err.Error())
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.Join("LEFT", "ai_model_file", "ai_model_manage.id = ai_model_file.model_id")
orderby := "ai_model_manage.created_unix desc"
if opts.SortType != "" {
orderby = opts.SortType
}
sess.OrderBy(orderby)
aiModelManages := make([]*AiModelManage, 0, setting.UI.IssuePagingNum)
if err := sess.Table("ai_model_manage").Where(cond).
if err := sess.Select("distinct(ai_model_manage.*)").Table("ai_model_manage").Where(where).
Find(&aiModelManages); err != nil {
log.Info("error=" + err.Error())
return nil, 0, fmt.Errorf("Find: %v", err)
}

return aiModelManages, count, nil
}

func QueryModelRepoByModelID(modelId string) (*Repository, error) {
r := &Repository{}
has, err := x.Where(builder.NewCond().
And(builder.Eq{"id": builder.Select("repo_id").
From("ai_model_manage").
Where(builder.Eq{"id": modelId})})).Get(r)
if err != nil {
return nil, err
} else if !has {
return nil, &ErrRecordNotExist{}
}
return r, nil
}

func QueryModelMapsByIds(ids []string) (map[string]*AiModelManage, error) {
sess := x.NewSession()
defer sess.Close()
re := make([]*AiModelManage, 0)
err := sess.Table(new(AiModelManage)).In("id", ids).Find(&re)
if err != nil {
return nil, err
}
resultMap := make(map[string]*AiModelManage, 0)
for _, m := range re {
resultMap[m.ID] = m
}
return resultMap, nil
}

//created_unix
func QueryModelIdsByPaging(pageSize, pageNum int, sort string) ([]string, error) {
sess := x.NewSession()
defer sess.Close()
re := make([]string, 0)
start := (pageNum - 1) * pageSize
err := sess.Table("ai_model_manage").Cols("id").OrderBy(sort).Limit(pageSize, start).Find(&re)
return re, err
}

+ 107
- 15
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)
@@ -273,7 +329,7 @@ func DeleteAttachments(attachments []*Attachment, remove bool) (int, error) {
log.Info("Message:%s\n", obsError.Message)
}
}
DeleteFileChunkById(a.UUID)
//rf := path.Join(a.UUID[0:1], a.UUID[1:2])
/*
files, err := repo.GetDatasetDirs(a.UUID, "")
@@ -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"`
}

+ 442
- 0
models/card_request.go View File

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

import (
"errors"
"strings"

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

const CARD_REQUEST_COMMIT = 1
const CARD_REQUEST_AGREE = 2
const CARD_REQEST_DISAGREE = 3

const RESOURCE_TYPE_SHARE = 1 //共享
const RESOURCE_TYPE_EXCLUSIVE = 2 //独占

const OrderByIDDesc = "card_request.id desc"
const OrderByStatus = "card_request.status asc,card_request.id desc"

type CardRequest struct {
ID int64 `xorm:"pk autoincr"`
ComputeResource string
UID int64
UserName string `xorm:"-"`
CardType string
AccCardsNum string
DiskCapacity int64
ResourceType int
BeginDate string
BeginUnix int64 `xorm:"INDEX"`
EndDate string
EndUnix int64 `xorm:"INDEX"`
Contact string
PhoneNumber string
EmailAddress string
Org string `xorm:"varchar(500)"`
Description string `xorm:"varchar(3000)"`
Status int
Review string `xorm:"varchar(3000)"`
CreatedUnix int64 `xorm:"INDEX created"`
UpdatedUnix int64 `xorm:"INDEX updated"`
DeleteUnix int64 `xorm:"deleted"`
}

type CardRequestSpecRes struct {
ID int64
ComputeResource string
UID int64
UserName string
CardType string
AccCardsNum string
DiskCapacity int64
ResourceType int
BeginDate string
BeginUnix int64
EndDate string
EndUnix int64
Contact string
PhoneNumber string
EmailAddress string
Org string
Description string
Status int
Review string
CreatedUnix int64
UpdatedUnix int64
DeleteUnix int64
Specs []RequestSpecInfo
}

func (CardRequestSpecRes) TableName() string {
return "card_request"
}

type CardRequestSpec struct {
ID int64 `xorm:"pk autoincr"`
RequestId int64 `xorm:"unique(idx_request_spec)"`
SpecId int64 `xorm:"unique(idx_request_spec)"`
CreatedTime timeutil.TimeStamp `xorm:"created"`
}

type CardRequestOptions struct {
ListOptions
UserID int64
OrderBy string
Keyword string

AiCenterCode string
QueueId int64
ComputeResource string
AccCardType string
Cluster string
ResourceType int
UseBeginTime int64
UseEndTime int64
BeginTimeUnix int64
EndTimeUnix int64
NeedSpec bool
}
type CardRequestShowList struct {
Total int64 `json:"total"`
CardRequestList []*CardRequestSpecShow `json:"cardRequestList"`
}

type CardRequestSpecShow struct {
ID int64 `json:"id"`
ComputeResource string `json:"compute_resource"`
CardType string `json:"card_type"`
AccCardsNum string `json:"acc_cards_num"`
BeginDate string `json:"begin_date"`
EndDate string `json:"end_date"`
ResourceType int `json:"resource_type"`
DiskCapacity int64 `json:"disk_capacity"`
UID int64 `json:"uid"`
UserName string `json:"user_name"`
TargetCenter []string `json:"target_center"`
Contact string `json:"contact"`
PhoneNumber string `json:"phone_number"`
EmailAddress string `json:"email_address"`
Org string `json:"org"`
Description string `json:"description"`
Status int `json:"status"`
Review string `json:"review"`
CreatedUnix int64 `json:"created_unix"`
Specs []RequestSpecInfo `json:"specs"`
}

type RequestSpecInfo struct {
ID int64
SourceSpecId string
AccCardsNum int
CpuCores int
MemGiB float32
GPUMemGiB float32
ShareMemGiB float32
UnitPrice int
Status int
UpdatedTime timeutil.TimeStamp
RequestId int64
//queue
Cluster string
AiCenterCode string
AiCenterName string
QueueCode string
QueueName string
QueueType string
QueueId int64
ComputeResource string
AccCardType string
HasInternet int
}

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

type CardRequestReview struct {
ID int64
Review string
SpecIds []int64
}

func AgreeCardRequest(r CardRequestReview) error {
sess := x.NewSession()
var err error
defer func() {
if err != nil {
sess.Rollback()
}
sess.Close()
}()

// find old scene
old := CardRequest{}
if has, _ := sess.ID(r.ID).Get(&old); !has {
return errors.New("CardRequest 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")
}

rs := CardRequest{
Status: CARD_REQUEST_AGREE,
Review: "",
}
if _, err = sess.ID(r.ID).Cols("status", "review").Update(&rs); err != nil {
return err
}

//delete scene spec relation
if _, err = sess.Where("request_id = ? ", r.ID).Delete(&CardRequestSpec{}); err != nil {
sess.Rollback()
return err
}

if len(r.SpecIds) == 0 {
return sess.Commit()
}
//build new scene spec relation
rss := make([]CardRequestSpec, len(r.SpecIds))
for i, v := range r.SpecIds {
rss[i] = CardRequestSpec{
RequestId: r.ID,
SpecId: v,
}
}
if _, err = sess.Insert(&rss); err != nil {
sess.Rollback()
return err
}

return sess.Commit()
}

func DisagreeCardRequest(r CardRequestReview) error {
sess := x.NewSession()
var err error
defer func() {
if err != nil {
sess.Rollback()
}
sess.Close()
}()

// find old scene
old := CardRequest{}
if has, _ := sess.ID(r.ID).Get(&old); !has {
return errors.New("CardRequest not exist")
}
//update review_message
rs := CardRequest{
Status: CARD_REQEST_DISAGREE,
Review: r.Review,
}
if _, err = sess.ID(r.ID).Update(&rs); err != nil {
return err
}

//delete scene spec relation
if _, err = sess.Where("request_id = ? ", r.ID).Delete(&CardRequestSpec{}); err != nil {
sess.Rollback()
return err
}

return sess.Commit()
}

func CreateCardRequest(cardRequest *CardRequest) error {
_, err := x.Insert(cardRequest)
return err
}
func GetCardRequestById(id int64) (*CardRequest, error) {
rel := new(CardRequest)
has, err := x.
ID(id).
Get(rel)
if err != nil {
return nil, err
} else if !has {
return nil, ErrNotExist{id}
}

return rel, nil
}

func SearchCardRequest(opts *CardRequestOptions) (int64, []*CardRequestSpecRes, error) {
var cond = builder.NewCond()
if opts.Page <= 0 {
opts.Page = 1
}

needJoinSpec := false
if opts.Keyword != "" {
lowerKeyWord := strings.ToLower(opts.Keyword)
cond = cond.And(builder.Or(builder.Like{"LOWER(card_request.contact)", lowerKeyWord},
builder.Like{"LOWER(card_request.acc_cards_num)", lowerKeyWord},
builder.Like{"LOWER(card_request.description)", lowerKeyWord}, builder.Like{"LOWER(card_request.description)", lowerKeyWord},
builder.Like{"LOWER(card_request.phone_number)", lowerKeyWord}, builder.Like{"LOWER(card_request.org)", lowerKeyWord},
builder.Like{"LOWER(\"user\".name)", lowerKeyWord}))
}
if opts.UserID != 0 {
cond = cond.And(builder.Eq{"\"user\".id": opts.UserID})
}

if opts.ResourceType != 0 {
cond = cond.And(builder.Eq{"card_request.resource_type": opts.ResourceType})
}
if opts.AiCenterCode != "" {
needJoinSpec = true
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})
needJoinSpec = true
}
if opts.ComputeResource != "" {
cond = cond.And(builder.Eq{"card_request.compute_resource": opts.ComputeResource})
}
if opts.AccCardType != "" {
cond = cond.And(builder.Eq{"card_request.card_type": opts.AccCardType})
}
if opts.Cluster != "" {
cond = cond.And(builder.Eq{"resource_queue.cluster": opts.Cluster})
needJoinSpec = true
}

if opts.UseBeginTime != 0 {
cond = cond.And(builder.Gte{"card_request.begin_unix": opts.UseBeginTime})
}
if opts.UseEndTime != 0 {
cond = cond.And(builder.Lte{"card_request.end_unix": opts.UseEndTime})
}

if opts.BeginTimeUnix != 0 {
cond = cond.And(builder.Gte{"card_request.created_unix": opts.BeginTimeUnix})
}
if opts.EndTimeUnix != 0 {
cond = cond.And(builder.Lte{"card_request.created_unix": opts.EndTimeUnix})
}
if opts.OrderBy == "" {
opts.OrderBy = OrderByIDDesc
}

cond = cond.And(builder.NewCond().Or(builder.Eq{"card_request.delete_unix": 0}).Or(builder.IsNull{"card_request.delete_unix"}))
cols := []string{"card_request.id", "card_request.compute_resource", "card_request.contact", "card_request.card_type", "card_request.acc_cards_num",
"card_request.disk_capacity", "card_request.resource_type", "card_request.begin_date", "card_request.end_date", "card_request.uid",
"card_request.phone_number", "card_request.email_address", "card_request.org", "card_request.description", "card_request.status", "card_request.review",
"card_request.created_unix"}
var count int64
var err error
if needJoinSpec {
count, err = x.Where(cond).
Distinct("card_request.id").
Join("INNER", "card_request_spec", "card_request_spec.request_id = card_request.id").
Join("INNER", "user", "\"user\".id = card_request.uid").
Join("INNER", "resource_specification", "resource_specification.id = card_request_spec.spec_id").
Join("INNER", "resource_queue", "resource_queue.id = resource_specification.queue_id").
Count(&CardRequestSpecRes{})
if err != nil {
return 0, nil, err
}
} else {
count, err = x.Where(cond).
Distinct("card_request.id").
Join("INNER", "user", "\"user\".id = card_request.uid").
Count(&CardRequestSpecRes{})
}

r := make([]*CardRequestSpecRes, 0)
if needJoinSpec {
if err = x.Where(cond).Distinct(cols...).
Join("INNER", "card_request_spec", "card_request_spec.request_id = card_request.id").
Join("INNER", "user", "\"user\".id = card_request.uid").
Join("INNER", "resource_specification", "resource_specification.id = card_request_spec.spec_id").
Join("INNER", "resource_queue", "resource_queue.id = resource_specification.queue_id").
OrderBy(opts.OrderBy).
Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).
Find(&r); err != nil {
return 0, nil, err
}
} else {
if err = x.Where(cond).Distinct(cols...).
Join("INNER", "user", "\"user\".id = card_request.uid").
OrderBy(opts.OrderBy).
Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).
Find(&r); err != nil {
return 0, nil, err
}
}

if len(r) == 0 {
return 0, r, err
}

for _, v := range r {
user, _ := GetUserByID(v.UID)
if user != nil {
v.UserName = user.Name
}

}

//find related specs
if opts.NeedSpec {
requestIds := make([]int64, 0, len(r))
for _, v := range r {
requestIds = append(requestIds, v.ID)
}

specs := make([]RequestSpecInfo, 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",
"card_request_spec.request_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.queue_name",
"resource_queue.queue_type", "resource_queue.ai_center_name",
"resource_queue.has_internet",
).In("card_request_spec.request_id", requestIds).
Join("INNER", "card_request_spec", "card_request_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][]RequestSpecInfo, 0)
for _, v := range specs {
if _, ok := specsMap[v.RequestId]; !ok {
specsMap[v.RequestId] = []RequestSpecInfo{v}
} else {
specsMap[v.RequestId] = append(specsMap[v.RequestId], v)
}
}

for i, v := range r {
s := specsMap[v.ID]
if s == nil {
s = make([]RequestSpecInfo, 0)
}
r[i].Specs = s
}
}

return count, r, nil
}

func UpdateCardRequest(cardRequest *CardRequest) error {
_, err := x.ID(cardRequest.ID).Cols("compute_resource", "contact", "card_type", "acc_cards_num", "disk_capacity", "resource_type", "begin_date", "end_date", "phone_number", "email_address", "org", "description", "begin_unix", "end_unix").Update(cardRequest)
return err
}

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


+ 35
- 0
models/cloudbrain_config.go View File

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

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

type CloudbrainConfig struct {
CloudbrainID int64 `xorm:"pk"`
OutputObjectPrefix string
OutputStorageType string
OutputBucket string
OutputEndpoint string
LogObjectPrefix string
LogStorageType string
LogBucket string
LogEndpoint string
ConfigurationSnapshot string `xorm:"text"`
ContainerDataSnapshot string `xorm:"text"`
CreatedTime timeutil.TimeStamp `xorm:"created"`
UpdatedTime timeutil.TimeStamp `xorm:"updated"`
}

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

func InsertCloudbrainConfig(config *CloudbrainConfig) (int64, error) {
return x.Insert(config)
}

+ 674
- 0
models/cloudbrain_image.go View File

@@ -0,0 +1,674 @@
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 ApplyStatus int

const NoneApply ApplyStatus = 1
const Applying ApplyStatus = 2
const OKApply ApplyStatus = 3
const FailApply ApplyStatus = 4

type Image struct {
ID int64 `xorm:"pk autoincr" json:"id"`
ImageID string `json:"image_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(1000)" 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"`
ComputeResource string `xorm:"varchar(30)" json:"compute_resource"`
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提交失败
ApplyStatus ApplyStatus `xorm:"DEFAULT 1" json:"apply_status"`
Message string `xorm:"varchar(500)" json:"message"`
UseCount int64 `xorm:"NOT NULL DEFAULT 0" json:"useCount"`
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
ApplyStatus int
CloudbrainType int
ListOptions
SearchOrderBy
}

type GrampusImageStatusResult struct {
GrampusResult

Image struct {
CreatedAt int `json:"createdAt"`
Id string `json:"id"`
ImageAddr string `json:"imageAddr"`
ImageDesc string `json:"imageDesc"`
ImageFullAddr string `json:"imageFullAddr"`
ImageName string `json:"imageName"`
ImageStatus int `json:"imageStatus"`
ImageVersion string `json:"imageVersion"`
SourceType int `json:"sourceType"`
SpaceId string `json:"spaceId"`
UpdatedAt int `json:"updatedAt"`
UserId string `json:"userId"`
Username string `json:"username"`
} `json:"image"`
}
type CommitImageGrampusParams struct {
Description string `json:"description"`
ImageName string `json:"imageName"`
ImageVersion string `json:"imageVersion"`
TaskName string `json:"taskName"`
}
type CommitGrampusImageParams struct {
CommitImageGrampusParams
IsPrivate bool
Topics []string
CloudBrainType int
UID int64
Place string
Type int
}

type CommitGrampusImageResult struct {
GrampusResult
ImageId string `json:"imageId"`
}
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.Where("cloudbrain_type=?", TypeCloudBrainOne).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 GetImageByTagAndCloudbrainType(tag string, cloudbrainType int) (*Image, error) {
image := &Image{Tag: tag}
has, err := x.
Where("cloudbrain_type=?", cloudbrainType).
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.ApplyStatus != 0 {
cond = cond.And(builder.Eq{"apply_status": opts.ApplyStatus})
}

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", "message", "apply_status").Update(image)
return err
}

func UpdateLocalImageStatus(image *Image) error {

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

func UpdateLocalImageStatusAndPlace(image *Image) error {

_, err := x.ID(image.ID).Cols("status", "place").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, recommend bool, applyStatus ApplyStatus, message string) error {

image := Image{Type: GetRecommondType(recommend), ApplyStatus: applyStatus, Message: message}
_, err := x.ID(imageId).Cols("type", "apply_status", "message").Update(image)
return err
}

func GetRecommondType(recommond bool) int {
if recommond {

return RECOMMOND_TYPE
} else {
return NORMAL_TYPE
}

}

+ 178
- 0
models/cloudbrain_spec.go View File

@@ -0,0 +1,178 @@
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
QueueName string
QueueType string
Cluster string
HasInternet int
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,
HasInternet: s.HasInternet,
}
}

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,
HasInternet: s.HasInternet,
}
}

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 GetSpecAccCardsNumByID(cloudbrainId int64) (*CloudbrainSpec, error) {
r := &CloudbrainSpec{}
if has, err := x.Select("acc_cards_num").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
}

func UpdateCloudbrainSpec(cloudbrainId int64, s *Specification) (int64, error) {
new := 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,
QueueName: s.QueueName,
QueueType: s.QueueType,
Cluster: s.Cluster,
AiCenterCode: s.AiCenterCode,
AiCenterName: s.AiCenterName,
IsExclusive: s.IsExclusive,
ExclusiveOrg: s.ExclusiveOrg,
HasInternet: s.HasInternet,
}
return x.Where("cloudbrain_id = ?", cloudbrainId).Update(&new)
}

+ 623
- 0
models/cloudbrain_static.go View File

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

import (
"strconv"
"time"

"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/timeutil"
"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"`
DetailedStatus string `json:"DetailedStatus"`
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"`
}

type XPUInfoBase struct {
ID int64 `xorm:"pk autoincr"`
CardType string
CardTypeShow string
ResourceType string
Company string
AccessTime string
}

type XPUInfoStatistic struct {
ID int64 `xorm:"pk autoincr"`
InfoID int64 `xorm:"index"`
Type int
UsedDuration int64
UsedCardHour int64
UserCount int64
TaskCount int64
UpdatedUnix int64
}

type XPUInfoStatisticExtendBase struct {
CardType string
CardTypeShow string
ResourceType string
Company string
AccessTime string
UpdatedUnix int64
UsedCardHour int64
UserCount int64
TaskCount int64
}

func (XPUInfoStatisticExtendBase) TableName() string {
return "xpu_info_statistic"
}

type XPUInfoStatisticShow struct {
CardType string `json:"card_type"`
ResourceType string `json:"resource_type"`
Company string `json:"company"`
AccessTime string `json:"access_time"`
Count int64 `json:"count"`
UpdatedUnix int64 `json:"updated_unix"`
}

const TypeAllDays = 0
const TypeSevenDays = 1
const TypeThirtyDays = 2

var XpuInfoCategories = map[string]string{"card": "used_card_hour", "user": "user_count", "task": "task_count"}
var XpuInfoType = map[string]int{"all": TypeAllDays, "7": TypeSevenDays, "30": TypeThirtyDays}

func GetXPUInfos() ([]*XPUInfoBase, error) {
infos := make([]*XPUInfoBase, 0)
err := xStatistic.Find(&infos)
return infos, err
}

func GetXPUStatisticInfos(dataType int, category string) ([]*XPUInfoStatisticShow, error) {
statistics := make([]XPUInfoStatisticExtendBase, 0)

s := xStatistic.Where("type=?", dataType).
Join("INNER", "xpu_info_base", "xpu_info_base.id = xpu_info_statistic.info_id")
err := s.OrderBy("xpu_info_statistic." + XpuInfoCategories[category] + " desc").Find(&statistics)

if err != nil {
return nil, err
}
var result = make([]*XPUInfoStatisticShow, 0)
for _, statistic := range statistics {
item := &XPUInfoStatisticShow{
CardType: statistic.CardTypeShow,
ResourceType: statistic.ResourceType,
Company: statistic.Company,
AccessTime: statistic.AccessTime,
UpdatedUnix: statistic.UpdatedUnix,
}

if category == "card" {
item.Count = statistic.UsedCardHour
} else if category == "user" {
item.Count = statistic.UserCount
} else {
item.Count = statistic.TaskCount
}
result = append(result, item)
}
return result, nil
}

func UpdateOrInsertXPUInfoStatistic(bean *XPUInfoStatistic) error {

xpuInfoIndb := new(XPUInfoStatistic)
has, err := xStatistic.Where("info_id = ? and type=?", bean.InfoID, bean.Type).Get(xpuInfoIndb)

if err != nil {
return err
}

if has {
_, err = xStatistic.ID(xpuInfoIndb.ID).Cols("used_duration", "user_count", "used_card_hour", "task_count", "updated_unix").Update(bean)

} else {
_, err = xStatistic.Insert(bean)
}
return err

}

func HasTotalTypeXPUInfoStatisticRecord() bool {
xpuInfo := new(XPUInfoStatistic)
total, _ := xStatistic.Where("type =?", TypeAllDays).Count(xpuInfo)
return total > 0
}

func FindTotalTypeXPUInfoStatisticRecords() ([]XPUInfoStatistic, error) {

xpuInfos := make([]XPUInfoStatistic, 0)

err := xStatistic.Where("type =?", TypeAllDays).Find(&xpuInfos)
return xpuInfos, err
}
func GetTotalTypeLatestUpdateUnix() int64 {
var updated []int64
xStatistic.Table("xpu_info_statistic").Cols("updated_unix").Find(&updated)
if len(updated) > 0 {
return updated[0]
}
return 0
}

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 GetCloudbrainCardTimeAndCountGroupByAICenter() ([]map[string]string, error) {
countSql := `select ai_center,SUM(
COALESCE(a.duration *
CASE
WHEN a.work_server_number = 0 THEN 1
ELSE COALESCE(a.work_server_number, 1)
END *
COALESCE(cloudbrain_spec.acc_cards_num, 1), 0)
) as card_duration,count(*) num from

(select id,duration,work_server_number,case when type=0 then 'OpenIOne' when type=1 then 'OpenITwo' when type=3 then 'OpenIChengdu' else split_part(ai_center, '+',1)
end ai_center
FROM public.cloudbrain ) a Left JOIN cloudbrain_spec on a.id = cloudbrain_spec.cloudbrain_id
where ai_center!='' group by a.ai_center order by card_duration 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 GetCreateHourPeriodCount(cloudbrains []*CloudbrainInfo, dateBeginTime time.Time, dateEndTime time.Time) (map[string]int64, error) {
var createHourPeriodCount = make(map[string]int64)
for _, cloudbrain := range cloudbrains {
if cloudbrain.StartTime != 0 && cloudbrain.EndTime == 0 {
cloudbrain.EndTime = timeutil.TimeStamp(time.Now().Unix())
}
hourBeginTime := dateBeginTime.Unix()
hourEndTime := hourBeginTime + int64(3600)
for hour := 0; hour < 24; hour++ {
if int64(cloudbrain.Cloudbrain.CreatedUnix) >= hourBeginTime && int64(cloudbrain.Cloudbrain.CreatedUnix) < hourEndTime {
createHourPeriodCount[strconv.Itoa(hour)] = createHourPeriodCount[strconv.Itoa(hour)] + 1
}
hourBeginTime = hourEndTime
hourEndTime = hourEndTime + int64(3600)
}
}
return createHourPeriodCount, nil
}

func GetRunHourPeriodCount(cloudbrains []*CloudbrainInfo, dateBeginTime time.Time, dateEndTime time.Time) (map[string]int64, error) {
var runHourPeriodCount = make(map[string]int64)
for _, cloudbrain := range cloudbrains {
if cloudbrain.StartTime != 0 && cloudbrain.EndTime == 0 {
cloudbrain.EndTime = timeutil.TimeStamp(time.Now().Unix())
}
hourBeginTime := dateBeginTime.Unix()
hourEndTime := hourBeginTime + int64(3600)
for hour := 0; hour < 24; hour++ {
if cloudbrain.StartTime.AsTime().Unix() < hourEndTime && cloudbrain.EndTime.AsTime().Unix() > hourBeginTime {
runHourPeriodCount[strconv.Itoa(hour)] = runHourPeriodCount[strconv.Itoa(hour)] + 1
}
hourBeginTime = hourEndTime
hourEndTime = hourEndTime + int64(3600)
}
}
return runHourPeriodCount, 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;

+ 82
- 0
models/error.go View File

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

import (
"fmt"
pg "github.com/lib/pq"

"code.gitea.io/gitea/modules/git"
)
@@ -2012,3 +2013,84 @@ 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")
}

func IsPGErrUniqueViolation(err error) bool {
if err != nil {
e := err.(*pg.Error)
//23505 is postgrey error code for unique_violation
//see https://www.postgresql.org/docs/current/errcodes-appendix.html
//if unique_violation,user role record exists,just return
if e.Code == "23505" {
return true
}
}
return false
}

type NetworkError struct {
}

func IsNetworkError(err error) bool {
_, ok := err.(NetworkError)
return ok
}

func (err NetworkError) Error() string {
return fmt.Sprintf("Network error")
}

type ErrModelartsDeployNotExist struct {
ID string
}

func (err ErrModelartsDeployNotExist) Error() string {
return fmt.Sprintf("Deployment %s does not exist", err.ID)
}

type ErrPretrainModelNotExist struct {
}

func IsErrPretrainModelNotExist(err error) bool {
_, ok := err.(ErrPretrainModelNotExist)
return ok
}

func (err ErrPretrainModelNotExist) Error() string {
return fmt.Sprintf("pretrain model is not exists")
}

type ErrCannotStopCreatingGrampusJob struct {
}

func IsErrCannotStopCreatingGrampusJob(err error) bool {
_, ok := err.(ErrCannotStopCreatingGrampusJob)
return ok
}

func (err ErrCannotStopCreatingGrampusJob) Error() string {
return fmt.Sprintf("job is creating, can not be stopped")
}

+ 117
- 5
models/file_chunk.go View File

@@ -5,6 +5,7 @@ import (

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

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

const (
TypeCloudBrainOne int = iota
TypeCloudBrainTwo
)

type FileChunk struct {
ID int64 `xorm:"pk autoincr"`
UUID string `xorm:"uuid UNIQUE"`
@@ -33,6 +29,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 +67,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)

@@ -65,6 +93,27 @@ func getFileChunkByMD5AndUser(e Engine, md5 string, userID int64, typeCloudBrain
return fileChunk, nil
}

// GetAttachmentByID returns attachment by given id
func GetFileChunksByUserId(userId int64, lastTime int64, isUploadFinished bool) ([]*FileChunk, error) {
return getFileChunksByUserId(x, userId, lastTime, isUploadFinished)
}

func getFileChunksByUserId(e Engine, userId int64, lastTime int64, isUploadFinished bool) ([]*FileChunk, error) {
fileChunks := make([]*FileChunk, 0)
cond := builder.NewCond()
cond = cond.And(builder.Eq{"user_id": userId})
if lastTime > 0 {
cond = cond.And(builder.Gte{"created_unix": lastTime})
}
if !isUploadFinished {
cond = cond.And(builder.Eq{"is_uploaded": 0})
}
if err := e.Where(cond).Find(&fileChunks); err != nil {
return nil, err
}
return fileChunks, nil
}

// GetAttachmentByID returns attachment by given id
func GetFileChunkByUUID(uuid string) (*FileChunk, error) {
return getFileChunkByUUID(x, uuid)
@@ -81,6 +130,41 @@ 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
}

func GetModelFileChunksByUserId(userId int64, lastTime int64, isUploadFinished bool) ([]*ModelFileChunk, error) {
return getModelFileChunksByUserId(x, userId, lastTime, isUploadFinished)
}

func getModelFileChunksByUserId(e Engine, userId int64, lastTime int64, isUploadFinished bool) ([]*ModelFileChunk, error) {
fileChunks := make([]*ModelFileChunk, 0)
cond := builder.NewCond()
cond = cond.And(builder.Eq{"user_id": userId})
if lastTime > 0 {
cond = cond.And(builder.Gte{"created_unix": lastTime})
}
if !isUploadFinished {
cond = cond.And(builder.Eq{"is_uploaded": 0})
}
if err := e.Where(cond).Find(&fileChunks); err != nil {
return nil, err
}
return fileChunks, nil
}

// InsertFileChunk insert a record into file_chunk.
func InsertFileChunk(fileChunk *FileChunk) (_ *FileChunk, err error) {
if _, err := x.Insert(fileChunk); err != nil {
@@ -90,6 +174,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 +203,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 +235,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


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save