Browse Source

Merge pull request 'V20220718合入develop' (#2526) from V20220718 into develop

Reviewed-on: https://git.openi.org.cn/OpenI/aiforge/pulls/2526
pull/2527/head
lewis 2 years ago
parent
commit
f793b63b01
100 changed files with 15715 additions and 504 deletions
  1. +20
    -44
      custom/public/rotation3D/rotation3D.css
  2. +11
    -3
      go.mod
  3. +54
    -0
      go.sum
  4. +688
    -0
      index.html
  5. +252
    -3
      models/ai_model_manage.go
  6. +123
    -23
      models/cloudbrain.go
  7. +15
    -3
      models/custom_migrations.go
  8. +8
    -0
      models/login_source.go
  9. +1
    -0
      models/models.go
  10. +36
    -0
      models/repo_activity_custom.go
  11. +47
    -0
      models/user.go
  12. +437
    -0
      models/user_analysis_for_activity.go
  13. +18
    -9
      models/user_business_analysis.go
  14. +14
    -0
      models/user_business_struct.go
  15. +26
    -0
      modules/auth/cloudbrain.go
  16. +46
    -1
      modules/auth/user_form.go
  17. +159
    -5
      modules/cloudbrain/cloudbrain.go
  18. +33
    -10
      modules/cloudbrain/resty.go
  19. +4
    -0
      modules/context/auth.go
  20. +64
    -21
      modules/grampus/grampus.go
  21. +201
    -32
      modules/modelarts/modelarts.go
  22. +111
    -0
      modules/modelarts/resty.go
  23. +61
    -0
      modules/phone/phone.go
  24. +43
    -0
      modules/public/dynamic.go
  25. +29
    -0
      modules/public/static.go
  26. +60
    -0
      modules/redis/redis_client/client.go
  27. +47
    -0
      modules/setting/phone.go
  28. +92
    -20
      modules/setting/setting.go
  29. +12
    -0
      modules/setting/slideimage.go
  30. +308
    -0
      modules/slideimage/slideimage.go
  31. +43
    -0
      modules/storage/minio_ext.go
  32. +80
    -8
      modules/storage/obs.go
  33. +13
    -0
      modules/templates/helper.go
  34. +59
    -2
      options/locale/locale_en-US.ini
  35. +61
    -4
      options/locale/locale_zh-CN.ini
  36. +0
    -24
      public/home/home.js
  37. BIN
      public/img/slide/bg/1.png
  38. BIN
      public/img/slide/bg/2.png
  39. BIN
      public/img/slide/bg/3.png
  40. BIN
      public/img/slide/bg/4.png
  41. BIN
      public/img/slide/mask/mask.png
  42. +386
    -0
      public/self/ztree/css/awesomeStyle/awesome.css
  43. +146
    -0
      public/self/ztree/css/awesomeStyle/awesome.less
  44. +480
    -0
      public/self/ztree/css/awesomeStyle/fa.less
  45. BIN
      public/self/ztree/css/awesomeStyle/img/loading.gif
  46. +33
    -0
      public/self/ztree/css/demo.css
  47. BIN
      public/self/ztree/css/metroStyle/img/line_conn.png
  48. BIN
      public/self/ztree/css/metroStyle/img/loading.gif
  49. BIN
      public/self/ztree/css/metroStyle/img/metro.gif
  50. BIN
      public/self/ztree/css/metroStyle/img/metro.png
  51. +96
    -0
      public/self/ztree/css/metroStyle/metroStyle.css
  52. BIN
      public/self/ztree/css/zTreeStyle/img/diy/1_close.png
  53. BIN
      public/self/ztree/css/zTreeStyle/img/diy/1_open.png
  54. BIN
      public/self/ztree/css/zTreeStyle/img/diy/2.png
  55. BIN
      public/self/ztree/css/zTreeStyle/img/diy/3.png
  56. BIN
      public/self/ztree/css/zTreeStyle/img/diy/4.png
  57. BIN
      public/self/ztree/css/zTreeStyle/img/diy/5.png
  58. BIN
      public/self/ztree/css/zTreeStyle/img/diy/6.png
  59. BIN
      public/self/ztree/css/zTreeStyle/img/diy/7.png
  60. BIN
      public/self/ztree/css/zTreeStyle/img/diy/8.png
  61. BIN
      public/self/ztree/css/zTreeStyle/img/diy/9.png
  62. BIN
      public/self/ztree/css/zTreeStyle/img/line_conn.gif
  63. BIN
      public/self/ztree/css/zTreeStyle/img/loading.gif
  64. BIN
      public/self/ztree/css/zTreeStyle/img/zTreeStandard.gif
  65. BIN
      public/self/ztree/css/zTreeStyle/img/zTreeStandard.png
  66. +99
    -0
      public/self/ztree/css/zTreeStyle/zTreeStyle.css
  67. +167
    -0
      public/self/ztree/js/jquery-1.4.4.min.js
  68. +3878
    -0
      public/self/ztree/js/jquery.ztree.all.js
  69. +2019
    -0
      public/self/ztree/js/jquery.ztree.core.js
  70. +652
    -0
      public/self/ztree/js/jquery.ztree.excheck.js
  71. +1207
    -0
      public/self/ztree/js/jquery.ztree.exedit.js
  72. +405
    -0
      public/self/ztree/js/jquery.ztree.exhide.js
  73. +16
    -0
      routers/api/v1/api.go
  74. +399
    -13
      routers/api/v1/repo/cloudbrain.go
  75. +20
    -4
      routers/api/v1/repo/cloudbrain_dashboard.go
  76. +91
    -9
      routers/api/v1/repo/modelarts.go
  77. +9
    -0
      routers/home.go
  78. +3
    -0
      routers/init.go
  79. +795
    -0
      routers/repo/ai_model_convert.go
  80. +81
    -14
      routers/repo/ai_model_manage.go
  81. +410
    -54
      routers/repo/cloudbrain.go
  82. +68
    -7
      routers/repo/grampus.go
  83. +141
    -88
      routers/repo/modelarts.go
  84. +3
    -0
      routers/repo/repo.go
  85. +119
    -1
      routers/repo/user_data_analysis.go
  86. +33
    -1
      routers/routes/routes.go
  87. +313
    -29
      routers/user/auth.go
  88. +14
    -0
      routers/user/setting/profile.go
  89. +93
    -0
      services/phone/phone.go
  90. +2
    -2
      templates/admin/cloudbrain/list.tmpl
  91. +2
    -0
      templates/admin/user/list.tmpl
  92. +66
    -13
      templates/base/footer.tmpl
  93. +5
    -4
      templates/custom/select_dataset_train.tmpl
  94. +27
    -0
      templates/custom/wait_count.tmpl
  95. +28
    -0
      templates/custom/wait_count_train.tmpl
  96. +2
    -8
      templates/home.tmpl
  97. +92
    -0
      templates/privacy.tmpl
  98. +1
    -1
      templates/repo/cloudbrain/benchmark/index.tmpl
  99. +37
    -38
      templates/repo/cloudbrain/benchmark/new.tmpl
  100. +1
    -6
      templates/repo/cloudbrain/benchmark/show.tmpl

+ 20
- 44
custom/public/rotation3D/rotation3D.css View File

@@ -29,51 +29,22 @@
}
.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; }
.rotation3D__item .cont .iconfont { font-size: 28px; margin-top: 30px; margin-bottom: 96px; display: block; height: 35px;}
.rotation3D__item .cont p{ color: #101010; }
.itemList .rotation3D__item .cont p::after{
font-size: 12px;
content: '';
position: absolute;
left: 0;
right: 0;
margin-top: 60px;
color: #101010;
}
.itemList .rotation3D__item:nth-child(1) .cont p::after{
content: "鹏城云脑一号";
}
.itemList .rotation3D__item:nth-child(2) .cont p::after{
content: "鹏城云脑二号";
}
.itemList .rotation3D__item:nth-child(3) .cont p::after{
content: "北大人工智能集群系统";
}
.itemList .rotation3D__item:nth-child(4) .cont p::after{
content: "合肥类脑智能开放平台";
.lineList .rotation3D__line:nth-child(5n+0) .dot{
}
.itemList .rotation3D__item:nth-child(5) .cont p::after{
content: "武汉人工智能计算中心";
.lineList .rotation3D__line:nth-child(5n+1) .dot{
animation-delay: 1s;
}
.itemList .rotation3D__item:nth-child(6) .cont p::after{
content: "西安未来人工智能计算中心";
.lineList .rotation3D__line:nth-child(5n+2) .dot{
animation-delay: 3s;
}
.itemList .rotation3D__item:nth-child(7) .cont p::after{
content: "更多接入中…";
.lineList .rotation3D__line:nth-child(5n+3) .dot{
animation-delay: 2s;
}
.itemList .rotation3D__item:nth-child(8) .cont p::after{
content: "中原人工智能计算中心";
.lineList .rotation3D__line:nth-child(5n+3) .dot{
animation-delay: 4s;
}
.itemList .rotation3D__item:nth-child(9) .cont p::after{
content: "成都人工智能计算中心";
}
.itemList .rotation3D__item:nth-child(10) .cont p::after{
content: "横琴先进智能计算中心";
}
.itemList .rotation3D__item:nth-child(11) .cont p::after{
content: "国家超级计算济南中心";
}

.rotation3D__item.blue{ color: #01e9fc; }
.rotation3D__item.green{ color: #b4b3ca; }
.rotation3D__item.yellow{ color: #ffd200; }
@@ -90,14 +61,17 @@
---------------------------*/
.rotation3D__line{
position: absolute; left: 50%; top: 50%;
display: block; width: 1px; height: 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;
}
.rotation3D__line .pos{ position: absolute; top: 0; }
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;
@@ -139,8 +113,10 @@
position: absolute;
font-size: 12px;
color: #888;
transform: rotate(180deg)scale(0.80);
}
transform:scale(0.80);
transform-origin:left;
white-space: nowrap;
}

/*颜色*/
.rotation3D__line.blue { color: #07b2f9; }


+ 11
- 3
go.mod View File

@@ -22,14 +22,21 @@ require (
github.com/PuerkitoBio/goquery v1.5.0
github.com/RichardKnop/machinery v1.6.9
github.com/RoaringBitmap/roaring v0.4.23 // indirect
github.com/alibabacloud-go/darabonba-openapi v0.1.18
github.com/alibabacloud-go/dysmsapi-20170525/v2 v2.0.9
github.com/alibabacloud-go/tea v1.1.17
github.com/alibabacloud-go/tea-utils v1.4.3
github.com/alibabacloud-go/tea-xml v1.1.2 // indirect
github.com/bgentry/speakeasy v0.1.0 // indirect
github.com/blevesearch/bleve v1.0.7
github.com/clbanning/mxj/v2 v2.5.5 // indirect
github.com/couchbase/gomemcached v0.0.0-20191004160342-7b5da2ec40b2 // indirect
github.com/cznic/b v0.0.0-20181122101859-a26611c4d92d // indirect
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 // indirect
github.com/cznic/strutil v0.0.0-20181122101858-275e90344537 // indirect
github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/disintegration/imaging v1.6.2
github.com/dustin/go-humanize v1.0.0
github.com/editorconfig/editorconfig-core-go/v2 v2.1.1
github.com/elliotchance/orderedmap v1.4.0
@@ -56,6 +63,7 @@ require (
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
@@ -112,9 +120,9 @@ 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 v1.1.30
github.com/yuin/goldmark-meta v0.0.0-20191126180153-f0638e958b60
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37
golang.org/x/mod v0.3.0 // indirect
golang.org/x/net v0.0.0-20200513185701-a91f0712d120
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
@@ -126,7 +134,7 @@ require (
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 // indirect
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
gopkg.in/ini.v1 v1.52.0
gopkg.in/ini.v1 v1.56.0
gopkg.in/ldap.v3 v3.0.2
gopkg.in/macaron.v1 v1.3.9 // indirect
gopkg.in/testfixtures.v2 v2.5.0


+ 54
- 0
go.sum View File

@@ -78,6 +78,35 @@ github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBb
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
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=
@@ -125,6 +154,8 @@ github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668/go.mod h1:H0wQ
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/chris-ramon/douceur v0.2.0 h1:IDMEdxlEUUBYBKE4z/mJnFyVXox+MjuEVDJNN27glkU=
github.com/chris-ramon/douceur v0.2.0/go.mod h1:wDW5xjJdeoMm1mRt4sD4c/LbF/mWdEpRXQKjTR8nIBE=
github.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9E=
github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/corbym/gocrest v1.0.3 h1:gwEdq6RkTmq+09CTuM29DfKOCtZ7G7bcyxs3IZ6EVdU=
github.com/corbym/gocrest v1.0.3/go.mod h1:maVFL5lbdS2PgfOQgGRWDYTeunSWQeiEgoNdTABShCs=
@@ -170,6 +201,8 @@ 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/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=
@@ -369,6 +402,7 @@ github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORR
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/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=
@@ -432,6 +466,8 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV
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=
@@ -673,11 +709,13 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1
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/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/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=
@@ -707,6 +745,7 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
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 +761,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=
@@ -761,6 +802,8 @@ github.com/yuin/goldmark v1.1.7/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
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 v1.1.30 h1:j4d4Lw3zqZelDhBksEo3BnWg9xhXRQGJPPSL6OApZjI=
github.com/yuin/goldmark v1.1.30/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/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
@@ -800,14 +843,19 @@ 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-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 h1:IaQbIIB2X/Mp/DKctl6ROxz1KyMlKp4uyvL6+kQ7C88=
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a 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=
@@ -848,6 +896,7 @@ golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLL
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-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120 h1:EZ3cVSzKOlJxAd8e8YAJ7no8nNypTxexh/YE/xW3ZEY=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/oauth2 v0.0.0-20180620175406-ef147856a6dd/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -868,6 +917,8 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/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=
@@ -933,6 +984,7 @@ 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-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515220128-d3bf790afa53 h1:vmsb6v0zUdmUlXfwKaYrHPPRCV0lHq/IwNIf0ASGjyQ=
golang.org/x/tools v0.0.0-20200515220128-d3bf790afa53/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1012,6 +1064,8 @@ 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=


+ 688
- 0
index.html View File

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

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

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

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

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

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

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



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

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

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

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



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

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

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

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

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


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


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

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




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


</div>


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



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

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

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

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

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

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

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

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

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

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





</div>


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

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

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


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










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

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


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


</body>
</html>


+ 252
- 3
models/ai_model_manage.go View File

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

import (
"fmt"
"time"

"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
@@ -39,6 +40,40 @@ type AiModelManage struct {
IsCanDelete bool
}

type AiModelConvert struct {
ID string `xorm:"pk"`
Name string `xorm:"INDEX NOT NULL"`
Status string `xorm:"NULL"`
StatusResult string `xorm:"NULL"`
SrcEngine int `xorm:"NOT NULL DEFAULT 0"`
RepoId int64 `xorm:"INDEX NULL"`
ModelId string `xorm:"NOT NULL"`
ModelName string `xorm:"NULL"`
ModelVersion string `xorm:"NOT NULL"`
ModelPath string `xorm:"NULL"`
DestFormat int `xorm:"NOT NULL DEFAULT 0"`
NetOutputFormat int `xorm:"NULL"`
UserId int64 `xorm:"NOT NULL"`
CloudBrainTaskId string `xorm:"NULL"`
ModelArtsVersionId string `xorm:"NULL"`
ContainerID string
ContainerIp string
RunTime int64 `xorm:"NULL"`
TrainJobDuration string
InputShape string `xorm:"varchar(2000)"`
InputDataFormat string `xorm:"NOT NULL"`
Description string `xorm:"varchar(2000)"`
Path string `xorm:"varchar(400) NOT NULL"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
StartTime timeutil.TimeStamp
EndTime timeutil.TimeStamp
UserName string
UserRelAvatarLink string
IsCanOper bool
IsCanDelete bool
}

type AiModelQueryOptions struct {
ListOptions
RepoID int64 // include all repos if empty
@@ -47,7 +82,124 @@ type AiModelQueryOptions struct {
SortType string
New int
// JobStatus CloudbrainStatus
Type int
Type int
Status int
}

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

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

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

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

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

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

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

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

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

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

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

func SaveModelToDb(model *AiModelManage) error {
@@ -63,6 +215,20 @@ func SaveModelToDb(model *AiModelManage) error {
return nil
}

func QueryModelConvertById(id string) (*AiModelConvert, error) {
sess := x.NewSession()
defer sess.Close()
sess.Select("*").Table(new(AiModelConvert)).Where("id='" + id + "'")
aiModelManageConvertList := make([]*AiModelConvert, 0)
err := sess.Find(&aiModelManageConvertList)
if err == nil {
if len(aiModelManageConvertList) == 1 {
return aiModelManageConvertList[0], nil
}
}
return nil, err
}

func QueryModelById(id string) (*AiModelManage, error) {
sess := x.NewSession()
defer sess.Close()
@@ -74,14 +240,28 @@ func QueryModelById(id string) (*AiModelManage, error) {
if len(aiModelManageList) == 1 {
return aiModelManageList[0], nil
}
} else {
log.Info("error=" + err.Error())
}
return nil, err
}

func DeleteModelById(id string) error {
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,
})
@@ -90,7 +270,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 {
@@ -201,3 +380,73 @@ func QueryModel(opts *AiModelQueryOptions) ([]*AiModelManage, int64, error) {

return aiModelManages, count, nil
}

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

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

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

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

return aiModelManageConvert, count, nil
}

+ 123
- 23
models/cloudbrain.go View File

@@ -134,7 +134,7 @@ type Cloudbrain struct {
CanDebug bool `xorm:"-"`
CanDel bool `xorm:"-"`
CanModify bool `xorm:"-"`
Type int
Type int `xorm:"INDEX"`
BenchmarkTypeID int
BenchmarkChildTypeID int

@@ -164,12 +164,11 @@ type Cloudbrain struct {
FlavorName string //规格名称
EngineName string //引擎名称
TotalVersionCount int //任务的所有版本数量,包括删除的

LabelName string //标签名称
ModelName string //模型名称
ModelVersion string //模型版本
CkptName string //权重文件名称
ResultUrl string //推理结果的obs路径
LabelName string //标签名称
ModelName string //模型名称
ModelVersion string //模型版本
CkptName string //权重文件名称
ResultUrl string //推理结果的obs路径

User *User `xorm:"-"`
Repo *Repository `xorm:"-"`
@@ -306,6 +305,22 @@ type CreateJobResult struct {
Payload map[string]interface{} `json:"payload"`
}

type QueueDetailResult struct {
Code string `json:"code"`
Msg string `json:"msg"`
Payload map[string]QueueDetail `json:"payload"`
}

type QueueDetail struct {
JobScheduleInfo JobScheduleInfo `json:"JobScheduleInfo"`
}

type JobScheduleInfo struct {
Pending int `json:"Pending"`
Running int `json:"Running"`
MedianPendingJobDurationSec int `json:"MedianPendingJobDurationSec"`
}

type GetJobResult struct {
Code string `json:"code"`
Msg string `json:"msg"`
@@ -570,11 +585,12 @@ type SpecialPools struct {
Pools []*SpecialPool `json:"pools"`
}
type SpecialPool struct {
Org string `json:"org"`
Type string `json:"type"`
IsExclusive bool `json:"isExclusive"`
Pool []*GpuInfo `json:"pool"`
JobType []string `json:"jobType"`
Org string `json:"org"`
Type string `json:"type"`
IsExclusive bool `json:"isExclusive"`
Pool []*GpuInfo `json:"pool"`
JobType []string `json:"jobType"`
ResourceSpec []*ResourceSpec `json:"resourceSpecs"`
}

type ImageInfosModelArts struct {
@@ -916,6 +932,28 @@ type NotebookDelResult struct {
InstanceID string `json:"instance_id"`
}

type CreateUserImageTrainJobParams struct {
JobName string `json:"job_name"`
Description string `json:"job_desc"`
Config UserImageConfig `json:"config"`
WorkspaceID string `json:"workspace_id"`
}

type UserImageConfig struct {
WorkServerNum int `json:"worker_server_num"`
AppUrl string `json:"app_url"` //训练作业的代码目录
BootFileUrl string `json:"boot_file_url"` //训练作业的代码启动文件,需要在代码目录下
Parameter []Parameter `json:"parameter"`
DataUrl string `json:"data_url"` //训练作业需要的数据集OBS路径URL
TrainUrl string `json:"train_url"` //训练作业的输出文件OBS路径URL
LogUrl string `json:"log_url"`
UserImageUrl string `json:"user_image_url"`
UserCommand string `json:"user_command"`
CreateVersion bool `json:"create_version"`
Flavor Flavor `json:"flavor"`
PoolID string `json:"pool_id"`
}

type CreateTrainJobParams struct {
JobName string `json:"job_name"`
Description string `json:"job_desc"`
@@ -963,6 +1001,11 @@ type CreateTrainJobVersionParams struct {
Config TrainJobVersionConfig `json:"config"`
}

type CreateTrainJobVersionUserImageParams struct {
Description string `json:"job_desc"`
Config TrainJobVersionUserImageConfig `json:"config"`
}

type TrainJobVersionConfig struct {
WorkServerNum int `json:"worker_server_num"`
AppUrl string `json:"app_url"` //训练作业的代码目录
@@ -977,6 +1020,21 @@ type TrainJobVersionConfig struct {
PreVersionId int64 `json:"pre_version_id"`
}

type TrainJobVersionUserImageConfig struct {
WorkServerNum int `json:"worker_server_num"`
AppUrl string `json:"app_url"` //训练作业的代码目录
BootFileUrl string `json:"boot_file_url"` //训练作业的代码启动文件,需要在代码目录下
Parameter []Parameter `json:"parameter"`
DataUrl string `json:"data_url"` //训练作业需要的数据集OBS路径URL
TrainUrl string `json:"train_url"` //训练作业的输出文件OBS路径URL
LogUrl string `json:"log_url"`
Flavor Flavor `json:"flavor"`
PoolID string `json:"pool_id"`
PreVersionId int64 `json:"pre_version_id"`
UserImageUrl string `json:"user_image_url"`
UserCommand string `json:"user_command"`
}

type CreateConfigParams struct {
ConfigName string `json:"config_name"`
Description string `json:"config_desc"`
@@ -1208,13 +1266,17 @@ type GrampusJobInfo struct {
UserID string `json:"userId"`
Tasks []GrampusTasks `json:"tasks"`
}

type Center struct {
ID string `json:"id"`
Name string `json:"name"`
}
type GrampusSpec struct {
CreatedAt int64 `json:"createdAt"`
UpdatedAt int64 `json:"updatedAt"`
ID string `json:"id"`
Name string `json:"name"`
ProcessorType string `json:"processorType"`
CreatedAt int64 `json:"createdAt"`
UpdatedAt int64 `json:"updatedAt"`
ID string `json:"id"`
Name string `json:"name"`
ProcessorType string `json:"processorType"`
Centers []Center `json:"centers"`
}

type GetGrampusResourceSpecsResult struct {
@@ -1409,7 +1471,7 @@ func Cloudbrains(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int64, error) {
return cloudbrains, count, nil
}

func QueryModelTrainJobVersionList(jobId string) ([]*CloudbrainInfo, int, error) {
func QueryModelTrainJobVersionList(jobId string) ([]*Cloudbrain, int, error) {
sess := x.NewSession()
defer sess.Close()

@@ -1424,7 +1486,7 @@ func QueryModelTrainJobVersionList(jobId string) ([]*CloudbrainInfo, int, error)
)

sess.OrderBy("cloudbrain.created_unix DESC")
cloudbrains := make([]*CloudbrainInfo, 0)
cloudbrains := make([]*Cloudbrain, 0)
if err := sess.Table(&Cloudbrain{}).Where(cond).
Find(&cloudbrains); err != nil {
return nil, 0, fmt.Errorf("Find: %v", err)
@@ -1446,9 +1508,9 @@ func QueryModelTrainJobList(repoId int64) ([]*CloudbrainInfo, int, error) {
cond = cond.And(
builder.Eq{"job_type": "TRAIN"},
)
cond = cond.And(
builder.In("type", 0, 1),
)
// cond = cond.And(
// builder.In("type", 0, 1),
// )

cloudbrains := make([]*CloudbrainInfo, 0)
if err := sess.Select("job_id,display_job_name").Table(&Cloudbrain{}).Where(cond).OrderBy("created_unix DESC").
@@ -1738,6 +1800,17 @@ func GetBenchmarkCountByUserID(userID int64) (int, error) {
return int(count), err
}

func GetWaitingCloudbrainCount(cloudbrainType int, computeResource string, jobTypes ...JobType) (int64, error) {
sess := x.Where("status=? and type=?", JobWaiting, cloudbrainType)
if len(jobTypes) > 0 {
sess.In("job_type", jobTypes)
}
if computeResource != "" {
sess.And("compute_resource=?", computeResource)
}
return sess.Count(new(Cloudbrain))
}

func GetCloudbrainNotebookCountByUserID(userID int64) (int, error) {
count, err := x.In("status", ModelArtsCreateQueue, ModelArtsCreating, ModelArtsStarting, ModelArtsReadyToStart, ModelArtsResizing, ModelArtsStartQueuing, ModelArtsRunning, ModelArtsRestarting).
And("job_type = ? and user_id = ? and type = ?", JobTypeDebug, userID, TypeCloudBrainTwo).Count(new(Cloudbrain))
@@ -2014,3 +2087,30 @@ func GetDatasetInfo(uuidStr string) (map[string]DatasetInfo, string, error) {

return datasetInfos, datasetNames, nil
}

func GetNewestJobsByAiCenter() ([]int64, error) {
ids := make([]int64, 0)
return ids, x.
Select("max(id) as id").
Where("type=? and ai_center!='' and ai_center is not null", TypeC2Net).
GroupBy("ai_center").
Table(Cloudbrain{}).
Find(&ids)
}

func GetNewestJobsByType() ([]int64, error) {
ids := make([]int64, 0)
return ids, x.
Select("max(id) as id").
In("type", TypeCloudBrainOne, TypeCloudBrainTwo).
GroupBy("type").
Table(Cloudbrain{}).
Find(&ids)
}

func GetCloudbrainByIDs(ids []int64) ([]*Cloudbrain, error) {
cloudbrains := make([]*Cloudbrain, 0)
return cloudbrains, x.
In("id", ids).
Find(&cloudbrains)
}

+ 15
- 3
models/custom_migrations.go View File

@@ -15,10 +15,15 @@ type CustomMigrationStatic struct {
Migrate func(*xorm.Engine, *xorm.Engine) error
}

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

var customMigrationsStatic []CustomMigrationStatic



func MigrateCustom(x *xorm.Engine) {

for _, m := range customMigrations {
@@ -35,13 +40,20 @@ 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 setPhoneNumberUniqueIndex(x *xorm.Engine) error {

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 syncTopicStruct(x *xorm.Engine) error {

query := "ALTER TABLE topic ALTER COLUMN name TYPE varchar(105);"


+ 8
- 0
models/login_source.go View File

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

if hasUser {
switch user.LoginType {


+ 1
- 0
models/models.go View File

@@ -144,6 +144,7 @@ func init() {
new(WechatBindLog),
new(OrgStatistic),
new(SearchRecord),
new(AiModelConvert),
)

tablesStatistic = append(tablesStatistic,


+ 36
- 0
models/repo_activity_custom.go View File

@@ -211,6 +211,42 @@ func setKeyContributerDict(contributorDistinctDict map[string]int, email string,
}
}

func GetAllUserPublicRepoKPIStats(startTime time.Time, endTime time.Time) (map[string]*git.UserKPIStats, error) {
authors := make(map[string]*git.UserKPIStats)
repositorys, err := GetAllRepositoriesByFilterCols("owner_name", "name", "is_private")
if err != nil {
return nil, err
}

for _, repository := range repositorys {
if repository.IsPrivate {
continue
}
authorsOneRepo, err1 := git.GetUserKPIStats(repository.RepoPath(), startTime, endTime)
if err1 != nil {
log.Warn("get user kpi status err:"+repository.RepoPath(), err1.Error())
continue
}

for key, value := range authorsOneRepo {
if _, ok := authors[key]; !ok {
authors[key] = &git.UserKPIStats{

Name: value.Name,
Email: value.Email,
Commits: 0,
CommitLines: 0,
}
}
authors[key].Commits += value.Commits
authors[key].CommitLines += value.CommitLines

}

}
return authors, nil
}

func GetAllUserKPIStats(startTime time.Time, endTime time.Time) (map[string]*git.UserKPIStats, error) {
authors := make(map[string]*git.UserKPIStats)
repositorys, err := GetAllRepositoriesByFilterCols("owner_name", "name")


+ 47
- 0
models/user.go View File

@@ -184,6 +184,8 @@ type User struct {
//Wechat
WechatOpenId string `xorm:"INDEX"`
WechatBindUnix timeutil.TimeStamp
//Mobile phone
PhoneNumber string `xorm:"VARCHAR(255)"`
}

// SearchOrganizationsOptions options to filter organizations
@@ -1442,6 +1444,31 @@ func getUserByName(e Engine, name string) (*User, error) {
return u, nil
}

func GetUserByPhoneNumber(phoneNumber string) (*User, error) {
return getUserByPhoneNumber(x, phoneNumber)
}

func getUserByPhoneNumber(e Engine, phoneNumber string) (*User, error) {
u := &User{PhoneNumber: phoneNumber}
has, err := e.Get(u)
if err != nil {
return nil, err
} else if !has {
return nil, ErrUserNotExist{0, "", 0}
}
return u, nil
}

func IsUserByPhoneNumberExist(phoneNumber string) (bool, error) {
return isUserByPhoneNumberExist(x, phoneNumber)

}

func isUserByPhoneNumberExist(e Engine, phoneNumber string) (bool, error) {
return e.Where("phone_number = ?", phoneNumber).Exist(&User{})

}

// GetUserEmailsByNames returns a list of e-mails corresponds to names of users
// that have their email notifications set to enabled or onmention.
func GetUserEmailsByNames(names []string) []string {
@@ -1610,6 +1637,26 @@ func GetUserByEmail(email string) (*User, error) {
return GetUserByEmailContext(DefaultDBContext(), email)
}

func GetUserByMainEmail(email string) (*User, error) {
if len(email) == 0 {
return nil, ErrUserNotExist{0, email, 0}
}

email = strings.ToLower(email)
// First try to find the user by primary email
user := &User{Email: email}
has, err := DefaultDBContext().e.Get(user)
if err != nil {
return nil, err
}
if has {
return user, nil
} else {
return nil, ErrUserNotExist{0, email, 0}
}

}

// GetUserByEmailContext returns the user object by given e-mail if exists with db context
func GetUserByEmailContext(ctx DBContext, email string) (*User, error) {
if len(email) == 0 {


+ 437
- 0
models/user_analysis_for_activity.go View File

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

import (
"fmt"
"time"

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

type UserBusinessAnalysisForActivity struct {
ID int64 `xorm:"pk"`
CountDate int64 `xorm:"pk"`
//action :ActionMergePullRequest // 11
CodeMergeCount int `xorm:"NOT NULL DEFAULT 0"`
//action :ActionCommitRepo
CommitCount int `xorm:"NOT NULL DEFAULT 0"`
//issue // 10
IssueCount int `xorm:"NOT NULL DEFAULT 0"`
//comment table current date
CommentCount int `xorm:"NOT NULL DEFAULT 0"`

//follow table
WatchedCount int `xorm:"NOT NULL DEFAULT 0"`

CommitCodeSize int `xorm:"NOT NULL DEFAULT 0"`
//issue, issueassigees
SolveIssueCount int `xorm:"NOT NULL DEFAULT 0"`
//use
RegistDate timeutil.TimeStamp `xorm:"NOT NULL"`

//user
Email string `xorm:"NOT NULL"`

Phone string `xorm:"NULL"`
//user
Name string `xorm:"NOT NULL"`
DataDate string `xorm:"NULL"`

CloudBrainTaskNum int `xorm:"NOT NULL DEFAULT 0"`
CommitDatasetNum int `xorm:"NOT NULL DEFAULT 0"`
//0
CommitModelCount int `xorm:"NOT NULL DEFAULT 0"`
}

func QueryDataForActivity(startTime time.Time, endTime time.Time) []*UserBusinessAnalysisForActivity {
sess := x.NewSession()
defer sess.Close()

result := make([]*UserBusinessAnalysisForActivity, 0)
publicRepo := queryPublicRepo()
start_unix := startTime.Unix()
end_unix := endTime.Unix()

CodeMergeCountMap := queryPullRequestPublic(start_unix, end_unix, publicRepo)
CommitCodeSizeMap, err := GetAllUserPublicRepoKPIStats(startTime, endTime)
if err != nil {
log.Info("error,info=" + err.Error())
}
CommitCountMap := queryCommitActionPublic(start_unix, end_unix, 5, publicRepo)
IssueCountMap, publicRepoIssueIdMap := queryCreateIssuePublic(start_unix, end_unix, publicRepo)
SolveIssueCountMap := querySolveIssuePublic(start_unix, end_unix, publicRepoIssueIdMap)
WatchedCountMap, _ := queryFollow(start_unix, end_unix)
CommentCountMap := queryCommentPublic(start_unix, end_unix, publicRepoIssueIdMap)
PublicDataSet := queryAllPublicDataSet(publicRepo)
DatasetFileNums := queryPublicDatasetFileNums(start_unix, end_unix, PublicDataSet)
AiModelManageMap := queryUserModelPublic(start_unix, end_unix, publicRepo)

cond := "type != 1 and is_active=true"
count, err := sess.Where(cond).Count(new(User))

var indexTotal int64
indexTotal = 0
for {
sess.Select("`user`.*").Table("user").Where(cond).OrderBy("id asc").Limit(PAGE_SIZE, int(indexTotal))
userList := make([]*User, 0)
sess.Find(&userList)

for i, userRecord := range userList {
var dateRecord UserBusinessAnalysisForActivity
dateRecord.ID = userRecord.ID
log.Info("i=" + fmt.Sprint(i) + " userName=" + userRecord.Name)
dateRecord.Email = userRecord.Email
dateRecord.Phone = userRecord.PhoneNumber
dateRecord.RegistDate = userRecord.CreatedUnix
dateRecord.Name = userRecord.Name

dateRecord.CodeMergeCount = getMapValue(dateRecord.ID, CodeMergeCountMap)
dateRecord.CommitCount = getMapValue(dateRecord.ID, CommitCountMap)
dateRecord.WatchedCount = getMapValue(dateRecord.ID, WatchedCountMap)
dateRecord.CommitDatasetNum = getMapValue(dateRecord.ID, DatasetFileNums)
dateRecord.IssueCount = getMapValue(dateRecord.ID, IssueCountMap)
dateRecord.CommentCount = getMapValue(dateRecord.ID, CommentCountMap)
if _, ok := CommitCodeSizeMap[dateRecord.Email]; !ok {
dateRecord.CommitCodeSize = 0
} else {
dateRecord.CommitCodeSize = int(CommitCodeSizeMap[dateRecord.Email].CommitLines)
}
dateRecord.SolveIssueCount = getMapValue(dateRecord.ID, SolveIssueCountMap)

dateRecord.CommitModelCount = getMapValue(dateRecord.ID, AiModelManageMap)

result = append(result, &dateRecord)
}
indexTotal += PAGE_SIZE
if indexTotal >= count {
break
}
}

return result
}
func querySolveIssuePublic(start_unix int64, end_unix int64, publicRepoIssueIdMap map[int64]int) map[int64]int {
sess := x.NewSession()
defer sess.Close()
resultMap := make(map[int64]int)
cond := "issue.is_closed=true and issue.closed_unix>=" + fmt.Sprint(start_unix) + " and issue.closed_unix<=" + fmt.Sprint(end_unix)

count, err := sess.Table("issue_assignees").Join("inner", "issue", "issue.id=issue_assignees.issue_id").Where(cond).Count(new(IssueAssignees))
if err != nil {
log.Info("query issue error. return.")
return resultMap
}
var indexTotal int64
indexTotal = 0
for {
issueAssigneesList := make([]*IssueAssignees, 0)
sess.Select("issue_assignees.*").Table("issue_assignees").
Join("inner", "issue", "issue.id=issue_assignees.issue_id").
Where(cond).OrderBy("issue_assignees.id asc").Limit(PAGE_SIZE, int(indexTotal))

sess.Find(&issueAssigneesList)

log.Info("query IssueAssignees size=" + fmt.Sprint(len(issueAssigneesList)))
for _, issueAssigneesRecord := range issueAssigneesList {
if isPublicRepo(issueAssigneesRecord.IssueID, publicRepoIssueIdMap) {
if _, ok := resultMap[issueAssigneesRecord.AssigneeID]; !ok {
resultMap[issueAssigneesRecord.AssigneeID] = 1
} else {
resultMap[issueAssigneesRecord.AssigneeID] += 1
}
}
}
indexTotal += PAGE_SIZE
if indexTotal >= count {
break
}
}

return resultMap
}

func queryPublicRepo() map[int64]int {
sess := x.NewSession()
defer sess.Close()
resultMap := make(map[int64]int)

count, err := sess.Table("repository").Count(new(Repository))
if err != nil {
log.Info("query Repository error. return.")
return resultMap
}
var indexTotal int64
indexTotal = 0
for {
repositoryList := make([]*Repository, 0)
sess.Select("*").Table("repository").OrderBy("id desc").Limit(PAGE_SIZE, int(indexTotal))
sess.Find(&repositoryList)
log.Info("query repo size=" + fmt.Sprint(len(repositoryList)))
for _, repositoryRecord := range repositoryList {
if repositoryRecord.IsPrivate {
continue
}
if _, ok := resultMap[repositoryRecord.ID]; !ok {
resultMap[repositoryRecord.ID] = 1
}
}
indexTotal += PAGE_SIZE
if indexTotal >= count {
break
}
}

return resultMap
}

func isPublicRepo(repoId int64, publicAllRepo map[int64]int) bool {
if _, ok := publicAllRepo[repoId]; !ok {
return false
}
return true
}

func queryPullRequestPublic(start_unix int64, end_unix int64, publicAllRepo map[int64]int) map[int64]int {
sess := x.NewSession()
defer sess.Close()
resultMap := make(map[int64]int)
cond := "pull_request.merged_unix>=" + fmt.Sprint(start_unix) + " and pull_request.merged_unix<=" + fmt.Sprint(end_unix)
count, err := sess.Table("issue").Join("inner", "pull_request", "issue.id=pull_request.issue_id").Where(cond).Count(new(Issue))
if err != nil {
log.Info("query issue error. return.")
return resultMap
}
var indexTotal int64
indexTotal = 0
for {
issueList := make([]*Issue, 0)
sess.Select("issue.*").Table("issue").Join("inner", "pull_request", "issue.id=pull_request.issue_id").Where(cond).OrderBy("issue.id asc").Limit(PAGE_SIZE, int(indexTotal))
sess.Find(&issueList)
log.Info("query issue(PR) size=" + fmt.Sprint(len(issueList)))
for _, issueRecord := range issueList {
if isPublicRepo(issueRecord.RepoID, publicAllRepo) {
if _, ok := resultMap[issueRecord.PosterID]; !ok {
resultMap[issueRecord.PosterID] = 1
} else {
resultMap[issueRecord.PosterID] += 1
}
}
}
indexTotal += PAGE_SIZE
if indexTotal >= count {
break
}
}
return resultMap
}

func queryCommitActionPublic(start_unix int64, end_unix int64, actionType int64, publicAllRepo map[int64]int) map[int64]int {
sess := x.NewSession()
defer sess.Close()
resultMap := make(map[int64]int)

cond := "user_id=act_user_id and op_type=" + fmt.Sprint(actionType) + " and created_unix>=" + fmt.Sprint(start_unix) + " and created_unix<=" + fmt.Sprint(end_unix)

count, err := sess.Where(cond).Count(new(Action))
if err != nil {
log.Info("query action error. return.")
return resultMap
}
var indexTotal int64
indexTotal = 0
for {
sess.Select("id,user_id,op_type,act_user_id,repo_id").Table("action").Where(cond).OrderBy("id asc").Limit(PAGE_SIZE, int(indexTotal))
actionList := make([]*Action, 0)
sess.Find(&actionList)

log.Info("query action size=" + fmt.Sprint(len(actionList)))
for _, actionRecord := range actionList {
if isPublicRepo(actionRecord.RepoID, publicAllRepo) {
if _, ok := resultMap[actionRecord.UserID]; !ok {
resultMap[actionRecord.UserID] = 1
} else {
resultMap[actionRecord.UserID] += 1
}
}
}
indexTotal += PAGE_SIZE
if indexTotal >= count {
break
}
}

return resultMap
}

func queryCreateIssuePublic(start_unix int64, end_unix int64, publicAllRepo map[int64]int) (map[int64]int, map[int64]int) {

sess := x.NewSession()
defer sess.Close()
resultMap := make(map[int64]int)
publicRepoIssueIdMap := make(map[int64]int)
cond := "is_pull=false and created_unix>=" + fmt.Sprint(start_unix) + " and created_unix<=" + fmt.Sprint(end_unix)

count, err := sess.Where(cond).Count(new(Issue))
if err != nil {
log.Info("query Issue error. return.")
return resultMap, publicRepoIssueIdMap
}
var indexTotal int64
indexTotal = 0
for {
sess.Select("id,poster_id,repo_id").Table("issue").Where(cond).OrderBy("id asc").Limit(PAGE_SIZE, int(indexTotal))
issueList := make([]*Issue, 0)
sess.Find(&issueList)
log.Info("query issue size=" + fmt.Sprint(len(issueList)))
for _, issueRecord := range issueList {
if isPublicRepo(issueRecord.RepoID, publicAllRepo) {
if _, ok := resultMap[issueRecord.PosterID]; !ok {
resultMap[issueRecord.PosterID] = 1
} else {
resultMap[issueRecord.PosterID] += 1
}
publicRepoIssueIdMap[issueRecord.ID] = 1
}
}
indexTotal += PAGE_SIZE
if indexTotal >= count {
break
}
}
return resultMap, publicRepoIssueIdMap

}

func queryCommentPublic(start_unix int64, end_unix int64, publicRepoIssueIdMap map[int64]int) map[int64]int {

sess := x.NewSession()
defer sess.Close()
cond := "created_unix>=" + fmt.Sprint(start_unix) + " and created_unix<=" + fmt.Sprint(end_unix)
resultMap := make(map[int64]int)
count, err := sess.Where(cond).Count(new(Comment))
if err != nil {
log.Info("query Comment error. return.")
return resultMap
}
var indexTotal int64
indexTotal = 0
for {
sess.Select("id,type,poster_id,issue_id").Table("comment").Where(cond).OrderBy("id asc").Limit(PAGE_SIZE, int(indexTotal))
commentList := make([]*Comment, 0)
sess.Find(&commentList)
log.Info("query Comment size=" + fmt.Sprint(len(commentList)))
for _, commentRecord := range commentList {
if isPublicRepo(commentRecord.IssueID, publicRepoIssueIdMap) {
if _, ok := resultMap[commentRecord.PosterID]; !ok {
resultMap[commentRecord.PosterID] = 1
} else {
resultMap[commentRecord.PosterID] += 1
}
}
}
indexTotal += PAGE_SIZE
if indexTotal >= count {
break
}
}
return resultMap
}

func queryAllPublicDataSet(publicAllRepo map[int64]int) map[int64]int {
sess := x.NewSession()
defer sess.Close()
publicDataSetIdMap := make(map[int64]int)
count, err := sess.Count(new(Dataset))
if err != nil {
log.Info("query dataset error. return.")
return publicDataSetIdMap
}
var indexTotal int64
indexTotal = 0
for {
sess.Select("id,user_id,repo_id").Table(new(Dataset)).OrderBy("id asc").Limit(PAGE_SIZE, int(indexTotal))
datasetList := make([]*Dataset, 0)
sess.Find(&datasetList)
log.Info("query datasetList size=" + fmt.Sprint(len(datasetList)))
for _, datasetRecord := range datasetList {
if isPublicRepo(datasetRecord.RepoID, publicAllRepo) {
publicDataSetIdMap[datasetRecord.ID] = 1
}
}
indexTotal += PAGE_SIZE
if indexTotal >= count {
break
}
}
return publicDataSetIdMap
}

func queryPublicDatasetFileNums(start_unix int64, end_unix int64, publicDataSetIdMap map[int64]int) map[int64]int {
sess := x.NewSession()
defer sess.Close()
resultNumMap := make(map[int64]int)
cond := " created_unix>=" + fmt.Sprint(start_unix) + " and created_unix<=" + fmt.Sprint(end_unix)

count, err := sess.Where(cond).Count(new(Attachment))
if err != nil {
log.Info("query attachment error. return.")
return resultNumMap
}
var indexTotal int64
indexTotal = 0
for {
sess.Select("id,uploader_id,size,dataset_id").Table("attachment").Where(cond).OrderBy("id asc").Limit(PAGE_SIZE, int(indexTotal))
attachmentList := make([]*Attachment, 0)
sess.Find(&attachmentList)

log.Info("query Attachment size=" + fmt.Sprint(len(attachmentList)))
for _, attachRecord := range attachmentList {
if isPublicRepo(attachRecord.DatasetID, publicDataSetIdMap) {
if _, ok := resultNumMap[attachRecord.UploaderID]; !ok {
resultNumMap[attachRecord.UploaderID] = 1
} else {
resultNumMap[attachRecord.UploaderID] += 1
}
}
}
indexTotal += PAGE_SIZE
if indexTotal >= count {
break
}
}
return resultNumMap
}

func queryUserModelPublic(start_unix int64, end_unix int64, publicAllRepo map[int64]int) map[int64]int {
sess := x.NewSession()
defer sess.Close()
resultMap := make(map[int64]int)
cond := " created_unix>=" + fmt.Sprint(start_unix) + " and created_unix<=" + fmt.Sprint(end_unix)
count, err := sess.Where(cond).Count(new(AiModelManage))
if err != nil {
log.Info("query AiModelManage error. return.")
return resultMap
}
var indexTotal int64
indexTotal = 0
for {
sess.Select("id,user_id,repo_id").Table("ai_model_manage").Where(cond).OrderBy("id asc").Limit(PAGE_SIZE, int(indexTotal))
aiModelList := make([]*AiModelManage, 0)
sess.Find(&aiModelList)
log.Info("query AiModelManage size=" + fmt.Sprint(len(aiModelList)))
for _, aiModelRecord := range aiModelList {
if isPublicRepo(aiModelRecord.RepoId, publicAllRepo) {
if _, ok := resultMap[aiModelRecord.UserId]; !ok {
resultMap[aiModelRecord.UserId] = 1
} else {
resultMap[aiModelRecord.UserId] += 1
}
}
}
indexTotal += PAGE_SIZE
if indexTotal >= count {
break
}
}
return resultMap
}

+ 18
- 9
models/user_business_analysis.go View File

@@ -105,6 +105,8 @@ type UserBusinessAnalysisAll struct {
CollectImage int `xorm:"NOT NULL DEFAULT 0"`
CollectedImage int `xorm:"NOT NULL DEFAULT 0"`
RecommendImage int `xorm:"NOT NULL DEFAULT 0"`

Phone string `xorm:"NULL"`
}

type UserBusinessAnalysis struct {
@@ -192,6 +194,8 @@ type UserBusinessAnalysis struct {
CollectImage int `xorm:"NOT NULL DEFAULT 0"`
CollectedImage int `xorm:"NOT NULL DEFAULT 0"`
RecommendImage int `xorm:"NOT NULL DEFAULT 0"`

Phone string `xorm:"NULL"`
}

type UserBusinessAnalysisQueryOptions struct {
@@ -358,7 +362,7 @@ func QueryUserStaticDataByTableName(start int, pageSize int, tableName string, q
var cond = builder.NewCond()
if len(userName) > 0 {
cond = cond.And(
builder.Like{"name", userName},
builder.Like{"lower(name)", strings.ToLower(userName)},
)
}
allCount, err := statictisSess.Where(cond).Count(queryObj)
@@ -475,6 +479,7 @@ func QueryUserStaticDataForUserDefine(opts *UserBusinessAnalysisQueryOptions, wi
dateRecord.CountDate = CountDate.Unix()
dateRecord.DataDate = DataDate
dateRecord.Email = userRecord.Email
dateRecord.Phone = userRecord.PhoneNumber
dateRecord.RegistDate = userRecord.CreatedUnix
dateRecord.Name = userRecord.Name
dateRecord.UserLocation = userRecord.Location
@@ -728,6 +733,7 @@ func refreshUserStaticTable(wikiCountMap map[string]int, tableName string, pageS
var dateRecordAll UserBusinessAnalysisAll
dateRecordAll.ID = userRecord.ID
dateRecordAll.Email = userRecord.Email
dateRecordAll.Phone = userRecord.PhoneNumber
dateRecordAll.RegistDate = userRecord.CreatedUnix
dateRecordAll.Name = userRecord.Name
dateRecordAll.GiteaAgeMonth = subMonth(currentTimeNow, userRecord.CreatedUnix.AsTime())
@@ -839,7 +845,7 @@ func insertTable(dateRecords []UserBusinessAnalysisAll, tableName string, static

insertBatchSql := "INSERT INTO public." + tableName +
"(id, count_date, code_merge_count, commit_count, issue_count, comment_count, focus_repo_count, star_repo_count, watched_count, gitea_age_month, commit_code_size, commit_dataset_size, " +
"commit_model_count, solve_issue_count, encyclopedias_count, regist_date, create_repo_count, login_count, open_i_index, email, name, data_date,cloud_brain_task_num,gpu_debug_job,npu_debug_job,gpu_train_job,npu_train_job,npu_inference_job,gpu_bench_mark_job,cloud_brain_run_time,commit_dataset_num,user_index,user_location,focus_other_user,collect_dataset,collected_dataset,recommend_dataset,collect_image,collected_image,recommend_image,user_index_primitive) " +
"commit_model_count, solve_issue_count, encyclopedias_count, regist_date, create_repo_count, login_count, open_i_index, email, name, data_date,cloud_brain_task_num,gpu_debug_job,npu_debug_job,gpu_train_job,npu_train_job,npu_inference_job,gpu_bench_mark_job,cloud_brain_run_time,commit_dataset_num,user_index,user_location,focus_other_user,collect_dataset,collected_dataset,recommend_dataset,collect_image,collected_image,recommend_image,user_index_primitive,phone) " +
"VALUES"

for i, record := range dateRecords {
@@ -848,7 +854,7 @@ func insertTable(dateRecords []UserBusinessAnalysisAll, tableName string, static
", " + fmt.Sprint(record.WatchedCount) + ", " + fmt.Sprint(record.GiteaAgeMonth) + ", " + fmt.Sprint(record.CommitCodeSize) + ", " + fmt.Sprint(record.CommitDatasetSize) +
", " + fmt.Sprint(record.CommitModelCount) + ", " + fmt.Sprint(record.SolveIssueCount) + ", " + fmt.Sprint(record.EncyclopediasCount) + ", " + fmt.Sprint(record.RegistDate) +
", " + fmt.Sprint(record.CreateRepoCount) + ", " + fmt.Sprint(record.LoginCount) + ", " + fmt.Sprint(record.OpenIIndex) + ", '" + record.Email + "', '" + record.Name + "', '" + record.DataDate + "'," + fmt.Sprint(record.CloudBrainTaskNum) + "," + fmt.Sprint(record.GpuDebugJob) + "," + fmt.Sprint(record.NpuDebugJob) + "," + fmt.Sprint(record.GpuTrainJob) + "," + fmt.Sprint(record.NpuTrainJob) + "," + fmt.Sprint(record.NpuInferenceJob) + "," + fmt.Sprint(record.GpuBenchMarkJob) + "," + fmt.Sprint(record.CloudBrainRunTime) + "," + fmt.Sprint(record.CommitDatasetNum) + "," + fmt.Sprint(record.UserIndex) + ",'" + record.UserLocation + "'," +
fmt.Sprint(record.FocusOtherUser) + "," + fmt.Sprint(record.CollectDataset) + "," + fmt.Sprint(record.CollectedDataset) + "," + fmt.Sprint(record.RecommendDataset) + "," + fmt.Sprint(record.CollectImage) + "," + fmt.Sprint(record.CollectedImage) + "," + fmt.Sprint(record.RecommendImage) + "," + fmt.Sprint(record.UserIndexPrimitive) + ")"
fmt.Sprint(record.FocusOtherUser) + "," + fmt.Sprint(record.CollectDataset) + "," + fmt.Sprint(record.CollectedDataset) + "," + fmt.Sprint(record.RecommendDataset) + "," + fmt.Sprint(record.CollectImage) + "," + fmt.Sprint(record.CollectedImage) + "," + fmt.Sprint(record.RecommendImage) + "," + fmt.Sprint(record.UserIndexPrimitive) + ",'" + record.Phone + "')"
if i < (len(dateRecords) - 1) {
insertBatchSql += ","
}
@@ -973,6 +979,7 @@ func CounDataByDateAndReCount(wikiCountMap map[string]int, startTime time.Time,
dateRecord.CountDate = CountDate.Unix()

dateRecord.Email = userRecord.Email
dateRecord.Phone = userRecord.PhoneNumber
dateRecord.RegistDate = userRecord.CreatedUnix
dateRecord.Name = userRecord.Name
dateRecord.GiteaAgeMonth = subMonth(currentTimeNow, userRecord.CreatedUnix.AsTime())
@@ -1028,12 +1035,12 @@ func CounDataByDateAndReCount(wikiCountMap map[string]int, startTime time.Time,
setUserMetrics(userMetrics, userRecord, start_unix, end_unix, dateRecord)
if getUserActivate(dateRecord) > 0 {
log.Info("has activity." + userRecord.Name)
addUserToMap(userNewAddActivity, userRecord.CreatedUnix, dateRecord.ID)
addUserToMap(userNewAddActivity, userRecord.CreatedUnix, dateRecord.ID, currentTimeNow)
}
if userRecord.IsActive {
addUserToMap(userAcitvateJsonMap, userRecord.CreatedUnix, dateRecord.ID)
addUserToMap(userAcitvateJsonMap, userRecord.CreatedUnix, dateRecord.ID, currentTimeNow)
}
addUserToMap(userCurrentDayRegistMap, userRecord.CreatedUnix, dateRecord.ID)
addUserToMap(userCurrentDayRegistMap, userRecord.CreatedUnix, dateRecord.ID, currentTimeNow)
}

indexTotal += PAGE_SIZE
@@ -1056,7 +1063,7 @@ func CounDataByDateAndReCount(wikiCountMap map[string]int, startTime time.Time,
useMetrics.NotActivateRegistUser = getMapKeyStringValue("NotActivateRegistUser", userMetrics)
useMetrics.TotalActivateRegistUser = getMapKeyStringValue("TotalActivateRegistUser", userMetrics)
useMetrics.TotalHasActivityUser = getMapKeyStringValue("TotalHasActivityUser", userMetrics)
useMetrics.CurrentDayRegistUser = getMapKeyStringValue("CurrentDayRegistUser", userMetrics)
count, err = sess.Where("type=0").Count(new(User))
if err != nil {
log.Info("query user error. return.")
@@ -1124,8 +1131,9 @@ func setUniqueUserId(jsonString string, value map[int64]int64) (string, int) {
return userIdArray, len(value)
}

func addUserToMap(currentUserActivity map[int64]map[int64]int64, registDate timeutil.TimeStamp, userId int64) {
CountDateTime := time.Date(registDate.Year(), registDate.AsTime().Month(), registDate.AsTime().Day(), 0, 1, 0, 0, registDate.AsTime().Location())
func addUserToMap(currentUserActivity map[int64]map[int64]int64, registDate timeutil.TimeStamp, userId int64, currentTimeNow time.Time) {
registTime := registDate.AsTimeInLocation(currentTimeNow.Location())
CountDateTime := time.Date(registTime.Year(), registTime.Month(), registTime.Day(), 0, 1, 0, 0, currentTimeNow.Location())
CountDate := CountDateTime.Unix()
if _, ok := currentUserActivity[CountDate]; !ok {
userIdMap := make(map[int64]int64, 0)
@@ -1149,6 +1157,7 @@ func setUserMetrics(userMetrics map[string]int, user *User, start_time int64, en
} else {
userMetrics["NotActivateRegistUser"] = getMapKeyStringValue("NotActivateRegistUser", userMetrics) + 1
}
userMetrics["CurrentDayRegistUser"] = getMapKeyStringValue("CurrentDayRegistUser", userMetrics) + 1
}
if user.IsActive {
userMetrics["TotalActivateRegistUser"] = getMapKeyStringValue("TotalActivateRegistUser", userMetrics) + 1


+ 14
- 0
models/user_business_struct.go View File

@@ -65,6 +65,8 @@ type UserBusinessAnalysisCurrentYear struct {
CollectImage int `xorm:"NOT NULL DEFAULT 0"`
CollectedImage int `xorm:"NOT NULL DEFAULT 0"`
RecommendImage int `xorm:"NOT NULL DEFAULT 0"`

Phone string `xorm:"NULL"`
}

type UserBusinessAnalysisLast30Day struct {
@@ -130,6 +132,8 @@ type UserBusinessAnalysisLast30Day struct {
CollectImage int `xorm:"NOT NULL DEFAULT 0"`
CollectedImage int `xorm:"NOT NULL DEFAULT 0"`
RecommendImage int `xorm:"NOT NULL DEFAULT 0"`

Phone string `xorm:"NULL"`
}

type UserBusinessAnalysisLastMonth struct {
@@ -195,6 +199,8 @@ type UserBusinessAnalysisLastMonth struct {
CollectImage int `xorm:"NOT NULL DEFAULT 0"`
CollectedImage int `xorm:"NOT NULL DEFAULT 0"`
RecommendImage int `xorm:"NOT NULL DEFAULT 0"`

Phone string `xorm:"NULL"`
}

type UserBusinessAnalysisCurrentMonth struct {
@@ -260,6 +266,8 @@ type UserBusinessAnalysisCurrentMonth struct {
CollectImage int `xorm:"NOT NULL DEFAULT 0"`
CollectedImage int `xorm:"NOT NULL DEFAULT 0"`
RecommendImage int `xorm:"NOT NULL DEFAULT 0"`

Phone string `xorm:"NULL"`
}

type UserBusinessAnalysisCurrentWeek struct {
@@ -326,6 +334,8 @@ type UserBusinessAnalysisCurrentWeek struct {
CollectImage int `xorm:"NOT NULL DEFAULT 0"`
CollectedImage int `xorm:"NOT NULL DEFAULT 0"`
RecommendImage int `xorm:"NOT NULL DEFAULT 0"`

Phone string `xorm:"NULL"`
}

type UserBusinessAnalysisYesterday struct {
@@ -392,6 +402,8 @@ type UserBusinessAnalysisYesterday struct {
CollectImage int `xorm:"NOT NULL DEFAULT 0"`
CollectedImage int `xorm:"NOT NULL DEFAULT 0"`
RecommendImage int `xorm:"NOT NULL DEFAULT 0"`

Phone string `xorm:"NULL"`
}

type UserBusinessAnalysisLastWeek struct {
@@ -458,6 +470,8 @@ type UserBusinessAnalysisLastWeek struct {
CollectImage int `xorm:"NOT NULL DEFAULT 0"`
CollectedImage int `xorm:"NOT NULL DEFAULT 0"`
RecommendImage int `xorm:"NOT NULL DEFAULT 0"`

Phone string `xorm:"NULL"`
}

type UserAnalysisPara struct {


+ 26
- 0
modules/auth/cloudbrain.go View File

@@ -50,6 +50,28 @@ type EditImageCloudBrainForm struct {
Topics string `form:"topics"`
}

type CreateCloudBrainInferencForm struct {
JobName string `form:"job_name" binding:"Required"`
DisplayJobName string `form:"display_job_name" binding:"Required"`
Image string `form:"image" binding:"Required"`
Command string `form:"command" binding:"Required"`
Attachment string `form:"attachment" binding:"Required"`
JobType string `form:"job_type" binding:"Required"`
BenchmarkCategory string `form:"get_benchmark_category"`
GpuType string `form:"gpu_type"`
TrainUrl string `form:"train_url"`
TestUrl string `form:"test_url"`
Description string `form:"description"`
ResourceSpecId int `form:"resource_spec_id" binding:"Required"`
BootFile string `form:"boot_file"`
Params string `form:"run_para_list"`
BranchName string `form:"branch_name"`
ModelName string `form:"model_name" binding:"Required"`
ModelVersion string `form:"model_version" binding:"Required"`
CkptName string `form:"ckpt_name" binding:"Required"`
LabelName string `form:"label_names" binding:"Required"`
}

func (f *CreateCloudBrainForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}
@@ -61,3 +83,7 @@ func (f *CommitImageCloudBrainForm) Validate(ctx *macaron.Context, errs binding.
func (f *EditImageCloudBrainForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}

func (f *CreateCloudBrainInferencForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}

+ 46
- 1
modules/auth/user_form.go View File

@@ -81,8 +81,11 @@ type RegisterForm struct {
UserName string `binding:"Required;AlphaDashDot;MaxSize(40)"`
Email string `binding:"Required;Email;MaxSize(254)"`
Password string `binding:"MaxSize(255)"`
PhoneNumber string `binding:"MaxSize(20)"`
VerifyCode string `binding:"MaxSize(10)"`
Retype string
GRecaptchaResponse string `form:"g-recaptcha-response"`
Agree bool
}

// Validate valideates the fields
@@ -105,7 +108,7 @@ func (f RegisterForm) IsEmailDomainWhitelisted() bool {
}

domain := strings.ToLower(f.Email[n+1:])
//support edu.cn
if strings.HasSuffix(domain, "edu.cn") {
return true
@@ -209,6 +212,8 @@ type UpdateProfileForm struct {
Location string `binding:"MaxSize(50)"`
Language string `binding:"Size(5)"`
Description string `binding:"MaxSize(255)"`
PhoneNumber string `binding:"MaxSize(20)"`
VerifyCode string `binding:"MaxSize(10)"`
}

// Validate validates the fields
@@ -364,3 +369,43 @@ type U2FDeleteForm struct {
func (f *U2FDeleteForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}

type PhoneNumberForm struct {
PhoneNumber string `binding:"Required;MaxSize(20)"`
Mode int `binding:"Required"`
SlideID string `binding:"Required;MaxSize(100)"`
}

func (f *PhoneNumberForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}

type PhoneNumberCodeForm struct {
PhoneNumber string `binding:"Required;MaxSize(20)"`
VerifyCode string `binding:"Required;MaxSize(10)"`
Remember bool
}

func (f *PhoneNumberCodeForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}

type ResetPassWordByPhoneForm struct {
PhoneNumber string `binding:"Required;MaxSize(20)"`
VerifyCode string `binding:"Required;MaxSize(10)"`
Password string `binding:"MaxSize(255)"`
Remember bool
}

func (f *ResetPassWordByPhoneForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}

type SlideImageForm struct {
SlideID string `binding:"Required"`
X int `binding:"Required"`
}

func (f *SlideImageForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
return validate(errs, ctx.Data, f, ctx.Locale)
}

+ 159
- 5
modules/cloudbrain/cloudbrain.go View File

@@ -3,6 +3,7 @@ package cloudbrain
import (
"encoding/json"
"errors"
"os"
"strconv"

"code.gitea.io/gitea/modules/timeutil"
@@ -17,7 +18,7 @@ import (
)

const (
Command = `pip3 install jupyterlab==2.2.5 -i https://pypi.tuna.tsinghua.edu.cn/simple;service ssh stop;jupyter lab --no-browser --ip=0.0.0.0 --allow-root --notebook-dir="/code" --port=80 --LabApp.token="" --LabApp.allow_origin="self https://cloudbrain.pcl.ac.cn"`
//Command = `pip3 install jupyterlab==2.2.5 -i https://pypi.tuna.tsinghua.edu.cn/simple;service ssh stop;jupyter lab --no-browser --ip=0.0.0.0 --allow-root --notebook-dir="/code" --port=80 --LabApp.token="" --LabApp.allow_origin="self https://cloudbrain.pcl.ac.cn"`
//CommandBenchmark = `echo "start benchmark";python /code/test.py;echo "end benchmark"`
CommandBenchmark = `echo "start benchmark";cd /benchmark && bash run_bk.sh;echo "end benchmark"`
CodeMountPath = "/code"
@@ -37,11 +38,15 @@ const (
Success = "S000"

DefaultBranchName = "master"

ResultPath = "/result"
)

var (
ResourceSpecs *models.ResourceSpecs
TrainResourceSpecs *models.ResourceSpecs
ResourceSpecs *models.ResourceSpecs
TrainResourceSpecs *models.ResourceSpecs
InferenceResourceSpecs *models.ResourceSpecs
SpecialPools *models.SpecialPools
)

type GenerateCloudBrainTaskReq struct {
@@ -68,6 +73,17 @@ type GenerateCloudBrainTaskReq struct {
BenchmarkTypeID int
BenchmarkChildTypeID int
ResourceSpecId int
ResultPath string
TrainUrl string
ModelName string
ModelVersion string
CkptName string
LabelName string
}

func GetCloudbrainDebugCommand() string {
var command = `pip3 install jupyterlab==3 -i https://pypi.tuna.tsinghua.edu.cn/simple;service ssh stop;jupyter lab --ServerApp.shutdown_no_activity_timeout=` + setting.CullIdleTimeout + ` --TerminalManager.cull_inactive_timeout=` + setting.CullIdleTimeout + ` --TerminalManager.cull_interval=` + setting.CullInterval + ` --MappingKernelManager.cull_idle_timeout=` + setting.CullIdleTimeout + ` --MappingKernelManager.cull_interval=` + setting.CullInterval + ` --MappingKernelManager.cull_connected=True --MappingKernelManager.cull_busy=True --no-browser --ip=0.0.0.0 --allow-root --notebook-dir="/code" --port=80 --ServerApp.token="" --ServerApp.allow_origin="self https://cloudbrain.pcl.ac.cn" `
return command
}

func isAdminOrOwnerOrJobCreater(ctx *context.Context, job *models.Cloudbrain, err error) bool {
@@ -213,7 +229,6 @@ func AdminOrImageCreaterRight(ctx *context.Context) {
func GenerateTask(req GenerateCloudBrainTaskReq) error {
var resourceSpec *models.ResourceSpec
var versionCount int

if req.JobType == string(models.JobTypeTrain) {
versionCount = 1
if TrainResourceSpecs == nil {
@@ -222,8 +237,20 @@ func GenerateTask(req GenerateCloudBrainTaskReq) error {
for _, spec := range TrainResourceSpecs.ResourceSpec {
if req.ResourceSpecId == spec.Id {
resourceSpec = spec
break
}
}
} else if req.JobType == string(models.JobTypeInference) {
if InferenceResourceSpecs == nil {
json.Unmarshal([]byte(setting.InferenceResourceSpecs), &InferenceResourceSpecs)
}
for _, spec := range InferenceResourceSpecs.ResourceSpec {
if req.ResourceSpecId == spec.Id {
resourceSpec = spec
break
}
}

} else {
if ResourceSpecs == nil {
json.Unmarshal([]byte(setting.ResourceSpecs), &ResourceSpecs)
@@ -231,10 +258,15 @@ func GenerateTask(req GenerateCloudBrainTaskReq) error {
for _, spec := range ResourceSpecs.ResourceSpec {
if req.ResourceSpecId == spec.Id {
resourceSpec = spec
break
}
}

}
//如果没有匹配到spec信息,尝试从专属资源池获取
if resourceSpec == nil && SpecialPools != nil {
resourceSpec = geMatchResourceSpec(req.JobType, req.GpuQueue, req.ResourceSpecId)
}

if resourceSpec == nil {
log.Error("no such resourceSpecId(%d)", req.ResourceSpecId, req.Ctx.Data["MsgID"])
@@ -277,6 +309,13 @@ func GenerateTask(req GenerateCloudBrainTaskReq) error {
ReadOnly: true,
},
},
{
HostPath: models.StHostPath{
Path: req.ResultPath,
MountPath: ResultPath,
ReadOnly: false,
},
},
}

if len(req.DatasetInfos) == 1 {
@@ -357,6 +396,12 @@ func GenerateTask(req GenerateCloudBrainTaskReq) error {
BootFile: req.BootFile,
DatasetName: req.DatasetNames,
Parameters: req.Params,
TrainUrl: req.TrainUrl,
ModelName: req.ModelName,
ModelVersion: req.ModelVersion,
CkptName: req.CkptName,
ResultUrl: req.ResultPath,
LabelName: req.LabelName,
CreatedUnix: createTime,
UpdatedUnix: createTime,
CommitID: req.CommitID,
@@ -377,6 +422,8 @@ func GenerateTask(req GenerateCloudBrainTaskReq) error {
notification.NotifyOtherTask(req.Ctx.User, req.Ctx.Repo.Repository, stringId, req.DisplayJobName, models.ActionCreateBenchMarkTask)
} else if string(models.JobTypeTrain) == req.JobType {
notification.NotifyOtherTask(req.Ctx.User, req.Ctx.Repo.Repository, jobID, req.DisplayJobName, models.ActionCreateGPUTrainTask)
} else if string(models.JobTypeInference) == req.JobType {
notification.NotifyOtherTask(req.Ctx.User, req.Ctx.Repo.Repository, jobID, req.DisplayJobName, models.ActionCreateInferenceTask)
} else {
notification.NotifyOtherTask(req.Ctx.User, req.Ctx.Repo.Repository, stringId, req.DisplayJobName, models.ActionCreateDebugGPUTask)
}
@@ -388,6 +435,15 @@ func IsBenchmarkJob(jobType string) bool {
return string(models.JobTypeBenchmark) == jobType || string(models.JobTypeBrainScore) == jobType || string(models.JobTypeSnn4imagenet) == jobType
}

func GetWaitingCloudbrainCount(cloudbrainType int, computeResource string, jobTypes ...models.JobType) int64 {
num, err := models.GetWaitingCloudbrainCount(cloudbrainType, computeResource, jobTypes...)
if err != nil {
log.Warn("Get waiting count err", err)
num = 0
}
return num
}

func RestartTask(ctx *context.Context, task *models.Cloudbrain, newID *string) error {
jobName := task.JobName

@@ -401,6 +457,11 @@ func RestartTask(ctx *context.Context, task *models.Cloudbrain, newID *string) e
}
}

//如果没有匹配到spec信息,尝试从专属资源池获取
if resourceSpec == nil && SpecialPools != nil {
resourceSpec = geMatchResourceSpec(task.JobType, task.GpuQueue, task.ResourceSpecId)
}

if resourceSpec == nil {
log.Error("no such resourceSpecId(%d)", task.ResourceSpecId, ctx.Data["MsgID"])
return errors.New("no such resourceSpec")
@@ -486,7 +547,7 @@ func RestartTask(ctx *context.Context, task *models.Cloudbrain, newID *string) e
GPUNumber: resourceSpec.GpuNum,
MemoryMB: resourceSpec.MemMiB,
ShmMB: resourceSpec.ShareMemMiB,
Command: Command,
Command: GetCloudbrainDebugCommand(),//Command,
NeedIBDevice: false,
IsMainRole: false,
UseNNI: false,
@@ -538,3 +599,96 @@ func RestartTask(ctx *context.Context, task *models.Cloudbrain, newID *string) e

return nil
}

func geMatchResourceSpec(jobType string, gpuQueue string, resourceSpecId int) *models.ResourceSpec {

for _, specialPool := range SpecialPools.Pools {

if specialPool.ResourceSpec != nil {
if IsElementExist(specialPool.JobType, jobType) && IsQueueInSpecialtPool(specialPool.Pool, gpuQueue) {
for _, spec := range specialPool.ResourceSpec {
if resourceSpecId == spec.Id {
return spec
}
}
}
}
}
return nil
}

func DelCloudBrainJob(jobId string) string {
task, err := models.GetCloudbrainByJobID(jobId)
if err != nil {
log.Error("get cloud brain err:", err)
return "cloudbrain.Delete_failed"

}
if task.Status != string(models.JobStopped) && task.Status != string(models.JobFailed) && task.Status != string(models.JobSucceeded) {
log.Error("the job(%s) has not been stopped", task.JobName)
return "cloudbrain.Not_Stopped"
}

err = models.DeleteJob(task)
if err != nil {
log.Error("DeleteJob failed:", err)
return "cloudbrain.Delete_failed"
}

deleteJobStorage(task.JobName)

return ""
}

func deleteJobStorage(jobName string) error {
//delete local
localJobPath := setting.JobPath + jobName
err := os.RemoveAll(localJobPath)
if err != nil {
log.Error("RemoveAll(%s) failed:%v", localJobPath, err)
}

dirPath := setting.CBCodePathPrefix + jobName + "/"
err = storage.Attachments.DeleteDir(dirPath)
if err != nil {
log.Error("DeleteDir(%s) failed:%v", localJobPath, err)
}

return nil
}

func InitSpecialPool() {
if SpecialPools == nil && setting.SpecialPools != "" {
json.Unmarshal([]byte(setting.SpecialPools), &SpecialPools)
}
}

func IsResourceSpecInSpecialPool(resourceSpecs []*models.ResourceSpec, resourceSpecId int) bool {
if resourceSpecs == nil || len(resourceSpecs) == 0 {
return true
}
for _, v := range resourceSpecs {
if v.Id == resourceSpecId {
return true
}
}
return false
}

func IsQueueInSpecialtPool(pool []*models.GpuInfo, queue string) bool {
for _, v := range pool {
if v.Queue == queue {
return true
}
}
return false
}

func IsElementExist(s []string, str string) bool {
for _, v := range s {
if v == str {
return true
}
}
return false
}

+ 33
- 10
modules/cloudbrain/resty.go View File

@@ -30,6 +30,7 @@ const (
LogPageSize = 500
LogPageTokenExpired = "5m"
pageSize = 15
QueuesDetailUrl = "/rest-server/api/v2/queuesdetail"
)

func getRestyClient() *resty.Client {
@@ -73,12 +74,44 @@ func loginCloudbrain() error {
return nil
}

func GetQueuesDetail() (*map[string]int, error) {
checkSetting()
client := getRestyClient()
var jobResult models.QueueDetailResult

var result = make(map[string]int, 0)

res, err := client.R().
SetHeader("Content-Type", "application/json").
SetAuthToken(TOKEN).
SetResult(&jobResult).
Get(HOST + QueuesDetailUrl)

if err != nil {
return nil, fmt.Errorf("resty get queues detail failed: %s", err)
}

if jobResult.Code != Success {
return nil, fmt.Errorf("jobResult err: %s", res.String())
}

for k, v := range jobResult.Payload {

result[k] = v.JobScheduleInfo.Pending

}
return &result, nil

}

func CreateJob(jobName string, createJobParams models.CreateJobParams) (*models.CreateJobResult, error) {
checkSetting()
client := getRestyClient()
var jobResult models.CreateJobResult

retry := 0
reqPara, _ := json.Marshal(createJobParams)
log.Warn("job req:", string(reqPara[:]))

sendjob:
res, err := client.R().
@@ -143,16 +176,6 @@ sendjob:
return &getJobResult, nil
}

func GetImages() (*models.GetImagesResult, error) {

return GetImagesPageable(1, 100, Custom, "")

}

func GetPublicImages() (*models.GetImagesResult, error) {
return GetImagesPageable(1, 100, Public, "")
}

func GetImagesPageable(page int, size int, imageType string, name string) (*models.GetImagesResult, error) {
checkSetting()
client := getRestyClient()


+ 4
- 0
modules/context/auth.go View File

@@ -53,6 +53,10 @@ func Toggle(options *ToggleOptions) macaron.Handler {
ctx.Data["Title"] = ctx.Tr("auth.prohibit_login")
ctx.HTML(200, "user/auth/prohibit_login")
return
} else if setting.PhoneService.Enabled && ctx.User.IsActive && ctx.User.PhoneNumber == "" && ctx.Req.URL.Path != "/bindPhone" {
ctx.Data["Title"] = ctx.Tr("phone.bind_phone")
ctx.HTML(200, "user/auth/bind_phone")
return
}

if ctx.User.MustChangePassword {


+ 64
- 21
modules/grampus/grampus.go View File

@@ -65,6 +65,7 @@ type GenerateTrainJobReq struct {
EngineName string
TotalVersionCount int
ComputeResource string
ProcessType string
DatasetName string
Params string
}
@@ -72,25 +73,7 @@ type GenerateTrainJobReq struct {
func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error) {
createTime := timeutil.TimeStampNow()

var CenterID []string
var CenterName []string

if SpecialPools != nil {
for _, pool := range SpecialPools.Pools {
if !pool.IsExclusive && strings.Contains(req.ComputeResource, pool.Type) {
org, _ := models.GetOrgByName(pool.Org)
if org != nil {
isOrgMember, _ := models.IsOrganizationMember(org.ID, ctx.User.ID)
if isOrgMember {
for _, info := range pool.Pool {
CenterID = append(CenterID, info.Queue)
CenterName = append(CenterName, info.Value)
}
}
}
}
}
}
centerID, centerName := getCentersParamter(ctx, req)

jobResult, err := createJob(models.CreateGrampusJobRequest{
Name: req.JobName,
@@ -101,8 +84,8 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error
ResourceSpecId: req.ResourceSpecId,
ImageId: req.ImageId,
ImageUrl: req.ImageUrl,
CenterID: CenterID,
CenterName: CenterName,
CenterID: centerID,
CenterName: centerName,
ReplicaNum: 1,
},
},
@@ -160,6 +143,66 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error
return nil
}

func getCentersParamter(ctx *context.Context, req *GenerateTrainJobReq) ([]string, []string) {
var centerID []string
var centerName []string

includeCenters := make(map[string]string)
excludeCenters := make(map[string]string)

if SpecialPools != nil {
for _, pool := range SpecialPools.Pools {
if !pool.IsExclusive && strings.Contains(req.ComputeResource, pool.Type) {
org, _ := models.GetOrgByName(pool.Org)
if org != nil {
isOrgMember, _ := models.IsOrganizationMember(org.ID, ctx.User.ID)
if isOrgMember {
for _, info := range pool.Pool {
includeCenters[info.Queue] = info.Value
}
} else {
for _, info := range pool.Pool {
excludeCenters[info.Queue] = info.Value
}
}
}
}
}

}

if len(includeCenters) > 0 {
//如果有专属资源池,根据专属资源池指定智算中心
for k, v := range includeCenters {
centerID = append(centerID, k)
centerName = append(centerName, v)
}
} else if len(excludeCenters) > 0 {
//否则,有要排除的中心,先获取所有中心,删除其中的排除中心,得到指定的智算中心
allCenters := make(map[string]string)
specs, err := GetResourceSpecs(req.ProcessType)
if err == nil {
for _, info := range specs.Infos {
for _, center := range info.Centers {
allCenters[center.ID] = center.Name
}

}
}

for k, _ := range excludeCenters {
delete(allCenters, k)
}

for k, v := range allCenters {
centerID = append(centerID, k)
centerName = append(centerName, v)
}

}
return centerID, centerName
}

func TransTrainJobStatus(status string) string {
if status == models.GrampusStatusPending {
status = models.GrampusStatusWaiting


+ 201
- 32
modules/modelarts/modelarts.go View File

@@ -19,14 +19,14 @@ import (

const (
//notebook
storageTypeOBS = "obs"
autoStopDuration = 4 * 60 * 60
autoStopDurationMs = 4 * 60 * 60 * 1000
DataSetMountPath = "/home/ma-user/work"
NotebookEnv = "Python3"
NotebookType = "Ascend"
FlavorInfo = "Ascend: 1*Ascend 910 CPU: 24 核 96GiB (modelarts.kat1.xlarge)"
storageTypeOBS = "obs"
autoStopDuration = 4 * 60 * 60
autoStopDurationMs = 4 * 60 * 60 * 1000
MORDELART_USER_IMAGE_ENGINE_ID = -1
DataSetMountPath = "/home/ma-user/work"
NotebookEnv = "Python3"
NotebookType = "Ascend"
FlavorInfo = "Ascend: 1*Ascend 910 CPU: 24 核 96GiB (modelarts.kat1.xlarge)"

//train-job
// ResourcePools = "{\"resource_pool\":[{\"id\":\"pool1328035d\", \"value\":\"专属资源池\"}]}"
@@ -98,6 +98,8 @@ type GenerateTrainJobReq struct {
VersionCount int
EngineName string
TotalVersionCount int
UserImageUrl string
UserCommand string
DatasetName string
}

@@ -136,6 +138,7 @@ type VersionInfo struct {
Version []struct {
ID int `json:"id"`
Value string `json:"value"`
Url string `json:"url"`
} `json:"version"`
}

@@ -314,15 +317,107 @@ func GenerateNotebook2(ctx *context.Context, displayJobName, jobName, uuid, desc

func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error) {
createTime := timeutil.TimeStampNow()
jobResult, err := createTrainJob(models.CreateTrainJobParams{
var jobResult *models.CreateTrainJobResult
var createErr error
if req.EngineID < 0 {
jobResult, createErr = createTrainJobUserImage(models.CreateUserImageTrainJobParams{
JobName: req.JobName,
Description: req.Description,
Config: models.UserImageConfig{
WorkServerNum: req.WorkServerNumber,
AppUrl: req.CodeObsPath,
BootFileUrl: req.BootFileUrl,
DataUrl: req.DataUrl,
TrainUrl: req.TrainUrl,
LogUrl: req.LogUrl,
PoolID: req.PoolID,
CreateVersion: true,
Flavor: models.Flavor{
Code: req.FlavorCode,
},
Parameter: req.Parameters,
UserImageUrl: req.UserImageUrl,
UserCommand: req.UserCommand,
},
})
} else {
jobResult, createErr = createTrainJob(models.CreateTrainJobParams{
JobName: req.JobName,
Description: req.Description,
Config: models.Config{
WorkServerNum: req.WorkServerNumber,
AppUrl: req.CodeObsPath,
BootFileUrl: req.BootFileUrl,
DataUrl: req.DataUrl,
EngineID: req.EngineID,
TrainUrl: req.TrainUrl,
LogUrl: req.LogUrl,
PoolID: req.PoolID,
CreateVersion: true,
Flavor: models.Flavor{
Code: req.FlavorCode,
},
Parameter: req.Parameters,
},
})
}
if createErr != nil {
log.Error("CreateJob failed: %v", createErr.Error())
return createErr
}
jobId := strconv.FormatInt(jobResult.JobID, 10)
createErr = models.CreateCloudbrain(&models.Cloudbrain{
Status: TransTrainJobStatus(jobResult.Status),
UserID: ctx.User.ID,
RepoID: ctx.Repo.Repository.ID,
JobID: jobId,
JobName: req.JobName,
DisplayJobName: req.DisplayJobName,
JobType: string(models.JobTypeTrain),
Type: models.TypeCloudBrainTwo,
VersionID: jobResult.VersionID,
VersionName: jobResult.VersionName,
Uuid: req.Uuid,
DatasetName: req.DatasetName,
CommitID: req.CommitID,
IsLatestVersion: req.IsLatestVersion,
ComputeResource: models.NPUResource,
EngineID: req.EngineID,
TrainUrl: req.TrainUrl,
BranchName: req.BranchName,
Parameters: req.Params,
BootFile: req.BootFile,
DataUrl: req.DataUrl,
LogUrl: req.LogUrl,
FlavorCode: req.FlavorCode,
Description: req.Description,
WorkServerNumber: req.WorkServerNumber,
FlavorName: req.FlavorName,
EngineName: req.EngineName,
VersionCount: req.VersionCount,
TotalVersionCount: req.TotalVersionCount,
CreatedUnix: createTime,
UpdatedUnix: createTime,
})

if createErr != nil {
log.Error("CreateCloudbrain(%s) failed:%v", req.DisplayJobName, createErr.Error())
return createErr
}
notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, jobId, req.DisplayJobName, models.ActionCreateTrainTask)
return nil
}

func GenerateModelConvertTrainJob(req *GenerateTrainJobReq) (*models.CreateTrainJobResult, error) {

return createTrainJobUserImage(models.CreateUserImageTrainJobParams{
JobName: req.JobName,
Description: req.Description,
Config: models.Config{
Config: models.UserImageConfig{
WorkServerNum: req.WorkServerNumber,
AppUrl: req.CodeObsPath,
BootFileUrl: req.BootFileUrl,
DataUrl: req.DataUrl,
EngineID: req.EngineID,
TrainUrl: req.TrainUrl,
LogUrl: req.LogUrl,
PoolID: req.PoolID,
@@ -330,20 +425,83 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error
Flavor: models.Flavor{
Code: req.FlavorCode,
},
Parameter: req.Parameters,
Parameter: req.Parameters,
UserImageUrl: req.UserImageUrl,
UserCommand: req.UserCommand,
},
})
if err != nil {
log.Error("CreateJob failed: %v", err.Error())
return err
}

func GenerateTrainJobVersion(ctx *context.Context, req *GenerateTrainJobReq, jobId string) (err error) {
createTime := timeutil.TimeStampNow()
var jobResult *models.CreateTrainJobResult
var createErr error
log.Info(" req.EngineID =" + fmt.Sprint(req.EngineID))
if req.EngineID < 0 {
jobResult, createErr = createTrainJobVersionUserImage(models.CreateTrainJobVersionUserImageParams{
Description: req.Description,
Config: models.TrainJobVersionUserImageConfig{
WorkServerNum: req.WorkServerNumber,
AppUrl: req.CodeObsPath,
BootFileUrl: req.BootFileUrl,
DataUrl: req.DataUrl,
TrainUrl: req.TrainUrl,
LogUrl: req.LogUrl,
PoolID: req.PoolID,
Flavor: models.Flavor{
Code: req.FlavorCode,
},
Parameter: req.Parameters,
PreVersionId: req.PreVersionId,
UserImageUrl: req.UserImageUrl,
UserCommand: req.UserCommand,
},
}, jobId)
} else {
jobResult, createErr = createTrainJobVersion(models.CreateTrainJobVersionParams{
Description: req.Description,
Config: models.TrainJobVersionConfig{
WorkServerNum: req.WorkServerNumber,
AppUrl: req.CodeObsPath,
BootFileUrl: req.BootFileUrl,
DataUrl: req.DataUrl,
EngineID: req.EngineID,
TrainUrl: req.TrainUrl,
LogUrl: req.LogUrl,
PoolID: req.PoolID,
Flavor: models.Flavor{
Code: req.FlavorCode,
},
Parameter: req.Parameters,
PreVersionId: req.PreVersionId,
},
}, jobId)
}
if createErr != nil {
log.Error("CreateJob failed: %v", createErr.Error())
return createErr
}

jobId := strconv.FormatInt(jobResult.JobID, 10)
err = models.CreateCloudbrain(&models.Cloudbrain{
var jobTypes []string
jobTypes = append(jobTypes, string(models.JobTypeTrain))
repo := ctx.Repo.Repository
VersionTaskList, VersionListCount, createErr := models.CloudbrainsVersionList(&models.CloudbrainsOptions{
RepoID: repo.ID,
Type: models.TypeCloudBrainTwo,
JobTypes: jobTypes,
JobID: strconv.FormatInt(jobResult.JobID, 10),
})
if createErr != nil {
ctx.ServerError("Cloudbrain", createErr)
return createErr
}
//将当前版本的isLatestVersion设置为"1"和任务数量更新,任务数量包括当前版本数VersionCount和历史创建的总版本数TotalVersionCount

createErr = models.CreateCloudbrain(&models.Cloudbrain{
Status: TransTrainJobStatus(jobResult.Status),
UserID: ctx.User.ID,
RepoID: ctx.Repo.Repository.ID,
JobID: jobId,
JobID: strconv.FormatInt(jobResult.JobID, 10),
JobName: req.JobName,
DisplayJobName: req.DisplayJobName,
JobType: string(models.JobTypeTrain),
@@ -354,6 +512,7 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error
DatasetName: req.DatasetName,
CommitID: req.CommitID,
IsLatestVersion: req.IsLatestVersion,
PreVersionName: req.PreVersionName,
ComputeResource: models.NPUResource,
EngineID: req.EngineID,
TrainUrl: req.TrainUrl,
@@ -362,45 +521,54 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error
BootFile: req.BootFile,
DataUrl: req.DataUrl,
LogUrl: req.LogUrl,
PreVersionId: req.PreVersionId,
FlavorCode: req.FlavorCode,
Description: req.Description,
WorkServerNumber: req.WorkServerNumber,
FlavorName: req.FlavorName,
EngineName: req.EngineName,
VersionCount: req.VersionCount,
TotalVersionCount: req.TotalVersionCount,
TotalVersionCount: VersionTaskList[0].TotalVersionCount + 1,
VersionCount: VersionListCount + 1,
CreatedUnix: createTime,
UpdatedUnix: createTime,
})
if createErr != nil {
log.Error("CreateCloudbrain(%s) failed:%v", req.JobName, createErr.Error())
return createErr
}

if err != nil {
log.Error("CreateCloudbrain(%s) failed:%v", req.DisplayJobName, err.Error())
return err
//将训练任务的上一版本的isLatestVersion设置为"0"
createErr = models.SetVersionCountAndLatestVersion(strconv.FormatInt(jobResult.JobID, 10), VersionTaskList[0].VersionName, VersionCount, NotLatestVersion, TotalVersionCount)
if createErr != nil {
ctx.ServerError("Update IsLatestVersion failed", createErr)
return createErr
}
notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, jobId, req.DisplayJobName, models.ActionCreateTrainTask)
return nil
return createErr
}

func GenerateTrainJobVersion(ctx *context.Context, req *GenerateTrainJobReq, jobId string) (err error) {
func GenerateTrainJobVersionByUserImage(ctx *context.Context, req *GenerateTrainJobReq, jobId string) (err error) {
createTime := timeutil.TimeStampNow()
jobResult, err := createTrainJobVersion(models.CreateTrainJobVersionParams{
jobResult, err := createTrainJobUserImage(models.CreateUserImageTrainJobParams{
JobName: req.JobName,
Description: req.Description,
Config: models.TrainJobVersionConfig{
Config: models.UserImageConfig{
WorkServerNum: req.WorkServerNumber,
AppUrl: req.CodeObsPath,
BootFileUrl: req.BootFileUrl,
DataUrl: req.DataUrl,
EngineID: req.EngineID,
TrainUrl: req.TrainUrl,
LogUrl: req.LogUrl,
PoolID: req.PoolID,
CreateVersion: true,
Flavor: models.Flavor{
Code: req.FlavorCode,
},
Parameter: req.Parameters,
PreVersionId: req.PreVersionId,
UserImageUrl: req.UserImageUrl,
UserCommand: req.UserCommand,
},
}, jobId)
})
if err != nil {
log.Error("CreateJob failed: %v", err.Error())
return err
@@ -438,7 +606,8 @@ func GenerateTrainJobVersion(ctx *context.Context, req *GenerateTrainJobReq, job
IsLatestVersion: req.IsLatestVersion,
PreVersionName: req.PreVersionName,
ComputeResource: models.NPUResource,
EngineID: req.EngineID,
EngineID: MORDELART_USER_IMAGE_ENGINE_ID,
Image: req.UserImageUrl,
TrainUrl: req.TrainUrl,
BranchName: req.BranchName,
Parameters: req.Params,


+ 111
- 0
modules/modelarts/resty.go View File

@@ -472,6 +472,62 @@ sendjob:
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("%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("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("数据集错误!")
}
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 createTrainJob(createJobParams models.CreateTrainJobParams) (*models.CreateTrainJobResult, error) {
checkSetting()
client := getRestyClient()
@@ -583,6 +639,61 @@ sendjob:
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()


+ 61
- 0
modules/phone/phone.go View File

@@ -0,0 +1,61 @@
package phone

import (
"math"
"math/rand"
"regexp"
"strconv"
"time"

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

openapi "github.com/alibabacloud-go/darabonba-openapi/client"
dysmsapi20170525 "github.com/alibabacloud-go/dysmsapi-20170525/v2/client"
util "github.com/alibabacloud-go/tea-utils/service"
"github.com/alibabacloud-go/tea/tea"
)

func IsValidPhoneNumber(phoneNumber string) bool {
pattern := "^1[3-9]\\d{9}$"
match, _ := regexp.MatchString(pattern, phoneNumber)
return match
}

func GenerateVerifyCode(n int) string {
min := int(math.Pow10(n - 1))
max := int(math.Pow10(n))
rand.Seed(time.Now().UnixNano())
return strconv.Itoa(rand.Intn(max-min) + min)

}

func createClient(accessKeyId *string, accessKeySecret *string) (_result *dysmsapi20170525.Client, _err error) {
config := &openapi.Config{
// 您的AccessKey ID
AccessKeyId: accessKeyId,
// 您的AccessKey Secret
AccessKeySecret: accessKeySecret,
}
// 访问的域名
config.Endpoint = tea.String("dysmsapi.aliyuncs.com")
_result = &dysmsapi20170525.Client{}
_result, _err = dysmsapi20170525.NewClient(config)
return _result, _err
}

func SendVerifyCode(phoneNumber string, verifyCode string) error {
client, _err := createClient(&setting.PhoneService.AccessKeyId, &setting.PhoneService.AccessKeySecret)
if _err != nil {
return _err
}

sendSmsRequest := &dysmsapi20170525.SendSmsRequest{
SignName: tea.String(setting.PhoneService.SignName),
TemplateCode: tea.String(setting.PhoneService.TemplateCode),
PhoneNumbers: tea.String(phoneNumber),
TemplateParam: tea.String("{\"code\":\"" + verifyCode + "\"}"),
}
runtime := &util.RuntimeOptions{}
_, _err = client.SendSmsWithOptions(sendSmsRequest, runtime)
return _err
}

+ 43
- 0
modules/public/dynamic.go View File

@@ -7,10 +7,53 @@
package public

import (
"fmt"
"io/ioutil"
"os"
"path"

"code.gitea.io/gitea/modules/setting"
"gitea.com/macaron/macaron"
"github.com/unknwon/com"
)

// Static implements the macaron static handler for serving assets.
func Static(opts *Options) macaron.Handler {
return opts.staticHandler(opts.Directory)
}

func Dir(name string) ([]string, error) {

var (
result []string
)

staticDir := path.Join(setting.StaticRootPath, "public", name)

if com.IsDir(staticDir) {
files, err := com.StatDir(staticDir, true)

if err != nil {
return []string{}, fmt.Errorf("Failed to read img directory. %v", err)
}

result = append(result, files...)
}

return result, nil
}

func Asset(name string) ([]byte, error) {

staticPath := path.Join(setting.StaticRootPath, "public", name)

if com.IsFile(staticPath) {
f, err := os.Open(staticPath)
defer f.Close()

if err == nil {
return ioutil.ReadAll(f)
}
}
return nil, fmt.Errorf("Asset file does not exist: %s", name)
}

+ 29
- 0
modules/public/static.go View File

@@ -7,6 +7,7 @@
package public

import (
"fmt"
"io/ioutil"

"gitea.com/macaron/macaron"
@@ -20,6 +21,16 @@ func Static(opts *Options) macaron.Handler {
return opts.staticHandler("")
}

func Dir(name string) ([]string, error) {

files, err := AssetDir(name)
if err != nil {
return []string{}, fmt.Errorf("Failed to read embedded directory. %v", err)
}

return files, nil
}

func Asset(name string) ([]byte, error) {
f, err := Assets.Open("/" + name)
if err != nil {
@@ -29,6 +40,24 @@ func Asset(name string) ([]byte, error) {
return ioutil.ReadAll(f)
}

func AssetDir(dirName string) ([]string, error) {
d, err := Assets.Open(dirName)
if err != nil {
return nil, err
}
defer d.Close()

files, err := d.Readdir(-1)
if err != nil {
return nil, err
}
var results = make([]string, 0, len(files))
for _, file := range files {
results = append(results, file.Name())
}
return results, nil
}

func AssetNames() []string {
realFS := Assets.(vfsgen۰FS)
var results = make([]string, 0, len(realFS))


+ 60
- 0
modules/redis/redis_client/client.go View File

@@ -41,6 +41,66 @@ func Setnx(key, value string, timeout time.Duration) (bool, error) {

}

func SETNX(conn redis.Conn, key, value string, seconds int) (bool, error) {
reply, err := conn.Do("SET", key, value, "NX", "EX", seconds)
return redis.Bool(reply, err)

}

func SET(conn redis.Conn, key, value string, seconds int) (bool, error) {
reply, err := conn.Do("SETEX", key, seconds, value)
return redis.Bool(reply, err)

}

func HSETNX(conn redis.Conn, key, subKey string, value interface{}) error {
_, err := conn.Do("HSETNX", key, subKey, value)
return err

}

func HGET(conn redis.Conn, key, subKey string) (interface{}, error) {
return conn.Do("HGET", key, subKey)
}
func EXISTS(conn redis.Conn, key string) (bool, error) {

reply, err := conn.Do("EXISTS", key)
return redis.Bool(reply, err)

}

func HEXISTS(conn redis.Conn, key string, subKey string) (bool, error) {

reply, err := conn.Do("HEXISTS", key, subKey)
return redis.Bool(reply, err)

}

func Expire(conn redis.Conn, key string, seconds int) error {
_, err := conn.Do("EXPIRE", key, seconds)
return err

}

func HINCRBY(conn redis.Conn, key, subKey string, value int) error {
_, err := conn.Do("HINCRBY", key, subKey, value)
return err
}
func GET(conn redis.Conn, key string) (interface{}, error) {
return conn.Do("GET", key)
}

func Ttl(conn redis.Conn, key string) (int, error) {

reply, err := conn.Do("TTL", key)
if err != nil {
return 0, err
}
n, _ := strconv.Atoi(fmt.Sprint(reply))
return n, nil

}

func Get(key string) (string, error) {
redisClient := labelmsg.Get()
defer redisClient.Close()


+ 47
- 0
modules/setting/phone.go View File

@@ -0,0 +1,47 @@
package setting

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

type Phone struct {
Enabled bool
VerifyCodeLength int
AccessKeyId string
AccessKeySecret string
SignName string
TemplateCode string
CodeTimeout int
RetryInterval int
MaxRetryTimes int
}

var (
// Phone verify info
PhoneService *Phone
)

func newPhoneService() {
sec := Cfg.Section("phone")
// Check phone setting.
if !sec.Key("ENABLED").MustBool() {
PhoneService = &Phone{
Enabled: sec.Key("ENABLED").MustBool(),
}
return
}

PhoneService = &Phone{
Enabled: sec.Key("ENABLED").MustBool(),
VerifyCodeLength: sec.Key("VERIFY_CODE_LEN").MustInt(6),
AccessKeyId: sec.Key("AccessKeyId").String(),
AccessKeySecret: sec.Key("AccessKeySecret").String(),
SignName: sec.Key("SignName").String(),
TemplateCode: sec.Key("TemplateCode").String(),
CodeTimeout: sec.Key("CODE_TIMEOUT").MustInt(60 * 5),
RetryInterval: sec.Key("RETRY_INTERVAL").MustInt(60 * 2),
MaxRetryTimes: sec.Key("MAX_RETRY").MustInt(5),
}

log.Info("Phone Service Enabled")
}

+ 92
- 20
modules/setting/setting.go View File

@@ -7,6 +7,7 @@ package setting

import (
"encoding/base64"
"encoding/json"
"fmt"
"io"
"io/ioutil"
@@ -64,7 +65,16 @@ const (
ReCaptcha = "recaptcha"
)

// settings
type C2NetSequenceInfo struct {
ID int `json:"id"`
Name string `json:"name"`
Content string `json:"content"`
}

type C2NetSqInfos struct {
C2NetSqInfo []*C2NetSequenceInfo `json:"sequence"`
}

var (
// AppVer settings
AppVer string
@@ -453,19 +463,26 @@ var (
DecompressOBSTaskName string

//cloudbrain config
CBAuthUser string
CBAuthPassword string
RestServerHost string
JobPath string
CBCodePathPrefix string
JobType string
GpuTypes string
DebugServerHost string
ResourceSpecs string
MaxDuration int64
TrainGpuTypes string
TrainResourceSpecs string
MaxDatasetNum int

CBAuthUser string
CBAuthPassword string
RestServerHost string
JobPath string
CBCodePathPrefix string
JobType string
GpuTypes string
SpecialPools string
DebugServerHost string
ResourceSpecs string
MaxDuration int64
TrainGpuTypes string
TrainResourceSpecs string
InferenceGpuTypes string
InferenceResourceSpecs string
MaxModelSize float64
MaxDatasetNum int
CullIdleTimeout string
CullInterval string

//benchmark config
IsBenchmarkEnabled bool
@@ -531,13 +548,16 @@ var (

//grampus config
Grampus = struct {
Env string
Host string
UserName string
Password string
SpecialPools string
Env string
Host string
UserName string
Password string
SpecialPools string
C2NetSequence string
}{}

C2NetInfos *C2NetSqInfos

//elk config
ElkUrl string
ElkUser string
@@ -612,6 +632,24 @@ var (
OrgName string
TeamName string
}{}

ModelConvert = struct {
GPU_PYTORCH_IMAGE string
GpuQueue string
GPU_TENSORFLOW_IMAGE string
NPU_MINDSPORE_16_IMAGE string
PytorchOnnxBootFile string
PytorchTrTBootFile string
MindsporeBootFile string
TensorFlowNpuBootFile string
TensorFlowGpuBootFile string
ConvertRepoPath string
GPU_Resource_Specs_ID int
NPU_FlavorCode string
NPU_PoolID string
NPU_MINDSPORE_IMAGE_ID int
NPU_TENSORFLOW_IMAGE_ID int
}{}
)

// DateLang transforms standard language locale name to corresponding value in datetime plugin.
@@ -1311,7 +1349,14 @@ func NewContext() {
MaxDuration = sec.Key("MAX_DURATION").MustInt64(14400)
TrainGpuTypes = sec.Key("TRAIN_GPU_TYPES").MustString("")
TrainResourceSpecs = sec.Key("TRAIN_RESOURCE_SPECS").MustString("")
MaxModelSize = sec.Key("MAX_MODEL_SIZE").MustFloat64(500)
InferenceGpuTypes = sec.Key("INFERENCE_GPU_TYPES").MustString("")
InferenceResourceSpecs = sec.Key("INFERENCE_RESOURCE_SPECS").MustString("")
SpecialPools = sec.Key("SPECIAL_POOL").MustString("")

MaxDatasetNum = sec.Key("MAX_DATASET_NUM").MustInt(5)
CullIdleTimeout = sec.Key("CULL_IDLE_TIMEOUT").MustString("900")
CullInterval = sec.Key("CULL_INTERVAL").MustString("60")

sec = Cfg.Section("benchmark")
IsBenchmarkEnabled = sec.Key("ENABLED").MustBool(false)
@@ -1406,6 +1451,27 @@ func NewContext() {
Course.TeamName = sec.Key("team_name").MustString("")

GetGrampusConfig()

getModelConvertConfig()
}

func getModelConvertConfig() {
sec := Cfg.Section("model_convert")
ModelConvert.GPU_PYTORCH_IMAGE = sec.Key("GPU_PYTORCH_IMAGE").MustString("dockerhub.pcl.ac.cn:5000/user-images/openi:tensorRT_7_zouap")
ModelConvert.GpuQueue = sec.Key("GpuQueue").MustString("openidgx")
ModelConvert.GPU_TENSORFLOW_IMAGE = sec.Key("GPU_TENSORFLOW_IMAGE").MustString("dockerhub.pcl.ac.cn:5000/user-images/openi:tf2onnx")
ModelConvert.NPU_MINDSPORE_16_IMAGE = sec.Key("NPU_MINDSPORE_16_IMAGE").MustString("swr.cn-south-222.ai.pcl.cn/openi/mindspore1.6.1_train_v1_openi:v3_ascend")
ModelConvert.PytorchOnnxBootFile = sec.Key("PytorchOnnxBootFile").MustString("convert_pytorch.py")
ModelConvert.PytorchTrTBootFile = sec.Key("PytorchTrTBootFile").MustString("convert_pytorch_tensorrt.py")
ModelConvert.MindsporeBootFile = sec.Key("MindsporeBootFile").MustString("convert_mindspore.py")
ModelConvert.TensorFlowNpuBootFile = sec.Key("TensorFlowNpuBootFile").MustString("convert_tensorflow.py")
ModelConvert.TensorFlowGpuBootFile = sec.Key("TensorFlowGpuBootFile").MustString("convert_tensorflow_gpu.py")
ModelConvert.ConvertRepoPath = sec.Key("ConvertRepoPath").MustString("https://git.openi.org.cn/zouap/npu_test")
ModelConvert.GPU_Resource_Specs_ID = sec.Key("GPU_Resource_Specs_ID").MustInt(1)
ModelConvert.NPU_FlavorCode = sec.Key("NPU_FlavorCode").MustString("modelarts.bm.910.arm.public.1")
ModelConvert.NPU_PoolID = sec.Key("NPU_PoolID").MustString("pool7908321a")
ModelConvert.NPU_MINDSPORE_IMAGE_ID = sec.Key("NPU_MINDSPORE_IMAGE_ID").MustInt(121)
ModelConvert.NPU_TENSORFLOW_IMAGE_ID = sec.Key("NPU_TENSORFLOW_IMAGE_ID").MustInt(35)
}

func GetGrampusConfig() {
@@ -1416,7 +1482,12 @@ func GetGrampusConfig() {
Grampus.UserName = sec.Key("USERNAME").MustString("")
Grampus.Password = sec.Key("PASSWORD").MustString("")
Grampus.SpecialPools = sec.Key("SPECIAL_POOL").MustString("")

Grampus.C2NetSequence = sec.Key("C2NET_SEQUENCE").MustString("{\"sequence\":[{\"id\":1,\"name\":\"cloudbrain_one\",\"content\":\"鹏城云脑一号\"},{\"id\":2,\"name\":\"cloudbrain_two\",\"content\":\"鹏城云脑二号\"},{\"id\":3,\"name\":\"beida\",\"content\":\"北大人工智能集群系统\"},{\"id\":4,\"name\":\"hefei\",\"content\":\"合肥类脑智能开放平台\"},{\"id\":5,\"name\":\"wuhan\",\"content\":\"武汉人工智能计算中心\"},{\"id\":6,\"name\":\"xian\",\"content\":\"西安未来人工智能计算中心\"},{\"id\":7,\"pclcci\":\"more\",\"content\":\"鹏城云计算所\"},{\"id\":8,\"name\":\"xuchang\",\"content\":\"中原人工智能计算中心\"},{\"id\":9,\"name\":\"chengdu\",\"content\":\"成都人工智能计算中心\"},{\"id\":10,\"name\":\"more\",\"content\":\"横琴先进智能计算中心\"},{\"id\":11,\"name\":\"more\",\"content\":\"国家超级计算济南中心\"}]}")
if Grampus.C2NetSequence != "" {
if err := json.Unmarshal([]byte(Grampus.C2NetSequence), &C2NetInfos); err != nil {
log.Error("Unmarshal(C2NetSequence) failed:%v", err)
}
}
}

func SetRadarMapConfig() {
@@ -1554,4 +1625,5 @@ func NewServices() {
newIndexerService()
newTaskService()
NewQueueService()
newPhoneService()
}

+ 12
- 0
modules/setting/slideimage.go View File

@@ -0,0 +1,12 @@
package setting

import (
"image"
)

var (
// the original images for generate slide image
SlideImagesBg []*image.Image
SlideMaskImage *image.Image
SlideImagesCount int
)

+ 308
- 0
modules/slideimage/slideimage.go View File

@@ -0,0 +1,308 @@
package slideimage

import (
"bytes"
"image"
"image/png"
"math"
"math/rand"
"path"
"strconv"
"strings"
"time"

"code.gitea.io/gitea/modules/labelmsg"
"github.com/gomodule/redigo/redis"

"code.gitea.io/gitea/modules/public"

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

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

"code.gitea.io/gitea/modules/redis/redis_client"

"gitea.com/macaron/macaron"
"github.com/disintegration/imaging"
"github.com/google/uuid"
)

type SlideImage struct {
SubURL string
URLPrefix string
SampleImages int
StdWidth int
StdHeight int
MaskSize int
ImageY int
ImageXStart int
MinImageX int
Expiration int
Tolerance int
CachePrefix string
CacheManualPrefix string
}

type Options struct {
// Suburl path. Default is empty.
SubURL string
// URL prefix of getting captcha pictures. Default is "/slideimage/".
URLPrefix string
//Default is 4
SampleImages int
//Image width default 391
StdWidth int
//Image Height default 196
StdHeight int
// default 51
MaskSize int
// default 125
ImageY int
//default 0
ImageXStart int
//容忍的误差 default 2px
Tolerance int
// default 150
MinImageX int
// default 600 seconds
Expiration int
//default slide:
CachePrefix string
//default mslide: 验证通过,在缓存中记录已进行过人工操作,然后在发送验证码之前再进行校验是否进行了人工操作。
CacheManualPrefix string
}

func NewSlideImage(opt Options) *SlideImage {
return &SlideImage{
SubURL: opt.SubURL,
URLPrefix: opt.URLPrefix,
SampleImages: opt.SampleImages,
StdWidth: opt.StdWidth,
StdHeight: opt.StdHeight,
MaskSize: opt.MaskSize,
ImageY: opt.ImageY,
ImageXStart: opt.ImageXStart,
Tolerance: opt.Tolerance,
MinImageX: opt.MinImageX,
Expiration: opt.Expiration,
CachePrefix: opt.CachePrefix,
}
}

func prepareOptions(options []Options) Options {
var opt Options
if len(options) > 0 {
opt = options[0]
}

opt.SubURL = strings.TrimSuffix(opt.SubURL, "/")

// Defaults.
if len(opt.URLPrefix) == 0 {
opt.URLPrefix = "/slideimage/"
} else if opt.URLPrefix[len(opt.URLPrefix)-1] != '/' {
opt.URLPrefix += "/"
}
if opt.SampleImages == 0 {
opt.SampleImages = 4
}
if opt.StdWidth == 0 {
opt.StdWidth = 391
}
if opt.StdHeight == 0 {
opt.StdHeight = 196
}
if opt.MaskSize == 0 {
opt.MaskSize = 51
}
if opt.ImageY == 0 {
opt.ImageY = 75
}
if opt.ImageXStart == 0 {
opt.ImageXStart = 2
}

if opt.Tolerance == 0 {
opt.Tolerance = 2
}

if opt.MinImageX == 0 {
opt.MinImageX = 150
}
if opt.Expiration == 0 {
opt.Expiration = 600
}

if len(opt.CachePrefix) == 0 {
opt.CachePrefix = "slide:"
}
if len(opt.CacheManualPrefix) == 0 {
opt.CacheManualPrefix = "mslide:"
}

return opt
}

func (s *SlideImage) key(id string) string {
return s.CachePrefix + id
}
func (s *SlideImage) mkey(id string) string {
return s.CacheManualPrefix + id
}

func (s *SlideImage) VerifyManual(id string) (bool, error) {
redisConn := labelmsg.Get()
defer redisConn.Close()
return redis_client.EXISTS(redisConn, s.mkey(id))
}

func (s *SlideImage) Verify(id string, x int) bool {
redisConn := labelmsg.Get()
defer redisConn.Close()

v, err := redis_client.GET(redisConn, s.key(id))
v1, err := redis.String(v, err)
if err != nil {
log.Warn("redis err", err)
return false
}
if v1 == "" {
return false
}
values := strings.Split(v1, "-")
imageRandX, _ := strconv.Atoi(values[1])
if int(math.Abs(float64(imageRandX-x))) <= s.Tolerance {
redis_client.SETNX(redisConn, s.mkey(id), "1", s.Expiration)
return true
}
return false

}

func (s *SlideImage) CreateCode() (string, int, int) {
nums := rand.Intn(s.SampleImages)
imageId := uuid.New().String()

//获取随机x坐标
imageRandX := rand.Intn(s.StdWidth - s.MaskSize - 3)
if imageRandX < s.MinImageX {
imageRandX += s.MinImageX
}
redis_client.Setex(s.key(imageId), strconv.Itoa(nums)+"-"+strconv.Itoa(imageRandX), time.Second*time.Duration(s.Expiration))
return imageId, nums, imageRandX
}

func SlideImager(options ...Options) macaron.Handler {
return func(ctx *macaron.Context) {
slideImage := NewSlideImage(prepareOptions(options))

if strings.HasPrefix(ctx.Req.URL.Path, slideImage.URLPrefix) {
id := path.Base(ctx.Req.URL.Path)
if i := strings.Index(id, "."); i > -1 {
id = id[:i]
}
isScreenshot := strings.HasSuffix(id, "screenshot")
if isScreenshot {
id = strings.TrimSuffix(id, "screenshot")
}

key := slideImage.key(id)
v, err := redis_client.Get(key)

if err != nil || v == "" {
ctx.Status(404)
ctx.Write([]byte("not found"))
//png.Encode(ctx.Resp, *setting.SlideImagesBg[0])
return
}

values := strings.Split(v, "-")

imageIndex, _ := strconv.Atoi(values[0])
imageRandX, _ := strconv.Atoi(values[1])
imageBg := setting.SlideImagesBg[imageIndex]

maxPotion := image.Point{
X: imageRandX + slideImage.MaskSize,
Y: slideImage.ImageY + slideImage.MaskSize,
}
minPotion := image.Point{
X: imageRandX,
Y: slideImage.ImageY,
}
subimg := image.Rectangle{
Max: maxPotion,
Min: minPotion,
}

if isScreenshot {
data := imaging.Crop(*imageBg, subimg)
png.Encode(ctx.Resp, data)

} else {
data := imaging.Overlay(*imageBg, *setting.SlideMaskImage, minPotion, 1.0)
png.Encode(ctx.Resp, data)
}
ctx.Status(200)
return

}
ctx.Data["SlideImageInfo"] = slideImage
ctx.Data["EnablePhone"] = setting.PhoneService.Enabled
ctx.Map(slideImage)

}
}

func InitSlideImage() {
if setting.PhoneService.Enabled {

filenames, err := public.Dir(path.Join("img", "slide", "bg"))
if err != nil {
panic("Slide Image Service init failed")
}
maskFileName, err := public.Dir(path.Join("img", "slide", "mask"))
if err != nil {
panic("Slide Image Service init failed")
}

for _, filename := range filenames {
if strings.HasSuffix(filename, ".png") {
content, err := public.Asset(path.Join("img", "slide", "bg", filename))

if err != nil {
log.Warn("can not open "+filename, err)
continue
}

m, err := png.Decode(bytes.NewReader(content))

if err != nil {
log.Warn("can not decode "+filename, err)
continue
}
setting.SlideImagesBg = append(setting.SlideImagesBg, &m)
}

}
setting.SlideImagesCount = len(setting.SlideImagesBg)
if setting.SlideImagesCount == 0 {
panic("Slide Image Service init failed")
}

maskContent, err := public.Asset(path.Join("img", "slide", "mask", maskFileName[0]))

if err != nil {
panic("Slide Image Service init failed")
}

MaskImage, err := png.Decode(bytes.NewReader(maskContent))
if err != nil {
panic("Slide Image Service init failed")
}
setting.SlideMaskImage = &MaskImage

log.Info("Slide Image Service Enabled")

}
}

+ 43
- 0
modules/storage/minio_ext.go View File

@@ -217,6 +217,49 @@ func GetOneLevelAllObjectUnderDirMinio(bucket string, prefixRootPath string, rel

}

func MinioGetFilesSize(bucketName string, Files []string) int64 {
_, core, err := getClients()
var fileTotalSize int64
fileTotalSize = 0
if err != nil {
log.Error("getClients failed:", err.Error())
return fileTotalSize
}
for _, file := range Files {
log.Info("file=" + file)
meta, err := core.StatObject(bucketName, file, miniov6.StatObjectOptions{})
if err != nil {
log.Info("Get file error:" + err.Error())
}
fileTotalSize += meta.Size
}
return fileTotalSize
}

func MinioCopyFiles(bucketName string, srcPath string, destPath string, Files []string) (int64, error) {
_, core, err := getClients()
var fileTotalSize int64
fileTotalSize = 0
if err != nil {
log.Error("getClients failed:", err.Error())
return fileTotalSize, err
}

for _, file := range Files {
srcObjectName := srcPath + file
destObjectName := destPath + file
log.Info("srcObjectName=" + srcObjectName + " destObjectName=" + destObjectName)
meta, err := core.StatObject(bucketName, srcObjectName, miniov6.StatObjectOptions{})
if err != nil {
log.Info("Get file error:" + err.Error())
}
core.CopyObject(bucketName, srcObjectName, bucketName, destObjectName, meta.UserMetadata)
fileTotalSize += meta.Size
}

return fileTotalSize, nil
}

func MinioPathCopy(bucketName string, srcPath string, destPath string) (int64, error) {
_, core, err := getClients()
var fileTotalSize int64


+ 80
- 8
modules/storage/obs.go View File

@@ -264,7 +264,47 @@ func ObsModelDownload(JobName string, fileName string) (io.ReadCloser, error) {
}
}

func ObsCopyManyFile(srcBucket string, srcPath string, destBucket string, destPath string) (int64, error) {
func ObsGetFilesSize(srcBucket string, Files []string) int64 {
var fileTotalSize int64
for _, file := range Files {
log.Info("file=" + file)
out, err := ObsCli.GetObjectMetadata(&obs.GetObjectMetadataInput{
Bucket: srcBucket,
Key: file,
})
if err != nil {
log.Info("Get File error, error=" + err.Error())
continue
}
fileTotalSize += out.ContentLength
}
return fileTotalSize
}

func ObsCopyManyFile(srcBucket string, srcPath string, destBucket string, destPath string, Files []string) (int64, error) {

var fileTotalSize int64

for _, file := range Files {
srcKey := srcPath + file
destKey := destPath + file
log.Info("srcKey=" + srcKey + " destKey=" + destKey)
out, err := ObsCli.GetObjectMetadata(&obs.GetObjectMetadataInput{
Bucket: srcBucket,
Key: srcKey,
})
if err != nil {
log.Info("Get File error, error=" + err.Error())
continue
}
obsCopyFile(srcBucket, srcKey, destBucket, destKey)
fileTotalSize += out.ContentLength
}

return fileTotalSize, nil
}

func ObsCopyAllFile(srcBucket string, srcPath string, destBucket string, destPath string) (int64, error) {
input := &obs.ListObjectsInput{}
input.Bucket = srcBucket
// 设置每页100个对象
@@ -330,6 +370,7 @@ func GetOneLevelAllObjectUnderDir(bucket string, prefixRootPath string, relative
output, err := ObsCli.ListObjects(input)
fileInfos := make([]FileInfo, 0)
prefixLen := len(input.Prefix)
fileMap := make(map[string]bool, 0)
if err == nil {
for _, val := range output.Contents {
log.Info("val key=" + val.Key)
@@ -338,23 +379,51 @@ func GetOneLevelAllObjectUnderDir(bucket string, prefixRootPath string, relative
if val.Key == input.Prefix {
continue
}
if strings.Contains(val.Key[prefixLen:len(val.Key)-1], "/") {
fileName = val.Key[prefixLen:]
log.Info("fileName =" + fileName)
files := strings.Split(fileName, "/")
if fileMap[files[0]] {
continue
} else {
fileMap[files[0]] = true
}
if strings.HasSuffix(val.Key, "/") {
ParenDir := relativePath
fileName = files[0]
if len(files) > 1 {
isDir = true
fileName = val.Key[prefixLen : len(val.Key)-1]
relativePath += val.Key[prefixLen:]
ParenDir += fileName + "/"
} else {
isDir = false
fileName = val.Key[prefixLen:]
}

// if strings.Contains(val.Key[prefixLen:len(val.Key)-1], "/") {

// files := strings.Split(fileName, "/")
// fileName = files[0]
// isDir = true
// if fileMap[files[0]] {
// continue
// } else {
// fileMap[files[0]] = true
// }
// } else {
// if strings.HasSuffix(val.Key, "/") {
// isDir = true
// fileName = val.Key[prefixLen : len(val.Key)-1]
// relativePath += val.Key[prefixLen:]
// } else {
// isDir = false
// fileName = val.Key[prefixLen:]
// }
// fileMap[fileName] = true
// }

fileInfo := FileInfo{
ModTime: val.LastModified.Local().Format("2006-01-02 15:04:05"),
FileName: fileName,
Size: val.Size,
IsDir: isDir,
ParenDir: relativePath,
ParenDir: ParenDir,
}
fileInfos = append(fileInfos, fileInfo)
}
@@ -424,6 +493,7 @@ func GetObsListObject(jobName, outPutPath, parentDir, versionName string) ([]Fil
input := &obs.ListObjectsInput{}
input.Bucket = setting.Bucket
input.Prefix = strings.TrimPrefix(path.Join(setting.TrainJobModelPath, jobName, outPutPath, versionName, parentDir), "/")
log.Info("bucket=" + input.Bucket + " Prefix=" + input.Prefix)
strPrefix := strings.Split(input.Prefix, "/")
output, err := ObsCli.ListObjects(input)
fileInfos := make([]FileInfo, 0)
@@ -575,6 +645,8 @@ func GetObsLogFileName(prefix string) (string, error) {
log.Error("PutObject failed:", err.Error())
return "", err
}

if output == nil || len(output.Contents) == 0 {
return "", errors.New("obs log files not exist")
}
return output.Contents[0].Key, nil
}

+ 13
- 0
modules/templates/helper.go View File

@@ -18,6 +18,7 @@ import (
"path/filepath"
"regexp"
"runtime"
"strconv"
"strings"
texttmpl "text/template"
"time"
@@ -327,6 +328,7 @@ func NewFuncMap() []template.FuncMap {
},
"GetRefType": GetRefType,
"GetRefName": GetRefName,
"MB2GB": MB2GB,
}}
}

@@ -785,3 +787,14 @@ func GetRefName(ref string) string {
reg := regexp.MustCompile(REF_TYPE_PATTERN)
return reg.ReplaceAllString(ref, "")
}

func MB2GB(size int64) string {
s := strconv.FormatFloat(float64(size)/float64(1024), 'f', 2, 64)
for strings.HasSuffix(s, "0") {
s = strings.TrimSuffix(s, "0")
}
if strings.HasSuffix(s, ".") {
s = strings.TrimSuffix(s, ".")
}
return s
}

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

@@ -32,6 +32,8 @@ captcha = CAPTCHA
twofa = Two-Factor Authentication
twofa_scratch = Two-Factor Scratch Code
passcode = Passcode
use_and_privacy_agree = I have read and agree to the <a target="_blank" href="%s">use agreement</a> and <a target="_blank" href="%s">privacy agreement</a>
sign_up_agree_tips = should agree to the use agreement and privacy agreement!

u2f_insert_key = Insert your security key
u2f_sign_in = Press the button on your security key. If your security key has no button, re-insert it.
@@ -197,6 +199,7 @@ no_reply_address_helper = Domain name for users with a hidden email address. For

[home]
uname_holder = Username or Email Address
login_uname_holder=Username/Email/Phone number
uname_holder_cloud_brain = cloudbrain username
password_holder = Password
switch_dashboard_context = Switch Dashboard Context
@@ -335,11 +338,16 @@ resent_limit_prompt = You have already requested an activation email recently. P
has_unconfirmed_mail = Hi %s, you have an unconfirmed email address (<b>%s</b>). If you haven't received a confirmation email or need to resend a new one, please click on the button below.
resend_mail = Click here to resend your activation email
email_not_associate = The email address is not associated with any account.
email_not_main=The email address is wrong, please input your primary email address.
email_not_right=The email address is not associated with any account, please input the right email address.
send_reset_mail = Send Account Recovery Email
please_enter_main_email=Please enter your primary email
please_enter_main_email_tips=Tips: Only the primary email can receive the recovery email.
reset_password = Account Recovery
invalid_code = Your confirmation code is invalid or has expired.
reset_password_helper = Recover Account
reset_password_wrong_user = You are signed in as %s, but the account recovery link is for %s
reset_password_wrong_user_phone=You are signed in, but the phone number is used by other user.
password_too_short = Password length cannot be less than %d characters.
non_local_account = Non-local users can not update their password through the openi web interface.
verify = Verify
@@ -374,6 +382,37 @@ authorization_failed = Authorization failed
authorization_failed_desc = The authorization failed because we detected an invalid request. Please contact the maintainer of the app you've tried to authorize.
disable_forgot_password_mail = Account recovery is disabled. Please contact your site administrator.
sspi_auth_failed = SSPI authentication failed
[phone]
format_err=The format of phone number is wrong.
query_err=Fail to query phone number, please try again later.
already_register=The phone number is already used.
not_register=The phone number is wrong.
not_modify=The phone number is not updated.
max_times=One phone number can not send verification code more than %s times a day.
too_fast=Send too frequently, please try again later.
manual_first=Please slide to finish the jigsaw first.
verify_code_fail=Please input right verification code.
bind_phone=Please Bind Your Phone.
bind_phone_fail=Fail to bind phone number, please try again later.
phone_number=Phone number
drag_the_slider_to_fill_the_puzzle=Drag the slider to the right to fill the puzzle
mobile_phone_verification_code=Phone verification code
please_enter_SMS_verification_code=Please enter SMS verification code
get_verification_code=Get verification code
new_login_password=New login password
please_enter_new_password=Please enter new password
second_resend=S resend
please_bind_your_mobile_number=Please Bind Your Phone Number
submit=Submit
please_enter_the_correct_mobile_number=Please enter the correct phone number
please_enter_the_correct_mobile_phone_verification_code=Please enter the correct phone verification code
email_retrieve_password=Email retrieve password
mobile_number_retrieve_password=Phone number retrieve password
mobile_login=Mobile login
account_password_login=Account password login
cloud_brain_user_login=Cloud brain user login
modify_phone_number=Modify phone number


[mail]
activate_account = Please activate your account
@@ -520,6 +559,7 @@ static.CollectImage=Collect Image Count
static.CollectedImage=Collected Image Count
static.RecommendImage=Recommended Image Count
static.email=Email
static.phone=Phone
static.location=Location
static.all=All
static.public.user_business_analysis_current_month=Current_Month
@@ -997,7 +1037,9 @@ image_delete_fail=Failed to delete image, please try again later.
image_overwrite=You had submitted the same name image before, are you sure to overwrite the original image?
download=Download
score=Score

wait_count_start = There are currently
wait_count_end = tasks queued
file_limit_100 = Display up to 100 files or folders in a single directory
images.name = Image Tag
images.name_placerholder = Please enter the image name
image.label_tooltips = Example Python 3.7, Tensorflow 2.0, cuda 10, pytorch 1.6
@@ -1176,6 +1218,14 @@ model.manage.Recall = Recall
model.manage.sava_model = Sava Model
model.manage.model_manage = ModelManage
model.manage.model_accuracy = Model Accuracy
model.convert=Model Transformation
model.list=Model List
model.manage.create_new_convert_task=Create Model Transformation Task

modelconvert.manage.create_error1=A model transformation task with the same name already exists.
modelconvert.manage.create_error2=Only one running model transformation task can be created.
modelconvert.manage.model_not_exist=The model does not exist.
modelconvert.manage.no_operate_right=No operation permission.

grampus.train_job.ai_center = AI Center
grampus.dataset_path_rule = The code is storaged in /cache/code;the dataset is storaged in /cache/dataset;and please put your model into /cache/output, then you can download it online。
@@ -2501,6 +2551,7 @@ users.new_account = Create User Account
users.name = Username
users.full_name = Full Name
users.activated = Activated
users.bind_phone = Bind Phone
users.admin = Admin
users.restricted = Restricted
users.repos = Repos
@@ -3080,4 +3131,10 @@ INFERENCE = INFERENCE
BENCHMARK = BENCHMARK
brain_area = Brain Area

error.dataset_select = dataset select error:the count exceed the limit or has same name
Delete_failed=Fail to delete the job, please try again later.
Not_Stopped=The job is not stopped, can not be deleted.
Already_stopped=The job is already stopped.
Stopped_failed=Fail to stop the job, please try again later.
Stopped_success_update_status_fail=Succeed in stopping th job, but failed to update the job status and duration time.

error.dataset_select = dataset select error:the count exceed the limit or has same name

+ 61
- 4
options/locale/locale_zh-CN.ini View File

@@ -32,6 +32,8 @@ captcha=验证码
twofa=两步验证
twofa_scratch=两步验证口令
passcode=验证码
use_and_privacy_agree = 我已阅读并同意 <a target="_blank" href="%s">使用协议</a> 和 <a target="_blank" href="%s">隐私协议</a>
sign_up_agree_tips = 注册时需同意使用协议和隐私协议

u2f_insert_key=插入安全密钥
u2f_sign_in=按下安全密钥上的按钮。如果安全密钥没有按钮,请重新插入。
@@ -198,6 +200,7 @@ no_reply_address_helper=具有隐藏电子邮件地址的用户的域名。例

[home]
uname_holder=登录名或电子邮箱地址
login_uname_holder=用户名/邮箱/手机号
uname_holder_cloud_brain=云脑登录名
password_holder=密码
switch_dashboard_context=切换控制面板用户
@@ -339,11 +342,16 @@ resent_limit_prompt=您请求发送激活邮件过于频繁,请等待 3 分钟
has_unconfirmed_mail=%s 您好,系统检测到您有一封发送至 <b>%s</b> 但未被确认的邮件。如果您未收到激活邮件,或需要重新发送,请单击下方的按钮。
resend_mail=单击此处重新发送确认邮件
email_not_associate=您输入的邮箱地址未被关联到任何帐号!
send_reset_mail=发送账户恢复邮件
email_not_main=电子邮箱地址不正确,请输入您设置的主要邮箱地址。
email_not_right=您输入了不存在的邮箱地址,请输入正确的邮箱地址。
send_reset_mail=发送密码找回邮件
please_enter_main_email=请输入接收通知提醒的主要邮箱地址
please_enter_main_email_tips=说明:如果您设置了多个邮箱地址,只有主要邮箱可以收到密码找回邮件,其他邮箱无法收到。
reset_password=账户恢复
invalid_code=此确认密钥无效或已过期。
reset_password_helper=恢复账户
reset_password_wrong_user=您已作为 %s 登录,无法使用链接恢复 %s 的账户。
reset_password_wrong_user_phone=您已登录,不能用别的账号的手机恢复。
password_too_short=密码长度不能少于 %d 位。
non_local_account=非本地帐户不能通过 openi 的 web 界面更改密码。
verify=验证
@@ -378,6 +386,37 @@ authorization_failed=授权失败
authorization_failed_desc=授权失败,这是一个无效的请求。请联系尝试授权应用的管理员。
disable_forgot_password_mail = Account recovery is disabled. Please contact your site administrator.
sspi_auth_failed=SSPI 认证失败
[phone]
format_err=手机号格式错误。
query_err=查询手机号失败,请稍后再试。
already_register=手机号已被使用。
not_register=手机号输入错误。
not_modify=手机号未修改。
max_times=一个手机号每天发送验证码次数不能超过%s次。
too_fast=验证码发送太频繁,请稍后再试。
manual_first=请先拖动滑块填充拼图。
verify_code_fail=请输入正确的短信验证码。
bind_phone=请绑定手机号。
bind_phone_fail=绑定手机号失败,请稍后再试。
phone_number=手机号码
drag_the_slider_to_fill_the_puzzle=向右拖动滑块填充拼图
mobile_phone_verification_code=手机验证码
please_enter_SMS_verification_code=请输入短信验证码
get_verification_code=获取验证码
new_login_password=新的登录密码
please_enter_new_password=请输入新的密码
second_resend=S后重发
please_bind_your_mobile_number=请绑定手机号
submit=提交
please_enter_the_correct_mobile_number=请输入正确的手机号
please_enter_the_correct_mobile_phone_verification_code=请输入正确格式的手机验证码
email_retrieve_password=邮箱找回密码
mobile_number_retrieve_password=手机号找回密码
mobile_login=手机登录
account_password_login=账号密码登录
cloud_brain_user_login=云脑1用户登录
modify_phone_number=修改手机号


[mail]
activate_account=请激活您的帐户
@@ -525,6 +564,7 @@ static.CollectImage=收藏镜像数
static.CollectedImage=被收藏镜像数
static.RecommendImage=被推荐镜像数
static.email=Email
static.phone=电话
static.location=所在地区
static.all=所有
static.public.user_business_analysis_current_month=本月
@@ -997,8 +1037,9 @@ image_delete_fail=删除镜像失败,请稍后再试。
image_overwrite=您已经提交过相同名称的镜像,您确定要覆盖原来提交的镜像吗?
download=模型下载
score=评分


wait_count_start = 当前有
wait_count_end = 个任务正在排队
file_limit_100 = 单目录下最多显示100个文件或文件夹
images.name = 镜像Tag
images.name_placerholder = 请输入镜像Tag
image.label_tooltips = 如Python 3.7, Tensorflow 2.0, cuda 10, pytorch 1.6
@@ -1189,6 +1230,14 @@ model.manage.Recall = 召回率
model.manage.sava_model = 保存模型
model.manage.model_manage = 模型管理
model.manage.model_accuracy = 模型精度
model.convert=模型转换任务
model.list=模型列表
model.manage.create_new_convert_task=创建模型转换任务

modelconvert.manage.create_error1=相同的名称模型转换任务已经存在。
modelconvert.manage.create_error2=只能创建一个正在运行的模型转换任务。
modelconvert.manage.model_not_exist=选择的模型不存在。
modelconvert.manage.no_operate_right=无操作权限。

grampus.train_job.ai_center=智算中心
grampus.dataset_path_rule = 训练脚本存储在/cache/code中,数据集存储在/cache/dataset中,训练输出请存储在/cache/output中以供后续下载。
@@ -2516,6 +2565,7 @@ users.new_account=创建新帐户
users.name=用户名
users.full_name=全名
users.activated=已激活
users.bind_phone=手机验证
users.admin=管理员
users.restricted=受限
users.repos=项目数
@@ -3095,4 +3145,11 @@ INFERENCE = 推理任务
BENCHMARK = 评测任务
brain_area = 脑区

error.dataset_select = 数据集选择错误:数量超过限制或者有同名数据集
Delete_failed=任务删除失败,请稍后再试。
Not_Stopped=任务还未终止,不能删除。
Already_stopped=任务已停止。
Stopped_failed=任务停止失败,请稍后再试。
Stopped_success_update_status_fail=任务停止成功,状态及运行时间更新失败。


error.dataset_select = 数据集选择错误:数量超过限制或者有同名数据集

+ 0
- 24
public/home/home.js View File

@@ -119,7 +119,6 @@ document.onreadystatechange = function () {
continue;
}
}
refresh3DInfo(record);
var recordPrefix = getMsg(record);
if(record.OpType == "6" || record.OpType == "10" || record.OpType == "12" || record.OpType == "13"){
html += recordPrefix + actionName;
@@ -208,29 +207,6 @@ function getTaskLink(record){
return re;
}

function refresh3DInfo(record){
if(record.OpType == "25" || record.OpType == "29" || record.OpType == "31"){
//cloudbrain one
var lines = $('.rotation3D__line');
var span = $('.rotation3D__line').find("span")[0];
//console.log(span);
span.innerText =record.RefName;
//$('.rotation3D__line').find("span").eq(0).text(record.RefName)
//console.log("cloudbrain one line length=" + lines.length);
//lines[0].find("span").text(record.RefName);
}else if(record.OpType == "26" || record.OpType == "27" || record.OpType == "28"){
//cloudbrain two
var lines = $('.rotation3D__line');
//console.log("cloudbrain two line length=" + lines.length);
var span = $('.rotation3D__line').find("span")[1];
//console.log(span);
if(span != null){
span.innerText =record.RefName;
}
}

}

function getMsg(record){
var html ="";
html += "<div class=\"swiper-slide item\">";


BIN
public/img/slide/bg/1.png View File

Before After
Width: 391  |  Height: 196  |  Size: 172 kB

BIN
public/img/slide/bg/2.png View File

Before After
Width: 391  |  Height: 196  |  Size: 116 kB

BIN
public/img/slide/bg/3.png View File

Before After
Width: 391  |  Height: 196  |  Size: 164 kB

BIN
public/img/slide/bg/4.png View File

Before After
Width: 391  |  Height: 196  |  Size: 163 kB

BIN
public/img/slide/mask/mask.png View File

Before After
Width: 51  |  Height: 51  |  Size: 199 B

+ 386
- 0
public/self/ztree/css/awesomeStyle/awesome.css View File

@@ -0,0 +1,386 @@
/*-------------------------------------
zTree Style using fontawesome instead of images

version: 1.1
author: Mike King
email: mikkelking @ hotmail . com
website: http://code.google.com/p/jquerytree/

-------------------------------------*/
/* Definitions ----------------------*/
/* End of Definitions ---------------*/
/* Imports -------------------------*/
/* End of Imports ------------------*/
.ztree * {
padding: 0;
margin: 0;
font-size: 12px;
font-family: Verdana, Arial, Helvetica, AppleGothic, sans-serif;
background-color: #af0000;
}
.ztree {
margin: 0;
padding: 5px;
color: #ffffff;
background-color: #af0000;
}
.ztree li {
padding: 0;
margin: 0;
list-style: none;
line-height: 17px;
text-align: left;
white-space: nowrap;
outline: 0;
}
.ztree li ul {
margin: 0px;
padding: 0 0 0 18px;
}
.ztree li a {
padding-right: 3px;
margin: 0;
cursor: pointer;
height: 17px;
color: #ffffff;
background-color: transparent;
text-decoration: none;
vertical-align: top;
display: inline-block;
}
.ztree li a input.rename {
height: 14px;
width: 80px;
padding: 0;
margin: 0;
color: #af0000;
background-color: #ffffff;
font-size: 12px;
border: 1px #585956 solid;
*border: 0px;
}
.ztree li a:hover {
text-decoration: underline;
}
.ztree li a.curSelectedNode {
padding-top: 0px;
background-color: #af4040;
color: #ffff00;
height: 17px;
opacity: 0.8;
}
.ztree li a.curSelectedNode_Edit {
padding-top: 0px;
background-color: transparent;
color: #ffff00;
height: 17px;
border: 1px #666 solid;
opacity: 0.8;
}
.ztree li a.tmpTargetNode_inner {
padding-top: 0px;
background-color: #aaa;
color: #ffff00;
height: 17px;
border: 1px #666 solid;
opacity: 0.8;
filter: alpha(opacity=80);
}
.ztree li span {
line-height: 17px;
margin-right: 2px;
background-color: transparent;
}
.ztree li span.button {
line-height: 0;
margin: 0;
padding: 0;
width: 15px;
height: 17px;
display: inline-block;
vertical-align: top;
border: 0px solid;
cursor: pointer;
outline: none;
background-color: transparent;
background-repeat: no-repeat;
background-attachment: scroll;
}
.ztree li span.button::before {
color: #ffffff;
font-family: FontAwesome;
padding-top: 10px;
}
.ztree li span.button.chk {
margin: 0px;
cursor: auto;
width: 12px;
display: inline-block;
padding-top: 10px;
padding-left: 2px;
}
.ztree li span.button.chk.checkbox_false_full::before {
content: "\f096";
}
.ztree li span.button.chk.checkbox_false_full_focus::before {
content: "\f096";
color: #ffff00;
}
.ztree li span.button.chk.checkbox_false_part::before {
content: "\f096";
color: #aaaaaa;
}
.ztree li span.button.chk.checkbox_false_part_focus::before {
content: "\f096";
color: #cad96c;
}
.ztree li span.button.chk.checkbox_false_disable::before {
content: "\f096";
color: #808080;
}
.ztree li span.button.chk.checkbox_true_full::before {
content: "\f046";
}
.ztree li span.button.chk.checkbox_true_full_focus::before {
content: "\f046";
}
.ztree li span.button.chk.checkbox_true_part::before {
content: "\f14a";
}
.ztree li span.button.chk.checkbox_true_part_focus::before {
content: "\f14a";
color: #ffff00;
}
.ztree li span.button.chk.checkbox_true_full_focus::before {
content: "\f046";
color: #ffff00;
}
.ztree li span.button.chk.checkbox_true_part::before {
content: "\f046";
color: #aaaaaa;
}
.ztree li span.button.chk.checkbox_true_part_focus::before {
content: "\f046";
color: #cad96c;
}
.ztree li span.button.chk.checkbox_true_disable::before {
content: "\f046";
color: #808080;
}
.ztree li span.button.chk.radio_false_full::before {
content: "\f10c";
}
.ztree li span.button.chk.radio_false_full_focus::before {
content: "\f10c";
color: #ffff00;
}
.ztree li span.button.chk.radio_false_part::before {
content: "\f10c";
color: #aaaaaa;
}
.ztree li span.button.chk.radio_false_part_focus::before {
content: "\f10c";
color: #ffff00;
}
.ztree li span.button.chk.radio_false_disable::before {
content: "\f1db";
color: #808080;
}
.ztree li span.button.chk.radio_true_full::before {
content: "\f192";
}
.ztree li span.button.chk.radio_true_full_focus::before {
content: "\f192";
color: #ffff00;
}
.ztree li span.button.chk.radio_true_part::before {
content: "\f192";
color: #aaaaaa;
}
.ztree li span.button.chk.radio_true_part_focus::before {
content: "\f192";
color: #aaaaaa;
}
.ztree li span.button.chk.radio_true_disable::before {
content: "\f1db";
color: #808080;
}
.ztree li span.button.switch {
width: 15px;
height: 17px;
}
.ztree li span.button.root_open::before {
content: "\f078";
padding-top: 10px;
padding-left: 2px;
display: inline-block;
}
.ztree li span.button.root_close::before {
content: "\f054";
padding-top: 10px;
padding-left: 2px;
display: inline-block;
}
.ztree li span.button.roots_open::before {
content: "\f078";
padding-top: 10px;
padding-left: 2px;
display: inline-block;
}
.ztree li span.button.roots_close::before {
content: "\f054";
padding-top: 10px;
padding-left: 2px;
display: inline-block;
}
.ztree li span.button.center_open::before {
content: "\f078";
padding-top: 10px;
padding-left: 2px;
display: inline-block;
}
.ztree li span.button.center_close::before {
content: "\f054";
padding-top: 10px;
padding-left: 2px;
display: inline-block;
}
.ztree li span.button.bottom_open::before {
content: "\f078";
padding-top: 10px;
padding-left: 2px;
display: inline-block;
}
.ztree li span.button.bottom_close::before {
content: "\f054";
padding-top: 10px;
padding-left: 2px;
display: inline-block;
}
.ztree li span.button.root_docu {
background: none;
}
.ztree li span.button.roots_docu::before {
content: "\f022";
padding-left: 2px;
display: inline-block;
color: #ffffff;
}
.ztree li span.button.center_docu::before {
padding-top: 10px;
padding-left: 2px;
display: inline-block;
color: #ffffff;
}
.ztree li span.button.bottom_docu::before {
padding-top: 10px;
padding-left: 2px;
display: inline-block;
color: #ffffff;
}
.ztree li span.button.noline_docu {
background: none;
}
.ztree li span.button.ico_open::before {
content: "\f115";
font-family: FontAwesome;
padding-top: 10px;
padding-left: 2px;
display: inline-block;
color: #ffffff;
}
.ztree li span.button.ico_close::before {
content: "\f114";
font-family: FontAwesome;
padding-top: 10px;
padding-left: 2px;
display: inline-block;
color: #ffffff;
}
.ztree li span.button.ico_docu::before {
content: "\f022";
font-family: FontAwesome;
padding-top: 10px;
padding-left: 2px;
display: inline-block;
color: #ffffff;
}
.ztree li span.button.edit {
margin-left: 4px;
margin-right: -1px;
vertical-align: top;
*vertical-align: middle;
padding-top: 10px;
}
.ztree li span.button.edit::before {
content: "\f044";
font-family: FontAwesome;
}
.ztree li span.button.remove {
margin-left: 4px;
margin-right: -1px;
vertical-align: top;
*vertical-align: middle;
padding-top: 10px;
}
.ztree li span.button.remove::before {
content: "\f1f8";
font-family: FontAwesome;
}
.ztree li span.button.add {
margin-left: 4px;
margin-right: -1px;
vertical-align: top;
*vertical-align: middle;
padding-top: 10px;
}
.ztree li span.button.add::before {
content: "\f067";
font-family: FontAwesome;
}
.ztree li span.button.ico_loading {
margin-right: 2px;
background: url(./img/loading.gif) no-repeat scroll 0 0 transparent;
vertical-align: top;
*vertical-align: middle;
}
ul.tmpTargetzTree {
background-color: #FFE6B0;
opacity: 0.8;
filter: alpha(opacity=80);
}
span.tmpzTreeMove_arrow {
width: 16px;
height: 17px;
display: inline-block;
padding: 0;
margin: 2px 0 0 1px;
border: 0 none;
position: absolute;
background-color: transparent;
background-attachment: scroll;
}
span.tmpzTreeMove_arrow::before {
content: "\f04b";
font-family: FontAwesome;
color: #ffff00;
}
ul.ztree.zTreeDragUL {
margin: 0;
padding: 0;
position: absolute;
width: auto;
height: auto;
overflow: hidden;
background-color: #cfcfcf;
border: 1px #ffff00 dotted;
opacity: 0.8;
filter: alpha(opacity=80);
}
.ztreeMask {
z-index: 10000;
background-color: #cfcfcf;
opacity: 0.0;
filter: alpha(opacity=0);
position: absolute;
}

+ 146
- 0
public/self/ztree/css/awesomeStyle/awesome.less View File

@@ -0,0 +1,146 @@
/*-------------------------------------
zTree Style using fontawesome instead of images

version: 1.1
author: Mike King
email: mikkelking @ hotmail . com
website: http://code.google.com/p/jquerytree/

-------------------------------------*/

/* Definitions ----------------------*/
@font-size: 12px;
// Regular icon and text color is white, which suits any medium -> dark background
@color-normal: white;
// Background color
@color-bg: #af0000;
// Highlight color
@color-highlight: yellow;
// Partially selected (checkboxes, radio buttons)
@color-partial: #aaaaaa;
// Partially selected and focused (checkboxes, radio buttons)
@color-partfocus: #cad96c;
// Disabled altogether
@color-disabled: #808080;
// Editing color
@color-edit: yellow;
@w: 15px;
@h: 17px;
@pad-left: 2px;
@pad-top: 10px;
/* End of Definitions ---------------*/

/* Imports -------------------------*/
@import "fa.less";
/* End of Imports ------------------*/

.ztree * {padding:0; margin:0; font-size:@font-size; font-family: Verdana, Arial, Helvetica, AppleGothic, sans-serif; background-color: @color-bg;}
.ztree {
margin:0; padding:5px; color:@color-normal; background-color: @color-bg;
li {
padding:0; margin:0; list-style:none; line-height:17px; text-align:left; white-space:nowrap; outline:0;
ul {
margin: 0px; padding:0 0 0 18px;
}
ul.line { }
a {padding-right:3px; margin:0; cursor:pointer; height:@h; color:@color-normal; background-color: transparent;
text-decoration:none; vertical-align:top; display: inline-block;
input.rename {height:14px; width:80px; padding:0; margin:0;
color: @color-bg; background-color: @color-normal;
font-size:@font-size; border:1px #585956 solid; *border:0px}
}
a:hover {text-decoration:underline}
a.curSelectedNode {padding-top:0px; background-color:#af4040; color:@color-highlight; height:@h; opacity:0.8;}
a.curSelectedNode_Edit {padding-top:0px; background-color:transparent; color:@color-highlight; height:@h; border:1px #666 solid; opacity:0.8;}
a.tmpTargetNode_inner {padding-top:0px; background-color:#aaa; color:@color-highlight; height:@h; border:1px #666 solid;
opacity:0.8; filter:alpha(opacity=80)}
a.tmpTargetNode_prev {}
a.tmpTargetNode_next {}
span {line-height:@h; margin-right:2px; background-color:transparent;}
span.button {line-height:0; margin:0; padding: 0; width:@w; height:@h; display: inline-block; vertical-align:top;
border:0px solid; cursor: pointer;outline:none;
background-color:transparent; background-repeat:no-repeat; background-attachment: scroll;
&::before{color: @color-normal; font-family: FontAwesome; padding-top:@pad-top;}
&.chk { margin:0px; cursor: auto; width: 12px;
display: inline-block;padding-top:@pad-top;padding-left:@pad-left;
&.checkbox_false_full::before {content: @fa-square-o;}
&.checkbox_false_full_focus::before {content: @fa-square-o; color:@color-highlight;}
&.checkbox_false_part::before {content: @fa-square-o;color: @color-partial;}
&.checkbox_false_part_focus::before {content: @fa-square-o; color:@color-partfocus;}
&.checkbox_false_disable::before {content: @fa-square-o; color:@color-disabled;}
&.checkbox_true_full::before {content: @fa-check-square-o;}
&.checkbox_true_full_focus::before {content: @fa-check-square-o;}
&.checkbox_true_part::before {content: @fa-check-square;}
&.checkbox_true_part_focus::before {content: @fa-check-square; color: @color-highlight}
&.checkbox_true_full_focus::before {content: @fa-check-square-o; color: @color-highlight}
&.checkbox_true_part::before {content: @fa-check-square-o;color: @color-partial}
&.checkbox_true_part_focus::before {content: @fa-check-square-o;color: @color-partfocus;}
&.checkbox_true_disable::before {content: @fa-check-square-o;color: @color-disabled}
&.radio_false_full::before {content: @fa-circle-o;}
&.radio_false_full_focus::before {content: @fa-circle-o;color: @color-highlight}
&.radio_false_part::before {content: @fa-circle-o;color: @color-partial}
&.radio_false_part_focus::before {content: @fa-circle-o;color: @color-highlight}
&.radio_false_disable::before {content: @fa-circle-thin;color: @color-disabled}
&.radio_true_full::before {content: @fa-dot-circle-o;}
&.radio_true_full_focus::before {content: @fa-dot-circle-o;color: @color-highlight}
&.radio_true_part::before {content: @fa-dot-circle-o;color: @color-partial}
&.radio_true_part_focus::before {content: @fa-dot-circle-o;color: @color-partial;}
&.radio_true_disable::before {content: @fa-circle-thin;color: @color-disabled}
}
&.switch {width:@w; height:@h}
&.root_open::before{content: @fa-chevron-down;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;}
&.root_close::before{content: @fa-chevron-right;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;}
&.roots_open::before{content: @fa-chevron-down;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;}
&.roots_close::before{content: @fa-chevron-right;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;}
&.center_open::before{content: @fa-chevron-down;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;}
&.center_close::before{content: @fa-chevron-right;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;}
&.bottom_open::before{content: @fa-chevron-down;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;}
&.bottom_close::before{content: @fa-chevron-right;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;}
&.noline_open{}
&.noline_close{}
&.root_docu{ background:none;}
&.roots_docu::before{content: @fa-list-alt;padding-left:@pad-left;display: inline-block;color:@color-normal;}
&.center_docu::before{padding-top:@pad-top;padding-left:@pad-left;display: inline-block;color:@color-normal;}
&.bottom_docu::before{padding-top:@pad-top;padding-left:@pad-left;display: inline-block;color:@color-normal;}
&.noline_docu{ background:none;}
&.ico_open::before {content: @fa-folder-open-o;font-family: FontAwesome;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;color:@color-normal;}
&.ico_close::before {content: @fa-folder-o;font-family: FontAwesome;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;color:@color-normal;}
&.ico_docu::before{content: @fa-list-alt;font-family: FontAwesome;padding-top:@pad-top;padding-left:@pad-left;display: inline-block;color:@color-normal;}
&.edit {margin-left:4px; margin-right: -1px; vertical-align:top; *vertical-align:middle;padding-top:@pad-top;}
&.edit::before{content: @fa-pencil-square-o;font-family: FontAwesome;}
&.remove {margin-left:4px; margin-right: -1px; vertical-align:top; *vertical-align:middle;padding-top:@pad-top;}
&.remove::before{content: @fa-trash;font-family: FontAwesome;}

&.add {margin-left:4px; margin-right: -1px; vertical-align:top; *vertical-align:middle;padding-top:@pad-top;}
&.add::before{content: @fa-plus;font-family: FontAwesome;}

&.ico_loading{margin-right:2px; background:url(./img/loading.gif) no-repeat scroll 0 0 transparent; vertical-align:top; *vertical-align:middle}
}

}
}


ul.tmpTargetzTree {background-color:#FFE6B0; opacity:0.8; filter:alpha(opacity=80)}

// this is the arrow that moves
span.tmpzTreeMove_arrow{width:16px; height:@h; display: inline-block;
padding:0; margin:2px 0 0 1px; border:0 none; position:absolute;
background-color:transparent; background-attachment: scroll;
}
span.tmpzTreeMove_arrow::before{content: @fa-play;font-family: FontAwesome;color: @color-highlight;
}
// outline

ul.ztree.zTreeDragUL {margin:0; padding:0; position:absolute; width:auto; height:auto;overflow:hidden;
background-color:#cfcfcf; border:1px @color-highlight dotted; opacity:0.8; filter:alpha(opacity=80)}
.ztreeMask {z-index:10000; background-color:#cfcfcf; opacity:0.0; filter:alpha(opacity=0); position:absolute}


+ 480
- 0
public/self/ztree/css/awesomeStyle/fa.less View File

@@ -0,0 +1,480 @@
@fa-glass: "\f000";
@fa-music: "\f001";
@fa-search: "\f002";
@fa-envelope-o: "\f003";
@fa-heart: "\f004";
@fa-star: "\f005";
@fa-star-o: "\f006";
@fa-user: "\f007";
@fa-film: "\f008";
@fa-th-large: "\f009";
@fa-th: "\f00a";
@fa-th-list: "\f00b";
@fa-check: "\f00c";
@fa-times: "\f00d";
@fa-search-plus: "\f00e";
@fa-search-minus: "\f010";
@fa-power-off: "\f011";
@fa-signal: "\f012";
@fa-cog: "\f013";
@fa-trash-o: "\f014";
@fa-home: "\f015";
@fa-file-o: "\f016";
@fa-clock-o: "\f017";
@fa-road: "\f018";
@fa-download: "\f019";
@fa-arrow-circle-o-down: "\f01a";
@fa-arrow-circle-o-up: "\f01b";
@fa-inbox: "\f01c";
@fa-play-circle-o: "\f01d";
@fa-repeat: "\f01e";
@fa-refresh: "\f021";
@fa-list-alt: "\f022";
@fa-lock: "\f023";
@fa-flag: "\f024";
@fa-headphones: "\f025";
@fa-volume-off: "\f026";
@fa-volume-down: "\f027";
@fa-volume-up: "\f028";
@fa-qrcode: "\f029";
@fa-barcode: "\f02a";
@fa-tag: "\f02b";
@fa-tags: "\f02c";
@fa-book: "\f02d";
@fa-bookmark: "\f02e";
@fa-print: "\f02f";
@fa-camera: "\f030";
@fa-font: "\f031";
@fa-bold: "\f032";
@fa-italic: "\f033";
@fa-text-height: "\f034";
@fa-text-width: "\f035";
@fa-align-left: "\f036";
@fa-align-center: "\f037";
@fa-align-right: "\f038";
@fa-align-justify: "\f039";
@fa-list: "\f03a";
@fa-outdent: "\f03b";
@fa-indent: "\f03c";
@fa-video-camera: "\f03d";
@fa-picture-o: "\f03e";
@fa-pencil: "\f040";
@fa-map-marker: "\f041";
@fa-adjust: "\f042";
@fa-tint: "\f043";
@fa-pencil-square-o: "\f044";
@fa-share-square-o: "\f045";
@fa-check-square-o: "\f046";
@fa-arrows: "\f047";
@fa-step-backward: "\f048";
@fa-fast-backward: "\f049";
@fa-backward: "\f04a";
@fa-play: "\f04b";
@fa-pause: "\f04c";
@fa-stop: "\f04d";
@fa-forward: "\f04e";
@fa-fast-forward: "\f050";
@fa-step-forward: "\f051";
@fa-eject: "\f052";
@fa-chevron-left: "\f053";
@fa-chevron-right: "\f054";
@fa-plus-circle: "\f055";
@fa-minus-circle: "\f056";
@fa-times-circle: "\f057";
@fa-check-circle: "\f058";
@fa-question-circle: "\f059";
@fa-info-circle: "\f05a";
@fa-crosshairs: "\f05b";
@fa-times-circle-o: "\f05c";
@fa-check-circle-o: "\f05d";
@fa-ban: "\f05e";
@fa-arrow-left: "\f060";
@fa-arrow-right: "\f061";
@fa-arrow-up: "\f062";
@fa-arrow-down: "\f063";
@fa-share: "\f064";
@fa-expand: "\f065";
@fa-compress: "\f066";
@fa-plus: "\f067";
@fa-minus: "\f068";
@fa-asterisk: "\f069";
@fa-exclamation-circle: "\f06a";
@fa-gift: "\f06b";
@fa-leaf: "\f06c";
@fa-fire: "\f06d";
@fa-eye: "\f06e";
@fa-eye-slash: "\f070";
@fa-exclamation-triangle: "\f071";
@fa-plane: "\f072";
@fa-calendar: "\f073";
@fa-random: "\f074";
@fa-comment: "\f075";
@fa-magnet: "\f076";
@fa-chevron-up: "\f077";
@fa-chevron-down: "\f078";
@fa-retweet: "\f079";
@fa-shopping-cart: "\f07a";
@fa-folder: "\f07b";
@fa-folder-open: "\f07c";
@fa-arrows-v: "\f07d";
@fa-arrows-h: "\f07e";
@fa-bar-chart: "\f080";
@fa-twitter-square: "\f081";
@fa-facebook-square: "\f082";
@fa-camera-retro: "\f083";
@fa-key: "\f084";
@fa-cogs: "\f085";
@fa-comments: "\f086";
@fa-thumbs-o-up: "\f087";
@fa-thumbs-o-down: "\f088";
@fa-star-half: "\f089";
@fa-heart-o: "\f08a";
@fa-sign-out: "\f08b";
@fa-linkedin-square: "\f08c";
@fa-thumb-tack: "\f08d";
@fa-external-link: "\f08e";
@fa-sign-in: "\f090";
@fa-trophy: "\f091";
@fa-github-square: "\f092";
@fa-upload: "\f093";
@fa-lemon-o: "\f094";
@fa-phone: "\f095";
@fa-square-o: "\f096";
@fa-bookmark-o: "\f097";
@fa-phone-square: "\f098";
@fa-twitter: "\f099";
@fa-facebook: "\f09a";
@fa-github: "\f09b";
@fa-unlock: "\f09c";
@fa-credit-card: "\f09d";
@fa-rss: "\f09e";
@fa-hdd-o: "\f0a0";
@fa-bullhorn: "\f0a1";
@fa-bell: "\f0f3";
@fa-certificate: "\f0a3";
@fa-hand-o-right: "\f0a4";
@fa-hand-o-left: "\f0a5";
@fa-hand-o-up: "\f0a6";
@fa-hand-o-down: "\f0a7";
@fa-arrow-circle-left: "\f0a8";
@fa-arrow-circle-right: "\f0a9";
@fa-arrow-circle-up: "\f0aa";
@fa-arrow-circle-down: "\f0ab";
@fa-globe: "\f0ac";
@fa-wrench: "\f0ad";
@fa-tasks: "\f0ae";
@fa-filter: "\f0b0";
@fa-briefcase: "\f0b1";
@fa-arrows-alt: "\f0b2";
@fa-users: "\f0c0";
@fa-link: "\f0c1";
@fa-cloud: "\f0c2";
@fa-flask: "\f0c3";
@fa-scissors: "\f0c4";
@fa-files-o: "\f0c5";
@fa-paperclip: "\f0c6";
@fa-floppy-o: "\f0c7";
@fa-square: "\f0c8";
@fa-bars: "\f0c9";
@fa-list-ul: "\f0ca";
@fa-list-ol: "\f0cb";
@fa-strikethrough: "\f0cc";
@fa-underline: "\f0cd";
@fa-table: "\f0ce";
@fa-magic: "\f0d0";
@fa-truck: "\f0d1";
@fa-pinterest: "\f0d2";
@fa-pinterest-square: "\f0d3";
@fa-google-plus-square: "\f0d4";
@fa-google-plus: "\f0d5";
@fa-money: "\f0d6";
@fa-caret-down: "\f0d7";
@fa-caret-up: "\f0d8";
@fa-caret-left: "\f0d9";
@fa-caret-right: "\f0da";
@fa-columns: "\f0db";
@fa-sort: "\f0dc";
@fa-sort-desc: "\f0dd";
@fa-sort-asc: "\f0de";
@fa-envelope: "\f0e0";
@fa-linkedin: "\f0e1";
@fa-undo: "\f0e2";
@fa-gavel: "\f0e3";
@fa-tachometer: "\f0e4";
@fa-comment-o: "\f0e5";
@fa-comments-o: "\f0e6";
@fa-bolt: "\f0e7";
@fa-sitemap: "\f0e8";
@fa-umbrella: "\f0e9";
@fa-clipboard: "\f0ea";
@fa-lightbulb-o: "\f0eb";
@fa-exchange: "\f0ec";
@fa-cloud-download: "\f0ed";
@fa-cloud-upload: "\f0ee";
@fa-user-md: "\f0f0";
@fa-stethoscope: "\f0f1";
@fa-suitcase: "\f0f2";
@fa-bell-o: "\f0a2";
@fa-coffee: "\f0f4";
@fa-cutlery: "\f0f5";
@fa-file-text-o: "\f0f6";
@fa-building-o: "\f0f7";
@fa-hospital-o: "\f0f8";
@fa-ambulance: "\f0f9";
@fa-medkit: "\f0fa";
@fa-fighter-jet: "\f0fb";
@fa-beer: "\f0fc";
@fa-h-square: "\f0fd";
@fa-plus-square: "\f0fe";
@fa-angle-double-left: "\f100";
@fa-angle-double-right: "\f101";
@fa-angle-double-up: "\f102";
@fa-angle-double-down: "\f103";
@fa-angle-left: "\f104";
@fa-angle-right: "\f105";
@fa-angle-up: "\f106";
@fa-angle-down: "\f107";
@fa-desktop: "\f108";
@fa-laptop: "\f109";
@fa-tablet: "\f10a";
@fa-mobile: "\f10b";
@fa-circle-o: "\f10c";
@fa-quote-left: "\f10d";
@fa-quote-right: "\f10e";
@fa-spinner: "\f110";
@fa-circle: "\f111";
@fa-reply: "\f112";
@fa-github-alt: "\f113";
@fa-folder-o: "\f114";
@fa-folder-open-o: "\f115";
@fa-smile-o: "\f118";
@fa-frown-o: "\f119";
@fa-meh-o: "\f11a";
@fa-gamepad: "\f11b";
@fa-keyboard-o: "\f11c";
@fa-flag-o: "\f11d";
@fa-flag-checkered: "\f11e";
@fa-terminal: "\f120";
@fa-code: "\f121";
@fa-reply-all: "\f122";
@fa-star-half-o: "\f123";
@fa-location-arrow: "\f124";
@fa-crop: "\f125";
@fa-code-fork: "\f126";
@fa-chain-broken: "\f127";
@fa-question: "\f128";
@fa-info: "\f129";
@fa-exclamation: "\f12a";
@fa-superscript: "\f12b";
@fa-subscript: "\f12c";
@fa-eraser: "\f12d";
@fa-puzzle-piece: "\f12e";
@fa-microphone: "\f130";
@fa-microphone-slash: "\f131";
@fa-shield: "\f132";
@fa-calendar-o: "\f133";
@fa-fire-extinguisher: "\f134";
@fa-rocket: "\f135";
@fa-maxcdn: "\f136";
@fa-chevron-circle-left: "\f137";
@fa-chevron-circle-right: "\f138";
@fa-chevron-circle-up: "\f139";
@fa-chevron-circle-down: "\f13a";
@fa-html5: "\f13b";
@fa-css3: "\f13c";
@fa-anchor: "\f13d";
@fa-unlock-alt: "\f13e";
@fa-bullseye: "\f140";
@fa-ellipsis-h: "\f141";
@fa-ellipsis-v: "\f142";
@fa-rss-square: "\f143";
@fa-play-circle: "\f144";
@fa-ticket: "\f145";
@fa-minus-square: "\f146";
@fa-minus-square-o: "\f147";
@fa-level-up: "\f148";
@fa-level-down: "\f149";
@fa-check-square: "\f14a";
@fa-pencil-square: "\f14b";
@fa-external-link-square: "\f14c";
@fa-share-square: "\f14d";
@fa-compass: "\f14e";
@fa-caret-square-o-down: "\f150";
@fa-caret-square-o-up: "\f151";
@fa-caret-square-o-right: "\f152";
@fa-eur: "\f153";
@fa-gbp: "\f154";
@fa-usd: "\f155";
@fa-inr: "\f156";
@fa-jpy: "\f157";
@fa-rub: "\f158";
@fa-krw: "\f159";
@fa-btc: "\f15a";
@fa-file: "\f15b";
@fa-file-text: "\f15c";
@fa-sort-alpha-asc: "\f15d";
@fa-sort-alpha-desc: "\f15e";
@fa-sort-amount-asc: "\f160";
@fa-sort-amount-desc: "\f161";
@fa-sort-numeric-asc: "\f162";
@fa-sort-numeric-desc: "\f163";
@fa-thumbs-up: "\f164";
@fa-thumbs-down: "\f165";
@fa-youtube-square: "\f166";
@fa-youtube: "\f167";
@fa-xing: "\f168";
@fa-xing-square: "\f169";
@fa-youtube-play: "\f16a";
@fa-dropbox: "\f16b";
@fa-stack-overflow: "\f16c";
@fa-instagram: "\f16d";
@fa-flickr: "\f16e";
@fa-adn: "\f170";
@fa-bitbucket: "\f171";
@fa-bitbucket-square: "\f172";
@fa-tumblr: "\f173";
@fa-tumblr-square: "\f174";
@fa-long-arrow-down: "\f175";
@fa-long-arrow-up: "\f176";
@fa-long-arrow-left: "\f177";
@fa-long-arrow-right: "\f178";
@fa-apple: "\f179";
@fa-windows: "\f17a";
@fa-android: "\f17b";
@fa-linux: "\f17c";
@fa-dribbble: "\f17d";
@fa-skype: "\f17e";
@fa-foursquare: "\f180";
@fa-trello: "\f181";
@fa-female: "\f182";
@fa-male: "\f183";
@fa-gittip: "\f184";
@fa-sun-o: "\f185";
@fa-moon-o: "\f186";
@fa-archive: "\f187";
@fa-bug: "\f188";
@fa-vk: "\f189";
@fa-weibo: "\f18a";
@fa-renren: "\f18b";
@fa-pagelines: "\f18c";
@fa-stack-exchange: "\f18d";
@fa-arrow-circle-o-right: "\f18e";
@fa-arrow-circle-o-left: "\f190";
@fa-caret-square-o-left: "\f191";
@fa-dot-circle-o: "\f192";
@fa-wheelchair: "\f193";
@fa-vimeo-square: "\f194";
@fa-try: "\f195";
@fa-plus-square-o: "\f196";
@fa-space-shuttle: "\f197";
@fa-slack: "\f198";
@fa-envelope-square: "\f199";
@fa-wordpress: "\f19a";
@fa-openid: "\f19b";
@fa-university: "\f19c";
@fa-graduation-cap: "\f19d";
@fa-yahoo: "\f19e";
@fa-google: "\f1a0";
@fa-reddit: "\f1a1";
@fa-reddit-square: "\f1a2";
@fa-stumbleupon-circle: "\f1a3";
@fa-stumbleupon: "\f1a4";
@fa-delicious: "\f1a5";
@fa-digg: "\f1a6";
@fa-pied-piper: "\f1a7";
@fa-pied-piper-alt: "\f1a8";
@fa-drupal: "\f1a9";
@fa-joomla: "\f1aa";
@fa-language: "\f1ab";
@fa-fax: "\f1ac";
@fa-building: "\f1ad";
@fa-child: "\f1ae";
@fa-paw: "\f1b0";
@fa-spoon: "\f1b1";
@fa-cube: "\f1b2";
@fa-cubes: "\f1b3";
@fa-behance: "\f1b4";
@fa-behance-square: "\f1b5";
@fa-steam: "\f1b6";
@fa-steam-square: "\f1b7";
@fa-recycle: "\f1b8";
@fa-car: "\f1b9";
@fa-taxi: "\f1ba";
@fa-tree: "\f1bb";
@fa-spotify: "\f1bc";
@fa-deviantart: "\f1bd";
@fa-soundcloud: "\f1be";
@fa-database: "\f1c0";
@fa-file-pdf-o: "\f1c1";
@fa-file-word-o: "\f1c2";
@fa-file-excel-o: "\f1c3";
@fa-file-powerpoint-o: "\f1c4";
@fa-file-image-o: "\f1c5";
@fa-file-archive-o: "\f1c6";
@fa-file-audio-o: "\f1c7";
@fa-file-video-o: "\f1c8";
@fa-file-code-o: "\f1c9";
@fa-vine: "\f1ca";
@fa-codepen: "\f1cb";
@fa-jsfiddle: "\f1cc";
@fa-life-ring: "\f1cd";
@fa-circle-o-notch: "\f1ce";
@fa-rebel: "\f1d0";
@fa-empire: "\f1d1";
@fa-git-square: "\f1d2";
@fa-git: "\f1d3";
@fa-hacker-news: "\f1d4";
@fa-tencent-weibo: "\f1d5";
@fa-qq: "\f1d6";
@fa-weixin: "\f1d7";
@fa-paper-plane: "\f1d8";
@fa-paper-plane-o: "\f1d9";
@fa-history: "\f1da";
@fa-circle-thin: "\f1db";
@fa-header: "\f1dc";
@fa-paragraph: "\f1dd";
@fa-sliders: "\f1de";
@fa-share-alt: "\f1e0";
@fa-share-alt-square: "\f1e1";
@fa-bomb: "\f1e2";
@fa-futbol-o: "\f1e3";
@fa-tty: "\f1e4";
@fa-binoculars: "\f1e5";
@fa-plug: "\f1e6";
@fa-slideshare: "\f1e7";
@fa-twitch: "\f1e8";
@fa-yelp: "\f1e9";
@fa-newspaper-o: "\f1ea";
@fa-wifi: "\f1eb";
@fa-calculator: "\f1ec";
@fa-paypal: "\f1ed";
@fa-google-wallet: "\f1ee";
@fa-cc-visa: "\f1f0";
@fa-cc-mastercard: "\f1f1";
@fa-cc-discover: "\f1f2";
@fa-cc-amex: "\f1f3";
@fa-cc-paypal: "\f1f4";
@fa-cc-stripe: "\f1f5";
@fa-bell-slash: "\f1f6";
@fa-bell-slash-o: "\f1f7";
@fa-trash: "\f1f8";
@fa-copyright: "\f1f9";
@fa-at: "\f1fa";
@fa-eyedropper: "\f1fb";
@fa-paint-brush: "\f1fc";
@fa-birthday-cake: "\f1fd";
@fa-area-chart: "\f1fe";
@fa-pie-chart: "\f200";
@fa-line-chart: "\f201";
@fa-lastfm: "\f202";
@fa-lastfm-square: "\f203";
@fa-toggle-off: "\f204";
@fa-toggle-on: "\f205";
@fa-bicycle: "\f206";
@fa-bus: "\f207";
@fa-ioxhost: "\f208";
@fa-angellist: "\f209";
@fa-cc: "\f20a";
@fa-ils: "\f20b";
@fa-meanpath: "\f20c";


BIN
public/self/ztree/css/awesomeStyle/img/loading.gif View File

Before After
Width: 16  |  Height: 16  |  Size: 381 B

+ 33
- 0
public/self/ztree/css/demo.css View File

@@ -0,0 +1,33 @@
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td {
margin: 0;padding: 0;border: 0;outline: 0;font-weight: inherit;font-style: inherit;font-size: 100%;font-family: inherit;vertical-align: baseline;}
body {color: #2f332a;font: 15px/21px Arial, Helvetica, simsun, sans-serif;background: #f0f6e4 \9;}
h1, h2, h3, h4, h5, h6 {color: #2f332a;font-weight: bold;font-family: Helvetica, Arial, sans-serif;padding-bottom: 5px;}
h1 {font-size: 24px;line-height: 34px;text-align: center;}
h2 {font-size: 14px;line-height: 24px;padding-top: 5px;}
h6 {font-weight: normal;font-size: 12px;letter-spacing: 1px;line-height: 24px;text-align: center;}
a {color:#3C6E31;text-decoration: underline;}
a:hover {background-color:#3C6E31;color:white;}
input.radio {margin: 0 2px 0 8px;}
input.radio.first {margin-left:0;}
input.empty {color: lightgray;}
code {color: #2f332a;}
.highlight_red {color:#A60000;}
.highlight_green {color:#A7F43D;}
li {list-style: circle;font-size: 12px;}
li.title {list-style: none;}
ul.list {margin-left: 17px;}

div.content_wrap {width: 600px;height:380px;}
div.content_wrap div.left{float: left;width: 250px;}
div.content_wrap div.right{float: right;width: 340px;}
div.zTreeDemoBackground {width:250px;height:362px;text-align:left;}

ul.ztree {margin-top: 10px;border: 1px solid #617775;background: #f0f6e4;width:220px;height:360px;overflow-y:scroll;overflow-x:auto;}
ul.log {border: 1px solid #617775;background: #f0f6e4;width:300px;height:170px;overflow: hidden;}
ul.log.small {height:45px;}
ul.log li {color: #666666;list-style: none;padding-left: 10px;}
ul.log li.dark {background-color: #E3E3E3;}

/* ruler */
div.ruler {height:20px; width:220px; background-color:#f0f6e4;border: 1px solid #333; margin-bottom: 5px; cursor: pointer}
div.ruler div.cursor {height:20px; width:30px; background-color:#3C6E31; color:white; text-align: right; padding-right: 5px; cursor: pointer}

BIN
public/self/ztree/css/metroStyle/img/line_conn.png View File

Before After
Width: 11  |  Height: 2  |  Size: 933 B

BIN
public/self/ztree/css/metroStyle/img/loading.gif View File

Before After
Width: 16  |  Height: 16  |  Size: 381 B

BIN
public/self/ztree/css/metroStyle/img/metro.gif View File

Before After
Width: 210  |  Height: 126  |  Size: 4.7 kB

BIN
public/self/ztree/css/metroStyle/img/metro.png View File

Before After
Width: 210  |  Height: 126  |  Size: 5.3 kB

+ 96
- 0
public/self/ztree/css/metroStyle/metroStyle.css View File

@@ -0,0 +1,96 @@
/*-------------------------------------
zTree Style

version: 3.4
author: Hunter.z
email: hunter.z@263.net
website: http://code.google.com/p/jquerytree/

-------------------------------------*/

.ztree * {padding:0; margin:0; font-size:12px; font-family: Verdana, Arial, Helvetica, AppleGothic, sans-serif}
.ztree {margin:0; padding:5px; color:#333}
.ztree li{padding:0; margin:0; list-style:none; line-height:17px; text-align:left; white-space:nowrap; outline:0}
.ztree li ul{ margin:0; padding:0 0 0 18px}
.ztree li ul.line{ background:url(./img/line_conn.png) 0 0 repeat-y;}

.ztree li a {padding-right:3px; margin:0; cursor:pointer; height:21px; color:#333; background-color: transparent; text-decoration:none; vertical-align:top; display: inline-block}
.ztree li a:hover {text-decoration:underline}
.ztree li a.curSelectedNode {padding-top:0px; background-color:#e5e5e5; color:black; height:21px; opacity:0.8;}
.ztree li a.curSelectedNode_Edit {padding-top:0px; background-color:#e5e5e5; color:black; height:21px; border:1px #666 solid; opacity:0.8;}
.ztree li a.tmpTargetNode_inner {padding-top:0px; background-color:#aaa; color:white; height:21px; border:1px #666 solid;
opacity:0.8; filter:alpha(opacity=80)}
.ztree li a.tmpTargetNode_prev {}
.ztree li a.tmpTargetNode_next {}
.ztree li a input.rename {height:14px; width:80px; padding:0; margin:0;
font-size:12px; border:1px #585956 solid; *border:0px}
.ztree li span {line-height:21px; margin-right:2px}
.ztree li span.button {line-height:0; margin:0; padding: 0; width:21px; height:21px; display: inline-block; vertical-align:middle;
border:0 none; cursor: pointer;outline:none;
background-color:transparent; background-repeat:no-repeat; background-attachment: scroll;
background-image:url("./img/metro.png"); *background-image:url("./img/metro.gif")}

.ztree li span.button.chk {width:13px; height:13px; margin:0 2px; cursor: auto}
.ztree li span.button.chk.checkbox_false_full {background-position: -5px -5px;}
.ztree li span.button.chk.checkbox_false_full_focus {background-position: -5px -26px;}
.ztree li span.button.chk.checkbox_false_part {background-position: -5px -48px;}
.ztree li span.button.chk.checkbox_false_part_focus {background-position: -5px -68px;}
.ztree li span.button.chk.checkbox_false_disable {background-position: -5px -89px;}
.ztree li span.button.chk.checkbox_true_full {background-position: -26px -5px;}
.ztree li span.button.chk.checkbox_true_full_focus {background-position: -26px -26px;}
.ztree li span.button.chk.checkbox_true_part {background-position: -26px -48px;}
.ztree li span.button.chk.checkbox_true_part_focus {background-position: -26px -68px;}
.ztree li span.button.chk.checkbox_true_disable {background-position: -26px -89px;}
.ztree li span.button.chk.radio_false_full {background-position: -47px -5px;}
.ztree li span.button.chk.radio_false_full_focus {background-position: -47px -26px;}
.ztree li span.button.chk.radio_false_part {background-position: -47px -47px;}
.ztree li span.button.chk.radio_false_part_focus {background-position: -47px -68px;}
.ztree li span.button.chk.radio_false_disable {background-position: -47px -89px;}
.ztree li span.button.chk.radio_true_full {background-position: -68px -5px;}
.ztree li span.button.chk.radio_true_full_focus {background-position: -68px -26px;}
.ztree li span.button.chk.radio_true_part {background-position: -68px -47px;}
.ztree li span.button.chk.radio_true_part_focus {background-position: -68px -68px;}
.ztree li span.button.chk.radio_true_disable {background-position: -68px -89px;}

.ztree li span.button.switch {width:21px; height:21px}
.ztree li span.button.root_open{background-position:-105px -63px}
.ztree li span.button.root_close{background-position:-126px -63px}
.ztree li span.button.roots_open{background-position: -105px 0;}
.ztree li span.button.roots_close{background-position: -126px 0;}
.ztree li span.button.center_open{background-position: -105px -21px;}
.ztree li span.button.center_close{background-position: -126px -21px;}
.ztree li span.button.bottom_open{background-position: -105px -42px;}
.ztree li span.button.bottom_close{background-position: -126px -42px;}
.ztree li span.button.noline_open{background-position: -105px -84px;}
.ztree li span.button.noline_close{background-position: -126px -84px;}
.ztree li span.button.root_docu{ background:none;}
.ztree li span.button.roots_docu{background-position: -84px 0;}
.ztree li span.button.center_docu{background-position: -84px -21px;}
.ztree li span.button.bottom_docu{background-position: -84px -42px;}
.ztree li span.button.noline_docu{ background:none;}

.ztree li span.button.ico_open{margin-right:2px; background-position: -147px -21px; vertical-align:top; *vertical-align:middle}
.ztree li span.button.ico_close{margin-right:2px; margin-right:2px; background-position: -147px 0; vertical-align:top; *vertical-align:middle}
.ztree li span.button.ico_docu{margin-right:2px; background-position: -147px -42px; vertical-align:top; *vertical-align:middle}
.ztree li span.button.edit {margin-left:2px; margin-right: -1px; background-position: -189px -21px; vertical-align:top; *vertical-align:middle}
.ztree li span.button.edit:hover {
background-position: -168px -21px;
}
.ztree li span.button.remove {margin-left:2px; margin-right: -1px; background-position: -189px -42px; vertical-align:top; *vertical-align:middle}
.ztree li span.button.remove:hover {
background-position: -168px -42px;
}
.ztree li span.button.add {margin-left:2px; margin-right: -1px; background-position: -189px 0; vertical-align:top; *vertical-align:middle}
.ztree li span.button.add:hover {
background-position: -168px 0;
}
.ztree li span.button.ico_loading{margin-right:2px; background:url(./img/loading.gif) no-repeat scroll 0 0 transparent; vertical-align:top; *vertical-align:middle}

ul.tmpTargetzTree {background-color:#FFE6B0; opacity:0.8; filter:alpha(opacity=80)}

span.tmpzTreeMove_arrow {width:16px; height:21px; display: inline-block; padding:0; margin:2px 0 0 1px; border:0 none; position:absolute;
background-color:transparent; background-repeat:no-repeat; background-attachment: scroll;
background-position:-168px -84px; background-image:url("./img/metro.png"); *background-image:url("./img/metro.gif")}

ul.ztree.zTreeDragUL {margin:0; padding:0; position:absolute; width:auto; height:auto;overflow:hidden; background-color:#cfcfcf; border:1px #00B83F dotted; opacity:0.8; filter:alpha(opacity=80)}
.ztreeMask {z-index:10000; background-color:#cfcfcf; opacity:0.0; filter:alpha(opacity=0); position:absolute}

BIN
public/self/ztree/css/zTreeStyle/img/diy/1_close.png View File

Before After
Width: 16  |  Height: 16  |  Size: 601 B

BIN
public/self/ztree/css/zTreeStyle/img/diy/1_open.png View File

Before After
Width: 16  |  Height: 16  |  Size: 580 B

BIN
public/self/ztree/css/zTreeStyle/img/diy/2.png View File

Before After
Width: 16  |  Height: 16  |  Size: 570 B

BIN
public/self/ztree/css/zTreeStyle/img/diy/3.png View File

Before After
Width: 16  |  Height: 16  |  Size: 762 B

BIN
public/self/ztree/css/zTreeStyle/img/diy/4.png View File

Before After
Width: 16  |  Height: 16  |  Size: 399 B

BIN
public/self/ztree/css/zTreeStyle/img/diy/5.png View File

Before After
Width: 16  |  Height: 16  |  Size: 710 B

BIN
public/self/ztree/css/zTreeStyle/img/diy/6.png View File

Before After
Width: 16  |  Height: 16  |  Size: 432 B

BIN
public/self/ztree/css/zTreeStyle/img/diy/7.png View File

Before After
Width: 16  |  Height: 16  |  Size: 534 B

BIN
public/self/ztree/css/zTreeStyle/img/diy/8.png View File

Before After
Width: 16  |  Height: 16  |  Size: 529 B

BIN
public/self/ztree/css/zTreeStyle/img/diy/9.png View File

Before After
Width: 16  |  Height: 16  |  Size: 467 B

BIN
public/self/ztree/css/zTreeStyle/img/line_conn.gif View File

Before After
Width: 9  |  Height: 2  |  Size: 45 B

BIN
public/self/ztree/css/zTreeStyle/img/loading.gif View File

Before After
Width: 16  |  Height: 16  |  Size: 381 B

BIN
public/self/ztree/css/zTreeStyle/img/zTreeStandard.gif View File

Before After
Width: 160  |  Height: 97  |  Size: 6.6 kB

BIN
public/self/ztree/css/zTreeStyle/img/zTreeStandard.png View File

Before After
Width: 160  |  Height: 97  |  Size: 11 kB

+ 99
- 0
public/self/ztree/css/zTreeStyle/zTreeStyle.css View File

@@ -0,0 +1,99 @@
/*-------------------------------------
zTree Style

version: 3.5.19
author: Hunter.z
email: hunter.z@263.net
website: http://code.google.com/p/jquerytree/

-------------------------------------*/

.ztree * {padding:0; margin:0; font-size:12px; font-family: Verdana, Arial, Helvetica, AppleGothic, sans-serif}
.ztree {margin:0; padding:5px; color:#333;margin-top: 0; height: 100%; max-height: 400px; overflow-y: scroll;}
.ztree::-webkit-scrollbar-track{background-color: #FFF;}
.ztree li{padding:5px 0; margin:0; list-style:none; line-height:14px; text-align:left; white-space:nowrap; outline:0; position: relative;}
.ztree li ul{ margin:0; padding:0 0 0 18px}
/*.ztree li ul.line{ background:url(./img/line_conn.gif) 0 0 repeat-y;}*/

.ztree li a {padding:1px 3px 0 0; margin:0; cursor:pointer; height:17px; color:#333; background-color: transparent;
text-decoration:none; vertical-align:top; display: inline-block}
.ztree li a:hover {text-decoration:none;}
.ztree li a:hover .node_name::after{content: '';position: absolute;left: 0;right: 0;top: 0;background-color: #f7f7f7;height: 100%;z-index: -1;}
.ztree li a.curSelectedNode {padding-top:0px; background-color:#FFE6B0; color:black; height:16px; border:1px #FFB951 solid; opacity:0.8;}
.ztree li a.curSelectedNode_Edit {padding-top:0px; background-color:#FFE6B0; color:black; height:16px; border:1px #FFB951 solid; opacity:0.8;}
.ztree li a.tmpTargetNode_inner {padding-top:0px; background-color:#316AC5; color:white; height:16px; border:1px #316AC5 solid;
opacity:0.8; filter:alpha(opacity=80)}
.ztree li a.tmpTargetNode_prev {}
.ztree li a.tmpTargetNode_next {}
.ztree li a input.rename {height:14px; width:80px; padding:0; margin:0;
font-size:12px; border:1px #7EC4CC solid; *border:0px}
.ztree li span {line-height:16px; margin-right:2px}
.ztree li span.button {line-height:0; margin:0; width:16px; height:16px; display: inline-block; vertical-align:middle;
border:0 none; cursor: pointer;outline:none;
background-color:transparent; background-repeat:no-repeat; background-attachment: scroll;
background-image:url("./img/zTreeStandard.png"); *background-image:url("./img/zTreeStandard.gif")}

.ztree li span.button.chk {width:13px; height:13px; margin:0 3px 0 0; cursor: auto}
.ztree li span.button.chk.checkbox_false_full {background-position:0 0}
.ztree li span.button.chk.checkbox_false_full_focus {background-position:0 -14px}
.ztree li span.button.chk.checkbox_false_part {background-position:0 -28px}
.ztree li span.button.chk.checkbox_false_part_focus {background-position:0 -42px}
.ztree li span.button.chk.checkbox_false_disable {background-position:0 -56px}
.ztree li span.button.chk.checkbox_true_full {background-position:-14px 0}
.ztree li span.button.chk.checkbox_true_full_focus {background-position:-14px -14px}
.ztree li span.button.chk.checkbox_true_part {background-position:-14px -28px}
.ztree li span.button.chk.checkbox_true_part_focus {background-position:-14px -42px}
.ztree li span.button.chk.checkbox_true_disable {background-position:-14px -56px}
.ztree li span.button.chk.radio_false_full {background-position:-28px 0}
.ztree li span.button.chk.radio_false_full_focus {background-position:-28px -14px}
.ztree li span.button.chk.radio_false_part {background-position:-28px -28px}
.ztree li span.button.chk.radio_false_part_focus {background-position:-28px -42px}
.ztree li span.button.chk.radio_false_disable {background-position:-28px -56px}
.ztree li span.button.chk.radio_true_full {background-position:-42px 0}
.ztree li span.button.chk.radio_true_full_focus {background-position:-42px -14px}
.ztree li span.button.chk.radio_true_part {background-position:-42px -28px}
.ztree li span.button.chk.radio_true_part_focus {background-position:-42px -42px}
.ztree li span.button.chk.radio_true_disable {background-position:-42px -56px}

.ztree li span.button.switch {width:18px; height:18px}
.ztree li span.button.root_open{background-position:-92px -54px}
.ztree li span.button.root_close{background-position:-74px -54px}
.ztree li span.button.roots_open{background-position:-92px 0}
.ztree li span.button.roots_close{background-position:-74px 0}
.ztree li span.button.center_open{background-position:-92px -18px}
.ztree li span.button.center_close{background-position:-74px -18px}
.ztree li span.button.bottom_open{background-position:-92px -36px}
.ztree li span.button.bottom_close{background-position:-74px -36px}
.ztree li span.button.noline_open{background-position:-92px -72px}
.ztree li span.button.noline_close{background-position:-74px -72px}
.ztree li span.button.root_docu{ background:none;}
.ztree li span.button.roots_docu{background-position:-56px 0}
.ztree li span.button.center_docu{background-position:-56px -18px}
.ztree li span.button.bottom_docu{background-position:-56px -36px}
.ztree li span.button.noline_docu{ background:none;}

.ztree li span.button.ico_open{margin-right:2px; background-position:-110px -16px; vertical-align:top; *vertical-align:middle}
.ztree li span.button.ico_close{margin-right:2px; background-position:-110px 0; vertical-align:top; *vertical-align:middle}
.ztree li span.button.ico_docu{margin-right:2px; background-position:-110px -32px; vertical-align:top; *vertical-align:middle}
.ztree li span.button.edit {margin-right:2px; background-position:-110px -48px; vertical-align:top; *vertical-align:middle}
.ztree li span.button.remove {margin-right:2px; background-position:-110px -64px; vertical-align:top; *vertical-align:middle}

.ztree li span.button.ico_loading{margin-right:2px; background:url(./img/loading.gif) no-repeat scroll 0 0 transparent; vertical-align:top; *vertical-align:middle}

ul.tmpTargetzTree {background-color:#FFE6B0; opacity:0.8; filter:alpha(opacity=80)}

span.tmpzTreeMove_arrow {width:16px; height:16px; display: inline-block; padding:0; margin:2px 0 0 1px; border:0 none; position:absolute;
background-color:transparent; background-repeat:no-repeat; background-attachment: scroll;
background-position:-110px -80px; background-image:url("./img/zTreeStandard.png"); *background-image:url("./img/zTreeStandard.gif")}

ul.ztree.zTreeDragUL {margin:0; padding:0; position:absolute; width:auto; height:auto;overflow:hidden; background-color:#cfcfcf; border:1px #00B83F dotted; opacity:0.8; filter:alpha(opacity=80)}
.zTreeMask {z-index:10000; background-color:#cfcfcf; opacity:0.0; filter:alpha(opacity=0); position:absolute}

/* level style*/
/*.ztree li span.button.level0 {
display:none;
}
.ztree li ul.level0 {
padding:0;
background:none;
}*/

+ 167
- 0
public/self/ztree/js/jquery-1.4.4.min.js View File

@@ -0,0 +1,167 @@
/*!
* jQuery JavaScript Library v1.4.4
* http://jquery.com/
*
* Copyright 2010, John Resig
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* Includes Sizzle.js
* http://sizzlejs.com/
* Copyright 2010, The Dojo Foundation
* Released under the MIT, BSD, and GPL Licenses.
*
* Date: Thu Nov 11 19:04:53 2010 -0500
*/
(function(E,B){function ka(a,b,d){if(d===B&&a.nodeType===1){d=a.getAttribute("data-"+b);if(typeof d==="string"){try{d=d==="true"?true:d==="false"?false:d==="null"?null:!c.isNaN(d)?parseFloat(d):Ja.test(d)?c.parseJSON(d):d}catch(e){}c.data(a,b,d)}else d=B}return d}function U(){return false}function ca(){return true}function la(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function Ka(a){var b,d,e,f,h,l,k,o,x,r,A,C=[];f=[];h=c.data(this,this.nodeType?"events":"__events__");if(typeof h==="function")h=
h.events;if(!(a.liveFired===this||!h||!h.live||a.button&&a.type==="click")){if(a.namespace)A=RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)");a.liveFired=this;var J=h.live.slice(0);for(k=0;k<J.length;k++){h=J[k];h.origType.replace(X,"")===a.type?f.push(h.selector):J.splice(k--,1)}f=c(a.target).closest(f,a.currentTarget);o=0;for(x=f.length;o<x;o++){r=f[o];for(k=0;k<J.length;k++){h=J[k];if(r.selector===h.selector&&(!A||A.test(h.namespace))){l=r.elem;e=null;if(h.preType==="mouseenter"||
h.preType==="mouseleave"){a.type=h.preType;e=c(a.relatedTarget).closest(h.selector)[0]}if(!e||e!==l)C.push({elem:l,handleObj:h,level:r.level})}}}o=0;for(x=C.length;o<x;o++){f=C[o];if(d&&f.level>d)break;a.currentTarget=f.elem;a.data=f.handleObj.data;a.handleObj=f.handleObj;A=f.handleObj.origHandler.apply(f.elem,arguments);if(A===false||a.isPropagationStopped()){d=f.level;if(A===false)b=false;if(a.isImmediatePropagationStopped())break}}return b}}function Y(a,b){return(a&&a!=="*"?a+".":"")+b.replace(La,
"`").replace(Ma,"&")}function ma(a,b,d){if(c.isFunction(b))return c.grep(a,function(f,h){return!!b.call(f,h,f)===d});else if(b.nodeType)return c.grep(a,function(f){return f===b===d});else if(typeof b==="string"){var e=c.grep(a,function(f){return f.nodeType===1});if(Na.test(b))return c.filter(b,e,!d);else b=c.filter(b,e)}return c.grep(a,function(f){return c.inArray(f,b)>=0===d})}function na(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var e=c.data(a[d++]),f=c.data(this,
e);if(e=e&&e.events){delete f.handle;f.events={};for(var h in e)for(var l in e[h])c.event.add(this,h,e[h][l],e[h][l].data)}}})}function Oa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function oa(a,b,d){var e=b==="width"?a.offsetWidth:a.offsetHeight;if(d==="border")return e;c.each(b==="width"?Pa:Qa,function(){d||(e-=parseFloat(c.css(a,"padding"+this))||0);if(d==="margin")e+=parseFloat(c.css(a,
"margin"+this))||0;else e-=parseFloat(c.css(a,"border"+this+"Width"))||0});return e}function da(a,b,d,e){if(c.isArray(b)&&b.length)c.each(b,function(f,h){d||Ra.test(a)?e(a,h):da(a+"["+(typeof h==="object"||c.isArray(h)?f:"")+"]",h,d,e)});else if(!d&&b!=null&&typeof b==="object")c.isEmptyObject(b)?e(a,""):c.each(b,function(f,h){da(a+"["+f+"]",h,d,e)});else e(a,b)}function S(a,b){var d={};c.each(pa.concat.apply([],pa.slice(0,b)),function(){d[this]=a});return d}function qa(a){if(!ea[a]){var b=c("<"+
a+">").appendTo("body"),d=b.css("display");b.remove();if(d==="none"||d==="")d="block";ea[a]=d}return ea[a]}function fa(a){return c.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var t=E.document,c=function(){function a(){if(!b.isReady){try{t.documentElement.doScroll("left")}catch(j){setTimeout(a,1);return}b.ready()}}var b=function(j,s){return new b.fn.init(j,s)},d=E.jQuery,e=E.$,f,h=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/,l=/\S/,k=/^\s+/,o=/\s+$/,x=/\W/,r=/\d/,A=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,
C=/^[\],:{}\s]*$/,J=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,w=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,I=/(?:^|:|,)(?:\s*\[)+/g,L=/(webkit)[ \/]([\w.]+)/,g=/(opera)(?:.*version)?[ \/]([\w.]+)/,i=/(msie) ([\w.]+)/,n=/(mozilla)(?:.*? rv:([\w.]+))?/,m=navigator.userAgent,p=false,q=[],u,y=Object.prototype.toString,F=Object.prototype.hasOwnProperty,M=Array.prototype.push,N=Array.prototype.slice,O=String.prototype.trim,D=Array.prototype.indexOf,R={};b.fn=b.prototype={init:function(j,
s){var v,z,H;if(!j)return this;if(j.nodeType){this.context=this[0]=j;this.length=1;return this}if(j==="body"&&!s&&t.body){this.context=t;this[0]=t.body;this.selector="body";this.length=1;return this}if(typeof j==="string")if((v=h.exec(j))&&(v[1]||!s))if(v[1]){H=s?s.ownerDocument||s:t;if(z=A.exec(j))if(b.isPlainObject(s)){j=[t.createElement(z[1])];b.fn.attr.call(j,s,true)}else j=[H.createElement(z[1])];else{z=b.buildFragment([v[1]],[H]);j=(z.cacheable?z.fragment.cloneNode(true):z.fragment).childNodes}return b.merge(this,
j)}else{if((z=t.getElementById(v[2]))&&z.parentNode){if(z.id!==v[2])return f.find(j);this.length=1;this[0]=z}this.context=t;this.selector=j;return this}else if(!s&&!x.test(j)){this.selector=j;this.context=t;j=t.getElementsByTagName(j);return b.merge(this,j)}else return!s||s.jquery?(s||f).find(j):b(s).find(j);else if(b.isFunction(j))return f.ready(j);if(j.selector!==B){this.selector=j.selector;this.context=j.context}return b.makeArray(j,this)},selector:"",jquery:"1.4.4",length:0,size:function(){return this.length},
toArray:function(){return N.call(this,0)},get:function(j){return j==null?this.toArray():j<0?this.slice(j)[0]:this[j]},pushStack:function(j,s,v){var z=b();b.isArray(j)?M.apply(z,j):b.merge(z,j);z.prevObject=this;z.context=this.context;if(s==="find")z.selector=this.selector+(this.selector?" ":"")+v;else if(s)z.selector=this.selector+"."+s+"("+v+")";return z},each:function(j,s){return b.each(this,j,s)},ready:function(j){b.bindReady();if(b.isReady)j.call(t,b);else q&&q.push(j);return this},eq:function(j){return j===
-1?this.slice(j):this.slice(j,+j+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(N.apply(this,arguments),"slice",N.call(arguments).join(","))},map:function(j){return this.pushStack(b.map(this,function(s,v){return j.call(s,v,s)}))},end:function(){return this.prevObject||b(null)},push:M,sort:[].sort,splice:[].splice};b.fn.init.prototype=b.fn;b.extend=b.fn.extend=function(){var j,s,v,z,H,G=arguments[0]||{},K=1,Q=arguments.length,ga=false;
if(typeof G==="boolean"){ga=G;G=arguments[1]||{};K=2}if(typeof G!=="object"&&!b.isFunction(G))G={};if(Q===K){G=this;--K}for(;K<Q;K++)if((j=arguments[K])!=null)for(s in j){v=G[s];z=j[s];if(G!==z)if(ga&&z&&(b.isPlainObject(z)||(H=b.isArray(z)))){if(H){H=false;v=v&&b.isArray(v)?v:[]}else v=v&&b.isPlainObject(v)?v:{};G[s]=b.extend(ga,v,z)}else if(z!==B)G[s]=z}return G};b.extend({noConflict:function(j){E.$=e;if(j)E.jQuery=d;return b},isReady:false,readyWait:1,ready:function(j){j===true&&b.readyWait--;
if(!b.readyWait||j!==true&&!b.isReady){if(!t.body)return setTimeout(b.ready,1);b.isReady=true;if(!(j!==true&&--b.readyWait>0))if(q){var s=0,v=q;for(q=null;j=v[s++];)j.call(t,b);b.fn.trigger&&b(t).trigger("ready").unbind("ready")}}},bindReady:function(){if(!p){p=true;if(t.readyState==="complete")return setTimeout(b.ready,1);if(t.addEventListener){t.addEventListener("DOMContentLoaded",u,false);E.addEventListener("load",b.ready,false)}else if(t.attachEvent){t.attachEvent("onreadystatechange",u);E.attachEvent("onload",
b.ready);var j=false;try{j=E.frameElement==null}catch(s){}t.documentElement.doScroll&&j&&a()}}},isFunction:function(j){return b.type(j)==="function"},isArray:Array.isArray||function(j){return b.type(j)==="array"},isWindow:function(j){return j&&typeof j==="object"&&"setInterval"in j},isNaN:function(j){return j==null||!r.test(j)||isNaN(j)},type:function(j){return j==null?String(j):R[y.call(j)]||"object"},isPlainObject:function(j){if(!j||b.type(j)!=="object"||j.nodeType||b.isWindow(j))return false;if(j.constructor&&
!F.call(j,"constructor")&&!F.call(j.constructor.prototype,"isPrototypeOf"))return false;for(var s in j);return s===B||F.call(j,s)},isEmptyObject:function(j){for(var s in j)return false;return true},error:function(j){throw j;},parseJSON:function(j){if(typeof j!=="string"||!j)return null;j=b.trim(j);if(C.test(j.replace(J,"@").replace(w,"]").replace(I,"")))return E.JSON&&E.JSON.parse?E.JSON.parse(j):(new Function("return "+j))();else b.error("Invalid JSON: "+j)},noop:function(){},globalEval:function(j){if(j&&
l.test(j)){var s=t.getElementsByTagName("head")[0]||t.documentElement,v=t.createElement("script");v.type="text/javascript";if(b.support.scriptEval)v.appendChild(t.createTextNode(j));else v.text=j;s.insertBefore(v,s.firstChild);s.removeChild(v)}},nodeName:function(j,s){return j.nodeName&&j.nodeName.toUpperCase()===s.toUpperCase()},each:function(j,s,v){var z,H=0,G=j.length,K=G===B||b.isFunction(j);if(v)if(K)for(z in j){if(s.apply(j[z],v)===false)break}else for(;H<G;){if(s.apply(j[H++],v)===false)break}else if(K)for(z in j){if(s.call(j[z],
z,j[z])===false)break}else for(v=j[0];H<G&&s.call(v,H,v)!==false;v=j[++H]);return j},trim:O?function(j){return j==null?"":O.call(j)}:function(j){return j==null?"":j.toString().replace(k,"").replace(o,"")},makeArray:function(j,s){var v=s||[];if(j!=null){var z=b.type(j);j.length==null||z==="string"||z==="function"||z==="regexp"||b.isWindow(j)?M.call(v,j):b.merge(v,j)}return v},inArray:function(j,s){if(s.indexOf)return s.indexOf(j);for(var v=0,z=s.length;v<z;v++)if(s[v]===j)return v;return-1},merge:function(j,
s){var v=j.length,z=0;if(typeof s.length==="number")for(var H=s.length;z<H;z++)j[v++]=s[z];else for(;s[z]!==B;)j[v++]=s[z++];j.length=v;return j},grep:function(j,s,v){var z=[],H;v=!!v;for(var G=0,K=j.length;G<K;G++){H=!!s(j[G],G);v!==H&&z.push(j[G])}return z},map:function(j,s,v){for(var z=[],H,G=0,K=j.length;G<K;G++){H=s(j[G],G,v);if(H!=null)z[z.length]=H}return z.concat.apply([],z)},guid:1,proxy:function(j,s,v){if(arguments.length===2)if(typeof s==="string"){v=j;j=v[s];s=B}else if(s&&!b.isFunction(s)){v=
s;s=B}if(!s&&j)s=function(){return j.apply(v||this,arguments)};if(j)s.guid=j.guid=j.guid||s.guid||b.guid++;return s},access:function(j,s,v,z,H,G){var K=j.length;if(typeof s==="object"){for(var Q in s)b.access(j,Q,s[Q],z,H,v);return j}if(v!==B){z=!G&&z&&b.isFunction(v);for(Q=0;Q<K;Q++)H(j[Q],s,z?v.call(j[Q],Q,H(j[Q],s)):v,G);return j}return K?H(j[0],s):B},now:function(){return(new Date).getTime()},uaMatch:function(j){j=j.toLowerCase();j=L.exec(j)||g.exec(j)||i.exec(j)||j.indexOf("compatible")<0&&n.exec(j)||
[];return{browser:j[1]||"",version:j[2]||"0"}},browser:{}});b.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(j,s){R["[object "+s+"]"]=s.toLowerCase()});m=b.uaMatch(m);if(m.browser){b.browser[m.browser]=true;b.browser.version=m.version}if(b.browser.webkit)b.browser.safari=true;if(D)b.inArray=function(j,s){return D.call(s,j)};if(!/\s/.test("\u00a0")){k=/^[\s\xA0]+/;o=/[\s\xA0]+$/}f=b(t);if(t.addEventListener)u=function(){t.removeEventListener("DOMContentLoaded",u,
false);b.ready()};else if(t.attachEvent)u=function(){if(t.readyState==="complete"){t.detachEvent("onreadystatechange",u);b.ready()}};return E.jQuery=E.$=b}();(function(){c.support={};var a=t.documentElement,b=t.createElement("script"),d=t.createElement("div"),e="script"+c.now();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";var f=d.getElementsByTagName("*"),h=d.getElementsByTagName("a")[0],l=t.createElement("select"),
k=l.appendChild(t.createElement("option"));if(!(!f||!f.length||!h)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(h.getAttribute("style")),hrefNormalized:h.getAttribute("href")==="/a",opacity:/^0.55$/.test(h.style.opacity),cssFloat:!!h.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:k.selected,deleteExpando:true,optDisabled:false,checkClone:false,
scriptEval:false,noCloneEvent:true,boxModel:null,inlineBlockNeedsLayout:false,shrinkWrapBlocks:false,reliableHiddenOffsets:true};l.disabled=true;c.support.optDisabled=!k.disabled;b.type="text/javascript";try{b.appendChild(t.createTextNode("window."+e+"=1;"))}catch(o){}a.insertBefore(b,a.firstChild);if(E[e]){c.support.scriptEval=true;delete E[e]}try{delete b.test}catch(x){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function r(){c.support.noCloneEvent=
false;d.detachEvent("onclick",r)});d.cloneNode(true).fireEvent("onclick")}d=t.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=t.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var r=t.createElement("div");r.style.width=r.style.paddingLeft="1px";t.body.appendChild(r);c.boxModel=c.support.boxModel=r.offsetWidth===2;if("zoom"in r.style){r.style.display="inline";r.style.zoom=
1;c.support.inlineBlockNeedsLayout=r.offsetWidth===2;r.style.display="";r.innerHTML="<div style='width:4px;'></div>";c.support.shrinkWrapBlocks=r.offsetWidth!==2}r.innerHTML="<table><tr><td style='padding:0;display:none'></td><td>t</td></tr></table>";var A=r.getElementsByTagName("td");c.support.reliableHiddenOffsets=A[0].offsetHeight===0;A[0].style.display="";A[1].style.display="none";c.support.reliableHiddenOffsets=c.support.reliableHiddenOffsets&&A[0].offsetHeight===0;r.innerHTML="";t.body.removeChild(r).style.display=
"none"});a=function(r){var A=t.createElement("div");r="on"+r;var C=r in A;if(!C){A.setAttribute(r,"return;");C=typeof A[r]==="function"}return C};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=f=h=null}})();var ra={},Ja=/^(?:\{.*\}|\[.*\])$/;c.extend({cache:{},uuid:0,expando:"jQuery"+c.now(),noData:{embed:true,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:true},data:function(a,b,d){if(c.acceptData(a)){a=a==E?ra:a;var e=a.nodeType,f=e?a[c.expando]:null,h=
c.cache;if(!(e&&!f&&typeof b==="string"&&d===B)){if(e)f||(a[c.expando]=f=++c.uuid);else h=a;if(typeof b==="object")if(e)h[f]=c.extend(h[f],b);else c.extend(h,b);else if(e&&!h[f])h[f]={};a=e?h[f]:h;if(d!==B)a[b]=d;return typeof b==="string"?a[b]:a}}},removeData:function(a,b){if(c.acceptData(a)){a=a==E?ra:a;var d=a.nodeType,e=d?a[c.expando]:a,f=c.cache,h=d?f[e]:e;if(b){if(h){delete h[b];d&&c.isEmptyObject(h)&&c.removeData(a)}}else if(d&&c.support.deleteExpando)delete a[c.expando];else if(a.removeAttribute)a.removeAttribute(c.expando);
else if(d)delete f[e];else for(var l in a)delete a[l]}},acceptData:function(a){if(a.nodeName){var b=c.noData[a.nodeName.toLowerCase()];if(b)return!(b===true||a.getAttribute("classid")!==b)}return true}});c.fn.extend({data:function(a,b){var d=null;if(typeof a==="undefined"){if(this.length){var e=this[0].attributes,f;d=c.data(this[0]);for(var h=0,l=e.length;h<l;h++){f=e[h].name;if(f.indexOf("data-")===0){f=f.substr(5);ka(this[0],f,d[f])}}}return d}else if(typeof a==="object")return this.each(function(){c.data(this,
a)});var k=a.split(".");k[1]=k[1]?"."+k[1]:"";if(b===B){d=this.triggerHandler("getData"+k[1]+"!",[k[0]]);if(d===B&&this.length){d=c.data(this[0],a);d=ka(this[0],a,d)}return d===B&&k[1]?this.data(k[0]):d}else return this.each(function(){var o=c(this),x=[k[0],b];o.triggerHandler("setData"+k[1]+"!",x);c.data(this,a,b);o.triggerHandler("changeData"+k[1]+"!",x)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var e=
c.data(a,b);if(!d)return e||[];if(!e||c.isArray(d))e=c.data(a,b,c.makeArray(d));else e.push(d);return e}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),e=d.shift();if(e==="inprogress")e=d.shift();if(e){b==="fx"&&d.unshift("inprogress");e.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===B)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,
a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var sa=/[\n\t]/g,ha=/\s+/,Sa=/\r/g,Ta=/^(?:href|src|style)$/,Ua=/^(?:button|input)$/i,Va=/^(?:button|input|object|select|textarea)$/i,Wa=/^a(?:rea)?$/i,ta=/^(?:radio|checkbox)$/i;c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",
colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};c.fn.extend({attr:function(a,b){return c.access(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(x){var r=c(this);r.addClass(a.call(this,x,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ha),d=0,e=this.length;d<e;d++){var f=this[d];if(f.nodeType===
1)if(f.className){for(var h=" "+f.className+" ",l=f.className,k=0,o=b.length;k<o;k++)if(h.indexOf(" "+b[k]+" ")<0)l+=" "+b[k];f.className=c.trim(l)}else f.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(o){var x=c(this);x.removeClass(a.call(this,o,x.attr("class")))});if(a&&typeof a==="string"||a===B)for(var b=(a||"").split(ha),d=0,e=this.length;d<e;d++){var f=this[d];if(f.nodeType===1&&f.className)if(a){for(var h=(" "+f.className+" ").replace(sa," "),
l=0,k=b.length;l<k;l++)h=h.replace(" "+b[l]+" "," ");f.className=c.trim(h)}else f.className=""}return this},toggleClass:function(a,b){var d=typeof a,e=typeof b==="boolean";if(c.isFunction(a))return this.each(function(f){var h=c(this);h.toggleClass(a.call(this,f,h.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var f,h=0,l=c(this),k=b,o=a.split(ha);f=o[h++];){k=e?k:!l.hasClass(f);l[k?"addClass":"removeClass"](f)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,
"__className__",this.className);this.className=this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(sa," ").indexOf(a)>-1)return true;return false},val:function(a){if(!arguments.length){var b=this[0];if(b){if(c.nodeName(b,"option")){var d=b.attributes.value;return!d||d.specified?b.value:b.text}if(c.nodeName(b,"select")){var e=b.selectedIndex;d=[];var f=b.options;b=b.type==="select-one";
if(e<0)return null;var h=b?e:0;for(e=b?e+1:f.length;h<e;h++){var l=f[h];if(l.selected&&(c.support.optDisabled?!l.disabled:l.getAttribute("disabled")===null)&&(!l.parentNode.disabled||!c.nodeName(l.parentNode,"optgroup"))){a=c(l).val();if(b)return a;d.push(a)}}return d}if(ta.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Sa,"")}return B}var k=c.isFunction(a);return this.each(function(o){var x=c(this),r=a;if(this.nodeType===1){if(k)r=
a.call(this,o,x.val());if(r==null)r="";else if(typeof r==="number")r+="";else if(c.isArray(r))r=c.map(r,function(C){return C==null?"":C+""});if(c.isArray(r)&&ta.test(this.type))this.checked=c.inArray(x.val(),r)>=0;else if(c.nodeName(this,"select")){var A=c.makeArray(r);c("option",this).each(function(){this.selected=c.inArray(c(this).val(),A)>=0});if(!A.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},
attr:function(a,b,d,e){if(!a||a.nodeType===3||a.nodeType===8)return B;if(e&&b in c.attrFn)return c(a)[b](d);e=a.nodeType!==1||!c.isXMLDoc(a);var f=d!==B;b=e&&c.props[b]||b;var h=Ta.test(b);if((b in a||a[b]!==B)&&e&&!h){if(f){b==="type"&&Ua.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");if(d===null)a.nodeType===1&&a.removeAttribute(b);else a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&
b.specified?b.value:Va.test(a.nodeName)||Wa.test(a.nodeName)&&a.href?0:B;return a[b]}if(!c.support.style&&e&&b==="style"){if(f)a.style.cssText=""+d;return a.style.cssText}f&&a.setAttribute(b,""+d);if(!a.attributes[b]&&a.hasAttribute&&!a.hasAttribute(b))return B;a=!c.support.hrefNormalized&&e&&h?a.getAttribute(b,2):a.getAttribute(b);return a===null?B:a}});var X=/\.(.*)$/,ia=/^(?:textarea|input|select)$/i,La=/\./g,Ma=/ /g,Xa=/[^\w\s.|`]/g,Ya=function(a){return a.replace(Xa,"\\$&")},ua={focusin:0,focusout:0};
c.event={add:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(c.isWindow(a)&&a!==E&&!a.frameElement)a=E;if(d===false)d=U;else if(!d)return;var f,h;if(d.handler){f=d;d=f.handler}if(!d.guid)d.guid=c.guid++;if(h=c.data(a)){var l=a.nodeType?"events":"__events__",k=h[l],o=h.handle;if(typeof k==="function"){o=k.handle;k=k.events}else if(!k){a.nodeType||(h[l]=h=function(){});h.events=k={}}if(!o)h.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,
arguments):B};o.elem=a;b=b.split(" ");for(var x=0,r;l=b[x++];){h=f?c.extend({},f):{handler:d,data:e};if(l.indexOf(".")>-1){r=l.split(".");l=r.shift();h.namespace=r.slice(0).sort().join(".")}else{r=[];h.namespace=""}h.type=l;if(!h.guid)h.guid=d.guid;var A=k[l],C=c.event.special[l]||{};if(!A){A=k[l]=[];if(!C.setup||C.setup.call(a,e,r,o)===false)if(a.addEventListener)a.addEventListener(l,o,false);else a.attachEvent&&a.attachEvent("on"+l,o)}if(C.add){C.add.call(a,h);if(!h.handler.guid)h.handler.guid=
d.guid}A.push(h);c.event.global[l]=true}a=null}}},global:{},remove:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(d===false)d=U;var f,h,l=0,k,o,x,r,A,C,J=a.nodeType?"events":"__events__",w=c.data(a),I=w&&w[J];if(w&&I){if(typeof I==="function"){w=I;I=I.events}if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(f in I)c.event.remove(a,f+b)}else{for(b=b.split(" ");f=b[l++];){r=f;k=f.indexOf(".")<0;o=[];if(!k){o=f.split(".");f=o.shift();x=RegExp("(^|\\.)"+
c.map(o.slice(0).sort(),Ya).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(A=I[f])if(d){r=c.event.special[f]||{};for(h=e||0;h<A.length;h++){C=A[h];if(d.guid===C.guid){if(k||x.test(C.namespace)){e==null&&A.splice(h--,1);r.remove&&r.remove.call(a,C)}if(e!=null)break}}if(A.length===0||e!=null&&A.length===1){if(!r.teardown||r.teardown.call(a,o)===false)c.removeEvent(a,f,w.handle);delete I[f]}}else for(h=0;h<A.length;h++){C=A[h];if(k||x.test(C.namespace)){c.event.remove(a,r,C.handler,h);A.splice(h--,1)}}}if(c.isEmptyObject(I)){if(b=
w.handle)b.elem=null;delete w.events;delete w.handle;if(typeof w==="function")c.removeData(a,J);else c.isEmptyObject(w)&&c.removeData(a)}}}}},trigger:function(a,b,d,e){var f=a.type||a;if(!e){a=typeof a==="object"?a[c.expando]?a:c.extend(c.Event(f),a):c.Event(f);if(f.indexOf("!")>=0){a.type=f=f.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[f]&&c.each(c.cache,function(){this.events&&this.events[f]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===
8)return B;a.result=B;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(e=d.nodeType?c.data(d,"handle"):(c.data(d,"__events__")||{}).handle)&&e.apply(d,b);e=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+f]&&d["on"+f].apply(d,b)===false){a.result=false;a.preventDefault()}}catch(h){}if(!a.isPropagationStopped()&&e)c.event.trigger(a,b,e,true);else if(!a.isDefaultPrevented()){var l;e=a.target;var k=f.replace(X,""),o=c.nodeName(e,"a")&&k===
"click",x=c.event.special[k]||{};if((!x._default||x._default.call(d,a)===false)&&!o&&!(e&&e.nodeName&&c.noData[e.nodeName.toLowerCase()])){try{if(e[k]){if(l=e["on"+k])e["on"+k]=null;c.event.triggered=true;e[k]()}}catch(r){}if(l)e["on"+k]=l;c.event.triggered=false}}},handle:function(a){var b,d,e,f;d=[];var h=c.makeArray(arguments);a=h[0]=c.event.fix(a||E.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;if(!b){e=a.type.split(".");a.type=e.shift();d=e.slice(0).sort();e=RegExp("(^|\\.)"+
d.join("\\.(?:.*\\.)?")+"(\\.|$)")}a.namespace=a.namespace||d.join(".");f=c.data(this,this.nodeType?"events":"__events__");if(typeof f==="function")f=f.events;d=(f||{})[a.type];if(f&&d){d=d.slice(0);f=0;for(var l=d.length;f<l;f++){var k=d[f];if(b||e.test(k.namespace)){a.handler=k.handler;a.data=k.data;a.handleObj=k;k=k.handler.apply(this,h);if(k!==B){a.result=k;if(k===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
fix:function(a){if(a[c.expando])return a;var b=a;a=c.Event(b);for(var d=this.props.length,e;d;){e=this.props[--d];a[e]=b[e]}if(!a.target)a.target=a.srcElement||t;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=t.documentElement;d=t.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(a.which==null&&(a.charCode!=null||a.keyCode!=null))a.which=a.charCode!=null?a.charCode:a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==B)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,Y(a.origType,a.selector),c.extend({},a,{handler:Ka,guid:a.handler.guid}))},remove:function(a){c.event.remove(this,
Y(a.origType,a.selector),a)}},beforeunload:{setup:function(a,b,d){if(c.isWindow(this))this.onbeforeunload=d},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};c.removeEvent=t.removeEventListener?function(a,b,d){a.removeEventListener&&a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent&&a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=a;this.type=a.type}else this.type=a;this.timeStamp=
c.now();this[c.expando]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=ca;var a=this.originalEvent;if(a)if(a.preventDefault)a.preventDefault();else a.returnValue=false},stopPropagation:function(){this.isPropagationStopped=ca;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=ca;this.stopPropagation()},isDefaultPrevented:U,isPropagationStopped:U,isImmediatePropagationStopped:U};
var va=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},wa=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?wa:va,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?wa:va)}}});if(!c.support.submitBubbles)c.event.special.submit={setup:function(){if(this.nodeName.toLowerCase()!==
"form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length){a.liveFired=B;return la("submit",this,arguments)}});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13){a.liveFired=B;return la("submit",this,arguments)}})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};if(!c.support.changeBubbles){var V,
xa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(e){return e.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},Z=function(a,b){var d=a.target,e,f;if(!(!ia.test(d.nodeName)||d.readOnly)){e=c.data(d,"_change_data");f=xa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",f);if(!(e===B||f===e))if(e!=null||f){a.type="change";a.liveFired=
B;return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:Z,beforedeactivate:Z,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return Z.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return Z.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,"_change_data",xa(a))}},setup:function(){if(this.type===
"file")return false;for(var a in V)c.event.add(this,a+".specialChange",V[a]);return ia.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return ia.test(this.nodeName)}};V=c.event.special.change.filters;V.focus=V.beforeactivate}t.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(e){e=c.event.fix(e);e.type=b;return c.event.trigger(e,null,e.target)}c.event.special[b]={setup:function(){ua[b]++===0&&t.addEventListener(a,d,true)},teardown:function(){--ua[b]===
0&&t.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,e,f){if(typeof d==="object"){for(var h in d)this[b](h,e,d[h],f);return this}if(c.isFunction(e)||e===false){f=e;e=B}var l=b==="one"?c.proxy(f,function(o){c(this).unbind(o,l);return f.apply(this,arguments)}):f;if(d==="unload"&&b!=="one")this.one(d,e,f);else{h=0;for(var k=this.length;h<k;h++)c.event.add(this[h],d,l,e)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&!a.preventDefault)for(var d in a)this.unbind(d,
a[d]);else{d=0;for(var e=this.length;d<e;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,e){return this.live(b,d,e,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){var d=c.Event(a);d.preventDefault();d.stopPropagation();c.event.trigger(d,b,this[0]);return d.result}},toggle:function(a){for(var b=arguments,d=
1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(e){var f=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,f+1);e.preventDefault();return b[f].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var ya={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,e,f,h){var l,k=0,o,x,r=h||this.selector;h=h?this:c(this.context);if(typeof d===
"object"&&!d.preventDefault){for(l in d)h[b](l,e,d[l],r);return this}if(c.isFunction(e)){f=e;e=B}for(d=(d||"").split(" ");(l=d[k++])!=null;){o=X.exec(l);x="";if(o){x=o[0];l=l.replace(X,"")}if(l==="hover")d.push("mouseenter"+x,"mouseleave"+x);else{o=l;if(l==="focus"||l==="blur"){d.push(ya[l]+x);l+=x}else l=(ya[l]||l)+x;if(b==="live"){x=0;for(var A=h.length;x<A;x++)c.event.add(h[x],"live."+Y(l,r),{data:e,selector:r,handler:f,origType:l,origHandler:f,preType:o})}else h.unbind("live."+Y(l,r),f)}}return this}});
c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),function(a,b){c.fn[b]=function(d,e){if(e==null){e=d;d=null}return arguments.length>0?this.bind(b,d,e):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});E.attachEvent&&!E.addEventListener&&c(E).bind("unload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});
(function(){function a(g,i,n,m,p,q){p=0;for(var u=m.length;p<u;p++){var y=m[p];if(y){var F=false;for(y=y[g];y;){if(y.sizcache===n){F=m[y.sizset];break}if(y.nodeType===1&&!q){y.sizcache=n;y.sizset=p}if(y.nodeName.toLowerCase()===i){F=y;break}y=y[g]}m[p]=F}}}function b(g,i,n,m,p,q){p=0;for(var u=m.length;p<u;p++){var y=m[p];if(y){var F=false;for(y=y[g];y;){if(y.sizcache===n){F=m[y.sizset];break}if(y.nodeType===1){if(!q){y.sizcache=n;y.sizset=p}if(typeof i!=="string"){if(y===i){F=true;break}}else if(k.filter(i,
[y]).length>0){F=y;break}}y=y[g]}m[p]=F}}}var d=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,f=Object.prototype.toString,h=false,l=true;[0,0].sort(function(){l=false;return 0});var k=function(g,i,n,m){n=n||[];var p=i=i||t;if(i.nodeType!==1&&i.nodeType!==9)return[];if(!g||typeof g!=="string")return n;var q,u,y,F,M,N=true,O=k.isXML(i),D=[],R=g;do{d.exec("");if(q=d.exec(R)){R=q[3];D.push(q[1]);if(q[2]){F=q[3];
break}}}while(q);if(D.length>1&&x.exec(g))if(D.length===2&&o.relative[D[0]])u=L(D[0]+D[1],i);else for(u=o.relative[D[0]]?[i]:k(D.shift(),i);D.length;){g=D.shift();if(o.relative[g])g+=D.shift();u=L(g,u)}else{if(!m&&D.length>1&&i.nodeType===9&&!O&&o.match.ID.test(D[0])&&!o.match.ID.test(D[D.length-1])){q=k.find(D.shift(),i,O);i=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]}if(i){q=m?{expr:D.pop(),set:C(m)}:k.find(D.pop(),D.length===1&&(D[0]==="~"||D[0]==="+")&&i.parentNode?i.parentNode:i,O);u=q.expr?k.filter(q.expr,
q.set):q.set;if(D.length>0)y=C(u);else N=false;for(;D.length;){q=M=D.pop();if(o.relative[M])q=D.pop();else M="";if(q==null)q=i;o.relative[M](y,q,O)}}else y=[]}y||(y=u);y||k.error(M||g);if(f.call(y)==="[object Array]")if(N)if(i&&i.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&k.contains(i,y[g])))n.push(u[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&n.push(u[g]);else n.push.apply(n,y);else C(y,n);if(F){k(F,p,n,m);k.uniqueSort(n)}return n};k.uniqueSort=function(g){if(w){h=
l;g.sort(w);if(h)for(var i=1;i<g.length;i++)g[i]===g[i-1]&&g.splice(i--,1)}return g};k.matches=function(g,i){return k(g,null,null,i)};k.matchesSelector=function(g,i){return k(i,null,null,[g]).length>0};k.find=function(g,i,n){var m;if(!g)return[];for(var p=0,q=o.order.length;p<q;p++){var u,y=o.order[p];if(u=o.leftMatch[y].exec(g)){var F=u[1];u.splice(1,1);if(F.substr(F.length-1)!=="\\"){u[1]=(u[1]||"").replace(/\\/g,"");m=o.find[y](u,i,n);if(m!=null){g=g.replace(o.match[y],"");break}}}}m||(m=i.getElementsByTagName("*"));
return{set:m,expr:g}};k.filter=function(g,i,n,m){for(var p,q,u=g,y=[],F=i,M=i&&i[0]&&k.isXML(i[0]);g&&i.length;){for(var N in o.filter)if((p=o.leftMatch[N].exec(g))!=null&&p[2]){var O,D,R=o.filter[N];D=p[1];q=false;p.splice(1,1);if(D.substr(D.length-1)!=="\\"){if(F===y)y=[];if(o.preFilter[N])if(p=o.preFilter[N](p,F,n,y,m,M)){if(p===true)continue}else q=O=true;if(p)for(var j=0;(D=F[j])!=null;j++)if(D){O=R(D,p,j,F);var s=m^!!O;if(n&&O!=null)if(s)q=true;else F[j]=false;else if(s){y.push(D);q=true}}if(O!==
B){n||(F=y);g=g.replace(o.match[N],"");if(!q)return[];break}}}if(g===u)if(q==null)k.error(g);else break;u=g}return F};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var o=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+\-]*)\))?/,
POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},relative:{"+":function(g,i){var n=typeof i==="string",m=n&&!/\W/.test(i);n=n&&!m;if(m)i=i.toLowerCase();m=0;for(var p=g.length,q;m<p;m++)if(q=g[m]){for(;(q=q.previousSibling)&&q.nodeType!==1;);g[m]=n||q&&q.nodeName.toLowerCase()===
i?q||false:q===i}n&&k.filter(i,g,true)},">":function(g,i){var n,m=typeof i==="string",p=0,q=g.length;if(m&&!/\W/.test(i))for(i=i.toLowerCase();p<q;p++){if(n=g[p]){n=n.parentNode;g[p]=n.nodeName.toLowerCase()===i?n:false}}else{for(;p<q;p++)if(n=g[p])g[p]=m?n.parentNode:n.parentNode===i;m&&k.filter(i,g,true)}},"":function(g,i,n){var m,p=e++,q=b;if(typeof i==="string"&&!/\W/.test(i)){m=i=i.toLowerCase();q=a}q("parentNode",i,p,g,m,n)},"~":function(g,i,n){var m,p=e++,q=b;if(typeof i==="string"&&!/\W/.test(i)){m=
i=i.toLowerCase();q=a}q("previousSibling",i,p,g,m,n)}},find:{ID:function(g,i,n){if(typeof i.getElementById!=="undefined"&&!n)return(g=i.getElementById(g[1]))&&g.parentNode?[g]:[]},NAME:function(g,i){if(typeof i.getElementsByName!=="undefined"){for(var n=[],m=i.getElementsByName(g[1]),p=0,q=m.length;p<q;p++)m[p].getAttribute("name")===g[1]&&n.push(m[p]);return n.length===0?null:n}},TAG:function(g,i){return i.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,i,n,m,p,q){g=" "+g[1].replace(/\\/g,
"")+" ";if(q)return g;q=0;for(var u;(u=i[q])!=null;q++)if(u)if(p^(u.className&&(" "+u.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))n||m.push(u);else if(n)i[q]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},CHILD:function(g){if(g[1]==="nth"){var i=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=i[1]+(i[2]||1)-0;g[3]=i[3]-0}g[0]=e++;return g},ATTR:function(g,i,n,
m,p,q){i=g[1].replace(/\\/g,"");if(!q&&o.attrMap[i])g[1]=o.attrMap[i];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,i,n,m,p){if(g[1]==="not")if((d.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,i);else{g=k.filter(g[3],i,n,true^p);n||m.push.apply(m,g);return false}else if(o.match.POS.test(g[0])||o.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===
true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,i,n){return!!k(n[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===
g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},setFilters:{first:function(g,i){return i===0},last:function(g,i,n,m){return i===m.length-1},even:function(g,i){return i%2===0},odd:function(g,i){return i%2===1},lt:function(g,i,n){return i<n[3]-0},gt:function(g,i,n){return i>n[3]-0},nth:function(g,i,n){return n[3]-
0===i},eq:function(g,i,n){return n[3]-0===i}},filter:{PSEUDO:function(g,i,n,m){var p=i[1],q=o.filters[p];if(q)return q(g,n,i,m);else if(p==="contains")return(g.textContent||g.innerText||k.getText([g])||"").indexOf(i[3])>=0;else if(p==="not"){i=i[3];n=0;for(m=i.length;n<m;n++)if(i[n]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+p)},CHILD:function(g,i){var n=i[1],m=g;switch(n){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(n===
"first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":n=i[2];var p=i[3];if(n===1&&p===0)return true;var q=i[0],u=g.parentNode;if(u&&(u.sizcache!==q||!g.nodeIndex)){var y=0;for(m=u.firstChild;m;m=m.nextSibling)if(m.nodeType===1)m.nodeIndex=++y;u.sizcache=q}m=g.nodeIndex-p;return n===0?m===0:m%n===0&&m/n>=0}},ID:function(g,i){return g.nodeType===1&&g.getAttribute("id")===i},TAG:function(g,i){return i==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===
i},CLASS:function(g,i){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(i)>-1},ATTR:function(g,i){var n=i[1];n=o.attrHandle[n]?o.attrHandle[n](g):g[n]!=null?g[n]:g.getAttribute(n);var m=n+"",p=i[2],q=i[4];return n==null?p==="!=":p==="="?m===q:p==="*="?m.indexOf(q)>=0:p==="~="?(" "+m+" ").indexOf(q)>=0:!q?m&&n!==false:p==="!="?m!==q:p==="^="?m.indexOf(q)===0:p==="$="?m.substr(m.length-q.length)===q:p==="|="?m===q||m.substr(0,q.length+1)===q+"-":false},POS:function(g,i,n,m){var p=o.setFilters[i[2]];
if(p)return p(g,n,i,m)}}},x=o.match.POS,r=function(g,i){return"\\"+(i-0+1)},A;for(A in o.match){o.match[A]=RegExp(o.match[A].source+/(?![^\[]*\])(?![^\(]*\))/.source);o.leftMatch[A]=RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[A].source.replace(/\\(\d+)/g,r))}var C=function(g,i){g=Array.prototype.slice.call(g,0);if(i){i.push.apply(i,g);return i}return g};try{Array.prototype.slice.call(t.documentElement.childNodes,0)}catch(J){C=function(g,i){var n=0,m=i||[];if(f.call(g)==="[object Array]")Array.prototype.push.apply(m,
g);else if(typeof g.length==="number")for(var p=g.length;n<p;n++)m.push(g[n]);else for(;g[n];n++)m.push(g[n]);return m}}var w,I;if(t.documentElement.compareDocumentPosition)w=function(g,i){if(g===i){h=true;return 0}if(!g.compareDocumentPosition||!i.compareDocumentPosition)return g.compareDocumentPosition?-1:1;return g.compareDocumentPosition(i)&4?-1:1};else{w=function(g,i){var n,m,p=[],q=[];n=g.parentNode;m=i.parentNode;var u=n;if(g===i){h=true;return 0}else if(n===m)return I(g,i);else if(n){if(!m)return 1}else return-1;
for(;u;){p.unshift(u);u=u.parentNode}for(u=m;u;){q.unshift(u);u=u.parentNode}n=p.length;m=q.length;for(u=0;u<n&&u<m;u++)if(p[u]!==q[u])return I(p[u],q[u]);return u===n?I(g,q[u],-1):I(p[u],i,1)};I=function(g,i,n){if(g===i)return n;for(g=g.nextSibling;g;){if(g===i)return-1;g=g.nextSibling}return 1}}k.getText=function(g){for(var i="",n,m=0;g[m];m++){n=g[m];if(n.nodeType===3||n.nodeType===4)i+=n.nodeValue;else if(n.nodeType!==8)i+=k.getText(n.childNodes)}return i};(function(){var g=t.createElement("div"),
i="script"+(new Date).getTime(),n=t.documentElement;g.innerHTML="<a name='"+i+"'/>";n.insertBefore(g,n.firstChild);if(t.getElementById(i)){o.find.ID=function(m,p,q){if(typeof p.getElementById!=="undefined"&&!q)return(p=p.getElementById(m[1]))?p.id===m[1]||typeof p.getAttributeNode!=="undefined"&&p.getAttributeNode("id").nodeValue===m[1]?[p]:B:[]};o.filter.ID=function(m,p){var q=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&q&&q.nodeValue===p}}n.removeChild(g);
n=g=null})();(function(){var g=t.createElement("div");g.appendChild(t.createComment(""));if(g.getElementsByTagName("*").length>0)o.find.TAG=function(i,n){var m=n.getElementsByTagName(i[1]);if(i[1]==="*"){for(var p=[],q=0;m[q];q++)m[q].nodeType===1&&p.push(m[q]);m=p}return m};g.innerHTML="<a href='#'></a>";if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")o.attrHandle.href=function(i){return i.getAttribute("href",2)};g=null})();t.querySelectorAll&&
function(){var g=k,i=t.createElement("div");i.innerHTML="<p class='TEST'></p>";if(!(i.querySelectorAll&&i.querySelectorAll(".TEST").length===0)){k=function(m,p,q,u){p=p||t;m=m.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!u&&!k.isXML(p))if(p.nodeType===9)try{return C(p.querySelectorAll(m),q)}catch(y){}else if(p.nodeType===1&&p.nodeName.toLowerCase()!=="object"){var F=p.getAttribute("id"),M=F||"__sizzle__";F||p.setAttribute("id",M);try{return C(p.querySelectorAll("#"+M+" "+m),q)}catch(N){}finally{F||
p.removeAttribute("id")}}return g(m,p,q,u)};for(var n in g)k[n]=g[n];i=null}}();(function(){var g=t.documentElement,i=g.matchesSelector||g.mozMatchesSelector||g.webkitMatchesSelector||g.msMatchesSelector,n=false;try{i.call(t.documentElement,"[test!='']:sizzle")}catch(m){n=true}if(i)k.matchesSelector=function(p,q){q=q.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(p))try{if(n||!o.match.PSEUDO.test(q)&&!/!=/.test(q))return i.call(p,q)}catch(u){}return k(q,null,null,[p]).length>0}})();(function(){var g=
t.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){o.order.splice(1,0,"CLASS");o.find.CLASS=function(i,n,m){if(typeof n.getElementsByClassName!=="undefined"&&!m)return n.getElementsByClassName(i[1])};g=null}}})();k.contains=t.documentElement.contains?function(g,i){return g!==i&&(g.contains?g.contains(i):true)}:t.documentElement.compareDocumentPosition?
function(g,i){return!!(g.compareDocumentPosition(i)&16)}:function(){return false};k.isXML=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false};var L=function(g,i){for(var n,m=[],p="",q=i.nodeType?[i]:i;n=o.match.PSEUDO.exec(g);){p+=n[0];g=g.replace(o.match.PSEUDO,"")}g=o.relative[g]?g+"*":g;n=0;for(var u=q.length;n<u;n++)k(g,q[n],m);return k.filter(p,m)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=k.getText;c.isXMLDoc=k.isXML;
c.contains=k.contains})();var Za=/Until$/,$a=/^(?:parents|prevUntil|prevAll)/,ab=/,/,Na=/^.[^:#\[\.,]*$/,bb=Array.prototype.slice,cb=c.expr.match.POS;c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,e=0,f=this.length;e<f;e++){d=b.length;c.find(a,this[e],b);if(e>0)for(var h=d;h<b.length;h++)for(var l=0;l<d;l++)if(b[l]===b[h]){b.splice(h--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,e=b.length;d<e;d++)if(c.contains(this,b[d]))return true})},
not:function(a){return this.pushStack(ma(this,a,false),"not",a)},filter:function(a){return this.pushStack(ma(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){var d=[],e,f,h=this[0];if(c.isArray(a)){var l,k={},o=1;if(h&&a.length){e=0;for(f=a.length;e<f;e++){l=a[e];k[l]||(k[l]=c.expr.match.POS.test(l)?c(l,b||this.context):l)}for(;h&&h.ownerDocument&&h!==b;){for(l in k){e=k[l];if(e.jquery?e.index(h)>-1:c(h).is(e))d.push({selector:l,elem:h,level:o})}h=
h.parentNode;o++}}return d}l=cb.test(a)?c(a,b||this.context):null;e=0;for(f=this.length;e<f;e++)for(h=this[e];h;)if(l?l.index(h)>-1:c.find.matchesSelector(h,a)){d.push(h);break}else{h=h.parentNode;if(!h||!h.ownerDocument||h===b)break}d=d.length>1?c.unique(d):d;return this.pushStack(d,"closest",a)},index:function(a){if(!a||typeof a==="string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var d=typeof a==="string"?c(a,b||this.context):
c.makeArray(a),e=c.merge(this.get(),d);return this.pushStack(!d[0]||!d[0].parentNode||d[0].parentNode.nodeType===11||!e[0]||!e[0].parentNode||e[0].parentNode.nodeType===11?e:c.unique(e))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,
2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,
b){c.fn[a]=function(d,e){var f=c.map(this,b,d);Za.test(a)||(e=d);if(e&&typeof e==="string")f=c.filter(e,f);f=this.length>1?c.unique(f):f;if((this.length>1||ab.test(e))&&$a.test(a))f=f.reverse();return this.pushStack(f,a,bb.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return b.length===1?c.find.matchesSelector(b[0],a)?[b[0]]:[]:c.find.matches(a,b)},dir:function(a,b,d){var e=[];for(a=a[b];a&&a.nodeType!==9&&(d===B||a.nodeType!==1||!c(a).is(d));){a.nodeType===1&&
e.push(a);a=a[b]}return e},nth:function(a,b,d){b=b||1;for(var e=0;a;a=a[d])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var za=/ jQuery\d+="(?:\d+|null)"/g,$=/^\s+/,Aa=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Ba=/<([\w:]+)/,db=/<tbody/i,eb=/<|&#?\w+;/,Ca=/<(?:script|object|embed|option|style)/i,Da=/checked\s*(?:[^=]|=\s*.checked.)/i,fb=/\=([^="'>\s]+\/)>/g,P={option:[1,
"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};P.optgroup=P.option;P.tbody=P.tfoot=P.colgroup=P.caption=P.thead;P.th=P.td;if(!c.support.htmlSerialize)P._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==B)return this.empty().append((this[0]&&this[0].ownerDocument||t).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,e;(e=this[d])!=null;d++)if(!a||c.filter(a,[e]).length){if(!b&&e.nodeType===1){c.cleanData(e.getElementsByTagName("*"));c.cleanData([e])}e.parentNode&&e.parentNode.removeChild(e)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,e=this.ownerDocument;if(!d){d=e.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(za,"").replace(fb,'="$1">').replace($,"")],e)[0]}else return this.cloneNode(true)});if(a===true){na(this,b);na(this.find("*"),b.find("*"))}return b},html:function(a){if(a===B)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(za,""):null;
else if(typeof a==="string"&&!Ca.test(a)&&(c.support.leadingWhitespace||!$.test(a))&&!P[(Ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Aa,"<$1></$2>");try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(e){this.empty().append(a)}}else c.isFunction(a)?this.each(function(f){var h=c(this);h.html(a.call(this,f,h.html()))}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=
c(this),e=d.html();d.replaceWith(a.call(this,b,e))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){var e,f,h,l=a[0],k=[];if(!c.support.checkClone&&arguments.length===3&&typeof l==="string"&&Da.test(l))return this.each(function(){c(this).domManip(a,
b,d,true)});if(c.isFunction(l))return this.each(function(x){var r=c(this);a[0]=l.call(this,x,b?r.html():B);r.domManip(a,b,d)});if(this[0]){e=l&&l.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:c.buildFragment(a,this,k);h=e.fragment;if(f=h.childNodes.length===1?h=h.firstChild:h.firstChild){b=b&&c.nodeName(f,"tr");f=0;for(var o=this.length;f<o;f++)d.call(b?c.nodeName(this[f],"table")?this[f].getElementsByTagName("tbody")[0]||this[f].appendChild(this[f].ownerDocument.createElement("tbody")):
this[f]:this[f],f>0||e.cacheable||this.length>1?h.cloneNode(true):h)}k.length&&c.each(k,Oa)}return this}});c.buildFragment=function(a,b,d){var e,f,h;b=b&&b[0]?b[0].ownerDocument||b[0]:t;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===t&&!Ca.test(a[0])&&(c.support.checkClone||!Da.test(a[0]))){f=true;if(h=c.fragments[a[0]])if(h!==1)e=h}if(!e){e=b.createDocumentFragment();c.clean(a,b,e,d)}if(f)c.fragments[a[0]]=h?e:1;return{fragment:e,cacheable:f}};c.fragments={};c.each({appendTo:"append",
prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var e=[];d=c(d);var f=this.length===1&&this[0].parentNode;if(f&&f.nodeType===11&&f.childNodes.length===1&&d.length===1){d[b](this[0]);return this}else{f=0;for(var h=d.length;f<h;f++){var l=(f>0?this.clone(true):this).get();c(d[f])[b](l);e=e.concat(l)}return this.pushStack(e,a,d.selector)}}});c.extend({clean:function(a,b,d,e){b=b||t;if(typeof b.createElement==="undefined")b=b.ownerDocument||
b[0]&&b[0].ownerDocument||t;for(var f=[],h=0,l;(l=a[h])!=null;h++){if(typeof l==="number")l+="";if(l){if(typeof l==="string"&&!eb.test(l))l=b.createTextNode(l);else if(typeof l==="string"){l=l.replace(Aa,"<$1></$2>");var k=(Ba.exec(l)||["",""])[1].toLowerCase(),o=P[k]||P._default,x=o[0],r=b.createElement("div");for(r.innerHTML=o[1]+l+o[2];x--;)r=r.lastChild;if(!c.support.tbody){x=db.test(l);k=k==="table"&&!x?r.firstChild&&r.firstChild.childNodes:o[1]==="<table>"&&!x?r.childNodes:[];for(o=k.length-
1;o>=0;--o)c.nodeName(k[o],"tbody")&&!k[o].childNodes.length&&k[o].parentNode.removeChild(k[o])}!c.support.leadingWhitespace&&$.test(l)&&r.insertBefore(b.createTextNode($.exec(l)[0]),r.firstChild);l=r.childNodes}if(l.nodeType)f.push(l);else f=c.merge(f,l)}}if(d)for(h=0;f[h];h++)if(e&&c.nodeName(f[h],"script")&&(!f[h].type||f[h].type.toLowerCase()==="text/javascript"))e.push(f[h].parentNode?f[h].parentNode.removeChild(f[h]):f[h]);else{f[h].nodeType===1&&f.splice.apply(f,[h+1,0].concat(c.makeArray(f[h].getElementsByTagName("script"))));
d.appendChild(f[h])}return f},cleanData:function(a){for(var b,d,e=c.cache,f=c.event.special,h=c.support.deleteExpando,l=0,k;(k=a[l])!=null;l++)if(!(k.nodeName&&c.noData[k.nodeName.toLowerCase()]))if(d=k[c.expando]){if((b=e[d])&&b.events)for(var o in b.events)f[o]?c.event.remove(k,o):c.removeEvent(k,o,b.handle);if(h)delete k[c.expando];else k.removeAttribute&&k.removeAttribute(c.expando);delete e[d]}}});var Ea=/alpha\([^)]*\)/i,gb=/opacity=([^)]*)/,hb=/-([a-z])/ig,ib=/([A-Z])/g,Fa=/^-?\d+(?:px)?$/i,
jb=/^-?\d/,kb={position:"absolute",visibility:"hidden",display:"block"},Pa=["Left","Right"],Qa=["Top","Bottom"],W,Ga,aa,lb=function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){if(arguments.length===2&&b===B)return this;return c.access(this,a,b,true,function(d,e,f){return f!==B?c.style(d,e,f):c.css(d,e)})};c.extend({cssHooks:{opacity:{get:function(a,b){if(b){var d=W(a,"opacity","opacity");return d===""?"1":d}else return a.style.opacity}}},cssNumber:{zIndex:true,fontWeight:true,opacity:true,
zoom:true,lineHeight:true},cssProps:{"float":c.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,d,e){if(!(!a||a.nodeType===3||a.nodeType===8||!a.style)){var f,h=c.camelCase(b),l=a.style,k=c.cssHooks[h];b=c.cssProps[h]||h;if(d!==B){if(!(typeof d==="number"&&isNaN(d)||d==null)){if(typeof d==="number"&&!c.cssNumber[h])d+="px";if(!k||!("set"in k)||(d=k.set(a,d))!==B)try{l[b]=d}catch(o){}}}else{if(k&&"get"in k&&(f=k.get(a,false,e))!==B)return f;return l[b]}}},css:function(a,b,d){var e,f=c.camelCase(b),
h=c.cssHooks[f];b=c.cssProps[f]||f;if(h&&"get"in h&&(e=h.get(a,true,d))!==B)return e;else if(W)return W(a,b,f)},swap:function(a,b,d){var e={},f;for(f in b){e[f]=a.style[f];a.style[f]=b[f]}d.call(a);for(f in b)a.style[f]=e[f]},camelCase:function(a){return a.replace(hb,lb)}});c.curCSS=c.css;c.each(["height","width"],function(a,b){c.cssHooks[b]={get:function(d,e,f){var h;if(e){if(d.offsetWidth!==0)h=oa(d,b,f);else c.swap(d,kb,function(){h=oa(d,b,f)});if(h<=0){h=W(d,b,b);if(h==="0px"&&aa)h=aa(d,b,b);
if(h!=null)return h===""||h==="auto"?"0px":h}if(h<0||h==null){h=d.style[b];return h===""||h==="auto"?"0px":h}return typeof h==="string"?h:h+"px"}},set:function(d,e){if(Fa.test(e)){e=parseFloat(e);if(e>=0)return e+"px"}else return e}}});if(!c.support.opacity)c.cssHooks.opacity={get:function(a,b){return gb.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var d=a.style;d.zoom=1;var e=c.isNaN(b)?"":"alpha(opacity="+b*100+")",f=
d.filter||"";d.filter=Ea.test(f)?f.replace(Ea,e):d.filter+" "+e}};if(t.defaultView&&t.defaultView.getComputedStyle)Ga=function(a,b,d){var e;d=d.replace(ib,"-$1").toLowerCase();if(!(b=a.ownerDocument.defaultView))return B;if(b=b.getComputedStyle(a,null)){e=b.getPropertyValue(d);if(e===""&&!c.contains(a.ownerDocument.documentElement,a))e=c.style(a,d)}return e};if(t.documentElement.currentStyle)aa=function(a,b){var d,e,f=a.currentStyle&&a.currentStyle[b],h=a.style;if(!Fa.test(f)&&jb.test(f)){d=h.left;
e=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;h.left=b==="fontSize"?"1em":f||0;f=h.pixelLeft+"px";h.left=d;a.runtimeStyle.left=e}return f===""?"auto":f};W=Ga||aa;if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=a.offsetHeight;return a.offsetWidth===0&&b===0||!c.support.reliableHiddenOffsets&&(a.style.display||c.css(a,"display"))==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var mb=c.now(),nb=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
ob=/^(?:select|textarea)/i,pb=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,qb=/^(?:GET|HEAD)$/,Ra=/\[\]$/,T=/\=\?(&|$)/,ja=/\?/,rb=/([?&])_=[^&]*/,sb=/^(\w+:)?\/\/([^\/?#]+)/,tb=/%20/g,ub=/#.*$/,Ha=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!=="string"&&Ha)return Ha.apply(this,arguments);else if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var f=a.slice(e,a.length);a=a.slice(0,e)}e="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b===
"object"){b=c.param(b,c.ajaxSettings.traditional);e="POST"}var h=this;c.ajax({url:a,type:e,dataType:"html",data:b,complete:function(l,k){if(k==="success"||k==="notmodified")h.html(f?c("<div>").append(l.responseText.replace(nb,"")).find(f):l.responseText);d&&h.each(d,[l.responseText,k,l])}});return this},serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&
!this.disabled&&(this.checked||ob.test(this.nodeName)||pb.test(this.type))}).map(function(a,b){var d=c(this).val();return d==null?null:c.isArray(d)?c.map(d,function(e){return{name:b.name,value:e}}):{name:b.name,value:d}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:e})},
getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:e})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return new E.XMLHttpRequest},accepts:{xml:"application/xml, text/xml",html:"text/html",
script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},ajax:function(a){var b=c.extend(true,{},c.ajaxSettings,a),d,e,f,h=b.type.toUpperCase(),l=qb.test(h);b.url=b.url.replace(ub,"");b.context=a&&a.context!=null?a.context:b;if(b.data&&b.processData&&typeof b.data!=="string")b.data=c.param(b.data,b.traditional);if(b.dataType==="jsonp"){if(h==="GET")T.test(b.url)||(b.url+=(ja.test(b.url)?"&":"?")+(b.jsonp||"callback")+"=?");else if(!b.data||
!T.test(b.data))b.data=(b.data?b.data+"&":"")+(b.jsonp||"callback")+"=?";b.dataType="json"}if(b.dataType==="json"&&(b.data&&T.test(b.data)||T.test(b.url))){d=b.jsonpCallback||"jsonp"+mb++;if(b.data)b.data=(b.data+"").replace(T,"="+d+"$1");b.url=b.url.replace(T,"="+d+"$1");b.dataType="script";var k=E[d];E[d]=function(m){if(c.isFunction(k))k(m);else{E[d]=B;try{delete E[d]}catch(p){}}f=m;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);r&&r.removeChild(A)}}if(b.dataType==="script"&&b.cache===null)b.cache=
false;if(b.cache===false&&l){var o=c.now(),x=b.url.replace(rb,"$1_="+o);b.url=x+(x===b.url?(ja.test(b.url)?"&":"?")+"_="+o:"")}if(b.data&&l)b.url+=(ja.test(b.url)?"&":"?")+b.data;b.global&&c.active++===0&&c.event.trigger("ajaxStart");o=(o=sb.exec(b.url))&&(o[1]&&o[1].toLowerCase()!==location.protocol||o[2].toLowerCase()!==location.host);if(b.dataType==="script"&&h==="GET"&&o){var r=t.getElementsByTagName("head")[0]||t.documentElement,A=t.createElement("script");if(b.scriptCharset)A.charset=b.scriptCharset;
A.src=b.url;if(!d){var C=false;A.onload=A.onreadystatechange=function(){if(!C&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){C=true;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);A.onload=A.onreadystatechange=null;r&&A.parentNode&&r.removeChild(A)}}}r.insertBefore(A,r.firstChild);return B}var J=false,w=b.xhr();if(w){b.username?w.open(h,b.url,b.async,b.username,b.password):w.open(h,b.url,b.async);try{if(b.data!=null&&!l||a&&a.contentType)w.setRequestHeader("Content-Type",
b.contentType);if(b.ifModified){c.lastModified[b.url]&&w.setRequestHeader("If-Modified-Since",c.lastModified[b.url]);c.etag[b.url]&&w.setRequestHeader("If-None-Match",c.etag[b.url])}o||w.setRequestHeader("X-Requested-With","XMLHttpRequest");w.setRequestHeader("Accept",b.dataType&&b.accepts[b.dataType]?b.accepts[b.dataType]+", */*; q=0.01":b.accepts._default)}catch(I){}if(b.beforeSend&&b.beforeSend.call(b.context,w,b)===false){b.global&&c.active--===1&&c.event.trigger("ajaxStop");w.abort();return false}b.global&&
c.triggerGlobal(b,"ajaxSend",[w,b]);var L=w.onreadystatechange=function(m){if(!w||w.readyState===0||m==="abort"){J||c.handleComplete(b,w,e,f);J=true;if(w)w.onreadystatechange=c.noop}else if(!J&&w&&(w.readyState===4||m==="timeout")){J=true;w.onreadystatechange=c.noop;e=m==="timeout"?"timeout":!c.httpSuccess(w)?"error":b.ifModified&&c.httpNotModified(w,b.url)?"notmodified":"success";var p;if(e==="success")try{f=c.httpData(w,b.dataType,b)}catch(q){e="parsererror";p=q}if(e==="success"||e==="notmodified")d||
c.handleSuccess(b,w,e,f);else c.handleError(b,w,e,p);d||c.handleComplete(b,w,e,f);m==="timeout"&&w.abort();if(b.async)w=null}};try{var g=w.abort;w.abort=function(){w&&Function.prototype.call.call(g,w);L("abort")}}catch(i){}b.async&&b.timeout>0&&setTimeout(function(){w&&!J&&L("timeout")},b.timeout);try{w.send(l||b.data==null?null:b.data)}catch(n){c.handleError(b,w,null,n);c.handleComplete(b,w,e,f)}b.async||L();return w}},param:function(a,b){var d=[],e=function(h,l){l=c.isFunction(l)?l():l;d[d.length]=
encodeURIComponent(h)+"="+encodeURIComponent(l)};if(b===B)b=c.ajaxSettings.traditional;if(c.isArray(a)||a.jquery)c.each(a,function(){e(this.name,this.value)});else for(var f in a)da(f,a[f],b,e);return d.join("&").replace(tb,"+")}});c.extend({active:0,lastModified:{},etag:{},handleError:function(a,b,d,e){a.error&&a.error.call(a.context,b,d,e);a.global&&c.triggerGlobal(a,"ajaxError",[b,a,e])},handleSuccess:function(a,b,d,e){a.success&&a.success.call(a.context,e,d,b);a.global&&c.triggerGlobal(a,"ajaxSuccess",
[b,a])},handleComplete:function(a,b,d){a.complete&&a.complete.call(a.context,b,d);a.global&&c.triggerGlobal(a,"ajaxComplete",[b,a]);a.global&&c.active--===1&&c.event.trigger("ajaxStop")},triggerGlobal:function(a,b,d){(a.context&&a.context.url==null?c(a.context):c.event).trigger(b,d)},httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===1223}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),
e=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(e)c.etag[b]=e;return a.status===304},httpData:function(a,b,d){var e=a.getResponseHeader("content-type")||"",f=b==="xml"||!b&&e.indexOf("xml")>=0;a=f?a.responseXML:a.responseText;f&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b==="json"||!b&&e.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&e.indexOf("javascript")>=0)c.globalEval(a);return a}});
if(E.ActiveXObject)c.ajaxSettings.xhr=function(){if(E.location.protocol!=="file:")try{return new E.XMLHttpRequest}catch(a){}try{return new E.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}};c.support.ajax=!!c.ajaxSettings.xhr();var ea={},vb=/^(?:toggle|show|hide)$/,wb=/^([+\-]=)?([\d+.\-]+)(.*)$/,ba,pa=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b,d){if(a||a===0)return this.animate(S("show",
3),a,b,d);else{d=0;for(var e=this.length;d<e;d++){a=this[d];b=a.style.display;if(!c.data(a,"olddisplay")&&b==="none")b=a.style.display="";b===""&&c.css(a,"display")==="none"&&c.data(a,"olddisplay",qa(a.nodeName))}for(d=0;d<e;d++){a=this[d];b=a.style.display;if(b===""||b==="none")a.style.display=c.data(a,"olddisplay")||""}return this}},hide:function(a,b,d){if(a||a===0)return this.animate(S("hide",3),a,b,d);else{a=0;for(b=this.length;a<b;a++){d=c.css(this[a],"display");d!=="none"&&c.data(this[a],"olddisplay",
d)}for(a=0;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b,d){var e=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||e?this.each(function(){var f=e?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(S("toggle",3),a,b,d);return this},fadeTo:function(a,b,d,e){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d,e)},animate:function(a,b,d,e){var f=c.speed(b,
d,e);if(c.isEmptyObject(a))return this.each(f.complete);return this[f.queue===false?"each":"queue"](function(){var h=c.extend({},f),l,k=this.nodeType===1,o=k&&c(this).is(":hidden"),x=this;for(l in a){var r=c.camelCase(l);if(l!==r){a[r]=a[l];delete a[l];l=r}if(a[l]==="hide"&&o||a[l]==="show"&&!o)return h.complete.call(this);if(k&&(l==="height"||l==="width")){h.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY];if(c.css(this,"display")==="inline"&&c.css(this,"float")==="none")if(c.support.inlineBlockNeedsLayout)if(qa(this.nodeName)===
"inline")this.style.display="inline-block";else{this.style.display="inline";this.style.zoom=1}else this.style.display="inline-block"}if(c.isArray(a[l])){(h.specialEasing=h.specialEasing||{})[l]=a[l][1];a[l]=a[l][0]}}if(h.overflow!=null)this.style.overflow="hidden";h.curAnim=c.extend({},a);c.each(a,function(A,C){var J=new c.fx(x,h,A);if(vb.test(C))J[C==="toggle"?o?"show":"hide":C](a);else{var w=wb.exec(C),I=J.cur()||0;if(w){var L=parseFloat(w[2]),g=w[3]||"px";if(g!=="px"){c.style(x,A,(L||1)+g);I=(L||
1)/J.cur()*I;c.style(x,A,I+g)}if(w[1])L=(w[1]==="-="?-1:1)*L+I;J.custom(I,L,g)}else J.custom(I,C,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);this.each(function(){for(var e=d.length-1;e>=0;e--)if(d[e].elem===this){b&&d[e](true);d.splice(e,1)}});b||this.dequeue();return this}});c.each({slideDown:S("show",1),slideUp:S("hide",1),slideToggle:S("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){c.fn[a]=function(d,e,f){return this.animate(b,
d,e,f)}});c.extend({speed:function(a,b,d){var e=a&&typeof a==="object"?c.extend({},a):{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};e.duration=c.fx.off?0:typeof e.duration==="number"?e.duration:e.duration in c.fx.speeds?c.fx.speeds[e.duration]:c.fx.speeds._default;e.old=e.complete;e.complete=function(){e.queue!==false&&c(this).dequeue();c.isFunction(e.old)&&e.old.call(this)};return e},easing:{linear:function(a,b,d,e){return d+e*a},swing:function(a,b,d,e){return(-Math.cos(a*
Math.PI)/2+0.5)*e+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||c.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a=parseFloat(c.css(this.elem,this.prop));return a&&a>-1E4?a:0},custom:function(a,b,d){function e(l){return f.step(l)}
var f=this,h=c.fx;this.startTime=c.now();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;this.pos=this.state=0;e.elem=this.elem;if(e()&&c.timers.push(e)&&!ba)ba=setInterval(h.tick,h.interval)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;
this.custom(this.cur(),0)},step:function(a){var b=c.now(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var e in this.options.curAnim)if(this.options.curAnim[e]!==true)d=false;if(d){if(this.options.overflow!=null&&!c.support.shrinkWrapBlocks){var f=this.elem,h=this.options;c.each(["","X","Y"],function(k,o){f.style["overflow"+o]=h.overflow[k]})}this.options.hide&&c(this.elem).hide();if(this.options.hide||
this.options.show)for(var l in this.options.curAnim)c.style(this.elem,l,this.options.orig[l]);this.options.complete.call(this.elem)}return false}else{a=b-this.startTime;this.state=a/this.options.duration;b=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||b](this.state,a,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=
c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||c.fx.stop()},interval:13,stop:function(){clearInterval(ba);ba=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===
b.elem}).length};var xb=/^t(?:able|d|h)$/i,Ia=/^(?:body|html)$/i;c.fn.offset="getBoundingClientRect"in t.documentElement?function(a){var b=this[0],d;if(a)return this.each(function(l){c.offset.setOffset(this,a,l)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);try{d=b.getBoundingClientRect()}catch(e){}var f=b.ownerDocument,h=f.documentElement;if(!d||!c.contains(h,b))return d||{top:0,left:0};b=f.body;f=fa(f);return{top:d.top+(f.pageYOffset||c.support.boxModel&&
h.scrollTop||b.scrollTop)-(h.clientTop||b.clientTop||0),left:d.left+(f.pageXOffset||c.support.boxModel&&h.scrollLeft||b.scrollLeft)-(h.clientLeft||b.clientLeft||0)}}:function(a){var b=this[0];if(a)return this.each(function(x){c.offset.setOffset(this,a,x)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d,e=b.offsetParent,f=b.ownerDocument,h=f.documentElement,l=f.body;d=(f=f.defaultView)?f.getComputedStyle(b,null):b.currentStyle;
for(var k=b.offsetTop,o=b.offsetLeft;(b=b.parentNode)&&b!==l&&b!==h;){if(c.offset.supportsFixedPosition&&d.position==="fixed")break;d=f?f.getComputedStyle(b,null):b.currentStyle;k-=b.scrollTop;o-=b.scrollLeft;if(b===e){k+=b.offsetTop;o+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&xb.test(b.nodeName))){k+=parseFloat(d.borderTopWidth)||0;o+=parseFloat(d.borderLeftWidth)||0}e=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&d.overflow!=="visible"){k+=
parseFloat(d.borderTopWidth)||0;o+=parseFloat(d.borderLeftWidth)||0}d=d}if(d.position==="relative"||d.position==="static"){k+=l.offsetTop;o+=l.offsetLeft}if(c.offset.supportsFixedPosition&&d.position==="fixed"){k+=Math.max(h.scrollTop,l.scrollTop);o+=Math.max(h.scrollLeft,l.scrollLeft)}return{top:k,left:o}};c.offset={initialize:function(){var a=t.body,b=t.createElement("div"),d,e,f,h=parseFloat(c.css(a,"marginTop"))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",
height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";a.insertBefore(b,a.firstChild);d=b.firstChild;e=d.firstChild;f=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=e.offsetTop!==5;this.doesAddBorderForTableAndCells=
f.offsetTop===5;e.style.position="fixed";e.style.top="20px";this.supportsFixedPosition=e.offsetTop===20||e.offsetTop===15;e.style.position=e.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=e.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==h;a.removeChild(b);c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.css(a,
"marginTop"))||0;d+=parseFloat(c.css(a,"marginLeft"))||0}return{top:b,left:d}},setOffset:function(a,b,d){var e=c.css(a,"position");if(e==="static")a.style.position="relative";var f=c(a),h=f.offset(),l=c.css(a,"top"),k=c.css(a,"left"),o=e==="absolute"&&c.inArray("auto",[l,k])>-1;e={};var x={};if(o)x=f.position();l=o?x.top:parseInt(l,10)||0;k=o?x.left:parseInt(k,10)||0;if(c.isFunction(b))b=b.call(a,d,h);if(b.top!=null)e.top=b.top-h.top+l;if(b.left!=null)e.left=b.left-h.left+k;"using"in b?b.using.call(a,
e):f.css(e)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),e=Ia.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.css(a,"marginTop"))||0;d.left-=parseFloat(c.css(a,"marginLeft"))||0;e.top+=parseFloat(c.css(b[0],"borderTopWidth"))||0;e.left+=parseFloat(c.css(b[0],"borderLeftWidth"))||0;return{top:d.top-e.top,left:d.left-e.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||t.body;a&&!Ia.test(a.nodeName)&&
c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(e){var f=this[0],h;if(!f)return null;if(e!==B)return this.each(function(){if(h=fa(this))h.scrollTo(!a?e:c(h).scrollLeft(),a?e:c(h).scrollTop());else this[d]=e});else return(h=fa(f))?"pageXOffset"in h?h[a?"pageYOffset":"pageXOffset"]:c.support.boxModel&&h.document.documentElement[d]||h.document.body[d]:f[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();
c.fn["inner"+b]=function(){return this[0]?parseFloat(c.css(this[0],d,"padding")):null};c.fn["outer"+b]=function(e){return this[0]?parseFloat(c.css(this[0],d,e?"margin":"border")):null};c.fn[d]=function(e){var f=this[0];if(!f)return e==null?null:this;if(c.isFunction(e))return this.each(function(l){var k=c(this);k[d](e.call(this,l,k[d]()))});if(c.isWindow(f))return f.document.compatMode==="CSS1Compat"&&f.document.documentElement["client"+b]||f.document.body["client"+b];else if(f.nodeType===9)return Math.max(f.documentElement["client"+
b],f.body["scroll"+b],f.documentElement["scroll"+b],f.body["offset"+b],f.documentElement["offset"+b]);else if(e===B){f=c.css(f,d);var h=parseFloat(f);return c.isNaN(h)?f:h}else return this.css(d,typeof e==="string"?e:e+"px")}})})(window);

+ 3878
- 0
public/self/ztree/js/jquery.ztree.all.js
File diff suppressed because it is too large
View File


+ 2019
- 0
public/self/ztree/js/jquery.ztree.core.js
File diff suppressed because it is too large
View File


+ 652
- 0
public/self/ztree/js/jquery.ztree.excheck.js View File

@@ -0,0 +1,652 @@
/*
* JQuery zTree excheck
* v3.5.48
* http://treejs.cn/
*
* Copyright (c) 2010 Hunter.z
*
* Licensed same as jquery - MIT License
* http://www.opensource.org/licenses/mit-license.php
*
* Date: 2020-11-21
*/

(function ($) {
//default consts of excheck
var _consts = {
event: {
CHECK: "ztree_check"
},
id: {
CHECK: "_check"
},
checkbox: {
STYLE: "checkbox",
DEFAULT: "chk",
DISABLED: "disable",
FALSE: "false",
TRUE: "true",
FULL: "full",
PART: "part",
FOCUS: "focus"
},
radio: {
STYLE: "radio",
TYPE_ALL: "all",
TYPE_LEVEL: "level"
}
},
//default setting of excheck
_setting = {
check: {
enable: false,
autoCheckTrigger: false,
chkStyle: _consts.checkbox.STYLE,
nocheckInherit: false,
chkDisabledInherit: false,
radioType: _consts.radio.TYPE_LEVEL,
chkboxType: {
"Y": "ps",
"N": "ps"
}
},
data: {
key: {
checked: "checked"
}
},
callback: {
beforeCheck: null,
onCheck: null
}
},
//default root of excheck
_initRoot = function (setting) {
var r = data.getRoot(setting);
r.radioCheckedList = [];
},
//default cache of excheck
_initCache = function (treeId) {
},
//default bind event of excheck
_bindEvent = function (setting) {
var o = setting.treeObj,
c = consts.event;
o.bind(c.CHECK, function (event, srcEvent, treeId, node) {
event.srcEvent = srcEvent;
tools.apply(setting.callback.onCheck, [event, treeId, node]);
});
},
_unbindEvent = function (setting) {
var o = setting.treeObj,
c = consts.event;
o.unbind(c.CHECK);
},
//default event proxy of excheck
_eventProxy = function (e) {
var target = e.target,
setting = data.getSetting(e.data.treeId),
tId = "", node = null,
nodeEventType = "", treeEventType = "",
nodeEventCallback = null, treeEventCallback = null;

if (tools.eqs(e.type, "mouseover")) {
if (setting.check.enable && tools.eqs(target.tagName, "span") && target.getAttribute("treeNode" + consts.id.CHECK) !== null) {
tId = tools.getNodeMainDom(target).id;
nodeEventType = "mouseoverCheck";
}
} else if (tools.eqs(e.type, "mouseout")) {
if (setting.check.enable && tools.eqs(target.tagName, "span") && target.getAttribute("treeNode" + consts.id.CHECK) !== null) {
tId = tools.getNodeMainDom(target).id;
nodeEventType = "mouseoutCheck";
}
} else if (tools.eqs(e.type, "click")) {
if (setting.check.enable && tools.eqs(target.tagName, "span") && target.getAttribute("treeNode" + consts.id.CHECK) !== null) {
tId = tools.getNodeMainDom(target).id;
nodeEventType = "checkNode";
}
}
if (tId.length > 0) {
node = data.getNodeCache(setting, tId);
switch (nodeEventType) {
case "checkNode" :
nodeEventCallback = _handler.onCheckNode;
break;
case "mouseoverCheck" :
nodeEventCallback = _handler.onMouseoverCheck;
break;
case "mouseoutCheck" :
nodeEventCallback = _handler.onMouseoutCheck;
break;
}
}
var proxyResult = {
stop: nodeEventType === "checkNode",
node: node,
nodeEventType: nodeEventType,
nodeEventCallback: nodeEventCallback,
treeEventType: treeEventType,
treeEventCallback: treeEventCallback
};
return proxyResult
},
//default init node of excheck
_initNode = function (setting, level, n, parentNode, isFirstNode, isLastNode, openFlag) {
if (!n) return;
var checked = data.nodeChecked(setting, n);
n.checkedOld = checked;
if (typeof n.nocheck == "string") n.nocheck = tools.eqs(n.nocheck, "true");
n.nocheck = !!n.nocheck || (setting.check.nocheckInherit && parentNode && !!parentNode.nocheck);
if (typeof n.chkDisabled == "string") n.chkDisabled = tools.eqs(n.chkDisabled, "true");
n.chkDisabled = !!n.chkDisabled || (setting.check.chkDisabledInherit && parentNode && !!parentNode.chkDisabled);
if (typeof n.halfCheck == "string") n.halfCheck = tools.eqs(n.halfCheck, "true");
n.halfCheck = !!n.halfCheck;
n.check_Child_State = -1;
n.check_Focus = false;
n.getCheckStatus = function () {
return data.getCheckStatus(setting, n);
};

if (setting.check.chkStyle == consts.radio.STYLE && setting.check.radioType == consts.radio.TYPE_ALL && checked) {
var r = data.getRoot(setting);
r.radioCheckedList.push(n);
}
},
//add dom for check
_beforeA = function (setting, node, html) {
if (setting.check.enable) {
data.makeChkFlag(setting, node);
html.push("<span ID='", node.tId, consts.id.CHECK, "' class='", view.makeChkClass(setting, node), "' treeNode", consts.id.CHECK, (node.nocheck === true ? " style='display:none;'" : ""), "></span>");
}
},
//update zTreeObj, add method of check
_zTreeTools = function (setting, zTreeTools) {
zTreeTools.checkNode = function (node, checked, checkTypeFlag, callbackFlag) {
var nodeChecked = data.nodeChecked(setting, node);
if (node.chkDisabled === true) return;
if (checked !== true && checked !== false) {
checked = !nodeChecked;
}
callbackFlag = !!callbackFlag;

if (nodeChecked === checked && !checkTypeFlag) {
return;
} else if (callbackFlag && tools.apply(this.setting.callback.beforeCheck, [this.setting.treeId, node], true) == false) {
return;
}
if (tools.uCanDo(this.setting) && this.setting.check.enable && node.nocheck !== true) {
data.nodeChecked(setting, node, checked);
var checkObj = $$(node, consts.id.CHECK, this.setting);
if (checkTypeFlag || this.setting.check.chkStyle === consts.radio.STYLE) view.checkNodeRelation(this.setting, node);
view.setChkClass(this.setting, checkObj, node);
view.repairParentChkClassWithSelf(this.setting, node);
if (callbackFlag) {
this.setting.treeObj.trigger(consts.event.CHECK, [null, this.setting.treeId, node]);
}
}
}

zTreeTools.checkAllNodes = function (checked) {
view.repairAllChk(this.setting, !!checked);
}

zTreeTools.getCheckedNodes = function (checked) {
checked = (checked !== false);
var children = data.nodeChildren(setting, data.getRoot(this.setting));
return data.getTreeCheckedNodes(this.setting, children, checked);
}

zTreeTools.getChangeCheckedNodes = function () {
var children = data.nodeChildren(setting, data.getRoot(this.setting));
return data.getTreeChangeCheckedNodes(this.setting, children);
}

zTreeTools.setChkDisabled = function (node, disabled, inheritParent, inheritChildren) {
disabled = !!disabled;
inheritParent = !!inheritParent;
inheritChildren = !!inheritChildren;
view.repairSonChkDisabled(this.setting, node, disabled, inheritChildren);
view.repairParentChkDisabled(this.setting, node.getParentNode(), disabled, inheritParent);
}

var _updateNode = zTreeTools.updateNode;
zTreeTools.updateNode = function (node, checkTypeFlag) {
if (_updateNode) _updateNode.apply(zTreeTools, arguments);
if (!node || !this.setting.check.enable) return;
var nObj = $$(node, this.setting);
if (nObj.get(0) && tools.uCanDo(this.setting)) {
var checkObj = $$(node, consts.id.CHECK, this.setting);
if (checkTypeFlag == true || this.setting.check.chkStyle === consts.radio.STYLE) view.checkNodeRelation(this.setting, node);
view.setChkClass(this.setting, checkObj, node);
view.repairParentChkClassWithSelf(this.setting, node);
}
}
},
//method of operate data
_data = {
getRadioCheckedList: function (setting) {
var checkedList = data.getRoot(setting).radioCheckedList;
for (var i = 0, j = checkedList.length; i < j; i++) {
if (!data.getNodeCache(setting, checkedList[i].tId)) {
checkedList.splice(i, 1);
i--;
j--;
}
}
return checkedList;
},
getCheckStatus: function (setting, node) {
if (!setting.check.enable || node.nocheck || node.chkDisabled) return null;
var checked = data.nodeChecked(setting, node),
r = {
checked: checked,
half: node.halfCheck ? node.halfCheck : (setting.check.chkStyle == consts.radio.STYLE ? (node.check_Child_State === 2) : (checked ? (node.check_Child_State > -1 && node.check_Child_State < 2) : (node.check_Child_State > 0)))
};
return r;
},
getTreeCheckedNodes: function (setting, nodes, checked, results) {
if (!nodes) return [];
var onlyOne = (checked && setting.check.chkStyle == consts.radio.STYLE && setting.check.radioType == consts.radio.TYPE_ALL);
results = !results ? [] : results;
for (var i = 0, l = nodes.length; i < l; i++) {
var node = nodes[i];
var children = data.nodeChildren(setting, node);
var nodeChecked = data.nodeChecked(setting, node);
if (node.nocheck !== true && node.chkDisabled !== true && nodeChecked == checked) {
results.push(node);
if (onlyOne) {
break;
}
}
data.getTreeCheckedNodes(setting, children, checked, results);
if (onlyOne && results.length > 0) {
break;
}
}
return results;
},
getTreeChangeCheckedNodes: function (setting, nodes, results) {
if (!nodes) return [];
results = !results ? [] : results;
for (var i = 0, l = nodes.length; i < l; i++) {
var node = nodes[i];
var children = data.nodeChildren(setting, node);
var nodeChecked = data.nodeChecked(setting, node);
if (node.nocheck !== true && node.chkDisabled !== true && nodeChecked != node.checkedOld) {
results.push(node);
}
data.getTreeChangeCheckedNodes(setting, children, results);
}
return results;
},
makeChkFlag: function (setting, node) {
if (!node) return;
var chkFlag = -1;
var children = data.nodeChildren(setting, node);
if (children) {
for (var i = 0, l = children.length; i < l; i++) {
var cNode = children[i];
var nodeChecked = data.nodeChecked(setting, cNode);
var tmp = -1;
if (setting.check.chkStyle == consts.radio.STYLE) {
if (cNode.nocheck === true || cNode.chkDisabled === true) {
tmp = cNode.check_Child_State;
} else if (cNode.halfCheck === true) {
tmp = 2;
} else if (nodeChecked) {
tmp = 2;
} else {
tmp = cNode.check_Child_State > 0 ? 2 : 0;
}
if (tmp == 2) {
chkFlag = 2;
break;
} else if (tmp == 0) {
chkFlag = 0;
}
} else if (setting.check.chkStyle == consts.checkbox.STYLE) {
if (cNode.nocheck === true || cNode.chkDisabled === true) {
tmp = cNode.check_Child_State;
} else if (cNode.halfCheck === true) {
tmp = 1;
} else if (nodeChecked) {
tmp = (cNode.check_Child_State === -1 || cNode.check_Child_State === 2) ? 2 : 1;
} else {
tmp = (cNode.check_Child_State > 0) ? 1 : 0;
}
if (tmp === 1) {
chkFlag = 1;
break;
} else if (tmp === 2 && chkFlag > -1 && i > 0 && tmp !== chkFlag) {
chkFlag = 1;
break;
} else if (chkFlag === 2 && tmp > -1 && tmp < 2) {
chkFlag = 1;
break;
} else if (tmp > -1) {
chkFlag = tmp;
}
}
}
}
node.check_Child_State = chkFlag;
}
},
//method of event proxy
_event = {},
//method of event handler
_handler = {
onCheckNode: function (event, node) {
if (node.chkDisabled === true) return false;
var setting = data.getSetting(event.data.treeId);
if (tools.apply(setting.callback.beforeCheck, [setting.treeId, node], true) == false) return true;
var nodeChecked = data.nodeChecked(setting, node);
data.nodeChecked(setting, node, !nodeChecked);
view.checkNodeRelation(setting, node);
var checkObj = $$(node, consts.id.CHECK, setting);
view.setChkClass(setting, checkObj, node);
view.repairParentChkClassWithSelf(setting, node);
setting.treeObj.trigger(consts.event.CHECK, [event, setting.treeId, node]);
return true;
},
onMouseoverCheck: function (event, node) {
if (node.chkDisabled === true) return false;
var setting = data.getSetting(event.data.treeId),
checkObj = $$(node, consts.id.CHECK, setting);
node.check_Focus = true;
view.setChkClass(setting, checkObj, node);
return true;
},
onMouseoutCheck: function (event, node) {
if (node.chkDisabled === true) return false;
var setting = data.getSetting(event.data.treeId),
checkObj = $$(node, consts.id.CHECK, setting);
node.check_Focus = false;
view.setChkClass(setting, checkObj, node);
return true;
}
},
//method of tools for zTree
_tools = {},
//method of operate ztree dom
_view = {
checkNodeRelation: function (setting, node) {
var pNode, i, l,
r = consts.radio;
var nodeChecked = data.nodeChecked(setting, node);
if (setting.check.chkStyle == r.STYLE) {
var checkedList = data.getRadioCheckedList(setting);
if (nodeChecked) {
if (setting.check.radioType == r.TYPE_ALL) {
for (i = checkedList.length - 1; i >= 0; i--) {
pNode = checkedList[i];
var pNodeChecked = data.nodeChecked(setting, pNode);
if (pNodeChecked && pNode != node) {
data.nodeChecked(setting, pNode, false);
checkedList.splice(i, 1);

view.setChkClass(setting, $$(pNode, consts.id.CHECK, setting), pNode);
if (pNode.parentTId != node.parentTId) {
view.repairParentChkClassWithSelf(setting, pNode);
}
}
}
checkedList.push(node);
} else {
var parentNode = (node.parentTId) ? node.getParentNode() : data.getRoot(setting);
var children = data.nodeChildren(setting, parentNode);
for (i = 0, l = children.length; i < l; i++) {
pNode = children[i];
var pNodeChecked = data.nodeChecked(setting, pNode);
if (pNodeChecked && pNode != node) {
data.nodeChecked(setting, pNode, false);
view.setChkClass(setting, $$(pNode, consts.id.CHECK, setting), pNode);
}
}
}
} else if (setting.check.radioType == r.TYPE_ALL) {
for (i = 0, l = checkedList.length; i < l; i++) {
if (node == checkedList[i]) {
checkedList.splice(i, 1);
break;
}
}
}

} else {
var children = data.nodeChildren(setting, node);
if (nodeChecked && (!children || children.length == 0 || setting.check.chkboxType.Y.indexOf("s") > -1)) {
view.setSonNodeCheckBox(setting, node, true);
}
if (!nodeChecked && (!children || children.length == 0 || setting.check.chkboxType.N.indexOf("s") > -1)) {
view.setSonNodeCheckBox(setting, node, false);
}
if (nodeChecked && setting.check.chkboxType.Y.indexOf("p") > -1) {
view.setParentNodeCheckBox(setting, node, true);
}
if (!nodeChecked && setting.check.chkboxType.N.indexOf("p") > -1) {
view.setParentNodeCheckBox(setting, node, false);
}
}
},
makeChkClass: function (setting, node) {
var c = consts.checkbox, r = consts.radio,
fullStyle = "";
var nodeChecked = data.nodeChecked(setting, node);
if (node.chkDisabled === true) {
fullStyle = c.DISABLED;
} else if (node.halfCheck) {
fullStyle = c.PART;
} else if (setting.check.chkStyle == r.STYLE) {
fullStyle = (node.check_Child_State < 1) ? c.FULL : c.PART;
} else {
fullStyle = nodeChecked ? ((node.check_Child_State === 2 || node.check_Child_State === -1) ? c.FULL : c.PART) : ((node.check_Child_State < 1) ? c.FULL : c.PART);
}
var chkName = setting.check.chkStyle + "_" + (nodeChecked ? c.TRUE : c.FALSE) + "_" + fullStyle;
chkName = (node.check_Focus && node.chkDisabled !== true) ? chkName + "_" + c.FOCUS : chkName;
return consts.className.BUTTON + " " + c.DEFAULT + " " + chkName;
},
repairAllChk: function (setting, checked) {
if (setting.check.enable && setting.check.chkStyle === consts.checkbox.STYLE) {
var root = data.getRoot(setting);
var children = data.nodeChildren(setting, root);
for (var i = 0, l = children.length; i < l; i++) {
var node = children[i];
if (node.nocheck !== true && node.chkDisabled !== true) {
data.nodeChecked(setting, node, checked);
}
view.setSonNodeCheckBox(setting, node, checked);
}
}
},
repairChkClass: function (setting, node) {
if (!node) return;
data.makeChkFlag(setting, node);
if (node.nocheck !== true) {
var checkObj = $$(node, consts.id.CHECK, setting);
view.setChkClass(setting, checkObj, node);
}
},
repairParentChkClass: function (setting, node) {
if (!node || !node.parentTId) return;
var pNode = node.getParentNode();
view.repairChkClass(setting, pNode);
view.repairParentChkClass(setting, pNode);
},
repairParentChkClassWithSelf: function (setting, node) {
if (!node) return;
var children = data.nodeChildren(setting, node);
if (children && children.length > 0) {
view.repairParentChkClass(setting, children[0]);
} else {
view.repairParentChkClass(setting, node);
}
},
repairSonChkDisabled: function (setting, node, chkDisabled, inherit) {
if (!node) return;
if (node.chkDisabled != chkDisabled) {
node.chkDisabled = chkDisabled;
}
view.repairChkClass(setting, node);
var children = data.nodeChildren(setting, node);
if (children && inherit) {
for (var i = 0, l = children.length; i < l; i++) {
var sNode = children[i];
view.repairSonChkDisabled(setting, sNode, chkDisabled, inherit);
}
}
},
repairParentChkDisabled: function (setting, node, chkDisabled, inherit) {
if (!node) return;
if (node.chkDisabled != chkDisabled && inherit) {
node.chkDisabled = chkDisabled;
}
view.repairChkClass(setting, node);
view.repairParentChkDisabled(setting, node.getParentNode(), chkDisabled, inherit);
},
setChkClass: function (setting, obj, node) {
if (!obj) return;
if (node.nocheck === true) {
obj.hide();
} else {
obj.show();
}
obj.attr('class', view.makeChkClass(setting, node));
},
setParentNodeCheckBox: function (setting, node, value, srcNode) {
var checkObj = $$(node, consts.id.CHECK, setting);
if (!srcNode) srcNode = node;
data.makeChkFlag(setting, node);
if (node.nocheck !== true && node.chkDisabled !== true) {
data.nodeChecked(setting, node, value);
view.setChkClass(setting, checkObj, node);
if (setting.check.autoCheckTrigger && node != srcNode) {
setting.treeObj.trigger(consts.event.CHECK, [null, setting.treeId, node]);
}
}
if (node.parentTId) {
var pSign = true;
if (!value) {
var pNodes = data.nodeChildren(setting, node.getParentNode());
for (var i = 0, l = pNodes.length; i < l; i++) {
var pNode = pNodes[i];
var nodeChecked = data.nodeChecked(setting, pNode);
if ((pNode.nocheck !== true && pNode.chkDisabled !== true && nodeChecked)
|| ((pNode.nocheck === true || pNode.chkDisabled === true) && pNode.check_Child_State > 0)) {
pSign = false;
break;
}
}
}
if (pSign) {
view.setParentNodeCheckBox(setting, node.getParentNode(), value, srcNode);
}
}
},
setSonNodeCheckBox: function (setting, node, value, srcNode) {
if (!node) return;
var checkObj = $$(node, consts.id.CHECK, setting);
if (!srcNode) srcNode = node;

var hasDisable = false;
var children = data.nodeChildren(setting, node);
if (children) {
for (var i = 0, l = children.length; i < l; i++) {
var sNode = children[i];
view.setSonNodeCheckBox(setting, sNode, value, srcNode);
if (sNode.chkDisabled === true) hasDisable = true;
}
}

if (node != data.getRoot(setting) && node.chkDisabled !== true) {
if (hasDisable && node.nocheck !== true) {
data.makeChkFlag(setting, node);
}
if (node.nocheck !== true && node.chkDisabled !== true) {
data.nodeChecked(setting, node, value);
if (!hasDisable) node.check_Child_State = (children && children.length > 0) ? (value ? 2 : 0) : -1;
} else {
node.check_Child_State = -1;
}
view.setChkClass(setting, checkObj, node);
if (setting.check.autoCheckTrigger && node != srcNode && node.nocheck !== true && node.chkDisabled !== true) {
setting.treeObj.trigger(consts.event.CHECK, [null, setting.treeId, node]);
}
}

}
},

_z = {
tools: _tools,
view: _view,
event: _event,
data: _data
};
$.extend(true, $.fn.zTree.consts, _consts);
$.extend(true, $.fn.zTree._z, _z);

var zt = $.fn.zTree,
tools = zt._z.tools,
consts = zt.consts,
view = zt._z.view,
data = zt._z.data,
event = zt._z.event,
$$ = tools.$;

data.nodeChecked = function (setting, node, newChecked) {
if (!node) {
return false;
}
var key = setting.data.key.checked;
if (typeof newChecked !== 'undefined') {
if (typeof newChecked === "string") {
newChecked = tools.eqs(newChecked, "true");
}
newChecked = !!newChecked;
node[key] = newChecked;
} else if (typeof node[key] == "string"){
node[key] = tools.eqs(node[key], "true");
} else {
node[key] = !!node[key];
}
return node[key];
};

data.exSetting(_setting);
data.addInitBind(_bindEvent);
data.addInitUnBind(_unbindEvent);
data.addInitCache(_initCache);
data.addInitNode(_initNode);
data.addInitProxy(_eventProxy, true);
data.addInitRoot(_initRoot);
data.addBeforeA(_beforeA);
data.addZTreeTools(_zTreeTools);

var _createNodes = view.createNodes;
view.createNodes = function (setting, level, nodes, parentNode, index) {
if (_createNodes) _createNodes.apply(view, arguments);
if (!nodes) return;
view.repairParentChkClassWithSelf(setting, parentNode);
}
var _removeNode = view.removeNode;
view.removeNode = function (setting, node) {
var parentNode = node.getParentNode();
if (_removeNode) _removeNode.apply(view, arguments);
if (!node || !parentNode) return;
view.repairChkClass(setting, parentNode);
view.repairParentChkClass(setting, parentNode);
}

var _appendNodes = view.appendNodes;
view.appendNodes = function (setting, level, nodes, parentNode, index, initFlag, openFlag) {
var html = "";
if (_appendNodes) {
html = _appendNodes.apply(view, arguments);
}
if (parentNode) {
data.makeChkFlag(setting, parentNode);
}
return html;
}
})(jQuery);

+ 1207
- 0
public/self/ztree/js/jquery.ztree.exedit.js
File diff suppressed because it is too large
View File


+ 405
- 0
public/self/ztree/js/jquery.ztree.exhide.js View File

@@ -0,0 +1,405 @@
/*
* JQuery zTree exHideNodes
* v3.5.48
* http://treejs.cn/
*
* Copyright (c) 2010 Hunter.z
*
* Licensed same as jquery - MIT License
* http://www.opensource.org/licenses/mit-license.php
*
* Date: 2020-11-21
*/

(function ($) {
var _setting = {
data: {
key: {
isHidden: "isHidden"
}
}
};
//default init node of exLib
var _initNode = function (setting, level, n, parentNode, isFirstNode, isLastNode, openFlag) {
var isHidden = data.isHidden(setting, n);
data.isHidden(setting, n, isHidden);
data.initHideForExCheck(setting, n);
},
//add dom for check
_beforeA = function (setting, node, html) {
},
//update zTreeObj, add method of exLib
_zTreeTools = function (setting, zTreeTools) {
zTreeTools.showNodes = function (nodes, options) {
view.showNodes(setting, nodes, options);
}
zTreeTools.showNode = function (node, options) {
if (!node) {
return;
}
view.showNodes(setting, [node], options);
}
zTreeTools.hideNodes = function (nodes, options) {
view.hideNodes(setting, nodes, options);
}
zTreeTools.hideNode = function (node, options) {
if (!node) {
return;
}
view.hideNodes(setting, [node], options);
}

var _checkNode = zTreeTools.checkNode;
if (_checkNode) {
zTreeTools.checkNode = function (node, checked, checkTypeFlag, callbackFlag) {
if (!!node && !!data.isHidden(setting, node)) {
return;
}
_checkNode.apply(zTreeTools, arguments);
}
}
},
//method of operate data
_data = {
initHideForExCheck: function (setting, n) {
var isHidden = data.isHidden(setting, n);
if (isHidden && setting.check && setting.check.enable) {
if (typeof n._nocheck == "undefined") {
n._nocheck = !!n.nocheck
n.nocheck = true;
}
n.check_Child_State = -1;
if (view.repairParentChkClassWithSelf) {
view.repairParentChkClassWithSelf(setting, n);
}
}
},
initShowForExCheck: function (setting, n) {
var isHidden = data.isHidden(setting, n);
if (!isHidden && setting.check && setting.check.enable) {
if (typeof n._nocheck != "undefined") {
n.nocheck = n._nocheck;
delete n._nocheck;
}
if (view.setChkClass) {
var checkObj = $$(n, consts.id.CHECK, setting);
view.setChkClass(setting, checkObj, n);
}
if (view.repairParentChkClassWithSelf) {
view.repairParentChkClassWithSelf(setting, n);
}
}
}
},
//method of operate ztree dom
_view = {
clearOldFirstNode: function (setting, node) {
var n = node.getNextNode();
while (!!n) {
if (n.isFirstNode) {
n.isFirstNode = false;
view.setNodeLineIcos(setting, n);
break;
}
if (n.isLastNode) {
break;
}
n = n.getNextNode();
}
},
clearOldLastNode: function (setting, node, openFlag) {
var n = node.getPreNode();
while (!!n) {
if (n.isLastNode) {
n.isLastNode = false;
if (openFlag) {
view.setNodeLineIcos(setting, n);
}
break;
}
if (n.isFirstNode) {
break;
}
n = n.getPreNode();
}
},
makeDOMNodeMainBefore: function (html, setting, node) {
var isHidden = data.isHidden(setting, node);
html.push("<li ", (isHidden ? "style='display:none;' " : ""), "id='", node.tId, "' class='", consts.className.LEVEL, node.level, "' tabindex='0' hidefocus='true' treenode>");
},
showNode: function (setting, node, options) {
data.isHidden(setting, node, false);
data.initShowForExCheck(setting, node);
$$(node, setting).show();
},
showNodes: function (setting, nodes, options) {
if (!nodes || nodes.length == 0) {
return;
}
var pList = {}, i, j;
for (i = 0, j = nodes.length; i < j; i++) {
var n = nodes[i];
if (!pList[n.parentTId]) {
var pn = n.getParentNode();
pList[n.parentTId] = (pn === null) ? data.getRoot(setting) : n.getParentNode();
}
view.showNode(setting, n, options);
}
for (var tId in pList) {
var children = data.nodeChildren(setting, pList[tId]);
view.setFirstNodeForShow(setting, children);
view.setLastNodeForShow(setting, children);
}
},
hideNode: function (setting, node, options) {
data.isHidden(setting, node, true);
node.isFirstNode = false;
node.isLastNode = false;
data.initHideForExCheck(setting, node);
view.cancelPreSelectedNode(setting, node);
$$(node, setting).hide();
},
hideNodes: function (setting, nodes, options) {
if (!nodes || nodes.length == 0) {
return;
}
var pList = {}, i, j;
for (i = 0, j = nodes.length; i < j; i++) {
var n = nodes[i];
if ((n.isFirstNode || n.isLastNode) && !pList[n.parentTId]) {
var pn = n.getParentNode();
pList[n.parentTId] = (pn === null) ? data.getRoot(setting) : n.getParentNode();
}
view.hideNode(setting, n, options);
}
for (var tId in pList) {
var children = data.nodeChildren(setting, pList[tId]);
view.setFirstNodeForHide(setting, children);
view.setLastNodeForHide(setting, children);
}
},
setFirstNode: function (setting, parentNode) {
var children = data.nodeChildren(setting, parentNode);
var isHidden = data.isHidden(setting, children[0], false);
if (children.length > 0 && !isHidden) {
children[0].isFirstNode = true;
} else if (children.length > 0) {
view.setFirstNodeForHide(setting, children);
}
},
setLastNode: function (setting, parentNode) {
var children = data.nodeChildren(setting, parentNode);
var isHidden = data.isHidden(setting, children[0]);
if (children.length > 0 && !isHidden) {
children[children.length - 1].isLastNode = true;
} else if (children.length > 0) {
view.setLastNodeForHide(setting, children);
}
},
setFirstNodeForHide: function (setting, nodes) {
var n, i, j;
for (i = 0, j = nodes.length; i < j; i++) {
n = nodes[i];
if (n.isFirstNode) {
break;
}
var isHidden = data.isHidden(setting, n);
if (!isHidden && !n.isFirstNode) {
n.isFirstNode = true;
view.setNodeLineIcos(setting, n);
break;
} else {
n = null;
}
}
return n;
},
setFirstNodeForShow: function (setting, nodes) {
var n, i, j, first, old;
for (i = 0, j = nodes.length; i < j; i++) {
n = nodes[i];
var isHidden = data.isHidden(setting, n);
if (!first && !isHidden && n.isFirstNode) {
first = n;
break;
} else if (!first && !isHidden && !n.isFirstNode) {
n.isFirstNode = true;
first = n;
view.setNodeLineIcos(setting, n);
} else if (first && n.isFirstNode) {
n.isFirstNode = false;
old = n;
view.setNodeLineIcos(setting, n);
break;
} else {
n = null;
}
}
return {"new": first, "old": old};
},
setLastNodeForHide: function (setting, nodes) {
var n, i;
for (i = nodes.length - 1; i >= 0; i--) {
n = nodes[i];
if (n.isLastNode) {
break;
}
var isHidden = data.isHidden(setting, n);
if (!isHidden && !n.isLastNode) {
n.isLastNode = true;
view.setNodeLineIcos(setting, n);
break;
} else {
n = null;
}
}
return n;
},
setLastNodeForShow: function (setting, nodes) {
var n, i, j, last, old;
for (i = nodes.length - 1; i >= 0; i--) {
n = nodes[i];
var isHidden = data.isHidden(setting, n);
if (!last && !isHidden && n.isLastNode) {
last = n;
break;
} else if (!last && !isHidden && !n.isLastNode) {
n.isLastNode = true;
last = n;
view.setNodeLineIcos(setting, n);
} else if (last && n.isLastNode) {
n.isLastNode = false;
old = n;
view.setNodeLineIcos(setting, n);
break;
} else {
n = null;
}
}
return {"new": last, "old": old};
}
},

_z = {
view: _view,
data: _data
};
$.extend(true, $.fn.zTree._z, _z);

var zt = $.fn.zTree,
tools = zt._z.tools,
consts = zt.consts,
view = zt._z.view,
data = zt._z.data,
event = zt._z.event,
$$ = tools.$;

data.isHidden = function (setting, node, newIsHidden) {
if (!node) {
return false;
}
var key = setting.data.key.isHidden;
if (typeof newIsHidden !== 'undefined') {
if (typeof newIsHidden === "string") {
newIsHidden = tools.eqs(newIsHidden, "true");
}
newIsHidden = !!newIsHidden;
node[key] = newIsHidden;
} else if (typeof node[key] == "string"){
node[key] = tools.eqs(node[key], "true");
} else {
node[key] = !!node[key];
}
return node[key];
};

data.exSetting(_setting);
data.addInitNode(_initNode);
data.addBeforeA(_beforeA);
data.addZTreeTools(_zTreeTools);

// Override method in core
var _dInitNode = data.initNode;
data.initNode = function (setting, level, node, parentNode, isFirstNode, isLastNode, openFlag) {
var tmpPNode = (parentNode) ? parentNode : data.getRoot(setting),
children = tmpPNode[setting.data.key.children];
data.tmpHideFirstNode = view.setFirstNodeForHide(setting, children);
data.tmpHideLastNode = view.setLastNodeForHide(setting, children);
if (openFlag) {
view.setNodeLineIcos(setting, data.tmpHideFirstNode);
view.setNodeLineIcos(setting, data.tmpHideLastNode);
}
isFirstNode = (data.tmpHideFirstNode === node);
isLastNode = (data.tmpHideLastNode === node);
if (_dInitNode) _dInitNode.apply(data, arguments);
if (openFlag && isLastNode) {
view.clearOldLastNode(setting, node, openFlag);
}
};

var _makeChkFlag = data.makeChkFlag;
if (!!_makeChkFlag) {
data.makeChkFlag = function (setting, node) {
if (!!node && !!data.isHidden(setting, node)) {
return;
}
_makeChkFlag.apply(data, arguments);
}
}

var _getTreeCheckedNodes = data.getTreeCheckedNodes;
if (!!_getTreeCheckedNodes) {
data.getTreeCheckedNodes = function (setting, nodes, checked, results) {
if (!!nodes && nodes.length > 0) {
var p = nodes[0].getParentNode();
if (!!p && !!data.isHidden(setting, p)) {
return [];
}
}
return _getTreeCheckedNodes.apply(data, arguments);
}
}

var _getTreeChangeCheckedNodes = data.getTreeChangeCheckedNodes;
if (!!_getTreeChangeCheckedNodes) {
data.getTreeChangeCheckedNodes = function (setting, nodes, results) {
if (!!nodes && nodes.length > 0) {
var p = nodes[0].getParentNode();
if (!!p && !!data.isHidden(setting, p)) {
return [];
}
}
return _getTreeChangeCheckedNodes.apply(data, arguments);
}
}

var _expandCollapseSonNode = view.expandCollapseSonNode;
if (!!_expandCollapseSonNode) {
view.expandCollapseSonNode = function (setting, node, expandFlag, animateFlag, callback) {
if (!!node && !!data.isHidden(setting, node)) {
return;
}
_expandCollapseSonNode.apply(view, arguments);
}
}

var _setSonNodeCheckBox = view.setSonNodeCheckBox;
if (!!_setSonNodeCheckBox) {
view.setSonNodeCheckBox = function (setting, node, value, srcNode) {
if (!!node && !!data.isHidden(setting, node)) {
return;
}
_setSonNodeCheckBox.apply(view, arguments);
}
}

var _repairParentChkClassWithSelf = view.repairParentChkClassWithSelf;
if (!!_repairParentChkClassWithSelf) {
view.repairParentChkClassWithSelf = function (setting, node) {
if (!!node && !!data.isHidden(setting, node)) {
return;
}
_repairParentChkClassWithSelf.apply(view, arguments);
}
}
})(jQuery);

+ 16
- 0
routers/api/v1/api.go View File

@@ -570,6 +570,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/query_user_last_month", operationReq, repo_ext.QueryUserStaticLastMonth)
m.Get("/query_user_yesterday", operationReq, repo_ext.QueryUserStaticYesterday)
m.Get("/query_user_all", operationReq, repo_ext.QueryUserStaticAll)
m.Get("/query_user_activity", operationReq, repo_ext.QueryUserActivity)
//cloudbrain board
m.Group("/cloudbrainboard", func() {
m.Get("/downloadAll", repo.DownloadCloudBrainBoard)
@@ -921,7 +922,20 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/stop_version", cloudbrain.AdminOrOwnerOrJobCreaterRightForTrain, repo_ext.CloudBrainStop)
})
})
m.Group("/inference-job", func() {
m.Group("/:jobid", func() {
m.Get("", repo.GetCloudBrainInferenceJob)
m.Post("/del", cloudbrain.AdminOrOwnerOrJobCreaterRightForTrain, repo.DelCloudBrainJob)
m.Get("/result_list", repo.InferencJobResultList)
})
})
}, reqRepoReader(models.UnitTypeCloudBrain))
m.Group("/modelmanage", func() {
m.Get("/:id", repo.GetCloudbrainModelConvertTask)
m.Get("/:id/log", repo.CloudbrainForModelConvertGetLog)
m.Get("/:id/modelartlog", repo.TrainJobForModelConvertGetLog)
m.Get("/:id/model_list", repo.CloudBrainModelConvertList)
}, reqRepoReader(models.UnitTypeModelManage))
m.Group("/modelarts", func() {
m.Group("/notebook", func() {
//m.Get("/:jobid", repo.GetModelArtsNotebook)
@@ -1056,6 +1070,8 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Post("/prd/event", authentication.AcceptWechatEvent)
})
m.Get("/wechat/material", authentication.GetMaterial)
m.Get("/cloudbrain/get_newest_job", repo.GetNewestJobs)
m.Get("/cloudbrain/get_center_info", repo.GetAICenterInfo)
}, securityHeaders(), context.APIContexter(), sudo())
}



+ 399
- 13
routers/api/v1/repo/cloudbrain.go View File

@@ -12,10 +12,13 @@ import (
"strings"
"time"

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

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/cloudbrain"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/modelarts"
"code.gitea.io/gitea/modules/storage"
routerRepo "code.gitea.io/gitea/routers/repo"
)
@@ -100,21 +103,203 @@ func GetCloudbrainTask(ctx *context.APIContext) {

}

func CloudbrainGetLog(ctx *context.Context) {
func GetCloudBrainInferenceJob(ctx *context.APIContext) {

jobID := ctx.Params(":jobid")
job, err := models.GetCloudbrainByJobID(jobID)
if err != nil {
ctx.NotFound(err)
return
}
jobResult, err := cloudbrain.GetJob(job.JobID)
if err != nil {
ctx.NotFound(err)
log.Error("GetJob failed:", err)
return
}
result, err := models.ConvertToJobResultPayload(jobResult.Payload)
if err != nil {
ctx.NotFound(err)
log.Error("ConvertToJobResultPayload failed:", err)
return
}

job.Status = result.JobStatus.State
if result.JobStatus.State != string(models.JobWaiting) && result.JobStatus.State != string(models.JobFailed) {
taskRoles := result.TaskRoles
taskRes, _ := models.ConvertToTaskPod(taskRoles[cloudbrain.SubTaskName].(map[string]interface{}))

job.ContainerIp = taskRes.TaskStatuses[0].ContainerIP
job.ContainerID = taskRes.TaskStatuses[0].ContainerID
job.Status = taskRes.TaskStatuses[0].State
}

if result.JobStatus.State != string(models.JobWaiting) {
models.ParseAndSetDurationFromCloudBrainOne(result, job)
err = models.UpdateJob(job)
if err != nil {
log.Error("UpdateJob failed:", err)
}
}

ctx.JSON(http.StatusOK, map[string]interface{}{
"JobID": jobID,
"JobStatus": job.Status,
"JobDuration": job.TrainJobDuration,
})

}

func DelCloudBrainJob(ctx *context.APIContext) {
jobID := ctx.Params(":jobid")

errStr := cloudbrain.DelCloudBrainJob(jobID)

if errStr != "" {
ctx.JSON(http.StatusOK, map[string]interface{}{
"Message": ctx.Tr(errStr),
"VersionName": "1",
"Code": 1,
})
} else {
ctx.JSON(http.StatusOK, map[string]interface{}{
"Message": "",
"VersionName": "1",
"Code": 0,
})
}

}

func InferencJobResultList(ctx *context.APIContext) {
jobID := ctx.Params(":jobid")
parentDir := ctx.Query("parentDir")
dirArray := strings.Split(parentDir, "/")

task, err := models.GetCloudbrainByJobID(jobID)
if err != nil {
log.Error("get cloud brain err:", err)
ctx.ServerError("get cloud brain information failed:", err)

}

//get dirs
dirs, err := routerRepo.GetResultDirs(task.JobName, parentDir)
if err != nil {
log.Error("GetModelDirs failed:%v", err.Error(), ctx.Data["msgID"])
ctx.ServerError("GetModelDirs failed:", err)
return
}

var fileInfos []storage.FileInfo
err = json.Unmarshal([]byte(dirs), &fileInfos)
if err != nil {
log.Error("json.Unmarshal failed:%v", err.Error(), ctx.Data["msgID"])
ctx.ServerError("json.Unmarshal failed:", err)
return
}

for i, fileInfo := range fileInfos {
temp, _ := time.Parse("2006-01-02 15:04:05", fileInfo.ModTime)
fileInfos[i].ModTime = temp.Local().Format("2006-01-02 15:04:05")
}

sort.Slice(fileInfos, func(i, j int) bool {
return fileInfos[i].ModTime > fileInfos[j].ModTime
})

ctx.JSON(http.StatusOK, map[string]interface{}{
"JobID": jobID,
"StatusOK": 0,
"Path": dirArray,
"Dirs": fileInfos,
"task": task,
"PageIsCloudBrain": true,
})

}

func GetCloudbrainModelConvertTask(ctx *context.APIContext) {
var (
err error
)
ID := ctx.Params(":id")
job, err := models.GetCloudbrainByID(ID)
job, err := models.QueryModelConvertById(ID)
if err != nil {
log.Error("GetCloudbrainByJobName failed: %v", err, ctx.Data["MsgID"])
ctx.ServerError(err.Error(), err)
ctx.NotFound(err)
log.Error("GetCloudbrainByID failed:", err)
return
}
if job.IsGpuTrainTask() {
jobResult, err := cloudbrain.GetJob(job.CloudBrainTaskId)
if err != nil {
ctx.NotFound(err)
log.Error("GetJob failed:", err)
return
}
result, _ := models.ConvertToJobResultPayload(jobResult.Payload)
if err != nil {
ctx.NotFound(err)
log.Error("ConvertToJobResultPayload failed:", err)
return
}

job.Status = result.JobStatus.State
taskRoles := result.TaskRoles
taskRes, _ := models.ConvertToTaskPod(taskRoles[cloudbrain.SubTaskName].(map[string]interface{}))
if result.JobStatus.State != string(models.JobWaiting) && result.JobStatus.State != string(models.JobFailed) {
job.ContainerIp = taskRes.TaskStatuses[0].ContainerIP
job.ContainerID = taskRes.TaskStatuses[0].ContainerID
job.Status = taskRes.TaskStatuses[0].State
}

if result.JobStatus.State != string(models.JobWaiting) {
models.ModelComputeAndSetDuration(job, result)
err = models.UpdateModelConvert(job)
if err != nil {
log.Error("UpdateJob failed:", err)
}
}
ctx.JSON(http.StatusOK, map[string]interface{}{
"ID": ID,
"JobName": result.Config.JobName,
"JobStatus": result.JobStatus.State,
"SubState": result.JobStatus.SubState,
"CreatedTime": time.Unix(result.JobStatus.CreatedTime/1000, 0).Format("2006-01-02 15:04:05"),
"CompletedTime": time.Unix(result.JobStatus.CompletedTime/1000, 0).Format("2006-01-02 15:04:05"),
})
} else {

result, err := modelarts.GetTrainJob(job.CloudBrainTaskId, job.ModelArtsVersionId)
if err != nil {
log.Error("get modelart job failed:", err)
ctx.NotFound(err)
return
}

job.Status = modelarts.TransTrainJobStatus(result.IntStatus)
job.RunTime = result.Duration / 1000
job.TrainJobDuration = models.ConvertDurationToStr(job.RunTime)
err = models.UpdateModelConvert(job)
if err != nil {
log.Error("UpdateJob failed:", err)
}

ctx.JSON(http.StatusOK, map[string]interface{}{
"ID": ID,
"JobStatus": job.Status,
})

}

}

func CloudbrainGetLogByJobId(jobId string, jobName string) map[string]interface{} {
var hits []models.Hits
result, err := cloudbrain.GetJobLog(job.JobID)
result, err := cloudbrain.GetJobLog(jobId)
if err != nil {
log.Error("GetJobLog failed: %v", err, ctx.Data["MsgID"])
ctx.ServerError(err.Error(), err)
return
log.Error("GetJobLog failed: %v", err)
return nil
}
hits = result.Hits.Hits

@@ -123,7 +308,7 @@ func CloudbrainGetLog(ctx *context.Context) {
for {
resultNext, err := cloudbrain.GetJobAllLog(result.ScrollID)
if err != nil {
log.Error("GetJobAllLog failed: %v", err, ctx.Data["MsgID"])
log.Error("GetJobAllLog failed: %v", err)
} else {
for _, hit := range resultNext.Hits.Hits {
hits = append(hits, hit)
@@ -148,12 +333,122 @@ func CloudbrainGetLog(ctx *context.Context) {
content += log.Source.Message + "\n"
}

ctx.JSON(http.StatusOK, map[string]interface{}{
"JobName": job.JobName,
return map[string]interface{}{
"JobName": jobName,
"Content": content,
})
}

}

func CloudbrainForModelConvertGetLog(ctx *context.Context) {
ID := ctx.Params(":id")
job, err := models.QueryModelConvertById(ID)
if err != nil {
log.Error("GetCloudbrainByJobName failed: %v", err, ctx.Data["MsgID"])
ctx.ServerError(err.Error(), err)
return
}

result := CloudbrainGetLogByJobId(job.CloudBrainTaskId, job.Name)
if result == nil {
log.Error("GetJobLog failed: %v", err, ctx.Data["MsgID"])
ctx.ServerError(err.Error(), err)
return
}
ctx.JSON(http.StatusOK, result)
}

func CloudbrainGetLog(ctx *context.Context) {
ID := ctx.Params(":id")
job, err := models.GetCloudbrainByID(ID)
if err != nil {
log.Error("GetCloudbrainByJobName failed: %v", err, ctx.Data["MsgID"])
ctx.ServerError(err.Error(), err)
return
}

result := CloudbrainGetLogByJobId(job.JobID, job.JobName)
if result == nil {
log.Error("GetJobLog failed: %v", err, ctx.Data["MsgID"])
ctx.ServerError(err.Error(), err)
return
}
ctx.JSON(http.StatusOK, result)
}

func CloudBrainModelConvertList(ctx *context.APIContext) {
var (
err error
)

ID := ctx.Params(":id")
parentDir := ctx.Query("parentDir")
dirArray := strings.Split(parentDir, "/")

job, err := models.QueryModelConvertById(ID)
if err != nil {
log.Error("GetCloudbrainByJobID(%s) failed:%v", job.Name, err.Error())
return
}
if job.IsGpuTrainTask() {
//get dirs
dirs, err := routerRepo.GetModelDirs(job.ID, parentDir)
if err != nil {
log.Error("GetModelDirs failed:%v", err.Error(), ctx.Data["msgID"])
ctx.ServerError("GetModelDirs failed:", err)
return
}

var fileInfos []storage.FileInfo
err = json.Unmarshal([]byte(dirs), &fileInfos)
if err != nil {
log.Error("json.Unmarshal failed:%v", err.Error(), ctx.Data["msgID"])
ctx.ServerError("json.Unmarshal failed:", err)
return
}

for i, fileInfo := range fileInfos {
temp, _ := time.Parse("2006-01-02 15:04:05", fileInfo.ModTime)
fileInfos[i].ModTime = temp.Local().Format("2006-01-02 15:04:05")
}

sort.Slice(fileInfos, func(i, j int) bool {
return fileInfos[i].ModTime > fileInfos[j].ModTime
})

ctx.JSON(http.StatusOK, map[string]interface{}{
"JobID": ID,
"VersionName": "",
"StatusOK": 0,
"Path": dirArray,
"Dirs": fileInfos,
"task": job,
"PageIsCloudBrain": true,
})
} else {
var jobID = ctx.Params(":id")
var versionName = "V0001"
parentDir := ctx.Query("parentDir")
dirArray := strings.Split(parentDir, "/")

models, err := storage.GetObsListObject(job.ID, "output/", parentDir, versionName)
if err != nil {
log.Info("get TrainJobListModel failed:", err)
ctx.ServerError("GetObsListObject:", err)
return
}

ctx.JSON(http.StatusOK, map[string]interface{}{
"JobID": jobID,
"VersionName": versionName,
"StatusOK": 0,
"Path": dirArray,
"Dirs": models,
"task": job,
"PageIsCloudBrain": true,
})
}

return
}

func CloudBrainModelList(ctx *context.APIContext) {
@@ -207,3 +502,94 @@ func CloudBrainModelList(ctx *context.APIContext) {
"PageIsCloudBrain": true,
})
}

type JobInfo struct {
JobName string `json:"job_name"`
AiCenterId int `json:"ai_center_id"`
}

func GetNewestJobs(ctx *context.APIContext) {
idsC2Net, err := models.GetNewestJobsByAiCenter()
if err != nil {
log.Error("GetNewestJobsByAiCenter(%s) failed:%v", err.Error())
return
}

idsCloudbrain, err := models.GetNewestJobsByType()
if err != nil {
log.Error("GetNewestJobsByType(%s) failed:%v", err.Error())
return
}

ids := make([]int64, len(idsC2Net), cap(idsC2Net)*2)
copy(ids, idsC2Net)
for _, id := range idsCloudbrain {
ids = append(ids, id)
}

jobs, err := models.GetCloudbrainByIDs(ids)
if err != nil {
log.Error("GetCloudbrainByIDs(%s) failed:%v", err.Error())
return
}

jobInfos := make([]JobInfo, 0)
for _, job := range jobs {
var id int
var content string
switch job.Type {
case models.TypeCloudBrainOne:
id, content = getAICenterID("cloudbrain_one")
if content == "" {
log.Error("job(%s) has no match config info", job.DisplayJobName)
continue
}
case models.TypeCloudBrainTwo:
id, content = getAICenterID("cloudbrain_two")
if content == "" {
log.Error("job(%s) has no match config info", job.DisplayJobName)
continue
}
case models.TypeC2Net:
centerInfo := strings.Split(job.AiCenter, "+")
if len(centerInfo) != 2 {
log.Error("job(%s):ai_center(%s) is wrong", job.DisplayJobName, job.AiCenter)
continue
}
id, content = getAICenterID(centerInfo[0])
if content == "" {
log.Error("job(%s) has no match config info", job.DisplayJobName)
continue
}
default:
log.Error("no match info")
continue
}

jobInfos = append(jobInfos, JobInfo{
JobName: job.DisplayJobName,
AiCenterId: id,
})
}

ctx.JSON(http.StatusOK, jobInfos)
}

func GetAICenterInfo(ctx *context.APIContext) {
if setting.C2NetInfos == nil {
log.Error("C2NET_SEQUENCE is incorrect")
return
}

ctx.JSON(http.StatusOK, setting.C2NetInfos.C2NetSqInfo)
}

func getAICenterID(name string) (int, string) {
for _, info := range setting.C2NetInfos.C2NetSqInfo {
if name == info.Name {
return info.ID, info.Content
}
}

return 0, ""
}

+ 20
- 4
routers/api/v1/repo/cloudbrain_dashboard.go View File

@@ -752,10 +752,26 @@ func GetCloudbrainsDetailData(ctx *context.Context) {
taskDetail.RepoAlias = ciTasks[i].Repo.OwnerName + "/" + ciTasks[i].Repo.Alias
}
if ciTasks[i].Cloudbrain.Status == string(models.JobWaiting) {
WaitTimeInt := time.Now().Unix() - ciTasks[i].Cloudbrain.CreatedUnix.AsTime().Unix()
taskDetail.WaitTime = models.ConvertDurationToStr(WaitTimeInt)
if WaitTimeInt < 0 {
taskDetail.WaitTime = "00:00:00"
if ciTasks[i].Cloudbrain.DeletedAt != nilTime {
WaitTimeInt := ciTasks[i].Cloudbrain.UpdatedUnix.AsTime().Unix() - ciTasks[i].Cloudbrain.CreatedUnix.AsTime().Unix()
taskDetail.WaitTime = models.ConvertDurationToStr(WaitTimeInt)
if WaitTimeInt < 0 {
taskDetail.WaitTime = "00:00:00"
}
} else {
if ciTasks[i].Cloudbrain.StartTime.AsTime().Unix() == 0 {
WaitTimeInt := time.Now().Unix() - ciTasks[i].Cloudbrain.CreatedUnix.AsTime().Unix()
taskDetail.WaitTime = models.ConvertDurationToStr(WaitTimeInt)
if WaitTimeInt < 0 {
taskDetail.WaitTime = "00:00:00"
}
} else {
WaitTimeInt := ciTasks[i].Cloudbrain.StartTime.AsTime().Unix() - ciTasks[i].Cloudbrain.CreatedUnix.AsTime().Unix()
taskDetail.WaitTime = models.ConvertDurationToStr(WaitTimeInt)
if WaitTimeInt < 0 {
taskDetail.WaitTime = "00:00:00"
}
}
}
} else if ciTasks[i].Cloudbrain.Status == string(models.JobStopped) && ciTasks[i].Cloudbrain.StartTime.AsTime().Unix() == 0 {
WaitTimeInt := ciTasks[i].Cloudbrain.EndTime.AsTime().Unix() - ciTasks[i].Cloudbrain.CreatedUnix.AsTime().Unix()


+ 91
- 9
routers/api/v1/repo/modelarts.go View File

@@ -6,12 +6,15 @@
package repo

import (
"code.gitea.io/gitea/modules/grampus"
"encoding/json"
"net/http"
"path"
"strconv"
"strings"

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

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/cloudbrain"
"code.gitea.io/gitea/modules/context"
@@ -240,13 +243,12 @@ func GetModelArtsTrainJobVersion(ctx *context.APIContext) {

}

func TrainJobGetLog(ctx *context.APIContext) {
func TrainJobForModelConvertGetLog(ctx *context.APIContext) {
var (
err error
)

var jobID = ctx.Params(":jobid")
var versionName = ctx.Query("version_name")
var jobID = ctx.Params(":id")
var baseLine = ctx.Query("base_line")
var order = ctx.Query("order")
var lines = ctx.Query("lines")
@@ -263,10 +265,18 @@ func TrainJobGetLog(ctx *context.APIContext) {
return
}

resultLogFile, result, err := trainJobGetLogContent(jobID, versionName, baseLine, order, lines_int)
resultLogFile, result, err := trainJobForModelConvertGetLogContent(jobID, baseLine, order, lines_int)
if err != nil {
log.Error("trainJobGetLog(%s) failed:%v", jobID, err.Error())
// ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobShow, nil)
ctx.JSON(http.StatusOK, map[string]interface{}{
"JobID": jobID,
"LogFileName": "",
"StartLine": "0",
"EndLine": "0",
"Content": "",
"Lines": 0,
})
return
}

@@ -282,20 +292,92 @@ func TrainJobGetLog(ctx *context.APIContext) {
})
}

func trainJobGetLogContent(jobID string, versionName string, baseLine string, order string, lines int) (*models.GetTrainJobLogFileNamesResult, *models.GetTrainJobLogResult, error) {
task, err := models.GetCloudbrainByJobIDAndVersionName(jobID, versionName)
func trainJobForModelConvertGetLogContent(jobID string, baseLine string, order string, lines int) (*models.GetTrainJobLogFileNamesResult, *models.GetTrainJobLogResult, error) {
task, err := models.QueryModelConvertById(jobID)
if err != nil {
log.Error("GetCloudbrainByJobID(%s) failed:%v", jobID, err.Error())
return nil, nil, err
}
resultLogFile, err := modelarts.GetTrainJobLogFileNames(task.CloudBrainTaskId, task.ModelArtsVersionId)
if err != nil {
log.Error("GetTrainJobLogFileNames(%s) failed:%v", task.CloudBrainTaskId, err.Error())
return nil, nil, err
}
result, err := modelarts.GetTrainJobLog(task.CloudBrainTaskId, task.ModelArtsVersionId, baseLine, resultLogFile.LogFileList[0], order, lines)
if err != nil {
log.Error("GetTrainJobLog(%s) failed:%v", task.CloudBrainTaskId, err.Error())
return nil, nil, err
}

resultLogFile, err := modelarts.GetTrainJobLogFileNames(jobID, strconv.FormatInt(task.VersionID, 10))
return resultLogFile, result, err
}

func TrainJobGetLog(ctx *context.APIContext) {
var (
err error
)

var jobID = ctx.Params(":jobid")
var versionName = ctx.Query("version_name")
var baseLine = ctx.Query("base_line")
var order = ctx.Query("order")
var lines = ctx.Query("lines")
lines_int, err := strconv.Atoi(lines)
if err != nil {
log.Error("change lines(%d) string to int failed", lines_int)
}

if order != modelarts.OrderDesc && order != modelarts.OrderAsc {
log.Error("order(%s) check failed", order)
ctx.JSON(http.StatusBadRequest, map[string]interface{}{
"err_msg": "order check failed",
})
return
}

task, err := models.GetCloudbrainByJobIDAndVersionName(jobID, versionName)
if err != nil {
log.Error("GetCloudbrainByJobID(%s) failed:%v", jobID, err.Error())
return
}
resultLogFile, result, err := trainJobGetLogContent(jobID, task.VersionID, baseLine, order, lines_int)
if err != nil {
log.Error("trainJobGetLog(%s) failed:%v", jobID, err.Error())
// ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobShow, nil)
return
}

prefix := strings.TrimPrefix(path.Join(setting.TrainJobModelPath, task.JobName, modelarts.LogPath, versionName), "/") + "/job"
_, err = storage.GetObsLogFileName(prefix)
var canLogDownload bool
if err != nil {
canLogDownload = false
} else {
canLogDownload = true
}

ctx.Data["log_file_name"] = resultLogFile.LogFileList[0]

ctx.JSON(http.StatusOK, map[string]interface{}{
"JobID": jobID,
"LogFileName": resultLogFile.LogFileList[0],
"StartLine": result.StartLine,
"EndLine": result.EndLine,
"Content": result.Content,
"Lines": result.Lines,
"CanLogDownload": canLogDownload,
})
}

func trainJobGetLogContent(jobID string, versionID int64, baseLine string, order string, lines int) (*models.GetTrainJobLogFileNamesResult, *models.GetTrainJobLogResult, error) {

resultLogFile, err := modelarts.GetTrainJobLogFileNames(jobID, strconv.FormatInt(versionID, 10))
if err != nil {
log.Error("GetTrainJobLogFileNames(%s) failed:%v", jobID, err.Error())
return nil, nil, err
}

result, err := modelarts.GetTrainJobLog(jobID, strconv.FormatInt(task.VersionID, 10), baseLine, resultLogFile.LogFileList[0], order, lines)
result, err := modelarts.GetTrainJobLog(jobID, strconv.FormatInt(versionID, 10), baseLine, resultLogFile.LogFileList[0], order, lines)
if err != nil {
log.Error("GetTrainJobLog(%s) failed:%v", jobID, err.Error())
return nil, nil, err


+ 9
- 0
routers/home.go View File

@@ -40,6 +40,7 @@ const (
tplExploreImages base.TplName = "explore/images"
tplExploreExploreDataAnalysis base.TplName = "explore/data_analysis"
tplHomeTerm base.TplName = "terms"
tplHomePrivacy base.TplName = "privacy"
)

// Home render home page
@@ -117,6 +118,10 @@ func Dashboard(ctx *context.Context) {
ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/change_password"
ctx.SetCookie("redirect_to", setting.AppSubURL+ctx.Req.URL.RequestURI(), 0, setting.AppSubURL)
ctx.Redirect(setting.AppSubURL + "/user/settings/change_password")
} else if setting.PhoneService.Enabled && ctx.User.IsActive && ctx.User.PhoneNumber == "" {
ctx.Data["Title"] = ctx.Tr("phone.bind_phone")
ctx.HTML(200, "user/auth/bind_phone")
return
} else {
user.Dashboard(ctx)
}
@@ -804,3 +809,7 @@ func RecommendHomeInfo(ctx *context.Context) {
func HomeTerm(ctx *context.Context) {
ctx.HTML(200, tplHomeTerm)
}

func HomePrivacy(ctx *context.Context) {
ctx.HTML(200, tplHomePrivacy)
}

+ 3
- 0
routers/init.go View File

@@ -10,6 +10,8 @@ import (
"strings"
"time"

"code.gitea.io/gitea/modules/slideimage"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/migrations"
"code.gitea.io/gitea/modules/auth/sso"
@@ -57,6 +59,7 @@ func checkRunMode() {
// NewServices init new services
func NewServices() {
setting.NewServices()
slideimage.InitSlideImage()
if err := storage.Init(); err != nil {
log.Fatal("storage init failed: %v", err)
}


+ 795
- 0
routers/repo/ai_model_convert.go View File

@@ -0,0 +1,795 @@
package repo

import (
"bufio"
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"path"
"strings"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/cloudbrain"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/modelarts"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/modules/timeutil"
uuid "github.com/satori/go.uuid"
)

const (
tplModelManageConvertIndex = "repo/modelmanage/convertIndex"
tplModelConvertInfo = "repo/modelmanage/convertshowinfo"
PYTORCH_ENGINE = 0
TENSORFLOW_ENGINE = 1
MINDSPORE_ENGIN = 2
ModelMountPath = "/model"
CodeMountPath = "/code"
DataSetMountPath = "/dataset"
LogFile = "log.txt"
DefaultBranchName = "master"
SubTaskName = "task1"
//GpuQueue = "openidgx"
Success = "S000"
//GPU_PYTORCH_IMAGE = "dockerhub.pcl.ac.cn:5000/user-images/openi:tensorRT_7_zouap"
//GPU_TENSORFLOW_IMAGE = "dockerhub.pcl.ac.cn:5000/user-images/openi:tf2onnx"
//NPU_MINDSPORE_16_IMAGE = "swr.cn-south-222.ai.pcl.cn/openi/mindspore1.6.1_train_v1_openi:v3_ascend"
//PytorchOnnxBootFile = "convert_pytorch.py"
//PytorchTrTBootFile = "convert_pytorch_tensorrt.py"
//MindsporeBootFile = "convert_mindspore.py"
//TensorFlowNpuBootFile = "convert_tensorflow.py"
//TensorFlowGpuBootFile = "convert_tensorflow_gpu.py"

//ConvertRepoPath = "https://git.openi.org.cn/zouap/npu_test"

CONVERT_FORMAT_ONNX = 0
CONVERT_FORMAT_TRT = 1

NetOutputFormat_FP32 = 0
NetOutputFormat_FP16 = 1

NPU_MINDSPORE_IMAGE_ID = 35
NPU_TENSORFLOW_IMAGE_ID = 121

//GPU_Resource_Specs_ID = 1 //cpu 1, gpu 1

//NPU_FlavorCode = "modelarts.bm.910.arm.public.1"
//NPU_PoolID = "pool7908321a"
)

var (
TrainResourceSpecs *models.ResourceSpecs
)

func SaveModelConvert(ctx *context.Context) {
log.Info("save model convert start.")
if !ctx.Repo.CanWrite(models.UnitTypeModelManage) {
ctx.JSON(200, map[string]string{
"result_code": "1",
"message": ctx.Tr("repo.modelconvert.manage.no_operate_right"),
})
return
}
name := ctx.Query("name")
desc := ctx.Query("desc")
modelId := ctx.Query("modelId")
modelPath := ctx.Query("ModelFile")
SrcEngine := ctx.QueryInt("SrcEngine")
InputShape := ctx.Query("inputshape")
InputDataFormat := ctx.Query("inputdataformat")
DestFormat := ctx.QueryInt("DestFormat")
NetOutputFormat := ctx.QueryInt("NetOutputFormat")

task, err := models.QueryModelById(modelId)
if err != nil {
log.Error("no such model!", err.Error())
ctx.JSON(200, map[string]string{
"result_code": "1",
"message": ctx.Tr("repo.modelconvert.manage.model_not_exist"),
})
return
}

convertList, err := models.QueryModelConvertByRepoID(ctx.Repo.Repository.ID)
if err == nil {
for _, convert := range convertList {
if convert.Name == name {
log.Info("convert.Name=" + name + " convert.id=" + convert.ID)
ctx.JSON(200, map[string]string{
"result_code": "1",
"message": ctx.Tr("repo.modelconvert.manage.create_error1"),
})
return
}
}
}

convertList, err = models.QueryModelConvertByUserID(ctx.User.ID)
if err == nil {
for _, convert := range convertList {
if isRunningTask(convert.Status) {
log.Info("convert.Status=" + convert.Status + " convert.id=" + convert.ID)
ctx.JSON(200, map[string]string{
"result_code": "1",
"message": ctx.Tr("repo.modelconvert.manage.create_error2"),
})
return
}
}
}

uuid := uuid.NewV4()
id := uuid.String()
modelConvert := &models.AiModelConvert{
ID: id,
Name: name,
Description: desc,
Status: string(models.JobWaiting),
SrcEngine: SrcEngine,
RepoId: ctx.Repo.Repository.ID,
ModelName: task.Name,
ModelVersion: task.Version,
ModelId: modelId,
ModelPath: modelPath,
DestFormat: DestFormat,
NetOutputFormat: NetOutputFormat,
InputShape: InputShape,
InputDataFormat: InputDataFormat,
UserId: ctx.User.ID,
}
models.SaveModelConvert(modelConvert)
go goCreateTask(modelConvert, ctx, task)

ctx.JSON(200, map[string]string{
"result_code": "0",
})
}

func isRunningTask(status string) bool {
stopStatus := []string{"COMPLETED", "STOPPED", "FAILED", "START_FAILED", "STOPPING", "SUCCEEDED"}
for _, sta := range stopStatus {
if sta == status {
return false
}
}
return true
}

func goCreateTask(modelConvert *models.AiModelConvert, ctx *context.Context, task *models.AiModelManage) error {
if modelConvert.IsGpuTrainTask() {
log.Info("create gpu train job.")
return createGpuTrainJob(modelConvert, ctx, task)
} else {
//create npu job
log.Info("create npu train job.")
return createNpuTrainJob(modelConvert, ctx, task.Path)
}
}

func createNpuTrainJob(modelConvert *models.AiModelConvert, ctx *context.Context, modelRelativePath string) error {
VersionOutputPath := "V0001"
codeLocalPath := setting.JobPath + modelConvert.ID + modelarts.CodePath
codeObsPath := "/" + setting.Bucket + modelarts.JobPath + modelConvert.ID + modelarts.CodePath
outputObsPath := "/" + setting.Bucket + modelarts.JobPath + modelConvert.ID + modelarts.OutputPath + VersionOutputPath + "/"
logObsPath := "/" + setting.Bucket + modelarts.JobPath + modelConvert.ID + modelarts.LogPath + VersionOutputPath + "/"
dataPath := "/" + modelRelativePath

_, err := ioutil.ReadDir(codeLocalPath)
if err == nil {
deleteLocalDir(codeLocalPath)
}
if err := downloadConvertCode(setting.ModelConvert.ConvertRepoPath, codeLocalPath, DefaultBranchName); err != nil {
log.Error("downloadCode failed, server timed out: %s (%v)", setting.ModelConvert.ConvertRepoPath, err)
return err
}
if err := obsMkdir(setting.CodePathPrefix + modelConvert.ID + modelarts.OutputPath + VersionOutputPath + "/"); err != nil {
log.Error("Failed to obsMkdir_output: %s (%v)", modelConvert.ID+modelarts.OutputPath, err)
return err
}
if err := obsMkdir(setting.CodePathPrefix + modelConvert.ID + modelarts.LogPath + VersionOutputPath + "/"); err != nil {
log.Error("Failed to obsMkdir_log: %s (%v)", modelConvert.ID+modelarts.LogPath, err)
return err
}
if err := uploadCodeToObs(codeLocalPath, modelConvert.ID, ""); err != nil {
log.Error("Failed to uploadCodeToObs: %s (%v)", modelConvert.ID, err)
return err
}
deleteLocalDir(codeLocalPath)

intputshape := strings.Split(modelConvert.InputShape, ",")
n := "256"
c := "1"
h := "28"
w := "28"
if len(intputshape) == 4 {
n = intputshape[0]
c = intputshape[1]
h = intputshape[2]
w = intputshape[3]
}

var engineId int64
engineId = int64(NPU_MINDSPORE_IMAGE_ID)
bootfile := setting.ModelConvert.MindsporeBootFile
if modelConvert.SrcEngine == TENSORFLOW_ENGINE {
engineId = int64(NPU_TENSORFLOW_IMAGE_ID)
bootfile = setting.ModelConvert.TensorFlowNpuBootFile
}
userCommand := "/bin/bash /home/work/run_train.sh 's3://" + codeObsPath + "' 'code/" + bootfile + "' '/tmp/log/train.log' --'data_url'='s3://" + dataPath + "' --'train_url'='s3://" + outputObsPath + "'"
userCommand += " --'model'='" + modelConvert.ModelPath + "'"
userCommand += " --'n'='" + fmt.Sprint(n) + "'"
userCommand += " --'c'='" + fmt.Sprint(c) + "'"
userCommand += " --'h'='" + fmt.Sprint(h) + "'"
userCommand += " --'w'='" + fmt.Sprint(w) + "'"

req := &modelarts.GenerateTrainJobReq{
JobName: modelConvert.ID,
DisplayJobName: modelConvert.Name,
DataUrl: dataPath,
Description: modelConvert.Description,
CodeObsPath: codeObsPath,
BootFileUrl: codeObsPath + bootfile,
BootFile: bootfile,
TrainUrl: outputObsPath,
FlavorCode: setting.ModelConvert.NPU_FlavorCode,
WorkServerNumber: 1,
IsLatestVersion: modelarts.IsLatestVersion,
EngineID: engineId,
LogUrl: logObsPath,
PoolID: setting.ModelConvert.NPU_PoolID,
//Parameters: param,
BranchName: DefaultBranchName,
UserImageUrl: setting.ModelConvert.NPU_MINDSPORE_16_IMAGE,
UserCommand: userCommand,
}
result, err := modelarts.GenerateModelConvertTrainJob(req)
if err == nil {
log.Info("jobId=" + fmt.Sprint(result.JobID) + " versionid=" + fmt.Sprint(result.VersionID))
models.UpdateModelConvertModelArts(modelConvert.ID, fmt.Sprint(result.JobID), fmt.Sprint(result.VersionID))
} else {
log.Info("create modelarts taks failed.error=" + err.Error())
models.UpdateModelConvertFailed(modelConvert.ID, "FAILED", err.Error())
}
return err
}

func downloadConvertCode(repopath string, codePath, branchName string) error {
//add "file:///" prefix to make the depth valid
if err := git.Clone(repopath, codePath, git.CloneRepoOptions{Branch: branchName, Depth: 1}); err != nil {
log.Error("Failed to clone repository: %s (%v)", repopath, err)
return err
}
log.Info("srcPath=" + repopath + " codePath=" + codePath)
configFile, err := os.OpenFile(codePath+"/.git/config", os.O_RDWR, 0666)
if err != nil {
log.Error("open file(%s) failed:%v", codePath+"/,git/config", err)
return err
}

defer configFile.Close()

pos := int64(0)
reader := bufio.NewReader(configFile)
for {
line, err := reader.ReadString('\n')
if err != nil {
if err == io.EOF {
log.Error("not find the remote-url")
return nil
} else {
log.Error("read error: %v", err)
return err
}
}

if strings.Contains(line, "url") && strings.Contains(line, ".git") {
originUrl := "\turl = " + repopath + "\n"
if len(line) > len(originUrl) {
originUrl += strings.Repeat(" ", len(line)-len(originUrl))
}
bytes := []byte(originUrl)
_, err := configFile.WriteAt(bytes, pos)
if err != nil {
log.Error("WriteAt failed:%v", err)
return err
}
break
}

pos += int64(len(line))
}

return nil
}

func downloadFromObsToLocal(task *models.AiModelManage, localPath string) error {
path := Model_prefix + models.AttachmentRelativePath(task.ID) + "/"
allFile, err := storage.GetAllObjectByBucketAndPrefix(setting.Bucket, path)
if err == nil {
_, errState := os.Stat(localPath)
if errState != nil {
if err = os.MkdirAll(localPath, os.ModePerm); err != nil {
return err
}
}
for _, oneFile := range allFile {
if oneFile.IsDir {
log.Info(" dir name:" + oneFile.FileName)
} else {
allFileName := localPath + "/" + oneFile.FileName
index := strings.LastIndex(allFileName, "/")
if index != -1 {
parentDir := allFileName[0:index]
if err = os.MkdirAll(parentDir, os.ModePerm); err != nil {
log.Info("make dir may be error," + err.Error())
}
}
fDest, err := os.Create(allFileName)
if err != nil {
log.Info("create file error, download file failed: %s\n", err.Error())
return err
}
body, err := storage.ObsDownloadAFile(setting.Bucket, path+oneFile.FileName)
if err != nil {
log.Info("download file failed: %s\n", err.Error())
return err
} else {
defer body.Close()
p := make([]byte, 1024)
var readErr error
var readCount int
// 读取对象内容
for {
readCount, readErr = body.Read(p)
if readCount > 0 {
fDest.Write(p[:readCount])
}
if readErr != nil {
break
}
}
}
}
}
} else {
log.Info("error,msg=" + err.Error())
return err
}
return nil
}

func createGpuTrainJob(modelConvert *models.AiModelConvert, ctx *context.Context, model *models.AiModelManage) error {
modelRelativePath := model.Path
command := ""
IMAGE_URL := setting.ModelConvert.GPU_PYTORCH_IMAGE
dataActualPath := setting.Attachment.Minio.RealPath + modelRelativePath

if modelConvert.SrcEngine == PYTORCH_ENGINE {
if modelConvert.DestFormat == CONVERT_FORMAT_ONNX {
command = getGpuModelConvertCommand(modelConvert.ID, modelConvert.ModelPath, modelConvert, setting.ModelConvert.PytorchOnnxBootFile)
} else if modelConvert.DestFormat == CONVERT_FORMAT_TRT {
command = getGpuModelConvertCommand(modelConvert.ID, modelConvert.ModelPath, modelConvert, setting.ModelConvert.PytorchTrTBootFile)
} else {
return errors.New("Not support the format.")
}
} else if modelConvert.SrcEngine == TENSORFLOW_ENGINE {
IMAGE_URL = setting.ModelConvert.GPU_TENSORFLOW_IMAGE
if modelConvert.DestFormat == CONVERT_FORMAT_ONNX {
command = getGpuModelConvertCommand(modelConvert.ID, modelConvert.ModelPath, modelConvert, setting.ModelConvert.TensorFlowGpuBootFile)
} else {
return errors.New("Not support the format.")
}
//如果模型在OBS上,需要下载到本地,并上传到minio中
if model.Type == models.TypeCloudBrainTwo {
relatetiveModelPath := setting.JobPath + modelConvert.ID + "/dataset"
log.Info("local dataset path:" + relatetiveModelPath)
downloadFromObsToLocal(model, relatetiveModelPath)
uploadCodeToMinio(relatetiveModelPath+"/", modelConvert.ID, "/dataset/")
deleteLocalDir(relatetiveModelPath)
dataActualPath = setting.Attachment.Minio.RealPath + setting.Attachment.Minio.Bucket + "/" + setting.CBCodePathPrefix + modelConvert.ID + "/dataset"
}
}
log.Info("dataActualPath=" + dataActualPath)

log.Info("command=" + command)

codePath := setting.JobPath + modelConvert.ID + CodeMountPath
downloadConvertCode(setting.ModelConvert.ConvertRepoPath, codePath, DefaultBranchName)

uploadCodeToMinio(codePath+"/", modelConvert.ID, CodeMountPath+"/")
deleteLocalDir(codePath)

minioCodePath := setting.Attachment.Minio.RealPath + setting.Attachment.Minio.Bucket + "/" + setting.CBCodePathPrefix + modelConvert.ID + "/code"
log.Info("minio codePath=" + minioCodePath)

modelPath := setting.JobPath + modelConvert.ID + ModelMountPath + "/"
log.Info("local modelPath=" + modelPath)
mkModelPath(modelPath)

uploadCodeToMinio(modelPath, modelConvert.ID, ModelMountPath+"/")
deleteLocalDir(modelPath)

minioModelPath := setting.Attachment.Minio.RealPath + setting.Attachment.Minio.Bucket + "/" + setting.CBCodePathPrefix + modelConvert.ID + "/model"
log.Info("minio model path=" + minioModelPath)

if TrainResourceSpecs == nil {
json.Unmarshal([]byte(setting.TrainResourceSpecs), &TrainResourceSpecs)
}
resourceSpec := TrainResourceSpecs.ResourceSpec[setting.ModelConvert.GPU_Resource_Specs_ID]
jobResult, err := cloudbrain.CreateJob(modelConvert.ID, models.CreateJobParams{
JobName: modelConvert.ID,
RetryCount: 1,
GpuType: setting.ModelConvert.GpuQueue,
Image: IMAGE_URL,
TaskRoles: []models.TaskRole{
{
Name: SubTaskName,
TaskNumber: 1,
MinSucceededTaskCount: 1,
MinFailedTaskCount: 1,
CPUNumber: resourceSpec.CpuNum,
GPUNumber: resourceSpec.GpuNum,
MemoryMB: resourceSpec.MemMiB,
ShmMB: resourceSpec.ShareMemMiB,
Command: command,
NeedIBDevice: false,
IsMainRole: false,
UseNNI: false,
},
},
Volumes: []models.Volume{
{
HostPath: models.StHostPath{
Path: minioCodePath,
MountPath: CodeMountPath,
ReadOnly: false,
},
},
{
HostPath: models.StHostPath{
Path: dataActualPath,
MountPath: DataSetMountPath,
ReadOnly: true,
},
},
{
HostPath: models.StHostPath{
Path: minioModelPath,
MountPath: ModelMountPath,
ReadOnly: false,
},
},
},
})
if err != nil {
log.Error("CreateJob failed:", err.Error(), ctx.Data["MsgID"])
models.UpdateModelConvertFailed(modelConvert.ID, "FAILED", err.Error())
return err
}
if jobResult.Code != Success {
log.Error("CreateJob(%s) failed:%s", modelConvert.ID, jobResult.Msg, ctx.Data["MsgID"])
models.UpdateModelConvertFailed(modelConvert.ID, "FAILED", err.Error())
return errors.New(jobResult.Msg)
}

var jobID = jobResult.Payload["jobId"].(string)
log.Info("jobId=" + jobID)
models.UpdateModelConvertCBTI(modelConvert.ID, jobID)

return nil
}

func deleteLocalDir(dirpath string) {
//TODO delete
_err := os.RemoveAll(dirpath)
if _err == nil {
log.Info("Delete local file:" + dirpath)
} else {
log.Info("Delete local file error: path=" + dirpath)
}
}

func getGpuModelConvertCommand(name string, modelFile string, modelConvert *models.AiModelConvert, bootfile string) string {
var command string

inputshape := strings.Split(modelConvert.InputShape, ",")
n := "256"
c := "1"
h := "28"
w := "28"
if len(inputshape) == 4 {
n = inputshape[0]
c = inputshape[1]
h = inputshape[2]
w = inputshape[3]
}
command += "python3 /code/" + bootfile + " --model " + modelFile + " --n " + n + " --c " + c + " --h " + h + " --w " + w
if modelConvert.DestFormat == CONVERT_FORMAT_TRT {
if modelConvert.NetOutputFormat == NetOutputFormat_FP16 {
command += " --fp16 True"
} else {
command += " --fp16 False"
}
}
command += " > " + ModelMountPath + "/" + name + "-" + LogFile
return command
}

func DeleteModelConvert(ctx *context.Context) {
log.Info("delete model convert start.")
id := ctx.Params(":id")
task, err := models.QueryModelConvertById(id)
if err == nil {
go deleteCloudBrainTask(task)
}
err = models.DeleteModelConvertById(id)
//TODO delete OBS文件及云脑任务
if err != nil {
ctx.JSON(500, err.Error())
} else {
ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelmanage/convert_model")
}
}

func deleteCloudBrainTask(task *models.AiModelConvert) {
if task.IsGpuTrainTask() {
log.Info("delete cloudbrain one resource.")
dirPath := setting.CBCodePathPrefix + task.ID + "/"
err := storage.Attachments.DeleteDir(dirPath)
if err != nil {
log.Error("DeleteDir(%s) failed:%v", dirPath, err)
}
} else {
log.Info("delete cloudbrain two resource.")
_, err := modelarts.DelTrainJob(task.CloudBrainTaskId)
if err != nil {
log.Error("DelTrainJob(%s) failed:%v", task.CloudBrainTaskId, err.Error())
}
DeleteJobStorage(task.ID)
}
}

func StopModelConvert(ctx *context.Context) {
id := ctx.Params(":id")
log.Info("stop model convert start.id=" + id)
job, err := models.QueryModelConvertById(id)
if err != nil {
ctx.ServerError("Not found task.", err)
return
}
if job.IsGpuTrainTask() {
err = cloudbrain.StopJob(job.CloudBrainTaskId)
if err != nil {
log.Error("Stop cloudbrain Job(%s) failed:%v", job.CloudBrainTaskId, err)
}
} else {
_, err = modelarts.StopTrainJob(job.CloudBrainTaskId, job.ModelArtsVersionId)
if err != nil {
log.Error("Stop modelarts Job(%s) failed:%v", job.CloudBrainTaskId, err)
}
}
job.Status = string(models.JobStopped)
if job.EndTime == 0 {
job.EndTime = timeutil.TimeStampNow()
}
models.ModelConvertSetDuration(job)
err = models.UpdateModelConvert(job)
if err != nil {
log.Error("UpdateModelConvert failed:", err)
}
ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelmanage/convert_model")
}

func ShowModelConvertInfo(ctx *context.Context) {
ctx.Data["ID"] = ctx.Query("ID")
ctx.Data["isModelManage"] = true
ctx.Data["ModelManageAccess"] = ctx.Repo.CanWrite(models.UnitTypeModelManage)

job, err := models.QueryModelConvertById(ctx.Query("ID"))
if err == nil {
if job.TrainJobDuration == "" {
job.TrainJobDuration = "00:00:00"
}
ctx.Data["task"] = job
} else {
ctx.ServerError("Not found task.", err)
return
}
ctx.Data["Name"] = job.Name
ctx.Data["canDownload"] = isOper(ctx, job.UserId)
user, err := models.GetUserByID(job.UserId)
if err == nil {
job.UserName = user.Name
job.UserRelAvatarLink = user.RelAvatarLink()
}

if job.IsGpuTrainTask() {
ctx.Data["npu_display"] = "none"
ctx.Data["gpu_display"] = "block"
if job.CloudBrainTaskId == "" {
ctx.Data["ExitDiagnostics"] = ""
ctx.Data["AppExitDiagnostics"] = ""
ctx.HTML(200, tplModelConvertInfo)
return
}
result, err := cloudbrain.GetJob(job.CloudBrainTaskId)
if err != nil {
log.Info("error:" + err.Error())
ctx.Data["error"] = err.Error()
ctx.HTML(200, tplModelConvertInfo)
return
}
if result != nil {
jobRes, _ := models.ConvertToJobResultPayload(result.Payload)
ctx.Data["result"] = jobRes
taskRoles := jobRes.TaskRoles
taskRes, _ := models.ConvertToTaskPod(taskRoles[cloudbrain.SubTaskName].(map[string]interface{}))
ctx.Data["taskRes"] = taskRes
ctx.Data["ExitDiagnostics"] = taskRes.TaskStatuses[0].ExitDiagnostics
ctx.Data["AppExitDiagnostics"] = jobRes.JobStatus.AppExitDiagnostics

job.Status = jobRes.JobStatus.State

if jobRes.JobStatus.State != string(models.JobWaiting) && jobRes.JobStatus.State != string(models.JobFailed) {
job.ContainerIp = taskRes.TaskStatuses[0].ContainerIP
job.ContainerID = taskRes.TaskStatuses[0].ContainerID
job.Status = taskRes.TaskStatuses[0].State
}
if jobRes.JobStatus.State != string(models.JobWaiting) {
models.ModelComputeAndSetDuration(job, jobRes)
err = models.UpdateModelConvert(job)
if err != nil {
log.Error("UpdateModelConvert failed:", err)
}
}
}
} else {
if job.CloudBrainTaskId != "" {
result, err := modelarts.GetTrainJob(job.CloudBrainTaskId, job.ModelArtsVersionId)
if err != nil {
log.Info("error:" + err.Error())
ctx.Data["error"] = err.Error()
return
}
job.Status = modelarts.TransTrainJobStatus(result.IntStatus)
job.RunTime = result.Duration / 1000
job.TrainJobDuration = models.ConvertDurationToStr(job.RunTime)
err = models.UpdateModelConvert(job)
if err != nil {
log.Error("UpdateJob failed:", err)
}
}
ctx.Data["npu_display"] = "block"
ctx.Data["gpu_display"] = "none"
ctx.Data["ExitDiagnostics"] = ""
ctx.Data["AppExitDiagnostics"] = ""
}

ctx.HTML(200, tplModelConvertInfo)
}

func ConvertModelTemplate(ctx *context.Context) {
ctx.Data["isModelManage"] = true
ctx.Data["TRAIN_COUNT"] = 0
SetModelCount(ctx)
ctx.Data["ModelManageAccess"] = ctx.Repo.CanWrite(models.UnitTypeModelManage)
ShowModelConvertPageInfo(ctx)
ctx.HTML(200, tplModelManageConvertIndex)
}

func ShowModelConvertPageInfo(ctx *context.Context) {
log.Info("ShowModelConvertInfo start.")
if !isQueryRight(ctx) {
log.Info("no right.")
ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
return
}
page := ctx.QueryInt("page")
if page <= 0 {
page = 1
}
pageSize := ctx.QueryInt("pageSize")
if pageSize <= 0 {
pageSize = setting.UI.IssuePagingNum
}
repoId := ctx.Repo.Repository.ID
modelResult, count, err := models.QueryModelConvert(&models.AiModelQueryOptions{
ListOptions: models.ListOptions{
Page: page,
PageSize: pageSize,
},
RepoID: repoId,
})
if err != nil {
log.Info("query db error." + err.Error())
ctx.ServerError("Cloudbrain", err)
return
}
ctx.Data["MODEL_CONVERT_COUNT"] = count
userIds := make([]int64, len(modelResult))
for i, model := range modelResult {
model.IsCanOper = isOper(ctx, model.UserId)
model.IsCanDelete = isCanDelete(ctx, model.UserId)
userIds[i] = model.UserId
}
userNameMap := queryUserName(userIds)
for _, model := range modelResult {
value := userNameMap[model.UserId]
if value != nil {
model.UserName = value.Name
model.UserRelAvatarLink = value.RelAvatarLink()
}
}
pager := context.NewPagination(int(count), page, pageSize, 5)
ctx.Data["Page"] = pager
ctx.Data["Tasks"] = modelResult

}

func ModelConvertDownloadModel(ctx *context.Context) {
log.Info("enter here......")
id := ctx.Params(":id")
job, err := models.QueryModelConvertById(id)
if err != nil {
ctx.ServerError("Not found task.", err)
return
}
AllDownload := ctx.QueryBool("AllDownload")
if AllDownload {
if job.IsGpuTrainTask() {
path := setting.CBCodePathPrefix + job.ID + "/model/"
allFile, err := storage.GetAllObjectByBucketAndPrefixMinio(setting.Attachment.Minio.Bucket, path)
if err == nil {
returnFileName := job.Name + ".zip"
MinioDownloadManyFile(path, ctx, returnFileName, allFile)
} else {
log.Info("error,msg=" + err.Error())
ctx.ServerError("no file to download.", err)
}
} else {
Prefix := path.Join(setting.TrainJobModelPath, job.ID, "output/", "V0001", "") + "/"
log.Info("bucket=" + setting.Bucket + "prefix=" + Prefix)
allFile, err := storage.GetAllObjectByBucketAndPrefix(setting.Bucket, Prefix)
if err == nil {
returnFileName := job.Name + ".zip"
ObsDownloadManyFile(Prefix, ctx, returnFileName, allFile)
} else {
log.Info("error,msg=" + err.Error())
ctx.ServerError("no file to download.", err)
}
}
} else {
parentDir := ctx.Query("parentDir")
fileName := ctx.Query("fileName")
jobName := ctx.Query("jobName")
if job.IsGpuTrainTask() {
filePath := "jobs/" + jobName + "/model/" + parentDir
url, err := storage.Attachments.PresignedGetURL(filePath, fileName)
if err != nil {
log.Error("PresignedGetURL failed: %v", err.Error(), ctx.Data["msgID"])
ctx.ServerError("PresignedGetURL", err)
return
}
//ctx.JSON(200, url)
http.Redirect(ctx.Resp, ctx.Req.Request, url, http.StatusTemporaryRedirect)
} else {
ObjectKey := path.Join(setting.TrainJobModelPath, job.ID, "output/", "V0001", parentDir, fileName)
log.Info("ObjectKey=" + ObjectKey)
url, err := storage.GetObsCreateSignedUrlByBucketAndKey(setting.Bucket, ObjectKey)
if err != nil {
log.Error("GetObsCreateSignedUrl failed: %v", err.Error(), ctx.Data["msgID"])
ctx.ServerError("GetObsCreateSignedUrl", err)
return
}
http.Redirect(ctx.Resp, ctx.Req.Request, url, http.StatusTemporaryRedirect)
}
}

}

+ 81
- 14
routers/repo/ai_model_manage.go View File

@@ -26,6 +26,7 @@ const (
tplModelInfo = "repo/modelmanage/showinfo"
MODEL_LATEST = 1
MODEL_NOT_LATEST = 0
MODEL_MAX_SIZE = 1024 * 1024 * 1024
)

func saveModelByParameters(jobId string, versionName string, name string, version string, label string, description string, engine int, ctx *context.Context) error {
@@ -58,14 +59,16 @@ func saveModelByParameters(jobId string, versionName string, name string, versio
}
}
cloudType := aiTask.Type
modelSelectedFile := ctx.Query("modelSelectedFile")
//download model zip //train type
if cloudType == models.TypeCloudBrainTwo {
modelPath, modelSize, err = downloadModelFromCloudBrainTwo(id, aiTask.JobName, "", aiTask.TrainUrl)
if aiTask.ComputeResource == models.NPUResource {
modelPath, modelSize, err = downloadModelFromCloudBrainTwo(id, aiTask.JobName, "", aiTask.TrainUrl, modelSelectedFile)
if err != nil {
log.Info("download model from CloudBrainTwo faild." + err.Error())
return err
}
} else if cloudType == models.TypeCloudBrainOne {
cloudType = models.TypeCloudBrainTwo
} else if aiTask.ComputeResource == models.GPUResource {
var ResourceSpecs *models.ResourceSpecs
json.Unmarshal([]byte(setting.ResourceSpecs), &ResourceSpecs)
for _, tmp := range ResourceSpecs.ResourceSpec {
@@ -74,12 +77,24 @@ func saveModelByParameters(jobId string, versionName string, name string, versio
aiTask.FlavorName = flaverName
}
}
modelPath, modelSize, err = downloadModelFromCloudBrainOne(id, aiTask.JobName, "", aiTask.TrainUrl)
modelPath, modelSize, err = downloadModelFromCloudBrainOne(id, aiTask.JobName, "", aiTask.TrainUrl, modelSelectedFile)
if err != nil {
log.Info("download model from CloudBrainOne faild." + err.Error())
return err
}
}
cloudType = models.TypeCloudBrainOne
}
// else if cloudType == models.TypeC2Net {
// if aiTask.ComputeResource == models.NPUResource {
// modelPath, modelSize, err = downloadModelFromCloudBrainTwo(id, aiTask.JobName, "", aiTask.TrainUrl, modelSelectedFile)
// if err != nil {
// log.Info("download model from CloudBrainTwo faild." + err.Error())
// return err
// }
// } else if aiTask.ComputeResource == models.GPUResource {

// }
// }
accuracy := make(map[string]string)
accuracy["F1"] = ""
accuracy["Recall"] = ""
@@ -163,7 +178,8 @@ func SaveModel(ctx *context.Context) {
description := ctx.Query("Description")
engine := ctx.QueryInt("Engine")
trainTaskCreate := ctx.QueryBool("trainTaskCreate")
log.Info("engine=" + fmt.Sprint(engine))
modelSelectedFile := ctx.Query("modelSelectedFile")
log.Info("engine=" + fmt.Sprint(engine) + " modelSelectedFile=" + modelSelectedFile)
if !trainTaskCreate {
if !ctx.Repo.CanWrite(models.UnitTypeModelManage) {
//ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
@@ -176,6 +192,10 @@ func SaveModel(ctx *context.Context) {
ctx.Error(500, fmt.Sprintf("JobId or VersionName is null."))
return
}
if modelSelectedFile == "" {
ctx.Error(500, fmt.Sprintf("Not selected model file."))
return
}

if name == "" || version == "" {
ctx.Error(500, fmt.Sprintf("name or version is null."))
@@ -193,11 +213,22 @@ func SaveModel(ctx *context.Context) {
log.Info("save model end.")
}

func downloadModelFromCloudBrainTwo(modelUUID string, jobName string, parentDir string, trainUrl string) (string, int64, error) {
func downloadModelFromCloudBrainTwo(modelUUID string, jobName string, parentDir string, trainUrl string, modelSelectedFile string) (string, int64, error) {
objectkey := strings.TrimPrefix(path.Join(setting.TrainJobModelPath, jobName, setting.OutPutPath, parentDir), "/")
if trainUrl != "" {
objectkey = strings.Trim(trainUrl[len(setting.Bucket)+1:], "/")
}
prefix := objectkey + "/"

filterFiles := strings.Split(modelSelectedFile, ";")
Files := make([]string, 0)
for _, shortFile := range filterFiles {
Files = append(Files, prefix+shortFile)
}
totalSize := storage.ObsGetFilesSize(setting.Bucket, Files)
if float64(totalSize) > setting.MaxModelSize*MODEL_MAX_SIZE {
return "", 0, errors.New("Cannot create model, as model is exceed " + fmt.Sprint(setting.MaxModelSize) + "G.")
}

modelDbResult, err := storage.GetOneLevelAllObjectUnderDir(setting.Bucket, objectkey, "")
log.Info("bucket=" + setting.Bucket + " objectkey=" + objectkey)
@@ -206,26 +237,33 @@ func downloadModelFromCloudBrainTwo(modelUUID string, jobName string, parentDir
return "", 0, err
}
if len(modelDbResult) == 0 {
return "", 0, errors.New("cannot create model, as model is empty.")
return "", 0, errors.New("Cannot create model, as model is empty.")
}

prefix := objectkey + "/"
destKeyNamePrefix := Model_prefix + models.AttachmentRelativePath(modelUUID) + "/"

size, err := storage.ObsCopyManyFile(setting.Bucket, prefix, setting.Bucket, destKeyNamePrefix)
size, err := storage.ObsCopyManyFile(setting.Bucket, prefix, setting.Bucket, destKeyNamePrefix, filterFiles)

dataActualPath := setting.Bucket + "/" + destKeyNamePrefix
return dataActualPath, size, nil
}

func downloadModelFromCloudBrainOne(modelUUID string, jobName string, parentDir string, trainUrl string) (string, int64, error) {
func downloadModelFromCloudBrainOne(modelUUID string, jobName string, parentDir string, trainUrl string, modelSelectedFile string) (string, int64, error) {
modelActualPath := storage.GetMinioPath(jobName, "/model/")
log.Info("modelActualPath=" + modelActualPath)
modelSrcPrefix := setting.CBCodePathPrefix + jobName + "/model/"
destKeyNamePrefix := Model_prefix + models.AttachmentRelativePath(modelUUID) + "/"
bucketName := setting.Attachment.Minio.Bucket
log.Info("destKeyNamePrefix=" + destKeyNamePrefix + " modelSrcPrefix=" + modelSrcPrefix + " bucket=" + bucketName)
size, err := storage.MinioPathCopy(bucketName, modelSrcPrefix, destKeyNamePrefix)
filterFiles := strings.Split(modelSelectedFile, ";")
Files := make([]string, 0)
for _, shortFile := range filterFiles {
Files = append(Files, modelSrcPrefix+shortFile)
}
totalSize := storage.MinioGetFilesSize(bucketName, Files)
if float64(totalSize) > setting.MaxModelSize*MODEL_MAX_SIZE {
return "", 0, errors.New("Cannot create model, as model is exceed " + fmt.Sprint(setting.MaxModelSize) + "G.")
}
size, err := storage.MinioCopyFiles(bucketName, modelSrcPrefix, destKeyNamePrefix, filterFiles)
if err == nil {
dataActualPath := bucketName + "/" + destKeyNamePrefix
return dataActualPath, size, nil
@@ -459,6 +497,35 @@ func QueryTrainJobList(ctx *context.Context) {

}

func QueryTrainModelList(ctx *context.Context) {
log.Info("query train job list. start.")
jobName := ctx.Query("jobName")
taskType := ctx.QueryInt("type")
VersionName := ctx.Query("VersionName")
if taskType == models.TypeCloudBrainTwo {
objectkey := path.Join(setting.TrainJobModelPath, jobName, setting.OutPutPath, VersionName) + "/"
modelDbResult, err := storage.GetAllObjectByBucketAndPrefix(setting.Bucket, objectkey)
log.Info("bucket=" + setting.Bucket + " objectkey=" + objectkey)
if err != nil {
log.Info("get TypeCloudBrainTwo TrainJobListModel failed:", err)
} else {
ctx.JSON(200, modelDbResult)
return
}
} else if taskType == models.TypeCloudBrainOne {
modelSrcPrefix := setting.CBCodePathPrefix + jobName + "/model/"
bucketName := setting.Attachment.Minio.Bucket
modelDbResult, err := storage.GetAllObjectByBucketAndPrefixMinio(bucketName, modelSrcPrefix)
if err != nil {
log.Info("get TypeCloudBrainOne TrainJobListModel failed:", err)
} else {
ctx.JSON(200, modelDbResult)
return
}
}
ctx.JSON(200, "")
}

func DownloadSingleModelFile(ctx *context.Context) {
log.Info("DownloadSingleModelFile start.")
id := ctx.Params(":ID")
@@ -766,7 +833,7 @@ func QueryModelListForPredict(ctx *context.Context) {
PageSize: -1,
},
RepoID: repoId,
Type: -1,
Type: ctx.QueryInt("type"),
New: -1,
})
if err != nil {


+ 410
- 54
routers/repo/cloudbrain.go View File

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

import (
"bufio"
"code.gitea.io/gitea/modules/grampus"
"encoding/json"
"errors"
"fmt"
@@ -16,6 +15,8 @@ import (
"time"
"unicode/utf8"

"code.gitea.io/gitea/modules/grampus"

"code.gitea.io/gitea/modules/timeutil"
"github.com/unknwon/i18n"

@@ -46,6 +47,9 @@ const (

tplCloudBrainTrainJobNew base.TplName = "repo/cloudbrain/trainjob/new"
tplCloudBrainTrainJobShow base.TplName = "repo/cloudbrain/trainjob/show"

tplCloudBrainInferenceJobNew base.TplName = "repo/cloudbrain/inference/new"
tplCloudBrainInferenceJobShow base.TplName = "repo/cloudbrain/inference/show"
)

var (
@@ -55,6 +59,7 @@ var (
benchmarkGpuInfos *models.GpuInfos
benchmarkResourceSpecs *models.ResourceSpecs
trainGpuInfos *models.GpuInfos
inferenceGpuInfos *models.GpuInfos
)

const BENCHMARK_TYPE_CODE = "repo.cloudbrain.benchmark.types"
@@ -96,46 +101,7 @@ func cloudBrainNewDataPrepare(ctx *context.Context) error {
var displayJobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:]
ctx.Data["display_job_name"] = displayJobName

result, err := cloudbrain.GetImages()
if err != nil {
ctx.Data["error"] = err.Error()
log.Error("cloudbrain.GetImages failed:", err.Error(), ctx.Data["MsgID"])
}

for i, payload := range result.Payload.ImageInfo {
if strings.HasPrefix(result.Payload.ImageInfo[i].Place, "192.168") {
result.Payload.ImageInfo[i].PlaceView = payload.Place[strings.Index(payload.Place, "/"):len(payload.Place)]
} else {
result.Payload.ImageInfo[i].PlaceView = payload.Place
}
}

ctx.Data["images"] = result.Payload.ImageInfo

resultPublic, err := cloudbrain.GetPublicImages()
if err != nil {
ctx.Data["error"] = err.Error()
log.Error("cloudbrain.GetPublicImages failed:", err.Error(), ctx.Data["MsgID"])
}

for i, payload := range resultPublic.Payload.ImageInfo {
if strings.HasPrefix(resultPublic.Payload.ImageInfo[i].Place, "192.168") {
resultPublic.Payload.ImageInfo[i].PlaceView = payload.Place[strings.Index(payload.Place, "/"):len(payload.Place)]
} else {
resultPublic.Payload.ImageInfo[i].PlaceView = payload.Place
}
}

ctx.Data["public_images"] = resultPublic.Payload.ImageInfo

attachs, err := models.GetAllUserAttachments(ctx.User.ID)
if err != nil {
log.Error("GetAllUserAttachments failed: %v", err, ctx.Data["MsgID"])
return err
}

ctx.Data["attachments"] = attachs
ctx.Data["command"] = cloudbrain.Command
ctx.Data["command"] = cloudbrain.GetCloudbrainDebugCommand()
ctx.Data["code_path"] = cloudbrain.CodeMountPath
ctx.Data["dataset_path"] = cloudbrain.DataSetMountPath
ctx.Data["model_path"] = cloudbrain.ModelMountPath
@@ -148,6 +114,12 @@ func cloudBrainNewDataPrepare(ctx *context.Context) error {
ctx.Data["benchmark_categories"] = categories.Category

ctx.Data["benchmark_types"] = GetBenchmarkTypes(ctx).BenchmarkType
queuesDetail, _ := cloudbrain.GetQueuesDetail()
if queuesDetail != nil {
ctx.Data["QueuesDetail"] = queuesDetail
}

cloudbrain.InitSpecialPool()

if gpuInfos == nil {
json.Unmarshal([]byte(setting.GpuTypes), &gpuInfos)
@@ -159,6 +131,13 @@ func cloudBrainNewDataPrepare(ctx *context.Context) error {
}
ctx.Data["train_gpu_types"] = trainGpuInfos.GpuInfo

if inferenceGpuInfos == nil && setting.InferenceGpuTypes != "" {
json.Unmarshal([]byte(setting.InferenceGpuTypes), &inferenceGpuInfos)
}
if inferenceGpuInfos != nil {
ctx.Data["inference_gpu_types"] = inferenceGpuInfos.GpuInfo
}

if benchmarkGpuInfos == nil {
json.Unmarshal([]byte(setting.BenchmarkGpuTypes), &benchmarkGpuInfos)
}
@@ -178,6 +157,52 @@ func cloudBrainNewDataPrepare(ctx *context.Context) error {
json.Unmarshal([]byte(setting.TrainResourceSpecs), &cloudbrain.TrainResourceSpecs)
}
ctx.Data["train_resource_specs"] = cloudbrain.TrainResourceSpecs.ResourceSpec

if cloudbrain.InferenceResourceSpecs == nil && setting.InferenceResourceSpecs != "" {
json.Unmarshal([]byte(setting.InferenceResourceSpecs), &cloudbrain.InferenceResourceSpecs)
}
if cloudbrain.InferenceResourceSpecs != nil {
ctx.Data["inference_resource_specs"] = cloudbrain.InferenceResourceSpecs.ResourceSpec
}

if cloudbrain.SpecialPools != nil {
var debugGpuTypes []*models.GpuInfo
var trainGpuTypes []*models.GpuInfo

for _, pool := range cloudbrain.SpecialPools.Pools {
org, _ := models.GetOrgByName(pool.Org)
if org != nil {
isOrgMember, _ := models.IsOrganizationMember(org.ID, ctx.User.ID)
if isOrgMember {
for _, jobType := range pool.JobType {
if jobType == string(models.JobTypeDebug) {
debugGpuTypes = append(debugGpuTypes, pool.Pool...)
if pool.ResourceSpec != nil {
ctx.Data["resource_specs"] = pool.ResourceSpec
}
} else if jobType == string(models.JobTypeTrain) {
trainGpuTypes = append(trainGpuTypes, pool.Pool...)
if pool.ResourceSpec != nil {
ctx.Data["train_resource_specs"] = pool.ResourceSpec
}
}
}
break
}
}

}

if len(debugGpuTypes) > 0 {
ctx.Data["gpu_types"] = debugGpuTypes
}

if len(trainGpuTypes) > 0 {
ctx.Data["train_gpu_types"] = trainGpuTypes
}

}

ctx.Data["params"] = ""
ctx.Data["branchName"] = ctx.Repo.BranchName

@@ -217,6 +242,10 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) {
repo := ctx.Repo.Repository
tpl := tplCloudBrainNew

if jobType == string(models.JobTypeTrain) {
tpl = tplCloudBrainTrainJobNew
}

tasks, err := models.GetCloudbrainsByDisplayJobName(repo.ID, jobType, displayJobName)
if err == nil {
if len(tasks) != 0 {
@@ -235,6 +264,7 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) {
}

if !jobNamePattern.MatchString(displayJobName) {
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr(ctx.Tr("repo.cloudbrain_jobname_err"), tpl, &form)
return
}
@@ -269,7 +299,7 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) {
return
}

command := cloudbrain.Command
command := cloudbrain.GetCloudbrainDebugCommand()
if jobType == string(models.JobTypeTrain) {
tpl = tplCloudBrainTrainJobNew
commandTrain, err := getTrainJobCommand(form)
@@ -282,6 +312,14 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) {
command = commandTrain
}

errStr := checkCloudBrainSpecialPool(ctx, jobType, gpuQueue, resourceSpecId)

if errStr != "" {
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr(errStr, tpl, &form)
return
}

if branchName == "" {
branchName = cloudbrain.DefaultBranchName
}
@@ -318,6 +356,7 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) {
BenchmarkTypeID: 0,
BenchmarkChildTypeID: 0,
ResourceSpecId: resourceSpecId,
ResultPath: storage.GetMinioPath(jobName, cloudbrain.ResultPath+"/"),
}

err = cloudbrain.GenerateTask(req)
@@ -334,6 +373,163 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) {
}
}


func CloudBrainInferenceJobCreate(ctx *context.Context, form auth.CreateCloudBrainInferencForm) {
ctx.Data["PageIsCloudBrain"] = true
displayJobName := form.DisplayJobName
jobName := util.ConvertDisplayJobNameToJobName(displayJobName)
image := strings.TrimSpace(form.Image)
uuid := form.Attachment
jobType := string(models.JobTypeInference)
gpuQueue := form.GpuType
codePath := setting.JobPath + jobName + cloudbrain.CodeMountPath
resourceSpecId := form.ResourceSpecId
branchName := form.BranchName
labelName := form.LabelName
repo := ctx.Repo.Repository

ckptUrl := setting.Attachment.Minio.RealPath + form.TrainUrl + form.CkptName
log.Info("ckpt url:" + ckptUrl)
tpl := tplCloudBrainInferenceJobNew
command, err := getInferenceJobCommand(form)
if err != nil {
log.Error("getTrainJobCommand failed: %v", err)
ctx.RenderWithErr(err.Error(), tpl, &form)
return
}

tasks, err := models.GetCloudbrainsByDisplayJobName(repo.ID, jobType, displayJobName)
if err == nil {
if len(tasks) != 0 {
log.Error("the job name did already exist", ctx.Data["MsgID"])
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr("the job name did already exist", tpl, &form)
return
}
} else {
if !models.IsErrJobNotExist(err) {
log.Error("system error, %v", err, ctx.Data["MsgID"])
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr("system error", tpl, &form)
return
}
}

if !jobNamePattern.MatchString(displayJobName) {
ctx.RenderWithErr(ctx.Tr("repo.cloudbrain_jobname_err"), tpl, &form)
return
}

count, err := models.GetCloudbrainCountByUserID(ctx.User.ID, jobType)
if err != nil {
log.Error("GetCloudbrainCountByUserID failed:%v", err, ctx.Data["MsgID"])
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr("system error", tpl, &form)
return
} else {
if count >= 1 {
log.Error("the user already has running or waiting task", ctx.Data["MsgID"])
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr(ctx.Tr("repo.cloudbrain.morethanonejob"), tpl, &form)
return
}
}

if branchName == "" {
branchName = cloudbrain.DefaultBranchName
}
downloadCode(repo, codePath, branchName)
uploadCodeToMinio(codePath+"/", jobName, cloudbrain.CodeMountPath+"/")
resultPath := setting.JobPath + jobName + cloudbrain.ResultPath + "/"
mkResultPath(resultPath)
uploadCodeToMinio(resultPath, jobName, cloudbrain.ResultPath+"/")

commitID, _ := ctx.Repo.GitRepo.GetBranchCommitID(branchName)

datasetInfos, datasetNames, err := models.GetDatasetInfo(uuid)
if err != nil {
log.Error("GetDatasetInfo failed: %v", err, ctx.Data["MsgID"])
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr(ctx.Tr("cloudbrain.error.dataset_select"), tpl, &form)
return
}

req := cloudbrain.GenerateCloudBrainTaskReq{
Ctx: ctx,
DisplayJobName: displayJobName,
JobName: jobName,
Image: image,
Command: command,
Uuids: uuid,
DatasetNames: datasetNames,
DatasetInfos: datasetInfos,
CodePath: storage.GetMinioPath(jobName, cloudbrain.CodeMountPath+"/"),
ModelPath: setting.Attachment.Minio.RealPath + form.TrainUrl,
BenchmarkPath: storage.GetMinioPath(jobName, cloudbrain.BenchMarkMountPath+"/"),
Snn4ImageNetPath: storage.GetMinioPath(jobName, cloudbrain.Snn4imagenetMountPath+"/"),
BrainScorePath: storage.GetMinioPath(jobName, cloudbrain.BrainScoreMountPath+"/"),
JobType: jobType,
GpuQueue: gpuQueue,
Description: form.Description,
BranchName: branchName,
BootFile: form.BootFile,
Params: form.Params,
CommitID: commitID,
ResourceSpecId: resourceSpecId,
ResultPath: storage.GetMinioPath(jobName, cloudbrain.ResultPath+"/"),
ModelName: form.ModelName,
ModelVersion: form.ModelVersion,
CkptName: form.CkptName,
TrainUrl: form.TrainUrl,
LabelName: labelName,
}

err = cloudbrain.GenerateTask(req)
if err != nil {
cloudBrainNewDataPrepare(ctx)
ctx.RenderWithErr(err.Error(), tpl, &form)
return
}

ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/inference-job")

}
/**
检查用户传输的参数是否符合专属资源池
*/
func checkCloudBrainSpecialPool(ctx *context.Context, jobType string, queue string, resourceSpecId int) string {
if cloudbrain.SpecialPools != nil {

var isInPoolOrg = false
var matchSpecialPool = false

for _, specialPool := range cloudbrain.SpecialPools.Pools {

if cloudbrain.IsElementExist(specialPool.JobType, jobType) && cloudbrain.IsQueueInSpecialtPool(specialPool.Pool, queue) {
if cloudbrain.IsResourceSpecInSpecialPool(specialPool.ResourceSpec, resourceSpecId) {
matchSpecialPool = true
org, _ := models.GetOrgByName(specialPool.Org)
if org != nil {
isInPoolOrg, _ = models.IsOrganizationMember(org.ID, ctx.User.ID)
if isInPoolOrg {
break //传入参数,和专属资源池匹配上了,检查通过
}
}
}

}

}
//资源池有匹配上,但是用户不在相应的组织中,返回错误信息。界面已经过滤了选择,界面操作不会到这个逻辑
if matchSpecialPool && !isInPoolOrg {
return ctx.Tr("repo.grampus.no_operate_right")
}

}
//没有匹配到资源池或者没有设置专属资源池,检查通过; 获取和资源池完全匹配检查通过
return ""
}

func CloudBrainRestart(ctx *context.Context) {
var ID = ctx.Params(":id")
var resultCode = "0"
@@ -369,7 +565,18 @@ func CloudBrainRestart(ctx *context.Context) {
for _, resourceType := range gpuInfos.GpuInfo {
if resourceType.Queue == task.GpuQueue {
hasSameResource = true
continue
break
}
}
if !hasSameResource && cloudbrain.SpecialPools != nil {

for _, specialPool := range cloudbrain.SpecialPools.Pools {
cloudbrain.IsElementExist(specialPool.JobType, string(models.JobTypeDebug))
for _, pool := range specialPool.Pool {
if pool.Queue == task.GpuQueue {
hasSameResource = true
}
}
}
}

@@ -432,7 +639,7 @@ func cloudBrainShow(ctx *context.Context, tpName base.TplName, jobType models.Jo

var task *models.Cloudbrain
var err error
if jobType == models.JobTypeTrain {
if jobType == models.JobTypeTrain || jobType == models.JobTypeInference {
task, err = models.GetCloudbrainByJobID(ctx.Params(":jobid"))
} else {
task, err = models.GetCloudbrainByIDWithDeleted(ctx.Params(":id"))
@@ -455,8 +662,41 @@ func cloudBrainShow(ctx *context.Context, tpName base.TplName, jobType models.Jo
if cloudbrain.TrainResourceSpecs == nil {
json.Unmarshal([]byte(setting.TrainResourceSpecs), &cloudbrain.TrainResourceSpecs)
}
hasSpec := false
for _, tmp := range cloudbrain.TrainResourceSpecs.ResourceSpec {
if tmp.Id == task.ResourceSpecId {
hasSpec = true
ctx.Data["GpuNum"] = tmp.GpuNum
ctx.Data["CpuNum"] = tmp.CpuNum
ctx.Data["MemMiB"] = tmp.MemMiB
ctx.Data["ShareMemMiB"] = tmp.ShareMemMiB
}
}

if !hasSpec && cloudbrain.SpecialPools != nil {
for _, specialPool := range cloudbrain.SpecialPools.Pools {

if specialPool.ResourceSpec != nil {

for _, spec := range specialPool.ResourceSpec {
if task.ResourceSpecId == spec.Id {
ctx.Data["GpuNum"] = spec.GpuNum
ctx.Data["CpuNum"] = spec.CpuNum
ctx.Data["MemMiB"] = spec.MemMiB
ctx.Data["ShareMemMiB"] = spec.ShareMemMiB
break
}
}
}
}
}

} else if task.JobType == string(models.JobTypeInference) {
if cloudbrain.InferenceResourceSpecs == nil {
json.Unmarshal([]byte(setting.InferenceResourceSpecs), &cloudbrain.InferenceResourceSpecs)
}
for _, tmp := range cloudbrain.InferenceResourceSpecs.ResourceSpec {
if tmp.Id == task.ResourceSpecId {
ctx.Data["GpuNum"] = tmp.GpuNum
ctx.Data["CpuNum"] = tmp.CpuNum
ctx.Data["MemMiB"] = tmp.MemMiB
@@ -491,6 +731,24 @@ func cloudBrainShow(ctx *context.Context, tpName base.TplName, jobType models.Jo
ctx.Data["resource_type"] = resourceType.Value
}
}
for _, specialPool := range cloudbrain.SpecialPools.Pools {

for _, resourceType := range specialPool.Pool {
if resourceType.Queue == jobRes.Config.GpuType {
ctx.Data["resource_type"] = resourceType.Value
}
}
}

} else if task.JobType == string(models.JobTypeInference) {
if inferenceGpuInfos == nil {
json.Unmarshal([]byte(setting.InferenceGpuTypes), &inferenceGpuInfos)
}
for _, resourceType := range inferenceGpuInfos.GpuInfo {
if resourceType.Queue == jobRes.Config.GpuType {
ctx.Data["resource_type"] = resourceType.Value
}
}
} else if cloudbrain.IsBenchmarkJob(task.JobType) {
if benchmarkGpuInfos == nil {
json.Unmarshal([]byte(setting.BenchmarkGpuTypes), &benchmarkGpuInfos)
@@ -573,7 +831,9 @@ func cloudBrainShow(ctx *context.Context, tpName base.TplName, jobType models.Jo
if task.TrainJobDuration == "" {
if task.Duration == 0 {
var duration int64
if task.Status == string(models.JobRunning) {
if task.Status == string(models.JobWaiting) {
duration = 0
} else if task.Status == string(models.JobRunning) {
duration = time.Now().Unix() - int64(task.CreatedUnix)
} else {
duration = int64(task.UpdatedUnix) - int64(task.CreatedUnix)
@@ -607,6 +867,8 @@ func cloudBrainShow(ctx *context.Context, tpName base.TplName, jobType models.Jo
}

ctx.Data["task"] = task
labelName := strings.Fields(task.LabelName)
ctx.Data["LabelName"] = labelName
ctx.Data["jobName"] = task.JobName
ctx.Data["displayJobName"] = task.DisplayJobName
version_list_task := make([]*models.Cloudbrain, 0)
@@ -846,7 +1108,7 @@ func CloudBrainStop(ctx *context.Context) {
if task.Status == string(models.JobStopped) || task.Status == string(models.JobFailed) || task.Status == string(models.JobSucceeded) {
log.Error("the job(%s) has been stopped", task.JobName, ctx.Data["msgID"])
resultCode = "-1"
errorMsg = "system error"
errorMsg = "cloudbrain.Already_stopped"
break
}

@@ -854,7 +1116,7 @@ func CloudBrainStop(ctx *context.Context) {
if err != nil {
log.Error("StopJob(%s) failed:%v", task.JobName, err, ctx.Data["msgID"])
resultCode = "-1"
errorMsg = "system error"
errorMsg = "cloudbrain.Stopped_failed"
break
}

@@ -867,7 +1129,7 @@ func CloudBrainStop(ctx *context.Context) {
if err != nil {
log.Error("UpdateJob(%s) failed:%v", task.JobName, err, ctx.Data["msgID"])
resultCode = "-1"
errorMsg = "system error"
errorMsg = "cloudbrain.Stopped_success_update_status_fail"
break
}

@@ -877,7 +1139,7 @@ func CloudBrainStop(ctx *context.Context) {

ctx.JSON(200, map[string]interface{}{
"result_code": resultCode,
"error_msg": errorMsg,
"error_msg": ctx.Tr(errorMsg),
"status": status,
"id": ID,
"StatusOK": 0,
@@ -1167,6 +1429,18 @@ func GetModelDirs(jobName string, parentDir string) (string, error) {
return getDirs(req)
}

func GetResultDirs(jobName string, parentDir string) (string, error) {
var req string
modelActualPath := storage.GetMinioPath(jobName, cloudbrain.ResultPath+"/")
if parentDir == "" {
req = "baseDir=" + modelActualPath
} else {
req = "baseDir=" + modelActualPath + "&parentDir=" + parentDir
}

return getDirs(req)
}

func CloudBrainDownloadModel(ctx *context.Context) {
parentDir := ctx.Query("parentDir")
fileName := ctx.Query("fileName")
@@ -1181,6 +1455,20 @@ func CloudBrainDownloadModel(ctx *context.Context) {
ctx.Resp.Header().Set("Cache-Control", "max-age=0")
http.Redirect(ctx.Resp, ctx.Req.Request, url, http.StatusMovedPermanently)
}
func CloudBrainDownloadInferenceResult(ctx *context.Context) {
parentDir := ctx.Query("parentDir")
fileName := ctx.Query("fileName")
jobName := ctx.Query("jobName")
filePath := "jobs/" + jobName + "/result/" + parentDir
url, err := storage.Attachments.PresignedGetURL(filePath, fileName)
if err != nil {
log.Error("PresignedGetURL failed: %v", err.Error(), ctx.Data["msgID"])
ctx.ServerError("PresignedGetURL", err)
return
}
ctx.Resp.Header().Set("Cache-Control", "max-age=0")
http.Redirect(ctx.Resp, ctx.Req.Request, url, http.StatusMovedPermanently)
}

func GetRate(ctx *context.Context) {
isObjectDetcionAll := ctx.QueryBool("isObjectDetcionAll")
@@ -1334,13 +1622,21 @@ func uploadCodeToMinio(codePath, jobName, parentDir string) error {
}

func mkModelPath(modelPath string) error {
err := os.MkdirAll(modelPath, os.ModePerm)
return mkPathAndReadMeFile(modelPath, "You can put the model file into this directory and download it by the web page.")
}

func mkResultPath(resultPath string) error {
return mkPathAndReadMeFile(resultPath, "You can put the result file into this directory and download it by the web page.")
}

func mkPathAndReadMeFile(path string, text string) error {
err := os.MkdirAll(path, os.ModePerm)
if err != nil {
log.Error("MkdirAll(%s) failed:%v", modelPath, err)
log.Error("MkdirAll(%s) failed:%v", path, err)
return err
}

fileName := modelPath + "README"
fileName := path + "README"
f, err := os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm)
if err != nil {
log.Error("OpenFile failed", err.Error())
@@ -1349,7 +1645,7 @@ func mkModelPath(modelPath string) error {

defer f.Close()

_, err = f.WriteString("You can put the model file into this directory and download it by the web page.")
_, err = f.WriteString(text)
if err != nil {
log.Error("WriteString failed", err.Error())
return err
@@ -2068,6 +2364,7 @@ func BenchMarkAlgorithmCreate(ctx *context.Context, form auth.CreateCloudBrainFo
BenchmarkTypeID: benchmarkTypeID,
BenchmarkChildTypeID: benchmarkChildTypeID,
ResourceSpecId: resourceSpecId,
ResultPath: storage.GetMinioPath(jobName, cloudbrain.ResultPath+"/"),
}

err = cloudbrain.GenerateTask(req)
@@ -2094,7 +2391,7 @@ func ModelBenchmarkCreate(ctx *context.Context, form auth.CreateCloudBrainForm)
repo := ctx.Repo.Repository

tpl := tplCloudBrainBenchmarkNew
command := cloudbrain.Command
command := cloudbrain.GetCloudbrainDebugCommand()

tasks, err := models.GetCloudbrainsByDisplayJobName(repo.ID, jobType, displayJobName)
if err == nil {
@@ -2196,6 +2493,7 @@ func ModelBenchmarkCreate(ctx *context.Context, form auth.CreateCloudBrainForm)
BenchmarkTypeID: 0,
BenchmarkChildTypeID: benchmarkChildTypeID,
ResourceSpecId: resourceSpecId,
ResultPath: storage.GetMinioPath(jobName, cloudbrain.ResultPath+"/"),
}

err = cloudbrain.GenerateTask(req)
@@ -2246,6 +2544,64 @@ func CloudBrainTrainJobNew(ctx *context.Context) {
ctx.HTML(http.StatusOK, tplCloudBrainTrainJobNew)
}

func InferenceCloudBrainJobNew(ctx *context.Context) {
err := cloudBrainNewDataPrepare(ctx)
if err != nil {
ctx.ServerError("get new train-job info failed", err)
return
}
ctx.HTML(http.StatusOK, tplCloudBrainInferenceJobNew)
}

func InferenceCloudBrainJobShow(ctx *context.Context) {
cloudBrainShow(ctx, tplCloudBrainInferenceJobShow, models.JobTypeInference)
}

func DownloadInferenceResultFile(ctx *context.Context) {
var jobID = ctx.Params(":jobid")
var versionName = ctx.Query("version_name")
task, err := models.GetCloudbrainByJobIDAndVersionName(jobID, versionName)
if err != nil {
log.Error("GetCloudbrainByJobID(%s) failed:%v", task.JobName, err.Error())
return
}

allFile, err := storage.GetAllObjectByBucketAndPrefixMinio(setting.Attachment.Minio.Bucket, task.ResultUrl)
returnFileName := task.DisplayJobName + ".zip"
MinioDownloadManyFile(task.ResultUrl, ctx, returnFileName, allFile)
}

func getInferenceJobCommand(form auth.CreateCloudBrainInferencForm) (string, error) {
var command string
bootFile := strings.TrimSpace(form.BootFile)
params := form.Params

if !strings.HasSuffix(bootFile, ".py") {
log.Error("bootFile(%s) format error", bootFile)
return command, errors.New("bootFile format error")
}

var parameters models.Parameters
var param string
if len(params) != 0 {
err := json.Unmarshal([]byte(params), &parameters)
if err != nil {
log.Error("Failed to Unmarshal params: %s (%v)", params, err)
return command, err
}

for _, parameter := range parameters.Parameter {
param += " --" + parameter.Label + "=" + parameter.Value
}
}

param += " --modelname" + "=" + form.CkptName

command += "python /code/" + bootFile + param + " > " + cloudbrain.ResultPath + "/" + form.DisplayJobName + "-" + cloudbrain.LogFile

return command, nil
}

func getTrainJobCommand(form auth.CreateCloudBrainForm) (string, error) {
var command string
bootFile := strings.TrimSpace(form.BootFile)


+ 68
- 7
routers/repo/grampus.go View File

@@ -43,6 +43,8 @@ func GrampusTrainJobGPUNew(ctx *context.Context) {
ctx.ServerError("get new train-job info failed", err)
return
}
waitCount := cloudbrain.GetWaitingCloudbrainCount(models.TypeC2Net, models.GPUResource, models.JobTypeTrain)
ctx.Data["WaitCount"] = waitCount
ctx.HTML(http.StatusOK, tplGrampusTrainJobGPUNew)
}

@@ -53,6 +55,8 @@ func GrampusTrainJobNPUNew(ctx *context.Context) {
ctx.ServerError("get new train-job info failed", err)
return
}
waitCount := cloudbrain.GetWaitingCloudbrainCount(models.TypeC2Net, models.NPUResource, models.JobTypeTrain)
ctx.Data["WaitCount"] = waitCount
ctx.HTML(200, tplGrampusTrainJobNPUNew)
}

@@ -75,27 +79,42 @@ func grampusTrainJobNewDataPrepare(ctx *context.Context, processType string) err

ctx.Data["GPUEnabled"] = true
ctx.Data["NPUEnabled"] = true

includeCenters := make(map[string]struct{})
excludeCenters := make(map[string]struct{})
if grampus.SpecialPools != nil {
for _, pool := range grampus.SpecialPools.Pools {
if pool.IsExclusive {
org, _ := models.GetOrgByName(pool.Org)
if org != nil {
isOrgMember, _ := models.IsOrganizationMember(org.ID, ctx.User.ID)
if !isOrgMember {
ctx.Data[pool.Type+"Enabled"] = false
if !IsUserInOrgPool(ctx.User.ID, pool) {
ctx.Data[pool.Type+"Enabled"] = false
}
} else {
if strings.Contains(strings.ToLower(processType), strings.ToLower(pool.Type)) {
if IsUserInOrgPool(ctx.User.ID, pool) {
for _, center := range pool.Pool {
includeCenters[center.Queue] = struct{}{}
}
} else {
for _, center := range pool.Pool {
excludeCenters[center.Queue] = struct{}{}
}

}

}

}
}
}

//get valid resource specs
specs, err := grampus.GetResourceSpecs(processType)

grampusSpecs := getFilterSpecBySpecialPool(specs, includeCenters, excludeCenters)

if err != nil {
log.Error("GetResourceSpecs failed:", err.Error())
} else {
ctx.Data["flavor_infos"] = specs.Infos
ctx.Data["flavor_infos"] = grampusSpecs
}

//get branches
@@ -117,6 +136,46 @@ func grampusTrainJobNewDataPrepare(ctx *context.Context, processType string) err
return nil
}

func getFilterSpecBySpecialPool(specs *models.GetGrampusResourceSpecsResult, includeCenters map[string]struct{}, excludeCenters map[string]struct{}) []models.GrampusSpec {
if len(includeCenters) == 0 && len(excludeCenters) == 0 {
return specs.Infos
}
var grampusSpecs []models.GrampusSpec
for _, info := range specs.Infos {
if isInIncludeCenters(info, includeCenters) || (len(excludeCenters) != 0 && isNotAllInExcludeCenters(info, excludeCenters)) {
grampusSpecs = append(grampusSpecs, info)
}

}
return grampusSpecs
}

func isInIncludeCenters(grampusSpec models.GrampusSpec, centers map[string]struct{}) bool {
for _, center := range grampusSpec.Centers {
if _, ok := centers[center.ID]; ok {
return true
}
}
return false
}
func isNotAllInExcludeCenters(grampusSpec models.GrampusSpec, centers map[string]struct{}) bool {
for _, center := range grampusSpec.Centers {
if _, ok := centers[center.ID]; !ok {
return true
}
}
return false
}

func IsUserInOrgPool(userId int64, pool *models.SpecialPool) bool {
org, _ := models.GetOrgByName(pool.Org)
if org != nil {
isOrgMember, _ := models.IsOrganizationMember(org.ID, userId)
return isOrgMember
}
return false
}

func grampusParamCheckCreateTrainJob(form auth.CreateGrampusTrainJobForm) error {
if !strings.HasSuffix(strings.TrimSpace(form.BootFile), ".py") {
log.Error("the boot file(%s) must be a python file", form.BootFile)
@@ -263,6 +322,7 @@ func GrampusTrainJobGpuCreate(ctx *context.Context, form auth.CreateGrampusTrain
JobName: jobName,
DisplayJobName: displayJobName,
ComputeResource: models.GPUResource,
ProcessType: grampus.ProcessorTypeGPU,
Command: command,
ResourceSpecId: form.FlavorID,
ImageUrl: image,
@@ -436,6 +496,7 @@ func GrampusTrainJobNpuCreate(ctx *context.Context, form auth.CreateGrampusTrain
JobName: jobName,
DisplayJobName: displayJobName,
ComputeResource: models.NPUResource,
ProcessType: grampus.ProcessorTypeNPU,
Command: command,
ResourceSpecId: form.FlavorID,
ImageId: form.ImageID,


+ 141
- 88
routers/repo/modelarts.go View File

@@ -119,7 +119,8 @@ func MustEnableModelArts(ctx *context.Context) {

func NotebookNew(ctx *context.Context) {
notebookNewDataPrepare(ctx)

waitCount := cloudbrain.GetWaitingCloudbrainCount(models.TypeCloudBrainTwo, "")
ctx.Data["WaitCount"] = waitCount
ctx.HTML(200, tplModelArtsNotebookNew)
}

@@ -630,6 +631,8 @@ func TrainJobNew(ctx *context.Context) {
ctx.ServerError("get new train-job info failed", err)
return
}
waitCount := cloudbrain.GetWaitingCloudbrainCount(models.TypeCloudBrainTwo, "")
ctx.Data["WaitCount"] = waitCount
ctx.HTML(200, tplModelArtsTrainJobNew)
}

@@ -782,6 +785,8 @@ func TrainJobNewVersion(ctx *context.Context) {
ctx.ServerError("get new train-job info failed", err)
return
}
waitCount := cloudbrain.GetWaitingCloudbrainCount(models.TypeCloudBrainTwo, "")
ctx.Data["WaitCount"] = waitCount
ctx.HTML(200, tplModelArtsTrainJobVersionNew)
}

@@ -979,7 +984,7 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm)
flavorCode := form.Flavor
params := form.Params
poolID := form.PoolID
isSaveParam := form.IsSaveParam
//isSaveParam := form.IsSaveParam
repo := ctx.Repo.Repository
codeLocalPath := setting.JobPath + jobName + modelarts.CodePath
codeObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.CodePath
@@ -1124,45 +1129,45 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm)
}

//save param config
if isSaveParam == "on" {
saveparams := append(param, models.Parameter{
Label: modelarts.TrainUrl,
Value: outputObsPath,
}, models.Parameter{
Label: modelarts.DataUrl,
Value: dataPath,
})
if form.ParameterTemplateName == "" {
log.Error("ParameterTemplateName is empty")
trainJobNewDataPrepare(ctx)
ctx.RenderWithErr("保存作业参数时,作业参数名称不能为空", tplModelArtsTrainJobNew, &form)
return
}
_, err := modelarts.CreateTrainJobConfig(models.CreateConfigParams{
ConfigName: form.ParameterTemplateName,
Description: form.PrameterDescription,
DataUrl: dataPath,
AppUrl: codeObsPath,
BootFileUrl: codeObsPath + bootFile,
TrainUrl: outputObsPath,
Flavor: models.Flavor{
Code: flavorCode,
},
WorkServerNum: workServerNumber,
EngineID: int64(engineID),
LogUrl: logObsPath,
PoolID: poolID,
Parameter: saveparams,
})
if err != nil {
log.Error("Failed to CreateTrainJobConfig: %v", err)
trainJobErrorNewDataPrepare(ctx, form)
ctx.RenderWithErr("保存作业参数失败:"+err.Error(), tplModelArtsTrainJobNew, &form)
return
}
}
// if isSaveParam == "on" {
// saveparams := append(param, models.Parameter{
// Label: modelarts.TrainUrl,
// Value: outputObsPath,
// }, models.Parameter{
// Label: modelarts.DataUrl,
// Value: dataPath,
// })
// if form.ParameterTemplateName == "" {
// log.Error("ParameterTemplateName is empty")
// trainJobNewDataPrepare(ctx)
// ctx.RenderWithErr("保存作业参数时,作业参数名称不能为空", tplModelArtsTrainJobNew, &form)
// return
// }
// _, err := modelarts.CreateTrainJobConfig(models.CreateConfigParams{
// ConfigName: form.ParameterTemplateName,
// Description: form.PrameterDescription,
// DataUrl: dataPath,
// AppUrl: codeObsPath,
// BootFileUrl: codeObsPath + bootFile,
// TrainUrl: outputObsPath,
// Flavor: models.Flavor{
// Code: flavorCode,
// },
// WorkServerNum: workServerNumber,
// EngineID: int64(engineID),
// LogUrl: logObsPath,
// PoolID: poolID,
// Parameter: saveparams,
// })
// if err != nil {
// log.Error("Failed to CreateTrainJobConfig: %v", err)
// trainJobErrorNewDataPrepare(ctx, form)
// ctx.RenderWithErr("保存作业参数失败:"+err.Error(), tplModelArtsTrainJobNew, &form)
// return
// }
// }

req := &modelarts.GenerateTrainJobReq{
JobName: jobName,
@@ -1190,6 +1195,9 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm)
TotalVersionCount: modelarts.TotalVersionCount,
DatasetName: datasetNames,
}
userCommand, userImageUrl := getUserCommand(engineID, req)
req.UserCommand = userCommand
req.UserImageUrl = userImageUrl

//将params转换Parameters.Parameter,出错时返回给前端
var Parameters modelarts.Parameters
@@ -1208,6 +1216,36 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm)
ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/train-job")
}

func getUserCommand(engineId int, req *modelarts.GenerateTrainJobReq) (string, string) {
userImageUrl := ""
userCommand := ""
if engineId < 0 {
tmpCodeObsPath := strings.Trim(req.CodeObsPath, "/")
tmpCodeObsPaths := strings.Split(tmpCodeObsPath, "/")
lastCodeDir := "code"
if len(tmpCodeObsPaths) > 0 {
lastCodeDir = tmpCodeObsPaths[len(tmpCodeObsPaths)-1]
}
userCommand = "/bin/bash /home/work/run_train.sh 's3://" + req.CodeObsPath + "' '" + lastCodeDir + "/" + req.BootFile + "' '/tmp/log/train.log' --'data_url'='s3://" + req.DataUrl + "' --'train_url'='s3://" + req.TrainUrl + "'"
var versionInfos modelarts.VersionInfo
if err := json.Unmarshal([]byte(setting.EngineVersions), &versionInfos); err != nil {
log.Info("json parse err." + err.Error())
} else {
for _, engine := range versionInfos.Version {
if engine.ID == engineId {
userImageUrl = engine.Url
break
}
}
}
for _, param := range req.Parameters {
userCommand += " --'" + param.Label + "'='" + param.Value + "'"
}
return userCommand, userImageUrl
}
return userCommand, userImageUrl
}

func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJobForm) {
ctx.Data["PageIsTrainJob"] = true
var jobID = ctx.Params(":jobid")
@@ -1244,7 +1282,7 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ
flavorCode := form.Flavor
params := form.Params
poolID := form.PoolID
isSaveParam := form.IsSaveParam
//isSaveParam := form.IsSaveParam
repo := ctx.Repo.Repository
codeLocalPath := setting.JobPath + jobName + modelarts.CodePath
codeObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.CodePath + VersionOutputPath + "/"
@@ -1364,46 +1402,46 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ
})
}

//save param config
if isSaveParam == "on" {
saveparams := append(param, models.Parameter{
Label: modelarts.TrainUrl,
Value: outputObsPath,
}, models.Parameter{
Label: modelarts.DataUrl,
Value: dataPath,
})
if form.ParameterTemplateName == "" {
log.Error("ParameterTemplateName is empty")
versionErrorDataPrepare(ctx, form)
ctx.RenderWithErr("保存作业参数时,作业参数名称不能为空", tplModelArtsTrainJobVersionNew, &form)
return
}
_, err := modelarts.CreateTrainJobConfig(models.CreateConfigParams{
ConfigName: form.ParameterTemplateName,
Description: form.PrameterDescription,
DataUrl: dataPath,
AppUrl: codeObsPath,
BootFileUrl: codeObsPath + bootFile,
TrainUrl: outputObsPath,
Flavor: models.Flavor{
Code: flavorCode,
},
WorkServerNum: workServerNumber,
EngineID: int64(engineID),
LogUrl: logObsPath,
PoolID: poolID,
Parameter: saveparams,
})
if err != nil {
log.Error("Failed to CreateTrainJobConfig: %v", err)
versionErrorDataPrepare(ctx, form)
ctx.RenderWithErr("保存作业参数失败:"+err.Error(), tplModelArtsTrainJobVersionNew, &form)
return
}
}
// //save param config
// if isSaveParam == "on" {
// saveparams := append(param, models.Parameter{
// Label: modelarts.TrainUrl,
// Value: outputObsPath,
// }, models.Parameter{
// Label: modelarts.DataUrl,
// Value: dataPath,
// })
// if form.ParameterTemplateName == "" {
// log.Error("ParameterTemplateName is empty")
// versionErrorDataPrepare(ctx, form)
// ctx.RenderWithErr("保存作业参数时,作业参数名称不能为空", tplModelArtsTrainJobVersionNew, &form)
// return
// }
// _, err := modelarts.CreateTrainJobConfig(models.CreateConfigParams{
// ConfigName: form.ParameterTemplateName,
// Description: form.PrameterDescription,
// DataUrl: dataPath,
// AppUrl: codeObsPath,
// BootFileUrl: codeObsPath + bootFile,
// TrainUrl: outputObsPath,
// Flavor: models.Flavor{
// Code: flavorCode,
// },
// WorkServerNum: workServerNumber,
// EngineID: int64(engineID),
// LogUrl: logObsPath,
// PoolID: poolID,
// Parameter: saveparams,
// })
// if err != nil {
// log.Error("Failed to CreateTrainJobConfig: %v", err)
// versionErrorDataPrepare(ctx, form)
// ctx.RenderWithErr("保存作业参数失败:"+err.Error(), tplModelArtsTrainJobVersionNew, &form)
// return
// }
// }

task, err := models.GetCloudbrainByJobIDAndVersionName(jobID, PreVersionName)
if err != nil {
@@ -1438,6 +1476,9 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ
TotalVersionCount: latestTask.TotalVersionCount + 1,
DatasetName: datasetNames,
}
userCommand, userImageUrl := getUserCommand(engineID, req)
req.UserCommand = userCommand
req.UserImageUrl = userImageUrl

err = modelarts.GenerateTrainJobVersion(ctx, req, jobID)
if err != nil {
@@ -1876,8 +1917,8 @@ func InferenceJobCreate(ctx *context.Context, form auth.CreateModelArtsInference
modelName := form.ModelName
modelVersion := form.ModelVersion
ckptName := form.CkptName
ckptUrl := form.TrainUrl + form.CkptName
ckptUrl := "/" + form.TrainUrl + form.CkptName
log.Info("ckpt url:" + ckptUrl)

count, err := models.GetCloudbrainInferenceJobCountByUserID(ctx.User.ID)
if err != nil {
@@ -2044,6 +2085,13 @@ func InferenceJobIndex(ctx *context.Context) {
page = 1
}

listType := ctx.Query("listType")
ctx.Data["ListType"] = listType

if listType == models.AllResource {
listType = ""
}

var jobTypes []string
jobTypes = append(jobTypes, string(models.JobTypeInference))
tasks, count, err := models.Cloudbrains(&models.CloudbrainsOptions{
@@ -2051,9 +2099,10 @@ func InferenceJobIndex(ctx *context.Context) {
Page: page,
PageSize: setting.UI.IssuePagingNum,
},
RepoID: repo.ID,
Type: models.TypeCloudBrainTwo,
JobTypes: jobTypes,
RepoID: repo.ID,
ComputeResource: listType,
JobTypes: jobTypes,
Type: models.TypeCloudBrainAll,
})
if err != nil {
ctx.ServerError("Cloudbrain", err)
@@ -2063,7 +2112,9 @@ func InferenceJobIndex(ctx *context.Context) {
for i, task := range tasks {
tasks[i].CanDel = cloudbrain.CanDeleteJob(ctx, &task.Cloudbrain)
tasks[i].CanModify = cloudbrain.CanModifyJob(ctx, &task.Cloudbrain)
tasks[i].ComputeResource = models.NPUResource
if tasks[i].ComputeResource == "" {
tasks[i].ComputeResource = models.NPUResource
}
}

repoId := ctx.Repo.Repository.ID
@@ -2095,6 +2146,8 @@ func InferenceJobNew(ctx *context.Context) {
ctx.ServerError("get new inference-job info failed", err)
return
}
waitCount := cloudbrain.GetWaitingCloudbrainCount(models.TypeCloudBrainTwo, "")
ctx.Data["WaitCount"] = waitCount
ctx.HTML(200, tplModelArtsInferenceJobNew)
}
func inferenceJobNewDataPrepare(ctx *context.Context) error {


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

@@ -195,8 +195,11 @@ func CreatePost(ctx *context.Context, form auth.CreateRepoForm) {
}
ctx.Data["ContextUser"] = ctxUser
if !form.AutoAgree {
ctx.Data["CheckedAutoAgree"] = ""
ctx.RenderWithErr(ctx.Tr("repo.template.one_promise"), tplCreate, form)
return
} else {
ctx.Data["CheckedAutoAgree"] = "checked"
}
if ctx.HasError() {
ctx.HTML(200, tplCreate)


+ 119
- 1
routers/repo/user_data_analysis.go View File

@@ -112,6 +112,7 @@ func getExcelHeader(ctx *context.Context) map[string]string {
excelHeader = append(excelHeader, ctx.Tr("user.static.RecommendImage"))

excelHeader = append(excelHeader, ctx.Tr("user.static.email"))
excelHeader = append(excelHeader, ctx.Tr("user.static.phone"))
excelHeader = append(excelHeader, ctx.Tr("user.static.location"))

excelHeader = append(excelHeader, ctx.Tr("user.static.registdate"))
@@ -193,6 +194,9 @@ func writeExcel(row int, xlsx *excelize.File, sheetName string, userRecord *mode
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.Email)
tmp = tmp + 1

xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.Phone)
tmp = tmp + 1

xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.UserLocation)
tmp = tmp + 1

@@ -268,6 +272,9 @@ func writeExcelPage(row int, xlsx *excelize.File, sheetName string, userRecord *
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.Email)
tmp = tmp + 1

xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.Phone)
tmp = tmp + 1

xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.UserLocation)
tmp = tmp + 1

@@ -601,7 +608,7 @@ func QueryUserStaticDataPage(ctx *context.Context) {
filename := sheetName + "_" + startDate + "_" + endDate + ".xlsx"
os.Remove(setting.AppDataPath + Excel_File_Path + filename)
go writeFileToDisk(ctx, count, re, filename)
ctx.JSON(http.StatusOK, ctx.Tr("user.static.downloadinfo")+setting.AppURL+"api/v1/download_user_define_file?filename="+filename)
ctx.JSON(http.StatusOK, ctx.Tr("user.static.downloadinfo")+"/api/v1/download_user_define_file?filename="+filename)
} else {
mapInterface := make(map[string]interface{})
re, count := models.QueryUserStaticDataPage(pageOpts)
@@ -721,3 +728,114 @@ func TimingCountData() {
startTime := currentTimeNow.AddDate(0, 0, -1).Format("2006-01-02")
TimingCountDataByDateAndReCount(startTime, false)
}

func QueryUserActivity(ctx *context.Context) {
startDate := ctx.Query("beginTime")
endDate := ctx.Query("endTime")

t, _ := time.Parse("2006-01-02", startDate)
startTime := time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location())
startTime = startTime.UTC()

t, _ = time.Parse("2006-01-02", endDate)
endTime := time.Date(t.Year(), t.Month(), t.Day(), 23, 59, 59, 0, t.Location())
endTime = endTime.UTC()

sheetName := ctx.Tr("user.static.sheetname")
filename := sheetName + "_" + startDate + "_" + endDate + ".xlsx"
filePath := setting.AppDataPath + Excel_File_Path + filename
os.Remove(setting.AppDataPath + Excel_File_Path + filename)

go writeUserActivityToExcel(startTime, endTime, filePath, ctx)

ctx.JSON(http.StatusOK, ctx.Tr("user.static.downloadinfo")+"/api/v1/download_user_define_file?filename="+filename)

}

func writeUserActivityToExcel(startTime time.Time, endTime time.Time, filePath string, ctx *context.Context) {
re := models.QueryDataForActivity(startTime, endTime)
log.Info("return count=" + fmt.Sprint(len(re)))
//writer exec file.
xlsx := excelize.NewFile()
sheetName := ctx.Tr("user.static.sheetname")
index := xlsx.NewSheet(sheetName)
xlsx.DeleteSheet("Sheet1")

excelHeader := make([]string, 0)
excelHeader = append(excelHeader, ctx.Tr("user.static.id"))
excelHeader = append(excelHeader, ctx.Tr("user.static.name"))
excelHeader = append(excelHeader, ctx.Tr("user.static.codemergecount"))
excelHeader = append(excelHeader, ctx.Tr("user.static.commitcount"))
excelHeader = append(excelHeader, ctx.Tr("user.static.issuecount"))
excelHeader = append(excelHeader, ctx.Tr("user.static.commentcount"))
excelHeader = append(excelHeader, ctx.Tr("user.static.watchedcount"))
excelHeader = append(excelHeader, ctx.Tr("user.static.commitcodesize"))
excelHeader = append(excelHeader, ctx.Tr("user.static.solveissuecount"))

excelHeader = append(excelHeader, ctx.Tr("user.static.CommitDatasetNum"))
excelHeader = append(excelHeader, ctx.Tr("user.static.CommitModelCount"))
excelHeader = append(excelHeader, ctx.Tr("user.static.email"))
excelHeader = append(excelHeader, ctx.Tr("user.static.phone"))
excelHeader = append(excelHeader, ctx.Tr("user.static.registdate"))

excelHeaderMap := make(map[string]string, 0)
var j byte
j = 0
for _, value := range excelHeader {
excelColumn := getColumn(j) + fmt.Sprint(1)
log.Info("excelColumn=" + excelColumn)
excelHeaderMap[excelColumn] = value
j++
}

for k, v := range excelHeaderMap {
//设置单元格的值
xlsx.SetCellValue(sheetName, k, v)
}

for i, userRecord := range re {
row := i + 2
rows := fmt.Sprint(row)
var tmp byte
tmp = 0
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.ID)
tmp = tmp + 1
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.Name)
tmp = tmp + 1
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.CodeMergeCount)
tmp = tmp + 1
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.CommitCount)
tmp = tmp + 1
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.IssueCount)
tmp = tmp + 1
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.CommentCount)
tmp = tmp + 1
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.WatchedCount)
tmp = tmp + 1
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.CommitCodeSize)
tmp = tmp + 1
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.SolveIssueCount)
tmp = tmp + 1
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.CommitDatasetNum)
tmp = tmp + 1
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.CommitModelCount)
tmp = tmp + 1
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.Email)
tmp = tmp + 1
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.Phone)
tmp = tmp + 1
formatTime := userRecord.RegistDate.Format("2006-01-02 15:04:05")
xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, formatTime[0:len(formatTime)-3])
tmp = tmp + 1
}

//设置默认打开的表单
xlsx.SetActiveSheet(index)

os.Mkdir(setting.AppDataPath+Excel_File_Path, 0755)
if err := xlsx.SaveAs(filePath); err != nil {
log.Info("writer exel error." + err.Error())
} else {
log.Info("write to file succeed, filepath=" + filePath)
}
}

+ 33
- 1
routers/routes/routes.go View File

@@ -12,6 +12,8 @@ import (
"text/template"
"time"

"code.gitea.io/gitea/modules/slideimage"

"code.gitea.io/gitea/routers/image"

"code.gitea.io/gitea/routers/authentication"
@@ -233,6 +235,10 @@ func NewMacaron() *macaron.Macaron {
m.Use(captcha.Captchaer(captcha.Options{
SubURL: setting.AppSubURL,
}))
m.Use(slideimage.SlideImager(slideimage.Options{
SubURL: setting.AppSubURL,
SampleImages: setting.SlideImagesCount,
}))
m.Use(session.Sessioner(session.Options{
Provider: setting.SessionConfig.Provider,
ProviderConfig: setting.SessionConfig.ProviderConfig,
@@ -333,6 +339,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/all/dosearch/", routers.SearchApi)
m.Post("/user/login/kanban", user.SignInPostAPI)
m.Get("/home/term", routers.HomeTerm)
m.Get("/home/privacy", routers.HomePrivacy)
m.Group("/explore", func() {
m.Get("", func(ctx *context.Context) {
ctx.Redirect(setting.AppSubURL + "/explore/repos")
@@ -366,8 +373,11 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Group("/user", func() {
m.Get("/login", user.SignIn)
m.Get("/login/cloud_brain", user.SignInCloudBrain)
m.Post("/login/cloud_brain", bindIgnErr(auth.SignInForm{}), user.SignInCloudBrainPost)
m.Post("/login", bindIgnErr(auth.SignInForm{}), user.SignInPost)

m.Get("/login/phone", user.SignInPhone)
m.Post("/login/phone", bindIgnErr(auth.PhoneNumberCodeForm{}), user.SignInPhonePost)
m.Group("", func() {
m.Combo("/login/openid").
Get(user.SignInOpenID).
@@ -407,6 +417,10 @@ func RegisterRoutes(m *macaron.Macaron) {
}, reqSignOut)

m.Any("/user/events", reqSignIn, events.Events)
m.Get("/slideImage", user.CreateSlideImageInfo)
m.Post("/verifySlideImage", bindIgnErr(auth.SlideImageForm{}), user.VerifySlideImage)
m.Post("/sendVerifyCode", bindIgnErr(auth.PhoneNumberForm{}), user.SendVerifyCode)
m.Post("/bindPhone", reqSignIn, bindIgnErr(auth.PhoneNumberCodeForm{}), user.BindPhone)

m.Group("/login/oauth", func() {
m.Get("/authorize", bindIgnErr(auth.AuthorizationForm{}), user.AuthorizeOAuth)
@@ -486,6 +500,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/email2user", user.Email2User)
m.Get("/recover_account", user.ResetPasswd)
m.Post("/recover_account", user.ResetPasswdPost)
m.Post("/recover_account_by_phone", bindIgnErr(auth.ResetPassWordByPhoneForm{}), user.ResetPasswdByPhonePost)
m.Get("/forgot_password", user.ForgotPasswd)
m.Post("/forgot_password", user.ForgotPasswdPost)
m.Post("/logout", user.SignOut)
@@ -1090,6 +1105,16 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/create", reqWechatBind, reqRepoCloudBrainWriter, repo.CloudBrainTrainJobNew)
m.Post("/create", reqWechatBind, reqRepoCloudBrainWriter, bindIgnErr(auth.CreateCloudBrainForm{}), repo.CloudBrainCreate)
})
m.Group("/inference-job", func() {
m.Group("/:jobid", func() {
m.Get("", reqRepoCloudBrainReader, repo.InferenceCloudBrainJobShow)
m.Get("/result_download", cloudbrain.AdminOrJobCreaterRightForTrain, repo.CloudBrainDownloadInferenceResult)

m.Get("/downloadall", repo.DownloadInferenceResultFile)
})
m.Get("/create", reqWechatBind, reqRepoCloudBrainWriter, repo.InferenceCloudBrainJobNew)
m.Post("/create", reqWechatBind, reqRepoCloudBrainWriter, bindIgnErr(auth.CreateCloudBrainInferencForm{}), repo.CloudBrainInferenceJobCreate)
})
}, context.RepoRef())
m.Group("/grampus", func() {
m.Group("/train-job", func() {
@@ -1111,19 +1136,26 @@ func RegisterRoutes(m *macaron.Macaron) {
}, context.RepoRef())
m.Group("/modelmanage", func() {
m.Post("/create_model", reqRepoModelManageWriter, repo.SaveModel)
m.Post("/create_model_convert", reqRepoModelManageWriter, repo.SaveModelConvert)
m.Post("/create_new_model", repo.SaveNewNameModel)
m.Delete("/delete_model", repo.DeleteModel)
m.Post("/delete_model_convert/:id", repo.DeleteModelConvert)
m.Post("/convert_stop/:id", repo.StopModelConvert)
m.Put("/modify_model", repo.ModifyModelInfo)
m.Get("/show_model", reqRepoModelManageReader, repo.ShowModelTemplate)
m.Get("/convert_model", reqRepoModelManageReader, repo.ConvertModelTemplate)
m.Get("/show_model_info", repo.ShowModelInfo)
m.Get("/show_model_convert_info", repo.ShowModelConvertInfo)
m.Get("/show_model_info_api", repo.ShowSingleModel)
m.Get("/show_model_api", repo.ShowModelPageInfo)
m.Get("/show_model_child_api", repo.ShowOneVersionOtherModel)
m.Get("/query_train_job", reqRepoCloudBrainReader, repo.QueryTrainJobList)
m.Get("/query_train_model", reqRepoCloudBrainReader, repo.QueryTrainModelList)
m.Get("/query_train_job_version", reqRepoCloudBrainReader, repo.QueryTrainJobVersionList)
m.Get("/query_model_for_predict", reqRepoModelManageReader, repo.QueryModelListForPredict)
m.Get("/query_modelfile_for_predict", reqRepoModelManageReader, repo.QueryModelFileForPredict)
m.Get("/query_onelevel_modelfile", reqRepoModelManageReader, repo.QueryOneLevelModelFile)
m.Get("/download_model_convert/:id", reqRepoModelManageReader, repo.ModelConvertDownloadModel)
m.Group("/:ID", func() {
m.Get("", repo.ShowSingleModel)
m.Get("/downloadsingle", repo.DownloadSingleModelFile)


+ 313
- 29
routers/user/auth.go View File

@@ -8,9 +8,19 @@ package user
import (
"errors"
"fmt"
"github.com/gomodule/redigo/redis"
"net/http"
"strconv"
"strings"

"code.gitea.io/gitea/modules/slideimage"

phoneService "code.gitea.io/gitea/services/phone"

"code.gitea.io/gitea/modules/labelmsg"

"code.gitea.io/gitea/modules/phone"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth"
"code.gitea.io/gitea/modules/auth/oauth2"
@@ -38,16 +48,18 @@ const (
tplSignIn base.TplName = "user/auth/signin"
// tplSignIn template for sign in page
tplSignInCloudBrain base.TplName = "user/auth/signin_cloud_brain"
tplSignInPhone base.TplName = "user/auth/signin_phone"
// tplSignUp template path for sign up page
tplSignUp base.TplName = "user/auth/signup"
// TplActivate template path for activate user
TplActivate base.TplName = "user/auth/activate"
tplForgotPassword base.TplName = "user/auth/forgot_passwd"
tplResetPassword base.TplName = "user/auth/reset_passwd"
tplTwofa base.TplName = "user/auth/twofa"
tplTwofaScratch base.TplName = "user/auth/twofa_scratch"
tplLinkAccount base.TplName = "user/auth/link_account"
tplU2F base.TplName = "user/auth/u2f"
TplActivate base.TplName = "user/auth/activate"
tplForgotPassword base.TplName = "user/auth/forgot_passwd"
tplForgotPasswordPhone base.TplName = "user/auth/forgot_passwd_phone"
tplResetPassword base.TplName = "user/auth/reset_passwd"
tplTwofa base.TplName = "user/auth/twofa"
tplTwofaScratch base.TplName = "user/auth/twofa_scratch"
tplLinkAccount base.TplName = "user/auth/link_account"
tplU2F base.TplName = "user/auth/u2f"
)

// AutoSignIn reads cookie and try to auto-login.
@@ -168,7 +180,7 @@ func SignInCloudBrain(ctx *context.Context) {
return
}

ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login"
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login/cloud_brain"
ctx.Data["PageIsSignIn"] = true
ctx.Data["PageIsCloudBrainLogin"] = true
ctx.Data["EnableCloudBrain"] = true
@@ -176,6 +188,50 @@ func SignInCloudBrain(ctx *context.Context) {
ctx.HTML(200, tplSignInCloudBrain)
}

func SignInPhone(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("sign_in")
// Check auto-login.
if checkAutoLogin(ctx) {
return
}

ctx.Data["PageIsPhoneLogin"] = true

ctx.HTML(200, tplSignInPhone)
}

func SignInPhonePost(ctx *context.Context, form auth.PhoneNumberCodeForm) {
ctx.Data["Title"] = ctx.Tr("sign_in")
ctx.Data["PageIsPhoneLogin"] = true
ctx.Data["IsCourse"] = ctx.QueryBool("course")
ctx.Data["EnableCloudBrain"] = true

if ctx.HasError() {
ctx.HTML(200, tplSignInPhone)
return
}

if !phoneService.IsVerifyCodeRight(strings.TrimSpace(form.PhoneNumber), strings.TrimSpace(form.VerifyCode)) {
ctx.RenderWithErr(ctx.Tr("phone.verify_code_fail"), tplSignInPhone, &form)
return
}

u, err := models.GetUserByPhoneNumber(strings.TrimSpace(form.PhoneNumber))

if err != nil {
if models.IsErrUserNotExist(err) {
ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tplSignInPhone, &form)
log.Info("Failed authentication attempt for %s from %s", form.PhoneNumber, ctx.RemoteAddr())
} else {
ctx.ServerError("UserSignIn", err)
}
return
}
models.SaveLoginInfoToDb(ctx.Req.Request, u)

handleSignIn(ctx, u, form.Remember)
}

func SignInPostAPI(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("sign_in")
UserName := ctx.Query("UserName")
@@ -211,8 +267,7 @@ func SignInPostAPI(ctx *context.Context) {
handleSignInFullNotRedirect(ctx, u, true, false)
}

// SignInPost response for sign in request
func SignInPost(ctx *context.Context, form auth.SignInForm) {
func SignInPostCommon(ctx *context.Context, form auth.SignInForm) {
ctx.Data["Title"] = ctx.Tr("sign_in")

orderedOAuth2Names, oauth2Providers, err := models.GetActiveOAuth2Providers()
@@ -223,11 +278,12 @@ func SignInPost(ctx *context.Context, form auth.SignInForm) {
ctx.Data["OrderedOAuth2Names"] = orderedOAuth2Names
ctx.Data["OAuth2Providers"] = oauth2Providers
ctx.Data["Title"] = ctx.Tr("sign_in")
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login"
ctx.Data["PageIsSignIn"] = true
ctx.Data["PageIsLogin"] = true
ctx.Data["IsCourse"] = ctx.QueryBool("course")
ctx.Data["EnableSSPI"] = models.IsSSPIEnabled()
ctx.Data["EnableCloudBrain"] = true

if ctx.HasError() {
ctx.HTML(200, tplSignIn)
@@ -256,7 +312,9 @@ func SignInPost(ctx *context.Context, form auth.SignInForm) {
ctx.HTML(200, "user/auth/prohibit_login")
}
} else {
ctx.ServerError("UserSignIn", err)
log.Warn("UserSignIn", err)
ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tplSignIn, &form)
log.Info("Failed authentication attempt for %s from %s", form.UserName, ctx.RemoteAddr())
}
return
}
@@ -294,6 +352,20 @@ func SignInPost(ctx *context.Context, form auth.SignInForm) {
ctx.Redirect(setting.AppSubURL + "/user/two_factor")
}


func SignInCloudBrainPost(ctx *context.Context, form auth.SignInForm) {
ctx.Data["PageIsCloudBrainLogin"] = true
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login/cloud_brain"
SignInPostCommon(ctx,form)
}

// SignInPost response for sign in request
func SignInPost(ctx *context.Context, form auth.SignInForm) {
ctx.Data["PageIsLogin"] = true
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login"
SignInPostCommon(ctx,form)
}

// TwoFactor shows the user a two-factor authentication page.
func TwoFactor(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("twofa")
@@ -1252,11 +1324,26 @@ func SignUpPost(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterFo
return
}

if setting.PhoneService.Enabled {
phoneNumber := strings.TrimSpace(form.PhoneNumber)
verifyCode := strings.TrimSpace(form.VerifyCode)
if !phoneService.IsVerifyCodeRight(phoneNumber, verifyCode) {
ctx.RenderWithErr(ctx.Tr("phone.verify_code_fail"), tplSignUp, &form)
return
}
}

if !form.Agree {
ctx.RenderWithErr(ctx.Tr("sign_up_agree_tips"), tplSignUp, &form)
return
}

u := &models.User{
Name: form.UserName,
Email: form.Email,
Passwd: form.Password,
IsActive: !setting.Service.RegisterEmailConfirm,
Name: form.UserName,
Email: form.Email,
Passwd: form.Password,
PhoneNumber: strings.TrimSpace(form.PhoneNumber),
IsActive: !setting.Service.RegisterEmailConfirm,
}
if err := models.CreateUser(u); err != nil {
switch {
@@ -1427,18 +1514,30 @@ func ActivateEmail(ctx *context.Context) {
// ForgotPasswd render the forget pasword page
func ForgotPasswd(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("auth.forgot_password_title")
forgetType := ctx.Query("type")

if setting.MailService == nil {
ctx.Data["IsResetDisable"] = true
ctx.HTML(200, tplForgotPassword)
return
}
if forgetType == "phone" {
if !setting.PhoneService.Enabled {
ctx.Data["IsResetDisable"] = true
ctx.HTML(200, tplForgotPasswordPhone)
return
}
ctx.Data["IsResetRequest"] = true
ctx.HTML(200, tplForgotPasswordPhone)
} else {

email := ctx.Query("email")
ctx.Data["Email"] = email
if setting.MailService == nil {
ctx.Data["IsResetDisable"] = true
ctx.HTML(200, tplForgotPassword)
return
}

ctx.Data["IsResetRequest"] = true
ctx.HTML(200, tplForgotPassword)
email := ctx.Query("email")
ctx.Data["Email"] = email

ctx.Data["IsResetRequest"] = true
ctx.HTML(200, tplForgotPassword)
}
}

// ForgotPasswdPost response for forget password request
@@ -1454,12 +1553,16 @@ func ForgotPasswdPost(ctx *context.Context) {
email := ctx.Query("email")
ctx.Data["Email"] = email

u, err := models.GetUserByEmail(email)
u, err := models.GetUserByMainEmail(email)
if err != nil {
if models.IsErrUserNotExist(err) {
ctx.Data["ResetPwdCodeLives"] = timeutil.MinutesToFriendly(setting.Service.ResetPwdCodeLives, ctx.Locale.Language())
ctx.Data["IsResetSent"] = true
ctx.HTML(200, tplForgotPassword)
ctx.Data["IsResetSent"] = false
if used, _ := models.IsEmailUsed(email); used {
ctx.RenderWithErr(ctx.Tr("auth.email_not_main"), tplForgotPassword, nil)
} else {
ctx.RenderWithErr(ctx.Tr("auth.email_not_right"), tplForgotPassword, nil)
}
return
}

@@ -1647,6 +1750,55 @@ func ResetPasswdPost(ctx *context.Context) {
handleSignInFull(ctx, u, remember, true)
}

func ResetPasswdByPhonePost(ctx *context.Context, form auth.ResetPassWordByPhoneForm) {
phoneNumber := strings.TrimSpace(form.PhoneNumber)
verifyCode := strings.TrimSpace(form.VerifyCode)
isRight := phoneService.IsVerifyCodeRight(phoneNumber, verifyCode)
if !isRight {
ctx.RenderWithErr(ctx.Tr("phone.verify_code_fail"), tplForgotPasswordPhone, form)
return
}

passwd := strings.TrimSpace(form.Password)
if len(passwd) < setting.MinPasswordLength {
ctx.RenderWithErr(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplForgotPasswordPhone, form)
return
} else if !password.IsComplexEnough(passwd) {
ctx.RenderWithErr(password.BuildComplexityError(ctx), tplForgotPasswordPhone, form)
return
}

u, err := models.GetUserByPhoneNumber(phoneNumber)
if err != nil {
log.Error("fail to query by phone number", err)
ctx.RenderWithErr(ctx.Tr("phone.query_err", setting.MinPasswordLength), tplForgotPasswordPhone, form)
return
}

if nil != ctx.User && u.ID != ctx.User.ID {
ctx.RenderWithErr(ctx.Tr("auth.reset_password_wrong_user", ctx.User.Email, u.Email), tplForgotPasswordPhone, form)
return
}

if u.Rands, err = models.GetUserSalt(); err != nil {
ctx.ServerError("UpdateUser", err)
return
}
if u.Salt, err = models.GetUserSalt(); err != nil {
ctx.ServerError("UpdateUser", err)
return
}
u.HashPassword(passwd)
u.MustChangePassword = false
if err := models.UpdateUserCols(u, "must_change_password", "passwd", "rands", "salt"); err != nil {
ctx.ServerError("UpdateUser", err)
return
}

handleSignInFull(ctx, u, form.Remember, true)

}

// MustChangePassword renders the page to change a user's password
func MustChangePassword(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("auth.must_change_password")
@@ -1709,3 +1861,135 @@ func MustChangePasswordPost(ctx *context.Context, cpt *captcha.Captcha, form aut

ctx.Redirect(setting.AppSubURL + "/")
}

func CreateSlideImageInfo(ctx *context.Context, slideImage *slideimage.SlideImage) {
id, _, _ := slideImage.CreateCode()
ctx.JSON(http.StatusOK, models.BaseMessage{0, id})
}

func VerifySlideImage(ctx *context.Context, slideImage *slideimage.SlideImage, form auth.SlideImageForm) {
if slideImage.Verify(form.SlideID, form.X) {
ctx.JSON(http.StatusOK, models.BaseOKMessage)
} else {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(""))
}
}

func BindPhone(ctx *context.Context, form auth.PhoneNumberCodeForm) {
if strings.TrimSpace(form.PhoneNumber) != "" && strings.TrimSpace(form.VerifyCode) != "" && phoneService.IsVerifyCodeRight(strings.TrimSpace(form.PhoneNumber), strings.TrimSpace(form.VerifyCode)) {

ctx.User.PhoneNumber = strings.TrimSpace(form.PhoneNumber)
if err := models.UpdateUserSetting(ctx.User); err != nil {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.bind_phone_fail")))
return
}
ctx.JSON(http.StatusOK, models.BaseOKMessage)
return
}

ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.verify_code_fail")))

}

func SendVerifyCode(ctx *context.Context, slideImage *slideimage.SlideImage, form auth.PhoneNumberForm) {
phoneNumber := strings.TrimSpace(form.PhoneNumber)

if !phone.IsValidPhoneNumber(phoneNumber) {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.format_err")))
return
}

hasManual, err := slideImage.VerifyManual(form.SlideID)
if err != nil {
log.Warn("redis err", err)
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.query_err")))
return

}
if !hasManual {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.query_err")))
return
}

if form.Mode != 2 {
has, err := models.IsUserByPhoneNumberExist(phoneNumber)
if err != nil {
log.Warn("sql err", err)
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.query_err")))
return
}

if form.Mode==0 { //注册

if has {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.already_register")))
return
}
} else { //手机号验证码登录 mode=1 忘记密码 mode=3
if !has {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.not_register")))
return
}

}

} else {
//修改手机号 mode=2 绑定手机
u, err := models.GetUserByPhoneNumber(phoneNumber)
if err != nil && !models.IsErrUserNotExist(err) {
log.Warn("sql err", err)
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.query_err")))
return
}

if u != nil {

if u.ID == ctx.User.ID { //没有修改手机号
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.not_modify")))
return
} else { //修改的手机已经被别的用户注册
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.already_register")))
return
}

}
}


redisConn := labelmsg.Get()
defer redisConn.Close()

sendTimes, err := phoneService.GetPhoneNumberSendTimes(redisConn, phoneNumber)
if err != nil && err!=redis.ErrNil {
log.Warn("redis err", err)
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.query_err")))
return

}
if sendTimes >= setting.PhoneService.MaxRetryTimes {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.max_times", strconv.Itoa(setting.PhoneService.MaxRetryTimes))))
return

}

ttl, err := phoneService.GetPhoneCodeTTL(redisConn, phoneNumber)
if err != nil {
log.Warn("redis err", err)
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.query_err")))
return

}
if setting.PhoneService.CodeTimeout-ttl < setting.PhoneService.RetryInterval {
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.too_fast")))
return
}
err = phoneService.SendVerifyCode(redisConn, phoneNumber)
if err != nil {
log.Warn("send code or redis err", err)
ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("phone.query_err")))
return
}

ctx.JSON(http.StatusOK, models.BaseOKMessage)

}

+ 14
- 0
routers/user/setting/profile.go View File

@@ -11,6 +11,8 @@ import (
"io/ioutil"
"strings"

phoneService "code.gitea.io/gitea/services/phone"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth"
"code.gitea.io/gitea/modules/base"
@@ -89,6 +91,18 @@ func ProfilePost(ctx *context.Context, form auth.UpdateProfileForm) {
return
}

if setting.PhoneService.Enabled && strings.TrimSpace(form.PhoneNumber) != "" {
if strings.TrimSpace(form.PhoneNumber) != ctx.User.PhoneNumber {
if phoneService.IsVerifyCodeRight(strings.TrimSpace(form.PhoneNumber), strings.TrimSpace(form.VerifyCode)) {
ctx.User.PhoneNumber = strings.TrimSpace(form.PhoneNumber)
} else {
ctx.Flash.Error(ctx.Tr("phone.verify_code_fail"))
ctx.Redirect(setting.AppSubURL + "/user/settings")
return
}
}
}

ctx.User.FullName = form.FullName

ctx.User.KeepEmailPrivate = form.KeepEmailPrivate


+ 93
- 0
services/phone/phone.go View File

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

import (
"fmt"
"time"

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

"code.gitea.io/gitea/modules/phone"
"code.gitea.io/gitea/modules/redis/redis_client"
"code.gitea.io/gitea/modules/setting"

"github.com/gomodule/redigo/redis"
)

//验证码存储前缀 使用时%s用手机号替代
const CODE_PREFIX = "P_C:%s"

//手机号发送验证码次数Hkey,%s对应日期, 存储在hset中,值是hashet,记录手机号和发送次数
const TIMES_PREFIX = "P_T:%s"

func GetPhoneNumberSendTimes(conn redis.Conn, phoneNumber string) (int, error) {
i, err := redis_client.HGET(conn, GetPhoneTimesHKey(), phoneNumber)

return redis.Int(i, err)
}

func GetPhoneCodeTTL(conn redis.Conn, phoneNumber string) (int, error) {
return redis_client.Ttl(conn, GetPhoneCodeKey(phoneNumber))

}
func SendVerifyCode(conn redis.Conn, phoneNumber string) error {
timesKey := GetPhoneTimesHKey()
exists, err := redis_client.EXISTS(conn, timesKey)
if err != nil {
return err
}
code := phone.GenerateVerifyCode(setting.PhoneService.VerifyCodeLength)
err = phone.SendVerifyCode(phoneNumber, code)
if err != nil {
return err
}
redis_client.SET(conn, GetPhoneCodeKey(phoneNumber), code, setting.PhoneService.CodeTimeout)
if !exists {
err = redis_client.HSETNX(conn, timesKey, phoneNumber, 1)
if err != nil {
return err
}
err = redis_client.Expire(conn, timesKey, getRemainSecondOfDay(time.Now()))
if err != nil {
return err
}

} else {
err = redis_client.HINCRBY(conn, timesKey, phoneNumber, 1)

if err != nil {
return err
}

}
return nil

}

func IsVerifyCodeRight(phoneNumer string, verifyCode string) bool {
if phoneNumer == "" {
return false
}
value, err := redis_client.Get(GetPhoneCodeKey(phoneNumer))
if err != nil {
log.Warn("redis err", err)
return false
} else {
if value == "" {
return false
}
return value == verifyCode
}
}

func GetPhoneCodeKey(phoneNumber string) string {
return fmt.Sprintf(CODE_PREFIX, phoneNumber)
}

func GetPhoneTimesHKey() string {
today := time.Now().Format("2006-01-02")
return fmt.Sprintf(TIMES_PREFIX, today)
}

func getRemainSecondOfDay(t time.Time) int {
return 86400 - 60*60*t.Hour() - 60*t.Minute() - t.Second()
}

+ 2
- 2
templates/admin/cloudbrain/list.tmpl View File

@@ -204,7 +204,7 @@
{{else}}
<a style="padding: 0.5rem 1rem;" id="ai-stop-{{$JobID}}"
class="ui basic ai_stop_version {{if eq .Status "KILLED" "FAILED" "START_FAILED" "KILLING" "COMPLETED" "SUCCEEDED" "STOPPED"}}disabled {{else}} blue {{end}}button"
data-repopath="{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .Cloudbrain.Type 0}}/cloudbrain/train-job{{else if eq .Cloudbrain.Type 2}}/grampus/train-job{{else}}/modelarts/{{if eq .JobType "INFERENCE"}}inference-job{{else}}train-job{{end}}{{end}}"
data-repopath="{{.Repo.OwnerName}}/{{.Repo.Name}}/{{if eq .JobType "INFERENCE"}}{{if eq .Cloudbrain.Type 1}}modelarts/inference-job{{else}}cloudbrain/train-job{{end}}{{else if eq .JobType "TRAIN"}}{{if eq .Cloudbrain.Type 1}}modelarts/train-job{{else if eq .Cloudbrain.Type 0}}cloudbrain/train-job{{else if eq .Cloudbrain.Type 2}}grampus/train-job{{end}}{{end}}"
data-jobid="{{$JobID}}" data-version="{{.VersionName}}">
{{$.i18n.Tr "repo.stop"}}
</a>
@@ -212,7 +212,7 @@
</div>
<!-- 删除任务 -->
<form class="ui compact buttons" id="delForm-{{$JobID}}"
action='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .JobType "BENCHMARK"}}/cloudbrain/benchmark{{else if or (eq .JobType "SNN4IMAGENET") (eq .JobType "BRAINSCORE")}}/cloudbrain{{else if eq .JobType "DEBUG"}}{{if eq .ComputeResource "NPU"}}/modelarts/notebook{{else}}/cloudbrain{{end}}{{else if eq .JobType "TRAIN"}}{{if eq .Cloudbrain.Type 1}}/modelarts/train-job{{else if eq .Cloudbrain.Type 0}}/cloudbrain/train-job{{else if eq .Cloudbrain.Type 2}}/grampus/train-job{{end}}{{end}}/{{$JobID}}/del?isadminpage=true'
action='{{AppSubUrl}}/{{.Repo.OwnerName}}/{{.Repo.Name}}{{if eq .JobType "BENCHMARK"}}/cloudbrain/benchmark{{else if or (eq .JobType "SNN4IMAGENET") (eq .JobType "BRAINSCORE")}}/cloudbrain{{else if eq .JobType "DEBUG"}}{{if eq .ComputeResource "NPU"}}/modelarts/notebook{{else}}/cloudbrain{{end}}{{else if eq .JobType "TRAIN"}}{{if eq .Cloudbrain.Type 1}}/modelarts/train-job{{else if eq .Cloudbrain.Type 0}}/cloudbrain/train-job{{else if eq .Cloudbrain.Type 2}}/grampus/train-job{{end}}{{else if eq .JobType "INFERENCE"}}{{if eq .Cloudbrain.Type 0}}/cloudbrain/train-job{{end}}{{end}}/{{$JobID}}/del?isadminpage=true'
method="post">
{{$.CsrfTokenHtml}}
<a style="padding: 0.5rem 1rem;margin-left:0.2rem" id="ai-delete-{{$JobID}}"


+ 2
- 0
templates/admin/user/list.tmpl View File

@@ -20,6 +20,7 @@
<th>{{.i18n.Tr "admin.users.name"}}</th>
<th>{{.i18n.Tr "email"}}</th>
<th>{{.i18n.Tr "admin.users.activated"}}</th>
<th>{{.i18n.Tr "admin.users.bind_phone"}}</th>
<th>{{.i18n.Tr "admin.users.admin"}}</th>
<th>{{.i18n.Tr "admin.users.restricted"}}</th>
<th>{{.i18n.Tr "admin.users.repos"}}</th>
@@ -35,6 +36,7 @@
<td><a href="{{AppSubUrl}}/{{.Name}}">{{.Name}}</a></td>
<td><span class="text truncate email">{{.Email}}</span></td>
<td><i class="fa fa{{if .IsActive}}-check{{end}}-square-o"></i></td>
<td><i class="fa fa{{if .PhoneNumber}}-check{{end}}-square-o"></i></td>
<td><i class="fa fa{{if .IsAdmin}}-check{{end}}-square-o"></i></td>
<td><i class="fa fa{{if .IsRestricted}}-check{{end}}-square-o"></i></td>
<td>{{.NumRepos}}</td>


+ 66
- 13
templates/base/footer.tmpl View File

@@ -51,23 +51,63 @@
<script src="/rotation3D/vue-2.6.10.min.js"></script>
<script src="/rotation3D/rotation3D.js?v={{MD5 AppVer}}"></script>
<script>
var jobTask={};
function queryAiCenterInfo(){
$.ajax({
type:"GET",
url:"/api/v1/cloudbrain/get_newest_job",
headers: {
authorization:token,
},
dataType:"json",
async:false,
success:function(json){
for(var i=0;i < json.length;i++){
jobTask[json[i].ai_center_id] =json[i].job_name;
}
},
error:function(response) {
console.log("query task info error.");
}
});

$.ajax({
type:"GET",
url:"/api/v1/cloudbrain/get_center_info",
headers: {
authorization:token,
},
dataType:"json",
async:false,
success:function(json){
displayAiCenterInfo(json);
},
error:function(response) {
}
});
}
function displayAiCenterInfo(json){
for(var i=0;i<json.length;i++){
var tmp ={};
tmp["name"]=json[i].name;
if(jobTask[json[i].id] != null){
tmp["type"]="blue";
}else{
tmp["type"]="green";
}
tmp["icon"]="";
tmp["content"]=json[i].content;
serverItemList.push(tmp);
}
}

var serverItemList=[];
queryAiCenterInfo();
var app = new Vue({
el: "#app",
//数据 blue, green, yellow
data: {
itemList: [
{ name:'鹏城云脑一号', type:'blue', icon:'', },
{ name:'鹏城云脑二号', type:'blue', icon:'', },
{ name:'北大人工智能集群系统', type:'green', icon:'', },
{ name:'合肥类脑智能开放平台', type:'green', icon:'', },
{ name:'武汉人工智能计算中心', type:'green', icon:'', },
{ name:'西安未来人工智能计算中心', type:'green', icon:'', },
{ name:'……', type:'yellow', icon:'', },
{ name:'中原人工智能计算中心', type:'green', icon:'', },
{ name:'成都人工智能计算中心', type:'green', icon:'', },
{ name:'横琴先进智能计算中心', type:'green', icon:'', },
{ name:'国家超级计算济南中心', type:'green', icon:'', },
],
itemList:serverItemList,
},
mounted: function () {
new Rotation3D({
@@ -84,6 +124,19 @@
},
methods: {},
});
$(document).ready(function(){
var pArrays=$('.itemList').find("p");
for(var i=0;i<pArrays.length;i++){
var p = pArrays[i];
p.innerText=serverItemList[i].content;
}
var lines=$('.lineList').find("span");
for(var i=0; i< lines.length;i++){
if(jobTask[i+1] != null){
lines[i].innerText = jobTask[i+1];
}
}
});
</script>
{{end}}



+ 5
- 4
templates/custom/select_dataset_train.tmpl View File

@@ -1,19 +1,20 @@
<div class="dataset-repolink" id="dataset-repolink-init" style="display: none;" data-repolink="{{.RepoLink}}"
data-dataset-type="{{.datasetType}}"></div>
<div class="inline required min_title field" id="dataset-base" style="margin-bottom: 0 !important;">
{{if or (.benchmarkMode) (.newInference)}}
<!-- {{if .benchmarkMode}}
<label
style="font-weight: normal;">{{if .benchmarkMode}}{{.i18n.Tr "repo.model_manager"}}</label><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>{{else}}{{.i18n.Tr "dataset.dataset"}}</label>&nbsp;&nbsp;&nbsp;&nbsp;{{end}}
{{else}}
<label class="label-fix-width" style="font-weight: normal;">{{.i18n.Tr "dataset.dataset"}}</label>
{{end}}
{{end}} -->
<label class="label-fix-width" style="font-weight: normal;">{{if .benchmarkMode}}{{.i18n.Tr "repo.model_manager"}}{{else}}{{.i18n.Tr "dataset.dataset"}}{{end}}</label>
<input type="hidden" name="attachment" :value="dataset_uuid">
<input class="disabled" type="text" :value="dataset_name" required onfocus="this.blur();" style="width: 48.5%;">
<el-button type="text" @click="dialogVisible = true" icon="el-icon-plus" style="color: #0366d6;">
{{if .benchmarkMode}}{{.i18n.Tr "repo.modelarts.infer_job.select_model"}}{{else}}{{.i18n.Tr "dataset.select_dataset"}}{{end}}
</el-button>
{{if .benchmarkMode}}
<span class="tooltips" style="display: block;padding-left: 0.5rem;">说明:先使用数据集功能上传模型,然后从数据集列表选模型。</span>
<span class="tooltips" style="display: block;margin-left:11.5rem;">说明:先使用数据集功能上传模型,然后从数据集列表选模型。</span>
{{end}}
<el-dialog title="{{.i18n.Tr "dataset.select_dataset"}}" :visible.sync="dialogVisible" width="50%">
<div v-loading="loadingDataIndex" style="position: relative;">


+ 27
- 0
templates/custom/wait_count.tmpl View File

@@ -0,0 +1,27 @@
<div class="tooltip-wati-count">
{{$queue := ""}}
{{$gpuQueue := 0}}
{{range $k,$v :=.gpu_types}}
{{if eq $k 0}}
{{ $queue = $v.Queue }}
{{ end }}
{{ end }}
{{ range $k,$v :=.QueuesDetail }}
{{if eq $k $queue}}
{{$gpuQueue =$v}}
{{ end }}
{{ end }}
<i
class="ri-error-warning-line"
style="margin-right: 0.5rem; font-size: 16px"
></i>
<span id="gpu-nums"
>{{.i18n.Tr "repo.wait_count_start"}}
{{if .QueuesDetail}}
{{ $gpuQueue }}
{{else}}
{{.WaitCount}}
{{ end }}
{{.i18n.Tr "repo.wait_count_end"}}</span
>
</div>

+ 28
- 0
templates/custom/wait_count_train.tmpl View File

@@ -0,0 +1,28 @@
<div class="tooltip-wati-count" style="margin-left: 155px">
{{$queue := ""}}
{{$gpuQueue := 0}}
{{range $k,$v :=.type}}
{{if eq $k 0}}
{{ $queue = $v.Queue }}
{{ end }}
{{ end }}

{{ range $k,$v :=.ctx.QueuesDetail }}
{{if eq $k $queue}}
{{$gpuQueue =$v}}
{{ end }}
{{ end }}
<i
class="ri-error-warning-line"
style="margin-right: 0.5rem; font-size: 16px"
></i>
<span id="gpu-nums"
>{{.ctx.i18n.Tr "repo.wait_count_start"}}
{{if .type}}
{{ $gpuQueue }}
{{else}}
{{.ctx.WaitCount}}
{{ end }}
{{.ctx.i18n.Tr "repo.wait_count_end"}}</span
>
</div>

+ 2
- 8
templates/home.tmpl View File

@@ -94,12 +94,6 @@
</div>

<div id="app" v-cloak>
<!--数据
<div class="aiData">
<p>完成AI任务<br><strong id="completed_task">1716</strong></p>
<p>运行AI任务<br><strong id="running_task">120</strong></p>
<p>等待AI任务<br><strong id="wait_task">80</strong></p>
</div>-->
<!--底座-->
<div class="rotation3D-baseMap"></div>
<!--旋转3D-->
@@ -129,13 +123,13 @@
<svg width="10" height="400">
<path id="path2" d="M0 400, 0 0" stroke-dasharray="5,10"/>
</svg>
<div class="dot dot2"><i class="el-icon-close"></i></div>
<div class="dot dot2"><i class="el-icon-close"></i><span></span></div>
</div>
<div v-if="item.type=='green'" class="pos">
<svg width="50" height="400">
<path id="path1" d="M0 400, 0 0" stroke-dasharray="5,10"/>
</svg>
<div class="dot dot1 ri-arrow-left-s-line"></div>
<div class="dot dot1 ri-arrow-left-s-line"><span></span></div>
</div>
</div>
</div>


+ 92
- 0
templates/privacy.tmpl View File

@@ -0,0 +1,92 @@
{{template "base/head_home" .}}
<style>
.text-indent {
text-indent: 2em;
}
</style>
<div class="ui container">
<h1 class="ui center am-pt-30 am-pb-20">OpenI启智社区AI协作平台隐私协议</h1>
<div class="ui divider am-pb-10"></div>
<p class="text-indent">
OpenI启智社区AI协作平台作为新一代人工智能领域开源开放开发协作平台,不仅为用户提供代码托管与数据集管理等服务,同时提供开发者所需的计算算力资源,一个良好的开发环境对用户、组织和项目都尤为重要。
</p>
<p class="text-indent">
为切实保护平台用户隐私权,优化用户体验,OpenI启智社区根据现行法规及政策,制定本《OpenI启智社区AI协作平台隐私协议》。本协议将详细说明OpenI启智社区AI协作平台在获取、管理及保护用户个人信息方面的政策及措施。本协议适用于OpenI启智社区AI协作平台向您提供的所有服务,无论您是通过计算机设备、移动终端或其他设备获得本平台的服务。
</p>
<p>
<strong>一、个人信息的收集</strong>
</p>
<p class="text-indent">
您已知悉且同意,在您注册本平台帐号或使用平台提供的服务时,本平台将记录您提供的相关个人信息,如:账号ID、密码、邮箱、手机号码等,上述个人信息是您获得平台提供服务的基础。
</p>
<p class="text-indent">
在您使用本平台前,我们会引导您阅读本协议,并在您接受本协议的基础上,获得您的相关个人信息。如果您不同意提供个人信息,您将无法使用本平台的全部或部分功能和服务。
</p>
<p>
<strong>二、个人信息的存储</strong>
</p>
<p class="text-indent">
本平台会采取合适的安全措施和技术手段存储及保护您的个人信息,以防止丢失、被误用、受到未授权访问或泄漏、被篡改或毁坏。您的个人信息存放在有密码控制的位于中国境内的服务器中,访问均是受到限制的。
</p>
<p class="text-indent">
根据本条款的规定,我们仅允许有必要知晓这些信息的平台员工等第三方访问个人信息,并要求他们履行相应的保密义务。
</p>
<p>
<strong>三、个人信息的管理</strong>
</p>
<p class="text-indent">
在完成平台账号注册并进行身份验证后,您可以查阅、修改、删除所提交的个人信息。一般情况下,您可随时浏览、修改、删除自己提交的信息,但出于安全性和身份识别的考虑,您可能无法修改注册时提供的某些初始注册信息及验证信息。
</p>
<p class="text-indent">
您有权自主更新或更正您的个人信息,或授权本平台工作人员进行信息更新、更正。在您进行信息更新或更正之前,我们会首先验证您的身份。
</p>
<p class="text-indent">
您可以自主注销本平台账号,注销后本平台不再收集您的个人信息,注销账号是不可逆的行为,一旦完成注销,本平台将删除有关您账户的一切信息。
</p>
<p>
<strong>四、个人信息的保护</strong>
</p>
<p class="text-indent">
本平台将尽一切合理努力保护其获得的用户个人信息。为防止用户个人信息在意外的、未经授权的情况下被非法访问、复制、修改、传送、遗失、破坏、处理或使用,本平台已经并将继续采取以下措施保护您的个人信息:<br>
</p>
<ol style="text-indent:0.5em;">
<li>以适当的方式对用户的个人信息进行加密护理,并通过隔离技术进行隔离;</li>
<li>在适当的位置使用密码对用户个人信息进行保护;</li>
<li>设立严格的数据使用和访问制度,采用严格的数据访问权限控制保护个人信息;</li>
<li>其他合理措施。</li>
</ol>
<p class="text-indent">
尽管已经采取了上述合理有效措施,并遵守相关法律规定要求的标准,但本平台仍然无法保证您的个人信息通过不安全途径进行交流时的安全性。因此,用户个人应采取积极措施保证个人信息的安全,如:定期修改账号密码,不将自己的账号密码等个人信息透露给他人。
</p>
<p class="text-indent">
您知悉,本平台提供的个人信息保护措施仅适用于OpenI启智社区AI协作平台,一旦您离开本平台,浏览或使用其他网站、服务及内容资源,本平台即没有能力及义务保护您在本平台以外的网站提交的任何个人信息,无论您登录或浏览上述网站是否基于本平台的链接或引导。
</p>
<p>
<strong>五、个人信息的使用和对外提供</strong>
</p>
<p class="text-indent">
未经您本人允许,平台不会向任何第三方公开您的个人信息,下列情形除外:
</p>
<ol style="text-indent:0.5em;">
<li>本平台已经取得您或您监护人的授权或同意;</li>
<li>司法机关或行政机关基于法定程序要求平台披露的;</li>
<li>本平台为维护自身合法权益而向用户提起诉讼或仲裁时;</li>
<li>根据您与本平台相关服务条款、应用许可使用协议的约定;</li>
<li>在法律允许的范围内,为保障本平台、平台用户以及社会公共利益免受损害时;</li>
<li>法律法规规定的其他情形。</li>
</ol>
<p>
<strong>六、对未成年人个人信息的特别保护</strong>
</p>
<p class="text-indent">
本平台非常重视对未成年人个人信息的保护。若您是18周岁以下的未成年人,在使用本平台的服务前,应确保事先取得监护人的同意,如您在平台上申请注册账号,本平台将默认为您已得到前述同意。本平台将根据国家相关法律法规及本协议的规定保护未成年人的个人信息。
</p>
<p>
<strong>七、隐私协议的修改</strong>
</p>
<p class="text-indent">
平台有权随时修改《OpenI启智社区AI协作平台隐私协议》的任何条款,一旦《OpenI启智社区AI协作平台隐私协议》内容发生变动,本平台将会在OpenI启智社区网站首页公告上公布修改后的政策,该公布行为视为本平台已经通知您修改内容。如果您不同意本平台对《OpenI启智社区AI协作平台隐私协议》相关条款所做的修改,您有权停止使用平台服务。如果您继续使用平台服务,则视为您接受平台对协议相关条款所做的修改。
</p>

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

+ 1
- 1
templates/repo/cloudbrain/benchmark/index.tmpl View File

@@ -64,7 +64,7 @@
<div class="dataset list">

<!-- 表头 -->
<div class="ui grid stackable" style="background: #f0f0f0;;">
<div class="ui grid stackable" style="background: #f0f0f0;">
<div class="row">
<div class="three wide column padding0" style="width: 18% !important;">
<span style="margin:0 6px">{{$.i18n.Tr "repo.cloudbrain_task"}}</span>


+ 37
- 38
templates/repo/cloudbrain/benchmark/new.tmpl View File

@@ -10,11 +10,9 @@
padding-left: 3rem !important;
}

.min_title {
.min_title{
font-size: 14px !important;
padding-left: 6rem !important;
margin-bottom: 2rem !important;

}

.width81 {
@@ -56,25 +54,26 @@
<form class="ui form model_form" action="{{.Link}}?benchmarkMode=model" method="post">
{{.CsrfTokenHtml}}
<input type="hidden" name="action" value="update">
<div class="required unite min_title inline field">
<label style="font-weight: normal;">{{.i18n.Tr "repo.cloudbrain.benchmark.evaluate_scenes"}}</label>
<div class="required min_title inline field">
<label class="label-fix-width" style="font-weight: normal;">{{.i18n.Tr "repo.cloudbrain.benchmark.evaluate_scenes"}}</label>
<div class="ui blue small menu compact selectcloudbrain">
<a class="item alogrithm_benchmark"
href="{{.Link}}?benchmarkMode=alogrithm">{{.i18n.Tr "repo.cloudbrain.benchmark.algorithm"}}</a>
<a class="active item model_benchmark"
href="{{.Link}}?benchmarkMode=model">{{.i18n.Tr "repo.cloudbrain.benchmark.model"}}</a>
</div>
{{template "custom/wait_count_train" Dict "ctx" $ "type" .benchmark_gpu_types}}
</div>
<div>
<div class="required unite min_title inline field">
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.job_name"}}</label>
<div class="required min_title inline field">
<label class="label-fix-width" style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.job_name"}}</label>
<input style="width: 80%;" name="display_job_name" id="trainjob_job_name"
placeholder={{.i18n.Tr "repo.modelarts.train_job.job_name"}} value="{{.display_job_name}}"
tabindex="3" autofocus required maxlength="254">
<span class="tooltips" style="display: block;">{{.i18n.Tr "cloudbrain.job_name_rule"}}</span>
<span class="tooltips" style="display: block;margin-left:11.5rem;">{{.i18n.Tr "cloudbrain.job_name_rule"}}</span>
</div>
<div class="required unite min_title inline field">
<label style="font-weight: normal;"
<div class="required min_title inline field">
<label class="label-fix-width" style="font-weight: normal;"
for="description">{{.i18n.Tr "repo.modelarts.train_job.description"}}</label>
<textarea style="width: 80%;" id="description" name="description" rows="3" required
maxlength="254" placeholder={{.i18n.Tr "repo.modelarts.train_job.new_place"}}
@@ -83,8 +82,8 @@
onkeyup="this.value=this.value.substring(0, 255)">{{.description}}</textarea>
</div>

<div class="required unite min_title inline field">
<label style="font-weight: normal;">{{.i18n.Tr "cloudbrain.gpu_type"}}</label>
<div class="required min_title inline field">
<label class="label-fix-width" style="font-weight: normal;">{{.i18n.Tr "cloudbrain.gpu_type"}}</label>
<select id="cloudbrain_gpu_type" class="ui search dropdown width48" placeholder="选择GPU类型"
name="gpu_type">
{{range .benchmark_gpu_types}}
@@ -92,9 +91,9 @@
{{end}}
</select>
</div>
<div class="required unite min_title two inline fields ">
<div class="required unite min_title two inline fields" style="margin-left: 80px;">
<div class="required ten wide field" style="width: 26.5% !important;">
<label style="font-weight: normal;">{{.i18n.Tr "cloudbrain.task_type"}}</label>
<label style="font-weight: normal;">{{.i18n.Tr "cloudbrain.task_type"}}</label>&nbsp;
<select id="cloudbrain_job_type" class="ui search dropdown job_type"
placeholder="select {{.i18n.Tr "cloudbrain.task_type"}}" name="job_type">
<option value="SNN4IMAGENET">SNN4IMAGENET</option>
@@ -124,8 +123,8 @@
<div id="images-new-cb">
</div>
{{template "custom/select_dataset_train" .}}
<div class="required unite min_title inline field" style="margin-top:2rem;">
<label style="font-weight: normal;">{{.i18n.Tr "cloudbrain.resource_specification"}}</label>
<div class="required min_title inline field" style="margin-top:2rem;">
<label class="label-fix-width" style="font-weight: normal;">{{.i18n.Tr "cloudbrain.resource_specification"}}</label>
<select id="cloudbrain_resource_spec" class="ui search dropdown"
placeholder="{{.i18n.Tr "cloudbrain.select_specification"}}" style='width:385px'
name="resource_spec_id">
@@ -136,7 +135,8 @@
{{end}}
</select>
</div>
<div class="inline unite min_title field">
<div class="inline min_title field">
<label class="label-fix-width" style="font-weight: normal;"></label>
<button class="ui create_train_job green button">
{{.i18n.Tr "repo.cloudbrain.new"}}
</button>
@@ -149,27 +149,28 @@
{{.CsrfTokenHtml}}
<input type="hidden" name="action" value="update">
<input type="hidden" name="job_type" value="BENCHMARK">
<div class="required unite min_title inline field">
<label style="font-weight: normal;">{{.i18n.Tr "repo.cloudbrain.benchmark.evaluate_scenes"}}</label>
<div class="required min_title inline field">
<label class="label-fix-width" style="font-weight: normal;">{{.i18n.Tr "repo.cloudbrain.benchmark.evaluate_scenes"}}</label>
<div class="ui blue small menu compact selectcloudbrain">
<a class="active item alogrithm_benchmark"
href="{{.Link}}?benchmarkMode=alogrithm">{{.i18n.Tr "repo.cloudbrain.benchmark.algorithm"}}</a>
<a class="item model_benchmark"
href="{{.Link}}?benchmarkMode=model">{{.i18n.Tr "repo.cloudbrain.benchmark.model"}}</a>
</div>
{{template "custom/wait_count_train" Dict "ctx" $ "type" .benchmark_gpu_types}}
</div>

<div>
<div class="required unite min_title inline field">
<label style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.job_name"}}</label>
<div class="required min_title inline field">
<label class="label-fix-width" style="font-weight: normal;">{{.i18n.Tr "repo.modelarts.train_job.job_name"}}</label>
<input style="width: 80%;" name="display_job_name" id="trainjob_job_name"
placeholder={{.i18n.Tr "repo.modelarts.train_job.job_name"}} value="{{.display_job_name}}"
tabindex="3" autofocus required maxlength="254">
<span class="tooltips" style="display: block;">{{.i18n.Tr "cloudbrain.job_name_rule"}}</span>
<span class="tooltips" style="display: block;margin-left: 11.5rem;">{{.i18n.Tr "cloudbrain.job_name_rule"}}</span>
</div>
<div class="unite min_title inline field">
<label style="font-weight: normal;"
for="description">{{.i18n.Tr "repo.modelarts.train_job.description"}}&nbsp;&nbsp;</label>
<div class="min_title inline field">
<label class="label-fix-width" style="font-weight: normal;"
for="description">{{.i18n.Tr "repo.modelarts.train_job.description"}}</label>
<textarea style="width: 80%;" id="description" name="description" rows="3" maxlength="254"
placeholder={{.i18n.Tr "repo.modelarts.train_job.new_place"}}
onchange="this.value=this.value.substring(0, 255)"
@@ -177,8 +178,8 @@
onkeyup="this.value=this.value.substring(0, 255)">{{.description}}</textarea>
</div>

<div class="required unite min_title inline field">
<label style="font-weight: normal;">{{.i18n.Tr "cloudbrain.gpu_type"}}</label>
<div class="required min_title inline field">
<label class="label-fix-width" style="font-weight: normal;">{{.i18n.Tr "cloudbrain.gpu_type"}}</label>
<select id="cloudbrain_gpu_type" class="ui search dropdown" placeholder="选择GPU类型"
style='width:385px' name="gpu_type">
{{range .benchmark_gpu_types}}
@@ -186,11 +187,11 @@
{{end}}
</select>
</div>
<div class="required unite inline min_title fields" style="width: 90%;">
<div class="required unite inline min_title fields" style="width: 90%;margin-left: 5.7rem;">&nbsp;
<div class="required eight wide field">
<label
style="font-weight: normal;white-space: nowrap;">{{.i18n.Tr "repo.cloudbrain.benchmark.evaluate_type"}}</label>
<span>&nbsp;</span>
<select class="ui fluid selection search dropdown" id="benchmark_types_id"
name="benchmark_types_id">
{{range .benchmark_types}}
@@ -215,8 +216,8 @@
<div id="images-new-cb">
</div>

<div class="required unite min_title inline field">
<label style="font-weight: normal;">{{.i18n.Tr "cloudbrain.resource_specification"}}</label>
<div class="required min_title inline field">
<label class="label-fix-width" style="font-weight: normal;">{{.i18n.Tr "cloudbrain.resource_specification"}}</label>
<select id="cloudbrain_resource_spec" class="ui search dropdown"
placeholder="{{.i18n.Tr "cloudbrain.select_specification"}}" style='width:385px'
name="resource_spec_id">
@@ -228,18 +229,16 @@
</select>
</div>

<div class="inline unite min_title field required">
<label
style="font-weight: normal;">{{.i18n.Tr "repo.cloudbrain.benchmark.evaluate_train"}}</label>
<div class="inline min_title field required">
<label class="label-fix-width" style="font-weight: normal;">{{.i18n.Tr "repo.cloudbrain.benchmark.evaluate_train"}}</label>
<input disabled="disabled" style="width: 33.5%;" name="train_file" id="train_file"
value="train.py" tabindex="3" autofocus required maxlength="254">
<a id="train_href_id" href="https://git.openi.org.cn/CV_benchmark/CV_reID_benchmark"
target="_blank">{{.i18n.Tr "repo.cloudbrain.benchmark.evaluate_train"}}</a>
</div>

<div class="inline unite min_title field required">
<label
style="font-weight: normal;">{{.i18n.Tr "repo.cloudbrain.benchmark.evaluate_test"}}</label>
<div class="inline min_title field required">
<label class="label-fix-width" style="font-weight: normal;">{{.i18n.Tr "repo.cloudbrain.benchmark.evaluate_test"}}</label>
<input disabled="disabled" style="width: 33.5%;" name="test_file" id="test_file" value="test.py"
tabindex="3" autofocus required maxlength="254">
<a id="test_href_id" href="https://git.openi.org.cn/CV_benchmark/CV_reID_benchmark"
@@ -248,6 +247,7 @@


<div class="inline unite min_title field">
<label class="label-fix-width" style="font-weight: normal;"></label>
<button class="ui create_train_job green button">
{{.i18n.Tr "repo.cloudbrain.new"}}
</button>
@@ -273,7 +273,6 @@
$(document).ready(() => {
$('.ui.search.dropdown.job_type').dropdown({
onChange: function (value, text, $selectedItem) {
console.log(value, text)
if (value === "BRAINSCORE") {
$('#brainscore_child_type').css('display', 'block')
$('#benchmark_model_example').attr('href', 'https://git.openi.org.cn/BDIP/similarity2brain_ann')


+ 1
- 6
templates/repo/cloudbrain/benchmark/show.tmpl View File

@@ -232,12 +232,7 @@
<span>
<div class="ac-display-inblock title_text acc-margin-bottom">
<span class="cti-mgRight-sm">
{{if not (eq .StartTime 0)}}
<td>{{TimeSinceUnix1 .StartTime}}</td>
{{else}}
<td>{{TimeSinceUnix1 .CreatedUnix}}
<td>
{{end}}
<td>{{TimeSinceUnix1 .CreatedUnix}}</td>
</span>

<span class="cti-mgRight-sm">{{$.i18n.Tr "repo.modelarts.status"}}:


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

Loading…
Cancel
Save