diff --git a/custom/public/rotation3D/rotation3D.css b/custom/public/rotation3D/rotation3D.css
index 032096b48..4fd12283c 100755
--- a/custom/public/rotation3D/rotation3D.css
+++ b/custom/public/rotation3D/rotation3D.css
@@ -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; }
diff --git a/go.mod b/go.mod
index c1e959f8e..387a34520 100755
--- a/go.mod
+++ b/go.mod
@@ -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
diff --git a/go.sum b/go.sum
index d9dc14b64..d55d7af48 100755
--- a/go.sum
+++ b/go.sum
@@ -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=
diff --git a/index.html b/index.html
new file mode 100644
index 000000000..643c31b06
--- /dev/null
+++ b/index.html
@@ -0,0 +1,688 @@
+
+
+
+
+
+
+ OpenI
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
This website works better with JavaScript.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Recommended Organizations
+
These excellent organizations are using the OpenI AI Collaboration Platform for collaborative development of projects. To show your organization here, Click here to submit.
+
More Organizations
+
+
+
+
+
Community Activities
+
The community has prepared a wealth of activities, waiting for you to participate!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Collaborative Development Environment
+
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.
+
+
+
+
+
+
+
+
+
+ The platform provides four elements of AI development: unified management of model code, data set, model and execution environment.
+
+
+
+
+
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+
+
+
+
+ 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.
+
+
+
+
+
+
+
+
+
+
+ Provide execution environment sharing, Once Configuration, Multiple Reuse. Lower the threshold of model development, and avoid spending repetitive time configuring complex environments.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ The platform has been connected with Pengcheng Cloudbrain and can use the rich computing resources of Pengcheng Cloudbrain to complete AI development tasks.
+ Pengcheng Cloudbrain'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.
+ 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.
+ If your model requires more computing resources, you can also apply for it separately.
+
+
+
+
Use Now
+
+
+
Apply Separately
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Copyright: New Generation Artificial Intelligence Open Source Open Platform (OpenI)
京ICP备18004880号
+
+ Powered_by 鹏城实验室云脑、
Trustie确实 、gitea
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/models/ai_model_manage.go b/models/ai_model_manage.go
index ed696fcf0..0ea01d6e5 100644
--- a/models/ai_model_manage.go
+++ b/models/ai_model_manage.go
@@ -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
+}
diff --git a/models/cloudbrain.go b/models/cloudbrain.go
index 85338d2bc..af53bad32 100755
--- a/models/cloudbrain.go
+++ b/models/cloudbrain.go
@@ -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)
+}
diff --git a/models/custom_migrations.go b/models/custom_migrations.go
index 65b53f0f4..041c4fb93 100755
--- a/models/custom_migrations.go
+++ b/models/custom_migrations.go
@@ -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);"
diff --git a/models/login_source.go b/models/login_source.go
index ca2aa5a4d..5641e5c59 100755
--- a/models/login_source.go
+++ b/models/login_source.go
@@ -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 {
diff --git a/models/models.go b/models/models.go
index 9d255c5e6..b714f4650 100755
--- a/models/models.go
+++ b/models/models.go
@@ -144,6 +144,7 @@ func init() {
new(WechatBindLog),
new(OrgStatistic),
new(SearchRecord),
+ new(AiModelConvert),
)
tablesStatistic = append(tablesStatistic,
diff --git a/models/repo_activity_custom.go b/models/repo_activity_custom.go
index cbe00b9d9..6e7921d75 100644
--- a/models/repo_activity_custom.go
+++ b/models/repo_activity_custom.go
@@ -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")
diff --git a/models/user.go b/models/user.go
index dd5a6f1d2..a423a843b 100755
--- a/models/user.go
+++ b/models/user.go
@@ -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 {
diff --git a/models/user_analysis_for_activity.go b/models/user_analysis_for_activity.go
new file mode 100644
index 000000000..e69eecae0
--- /dev/null
+++ b/models/user_analysis_for_activity.go
@@ -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
+}
diff --git a/models/user_business_analysis.go b/models/user_business_analysis.go
index e058c0df8..cb503d669 100644
--- a/models/user_business_analysis.go
+++ b/models/user_business_analysis.go
@@ -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
diff --git a/models/user_business_struct.go b/models/user_business_struct.go
index 870a64bc7..36ef077e2 100644
--- a/models/user_business_struct.go
+++ b/models/user_business_struct.go
@@ -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 {
diff --git a/modules/auth/cloudbrain.go b/modules/auth/cloudbrain.go
index e5be38084..160328b5b 100755
--- a/modules/auth/cloudbrain.go
+++ b/modules/auth/cloudbrain.go
@@ -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)
+}
diff --git a/modules/auth/user_form.go b/modules/auth/user_form.go
index 86771b9f8..130586a5a 100755
--- a/modules/auth/user_form.go
+++ b/modules/auth/user_form.go
@@ -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)
+}
diff --git a/modules/cloudbrain/cloudbrain.go b/modules/cloudbrain/cloudbrain.go
index 430304dd5..6cbb97999 100755
--- a/modules/cloudbrain/cloudbrain.go
+++ b/modules/cloudbrain/cloudbrain.go
@@ -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
+}
diff --git a/modules/cloudbrain/resty.go b/modules/cloudbrain/resty.go
index e70dbdd2b..7b714c4b5 100755
--- a/modules/cloudbrain/resty.go
+++ b/modules/cloudbrain/resty.go
@@ -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()
diff --git a/modules/context/auth.go b/modules/context/auth.go
index 287823dea..efde3bc2e 100755
--- a/modules/context/auth.go
+++ b/modules/context/auth.go
@@ -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 {
diff --git a/modules/grampus/grampus.go b/modules/grampus/grampus.go
index e83ccccc6..caaae4e8e 100755
--- a/modules/grampus/grampus.go
+++ b/modules/grampus/grampus.go
@@ -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
diff --git a/modules/modelarts/modelarts.go b/modules/modelarts/modelarts.go
index 79aeb4cb0..1f39d0fac 100755
--- a/modules/modelarts/modelarts.go
+++ b/modules/modelarts/modelarts.go
@@ -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,
diff --git a/modules/modelarts/resty.go b/modules/modelarts/resty.go
index 6a2803cb1..46c273a8b 100755
--- a/modules/modelarts/resty.go
+++ b/modules/modelarts/resty.go
@@ -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()
diff --git a/modules/phone/phone.go b/modules/phone/phone.go
new file mode 100644
index 000000000..9349a75ec
--- /dev/null
+++ b/modules/phone/phone.go
@@ -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
+}
diff --git a/modules/public/dynamic.go b/modules/public/dynamic.go
index 07f83e1f6..4f10895a1 100644
--- a/modules/public/dynamic.go
+++ b/modules/public/dynamic.go
@@ -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)
+}
diff --git a/modules/public/static.go b/modules/public/static.go
index 76050632c..6eac8a751 100644
--- a/modules/public/static.go
+++ b/modules/public/static.go
@@ -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))
diff --git a/modules/redis/redis_client/client.go b/modules/redis/redis_client/client.go
index 437aecdae..ebcff4930 100644
--- a/modules/redis/redis_client/client.go
+++ b/modules/redis/redis_client/client.go
@@ -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()
diff --git a/modules/setting/phone.go b/modules/setting/phone.go
new file mode 100644
index 000000000..16b4ebf19
--- /dev/null
+++ b/modules/setting/phone.go
@@ -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")
+}
diff --git a/modules/setting/setting.go b/modules/setting/setting.go
index 2058c51a8..6ec54fdff 100755
--- a/modules/setting/setting.go
+++ b/modules/setting/setting.go
@@ -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()
}
diff --git a/modules/setting/slideimage.go b/modules/setting/slideimage.go
new file mode 100644
index 000000000..c68beb185
--- /dev/null
+++ b/modules/setting/slideimage.go
@@ -0,0 +1,12 @@
+package setting
+
+import (
+ "image"
+)
+
+var (
+ // the original images for generate slide image
+ SlideImagesBg []*image.Image
+ SlideMaskImage *image.Image
+ SlideImagesCount int
+)
diff --git a/modules/slideimage/slideimage.go b/modules/slideimage/slideimage.go
new file mode 100644
index 000000000..748cc2d6c
--- /dev/null
+++ b/modules/slideimage/slideimage.go
@@ -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")
+
+ }
+}
diff --git a/modules/storage/minio_ext.go b/modules/storage/minio_ext.go
index 514ac7204..4ad83da82 100755
--- a/modules/storage/minio_ext.go
+++ b/modules/storage/minio_ext.go
@@ -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
diff --git a/modules/storage/obs.go b/modules/storage/obs.go
index 33730b72c..29b7998f7 100755
--- a/modules/storage/obs.go
+++ b/modules/storage/obs.go
@@ -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
}
diff --git a/modules/templates/helper.go b/modules/templates/helper.go
index dbb9354aa..857e365f8 100755
--- a/modules/templates/helper.go
+++ b/modules/templates/helper.go
@@ -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
+}
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 39a892cc7..b641f4011 100755
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -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 use agreement and privacy agreement
+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 (%s ). 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
\ No newline at end of file
+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
diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini
index b9acb6ec0..87630d6c0 100755
--- a/options/locale/locale_zh-CN.ini
+++ b/options/locale/locale_zh-CN.ini
@@ -32,6 +32,8 @@ captcha=验证码
twofa=两步验证
twofa_scratch=两步验证口令
passcode=验证码
+use_and_privacy_agree = 我已阅读并同意 使用协议 和 隐私协议
+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 您好,系统检测到您有一封发送至 %s 但未被确认的邮件。如果您未收到激活邮件,或需要重新发送,请单击下方的按钮。
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 = 数据集选择错误:数量超过限制或者有同名数据集
\ No newline at end of file
+Delete_failed=任务删除失败,请稍后再试。
+Not_Stopped=任务还未终止,不能删除。
+Already_stopped=任务已停止。
+Stopped_failed=任务停止失败,请稍后再试。
+Stopped_success_update_status_fail=任务停止成功,状态及运行时间更新失败。
+
+
+error.dataset_select = 数据集选择错误:数量超过限制或者有同名数据集
diff --git a/public/home/home.js b/public/home/home.js
index 95ea3da4c..70b9d7253 100755
--- a/public/home/home.js
+++ b/public/home/home.js
@@ -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 += "";
diff --git a/public/img/slide/bg/1.png b/public/img/slide/bg/1.png
new file mode 100644
index 000000000..d76aa3725
Binary files /dev/null and b/public/img/slide/bg/1.png differ
diff --git a/public/img/slide/bg/2.png b/public/img/slide/bg/2.png
new file mode 100644
index 000000000..73536b138
Binary files /dev/null and b/public/img/slide/bg/2.png differ
diff --git a/public/img/slide/bg/3.png b/public/img/slide/bg/3.png
new file mode 100644
index 000000000..530e5b5c0
Binary files /dev/null and b/public/img/slide/bg/3.png differ
diff --git a/public/img/slide/bg/4.png b/public/img/slide/bg/4.png
new file mode 100644
index 000000000..c9b0127f0
Binary files /dev/null and b/public/img/slide/bg/4.png differ
diff --git a/public/img/slide/mask/mask.png b/public/img/slide/mask/mask.png
new file mode 100644
index 000000000..562f75b05
Binary files /dev/null and b/public/img/slide/mask/mask.png differ
diff --git a/public/self/ztree/css/awesomeStyle/awesome.css b/public/self/ztree/css/awesomeStyle/awesome.css
new file mode 100644
index 000000000..5fa82249a
--- /dev/null
+++ b/public/self/ztree/css/awesomeStyle/awesome.css
@@ -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;
+}
diff --git a/public/self/ztree/css/awesomeStyle/awesome.less b/public/self/ztree/css/awesomeStyle/awesome.less
new file mode 100644
index 000000000..bf8508e4d
--- /dev/null
+++ b/public/self/ztree/css/awesomeStyle/awesome.less
@@ -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}
+
diff --git a/public/self/ztree/css/awesomeStyle/fa.less b/public/self/ztree/css/awesomeStyle/fa.less
new file mode 100644
index 000000000..3714884a7
--- /dev/null
+++ b/public/self/ztree/css/awesomeStyle/fa.less
@@ -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";
+
diff --git a/public/self/ztree/css/awesomeStyle/img/loading.gif b/public/self/ztree/css/awesomeStyle/img/loading.gif
new file mode 100644
index 000000000..e8c289293
Binary files /dev/null and b/public/self/ztree/css/awesomeStyle/img/loading.gif differ
diff --git a/public/self/ztree/css/demo.css b/public/self/ztree/css/demo.css
new file mode 100644
index 000000000..f6dba0de8
--- /dev/null
+++ b/public/self/ztree/css/demo.css
@@ -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}
\ No newline at end of file
diff --git a/public/self/ztree/css/metroStyle/img/line_conn.png b/public/self/ztree/css/metroStyle/img/line_conn.png
new file mode 100644
index 000000000..b211da2fa
Binary files /dev/null and b/public/self/ztree/css/metroStyle/img/line_conn.png differ
diff --git a/public/self/ztree/css/metroStyle/img/loading.gif b/public/self/ztree/css/metroStyle/img/loading.gif
new file mode 100644
index 000000000..e8c289293
Binary files /dev/null and b/public/self/ztree/css/metroStyle/img/loading.gif differ
diff --git a/public/self/ztree/css/metroStyle/img/metro.gif b/public/self/ztree/css/metroStyle/img/metro.gif
new file mode 100644
index 000000000..664b969a2
Binary files /dev/null and b/public/self/ztree/css/metroStyle/img/metro.gif differ
diff --git a/public/self/ztree/css/metroStyle/img/metro.png b/public/self/ztree/css/metroStyle/img/metro.png
new file mode 100644
index 000000000..e9e58a3a7
Binary files /dev/null and b/public/self/ztree/css/metroStyle/img/metro.png differ
diff --git a/public/self/ztree/css/metroStyle/metroStyle.css b/public/self/ztree/css/metroStyle/metroStyle.css
new file mode 100644
index 000000000..af81f4239
--- /dev/null
+++ b/public/self/ztree/css/metroStyle/metroStyle.css
@@ -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}
diff --git a/public/self/ztree/css/zTreeStyle/img/diy/1_close.png b/public/self/ztree/css/zTreeStyle/img/diy/1_close.png
new file mode 100644
index 000000000..68ccb3c3b
Binary files /dev/null and b/public/self/ztree/css/zTreeStyle/img/diy/1_close.png differ
diff --git a/public/self/ztree/css/zTreeStyle/img/diy/1_open.png b/public/self/ztree/css/zTreeStyle/img/diy/1_open.png
new file mode 100644
index 000000000..d6ff36d3a
Binary files /dev/null and b/public/self/ztree/css/zTreeStyle/img/diy/1_open.png differ
diff --git a/public/self/ztree/css/zTreeStyle/img/diy/2.png b/public/self/ztree/css/zTreeStyle/img/diy/2.png
new file mode 100644
index 000000000..9eff506ba
Binary files /dev/null and b/public/self/ztree/css/zTreeStyle/img/diy/2.png differ
diff --git a/public/self/ztree/css/zTreeStyle/img/diy/3.png b/public/self/ztree/css/zTreeStyle/img/diy/3.png
new file mode 100644
index 000000000..d7ba6d0c6
Binary files /dev/null and b/public/self/ztree/css/zTreeStyle/img/diy/3.png differ
diff --git a/public/self/ztree/css/zTreeStyle/img/diy/4.png b/public/self/ztree/css/zTreeStyle/img/diy/4.png
new file mode 100644
index 000000000..753e2bfd5
Binary files /dev/null and b/public/self/ztree/css/zTreeStyle/img/diy/4.png differ
diff --git a/public/self/ztree/css/zTreeStyle/img/diy/5.png b/public/self/ztree/css/zTreeStyle/img/diy/5.png
new file mode 100644
index 000000000..0c5eccd56
Binary files /dev/null and b/public/self/ztree/css/zTreeStyle/img/diy/5.png differ
diff --git a/public/self/ztree/css/zTreeStyle/img/diy/6.png b/public/self/ztree/css/zTreeStyle/img/diy/6.png
new file mode 100644
index 000000000..070b8352d
Binary files /dev/null and b/public/self/ztree/css/zTreeStyle/img/diy/6.png differ
diff --git a/public/self/ztree/css/zTreeStyle/img/diy/7.png b/public/self/ztree/css/zTreeStyle/img/diy/7.png
new file mode 100644
index 000000000..532b037f2
Binary files /dev/null and b/public/self/ztree/css/zTreeStyle/img/diy/7.png differ
diff --git a/public/self/ztree/css/zTreeStyle/img/diy/8.png b/public/self/ztree/css/zTreeStyle/img/diy/8.png
new file mode 100644
index 000000000..a8f3a86e7
Binary files /dev/null and b/public/self/ztree/css/zTreeStyle/img/diy/8.png differ
diff --git a/public/self/ztree/css/zTreeStyle/img/diy/9.png b/public/self/ztree/css/zTreeStyle/img/diy/9.png
new file mode 100644
index 000000000..4db73cd41
Binary files /dev/null and b/public/self/ztree/css/zTreeStyle/img/diy/9.png differ
diff --git a/public/self/ztree/css/zTreeStyle/img/line_conn.gif b/public/self/ztree/css/zTreeStyle/img/line_conn.gif
new file mode 100644
index 000000000..d561d36a9
Binary files /dev/null and b/public/self/ztree/css/zTreeStyle/img/line_conn.gif differ
diff --git a/public/self/ztree/css/zTreeStyle/img/loading.gif b/public/self/ztree/css/zTreeStyle/img/loading.gif
new file mode 100644
index 000000000..e8c289293
Binary files /dev/null and b/public/self/ztree/css/zTreeStyle/img/loading.gif differ
diff --git a/public/self/ztree/css/zTreeStyle/img/zTreeStandard.gif b/public/self/ztree/css/zTreeStyle/img/zTreeStandard.gif
new file mode 100644
index 000000000..4b640d22e
Binary files /dev/null and b/public/self/ztree/css/zTreeStyle/img/zTreeStandard.gif differ
diff --git a/public/self/ztree/css/zTreeStyle/img/zTreeStandard.png b/public/self/ztree/css/zTreeStyle/img/zTreeStandard.png
new file mode 100644
index 000000000..cd46ddde9
Binary files /dev/null and b/public/self/ztree/css/zTreeStyle/img/zTreeStandard.png differ
diff --git a/public/self/ztree/css/zTreeStyle/zTreeStyle.css b/public/self/ztree/css/zTreeStyle/zTreeStyle.css
new file mode 100644
index 000000000..39e346a23
--- /dev/null
+++ b/public/self/ztree/css/zTreeStyle/zTreeStyle.css
@@ -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;
+}*/
\ No newline at end of file
diff --git a/public/self/ztree/js/jquery-1.4.4.min.js b/public/self/ztree/js/jquery-1.4.4.min.js
new file mode 100644
index 000000000..8f3ca2e2d
--- /dev/null
+++ b/public/self/ztree/js/jquery-1.4.4.min.js
@@ -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
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(;K0))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(;Ha ";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=" ";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="
";c.support.shrinkWrapBlocks=r.offsetWidth!==2}r.innerHTML="";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-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=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=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-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;h0?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;p0){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;i0};k.find=function(g,i,n){var m;if(!g)return[];for(var p=0,q=o.order.length;p":function(g,i){var n,m=typeof i==="string",p=0,q=g.length;if(m&&!/\W/.test(i))for(i=i.toLowerCase();p=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 in[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=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
";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=" ";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="
";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="
";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;n0)for(var h=d;h0},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-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-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=/\s]+\/)>/g,P={option:[1,
+""," "],legend:[1,""," "],thead:[1,""],tr:[2,""],td:[3,""],col:[2,""],area:[1,""," "],_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","
"];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;b0||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;f0?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]==="