diff --git a/.gitignore b/.gitignore index 9f34fea2a..ceeeaa92c 100644 --- a/.gitignore +++ b/.gitignore @@ -55,6 +55,7 @@ coverage.all !/custom/conf/templates /custom/conf/app.ini !/custom/conf/app.ini.sample +/custom/public/kanban /data /indexers /log diff --git a/README.md b/README.md index 99f6a6e8c..1d9ab8d06 100644 --- a/README.md +++ b/README.md @@ -54,4 +54,7 @@ ## 平台引用 如果本平台对您的科研工作提供了帮助,可在论文致谢中加入: 英文版:```Thanks for the support provided by OpenI Community (https://git.openi.org.cn).``` -中文版:```感谢启智社区提供的技术支持(https://git.openi.org.cn)。``` \ No newline at end of file +中文版:```感谢启智社区提供的技术支持(https://git.openi.org.cn)。``` + +如果您的成果中引用了本平台,也欢迎在下述开源项目中提交您的成果信息: +https://git.openi.org.cn/OpenIOSSG/references 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 387a34520..3b83aced9 100755 --- a/go.mod +++ b/go.mod @@ -22,6 +22,7 @@ require ( github.com/PuerkitoBio/goquery v1.5.0 github.com/RichardKnop/machinery v1.6.9 github.com/RoaringBitmap/roaring v0.4.23 // indirect + github.com/alecthomas/chroma v0.10.0 github.com/alibabacloud-go/darabonba-openapi v0.1.18 github.com/alibabacloud-go/dysmsapi-20170525/v2 v2.0.9 github.com/alibabacloud-go/tea v1.1.17 @@ -120,8 +121,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.30 - github.com/yuin/goldmark-meta v0.0.0-20191126180153-f0638e958b60 + github.com/yuin/goldmark v1.4.13 + github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594 + github.com/yuin/goldmark-meta v1.1.0 golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 golang.org/x/mod v0.3.0 // indirect golang.org/x/net v0.0.0-20200513185701-a91f0712d120 @@ -138,7 +140,7 @@ require ( gopkg.in/ldap.v3 v3.0.2 gopkg.in/macaron.v1 v1.3.9 // indirect gopkg.in/testfixtures.v2 v2.5.0 - gopkg.in/yaml.v2 v2.2.8 + gopkg.in/yaml.v2 v2.3.0 mvdan.cc/xurls/v2 v2.1.0 strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 xorm.io/builder v0.3.7 diff --git a/go.sum b/go.sum index d55d7af48..e0c11f261 100755 --- a/go.sum +++ b/go.sum @@ -76,6 +76,8 @@ github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMx github.com/Unknwon/com v0.0.0-20190321035513-0fed4efef755/go.mod h1:voKvFVpXBJxdIPeqjoJuLK+UVcRlo/JLjeToGxPYu68= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= +github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek= +github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.2/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc= @@ -203,6 +205,8 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= +github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= +github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= @@ -709,12 +713,14 @@ 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 h1:MkTeG1DMwsrdH7QtLXy5W+fUxWq+vmb6cLmyJ7aRtF0= github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8= github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= @@ -804,8 +810,16 @@ 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 v1.4.5/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg= +github.com/yuin/goldmark v1.4.6/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg= +github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594 h1:yHfZyN55+5dp1wG7wDKv8HQ044moxkyGq12KFFMFDxg= +github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594/go.mod h1:U9ihbh+1ZN7fR5Se3daSPoz1CGF9IYtSvWwVQtnzGHU= github.com/yuin/goldmark-meta v0.0.0-20191126180153-f0638e958b60 h1:gZucqLjL1eDzVWrXj4uiWeMbAopJlBR2mKQAsTGdPwo= github.com/yuin/goldmark-meta v0.0.0-20191126180153-f0638e958b60/go.mod h1:i9VhcIHN2PxXMbQrKqXNueok6QNONoPjNMoj9MygVL0= +github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc= +github.com/yuin/goldmark-meta v1.1.0/go.mod h1:U4spWENafuA7Zyg+Lj5RqK/MF+ovMYtBvXi1lBb2VP0= github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs= github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= @@ -1086,6 +1100,8 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + +
+ + +
+ + + + + + +
+
+
+

+ Explore Better AI +
+ OpenI AI Development Cooperation Platform +
+

+

The one-stop collaborative development environment for AI field provides AI development pipeline integrating code development, data management, model debugging, reasoning and evaluation

+ + + Use Now + +
+
+
+

* Only show the dynamics of open source projects

+
+
+
+
+ +
+
+
+
+
+ +
+
+ + +
+
+
+

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!

+
+
+
+
+ +
+
+
+
+
+ +
+
+ + +
+
+
+
+

Recommended Projects

+

Excellent AI projects recommendation. To show your project here, Click here to submit.Click here to explore more projects.

+
+ +
+
+ +
+
+
+ +
+ +
+
+
+

智算网络

+

人工智能算力网络推进联盟已接入10家智算中心,算力总规模1542P

+
+ +
+ + +
+ +
+ + +
+
+
+
+
+ +

+
+
+
+
+
+
+
+ + + +
+
+
+ + + +
+
+
+ + + +
+
+
+
+
+
+
+
+ + +
+ +
+

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.

+
+
+
+
+ +
+
+

Unified Management of Development Elements

+
+ The platform provides four elements of AI development: unified management of model code, data set, model and execution environment. +
+
+
+
+
+ +
+
+

Data Collaboration and Sharing

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

Model Management and Sharing

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

Once Configuration, Multiple Reuse

+
+ Provide execution environment sharing, Once Configuration, Multiple Reuse. Lower the threshold of model development, and avoid spending repetitive time configuring complex environments. +
+
+
+
+
+ + +
+
+
+
+ +
+
+

PengCheng Cloudbrain Open Source Collaboration

+

+ 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 +
+
+
+
+
+ + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/models/admin_operate_log.go b/models/admin_operate_log.go new file mode 100644 index 000000000..439779ea6 --- /dev/null +++ b/models/admin_operate_log.go @@ -0,0 +1,49 @@ +package models + +import ( + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/timeutil" + "encoding/json" +) + +type AdminOperateLog struct { + ID int64 `xorm:"pk autoincr"` + BizType string + OperateType string + OldValue string `xorm:"TEXT"` + NewValue string `xorm:"TEXT"` + RelatedId string `xorm:"INDEX"` + Comment string + CreatedTime timeutil.TimeStamp `xorm:"created"` + CreatedBy int64 +} + +type LogValues struct { + Params []LogValue +} + +type LogValue struct { + Key string + Val interface{} +} + +func (l *LogValues) Add(key string, val interface{}) *LogValues { + l.Params = append(l.Params, LogValue{Key: key, Val: val}) + return l +} + +func (l *LogValues) JsonString() string { + if len(l.Params) == 0 { + return "" + } + b, err := json.Marshal(l) + if err != nil { + log.Error("LogValues JsonString error . %v", err) + return "" + } + return string(b) +} + +func InsertAdminOperateLog(log AdminOperateLog) (int64, error) { + return x.Insert(&log) +} 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 2de74f376..2b7348175 100755 --- a/models/cloudbrain.go +++ b/models/cloudbrain.go @@ -111,6 +111,20 @@ const ( GrampusStatusWaiting = "WAITING" ) +const ( + //cluster + OpenICluster = "OpenI" + C2NetCluster = "C2Net" + + //AI center + AICenterOfCloudBrainOne = "OpenIOne" + AICenterOfCloudBrainTwo = "OpenITwo" + + //ComputeResource + GPU = "GPU" + NPU = "NPU" +) + type Cloudbrain struct { ID int64 `xorm:"pk autoincr"` JobID string `xorm:"INDEX NOT NULL"` @@ -134,14 +148,16 @@ type Cloudbrain struct { CanDebug bool `xorm:"-"` CanDel bool `xorm:"-"` CanModify bool `xorm:"-"` - Type int + Type int `xorm:"INDEX"` BenchmarkTypeID int BenchmarkChildTypeID int + CardType string + Cluster string VersionID int64 //版本id VersionName string `xorm:"INDEX"` //当前版本 Uuid string //数据集id - DatasetName string + DatasetName string `xorm:"varchar(2000)"` VersionCount int //任务的当前版本数量,不包括删除的 IsLatestVersion string //是否是最新版本,1是,0否 CommitID string //提交的仓库代码id @@ -164,12 +180,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:"-"` @@ -178,6 +193,7 @@ type Cloudbrain struct { BenchmarkTypeRankLink string `xorm:"-"` StartTime timeutil.TimeStamp EndTime timeutil.TimeStamp + Spec *Specification `xorm:"-"` } type CloudbrainShow struct { @@ -246,7 +262,16 @@ func (task *Cloudbrain) CorrectCreateUnix() { func (task *Cloudbrain) IsTerminal() bool { status := task.Status - return status == string(ModelArtsTrainJobCompleted) || status == string(ModelArtsTrainJobFailed) || status == string(ModelArtsTrainJobKilled) || status == string(ModelArtsStopped) || status == string(JobStopped) || status == string(JobFailed) || status == string(JobSucceeded) + return status == string(ModelArtsTrainJobCompleted) || status == string(ModelArtsTrainJobFailed) || + status == string(ModelArtsTrainJobKilled) || status == string(ModelArtsStopped) || + status == string(JobStopped) || status == string(JobFailed) || + status == string(JobSucceeded) || status == GrampusStatusFailed || + status == GrampusStatusSucceeded || status == GrampusStatusStopped +} +func (task *Cloudbrain) IsRunning() bool { + status := task.Status + return status == string(ModelArtsTrainJobRunning) || status == string(ModelArtsRunning) || + status == string(JobRunning) || status == GrampusStatusRunning } func ConvertDurationToStr(duration int64) string { @@ -345,6 +370,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"` @@ -385,6 +426,9 @@ type CloudbrainsOptions struct { ComputeResource string BeginTimeUnix int64 EndTimeUnix int64 + AiCenter string + NeedDeleteInfo string + Cluster string } type TaskPod struct { @@ -617,6 +661,7 @@ type SpecialPool struct { Pool []*GpuInfo `json:"pool"` JobType []string `json:"jobType"` ResourceSpec []*ResourceSpec `json:"resourceSpecs"` + Flavor []*FlavorInfo `json:"flavor"` } type ImageInfosModelArts struct { @@ -958,6 +1003,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"` @@ -1005,6 +1072,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"` //训练作业的代码目录 @@ -1019,6 +1091,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"` @@ -1231,6 +1318,52 @@ type LogFile struct { Name string } +type JobList struct { + JobName string `json:"job_name"` + JobID int64 `json:"job_id"` + VersionID int64 `json:"version_id"` + VersionCount int64 `json:"version_count"` + Description string `json:"job_desc"` + IntStatus int `json:"status"` +} + +type GetTrainJobListResult struct { + ErrorResult + JobTotalCount int `json:"job_total_count"` //查询到的用户创建作业总数 + JobCountLimit int `json:"job_count_limit"` //用户还可以创建训练作业的数量 + Quotas int `json:"quotas"` //训练作业的运行数量上限 + JobList []JobList `json:"jobs"` +} + +type JobVersionList struct { + VersionName string `json:"version_name"` + VersionID int64 `json:"version_id"` + IntStatus int `json:"status"` +} + +type GetTrainJobVersionListResult struct { + ErrorResult + JobID int64 `json:"job_id"` + JobName string `json:"job_name"` + JobDesc string `json:"job_desc"` + VersionCount int64 `json:"version_count"` + JobVersionList []JobVersionList `json:"versions"` +} + +type NotebookList struct { + JobName string `json:"name"` + JobID string `json:"id"` + Status string `json:"status"` +} + +type GetNotebookListResult struct { + TotalCount int64 `json:"total"` //总的记录数量 + CurrentPage int `json:"current"` //当前页数 + TotalPages int `json:"pages"` //总的页数 + Size int `json:"size"` //每一页的数量 + NotebookList []NotebookList `json:"data"` +} + //Grampus type GrampusResult struct { ErrorCode int `json:"errorCode"` @@ -1261,6 +1394,34 @@ type GrampusSpec struct { Name string `json:"name"` ProcessorType string `json:"processorType"` Centers []Center `json:"centers"` + SpecInfo SpecInfo `json:"specInfo"` +} + +type GrampusAiCenter struct { + AccDevices []GrampusAccDevice `json:"accDevices"` + Id string `json:"id"` + Name string `json:"name"` + Resource []GrampusCenterResource `json:"resource"` +} + +type GrampusAccDevice struct { + Kind string `json:"kind"` //加速卡类别, npu.huawei.com/NPU,nvidia.com/gpu,cambricon.com/mlu + Model string `json:"model"` //加速卡型号 +} + +type GrampusCenterResource struct { + Allocated string `json:"allocated"` + Capacity string `json:"capacity"` + Name string `json:"name"` +} + +type SpecInfo struct { + AccDeviceKind string `json:"accDeviceKind"` + AccDeviceMemory string `json:"accDeviceMemory"` + AccDeviceModel string `json:"accDeviceModel"` + AccDeviceNum int `json:"accDeviceNum"` + CpuCoreNum int `json:"cpuCoreNum"` + MemorySize string `json:"memorySize"` } type GetGrampusResourceSpecsResult struct { @@ -1268,6 +1429,12 @@ type GetGrampusResourceSpecsResult struct { Infos []GrampusSpec `json:"resourceSpecs"` } +type GetGrampusAiCentersResult struct { + GrampusResult + Infos []GrampusAiCenter `json:"aiCenterInfos"` + TotalSize int `json:"totalSize"` +} + type GrampusImage struct { CreatedAt int64 `json:"createdAt"` UpdatedAt int64 `json:"updatedAt"` @@ -1375,6 +1542,23 @@ func Cloudbrains(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int64, error) { ) } } + if (opts.AiCenter) != "" { + cond = cond.And( + builder.Like{"cloudbrain.ai_center", opts.AiCenter}, + ) + } + if (opts.Cluster) != "" { + if opts.Cluster == "resource_cluster_openi" { + cond = cond.And( + builder.Or(builder.Eq{"cloudbrain.type": TypeCloudBrainOne}, builder.Eq{"cloudbrain.type": TypeCloudBrainTwo}), + ) + } + if opts.Cluster == "resource_cluster_c2net" { + cond = cond.And( + builder.Eq{"cloudbrain.type": TypeC2Net}, + ) + } + } if (opts.IsLatestVersion) != "" { cond = cond.And(builder.Or(builder.And(builder.Eq{"cloudbrain.is_latest_version": opts.IsLatestVersion}, builder.Eq{"cloudbrain.job_type": "TRAIN"}), builder.Neq{"cloudbrain.job_type": "TRAIN"})) @@ -1455,7 +1639,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() @@ -1470,7 +1654,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) @@ -1492,9 +1676,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"). @@ -1580,11 +1764,24 @@ func CloudbrainsVersionList(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int, e } func CreateCloudbrain(cloudbrain *Cloudbrain) (err error) { + session := x.NewSession() + defer session.Close() + + err = session.Begin() cloudbrain.TrainJobDuration = DURATION_STR_ZERO - if _, err = x.NoAutoTime().Insert(cloudbrain); err != nil { + if _, err = session.NoAutoTime().InsertOne(cloudbrain); err != nil { + session.Rollback() return err } + if cloudbrain.Spec != nil { + if _, err = session.Insert(NewCloudBrainSpec(cloudbrain.ID, *cloudbrain.Spec)); err != nil { + session.Rollback() + return err + } + } + session.Commit() + go IncreaseDatasetUseCount(cloudbrain.Uuid) return nil } @@ -1652,12 +1849,34 @@ func GetCloudbrainsNeededStopByUserID(userID int64) ([]*Cloudbrain, error) { return cloudBrains, err } +func GetModelartsReDebugTaskByJobId(jobID string) ([]*Cloudbrain, error) { + sess := x.NewSession() + defer sess.Close() + var cond = builder.NewCond() + cond = cond.And( + builder.Eq{"cloudbrain.job_id": jobID}, + ) + sess.OrderBy("cloudbrain.created_unix ASC limit 1") + cloudbrains := make([]*Cloudbrain, 0, 10) + if err := sess.Table(&Cloudbrain{}).Unscoped().Where(cond). + Find(&cloudbrains); err != nil { + log.Info("find error.") + } + return cloudbrains, nil +} + func GetCloudbrainsNeededStopByRepoID(repoID int64) ([]*Cloudbrain, error) { cloudBrains := make([]*Cloudbrain, 0) err := x.Cols("job_id", "status", "type", "job_type", "version_id", "start_time").Where("repo_id=? AND status !=?", repoID, string(JobStopped)).Find(&cloudBrains) return cloudBrains, err } +func GetCloudbrainsNeededDeleteByRepoID(repoID int64) ([]*Cloudbrain, error) { + cloudBrains := make([]*Cloudbrain, 0) + err := x.Where("repo_id=?", repoID).Find(&cloudBrains) + return cloudBrains, err +} + func GetCloudbrainsByDisplayJobName(repoID int64, jobType string, displayJobName string) ([]*Cloudbrain, error) { cloudBrains := make([]*Cloudbrain, 0) err := x.Cols("job_id", "job_name", "repo_id", "user_id", "job_type", "display_job_name").Where("repo_id=? AND job_type =? AND lower(display_job_name) = lower(?)", repoID, jobType, displayJobName).Find(&cloudBrains) @@ -1784,6 +2003,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)) @@ -1830,11 +2060,18 @@ func RestartCloudbrain(old *Cloudbrain, new *Cloudbrain) (err error) { return err } - if _, err = sess.NoAutoTime().Insert(new); err != nil { + if _, err = sess.NoAutoTime().InsertOne(new); err != nil { sess.Rollback() return err } + if new.Spec != nil { + if _, err = sess.Insert(NewCloudBrainSpec(new.ID, *new.Spec)); err != nil { + sess.Rollback() + return err + } + } + if err = sess.Commit(); err != nil { return err } @@ -1889,8 +2126,27 @@ func CloudbrainAll(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int64, error) { } } + if (opts.AiCenter) != "" { + cond = cond.And( + builder.Like{"cloudbrain.ai_center", opts.AiCenter}, + ) + } + if (opts.NeedDeleteInfo) != "" { + if opts.NeedDeleteInfo == "yes" { + cond = cond.And( + builder.And(builder.NotNull{"cloudbrain.deleted_at"}), + ) + } + if opts.NeedDeleteInfo == "no" { + cond = cond.And( + builder.And(builder.IsNull{"cloudbrain.deleted_at"}), + ) + } + } + if (opts.IsLatestVersion) != "" { - cond = cond.And(builder.Or(builder.And(builder.Eq{"cloudbrain.is_latest_version": opts.IsLatestVersion}, builder.Eq{"cloudbrain.job_type": "TRAIN"}), builder.Neq{"cloudbrain.job_type": "TRAIN"})) + cond = cond.And(builder.Or(builder.And(builder.Eq{"cloudbrain.is_latest_version": opts.IsLatestVersion}, + builder.Eq{"cloudbrain.job_type": "TRAIN"}), builder.Neq{"cloudbrain.job_type": "TRAIN"})) } if len(opts.CloudbrainIDs) > 0 { @@ -1928,7 +2184,8 @@ func CloudbrainAll(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int64, error) { } else { lowerKeyWord := strings.ToLower(opts.Keyword) - cond = cond.And(builder.Or(builder.Like{"LOWER(cloudbrain.job_name)", lowerKeyWord}, builder.Like{"LOWER(cloudbrain.display_job_name)", lowerKeyWord}, builder.Like{"`user`.lower_name", lowerKeyWord})) + cond = cond.And(builder.Or(builder.Like{"LOWER(cloudbrain.job_name)", lowerKeyWord}, + builder.Like{"LOWER(cloudbrain.display_job_name)", lowerKeyWord}, builder.Like{"`user`.lower_name", lowerKeyWord})) count, err = sess.Table(&Cloudbrain{}).Unscoped().Where(cond). Join("left", "`user`", condition).Count(new(CloudbrainInfo)) @@ -2006,7 +2263,8 @@ func CloudbrainAllStatic(opts *CloudbrainsOptions) ([]*CloudbrainInfo, int64, er } sess.OrderBy("cloudbrain.created_unix DESC") cloudbrains := make([]*CloudbrainInfo, 0, setting.UI.IssuePagingNum) - if err := sess.Cols("status", "type", "job_type", "train_job_duration", "duration", "compute_resource", "created_unix", "start_time", "end_time").Table(&Cloudbrain{}).Unscoped().Where(cond). + if err := sess.Cols("status", "type", "job_type", "train_job_duration", "duration", "compute_resource", + "created_unix", "start_time", "end_time").Table(&Cloudbrain{}).Unscoped().Where(cond). Find(&cloudbrains); err != nil { return nil, 0, fmt.Errorf("Find: %v", err) } @@ -2180,7 +2438,18 @@ func GetDatasetInfo(uuidStr string) (map[string]DatasetInfo, string, error) { log.Error("GetAttachmentsByUUIDs failed: %v", err) return nil, datasetNames, err } - for i, attach := range attachs { + for i, tmpUuid := range uuids { + var attach *Attachment + for _, tmpAttach := range attachs { + if tmpAttach.UUID == tmpUuid { + attach = tmpAttach + break + } + } + if attach == nil { + log.Error("GetAttachmentsByUUIDs failed: %v", err) + return nil, datasetNames, err + } fileName := strings.TrimSuffix(strings.TrimSuffix(strings.TrimSuffix(attach.Name, ".zip"), ".tar.gz"), ".tgz") for _, datasetInfo := range datasetInfos { if fileName == datasetInfo.Name { @@ -2208,3 +2477,147 @@ func GetDatasetInfo(uuidStr string) (map[string]DatasetInfo, string, error) { return datasetInfos, datasetNames, nil } + +var ( + SpecsMapInitFlag = false + CloudbrainDebugResourceSpecsMap map[int]*ResourceSpec + CloudbrainTrainResourceSpecsMap map[int]*ResourceSpec + CloudbrainInferenceResourceSpecsMap map[int]*ResourceSpec + CloudbrainBenchmarkResourceSpecsMap map[int]*ResourceSpec + CloudbrainSpecialResourceSpecsMap map[int]*ResourceSpec + GpuInfosMapInitFlag = false + CloudbrainDebugGpuInfosMap map[string]*GpuInfo + CloudbrainTrainGpuInfosMap map[string]*GpuInfo + CloudbrainInferenceGpuInfosMap map[string]*GpuInfo + CloudbrainBenchmarkGpuInfosMap map[string]*GpuInfo + CloudbrainSpecialGpuInfosMap map[string]*GpuInfo +) + +func InitCloudbrainOneResourceSpecMap() { + if CloudbrainDebugResourceSpecsMap == nil || len(CloudbrainDebugResourceSpecsMap) == 0 { + t := ResourceSpecs{} + json.Unmarshal([]byte(setting.ResourceSpecs), &t) + CloudbrainDebugResourceSpecsMap = make(map[int]*ResourceSpec, len(t.ResourceSpec)) + for _, spec := range t.ResourceSpec { + CloudbrainDebugResourceSpecsMap[spec.Id] = spec + } + } + if CloudbrainTrainResourceSpecsMap == nil || len(CloudbrainTrainResourceSpecsMap) == 0 { + t := ResourceSpecs{} + json.Unmarshal([]byte(setting.TrainResourceSpecs), &t) + CloudbrainTrainResourceSpecsMap = make(map[int]*ResourceSpec, len(t.ResourceSpec)) + for _, spec := range t.ResourceSpec { + CloudbrainTrainResourceSpecsMap[spec.Id] = spec + } + } + if CloudbrainInferenceResourceSpecsMap == nil || len(CloudbrainInferenceResourceSpecsMap) == 0 { + t := ResourceSpecs{} + json.Unmarshal([]byte(setting.InferenceResourceSpecs), &t) + CloudbrainInferenceResourceSpecsMap = make(map[int]*ResourceSpec, len(t.ResourceSpec)) + for _, spec := range t.ResourceSpec { + CloudbrainInferenceResourceSpecsMap[spec.Id] = spec + } + } + if CloudbrainBenchmarkResourceSpecsMap == nil || len(CloudbrainBenchmarkResourceSpecsMap) == 0 { + t := ResourceSpecs{} + json.Unmarshal([]byte(setting.BenchmarkResourceSpecs), &t) + CloudbrainBenchmarkResourceSpecsMap = make(map[int]*ResourceSpec, len(t.ResourceSpec)) + for _, spec := range t.ResourceSpec { + CloudbrainBenchmarkResourceSpecsMap[spec.Id] = spec + } + } + if CloudbrainSpecialResourceSpecsMap == nil || len(CloudbrainSpecialResourceSpecsMap) == 0 { + t := SpecialPools{} + json.Unmarshal([]byte(setting.SpecialPools), &t) + for _, pool := range t.Pools { + CloudbrainSpecialResourceSpecsMap = make(map[int]*ResourceSpec, len(pool.ResourceSpec)) + for _, spec := range pool.ResourceSpec { + CloudbrainSpecialResourceSpecsMap[spec.Id] = spec + } + } + } + SpecsMapInitFlag = true +} + +func InitCloudbrainOneGpuInfoMap() { + if CloudbrainDebugGpuInfosMap == nil || len(CloudbrainDebugGpuInfosMap) == 0 { + t := GpuInfos{} + json.Unmarshal([]byte(setting.GpuTypes), &t) + CloudbrainDebugGpuInfosMap = make(map[string]*GpuInfo, len(t.GpuInfo)) + for _, GpuInfo := range t.GpuInfo { + CloudbrainDebugGpuInfosMap[GpuInfo.Queue] = GpuInfo + } + } + if CloudbrainTrainGpuInfosMap == nil || len(CloudbrainTrainGpuInfosMap) == 0 { + t := GpuInfos{} + json.Unmarshal([]byte(setting.TrainGpuTypes), &t) + CloudbrainTrainGpuInfosMap = make(map[string]*GpuInfo, len(t.GpuInfo)) + for _, GpuInfo := range t.GpuInfo { + CloudbrainTrainGpuInfosMap[GpuInfo.Queue] = GpuInfo + } + } + if CloudbrainInferenceGpuInfosMap == nil || len(CloudbrainInferenceGpuInfosMap) == 0 { + t := GpuInfos{} + json.Unmarshal([]byte(setting.InferenceGpuTypes), &t) + CloudbrainInferenceGpuInfosMap = make(map[string]*GpuInfo, len(t.GpuInfo)) + for _, GpuInfo := range t.GpuInfo { + CloudbrainInferenceGpuInfosMap[GpuInfo.Queue] = GpuInfo + } + } + if CloudbrainBenchmarkGpuInfosMap == nil || len(CloudbrainBenchmarkGpuInfosMap) == 0 { + t := GpuInfos{} + json.Unmarshal([]byte(setting.BenchmarkGpuTypes), &t) + CloudbrainBenchmarkGpuInfosMap = make(map[string]*GpuInfo, len(t.GpuInfo)) + for _, GpuInfo := range t.GpuInfo { + CloudbrainBenchmarkGpuInfosMap[GpuInfo.Queue] = GpuInfo + } + } + if CloudbrainSpecialGpuInfosMap == nil || len(CloudbrainSpecialGpuInfosMap) == 0 { + t := SpecialPools{} + json.Unmarshal([]byte(setting.SpecialPools), &t) + for _, pool := range t.Pools { + CloudbrainSpecialGpuInfosMap = make(map[string]*GpuInfo, len(pool.Pool)) + for _, GpuInfo := range pool.Pool { + CloudbrainSpecialGpuInfosMap[GpuInfo.Queue] = GpuInfo + } + } + } + GpuInfosMapInitFlag = true +} +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) +} + +func GetCloudbrainWithDeletedByIDs(ids []int64) ([]*Cloudbrain, error) { + cloudbrains := make([]*Cloudbrain, 0) + return cloudbrains, x. + In("id", ids).Unscoped().Find(&cloudbrains) +} + +func GetCloudbrainCountByJobName(jobName, jobType string, typeCloudbrain int) (int, error) { + count, err := x.Where("job_name = ? and job_type= ? and type = ?", jobName, jobType, typeCloudbrain).Count(new(Cloudbrain)) + return int(count), err +} diff --git a/models/cloudbrain_image.go b/models/cloudbrain_image.go index 71f0c2c94..9a8f9cdcb 100755 --- a/models/cloudbrain_image.go +++ b/models/cloudbrain_image.go @@ -518,6 +518,10 @@ func UpdateLocalImageStatus(image *Image) error { return err } +func UpdateAutoIncrementIndex() { + x.Exec("SELECT setval('image_id_seq', (SELECT MAX(id) from image))") +} + func DeleteLocalImage(id int64) error { image := new(Image) _, err := x.ID(id).Delete(image) diff --git a/models/cloudbrain_spec.go b/models/cloudbrain_spec.go new file mode 100644 index 000000000..8aa652b17 --- /dev/null +++ b/models/cloudbrain_spec.go @@ -0,0 +1,109 @@ +package models + +import ( + "code.gitea.io/gitea/modules/timeutil" +) + +type CloudbrainSpec struct { + CloudbrainID int64 `xorm:"pk"` + SpecId int64 `xorm:"index"` + SourceSpecId string + AccCardsNum int + AccCardType string + CpuCores int + MemGiB float32 + GPUMemGiB float32 + ShareMemGiB float32 + ComputeResource string + UnitPrice int + QueueId int64 + QueueCode string + Cluster string + AiCenterCode string + AiCenterName string + IsExclusive bool + ExclusiveOrg string + CreatedTime timeutil.TimeStamp `xorm:"created"` + UpdatedTime timeutil.TimeStamp `xorm:"updated"` +} + +func (s CloudbrainSpec) ConvertToSpecification() *Specification { + return &Specification{ + ID: s.SpecId, + SourceSpecId: s.SourceSpecId, + AccCardsNum: s.AccCardsNum, + AccCardType: s.AccCardType, + CpuCores: s.CpuCores, + MemGiB: s.MemGiB, + GPUMemGiB: s.GPUMemGiB, + ShareMemGiB: s.ShareMemGiB, + ComputeResource: s.ComputeResource, + UnitPrice: s.UnitPrice, + QueueId: s.QueueId, + QueueCode: s.QueueCode, + Cluster: s.Cluster, + AiCenterCode: s.AiCenterCode, + AiCenterName: s.AiCenterName, + IsExclusive: s.IsExclusive, + ExclusiveOrg: s.ExclusiveOrg, + } +} + +func NewCloudBrainSpec(cloudbrainId int64, s Specification) CloudbrainSpec { + return CloudbrainSpec{ + CloudbrainID: cloudbrainId, + SpecId: s.ID, + SourceSpecId: s.SourceSpecId, + AccCardsNum: s.AccCardsNum, + AccCardType: s.AccCardType, + CpuCores: s.CpuCores, + MemGiB: s.MemGiB, + GPUMemGiB: s.GPUMemGiB, + ShareMemGiB: s.ShareMemGiB, + ComputeResource: s.ComputeResource, + UnitPrice: s.UnitPrice, + QueueId: s.QueueId, + QueueCode: s.QueueCode, + Cluster: s.Cluster, + AiCenterCode: s.AiCenterCode, + AiCenterName: s.AiCenterName, + IsExclusive: s.IsExclusive, + ExclusiveOrg: s.ExclusiveOrg, + } +} + +func InsertCloudbrainSpec(c CloudbrainSpec) (int64, error) { + return x.Insert(&c) +} + +func GetCloudbrainSpecByID(cloudbrainId int64) (*CloudbrainSpec, error) { + r := &CloudbrainSpec{} + if has, err := x.Where("cloudbrain_id = ?", cloudbrainId).Get(r); err != nil { + return nil, err + } else if !has { + return nil, nil + } + return r, nil +} + +func FindCloudbrainTask(page, pageSize int) ([]*Cloudbrain, error) { + r := make([]*Cloudbrain, 0) + err := x.Unscoped(). + Limit(pageSize, (page-1)*pageSize). + OrderBy("cloudbrain.id"). + Find(&r) + if err != nil { + return nil, err + } + return r, nil +} + +func CountNoSpecHistoricTask() (int64, error) { + n, err := x.Unscoped(). + Where(" 1=1 and not exists (select 1 from cloudbrain_spec where cloudbrain.id = cloudbrain_spec.cloudbrain_id)"). + Count(&Cloudbrain{}) + if err != nil { + return 0, err + } + return n, nil +} diff --git a/models/cloudbrain_static.go b/models/cloudbrain_static.go index 03cd7d2bc..371b30f66 100644 --- a/models/cloudbrain_static.go +++ b/models/cloudbrain_static.go @@ -29,133 +29,11 @@ type TaskDetail struct { RepoAlias string `json:"RepoAlias"` RepoID int64 `json:"RepoID"` IsDelete bool `json:"IsDelete"` -} - -func GetDebugOnePeriodCount(beginTime time.Time, endTime time.Time) (int64, error) { - countSql := "SELECT count(*) FROM " + - "public.cloudbrain where created_unix >=" + strconv.FormatInt(beginTime.Unix(), 10) + - " and created_unix<" + strconv.FormatInt(endTime.Unix(), 10) + - " and job_type ='" + string(JobTypeDebug) + "'" + - " and type='" + strconv.Itoa(TypeCloudBrainOne) + "'" - - return x.SQL(countSql).Count() -} -func GetDebugOnePeriodDuration(beginTime time.Time, endTime time.Time) (int64, error) { - total, err := x.Where("created_unix >= ? And created_unix < ? And job_type = ? And type = ? ", strconv.FormatInt(beginTime.Unix(), 10), strconv.FormatInt(endTime.Unix(), 10), JobTypeDebug, TypeCloudBrainOne).SumInt(&Cloudbrain{}, "duration") - if err != nil { - return 0, err - } - - return total, nil -} - -func GetTrainOnePeriodCount(beginTime time.Time, endTime time.Time) (int64, error) { - countSql := "SELECT count(*) FROM " + - "public.cloudbrain where created_unix >=" + strconv.FormatInt(beginTime.Unix(), 10) + - " and created_unix<" + strconv.FormatInt(endTime.Unix(), 10) + - " and job_type ='" + string(JobTypeTrain) + "'" + - " and type='" + strconv.Itoa(TypeCloudBrainOne) + "'" - - return x.SQL(countSql).Count() -} -func GetTrainOnePeriodDuration(beginTime time.Time, endTime time.Time) (int64, error) { - total, err := x.Where("created_unix >= ? And created_unix < ? And job_type = ? And type = ? ", strconv.FormatInt(beginTime.Unix(), 10), strconv.FormatInt(endTime.Unix(), 10), JobTypeTrain, TypeCloudBrainOne).SumInt(&Cloudbrain{}, "duration") - if err != nil { - return 0, err - } - - return total, nil -} - -func GetBenchmarkOnePeriodCount(beginTime time.Time, endTime time.Time) (int64, error) { - countSql := "SELECT count(*) FROM " + - "public.cloudbrain where created_unix >=" + strconv.FormatInt(beginTime.Unix(), 10) + - " and created_unix<" + strconv.FormatInt(endTime.Unix(), 10) + - " and job_type ='" + string(JobTypeBenchmark) + "'" + - " and type='" + strconv.Itoa(TypeCloudBrainOne) + "'" - return x.SQL(countSql).Count() -} -func GetBenchmarkOnePeriodDuration(beginTime time.Time, endTime time.Time) (int64, error) { - total, err := x.Where("created_unix >= ? And created_unix < ? And job_type = ? And type = ? ", strconv.FormatInt(beginTime.Unix(), 10), strconv.FormatInt(endTime.Unix(), 10), JobTypeBenchmark, TypeCloudBrainOne).SumInt(&Cloudbrain{}, "duration") - if err != nil { - return 0, err - } - - return total, nil -} -func GetDebugTwoPeriodCount(beginTime time.Time, endTime time.Time) (int64, error) { - countSql := "SELECT count(*) FROM " + - "public.cloudbrain where created_unix >=" + strconv.FormatInt(beginTime.Unix(), 10) + - " and created_unix<" + strconv.FormatInt(endTime.Unix(), 10) + - " and job_type ='" + string(JobTypeDebug) + "'" + - " and type='" + strconv.Itoa(TypeCloudBrainTwo) + "'" - return x.SQL(countSql).Count() -} -func GetDebugTwoPeriodDuration(beginTime time.Time, endTime time.Time) (int64, error) { - total, err := x.Where("created_unix >= ? And created_unix < ? And job_type = ? And type = ? ", strconv.FormatInt(beginTime.Unix(), 10), strconv.FormatInt(endTime.Unix(), 10), JobTypeDebug, TypeCloudBrainTwo).SumInt(&Cloudbrain{}, "duration") - if err != nil { - return 0, err - } - return total, nil -} -func GetTrainTwoPeriodCount(beginTime time.Time, endTime time.Time) (int64, error) { - countSql := "SELECT count(*) FROM " + - "public.cloudbrain where created_unix >=" + strconv.FormatInt(beginTime.Unix(), 10) + - " and created_unix<" + strconv.FormatInt(endTime.Unix(), 10) + - " and job_type ='" + string(JobTypeTrain) + "'" + - " and type='" + strconv.Itoa(TypeCloudBrainTwo) + "'" - return x.SQL(countSql).Count() -} -func GetTrainTwoPeriodDuration(beginTime time.Time, endTime time.Time) (int64, error) { - total, err := x.Where("created_unix >= ? And created_unix < ? And job_type = ? And type = ? ", strconv.FormatInt(beginTime.Unix(), 10), strconv.FormatInt(endTime.Unix(), 10), JobTypeTrain, TypeCloudBrainTwo).SumInt(&Cloudbrain{}, "duration") - if err != nil { - return 0, err - } - return total, nil -} -func GetInferenceTwoPeriodCount(beginTime time.Time, endTime time.Time) (int64, error) { - countSql := "SELECT count(*) FROM " + - "public.cloudbrain where created_unix >=" + strconv.FormatInt(beginTime.Unix(), 10) + - " and created_unix<" + strconv.FormatInt(endTime.Unix(), 10) + - " and job_type ='" + string(JobTypeInference) + "'" + - " and type='" + strconv.Itoa(TypeCloudBrainTwo) + "'" - return x.SQL(countSql).Count() -} -func GetInferenceTwoPeriodDuration(beginTime time.Time, endTime time.Time) (int64, error) { - total, err := x.Where("created_unix >= ? And created_unix < ? And job_type = ? And type = ? ", strconv.FormatInt(beginTime.Unix(), 10), strconv.FormatInt(endTime.Unix(), 10), JobTypeInference, TypeCloudBrainTwo).SumInt(&Cloudbrain{}, "duration") - if err != nil { - return 0, err - } - return total, nil -} - -func GetCloudBrainOnePeriodCount(beginTime time.Time, endTime time.Time) (int64, error) { - countSql := "SELECT count(*) FROM " + - "public.cloudbrain where created_unix >=" + strconv.FormatInt(beginTime.Unix(), 10) + - " and created_unix<" + strconv.FormatInt(endTime.Unix(), 10) + - " and type='" + strconv.Itoa(TypeCloudBrainOne) + "'" - return x.SQL(countSql).Count() -} -func GetCloudBrainOnePeriodDuration(beginTime time.Time, endTime time.Time) (int64, error) { - total, err := x.Where("created_unix >= ? And created_unix < ? And type = ? ", strconv.FormatInt(beginTime.Unix(), 10), strconv.FormatInt(endTime.Unix(), 10), TypeCloudBrainOne).SumInt(&Cloudbrain{}, "duration") - if err != nil { - return 0, err - } - return total, nil -} -func GetCloudBrainTwoPeriodCount(beginTime time.Time, endTime time.Time) (int64, error) { - countSql := "SELECT count(*) FROM " + - "public.cloudbrain where created_unix >=" + strconv.FormatInt(beginTime.Unix(), 10) + - " and created_unix<" + strconv.FormatInt(endTime.Unix(), 10) + - " and type='" + strconv.Itoa(TypeCloudBrainTwo) + "'" - return x.SQL(countSql).Count() -} -func GetCloudBrainTwoPeriodDuration(beginTime time.Time, endTime time.Time) (int64, error) { - total, err := x.Where("created_unix >= ? And created_unix < ? And type = ? ", strconv.FormatInt(beginTime.Unix(), 10), strconv.FormatInt(endTime.Unix(), 10), TypeCloudBrainTwo).SumInt(&Cloudbrain{}, "duration") - if err != nil { - return 0, err - } - return total, nil + CardNum int `json:"CardNum"` + CardType string `json:"CardType"` + CardDuration string `json:"CardDuration"` + AiCenter string `json:"AiCenter"` + FlavorName string `json:"FlavorName"` } func GetTodayCreatorCount(beginTime time.Time, endTime time.Time) (int64, error) { @@ -221,6 +99,7 @@ func GetWaittingTop() ([]*CloudbrainInfo, error) { } return cloudbrains, nil } + func GetRunningTop() ([]*CloudbrainInfo, error) { sess := x.NewSession() defer sess.Close() diff --git a/models/cloudbrain_temp.go b/models/cloudbrain_temp.go new file mode 100755 index 000000000..389ee610a --- /dev/null +++ b/models/cloudbrain_temp.go @@ -0,0 +1,68 @@ +package models + +import ( + "time" + + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/timeutil" +) + +const ( + TempJobId = "TEMP" + TempVersionId = TempJobId + TempJobStatus = TempJobId +) + +type CloudbrainTemp struct { + ID int64 `xorm:"pk autoincr"` + JobID string `xorm:"NOT NULL DEFAULT 'TEMP'"` + VersionID string `xorm:"NOT NULL DEFAULT 'TEMP'"` + JobName string `xorm:"NOT NULL "` + Type int `xorm:"NOT NULL "` + JobType string `xorm:"INDEX NOT NULL DEFAULT 'DEBUG'"` + Status string `xorm:"INDEX NOT NULL DEFAULT 'TEMP'"` + QueryTimes int `xorm:"INDEX NOT NULL DEFAULT 0"` + CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` + UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` + DeletedAt time.Time `xorm:"deleted"` +} + +func InsertCloudbrainTemp(temp *CloudbrainTemp) (err error) { + if _, err = x.Insert(temp); err != nil { + return err + } + + return nil +} + +func getCloudBrainTemp(temp *CloudbrainTemp) (*CloudbrainTemp, error) { + has, err := x.Get(temp) + if err != nil { + return nil, err + } else if !has { + return nil, ErrJobNotExist{} + } + return temp, nil +} + +func GetCloudBrainTempJobs() ([]*CloudbrainTemp, error) { + jobs := make([]*CloudbrainTemp, 0, 10) + return jobs, x.In("status", TempJobStatus, string(ModelArtsStopping), string(ModelArtsTrainJobKilling)). + And("query_times < ?", setting.MaxTempQueryTimes). + Limit(100). + Find(&jobs) +} + +func DeleteCloudbrainTemp(temp *CloudbrainTemp) error { + return deleteCloudbrainTemp(x, temp) +} + +func deleteCloudbrainTemp(e Engine, temp *CloudbrainTemp) error { + _, err := e.ID(temp.ID).Delete(temp) + return err +} + +func UpdateCloudbrainTemp(temp *CloudbrainTemp) error { + _, err := x.ID(temp.ID).AllCols().Update(temp) + return err +} diff --git a/models/dataset.go b/models/dataset.go index 7f049f068..e91adb7d2 100755 --- a/models/dataset.go +++ b/models/dataset.go @@ -6,6 +6,8 @@ import ( "sort" "strings" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/timeutil" @@ -88,7 +90,7 @@ func (datasets DatasetList) loadAttributes(e Engine) error { if err := e. Where("id > 0"). In("id", keysInt64(set)). - Cols("id", "owner_id", "owner_name", "lower_name", "name", "description", "alias", "lower_alias","is_private"). + Cols("id", "owner_id", "owner_name", "lower_name", "name", "description", "alias", "lower_alias", "is_private"). Find(&repos); err != nil { return fmt.Errorf("find repos: %v", err) } @@ -121,12 +123,12 @@ func (datasets DatasetList) loadAttachmentAttributes(opts *SearchDatasetOptions) if attachment.DatasetID == datasets[i].ID { if opts.StarByMe { - permission,ok := permissionMap[datasets[i].ID]; + permission, ok := permissionMap[datasets[i].ID] if !ok { permission = false datasets[i].Repo.GetOwner() - if datasets[i].Repo.Owner.IsOrganization() { + if datasets[i].Repo.Owner.IsOrganization() { if datasets[i].Repo.Owner.IsUserPartOfOrg(opts.User.ID) { log.Info("user is member of org.") permission = true @@ -140,10 +142,10 @@ func (datasets DatasetList) loadAttachmentAttributes(opts *SearchDatasetOptions) } } - permissionMap[datasets[i].ID]=permission + permissionMap[datasets[i].ID] = permission } - if permission{ + if permission { datasets[i].Attachments = append(datasets[i].Attachments, attachment) } else if !attachment.IsPrivate { datasets[i].Attachments = append(datasets[i].Attachments, attachment) @@ -159,8 +161,8 @@ func (datasets DatasetList) loadAttachmentAttributes(opts *SearchDatasetOptions) } for i := range datasets { - if datasets[i].Attachments==nil{ - datasets[i].Attachments=[]*Attachment{} + if datasets[i].Attachments == nil { + datasets[i].Attachments = []*Attachment{} } datasets[i].Repo.Owner = nil } @@ -178,7 +180,7 @@ type SearchDatasetOptions struct { Category string Task string License string - DatasetIDs []int64 // 目前只在StarByMe为true时起作用 + DatasetIDs []int64 ListOptions SearchOrderBy IsOwner bool @@ -188,6 +190,7 @@ type SearchDatasetOptions struct { JustNeedZipFile bool NeedAttachment bool UploadAttachmentByMe bool + QueryReference bool } func CreateDataset(dataset *Dataset) (err error) { @@ -258,7 +261,7 @@ func SearchDatasetCondition(opts *SearchDatasetOptions) builder.Cond { } } if len(opts.DatasetIDs) > 0 { - if opts.StarByMe { + if opts.StarByMe || (opts.RepoID == 0 && opts.QueryReference) { cond = cond.And(builder.In("dataset.id", opts.DatasetIDs)) } else { subCon := builder.NewCond() @@ -329,13 +332,15 @@ func SearchDatasetByCondition(opts *SearchDatasetOptions, cond builder.Cond) (Da return nil, 0, fmt.Errorf("Count: %v", err) } - sess.Select(selectColumnsSql).Join("INNER", "repository", "repository.id = dataset.repo_id"). + builderQuery := builder.Dialect(setting.Database.Type).Select("id", "title", "status", "category", "description", "download_times", "license", "task", "release_id", "user_id", "repo_id", "created_unix", "updated_unix", "num_stars", "recommend", "use_count").From(builder.Dialect(setting.Database.Type).Select(selectColumnsSql).From("dataset").Join("INNER", "repository", "repository.id = dataset.repo_id"). Join("INNER", "attachment", "attachment.dataset_id=dataset.id"). - Where(cond).OrderBy(opts.SearchOrderBy.String()) + Where(cond), "d").OrderBy(opts.SearchOrderBy.String()) + if opts.PageSize > 0 { - sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) + builderQuery.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) } - if err = sess.Find(&datasets); err != nil { + + if err = sess.SQL(builderQuery).Find(&datasets); err != nil { return nil, 0, fmt.Errorf("Dataset: %v", err) } @@ -585,3 +590,13 @@ func GetTeamDatasetIdsByUserID(userID int64) []int64 { Cols("dataset.id").Find(&datasets) return datasets } + +func UpdateDatasetCreateUser(ID int64, user *User) error { + _, err := x.Where("id = ?", ID).Cols("user_id").Update(&Dataset{ + UserID: user.ID, + }) + if err != nil { + return err + } + return nil +} diff --git a/models/dataset_reference.go b/models/dataset_reference.go new file mode 100644 index 000000000..a43cd625e --- /dev/null +++ b/models/dataset_reference.go @@ -0,0 +1,88 @@ +package models + +import ( + "strconv" + "strings" + + "code.gitea.io/gitea/modules/timeutil" +) + +type DatasetReference struct { + ID int64 `xorm:"pk autoincr"` + RepoID int64 `xorm:"INDEX unique"` + DatasetID string `xorm:"TEXT"` + CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` +} + +func GetDatasetIdsByRepoID(repoID int64) []int64 { + var datasets []int64 + var datasetIds []string + _ = x.Table("dataset_reference").Where("repo_id=?", repoID). + Cols("dataset_reference.dataset_id").Find(&datasetIds) + if len(datasetIds) > 0 { + for _, datasetIdStr := range strings.Split(datasetIds[0], ",") { + datasetId, err := strconv.ParseInt(datasetIdStr, 10, 64) + if err != nil { + continue + } + datasets = append(datasets, datasetId) + } + } + + return datasets +} + +func HasReferenceDataset(repoID int64) bool { + + var datasetIds []string + _ = x.Table("dataset_reference").Where("repo_id=?", repoID). + Cols("dataset_reference.dataset_id").Find(&datasetIds) + return len(datasetIds) > 0 +} + +func getReferenceDatasetStr(repoID int64) string { + + var datasetIds []string + _ = x.Table("dataset_reference").Where("repo_id=?", repoID). + Cols("dataset_reference.dataset_id").Find(&datasetIds) + if len(datasetIds) > 0 { + return datasetIds[0] + } + return "" +} + +func DeleteReferenceDatasetIdsByRepoID(repoID int64) error { + + _, err := x.Exec("delete from dataset_reference where repo_id=?", repoID) + return err +} + +func NewDatasetIdsByRepoID(repoID int64, datasetIds []int64) error { + if len(datasetIds) == 0 { //关联数据集数组为空 + DeleteReferenceDatasetIdsByRepoID(repoID) + } + var datasetsStrArray []string + for _, datasetId := range datasetIds { + datasetsStrArray = append(datasetsStrArray, strconv.FormatInt(datasetId, 10)) + } + + newDatasetStr := strings.Join(datasetsStrArray, ",") + oldDatasetStr := getReferenceDatasetStr(repoID) + if newDatasetStr == oldDatasetStr { //关联数据集无变化,不需要处理 + return nil + } + if oldDatasetStr != "" { //已经存在关联数据集 + _, err := x.Exec("update dataset_reference set dataset_id=? where repo_id=?", newDatasetStr, repoID) + + return err + } else { + datasetReference := DatasetReference{ + DatasetID: newDatasetStr, + RepoID: repoID, + } + + _, err := x.Insert(datasetReference) + return err + } + +} diff --git a/models/dbsql/dataset_foreigntable_for_es.sql b/models/dbsql/dataset_foreigntable_for_es.sql index 02e5f0ddf..4a0cae70e 100644 --- a/models/dbsql/dataset_foreigntable_for_es.sql +++ b/models/dbsql/dataset_foreigntable_for_es.sql @@ -158,16 +158,20 @@ DROP TRIGGER IF EXISTS es_update_dataset on public.dataset; CREATE OR REPLACE FUNCTION public.update_dataset() RETURNS trigger AS $def$ BEGIN - UPDATE public.dataset_es - SET description=NEW.description, - title=NEW.title, - category=NEW.category, - task=NEW.task, - download_times=NEW.download_times, - updated_unix=NEW.updated_unix, - file_name=(select array_to_string(array_agg(name order by created_unix desc),'-#,#-') from public.attachment where dataset_id=NEW.id and is_private=false), - file_desc=(select array_to_string(array_agg(description order by created_unix desc),'-#,#-') from public.attachment where dataset_id=NEW.id and is_private=false) - where id=NEW.id; + if (NEW.status=0) then + delete from public.dataset_es where id=NEW.id; + elsif (NEW.status=1) then + UPDATE public.dataset_es + SET description=NEW.description, + title=NEW.title, + category=NEW.category, + task=NEW.task, + download_times=NEW.download_times, + updated_unix=NEW.updated_unix, + file_name=(select array_to_string(array_agg(name order by created_unix desc),'-#,#-') from public.attachment where dataset_id=NEW.id and is_private=false), + file_desc=(select array_to_string(array_agg(description order by created_unix desc),'-#,#-') from public.attachment where dataset_id=NEW.id and is_private=false) + where id=NEW.id; + end if; return new; END $def$ diff --git a/models/dbsql/repo_foreigntable_for_es.sql b/models/dbsql/repo_foreigntable_for_es.sql index e927eb7c2..82c8781af 100644 --- a/models/dbsql/repo_foreigntable_for_es.sql +++ b/models/dbsql/repo_foreigntable_for_es.sql @@ -461,7 +461,7 @@ $def$ if not OLD.is_private and NEW.is_private then delete from public.issue_es where repo_id=NEW.id; - delete from public.dataset_es where repo_id=NEW.id; + -- delete from public.dataset_es where repo_id=NEW.id; delete from public.repository_es where id=NEW.id; end if; diff --git a/models/models.go b/models/models.go index 731b31960..a4fa8e819 100755 --- a/models/models.go +++ b/models/models.go @@ -152,6 +152,15 @@ func init() { new(PointAccountLog), new(PointAccount), new(RewardAdminLog), + new(AiModelConvert), + new(ResourceQueue), + new(ResourceSpecification), + new(ResourceScene), + new(ResourceSceneSpec), + new(AdminOperateLog), + new(CloudbrainSpec), + new(CloudbrainTemp), + new(DatasetReference), ) tablesStatistic = append(tablesStatistic, diff --git a/models/org.go b/models/org.go index c956f1f89..45d13fc1b 100755 --- a/models/org.go +++ b/models/org.go @@ -470,6 +470,14 @@ func isOrganizationMember(e Engine, orgID, uid int64) (bool, error) { Exist() } +func IsOrganizationMemberByOrgName(orgName string, uid int64) (bool, error) { + org, _ := GetOrgByName(orgName) + if org != nil { + return IsOrganizationMember(org.ID, uid) + } + return false, nil +} + // IsPublicMembership returns true if given user public his/her membership. func IsPublicMembership(orgID, uid int64) (bool, error) { return x. diff --git a/models/repo.go b/models/repo.go index 0e63f4c37..6e5ee3ff6 100755 --- a/models/repo.go +++ b/models/repo.go @@ -1609,13 +1609,6 @@ func updateRepository(e Engine, repo *Repository, visibilityChanged bool) (err e if err != nil { return err } - //If repo has become private, we need set dataset and dataset_file to private - _, err = e.Where("repo_id = ? and status <> 2", repo.ID).Cols("status").Update(&Dataset{ - Status: 0, - }) - if err != nil { - return err - } dataset, err := GetDatasetByRepo(repo) if err != nil && !IsErrNotExist(err) { @@ -1630,6 +1623,14 @@ func updateRepository(e Engine, repo *Repository, visibilityChanged bool) (err e } } + //If repo has become private, we need set dataset and dataset_file to private + _, err = e.Where("repo_id = ? and status <> 2", repo.ID).Cols("status").Update(&Dataset{ + Status: 0, + }) + if err != nil { + return err + } + } else { //If repo has become public, we need set dataset to public _, err = e.Where("repo_id = ? and status <> 2", repo.ID).Cols("status").Update(&Dataset{ @@ -1793,7 +1794,6 @@ func DeleteRepository(doer *User, uid, repoID int64) error { // Delete dataset attachment record and remove related files deleteDatasetAttachmentByRepoId(sess, repoID) - if err = deleteBeans(sess, &Access{RepoID: repo.ID}, &Action{RepoID: repo.ID}, @@ -2255,6 +2255,18 @@ func CheckRepoStats(ctx context.Context) error { "UPDATE `repository` SET num_stars=(SELECT COUNT(*) FROM `star` WHERE repo_id=?) WHERE id=?", "repository count 'num_stars'", }, + //Repository.NumIssues + { + "SELECT repo.id FROM `repository` repo WHERE repo.num_issues!=(SELECT COUNT(*) FROM `issue` WHERE repo_id=repo.id AND is_pull=false)", + "UPDATE `repository` SET num_issues=(SELECT COUNT(*) FROM `issue` WHERE repo_id=? AND is_pull=false) WHERE id=?", + "repository count 'num_issues'", + }, + //Repository.NumPulls + { + "SELECT repo.id FROM `repository` repo WHERE repo.num_pulls!=(SELECT COUNT(*) FROM `issue` WHERE repo_id=repo.id AND is_pull=true)", + "UPDATE `repository` SET num_pulls=(SELECT COUNT(*) FROM `issue` WHERE repo_id=? AND is_pull=true) WHERE id=?", + "repository count 'num_pulls'", + }, // Label.NumIssues { "SELECT label.id FROM `label` WHERE label.num_issues!=(SELECT COUNT(*) FROM `issue_label` WHERE label_id=label.id)", diff --git a/models/repo_activity_custom.go b/models/repo_activity_custom.go index cbe00b9d9..b6fffca0e 100644 --- a/models/repo_activity_custom.go +++ b/models/repo_activity_custom.go @@ -211,6 +211,45 @@ 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, + } + } + if value.Email == "1250125907@qq.com" || value.Email == "peiyongyu-34@163.com" { + log.Info("repo path=" + repository.RepoPath()) + } + authors[key].Commits += value.Commits + authors[key].CommitLines += value.CommitLines + + } + + } + return authors, nil +} + func GetAllUserKPIStats(startTime time.Time, endTime time.Time) (map[string]*git.UserKPIStats, error) { authors := make(map[string]*git.UserKPIStats) repositorys, err := GetAllRepositoriesByFilterCols("owner_name", "name") diff --git a/models/resource_queue.go b/models/resource_queue.go new file mode 100644 index 000000000..ff78fcc40 --- /dev/null +++ b/models/resource_queue.go @@ -0,0 +1,349 @@ +package models + +import ( + "code.gitea.io/gitea/modules/timeutil" + "errors" + "strconv" + "strings" + "xorm.io/builder" +) + +type ResourceQueue struct { + ID int64 `xorm:"pk autoincr"` + QueueCode string + Cluster string `xorm:"notnull"` + AiCenterCode string + AiCenterName string + ComputeResource string + AccCardType string + CardsTotalNum int + IsAutomaticSync bool + Remark string + DeletedTime timeutil.TimeStamp `xorm:"deleted"` + CreatedTime timeutil.TimeStamp `xorm:"created"` + CreatedBy int64 + UpdatedTime timeutil.TimeStamp `xorm:"updated"` + UpdatedBy int64 +} + +func (r ResourceQueue) ConvertToRes() *ResourceQueueRes { + return &ResourceQueueRes{ + ID: r.ID, + QueueCode: r.QueueCode, + Cluster: r.Cluster, + AiCenterCode: r.AiCenterCode, + AiCenterName: r.AiCenterName, + ComputeResource: r.ComputeResource, + AccCardType: r.AccCardType, + CardsTotalNum: r.CardsTotalNum, + UpdatedTime: r.UpdatedTime, + Remark: r.Remark, + } +} + +type ResourceQueueReq struct { + QueueCode string + Cluster string `binding:"Required"` + AiCenterCode string + ComputeResource string `binding:"Required"` + AccCardType string `binding:"Required"` + CardsTotalNum int + CreatorId int64 + IsAutomaticSync bool + Remark string +} + +func (r ResourceQueueReq) ToDTO() ResourceQueue { + q := ResourceQueue{ + QueueCode: r.QueueCode, + Cluster: r.Cluster, + AiCenterCode: r.AiCenterCode, + ComputeResource: strings.ToUpper(r.ComputeResource), + AccCardType: strings.ToUpper(r.AccCardType), + CardsTotalNum: r.CardsTotalNum, + IsAutomaticSync: r.IsAutomaticSync, + Remark: r.Remark, + CreatedBy: r.CreatorId, + UpdatedBy: r.CreatorId, + } + if r.Cluster == OpenICluster { + if r.AiCenterCode == AICenterOfCloudBrainOne { + q.AiCenterName = "云脑一" + } else if r.AiCenterCode == AICenterOfCloudBrainTwo { + q.AiCenterName = "云脑二" + } + } + return q +} + +type SearchResourceQueueOptions struct { + ListOptions + Cluster string + AiCenterCode string + ComputeResource string + AccCardType string +} + +type ResourceQueueListRes struct { + TotalSize int64 + List []*ResourceQueueRes +} + +type ResourceQueueCodesRes struct { + ID int64 + QueueCode string + Cluster string + AiCenterCode string + AiCenterName string +} + +func (ResourceQueueCodesRes) TableName() string { + return "resource_queue" +} + +type ResourceAiCenterRes struct { + AiCenterCode string + AiCenterName string +} + +type GetQueueCodesOptions struct { + Cluster string +} + +func NewResourceQueueListRes(totalSize int64, list []ResourceQueue) *ResourceQueueListRes { + resList := make([]*ResourceQueueRes, len(list)) + for i, v := range list { + resList[i] = v.ConvertToRes() + } + return &ResourceQueueListRes{ + TotalSize: totalSize, + List: resList, + } +} + +type ResourceQueueRes struct { + ID int64 + QueueCode string + Cluster string + AiCenterCode string + AiCenterName string + ComputeResource string + AccCardType string + CardsTotalNum int + UpdatedTime timeutil.TimeStamp + Remark string +} + +func InsertResourceQueue(queue ResourceQueue) (int64, error) { + return x.Insert(&queue) +} + +func UpdateResourceQueueById(queueId int64, queue ResourceQueue) (int64, error) { + return x.ID(queueId).Update(&queue) +} + +func SearchResourceQueue(opts SearchResourceQueueOptions) (int64, []ResourceQueue, error) { + var cond = builder.NewCond() + if opts.Page <= 0 { + opts.Page = 1 + } + if opts.Cluster != "" { + cond = cond.And(builder.Eq{"cluster": opts.Cluster}) + } + if opts.AiCenterCode != "" { + cond = cond.And(builder.Eq{"ai_center_code": opts.AiCenterCode}) + } + if opts.ComputeResource != "" { + cond = cond.And(builder.Eq{"compute_resource": opts.ComputeResource}) + } + if opts.AccCardType != "" { + cond = cond.And(builder.Eq{"acc_card_type": opts.AccCardType}) + } + n, err := x.Where(cond).Unscoped().Count(&ResourceQueue{}) + if err != nil { + return 0, nil, err + } + + r := make([]ResourceQueue, 0) + err = x.Where(cond).Desc("id").Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).Unscoped().Find(&r) + if err != nil { + return 0, nil, err + } + return n, r, nil +} + +func GetResourceQueueCodes(opts GetQueueCodesOptions) ([]*ResourceQueueCodesRes, error) { + cond := builder.NewCond() + if opts.Cluster != "" { + cond = cond.And(builder.Eq{"cluster": opts.Cluster}) + } + cond = cond.And(builder.Or(builder.IsNull{"deleted_time"}, builder.Eq{"deleted_time": 0})) + r := make([]*ResourceQueueCodesRes, 0) + err := x.Where(cond).OrderBy("cluster desc,ai_center_code asc").Find(&r) + if err != nil { + return nil, err + } + return r, nil +} + +func GetResourceQueue(r *ResourceQueue) (*ResourceQueue, error) { + has, err := x.Get(r) + if err != nil { + return nil, err + } else if !has { + return nil, nil + } + return r, nil +} + +func ParseComputeResourceFormGrampus(grampusDeviceKind string) string { + t := strings.Split(grampusDeviceKind, "/") + if len(t) < 2 { + return "" + } + return strings.ToUpper(t[1]) +} + +type MemSize struct { + Sizes []string + Hex int +} + +var memSize = MemSize{Sizes: []string{"K", "M", "G", "T", "P", "E"}, Hex: 1000} +var iMemSize = MemSize{Sizes: []string{"Ki", "Mi", "Gi", "Ti", "Pi", "Ei"}, Hex: 1024} + +func MatchMemSize(memSize MemSize, val string) (int, float32, error) { + for i, v := range memSize.Sizes { + if strings.HasSuffix(val, v) { + s := strings.TrimSuffix(val, v) + f, err := strconv.ParseFloat(s, 32) + if err != nil { + return 0, 0, err + } + return i, float32(f), nil + } + } + return -1, 0, nil +} + +//TransferMemSize transfer oldValue format from old index to new index +//eg: memSize.Sizes = []string{"M", "G", "T", "P", "E"}, oldValue = 10 , oldIndex = 1 , newIndex = 0. it means transfer 10G to 10000M +//so it returns 10000 +func TransferMemSize(memSize MemSize, oldValue float32, oldIndex int, newIndex int) float32 { + diff := oldIndex - newIndex + r := oldValue + if diff > 0 { + r = oldValue * float32(diff) * float32(memSize.Hex) + } else if diff < 0 { + r = oldValue / float32(-1*diff) / float32(memSize.Hex) + } + return r +} + +//ParseMemSize find the memSize which matches value's format,and parse the number from value +func ParseMemSize(value string, memSize MemSize, newIndex int) (bool, float32, error) { + index, r, err := MatchMemSize(memSize, value) + if err != nil { + return false, 0, err + } + if index < 0 { + return false, 0, nil + } + return true, TransferMemSize(memSize, r, index, newIndex), nil +} + +func ParseMemSizeFromGrampus(grampusMemSize string) (float32, error) { + if grampusMemSize == "" { + return 0, nil + } + memflag, memResult, err := ParseMemSize(grampusMemSize, memSize, 2) + if err != nil { + return 0, err + } + if memflag { + return memResult, nil + } + + iMemFlag, imemResult, err := ParseMemSize(grampusMemSize, iMemSize, 2) + if err != nil { + return 0, err + } + if iMemFlag { + return imemResult, nil + } + return 0, errors.New("grampus memSize format error") +} + +func SyncGrampusQueues(updateList []ResourceQueue, insertList []ResourceQueue, existIds []int64) error { + sess := x.NewSession() + var err error + defer func() { + if err != nil { + sess.Rollback() + } + sess.Close() + }() + + //delete queues that no longer exists + deleteQueueIds := make([]int64, 0) + queueCond := builder.NewCond() + queueCond = queueCond.And(builder.NotIn("resource_queue.id", existIds)).And(builder.Eq{"resource_queue.cluster": C2NetCluster}) + if err := sess.Cols("resource_queue.id").Table("resource_queue"). + Where(queueCond).Find(&deleteQueueIds); err != nil { + return err + } + + if len(deleteQueueIds) > 0 { + if _, err = sess.In("id", deleteQueueIds).Update(&ResourceQueue{Remark: "自动同步时被下架"}); err != nil { + return err + } + if _, err = sess.In("id", deleteQueueIds).Delete(&ResourceQueue{}); err != nil { + return err + } + + //delete specs and scene that no longer exists + deleteSpcIds := make([]int64, 0) + if err := sess.Cols("resource_specification.id").Table("resource_specification"). + In("queue_id", deleteQueueIds).Find(&deleteSpcIds); err != nil { + return err + } + if len(deleteSpcIds) > 0 { + if _, err = sess.In("id", deleteSpcIds).Update(&ResourceSpecification{Status: SpecOffShelf}); err != nil { + return err + } + if _, err = sess.In("spec_id", deleteSpcIds).Delete(&ResourceSceneSpec{}); err != nil { + return err + } + } + + } + + //update exists specs + if len(updateList) > 0 { + for _, v := range updateList { + if _, err = sess.ID(v.ID).Update(&v); err != nil { + return err + } + } + + } + + //insert new specs + if len(insertList) > 0 { + if _, err = sess.Insert(insertList); err != nil { + return err + } + } + + return sess.Commit() +} + +func GetResourceAiCenters() ([]ResourceAiCenterRes, error) { + r := make([]ResourceAiCenterRes, 0) + + err := x.SQL("SELECT t.ai_center_code, t.ai_center_name FROM (SELECT DISTINCT ai_center_code, ai_center_name,cluster FROM resource_queue WHERE (deleted_time IS NULL OR deleted_time=0)) t ORDER BY cluster desc,ai_center_code asc").Find(&r) + if err != nil { + return nil, err + } + return r, nil +} diff --git a/models/resource_scene.go b/models/resource_scene.go new file mode 100644 index 000000000..0c921b578 --- /dev/null +++ b/models/resource_scene.go @@ -0,0 +1,329 @@ +package models + +import ( + "code.gitea.io/gitea/modules/timeutil" + "errors" + "xorm.io/builder" +) + +const ( + Exclusive = iota + 1 + NotExclusive +) + +type ResourceScene struct { + ID int64 `xorm:"pk autoincr"` + SceneName string + JobType string + IsExclusive bool + ExclusiveOrg string + CreatedTime timeutil.TimeStamp `xorm:"created"` + CreatedBy int64 + UpdatedTime timeutil.TimeStamp `xorm:"updated"` + UpdatedBy int64 + DeleteTime timeutil.TimeStamp `xorm:"deleted"` + DeletedBy int64 +} + +type ResourceSceneSpec struct { + ID int64 `xorm:"pk autoincr"` + SceneId int64 `xorm:"unique(idx_scene_spec)"` + SpecId int64 `xorm:"unique(idx_scene_spec)"` + CreatedTime timeutil.TimeStamp `xorm:"created"` +} + +type ResourceSceneReq struct { + ID int64 + SceneName string + JobType string + IsExclusive bool + ExclusiveOrg string + CreatorId int64 + SpecIds []int64 +} + +type SearchResourceSceneOptions struct { + ListOptions + JobType string + IsExclusive int + AiCenterCode string + QueueId int64 +} + +type ResourceSceneListRes struct { + TotalSize int64 + List []ResourceSceneRes +} + +func NewResourceSceneListRes(totalSize int64, list []ResourceSceneRes) *ResourceSceneListRes { + return &ResourceSceneListRes{ + TotalSize: totalSize, + List: list, + } +} + +type ResourceSceneRes struct { + ID int64 + SceneName string + JobType JobType + IsExclusive bool + ExclusiveOrg string + Specs []ResourceSpecWithSceneId +} + +func (ResourceSceneRes) TableName() string { + return "resource_scene" +} + +type ResourceSceneBriefRes struct { + ID int64 + SceneName string +} + +func (ResourceSceneBriefRes) TableName() string { + return "resource_scene" +} + +type ResourceSpecWithSceneId struct { + ID int64 + SourceSpecId string + AccCardsNum int + CpuCores int + MemGiB float32 + GPUMemGiB float32 + ShareMemGiB float32 + UnitPrice int + Status int + UpdatedTime timeutil.TimeStamp + SceneId int64 + //queue + Cluster string + AiCenterCode string + AiCenterName string + QueueCode string + QueueId int64 + ComputeResource string + AccCardType string +} + +func (ResourceSpecWithSceneId) TableName() string { + return "resource_specification" +} + +func InsertResourceScene(r ResourceSceneReq) error { + sess := x.NewSession() + defer sess.Close() + + //check + specs := make([]ResourceSpecification, 0) + cond := builder.In("id", r.SpecIds).And(builder.Eq{"status": SpecOnShelf}) + if err := sess.Where(cond).Find(&specs); err != nil { + return err + } + if len(specs) < len(r.SpecIds) { + return errors.New("specIds not correct") + } + + rs := ResourceScene{ + SceneName: r.SceneName, + JobType: r.JobType, + IsExclusive: r.IsExclusive, + ExclusiveOrg: r.ExclusiveOrg, + CreatedBy: r.CreatorId, + UpdatedBy: r.CreatorId, + } + _, err := sess.InsertOne(&rs) + if err != nil { + sess.Rollback() + return err + } + + if len(r.SpecIds) == 0 { + return sess.Commit() + } + rss := make([]ResourceSceneSpec, len(r.SpecIds)) + for i, v := range r.SpecIds { + rss[i] = ResourceSceneSpec{ + SceneId: rs.ID, + SpecId: v, + } + } + + _, err = sess.Insert(&rss) + if err != nil { + sess.Rollback() + return err + } + + return sess.Commit() +} + +func UpdateResourceScene(r ResourceSceneReq) error { + sess := x.NewSession() + var err error + defer func() { + if err != nil { + sess.Rollback() + } + sess.Close() + }() + + // find old scene + old := ResourceScene{} + if has, _ := sess.ID(r.ID).Get(&old); !has { + return errors.New("ResourceScene not exist") + } + //check specification + specs := make([]ResourceSpecification, 0) + cond := builder.In("id", r.SpecIds).And(builder.Eq{"status": SpecOnShelf}) + if err := sess.Where(cond).Find(&specs); err != nil { + return err + } + if len(specs) < len(r.SpecIds) { + return errors.New("specIds not correct") + } + + //update scene + rs := ResourceScene{ + SceneName: r.SceneName, + IsExclusive: r.IsExclusive, + ExclusiveOrg: r.ExclusiveOrg, + } + if _, err = sess.ID(r.ID).UseBool("is_exclusive").Update(&rs); err != nil { + return err + } + + //delete scene spec relation + if _, err = sess.Where("scene_id = ? ", r.ID).Delete(&ResourceSceneSpec{}); err != nil { + sess.Rollback() + return err + } + + if len(r.SpecIds) == 0 { + return sess.Commit() + } + //build new scene spec relation + rss := make([]ResourceSceneSpec, len(r.SpecIds)) + for i, v := range r.SpecIds { + rss[i] = ResourceSceneSpec{ + SceneId: r.ID, + SpecId: v, + } + } + if _, err = sess.Insert(&rss); err != nil { + sess.Rollback() + return err + } + + return sess.Commit() +} + +func DeleteResourceScene(sceneId int64) error { + sess := x.NewSession() + var err error + defer func() { + if err != nil { + sess.Rollback() + } + sess.Close() + }() + + if _, err = sess.ID(sceneId).Delete(&ResourceScene{}); err != nil { + return err + } + if _, err = sess.Where("scene_id = ? ", sceneId).Delete(&ResourceSceneSpec{}); err != nil { + return err + } + return sess.Commit() +} + +func SearchResourceScene(opts SearchResourceSceneOptions) (int64, []ResourceSceneRes, error) { + var cond = builder.NewCond() + if opts.Page <= 0 { + opts.Page = 1 + } + if opts.JobType != "" { + cond = cond.And(builder.Eq{"resource_scene.job_type": opts.JobType}) + } + if opts.IsExclusive == Exclusive { + cond = cond.And(builder.Eq{"resource_scene.is_exclusive": 1}) + } else if opts.IsExclusive == NotExclusive { + cond = cond.And(builder.Eq{"resource_scene.is_exclusive": 0}) + } + if opts.AiCenterCode != "" { + cond = cond.And(builder.Eq{"resource_queue.ai_center_code": opts.AiCenterCode}) + } + if opts.QueueId > 0 { + cond = cond.And(builder.Eq{"resource_queue.id": opts.QueueId}) + } + cond = cond.And(builder.NewCond().Or(builder.Eq{"resource_scene.delete_time": 0}).Or(builder.IsNull{"resource_scene.delete_time"})) + cols := []string{"resource_scene.id", "resource_scene.scene_name", "resource_scene.job_type", "resource_scene.is_exclusive", + "resource_scene.exclusive_org"} + count, err := x.Where(cond). + Distinct("resource_scene.id"). + Join("INNER", "resource_scene_spec", "resource_scene_spec.scene_id = resource_scene.id"). + Join("INNER", "resource_specification", "resource_specification.id = resource_scene_spec.spec_id"). + Join("INNER", "resource_queue", "resource_queue.id = resource_specification.queue_id"). + Count(&ResourceSceneRes{}) + if err != nil { + return 0, nil, err + } + + r := make([]ResourceSceneRes, 0) + if err = x.Where(cond).Distinct(cols...). + Join("INNER", "resource_scene_spec", "resource_scene_spec.scene_id = resource_scene.id"). + Join("INNER", "resource_specification", "resource_specification.id = resource_scene_spec.spec_id"). + Join("INNER", "resource_queue", "resource_queue.id = resource_specification.queue_id"). + Desc("resource_scene.id"). + Limit(opts.PageSize, (opts.Page-1)*opts.PageSize). + Find(&r); err != nil { + return 0, nil, err + } + + if len(r) == 0 { + return 0, r, err + } + //find related specs + sceneIds := make([]int64, 0, len(r)) + for _, v := range r { + sceneIds = append(sceneIds, v.ID) + } + + specs := make([]ResourceSpecWithSceneId, 0) + + if err := x.Cols("resource_specification.id", "resource_specification.source_spec_id", + "resource_specification.acc_cards_num", "resource_specification.cpu_cores", + "resource_specification.mem_gi_b", "resource_specification.gpu_mem_gi_b", + "resource_specification.share_mem_gi_b", "resource_specification.unit_price", + "resource_specification.status", "resource_specification.updated_time", + "resource_scene_spec.scene_id", "resource_queue.cluster", + "resource_queue.ai_center_code", "resource_queue.acc_card_type", + "resource_queue.id as queue_id", "resource_queue.compute_resource", + "resource_queue.queue_code", "resource_queue.ai_center_name", + ).In("resource_scene_spec.scene_id", sceneIds). + Join("INNER", "resource_scene_spec", "resource_scene_spec.spec_id = resource_specification.id"). + Join("INNER", "resource_queue", "resource_queue.ID = resource_specification.queue_id"). + OrderBy("resource_specification.acc_cards_num"). + Find(&specs); err != nil { + return 0, nil, err + } + + specsMap := make(map[int64][]ResourceSpecWithSceneId, 0) + for _, v := range specs { + if _, ok := specsMap[v.SceneId]; !ok { + specsMap[v.SceneId] = []ResourceSpecWithSceneId{v} + } else { + specsMap[v.SceneId] = append(specsMap[v.SceneId], v) + } + } + + for i, v := range r { + s := specsMap[v.ID] + if s == nil { + s = make([]ResourceSpecWithSceneId, 0) + } + r[i].Specs = s + } + + return count, r, nil +} diff --git a/models/resource_specification.go b/models/resource_specification.go new file mode 100644 index 000000000..ee43371af --- /dev/null +++ b/models/resource_specification.go @@ -0,0 +1,550 @@ +package models + +import ( + "code.gitea.io/gitea/modules/timeutil" + "fmt" + "xorm.io/builder" +) + +const ( + SpecNotVerified int = iota + 1 + SpecOnShelf + SpecOffShelf +) + +type ResourceSpecification struct { + ID int64 `xorm:"pk autoincr"` + QueueId int64 `xorm:"INDEX"` + SourceSpecId string `xorm:"INDEX"` + AccCardsNum int + CpuCores int + MemGiB float32 + GPUMemGiB float32 + ShareMemGiB float32 + UnitPrice int + Status int + IsAutomaticSync bool + CreatedTime timeutil.TimeStamp `xorm:"created"` + CreatedBy int64 + UpdatedTime timeutil.TimeStamp `xorm:"updated"` + UpdatedBy int64 +} + +func (r ResourceSpecification) ConvertToRes() *ResourceSpecificationRes { + return &ResourceSpecificationRes{ + ID: r.ID, + SourceSpecId: r.SourceSpecId, + AccCardsNum: r.AccCardsNum, + CpuCores: r.CpuCores, + MemGiB: r.MemGiB, + ShareMemGiB: r.ShareMemGiB, + GPUMemGiB: r.GPUMemGiB, + UnitPrice: r.UnitPrice, + Status: r.Status, + UpdatedTime: r.UpdatedTime, + } +} + +type ResourceSpecificationReq struct { + QueueId int64 `binding:"Required"` + SourceSpecId string + AccCardsNum int + CpuCores int + MemGiB float32 + GPUMemGiB float32 + ShareMemGiB float32 + UnitPrice int + Status int + IsAutomaticSync bool + CreatorId int64 +} + +func (r ResourceSpecificationReq) ToDTO() ResourceSpecification { + return ResourceSpecification{ + QueueId: r.QueueId, + SourceSpecId: r.SourceSpecId, + AccCardsNum: r.AccCardsNum, + CpuCores: r.CpuCores, + MemGiB: r.MemGiB, + GPUMemGiB: r.GPUMemGiB, + ShareMemGiB: r.ShareMemGiB, + UnitPrice: r.UnitPrice, + Status: r.Status, + IsAutomaticSync: r.IsAutomaticSync, + CreatedBy: r.CreatorId, + UpdatedBy: r.CreatorId, + } +} + +type SearchResourceSpecificationOptions struct { + ListOptions + QueueId int64 + Status int + Cluster string +} + +type SearchResourceBriefSpecificationOptions struct { + QueueId int64 + Cluster string +} + +type ResourceSpecAndQueueListRes struct { + TotalSize int64 + List []*ResourceSpecAndQueueRes +} + +func NewResourceSpecAndQueueListRes(totalSize int64, list []ResourceSpecAndQueue) *ResourceSpecAndQueueListRes { + resList := make([]*ResourceSpecAndQueueRes, len(list)) + for i, v := range list { + resList[i] = v.ConvertToRes() + } + return &ResourceSpecAndQueueListRes{ + TotalSize: totalSize, + List: resList, + } +} + +type ResourceSpecificationRes struct { + ID int64 + SourceSpecId string + AccCardsNum int + CpuCores int + MemGiB float32 + GPUMemGiB float32 + ShareMemGiB float32 + UnitPrice int + Status int + UpdatedTime timeutil.TimeStamp +} + +func (ResourceSpecificationRes) TableName() string { + return "resource_specification" +} + +type ResourceSpecAndQueueRes struct { + Spec *ResourceSpecificationRes + Queue *ResourceQueueRes +} + +type ResourceSpecAndQueue struct { + ResourceSpecification `xorm:"extends"` + ResourceQueue `xorm:"extends"` +} + +func (*ResourceSpecAndQueue) TableName() string { + return "resource_specification" +} + +func (r ResourceSpecAndQueue) ConvertToRes() *ResourceSpecAndQueueRes { + return &ResourceSpecAndQueueRes{ + Spec: r.ResourceSpecification.ConvertToRes(), + Queue: r.ResourceQueue.ConvertToRes(), + } +} + +type FindSpecsOptions struct { + JobType JobType + ComputeResource string + Cluster string + AiCenterCode string + SpecId int64 + QueueCode string + SourceSpecId string + AccCardsNum int + UseAccCardsNum bool + AccCardType string + CpuCores int + UseCpuCores bool + MemGiB float32 + UseMemGiB bool + GPUMemGiB float32 + UseGPUMemGiB bool + ShareMemGiB float32 + UseShareMemGiB bool + //if true,find specs no matter used or not used in scene. if false,only find specs used in scene + RequestAll bool +} + +type Specification struct { + ID int64 + SourceSpecId string + AccCardsNum int + AccCardType string + CpuCores int + MemGiB float32 + GPUMemGiB float32 + ShareMemGiB float32 + ComputeResource string + UnitPrice int + QueueId int64 + QueueCode string + Cluster string + AiCenterCode string + AiCenterName string + IsExclusive bool + ExclusiveOrg string +} + +func (Specification) TableName() string { + return "resource_specification" +} + +func InsertResourceSpecification(r ResourceSpecification) (int64, error) { + return x.Insert(&r) +} + +func UpdateResourceSpecificationById(queueId int64, spec ResourceSpecification) (int64, error) { + return x.ID(queueId).Update(&spec) +} +func UpdateSpecUnitPriceById(id int64, unitPrice int) error { + _, err := x.Exec("update resource_specification set unit_price = ? ,updated_time = ? where id = ?", unitPrice, timeutil.TimeStampNow(), id) + return err +} + +func SearchResourceSpecification(opts SearchResourceSpecificationOptions) (int64, []ResourceSpecAndQueue, error) { + var cond = builder.NewCond() + if opts.Page <= 0 { + opts.Page = 1 + } + if opts.QueueId > 0 { + cond = cond.And(builder.Eq{"resource_specification.queue_id": opts.QueueId}) + } + if opts.Status > 0 { + cond = cond.And(builder.Eq{"resource_specification.status": opts.Status}) + } + if opts.Cluster != "" { + cond = cond.And(builder.Eq{"resource_queue.cluster": opts.Cluster}) + } + //cond = cond.And(builder.Or(builder.Eq{"resource_queue.deleted_time": 0}).Or(builder.IsNull{"resource_queue.deleted_time"})) + n, err := x.Where(cond).Join("INNER", "resource_queue", "resource_queue.ID = resource_specification.queue_id"). + Unscoped().Count(&ResourceSpecAndQueue{}) + if err != nil { + return 0, nil, err + } + + r := make([]ResourceSpecAndQueue, 0) + err = x.Where(cond). + Join("INNER", "resource_queue", "resource_queue.ID = resource_specification.queue_id"). + Desc("resource_specification.id"). + Limit(opts.PageSize, (opts.Page-1)*opts.PageSize). + Unscoped().Find(&r) + if err != nil { + return 0, nil, err + } + return n, r, nil +} + +func GetSpecScenes(specId int64) ([]ResourceSceneBriefRes, error) { + r := make([]ResourceSceneBriefRes, 0) + err := x.Where("resource_scene_spec.spec_id = ?", specId). + Join("INNER", "resource_scene_spec", "resource_scene_spec.scene_id = resource_scene.id"). + Find(&r) + if err != nil { + return nil, err + } + return r, nil +} + +func ResourceSpecOnShelf(id int64, unitPrice int) error { + _, err := x.Exec("update resource_specification set unit_price = ?,updated_time = ?,status = ? where id = ?", unitPrice, timeutil.TimeStampNow(), SpecOnShelf, id) + return err +} + +func ResourceSpecOffShelf(id int64) (int64, error) { + sess := x.NewSession() + var err error + defer func() { + if err != nil { + sess.Rollback() + } + sess.Close() + }() + //delete scene spec relation + if _, err = sess.Where("spec_id = ?", id).Delete(&ResourceSceneSpec{}); err != nil { + return 0, err + } + + param := ResourceSpecification{ + Status: SpecOffShelf, + } + n, err := sess.Where("id = ? and status = ?", id, SpecOnShelf).Update(¶m) + if err != nil { + return 0, err + } + sess.Commit() + return n, err +} + +func GetResourceSpecification(r *ResourceSpecification) (*ResourceSpecification, error) { + has, err := x.Get(r) + if err != nil { + return nil, err + } else if !has { + return nil, nil + } + return r, nil +} + +func SyncGrampusSpecs(updateList []ResourceSpecification, insertList []ResourceSpecification, existIds []int64) error { + sess := x.NewSession() + var err error + defer func() { + if err != nil { + sess.Rollback() + } + sess.Close() + }() + //delete specs and scene that no longer exists + deleteIds := make([]int64, 0) + cond := builder.NewCond() + cond = cond.And(builder.NotIn("resource_specification.id", existIds)).And(builder.Eq{"resource_queue.cluster": C2NetCluster}) + if err := sess.Cols("resource_specification.id").Table("resource_specification"). + Where(cond).Join("INNER", "resource_queue", "resource_queue.id = resource_specification.queue_id"). + Find(&deleteIds); err != nil { + return err + } + if len(deleteIds) > 0 { + if _, err = sess.In("id", deleteIds).Update(&ResourceSpecification{Status: SpecOffShelf}); err != nil { + return err + } + if _, err = sess.In("spec_id", deleteIds).Delete(&ResourceSceneSpec{}); err != nil { + return err + } + } + + //update exists specs + if len(updateList) > 0 { + for _, v := range updateList { + if _, err = sess.ID(v.ID).Update(&v); err != nil { + return err + } + } + + } + + //insert new specs + if len(insertList) > 0 { + if _, err = sess.Insert(insertList); err != nil { + return err + } + } + + return sess.Commit() +} + +//FindSpecs +func FindSpecs(opts FindSpecsOptions) ([]*Specification, error) { + var cond = builder.NewCond() + if !opts.RequestAll && opts.JobType != "" { + cond = cond.And(builder.Eq{"resource_scene.job_type": opts.JobType}) + } + if opts.ComputeResource != "" { + cond = cond.And(builder.Eq{"resource_queue.compute_resource": opts.ComputeResource}) + } + if opts.Cluster != "" { + cond = cond.And(builder.Eq{"resource_queue.cluster": opts.Cluster}) + } + if opts.AiCenterCode != "" { + cond = cond.And(builder.Eq{"resource_queue.ai_center_code": opts.AiCenterCode}) + } + if opts.SpecId > 0 { + cond = cond.And(builder.Eq{"resource_specification.id": opts.SpecId}) + } + if opts.QueueCode != "" { + cond = cond.And(builder.Eq{"resource_queue.queue_code": opts.QueueCode}) + } + if opts.SourceSpecId != "" { + cond = cond.And(builder.Eq{"resource_specification.source_spec_id": opts.SourceSpecId}) + } + if opts.UseAccCardsNum { + cond = cond.And(builder.Eq{"resource_specification.acc_cards_num": opts.AccCardsNum}) + } + if opts.AccCardType != "" { + cond = cond.And(builder.Eq{"resource_queue.acc_card_type": opts.AccCardType}) + } + if opts.UseCpuCores { + cond = cond.And(builder.Eq{"resource_specification.cpu_cores": opts.CpuCores}) + } + if opts.UseMemGiB { + cond = cond.And(builder.Eq{"resource_specification.mem_gi_b": opts.MemGiB}) + } + if opts.UseGPUMemGiB { + cond = cond.And(builder.Eq{"resource_specification.gpu_mem_gi_b": opts.GPUMemGiB}) + } + if opts.UseShareMemGiB { + cond = cond.And(builder.Eq{"resource_specification.share_mem_gi_b": opts.ShareMemGiB}) + } + r := make([]*Specification, 0) + s := x.Where(cond). + Join("INNER", "resource_queue", "resource_queue.id = resource_specification.queue_id") + + if !opts.RequestAll { + s = s.Join("INNER", "resource_scene_spec", "resource_scene_spec.spec_id = resource_specification.id"). + Join("INNER", "resource_scene", "resource_scene_spec.scene_id = resource_scene.id") + } + err := s.OrderBy("resource_queue.compute_resource asc,resource_queue.acc_card_type asc,resource_specification.acc_cards_num asc,resource_specification.cpu_cores asc"). + Unscoped().Find(&r) + if err != nil { + return nil, err + } + return r, nil +} + +func InitQueueAndSpec(queue ResourceQueue, spec ResourceSpecification) (*Specification, error) { + sess := x.NewSession() + defer sess.Close() + + sess.Begin() + param := ResourceQueue{ + QueueCode: queue.QueueCode, + Cluster: queue.Cluster, + AiCenterCode: queue.AiCenterCode, + ComputeResource: queue.ComputeResource, + AccCardType: queue.AccCardType, + } + _, err := sess.Get(¶m) + if err != nil { + sess.Rollback() + return nil, err + } + if param.ID == 0 { + _, err = sess.InsertOne(&queue) + if err != nil { + sess.Rollback() + return nil, err + } + } else { + queue = param + } + + spec.QueueId = queue.ID + _, err = sess.InsertOne(&spec) + if err != nil { + sess.Rollback() + return nil, err + } + sess.Commit() + return BuildSpecification(queue, spec), nil +} + +func BuildSpecification(queue ResourceQueue, spec ResourceSpecification) *Specification { + return &Specification{ + ID: spec.ID, + SourceSpecId: spec.SourceSpecId, + AccCardsNum: spec.AccCardsNum, + AccCardType: queue.AccCardType, + CpuCores: spec.CpuCores, + MemGiB: spec.MemGiB, + GPUMemGiB: spec.GPUMemGiB, + ShareMemGiB: spec.ShareMemGiB, + ComputeResource: queue.ComputeResource, + UnitPrice: spec.UnitPrice, + QueueId: queue.ID, + QueueCode: queue.QueueCode, + Cluster: queue.Cluster, + AiCenterCode: queue.AiCenterCode, + AiCenterName: queue.AiCenterName, + } +} + +func GetCloudbrainOneAccCardType(queueCode string) string { + switch queueCode { + case "a100": + return "A100" + case "openidebug": + return "T4" + case "openidgx": + return "V100" + + } + return "" +} + +var cloudbrainTwoSpecsInitFlag = false +var cloudbrainTwoSpecs map[string]*Specification + +func GetCloudbrainTwoSpecs() (map[string]*Specification, error) { + if !cloudbrainTwoSpecsInitFlag { + r, err := InitCloudbrainTwoSpecs() + if err != nil { + return nil, err + } + cloudbrainTwoSpecsInitFlag = true + cloudbrainTwoSpecs = r + } + return cloudbrainTwoSpecs, nil +} + +func InitCloudbrainTwoSpecs() (map[string]*Specification, error) { + r := make(map[string]*Specification, 0) + + queue, err := GetResourceQueue(&ResourceQueue{QueueCode: "openisupport"}) + if err != nil { + return nil, err + } + if queue == nil { + queue = &ResourceQueue{ + QueueCode: "openisupport", + Cluster: OpenICluster, + AiCenterCode: AICenterOfCloudBrainTwo, + AiCenterName: "云脑二", + ComputeResource: NPU, + AccCardType: "ASCEND910", + Remark: "处理历史云脑任务时自动生成", + } + _, err = x.InsertOne(queue) + if err != nil { + return nil, err + } + } + for i := 1; i <= 8; i = i * 2 { + sourceSpecId := "modelarts.bm.910.arm.public." + fmt.Sprint(i) + spec, err := GetResourceSpecification(&ResourceSpecification{ + SourceSpecId: sourceSpecId, + QueueId: queue.ID, + }) + if err != nil { + return nil, err + } + if spec == nil { + spec = &ResourceSpecification{ + QueueId: queue.ID, + SourceSpecId: sourceSpecId, + AccCardsNum: i, + CpuCores: i * 24, + MemGiB: float32(i * 256), + GPUMemGiB: float32(32), + Status: SpecOffShelf, + } + _, err = x.Insert(spec) + if err != nil { + return nil, err + } + } + r[sourceSpecId] = BuildSpecification(*queue, *spec) + } + return r, nil +} + +var grampusSpecsInitFlag = false +var grampusSpecs map[string]*Specification + +func GetGrampusSpecs() (map[string]*Specification, error) { + if !grampusSpecsInitFlag { + specMap := make(map[string]*Specification, 0) + r, err := FindSpecs(FindSpecsOptions{ + Cluster: C2NetCluster, + RequestAll: true, + }) + if err != nil { + return nil, err + } + for _, spec := range r { + specMap[spec.SourceSpecId] = spec + specMap[spec.SourceSpecId+"_"+spec.AiCenterCode] = spec + } + grampusSpecsInitFlag = true + grampusSpecs = specMap + } + return grampusSpecs, nil +} diff --git a/models/user.go b/models/user.go index e08832b8e..f40eb699f 100755 --- a/models/user.go +++ b/models/user.go @@ -1772,7 +1772,6 @@ func (opts *SearchUserOptions) toConds() builder.Cond { if !opts.IsActive.IsNone() { cond = cond.And(builder.Eq{"is_active": opts.IsActive.IsTrue()}) } - return cond } @@ -1784,12 +1783,15 @@ func SearchUsers(opts *SearchUserOptions) (users []*User, _ int64, _ error) { if err != nil { return nil, 0, fmt.Errorf("Count: %v", err) } - + orderby := opts.OrderBy.String() if len(opts.OrderBy) == 0 { - opts.OrderBy = SearchOrderByAlphabetically + orderby = SearchOrderByAlphabetically.String() + lowerKeyword := strings.ToLower(opts.Keyword) + if len(opts.Keyword) > 0 { + orderby = "CASE when lower_name='" + lowerKeyword + "' then 0 when strpos(lower_name,'" + lowerKeyword + "')>0 then 1 else 2 END ASC,lower_name ASC" + } } - - sess := x.Where(cond).OrderBy(opts.OrderBy.String()) + sess := x.Where(cond).OrderBy(orderby) if opts.Page != 0 { sess = opts.setSessionPagination(sess) } diff --git a/models/user_analysis_for_activity.go b/models/user_analysis_for_activity.go new file mode 100644 index 000000000..2066697d2 --- /dev/null +++ b/models/user_analysis_for_activity.go @@ -0,0 +1,451 @@ +package models + +import ( + "fmt" + "time" + + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/timeutil" + "xorm.io/builder" +) + +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 := "issue.created_unix>=" + fmt.Sprint(start_unix) + " and issue.created_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 +} + +func QueryUserLoginInfo(userIds []int64) []*UserLoginLog { + statictisSess := xStatistic.NewSession() + defer statictisSess.Close() + var cond = builder.NewCond() + cond = cond.And(builder.In("u_id", userIds)) + statictisSess.Select("*").Table(new(UserLoginLog)).Where(cond) + loginList := make([]*UserLoginLog, 0) + + statictisSess.Find(&loginList) + + return loginList +} diff --git a/models/user_business_analysis.go b/models/user_business_analysis.go index e058c0df8..0c67a569a 100644 --- a/models/user_business_analysis.go +++ b/models/user_business_analysis.go @@ -105,12 +105,14 @@ 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 { - ID int64 `xorm:"pk"` - - CountDate int64 `xorm:"pk"` + ID int64 `xorm:"pk"` + DataDate string `xorm:"pk"` + CountDate int64 `xorm:"NULL"` //action :ActionMergePullRequest // 11 CodeMergeCount int `xorm:"NOT NULL DEFAULT 0"` @@ -169,8 +171,6 @@ type UserBusinessAnalysis struct { //user Name string `xorm:"NOT NULL"` - DataDate string `xorm:"NULL"` - CloudBrainTaskNum int `xorm:"NOT NULL DEFAULT 0"` GpuDebugJob int `xorm:"NOT NULL DEFAULT 0"` NpuDebugJob int `xorm:"NOT NULL DEFAULT 0"` @@ -192,6 +192,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 +360,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) @@ -407,6 +409,51 @@ func QueryUserStaticDataAll(opts *UserBusinessAnalysisQueryOptions) ([]*UserBusi return userBusinessAnalysisReturnList, allCount } +func QueryDataForUserDefineFromDb(opts *UserBusinessAnalysisQueryOptions, key string) ([]*UserBusinessAnalysis, int64) { + statictisSess := xStatistic.NewSession() + defer statictisSess.Close() + + var cond = builder.NewCond() + cond = cond.And( + builder.Eq{"data_date": key}, + ) + if len(opts.UserName) > 0 { + cond = cond.And( + builder.Like{"name", opts.UserName}, + ) + } + allCount, err := statictisSess.Where(cond).Count(new(UserBusinessAnalysis)) + if err == nil { + if allCount > 0 { + userBusinessAnalysisList := make([]*UserBusinessAnalysis, 0) + if err := statictisSess.Table("user_business_analysis").Where(cond).OrderBy("id desc").Limit(opts.PageSize, (opts.Page-1)*opts.PageSize). + Find(&userBusinessAnalysisList); err != nil { + return nil, 0 + } + return userBusinessAnalysisList, allCount + } + } + return nil, 0 +} + +func WriteDataToDb(dataList []*UserBusinessAnalysis, key string) { + statictisSess := xStatistic.NewSession() + defer statictisSess.Close() + log.Info("write to db, size=" + fmt.Sprint(len(dataList))) + userBusinessAnalysisList := make([]*UserBusinessAnalysis, 0) + for _, data := range dataList { + data.DataDate = key + userBusinessAnalysisList = append(userBusinessAnalysisList, data) + if len(userBusinessAnalysisList) > BATCH_INSERT_SIZE { + statictisSess.Insert(userBusinessAnalysisList) + userBusinessAnalysisList = make([]*UserBusinessAnalysis, 0) + } + } + if len(userBusinessAnalysisList) > 0 { + statictisSess.Insert(userBusinessAnalysisList) + } +} + func QueryUserStaticDataForUserDefine(opts *UserBusinessAnalysisQueryOptions, wikiCountMap map[string]int) ([]*UserBusinessAnalysis, int64) { log.Info("start to count other user info data") sess := x.NewSession() @@ -475,6 +522,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 +776,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 +888,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 +897,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 += "," } @@ -914,7 +963,7 @@ func CounDataByDateAndReCount(wikiCountMap map[string]int, startTime time.Time, CountDate = time.Date(startTime.Year(), startTime.Month(), startTime.Day(), 0, 1, 0, 0, currentTimeNow.Location()) } - DataDate := startTime.Format("2006-01-02") + DataDate := CountDate.Format("2006-01-02") CodeMergeCountMap := queryPullRequest(start_unix, end_unix) CommitCountMap := queryCommitAction(start_unix, end_unix, 5) IssueCountMap := queryCreateIssue(start_unix, end_unix) @@ -948,6 +997,9 @@ func CounDataByDateAndReCount(wikiCountMap map[string]int, startTime time.Time, statictisSess := xStatistic.NewSession() defer statictisSess.Close() + log.Info("truncate all data from table:user_business_analysis ") + statictisSess.Exec("TRUNCATE TABLE user_business_analysis") + cond := "type != 1" count, err := sess.Where(cond).Count(new(User)) if err != nil { @@ -973,6 +1025,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 +1081,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 +1109,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.") @@ -1096,6 +1149,8 @@ func updateNewUserAcitivity(currentUserActivity map[int64]map[int64]int64, userA ",activate_regist_user=" + fmt.Sprint(useMetrics.ActivateRegistUser) + ",not_activate_regist_user=" + fmt.Sprint(useMetrics.CurrentDayRegistUser-useMetrics.ActivateRegistUser) + ",current_day_regist_user=" + fmt.Sprint(useMetrics.CurrentDayRegistUser) + + ",activate_index=" + fmt.Sprint(float64(useMetrics.ActivateRegistUser)/float64(useMetrics.CurrentDayRegistUser)) + + ",data_date='" + time.Unix(key, 0).Format("2006-01-02") + "'" + " where count_date=" + fmt.Sprint(key) statictisSess.Exec(updateSql) @@ -1124,8 +1179,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 +1205,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..5bd294f2a 100755 --- a/modules/auth/cloudbrain.go +++ b/modules/auth/cloudbrain.go @@ -23,6 +23,8 @@ type CreateCloudBrainForm struct { BootFile string `form:"boot_file"` Params string `form:"run_para_list"` BranchName string `form:"branch_name"` + DatasetName string `form:"dataset_name"` + SpecId int64 `form:"spec_id"` } type CommitImageCloudBrainForm struct { @@ -50,6 +52,30 @@ 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"` + DatasetName string `form:"dataset_name"` + SpecId int64 `form:"spec_id"` +} + func (f *CreateCloudBrainForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { return validate(errs, ctx.Data, f, ctx.Locale) } @@ -61,3 +87,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/dataset.go b/modules/auth/dataset.go index 71b5ac938..8aed3a8c2 100755 --- a/modules/auth/dataset.go +++ b/modules/auth/dataset.go @@ -44,3 +44,11 @@ type EditAttachmentForm struct { func (f *EditAttachmentForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { return validate(errs, ctx.Data, f, ctx.Locale) } + +type ReferenceDatasetForm struct { + DatasetID []int64 `binding:"Required"` +} + +func (f *ReferenceDatasetForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { + return validate(errs, ctx.Data, f, ctx.Locale) +} diff --git a/modules/auth/grampus.go b/modules/auth/grampus.go index ebf0defde..21008ea09 100755 --- a/modules/auth/grampus.go +++ b/modules/auth/grampus.go @@ -11,14 +11,14 @@ type CreateGrampusTrainJobForm struct { Attachment string `form:"attachment" binding:"Required"` BootFile string `form:"boot_file" binding:"Required"` ImageID string `form:"image_id" binding:"Required"` - FlavorID string `form:"flavor" binding:"Required"` Params string `form:"run_para_list" binding:"Required"` Description string `form:"description"` BranchName string `form:"branch_name" binding:"Required"` - FlavorName string `form:"flavor_name" binding:"Required"` EngineName string `form:"engine_name" binding:"Required"` WorkServerNumber int `form:"work_server_number" binding:"Required"` Image string `form:"image"` + DatasetName string `form:"dataset_name"` + SpecId int64 `form:"spec_id"` } func (f *CreateGrampusTrainJobForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { diff --git a/modules/auth/modelarts.go b/modules/auth/modelarts.go index ce41f5d1e..23e1f325a 100755 --- a/modules/auth/modelarts.go +++ b/modules/auth/modelarts.go @@ -22,6 +22,7 @@ type CreateModelArtsNotebookForm struct { Description string `form:"description"` Flavor string `form:"flavor" binding:"Required"` ImageId string `form:"image_id" binding:"Required"` + SpecId int64 `form:"spec_id" binding:"Required"` } func (f *CreateModelArtsNotebookForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { @@ -46,6 +47,7 @@ type CreateModelArtsTrainJobForm struct { VersionName string `form:"version_name" binding:"Required"` FlavorName string `form:"flaver_names" binding:"Required"` EngineName string `form:"engine_names" binding:"Required"` + SpecId int64 `form:"spec_id" binding:"Required"` } type CreateModelArtsInferenceJobForm struct { @@ -71,6 +73,7 @@ type CreateModelArtsInferenceJobForm struct { ModelName string `form:"model_name" binding:"Required"` ModelVersion string `form:"model_version" binding:"Required"` CkptName string `form:"ckpt_name" binding:"Required"` + SpecId int64 `form:"spec_id" binding:"Required"` } func (f *CreateModelArtsTrainJobForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { diff --git a/modules/auth/wechat/client.go b/modules/auth/wechat/client.go index 28d448b53..7f01d82f3 100644 --- a/modules/auth/wechat/client.go +++ b/modules/auth/wechat/client.go @@ -19,6 +19,7 @@ const ( ACCESS_TOKEN_PATH = "/cgi-bin/token" QR_CODE_PATH = "/cgi-bin/qrcode/create" GET_MATERIAL_PATH = "/cgi-bin/material/batchget_material" + SEND_TEMPLATE_PATH = "/cgi-bin/message/template/send" ACTION_QR_STR_SCENE = "QR_STR_SCENE" ERR_CODE_ACCESSTOKEN_EXPIRE = 42001 @@ -41,12 +42,34 @@ type QRCodeRequest struct { Action_info ActionInfo `json:"action_info"` Expire_seconds int `json:"expire_seconds"` } + type MaterialRequest struct { Type string `json:"type"` Offset int `json:"offset"` Count int `json:"count"` } +type TemplateMsgRequest struct { + ToUser string `json:"touser"` + TemplateId string `json:"template_id"` + Url string `json:"url"` + ClientMsgId string `json:"client_msg_id"` + Data interface{} `json:"data"` +} +type TemplateValue struct { + Value string `json:"value"` + Color string `json:"color"` +} + +type DefaultWechatTemplate struct { + First TemplateValue `json:"first"` + Keyword1 TemplateValue `json:"keyword1"` + Keyword2 TemplateValue `json:"keyword2"` + Keyword3 TemplateValue `json:"keyword3"` + Keyword4 TemplateValue `json:"keyword4"` + Remark TemplateValue `json:"remark"` +} + type ActionInfo struct { Scene Scene `json:"scene"` } @@ -163,3 +186,27 @@ func getErrorCodeFromResponse(r *resty.Response) int { c, _ := strconv.Atoi(fmt.Sprint(code)) return c } + +func sendTemplateMsg(req TemplateMsgRequest) (error, bool) { + client := getWechatRestyClient() + + bodyJson, _ := json.Marshal(req) + r, err := client.R(). + SetHeader("Content-Type", "application/json"). + SetQueryParam("access_token", GetWechatAccessToken()). + SetBody(bodyJson). + Post(setting.WechatApiHost + SEND_TEMPLATE_PATH) + if err != nil { + log.Error("sendTemplateMsg,e=%v", err) + return nil, false + } + a := r.Body() + resultMap := make(map[string]interface{}, 0) + json.Unmarshal(a, &resultMap) + errcode := resultMap["errcode"] + log.Info("sendTemplateMsg,%v", r) + if errcode == fmt.Sprint(ERR_CODE_ACCESSTOKEN_EXPIRE) || errcode == fmt.Sprint(ERR_CODE_ACCESSTOKEN_INVALID) { + return nil, true + } + return nil, false +} diff --git a/modules/auth/wechat/cloudbrain.go b/modules/auth/wechat/cloudbrain.go new file mode 100644 index 000000000..193edd1da --- /dev/null +++ b/modules/auth/wechat/cloudbrain.go @@ -0,0 +1,167 @@ +package wechat + +import ( + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + "fmt" + "time" +) + +type JobOperateType string + +const ( + JobOperateTypeStart JobOperateType = "start" + JobOperateTypeStop JobOperateType = "stop" +) + +type CloudbrainStartMsg struct { +} + +func (CloudbrainStartMsg) Data(ctx *TemplateContext) *DefaultWechatTemplate { + return &DefaultWechatTemplate{ + First: TemplateValue{Value: setting.CloudbrainStartedTitle}, + Keyword1: TemplateValue{Value: ctx.Cloudbrain.DisplayJobName}, + Keyword2: TemplateValue{Value: getJobTypeDisplayName(ctx.Cloudbrain.JobType)}, + Keyword3: TemplateValue{Value: time.Unix(int64(ctx.Cloudbrain.CreatedUnix), 0).Format("2006-01-02 15:04:05")}, + Remark: TemplateValue{Value: setting.CloudbrainStartedRemark}, + } +} + +func (CloudbrainStartMsg) ShouldSend(ctx *TemplateContext) bool { + if len(setting.CloudbrainStartedNotifyList) == 0 { + return false + } + for _, v := range setting.CloudbrainStartedNotifyList { + if v == ctx.Cloudbrain.JobType { + return true + } + } + return false +} + +func (CloudbrainStartMsg) MsgId(ctx *TemplateContext) string { + return string(JobOperateTypeStart) + "_" + fmt.Sprint(ctx.Cloudbrain.ID) +} + +func (CloudbrainStartMsg) Url(ctx *TemplateContext) string { + repo, err := models.GetRepositoryByID(ctx.Cloudbrain.RepoID) + if err != nil { + log.Error("CloudbrainStartMsg GetRepositoryByID error,%v", err) + return "" + } + return getCloudbrainTemplateUrl(*ctx.Cloudbrain, repo) +} + +func (CloudbrainStartMsg) TemplateId(ctx *TemplateContext) string { + return setting.CloudbrainStartedTemplateId +} + +type CloudbrainStopMsg struct { +} + +func (CloudbrainStopMsg) Data(ctx *TemplateContext) *DefaultWechatTemplate { + return &DefaultWechatTemplate{ + First: TemplateValue{Value: setting.CloudbrainStoppedTitle}, + Keyword1: TemplateValue{Value: ctx.Cloudbrain.DisplayJobName}, + Keyword2: TemplateValue{Value: getJobTypeDisplayName(ctx.Cloudbrain.JobType)}, + Keyword3: TemplateValue{Value: time.Unix(int64(ctx.Cloudbrain.CreatedUnix), 0).Format("2006-01-02 15:04:05")}, + Keyword4: TemplateValue{Value: time.Unix(int64(ctx.Cloudbrain.EndTime), 0).Format("2006-01-02 15:04:05")}, + Remark: TemplateValue{Value: setting.CloudbrainStoppedRemark}, + } +} + +func (CloudbrainStopMsg) ShouldSend(ctx *TemplateContext) bool { + if len(setting.CloudbrainStoppedNotifyList) == 0 { + return false + } + for _, v := range setting.CloudbrainStoppedNotifyList { + if v == ctx.Cloudbrain.JobType { + if ctx.Cloudbrain.Duration > 0 && ctx.Cloudbrain.EndTime > 0 { + return true + } + break + } + } + return false +} + +func (CloudbrainStopMsg) MsgId(ctx *TemplateContext) string { + return string(JobOperateTypeStop) + "_" + fmt.Sprint(ctx.Cloudbrain.ID) +} + +func (CloudbrainStopMsg) Url(ctx *TemplateContext) string { + repo, err := models.GetRepositoryByID(ctx.Cloudbrain.RepoID) + if err != nil { + log.Error("CloudbrainStopMsg GetRepositoryByID error,%v", err) + return "" + } + return getCloudbrainTemplateUrl(*ctx.Cloudbrain, repo) +} + +func (CloudbrainStopMsg) TemplateId(ctx *TemplateContext) string { + return setting.CloudbrainStoppedTemplateId +} + +var startMsg = &CloudbrainStartMsg{} +var stopMsg = &CloudbrainStopMsg{} + +func GetTemplateFromOperateType(operate JobOperateType) Template { + switch operate { + case JobOperateTypeStart: + return startMsg + case JobOperateTypeStop: + return stopMsg + } + return nil +} + +func GetJobOperateTypeFromCloudbrainStatus(cloudbrain *models.Cloudbrain) JobOperateType { + if cloudbrain.IsTerminal() { + return JobOperateTypeStop + } + if cloudbrain.IsRunning() { + return JobOperateTypeStart + } + return "" +} + +func getCloudbrainTemplateUrl(cloudbrain models.Cloudbrain, repo *models.Repository) string { + url := setting.AppURL + repo.FullName() + + switch cloudbrain.JobType { + case string(models.JobTypeDebug): + if cloudbrain.ComputeResource == "CPU/GPU" { + url += "/cloudbrain/" + fmt.Sprint(cloudbrain.ID) + } else { + url += "/modelarts/notebook/" + fmt.Sprint(cloudbrain.ID) + } + case string(models.JobTypeBenchmark): + url += "/cloudbrain/benchmark/" + fmt.Sprint(cloudbrain.ID) + case string(models.JobTypeTrain): + if cloudbrain.Type == models.TypeCloudBrainOne { + url += "/cloudbrain/train-job/" + fmt.Sprint(cloudbrain.JobID) + } else if cloudbrain.Type == models.TypeCloudBrainTwo { + url += "/modelarts/train-job/" + fmt.Sprint(cloudbrain.JobID) + } else if cloudbrain.Type == models.TypeC2Net { + url += "/grampus/train-job/" + fmt.Sprint(cloudbrain.JobID) + } + case string(models.JobTypeInference): + url += "/modelarts/inference-job/" + fmt.Sprint(cloudbrain.JobID) + } + return url +} + +func getJobTypeDisplayName(jobType string) string { + switch jobType { + case string(models.JobTypeDebug): + return "调试任务" + case string(models.JobTypeBenchmark): + return "评测任务" + case string(models.JobTypeTrain): + return "训练任务" + case string(models.JobTypeInference): + return "推理任务" + } + return "" +} diff --git a/modules/auth/wechat/template.go b/modules/auth/wechat/template.go new file mode 100644 index 000000000..2ebd2667d --- /dev/null +++ b/modules/auth/wechat/template.go @@ -0,0 +1,63 @@ +package wechat + +import ( + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/log" + "errors" + "fmt" +) + +type Template interface { + ShouldSend(ctx *TemplateContext) bool + Data(ctx *TemplateContext) *DefaultWechatTemplate + MsgId(ctx *TemplateContext) string + Url(ctx *TemplateContext) string + TemplateId(ctx *TemplateContext) string +} + +type TemplateContext struct { + Cloudbrain *models.Cloudbrain +} + +func SendTemplateMsg(template Template, ctx *TemplateContext, userId int64) error { + defer func() { + if err := recover(); err != nil { + combinedErr := fmt.Errorf("%s\n%s", err, log.Stack(2)) + log.Error("PANIC:", combinedErr) + } + }() + if !template.ShouldSend(ctx) { + log.Info("SendTemplateMsg should not Send.jobId=%d jobType=%s", ctx.Cloudbrain.ID, ctx.Cloudbrain.JobType) + return nil + } + + openId := models.GetUserWechatOpenId(userId) + if openId == "" { + log.Error("Wechat openId not exist,userId=%d", userId) + return errors.New("Wechat openId not exist") + } + req := TemplateMsgRequest{ + ToUser: openId, + TemplateId: template.TemplateId(ctx), + Url: template.Url(ctx), + ClientMsgId: template.MsgId(ctx), + Data: template.Data(ctx), + } + err, retryFlag := sendTemplateMsg(req) + if retryFlag { + log.Info("SendTemplateMsg calling") + refreshAccessToken() + err, _ = sendTemplateMsg(req) + if err != nil { + log.Error("SendTemplateMsg err. %v", err) + return err + } + return nil + } + if err != nil { + log.Error("SendTemplateMsg err. %v", err) + return err + } + log.Info("SendTemplateMsg success") + return nil +} diff --git a/modules/cloudbrain/cloudbrain.go b/modules/cloudbrain/cloudbrain.go index 6c0681aba..85dcb1ac5 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,12 +38,15 @@ const ( Success = "S000" DefaultBranchName = "master" + + ResultPath = "/result" ) var ( - ResourceSpecs *models.ResourceSpecs - TrainResourceSpecs *models.ResourceSpecs - SpecialPools *models.SpecialPools + ResourceSpecs *models.ResourceSpecs + TrainResourceSpecs *models.ResourceSpecs + InferenceResourceSpecs *models.ResourceSpecs + SpecialPools *models.SpecialPools ) type GenerateCloudBrainTaskReq struct { @@ -57,7 +61,6 @@ type GenerateCloudBrainTaskReq struct { Snn4ImageNetPath string BrainScorePath string JobType string - GpuQueue string Description string BranchName string BootFile string @@ -68,7 +71,18 @@ type GenerateCloudBrainTaskReq struct { DatasetInfos map[string]models.DatasetInfo BenchmarkTypeID int BenchmarkChildTypeID int - ResourceSpecId int + ResultPath string + TrainUrl string + ModelName string + ModelVersion string + CkptName string + LabelName string + Spec *models.Specification +} + +func GetCloudbrainDebugCommand() string { + var command = `pip3 install jupyterlab==3 -i https://pypi.tuna.tsinghua.edu.cn/simple;pip3 install -U "nbclassic>=0.2.8" -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="" --LabApp.token="" --ServerApp.allow_origin="self https://cloudbrain.pcl.ac.cn" ` + return command } func isAdminOrOwnerOrJobCreater(ctx *context.Context, job *models.Cloudbrain, err error) bool { @@ -127,8 +141,8 @@ func isAdminOrImageCreater(ctx *context.Context, image *models.Image, err error) func AdminOrOwnerOrJobCreaterRight(ctx *context.Context) { - var ID = ctx.Params(":id") - job, err := models.GetCloudbrainByID(ID) + var id = ctx.Params(":id") + job, err := models.GetCloudbrainByID(id) if err != nil { log.Error("GetCloudbrainByID failed:%v", err.Error()) ctx.NotFound(ctx.Req.URL.RequestURI(), nil) @@ -143,8 +157,8 @@ func AdminOrOwnerOrJobCreaterRight(ctx *context.Context) { func AdminOrJobCreaterRight(ctx *context.Context) { - var ID = ctx.Params(":id") - job, err := models.GetCloudbrainByID(ID) + var id = ctx.Params(":id") + job, err := models.GetCloudbrainByID(id) if err != nil { log.Error("GetCloudbrainByID failed:%v", err.Error()) ctx.NotFound(ctx.Req.URL.RequestURI(), nil) @@ -212,54 +226,9 @@ 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 { - json.Unmarshal([]byte(setting.TrainResourceSpecs), &TrainResourceSpecs) - } - for _, spec := range TrainResourceSpecs.ResourceSpec { - if req.ResourceSpecId == spec.Id { - resourceSpec = spec - break - } - } - } else { - if ResourceSpecs == nil { - json.Unmarshal([]byte(setting.ResourceSpecs), &ResourceSpecs) - } - for _, spec := range ResourceSpecs.ResourceSpec { - if req.ResourceSpecId == spec.Id { - resourceSpec = spec - break - } - } - - } - //如果没有匹配到spec信息,尝试从专属资源池获取 - if resourceSpec == nil && SpecialPools != nil { - for _, specialPool := range SpecialPools.Pools { - if resourceSpec != nil { - break - } - if specialPool.ResourceSpec != nil { - if IsElementExist(specialPool.JobType, req.JobType) && IsQueueInSpecialtPool(specialPool.Pool, req.GpuQueue) { - for _, spec := range specialPool.ResourceSpec { - if req.ResourceSpecId == spec.Id { - resourceSpec = spec - break - } - } - } - } - } - } - - if resourceSpec == nil { - log.Error("no such resourceSpecId(%d)", req.ResourceSpecId, req.Ctx.Data["MsgID"]) - return errors.New("no such resourceSpec") } volumes := []models.Volume{ @@ -298,6 +267,13 @@ func GenerateTask(req GenerateCloudBrainTaskReq) error { ReadOnly: true, }, }, + { + HostPath: models.StHostPath{ + Path: req.ResultPath, + MountPath: ResultPath, + ReadOnly: false, + }, + }, } if len(req.DatasetInfos) == 1 { @@ -308,7 +284,7 @@ func GenerateTask(req GenerateCloudBrainTaskReq) error { ReadOnly: true, }, }) - } else { + } else if len(req.DatasetInfos) > 1 { for _, dataset := range req.DatasetInfos { volumes = append(volumes, models.Volume{ HostPath: models.StHostPath{ @@ -324,7 +300,7 @@ func GenerateTask(req GenerateCloudBrainTaskReq) error { jobResult, err := CreateJob(req.JobName, models.CreateJobParams{ JobName: req.JobName, RetryCount: 1, - GpuType: req.GpuQueue, + GpuType: req.Spec.QueueCode, Image: req.Image, TaskRoles: []models.TaskRole{ { @@ -332,10 +308,10 @@ func GenerateTask(req GenerateCloudBrainTaskReq) error { TaskNumber: 1, MinSucceededTaskCount: 1, MinFailedTaskCount: 1, - CPUNumber: resourceSpec.CpuNum, - GPUNumber: resourceSpec.GpuNum, - MemoryMB: resourceSpec.MemMiB, - ShmMB: resourceSpec.ShareMemMiB, + CPUNumber: req.Spec.CpuCores, + GPUNumber: req.Spec.AccCardsNum, + MemoryMB: int(req.Spec.MemGiB * 1024), + ShmMB: int(req.Spec.ShareMemGiB * 1024), Command: req.Command, NeedIBDevice: false, IsMainRole: false, @@ -366,8 +342,7 @@ func GenerateTask(req GenerateCloudBrainTaskReq) error { Type: models.TypeCloudBrainOne, Uuid: req.Uuids, Image: req.Image, - GpuQueue: req.GpuQueue, - ResourceSpecId: req.ResourceSpecId, + GpuQueue: req.Spec.QueueCode, ComputeResource: models.GPUResource, BenchmarkTypeID: req.BenchmarkTypeID, BenchmarkChildTypeID: req.BenchmarkChildTypeID, @@ -378,9 +353,16 @@ 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, + Spec: req.Spec, }) if err != nil { @@ -392,12 +374,15 @@ func GenerateTask(req GenerateCloudBrainTaskReq) error { log.Error("GetCloudbrainByJobID failed: %v", err.Error()) return err } + stringId := strconv.FormatInt(task.ID, 10) if IsBenchmarkJob(req.JobType) { 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) } @@ -409,30 +394,29 @@ 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 - var resourceSpec *models.ResourceSpec - if ResourceSpecs == nil { - json.Unmarshal([]byte(setting.ResourceSpecs), &ResourceSpecs) - } - for _, spec := range ResourceSpecs.ResourceSpec { - if task.ResourceSpecId == spec.Id { - resourceSpec = spec + spec := task.Spec + var datasetInfos map[string]models.DatasetInfo + if task.Uuid != "" { + var err error + datasetInfos, _, err = models.GetDatasetInfo(task.Uuid) + if err != nil { + log.Error("GetDatasetInfo failed:%v", err, ctx.Data["MsgID"]) + return err } } - if resourceSpec == nil { - log.Error("no such resourceSpecId(%d)", task.ResourceSpecId, ctx.Data["MsgID"]) - return errors.New("no such resourceSpec") - } - - datasetInfos, _, err := models.GetDatasetInfo(task.Uuid) - if err != nil { - log.Error("GetDatasetInfo failed:%v", err, ctx.Data["MsgID"]) - return err - } - volumes := []models.Volume{ { HostPath: models.StHostPath{ @@ -470,24 +454,25 @@ func RestartTask(ctx *context.Context, task *models.Cloudbrain, newID *string) e }, }, } - - if len(datasetInfos) == 1 { - volumes = append(volumes, models.Volume{ - HostPath: models.StHostPath{ - Path: datasetInfos[task.Uuid].DataLocalPath, - MountPath: DataSetMountPath, - ReadOnly: true, - }, - }) - } else { - for _, dataset := range datasetInfos { + if datasetInfos != nil { + if len(datasetInfos) == 1 { volumes = append(volumes, models.Volume{ HostPath: models.StHostPath{ - Path: dataset.DataLocalPath, - MountPath: DataSetMountPath + "/" + dataset.Name, + Path: datasetInfos[task.Uuid].DataLocalPath, + MountPath: DataSetMountPath, ReadOnly: true, }, }) + } else { + for _, dataset := range datasetInfos { + volumes = append(volumes, models.Volume{ + HostPath: models.StHostPath{ + Path: dataset.DataLocalPath, + MountPath: DataSetMountPath + "/" + dataset.Name, + ReadOnly: true, + }, + }) + } } } @@ -503,11 +488,11 @@ func RestartTask(ctx *context.Context, task *models.Cloudbrain, newID *string) e TaskNumber: 1, MinSucceededTaskCount: 1, MinFailedTaskCount: 1, - CPUNumber: resourceSpec.CpuNum, - GPUNumber: resourceSpec.GpuNum, - MemoryMB: resourceSpec.MemMiB, - ShmMB: resourceSpec.ShareMemMiB, - Command: Command, + CPUNumber: spec.CpuCores, + GPUNumber: spec.AccCardsNum, + MemoryMB: int(spec.MemGiB * 1024), + ShmMB: int(spec.ShareMemGiB * 1024), + Command: GetCloudbrainDebugCommand(), //Command, NeedIBDevice: false, IsMainRole: false, UseNNI: false, @@ -544,6 +529,7 @@ func RestartTask(ctx *context.Context, task *models.Cloudbrain, newID *string) e CreatedUnix: createTime, UpdatedUnix: createTime, BranchName: task.BranchName, + Spec: spec, } err = models.RestartCloudbrain(task, newTask) @@ -560,6 +546,63 @@ 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) diff --git a/modules/cloudbrain/resty.go b/modules/cloudbrain/resty.go index d45468ddb..006a482ef 100755 --- a/modules/cloudbrain/resty.go +++ b/modules/cloudbrain/resty.go @@ -29,8 +29,10 @@ const ( Public = "public" Custom = "custom" LogPageSize = 500 + errInvalidToken = "S401" LogPageTokenExpired = "5m" pageSize = 15 + QueuesDetailUrl = "/rest-server/api/v2/queuesdetail" ) func getRestyClient() *resty.Client { @@ -59,7 +61,7 @@ func loginCloudbrain() error { res, err := client.R(). SetHeader("Content-Type", "application/json"). - SetBody(map[string]interface{}{"username": username, "password": password, "expiration": "604800"}). + SetBody(map[string]interface{}{"username": username, "password": password, "expiration": conf.Expiration}). SetResult(&loginResult). Post(HOST + "/rest-server/api/v1/token") if err != nil { @@ -74,12 +76,51 @@ 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) + retry := 0 +sendjob: + 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 == errInvalidToken && retry < 1 { + retry++ + _ = loginCloudbrain() + goto sendjob + } + + 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(). @@ -90,16 +131,12 @@ sendjob: Post(HOST + "/rest-server/api/v1/jobs/") if err != nil { - if res != nil { - var response models.CloudBrainResult - json.Unmarshal(res.Body(), &response) - log.Error("code(%s), msg(%s)", response.Code, response.Msg) - return nil, fmt.Errorf(response.Msg) - } return nil, fmt.Errorf("resty create job: %s", err) } - if jobResult.Code == "S401" && retry < 1 { + var response models.CloudBrainResult + json.Unmarshal(res.Body(), &response) + if response.Code == errInvalidToken && retry < 1 { retry++ _ = loginCloudbrain() goto sendjob @@ -131,7 +168,9 @@ sendjob: return nil, fmt.Errorf("resty GetJob: %v", err) } - if getJobResult.Code == "S401" && retry < 1 { + var response models.CloudBrainResult + json.Unmarshal(res.Body(), &response) + if response.Code == errInvalidToken && retry < 1 { retry++ _ = loginCloudbrain() goto sendjob @@ -144,16 +183,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() @@ -174,13 +203,8 @@ sendjob: } var response models.CloudBrainResult - err = json.Unmarshal(res.Body(), &response) - if err != nil { - log.Error("json.Unmarshal failed: %s", err.Error()) - return &getImagesResult, fmt.Errorf("json.Unmarshal failed: %s", err.Error()) - } - - if response.Code == "S401" && retry < 1 { + json.Unmarshal(res.Body(), &response) + if response.Code == errInvalidToken && retry < 1 { retry++ _ = loginCloudbrain() goto sendjob @@ -268,7 +292,9 @@ sendjob: return fmt.Errorf("resty CommitImage: %v", err) } - if result.Code == "S401" && retry < 1 { + var response models.CloudBrainResult + json.Unmarshal(res.Body(), &response) + if response.Code == errInvalidToken && retry < 1 { retry++ _ = loginCloudbrain() goto sendjob @@ -290,6 +316,7 @@ sendjob: } err = models.WithTx(func(ctx models.DBContext) error { + models.UpdateAutoIncrementIndex() if dbImage != nil { dbImage.IsPrivate = params.IsPrivate dbImage.Description = params.ImageDescription @@ -423,7 +450,9 @@ sendjob: return fmt.Errorf("resty StopJob: %v", err) } - if result.Code == "S401" && retry < 1 { + var response models.CloudBrainResult + json.Unmarshal(res.Body(), &response) + if response.Code == errInvalidToken && retry < 1 { retry++ _ = loginCloudbrain() goto sendjob diff --git a/modules/context/context.go b/modules/context/context.go index 2c935881c..8c7808466 100755 --- a/modules/context/context.go +++ b/modules/context/context.go @@ -6,7 +6,6 @@ package context import ( - "code.gitea.io/gitea/routers/notice" "html" "html/template" "io" @@ -16,6 +15,8 @@ import ( "strings" "time" + "code.gitea.io/gitea/routers/notice" + "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" diff --git a/modules/context/permission_json.go b/modules/context/permission_json.go new file mode 100644 index 000000000..28bc3f6a9 --- /dev/null +++ b/modules/context/permission_json.go @@ -0,0 +1,43 @@ +package context + +import ( + "net/http" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/log" + "gitea.com/macaron/macaron" +) + +func RequireRepoReaderJson(unitType models.UnitType) macaron.Handler { + return func(ctx *Context) { + if !ctx.Repo.CanRead(unitType) { + if log.IsTrace() { + if ctx.IsSigned { + log.Trace("Permission Denied: User %-v cannot read %-v in Repo %-v\n"+ + "User in Repo has Permissions: %-+v", + ctx.User, + unitType, + ctx.Repo.Repository, + ctx.Repo.Permission) + } else { + log.Trace("Permission Denied: Anonymous user cannot read %-v in Repo %-v\n"+ + "Anonymous user in Repo has Permissions: %-+v", + unitType, + ctx.Repo.Repository, + ctx.Repo.Permission) + } + } + ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("error.no_right"))) + return + } + } +} + +func RequireRepoWriterJson(unitType models.UnitType) macaron.Handler { + return func(ctx *Context) { + if !ctx.Repo.CanWrite(unitType) { + ctx.JSON(http.StatusOK, models.BaseErrorMessage(ctx.Tr("error.no_right"))) + return + } + } +} diff --git a/modules/cron/tasks_basic.go b/modules/cron/tasks_basic.go index 7d6c7df33..04cd7fe41 100755 --- a/modules/cron/tasks_basic.go +++ b/modules/cron/tasks_basic.go @@ -6,6 +6,8 @@ package cron import ( "code.gitea.io/gitea/services/reward" + "code.gitea.io/gitea/services/cloudbrain/resource" + "code.gitea.io/gitea/modules/modelarts" "context" "time" @@ -230,6 +232,28 @@ func registerCloudbrainPointDeductTask() { }) } +func registerSyncResourceSpecs() { + RegisterTaskFatal("sync_grampus_specs", &BaseConfig{ + Enabled: true, + RunAtStart: true, + Schedule: "0 0 1 * * ?", + }, func(ctx context.Context, _ *models.User, _ Config) error { + resource.SyncGrampusQueueAndSpecs() + return nil + }) +} + +func registerSyncModelArtsTempJobs() { + RegisterTaskFatal("sync_model_arts_temp_jobs", &BaseConfig{ + Enabled: true, + RunAtStart: false, + Schedule: "@every 1m", + }, func(ctx context.Context, _ *models.User, _ Config) error { + modelarts.SyncTempStatusJob() + return nil + }) +} + func initBasicTasks() { registerUpdateMirrorTask() registerRepoHealthCheck() @@ -250,6 +274,8 @@ func initBasicTasks() { registerSyncCloudbrainStatus() registerHandleOrgStatistic() + registerSyncResourceSpecs() + registerSyncModelArtsTempJobs() //registerRewardPeriodTask() registerCloudbrainPointDeductTask() diff --git a/modules/dataset/dataset.go b/modules/dataset/dataset.go index a180af184..297ed460d 100644 --- a/modules/dataset/dataset.go +++ b/modules/dataset/dataset.go @@ -1,5 +1,7 @@ package dataset +import "code.gitea.io/gitea/models" + func GetResourceType(cloudbrainType int) string { if cloudbrainType == 0 { return "CPU/GPU" @@ -15,3 +17,19 @@ func GetStatusText(isPrivate bool) string { return "dataset.public" } } + +func IsShowDataSetOfCurrentRepo(repoID int64) bool { + repo := models.Repository{ + ID: repoID, + } + + dataset, _ := models.GetDatasetByRepo(&repo) + if dataset != nil { + return true + } + if models.HasReferenceDataset(repoID) { + return false + } + return true + +} diff --git a/modules/git/repo_stats_custom.go b/modules/git/repo_stats_custom.go index 1a7b657d5..52d91e77a 100644 --- a/modules/git/repo_stats_custom.go +++ b/modules/git/repo_stats_custom.go @@ -62,7 +62,7 @@ func GetUserKPIStats(repoPath string, startTime time.Time, endTime time.Time) (m after := startTime.Format(time.RFC3339) until := endTime.Format(time.RFC3339) - args := []string{"log", "--numstat", "--no-merges", "--branches=*", "--pretty=format:---%n%h%n%an%n%ae%n", "--date=iso", fmt.Sprintf("--after='%s'", after), fmt.Sprintf("--until=='%s'", until)} + args := []string{"log", "--numstat", "--no-merges", "--branches=*", "--pretty=format:---%n%h%n%an%n%ae%n", "--date=iso", fmt.Sprintf("--after='%s'", after), fmt.Sprintf("--until='%s'", until)} stdout, err := NewCommand(args...).RunInDirBytes(repoPath) if err != nil { return nil, err diff --git a/modules/grampus/grampus.go b/modules/grampus/grampus.go index caaae4e8e..934c0933d 100755 --- a/modules/grampus/grampus.go +++ b/modules/grampus/grampus.go @@ -23,9 +23,7 @@ const ( NpuWorkDir = "/cache/" CommandPrepareScript = ";mkdir -p output;mkdir -p code;mkdir -p dataset;echo \"start loading script\";wget -q https://git.openi.org.cn/OpenIOSSG/script_for_grampus/archive/master.zip;" + - "echo \"finish loading script\";unzip -q master.zip;cd script_for_grampus;chmod 777 downloader_for_obs uploader_for_obs downloader_for_minio uploader_for_minio;" - //CommandPrepareScript = "pwd;cd /cache;mkdir -p output;mkdir -p code;mkdir -p dataset;echo \"start loading script\";wget -q https://git.openi.org.cn/OpenIOSSG/script_for_grampus/archive/master.zip;" + - // "echo \"finish loading script\";unzip -q master.zip;cd script_for_grampus;chmod 777 downloader_for_obs uploader_for_obs downloader_for_minio uploader_for_minio;" + "echo \"finish loading script\";unzip -q master.zip;cd script_for_grampus;chmod 777 downloader_for_obs uploader_for_npu downloader_for_minio uploader_for_gpu;" CodeArchiveName = "master.zip" ) @@ -39,11 +37,10 @@ var ( ) type GenerateTrainJobReq struct { - JobName string - Command string - ResourceSpecId string - ImageUrl string //与image_id二选一,都有的情况下优先image_url - ImageId string + JobName string + Command string + ImageUrl string //与image_id二选一,都有的情况下优先image_url + ImageId string DisplayJobName string Uuid string @@ -60,7 +57,6 @@ type GenerateTrainJobReq struct { BranchName string PreVersionId int64 PreVersionName string - FlavorName string VersionCount int EngineName string TotalVersionCount int @@ -68,6 +64,7 @@ type GenerateTrainJobReq struct { ProcessType string DatasetName string Params string + Spec *models.Specification } func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error) { @@ -81,7 +78,7 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error { Name: req.JobName, Command: req.Command, - ResourceSpecId: req.ResourceSpecId, + ResourceSpecId: req.Spec.SourceSpecId, ImageId: req.ImageId, ImageUrl: req.ImageUrl, CenterID: centerID, @@ -116,15 +113,14 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error Parameters: req.Params, BootFile: req.BootFile, DataUrl: req.DataUrl, - FlavorCode: req.ResourceSpecId, Description: req.Description, WorkServerNumber: req.WorkServerNumber, - FlavorName: req.FlavorName, EngineName: req.EngineName, VersionCount: req.VersionCount, TotalVersionCount: req.TotalVersionCount, CreatedUnix: createTime, UpdatedUnix: createTime, + Spec: req.Spec, }) if err != nil { diff --git a/modules/grampus/resty.go b/modules/grampus/resty.go index 5e8722b4b..427b801cc 100755 --- a/modules/grampus/resty.go +++ b/modules/grampus/resty.go @@ -23,6 +23,7 @@ const ( urlGetToken = urlOpenApiV1 + "token" urlTrainJob = urlOpenApiV1 + "trainjob" urlGetResourceSpecs = urlOpenApiV1 + "resourcespec" + urlGetAiCenter = urlOpenApiV1 + "sharescreen/aicenter" urlGetImages = urlOpenApiV1 + "image" errorIllegalToken = 1005 @@ -275,3 +276,35 @@ sendjob: return &result, nil } + +func GetAiCenters(pageIndex, pageSize int) (*models.GetGrampusAiCentersResult, error) { + checkSetting() + client := getRestyClient() + var result models.GetGrampusAiCentersResult + + retry := 0 + +sendjob: + _, err := client.R(). + SetAuthToken(TOKEN). + SetResult(&result). + Get(HOST + urlGetAiCenter + "?pageIndex=" + fmt.Sprint(pageIndex) + "&pageSize=" + fmt.Sprint(pageSize)) + + if err != nil { + return nil, fmt.Errorf("resty GetAiCenters: %v", err) + } + + if result.ErrorCode == errorIllegalToken && retry < 1 { + retry++ + log.Info("retry get token") + _ = getToken() + goto sendjob + } + + if result.ErrorCode != 0 { + log.Error("GetAiCenters failed(%d): %s", result.ErrorCode, result.ErrorMsg) + return &result, fmt.Errorf("GetAiCenters failed(%d): %s", result.ErrorCode, result.ErrorMsg) + } + + return &result, nil +} diff --git a/modules/markup/markdown/markdown.go b/modules/markup/markdown/markdown.go index e50301ffe..407339461 100644 --- a/modules/markup/markdown/markdown.go +++ b/modules/markup/markdown/markdown.go @@ -7,6 +7,7 @@ package markdown import ( "bytes" + "strings" "sync" "code.gitea.io/gitea/modules/log" @@ -14,6 +15,8 @@ import ( "code.gitea.io/gitea/modules/markup/common" "code.gitea.io/gitea/modules/setting" giteautil "code.gitea.io/gitea/modules/util" + chromahtml "github.com/alecthomas/chroma/formatters/html" + highlighting "github.com/yuin/goldmark-highlighting" "github.com/yuin/goldmark" meta "github.com/yuin/goldmark-meta" @@ -42,16 +45,48 @@ func NewGiteaParseContext(urlPrefix string, isWiki bool) parser.Context { func RenderRaw(body []byte, urlPrefix string, wikiMarkdown bool) []byte { once.Do(func() { converter = goldmark.New( - goldmark.WithExtensions(extension.Table, + goldmark.WithExtensions( + extension.NewTable( + extension.WithTableCellAlignMethod(extension.TableCellAlignAttribute)), extension.Strikethrough, extension.TaskList, extension.DefinitionList, common.FootnoteExtension, - extension.NewTypographer( - extension.WithTypographicSubstitutions(extension.TypographicSubstitutions{ - extension.EnDash: nil, - extension.EmDash: nil, - extension.Ellipsis: nil, + highlighting.NewHighlighting( + highlighting.WithFormatOptions( + chromahtml.WithClasses(true), + chromahtml.PreventSurroundingPre(true), + ), + highlighting.WithWrapperRenderer(func(w util.BufWriter, c highlighting.CodeBlockContext, entering bool) { + if entering { + language, _ := c.Language() + if language == nil { + language = []byte("text") + } + + languageStr := string(language) + + preClasses := []string{"code-block"} + if languageStr == "mermaid" { + preClasses = append(preClasses, "is-loading") + } + + _, err := w.WriteString(`
`)
+							if err != nil {
+								return
+							}
+
+							// include language-x class as part of commonmark spec
+							_, err = w.WriteString(``)
+							if err != nil {
+								return
+							}
+						} else {
+							_, err := w.WriteString("
") + if err != nil { + return + } + } }), ), meta.Meta, diff --git a/modules/modelarts/modelarts.go b/modules/modelarts/modelarts.go index 36d9ec3dd..f5b90a277 100755 --- a/modules/modelarts/modelarts.go +++ b/modules/modelarts/modelarts.go @@ -6,8 +6,7 @@ import ( "fmt" "path" "strconv" - - "code.gitea.io/gitea/modules/timeutil" + "strings" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" @@ -15,18 +14,19 @@ import ( "code.gitea.io/gitea/modules/notification" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/storage" + "code.gitea.io/gitea/modules/timeutil" ) 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\":\"专属资源池\"}]}" @@ -59,7 +59,7 @@ const ( PerPage = 10 IsLatestVersion = "1" NotLatestVersion = "0" - VersionCount = 1 + VersionCountOne = 1 SortByCreateTime = "create_time" ConfigTypeCustom = "custom" @@ -67,9 +67,11 @@ const ( ) var ( - poolInfos *models.PoolInfos - FlavorInfos *models.FlavorInfos - ImageInfos *models.ImageInfosModelArts + poolInfos *models.PoolInfos + FlavorInfos *models.FlavorInfos + ImageInfos *models.ImageInfosModelArts + TrainFlavorInfos *Flavor + SpecialPools *models.SpecialPools ) type GenerateTrainJobReq struct { @@ -82,7 +84,6 @@ type GenerateTrainJobReq struct { BootFileUrl string DataUrl string TrainUrl string - FlavorCode string LogUrl string PoolID string WorkServerNumber int @@ -94,11 +95,15 @@ type GenerateTrainJobReq struct { BranchName string PreVersionId int64 PreVersionName string + FlavorCode string FlavorName string VersionCount int EngineName string TotalVersionCount int + UserImageUrl string + UserCommand string DatasetName string + Spec *models.Specification } type GenerateInferenceJobReq struct { @@ -111,7 +116,6 @@ type GenerateInferenceJobReq struct { BootFileUrl string DataUrl string TrainUrl string - FlavorCode string LogUrl string PoolID string WorkServerNumber int @@ -130,12 +134,15 @@ type GenerateInferenceJobReq struct { ModelVersion string CkptName string ResultUrl string + Spec *models.Specification + DatasetName string } type VersionInfo struct { Version []struct { ID int `json:"id"` Value string `json:"value"` + Url string `json:"url"` } `json:"version"` } @@ -252,7 +259,7 @@ func GenerateTask(ctx *context.Context, jobName, uuid, description, flavor strin return nil } -func GenerateNotebook2(ctx *context.Context, displayJobName, jobName, uuid, description, flavor, imageId string) error { +func GenerateNotebook2(ctx *context.Context, displayJobName, jobName, uuid, description, imageId string, spec *models.Specification) error { if poolInfos == nil { json.Unmarshal([]byte(setting.PoolInfos), &poolInfos) } @@ -266,7 +273,7 @@ func GenerateNotebook2(ctx *context.Context, displayJobName, jobName, uuid, desc jobResult, err := createNotebook2(models.CreateNotebook2Params{ JobName: jobName, Description: description, - Flavor: flavor, + Flavor: spec.SourceSpecId, Duration: autoStopDurationMs, ImageID: imageId, PoolID: poolInfos.PoolInfo[0].PoolId, @@ -280,15 +287,30 @@ func GenerateNotebook2(ctx *context.Context, displayJobName, jobName, uuid, desc }) if err != nil { log.Error("createNotebook2 failed: %v", err.Error()) + if strings.HasPrefix(err.Error(), UnknownErrorPrefix) { + log.Info("(%s)unknown error, set temp status", displayJobName) + errTemp := models.InsertCloudbrainTemp(&models.CloudbrainTemp{ + JobID: models.TempJobId, + VersionID: models.TempVersionId, + Status: models.TempJobStatus, + Type: models.TypeCloudBrainTwo, + JobName: jobName, + JobType: string(models.JobTypeDebug), + }) + if errTemp != nil { + log.Error("InsertCloudbrainTemp failed: %v", errTemp.Error()) + return errTemp + } + } return err } - err = models.CreateCloudbrain(&models.Cloudbrain{ + task := &models.Cloudbrain{ Status: jobResult.Status, UserID: ctx.User.ID, RepoID: ctx.Repo.Repository.ID, JobID: jobResult.ID, JobName: jobName, - FlavorCode: flavor, + FlavorCode: spec.SourceSpecId, DisplayJobName: displayJobName, JobType: string(models.JobTypeDebug), Type: models.TypeCloudBrainTwo, @@ -298,16 +320,14 @@ func GenerateNotebook2(ctx *context.Context, displayJobName, jobName, uuid, desc Description: description, CreatedUnix: createTime, UpdatedUnix: createTime, - }) - - if err != nil { - return err + Spec: spec, } - task, err := models.GetCloudbrainByName(jobName) + + err = models.CreateCloudbrain(task) if err != nil { - log.Error("GetCloudbrainByName failed: %v", err.Error()) return err } + stringId := strconv.FormatInt(task.ID, 10) notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, stringId, displayJobName, models.ActionCreateDebugNPUTask) return nil @@ -315,32 +335,71 @@ 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{ - 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, + 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.Spec.SourceSpecId, + }, + Parameter: req.Parameters, + UserImageUrl: req.UserImageUrl, + UserCommand: req.UserCommand, }, - Parameter: req.Parameters, - }, - }) - if err != nil { - log.Error("CreateJob failed: %v", err.Error()) - return err + }) + } 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.Spec.SourceSpecId, + }, + Parameter: req.Parameters, + }, + }) + } + if createErr != nil { + log.Error("createTrainJob failed: %v", createErr.Error()) + if strings.HasPrefix(createErr.Error(), UnknownErrorPrefix) { + log.Info("(%s)unknown error, set temp status", req.DisplayJobName) + errTemp := models.InsertCloudbrainTemp(&models.CloudbrainTemp{ + JobID: models.TempJobId, + VersionID: models.TempVersionId, + Status: models.TempJobStatus, + Type: models.TypeCloudBrainTwo, + JobName: req.JobName, + JobType: string(models.JobTypeTrain), + }) + if errTemp != nil { + log.Error("InsertCloudbrainTemp failed: %v", errTemp.Error()) + return errTemp + } + } + return createErr } - jobId := strconv.FormatInt(jobResult.JobID, 10) - err = models.CreateCloudbrain(&models.Cloudbrain{ + createErr = models.CreateCloudbrain(&models.Cloudbrain{ Status: TransTrainJobStatus(jobResult.Status), UserID: ctx.User.ID, RepoID: ctx.Repo.Repository.ID, @@ -363,7 +422,7 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error BootFile: req.BootFile, DataUrl: req.DataUrl, LogUrl: req.LogUrl, - FlavorCode: req.FlavorCode, + FlavorCode: req.Spec.SourceSpecId, Description: req.Description, WorkServerNumber: req.WorkServerNumber, FlavorName: req.FlavorName, @@ -372,57 +431,122 @@ func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error TotalVersionCount: req.TotalVersionCount, CreatedUnix: createTime, UpdatedUnix: createTime, + Spec: req.Spec, }) - if err != nil { - log.Error("CreateCloudbrain(%s) failed:%v", req.DisplayJobName, err.Error()) - return err + 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 GenerateTrainJobVersion(ctx *context.Context, req *GenerateTrainJobReq, jobId string) (err error) { - createTime := timeutil.TimeStampNow() - jobResult, err := createTrainJobVersion(models.CreateTrainJobVersionParams{ +func GenerateModelConvertTrainJob(req *GenerateTrainJobReq) (*models.CreateTrainJobResult, error) { + + return 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 + }) +} + +func GenerateTrainJobVersion(ctx *context.Context, req *GenerateTrainJobReq, jobId string) (err error) { + createTime := timeutil.TimeStampNow() + var jobResult *models.CreateTrainJobResult + var createErr error + + 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.Spec.SourceSpecId, + }, + 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.Spec.SourceSpecId, + }, + Parameter: req.Parameters, + PreVersionId: req.PreVersionId, + }, + }, jobId) + } + if createErr != nil { + log.Error("createTrainJobVersion failed: %v", createErr.Error()) + if strings.HasPrefix(createErr.Error(), UnknownErrorPrefix) { + log.Info("(%s)unknown error, set temp status", req.DisplayJobName) + errTemp := models.InsertCloudbrainTemp(&models.CloudbrainTemp{ + JobID: jobId, + VersionID: models.TempVersionId, + Status: models.TempJobStatus, + Type: models.TypeCloudBrainTwo, + JobName: req.JobName, + JobType: string(models.JobTypeTrain), + }) + if errTemp != nil { + log.Error("InsertCloudbrainTemp failed: %v", errTemp.Error()) + return errTemp + } + } + return createErr } var jobTypes []string jobTypes = append(jobTypes, string(models.JobTypeTrain)) repo := ctx.Repo.Repository - VersionTaskList, VersionListCount, err := models.CloudbrainsVersionList(&models.CloudbrainsOptions{ + VersionTaskList, VersionListCount, createErr := models.CloudbrainsVersionList(&models.CloudbrainsOptions{ RepoID: repo.ID, Type: models.TypeCloudBrainTwo, JobTypes: jobTypes, JobID: strconv.FormatInt(jobResult.JobID, 10), }) - if err != nil { - ctx.ServerError("Cloudbrain", err) - return err + if createErr != nil { + ctx.ServerError("Cloudbrain", createErr) + return createErr } //将当前版本的isLatestVersion设置为"1"和任务数量更新,任务数量包括当前版本数VersionCount和历史创建的总版本数TotalVersionCount - err = models.CreateCloudbrain(&models.Cloudbrain{ + createErr = models.CreateCloudbrain(&models.Cloudbrain{ Status: TransTrainJobStatus(jobResult.Status), UserID: ctx.User.ID, RepoID: ctx.Repo.Repository.ID, @@ -447,7 +571,7 @@ func GenerateTrainJobVersion(ctx *context.Context, req *GenerateTrainJobReq, job DataUrl: req.DataUrl, LogUrl: req.LogUrl, PreVersionId: req.PreVersionId, - FlavorCode: req.FlavorCode, + FlavorCode: req.Spec.SourceSpecId, Description: req.Description, WorkServerNumber: req.WorkServerNumber, FlavorName: req.FlavorName, @@ -456,20 +580,21 @@ func GenerateTrainJobVersion(ctx *context.Context, req *GenerateTrainJobReq, job VersionCount: VersionListCount + 1, CreatedUnix: createTime, UpdatedUnix: createTime, + Spec: req.Spec, }) - if err != nil { - log.Error("CreateCloudbrain(%s) failed:%v", req.JobName, err.Error()) - return err + if createErr != nil { + log.Error("CreateCloudbrain(%s) failed:%v", req.JobName, createErr.Error()) + return createErr } //将训练任务的上一版本的isLatestVersion设置为"0" - err = models.SetVersionCountAndLatestVersion(strconv.FormatInt(jobResult.JobID, 10), VersionTaskList[0].VersionName, VersionCount, NotLatestVersion, TotalVersionCount) - if err != nil { - ctx.ServerError("Update IsLatestVersion failed", err) - return err + createErr = models.SetVersionCountAndLatestVersion(strconv.FormatInt(jobResult.JobID, 10), VersionTaskList[0].VersionName, VersionCountOne, NotLatestVersion, TotalVersionCount) + if createErr != nil { + ctx.ServerError("Update IsLatestVersion failed", createErr) + return createErr } - return err + return createErr } func TransTrainJobStatus(status int) string { @@ -546,21 +671,36 @@ func GenerateInferenceJob(ctx *context.Context, req *GenerateInferenceJobReq) (e PoolID: req.PoolID, CreateVersion: true, Flavor: models.Flavor{ - Code: req.FlavorCode, + Code: req.Spec.SourceSpecId, }, Parameter: req.Parameters, }, }) if err != nil { - log.Error("CreateJob failed: %v", err.Error()) + log.Error("createInferenceJob failed: %v", err.Error()) + if strings.HasPrefix(err.Error(), UnknownErrorPrefix) { + log.Info("(%s)unknown error, set temp status", req.DisplayJobName) + err = models.InsertCloudbrainTemp(&models.CloudbrainTemp{ + JobID: models.TempJobId, + VersionID: models.TempVersionId, + Status: models.TempJobStatus, + Type: models.TypeCloudBrainTwo, + JobName: req.JobName, + JobType: string(models.JobTypeInference), + }) + if err != nil { + log.Error("InsertCloudbrainTemp failed: %v", err.Error()) + return err + } + } return err } - attach, err := models.GetAttachmentByUUID(req.Uuid) - if err != nil { - log.Error("GetAttachmentByUUID(%s) failed:%v", strconv.FormatInt(jobResult.JobID, 10), err.Error()) - return err - } + // attach, err := models.GetAttachmentByUUID(req.Uuid) + // if err != nil { + // log.Error("GetAttachmentByUUID(%s) failed:%v", strconv.FormatInt(jobResult.JobID, 10), err.Error()) + // return err + // } jobID := strconv.FormatInt(jobResult.JobID, 10) err = models.CreateCloudbrain(&models.Cloudbrain{ Status: TransTrainJobStatus(jobResult.Status), @@ -574,7 +714,7 @@ func GenerateInferenceJob(ctx *context.Context, req *GenerateInferenceJobReq) (e VersionID: jobResult.VersionID, VersionName: jobResult.VersionName, Uuid: req.Uuid, - DatasetName: attach.Name, + DatasetName: req.DatasetName, CommitID: req.CommitID, EngineID: req.EngineID, TrainUrl: req.TrainUrl, @@ -583,7 +723,7 @@ func GenerateInferenceJob(ctx *context.Context, req *GenerateInferenceJobReq) (e BootFile: req.BootFile, DataUrl: req.DataUrl, LogUrl: req.LogUrl, - FlavorCode: req.FlavorCode, + FlavorCode: req.Spec.SourceSpecId, Description: req.Description, WorkServerNumber: req.WorkServerNumber, FlavorName: req.FlavorName, @@ -599,6 +739,7 @@ func GenerateInferenceJob(ctx *context.Context, req *GenerateInferenceJobReq) (e ResultUrl: req.ResultUrl, CreatedUnix: createTime, UpdatedUnix: createTime, + Spec: req.Spec, }) if err != nil { @@ -631,3 +772,461 @@ func GetNotebookImageName(imageId string) (string, error) { return imageName, nil } + +func InitSpecialPool() { + if SpecialPools == nil && setting.ModelArtsSpecialPools != "" { + json.Unmarshal([]byte(setting.ModelArtsSpecialPools), &SpecialPools) + } +} + +func HandleTrainJobInfo(task *models.Cloudbrain) error { + + result, err := GetTrainJob(task.JobID, strconv.FormatInt(task.VersionID, 10)) + if err != nil { + log.Error("GetTrainJob(%s) failed:%v", task.DisplayJobName, err) + return err + } + + if result != nil { + oldStatus := task.Status + task.Status = TransTrainJobStatus(result.IntStatus) + task.Duration = result.Duration / 1000 + task.TrainJobDuration = result.TrainJobDuration + + if task.StartTime == 0 && result.StartTime > 0 { + task.StartTime = timeutil.TimeStamp(result.StartTime / 1000) + } + task.TrainJobDuration = models.ConvertDurationToStr(task.Duration) + if task.EndTime == 0 && models.IsTrainJobTerminal(task.Status) && task.StartTime > 0 { + task.EndTime = task.StartTime.Add(task.Duration) + } + task.CorrectCreateUnix() + if oldStatus != task.Status { + notification.NotifyChangeCloudbrainStatus(task, oldStatus) + } + err = models.UpdateJob(task) + if err != nil { + log.Error("UpdateJob(%s) failed:%v", task.JobName, err) + return err + } + } + + return nil +} + +func HandleNotebookInfo(task *models.Cloudbrain) error { + + result, err := GetNotebook2(task.JobID) + if err != nil { + log.Error("GetNotebook2(%s) failed:%v", task.DisplayJobName, err) + return err + } + + if result != nil { + oldStatus := task.Status + task.Status = result.Status + if task.StartTime == 0 && result.Lease.UpdateTime > 0 { + task.StartTime = timeutil.TimeStamp(result.Lease.UpdateTime / 1000) + } + if task.EndTime == 0 && models.IsModelArtsDebugJobTerminal(task.Status) { + task.EndTime = timeutil.TimeStampNow() + } + task.CorrectCreateUnix() + task.ComputeAndSetDuration() + if oldStatus != task.Status { + notification.NotifyChangeCloudbrainStatus(task, oldStatus) + } + if task.FlavorCode == "" { + task.FlavorCode = result.Flavor + } + err = models.UpdateJob(task) + if err != nil { + log.Error("UpdateJob(%s) failed:%v", task.DisplayJobName, err) + return err + } + } + + return nil +} + +func SyncTempStatusJob() { + jobs, err := models.GetCloudBrainTempJobs() + if err != nil { + log.Error("GetCloudBrainTempJobs failed:%v", err.Error()) + return + } + + for _, temp := range jobs { + log.Info("start to handle record: %s", temp.JobName) + if temp.Type == models.TypeCloudBrainTwo { + if temp.JobType == string(models.JobTypeDebug) { + err = handleNotebook(temp) + if err != nil { + log.Error("handleNotebook falied:%v", err) + break + } + } else if temp.JobType == string(models.JobTypeTrain) || temp.JobType == string(models.JobTypeInference) { + _, err = models.GetCloudbrainByJobID(temp.JobID) + if err != nil { + //one version + err = handleTrainJob(temp) + if err != nil { + log.Error("handleTrainJob falied:%v", err) + break + } + } else { + //multi version + err = handleTrainJobMultiVersion(temp) + if err != nil { + log.Error("handleTrainJobMultiVersion falied:%v", err) + break + } + } + } + } + } + + return +} + +func handleNotebook(temp *models.CloudbrainTemp) error { + if temp.Status == models.TempJobStatus { + err := handleTempNotebook(temp) + if err != nil { + log.Error("handleTempNotebook failed:%v", err) + return err + } + } else if temp.Status == string(models.ModelArtsStopping) { + res, err := GetNotebook2(temp.JobID) + if err != nil { + log.Error("GetNotebook2 failed:%v", err) + return err + } + + temp.Status = res.Status + if temp.Status == string(models.ModelArtsStopped) { + err = models.UpdateCloudbrainTemp(temp) + if err != nil { + log.Error("UpdateCloudbrainTemp failed:%v", err) + return err + } + + _, err := DelNotebook2(temp.JobID) + if err != nil { + log.Error("DelNotebook2 failed:%v", err) + return err + } + + temp.Status = string(models.ModelArtsDeleted) + err = models.UpdateCloudbrainTemp(temp) + if err != nil { + log.Error("UpdateCloudbrainTemp failed:%v", err) + return err + } + } + } + + return nil +} + +func handleTempNotebook(temp *models.CloudbrainTemp) error { + var err error + var isExist bool + + for { + result, err := GetNotebookList(1000, 0, "createTime", "DESC", temp.JobName) + if err != nil { + log.Error("GetNotebookList failed:%v", err) + break + } + + temp.QueryTimes++ + err = models.UpdateCloudbrainTemp(temp) + if err != nil { + log.Error("UpdateCloudbrainTemp failed:%v", err) + } + + if result != nil { + for _, notebook := range result.NotebookList { + if temp.JobID == models.TempJobId { + //new notebook + if notebook.JobName == temp.JobName { + isExist = true + temp.Status = notebook.Status + temp.JobID = notebook.JobID + break + } + } else { + //restart: always can find one record + if notebook.JobName == temp.JobName { + if notebook.Status != string(models.ModelArtsStopped) { + isExist = true + temp.Status = notebook.Status + temp.JobID = notebook.JobID + break + } + } + } + } + + if isExist { + log.Info("find the record(%s), status(%s)", temp.JobName, temp.Status) + if temp.Status == string(models.ModelArtsCreateFailed) { + err = models.UpdateCloudbrainTemp(temp) + if err != nil { + log.Error("UpdateCloudbrainTemp failed:%v", err) + break + } + + _, err := DelNotebook2(temp.JobID) + if err != nil { + log.Error("DelNotebook2(%s) failed:%v", temp.JobName, err) + break + } + + temp.Status = string(models.ModelArtsDeleted) + } else { + _, err := ManageNotebook2(temp.JobID, models.NotebookAction{Action: models.ActionStop}) + if err != nil { + log.Error("ManageNotebook2(%s) failed:%v", temp.JobName, err) + break + } + temp.Status = string(models.ModelArtsStopping) + } + + models.UpdateCloudbrainTemp(temp) + } else { + log.Error("can not find the record(%s) till now", temp.JobName) + err = errors.New("not found") + break + } + } else { + log.Error("can not find the record(%s) till now", temp.JobName) + err = errors.New("not found") + break + } + + break + } + + if temp.QueryTimes >= setting.MaxTempQueryTimes && !isExist { + log.Info("reach MaxTempQueryTimes, set the job failed") + + temp.Status = string(models.ModelArtsTrainJobFailed) + err = models.UpdateCloudbrainTemp(temp) + if err != nil { + log.Error("UpdateCloudbrainTemp(%s) failed:%v", temp.JobName, err) + return err + } + } + + return err +} + +func handleTrainJob(temp *models.CloudbrainTemp) error { + if temp.Status == models.TempJobStatus { + err := handleTempTrainJob(temp) + if err != nil { + log.Error("handleTempTrainJob failed:%v", err) + return err + } + } else if temp.Status == string(models.ModelArtsTrainJobKilling) { + res, err := GetTrainJob(temp.JobID, temp.VersionID) + if err != nil { + log.Error("GetTrainJob failed:%v", err) + return err + } + + temp.Status = TransTrainJobStatus(res.IntStatus) + if temp.Status == string(models.ModelArtsTrainJobKilled) { + err = models.UpdateCloudbrainTemp(temp) + if err != nil { + log.Error("UpdateCloudbrainTemp failed:%v", err) + return err + } + + _, err := DelTrainJob(temp.JobID) + if err != nil { + log.Error("DelTrainJob failed:%v", err) + return err + } + + temp.Status = string(models.ModelArtsDeleted) + err = models.UpdateCloudbrainTemp(temp) + if err != nil { + log.Error("UpdateCloudbrainTemp failed:%v", err) + return err + } + } + } + + return nil +} + +func handleTrainJobMultiVersion(temp *models.CloudbrainTemp) error { + if temp.Status == models.TempJobStatus { + err := handleTempTrainJobMultiVersion(temp) + if err != nil { + log.Error("handleTempTrainJobMultiVersion failed:%v", err) + return err + } + } else if temp.Status == string(models.ModelArtsTrainJobKilling) { + res, err := GetTrainJob(temp.JobID, temp.VersionID) + if err != nil { + log.Error("GetTrainJob failed:%v", err) + return err + } + + temp.Status = TransTrainJobStatus(res.IntStatus) + if temp.Status == string(models.ModelArtsTrainJobKilled) { + err = models.UpdateCloudbrainTemp(temp) + if err != nil { + log.Error("UpdateCloudbrainTemp failed:%v", err) + return err + } + + _, err := DelTrainJobVersion(temp.JobID, temp.VersionID) + if err != nil { + log.Error("DelTrainJob failed:%v", err) + return err + } + + temp.Status = string(models.ModelArtsDeleted) + err = models.UpdateCloudbrainTemp(temp) + if err != nil { + log.Error("UpdateCloudbrainTemp failed:%v", err) + return err + } + } + + } + + return nil +} + +func handleTempTrainJobMultiVersion(temp *models.CloudbrainTemp) error { + var err error + var isExist bool + + for { + result, err := GetTrainJobVersionList(1000, 1, temp.JobID) + if err != nil { + log.Error("GetTrainJobVersionList failed:%v", err) + break + } + + temp.QueryTimes++ + err = models.UpdateCloudbrainTemp(temp) + if err != nil { + log.Error("UpdateCloudbrainTemp failed:%v", err) + } + + if result != nil { + count, _ := models.GetCloudbrainCountByJobName(temp.JobName, temp.JobType, temp.Type) + if result.VersionCount == int64(count+1) { + isExist = true + temp.Status = TransTrainJobStatus(result.JobVersionList[0].IntStatus) + temp.VersionID = strconv.FormatInt(result.JobVersionList[0].VersionID, 10) + + log.Info("find the record(%s), status(%s)", temp.JobName, temp.Status) + + _, err := StopTrainJob(temp.JobID, temp.VersionID) + if err != nil { + log.Error("StopTrainJob failed:%v", err) + break + } + temp.Status = string(models.ModelArtsTrainJobKilling) + err = models.UpdateCloudbrainTemp(temp) + if err != nil { + log.Error("UpdateCloudbrainTemp(%s) failed:%v", temp.JobName, err) + break + } + } else { + log.Error("can not find the record(%s) till now", temp.JobName) + err = errors.New("not found") + break + } + } + + break + } + + if temp.QueryTimes >= setting.MaxTempQueryTimes && !isExist { + log.Info("reach MaxTempQueryTimes, set the job failed") + + temp.Status = string(models.ModelArtsTrainJobFailed) + err = models.UpdateCloudbrainTemp(temp) + if err != nil { + log.Error("UpdateCloudbrainTemp(%s) failed:%v", temp.JobName, err) + return err + } + } + + return err +} + +func handleTempTrainJob(temp *models.CloudbrainTemp) error { + var err error + var isExist bool + + for { + result, err := GetTrainJobList(1000, 1, "create_time", "desc", temp.JobName) + if err != nil { + log.Error("GetTrainJobList failed:%v", err) + break + } + + temp.QueryTimes++ + err = models.UpdateCloudbrainTemp(temp) + if err != nil { + log.Error("UpdateCloudbrainTemp failed:%v", err) + } + + if result != nil { + for _, job := range result.JobList { + if temp.JobName == job.JobName && TransTrainJobStatus(job.IntStatus) != string(models.ModelArtsTrainJobFailed) { + isExist = true + temp.Status = TransTrainJobStatus(job.IntStatus) + temp.JobID = strconv.FormatInt(job.JobID, 10) + temp.VersionID = strconv.FormatInt(job.VersionID, 10) + + log.Info("find the record(%s), status(%s)", temp.JobName, temp.Status) + + _, err = StopTrainJob(temp.JobID, temp.VersionID) + if err != nil { + log.Error("StopTrainJob(%s) failed:%v", temp.JobName, err) + break + } + + temp.Status = string(models.ModelArtsTrainJobKilling) + err = models.UpdateCloudbrainTemp(temp) + if err != nil { + log.Error("UpdateCloudbrainTemp(%s) failed:%v", temp.JobName, err) + break + } + } + } + + if !isExist { + log.Error("can not find the record(%s) till now", temp.JobName) + err = errors.New("not found") + break + } + } + + break + } + + if temp.QueryTimes >= setting.MaxTempQueryTimes && !isExist { + log.Info("reach MaxTempQueryTimes, set the job failed") + + temp.Status = string(models.ModelArtsTrainJobFailed) + err = models.UpdateCloudbrainTemp(temp) + if err != nil { + log.Error("UpdateCloudbrainTemp(%s) failed:%v", temp.JobName, err) + return err + } + } + + return err +} diff --git a/modules/modelarts/resty.go b/modules/modelarts/resty.go index 6a2803cb1..fd1c467f3 100755 --- a/modules/modelarts/resty.go +++ b/modules/modelarts/resty.go @@ -37,6 +37,7 @@ const ( NotebookNotFound = "ModelArts.6404" NotebookNoPermission = "ModelArts.6407" NotebookInvalid = "ModelArts.6400" + UnknownErrorPrefix = "UNKNOWN:" ) func getRestyClient() *resty.Client { @@ -298,6 +299,10 @@ sendjob: return &result, fmt.Errorf("son.Unmarshal failed: %s", err.Error()) } + if res.StatusCode() == http.StatusBadGateway { + return &result, fmt.Errorf(UnknownErrorPrefix+"createNotebook2 failed(%s): %s", response.ErrorCode, response.ErrorMsg) + } + if len(response.ErrorCode) != 0 { log.Error("ManageNotebook2 failed(%s): %s", response.ErrorCode, response.ErrorMsg) if response.ErrorCode == modelartsIllegalToken && retry < 1 { @@ -472,7 +477,7 @@ sendjob: return &result, nil } -func createTrainJob(createJobParams models.CreateTrainJobParams) (*models.CreateTrainJobResult, error) { +func createTrainJobUserImage(createJobParams models.CreateUserImageTrainJobParams) (*models.CreateTrainJobResult, error) { checkSetting() client := getRestyClient() var result models.CreateTrainJobResult @@ -500,6 +505,63 @@ sendjob: goto sendjob } + if res.StatusCode() != http.StatusOK { + var temp models.ErrorResult + if err = json.Unmarshal([]byte(res.String()), &temp); err != nil { + log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error()) + return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error()) + } + log.Error("createTrainJobUserImage failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) + bootFileErrorMsg := "Invalid OBS path '" + createJobParams.Config.BootFileUrl + "'." + dataSetErrorMsg := "Invalid OBS path '" + createJobParams.Config.DataUrl + "'." + if temp.ErrorMsg == bootFileErrorMsg { + log.Error("启动文件错误!createTrainJobUserImage failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) + return &result, fmt.Errorf("启动文件错误!") + } + if temp.ErrorMsg == dataSetErrorMsg { + log.Error("数据集错误!createTrainJobUserImage failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) + return &result, fmt.Errorf("数据集错误!") + } + if res.StatusCode() == http.StatusBadGateway { + return &result, fmt.Errorf(UnknownErrorPrefix+"createTrainJobUserImage failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) + } else { + return &result, fmt.Errorf("createTrainJobUserImage failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) + } + } + + if !result.IsSuccess { + log.Error("createTrainJobUserImage failed(%s): %s", result.ErrorCode, result.ErrorMsg) + return &result, fmt.Errorf("createTrainJobUserImage failed(%s): %s", result.ErrorCode, result.ErrorMsg) + } + + return &result, nil +} + +func createTrainJob(createJobParams models.CreateTrainJobParams) (*models.CreateTrainJobResult, error) { + checkSetting() + client := getRestyClient() + var result models.CreateTrainJobResult + + retry := 0 + +sendjob: + res, err := client.R(). + SetHeader("Content-Type", "application/json"). + SetAuthToken(TOKEN). + SetBody(createJobParams). + SetResult(&result). + Post(HOST + "/v1/" + setting.ProjectID + urlTrainJob) + + if err != nil { + return nil, fmt.Errorf("resty create train-job: %s", err) + } + + if res.StatusCode() == http.StatusUnauthorized && retry < 1 { + retry++ + _ = getToken() + goto sendjob + } + if res.StatusCode() != http.StatusOK { var temp models.ErrorResult if err = json.Unmarshal([]byte(res.String()), &temp); err != nil { @@ -507,17 +569,21 @@ sendjob: 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 { + 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 { + 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 res.StatusCode() == http.StatusBadGateway { + return &result, fmt.Errorf(UnknownErrorPrefix+"createTrainJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) + } else { + return &result, fmt.Errorf("createTrainJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) + } } if !result.IsSuccess { @@ -535,6 +601,64 @@ func createTrainJobVersion(createJobVersionParams models.CreateTrainJobVersionPa retry := 0 +sendjob: + res, err := client.R(). + SetHeader("Content-Type", "application/json"). + SetAuthToken(TOKEN). + SetBody(createJobVersionParams). + SetResult(&result). + Post(HOST + "/v1/" + setting.ProjectID + urlTrainJob + "/" + jobID + "/versions") + + if err != nil { + return nil, fmt.Errorf("resty create train-job version: %s", err) + } + + if res.StatusCode() == http.StatusUnauthorized && retry < 1 { + retry++ + _ = getToken() + goto sendjob + } + + if res.StatusCode() != http.StatusOK { + var temp models.ErrorResult + if err = json.Unmarshal([]byte(res.String()), &temp); err != nil { + log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error()) + return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error()) + } + + log.Error("createTrainJobVersion failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) + bootFileErrorMsg := "Invalid OBS path '" + createJobVersionParams.Config.BootFileUrl + "'." + dataSetErrorMsg := "Invalid OBS path '" + createJobVersionParams.Config.DataUrl + "'." + if temp.ErrorMsg == bootFileErrorMsg { + log.Error("启动文件错误!createTrainJobVersion failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) + return &result, fmt.Errorf("启动文件错误!") + } + if temp.ErrorMsg == dataSetErrorMsg { + log.Error("数据集错误!createTrainJobVersion failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) + return &result, fmt.Errorf("数据集错误!") + } + if res.StatusCode() == http.StatusBadGateway { + return &result, fmt.Errorf(UnknownErrorPrefix+"createTrainJobVersion failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) + } else { + return &result, fmt.Errorf("createTrainJobVersion failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) + } + } + + if !result.IsSuccess { + log.Error("createTrainJobVersion failed(%s): %s", result.ErrorCode, result.ErrorMsg) + return &result, fmt.Errorf("createTrainJobVersion failed(%s): %s", result.ErrorCode, result.ErrorMsg) + } + + return &result, nil +} + +func createTrainJobVersionUserImage(createJobVersionParams models.CreateTrainJobVersionUserImageParams, jobID string) (*models.CreateTrainJobResult, error) { + checkSetting() + client := getRestyClient() + var result models.CreateTrainJobResult + + retry := 0 + sendjob: res, err := client.R(). SetHeader("Content-Type", "application/json"). @@ -650,9 +774,6 @@ sendjob: goto sendjob } - //temp, _ := json.Marshal(req) - //log.Info("%s", temp) - if res.StatusCode() != http.StatusOK { var temp models.ErrorResult if err = json.Unmarshal([]byte(res.String()), &temp); err != nil { @@ -1061,7 +1182,11 @@ sendjob: log.Error("数据集错误!createInferenceJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) return &result, fmt.Errorf("数据集错误!") } - return &result, fmt.Errorf("createInferenceJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) + if res.StatusCode() == http.StatusBadGateway { + return &result, fmt.Errorf(UnknownErrorPrefix+"createInferenceJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) + } else { + return &result, fmt.Errorf("createInferenceJob failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) + } } if !result.IsSuccess { @@ -1101,7 +1226,11 @@ sendjob: err = json.Unmarshal(res.Body(), &response) if err != nil { log.Error("json.Unmarshal failed: %s", err.Error()) - return &result, fmt.Errorf("son.Unmarshal failed: %s", err.Error()) + return &result, fmt.Errorf("json.Unmarshal failed: %s", err.Error()) + } + + if res.StatusCode() == http.StatusBadGateway { + return &result, fmt.Errorf(UnknownErrorPrefix+"createNotebook2 failed(%s): %s", response.ErrorCode, response.ErrorMsg) } if len(response.ErrorCode) != 0 { @@ -1160,3 +1289,139 @@ sendjob: return &result, nil } + +func GetTrainJobList(perPage, page int, sortBy, order, searchContent string) (*models.GetTrainJobListResult, error) { + checkSetting() + client := getRestyClient() + var result models.GetTrainJobListResult + + retry := 0 + +sendjob: + res, err := client.R(). + SetQueryParams(map[string]string{ + "per_page": strconv.Itoa(perPage), + "page": strconv.Itoa(page), + "sortBy": sortBy, + "order": order, + "search_content": searchContent, + }). + SetAuthToken(TOKEN). + SetResult(&result). + Get(HOST + "/v1/" + setting.ProjectID + urlTrainJob) + + if err != nil { + return nil, fmt.Errorf("resty GetTrainJobList: %v", err) + } + + if res.StatusCode() == http.StatusUnauthorized && retry < 1 { + retry++ + _ = getToken() + goto sendjob + } + + if res.StatusCode() != http.StatusOK { + var temp models.ErrorResult + if err = json.Unmarshal([]byte(res.String()), &temp); err != nil { + log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error()) + return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error()) + } + log.Error("GetTrainJobList failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) + return &result, fmt.Errorf(temp.ErrorMsg) + } + + if !result.IsSuccess { + log.Error("GetTrainJobList failed(%s): %s", result.ErrorCode, result.ErrorMsg) + return &result, fmt.Errorf(result.ErrorMsg) + } + + return &result, nil +} + +func GetTrainJobVersionList(perPage, page int, jobID string) (*models.GetTrainJobVersionListResult, error) { + checkSetting() + client := getRestyClient() + var result models.GetTrainJobVersionListResult + + retry := 0 + +sendjob: + res, err := client.R(). + SetQueryParams(map[string]string{ + "per_page": strconv.Itoa(perPage), + "page": strconv.Itoa(page), + }). + SetAuthToken(TOKEN). + SetResult(&result). + Get(HOST + "/v1/" + setting.ProjectID + urlTrainJob + "/" + jobID + "/versions") + + if err != nil { + return nil, fmt.Errorf("resty GetTrainJobVersionList: %v", err) + } + + if res.StatusCode() == http.StatusUnauthorized && retry < 1 { + retry++ + _ = getToken() + goto sendjob + } + + if res.StatusCode() != http.StatusOK { + var temp models.ErrorResult + if err = json.Unmarshal([]byte(res.String()), &temp); err != nil { + log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error()) + return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error()) + } + log.Error("GetTrainJobVersionList failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) + return &result, fmt.Errorf(temp.ErrorMsg) + } + + if !result.IsSuccess { + log.Error("GetTrainJobVersionList failed(%s): %s", result.ErrorCode, result.ErrorMsg) + return &result, fmt.Errorf(result.ErrorMsg) + } + + return &result, nil +} + +func GetNotebookList(limit, offset int, sortBy, order, searchContent string) (*models.GetNotebookListResult, error) { + checkSetting() + client := getRestyClient() + var result models.GetNotebookListResult + + retry := 0 + +sendjob: + res, err := client.R(). + SetQueryParams(map[string]string{ + "limit": strconv.Itoa(limit), + "offset": strconv.Itoa(offset), + "name": searchContent, + "sort_key": sortBy, + "sort_dir": order, + }). + SetAuthToken(TOKEN). + SetResult(&result). + Get(HOST + "/v1/" + setting.ProjectID + urlNotebook2) + + if err != nil { + return nil, fmt.Errorf("resty GetNotebookList: %v", err) + } + + if res.StatusCode() == http.StatusUnauthorized && retry < 1 { + retry++ + _ = getToken() + goto sendjob + } + + if res.StatusCode() != http.StatusOK { + var temp models.ErrorResult + if err = json.Unmarshal([]byte(res.String()), &temp); err != nil { + log.Error("json.Unmarshal failed(%s): %v", res.String(), err.Error()) + return &result, fmt.Errorf("json.Unmarshal failed(%s): %v", res.String(), err.Error()) + } + log.Error("GetNotebookList failed(%d):%s(%s)", res.StatusCode(), temp.ErrorCode, temp.ErrorMsg) + return &result, fmt.Errorf(temp.ErrorMsg) + } + + return &result, nil +} diff --git a/modules/notification/base/notifier.go b/modules/notification/base/notifier.go index 1429dc090..354de1ab2 100644 --- a/modules/notification/base/notifier.go +++ b/modules/notification/base/notifier.go @@ -62,4 +62,6 @@ type Notifier interface { NotifyCreateImage(doer *models.User, image models.Image) NotifyImageRecommend(optUser *models.User, image *models.Image, action string) NotifyChangeUserAvatar(user *models.User, form auth.AvatarForm) + + NotifyChangeCloudbrainStatus(cloudbrain *models.Cloudbrain, oldStatus string) } diff --git a/modules/notification/base/null.go b/modules/notification/base/null.go index 27ed24f15..5cd812b5d 100644 --- a/modules/notification/base/null.go +++ b/modules/notification/base/null.go @@ -176,3 +176,7 @@ func (*NullNotifier) NotifyImageRecommend(optUser *models.User, image *models.Im func (*NullNotifier) NotifyChangeUserAvatar(user *models.User, form auth.AvatarForm) { } + +func (*NullNotifier) NotifyChangeCloudbrainStatus(cloudbrain *models.Cloudbrain, oldStatus string) { + +} diff --git a/modules/notification/notification.go b/modules/notification/notification.go index 6c96d58da..e98221886 100644 --- a/modules/notification/notification.go +++ b/modules/notification/notification.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/modules/notification/mail" "code.gitea.io/gitea/modules/notification/ui" "code.gitea.io/gitea/modules/notification/webhook" + wechatNotifier "code.gitea.io/gitea/modules/notification/wechat" "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" ) @@ -36,6 +37,7 @@ func NewContext() { RegisterNotifier(indexer.NewNotifier()) RegisterNotifier(webhook.NewNotifier()) RegisterNotifier(action.NewNotifier()) + RegisterNotifier(wechatNotifier.NewNotifier()) } // NotifyUploadAttachment notifies attachment upload message to notifiers @@ -305,3 +307,10 @@ func NotifyChangeUserAvatar(user *models.User, form auth.AvatarForm) { notifier.NotifyChangeUserAvatar(user, form) } } + +// NotifyChangeCloudbrainStatus +func NotifyChangeCloudbrainStatus(cloudbrain *models.Cloudbrain, oldStatus string) { + for _, notifier := range notifiers { + notifier.NotifyChangeCloudbrainStatus(cloudbrain, oldStatus) + } +} diff --git a/modules/notification/wechat/wechat.go b/modules/notification/wechat/wechat.go new file mode 100644 index 000000000..cd72bb54e --- /dev/null +++ b/modules/notification/wechat/wechat.go @@ -0,0 +1,36 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package wechat + +import ( + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/auth/wechat" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/notification/base" +) + +type wechatNotifier struct { + base.NullNotifier +} + +var ( + _ base.Notifier = &wechatNotifier{} +) + +// NewNotifier create a new wechatNotifier notifier +func NewNotifier() base.Notifier { + return &wechatNotifier{} +} + +func (*wechatNotifier) NotifyChangeCloudbrainStatus(cloudbrain *models.Cloudbrain, oldStatus string) { + log.Info("NotifyChangeCloudbrainStatus cloudbrain.id=%d cloudbrain.status=%s oldStatus=%s", cloudbrain.ID, cloudbrain.Status, oldStatus) + operateType := wechat.GetJobOperateTypeFromCloudbrainStatus(cloudbrain) + if operateType == "" { + log.Info("NotifyChangeCloudbrainStatus operateType is incorrect") + return + } + template := wechat.GetTemplateFromOperateType(operateType) + go wechat.SendTemplateMsg(template, &wechat.TemplateContext{Cloudbrain: cloudbrain}, cloudbrain.UserID) +} diff --git a/modules/setting/cloudbrain.go b/modules/setting/cloudbrain.go index 2d80eea25..c06676243 100755 --- a/modules/setting/cloudbrain.go +++ b/modules/setting/cloudbrain.go @@ -5,6 +5,7 @@ type CloudbrainLoginConfig struct { Password string Host string ImageURLPrefix string + Expiration string } var ( @@ -17,5 +18,6 @@ func GetCloudbrainConfig() CloudbrainLoginConfig { Cloudbrain.Password = cloudbrainSec.Key("PASSWORD").MustString("") Cloudbrain.Host = cloudbrainSec.Key("REST_SERVER_HOST").MustString("") Cloudbrain.ImageURLPrefix = cloudbrainSec.Key("IMAGE_URL_PREFIX").MustString("") + Cloudbrain.Expiration = cloudbrainSec.Key("EXPIRATION").MustString("604800") return Cloudbrain } diff --git a/modules/setting/repository.go b/modules/setting/repository.go index ee4f8b379..1eb1875b2 100644 --- a/modules/setting/repository.go +++ b/modules/setting/repository.go @@ -193,8 +193,9 @@ var ( Wiki: []string{"never"}, }, } - RepoRootPath string - ScriptType = "bash" + RepoRootPath string + RepoMaxReferenceDatasetNum int + ScriptType = "bash" ) func newRepository() { @@ -210,6 +211,8 @@ func newRepository() { Repository.UseCompatSSHURI = sec.Key("USE_COMPAT_SSH_URI").MustBool() Repository.MaxCreationLimit = sec.Key("MAX_CREATION_LIMIT").MustInt(-1) RepoRootPath = sec.Key("ROOT").MustString(path.Join(homeDir, "gitea-repositories")) + RepoMaxReferenceDatasetNum = sec.Key("MAX_REF_DATASET_NUM").MustInt(20) + forcePathSeparator(RepoRootPath) if !filepath.IsAbs(RepoRootPath) { RepoRootPath = filepath.Join(AppWorkPath, RepoRootPath) diff --git a/modules/setting/setting.go b/modules/setting/setting.go index dca41b8a0..10950a90c 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 @@ -456,20 +466,26 @@ var ( DecompressOBSTaskName string //cloudbrain config - 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 - 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 @@ -526,22 +542,27 @@ var ( DebugHost string ImageInfos string Capacity int + MaxTempQueryTimes int //train-job - ResourcePools string - Engines string - EngineVersions string - FlavorInfos string - TrainJobFLAVORINFOS string + ResourcePools string + Engines string + EngineVersions string + FlavorInfos string + TrainJobFLAVORINFOS string + ModelArtsSpecialPools string //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 @@ -573,6 +594,16 @@ var ( TreePathOfAutoMsgReply string TreePathOfSubscribe string + //wechat template msg config + CloudbrainStartedTemplateId string + CloudbrainStartedNotifyList []string + CloudbrainStartedTitle string + CloudbrainStartedRemark string + CloudbrainStoppedTemplateId string + CloudbrainStoppedNotifyList []string + CloudbrainStoppedTitle string + CloudbrainStoppedRemark string + //nginx proxy PROXYURL string RadarMap = struct { @@ -623,6 +654,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. @@ -1322,9 +1371,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) @@ -1381,11 +1435,13 @@ func NewContext() { Flavor = sec.Key("FLAVOR").MustString("") ImageInfos = sec.Key("IMAGE_INFOS").MustString("") Capacity = sec.Key("IMAGE_INFOS").MustInt(100) + MaxTempQueryTimes = sec.Key("MAX_TEMP_QUERY_TIMES").MustInt(30) ResourcePools = sec.Key("Resource_Pools").MustString("") Engines = sec.Key("Engines").MustString("") EngineVersions = sec.Key("Engine_Versions").MustString("") FlavorInfos = sec.Key("FLAVOR_INFOS").MustString("") TrainJobFLAVORINFOS = sec.Key("TrainJob_FLAVOR_INFOS").MustString("") + ModelArtsSpecialPools = sec.Key("SPECIAL_POOL").MustString("") sec = Cfg.Section("elk") ElkUrl = sec.Key("ELKURL").MustString("") @@ -1400,7 +1456,7 @@ func NewContext() { WechatApiHost = sec.Key("HOST").MustString("https://api.weixin.qq.com") WechatApiTimeoutSeconds = sec.Key("TIMEOUT_SECONDS").MustInt(3) WechatAppId = sec.Key("APP_ID").MustString("wxba77b915a305a57d") - WechatAppSecret = sec.Key("APP_SECRET").MustString("e48e13f315adc32749ddc7057585f198") + WechatAppSecret = sec.Key("APP_SECRET").MustString("") WechatQRCodeExpireSeconds = sec.Key("QR_CODE_EXPIRE_SECONDS").MustInt(120) WechatAuthSwitch = sec.Key("AUTH_SWITCH").MustBool(false) UserNameOfWechatReply = sec.Key("AUTO_REPLY_USER_NAME").MustString("OpenIOSSG") @@ -1409,6 +1465,14 @@ func NewContext() { TreePathOfAutoMsgReply = sec.Key("AUTO_REPLY_TREE_PATH").MustString("wechat/auto_reply.json") TreePathOfSubscribe = sec.Key("SUBSCRIBE_TREE_PATH").MustString("wechat/subscribe_reply.json") WechatAuthSwitch = sec.Key("AUTH_SWITCH").MustBool(false) + CloudbrainStartedTemplateId = sec.Key("CLOUDBRAIN_STARTED_TEMPLATE_ID").MustString("") + CloudbrainStartedNotifyList = strings.Split(sec.Key("CLOUDBRAIN_STARTED_NOTIFY_LIST").MustString("DEBUG"), ",") + CloudbrainStartedTitle = sec.Key("CLOUDBRAIN_STARTED_TITLE").MustString("您好,您提交的算力资源申请已通过,任务已启动,请您关注运行情况。") + CloudbrainStartedRemark = sec.Key("CLOUDBRAIN_STARTED_REMARK").MustString("感谢您的耐心等待。") + CloudbrainStoppedTemplateId = sec.Key("CLOUDBRAIN_STOPPED_TEMPLATE_ID").MustString("") + CloudbrainStoppedNotifyList = strings.Split(sec.Key("CLOUDBRAIN_STOPPED_NOTIFY_LIST").MustString("TRAIN"), ",") + CloudbrainStoppedTitle = sec.Key("CLOUDBRAIN_STOPPED_TITLE").MustString("您好,您申请的算力资源已结束使用,任务已完成运行,请您关注运行结果。") + CloudbrainStoppedRemark = sec.Key("CLOUDBRAIN_STOPPED_REMARK").MustString("感谢您的耐心等待。") sec = Cfg.Section("point") CloudBrainPaySwitch = sec.Key("CLOUDBRAIN_PAY_SWITCH").MustBool(false) @@ -1426,6 +1490,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() { @@ -1436,7 +1521,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() { diff --git a/modules/storage/minio_ext.go b/modules/storage/minio_ext.go index 514ac7204..4b738c068 100755 --- a/modules/storage/minio_ext.go +++ b/modules/storage/minio_ext.go @@ -179,31 +179,39 @@ func GetOneLevelAllObjectUnderDirMinio(bucket string, prefixRootPath string, rel output, err := core.ListObjects(bucket, Prefix, "", "", 1000) fileInfos := make([]FileInfo, 0) prefixLen := len(Prefix) + fileMap := make(map[string]bool, 0) if err == nil { for _, val := range output.Contents { + log.Info("val key=" + val.Key) var isDir bool var fileName string if val.Key == 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:] } + 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) } @@ -217,6 +225,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..2cb3af927 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,28 @@ 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:] } 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 +470,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 +622,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 797ccdb2e..3e424454b 100755 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -97,23 +97,24 @@ func NewFuncMap() []template.FuncMap { "AllowedReactions": func() []string { return setting.UI.Reactions }, - "AvatarLink": models.AvatarLink, - "Safe": Safe, - "SafeJS": SafeJS, - "Str2html": Str2html, - "subOne": subOne, - "TimeSince": timeutil.TimeSince, - "TimeSinceUnix": timeutil.TimeSinceUnix, - "TimeSinceUnix1": timeutil.TimeSinceUnix1, - "AttachmentResourceType": dataset.GetResourceType, - "AttachmentStatus": dataset.GetStatusText, - "TimeSinceUnixShort": timeutil.TimeSinceUnixShort, - "RawTimeSince": timeutil.RawTimeSince, - "FileSize": base.FileSize, - "PrettyNumber": base.PrettyNumber, - "Subtract": base.Subtract, - "EntryIcon": base.EntryIcon, - "MigrationIcon": MigrationIcon, + "AvatarLink": models.AvatarLink, + "Safe": Safe, + "SafeJS": SafeJS, + "Str2html": Str2html, + "subOne": subOne, + "TimeSince": timeutil.TimeSince, + "TimeSinceUnix": timeutil.TimeSinceUnix, + "TimeSinceUnix1": timeutil.TimeSinceUnix1, + "AttachmentResourceType": dataset.GetResourceType, + "AttachmentStatus": dataset.GetStatusText, + "IsShowDataSetOfCurrentRepo": dataset.IsShowDataSetOfCurrentRepo, + "TimeSinceUnixShort": timeutil.TimeSinceUnixShort, + "RawTimeSince": timeutil.RawTimeSince, + "FileSize": base.FileSize, + "PrettyNumber": base.PrettyNumber, + "Subtract": base.Subtract, + "EntryIcon": base.EntryIcon, + "MigrationIcon": MigrationIcon, "Add": func(a, b int) int { return a + b }, @@ -357,13 +358,15 @@ func NewTextFuncMap() []texttmpl.FuncMap { "AppDomain": func() string { return setting.Domain }, - "TimeSince": timeutil.TimeSince, - "TimeSinceUnix": timeutil.TimeSinceUnix, - "TimeSinceUnix1": timeutil.TimeSinceUnix1, - "TimeSinceUnixShort": timeutil.TimeSinceUnixShort, - "RawTimeSince": timeutil.RawTimeSince, - "AttachmentResourceType": dataset.GetResourceType, - "AttachmentStatus": dataset.GetStatusText, + "TimeSince": timeutil.TimeSince, + "TimeSinceUnix": timeutil.TimeSinceUnix, + "TimeSinceUnix1": timeutil.TimeSinceUnix1, + "TimeSinceUnixShort": timeutil.TimeSinceUnixShort, + "RawTimeSince": timeutil.RawTimeSince, + "AttachmentResourceType": dataset.GetResourceType, + "AttachmentStatus": dataset.GetStatusText, + "IsShowDataSetOfCurrentRepo": dataset.IsShowDataSetOfCurrentRepo, + "DateFmtLong": func(t time.Time) string { return t.Format(time.RFC1123Z) }, diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 15f33fa68..e4567adcb 100755 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -99,6 +99,7 @@ error500= Sorry, the site has encountered some problems, we are trying to initialized first ; debug_task_running_limit =Running time: no more than 4 hours, it will automatically stop if it exceeds 4 hours; dataset_desc = Dataset: Cloud Brain 1 provides CPU/GPU,Cloud Brain 2 provides Ascend NPU.And dataset also needs to be uploaded to the corresponding environment; -platform_instructions = Instructions for use: You can refer to the Xiaobai training camp course of Qizhi AI collaboration platform. +platform_instructions = Instructions for use: You can refer to the Xiaobai training camp course of Openi AI collaboration platform. model_not_exist = Model file: You do not have a model file yet, please generate and export the model through the training task first ; benchmark_leaderboards = Benchmark leaderboards @@ -1209,7 +1233,7 @@ model.manage.version = Version model.manage.label = Label model.manage.size = Size model.manage.create_time = Create Time -model.manage.Description = Description +model.manage.description = Description model.manage.Accuracy = Accuracy model.manage.F1 = F1 model.manage.Precision = Precision @@ -1217,6 +1241,57 @@ 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 + +model.manage.notcreatemodel=No model has been created. +model.manage.init1=Code version: You have not initialized the code repository, please +model.manage.init2=initialized first ; +model.manage.createtrainjob_tip=Training task: you haven't created a training task, please create it first +model.manage.createtrainjob=Training task +model.manage.delete=Delete Model +model.manage.delete_confirm=Are you sure to delete this model? Once this model is deleted, it cannot be restored. +model.manage.select.trainjob=Select train task +model.manage.select.version=Select version +model.manage.engine=Model engine +model.manage.select.engine=Select model engine +model.manage.modelfile=Model file +model.manage.modellabel=Model label +model.manage.modeldesc=Model description +model.manage.baseinfo=Base Information +modelconvert.notcreate=No model conversion task has been created. +modelconvert.importfirst1=Please import first +modelconvert.importfirst2=download model +modelconvert.importfirst3=, then converts it. +modelconvert.download=Download +modelconvert.taskname=Task name +modelconvert.modelname=Model name +modelconvert.selectmodel=Select model +modelconvert.modelversion=Model version +modelconvert.selectversion=Select version +modelconvert.selectmodelfile=Select model file +modelconvert.taskstatus=Status +modelconvert.srcengine=Source model engine +modelconvert.outputformat=Output format +modelconvert.createtime=Created time +modelconvert.inputdataformat=Input data format +modelconvert.inputshape=Input tensor shape +modelconvert.inputshapetip=For example: 1,1,32,32, corresponding to the input data format. +modelconvert.netoutputdata=Network output data type +modelconvert.taskdesc=Task description +modelconvert.newtask=New +modelconvert.createtask=Create model transformation task +modelconvert.taskurlname=Model transformation task +log_scroll_start=Scroll to top +log_scroll_end=Scroll to bottom +modelconvert.tasknameempty=Please enter a task name. +modelconvert.inputshapeerror=Format input error, please input such as: 1,1,32,32, corresponding to the input data format. + +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=You have no right to do the operation. 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。 @@ -2309,7 +2384,7 @@ topic.count_prompt = You can not select more than 25 topics topic.format_prompt = Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long. imagetopic.format_prompt = Topics can be up to 35 characters long. use_repo_agreement=I promise that the content of this warehouse does not violate any national laws and regulations. During the use of the warehouse, I will abide by the OpenI community management regulations and platform usage rules, and will not conduct malicious attacks, mining, or any other illegal or disruptive platform order. Information release and related behaviors. For more information please refer to -openi_use_agreement=OpenI Qizhi Community Platform Use Agreement. +openi_use_agreement=OpenI Openi Community Platform Use Agreement. [org] org_name_holder = Organization Name org_full_name_holder = Organization Full Name @@ -2950,6 +3025,13 @@ notices.desc = Description notices.op = Op. notices.delete_success = The system notices have been deleted. +user_management = User Management +resource_management = Resource Management +resource_pool = Resource Pool(queue) +resource_price = Resource Price +application_scenario = Application Scenario +system_configuration = System Configuration + [action] create_repo = created repository %s rename_repo = renamed repository from %[1]s to %[3]s @@ -2976,15 +3058,15 @@ mirror_sync_delete = synced and deleted reference %[2]s at %s#%[2]s` reject_pull_request = `suggested changes for %s#%[2]s` upload_dataset=`upload dataset %s` -task_gpudebugjob=`created CPU/GPU type debugging task%s` +task_gpudebugjob=`created CPU/GPU type debugging task %s` task_npudebugjob=`created NPU type debugging task %s` -task_nputrainjob=`created NPU training task%s` +task_nputrainjob=`created NPU training task %s` task_inferencejob=`created reasoning task %s` task_benchmark=`created profiling task %s` task_createmodel=`created new model %s` -task_gputrainjob=`created CPU/GPU training task%s` -task_c2netnputrainjob=`created NPU training task%s` -task_c2netgputrainjob=`created CPU/GPU training task%s` +task_gputrainjob=`created CPU/GPU training task %s` +task_c2netnputrainjob=`created NPU training task %s` +task_c2netgputrainjob=`created CPU/GPU training task %s` [tool] ago = %s ago @@ -3073,9 +3155,13 @@ Platform_Tutorial = Tutorial foot.advice_feedback = Feedback [cloudbrain] +all_resource_cluster=All Cluster +all_ai_center=All Computing NET resource_cluster = Resource Cluster resource_cluster_openi = OpenI Resource Cluster resource_cluster_c2net = China Computing NET +resource_cluster_openi_simple = OpenI +resource_cluster_c2net_simple = C²NET compute_resource = Computing resources task_name = Task name task_type = Task type @@ -3096,13 +3182,17 @@ select_dataset = select dataset specification = specification select_specification = select specification description = description +wrong_specification=You cannot use this specification, please choose another item. +resource_use=Resource Occupancy job_name_rule = Please enter letters, numbers, _ and - up to 64 characters and cannot end with a dash (-). -dataset_path_rule = The dataset location is stored in the environment variable data_url, and the training output path is stored in the environment variable train_url. +train_dataset_path_rule = The dataset location is stored in the environment variable data_url, and the output path is stored in the environment variable train_url. +infer_dataset_path_rule = The dataset location is stored in the environment variable data_url, and the output path is stored in the environment variable result_url. view_sample = View sample inference_output_path_rule = The inference output path is stored in the environment variable result_url. model_file_path_rule=The model file location is stored in the environment variable ckpt_url - +model_file_postfix_rule = The supported format of the model file is [ckpt, pb, h5, json, pkl, pth, t7, pdparams, onnx, pbtxt, keras, mlmodel, cfg, pt] +model_convert_postfix_rule = The supported format of the model file is [.pth, .pkl, .onnx, .mindir, .ckpt, .pb] delete_task = Delete task task_delete_confirm = Are you sure you want to delete this task? Once this task is deleted, it cannot be recovered. operate_confirm = confirm @@ -3112,6 +3202,9 @@ gpu_num = GPU cpu_num = CPU memory = Memory shared_memory = Shared Memory +gpu_memory = GPU Memory +free = Free +point_hr = Point/hr DEBUG = DEBUG @@ -3122,7 +3215,16 @@ INFERENCE = INFERENCE BENCHMARK = BENCHMARK brain_area = Brain Area +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. +load_code_failed=Fail to load code, please check if the right branch is selected. + error.dataset_select = dataset select error:the count exceed the limit or has same name +new_train_gpu_tooltips = The code is storaged in %s, the dataset is storaged in %s, and please put your model into %s then you can download it online +new_infer_gpu_tooltips = The dataset is stored in %s, the model file is stored in %s, please store the inference output in %s for subsequent downloads. [points] points = points diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 230bd35b1..0c6942e80 100755 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -100,6 +100,7 @@ error500=抱歉,站点遇到一些问题,我们正尝试修复网页 [error] occurred=发生错误 report_message=发生错误 +no_right=您没有权限执行本操作。 [install] install=安装页面 @@ -255,13 +256,16 @@ page_dev_env_desc2_title=模型管理与共享 page_dev_env_desc2_desc=将模型与代码版本建立关联,可以基于代码历史版本,使用不同的方式调整模型,并将结果保存下来;训练好的模型可以开放共享,让更多人的使用模型测试并提出反馈 page_dev_env_desc3_title=一次配置,多次使用 page_dev_env_desc3_desc=提供执行环境共享,一次配置,多次使用,降低模型开发门槛,避免花费重复的时间配置复杂的环境 -page_dev_yunlao=鹏城云脑开源协同 -page_dev_yunlao_desc1=平台已经与鹏城云脑打通,可以利用鹏城云脑的丰富算力资源,完成AI开发任务 -page_dev_yunlao_desc2=鹏城云脑现有AI算力100P FLOPS@FP16(每秒十亿亿次半精度浮点计算),主要硬件基础设施由搭载英伟达Tesla V100 的GPU服务器和搭载鲲鹏、昇腾处理器的Atlas 900 AI集群构成 -page_dev_yunlao_desc3=开发者可以根据使用需求,自由选择相应计算资源,可以测试模型在不同硬件环境下的适配能力、性能、稳定性等 -page_dev_yunlao_desc4=如果您的模型需要更多的计算资源,也可以单独申请 +page_dev_yunlao=启智AI协作平台 +page_dev_yunlao_desc1=启智AI协作平台已经与鹏城云脑、中国算力网(C²NET)一期打通,可以利用鹏城云脑和中国算力网的丰富算力资源,完成AI开发任务。 +page_dev_yunlao_desc2=鹏城云脑现有AI算力100P FLOPS@FP16(每秒十亿亿次半精度浮点计算),主要硬件基础设施由搭载英伟达Tesla V100 和A100 的GPU服务器,以及搭载鲲鹏、昇腾处理器的Atlas 900 AI集群构成。 +page_dev_yunlao_desc3=中国算力网(C²NET)一期可实现不同人工智能计算中心之间高速网络互联,实现算力合理调度和资源弹性分配。目前已接入11家智算中心,算力总规模1924P OPS@FP16。启智AI协作平台已接入其中的鹏城云计算所、成都智算中心、中原智算中心、合肥类脑等节点。 +page_dev_yunlao_desc4=开发者可以根据使用需求,自由选择相应计算资源,可以测试模型在不同硬件环境下的适配能力、性能、稳定性等。 +page_dev_yunlao_desc5=如果您的模型需要更多的计算资源,也可以单独申请。 page_dev_yunlao_apply=单独申请 - +c2net_title=智算网络 +c2net_desc=人工智能算力网络推进联盟已接入11家智算中心,算力总规模1924P +c2net_center=中心 search=搜索 search_repo=项目 search_dataset=数据集 @@ -565,6 +569,7 @@ static.CollectImage=收藏镜像数 static.CollectedImage=被收藏镜像数 static.RecommendImage=被推荐镜像数 static.email=Email +static.phone=电话 static.location=所在地区 static.all=所有 static.public.user_business_analysis_current_month=本月 @@ -830,6 +835,12 @@ create_dataset_fail=创建数据集失败。 query_dataset_fail=查询数据集失败。 edit_attachment_fail=修改描述失败。 +reference_dataset_fail=关联数据集失败,请稍后再试。 +cancel_reference_dataset_fail=取消关联数据集失败,请稍后再试。 + +download_url=数据集下载地址 +download_copy=复制链接 +download_oper=操作 show_dataset=数据集 edit_dataset=编辑数据集 update_dataset=更新数据集 @@ -922,6 +933,7 @@ dataset_explain = 数据集:云脑1提供 CPU / GPU 资源,云脑2提供 Asc dataset_instructions_for_use = 使用说明:可以参考启智AI协作平台 dataset_camp_course = 小白训练营课程 dataset_upload = 上传 +dataset_upload_status = 上传状态 dataset_file_name = 文件名称 dataset_available_clusters = 可用集群 dataset_upload_time = 上传时间 @@ -948,6 +960,13 @@ unzip_failed=解压失败 unzip_stared=解压中 unzip_status=解压状态 collection_num=收藏数量 +current_dataset=当前数据集 +linked_dataset=关联数据集 +unfavorite=取消收藏 +favorite=收藏 +disassociate=取消关联 +benchmark_dataset_tip=说明:先使用数据集功能上传模型,然后从数据集列表选模型。 + [repo] owner=拥有者 repo_name=项目名称 @@ -1006,7 +1025,7 @@ datasets.desc=数据集功能 cloudbrain_helper=使用GPU/NPU资源,开启Notebook、模型训练任务等 model_manager = 模型 -model_noright=无权限操作 +model_noright=您没有操作权限。 model_rename=模型名称重复,请修改模型名称 @@ -1037,8 +1056,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 @@ -1075,6 +1095,7 @@ cloudbrain_operate=操作 cloudbrain_status_createtime=状态/创建时间 cloudbrain_status_runtime = 运行时长 cloudbrain_jobname_err=只能以小写字母或数字开头且只包含小写字母、数字、_和-,不能以_结尾,最长36个字符。 +cloudbrain_bootfile_err=仓库中不存在启动文件 cloudbrain_query_fail=查询云脑任务失败。 cloudbrain.mirror_tag = 镜像标签 cloudbrain.mirror_description = 镜像描述 @@ -1107,6 +1128,10 @@ modelarts.deletetime=删除时间 modelarts.version_nums=版本数 modelarts.version=版本 modelarts.computing_resources=计算资源 +modelarts.cluster.computing_resources=集群/计算资源 +modelarts.ai_center=智算中心 +modelarts.card_type=卡类型 +modelarts.cluster=集群 modelarts.notebook=调试任务 modelarts.train_job=训练任务 modelarts.train_job.new_debug=新建调试任务 @@ -1229,6 +1254,58 @@ 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=创建模型转换任务 + +model.manage.notcreatemodel=未创建过模型 +model.manage.init1=代码版本:您还没有初始化代码仓库,请先 +model.manage.init2=创建代码版本; +model.manage.createtrainjob_tip=训练任务:您还没创建过训练任务,请先创建 +model.manage.createtrainjob=训练任务 +model.manage.delete=删除模型 +model.manage.delete_confirm=你确认删除该模型么?此模型一旦删除不可恢复。 +model.manage.select.trainjob=选择训练任务 +model.manage.select.version=选择版本 +model.manage.engine=模型框架 +model.manage.select.engine=选择模型框架 +model.manage.modelfile=模型文件 +model.manage.modellabel=模型标签 +model.manage.modeldesc=模型描述 +model.manage.baseinfo=基本信息 +modelconvert.notcreate=未创建过模型转换任务 +modelconvert.importfirst1=请您先导入 +modelconvert.importfirst2=模型下载 +modelconvert.importfirst3=,然后再对其进行转换。 +modelconvert.download=下载 +modelconvert.taskname=任务名称 +modelconvert.modelname=模型名称 +modelconvert.selectmodel=选择模型 +modelconvert.modelversion=模型版本 +modelconvert.selectversion=选择版本 +modelconvert.selectmodelfile=选择模型文件 +modelconvert.taskstatus=状态 +modelconvert.srcengine=原模型框架 +modelconvert.outputformat=转换后格式 +modelconvert.createtime=创建时间 +modelconvert.inputdataformat=输入数据格式 +modelconvert.inputshape=输入张量形状 +modelconvert.inputshapetip=如:1,1,32,32,与输入数据格式对应。 +modelconvert.netoutputdata=网络输出数据类型 +modelconvert.taskdesc=任务描述 +modelconvert.newtask=新建任务 +modelconvert.createtask=创建模型转换任务 + +modelconvert.taskurlname=模型转换任务 +log_scroll_start=滚动到顶部 +log_scroll_end=滚动到底部 +modelconvert.tasknameempty=请输入任务名称。 +modelconvert.inputshapeerror=格式输入错误,请输入如:1,1,32,32,与输入数据格式对应。 + +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中以供后续下载。 @@ -2964,6 +3041,13 @@ notices.desc=提示描述 notices.op=操作 notices.delete_success=系统通知已被删除。 +user_management = 用户管理 +resource_management = 资源管理 +resource_pool = 资源池(队列) +resource_price = 资源规格单价 +application_scenario = 应用场景 +system_configuration = 系统配置 + [action] create_repo=创建了项目 %s rename_repo=重命名项目 %[1]s%[3]s @@ -3087,9 +3171,13 @@ Platform_Tutorial=新手指引 foot.advice_feedback = 意见反馈 [cloudbrain] +all_resource_cluster=全部集群 +all_ai_center=全部智算中心 resource_cluster = 算力集群 resource_cluster_openi = 启智集群 resource_cluster_c2net = 智算网络集群 +resource_cluster_openi_simple = 启智 +resource_cluster_c2net_simple = 智算网络 compute_resource = 计算资源 task_name = 任务名称 task_type = 任务类型 @@ -3110,23 +3198,31 @@ select_dataset = 选择数据集 specification = 规格 select_specification = 选择资源规格 description = 描述 +card_duration = 运行卡时 +card_type = 卡类型 +wrong_specification=您目前不能使用这个资源规格,请选择其他资源规格。 job_name_rule = 请输入字母、数字、_和-,最长64个字符,且不能以中划线(-)结尾。 -dataset_path_rule = 数据集位置存储在环境变量data_url中,训练输出路径存储在环境变量train_url中。 +train_dataset_path_rule = 数据集位置存储在环境变量data_url中,训练输出路径存储在环境变量train_url中。 +infer_dataset_path_rule = 数据集位置存储在环境变量data_url中,推理输出路径存储在环境变量result_url中。 view_sample = 查看样例 inference_output_path_rule = 推理输出路径存储在环境变量result_url中。 model_file_path_rule = 模型文件位置存储在环境变量ckpt_url中。 - +model_file_postfix_rule = 模型文件支持的格式为 [ckpt, pb, h5, json, pkl, pth, t7, pdparams, onnx, pbtxt, keras, mlmodel, cfg, pt] +model_convert_postfix_rule = 模型文件支持的格式为 [.pth, .pkl, .onnx, .mindir, .ckpt, .pb] delete_task = 删除任务 task_delete_confirm = 你确认删除该任务么?此任务一旦删除不可恢复。 operate_confirm = 确定操作 operate_cancel = 取消操作 - +resource_use=资源占用情况 gpu_num = GPU数 cpu_num = CPU数 memory = 内存 shared_memory = 共享内存 +gpu_memory = 显存 +free = 免费 +point_hr = 积分/时 DEBUG = 调试任务 SNN4IMAGENET = 评测任务 @@ -3136,6 +3232,14 @@ INFERENCE = 推理任务 BENCHMARK = 评测任务 brain_area = 脑区 +Delete_failed=任务删除失败,请稍后再试。 +Not_Stopped=任务还未终止,不能删除。 +Already_stopped=任务已停止。 +Stopped_failed=任务停止失败,请稍后再试。 +Stopped_success_update_status_fail=任务停止成功,状态及运行时间更新失败。 +load_code_failed=代码加载失败,请确认选择了正确的分支。 + + error.dataset_select = 数据集选择错误:数量超过限制或者有同名数据集 [points] @@ -3147,3 +3251,6 @@ hours = 小时 expected_time = ,预计可用 points_acquisition_instructions = 积分获取说明 insufficient_points_balance = 积分余额不足 + +new_train_gpu_tooltips =训练脚本存储在%s中,数据集存储在%s中,训练输出请存储在%s中以供后续下载。 +new_infer_gpu_tooltips = 数据集存储在%s中,模型文件存储在%s中,推理输出请存储在%s中以供后续下载。 diff --git a/package-lock.json b/package-lock.json index 9f8961467..192f3f342 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4395,6 +4395,7 @@ "version": "3.0.0", "resolved": "https://registry.npm.taobao.org/date-format/download/date-format-3.0.0.tgz", "integrity": "sha1-64eANlx9KxURB4+0keZHl4DzrZU=", + "deprecated": "3.x is no longer supported. Please upgrade to 4.x or higher.", "engines": { "node": ">=4.0" } @@ -15641,6 +15642,7 @@ "version": "2.2.4", "resolved": "https://registry.npm.taobao.org/streamroller/download/streamroller-2.2.4.tgz", "integrity": "sha1-wZjO1C25QIamGTYIGHzoCl8rDlM=", + "deprecated": "2.x is no longer supported. Please upgrade to 3.x or higher.", "dependencies": { "date-format": "^2.1.0", "debug": "^4.1.1", @@ -15654,6 +15656,7 @@ "version": "2.1.0", "resolved": "https://registry.npm.taobao.org/date-format/download/date-format-2.1.0.tgz", "integrity": "sha1-MdW16iEc9f12TNOLr50DPffhJc8=", + "deprecated": "2.x is no longer supported. Please upgrade to 4.x or higher.", "engines": { "node": ">=4.0" } 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/self/css/notebook/notebook.css b/public/self/css/notebook/notebook.css index c75c33865..0e4979660 100644 --- a/public/self/css/notebook/notebook.css +++ b/public/self/css/notebook/notebook.css @@ -1,6 +1,7 @@ .nb-notebook { line-height: 1.5; - margin-left: 7em; + margin-left: 6em; + } .nb-stdout, .nb-stderr { @@ -15,6 +16,7 @@ .nb-cell + .nb-cell { margin-top: 0.5em; + max-width: 100%; } .nb-output table { @@ -40,6 +42,11 @@ padding-left: 1em; } +.nb-notebook img { + max-width: 80%; + padding: 3px; +} + .nb-cell { position: relative; } @@ -60,7 +67,8 @@ } .nb-output img { - max-width: 100%; + max-width: 80%; + padding: 3px; } .nb-output:before, .nb-input:before { diff --git a/public/self/dataset_preview.js b/public/self/dataset_preview.js index e6b79dd7d..81620e1a0 100644 --- a/public/self/dataset_preview.js +++ b/public/self/dataset_preview.js @@ -123,13 +123,13 @@ function loadimg(uuid,filename){ function loadimg(){ var length = labeltastresult[fileindex].pic_image_field.length; -  if(labeltastresult[fileindex].pic_image_field.substring(length - 5) == ".json"  -     || labeltastresult[fileindex].pic_image_field.substring(length - 4) == ".xml" -     || labeltastresult[fileindex].pic_image_field.substring(length - 4) == ".txt" -     || labeltastresult[fileindex].pic_image_field.substring(length - 4) == ".csv" -     || labeltastresult[fileindex].pic_image_field.substring(length - 3) == ".md" -     || labeltastresult[fileindex].pic_image_field.substring(length - 3) == ".py" -     || labeltastresult[fileindex].pic_image_field.substring(length - 3) == ".sh"){ +  if(labeltastresult[fileindex].pic_image_field.substring(length - 5).toLowerCase() == ".json"  +     || labeltastresult[fileindex].pic_image_field.substring(length - 4).toLowerCase() == ".xml" +     || labeltastresult[fileindex].pic_image_field.substring(length - 4).toLowerCase() == ".txt" +     || labeltastresult[fileindex].pic_image_field.substring(length - 4).toLowerCase() == ".csv" +     || labeltastresult[fileindex].pic_image_field.substring(length - 3).toLowerCase() == ".md" +     || labeltastresult[fileindex].pic_image_field.substring(length - 3).toLowerCase() == ".py" +     || labeltastresult[fileindex].pic_image_field.substring(length - 3).toLowerCase() == ".sh"){ //文本 canvas.style.display="none"; @@ -138,11 +138,11 @@ function loadimg(){ $('#textcontent').height(canvas.height-40) $("#textcontent").text(textContent); }else{ - if(labeltastresult[fileindex].pic_image_field.substring(length - 5) == ".jpeg"  -    || labeltastresult[fileindex].pic_image_field.substring(length - 4) == ".jpg" -    || labeltastresult[fileindex].pic_image_field.substring(length - 4) == ".bmp" -    || labeltastresult[fileindex].pic_image_field.substring(length - 4) == ".gif" -    || labeltastresult[fileindex].pic_image_field.substring(length - 4) == ".png"){ + if(labeltastresult[fileindex].pic_image_field.substring(length - 5).toLowerCase() == ".jpeg"  +    || labeltastresult[fileindex].pic_image_field.substring(length - 4).toLowerCase() == ".jpg" +    || labeltastresult[fileindex].pic_image_field.substring(length - 4).toLowerCase() == ".bmp" +    || labeltastresult[fileindex].pic_image_field.substring(length - 4).toLowerCase() == ".gif" +    || labeltastresult[fileindex].pic_image_field.substring(length - 4).toLowerCase() == ".png"){ canvas.style.display="block"; document.getElementById("textcontent").style.display="none"; img.src = ip + "/getgiteaimage?uuid=" + dataset_id + "&filename=" + labeltastresult[fileindex].pic_image_field; 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;kd)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(;H
a";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="
t
";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>");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>");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]===""&&!x?r.childNodes:[];for(o=k.length- +1;o>=0;--o)c.nodeName(k[o],"tbody")&&!k[o].childNodes.length&&k[o].parentNode.removeChild(k[o])}!c.support.leadingWhitespace&&$.test(l)&&r.insertBefore(b.createTextNode($.exec(l)[0]),r.firstChild);l=r.childNodes}if(l.nodeType)f.push(l);else f=c.merge(f,l)}}if(d)for(h=0;f[h];h++)if(e&&c.nodeName(f[h],"script")&&(!f[h].type||f[h].type.toLowerCase()==="text/javascript"))e.push(f[h].parentNode?f[h].parentNode.removeChild(f[h]):f[h]);else{f[h].nodeType===1&&f.splice.apply(f,[h+1,0].concat(c.makeArray(f[h].getElementsByTagName("script")))); +d.appendChild(f[h])}return f},cleanData:function(a){for(var b,d,e=c.cache,f=c.event.special,h=c.support.deleteExpando,l=0,k;(k=a[l])!=null;l++)if(!(k.nodeName&&c.noData[k.nodeName.toLowerCase()]))if(d=k[c.expando]){if((b=e[d])&&b.events)for(var o in b.events)f[o]?c.event.remove(k,o):c.removeEvent(k,o,b.handle);if(h)delete k[c.expando];else k.removeAttribute&&k.removeAttribute(c.expando);delete e[d]}}});var Ea=/alpha\([^)]*\)/i,gb=/opacity=([^)]*)/,hb=/-([a-z])/ig,ib=/([A-Z])/g,Fa=/^-?\d+(?:px)?$/i, +jb=/^-?\d/,kb={position:"absolute",visibility:"hidden",display:"block"},Pa=["Left","Right"],Qa=["Top","Bottom"],W,Ga,aa,lb=function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){if(arguments.length===2&&b===B)return this;return c.access(this,a,b,true,function(d,e,f){return f!==B?c.style(d,e,f):c.css(d,e)})};c.extend({cssHooks:{opacity:{get:function(a,b){if(b){var d=W(a,"opacity","opacity");return d===""?"1":d}else return a.style.opacity}}},cssNumber:{zIndex:true,fontWeight:true,opacity:true, +zoom:true,lineHeight:true},cssProps:{"float":c.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,d,e){if(!(!a||a.nodeType===3||a.nodeType===8||!a.style)){var f,h=c.camelCase(b),l=a.style,k=c.cssHooks[h];b=c.cssProps[h]||h;if(d!==B){if(!(typeof d==="number"&&isNaN(d)||d==null)){if(typeof d==="number"&&!c.cssNumber[h])d+="px";if(!k||!("set"in k)||(d=k.set(a,d))!==B)try{l[b]=d}catch(o){}}}else{if(k&&"get"in k&&(f=k.get(a,false,e))!==B)return f;return l[b]}}},css:function(a,b,d){var e,f=c.camelCase(b), +h=c.cssHooks[f];b=c.cssProps[f]||f;if(h&&"get"in h&&(e=h.get(a,true,d))!==B)return e;else if(W)return W(a,b,f)},swap:function(a,b,d){var e={},f;for(f in b){e[f]=a.style[f];a.style[f]=b[f]}d.call(a);for(f in b)a.style[f]=e[f]},camelCase:function(a){return a.replace(hb,lb)}});c.curCSS=c.css;c.each(["height","width"],function(a,b){c.cssHooks[b]={get:function(d,e,f){var h;if(e){if(d.offsetWidth!==0)h=oa(d,b,f);else c.swap(d,kb,function(){h=oa(d,b,f)});if(h<=0){h=W(d,b,b);if(h==="0px"&&aa)h=aa(d,b,b); +if(h!=null)return h===""||h==="auto"?"0px":h}if(h<0||h==null){h=d.style[b];return h===""||h==="auto"?"0px":h}return typeof h==="string"?h:h+"px"}},set:function(d,e){if(Fa.test(e)){e=parseFloat(e);if(e>=0)return e+"px"}else return e}}});if(!c.support.opacity)c.cssHooks.opacity={get:function(a,b){return gb.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var d=a.style;d.zoom=1;var e=c.isNaN(b)?"":"alpha(opacity="+b*100+")",f= +d.filter||"";d.filter=Ea.test(f)?f.replace(Ea,e):d.filter+" "+e}};if(t.defaultView&&t.defaultView.getComputedStyle)Ga=function(a,b,d){var e;d=d.replace(ib,"-$1").toLowerCase();if(!(b=a.ownerDocument.defaultView))return B;if(b=b.getComputedStyle(a,null)){e=b.getPropertyValue(d);if(e===""&&!c.contains(a.ownerDocument.documentElement,a))e=c.style(a,d)}return e};if(t.documentElement.currentStyle)aa=function(a,b){var d,e,f=a.currentStyle&&a.currentStyle[b],h=a.style;if(!Fa.test(f)&&jb.test(f)){d=h.left; +e=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;h.left=b==="fontSize"?"1em":f||0;f=h.pixelLeft+"px";h.left=d;a.runtimeStyle.left=e}return f===""?"auto":f};W=Ga||aa;if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=a.offsetHeight;return a.offsetWidth===0&&b===0||!c.support.reliableHiddenOffsets&&(a.style.display||c.css(a,"display"))==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var mb=c.now(),nb=/)<[^<]*)*<\/script>/gi, +ob=/^(?:select|textarea)/i,pb=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,qb=/^(?:GET|HEAD)$/,Ra=/\[\]$/,T=/\=\?(&|$)/,ja=/\?/,rb=/([?&])_=[^&]*/,sb=/^(\w+:)?\/\/([^\/?#]+)/,tb=/%20/g,ub=/#.*$/,Ha=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!=="string"&&Ha)return Ha.apply(this,arguments);else if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var f=a.slice(e,a.length);a=a.slice(0,e)}e="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b=== +"object"){b=c.param(b,c.ajaxSettings.traditional);e="POST"}var h=this;c.ajax({url:a,type:e,dataType:"html",data:b,complete:function(l,k){if(k==="success"||k==="notmodified")h.html(f?c("
").append(l.responseText.replace(nb,"")).find(f):l.responseText);d&&h.each(d,[l.responseText,k,l])}});return this},serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&& +!this.disabled&&(this.checked||ob.test(this.nodeName)||pb.test(this.type))}).map(function(a,b){var d=c(this).val();return d==null?null:c.isArray(d)?c.map(d,function(e){return{name:b.name,value:e}}):{name:b.name,value:d}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:e})}, +getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:e})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return new E.XMLHttpRequest},accepts:{xml:"application/xml, text/xml",html:"text/html", +script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},ajax:function(a){var b=c.extend(true,{},c.ajaxSettings,a),d,e,f,h=b.type.toUpperCase(),l=qb.test(h);b.url=b.url.replace(ub,"");b.context=a&&a.context!=null?a.context:b;if(b.data&&b.processData&&typeof b.data!=="string")b.data=c.param(b.data,b.traditional);if(b.dataType==="jsonp"){if(h==="GET")T.test(b.url)||(b.url+=(ja.test(b.url)?"&":"?")+(b.jsonp||"callback")+"=?");else if(!b.data|| +!T.test(b.data))b.data=(b.data?b.data+"&":"")+(b.jsonp||"callback")+"=?";b.dataType="json"}if(b.dataType==="json"&&(b.data&&T.test(b.data)||T.test(b.url))){d=b.jsonpCallback||"jsonp"+mb++;if(b.data)b.data=(b.data+"").replace(T,"="+d+"$1");b.url=b.url.replace(T,"="+d+"$1");b.dataType="script";var k=E[d];E[d]=function(m){if(c.isFunction(k))k(m);else{E[d]=B;try{delete E[d]}catch(p){}}f=m;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);r&&r.removeChild(A)}}if(b.dataType==="script"&&b.cache===null)b.cache= +false;if(b.cache===false&&l){var o=c.now(),x=b.url.replace(rb,"$1_="+o);b.url=x+(x===b.url?(ja.test(b.url)?"&":"?")+"_="+o:"")}if(b.data&&l)b.url+=(ja.test(b.url)?"&":"?")+b.data;b.global&&c.active++===0&&c.event.trigger("ajaxStart");o=(o=sb.exec(b.url))&&(o[1]&&o[1].toLowerCase()!==location.protocol||o[2].toLowerCase()!==location.host);if(b.dataType==="script"&&h==="GET"&&o){var r=t.getElementsByTagName("head")[0]||t.documentElement,A=t.createElement("script");if(b.scriptCharset)A.charset=b.scriptCharset; +A.src=b.url;if(!d){var C=false;A.onload=A.onreadystatechange=function(){if(!C&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){C=true;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);A.onload=A.onreadystatechange=null;r&&A.parentNode&&r.removeChild(A)}}}r.insertBefore(A,r.firstChild);return B}var J=false,w=b.xhr();if(w){b.username?w.open(h,b.url,b.async,b.username,b.password):w.open(h,b.url,b.async);try{if(b.data!=null&&!l||a&&a.contentType)w.setRequestHeader("Content-Type", +b.contentType);if(b.ifModified){c.lastModified[b.url]&&w.setRequestHeader("If-Modified-Since",c.lastModified[b.url]);c.etag[b.url]&&w.setRequestHeader("If-None-Match",c.etag[b.url])}o||w.setRequestHeader("X-Requested-With","XMLHttpRequest");w.setRequestHeader("Accept",b.dataType&&b.accepts[b.dataType]?b.accepts[b.dataType]+", */*; q=0.01":b.accepts._default)}catch(I){}if(b.beforeSend&&b.beforeSend.call(b.context,w,b)===false){b.global&&c.active--===1&&c.event.trigger("ajaxStop");w.abort();return false}b.global&& +c.triggerGlobal(b,"ajaxSend",[w,b]);var L=w.onreadystatechange=function(m){if(!w||w.readyState===0||m==="abort"){J||c.handleComplete(b,w,e,f);J=true;if(w)w.onreadystatechange=c.noop}else if(!J&&w&&(w.readyState===4||m==="timeout")){J=true;w.onreadystatechange=c.noop;e=m==="timeout"?"timeout":!c.httpSuccess(w)?"error":b.ifModified&&c.httpNotModified(w,b.url)?"notmodified":"success";var p;if(e==="success")try{f=c.httpData(w,b.dataType,b)}catch(q){e="parsererror";p=q}if(e==="success"||e==="notmodified")d|| +c.handleSuccess(b,w,e,f);else c.handleError(b,w,e,p);d||c.handleComplete(b,w,e,f);m==="timeout"&&w.abort();if(b.async)w=null}};try{var g=w.abort;w.abort=function(){w&&Function.prototype.call.call(g,w);L("abort")}}catch(i){}b.async&&b.timeout>0&&setTimeout(function(){w&&!J&&L("timeout")},b.timeout);try{w.send(l||b.data==null?null:b.data)}catch(n){c.handleError(b,w,null,n);c.handleComplete(b,w,e,f)}b.async||L();return w}},param:function(a,b){var d=[],e=function(h,l){l=c.isFunction(l)?l():l;d[d.length]= +encodeURIComponent(h)+"="+encodeURIComponent(l)};if(b===B)b=c.ajaxSettings.traditional;if(c.isArray(a)||a.jquery)c.each(a,function(){e(this.name,this.value)});else for(var f in a)da(f,a[f],b,e);return d.join("&").replace(tb,"+")}});c.extend({active:0,lastModified:{},etag:{},handleError:function(a,b,d,e){a.error&&a.error.call(a.context,b,d,e);a.global&&c.triggerGlobal(a,"ajaxError",[b,a,e])},handleSuccess:function(a,b,d,e){a.success&&a.success.call(a.context,e,d,b);a.global&&c.triggerGlobal(a,"ajaxSuccess", +[b,a])},handleComplete:function(a,b,d){a.complete&&a.complete.call(a.context,b,d);a.global&&c.triggerGlobal(a,"ajaxComplete",[b,a]);a.global&&c.active--===1&&c.event.trigger("ajaxStop")},triggerGlobal:function(a,b,d){(a.context&&a.context.url==null?c(a.context):c.event).trigger(b,d)},httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===1223}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"), +e=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(e)c.etag[b]=e;return a.status===304},httpData:function(a,b,d){var e=a.getResponseHeader("content-type")||"",f=b==="xml"||!b&&e.indexOf("xml")>=0;a=f?a.responseXML:a.responseText;f&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b==="json"||!b&&e.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&e.indexOf("javascript")>=0)c.globalEval(a);return a}}); +if(E.ActiveXObject)c.ajaxSettings.xhr=function(){if(E.location.protocol!=="file:")try{return new E.XMLHttpRequest}catch(a){}try{return new E.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}};c.support.ajax=!!c.ajaxSettings.xhr();var ea={},vb=/^(?:toggle|show|hide)$/,wb=/^([+\-]=)?([\d+.\-]+)(.*)$/,ba,pa=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b,d){if(a||a===0)return this.animate(S("show", +3),a,b,d);else{d=0;for(var e=this.length;d=0;e--)if(d[e].elem===this){b&&d[e](true);d.splice(e,1)}});b||this.dequeue();return this}});c.each({slideDown:S("show",1),slideUp:S("hide",1),slideToggle:S("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){c.fn[a]=function(d,e,f){return this.animate(b, +d,e,f)}});c.extend({speed:function(a,b,d){var e=a&&typeof a==="object"?c.extend({},a):{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};e.duration=c.fx.off?0:typeof e.duration==="number"?e.duration:e.duration in c.fx.speeds?c.fx.speeds[e.duration]:c.fx.speeds._default;e.old=e.complete;e.complete=function(){e.queue!==false&&c(this).dequeue();c.isFunction(e.old)&&e.old.call(this)};return e},easing:{linear:function(a,b,d,e){return d+e*a},swing:function(a,b,d,e){return(-Math.cos(a* +Math.PI)/2+0.5)*e+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||c.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a=parseFloat(c.css(this.elem,this.prop));return a&&a>-1E4?a:0},custom:function(a,b,d){function e(l){return f.step(l)} +var f=this,h=c.fx;this.startTime=c.now();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;this.pos=this.state=0;e.elem=this.elem;if(e()&&c.timers.push(e)&&!ba)ba=setInterval(h.tick,h.interval)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true; +this.custom(this.cur(),0)},step:function(a){var b=c.now(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var e in this.options.curAnim)if(this.options.curAnim[e]!==true)d=false;if(d){if(this.options.overflow!=null&&!c.support.shrinkWrapBlocks){var f=this.elem,h=this.options;c.each(["","X","Y"],function(k,o){f.style["overflow"+o]=h.overflow[k]})}this.options.hide&&c(this.elem).hide();if(this.options.hide|| +this.options.show)for(var l in this.options.curAnim)c.style(this.elem,l,this.options.orig[l]);this.options.complete.call(this.elem)}return false}else{a=b-this.startTime;this.state=a/this.options.duration;b=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||b](this.state,a,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a= +c.timers,b=0;b-1;e={};var x={};if(o)x=f.position();l=o?x.top:parseInt(l,10)||0;k=o?x.left:parseInt(k,10)||0;if(c.isFunction(b))b=b.call(a,d,h);if(b.top!=null)e.top=b.top-h.top+l;if(b.left!=null)e.left=b.left-h.left+k;"using"in b?b.using.call(a, +e):f.css(e)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),e=Ia.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.css(a,"marginTop"))||0;d.left-=parseFloat(c.css(a,"marginLeft"))||0;e.top+=parseFloat(c.css(b[0],"borderTopWidth"))||0;e.left+=parseFloat(c.css(b[0],"borderLeftWidth"))||0;return{top:d.top-e.top,left:d.left-e.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||t.body;a&&!Ia.test(a.nodeName)&& +c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(e){var f=this[0],h;if(!f)return null;if(e!==B)return this.each(function(){if(h=fa(this))h.scrollTo(!a?e:c(h).scrollLeft(),a?e:c(h).scrollTop());else this[d]=e});else return(h=fa(f))?"pageXOffset"in h?h[a?"pageYOffset":"pageXOffset"]:c.support.boxModel&&h.document.documentElement[d]||h.document.body[d]:f[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase(); +c.fn["inner"+b]=function(){return this[0]?parseFloat(c.css(this[0],d,"padding")):null};c.fn["outer"+b]=function(e){return this[0]?parseFloat(c.css(this[0],d,e?"margin":"border")):null};c.fn[d]=function(e){var f=this[0];if(!f)return e==null?null:this;if(c.isFunction(e))return this.each(function(l){var k=c(this);k[d](e.call(this,l,k[d]()))});if(c.isWindow(f))return f.document.compatMode==="CSS1Compat"&&f.document.documentElement["client"+b]||f.document.body["client"+b];else if(f.nodeType===9)return Math.max(f.documentElement["client"+ +b],f.body["scroll"+b],f.documentElement["scroll"+b],f.body["offset"+b],f.documentElement["offset"+b]);else if(e===B){f=c.css(f,d);var h=parseFloat(f);return c.isNaN(h)?f:h}else return this.css(d,typeof e==="string"?e:e+"px")}})})(window); diff --git a/public/self/ztree/js/jquery.ztree.all.js b/public/self/ztree/js/jquery.ztree.all.js new file mode 100644 index 000000000..dbca0e564 --- /dev/null +++ b/public/self/ztree/js/jquery.ztree.all.js @@ -0,0 +1,3878 @@ +/* + * JQuery zTree core + * v3.5.48 + * http://treejs.cn/ + * + * Copyright (c) 2010 Hunter.z + * + * Licensed same as jquery - MIT License + * http://www.opensource.org/licenses/mit-license.php + * + * Date: 2020-11-21 + */ + +(function ($) { + var settings = {}, roots = {}, caches = {}, + //default consts of core + _consts = { + className: { + BUTTON: "button", + LEVEL: "level", + ICO_LOADING: "ico_loading", + SWITCH: "switch", + NAME: 'node_name' + }, + event: { + NODECREATED: "ztree_nodeCreated", + CLICK: "ztree_click", + EXPAND: "ztree_expand", + COLLAPSE: "ztree_collapse", + ASYNC_SUCCESS: "ztree_async_success", + ASYNC_ERROR: "ztree_async_error", + REMOVE: "ztree_remove", + SELECTED: "ztree_selected", + UNSELECTED: "ztree_unselected" + }, + id: { + A: "_a", + ICON: "_ico", + SPAN: "_span", + SWITCH: "_switch", + UL: "_ul" + }, + line: { + ROOT: "root", + ROOTS: "roots", + CENTER: "center", + BOTTOM: "bottom", + NOLINE: "noline", + LINE: "line" + }, + folder: { + OPEN: "open", + CLOSE: "close", + DOCU: "docu" + }, + node: { + CURSELECTED: "curSelectedNode" + } + }, + //default setting of core + _setting = { + treeId: "", + treeObj: null, + view: { + addDiyDom: null, + autoCancelSelected: true, + dblClickExpand: true, + expandSpeed: "fast", + fontCss: {}, + nodeClasses: {}, + nameIsHTML: false, + selectedMulti: true, + showIcon: true, + showLine: true, + showTitle: true, + txtSelectedEnable: false + }, + data: { + key: { + isParent: "isParent", + children: "children", + name: "name", + title: "", + url: "url", + icon: "icon" + }, + render: { + name: null, + title: null, + }, + simpleData: { + enable: false, + idKey: "id", + pIdKey: "pId", + rootPId: null + }, + keep: { + parent: false, + leaf: false + } + }, + async: { + enable: false, + contentType: "application/x-www-form-urlencoded", + type: "post", + dataType: "text", + headers: {}, + xhrFields: {}, + url: "", + autoParam: [], + otherParam: [], + dataFilter: null + }, + callback: { + beforeAsync: null, + beforeClick: null, + beforeDblClick: null, + beforeRightClick: null, + beforeMouseDown: null, + beforeMouseUp: null, + beforeExpand: null, + beforeCollapse: null, + beforeRemove: null, + + onAsyncError: null, + onAsyncSuccess: null, + onNodeCreated: null, + onClick: null, + onDblClick: null, + onRightClick: null, + onMouseDown: null, + onMouseUp: null, + onExpand: null, + onCollapse: null, + onRemove: null + } + }, + //default root of core + //zTree use root to save full data + _initRoot = function (setting) { + var r = data.getRoot(setting); + if (!r) { + r = {}; + data.setRoot(setting, r); + } + data.nodeChildren(setting, r, []); + r.expandTriggerFlag = false; + r.curSelectedList = []; + r.noSelection = true; + r.createdNodes = []; + r.zId = 0; + r._ver = (new Date()).getTime(); + }, + //default cache of core + _initCache = function (setting) { + var c = data.getCache(setting); + if (!c) { + c = {}; + data.setCache(setting, c); + } + c.nodes = []; + c.doms = []; + }, + //default bindEvent of core + _bindEvent = function (setting) { + var o = setting.treeObj, + c = consts.event; + o.bind(c.NODECREATED, function (event, treeId, node) { + tools.apply(setting.callback.onNodeCreated, [event, treeId, node]); + }); + + o.bind(c.CLICK, function (event, srcEvent, treeId, node, clickFlag) { + tools.apply(setting.callback.onClick, [srcEvent, treeId, node, clickFlag]); + }); + + o.bind(c.EXPAND, function (event, treeId, node) { + tools.apply(setting.callback.onExpand, [event, treeId, node]); + }); + + o.bind(c.COLLAPSE, function (event, treeId, node) { + tools.apply(setting.callback.onCollapse, [event, treeId, node]); + }); + + o.bind(c.ASYNC_SUCCESS, function (event, treeId, node, msg) { + tools.apply(setting.callback.onAsyncSuccess, [event, treeId, node, msg]); + }); + + o.bind(c.ASYNC_ERROR, function (event, treeId, node, XMLHttpRequest, textStatus, errorThrown) { + tools.apply(setting.callback.onAsyncError, [event, treeId, node, XMLHttpRequest, textStatus, errorThrown]); + }); + + o.bind(c.REMOVE, function (event, treeId, treeNode) { + tools.apply(setting.callback.onRemove, [event, treeId, treeNode]); + }); + + o.bind(c.SELECTED, function (event, treeId, node) { + tools.apply(setting.callback.onSelected, [treeId, node]); + }); + o.bind(c.UNSELECTED, function (event, treeId, node) { + tools.apply(setting.callback.onUnSelected, [treeId, node]); + }); + }, + _unbindEvent = function (setting) { + var o = setting.treeObj, + c = consts.event; + o.unbind(c.NODECREATED) + .unbind(c.CLICK) + .unbind(c.EXPAND) + .unbind(c.COLLAPSE) + .unbind(c.ASYNC_SUCCESS) + .unbind(c.ASYNC_ERROR) + .unbind(c.REMOVE) + .unbind(c.SELECTED) + .unbind(c.UNSELECTED); + }, + //default event proxy of core + _eventProxy = function (event) { + var target = event.target, + setting = data.getSetting(event.data.treeId), + tId = "", node = null, + nodeEventType = "", treeEventType = "", + nodeEventCallback = null, treeEventCallback = null, + tmp = null; + + if (tools.eqs(event.type, "mousedown")) { + treeEventType = "mousedown"; + } else if (tools.eqs(event.type, "mouseup")) { + treeEventType = "mouseup"; + } else if (tools.eqs(event.type, "contextmenu")) { + treeEventType = "contextmenu"; + } else if (tools.eqs(event.type, "click")) { + if (tools.eqs(target.tagName, "span") && target.getAttribute("treeNode" + consts.id.SWITCH) !== null) { + tId = tools.getNodeMainDom(target).id; + nodeEventType = "switchNode"; + } else { + tmp = tools.getMDom(setting, target, [{tagName: "a", attrName: "treeNode" + consts.id.A}]); + if (tmp) { + tId = tools.getNodeMainDom(tmp).id; + nodeEventType = "clickNode"; + } + } + } else if (tools.eqs(event.type, "dblclick")) { + treeEventType = "dblclick"; + tmp = tools.getMDom(setting, target, [{tagName: "a", attrName: "treeNode" + consts.id.A}]); + if (tmp) { + tId = tools.getNodeMainDom(tmp).id; + nodeEventType = "switchNode"; + } + } + if (treeEventType.length > 0 && tId.length == 0) { + tmp = tools.getMDom(setting, target, [{tagName: "a", attrName: "treeNode" + consts.id.A}]); + if (tmp) { + tId = tools.getNodeMainDom(tmp).id; + } + } + // event to node + if (tId.length > 0) { + node = data.getNodeCache(setting, tId); + switch (nodeEventType) { + case "switchNode" : + var isParent = data.nodeIsParent(setting, node); + if (!isParent) { + nodeEventType = ""; + } else if (tools.eqs(event.type, "click") + || (tools.eqs(event.type, "dblclick") && tools.apply(setting.view.dblClickExpand, [setting.treeId, node], setting.view.dblClickExpand))) { + nodeEventCallback = handler.onSwitchNode; + } else { + nodeEventType = ""; + } + break; + case "clickNode" : + nodeEventCallback = handler.onClickNode; + break; + } + } + // event to zTree + switch (treeEventType) { + case "mousedown" : + treeEventCallback = handler.onZTreeMousedown; + break; + case "mouseup" : + treeEventCallback = handler.onZTreeMouseup; + break; + case "dblclick" : + treeEventCallback = handler.onZTreeDblclick; + break; + case "contextmenu" : + treeEventCallback = handler.onZTreeContextmenu; + break; + } + var proxyResult = { + stop: false, + node: node, + nodeEventType: nodeEventType, + nodeEventCallback: nodeEventCallback, + treeEventType: treeEventType, + treeEventCallback: treeEventCallback + }; + return proxyResult + }, + //default init node of core + _initNode = function (setting, level, n, parentNode, isFirstNode, isLastNode, openFlag) { + if (!n) return; + var r = data.getRoot(setting), + children = data.nodeChildren(setting, n); + n.level = level; + n.tId = setting.treeId + "_" + (++r.zId); + n.parentTId = parentNode ? parentNode.tId : null; + n.open = (typeof n.open == "string") ? tools.eqs(n.open, "true") : !!n.open; + var isParent = data.nodeIsParent(setting, n); + if (tools.isArray(children)) { + data.nodeIsParent(setting, n, true); + n.zAsync = true; + } else { + isParent = data.nodeIsParent(setting, n, isParent); + n.open = (isParent && !setting.async.enable) ? n.open : false; + n.zAsync = !isParent; + } + n.isFirstNode = isFirstNode; + n.isLastNode = isLastNode; + n.getParentNode = function () { + return data.getNodeCache(setting, n.parentTId); + }; + n.getPreNode = function () { + return data.getPreNode(setting, n); + }; + n.getNextNode = function () { + return data.getNextNode(setting, n); + }; + n.getIndex = function () { + return data.getNodeIndex(setting, n); + }; + n.getPath = function () { + return data.getNodePath(setting, n); + }; + n.isAjaxing = false; + data.fixPIdKeyValue(setting, n); + }, + _init = { + bind: [_bindEvent], + unbind: [_unbindEvent], + caches: [_initCache], + nodes: [_initNode], + proxys: [_eventProxy], + roots: [_initRoot], + beforeA: [], + afterA: [], + innerBeforeA: [], + innerAfterA: [], + zTreeTools: [] + }, + //method of operate data + data = { + addNodeCache: function (setting, node) { + data.getCache(setting).nodes[data.getNodeCacheId(node.tId)] = node; + }, + getNodeCacheId: function (tId) { + return tId.substring(tId.lastIndexOf("_") + 1); + }, + addAfterA: function (afterA) { + _init.afterA.push(afterA); + }, + addBeforeA: function (beforeA) { + _init.beforeA.push(beforeA); + }, + addInnerAfterA: function (innerAfterA) { + _init.innerAfterA.push(innerAfterA); + }, + addInnerBeforeA: function (innerBeforeA) { + _init.innerBeforeA.push(innerBeforeA); + }, + addInitBind: function (bindEvent) { + _init.bind.push(bindEvent); + }, + addInitUnBind: function (unbindEvent) { + _init.unbind.push(unbindEvent); + }, + addInitCache: function (initCache) { + _init.caches.push(initCache); + }, + addInitNode: function (initNode) { + _init.nodes.push(initNode); + }, + addInitProxy: function (initProxy, isFirst) { + if (!!isFirst) { + _init.proxys.splice(0, 0, initProxy); + } else { + _init.proxys.push(initProxy); + } + }, + addInitRoot: function (initRoot) { + _init.roots.push(initRoot); + }, + addNodesData: function (setting, parentNode, index, nodes) { + var children = data.nodeChildren(setting, parentNode), params; + if (!children) { + children = data.nodeChildren(setting, parentNode, []); + index = -1; + } else if (index >= children.length) { + index = -1; + } + + if (children.length > 0 && index === 0) { + children[0].isFirstNode = false; + view.setNodeLineIcos(setting, children[0]); + } else if (children.length > 0 && index < 0) { + children[children.length - 1].isLastNode = false; + view.setNodeLineIcos(setting, children[children.length - 1]); + } + data.nodeIsParent(setting, parentNode, true); + + if (index < 0) { + data.nodeChildren(setting, parentNode, children.concat(nodes)); + } else { + params = [index, 0].concat(nodes); + children.splice.apply(children, params); + } + }, + addSelectedNode: function (setting, node) { + var root = data.getRoot(setting); + if (!data.isSelectedNode(setting, node)) { + root.curSelectedList.push(node); + } + }, + addCreatedNode: function (setting, node) { + if (!!setting.callback.onNodeCreated || !!setting.view.addDiyDom) { + var root = data.getRoot(setting); + root.createdNodes.push(node); + } + }, + addZTreeTools: function (zTreeTools) { + _init.zTreeTools.push(zTreeTools); + }, + exSetting: function (s) { + $.extend(true, _setting, s); + }, + fixPIdKeyValue: function (setting, node) { + if (setting.data.simpleData.enable) { + node[setting.data.simpleData.pIdKey] = node.parentTId ? node.getParentNode()[setting.data.simpleData.idKey] : setting.data.simpleData.rootPId; + } + }, + getAfterA: function (setting, node, array) { + for (var i = 0, j = _init.afterA.length; i < j; i++) { + _init.afterA[i].apply(this, arguments); + } + }, + getBeforeA: function (setting, node, array) { + for (var i = 0, j = _init.beforeA.length; i < j; i++) { + _init.beforeA[i].apply(this, arguments); + } + }, + getInnerAfterA: function (setting, node, array) { + for (var i = 0, j = _init.innerAfterA.length; i < j; i++) { + _init.innerAfterA[i].apply(this, arguments); + } + }, + getInnerBeforeA: function (setting, node, array) { + for (var i = 0, j = _init.innerBeforeA.length; i < j; i++) { + _init.innerBeforeA[i].apply(this, arguments); + } + }, + getCache: function (setting) { + return caches[setting.treeId]; + }, + getNodeIndex: function (setting, node) { + if (!node) return null; + var p = node.parentTId ? node.getParentNode() : data.getRoot(setting), + children = data.nodeChildren(setting, p); + for (var i = 0, l = children.length - 1; i <= l; i++) { + if (children[i] === node) { + return i; + } + } + return -1; + }, + getNextNode: function (setting, node) { + if (!node) return null; + var p = node.parentTId ? node.getParentNode() : data.getRoot(setting), + children = data.nodeChildren(setting, p); + for (var i = 0, l = children.length - 1; i <= l; i++) { + if (children[i] === node) { + return (i == l ? null : children[i + 1]); + } + } + return null; + }, + getNodeByParam: function (setting, nodes, key, value) { + if (!nodes || !key) return null; + for (var i = 0, l = nodes.length; i < l; i++) { + var node = nodes[i]; + if (node[key] == value) { + return nodes[i]; + } + var children = data.nodeChildren(setting, node); + var tmp = data.getNodeByParam(setting, children, key, value); + if (tmp) return tmp; + } + return null; + }, + getNodeCache: function (setting, tId) { + if (!tId) return null; + var n = caches[setting.treeId].nodes[data.getNodeCacheId(tId)]; + return n ? n : null; + }, + getNodePath: function (setting, node) { + if (!node) return null; + + var path; + if (node.parentTId) { + path = node.getParentNode().getPath(); + } else { + path = []; + } + + if (path) { + path.push(node); + } + + return path; + }, + getNodes: function (setting) { + return data.nodeChildren(setting, data.getRoot(setting)); + }, + getNodesByParam: function (setting, nodes, key, value) { + if (!nodes || !key) return []; + var result = []; + for (var i = 0, l = nodes.length; i < l; i++) { + var node = nodes[i]; + if (node[key] == value) { + result.push(node); + } + var children = data.nodeChildren(setting, node); + result = result.concat(data.getNodesByParam(setting, children, key, value)); + } + return result; + }, + getNodesByParamFuzzy: function (setting, nodes, key, value) { + if (!nodes || !key) return []; + var result = []; + value = value.toLowerCase(); + for (var i = 0, l = nodes.length; i < l; i++) { + var node = nodes[i]; + if (typeof node[key] == "string" && nodes[i][key].toLowerCase().indexOf(value) > -1) { + result.push(node); + } + var children = data.nodeChildren(setting, node); + result = result.concat(data.getNodesByParamFuzzy(setting, children, key, value)); + } + return result; + }, + getNodesByFilter: function (setting, nodes, filter, isSingle, invokeParam) { + if (!nodes) return (isSingle ? null : []); + var result = isSingle ? null : []; + for (var i = 0, l = nodes.length; i < l; i++) { + var node = nodes[i]; + if (tools.apply(filter, [node, invokeParam], false)) { + if (isSingle) { + return node; + } + result.push(node); + } + var children = data.nodeChildren(setting, node); + var tmpResult = data.getNodesByFilter(setting, children, filter, isSingle, invokeParam); + if (isSingle && !!tmpResult) { + return tmpResult; + } + result = isSingle ? tmpResult : result.concat(tmpResult); + } + return result; + }, + getPreNode: function (setting, node) { + if (!node) return null; + var p = node.parentTId ? node.getParentNode() : data.getRoot(setting), + children = data.nodeChildren(setting, p); + for (var i = 0, l = children.length; i < l; i++) { + if (children[i] === node) { + return (i == 0 ? null : children[i - 1]); + } + } + return null; + }, + getRoot: function (setting) { + return setting ? roots[setting.treeId] : null; + }, + getRoots: function () { + return roots; + }, + getSetting: function (treeId) { + return settings[treeId]; + }, + getSettings: function () { + return settings; + }, + getZTreeTools: function (treeId) { + var r = this.getRoot(this.getSetting(treeId)); + return r ? r.treeTools : null; + }, + initCache: function (setting) { + for (var i = 0, j = _init.caches.length; i < j; i++) { + _init.caches[i].apply(this, arguments); + } + }, + initNode: function (setting, level, node, parentNode, preNode, nextNode) { + for (var i = 0, j = _init.nodes.length; i < j; i++) { + _init.nodes[i].apply(this, arguments); + } + }, + initRoot: function (setting) { + for (var i = 0, j = _init.roots.length; i < j; i++) { + _init.roots[i].apply(this, arguments); + } + }, + isSelectedNode: function (setting, node) { + var root = data.getRoot(setting); + for (var i = 0, j = root.curSelectedList.length; i < j; i++) { + if (node === root.curSelectedList[i]) return true; + } + return false; + }, + nodeChildren: function (setting, node, newChildren) { + if (!node) { + return null; + } + var key = setting.data.key.children; + if (typeof newChildren !== 'undefined') { + node[key] = newChildren; + } + return node[key]; + }, + nodeIsParent: function (setting, node, newIsParent) { + if (!node) { + return false; + } + var key = setting.data.key.isParent; + if (typeof newIsParent !== 'undefined') { + if (typeof newIsParent === "string") { + newIsParent = tools.eqs(newIsParent, "true"); + } + newIsParent = !!newIsParent; + node[key] = newIsParent; + } else if (typeof node[key] == "string"){ + node[key] = tools.eqs(node[key], "true"); + } else { + node[key] = !!node[key]; + } + return node[key]; + }, + nodeName: function (setting, node, newName) { + var key = setting.data.key.name; + if (typeof newName !== 'undefined') { + node[key] = newName; + } + var rawName = "" + node[key]; + if(typeof setting.data.render.name === 'function') { + return setting.data.render.name.call(this,rawName,node); + } + return rawName; + }, + nodeTitle: function (setting, node) { + var t = setting.data.key.title === "" ? setting.data.key.name : setting.data.key.title; + var rawTitle = "" + node[t]; + if(typeof setting.data.render.title === 'function') { + return setting.data.render.title.call(this,rawTitle,node); + } + return rawTitle; + }, + removeNodeCache: function (setting, node) { + var children = data.nodeChildren(setting, node); + if (children) { + for (var i = 0, l = children.length; i < l; i++) { + data.removeNodeCache(setting, children[i]); + } + } + data.getCache(setting).nodes[data.getNodeCacheId(node.tId)] = null; + }, + removeSelectedNode: function (setting, node) { + var root = data.getRoot(setting); + for (var i = 0, j = root.curSelectedList.length; i < j; i++) { + if (node === root.curSelectedList[i] || !data.getNodeCache(setting, root.curSelectedList[i].tId)) { + root.curSelectedList.splice(i, 1); + setting.treeObj.trigger(consts.event.UNSELECTED, [setting.treeId, node]); + i--; + j--; + } + } + }, + setCache: function (setting, cache) { + caches[setting.treeId] = cache; + }, + setRoot: function (setting, root) { + roots[setting.treeId] = root; + }, + setZTreeTools: function (setting, zTreeTools) { + for (var i = 0, j = _init.zTreeTools.length; i < j; i++) { + _init.zTreeTools[i].apply(this, arguments); + } + }, + transformToArrayFormat: function (setting, nodes) { + if (!nodes) return []; + var r = []; + if (tools.isArray(nodes)) { + for (var i = 0, l = nodes.length; i < l; i++) { + var node = nodes[i]; + _do(node); + } + } else { + _do(nodes); + } + return r; + + function _do(_node) { + r.push(_node); + var children = data.nodeChildren(setting, _node); + if (children) { + r = r.concat(data.transformToArrayFormat(setting, children)); + } + } + }, + transformTozTreeFormat: function (setting, sNodes) { + var i, l, + key = setting.data.simpleData.idKey, + parentKey = setting.data.simpleData.pIdKey; + if (!key || key == "" || !sNodes) return []; + + if (tools.isArray(sNodes)) { + var r = []; + var tmpMap = {}; + for (i = 0, l = sNodes.length; i < l; i++) { + tmpMap[sNodes[i][key]] = sNodes[i]; + } + for (i = 0, l = sNodes.length; i < l; i++) { + var p = tmpMap[sNodes[i][parentKey]]; + if (p && sNodes[i][key] != sNodes[i][parentKey]) { + var children = data.nodeChildren(setting, p); + if (!children) { + children = data.nodeChildren(setting, p, []); + } + children.push(sNodes[i]); + } else { + r.push(sNodes[i]); + } + } + return r; + } else { + return [sNodes]; + } + } + }, + //method of event proxy + event = { + bindEvent: function (setting) { + for (var i = 0, j = _init.bind.length; i < j; i++) { + _init.bind[i].apply(this, arguments); + } + }, + unbindEvent: function (setting) { + for (var i = 0, j = _init.unbind.length; i < j; i++) { + _init.unbind[i].apply(this, arguments); + } + }, + bindTree: function (setting) { + var eventParam = { + treeId: setting.treeId + }, + o = setting.treeObj; + if (!setting.view.txtSelectedEnable) { + // for can't select text + o.bind('selectstart', handler.onSelectStart).css({ + "-moz-user-select": "-moz-none" + }); + } + o.bind('click', eventParam, event.proxy); + o.bind('dblclick', eventParam, event.proxy); + o.bind('mouseover', eventParam, event.proxy); + o.bind('mouseout', eventParam, event.proxy); + o.bind('mousedown', eventParam, event.proxy); + o.bind('mouseup', eventParam, event.proxy); + o.bind('contextmenu', eventParam, event.proxy); + }, + unbindTree: function (setting) { + var o = setting.treeObj; + o.unbind('selectstart', handler.onSelectStart) + .unbind('click', event.proxy) + .unbind('dblclick', event.proxy) + .unbind('mouseover', event.proxy) + .unbind('mouseout', event.proxy) + .unbind('mousedown', event.proxy) + .unbind('mouseup', event.proxy) + .unbind('contextmenu', event.proxy); + }, + doProxy: function (e) { + var results = []; + for (var i = 0, j = _init.proxys.length; i < j; i++) { + var proxyResult = _init.proxys[i].apply(this, arguments); + results.push(proxyResult); + if (proxyResult.stop) { + break; + } + } + return results; + }, + proxy: function (e) { + var setting = data.getSetting(e.data.treeId); + if (!tools.uCanDo(setting, e)) return true; + var results = event.doProxy(e), + r = true, x = false; + for (var i = 0, l = results.length; i < l; i++) { + var proxyResult = results[i]; + if (proxyResult.nodeEventCallback) { + x = true; + r = proxyResult.nodeEventCallback.apply(proxyResult, [e, proxyResult.node]) && r; + } + if (proxyResult.treeEventCallback) { + x = true; + r = proxyResult.treeEventCallback.apply(proxyResult, [e, proxyResult.node]) && r; + } + } + return r; + } + }, + //method of event handler + handler = { + onSwitchNode: function (event, node) { + var setting = data.getSetting(event.data.treeId); + if (node.open) { + if (tools.apply(setting.callback.beforeCollapse, [setting.treeId, node], true) == false) return true; + data.getRoot(setting).expandTriggerFlag = true; + view.switchNode(setting, node); + } else { + if (tools.apply(setting.callback.beforeExpand, [setting.treeId, node], true) == false) return true; + data.getRoot(setting).expandTriggerFlag = true; + view.switchNode(setting, node); + } + return true; + }, + onClickNode: function (event, node) { + var setting = data.getSetting(event.data.treeId), + clickFlag = ((setting.view.autoCancelSelected && (event.ctrlKey || event.metaKey)) && data.isSelectedNode(setting, node)) ? 0 : (setting.view.autoCancelSelected && (event.ctrlKey || event.metaKey) && setting.view.selectedMulti) ? 2 : 1; + if (tools.apply(setting.callback.beforeClick, [setting.treeId, node, clickFlag], true) == false) return true; + if (clickFlag === 0) { + view.cancelPreSelectedNode(setting, node); + } else { + view.selectNode(setting, node, clickFlag === 2); + } + setting.treeObj.trigger(consts.event.CLICK, [event, setting.treeId, node, clickFlag]); + return true; + }, + onZTreeMousedown: function (event, node) { + var setting = data.getSetting(event.data.treeId); + if (tools.apply(setting.callback.beforeMouseDown, [setting.treeId, node], true)) { + tools.apply(setting.callback.onMouseDown, [event, setting.treeId, node]); + } + return true; + }, + onZTreeMouseup: function (event, node) { + var setting = data.getSetting(event.data.treeId); + if (tools.apply(setting.callback.beforeMouseUp, [setting.treeId, node], true)) { + tools.apply(setting.callback.onMouseUp, [event, setting.treeId, node]); + } + return true; + }, + onZTreeDblclick: function (event, node) { + var setting = data.getSetting(event.data.treeId); + if (tools.apply(setting.callback.beforeDblClick, [setting.treeId, node], true)) { + tools.apply(setting.callback.onDblClick, [event, setting.treeId, node]); + } + return true; + }, + onZTreeContextmenu: function (event, node) { + var setting = data.getSetting(event.data.treeId); + if (tools.apply(setting.callback.beforeRightClick, [setting.treeId, node], true)) { + tools.apply(setting.callback.onRightClick, [event, setting.treeId, node]); + } + return (typeof setting.callback.onRightClick) != "function"; + }, + onSelectStart: function (e) { + var n = e.originalEvent.srcElement.nodeName.toLowerCase(); + return (n === "input" || n === "textarea"); + } + }, + //method of tools for zTree + tools = { + apply: function (fun, param, defaultValue) { + if ((typeof fun) == "function") { + return fun.apply(zt, param ? param : []); + } + return defaultValue; + }, + canAsync: function (setting, node) { + var children = data.nodeChildren(setting, node); + var isParent = data.nodeIsParent(setting, node); + return (setting.async.enable && node && isParent && !(node.zAsync || (children && children.length > 0))); + }, + clone: function (obj) { + if (obj === null) return null; + var o = tools.isArray(obj) ? [] : {}; + for (var i in obj) { + o[i] = (obj[i] instanceof Date) ? new Date(obj[i].getTime()) : (typeof obj[i] === "object" ? tools.clone(obj[i]) : obj[i]); + } + return o; + }, + eqs: function (str1, str2) { + return str1.toLowerCase() === str2.toLowerCase(); + }, + isArray: function (arr) { + return Object.prototype.toString.apply(arr) === "[object Array]"; + }, + isElement: function (o) { + return ( + typeof HTMLElement === "object" ? o instanceof HTMLElement : //DOM2 + o && typeof o === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName === "string" + ); + }, + $: function (node, exp, setting) { + if (!!exp && typeof exp != "string") { + setting = exp; + exp = ""; + } + if (typeof node == "string") { + return $(node, setting ? setting.treeObj.get(0).ownerDocument : null); + } else { + return $("#" + node.tId + exp, setting ? setting.treeObj : null); + } + }, + getMDom: function (setting, curDom, targetExpr) { + if (!curDom) return null; + while (curDom && curDom.id !== setting.treeId) { + for (var i = 0, l = targetExpr.length; curDom.tagName && i < l; i++) { + if (tools.eqs(curDom.tagName, targetExpr[i].tagName) && curDom.getAttribute(targetExpr[i].attrName) !== null) { + return curDom; + } + } + curDom = curDom.parentNode; + } + return null; + }, + getNodeMainDom: function (target) { + return ($(target).parent("li").get(0) || $(target).parentsUntil("li").parent().get(0)); + }, + isChildOrSelf: function (dom, parentId) { + return ($(dom).closest("#" + parentId).length > 0); + }, + uCanDo: function (setting, e) { + return true; + } + }, + //method of operate ztree dom + view = { + addNodes: function (setting, parentNode, index, newNodes, isSilent) { + var isParent = data.nodeIsParent(setting, parentNode); + if (setting.data.keep.leaf && parentNode && !isParent) { + return; + } + if (!tools.isArray(newNodes)) { + newNodes = [newNodes]; + } + if (setting.data.simpleData.enable) { + newNodes = data.transformTozTreeFormat(setting, newNodes); + } + if (parentNode) { + var target_switchObj = $$(parentNode, consts.id.SWITCH, setting), + target_icoObj = $$(parentNode, consts.id.ICON, setting), + target_ulObj = $$(parentNode, consts.id.UL, setting); + + if (!parentNode.open) { + view.replaceSwitchClass(parentNode, target_switchObj, consts.folder.CLOSE); + view.replaceIcoClass(parentNode, target_icoObj, consts.folder.CLOSE); + parentNode.open = false; + target_ulObj.css({ + "display": "none" + }); + } + + data.addNodesData(setting, parentNode, index, newNodes); + view.createNodes(setting, parentNode.level + 1, newNodes, parentNode, index); + if (!isSilent) { + view.expandCollapseParentNode(setting, parentNode, true); + } + } else { + data.addNodesData(setting, data.getRoot(setting), index, newNodes); + view.createNodes(setting, 0, newNodes, null, index); + } + }, + appendNodes: function (setting, level, nodes, parentNode, index, initFlag, openFlag) { + if (!nodes) return []; + var html = []; + + var tmpPNode = (parentNode) ? parentNode : data.getRoot(setting), + tmpPChild = data.nodeChildren(setting, tmpPNode), + isFirstNode, isLastNode; + + if (!tmpPChild || index >= tmpPChild.length - nodes.length) { + index = -1; + } + + for (var i = 0, l = nodes.length; i < l; i++) { + var node = nodes[i]; + if (initFlag) { + isFirstNode = ((index === 0 || tmpPChild.length == nodes.length) && (i == 0)); + isLastNode = (index < 0 && i == (nodes.length - 1)); + data.initNode(setting, level, node, parentNode, isFirstNode, isLastNode, openFlag); + data.addNodeCache(setting, node); + } + var isParent = data.nodeIsParent(setting, node); + + var childHtml = []; + var children = data.nodeChildren(setting, node); + if (children && children.length > 0) { + //make child html first, because checkType + childHtml = view.appendNodes(setting, level + 1, children, node, -1, initFlag, openFlag && node.open); + } + if (openFlag) { + view.makeDOMNodeMainBefore(html, setting, node); + view.makeDOMNodeLine(html, setting, node); + data.getBeforeA(setting, node, html); + view.makeDOMNodeNameBefore(html, setting, node); + data.getInnerBeforeA(setting, node, html); + view.makeDOMNodeIcon(html, setting, node); + data.getInnerAfterA(setting, node, html); + view.makeDOMNodeNameAfter(html, setting, node); + data.getAfterA(setting, node, html); + if (isParent && node.open) { + view.makeUlHtml(setting, node, html, childHtml.join('')); + } + view.makeDOMNodeMainAfter(html, setting, node); + data.addCreatedNode(setting, node); + } + } + return html; + }, + appendParentULDom: function (setting, node) { + var html = [], + nObj = $$(node, setting); + if (!nObj.get(0) && !!node.parentTId) { + view.appendParentULDom(setting, node.getParentNode()); + nObj = $$(node, setting); + } + var ulObj = $$(node, consts.id.UL, setting); + if (ulObj.get(0)) { + ulObj.remove(); + } + var children = data.nodeChildren(setting, node), + childHtml = view.appendNodes(setting, node.level + 1, children, node, -1, false, true); + view.makeUlHtml(setting, node, html, childHtml.join('')); + nObj.append(html.join('')); + }, + asyncNode: function (setting, node, isSilent, callback) { + var i, l; + var isParent = data.nodeIsParent(setting, node); + if (node && !isParent) { + tools.apply(callback); + return false; + } else if (node && node.isAjaxing) { + return false; + } else if (tools.apply(setting.callback.beforeAsync, [setting.treeId, node], true) == false) { + tools.apply(callback); + return false; + } + if (node) { + node.isAjaxing = true; + var icoObj = $$(node, consts.id.ICON, setting); + icoObj.attr({"style": "", "class": consts.className.BUTTON + " " + consts.className.ICO_LOADING}); + } + + var tmpParam = {}; + var autoParam = tools.apply(setting.async.autoParam, [setting.treeId, node], setting.async.autoParam); + for (i = 0, l = autoParam.length; node && i < l; i++) { + var pKey = autoParam[i].split("="), spKey = pKey; + if (pKey.length > 1) { + spKey = pKey[1]; + pKey = pKey[0]; + } + tmpParam[spKey] = node[pKey]; + } + var otherParam = tools.apply(setting.async.otherParam, [setting.treeId, node], setting.async.otherParam); + if (tools.isArray(otherParam)) { + for (i = 0, l = otherParam.length; i < l; i += 2) { + tmpParam[otherParam[i]] = otherParam[i + 1]; + } + } else { + for (var p in otherParam) { + tmpParam[p] = otherParam[p]; + } + } + + var _tmpV = data.getRoot(setting)._ver; + $.ajax({ + contentType: setting.async.contentType, + cache: false, + type: setting.async.type, + url: tools.apply(setting.async.url, [setting.treeId, node], setting.async.url), + data: setting.async.contentType.indexOf('application/json') > -1 ? JSON.stringify(tmpParam) : tmpParam, + dataType: setting.async.dataType, + headers: setting.async.headers, + xhrFields: setting.async.xhrFields, + success: function (msg) { + if (_tmpV != data.getRoot(setting)._ver) { + return; + } + var newNodes = []; + try { + if (!msg || msg.length == 0) { + newNodes = []; + } else if (typeof msg == "string") { + newNodes = eval("(" + msg + ")"); + } else { + newNodes = msg; + } + } catch (err) { + newNodes = msg; + } + + if (node) { + node.isAjaxing = null; + node.zAsync = true; + } + view.setNodeLineIcos(setting, node); + if (newNodes && newNodes !== "") { + newNodes = tools.apply(setting.async.dataFilter, [setting.treeId, node, newNodes], newNodes); + view.addNodes(setting, node, -1, !!newNodes ? tools.clone(newNodes) : [], !!isSilent); + } else { + view.addNodes(setting, node, -1, [], !!isSilent); + } + setting.treeObj.trigger(consts.event.ASYNC_SUCCESS, [setting.treeId, node, msg]); + tools.apply(callback); + }, + error: function (XMLHttpRequest, textStatus, errorThrown) { + if (_tmpV != data.getRoot(setting)._ver) { + return; + } + if (node) node.isAjaxing = null; + view.setNodeLineIcos(setting, node); + setting.treeObj.trigger(consts.event.ASYNC_ERROR, [setting.treeId, node, XMLHttpRequest, textStatus, errorThrown]); + } + }); + return true; + }, + cancelPreSelectedNode: function (setting, node, excludeNode) { + var list = data.getRoot(setting).curSelectedList, + i, n; + for (i = list.length - 1; i >= 0; i--) { + n = list[i]; + if (node === n || (!node && (!excludeNode || excludeNode !== n))) { + $$(n, consts.id.A, setting).removeClass(consts.node.CURSELECTED); + if (node) { + data.removeSelectedNode(setting, node); + break; + } else { + list.splice(i, 1); + setting.treeObj.trigger(consts.event.UNSELECTED, [setting.treeId, n]); + } + } + } + }, + createNodeCallback: function (setting) { + if (!!setting.callback.onNodeCreated || !!setting.view.addDiyDom) { + var root = data.getRoot(setting); + while (root.createdNodes.length > 0) { + var node = root.createdNodes.shift(); + tools.apply(setting.view.addDiyDom, [setting.treeId, node]); + if (!!setting.callback.onNodeCreated) { + setting.treeObj.trigger(consts.event.NODECREATED, [setting.treeId, node]); + } + } + } + }, + createNodes: function (setting, level, nodes, parentNode, index) { + if (!nodes || nodes.length == 0) return; + var root = data.getRoot(setting), + openFlag = !parentNode || parentNode.open || !!$$(data.nodeChildren(setting, parentNode)[0], setting).get(0); + root.createdNodes = []; + var zTreeHtml = view.appendNodes(setting, level, nodes, parentNode, index, true, openFlag), + parentObj, nextObj; + + if (!parentNode) { + parentObj = setting.treeObj; + //setting.treeObj.append(zTreeHtml.join('')); + } else { + var ulObj = $$(parentNode, consts.id.UL, setting); + if (ulObj.get(0)) { + parentObj = ulObj; + //ulObj.append(zTreeHtml.join('')); + } + } + if (parentObj) { + if (index >= 0) { + nextObj = parentObj.children()[index]; + } + if (index >= 0 && nextObj) { + $(nextObj).before(zTreeHtml.join('')); + } else { + parentObj.append(zTreeHtml.join('')); + } + } + + view.createNodeCallback(setting); + }, + destroy: function (setting) { + if (!setting) return; + data.initCache(setting); + data.initRoot(setting); + event.unbindTree(setting); + event.unbindEvent(setting); + setting.treeObj.empty(); + delete settings[setting.treeId]; + }, + expandCollapseNode: function (setting, node, expandFlag, animateFlag, callback) { + var root = data.getRoot(setting); + var tmpCb, _callback; + if (!node) { + tools.apply(callback, []); + return; + } + var children = data.nodeChildren(setting, node); + var isParent = data.nodeIsParent(setting, node); + if (root.expandTriggerFlag) { + _callback = callback; + tmpCb = function () { + if (_callback) _callback(); + if (node.open) { + setting.treeObj.trigger(consts.event.EXPAND, [setting.treeId, node]); + } else { + setting.treeObj.trigger(consts.event.COLLAPSE, [setting.treeId, node]); + } + }; + callback = tmpCb; + root.expandTriggerFlag = false; + } + if (!node.open && isParent && ((!$$(node, consts.id.UL, setting).get(0)) || (children && children.length > 0 && !$$(children[0], setting).get(0)))) { + view.appendParentULDom(setting, node); + view.createNodeCallback(setting); + } + if (node.open == expandFlag) { + tools.apply(callback, []); + return; + } + var ulObj = $$(node, consts.id.UL, setting), + switchObj = $$(node, consts.id.SWITCH, setting), + icoObj = $$(node, consts.id.ICON, setting); + + if (isParent) { + node.open = !node.open; + if (node.iconOpen && node.iconClose) { + icoObj.attr("style", view.makeNodeIcoStyle(setting, node)); + } + + if (node.open) { + view.replaceSwitchClass(node, switchObj, consts.folder.OPEN); + view.replaceIcoClass(node, icoObj, consts.folder.OPEN); + if (animateFlag == false || setting.view.expandSpeed == "") { + ulObj.show(); + tools.apply(callback, []); + } else { + if (children && children.length > 0) { + ulObj.slideDown(setting.view.expandSpeed, callback); + } else { + ulObj.show(); + tools.apply(callback, []); + } + } + } else { + view.replaceSwitchClass(node, switchObj, consts.folder.CLOSE); + view.replaceIcoClass(node, icoObj, consts.folder.CLOSE); + if (animateFlag == false || setting.view.expandSpeed == "" || !(children && children.length > 0)) { + ulObj.hide(); + tools.apply(callback, []); + } else { + ulObj.slideUp(setting.view.expandSpeed, callback); + } + } + } else { + tools.apply(callback, []); + } + }, + expandCollapseParentNode: function (setting, node, expandFlag, animateFlag, callback) { + if (!node) return; + if (!node.parentTId) { + view.expandCollapseNode(setting, node, expandFlag, animateFlag, callback); + return; + } else { + view.expandCollapseNode(setting, node, expandFlag, animateFlag); + } + if (node.parentTId) { + view.expandCollapseParentNode(setting, node.getParentNode(), expandFlag, animateFlag, callback); + } + }, + expandCollapseSonNode: function (setting, node, expandFlag, animateFlag, callback) { + var root = data.getRoot(setting), + treeNodes = (node) ? data.nodeChildren(setting, node) : data.nodeChildren(setting, root), + selfAnimateSign = (node) ? false : animateFlag, + expandTriggerFlag = data.getRoot(setting).expandTriggerFlag; + data.getRoot(setting).expandTriggerFlag = false; + if (treeNodes) { + for (var i = 0, l = treeNodes.length; i < l; i++) { + if (treeNodes[i]) view.expandCollapseSonNode(setting, treeNodes[i], expandFlag, selfAnimateSign); + } + } + data.getRoot(setting).expandTriggerFlag = expandTriggerFlag; + view.expandCollapseNode(setting, node, expandFlag, animateFlag, callback); + }, + isSelectedNode: function (setting, node) { + if (!node) { + return false; + } + var list = data.getRoot(setting).curSelectedList, + i; + for (i = list.length - 1; i >= 0; i--) { + if (node === list[i]) { + return true; + } + } + return false; + }, + makeDOMNodeIcon: function (html, setting, node) { + var nameStr = data.nodeName(setting, node), + name = setting.view.nameIsHTML ? nameStr : nameStr.replace(/&/g, '&').replace(//g, '>'); + html.push("", name, ""); + }, + makeDOMNodeLine: function (html, setting, node) { + html.push(""); + }, + makeDOMNodeMainAfter: function (html, setting, node) { + html.push(""); + }, + makeDOMNodeMainBefore: function (html, setting, node) { + html.push("
  • "); + }, + makeDOMNodeNameAfter: function (html, setting, node) { + html.push(""); + }, + makeDOMNodeNameBefore: function (html, setting, node) { + var title = data.nodeTitle(setting, node), + url = view.makeNodeUrl(setting, node), + fontcss = view.makeNodeFontCss(setting, node), + nodeClasses = view.makeNodeClasses(setting, node), + fontStyle = []; + for (var f in fontcss) { + fontStyle.push(f, ":", fontcss[f], ";"); + } + html.push(" 0) ? " href='" + url + "'" : ""), " target='", view.makeNodeTarget(node), "' style='", fontStyle.join(''), + "'"); + if (tools.apply(setting.view.showTitle, [setting.treeId, node], setting.view.showTitle) && title) { + html.push("title='", title.replace(/'/g, "'").replace(//g, '>'), "'"); + } + html.push(">"); + }, + makeNodeFontCss: function (setting, node) { + var fontCss = tools.apply(setting.view.fontCss, [setting.treeId, node], setting.view.fontCss); + return (fontCss && ((typeof fontCss) != "function")) ? fontCss : {}; + }, + makeNodeClasses: function (setting, node) { + var classes = tools.apply(setting.view.nodeClasses, [setting.treeId, node], setting.view.nodeClasses); + return (classes && (typeof classes !== "function")) ? classes : {add:[], remove:[]}; + }, + makeNodeIcoClass: function (setting, node) { + var icoCss = ["ico"]; + if (!node.isAjaxing) { + var isParent = data.nodeIsParent(setting, node); + icoCss[0] = (node.iconSkin ? node.iconSkin + "_" : "") + icoCss[0]; + if (isParent) { + icoCss.push(node.open ? consts.folder.OPEN : consts.folder.CLOSE); + } else { + icoCss.push(consts.folder.DOCU); + } + } + return consts.className.BUTTON + " " + icoCss.join('_'); + }, + makeNodeIcoStyle: function (setting, node) { + var icoStyle = []; + if (!node.isAjaxing) { + var isParent = data.nodeIsParent(setting, node); + var icon = (isParent && node.iconOpen && node.iconClose) ? (node.open ? node.iconOpen : node.iconClose) : node[setting.data.key.icon]; + if (icon) icoStyle.push("background:url(", icon, ") 0 0 no-repeat;"); + if (setting.view.showIcon == false || !tools.apply(setting.view.showIcon, [setting.treeId, node], true)) { + icoStyle.push("display:none;"); + } + } + return icoStyle.join(''); + }, + makeNodeLineClass: function (setting, node) { + var lineClass = []; + if (setting.view.showLine) { + if (node.level == 0 && node.isFirstNode && node.isLastNode) { + lineClass.push(consts.line.ROOT); + } else if (node.level == 0 && node.isFirstNode) { + lineClass.push(consts.line.ROOTS); + } else if (node.isLastNode) { + lineClass.push(consts.line.BOTTOM); + } else { + lineClass.push(consts.line.CENTER); + } + } else { + lineClass.push(consts.line.NOLINE); + } + if (data.nodeIsParent(setting, node)) { + lineClass.push(node.open ? consts.folder.OPEN : consts.folder.CLOSE); + } else { + lineClass.push(consts.folder.DOCU); + } + return view.makeNodeLineClassEx(node) + lineClass.join('_'); + }, + makeNodeLineClassEx: function (node) { + return consts.className.BUTTON + " " + consts.className.LEVEL + node.level + " " + consts.className.SWITCH + " "; + }, + makeNodeTarget: function (node) { + return (node.target || "_blank"); + }, + makeNodeUrl: function (setting, node) { + var urlKey = setting.data.key.url; + return node[urlKey] ? node[urlKey] : null; + }, + makeUlHtml: function (setting, node, html, content) { + html.push("
      "); + html.push(content); + html.push("
    "); + }, + makeUlLineClass: function (setting, node) { + return ((setting.view.showLine && !node.isLastNode) ? consts.line.LINE : ""); + }, + removeChildNodes: function (setting, node) { + if (!node) return; + var nodes = data.nodeChildren(setting, node); + if (!nodes) return; + + for (var i = 0, l = nodes.length; i < l; i++) { + data.removeNodeCache(setting, nodes[i]); + } + data.removeSelectedNode(setting); + delete node[setting.data.key.children]; + + if (!setting.data.keep.parent) { + data.nodeIsParent(setting, node, false); + node.open = false; + var tmp_switchObj = $$(node, consts.id.SWITCH, setting), + tmp_icoObj = $$(node, consts.id.ICON, setting); + view.replaceSwitchClass(node, tmp_switchObj, consts.folder.DOCU); + view.replaceIcoClass(node, tmp_icoObj, consts.folder.DOCU); + $$(node, consts.id.UL, setting).remove(); + } else { + $$(node, consts.id.UL, setting).empty(); + } + }, + scrollIntoView: function (setting, dom) { + if (!dom) { + return; + } + // support IE 7 / 8 + if (typeof Element === 'undefined' || typeof HTMLElement === 'undefined') { + var contRect = setting.treeObj.get(0).getBoundingClientRect(), + findMeRect = dom.getBoundingClientRect(); + if (findMeRect.top < contRect.top || findMeRect.bottom > contRect.bottom + || findMeRect.right > contRect.right || findMeRect.left < contRect.left) { + dom.scrollIntoView(); + } + return; + } + // CC-BY jocki84@googlemail.com, https://gist.github.com/jocki84/6ffafd003387179a988e + if (!Element.prototype.scrollIntoViewIfNeeded) { + Element.prototype.scrollIntoViewIfNeeded = function (centerIfNeeded) { + "use strict"; + + function makeRange(start, length) { + return {"start": start, "length": length, "end": start + length}; + } + + function coverRange(inner, outer) { + if ( + false === centerIfNeeded || + (outer.start < inner.end && inner.start < outer.end) + ) { + return Math.max( + inner.end - outer.length, + Math.min(outer.start, inner.start) + ); + } + return (inner.start + inner.end - outer.length) / 2; + } + + function makePoint(x, y) { + return { + "x": x, + "y": y, + "translate": function translate(dX, dY) { + return makePoint(x + dX, y + dY); + } + }; + } + + function absolute(elem, pt) { + while (elem) { + pt = pt.translate(elem.offsetLeft, elem.offsetTop); + elem = elem.offsetParent; + } + return pt; + } + + var target = absolute(this, makePoint(0, 0)), + extent = makePoint(this.offsetWidth, this.offsetHeight), + elem = this.parentNode, + origin; + + while (elem instanceof HTMLElement) { + // Apply desired scroll amount. + origin = absolute(elem, makePoint(elem.clientLeft, elem.clientTop)); + elem.scrollLeft = coverRange( + makeRange(target.x - origin.x, extent.x), + makeRange(elem.scrollLeft, elem.clientWidth) + ); + elem.scrollTop = coverRange( + makeRange(target.y - origin.y, extent.y), + makeRange(elem.scrollTop, elem.clientHeight) + ); + + // Determine actual scroll amount by reading back scroll properties. + target = target.translate(-elem.scrollLeft, -elem.scrollTop); + elem = elem.parentNode; + } + }; + } + dom.scrollIntoViewIfNeeded(); + }, + setFirstNode: function (setting, parentNode) { + var children = data.nodeChildren(setting, parentNode); + if (children.length > 0) { + children[0].isFirstNode = true; + } + }, + setLastNode: function (setting, parentNode) { + var children = data.nodeChildren(setting, parentNode); + if (children.length > 0) { + children[children.length - 1].isLastNode = true; + } + }, + removeNode: function (setting, node) { + var root = data.getRoot(setting), + parentNode = (node.parentTId) ? node.getParentNode() : root; + + node.isFirstNode = false; + node.isLastNode = false; + node.getPreNode = function () { + return null; + }; + node.getNextNode = function () { + return null; + }; + + if (!data.getNodeCache(setting, node.tId)) { + return; + } + + $$(node, setting).remove(); + data.removeNodeCache(setting, node); + data.removeSelectedNode(setting, node); + + var children = data.nodeChildren(setting, parentNode); + for (var i = 0, l = children.length; i < l; i++) { + if (children[i].tId == node.tId) { + children.splice(i, 1); + break; + } + } + view.setFirstNode(setting, parentNode); + view.setLastNode(setting, parentNode); + + var tmp_ulObj, tmp_switchObj, tmp_icoObj, + childLength = children.length; + + //repair nodes old parent + if (!setting.data.keep.parent && childLength == 0) { + //old parentNode has no child nodes + data.nodeIsParent(setting, parentNode, false); + parentNode.open = false; + delete parentNode[setting.data.key.children]; + tmp_ulObj = $$(parentNode, consts.id.UL, setting); + tmp_switchObj = $$(parentNode, consts.id.SWITCH, setting); + tmp_icoObj = $$(parentNode, consts.id.ICON, setting); + view.replaceSwitchClass(parentNode, tmp_switchObj, consts.folder.DOCU); + view.replaceIcoClass(parentNode, tmp_icoObj, consts.folder.DOCU); + tmp_ulObj.css("display", "none"); + + } else if (setting.view.showLine && childLength > 0) { + //old parentNode has child nodes + var newLast = children[childLength - 1]; + tmp_ulObj = $$(newLast, consts.id.UL, setting); + tmp_switchObj = $$(newLast, consts.id.SWITCH, setting); + tmp_icoObj = $$(newLast, consts.id.ICON, setting); + if (parentNode == root) { + if (children.length == 1) { + //node was root, and ztree has only one root after move node + view.replaceSwitchClass(newLast, tmp_switchObj, consts.line.ROOT); + } else { + var tmp_first_switchObj = $$(children[0], consts.id.SWITCH, setting); + view.replaceSwitchClass(children[0], tmp_first_switchObj, consts.line.ROOTS); + view.replaceSwitchClass(newLast, tmp_switchObj, consts.line.BOTTOM); + } + } else { + view.replaceSwitchClass(newLast, tmp_switchObj, consts.line.BOTTOM); + } + tmp_ulObj.removeClass(consts.line.LINE); + } + }, + replaceIcoClass: function (node, obj, newName) { + if (!obj || node.isAjaxing) return; + var tmpName = obj.attr("class"); + if (tmpName == undefined) return; + var tmpList = tmpName.split("_"); + switch (newName) { + case consts.folder.OPEN: + case consts.folder.CLOSE: + case consts.folder.DOCU: + tmpList[tmpList.length - 1] = newName; + break; + } + obj.attr("class", tmpList.join("_")); + }, + replaceSwitchClass: function (node, obj, newName) { + if (!obj) return; + var tmpName = obj.attr("class"); + if (tmpName == undefined) return; + var tmpList = tmpName.split("_"); + switch (newName) { + case consts.line.ROOT: + case consts.line.ROOTS: + case consts.line.CENTER: + case consts.line.BOTTOM: + case consts.line.NOLINE: + tmpList[0] = view.makeNodeLineClassEx(node) + newName; + break; + case consts.folder.OPEN: + case consts.folder.CLOSE: + case consts.folder.DOCU: + tmpList[1] = newName; + break; + } + obj.attr("class", tmpList.join("_")); + if (newName !== consts.folder.DOCU) { + obj.removeAttr("disabled"); + } else { + obj.attr("disabled", "disabled"); + } + }, + selectNode: function (setting, node, addFlag) { + if (!addFlag) { + view.cancelPreSelectedNode(setting, null, node); + } + $$(node, consts.id.A, setting).addClass(consts.node.CURSELECTED); + data.addSelectedNode(setting, node); + setting.treeObj.trigger(consts.event.SELECTED, [setting.treeId, node]); + }, + setNodeFontCss: function (setting, treeNode) { + var aObj = $$(treeNode, consts.id.A, setting), + fontCss = view.makeNodeFontCss(setting, treeNode); + if (fontCss) { + aObj.css(fontCss); + } + }, + setNodeClasses: function (setting, treeNode) { + var aObj = $$(treeNode, consts.id.A, setting), + classes = view.makeNodeClasses(setting, treeNode); + if ('add' in classes && classes.add.length) { + aObj.addClass(classes.add.join(' ')); + } + if ('remove' in classes && classes.remove.length) { + aObj.removeClass(classes.remove.join(' ')); + } + }, + setNodeLineIcos: function (setting, node) { + if (!node) return; + var switchObj = $$(node, consts.id.SWITCH, setting), + ulObj = $$(node, consts.id.UL, setting), + icoObj = $$(node, consts.id.ICON, setting), + ulLine = view.makeUlLineClass(setting, node); + if (ulLine.length == 0) { + ulObj.removeClass(consts.line.LINE); + } else { + ulObj.addClass(ulLine); + } + switchObj.attr("class", view.makeNodeLineClass(setting, node)); + if (data.nodeIsParent(setting, node)) { + switchObj.removeAttr("disabled"); + } else { + switchObj.attr("disabled", "disabled"); + } + icoObj.removeAttr("style"); + icoObj.attr("style", view.makeNodeIcoStyle(setting, node)); + icoObj.attr("class", view.makeNodeIcoClass(setting, node)); + }, + setNodeName: function (setting, node) { + var title = data.nodeTitle(setting, node), + nObj = $$(node, consts.id.SPAN, setting); + nObj.empty(); + if (setting.view.nameIsHTML) { + nObj.html(data.nodeName(setting, node)); + } else { + nObj.text(data.nodeName(setting, node)); + } + if (tools.apply(setting.view.showTitle, [setting.treeId, node], setting.view.showTitle)) { + var aObj = $$(node, consts.id.A, setting); + aObj.attr("title", !title ? "" : title); + } + }, + setNodeTarget: function (setting, node) { + var aObj = $$(node, consts.id.A, setting); + aObj.attr("target", view.makeNodeTarget(node)); + }, + setNodeUrl: function (setting, node) { + var aObj = $$(node, consts.id.A, setting), + url = view.makeNodeUrl(setting, node); + if (url == null || url.length == 0) { + aObj.removeAttr("href"); + } else { + aObj.attr("href", url); + } + }, + switchNode: function (setting, node) { + if (node.open || !tools.canAsync(setting, node)) { + view.expandCollapseNode(setting, node, !node.open); + } else if (setting.async.enable) { + if (!view.asyncNode(setting, node)) { + view.expandCollapseNode(setting, node, !node.open); + return; + } + } else if (node) { + view.expandCollapseNode(setting, node, !node.open); + } + } + }; + // zTree defind + $.fn.zTree = { + consts: _consts, + _z: { + tools: tools, + view: view, + event: event, + data: data + }, + getZTreeObj: function (treeId) { + var o = data.getZTreeTools(treeId); + return o ? o : null; + }, + destroy: function (treeId) { + if (!!treeId && treeId.length > 0) { + view.destroy(data.getSetting(treeId)); + } else { + for (var s in settings) { + view.destroy(settings[s]); + } + } + }, + init: function (obj, zSetting, zNodes) { + var setting = tools.clone(_setting); + $.extend(true, setting, zSetting); + setting.treeId = obj.attr("id"); + setting.treeObj = obj; + setting.treeObj.empty(); + settings[setting.treeId] = setting; + //For some older browser,(e.g., ie6) + if (typeof document.body.style.maxHeight === "undefined") { + setting.view.expandSpeed = ""; + } + data.initRoot(setting); + var root = data.getRoot(setting); + zNodes = zNodes ? tools.clone(tools.isArray(zNodes) ? zNodes : [zNodes]) : []; + if (setting.data.simpleData.enable) { + data.nodeChildren(setting, root, data.transformTozTreeFormat(setting, zNodes)); + } else { + data.nodeChildren(setting, root, zNodes); + } + + data.initCache(setting); + event.unbindTree(setting); + event.bindTree(setting); + event.unbindEvent(setting); + event.bindEvent(setting); + + var zTreeTools = { + setting: setting, + addNodes: function (parentNode, index, newNodes, isSilent) { + if (!parentNode) parentNode = null; + var isParent = data.nodeIsParent(setting, parentNode); + if (parentNode && !isParent && setting.data.keep.leaf) return null; + + var i = parseInt(index, 10); + if (isNaN(i)) { + isSilent = !!newNodes; + newNodes = index; + index = -1; + } else { + index = i; + } + if (!newNodes) return null; + + + var xNewNodes = tools.clone(tools.isArray(newNodes) ? newNodes : [newNodes]); + + function addCallback() { + view.addNodes(setting, parentNode, index, xNewNodes, (isSilent == true)); + } + + if (tools.canAsync(setting, parentNode)) { + view.asyncNode(setting, parentNode, isSilent, addCallback); + } else { + addCallback(); + } + return xNewNodes; + }, + cancelSelectedNode: function (node) { + view.cancelPreSelectedNode(setting, node); + }, + destroy: function () { + view.destroy(setting); + }, + expandAll: function (expandFlag) { + expandFlag = !!expandFlag; + view.expandCollapseSonNode(setting, null, expandFlag, true); + return expandFlag; + }, + expandNode: function (node, expandFlag, sonSign, focus, callbackFlag) { + if (!node || !data.nodeIsParent(setting, node)) return null; + if (expandFlag !== true && expandFlag !== false) { + expandFlag = !node.open; + } + callbackFlag = !!callbackFlag; + + if (callbackFlag && expandFlag && (tools.apply(setting.callback.beforeExpand, [setting.treeId, node], true) == false)) { + return null; + } else if (callbackFlag && !expandFlag && (tools.apply(setting.callback.beforeCollapse, [setting.treeId, node], true) == false)) { + return null; + } + if (expandFlag && node.parentTId) { + view.expandCollapseParentNode(setting, node.getParentNode(), expandFlag, false); + } + if (expandFlag === node.open && !sonSign) { + return null; + } + + data.getRoot(setting).expandTriggerFlag = callbackFlag; + if (!tools.canAsync(setting, node) && sonSign) { + view.expandCollapseSonNode(setting, node, expandFlag, true, showNodeFocus); + } else { + node.open = !expandFlag; + view.switchNode(this.setting, node); + showNodeFocus(); + } + return expandFlag; + + function showNodeFocus() { + var a = $$(node, consts.id.A, setting).get(0); + if (a && focus !== false) { + view.scrollIntoView(setting, a); + } + } + }, + getNodes: function () { + return data.getNodes(setting); + }, + getNodeByParam: function (key, value, parentNode) { + if (!key) return null; + return data.getNodeByParam(setting, parentNode ? data.nodeChildren(setting, parentNode) : data.getNodes(setting), key, value); + }, + getNodeByTId: function (tId) { + return data.getNodeCache(setting, tId); + }, + getNodesByParam: function (key, value, parentNode) { + if (!key) return null; + return data.getNodesByParam(setting, parentNode ? data.nodeChildren(setting, parentNode) : data.getNodes(setting), key, value); + }, + getNodesByParamFuzzy: function (key, value, parentNode) { + if (!key) return null; + return data.getNodesByParamFuzzy(setting, parentNode ? data.nodeChildren(setting, parentNode) : data.getNodes(setting), key, value); + }, + getNodesByFilter: function (filter, isSingle, parentNode, invokeParam) { + isSingle = !!isSingle; + if (!filter || (typeof filter != "function")) return (isSingle ? null : []); + return data.getNodesByFilter(setting, parentNode ? data.nodeChildren(setting, parentNode) : data.getNodes(setting), filter, isSingle, invokeParam); + }, + getNodeIndex: function (node) { + if (!node) return null; + var parentNode = (node.parentTId) ? node.getParentNode() : data.getRoot(setting); + var children = data.nodeChildren(setting, parentNode); + for (var i = 0, l = children.length; i < l; i++) { + if (children[i] == node) return i; + } + return -1; + }, + getSelectedNodes: function () { + var r = [], list = data.getRoot(setting).curSelectedList; + for (var i = 0, l = list.length; i < l; i++) { + r.push(list[i]); + } + return r; + }, + isSelectedNode: function (node) { + return data.isSelectedNode(setting, node); + }, + reAsyncChildNodesPromise: function (parentNode, reloadType, isSilent) { + var promise = new Promise(function (resolve, reject) { + try { + zTreeTools.reAsyncChildNodes(parentNode, reloadType, isSilent, function () { + resolve(parentNode); + }); + } catch (e) { + reject(e); + } + }); + return promise; + }, + reAsyncChildNodes: function (parentNode, reloadType, isSilent, callback) { + if (!this.setting.async.enable) return; + var isRoot = !parentNode; + if (isRoot) { + parentNode = data.getRoot(setting); + } + if (reloadType == "refresh") { + var children = data.nodeChildren(setting, parentNode); + for (var i = 0, l = children ? children.length : 0; i < l; i++) { + data.removeNodeCache(setting, children[i]); + } + data.removeSelectedNode(setting); + data.nodeChildren(setting, parentNode, []); + if (isRoot) { + this.setting.treeObj.empty(); + } else { + var ulObj = $$(parentNode, consts.id.UL, setting); + ulObj.empty(); + } + } + view.asyncNode(this.setting, isRoot ? null : parentNode, !!isSilent, callback); + }, + refresh: function () { + this.setting.treeObj.empty(); + var root = data.getRoot(setting), + nodes = data.nodeChildren(setting, root); + data.initRoot(setting); + data.nodeChildren(setting, root, nodes); + data.initCache(setting); + view.createNodes(setting, 0, data.nodeChildren(setting, root), null, -1); + }, + removeChildNodes: function (node) { + if (!node) return null; + var nodes = data.nodeChildren(setting, node); + view.removeChildNodes(setting, node); + return nodes ? nodes : null; + }, + removeNode: function (node, callbackFlag) { + if (!node) return; + callbackFlag = !!callbackFlag; + if (callbackFlag && tools.apply(setting.callback.beforeRemove, [setting.treeId, node], true) == false) return; + view.removeNode(setting, node); + if (callbackFlag) { + this.setting.treeObj.trigger(consts.event.REMOVE, [setting.treeId, node]); + } + }, + selectNode: function (node, addFlag, isSilent) { + if (!node) return; + if (tools.uCanDo(setting)) { + addFlag = setting.view.selectedMulti && addFlag; + if (node.parentTId) { + view.expandCollapseParentNode(setting, node.getParentNode(), true, false, showNodeFocus); + } else if (!isSilent) { + try { + $$(node, setting).focus().blur(); + } catch (e) { + } + } + view.selectNode(setting, node, addFlag); + } + + function showNodeFocus() { + if (isSilent) { + return; + } + var a = $$(node, setting).get(0); + view.scrollIntoView(setting, a); + } + }, + transformTozTreeNodes: function (simpleNodes) { + return data.transformTozTreeFormat(setting, simpleNodes); + }, + transformToArray: function (nodes) { + return data.transformToArrayFormat(setting, nodes); + }, + updateNode: function (node, checkTypeFlag) { + if (!node) return; + var nObj = $$(node, setting); + if (nObj.get(0) && tools.uCanDo(setting)) { + view.setNodeName(setting, node); + view.setNodeTarget(setting, node); + view.setNodeUrl(setting, node); + view.setNodeLineIcos(setting, node); + view.setNodeFontCss(setting, node); + view.setNodeClasses(setting, node); + } + } + }; + root.treeTools = zTreeTools; + data.setZTreeTools(setting, zTreeTools); + var children = data.nodeChildren(setting, root); + if (children && children.length > 0) { + view.createNodes(setting, 0, children, null, -1); + } else if (setting.async.enable && setting.async.url && setting.async.url !== '') { + view.asyncNode(setting); + } + return zTreeTools; + } + }; + + var zt = $.fn.zTree, + $$ = tools.$, + consts = zt.consts; +})(jQuery); +/* + * JQuery zTree excheck + * v3.5.48 + * http://treejs.cn/ + * + * Copyright (c) 2010 Hunter.z + * + * Licensed same as jquery - MIT License + * http://www.opensource.org/licenses/mit-license.php + * + * Date: 2020-11-21 + */ + +(function ($) { + //default consts of excheck + var _consts = { + event: { + CHECK: "ztree_check" + }, + id: { + CHECK: "_check" + }, + checkbox: { + STYLE: "checkbox", + DEFAULT: "chk", + DISABLED: "disable", + FALSE: "false", + TRUE: "true", + FULL: "full", + PART: "part", + FOCUS: "focus" + }, + radio: { + STYLE: "radio", + TYPE_ALL: "all", + TYPE_LEVEL: "level" + } + }, + //default setting of excheck + _setting = { + check: { + enable: false, + autoCheckTrigger: false, + chkStyle: _consts.checkbox.STYLE, + nocheckInherit: false, + chkDisabledInherit: false, + radioType: _consts.radio.TYPE_LEVEL, + chkboxType: { + "Y": "ps", + "N": "ps" + } + }, + data: { + key: { + checked: "checked" + } + }, + callback: { + beforeCheck: null, + onCheck: null + } + }, + //default root of excheck + _initRoot = function (setting) { + var r = data.getRoot(setting); + r.radioCheckedList = []; + }, + //default cache of excheck + _initCache = function (treeId) { + }, + //default bind event of excheck + _bindEvent = function (setting) { + var o = setting.treeObj, + c = consts.event; + o.bind(c.CHECK, function (event, srcEvent, treeId, node) { + event.srcEvent = srcEvent; + tools.apply(setting.callback.onCheck, [event, treeId, node]); + }); + }, + _unbindEvent = function (setting) { + var o = setting.treeObj, + c = consts.event; + o.unbind(c.CHECK); + }, + //default event proxy of excheck + _eventProxy = function (e) { + var target = e.target, + setting = data.getSetting(e.data.treeId), + tId = "", node = null, + nodeEventType = "", treeEventType = "", + nodeEventCallback = null, treeEventCallback = null; + + if (tools.eqs(e.type, "mouseover")) { + if (setting.check.enable && tools.eqs(target.tagName, "span") && target.getAttribute("treeNode" + consts.id.CHECK) !== null) { + tId = tools.getNodeMainDom(target).id; + nodeEventType = "mouseoverCheck"; + } + } else if (tools.eqs(e.type, "mouseout")) { + if (setting.check.enable && tools.eqs(target.tagName, "span") && target.getAttribute("treeNode" + consts.id.CHECK) !== null) { + tId = tools.getNodeMainDom(target).id; + nodeEventType = "mouseoutCheck"; + } + } else if (tools.eqs(e.type, "click")) { + if (setting.check.enable && tools.eqs(target.tagName, "span") && target.getAttribute("treeNode" + consts.id.CHECK) !== null) { + tId = tools.getNodeMainDom(target).id; + nodeEventType = "checkNode"; + } + } + if (tId.length > 0) { + node = data.getNodeCache(setting, tId); + switch (nodeEventType) { + case "checkNode" : + nodeEventCallback = _handler.onCheckNode; + break; + case "mouseoverCheck" : + nodeEventCallback = _handler.onMouseoverCheck; + break; + case "mouseoutCheck" : + nodeEventCallback = _handler.onMouseoutCheck; + break; + } + } + var proxyResult = { + stop: nodeEventType === "checkNode", + node: node, + nodeEventType: nodeEventType, + nodeEventCallback: nodeEventCallback, + treeEventType: treeEventType, + treeEventCallback: treeEventCallback + }; + return proxyResult + }, + //default init node of excheck + _initNode = function (setting, level, n, parentNode, isFirstNode, isLastNode, openFlag) { + if (!n) return; + var checked = data.nodeChecked(setting, n); + n.checkedOld = checked; + if (typeof n.nocheck == "string") n.nocheck = tools.eqs(n.nocheck, "true"); + n.nocheck = !!n.nocheck || (setting.check.nocheckInherit && parentNode && !!parentNode.nocheck); + if (typeof n.chkDisabled == "string") n.chkDisabled = tools.eqs(n.chkDisabled, "true"); + n.chkDisabled = !!n.chkDisabled || (setting.check.chkDisabledInherit && parentNode && !!parentNode.chkDisabled); + if (typeof n.halfCheck == "string") n.halfCheck = tools.eqs(n.halfCheck, "true"); + n.halfCheck = !!n.halfCheck; + n.check_Child_State = -1; + n.check_Focus = false; + n.getCheckStatus = function () { + return data.getCheckStatus(setting, n); + }; + + if (setting.check.chkStyle == consts.radio.STYLE && setting.check.radioType == consts.radio.TYPE_ALL && checked) { + var r = data.getRoot(setting); + r.radioCheckedList.push(n); + } + }, + //add dom for check + _beforeA = function (setting, node, html) { + if (setting.check.enable) { + data.makeChkFlag(setting, node); + html.push(""); + } + }, + //update zTreeObj, add method of check + _zTreeTools = function (setting, zTreeTools) { + zTreeTools.checkNode = function (node, checked, checkTypeFlag, callbackFlag) { + var nodeChecked = data.nodeChecked(setting, node); + if (node.chkDisabled === true) return; + if (checked !== true && checked !== false) { + checked = !nodeChecked; + } + callbackFlag = !!callbackFlag; + + if (nodeChecked === checked && !checkTypeFlag) { + return; + } else if (callbackFlag && tools.apply(this.setting.callback.beforeCheck, [this.setting.treeId, node], true) == false) { + return; + } + if (tools.uCanDo(this.setting) && this.setting.check.enable && node.nocheck !== true) { + data.nodeChecked(setting, node, checked); + var checkObj = $$(node, consts.id.CHECK, this.setting); + if (checkTypeFlag || this.setting.check.chkStyle === consts.radio.STYLE) view.checkNodeRelation(this.setting, node); + view.setChkClass(this.setting, checkObj, node); + view.repairParentChkClassWithSelf(this.setting, node); + if (callbackFlag) { + this.setting.treeObj.trigger(consts.event.CHECK, [null, this.setting.treeId, node]); + } + } + } + + zTreeTools.checkAllNodes = function (checked) { + view.repairAllChk(this.setting, !!checked); + } + + zTreeTools.getCheckedNodes = function (checked) { + checked = (checked !== false); + var children = data.nodeChildren(setting, data.getRoot(this.setting)); + return data.getTreeCheckedNodes(this.setting, children, checked); + } + + zTreeTools.getChangeCheckedNodes = function () { + var children = data.nodeChildren(setting, data.getRoot(this.setting)); + return data.getTreeChangeCheckedNodes(this.setting, children); + } + + zTreeTools.setChkDisabled = function (node, disabled, inheritParent, inheritChildren) { + disabled = !!disabled; + inheritParent = !!inheritParent; + inheritChildren = !!inheritChildren; + view.repairSonChkDisabled(this.setting, node, disabled, inheritChildren); + view.repairParentChkDisabled(this.setting, node.getParentNode(), disabled, inheritParent); + } + + var _updateNode = zTreeTools.updateNode; + zTreeTools.updateNode = function (node, checkTypeFlag) { + if (_updateNode) _updateNode.apply(zTreeTools, arguments); + if (!node || !this.setting.check.enable) return; + var nObj = $$(node, this.setting); + if (nObj.get(0) && tools.uCanDo(this.setting)) { + var checkObj = $$(node, consts.id.CHECK, this.setting); + if (checkTypeFlag == true || this.setting.check.chkStyle === consts.radio.STYLE) view.checkNodeRelation(this.setting, node); + view.setChkClass(this.setting, checkObj, node); + view.repairParentChkClassWithSelf(this.setting, node); + } + } + }, + //method of operate data + _data = { + getRadioCheckedList: function (setting) { + var checkedList = data.getRoot(setting).radioCheckedList; + for (var i = 0, j = checkedList.length; i < j; i++) { + if (!data.getNodeCache(setting, checkedList[i].tId)) { + checkedList.splice(i, 1); + i--; + j--; + } + } + return checkedList; + }, + getCheckStatus: function (setting, node) { + if (!setting.check.enable || node.nocheck || node.chkDisabled) return null; + var checked = data.nodeChecked(setting, node), + r = { + checked: checked, + half: node.halfCheck ? node.halfCheck : (setting.check.chkStyle == consts.radio.STYLE ? (node.check_Child_State === 2) : (checked ? (node.check_Child_State > -1 && node.check_Child_State < 2) : (node.check_Child_State > 0))) + }; + return r; + }, + getTreeCheckedNodes: function (setting, nodes, checked, results) { + if (!nodes) return []; + var onlyOne = (checked && setting.check.chkStyle == consts.radio.STYLE && setting.check.radioType == consts.radio.TYPE_ALL); + results = !results ? [] : results; + for (var i = 0, l = nodes.length; i < l; i++) { + var node = nodes[i]; + var children = data.nodeChildren(setting, node); + var nodeChecked = data.nodeChecked(setting, node); + if (node.nocheck !== true && node.chkDisabled !== true && nodeChecked == checked) { + results.push(node); + if (onlyOne) { + break; + } + } + data.getTreeCheckedNodes(setting, children, checked, results); + if (onlyOne && results.length > 0) { + break; + } + } + return results; + }, + getTreeChangeCheckedNodes: function (setting, nodes, results) { + if (!nodes) return []; + results = !results ? [] : results; + for (var i = 0, l = nodes.length; i < l; i++) { + var node = nodes[i]; + var children = data.nodeChildren(setting, node); + var nodeChecked = data.nodeChecked(setting, node); + if (node.nocheck !== true && node.chkDisabled !== true && nodeChecked != node.checkedOld) { + results.push(node); + } + data.getTreeChangeCheckedNodes(setting, children, results); + } + return results; + }, + makeChkFlag: function (setting, node) { + if (!node) return; + var chkFlag = -1; + var children = data.nodeChildren(setting, node); + if (children) { + for (var i = 0, l = children.length; i < l; i++) { + var cNode = children[i]; + var nodeChecked = data.nodeChecked(setting, cNode); + var tmp = -1; + if (setting.check.chkStyle == consts.radio.STYLE) { + if (cNode.nocheck === true || cNode.chkDisabled === true) { + tmp = cNode.check_Child_State; + } else if (cNode.halfCheck === true) { + tmp = 2; + } else if (nodeChecked) { + tmp = 2; + } else { + tmp = cNode.check_Child_State > 0 ? 2 : 0; + } + if (tmp == 2) { + chkFlag = 2; + break; + } else if (tmp == 0) { + chkFlag = 0; + } + } else if (setting.check.chkStyle == consts.checkbox.STYLE) { + if (cNode.nocheck === true || cNode.chkDisabled === true) { + tmp = cNode.check_Child_State; + } else if (cNode.halfCheck === true) { + tmp = 1; + } else if (nodeChecked) { + tmp = (cNode.check_Child_State === -1 || cNode.check_Child_State === 2) ? 2 : 1; + } else { + tmp = (cNode.check_Child_State > 0) ? 1 : 0; + } + if (tmp === 1) { + chkFlag = 1; + break; + } else if (tmp === 2 && chkFlag > -1 && i > 0 && tmp !== chkFlag) { + chkFlag = 1; + break; + } else if (chkFlag === 2 && tmp > -1 && tmp < 2) { + chkFlag = 1; + break; + } else if (tmp > -1) { + chkFlag = tmp; + } + } + } + } + node.check_Child_State = chkFlag; + } + }, + //method of event proxy + _event = {}, + //method of event handler + _handler = { + onCheckNode: function (event, node) { + if (node.chkDisabled === true) return false; + var setting = data.getSetting(event.data.treeId); + if (tools.apply(setting.callback.beforeCheck, [setting.treeId, node], true) == false) return true; + var nodeChecked = data.nodeChecked(setting, node); + data.nodeChecked(setting, node, !nodeChecked); + view.checkNodeRelation(setting, node); + var checkObj = $$(node, consts.id.CHECK, setting); + view.setChkClass(setting, checkObj, node); + view.repairParentChkClassWithSelf(setting, node); + setting.treeObj.trigger(consts.event.CHECK, [event, setting.treeId, node]); + return true; + }, + onMouseoverCheck: function (event, node) { + if (node.chkDisabled === true) return false; + var setting = data.getSetting(event.data.treeId), + checkObj = $$(node, consts.id.CHECK, setting); + node.check_Focus = true; + view.setChkClass(setting, checkObj, node); + return true; + }, + onMouseoutCheck: function (event, node) { + if (node.chkDisabled === true) return false; + var setting = data.getSetting(event.data.treeId), + checkObj = $$(node, consts.id.CHECK, setting); + node.check_Focus = false; + view.setChkClass(setting, checkObj, node); + return true; + } + }, + //method of tools for zTree + _tools = {}, + //method of operate ztree dom + _view = { + checkNodeRelation: function (setting, node) { + var pNode, i, l, + r = consts.radio; + var nodeChecked = data.nodeChecked(setting, node); + if (setting.check.chkStyle == r.STYLE) { + var checkedList = data.getRadioCheckedList(setting); + if (nodeChecked) { + if (setting.check.radioType == r.TYPE_ALL) { + for (i = checkedList.length - 1; i >= 0; i--) { + pNode = checkedList[i]; + var pNodeChecked = data.nodeChecked(setting, pNode); + if (pNodeChecked && pNode != node) { + data.nodeChecked(setting, pNode, false); + checkedList.splice(i, 1); + + view.setChkClass(setting, $$(pNode, consts.id.CHECK, setting), pNode); + if (pNode.parentTId != node.parentTId) { + view.repairParentChkClassWithSelf(setting, pNode); + } + } + } + checkedList.push(node); + } else { + var parentNode = (node.parentTId) ? node.getParentNode() : data.getRoot(setting); + var children = data.nodeChildren(setting, parentNode); + for (i = 0, l = children.length; i < l; i++) { + pNode = children[i]; + var pNodeChecked = data.nodeChecked(setting, pNode); + if (pNodeChecked && pNode != node) { + data.nodeChecked(setting, pNode, false); + view.setChkClass(setting, $$(pNode, consts.id.CHECK, setting), pNode); + } + } + } + } else if (setting.check.radioType == r.TYPE_ALL) { + for (i = 0, l = checkedList.length; i < l; i++) { + if (node == checkedList[i]) { + checkedList.splice(i, 1); + break; + } + } + } + + } else { + var children = data.nodeChildren(setting, node); + if (nodeChecked && (!children || children.length == 0 || setting.check.chkboxType.Y.indexOf("s") > -1)) { + view.setSonNodeCheckBox(setting, node, true); + } + if (!nodeChecked && (!children || children.length == 0 || setting.check.chkboxType.N.indexOf("s") > -1)) { + view.setSonNodeCheckBox(setting, node, false); + } + if (nodeChecked && setting.check.chkboxType.Y.indexOf("p") > -1) { + view.setParentNodeCheckBox(setting, node, true); + } + if (!nodeChecked && setting.check.chkboxType.N.indexOf("p") > -1) { + view.setParentNodeCheckBox(setting, node, false); + } + } + }, + makeChkClass: function (setting, node) { + var c = consts.checkbox, r = consts.radio, + fullStyle = ""; + var nodeChecked = data.nodeChecked(setting, node); + if (node.chkDisabled === true) { + fullStyle = c.DISABLED; + } else if (node.halfCheck) { + fullStyle = c.PART; + } else if (setting.check.chkStyle == r.STYLE) { + fullStyle = (node.check_Child_State < 1) ? c.FULL : c.PART; + } else { + fullStyle = nodeChecked ? ((node.check_Child_State === 2 || node.check_Child_State === -1) ? c.FULL : c.PART) : ((node.check_Child_State < 1) ? c.FULL : c.PART); + } + var chkName = setting.check.chkStyle + "_" + (nodeChecked ? c.TRUE : c.FALSE) + "_" + fullStyle; + chkName = (node.check_Focus && node.chkDisabled !== true) ? chkName + "_" + c.FOCUS : chkName; + return consts.className.BUTTON + " " + c.DEFAULT + " " + chkName; + }, + repairAllChk: function (setting, checked) { + if (setting.check.enable && setting.check.chkStyle === consts.checkbox.STYLE) { + var root = data.getRoot(setting); + var children = data.nodeChildren(setting, root); + for (var i = 0, l = children.length; i < l; i++) { + var node = children[i]; + if (node.nocheck !== true && node.chkDisabled !== true) { + data.nodeChecked(setting, node, checked); + } + view.setSonNodeCheckBox(setting, node, checked); + } + } + }, + repairChkClass: function (setting, node) { + if (!node) return; + data.makeChkFlag(setting, node); + if (node.nocheck !== true) { + var checkObj = $$(node, consts.id.CHECK, setting); + view.setChkClass(setting, checkObj, node); + } + }, + repairParentChkClass: function (setting, node) { + if (!node || !node.parentTId) return; + var pNode = node.getParentNode(); + view.repairChkClass(setting, pNode); + view.repairParentChkClass(setting, pNode); + }, + repairParentChkClassWithSelf: function (setting, node) { + if (!node) return; + var children = data.nodeChildren(setting, node); + if (children && children.length > 0) { + view.repairParentChkClass(setting, children[0]); + } else { + view.repairParentChkClass(setting, node); + } + }, + repairSonChkDisabled: function (setting, node, chkDisabled, inherit) { + if (!node) return; + if (node.chkDisabled != chkDisabled) { + node.chkDisabled = chkDisabled; + } + view.repairChkClass(setting, node); + var children = data.nodeChildren(setting, node); + if (children && inherit) { + for (var i = 0, l = children.length; i < l; i++) { + var sNode = children[i]; + view.repairSonChkDisabled(setting, sNode, chkDisabled, inherit); + } + } + }, + repairParentChkDisabled: function (setting, node, chkDisabled, inherit) { + if (!node) return; + if (node.chkDisabled != chkDisabled && inherit) { + node.chkDisabled = chkDisabled; + } + view.repairChkClass(setting, node); + view.repairParentChkDisabled(setting, node.getParentNode(), chkDisabled, inherit); + }, + setChkClass: function (setting, obj, node) { + if (!obj) return; + if (node.nocheck === true) { + obj.hide(); + } else { + obj.show(); + } + obj.attr('class', view.makeChkClass(setting, node)); + }, + setParentNodeCheckBox: function (setting, node, value, srcNode) { + var checkObj = $$(node, consts.id.CHECK, setting); + if (!srcNode) srcNode = node; + data.makeChkFlag(setting, node); + if (node.nocheck !== true && node.chkDisabled !== true) { + data.nodeChecked(setting, node, value); + view.setChkClass(setting, checkObj, node); + if (setting.check.autoCheckTrigger && node != srcNode) { + setting.treeObj.trigger(consts.event.CHECK, [null, setting.treeId, node]); + } + } + if (node.parentTId) { + var pSign = true; + if (!value) { + var pNodes = data.nodeChildren(setting, node.getParentNode()); + for (var i = 0, l = pNodes.length; i < l; i++) { + var pNode = pNodes[i]; + var nodeChecked = data.nodeChecked(setting, pNode); + if ((pNode.nocheck !== true && pNode.chkDisabled !== true && nodeChecked) + || ((pNode.nocheck === true || pNode.chkDisabled === true) && pNode.check_Child_State > 0)) { + pSign = false; + break; + } + } + } + if (pSign) { + view.setParentNodeCheckBox(setting, node.getParentNode(), value, srcNode); + } + } + }, + setSonNodeCheckBox: function (setting, node, value, srcNode) { + if (!node) return; + var checkObj = $$(node, consts.id.CHECK, setting); + if (!srcNode) srcNode = node; + + var hasDisable = false; + var children = data.nodeChildren(setting, node); + if (children) { + for (var i = 0, l = children.length; i < l; i++) { + var sNode = children[i]; + view.setSonNodeCheckBox(setting, sNode, value, srcNode); + if (sNode.chkDisabled === true) hasDisable = true; + } + } + + if (node != data.getRoot(setting) && node.chkDisabled !== true) { + if (hasDisable && node.nocheck !== true) { + data.makeChkFlag(setting, node); + } + if (node.nocheck !== true && node.chkDisabled !== true) { + data.nodeChecked(setting, node, value); + if (!hasDisable) node.check_Child_State = (children && children.length > 0) ? (value ? 2 : 0) : -1; + } else { + node.check_Child_State = -1; + } + view.setChkClass(setting, checkObj, node); + if (setting.check.autoCheckTrigger && node != srcNode && node.nocheck !== true && node.chkDisabled !== true) { + setting.treeObj.trigger(consts.event.CHECK, [null, setting.treeId, node]); + } + } + + } + }, + + _z = { + tools: _tools, + view: _view, + event: _event, + data: _data + }; + $.extend(true, $.fn.zTree.consts, _consts); + $.extend(true, $.fn.zTree._z, _z); + + var zt = $.fn.zTree, + tools = zt._z.tools, + consts = zt.consts, + view = zt._z.view, + data = zt._z.data, + event = zt._z.event, + $$ = tools.$; + + data.nodeChecked = function (setting, node, newChecked) { + if (!node) { + return false; + } + var key = setting.data.key.checked; + if (typeof newChecked !== 'undefined') { + if (typeof newChecked === "string") { + newChecked = tools.eqs(newChecked, "true"); + } + newChecked = !!newChecked; + node[key] = newChecked; + } else if (typeof node[key] == "string"){ + node[key] = tools.eqs(node[key], "true"); + } else { + node[key] = !!node[key]; + } + return node[key]; + }; + + data.exSetting(_setting); + data.addInitBind(_bindEvent); + data.addInitUnBind(_unbindEvent); + data.addInitCache(_initCache); + data.addInitNode(_initNode); + data.addInitProxy(_eventProxy, true); + data.addInitRoot(_initRoot); + data.addBeforeA(_beforeA); + data.addZTreeTools(_zTreeTools); + + var _createNodes = view.createNodes; + view.createNodes = function (setting, level, nodes, parentNode, index) { + if (_createNodes) _createNodes.apply(view, arguments); + if (!nodes) return; + view.repairParentChkClassWithSelf(setting, parentNode); + } + var _removeNode = view.removeNode; + view.removeNode = function (setting, node) { + var parentNode = node.getParentNode(); + if (_removeNode) _removeNode.apply(view, arguments); + if (!node || !parentNode) return; + view.repairChkClass(setting, parentNode); + view.repairParentChkClass(setting, parentNode); + } + + var _appendNodes = view.appendNodes; + view.appendNodes = function (setting, level, nodes, parentNode, index, initFlag, openFlag) { + var html = ""; + if (_appendNodes) { + html = _appendNodes.apply(view, arguments); + } + if (parentNode) { + data.makeChkFlag(setting, parentNode); + } + return html; + } +})(jQuery); +/* + * JQuery zTree exedit + * v3.5.48 + * http://treejs.cn/ + * + * Copyright (c) 2010 Hunter.z + * + * Licensed same as jquery - MIT License + * http://www.opensource.org/licenses/mit-license.php + * + * Date: 2020-11-21 + */ + +(function ($) { + //default consts of exedit + var _consts = { + event: { + DRAG: "ztree_drag", + DROP: "ztree_drop", + RENAME: "ztree_rename", + DRAGMOVE: "ztree_dragmove" + }, + id: { + EDIT: "_edit", + INPUT: "_input", + REMOVE: "_remove" + }, + move: { + TYPE_INNER: "inner", + TYPE_PREV: "prev", + TYPE_NEXT: "next" + }, + node: { + CURSELECTED_EDIT: "curSelectedNode_Edit", + TMPTARGET_TREE: "tmpTargetzTree", + TMPTARGET_NODE: "tmpTargetNode" + } + }, + //default setting of exedit + _setting = { + edit: { + enable: false, + editNameSelectAll: false, + showRemoveBtn: true, + showRenameBtn: true, + removeTitle: "remove", + renameTitle: "rename", + drag: { + autoExpandTrigger: false, + isCopy: true, + isMove: true, + prev: true, + next: true, + inner: true, + minMoveSize: 5, + borderMax: 10, + borderMin: -5, + maxShowNodeNum: 5, + autoOpenTime: 500 + } + }, + view: { + addHoverDom: null, + removeHoverDom: null + }, + callback: { + beforeDrag: null, + beforeDragOpen: null, + beforeDrop: null, + beforeEditName: null, + beforeRename: null, + onDrag: null, + onDragMove: null, + onDrop: null, + onRename: null + } + }, + //default root of exedit + _initRoot = function (setting) { + var r = data.getRoot(setting), rs = data.getRoots(); + r.curEditNode = null; + r.curEditInput = null; + r.curHoverNode = null; + r.dragFlag = 0; + r.dragNodeShowBefore = []; + r.dragMaskList = new Array(); + rs.showHoverDom = true; + }, + //default cache of exedit + _initCache = function (treeId) { + }, + //default bind event of exedit + _bindEvent = function (setting) { + var o = setting.treeObj; + var c = consts.event; + o.bind(c.RENAME, function (event, treeId, treeNode, isCancel) { + tools.apply(setting.callback.onRename, [event, treeId, treeNode, isCancel]); + }); + + o.bind(c.DRAG, function (event, srcEvent, treeId, treeNodes) { + tools.apply(setting.callback.onDrag, [srcEvent, treeId, treeNodes]); + }); + + o.bind(c.DRAGMOVE, function (event, srcEvent, treeId, treeNodes) { + tools.apply(setting.callback.onDragMove, [srcEvent, treeId, treeNodes]); + }); + + o.bind(c.DROP, function (event, srcEvent, treeId, treeNodes, targetNode, moveType, isCopy) { + tools.apply(setting.callback.onDrop, [srcEvent, treeId, treeNodes, targetNode, moveType, isCopy]); + }); + }, + _unbindEvent = function (setting) { + var o = setting.treeObj; + var c = consts.event; + o.unbind(c.RENAME); + o.unbind(c.DRAG); + o.unbind(c.DRAGMOVE); + o.unbind(c.DROP); + }, + //default event proxy of exedit + _eventProxy = function (e) { + var target = e.target, + setting = data.getSetting(e.data.treeId), + relatedTarget = e.relatedTarget, + tId = "", node = null, + nodeEventType = "", treeEventType = "", + nodeEventCallback = null, treeEventCallback = null, + tmp = null; + + if (tools.eqs(e.type, "mouseover")) { + tmp = tools.getMDom(setting, target, [{tagName: "a", attrName: "treeNode" + consts.id.A}]); + if (tmp) { + tId = tools.getNodeMainDom(tmp).id; + nodeEventType = "hoverOverNode"; + } + } else if (tools.eqs(e.type, "mouseout")) { + tmp = tools.getMDom(setting, relatedTarget, [{tagName: "a", attrName: "treeNode" + consts.id.A}]); + if (!tmp) { + tId = "remove"; + nodeEventType = "hoverOutNode"; + } + } else if (tools.eqs(e.type, "mousedown")) { + tmp = tools.getMDom(setting, target, [{tagName: "a", attrName: "treeNode" + consts.id.A}]); + if (tmp) { + tId = tools.getNodeMainDom(tmp).id; + nodeEventType = "mousedownNode"; + } + } + if (tId.length > 0) { + node = data.getNodeCache(setting, tId); + switch (nodeEventType) { + case "mousedownNode" : + nodeEventCallback = _handler.onMousedownNode; + break; + case "hoverOverNode" : + nodeEventCallback = _handler.onHoverOverNode; + break; + case "hoverOutNode" : + nodeEventCallback = _handler.onHoverOutNode; + break; + } + } + var proxyResult = { + stop: false, + node: node, + nodeEventType: nodeEventType, + nodeEventCallback: nodeEventCallback, + treeEventType: treeEventType, + treeEventCallback: treeEventCallback + }; + return proxyResult + }, + //default init node of exedit + _initNode = function (setting, level, n, parentNode, isFirstNode, isLastNode, openFlag) { + if (!n) return; + n.isHover = false; + n.editNameFlag = false; + }, + //update zTreeObj, add method of edit + _zTreeTools = function (setting, zTreeTools) { + zTreeTools.cancelEditName = function (newName) { + var root = data.getRoot(this.setting); + if (!root.curEditNode) return; + view.cancelCurEditNode(this.setting, newName ? newName : null, true); + } + zTreeTools.copyNode = function (targetNode, node, moveType, isSilent) { + if (!node) return null; + var isParent = data.nodeIsParent(setting, targetNode); + if (targetNode && !isParent && this.setting.data.keep.leaf && moveType === consts.move.TYPE_INNER) return null; + var _this = this, + newNode = tools.clone(node); + if (!targetNode) { + targetNode = null; + moveType = consts.move.TYPE_INNER; + } + if (moveType == consts.move.TYPE_INNER) { + function copyCallback() { + view.addNodes(_this.setting, targetNode, -1, [newNode], isSilent); + } + + if (tools.canAsync(this.setting, targetNode)) { + view.asyncNode(this.setting, targetNode, isSilent, copyCallback); + } else { + copyCallback(); + } + } else { + view.addNodes(this.setting, targetNode.parentNode, -1, [newNode], isSilent); + view.moveNode(this.setting, targetNode, newNode, moveType, false, isSilent); + } + return newNode; + } + zTreeTools.editName = function (node) { + if (!node || !node.tId || node !== data.getNodeCache(this.setting, node.tId)) return; + if (node.parentTId) view.expandCollapseParentNode(this.setting, node.getParentNode(), true); + view.editNode(this.setting, node) + } + zTreeTools.moveNode = function (targetNode, node, moveType, isSilent) { + if (!node) return node; + var isParent = data.nodeIsParent(setting, targetNode); + if (targetNode && !isParent && this.setting.data.keep.leaf && moveType === consts.move.TYPE_INNER) { + return null; + } else if (targetNode && ((node.parentTId == targetNode.tId && moveType == consts.move.TYPE_INNER) || $$(node, this.setting).find("#" + targetNode.tId).length > 0)) { + return null; + } else if (!targetNode) { + targetNode = null; + } + var _this = this; + + function moveCallback() { + view.moveNode(_this.setting, targetNode, node, moveType, false, isSilent); + } + + if (tools.canAsync(this.setting, targetNode) && moveType === consts.move.TYPE_INNER) { + view.asyncNode(this.setting, targetNode, isSilent, moveCallback); + } else { + moveCallback(); + } + return node; + } + zTreeTools.setEditable = function (editable) { + this.setting.edit.enable = editable; + return this.refresh(); + } + }, + //method of operate data + _data = { + setSonNodeLevel: function (setting, parentNode, node) { + if (!node) return; + var children = data.nodeChildren(setting, node); + var oldLevel = node.level; + node.level = (parentNode) ? parentNode.level + 1 : 0; + view.repairNodeLevelClass(setting, node, oldLevel); + + if (!children) return; + for (var i = 0, l = children.length; i < l; i++) { + if (children[i]) data.setSonNodeLevel(setting, node, children[i]); + } + } + }, + //method of event proxy + _event = {}, + //method of event handler + _handler = { + onHoverOverNode: function (event, node) { + var setting = data.getSetting(event.data.treeId), + root = data.getRoot(setting); + if (root.curHoverNode != node) { + _handler.onHoverOutNode(event); + } + root.curHoverNode = node; + view.addHoverDom(setting, node); + }, + onHoverOutNode: function (event, node) { + var setting = data.getSetting(event.data.treeId), + root = data.getRoot(setting); + if (root.curHoverNode && !data.isSelectedNode(setting, root.curHoverNode)) { + view.removeTreeDom(setting, root.curHoverNode); + root.curHoverNode = null; + } + }, + onMousedownNode: function (eventMouseDown, _node) { + var i, l, + setting = data.getSetting(eventMouseDown.data.treeId), + root = data.getRoot(setting), roots = data.getRoots(); + //right click can't drag & drop + if (eventMouseDown.button == 2 || !setting.edit.enable || (!setting.edit.drag.isCopy && !setting.edit.drag.isMove)) return true; + + //input of edit node name can't drag & drop + var target = eventMouseDown.target, + _nodes = data.getRoot(setting).curSelectedList, + nodes = []; + if (!data.isSelectedNode(setting, _node)) { + nodes = [_node]; + } else { + for (i = 0, l = _nodes.length; i < l; i++) { + if (_nodes[i].editNameFlag && tools.eqs(target.tagName, "input") && target.getAttribute("treeNode" + consts.id.INPUT) !== null) { + return true; + } + nodes.push(_nodes[i]); + if (nodes[0].parentTId !== _nodes[i].parentTId) { + nodes = [_node]; + break; + } + } + } + + view.editNodeBlur = true; + view.cancelCurEditNode(setting); + + var doc = $(setting.treeObj.get(0).ownerDocument), + body = $(setting.treeObj.get(0).ownerDocument.body), curNode, tmpArrow, tmpTarget, + isOtherTree = false, + targetSetting = setting, + sourceSetting = setting, + preNode, nextNode, + preTmpTargetNodeId = null, + preTmpMoveType = null, + tmpTargetNodeId = null, + moveType = consts.move.TYPE_INNER, + mouseDownX = eventMouseDown.clientX, + mouseDownY = eventMouseDown.clientY, + startTime = (new Date()).getTime(); + + if (tools.uCanDo(setting)) { + doc.bind("mousemove", _docMouseMove); + } + + function _docMouseMove(event) { + //avoid start drag after click node + if (root.dragFlag == 0 && Math.abs(mouseDownX - event.clientX) < setting.edit.drag.minMoveSize + && Math.abs(mouseDownY - event.clientY) < setting.edit.drag.minMoveSize) { + return true; + } + var i, l, tmpNode, tmpDom, tmpNodes; + body.css("cursor", "pointer"); + + if (root.dragFlag == 0) { + if (tools.apply(setting.callback.beforeDrag, [setting.treeId, nodes], true) == false) { + _docMouseUp(event); + return true; + } + + for (i = 0, l = nodes.length; i < l; i++) { + if (i == 0) { + root.dragNodeShowBefore = []; + } + tmpNode = nodes[i]; + if (data.nodeIsParent(setting, tmpNode) && tmpNode.open) { + view.expandCollapseNode(setting, tmpNode, !tmpNode.open); + root.dragNodeShowBefore[tmpNode.tId] = true; + } else { + root.dragNodeShowBefore[tmpNode.tId] = false; + } + } + + root.dragFlag = 1; + roots.showHoverDom = false; + tools.showIfameMask(setting, true); + + //sort + var isOrder = true, lastIndex = -1; + if (nodes.length > 1) { + var pNodes = nodes[0].parentTId ? data.nodeChildren(setting, nodes[0].getParentNode()) : data.getNodes(setting); + tmpNodes = []; + for (i = 0, l = pNodes.length; i < l; i++) { + if (root.dragNodeShowBefore[pNodes[i].tId] !== undefined) { + if (isOrder && lastIndex > -1 && (lastIndex + 1) !== i) { + isOrder = false; + } + tmpNodes.push(pNodes[i]); + lastIndex = i; + } + if (nodes.length === tmpNodes.length) { + nodes = tmpNodes; + break; + } + } + } + if (isOrder) { + preNode = nodes[0].getPreNode(); + nextNode = nodes[nodes.length - 1].getNextNode(); + } + + //set node in selected + curNode = $$("
      ", setting); + for (i = 0, l = nodes.length; i < l; i++) { + tmpNode = nodes[i]; + tmpNode.editNameFlag = false; + view.selectNode(setting, tmpNode, i > 0); + view.removeTreeDom(setting, tmpNode); + + if (i > setting.edit.drag.maxShowNodeNum - 1) { + continue; + } + + tmpDom = $$("
    • ", setting); + tmpDom.append($$(tmpNode, consts.id.A, setting).clone()); + tmpDom.css("padding", "0"); + tmpDom.children("#" + tmpNode.tId + consts.id.A).removeClass(consts.node.CURSELECTED); + curNode.append(tmpDom); + if (i == setting.edit.drag.maxShowNodeNum - 1) { + tmpDom = $$("
    • ...
    • ", setting); + curNode.append(tmpDom); + } + } + curNode.attr("id", nodes[0].tId + consts.id.UL + "_tmp"); + curNode.addClass(setting.treeObj.attr("class")); + curNode.appendTo(body); + + tmpArrow = $$("", setting); + tmpArrow.attr("id", "zTreeMove_arrow_tmp"); + tmpArrow.appendTo(body); + + setting.treeObj.trigger(consts.event.DRAG, [event, setting.treeId, nodes]); + } + + if (root.dragFlag == 1) { + if (tmpTarget && tmpArrow.attr("id") == event.target.id && tmpTargetNodeId && (event.clientX + doc.scrollLeft() + 2) > ($("#" + tmpTargetNodeId + consts.id.A, tmpTarget).offset().left)) { + var xT = $("#" + tmpTargetNodeId + consts.id.A, tmpTarget); + event.target = (xT.length > 0) ? xT.get(0) : event.target; + } else if (tmpTarget) { + tmpTarget.removeClass(consts.node.TMPTARGET_TREE); + if (tmpTargetNodeId) $("#" + tmpTargetNodeId + consts.id.A, tmpTarget).removeClass(consts.node.TMPTARGET_NODE + "_" + consts.move.TYPE_PREV) + .removeClass(consts.node.TMPTARGET_NODE + "_" + _consts.move.TYPE_NEXT).removeClass(consts.node.TMPTARGET_NODE + "_" + _consts.move.TYPE_INNER); + } + tmpTarget = null; + tmpTargetNodeId = null; + + //judge drag & drop in multi ztree + isOtherTree = false; + targetSetting = setting; + var settings = data.getSettings(); + for (var s in settings) { + if (settings[s].treeId && settings[s].edit.enable && settings[s].treeId != setting.treeId + && (event.target.id == settings[s].treeId || $(event.target).parents("#" + settings[s].treeId).length > 0)) { + isOtherTree = true; + targetSetting = settings[s]; + } + } + + var docScrollTop = doc.scrollTop(), + docScrollLeft = doc.scrollLeft(), + treeOffset = targetSetting.treeObj.offset(), + scrollHeight = targetSetting.treeObj.get(0).scrollHeight, + scrollWidth = targetSetting.treeObj.get(0).scrollWidth, + dTop = (event.clientY + docScrollTop - treeOffset.top), + dBottom = (targetSetting.treeObj.height() + treeOffset.top - event.clientY - docScrollTop), + dLeft = (event.clientX + docScrollLeft - treeOffset.left), + dRight = (targetSetting.treeObj.width() + treeOffset.left - event.clientX - docScrollLeft), + isTop = (dTop < setting.edit.drag.borderMax && dTop > setting.edit.drag.borderMin), + isBottom = (dBottom < setting.edit.drag.borderMax && dBottom > setting.edit.drag.borderMin), + isLeft = (dLeft < setting.edit.drag.borderMax && dLeft > setting.edit.drag.borderMin), + isRight = (dRight < setting.edit.drag.borderMax && dRight > setting.edit.drag.borderMin), + isTreeInner = dTop > setting.edit.drag.borderMin && dBottom > setting.edit.drag.borderMin && dLeft > setting.edit.drag.borderMin && dRight > setting.edit.drag.borderMin, + isTreeTop = (isTop && targetSetting.treeObj.scrollTop() <= 0), + isTreeBottom = (isBottom && (targetSetting.treeObj.scrollTop() + targetSetting.treeObj.height() + 10) >= scrollHeight), + isTreeLeft = (isLeft && targetSetting.treeObj.scrollLeft() <= 0), + isTreeRight = (isRight && (targetSetting.treeObj.scrollLeft() + targetSetting.treeObj.width() + 10) >= scrollWidth); + + if (event.target && tools.isChildOrSelf(event.target, targetSetting.treeId)) { + //get node
    • dom + var targetObj = event.target; + while (targetObj && targetObj.tagName && !tools.eqs(targetObj.tagName, "li") && targetObj.id != targetSetting.treeId) { + targetObj = targetObj.parentNode; + } + + var canMove = true; + //don't move to self or children of self + for (i = 0, l = nodes.length; i < l; i++) { + tmpNode = nodes[i]; + if (targetObj.id === tmpNode.tId) { + canMove = false; + break; + } else if ($$(tmpNode, setting).find("#" + targetObj.id).length > 0) { + canMove = false; + break; + } + } + if (canMove && event.target && tools.isChildOrSelf(event.target, targetObj.id + consts.id.A)) { + tmpTarget = $(targetObj); + tmpTargetNodeId = targetObj.id; + } + } + + //the mouse must be in zTree + tmpNode = nodes[0]; + if (isTreeInner && tools.isChildOrSelf(event.target, targetSetting.treeId)) { + //judge mouse move in root of ztree + if (!tmpTarget && (event.target.id == targetSetting.treeId || isTreeTop || isTreeBottom || isTreeLeft || isTreeRight) && (isOtherTree || (!isOtherTree && tmpNode.parentTId))) { + tmpTarget = targetSetting.treeObj; + } + //auto scroll top + if (isTop) { + targetSetting.treeObj.scrollTop(targetSetting.treeObj.scrollTop() - 10); + } else if (isBottom) { + targetSetting.treeObj.scrollTop(targetSetting.treeObj.scrollTop() + 10); + } + if (isLeft) { + targetSetting.treeObj.scrollLeft(targetSetting.treeObj.scrollLeft() - 10); + } else if (isRight) { + targetSetting.treeObj.scrollLeft(targetSetting.treeObj.scrollLeft() + 10); + } + //auto scroll left + if (tmpTarget && tmpTarget != targetSetting.treeObj && tmpTarget.offset().left < targetSetting.treeObj.offset().left) { + targetSetting.treeObj.scrollLeft(targetSetting.treeObj.scrollLeft() + tmpTarget.offset().left - targetSetting.treeObj.offset().left); + } + } + + curNode.css({ + "top": (event.clientY + docScrollTop + 3) + "px", + "left": (event.clientX + docScrollLeft + 3) + "px" + }); + + var dX = 0; + var dY = 0; + if (tmpTarget && tmpTarget.attr("id") != targetSetting.treeId) { + var tmpTargetNode = tmpTargetNodeId == null ? null : data.getNodeCache(targetSetting, tmpTargetNodeId), + isCopy = ((event.ctrlKey || event.metaKey) && setting.edit.drag.isMove && setting.edit.drag.isCopy) || (!setting.edit.drag.isMove && setting.edit.drag.isCopy), + isPrev = !!(preNode && tmpTargetNodeId === preNode.tId), + isNext = !!(nextNode && tmpTargetNodeId === nextNode.tId), + isInner = (tmpNode.parentTId && tmpNode.parentTId == tmpTargetNodeId), + canPrev = (isCopy || !isNext) && tools.apply(targetSetting.edit.drag.prev, [targetSetting.treeId, nodes, tmpTargetNode], !!targetSetting.edit.drag.prev), + canNext = (isCopy || !isPrev) && tools.apply(targetSetting.edit.drag.next, [targetSetting.treeId, nodes, tmpTargetNode], !!targetSetting.edit.drag.next), + canInner = (isCopy || !isInner) && !(targetSetting.data.keep.leaf && !data.nodeIsParent(setting, tmpTargetNode)) && tools.apply(targetSetting.edit.drag.inner, [targetSetting.treeId, nodes, tmpTargetNode], !!targetSetting.edit.drag.inner); + + function clearMove() { + tmpTarget = null; + tmpTargetNodeId = ""; + moveType = consts.move.TYPE_INNER; + tmpArrow.css({ + "display": "none" + }); + if (window.zTreeMoveTimer) { + clearTimeout(window.zTreeMoveTimer); + window.zTreeMoveTargetNodeTId = null + } + } + + if (!canPrev && !canNext && !canInner) { + clearMove(); + } else { + var tmpTargetA = $("#" + tmpTargetNodeId + consts.id.A, tmpTarget), + tmpNextA = tmpTargetNode.isLastNode ? null : $("#" + tmpTargetNode.getNextNode().tId + consts.id.A, tmpTarget.next()), + tmpTop = tmpTargetA.offset().top, + tmpLeft = tmpTargetA.offset().left, + prevPercent = canPrev ? (canInner ? 0.25 : (canNext ? 0.5 : 1)) : -1, + nextPercent = canNext ? (canInner ? 0.75 : (canPrev ? 0.5 : 0)) : -1, + dY_percent = (event.clientY + docScrollTop - tmpTop) / tmpTargetA.height(); + + if ((prevPercent == 1 || dY_percent <= prevPercent && dY_percent >= -.2) && canPrev) { + dX = 1 - tmpArrow.width(); + dY = tmpTop - tmpArrow.height() / 2; + moveType = consts.move.TYPE_PREV; + } else if ((nextPercent == 0 || dY_percent >= nextPercent && dY_percent <= 1.2) && canNext) { + dX = 1 - tmpArrow.width(); + dY = (tmpNextA == null || (data.nodeIsParent(setting, tmpTargetNode) && tmpTargetNode.open)) ? (tmpTop + tmpTargetA.height() - tmpArrow.height() / 2) : (tmpNextA.offset().top - tmpArrow.height() / 2); + moveType = consts.move.TYPE_NEXT; + } else if (canInner) { + dX = 5 - tmpArrow.width(); + dY = tmpTop; + moveType = consts.move.TYPE_INNER; + } else { + clearMove(); + } + + if (tmpTarget) { + tmpArrow.css({ + "display": "block", + "top": dY + "px", + "left": (tmpLeft + dX) + "px" + }); + tmpTargetA.addClass(consts.node.TMPTARGET_NODE + "_" + moveType); + + if (preTmpTargetNodeId != tmpTargetNodeId || preTmpMoveType != moveType) { + startTime = (new Date()).getTime(); + } + if (tmpTargetNode && data.nodeIsParent(setting, tmpTargetNode) && moveType == consts.move.TYPE_INNER) { + var startTimer = true; + if (window.zTreeMoveTimer && window.zTreeMoveTargetNodeTId !== tmpTargetNode.tId) { + clearTimeout(window.zTreeMoveTimer); + window.zTreeMoveTargetNodeTId = null; + } else if (window.zTreeMoveTimer && window.zTreeMoveTargetNodeTId === tmpTargetNode.tId) { + startTimer = false; + } + if (startTimer) { + window.zTreeMoveTimer = setTimeout(function () { + if (moveType != consts.move.TYPE_INNER) return; + if (tmpTargetNode && data.nodeIsParent(setting, tmpTargetNode) && !tmpTargetNode.open && (new Date()).getTime() - startTime > targetSetting.edit.drag.autoOpenTime + && tools.apply(targetSetting.callback.beforeDragOpen, [targetSetting.treeId, tmpTargetNode], true)) { + view.switchNode(targetSetting, tmpTargetNode); + if (targetSetting.edit.drag.autoExpandTrigger) { + targetSetting.treeObj.trigger(consts.event.EXPAND, [targetSetting.treeId, tmpTargetNode]); + } + } + }, targetSetting.edit.drag.autoOpenTime + 50); + window.zTreeMoveTargetNodeTId = tmpTargetNode.tId; + } + } + } + } + } else { + moveType = consts.move.TYPE_INNER; + if (tmpTarget && tools.apply(targetSetting.edit.drag.inner, [targetSetting.treeId, nodes, null], !!targetSetting.edit.drag.inner)) { + tmpTarget.addClass(consts.node.TMPTARGET_TREE); + } else { + tmpTarget = null; + } + tmpArrow.css({ + "display": "none" + }); + if (window.zTreeMoveTimer) { + clearTimeout(window.zTreeMoveTimer); + window.zTreeMoveTargetNodeTId = null; + } + } + preTmpTargetNodeId = tmpTargetNodeId; + preTmpMoveType = moveType; + + setting.treeObj.trigger(consts.event.DRAGMOVE, [event, setting.treeId, nodes]); + } + return false; + } + + doc.bind("mouseup", _docMouseUp); + + function _docMouseUp(event) { + if (window.zTreeMoveTimer) { + clearTimeout(window.zTreeMoveTimer); + window.zTreeMoveTargetNodeTId = null; + } + preTmpTargetNodeId = null; + preTmpMoveType = null; + doc.unbind("mousemove", _docMouseMove); + doc.unbind("mouseup", _docMouseUp); + doc.unbind("selectstart", _docSelect); + body.css("cursor", ""); + if (tmpTarget) { + tmpTarget.removeClass(consts.node.TMPTARGET_TREE); + if (tmpTargetNodeId) $("#" + tmpTargetNodeId + consts.id.A, tmpTarget).removeClass(consts.node.TMPTARGET_NODE + "_" + consts.move.TYPE_PREV) + .removeClass(consts.node.TMPTARGET_NODE + "_" + _consts.move.TYPE_NEXT).removeClass(consts.node.TMPTARGET_NODE + "_" + _consts.move.TYPE_INNER); + } + tools.showIfameMask(setting, false); + + roots.showHoverDom = true; + if (root.dragFlag == 0) return; + root.dragFlag = 0; + + var i, l, tmpNode; + for (i = 0, l = nodes.length; i < l; i++) { + tmpNode = nodes[i]; + if (data.nodeIsParent(setting, tmpNode) && root.dragNodeShowBefore[tmpNode.tId] && !tmpNode.open) { + view.expandCollapseNode(setting, tmpNode, !tmpNode.open); + delete root.dragNodeShowBefore[tmpNode.tId]; + } + } + + if (curNode) curNode.remove(); + if (tmpArrow) tmpArrow.remove(); + + var isCopy = ((event.ctrlKey || event.metaKey) && setting.edit.drag.isMove && setting.edit.drag.isCopy) || (!setting.edit.drag.isMove && setting.edit.drag.isCopy); + if (!isCopy && tmpTarget && tmpTargetNodeId && nodes[0].parentTId && tmpTargetNodeId == nodes[0].parentTId && moveType == consts.move.TYPE_INNER) { + tmpTarget = null; + } + if (tmpTarget) { + var dragTargetNode = tmpTargetNodeId == null ? null : data.getNodeCache(targetSetting, tmpTargetNodeId); + if (tools.apply(setting.callback.beforeDrop, [targetSetting.treeId, nodes, dragTargetNode, moveType, isCopy], true) == false) { + view.selectNodes(sourceSetting, nodes); + return; + } + var newNodes = isCopy ? tools.clone(nodes) : nodes; + + function dropCallback() { + if (isOtherTree) { + if (!isCopy) { + for (var i = 0, l = nodes.length; i < l; i++) { + view.removeNode(setting, nodes[i]); + } + } + if (moveType == consts.move.TYPE_INNER) { + view.addNodes(targetSetting, dragTargetNode, -1, newNodes); + } else { + view.addNodes(targetSetting, dragTargetNode.getParentNode(), moveType == consts.move.TYPE_PREV ? dragTargetNode.getIndex() : dragTargetNode.getIndex() + 1, newNodes); + } + } else { + if (isCopy && moveType == consts.move.TYPE_INNER) { + view.addNodes(targetSetting, dragTargetNode, -1, newNodes); + } else if (isCopy) { + view.addNodes(targetSetting, dragTargetNode.getParentNode(), moveType == consts.move.TYPE_PREV ? dragTargetNode.getIndex() : dragTargetNode.getIndex() + 1, newNodes); + } else { + if (moveType != consts.move.TYPE_NEXT) { + for (i = 0, l = newNodes.length; i < l; i++) { + view.moveNode(targetSetting, dragTargetNode, newNodes[i], moveType, false); + } + } else { + for (i = -1, l = newNodes.length - 1; i < l; l--) { + view.moveNode(targetSetting, dragTargetNode, newNodes[l], moveType, false); + } + } + } + } + view.selectNodes(targetSetting, newNodes); + + var a = $$(newNodes[0], setting).get(0); + view.scrollIntoView(setting, a); + + setting.treeObj.trigger(consts.event.DROP, [event, targetSetting.treeId, newNodes, dragTargetNode, moveType, isCopy]); + } + + if (moveType == consts.move.TYPE_INNER && tools.canAsync(targetSetting, dragTargetNode)) { + view.asyncNode(targetSetting, dragTargetNode, false, dropCallback); + } else { + dropCallback(); + } + + } else { + view.selectNodes(sourceSetting, nodes); + setting.treeObj.trigger(consts.event.DROP, [event, setting.treeId, nodes, null, null, null]); + } + } + + doc.bind("selectstart", _docSelect); + + function _docSelect() { + return false; + } + + // 2018-03-30 FireFox has fixed this issue. + //Avoid FireFox's Bug + //If zTree Div CSS set 'overflow', so drag node outside of zTree, and event.target is error. + // if(eventMouseDown.preventDefault) { + // eventMouseDown.preventDefault(); + // } + return true; + } + }, + //method of tools for zTree + _tools = { + getAbs: function (obj) { + var oRect = obj.getBoundingClientRect(), + scrollTop = document.body.scrollTop + document.documentElement.scrollTop, + scrollLeft = document.body.scrollLeft + document.documentElement.scrollLeft; + return [oRect.left + scrollLeft, oRect.top + scrollTop]; + }, + inputFocus: function (inputObj) { + if (inputObj.get(0)) { + inputObj.focus(); + tools.setCursorPosition(inputObj.get(0), inputObj.val().length); + } + }, + inputSelect: function (inputObj) { + if (inputObj.get(0)) { + inputObj.focus(); + inputObj.select(); + } + }, + setCursorPosition: function (obj, pos) { + if (obj.setSelectionRange) { + obj.focus(); + obj.setSelectionRange(pos, pos); + } else if (obj.createTextRange) { + var range = obj.createTextRange(); + range.collapse(true); + range.moveEnd('character', pos); + range.moveStart('character', pos); + range.select(); + } + }, + showIfameMask: function (setting, showSign) { + var root = data.getRoot(setting); + //clear full mask + while (root.dragMaskList.length > 0) { + root.dragMaskList[0].remove(); + root.dragMaskList.shift(); + } + if (showSign) { + //show mask + var iframeList = $$("iframe", setting); + for (var i = 0, l = iframeList.length; i < l; i++) { + var obj = iframeList.get(i), + r = tools.getAbs(obj), + dragMask = $$("
      ", setting); + dragMask.appendTo($$("body", setting)); + root.dragMaskList.push(dragMask); + } + } + } + }, + //method of operate ztree dom + _view = { + addEditBtn: function (setting, node) { + if (node.editNameFlag || $$(node, consts.id.EDIT, setting).length > 0) { + return; + } + if (!tools.apply(setting.edit.showRenameBtn, [setting.treeId, node], setting.edit.showRenameBtn)) { + return; + } + var aObj = $$(node, consts.id.A, setting), + editStr = ""; + aObj.append(editStr); + + $$(node, consts.id.EDIT, setting).bind('click', + function () { + if (!tools.uCanDo(setting) || tools.apply(setting.callback.beforeEditName, [setting.treeId, node], true) == false) return false; + view.editNode(setting, node); + return false; + } + ).show(); + }, + addRemoveBtn: function (setting, node) { + if (node.editNameFlag || $$(node, consts.id.REMOVE, setting).length > 0) { + return; + } + if (!tools.apply(setting.edit.showRemoveBtn, [setting.treeId, node], setting.edit.showRemoveBtn)) { + return; + } + var aObj = $$(node, consts.id.A, setting), + removeStr = ""; + aObj.append(removeStr); + + $$(node, consts.id.REMOVE, setting).bind('click', + function () { + if (!tools.uCanDo(setting) || tools.apply(setting.callback.beforeRemove, [setting.treeId, node], true) == false) return false; + view.removeNode(setting, node); + setting.treeObj.trigger(consts.event.REMOVE, [setting.treeId, node]); + return false; + } + ).bind('mousedown', + function (eventMouseDown) { + return true; + } + ).show(); + }, + addHoverDom: function (setting, node) { + if (data.getRoots().showHoverDom) { + node.isHover = true; + if (setting.edit.enable) { + view.addEditBtn(setting, node); + view.addRemoveBtn(setting, node); + } + tools.apply(setting.view.addHoverDom, [setting.treeId, node]); + } + }, + cancelCurEditNode: function (setting, forceName, isCancel) { + var root = data.getRoot(setting), + node = root.curEditNode; + + if (node) { + var inputObj = root.curEditInput, + newName = forceName ? forceName : (isCancel ? data.nodeName(setting, node) : inputObj.val()); + if (tools.apply(setting.callback.beforeRename, [setting.treeId, node, newName, isCancel], true) === false) { + return false; + } + data.nodeName(setting, node, newName); + var aObj = $$(node, consts.id.A, setting); + aObj.removeClass(consts.node.CURSELECTED_EDIT); + inputObj.unbind(); + view.setNodeName(setting, node); + node.editNameFlag = false; + root.curEditNode = null; + root.curEditInput = null; + view.selectNode(setting, node, false); + setting.treeObj.trigger(consts.event.RENAME, [setting.treeId, node, isCancel]); + } + root.noSelection = true; + return true; + }, + editNode: function (setting, node) { + var root = data.getRoot(setting); + view.editNodeBlur = false; + if (data.isSelectedNode(setting, node) && root.curEditNode == node && node.editNameFlag) { + setTimeout(function () { + tools.inputFocus(root.curEditInput); + }, 0); + return; + } + node.editNameFlag = true; + view.removeTreeDom(setting, node); + view.cancelCurEditNode(setting); + view.selectNode(setting, node, false); + $$(node, consts.id.SPAN, setting).html(""); + var inputObj = $$(node, consts.id.INPUT, setting); + inputObj.attr("value", data.nodeName(setting, node)); + if (setting.edit.editNameSelectAll) { + tools.inputSelect(inputObj); + } else { + tools.inputFocus(inputObj); + } + + inputObj.bind('blur', function (event) { + if (!view.editNodeBlur) { + view.cancelCurEditNode(setting); + } + }).bind('keydown', function (event) { + if (event.keyCode == "13") { + view.editNodeBlur = true; + view.cancelCurEditNode(setting); + } else if (event.keyCode == "27") { + view.cancelCurEditNode(setting, null, true); + } + }).bind('click', function (event) { + return false; + }).bind('dblclick', function (event) { + return false; + }); + + $$(node, consts.id.A, setting).addClass(consts.node.CURSELECTED_EDIT); + root.curEditInput = inputObj; + root.noSelection = false; + root.curEditNode = node; + }, + moveNode: function (setting, targetNode, node, moveType, animateFlag, isSilent) { + var root = data.getRoot(setting); + if (targetNode == node) return; + if (setting.data.keep.leaf && targetNode && !data.nodeIsParent(setting, targetNode) && moveType == consts.move.TYPE_INNER) return; + var oldParentNode = (node.parentTId ? node.getParentNode() : root), + targetNodeIsRoot = (targetNode === null || targetNode == root); + if (targetNodeIsRoot && targetNode === null) targetNode = root; + if (targetNodeIsRoot) moveType = consts.move.TYPE_INNER; + var targetParentNode = (targetNode.parentTId ? targetNode.getParentNode() : root); + + if (moveType != consts.move.TYPE_PREV && moveType != consts.move.TYPE_NEXT) { + moveType = consts.move.TYPE_INNER; + } + + if (moveType == consts.move.TYPE_INNER) { + if (targetNodeIsRoot) { + //parentTId of root node is null + node.parentTId = null; + } else { + if (!data.nodeIsParent(setting, targetNode)) { + data.nodeIsParent(setting, targetNode, true); + targetNode.open = !!targetNode.open; + view.setNodeLineIcos(setting, targetNode); + } + node.parentTId = targetNode.tId; + } + } + + //move node Dom + var targetObj, target_ulObj; + if (targetNodeIsRoot) { + targetObj = setting.treeObj; + target_ulObj = targetObj; + } else { + if (!isSilent && moveType == consts.move.TYPE_INNER) { + view.expandCollapseNode(setting, targetNode, true, false); + } else if (!isSilent) { + view.expandCollapseNode(setting, targetNode.getParentNode(), true, false); + } + targetObj = $$(targetNode, setting); + target_ulObj = $$(targetNode, consts.id.UL, setting); + if (!!targetObj.get(0) && !target_ulObj.get(0)) { + var ulstr = []; + view.makeUlHtml(setting, targetNode, ulstr, ''); + targetObj.append(ulstr.join('')); + } + target_ulObj = $$(targetNode, consts.id.UL, setting); + } + var nodeDom = $$(node, setting); + if (!nodeDom.get(0)) { + nodeDom = view.appendNodes(setting, node.level, [node], null, -1, false, true).join(''); + } else if (!targetObj.get(0)) { + nodeDom.remove(); + } + if (target_ulObj.get(0) && moveType == consts.move.TYPE_INNER) { + target_ulObj.append(nodeDom); + } else if (targetObj.get(0) && moveType == consts.move.TYPE_PREV) { + targetObj.before(nodeDom); + } else if (targetObj.get(0) && moveType == consts.move.TYPE_NEXT) { + targetObj.after(nodeDom); + } + + //repair the data after move + var i, l, + tmpSrcIndex = -1, + tmpTargetIndex = 0, + oldNeighbor = null, + newNeighbor = null, + oldLevel = node.level; + var oldChildren = data.nodeChildren(setting, oldParentNode); + var targetParentChildren = data.nodeChildren(setting, targetParentNode); + var targetChildren = data.nodeChildren(setting, targetNode); + if (node.isFirstNode) { + tmpSrcIndex = 0; + if (oldChildren.length > 1) { + oldNeighbor = oldChildren[1]; + oldNeighbor.isFirstNode = true; + } + } else if (node.isLastNode) { + tmpSrcIndex = oldChildren.length - 1; + oldNeighbor = oldChildren[tmpSrcIndex - 1]; + oldNeighbor.isLastNode = true; + } else { + for (i = 0, l = oldChildren.length; i < l; i++) { + if (oldChildren[i].tId == node.tId) { + tmpSrcIndex = i; + break; + } + } + } + if (tmpSrcIndex >= 0) { + oldChildren.splice(tmpSrcIndex, 1); + } + if (moveType != consts.move.TYPE_INNER) { + for (i = 0, l = targetParentChildren.length; i < l; i++) { + if (targetParentChildren[i].tId == targetNode.tId) tmpTargetIndex = i; + } + } + if (moveType == consts.move.TYPE_INNER) { + if (!targetChildren) { + targetChildren = data.nodeChildren(setting, targetNode, []); + } + if (targetChildren.length > 0) { + newNeighbor = targetChildren[targetChildren.length - 1]; + newNeighbor.isLastNode = false; + } + targetChildren.splice(targetChildren.length, 0, node); + node.isLastNode = true; + node.isFirstNode = (targetChildren.length == 1); + } else if (targetNode.isFirstNode && moveType == consts.move.TYPE_PREV) { + targetParentChildren.splice(tmpTargetIndex, 0, node); + newNeighbor = targetNode; + newNeighbor.isFirstNode = false; + node.parentTId = targetNode.parentTId; + node.isFirstNode = true; + node.isLastNode = false; + + } else if (targetNode.isLastNode && moveType == consts.move.TYPE_NEXT) { + targetParentChildren.splice(tmpTargetIndex + 1, 0, node); + newNeighbor = targetNode; + newNeighbor.isLastNode = false; + node.parentTId = targetNode.parentTId; + node.isFirstNode = false; + node.isLastNode = true; + + } else { + if (moveType == consts.move.TYPE_PREV) { + targetParentChildren.splice(tmpTargetIndex, 0, node); + } else { + targetParentChildren.splice(tmpTargetIndex + 1, 0, node); + } + node.parentTId = targetNode.parentTId; + node.isFirstNode = false; + node.isLastNode = false; + } + data.fixPIdKeyValue(setting, node); + data.setSonNodeLevel(setting, node.getParentNode(), node); + + //repair node what been moved + view.setNodeLineIcos(setting, node); + view.repairNodeLevelClass(setting, node, oldLevel); + + //repair node's old parentNode dom + if (!setting.data.keep.parent && oldChildren.length < 1) { + //old parentNode has no child nodes + data.nodeIsParent(setting, oldParentNode, false); + oldParentNode.open = false; + var tmp_ulObj = $$(oldParentNode, consts.id.UL, setting), + tmp_switchObj = $$(oldParentNode, consts.id.SWITCH, setting), + tmp_icoObj = $$(oldParentNode, consts.id.ICON, setting); + view.replaceSwitchClass(oldParentNode, tmp_switchObj, consts.folder.DOCU); + view.replaceIcoClass(oldParentNode, tmp_icoObj, consts.folder.DOCU); + tmp_ulObj.css("display", "none"); + + } else if (oldNeighbor) { + //old neighbor node + view.setNodeLineIcos(setting, oldNeighbor); + } + + //new neighbor node + if (newNeighbor) { + view.setNodeLineIcos(setting, newNeighbor); + } + + //repair checkbox / radio + if (!!setting.check && setting.check.enable && view.repairChkClass) { + view.repairChkClass(setting, oldParentNode); + view.repairParentChkClassWithSelf(setting, oldParentNode); + if (oldParentNode != node.parent) + view.repairParentChkClassWithSelf(setting, node); + } + + //expand parents after move + if (!isSilent) { + view.expandCollapseParentNode(setting, node.getParentNode(), true, animateFlag); + } + }, + removeEditBtn: function (setting, node) { + $$(node, consts.id.EDIT, setting).unbind().remove(); + }, + removeRemoveBtn: function (setting, node) { + $$(node, consts.id.REMOVE, setting).unbind().remove(); + }, + removeTreeDom: function (setting, node) { + node.isHover = false; + view.removeEditBtn(setting, node); + view.removeRemoveBtn(setting, node); + tools.apply(setting.view.removeHoverDom, [setting.treeId, node]); + }, + repairNodeLevelClass: function (setting, node, oldLevel) { + if (oldLevel === node.level) return; + var liObj = $$(node, setting), + aObj = $$(node, consts.id.A, setting), + ulObj = $$(node, consts.id.UL, setting), + oldClass = consts.className.LEVEL + oldLevel, + newClass = consts.className.LEVEL + node.level; + liObj.removeClass(oldClass); + liObj.addClass(newClass); + aObj.removeClass(oldClass); + aObj.addClass(newClass); + ulObj.removeClass(oldClass); + ulObj.addClass(newClass); + }, + selectNodes: function (setting, nodes) { + for (var i = 0, l = nodes.length; i < l; i++) { + view.selectNode(setting, nodes[i], i > 0); + } + } + }, + + _z = { + tools: _tools, + view: _view, + event: _event, + data: _data + }; + $.extend(true, $.fn.zTree.consts, _consts); + $.extend(true, $.fn.zTree._z, _z); + + var zt = $.fn.zTree, + tools = zt._z.tools, + consts = zt.consts, + view = zt._z.view, + data = zt._z.data, + event = zt._z.event, + $$ = tools.$; + + data.exSetting(_setting); + data.addInitBind(_bindEvent); + data.addInitUnBind(_unbindEvent); + data.addInitCache(_initCache); + data.addInitNode(_initNode); + data.addInitProxy(_eventProxy); + data.addInitRoot(_initRoot); + data.addZTreeTools(_zTreeTools); + + var _cancelPreSelectedNode = view.cancelPreSelectedNode; + view.cancelPreSelectedNode = function (setting, node) { + var list = data.getRoot(setting).curSelectedList; + for (var i = 0, j = list.length; i < j; i++) { + if (!node || node === list[i]) { + view.removeTreeDom(setting, list[i]); + if (node) break; + } + } + if (_cancelPreSelectedNode) _cancelPreSelectedNode.apply(view, arguments); + } + + var _createNodes = view.createNodes; + view.createNodes = function (setting, level, nodes, parentNode, index) { + if (_createNodes) { + _createNodes.apply(view, arguments); + } + if (!nodes) return; + if (view.repairParentChkClassWithSelf) { + view.repairParentChkClassWithSelf(setting, parentNode); + } + } + + var _makeNodeUrl = view.makeNodeUrl; + view.makeNodeUrl = function (setting, node) { + return setting.edit.enable ? null : (_makeNodeUrl.apply(view, arguments)); + } + + var _removeNode = view.removeNode; + view.removeNode = function (setting, node) { + var root = data.getRoot(setting); + if (root.curEditNode === node) root.curEditNode = null; + if (_removeNode) { + _removeNode.apply(view, arguments); + } + } + + var _selectNode = view.selectNode; + view.selectNode = function (setting, node, addFlag) { + var root = data.getRoot(setting); + if (data.isSelectedNode(setting, node) && root.curEditNode == node && node.editNameFlag) { + return false; + } + if (_selectNode) _selectNode.apply(view, arguments); + view.addHoverDom(setting, node); + return true; + } + + var _uCanDo = tools.uCanDo; + tools.uCanDo = function (setting, e) { + var root = data.getRoot(setting); + if (e && (tools.eqs(e.type, "mouseover") || tools.eqs(e.type, "mouseout") || tools.eqs(e.type, "mousedown") || tools.eqs(e.type, "mouseup"))) { + return true; + } + if (root.curEditNode) { + view.editNodeBlur = false; + root.curEditInput.focus(); + } + return (!root.curEditNode) && (_uCanDo ? _uCanDo.apply(view, arguments) : true); + } +})(jQuery); \ No newline at end of file diff --git a/public/self/ztree/js/jquery.ztree.core.js b/public/self/ztree/js/jquery.ztree.core.js new file mode 100644 index 000000000..9226f19d6 --- /dev/null +++ b/public/self/ztree/js/jquery.ztree.core.js @@ -0,0 +1,2019 @@ +/* + * JQuery zTree core + * v3.5.48 + * http://treejs.cn/ + * + * Copyright (c) 2010 Hunter.z + * + * Licensed same as jquery - MIT License + * http://www.opensource.org/licenses/mit-license.php + * + * Date: 2020-11-21 + */ + +(function ($) { + var settings = {}, roots = {}, caches = {}, + //default consts of core + _consts = { + className: { + BUTTON: "button", + LEVEL: "level", + ICO_LOADING: "ico_loading", + SWITCH: "switch", + NAME: 'node_name' + }, + event: { + NODECREATED: "ztree_nodeCreated", + CLICK: "ztree_click", + EXPAND: "ztree_expand", + COLLAPSE: "ztree_collapse", + ASYNC_SUCCESS: "ztree_async_success", + ASYNC_ERROR: "ztree_async_error", + REMOVE: "ztree_remove", + SELECTED: "ztree_selected", + UNSELECTED: "ztree_unselected" + }, + id: { + A: "_a", + ICON: "_ico", + SPAN: "_span", + SWITCH: "_switch", + UL: "_ul" + }, + line: { + ROOT: "root", + ROOTS: "roots", + CENTER: "center", + BOTTOM: "bottom", + NOLINE: "noline", + LINE: "line" + }, + folder: { + OPEN: "open", + CLOSE: "close", + DOCU: "docu" + }, + node: { + CURSELECTED: "curSelectedNode" + } + }, + //default setting of core + _setting = { + treeId: "", + treeObj: null, + view: { + addDiyDom: null, + autoCancelSelected: true, + dblClickExpand: true, + expandSpeed: "fast", + fontCss: {}, + nodeClasses: {}, + nameIsHTML: false, + selectedMulti: true, + showIcon: true, + showLine: true, + showTitle: true, + txtSelectedEnable: false + }, + data: { + key: { + isParent: "isParent", + children: "children", + name: "name", + title: "", + url: "url", + icon: "icon" + }, + render: { + name: null, + title: null, + }, + simpleData: { + enable: false, + idKey: "id", + pIdKey: "pId", + rootPId: null + }, + keep: { + parent: false, + leaf: false + } + }, + async: { + enable: false, + contentType: "application/x-www-form-urlencoded", + type: "post", + dataType: "text", + headers: {}, + xhrFields: {}, + url: "", + autoParam: [], + otherParam: [], + dataFilter: null + }, + callback: { + beforeAsync: null, + beforeClick: null, + beforeDblClick: null, + beforeRightClick: null, + beforeMouseDown: null, + beforeMouseUp: null, + beforeExpand: null, + beforeCollapse: null, + beforeRemove: null, + + onAsyncError: null, + onAsyncSuccess: null, + onNodeCreated: null, + onClick: null, + onDblClick: null, + onRightClick: null, + onMouseDown: null, + onMouseUp: null, + onExpand: null, + onCollapse: null, + onRemove: null + } + }, + //default root of core + //zTree use root to save full data + _initRoot = function (setting) { + var r = data.getRoot(setting); + if (!r) { + r = {}; + data.setRoot(setting, r); + } + data.nodeChildren(setting, r, []); + r.expandTriggerFlag = false; + r.curSelectedList = []; + r.noSelection = true; + r.createdNodes = []; + r.zId = 0; + r._ver = (new Date()).getTime(); + }, + //default cache of core + _initCache = function (setting) { + var c = data.getCache(setting); + if (!c) { + c = {}; + data.setCache(setting, c); + } + c.nodes = []; + c.doms = []; + }, + //default bindEvent of core + _bindEvent = function (setting) { + var o = setting.treeObj, + c = consts.event; + o.bind(c.NODECREATED, function (event, treeId, node) { + tools.apply(setting.callback.onNodeCreated, [event, treeId, node]); + }); + + o.bind(c.CLICK, function (event, srcEvent, treeId, node, clickFlag) { + tools.apply(setting.callback.onClick, [srcEvent, treeId, node, clickFlag]); + }); + + o.bind(c.EXPAND, function (event, treeId, node) { + tools.apply(setting.callback.onExpand, [event, treeId, node]); + }); + + o.bind(c.COLLAPSE, function (event, treeId, node) { + tools.apply(setting.callback.onCollapse, [event, treeId, node]); + }); + + o.bind(c.ASYNC_SUCCESS, function (event, treeId, node, msg) { + tools.apply(setting.callback.onAsyncSuccess, [event, treeId, node, msg]); + }); + + o.bind(c.ASYNC_ERROR, function (event, treeId, node, XMLHttpRequest, textStatus, errorThrown) { + tools.apply(setting.callback.onAsyncError, [event, treeId, node, XMLHttpRequest, textStatus, errorThrown]); + }); + + o.bind(c.REMOVE, function (event, treeId, treeNode) { + tools.apply(setting.callback.onRemove, [event, treeId, treeNode]); + }); + + o.bind(c.SELECTED, function (event, treeId, node) { + tools.apply(setting.callback.onSelected, [treeId, node]); + }); + o.bind(c.UNSELECTED, function (event, treeId, node) { + tools.apply(setting.callback.onUnSelected, [treeId, node]); + }); + }, + _unbindEvent = function (setting) { + var o = setting.treeObj, + c = consts.event; + o.unbind(c.NODECREATED) + .unbind(c.CLICK) + .unbind(c.EXPAND) + .unbind(c.COLLAPSE) + .unbind(c.ASYNC_SUCCESS) + .unbind(c.ASYNC_ERROR) + .unbind(c.REMOVE) + .unbind(c.SELECTED) + .unbind(c.UNSELECTED); + }, + //default event proxy of core + _eventProxy = function (event) { + var target = event.target, + setting = data.getSetting(event.data.treeId), + tId = "", node = null, + nodeEventType = "", treeEventType = "", + nodeEventCallback = null, treeEventCallback = null, + tmp = null; + + if (tools.eqs(event.type, "mousedown")) { + treeEventType = "mousedown"; + } else if (tools.eqs(event.type, "mouseup")) { + treeEventType = "mouseup"; + } else if (tools.eqs(event.type, "contextmenu")) { + treeEventType = "contextmenu"; + } else if (tools.eqs(event.type, "click")) { + if (tools.eqs(target.tagName, "span") && target.getAttribute("treeNode" + consts.id.SWITCH) !== null) { + tId = tools.getNodeMainDom(target).id; + nodeEventType = "switchNode"; + } else { + tmp = tools.getMDom(setting, target, [{tagName: "a", attrName: "treeNode" + consts.id.A}]); + if (tmp) { + tId = tools.getNodeMainDom(tmp).id; + nodeEventType = "clickNode"; + } + } + } else if (tools.eqs(event.type, "dblclick")) { + treeEventType = "dblclick"; + tmp = tools.getMDom(setting, target, [{tagName: "a", attrName: "treeNode" + consts.id.A}]); + if (tmp) { + tId = tools.getNodeMainDom(tmp).id; + nodeEventType = "switchNode"; + } + } + if (treeEventType.length > 0 && tId.length == 0) { + tmp = tools.getMDom(setting, target, [{tagName: "a", attrName: "treeNode" + consts.id.A}]); + if (tmp) { + tId = tools.getNodeMainDom(tmp).id; + } + } + // event to node + if (tId.length > 0) { + node = data.getNodeCache(setting, tId); + switch (nodeEventType) { + case "switchNode" : + var isParent = data.nodeIsParent(setting, node); + if (!isParent) { + nodeEventType = ""; + } else if (tools.eqs(event.type, "click") + || (tools.eqs(event.type, "dblclick") && tools.apply(setting.view.dblClickExpand, [setting.treeId, node], setting.view.dblClickExpand))) { + nodeEventCallback = handler.onSwitchNode; + } else { + nodeEventType = ""; + } + break; + case "clickNode" : + nodeEventCallback = handler.onClickNode; + break; + } + } + // event to zTree + switch (treeEventType) { + case "mousedown" : + treeEventCallback = handler.onZTreeMousedown; + break; + case "mouseup" : + treeEventCallback = handler.onZTreeMouseup; + break; + case "dblclick" : + treeEventCallback = handler.onZTreeDblclick; + break; + case "contextmenu" : + treeEventCallback = handler.onZTreeContextmenu; + break; + } + var proxyResult = { + stop: false, + node: node, + nodeEventType: nodeEventType, + nodeEventCallback: nodeEventCallback, + treeEventType: treeEventType, + treeEventCallback: treeEventCallback + }; + return proxyResult + }, + //default init node of core + _initNode = function (setting, level, n, parentNode, isFirstNode, isLastNode, openFlag) { + if (!n) return; + var r = data.getRoot(setting), + children = data.nodeChildren(setting, n); + n.level = level; + n.tId = setting.treeId + "_" + (++r.zId); + n.parentTId = parentNode ? parentNode.tId : null; + n.open = (typeof n.open == "string") ? tools.eqs(n.open, "true") : !!n.open; + var isParent = data.nodeIsParent(setting, n); + if (tools.isArray(children)) { + data.nodeIsParent(setting, n, true); + n.zAsync = true; + } else { + isParent = data.nodeIsParent(setting, n, isParent); + n.open = (isParent && !setting.async.enable) ? n.open : false; + n.zAsync = !isParent; + } + n.isFirstNode = isFirstNode; + n.isLastNode = isLastNode; + n.getParentNode = function () { + return data.getNodeCache(setting, n.parentTId); + }; + n.getPreNode = function () { + return data.getPreNode(setting, n); + }; + n.getNextNode = function () { + return data.getNextNode(setting, n); + }; + n.getIndex = function () { + return data.getNodeIndex(setting, n); + }; + n.getPath = function () { + return data.getNodePath(setting, n); + }; + n.isAjaxing = false; + data.fixPIdKeyValue(setting, n); + }, + _init = { + bind: [_bindEvent], + unbind: [_unbindEvent], + caches: [_initCache], + nodes: [_initNode], + proxys: [_eventProxy], + roots: [_initRoot], + beforeA: [], + afterA: [], + innerBeforeA: [], + innerAfterA: [], + zTreeTools: [] + }, + //method of operate data + data = { + addNodeCache: function (setting, node) { + data.getCache(setting).nodes[data.getNodeCacheId(node.tId)] = node; + }, + getNodeCacheId: function (tId) { + return tId.substring(tId.lastIndexOf("_") + 1); + }, + addAfterA: function (afterA) { + _init.afterA.push(afterA); + }, + addBeforeA: function (beforeA) { + _init.beforeA.push(beforeA); + }, + addInnerAfterA: function (innerAfterA) { + _init.innerAfterA.push(innerAfterA); + }, + addInnerBeforeA: function (innerBeforeA) { + _init.innerBeforeA.push(innerBeforeA); + }, + addInitBind: function (bindEvent) { + _init.bind.push(bindEvent); + }, + addInitUnBind: function (unbindEvent) { + _init.unbind.push(unbindEvent); + }, + addInitCache: function (initCache) { + _init.caches.push(initCache); + }, + addInitNode: function (initNode) { + _init.nodes.push(initNode); + }, + addInitProxy: function (initProxy, isFirst) { + if (!!isFirst) { + _init.proxys.splice(0, 0, initProxy); + } else { + _init.proxys.push(initProxy); + } + }, + addInitRoot: function (initRoot) { + _init.roots.push(initRoot); + }, + addNodesData: function (setting, parentNode, index, nodes) { + var children = data.nodeChildren(setting, parentNode), params; + if (!children) { + children = data.nodeChildren(setting, parentNode, []); + index = -1; + } else if (index >= children.length) { + index = -1; + } + + if (children.length > 0 && index === 0) { + children[0].isFirstNode = false; + view.setNodeLineIcos(setting, children[0]); + } else if (children.length > 0 && index < 0) { + children[children.length - 1].isLastNode = false; + view.setNodeLineIcos(setting, children[children.length - 1]); + } + data.nodeIsParent(setting, parentNode, true); + + if (index < 0) { + data.nodeChildren(setting, parentNode, children.concat(nodes)); + } else { + params = [index, 0].concat(nodes); + children.splice.apply(children, params); + } + }, + addSelectedNode: function (setting, node) { + var root = data.getRoot(setting); + if (!data.isSelectedNode(setting, node)) { + root.curSelectedList.push(node); + } + }, + addCreatedNode: function (setting, node) { + if (!!setting.callback.onNodeCreated || !!setting.view.addDiyDom) { + var root = data.getRoot(setting); + root.createdNodes.push(node); + } + }, + addZTreeTools: function (zTreeTools) { + _init.zTreeTools.push(zTreeTools); + }, + exSetting: function (s) { + $.extend(true, _setting, s); + }, + fixPIdKeyValue: function (setting, node) { + if (setting.data.simpleData.enable) { + node[setting.data.simpleData.pIdKey] = node.parentTId ? node.getParentNode()[setting.data.simpleData.idKey] : setting.data.simpleData.rootPId; + } + }, + getAfterA: function (setting, node, array) { + for (var i = 0, j = _init.afterA.length; i < j; i++) { + _init.afterA[i].apply(this, arguments); + } + }, + getBeforeA: function (setting, node, array) { + for (var i = 0, j = _init.beforeA.length; i < j; i++) { + _init.beforeA[i].apply(this, arguments); + } + }, + getInnerAfterA: function (setting, node, array) { + for (var i = 0, j = _init.innerAfterA.length; i < j; i++) { + _init.innerAfterA[i].apply(this, arguments); + } + }, + getInnerBeforeA: function (setting, node, array) { + for (var i = 0, j = _init.innerBeforeA.length; i < j; i++) { + _init.innerBeforeA[i].apply(this, arguments); + } + }, + getCache: function (setting) { + return caches[setting.treeId]; + }, + getNodeIndex: function (setting, node) { + if (!node) return null; + var p = node.parentTId ? node.getParentNode() : data.getRoot(setting), + children = data.nodeChildren(setting, p); + for (var i = 0, l = children.length - 1; i <= l; i++) { + if (children[i] === node) { + return i; + } + } + return -1; + }, + getNextNode: function (setting, node) { + if (!node) return null; + var p = node.parentTId ? node.getParentNode() : data.getRoot(setting), + children = data.nodeChildren(setting, p); + for (var i = 0, l = children.length - 1; i <= l; i++) { + if (children[i] === node) { + return (i == l ? null : children[i + 1]); + } + } + return null; + }, + getNodeByParam: function (setting, nodes, key, value) { + if (!nodes || !key) return null; + for (var i = 0, l = nodes.length; i < l; i++) { + var node = nodes[i]; + if (node[key] == value) { + return nodes[i]; + } + var children = data.nodeChildren(setting, node); + var tmp = data.getNodeByParam(setting, children, key, value); + if (tmp) return tmp; + } + return null; + }, + getNodeCache: function (setting, tId) { + if (!tId) return null; + var n = caches[setting.treeId].nodes[data.getNodeCacheId(tId)]; + return n ? n : null; + }, + getNodePath: function (setting, node) { + if (!node) return null; + + var path; + if (node.parentTId) { + path = node.getParentNode().getPath(); + } else { + path = []; + } + + if (path) { + path.push(node); + } + + return path; + }, + getNodes: function (setting) { + return data.nodeChildren(setting, data.getRoot(setting)); + }, + getNodesByParam: function (setting, nodes, key, value) { + if (!nodes || !key) return []; + var result = []; + for (var i = 0, l = nodes.length; i < l; i++) { + var node = nodes[i]; + if (node[key] == value) { + result.push(node); + } + var children = data.nodeChildren(setting, node); + result = result.concat(data.getNodesByParam(setting, children, key, value)); + } + return result; + }, + getNodesByParamFuzzy: function (setting, nodes, key, value) { + if (!nodes || !key) return []; + var result = []; + value = value.toLowerCase(); + for (var i = 0, l = nodes.length; i < l; i++) { + var node = nodes[i]; + if (typeof node[key] == "string" && nodes[i][key].toLowerCase().indexOf(value) > -1) { + result.push(node); + } + var children = data.nodeChildren(setting, node); + result = result.concat(data.getNodesByParamFuzzy(setting, children, key, value)); + } + return result; + }, + getNodesByFilter: function (setting, nodes, filter, isSingle, invokeParam) { + if (!nodes) return (isSingle ? null : []); + var result = isSingle ? null : []; + for (var i = 0, l = nodes.length; i < l; i++) { + var node = nodes[i]; + if (tools.apply(filter, [node, invokeParam], false)) { + if (isSingle) { + return node; + } + result.push(node); + } + var children = data.nodeChildren(setting, node); + var tmpResult = data.getNodesByFilter(setting, children, filter, isSingle, invokeParam); + if (isSingle && !!tmpResult) { + return tmpResult; + } + result = isSingle ? tmpResult : result.concat(tmpResult); + } + return result; + }, + getPreNode: function (setting, node) { + if (!node) return null; + var p = node.parentTId ? node.getParentNode() : data.getRoot(setting), + children = data.nodeChildren(setting, p); + for (var i = 0, l = children.length; i < l; i++) { + if (children[i] === node) { + return (i == 0 ? null : children[i - 1]); + } + } + return null; + }, + getRoot: function (setting) { + return setting ? roots[setting.treeId] : null; + }, + getRoots: function () { + return roots; + }, + getSetting: function (treeId) { + return settings[treeId]; + }, + getSettings: function () { + return settings; + }, + getZTreeTools: function (treeId) { + var r = this.getRoot(this.getSetting(treeId)); + return r ? r.treeTools : null; + }, + initCache: function (setting) { + for (var i = 0, j = _init.caches.length; i < j; i++) { + _init.caches[i].apply(this, arguments); + } + }, + initNode: function (setting, level, node, parentNode, preNode, nextNode) { + for (var i = 0, j = _init.nodes.length; i < j; i++) { + _init.nodes[i].apply(this, arguments); + } + }, + initRoot: function (setting) { + for (var i = 0, j = _init.roots.length; i < j; i++) { + _init.roots[i].apply(this, arguments); + } + }, + isSelectedNode: function (setting, node) { + var root = data.getRoot(setting); + for (var i = 0, j = root.curSelectedList.length; i < j; i++) { + if (node === root.curSelectedList[i]) return true; + } + return false; + }, + nodeChildren: function (setting, node, newChildren) { + if (!node) { + return null; + } + var key = setting.data.key.children; + if (typeof newChildren !== 'undefined') { + node[key] = newChildren; + } + return node[key]; + }, + nodeIsParent: function (setting, node, newIsParent) { + if (!node) { + return false; + } + var key = setting.data.key.isParent; + if (typeof newIsParent !== 'undefined') { + if (typeof newIsParent === "string") { + newIsParent = tools.eqs(newIsParent, "true"); + } + newIsParent = !!newIsParent; + node[key] = newIsParent; + } else if (typeof node[key] == "string"){ + node[key] = tools.eqs(node[key], "true"); + } else { + node[key] = !!node[key]; + } + return node[key]; + }, + nodeName: function (setting, node, newName) { + var key = setting.data.key.name; + if (typeof newName !== 'undefined') { + node[key] = newName; + } + var rawName = "" + node[key]; + if(typeof setting.data.render.name === 'function') { + return setting.data.render.name.call(this,rawName,node); + } + return rawName; + }, + nodeTitle: function (setting, node) { + var t = setting.data.key.title === "" ? setting.data.key.name : setting.data.key.title; + var rawTitle = "" + node[t]; + if(typeof setting.data.render.title === 'function') { + return setting.data.render.title.call(this,rawTitle,node); + } + return rawTitle; + }, + removeNodeCache: function (setting, node) { + var children = data.nodeChildren(setting, node); + if (children) { + for (var i = 0, l = children.length; i < l; i++) { + data.removeNodeCache(setting, children[i]); + } + } + data.getCache(setting).nodes[data.getNodeCacheId(node.tId)] = null; + }, + removeSelectedNode: function (setting, node) { + var root = data.getRoot(setting); + for (var i = 0, j = root.curSelectedList.length; i < j; i++) { + if (node === root.curSelectedList[i] || !data.getNodeCache(setting, root.curSelectedList[i].tId)) { + root.curSelectedList.splice(i, 1); + setting.treeObj.trigger(consts.event.UNSELECTED, [setting.treeId, node]); + i--; + j--; + } + } + }, + setCache: function (setting, cache) { + caches[setting.treeId] = cache; + }, + setRoot: function (setting, root) { + roots[setting.treeId] = root; + }, + setZTreeTools: function (setting, zTreeTools) { + for (var i = 0, j = _init.zTreeTools.length; i < j; i++) { + _init.zTreeTools[i].apply(this, arguments); + } + }, + transformToArrayFormat: function (setting, nodes) { + if (!nodes) return []; + var r = []; + if (tools.isArray(nodes)) { + for (var i = 0, l = nodes.length; i < l; i++) { + var node = nodes[i]; + _do(node); + } + } else { + _do(nodes); + } + return r; + + function _do(_node) { + r.push(_node); + var children = data.nodeChildren(setting, _node); + if (children) { + r = r.concat(data.transformToArrayFormat(setting, children)); + } + } + }, + transformTozTreeFormat: function (setting, sNodes) { + var i, l, + key = setting.data.simpleData.idKey, + parentKey = setting.data.simpleData.pIdKey; + if (!key || key == "" || !sNodes) return []; + + if (tools.isArray(sNodes)) { + var r = []; + var tmpMap = {}; + for (i = 0, l = sNodes.length; i < l; i++) { + tmpMap[sNodes[i][key]] = sNodes[i]; + } + for (i = 0, l = sNodes.length; i < l; i++) { + var p = tmpMap[sNodes[i][parentKey]]; + if (p && sNodes[i][key] != sNodes[i][parentKey]) { + var children = data.nodeChildren(setting, p); + if (!children) { + children = data.nodeChildren(setting, p, []); + } + children.push(sNodes[i]); + } else { + r.push(sNodes[i]); + } + } + return r; + } else { + return [sNodes]; + } + } + }, + //method of event proxy + event = { + bindEvent: function (setting) { + for (var i = 0, j = _init.bind.length; i < j; i++) { + _init.bind[i].apply(this, arguments); + } + }, + unbindEvent: function (setting) { + for (var i = 0, j = _init.unbind.length; i < j; i++) { + _init.unbind[i].apply(this, arguments); + } + }, + bindTree: function (setting) { + var eventParam = { + treeId: setting.treeId + }, + o = setting.treeObj; + if (!setting.view.txtSelectedEnable) { + // for can't select text + o.bind('selectstart', handler.onSelectStart).css({ + "-moz-user-select": "-moz-none" + }); + } + o.bind('click', eventParam, event.proxy); + o.bind('dblclick', eventParam, event.proxy); + o.bind('mouseover', eventParam, event.proxy); + o.bind('mouseout', eventParam, event.proxy); + o.bind('mousedown', eventParam, event.proxy); + o.bind('mouseup', eventParam, event.proxy); + o.bind('contextmenu', eventParam, event.proxy); + }, + unbindTree: function (setting) { + var o = setting.treeObj; + o.unbind('selectstart', handler.onSelectStart) + .unbind('click', event.proxy) + .unbind('dblclick', event.proxy) + .unbind('mouseover', event.proxy) + .unbind('mouseout', event.proxy) + .unbind('mousedown', event.proxy) + .unbind('mouseup', event.proxy) + .unbind('contextmenu', event.proxy); + }, + doProxy: function (e) { + var results = []; + for (var i = 0, j = _init.proxys.length; i < j; i++) { + var proxyResult = _init.proxys[i].apply(this, arguments); + results.push(proxyResult); + if (proxyResult.stop) { + break; + } + } + return results; + }, + proxy: function (e) { + var setting = data.getSetting(e.data.treeId); + if (!tools.uCanDo(setting, e)) return true; + var results = event.doProxy(e), + r = true, x = false; + for (var i = 0, l = results.length; i < l; i++) { + var proxyResult = results[i]; + if (proxyResult.nodeEventCallback) { + x = true; + r = proxyResult.nodeEventCallback.apply(proxyResult, [e, proxyResult.node]) && r; + } + if (proxyResult.treeEventCallback) { + x = true; + r = proxyResult.treeEventCallback.apply(proxyResult, [e, proxyResult.node]) && r; + } + } + return r; + } + }, + //method of event handler + handler = { + onSwitchNode: function (event, node) { + var setting = data.getSetting(event.data.treeId); + if (node.open) { + if (tools.apply(setting.callback.beforeCollapse, [setting.treeId, node], true) == false) return true; + data.getRoot(setting).expandTriggerFlag = true; + view.switchNode(setting, node); + } else { + if (tools.apply(setting.callback.beforeExpand, [setting.treeId, node], true) == false) return true; + data.getRoot(setting).expandTriggerFlag = true; + view.switchNode(setting, node); + } + return true; + }, + onClickNode: function (event, node) { + var setting = data.getSetting(event.data.treeId), + clickFlag = ((setting.view.autoCancelSelected && (event.ctrlKey || event.metaKey)) && data.isSelectedNode(setting, node)) ? 0 : (setting.view.autoCancelSelected && (event.ctrlKey || event.metaKey) && setting.view.selectedMulti) ? 2 : 1; + if (tools.apply(setting.callback.beforeClick, [setting.treeId, node, clickFlag], true) == false) return true; + if (clickFlag === 0) { + view.cancelPreSelectedNode(setting, node); + } else { + view.selectNode(setting, node, clickFlag === 2); + } + setting.treeObj.trigger(consts.event.CLICK, [event, setting.treeId, node, clickFlag]); + return true; + }, + onZTreeMousedown: function (event, node) { + var setting = data.getSetting(event.data.treeId); + if (tools.apply(setting.callback.beforeMouseDown, [setting.treeId, node], true)) { + tools.apply(setting.callback.onMouseDown, [event, setting.treeId, node]); + } + return true; + }, + onZTreeMouseup: function (event, node) { + var setting = data.getSetting(event.data.treeId); + if (tools.apply(setting.callback.beforeMouseUp, [setting.treeId, node], true)) { + tools.apply(setting.callback.onMouseUp, [event, setting.treeId, node]); + } + return true; + }, + onZTreeDblclick: function (event, node) { + var setting = data.getSetting(event.data.treeId); + if (tools.apply(setting.callback.beforeDblClick, [setting.treeId, node], true)) { + tools.apply(setting.callback.onDblClick, [event, setting.treeId, node]); + } + return true; + }, + onZTreeContextmenu: function (event, node) { + var setting = data.getSetting(event.data.treeId); + if (tools.apply(setting.callback.beforeRightClick, [setting.treeId, node], true)) { + tools.apply(setting.callback.onRightClick, [event, setting.treeId, node]); + } + return (typeof setting.callback.onRightClick) != "function"; + }, + onSelectStart: function (e) { + var n = e.originalEvent.srcElement.nodeName.toLowerCase(); + return (n === "input" || n === "textarea"); + } + }, + //method of tools for zTree + tools = { + apply: function (fun, param, defaultValue) { + if ((typeof fun) == "function") { + return fun.apply(zt, param ? param : []); + } + return defaultValue; + }, + canAsync: function (setting, node) { + var children = data.nodeChildren(setting, node); + var isParent = data.nodeIsParent(setting, node); + return (setting.async.enable && node && isParent && !(node.zAsync || (children && children.length > 0))); + }, + clone: function (obj) { + if (obj === null) return null; + var o = tools.isArray(obj) ? [] : {}; + for (var i in obj) { + o[i] = (obj[i] instanceof Date) ? new Date(obj[i].getTime()) : (typeof obj[i] === "object" ? tools.clone(obj[i]) : obj[i]); + } + return o; + }, + eqs: function (str1, str2) { + return str1.toLowerCase() === str2.toLowerCase(); + }, + isArray: function (arr) { + return Object.prototype.toString.apply(arr) === "[object Array]"; + }, + isElement: function (o) { + return ( + typeof HTMLElement === "object" ? o instanceof HTMLElement : //DOM2 + o && typeof o === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName === "string" + ); + }, + $: function (node, exp, setting) { + if (!!exp && typeof exp != "string") { + setting = exp; + exp = ""; + } + if (typeof node == "string") { + return $(node, setting ? setting.treeObj.get(0).ownerDocument : null); + } else { + return $("#" + node.tId + exp, setting ? setting.treeObj : null); + } + }, + getMDom: function (setting, curDom, targetExpr) { + if (!curDom) return null; + while (curDom && curDom.id !== setting.treeId) { + for (var i = 0, l = targetExpr.length; curDom.tagName && i < l; i++) { + if (tools.eqs(curDom.tagName, targetExpr[i].tagName) && curDom.getAttribute(targetExpr[i].attrName) !== null) { + return curDom; + } + } + curDom = curDom.parentNode; + } + return null; + }, + getNodeMainDom: function (target) { + return ($(target).parent("li").get(0) || $(target).parentsUntil("li").parent().get(0)); + }, + isChildOrSelf: function (dom, parentId) { + return ($(dom).closest("#" + parentId).length > 0); + }, + uCanDo: function (setting, e) { + return true; + } + }, + //method of operate ztree dom + view = { + addNodes: function (setting, parentNode, index, newNodes, isSilent) { + var isParent = data.nodeIsParent(setting, parentNode); + if (setting.data.keep.leaf && parentNode && !isParent) { + return; + } + if (!tools.isArray(newNodes)) { + newNodes = [newNodes]; + } + if (setting.data.simpleData.enable) { + newNodes = data.transformTozTreeFormat(setting, newNodes); + } + if (parentNode) { + var target_switchObj = $$(parentNode, consts.id.SWITCH, setting), + target_icoObj = $$(parentNode, consts.id.ICON, setting), + target_ulObj = $$(parentNode, consts.id.UL, setting); + + if (!parentNode.open) { + view.replaceSwitchClass(parentNode, target_switchObj, consts.folder.CLOSE); + view.replaceIcoClass(parentNode, target_icoObj, consts.folder.CLOSE); + parentNode.open = false; + target_ulObj.css({ + "display": "none" + }); + } + + data.addNodesData(setting, parentNode, index, newNodes); + view.createNodes(setting, parentNode.level + 1, newNodes, parentNode, index); + if (!isSilent) { + view.expandCollapseParentNode(setting, parentNode, true); + } + } else { + data.addNodesData(setting, data.getRoot(setting), index, newNodes); + view.createNodes(setting, 0, newNodes, null, index); + } + }, + appendNodes: function (setting, level, nodes, parentNode, index, initFlag, openFlag) { + if (!nodes) return []; + var html = []; + + var tmpPNode = (parentNode) ? parentNode : data.getRoot(setting), + tmpPChild = data.nodeChildren(setting, tmpPNode), + isFirstNode, isLastNode; + + if (!tmpPChild || index >= tmpPChild.length - nodes.length) { + index = -1; + } + + for (var i = 0, l = nodes.length; i < l; i++) { + var node = nodes[i]; + if (initFlag) { + isFirstNode = ((index === 0 || tmpPChild.length == nodes.length) && (i == 0)); + isLastNode = (index < 0 && i == (nodes.length - 1)); + data.initNode(setting, level, node, parentNode, isFirstNode, isLastNode, openFlag); + data.addNodeCache(setting, node); + } + var isParent = data.nodeIsParent(setting, node); + + var childHtml = []; + var children = data.nodeChildren(setting, node); + if (children && children.length > 0) { + //make child html first, because checkType + childHtml = view.appendNodes(setting, level + 1, children, node, -1, initFlag, openFlag && node.open); + } + if (openFlag) { + view.makeDOMNodeMainBefore(html, setting, node); + view.makeDOMNodeLine(html, setting, node); + data.getBeforeA(setting, node, html); + view.makeDOMNodeNameBefore(html, setting, node); + data.getInnerBeforeA(setting, node, html); + view.makeDOMNodeIcon(html, setting, node); + data.getInnerAfterA(setting, node, html); + view.makeDOMNodeNameAfter(html, setting, node); + data.getAfterA(setting, node, html); + if (isParent && node.open) { + view.makeUlHtml(setting, node, html, childHtml.join('')); + } + view.makeDOMNodeMainAfter(html, setting, node); + data.addCreatedNode(setting, node); + } + } + return html; + }, + appendParentULDom: function (setting, node) { + var html = [], + nObj = $$(node, setting); + if (!nObj.get(0) && !!node.parentTId) { + view.appendParentULDom(setting, node.getParentNode()); + nObj = $$(node, setting); + } + var ulObj = $$(node, consts.id.UL, setting); + if (ulObj.get(0)) { + ulObj.remove(); + } + var children = data.nodeChildren(setting, node), + childHtml = view.appendNodes(setting, node.level + 1, children, node, -1, false, true); + view.makeUlHtml(setting, node, html, childHtml.join('')); + nObj.append(html.join('')); + }, + asyncNode: function (setting, node, isSilent, callback) { + var i, l; + var isParent = data.nodeIsParent(setting, node); + if (node && !isParent) { + tools.apply(callback); + return false; + } else if (node && node.isAjaxing) { + return false; + } else if (tools.apply(setting.callback.beforeAsync, [setting.treeId, node], true) == false) { + tools.apply(callback); + return false; + } + if (node) { + node.isAjaxing = true; + var icoObj = $$(node, consts.id.ICON, setting); + icoObj.attr({"style": "", "class": consts.className.BUTTON + " " + consts.className.ICO_LOADING}); + } + + var tmpParam = {}; + var autoParam = tools.apply(setting.async.autoParam, [setting.treeId, node], setting.async.autoParam); + for (i = 0, l = autoParam.length; node && i < l; i++) { + var pKey = autoParam[i].split("="), spKey = pKey; + if (pKey.length > 1) { + spKey = pKey[1]; + pKey = pKey[0]; + } + tmpParam[spKey] = node[pKey]; + } + var otherParam = tools.apply(setting.async.otherParam, [setting.treeId, node], setting.async.otherParam); + if (tools.isArray(otherParam)) { + for (i = 0, l = otherParam.length; i < l; i += 2) { + tmpParam[otherParam[i]] = otherParam[i + 1]; + } + } else { + for (var p in otherParam) { + tmpParam[p] = otherParam[p]; + } + } + + var _tmpV = data.getRoot(setting)._ver; + $.ajax({ + contentType: setting.async.contentType, + cache: false, + type: setting.async.type, + url: tools.apply(setting.async.url, [setting.treeId, node], setting.async.url), + data: setting.async.contentType.indexOf('application/json') > -1 ? JSON.stringify(tmpParam) : tmpParam, + dataType: setting.async.dataType, + headers: setting.async.headers, + xhrFields: setting.async.xhrFields, + success: function (msg) { + if (_tmpV != data.getRoot(setting)._ver) { + return; + } + var newNodes = []; + try { + if (!msg || msg.length == 0) { + newNodes = []; + } else if (typeof msg == "string") { + newNodes = eval("(" + msg + ")"); + } else { + newNodes = msg; + } + } catch (err) { + newNodes = msg; + } + + if (node) { + node.isAjaxing = null; + node.zAsync = true; + } + view.setNodeLineIcos(setting, node); + if (newNodes && newNodes !== "") { + newNodes = tools.apply(setting.async.dataFilter, [setting.treeId, node, newNodes], newNodes); + view.addNodes(setting, node, -1, !!newNodes ? tools.clone(newNodes) : [], !!isSilent); + } else { + view.addNodes(setting, node, -1, [], !!isSilent); + } + setting.treeObj.trigger(consts.event.ASYNC_SUCCESS, [setting.treeId, node, msg]); + tools.apply(callback); + }, + error: function (XMLHttpRequest, textStatus, errorThrown) { + if (_tmpV != data.getRoot(setting)._ver) { + return; + } + if (node) node.isAjaxing = null; + view.setNodeLineIcos(setting, node); + setting.treeObj.trigger(consts.event.ASYNC_ERROR, [setting.treeId, node, XMLHttpRequest, textStatus, errorThrown]); + } + }); + return true; + }, + cancelPreSelectedNode: function (setting, node, excludeNode) { + var list = data.getRoot(setting).curSelectedList, + i, n; + for (i = list.length - 1; i >= 0; i--) { + n = list[i]; + if (node === n || (!node && (!excludeNode || excludeNode !== n))) { + $$(n, consts.id.A, setting).removeClass(consts.node.CURSELECTED); + if (node) { + data.removeSelectedNode(setting, node); + break; + } else { + list.splice(i, 1); + setting.treeObj.trigger(consts.event.UNSELECTED, [setting.treeId, n]); + } + } + } + }, + createNodeCallback: function (setting) { + if (!!setting.callback.onNodeCreated || !!setting.view.addDiyDom) { + var root = data.getRoot(setting); + while (root.createdNodes.length > 0) { + var node = root.createdNodes.shift(); + tools.apply(setting.view.addDiyDom, [setting.treeId, node]); + if (!!setting.callback.onNodeCreated) { + setting.treeObj.trigger(consts.event.NODECREATED, [setting.treeId, node]); + } + } + } + }, + createNodes: function (setting, level, nodes, parentNode, index) { + if (!nodes || nodes.length == 0) return; + var root = data.getRoot(setting), + openFlag = !parentNode || parentNode.open || !!$$(data.nodeChildren(setting, parentNode)[0], setting).get(0); + root.createdNodes = []; + var zTreeHtml = view.appendNodes(setting, level, nodes, parentNode, index, true, openFlag), + parentObj, nextObj; + + if (!parentNode) { + parentObj = setting.treeObj; + //setting.treeObj.append(zTreeHtml.join('')); + } else { + var ulObj = $$(parentNode, consts.id.UL, setting); + if (ulObj.get(0)) { + parentObj = ulObj; + //ulObj.append(zTreeHtml.join('')); + } + } + if (parentObj) { + if (index >= 0) { + nextObj = parentObj.children()[index]; + } + if (index >= 0 && nextObj) { + $(nextObj).before(zTreeHtml.join('')); + } else { + parentObj.append(zTreeHtml.join('')); + } + } + + view.createNodeCallback(setting); + }, + destroy: function (setting) { + if (!setting) return; + data.initCache(setting); + data.initRoot(setting); + event.unbindTree(setting); + event.unbindEvent(setting); + setting.treeObj.empty(); + delete settings[setting.treeId]; + }, + expandCollapseNode: function (setting, node, expandFlag, animateFlag, callback) { + var root = data.getRoot(setting); + var tmpCb, _callback; + if (!node) { + tools.apply(callback, []); + return; + } + var children = data.nodeChildren(setting, node); + var isParent = data.nodeIsParent(setting, node); + if (root.expandTriggerFlag) { + _callback = callback; + tmpCb = function () { + if (_callback) _callback(); + if (node.open) { + setting.treeObj.trigger(consts.event.EXPAND, [setting.treeId, node]); + } else { + setting.treeObj.trigger(consts.event.COLLAPSE, [setting.treeId, node]); + } + }; + callback = tmpCb; + root.expandTriggerFlag = false; + } + if (!node.open && isParent && ((!$$(node, consts.id.UL, setting).get(0)) || (children && children.length > 0 && !$$(children[0], setting).get(0)))) { + view.appendParentULDom(setting, node); + view.createNodeCallback(setting); + } + if (node.open == expandFlag) { + tools.apply(callback, []); + return; + } + var ulObj = $$(node, consts.id.UL, setting), + switchObj = $$(node, consts.id.SWITCH, setting), + icoObj = $$(node, consts.id.ICON, setting); + + if (isParent) { + node.open = !node.open; + if (node.iconOpen && node.iconClose) { + icoObj.attr("style", view.makeNodeIcoStyle(setting, node)); + } + + if (node.open) { + view.replaceSwitchClass(node, switchObj, consts.folder.OPEN); + view.replaceIcoClass(node, icoObj, consts.folder.OPEN); + if (animateFlag == false || setting.view.expandSpeed == "") { + ulObj.show(); + tools.apply(callback, []); + } else { + if (children && children.length > 0) { + ulObj.slideDown(setting.view.expandSpeed, callback); + } else { + ulObj.show(); + tools.apply(callback, []); + } + } + } else { + view.replaceSwitchClass(node, switchObj, consts.folder.CLOSE); + view.replaceIcoClass(node, icoObj, consts.folder.CLOSE); + if (animateFlag == false || setting.view.expandSpeed == "" || !(children && children.length > 0)) { + ulObj.hide(); + tools.apply(callback, []); + } else { + ulObj.slideUp(setting.view.expandSpeed, callback); + } + } + } else { + tools.apply(callback, []); + } + }, + expandCollapseParentNode: function (setting, node, expandFlag, animateFlag, callback) { + if (!node) return; + if (!node.parentTId) { + view.expandCollapseNode(setting, node, expandFlag, animateFlag, callback); + return; + } else { + view.expandCollapseNode(setting, node, expandFlag, animateFlag); + } + if (node.parentTId) { + view.expandCollapseParentNode(setting, node.getParentNode(), expandFlag, animateFlag, callback); + } + }, + expandCollapseSonNode: function (setting, node, expandFlag, animateFlag, callback) { + var root = data.getRoot(setting), + treeNodes = (node) ? data.nodeChildren(setting, node) : data.nodeChildren(setting, root), + selfAnimateSign = (node) ? false : animateFlag, + expandTriggerFlag = data.getRoot(setting).expandTriggerFlag; + data.getRoot(setting).expandTriggerFlag = false; + if (treeNodes) { + for (var i = 0, l = treeNodes.length; i < l; i++) { + if (treeNodes[i]) view.expandCollapseSonNode(setting, treeNodes[i], expandFlag, selfAnimateSign); + } + } + data.getRoot(setting).expandTriggerFlag = expandTriggerFlag; + view.expandCollapseNode(setting, node, expandFlag, animateFlag, callback); + }, + isSelectedNode: function (setting, node) { + if (!node) { + return false; + } + var list = data.getRoot(setting).curSelectedList, + i; + for (i = list.length - 1; i >= 0; i--) { + if (node === list[i]) { + return true; + } + } + return false; + }, + makeDOMNodeIcon: function (html, setting, node) { + var nameStr = data.nodeName(setting, node), + name = setting.view.nameIsHTML ? nameStr : nameStr.replace(/&/g, '&').replace(//g, '>'); + html.push("", name, ""); + }, + makeDOMNodeLine: function (html, setting, node) { + html.push(""); + }, + makeDOMNodeMainAfter: function (html, setting, node) { + html.push("
    • "); + }, + makeDOMNodeMainBefore: function (html, setting, node) { + html.push("
    • "); + }, + makeDOMNodeNameAfter: function (html, setting, node) { + html.push(""); + }, + makeDOMNodeNameBefore: function (html, setting, node) { + var title = data.nodeTitle(setting, node), + url = view.makeNodeUrl(setting, node), + fontcss = view.makeNodeFontCss(setting, node), + nodeClasses = view.makeNodeClasses(setting, node), + fontStyle = []; + for (var f in fontcss) { + fontStyle.push(f, ":", fontcss[f], ";"); + } + html.push(" 0) ? " href='" + url + "'" : ""), " target='", view.makeNodeTarget(node), "' style='", fontStyle.join(''), + "'"); + if (tools.apply(setting.view.showTitle, [setting.treeId, node], setting.view.showTitle) && title) { + html.push("title='", title.replace(/'/g, "'").replace(//g, '>'), "'"); + } + html.push(">"); + }, + makeNodeFontCss: function (setting, node) { + var fontCss = tools.apply(setting.view.fontCss, [setting.treeId, node], setting.view.fontCss); + return (fontCss && ((typeof fontCss) != "function")) ? fontCss : {}; + }, + makeNodeClasses: function (setting, node) { + var classes = tools.apply(setting.view.nodeClasses, [setting.treeId, node], setting.view.nodeClasses); + return (classes && (typeof classes !== "function")) ? classes : {add:[], remove:[]}; + }, + makeNodeIcoClass: function (setting, node) { + var icoCss = ["ico"]; + if (!node.isAjaxing) { + var isParent = data.nodeIsParent(setting, node); + icoCss[0] = (node.iconSkin ? node.iconSkin + "_" : "") + icoCss[0]; + if (isParent) { + icoCss.push(node.open ? consts.folder.OPEN : consts.folder.CLOSE); + } else { + icoCss.push(consts.folder.DOCU); + } + } + return consts.className.BUTTON + " " + icoCss.join('_'); + }, + makeNodeIcoStyle: function (setting, node) { + var icoStyle = []; + if (!node.isAjaxing) { + var isParent = data.nodeIsParent(setting, node); + var icon = (isParent && node.iconOpen && node.iconClose) ? (node.open ? node.iconOpen : node.iconClose) : node[setting.data.key.icon]; + if (icon) icoStyle.push("background:url(", icon, ") 0 0 no-repeat;"); + if (setting.view.showIcon == false || !tools.apply(setting.view.showIcon, [setting.treeId, node], true)) { + icoStyle.push("display:none;"); + } + } + return icoStyle.join(''); + }, + makeNodeLineClass: function (setting, node) { + var lineClass = []; + if (setting.view.showLine) { + if (node.level == 0 && node.isFirstNode && node.isLastNode) { + lineClass.push(consts.line.ROOT); + } else if (node.level == 0 && node.isFirstNode) { + lineClass.push(consts.line.ROOTS); + } else if (node.isLastNode) { + lineClass.push(consts.line.BOTTOM); + } else { + lineClass.push(consts.line.CENTER); + } + } else { + lineClass.push(consts.line.NOLINE); + } + if (data.nodeIsParent(setting, node)) { + lineClass.push(node.open ? consts.folder.OPEN : consts.folder.CLOSE); + } else { + lineClass.push(consts.folder.DOCU); + } + return view.makeNodeLineClassEx(node) + lineClass.join('_'); + }, + makeNodeLineClassEx: function (node) { + return consts.className.BUTTON + " " + consts.className.LEVEL + node.level + " " + consts.className.SWITCH + " "; + }, + makeNodeTarget: function (node) { + return (node.target || "_blank"); + }, + makeNodeUrl: function (setting, node) { + var urlKey = setting.data.key.url; + return node[urlKey] ? node[urlKey] : null; + }, + makeUlHtml: function (setting, node, html, content) { + html.push("
        "); + html.push(content); + html.push("
      "); + }, + makeUlLineClass: function (setting, node) { + return ((setting.view.showLine && !node.isLastNode) ? consts.line.LINE : ""); + }, + removeChildNodes: function (setting, node) { + if (!node) return; + var nodes = data.nodeChildren(setting, node); + if (!nodes) return; + + for (var i = 0, l = nodes.length; i < l; i++) { + data.removeNodeCache(setting, nodes[i]); + } + data.removeSelectedNode(setting); + delete node[setting.data.key.children]; + + if (!setting.data.keep.parent) { + data.nodeIsParent(setting, node, false); + node.open = false; + var tmp_switchObj = $$(node, consts.id.SWITCH, setting), + tmp_icoObj = $$(node, consts.id.ICON, setting); + view.replaceSwitchClass(node, tmp_switchObj, consts.folder.DOCU); + view.replaceIcoClass(node, tmp_icoObj, consts.folder.DOCU); + $$(node, consts.id.UL, setting).remove(); + } else { + $$(node, consts.id.UL, setting).empty(); + } + }, + scrollIntoView: function (setting, dom) { + if (!dom) { + return; + } + // support IE 7 / 8 + if (typeof Element === 'undefined' || typeof HTMLElement === 'undefined') { + var contRect = setting.treeObj.get(0).getBoundingClientRect(), + findMeRect = dom.getBoundingClientRect(); + if (findMeRect.top < contRect.top || findMeRect.bottom > contRect.bottom + || findMeRect.right > contRect.right || findMeRect.left < contRect.left) { + dom.scrollIntoView(); + } + return; + } + // CC-BY jocki84@googlemail.com, https://gist.github.com/jocki84/6ffafd003387179a988e + if (!Element.prototype.scrollIntoViewIfNeeded) { + Element.prototype.scrollIntoViewIfNeeded = function (centerIfNeeded) { + "use strict"; + + function makeRange(start, length) { + return {"start": start, "length": length, "end": start + length}; + } + + function coverRange(inner, outer) { + if ( + false === centerIfNeeded || + (outer.start < inner.end && inner.start < outer.end) + ) { + return Math.max( + inner.end - outer.length, + Math.min(outer.start, inner.start) + ); + } + return (inner.start + inner.end - outer.length) / 2; + } + + function makePoint(x, y) { + return { + "x": x, + "y": y, + "translate": function translate(dX, dY) { + return makePoint(x + dX, y + dY); + } + }; + } + + function absolute(elem, pt) { + while (elem) { + pt = pt.translate(elem.offsetLeft, elem.offsetTop); + elem = elem.offsetParent; + } + return pt; + } + + var target = absolute(this, makePoint(0, 0)), + extent = makePoint(this.offsetWidth, this.offsetHeight), + elem = this.parentNode, + origin; + + while (elem instanceof HTMLElement) { + // Apply desired scroll amount. + origin = absolute(elem, makePoint(elem.clientLeft, elem.clientTop)); + elem.scrollLeft = coverRange( + makeRange(target.x - origin.x, extent.x), + makeRange(elem.scrollLeft, elem.clientWidth) + ); + elem.scrollTop = coverRange( + makeRange(target.y - origin.y, extent.y), + makeRange(elem.scrollTop, elem.clientHeight) + ); + + // Determine actual scroll amount by reading back scroll properties. + target = target.translate(-elem.scrollLeft, -elem.scrollTop); + elem = elem.parentNode; + } + }; + } + dom.scrollIntoViewIfNeeded(); + }, + setFirstNode: function (setting, parentNode) { + var children = data.nodeChildren(setting, parentNode); + if (children.length > 0) { + children[0].isFirstNode = true; + } + }, + setLastNode: function (setting, parentNode) { + var children = data.nodeChildren(setting, parentNode); + if (children.length > 0) { + children[children.length - 1].isLastNode = true; + } + }, + removeNode: function (setting, node) { + var root = data.getRoot(setting), + parentNode = (node.parentTId) ? node.getParentNode() : root; + + node.isFirstNode = false; + node.isLastNode = false; + node.getPreNode = function () { + return null; + }; + node.getNextNode = function () { + return null; + }; + + if (!data.getNodeCache(setting, node.tId)) { + return; + } + + $$(node, setting).remove(); + data.removeNodeCache(setting, node); + data.removeSelectedNode(setting, node); + + var children = data.nodeChildren(setting, parentNode); + for (var i = 0, l = children.length; i < l; i++) { + if (children[i].tId == node.tId) { + children.splice(i, 1); + break; + } + } + view.setFirstNode(setting, parentNode); + view.setLastNode(setting, parentNode); + + var tmp_ulObj, tmp_switchObj, tmp_icoObj, + childLength = children.length; + + //repair nodes old parent + if (!setting.data.keep.parent && childLength == 0) { + //old parentNode has no child nodes + data.nodeIsParent(setting, parentNode, false); + parentNode.open = false; + delete parentNode[setting.data.key.children]; + tmp_ulObj = $$(parentNode, consts.id.UL, setting); + tmp_switchObj = $$(parentNode, consts.id.SWITCH, setting); + tmp_icoObj = $$(parentNode, consts.id.ICON, setting); + view.replaceSwitchClass(parentNode, tmp_switchObj, consts.folder.DOCU); + view.replaceIcoClass(parentNode, tmp_icoObj, consts.folder.DOCU); + tmp_ulObj.css("display", "none"); + + } else if (setting.view.showLine && childLength > 0) { + //old parentNode has child nodes + var newLast = children[childLength - 1]; + tmp_ulObj = $$(newLast, consts.id.UL, setting); + tmp_switchObj = $$(newLast, consts.id.SWITCH, setting); + tmp_icoObj = $$(newLast, consts.id.ICON, setting); + if (parentNode == root) { + if (children.length == 1) { + //node was root, and ztree has only one root after move node + view.replaceSwitchClass(newLast, tmp_switchObj, consts.line.ROOT); + } else { + var tmp_first_switchObj = $$(children[0], consts.id.SWITCH, setting); + view.replaceSwitchClass(children[0], tmp_first_switchObj, consts.line.ROOTS); + view.replaceSwitchClass(newLast, tmp_switchObj, consts.line.BOTTOM); + } + } else { + view.replaceSwitchClass(newLast, tmp_switchObj, consts.line.BOTTOM); + } + tmp_ulObj.removeClass(consts.line.LINE); + } + }, + replaceIcoClass: function (node, obj, newName) { + if (!obj || node.isAjaxing) return; + var tmpName = obj.attr("class"); + if (tmpName == undefined) return; + var tmpList = tmpName.split("_"); + switch (newName) { + case consts.folder.OPEN: + case consts.folder.CLOSE: + case consts.folder.DOCU: + tmpList[tmpList.length - 1] = newName; + break; + } + obj.attr("class", tmpList.join("_")); + }, + replaceSwitchClass: function (node, obj, newName) { + if (!obj) return; + var tmpName = obj.attr("class"); + if (tmpName == undefined) return; + var tmpList = tmpName.split("_"); + switch (newName) { + case consts.line.ROOT: + case consts.line.ROOTS: + case consts.line.CENTER: + case consts.line.BOTTOM: + case consts.line.NOLINE: + tmpList[0] = view.makeNodeLineClassEx(node) + newName; + break; + case consts.folder.OPEN: + case consts.folder.CLOSE: + case consts.folder.DOCU: + tmpList[1] = newName; + break; + } + obj.attr("class", tmpList.join("_")); + if (newName !== consts.folder.DOCU) { + obj.removeAttr("disabled"); + } else { + obj.attr("disabled", "disabled"); + } + }, + selectNode: function (setting, node, addFlag) { + if (!addFlag) { + view.cancelPreSelectedNode(setting, null, node); + } + $$(node, consts.id.A, setting).addClass(consts.node.CURSELECTED); + data.addSelectedNode(setting, node); + setting.treeObj.trigger(consts.event.SELECTED, [setting.treeId, node]); + }, + setNodeFontCss: function (setting, treeNode) { + var aObj = $$(treeNode, consts.id.A, setting), + fontCss = view.makeNodeFontCss(setting, treeNode); + if (fontCss) { + aObj.css(fontCss); + } + }, + setNodeClasses: function (setting, treeNode) { + var aObj = $$(treeNode, consts.id.A, setting), + classes = view.makeNodeClasses(setting, treeNode); + if ('add' in classes && classes.add.length) { + aObj.addClass(classes.add.join(' ')); + } + if ('remove' in classes && classes.remove.length) { + aObj.removeClass(classes.remove.join(' ')); + } + }, + setNodeLineIcos: function (setting, node) { + if (!node) return; + var switchObj = $$(node, consts.id.SWITCH, setting), + ulObj = $$(node, consts.id.UL, setting), + icoObj = $$(node, consts.id.ICON, setting), + ulLine = view.makeUlLineClass(setting, node); + if (ulLine.length == 0) { + ulObj.removeClass(consts.line.LINE); + } else { + ulObj.addClass(ulLine); + } + switchObj.attr("class", view.makeNodeLineClass(setting, node)); + if (data.nodeIsParent(setting, node)) { + switchObj.removeAttr("disabled"); + } else { + switchObj.attr("disabled", "disabled"); + } + icoObj.removeAttr("style"); + icoObj.attr("style", view.makeNodeIcoStyle(setting, node)); + icoObj.attr("class", view.makeNodeIcoClass(setting, node)); + }, + setNodeName: function (setting, node) { + var title = data.nodeTitle(setting, node), + nObj = $$(node, consts.id.SPAN, setting); + nObj.empty(); + if (setting.view.nameIsHTML) { + nObj.html(data.nodeName(setting, node)); + } else { + nObj.text(data.nodeName(setting, node)); + } + if (tools.apply(setting.view.showTitle, [setting.treeId, node], setting.view.showTitle)) { + var aObj = $$(node, consts.id.A, setting); + aObj.attr("title", !title ? "" : title); + } + }, + setNodeTarget: function (setting, node) { + var aObj = $$(node, consts.id.A, setting); + aObj.attr("target", view.makeNodeTarget(node)); + }, + setNodeUrl: function (setting, node) { + var aObj = $$(node, consts.id.A, setting), + url = view.makeNodeUrl(setting, node); + if (url == null || url.length == 0) { + aObj.removeAttr("href"); + } else { + aObj.attr("href", url); + } + }, + switchNode: function (setting, node) { + if (node.open || !tools.canAsync(setting, node)) { + view.expandCollapseNode(setting, node, !node.open); + } else if (setting.async.enable) { + if (!view.asyncNode(setting, node)) { + view.expandCollapseNode(setting, node, !node.open); + return; + } + } else if (node) { + view.expandCollapseNode(setting, node, !node.open); + } + } + }; + // zTree defind + $.fn.zTree = { + consts: _consts, + _z: { + tools: tools, + view: view, + event: event, + data: data + }, + getZTreeObj: function (treeId) { + var o = data.getZTreeTools(treeId); + return o ? o : null; + }, + destroy: function (treeId) { + if (!!treeId && treeId.length > 0) { + view.destroy(data.getSetting(treeId)); + } else { + for (var s in settings) { + view.destroy(settings[s]); + } + } + }, + init: function (obj, zSetting, zNodes) { + var setting = tools.clone(_setting); + $.extend(true, setting, zSetting); + setting.treeId = obj.attr("id"); + setting.treeObj = obj; + setting.treeObj.empty(); + settings[setting.treeId] = setting; + //For some older browser,(e.g., ie6) + if (typeof document.body.style.maxHeight === "undefined") { + setting.view.expandSpeed = ""; + } + data.initRoot(setting); + var root = data.getRoot(setting); + zNodes = zNodes ? tools.clone(tools.isArray(zNodes) ? zNodes : [zNodes]) : []; + if (setting.data.simpleData.enable) { + data.nodeChildren(setting, root, data.transformTozTreeFormat(setting, zNodes)); + } else { + data.nodeChildren(setting, root, zNodes); + } + + data.initCache(setting); + event.unbindTree(setting); + event.bindTree(setting); + event.unbindEvent(setting); + event.bindEvent(setting); + + var zTreeTools = { + setting: setting, + addNodes: function (parentNode, index, newNodes, isSilent) { + if (!parentNode) parentNode = null; + var isParent = data.nodeIsParent(setting, parentNode); + if (parentNode && !isParent && setting.data.keep.leaf) return null; + + var i = parseInt(index, 10); + if (isNaN(i)) { + isSilent = !!newNodes; + newNodes = index; + index = -1; + } else { + index = i; + } + if (!newNodes) return null; + + + var xNewNodes = tools.clone(tools.isArray(newNodes) ? newNodes : [newNodes]); + + function addCallback() { + view.addNodes(setting, parentNode, index, xNewNodes, (isSilent == true)); + } + + if (tools.canAsync(setting, parentNode)) { + view.asyncNode(setting, parentNode, isSilent, addCallback); + } else { + addCallback(); + } + return xNewNodes; + }, + cancelSelectedNode: function (node) { + view.cancelPreSelectedNode(setting, node); + }, + destroy: function () { + view.destroy(setting); + }, + expandAll: function (expandFlag) { + expandFlag = !!expandFlag; + view.expandCollapseSonNode(setting, null, expandFlag, true); + return expandFlag; + }, + expandNode: function (node, expandFlag, sonSign, focus, callbackFlag) { + if (!node || !data.nodeIsParent(setting, node)) return null; + if (expandFlag !== true && expandFlag !== false) { + expandFlag = !node.open; + } + callbackFlag = !!callbackFlag; + + if (callbackFlag && expandFlag && (tools.apply(setting.callback.beforeExpand, [setting.treeId, node], true) == false)) { + return null; + } else if (callbackFlag && !expandFlag && (tools.apply(setting.callback.beforeCollapse, [setting.treeId, node], true) == false)) { + return null; + } + if (expandFlag && node.parentTId) { + view.expandCollapseParentNode(setting, node.getParentNode(), expandFlag, false); + } + if (expandFlag === node.open && !sonSign) { + return null; + } + + data.getRoot(setting).expandTriggerFlag = callbackFlag; + if (!tools.canAsync(setting, node) && sonSign) { + view.expandCollapseSonNode(setting, node, expandFlag, true, showNodeFocus); + } else { + node.open = !expandFlag; + view.switchNode(this.setting, node); + showNodeFocus(); + } + return expandFlag; + + function showNodeFocus() { + var a = $$(node, consts.id.A, setting).get(0); + if (a && focus !== false) { + view.scrollIntoView(setting, a); + } + } + }, + getNodes: function () { + return data.getNodes(setting); + }, + getNodeByParam: function (key, value, parentNode) { + if (!key) return null; + return data.getNodeByParam(setting, parentNode ? data.nodeChildren(setting, parentNode) : data.getNodes(setting), key, value); + }, + getNodeByTId: function (tId) { + return data.getNodeCache(setting, tId); + }, + getNodesByParam: function (key, value, parentNode) { + if (!key) return null; + return data.getNodesByParam(setting, parentNode ? data.nodeChildren(setting, parentNode) : data.getNodes(setting), key, value); + }, + getNodesByParamFuzzy: function (key, value, parentNode) { + if (!key) return null; + return data.getNodesByParamFuzzy(setting, parentNode ? data.nodeChildren(setting, parentNode) : data.getNodes(setting), key, value); + }, + getNodesByFilter: function (filter, isSingle, parentNode, invokeParam) { + isSingle = !!isSingle; + if (!filter || (typeof filter != "function")) return (isSingle ? null : []); + return data.getNodesByFilter(setting, parentNode ? data.nodeChildren(setting, parentNode) : data.getNodes(setting), filter, isSingle, invokeParam); + }, + getNodeIndex: function (node) { + if (!node) return null; + var parentNode = (node.parentTId) ? node.getParentNode() : data.getRoot(setting); + var children = data.nodeChildren(setting, parentNode); + for (var i = 0, l = children.length; i < l; i++) { + if (children[i] == node) return i; + } + return -1; + }, + getSelectedNodes: function () { + var r = [], list = data.getRoot(setting).curSelectedList; + for (var i = 0, l = list.length; i < l; i++) { + r.push(list[i]); + } + return r; + }, + isSelectedNode: function (node) { + return data.isSelectedNode(setting, node); + }, + reAsyncChildNodesPromise: function (parentNode, reloadType, isSilent) { + var promise = new Promise(function (resolve, reject) { + try { + zTreeTools.reAsyncChildNodes(parentNode, reloadType, isSilent, function () { + resolve(parentNode); + }); + } catch (e) { + reject(e); + } + }); + return promise; + }, + reAsyncChildNodes: function (parentNode, reloadType, isSilent, callback) { + if (!this.setting.async.enable) return; + var isRoot = !parentNode; + if (isRoot) { + parentNode = data.getRoot(setting); + } + if (reloadType == "refresh") { + var children = data.nodeChildren(setting, parentNode); + for (var i = 0, l = children ? children.length : 0; i < l; i++) { + data.removeNodeCache(setting, children[i]); + } + data.removeSelectedNode(setting); + data.nodeChildren(setting, parentNode, []); + if (isRoot) { + this.setting.treeObj.empty(); + } else { + var ulObj = $$(parentNode, consts.id.UL, setting); + ulObj.empty(); + } + } + view.asyncNode(this.setting, isRoot ? null : parentNode, !!isSilent, callback); + }, + refresh: function () { + this.setting.treeObj.empty(); + var root = data.getRoot(setting), + nodes = data.nodeChildren(setting, root); + data.initRoot(setting); + data.nodeChildren(setting, root, nodes); + data.initCache(setting); + view.createNodes(setting, 0, data.nodeChildren(setting, root), null, -1); + }, + removeChildNodes: function (node) { + if (!node) return null; + var nodes = data.nodeChildren(setting, node); + view.removeChildNodes(setting, node); + return nodes ? nodes : null; + }, + removeNode: function (node, callbackFlag) { + if (!node) return; + callbackFlag = !!callbackFlag; + if (callbackFlag && tools.apply(setting.callback.beforeRemove, [setting.treeId, node], true) == false) return; + view.removeNode(setting, node); + if (callbackFlag) { + this.setting.treeObj.trigger(consts.event.REMOVE, [setting.treeId, node]); + } + }, + selectNode: function (node, addFlag, isSilent) { + if (!node) return; + if (tools.uCanDo(setting)) { + addFlag = setting.view.selectedMulti && addFlag; + if (node.parentTId) { + view.expandCollapseParentNode(setting, node.getParentNode(), true, false, showNodeFocus); + } else if (!isSilent) { + try { + $$(node, setting).focus().blur(); + } catch (e) { + } + } + view.selectNode(setting, node, addFlag); + } + + function showNodeFocus() { + if (isSilent) { + return; + } + var a = $$(node, setting).get(0); + view.scrollIntoView(setting, a); + } + }, + transformTozTreeNodes: function (simpleNodes) { + return data.transformTozTreeFormat(setting, simpleNodes); + }, + transformToArray: function (nodes) { + return data.transformToArrayFormat(setting, nodes); + }, + updateNode: function (node, checkTypeFlag) { + if (!node) return; + var nObj = $$(node, setting); + if (nObj.get(0) && tools.uCanDo(setting)) { + view.setNodeName(setting, node); + view.setNodeTarget(setting, node); + view.setNodeUrl(setting, node); + view.setNodeLineIcos(setting, node); + view.setNodeFontCss(setting, node); + view.setNodeClasses(setting, node); + } + } + }; + root.treeTools = zTreeTools; + data.setZTreeTools(setting, zTreeTools); + var children = data.nodeChildren(setting, root); + if (children && children.length > 0) { + view.createNodes(setting, 0, children, null, -1); + } else if (setting.async.enable && setting.async.url && setting.async.url !== '') { + view.asyncNode(setting); + } + return zTreeTools; + } + }; + + var zt = $.fn.zTree, + $$ = tools.$, + consts = zt.consts; +})(jQuery); \ No newline at end of file diff --git a/public/self/ztree/js/jquery.ztree.excheck.js b/public/self/ztree/js/jquery.ztree.excheck.js new file mode 100644 index 000000000..44b533672 --- /dev/null +++ b/public/self/ztree/js/jquery.ztree.excheck.js @@ -0,0 +1,652 @@ +/* + * JQuery zTree excheck + * v3.5.48 + * http://treejs.cn/ + * + * Copyright (c) 2010 Hunter.z + * + * Licensed same as jquery - MIT License + * http://www.opensource.org/licenses/mit-license.php + * + * Date: 2020-11-21 + */ + +(function ($) { + //default consts of excheck + var _consts = { + event: { + CHECK: "ztree_check" + }, + id: { + CHECK: "_check" + }, + checkbox: { + STYLE: "checkbox", + DEFAULT: "chk", + DISABLED: "disable", + FALSE: "false", + TRUE: "true", + FULL: "full", + PART: "part", + FOCUS: "focus" + }, + radio: { + STYLE: "radio", + TYPE_ALL: "all", + TYPE_LEVEL: "level" + } + }, + //default setting of excheck + _setting = { + check: { + enable: false, + autoCheckTrigger: false, + chkStyle: _consts.checkbox.STYLE, + nocheckInherit: false, + chkDisabledInherit: false, + radioType: _consts.radio.TYPE_LEVEL, + chkboxType: { + "Y": "ps", + "N": "ps" + } + }, + data: { + key: { + checked: "checked" + } + }, + callback: { + beforeCheck: null, + onCheck: null + } + }, + //default root of excheck + _initRoot = function (setting) { + var r = data.getRoot(setting); + r.radioCheckedList = []; + }, + //default cache of excheck + _initCache = function (treeId) { + }, + //default bind event of excheck + _bindEvent = function (setting) { + var o = setting.treeObj, + c = consts.event; + o.bind(c.CHECK, function (event, srcEvent, treeId, node) { + event.srcEvent = srcEvent; + tools.apply(setting.callback.onCheck, [event, treeId, node]); + }); + }, + _unbindEvent = function (setting) { + var o = setting.treeObj, + c = consts.event; + o.unbind(c.CHECK); + }, + //default event proxy of excheck + _eventProxy = function (e) { + var target = e.target, + setting = data.getSetting(e.data.treeId), + tId = "", node = null, + nodeEventType = "", treeEventType = "", + nodeEventCallback = null, treeEventCallback = null; + + if (tools.eqs(e.type, "mouseover")) { + if (setting.check.enable && tools.eqs(target.tagName, "span") && target.getAttribute("treeNode" + consts.id.CHECK) !== null) { + tId = tools.getNodeMainDom(target).id; + nodeEventType = "mouseoverCheck"; + } + } else if (tools.eqs(e.type, "mouseout")) { + if (setting.check.enable && tools.eqs(target.tagName, "span") && target.getAttribute("treeNode" + consts.id.CHECK) !== null) { + tId = tools.getNodeMainDom(target).id; + nodeEventType = "mouseoutCheck"; + } + } else if (tools.eqs(e.type, "click")) { + if (setting.check.enable && tools.eqs(target.tagName, "span") && target.getAttribute("treeNode" + consts.id.CHECK) !== null) { + tId = tools.getNodeMainDom(target).id; + nodeEventType = "checkNode"; + } + } + if (tId.length > 0) { + node = data.getNodeCache(setting, tId); + switch (nodeEventType) { + case "checkNode" : + nodeEventCallback = _handler.onCheckNode; + break; + case "mouseoverCheck" : + nodeEventCallback = _handler.onMouseoverCheck; + break; + case "mouseoutCheck" : + nodeEventCallback = _handler.onMouseoutCheck; + break; + } + } + var proxyResult = { + stop: nodeEventType === "checkNode", + node: node, + nodeEventType: nodeEventType, + nodeEventCallback: nodeEventCallback, + treeEventType: treeEventType, + treeEventCallback: treeEventCallback + }; + return proxyResult + }, + //default init node of excheck + _initNode = function (setting, level, n, parentNode, isFirstNode, isLastNode, openFlag) { + if (!n) return; + var checked = data.nodeChecked(setting, n); + n.checkedOld = checked; + if (typeof n.nocheck == "string") n.nocheck = tools.eqs(n.nocheck, "true"); + n.nocheck = !!n.nocheck || (setting.check.nocheckInherit && parentNode && !!parentNode.nocheck); + if (typeof n.chkDisabled == "string") n.chkDisabled = tools.eqs(n.chkDisabled, "true"); + n.chkDisabled = !!n.chkDisabled || (setting.check.chkDisabledInherit && parentNode && !!parentNode.chkDisabled); + if (typeof n.halfCheck == "string") n.halfCheck = tools.eqs(n.halfCheck, "true"); + n.halfCheck = !!n.halfCheck; + n.check_Child_State = -1; + n.check_Focus = false; + n.getCheckStatus = function () { + return data.getCheckStatus(setting, n); + }; + + if (setting.check.chkStyle == consts.radio.STYLE && setting.check.radioType == consts.radio.TYPE_ALL && checked) { + var r = data.getRoot(setting); + r.radioCheckedList.push(n); + } + }, + //add dom for check + _beforeA = function (setting, node, html) { + if (setting.check.enable) { + data.makeChkFlag(setting, node); + html.push(""); + } + }, + //update zTreeObj, add method of check + _zTreeTools = function (setting, zTreeTools) { + zTreeTools.checkNode = function (node, checked, checkTypeFlag, callbackFlag) { + var nodeChecked = data.nodeChecked(setting, node); + if (node.chkDisabled === true) return; + if (checked !== true && checked !== false) { + checked = !nodeChecked; + } + callbackFlag = !!callbackFlag; + + if (nodeChecked === checked && !checkTypeFlag) { + return; + } else if (callbackFlag && tools.apply(this.setting.callback.beforeCheck, [this.setting.treeId, node], true) == false) { + return; + } + if (tools.uCanDo(this.setting) && this.setting.check.enable && node.nocheck !== true) { + data.nodeChecked(setting, node, checked); + var checkObj = $$(node, consts.id.CHECK, this.setting); + if (checkTypeFlag || this.setting.check.chkStyle === consts.radio.STYLE) view.checkNodeRelation(this.setting, node); + view.setChkClass(this.setting, checkObj, node); + view.repairParentChkClassWithSelf(this.setting, node); + if (callbackFlag) { + this.setting.treeObj.trigger(consts.event.CHECK, [null, this.setting.treeId, node]); + } + } + } + + zTreeTools.checkAllNodes = function (checked) { + view.repairAllChk(this.setting, !!checked); + } + + zTreeTools.getCheckedNodes = function (checked) { + checked = (checked !== false); + var children = data.nodeChildren(setting, data.getRoot(this.setting)); + return data.getTreeCheckedNodes(this.setting, children, checked); + } + + zTreeTools.getChangeCheckedNodes = function () { + var children = data.nodeChildren(setting, data.getRoot(this.setting)); + return data.getTreeChangeCheckedNodes(this.setting, children); + } + + zTreeTools.setChkDisabled = function (node, disabled, inheritParent, inheritChildren) { + disabled = !!disabled; + inheritParent = !!inheritParent; + inheritChildren = !!inheritChildren; + view.repairSonChkDisabled(this.setting, node, disabled, inheritChildren); + view.repairParentChkDisabled(this.setting, node.getParentNode(), disabled, inheritParent); + } + + var _updateNode = zTreeTools.updateNode; + zTreeTools.updateNode = function (node, checkTypeFlag) { + if (_updateNode) _updateNode.apply(zTreeTools, arguments); + if (!node || !this.setting.check.enable) return; + var nObj = $$(node, this.setting); + if (nObj.get(0) && tools.uCanDo(this.setting)) { + var checkObj = $$(node, consts.id.CHECK, this.setting); + if (checkTypeFlag == true || this.setting.check.chkStyle === consts.radio.STYLE) view.checkNodeRelation(this.setting, node); + view.setChkClass(this.setting, checkObj, node); + view.repairParentChkClassWithSelf(this.setting, node); + } + } + }, + //method of operate data + _data = { + getRadioCheckedList: function (setting) { + var checkedList = data.getRoot(setting).radioCheckedList; + for (var i = 0, j = checkedList.length; i < j; i++) { + if (!data.getNodeCache(setting, checkedList[i].tId)) { + checkedList.splice(i, 1); + i--; + j--; + } + } + return checkedList; + }, + getCheckStatus: function (setting, node) { + if (!setting.check.enable || node.nocheck || node.chkDisabled) return null; + var checked = data.nodeChecked(setting, node), + r = { + checked: checked, + half: node.halfCheck ? node.halfCheck : (setting.check.chkStyle == consts.radio.STYLE ? (node.check_Child_State === 2) : (checked ? (node.check_Child_State > -1 && node.check_Child_State < 2) : (node.check_Child_State > 0))) + }; + return r; + }, + getTreeCheckedNodes: function (setting, nodes, checked, results) { + if (!nodes) return []; + var onlyOne = (checked && setting.check.chkStyle == consts.radio.STYLE && setting.check.radioType == consts.radio.TYPE_ALL); + results = !results ? [] : results; + for (var i = 0, l = nodes.length; i < l; i++) { + var node = nodes[i]; + var children = data.nodeChildren(setting, node); + var nodeChecked = data.nodeChecked(setting, node); + if (node.nocheck !== true && node.chkDisabled !== true && nodeChecked == checked) { + results.push(node); + if (onlyOne) { + break; + } + } + data.getTreeCheckedNodes(setting, children, checked, results); + if (onlyOne && results.length > 0) { + break; + } + } + return results; + }, + getTreeChangeCheckedNodes: function (setting, nodes, results) { + if (!nodes) return []; + results = !results ? [] : results; + for (var i = 0, l = nodes.length; i < l; i++) { + var node = nodes[i]; + var children = data.nodeChildren(setting, node); + var nodeChecked = data.nodeChecked(setting, node); + if (node.nocheck !== true && node.chkDisabled !== true && nodeChecked != node.checkedOld) { + results.push(node); + } + data.getTreeChangeCheckedNodes(setting, children, results); + } + return results; + }, + makeChkFlag: function (setting, node) { + if (!node) return; + var chkFlag = -1; + var children = data.nodeChildren(setting, node); + if (children) { + for (var i = 0, l = children.length; i < l; i++) { + var cNode = children[i]; + var nodeChecked = data.nodeChecked(setting, cNode); + var tmp = -1; + if (setting.check.chkStyle == consts.radio.STYLE) { + if (cNode.nocheck === true || cNode.chkDisabled === true) { + tmp = cNode.check_Child_State; + } else if (cNode.halfCheck === true) { + tmp = 2; + } else if (nodeChecked) { + tmp = 2; + } else { + tmp = cNode.check_Child_State > 0 ? 2 : 0; + } + if (tmp == 2) { + chkFlag = 2; + break; + } else if (tmp == 0) { + chkFlag = 0; + } + } else if (setting.check.chkStyle == consts.checkbox.STYLE) { + if (cNode.nocheck === true || cNode.chkDisabled === true) { + tmp = cNode.check_Child_State; + } else if (cNode.halfCheck === true) { + tmp = 1; + } else if (nodeChecked) { + tmp = (cNode.check_Child_State === -1 || cNode.check_Child_State === 2) ? 2 : 1; + } else { + tmp = (cNode.check_Child_State > 0) ? 1 : 0; + } + if (tmp === 1) { + chkFlag = 1; + break; + } else if (tmp === 2 && chkFlag > -1 && i > 0 && tmp !== chkFlag) { + chkFlag = 1; + break; + } else if (chkFlag === 2 && tmp > -1 && tmp < 2) { + chkFlag = 1; + break; + } else if (tmp > -1) { + chkFlag = tmp; + } + } + } + } + node.check_Child_State = chkFlag; + } + }, + //method of event proxy + _event = {}, + //method of event handler + _handler = { + onCheckNode: function (event, node) { + if (node.chkDisabled === true) return false; + var setting = data.getSetting(event.data.treeId); + if (tools.apply(setting.callback.beforeCheck, [setting.treeId, node], true) == false) return true; + var nodeChecked = data.nodeChecked(setting, node); + data.nodeChecked(setting, node, !nodeChecked); + view.checkNodeRelation(setting, node); + var checkObj = $$(node, consts.id.CHECK, setting); + view.setChkClass(setting, checkObj, node); + view.repairParentChkClassWithSelf(setting, node); + setting.treeObj.trigger(consts.event.CHECK, [event, setting.treeId, node]); + return true; + }, + onMouseoverCheck: function (event, node) { + if (node.chkDisabled === true) return false; + var setting = data.getSetting(event.data.treeId), + checkObj = $$(node, consts.id.CHECK, setting); + node.check_Focus = true; + view.setChkClass(setting, checkObj, node); + return true; + }, + onMouseoutCheck: function (event, node) { + if (node.chkDisabled === true) return false; + var setting = data.getSetting(event.data.treeId), + checkObj = $$(node, consts.id.CHECK, setting); + node.check_Focus = false; + view.setChkClass(setting, checkObj, node); + return true; + } + }, + //method of tools for zTree + _tools = {}, + //method of operate ztree dom + _view = { + checkNodeRelation: function (setting, node) { + var pNode, i, l, + r = consts.radio; + var nodeChecked = data.nodeChecked(setting, node); + if (setting.check.chkStyle == r.STYLE) { + var checkedList = data.getRadioCheckedList(setting); + if (nodeChecked) { + if (setting.check.radioType == r.TYPE_ALL) { + for (i = checkedList.length - 1; i >= 0; i--) { + pNode = checkedList[i]; + var pNodeChecked = data.nodeChecked(setting, pNode); + if (pNodeChecked && pNode != node) { + data.nodeChecked(setting, pNode, false); + checkedList.splice(i, 1); + + view.setChkClass(setting, $$(pNode, consts.id.CHECK, setting), pNode); + if (pNode.parentTId != node.parentTId) { + view.repairParentChkClassWithSelf(setting, pNode); + } + } + } + checkedList.push(node); + } else { + var parentNode = (node.parentTId) ? node.getParentNode() : data.getRoot(setting); + var children = data.nodeChildren(setting, parentNode); + for (i = 0, l = children.length; i < l; i++) { + pNode = children[i]; + var pNodeChecked = data.nodeChecked(setting, pNode); + if (pNodeChecked && pNode != node) { + data.nodeChecked(setting, pNode, false); + view.setChkClass(setting, $$(pNode, consts.id.CHECK, setting), pNode); + } + } + } + } else if (setting.check.radioType == r.TYPE_ALL) { + for (i = 0, l = checkedList.length; i < l; i++) { + if (node == checkedList[i]) { + checkedList.splice(i, 1); + break; + } + } + } + + } else { + var children = data.nodeChildren(setting, node); + if (nodeChecked && (!children || children.length == 0 || setting.check.chkboxType.Y.indexOf("s") > -1)) { + view.setSonNodeCheckBox(setting, node, true); + } + if (!nodeChecked && (!children || children.length == 0 || setting.check.chkboxType.N.indexOf("s") > -1)) { + view.setSonNodeCheckBox(setting, node, false); + } + if (nodeChecked && setting.check.chkboxType.Y.indexOf("p") > -1) { + view.setParentNodeCheckBox(setting, node, true); + } + if (!nodeChecked && setting.check.chkboxType.N.indexOf("p") > -1) { + view.setParentNodeCheckBox(setting, node, false); + } + } + }, + makeChkClass: function (setting, node) { + var c = consts.checkbox, r = consts.radio, + fullStyle = ""; + var nodeChecked = data.nodeChecked(setting, node); + if (node.chkDisabled === true) { + fullStyle = c.DISABLED; + } else if (node.halfCheck) { + fullStyle = c.PART; + } else if (setting.check.chkStyle == r.STYLE) { + fullStyle = (node.check_Child_State < 1) ? c.FULL : c.PART; + } else { + fullStyle = nodeChecked ? ((node.check_Child_State === 2 || node.check_Child_State === -1) ? c.FULL : c.PART) : ((node.check_Child_State < 1) ? c.FULL : c.PART); + } + var chkName = setting.check.chkStyle + "_" + (nodeChecked ? c.TRUE : c.FALSE) + "_" + fullStyle; + chkName = (node.check_Focus && node.chkDisabled !== true) ? chkName + "_" + c.FOCUS : chkName; + return consts.className.BUTTON + " " + c.DEFAULT + " " + chkName; + }, + repairAllChk: function (setting, checked) { + if (setting.check.enable && setting.check.chkStyle === consts.checkbox.STYLE) { + var root = data.getRoot(setting); + var children = data.nodeChildren(setting, root); + for (var i = 0, l = children.length; i < l; i++) { + var node = children[i]; + if (node.nocheck !== true && node.chkDisabled !== true) { + data.nodeChecked(setting, node, checked); + } + view.setSonNodeCheckBox(setting, node, checked); + } + } + }, + repairChkClass: function (setting, node) { + if (!node) return; + data.makeChkFlag(setting, node); + if (node.nocheck !== true) { + var checkObj = $$(node, consts.id.CHECK, setting); + view.setChkClass(setting, checkObj, node); + } + }, + repairParentChkClass: function (setting, node) { + if (!node || !node.parentTId) return; + var pNode = node.getParentNode(); + view.repairChkClass(setting, pNode); + view.repairParentChkClass(setting, pNode); + }, + repairParentChkClassWithSelf: function (setting, node) { + if (!node) return; + var children = data.nodeChildren(setting, node); + if (children && children.length > 0) { + view.repairParentChkClass(setting, children[0]); + } else { + view.repairParentChkClass(setting, node); + } + }, + repairSonChkDisabled: function (setting, node, chkDisabled, inherit) { + if (!node) return; + if (node.chkDisabled != chkDisabled) { + node.chkDisabled = chkDisabled; + } + view.repairChkClass(setting, node); + var children = data.nodeChildren(setting, node); + if (children && inherit) { + for (var i = 0, l = children.length; i < l; i++) { + var sNode = children[i]; + view.repairSonChkDisabled(setting, sNode, chkDisabled, inherit); + } + } + }, + repairParentChkDisabled: function (setting, node, chkDisabled, inherit) { + if (!node) return; + if (node.chkDisabled != chkDisabled && inherit) { + node.chkDisabled = chkDisabled; + } + view.repairChkClass(setting, node); + view.repairParentChkDisabled(setting, node.getParentNode(), chkDisabled, inherit); + }, + setChkClass: function (setting, obj, node) { + if (!obj) return; + if (node.nocheck === true) { + obj.hide(); + } else { + obj.show(); + } + obj.attr('class', view.makeChkClass(setting, node)); + }, + setParentNodeCheckBox: function (setting, node, value, srcNode) { + var checkObj = $$(node, consts.id.CHECK, setting); + if (!srcNode) srcNode = node; + data.makeChkFlag(setting, node); + if (node.nocheck !== true && node.chkDisabled !== true) { + data.nodeChecked(setting, node, value); + view.setChkClass(setting, checkObj, node); + if (setting.check.autoCheckTrigger && node != srcNode) { + setting.treeObj.trigger(consts.event.CHECK, [null, setting.treeId, node]); + } + } + if (node.parentTId) { + var pSign = true; + if (!value) { + var pNodes = data.nodeChildren(setting, node.getParentNode()); + for (var i = 0, l = pNodes.length; i < l; i++) { + var pNode = pNodes[i]; + var nodeChecked = data.nodeChecked(setting, pNode); + if ((pNode.nocheck !== true && pNode.chkDisabled !== true && nodeChecked) + || ((pNode.nocheck === true || pNode.chkDisabled === true) && pNode.check_Child_State > 0)) { + pSign = false; + break; + } + } + } + if (pSign) { + view.setParentNodeCheckBox(setting, node.getParentNode(), value, srcNode); + } + } + }, + setSonNodeCheckBox: function (setting, node, value, srcNode) { + if (!node) return; + var checkObj = $$(node, consts.id.CHECK, setting); + if (!srcNode) srcNode = node; + + var hasDisable = false; + var children = data.nodeChildren(setting, node); + if (children) { + for (var i = 0, l = children.length; i < l; i++) { + var sNode = children[i]; + view.setSonNodeCheckBox(setting, sNode, value, srcNode); + if (sNode.chkDisabled === true) hasDisable = true; + } + } + + if (node != data.getRoot(setting) && node.chkDisabled !== true) { + if (hasDisable && node.nocheck !== true) { + data.makeChkFlag(setting, node); + } + if (node.nocheck !== true && node.chkDisabled !== true) { + data.nodeChecked(setting, node, value); + if (!hasDisable) node.check_Child_State = (children && children.length > 0) ? (value ? 2 : 0) : -1; + } else { + node.check_Child_State = -1; + } + view.setChkClass(setting, checkObj, node); + if (setting.check.autoCheckTrigger && node != srcNode && node.nocheck !== true && node.chkDisabled !== true) { + setting.treeObj.trigger(consts.event.CHECK, [null, setting.treeId, node]); + } + } + + } + }, + + _z = { + tools: _tools, + view: _view, + event: _event, + data: _data + }; + $.extend(true, $.fn.zTree.consts, _consts); + $.extend(true, $.fn.zTree._z, _z); + + var zt = $.fn.zTree, + tools = zt._z.tools, + consts = zt.consts, + view = zt._z.view, + data = zt._z.data, + event = zt._z.event, + $$ = tools.$; + + data.nodeChecked = function (setting, node, newChecked) { + if (!node) { + return false; + } + var key = setting.data.key.checked; + if (typeof newChecked !== 'undefined') { + if (typeof newChecked === "string") { + newChecked = tools.eqs(newChecked, "true"); + } + newChecked = !!newChecked; + node[key] = newChecked; + } else if (typeof node[key] == "string"){ + node[key] = tools.eqs(node[key], "true"); + } else { + node[key] = !!node[key]; + } + return node[key]; + }; + + data.exSetting(_setting); + data.addInitBind(_bindEvent); + data.addInitUnBind(_unbindEvent); + data.addInitCache(_initCache); + data.addInitNode(_initNode); + data.addInitProxy(_eventProxy, true); + data.addInitRoot(_initRoot); + data.addBeforeA(_beforeA); + data.addZTreeTools(_zTreeTools); + + var _createNodes = view.createNodes; + view.createNodes = function (setting, level, nodes, parentNode, index) { + if (_createNodes) _createNodes.apply(view, arguments); + if (!nodes) return; + view.repairParentChkClassWithSelf(setting, parentNode); + } + var _removeNode = view.removeNode; + view.removeNode = function (setting, node) { + var parentNode = node.getParentNode(); + if (_removeNode) _removeNode.apply(view, arguments); + if (!node || !parentNode) return; + view.repairChkClass(setting, parentNode); + view.repairParentChkClass(setting, parentNode); + } + + var _appendNodes = view.appendNodes; + view.appendNodes = function (setting, level, nodes, parentNode, index, initFlag, openFlag) { + var html = ""; + if (_appendNodes) { + html = _appendNodes.apply(view, arguments); + } + if (parentNode) { + data.makeChkFlag(setting, parentNode); + } + return html; + } +})(jQuery); \ No newline at end of file diff --git a/public/self/ztree/js/jquery.ztree.exedit.js b/public/self/ztree/js/jquery.ztree.exedit.js new file mode 100644 index 000000000..cfa83baf2 --- /dev/null +++ b/public/self/ztree/js/jquery.ztree.exedit.js @@ -0,0 +1,1207 @@ +/* + * JQuery zTree exedit + * v3.5.48 + * http://treejs.cn/ + * + * Copyright (c) 2010 Hunter.z + * + * Licensed same as jquery - MIT License + * http://www.opensource.org/licenses/mit-license.php + * + * Date: 2020-11-21 + */ + +(function ($) { + //default consts of exedit + var _consts = { + event: { + DRAG: "ztree_drag", + DROP: "ztree_drop", + RENAME: "ztree_rename", + DRAGMOVE: "ztree_dragmove" + }, + id: { + EDIT: "_edit", + INPUT: "_input", + REMOVE: "_remove" + }, + move: { + TYPE_INNER: "inner", + TYPE_PREV: "prev", + TYPE_NEXT: "next" + }, + node: { + CURSELECTED_EDIT: "curSelectedNode_Edit", + TMPTARGET_TREE: "tmpTargetzTree", + TMPTARGET_NODE: "tmpTargetNode" + } + }, + //default setting of exedit + _setting = { + edit: { + enable: false, + editNameSelectAll: false, + showRemoveBtn: true, + showRenameBtn: true, + removeTitle: "remove", + renameTitle: "rename", + drag: { + autoExpandTrigger: false, + isCopy: true, + isMove: true, + prev: true, + next: true, + inner: true, + minMoveSize: 5, + borderMax: 10, + borderMin: -5, + maxShowNodeNum: 5, + autoOpenTime: 500 + } + }, + view: { + addHoverDom: null, + removeHoverDom: null + }, + callback: { + beforeDrag: null, + beforeDragOpen: null, + beforeDrop: null, + beforeEditName: null, + beforeRename: null, + onDrag: null, + onDragMove: null, + onDrop: null, + onRename: null + } + }, + //default root of exedit + _initRoot = function (setting) { + var r = data.getRoot(setting), rs = data.getRoots(); + r.curEditNode = null; + r.curEditInput = null; + r.curHoverNode = null; + r.dragFlag = 0; + r.dragNodeShowBefore = []; + r.dragMaskList = new Array(); + rs.showHoverDom = true; + }, + //default cache of exedit + _initCache = function (treeId) { + }, + //default bind event of exedit + _bindEvent = function (setting) { + var o = setting.treeObj; + var c = consts.event; + o.bind(c.RENAME, function (event, treeId, treeNode, isCancel) { + tools.apply(setting.callback.onRename, [event, treeId, treeNode, isCancel]); + }); + + o.bind(c.DRAG, function (event, srcEvent, treeId, treeNodes) { + tools.apply(setting.callback.onDrag, [srcEvent, treeId, treeNodes]); + }); + + o.bind(c.DRAGMOVE, function (event, srcEvent, treeId, treeNodes) { + tools.apply(setting.callback.onDragMove, [srcEvent, treeId, treeNodes]); + }); + + o.bind(c.DROP, function (event, srcEvent, treeId, treeNodes, targetNode, moveType, isCopy) { + tools.apply(setting.callback.onDrop, [srcEvent, treeId, treeNodes, targetNode, moveType, isCopy]); + }); + }, + _unbindEvent = function (setting) { + var o = setting.treeObj; + var c = consts.event; + o.unbind(c.RENAME); + o.unbind(c.DRAG); + o.unbind(c.DRAGMOVE); + o.unbind(c.DROP); + }, + //default event proxy of exedit + _eventProxy = function (e) { + var target = e.target, + setting = data.getSetting(e.data.treeId), + relatedTarget = e.relatedTarget, + tId = "", node = null, + nodeEventType = "", treeEventType = "", + nodeEventCallback = null, treeEventCallback = null, + tmp = null; + + if (tools.eqs(e.type, "mouseover")) { + tmp = tools.getMDom(setting, target, [{tagName: "a", attrName: "treeNode" + consts.id.A}]); + if (tmp) { + tId = tools.getNodeMainDom(tmp).id; + nodeEventType = "hoverOverNode"; + } + } else if (tools.eqs(e.type, "mouseout")) { + tmp = tools.getMDom(setting, relatedTarget, [{tagName: "a", attrName: "treeNode" + consts.id.A}]); + if (!tmp) { + tId = "remove"; + nodeEventType = "hoverOutNode"; + } + } else if (tools.eqs(e.type, "mousedown")) { + tmp = tools.getMDom(setting, target, [{tagName: "a", attrName: "treeNode" + consts.id.A}]); + if (tmp) { + tId = tools.getNodeMainDom(tmp).id; + nodeEventType = "mousedownNode"; + } + } + if (tId.length > 0) { + node = data.getNodeCache(setting, tId); + switch (nodeEventType) { + case "mousedownNode" : + nodeEventCallback = _handler.onMousedownNode; + break; + case "hoverOverNode" : + nodeEventCallback = _handler.onHoverOverNode; + break; + case "hoverOutNode" : + nodeEventCallback = _handler.onHoverOutNode; + break; + } + } + var proxyResult = { + stop: false, + node: node, + nodeEventType: nodeEventType, + nodeEventCallback: nodeEventCallback, + treeEventType: treeEventType, + treeEventCallback: treeEventCallback + }; + return proxyResult + }, + //default init node of exedit + _initNode = function (setting, level, n, parentNode, isFirstNode, isLastNode, openFlag) { + if (!n) return; + n.isHover = false; + n.editNameFlag = false; + }, + //update zTreeObj, add method of edit + _zTreeTools = function (setting, zTreeTools) { + zTreeTools.cancelEditName = function (newName) { + var root = data.getRoot(this.setting); + if (!root.curEditNode) return; + view.cancelCurEditNode(this.setting, newName ? newName : null, true); + } + zTreeTools.copyNode = function (targetNode, node, moveType, isSilent) { + if (!node) return null; + var isParent = data.nodeIsParent(setting, targetNode); + if (targetNode && !isParent && this.setting.data.keep.leaf && moveType === consts.move.TYPE_INNER) return null; + var _this = this, + newNode = tools.clone(node); + if (!targetNode) { + targetNode = null; + moveType = consts.move.TYPE_INNER; + } + if (moveType == consts.move.TYPE_INNER) { + function copyCallback() { + view.addNodes(_this.setting, targetNode, -1, [newNode], isSilent); + } + + if (tools.canAsync(this.setting, targetNode)) { + view.asyncNode(this.setting, targetNode, isSilent, copyCallback); + } else { + copyCallback(); + } + } else { + view.addNodes(this.setting, targetNode.parentNode, -1, [newNode], isSilent); + view.moveNode(this.setting, targetNode, newNode, moveType, false, isSilent); + } + return newNode; + } + zTreeTools.editName = function (node) { + if (!node || !node.tId || node !== data.getNodeCache(this.setting, node.tId)) return; + if (node.parentTId) view.expandCollapseParentNode(this.setting, node.getParentNode(), true); + view.editNode(this.setting, node) + } + zTreeTools.moveNode = function (targetNode, node, moveType, isSilent) { + if (!node) return node; + var isParent = data.nodeIsParent(setting, targetNode); + if (targetNode && !isParent && this.setting.data.keep.leaf && moveType === consts.move.TYPE_INNER) { + return null; + } else if (targetNode && ((node.parentTId == targetNode.tId && moveType == consts.move.TYPE_INNER) || $$(node, this.setting).find("#" + targetNode.tId).length > 0)) { + return null; + } else if (!targetNode) { + targetNode = null; + } + var _this = this; + + function moveCallback() { + view.moveNode(_this.setting, targetNode, node, moveType, false, isSilent); + } + + if (tools.canAsync(this.setting, targetNode) && moveType === consts.move.TYPE_INNER) { + view.asyncNode(this.setting, targetNode, isSilent, moveCallback); + } else { + moveCallback(); + } + return node; + } + zTreeTools.setEditable = function (editable) { + this.setting.edit.enable = editable; + return this.refresh(); + } + }, + //method of operate data + _data = { + setSonNodeLevel: function (setting, parentNode, node) { + if (!node) return; + var children = data.nodeChildren(setting, node); + var oldLevel = node.level; + node.level = (parentNode) ? parentNode.level + 1 : 0; + view.repairNodeLevelClass(setting, node, oldLevel); + + if (!children) return; + for (var i = 0, l = children.length; i < l; i++) { + if (children[i]) data.setSonNodeLevel(setting, node, children[i]); + } + } + }, + //method of event proxy + _event = {}, + //method of event handler + _handler = { + onHoverOverNode: function (event, node) { + var setting = data.getSetting(event.data.treeId), + root = data.getRoot(setting); + if (root.curHoverNode != node) { + _handler.onHoverOutNode(event); + } + root.curHoverNode = node; + view.addHoverDom(setting, node); + }, + onHoverOutNode: function (event, node) { + var setting = data.getSetting(event.data.treeId), + root = data.getRoot(setting); + if (root.curHoverNode && !data.isSelectedNode(setting, root.curHoverNode)) { + view.removeTreeDom(setting, root.curHoverNode); + root.curHoverNode = null; + } + }, + onMousedownNode: function (eventMouseDown, _node) { + var i, l, + setting = data.getSetting(eventMouseDown.data.treeId), + root = data.getRoot(setting), roots = data.getRoots(); + //right click can't drag & drop + if (eventMouseDown.button == 2 || !setting.edit.enable || (!setting.edit.drag.isCopy && !setting.edit.drag.isMove)) return true; + + //input of edit node name can't drag & drop + var target = eventMouseDown.target, + _nodes = data.getRoot(setting).curSelectedList, + nodes = []; + if (!data.isSelectedNode(setting, _node)) { + nodes = [_node]; + } else { + for (i = 0, l = _nodes.length; i < l; i++) { + if (_nodes[i].editNameFlag && tools.eqs(target.tagName, "input") && target.getAttribute("treeNode" + consts.id.INPUT) !== null) { + return true; + } + nodes.push(_nodes[i]); + if (nodes[0].parentTId !== _nodes[i].parentTId) { + nodes = [_node]; + break; + } + } + } + + view.editNodeBlur = true; + view.cancelCurEditNode(setting); + + var doc = $(setting.treeObj.get(0).ownerDocument), + body = $(setting.treeObj.get(0).ownerDocument.body), curNode, tmpArrow, tmpTarget, + isOtherTree = false, + targetSetting = setting, + sourceSetting = setting, + preNode, nextNode, + preTmpTargetNodeId = null, + preTmpMoveType = null, + tmpTargetNodeId = null, + moveType = consts.move.TYPE_INNER, + mouseDownX = eventMouseDown.clientX, + mouseDownY = eventMouseDown.clientY, + startTime = (new Date()).getTime(); + + if (tools.uCanDo(setting)) { + doc.bind("mousemove", _docMouseMove); + } + + function _docMouseMove(event) { + //avoid start drag after click node + if (root.dragFlag == 0 && Math.abs(mouseDownX - event.clientX) < setting.edit.drag.minMoveSize + && Math.abs(mouseDownY - event.clientY) < setting.edit.drag.minMoveSize) { + return true; + } + var i, l, tmpNode, tmpDom, tmpNodes; + body.css("cursor", "pointer"); + + if (root.dragFlag == 0) { + if (tools.apply(setting.callback.beforeDrag, [setting.treeId, nodes], true) == false) { + _docMouseUp(event); + return true; + } + + for (i = 0, l = nodes.length; i < l; i++) { + if (i == 0) { + root.dragNodeShowBefore = []; + } + tmpNode = nodes[i]; + if (data.nodeIsParent(setting, tmpNode) && tmpNode.open) { + view.expandCollapseNode(setting, tmpNode, !tmpNode.open); + root.dragNodeShowBefore[tmpNode.tId] = true; + } else { + root.dragNodeShowBefore[tmpNode.tId] = false; + } + } + + root.dragFlag = 1; + roots.showHoverDom = false; + tools.showIfameMask(setting, true); + + //sort + var isOrder = true, lastIndex = -1; + if (nodes.length > 1) { + var pNodes = nodes[0].parentTId ? data.nodeChildren(setting, nodes[0].getParentNode()) : data.getNodes(setting); + tmpNodes = []; + for (i = 0, l = pNodes.length; i < l; i++) { + if (root.dragNodeShowBefore[pNodes[i].tId] !== undefined) { + if (isOrder && lastIndex > -1 && (lastIndex + 1) !== i) { + isOrder = false; + } + tmpNodes.push(pNodes[i]); + lastIndex = i; + } + if (nodes.length === tmpNodes.length) { + nodes = tmpNodes; + break; + } + } + } + if (isOrder) { + preNode = nodes[0].getPreNode(); + nextNode = nodes[nodes.length - 1].getNextNode(); + } + + //set node in selected + curNode = $$("
        ", setting); + for (i = 0, l = nodes.length; i < l; i++) { + tmpNode = nodes[i]; + tmpNode.editNameFlag = false; + view.selectNode(setting, tmpNode, i > 0); + view.removeTreeDom(setting, tmpNode); + + if (i > setting.edit.drag.maxShowNodeNum - 1) { + continue; + } + + tmpDom = $$("
      • ", setting); + tmpDom.append($$(tmpNode, consts.id.A, setting).clone()); + tmpDom.css("padding", "0"); + tmpDom.children("#" + tmpNode.tId + consts.id.A).removeClass(consts.node.CURSELECTED); + curNode.append(tmpDom); + if (i == setting.edit.drag.maxShowNodeNum - 1) { + tmpDom = $$("
      • ...
      • ", setting); + curNode.append(tmpDom); + } + } + curNode.attr("id", nodes[0].tId + consts.id.UL + "_tmp"); + curNode.addClass(setting.treeObj.attr("class")); + curNode.appendTo(body); + + tmpArrow = $$("", setting); + tmpArrow.attr("id", "zTreeMove_arrow_tmp"); + tmpArrow.appendTo(body); + + setting.treeObj.trigger(consts.event.DRAG, [event, setting.treeId, nodes]); + } + + if (root.dragFlag == 1) { + if (tmpTarget && tmpArrow.attr("id") == event.target.id && tmpTargetNodeId && (event.clientX + doc.scrollLeft() + 2) > ($("#" + tmpTargetNodeId + consts.id.A, tmpTarget).offset().left)) { + var xT = $("#" + tmpTargetNodeId + consts.id.A, tmpTarget); + event.target = (xT.length > 0) ? xT.get(0) : event.target; + } else if (tmpTarget) { + tmpTarget.removeClass(consts.node.TMPTARGET_TREE); + if (tmpTargetNodeId) $("#" + tmpTargetNodeId + consts.id.A, tmpTarget).removeClass(consts.node.TMPTARGET_NODE + "_" + consts.move.TYPE_PREV) + .removeClass(consts.node.TMPTARGET_NODE + "_" + _consts.move.TYPE_NEXT).removeClass(consts.node.TMPTARGET_NODE + "_" + _consts.move.TYPE_INNER); + } + tmpTarget = null; + tmpTargetNodeId = null; + + //judge drag & drop in multi ztree + isOtherTree = false; + targetSetting = setting; + var settings = data.getSettings(); + for (var s in settings) { + if (settings[s].treeId && settings[s].edit.enable && settings[s].treeId != setting.treeId + && (event.target.id == settings[s].treeId || $(event.target).parents("#" + settings[s].treeId).length > 0)) { + isOtherTree = true; + targetSetting = settings[s]; + } + } + + var docScrollTop = doc.scrollTop(), + docScrollLeft = doc.scrollLeft(), + treeOffset = targetSetting.treeObj.offset(), + scrollHeight = targetSetting.treeObj.get(0).scrollHeight, + scrollWidth = targetSetting.treeObj.get(0).scrollWidth, + dTop = (event.clientY + docScrollTop - treeOffset.top), + dBottom = (targetSetting.treeObj.height() + treeOffset.top - event.clientY - docScrollTop), + dLeft = (event.clientX + docScrollLeft - treeOffset.left), + dRight = (targetSetting.treeObj.width() + treeOffset.left - event.clientX - docScrollLeft), + isTop = (dTop < setting.edit.drag.borderMax && dTop > setting.edit.drag.borderMin), + isBottom = (dBottom < setting.edit.drag.borderMax && dBottom > setting.edit.drag.borderMin), + isLeft = (dLeft < setting.edit.drag.borderMax && dLeft > setting.edit.drag.borderMin), + isRight = (dRight < setting.edit.drag.borderMax && dRight > setting.edit.drag.borderMin), + isTreeInner = dTop > setting.edit.drag.borderMin && dBottom > setting.edit.drag.borderMin && dLeft > setting.edit.drag.borderMin && dRight > setting.edit.drag.borderMin, + isTreeTop = (isTop && targetSetting.treeObj.scrollTop() <= 0), + isTreeBottom = (isBottom && (targetSetting.treeObj.scrollTop() + targetSetting.treeObj.height() + 10) >= scrollHeight), + isTreeLeft = (isLeft && targetSetting.treeObj.scrollLeft() <= 0), + isTreeRight = (isRight && (targetSetting.treeObj.scrollLeft() + targetSetting.treeObj.width() + 10) >= scrollWidth); + + if (event.target && tools.isChildOrSelf(event.target, targetSetting.treeId)) { + //get node
      • dom + var targetObj = event.target; + while (targetObj && targetObj.tagName && !tools.eqs(targetObj.tagName, "li") && targetObj.id != targetSetting.treeId) { + targetObj = targetObj.parentNode; + } + + var canMove = true; + //don't move to self or children of self + for (i = 0, l = nodes.length; i < l; i++) { + tmpNode = nodes[i]; + if (targetObj.id === tmpNode.tId) { + canMove = false; + break; + } else if ($$(tmpNode, setting).find("#" + targetObj.id).length > 0) { + canMove = false; + break; + } + } + if (canMove && event.target && tools.isChildOrSelf(event.target, targetObj.id + consts.id.A)) { + tmpTarget = $(targetObj); + tmpTargetNodeId = targetObj.id; + } + } + + //the mouse must be in zTree + tmpNode = nodes[0]; + if (isTreeInner && tools.isChildOrSelf(event.target, targetSetting.treeId)) { + //judge mouse move in root of ztree + if (!tmpTarget && (event.target.id == targetSetting.treeId || isTreeTop || isTreeBottom || isTreeLeft || isTreeRight) && (isOtherTree || (!isOtherTree && tmpNode.parentTId))) { + tmpTarget = targetSetting.treeObj; + } + //auto scroll top + if (isTop) { + targetSetting.treeObj.scrollTop(targetSetting.treeObj.scrollTop() - 10); + } else if (isBottom) { + targetSetting.treeObj.scrollTop(targetSetting.treeObj.scrollTop() + 10); + } + if (isLeft) { + targetSetting.treeObj.scrollLeft(targetSetting.treeObj.scrollLeft() - 10); + } else if (isRight) { + targetSetting.treeObj.scrollLeft(targetSetting.treeObj.scrollLeft() + 10); + } + //auto scroll left + if (tmpTarget && tmpTarget != targetSetting.treeObj && tmpTarget.offset().left < targetSetting.treeObj.offset().left) { + targetSetting.treeObj.scrollLeft(targetSetting.treeObj.scrollLeft() + tmpTarget.offset().left - targetSetting.treeObj.offset().left); + } + } + + curNode.css({ + "top": (event.clientY + docScrollTop + 3) + "px", + "left": (event.clientX + docScrollLeft + 3) + "px" + }); + + var dX = 0; + var dY = 0; + if (tmpTarget && tmpTarget.attr("id") != targetSetting.treeId) { + var tmpTargetNode = tmpTargetNodeId == null ? null : data.getNodeCache(targetSetting, tmpTargetNodeId), + isCopy = ((event.ctrlKey || event.metaKey) && setting.edit.drag.isMove && setting.edit.drag.isCopy) || (!setting.edit.drag.isMove && setting.edit.drag.isCopy), + isPrev = !!(preNode && tmpTargetNodeId === preNode.tId), + isNext = !!(nextNode && tmpTargetNodeId === nextNode.tId), + isInner = (tmpNode.parentTId && tmpNode.parentTId == tmpTargetNodeId), + canPrev = (isCopy || !isNext) && tools.apply(targetSetting.edit.drag.prev, [targetSetting.treeId, nodes, tmpTargetNode], !!targetSetting.edit.drag.prev), + canNext = (isCopy || !isPrev) && tools.apply(targetSetting.edit.drag.next, [targetSetting.treeId, nodes, tmpTargetNode], !!targetSetting.edit.drag.next), + canInner = (isCopy || !isInner) && !(targetSetting.data.keep.leaf && !data.nodeIsParent(setting, tmpTargetNode)) && tools.apply(targetSetting.edit.drag.inner, [targetSetting.treeId, nodes, tmpTargetNode], !!targetSetting.edit.drag.inner); + + function clearMove() { + tmpTarget = null; + tmpTargetNodeId = ""; + moveType = consts.move.TYPE_INNER; + tmpArrow.css({ + "display": "none" + }); + if (window.zTreeMoveTimer) { + clearTimeout(window.zTreeMoveTimer); + window.zTreeMoveTargetNodeTId = null + } + } + + if (!canPrev && !canNext && !canInner) { + clearMove(); + } else { + var tmpTargetA = $("#" + tmpTargetNodeId + consts.id.A, tmpTarget), + tmpNextA = tmpTargetNode.isLastNode ? null : $("#" + tmpTargetNode.getNextNode().tId + consts.id.A, tmpTarget.next()), + tmpTop = tmpTargetA.offset().top, + tmpLeft = tmpTargetA.offset().left, + prevPercent = canPrev ? (canInner ? 0.25 : (canNext ? 0.5 : 1)) : -1, + nextPercent = canNext ? (canInner ? 0.75 : (canPrev ? 0.5 : 0)) : -1, + dY_percent = (event.clientY + docScrollTop - tmpTop) / tmpTargetA.height(); + + if ((prevPercent == 1 || dY_percent <= prevPercent && dY_percent >= -.2) && canPrev) { + dX = 1 - tmpArrow.width(); + dY = tmpTop - tmpArrow.height() / 2; + moveType = consts.move.TYPE_PREV; + } else if ((nextPercent == 0 || dY_percent >= nextPercent && dY_percent <= 1.2) && canNext) { + dX = 1 - tmpArrow.width(); + dY = (tmpNextA == null || (data.nodeIsParent(setting, tmpTargetNode) && tmpTargetNode.open)) ? (tmpTop + tmpTargetA.height() - tmpArrow.height() / 2) : (tmpNextA.offset().top - tmpArrow.height() / 2); + moveType = consts.move.TYPE_NEXT; + } else if (canInner) { + dX = 5 - tmpArrow.width(); + dY = tmpTop; + moveType = consts.move.TYPE_INNER; + } else { + clearMove(); + } + + if (tmpTarget) { + tmpArrow.css({ + "display": "block", + "top": dY + "px", + "left": (tmpLeft + dX) + "px" + }); + tmpTargetA.addClass(consts.node.TMPTARGET_NODE + "_" + moveType); + + if (preTmpTargetNodeId != tmpTargetNodeId || preTmpMoveType != moveType) { + startTime = (new Date()).getTime(); + } + if (tmpTargetNode && data.nodeIsParent(setting, tmpTargetNode) && moveType == consts.move.TYPE_INNER) { + var startTimer = true; + if (window.zTreeMoveTimer && window.zTreeMoveTargetNodeTId !== tmpTargetNode.tId) { + clearTimeout(window.zTreeMoveTimer); + window.zTreeMoveTargetNodeTId = null; + } else if (window.zTreeMoveTimer && window.zTreeMoveTargetNodeTId === tmpTargetNode.tId) { + startTimer = false; + } + if (startTimer) { + window.zTreeMoveTimer = setTimeout(function () { + if (moveType != consts.move.TYPE_INNER) return; + if (tmpTargetNode && data.nodeIsParent(setting, tmpTargetNode) && !tmpTargetNode.open && (new Date()).getTime() - startTime > targetSetting.edit.drag.autoOpenTime + && tools.apply(targetSetting.callback.beforeDragOpen, [targetSetting.treeId, tmpTargetNode], true)) { + view.switchNode(targetSetting, tmpTargetNode); + if (targetSetting.edit.drag.autoExpandTrigger) { + targetSetting.treeObj.trigger(consts.event.EXPAND, [targetSetting.treeId, tmpTargetNode]); + } + } + }, targetSetting.edit.drag.autoOpenTime + 50); + window.zTreeMoveTargetNodeTId = tmpTargetNode.tId; + } + } + } + } + } else { + moveType = consts.move.TYPE_INNER; + if (tmpTarget && tools.apply(targetSetting.edit.drag.inner, [targetSetting.treeId, nodes, null], !!targetSetting.edit.drag.inner)) { + tmpTarget.addClass(consts.node.TMPTARGET_TREE); + } else { + tmpTarget = null; + } + tmpArrow.css({ + "display": "none" + }); + if (window.zTreeMoveTimer) { + clearTimeout(window.zTreeMoveTimer); + window.zTreeMoveTargetNodeTId = null; + } + } + preTmpTargetNodeId = tmpTargetNodeId; + preTmpMoveType = moveType; + + setting.treeObj.trigger(consts.event.DRAGMOVE, [event, setting.treeId, nodes]); + } + return false; + } + + doc.bind("mouseup", _docMouseUp); + + function _docMouseUp(event) { + if (window.zTreeMoveTimer) { + clearTimeout(window.zTreeMoveTimer); + window.zTreeMoveTargetNodeTId = null; + } + preTmpTargetNodeId = null; + preTmpMoveType = null; + doc.unbind("mousemove", _docMouseMove); + doc.unbind("mouseup", _docMouseUp); + doc.unbind("selectstart", _docSelect); + body.css("cursor", ""); + if (tmpTarget) { + tmpTarget.removeClass(consts.node.TMPTARGET_TREE); + if (tmpTargetNodeId) $("#" + tmpTargetNodeId + consts.id.A, tmpTarget).removeClass(consts.node.TMPTARGET_NODE + "_" + consts.move.TYPE_PREV) + .removeClass(consts.node.TMPTARGET_NODE + "_" + _consts.move.TYPE_NEXT).removeClass(consts.node.TMPTARGET_NODE + "_" + _consts.move.TYPE_INNER); + } + tools.showIfameMask(setting, false); + + roots.showHoverDom = true; + if (root.dragFlag == 0) return; + root.dragFlag = 0; + + var i, l, tmpNode; + for (i = 0, l = nodes.length; i < l; i++) { + tmpNode = nodes[i]; + if (data.nodeIsParent(setting, tmpNode) && root.dragNodeShowBefore[tmpNode.tId] && !tmpNode.open) { + view.expandCollapseNode(setting, tmpNode, !tmpNode.open); + delete root.dragNodeShowBefore[tmpNode.tId]; + } + } + + if (curNode) curNode.remove(); + if (tmpArrow) tmpArrow.remove(); + + var isCopy = ((event.ctrlKey || event.metaKey) && setting.edit.drag.isMove && setting.edit.drag.isCopy) || (!setting.edit.drag.isMove && setting.edit.drag.isCopy); + if (!isCopy && tmpTarget && tmpTargetNodeId && nodes[0].parentTId && tmpTargetNodeId == nodes[0].parentTId && moveType == consts.move.TYPE_INNER) { + tmpTarget = null; + } + if (tmpTarget) { + var dragTargetNode = tmpTargetNodeId == null ? null : data.getNodeCache(targetSetting, tmpTargetNodeId); + if (tools.apply(setting.callback.beforeDrop, [targetSetting.treeId, nodes, dragTargetNode, moveType, isCopy], true) == false) { + view.selectNodes(sourceSetting, nodes); + return; + } + var newNodes = isCopy ? tools.clone(nodes) : nodes; + + function dropCallback() { + if (isOtherTree) { + if (!isCopy) { + for (var i = 0, l = nodes.length; i < l; i++) { + view.removeNode(setting, nodes[i]); + } + } + if (moveType == consts.move.TYPE_INNER) { + view.addNodes(targetSetting, dragTargetNode, -1, newNodes); + } else { + view.addNodes(targetSetting, dragTargetNode.getParentNode(), moveType == consts.move.TYPE_PREV ? dragTargetNode.getIndex() : dragTargetNode.getIndex() + 1, newNodes); + } + } else { + if (isCopy && moveType == consts.move.TYPE_INNER) { + view.addNodes(targetSetting, dragTargetNode, -1, newNodes); + } else if (isCopy) { + view.addNodes(targetSetting, dragTargetNode.getParentNode(), moveType == consts.move.TYPE_PREV ? dragTargetNode.getIndex() : dragTargetNode.getIndex() + 1, newNodes); + } else { + if (moveType != consts.move.TYPE_NEXT) { + for (i = 0, l = newNodes.length; i < l; i++) { + view.moveNode(targetSetting, dragTargetNode, newNodes[i], moveType, false); + } + } else { + for (i = -1, l = newNodes.length - 1; i < l; l--) { + view.moveNode(targetSetting, dragTargetNode, newNodes[l], moveType, false); + } + } + } + } + view.selectNodes(targetSetting, newNodes); + + var a = $$(newNodes[0], setting).get(0); + view.scrollIntoView(setting, a); + + setting.treeObj.trigger(consts.event.DROP, [event, targetSetting.treeId, newNodes, dragTargetNode, moveType, isCopy]); + } + + if (moveType == consts.move.TYPE_INNER && tools.canAsync(targetSetting, dragTargetNode)) { + view.asyncNode(targetSetting, dragTargetNode, false, dropCallback); + } else { + dropCallback(); + } + + } else { + view.selectNodes(sourceSetting, nodes); + setting.treeObj.trigger(consts.event.DROP, [event, setting.treeId, nodes, null, null, null]); + } + } + + doc.bind("selectstart", _docSelect); + + function _docSelect() { + return false; + } + + // 2018-03-30 FireFox has fixed this issue. + //Avoid FireFox's Bug + //If zTree Div CSS set 'overflow', so drag node outside of zTree, and event.target is error. + // if(eventMouseDown.preventDefault) { + // eventMouseDown.preventDefault(); + // } + return true; + } + }, + //method of tools for zTree + _tools = { + getAbs: function (obj) { + var oRect = obj.getBoundingClientRect(), + scrollTop = document.body.scrollTop + document.documentElement.scrollTop, + scrollLeft = document.body.scrollLeft + document.documentElement.scrollLeft; + return [oRect.left + scrollLeft, oRect.top + scrollTop]; + }, + inputFocus: function (inputObj) { + if (inputObj.get(0)) { + inputObj.focus(); + tools.setCursorPosition(inputObj.get(0), inputObj.val().length); + } + }, + inputSelect: function (inputObj) { + if (inputObj.get(0)) { + inputObj.focus(); + inputObj.select(); + } + }, + setCursorPosition: function (obj, pos) { + if (obj.setSelectionRange) { + obj.focus(); + obj.setSelectionRange(pos, pos); + } else if (obj.createTextRange) { + var range = obj.createTextRange(); + range.collapse(true); + range.moveEnd('character', pos); + range.moveStart('character', pos); + range.select(); + } + }, + showIfameMask: function (setting, showSign) { + var root = data.getRoot(setting); + //clear full mask + while (root.dragMaskList.length > 0) { + root.dragMaskList[0].remove(); + root.dragMaskList.shift(); + } + if (showSign) { + //show mask + var iframeList = $$("iframe", setting); + for (var i = 0, l = iframeList.length; i < l; i++) { + var obj = iframeList.get(i), + r = tools.getAbs(obj), + dragMask = $$("
        ", setting); + dragMask.appendTo($$("body", setting)); + root.dragMaskList.push(dragMask); + } + } + } + }, + //method of operate ztree dom + _view = { + addEditBtn: function (setting, node) { + if (node.editNameFlag || $$(node, consts.id.EDIT, setting).length > 0) { + return; + } + if (!tools.apply(setting.edit.showRenameBtn, [setting.treeId, node], setting.edit.showRenameBtn)) { + return; + } + var aObj = $$(node, consts.id.A, setting), + editStr = ""; + aObj.append(editStr); + + $$(node, consts.id.EDIT, setting).bind('click', + function () { + if (!tools.uCanDo(setting) || tools.apply(setting.callback.beforeEditName, [setting.treeId, node], true) == false) return false; + view.editNode(setting, node); + return false; + } + ).show(); + }, + addRemoveBtn: function (setting, node) { + if (node.editNameFlag || $$(node, consts.id.REMOVE, setting).length > 0) { + return; + } + if (!tools.apply(setting.edit.showRemoveBtn, [setting.treeId, node], setting.edit.showRemoveBtn)) { + return; + } + var aObj = $$(node, consts.id.A, setting), + removeStr = ""; + aObj.append(removeStr); + + $$(node, consts.id.REMOVE, setting).bind('click', + function () { + if (!tools.uCanDo(setting) || tools.apply(setting.callback.beforeRemove, [setting.treeId, node], true) == false) return false; + view.removeNode(setting, node); + setting.treeObj.trigger(consts.event.REMOVE, [setting.treeId, node]); + return false; + } + ).bind('mousedown', + function (eventMouseDown) { + return true; + } + ).show(); + }, + addHoverDom: function (setting, node) { + if (data.getRoots().showHoverDom) { + node.isHover = true; + if (setting.edit.enable) { + view.addEditBtn(setting, node); + view.addRemoveBtn(setting, node); + } + tools.apply(setting.view.addHoverDom, [setting.treeId, node]); + } + }, + cancelCurEditNode: function (setting, forceName, isCancel) { + var root = data.getRoot(setting), + node = root.curEditNode; + + if (node) { + var inputObj = root.curEditInput, + newName = forceName ? forceName : (isCancel ? data.nodeName(setting, node) : inputObj.val()); + if (tools.apply(setting.callback.beforeRename, [setting.treeId, node, newName, isCancel], true) === false) { + return false; + } + data.nodeName(setting, node, newName); + var aObj = $$(node, consts.id.A, setting); + aObj.removeClass(consts.node.CURSELECTED_EDIT); + inputObj.unbind(); + view.setNodeName(setting, node); + node.editNameFlag = false; + root.curEditNode = null; + root.curEditInput = null; + view.selectNode(setting, node, false); + setting.treeObj.trigger(consts.event.RENAME, [setting.treeId, node, isCancel]); + } + root.noSelection = true; + return true; + }, + editNode: function (setting, node) { + var root = data.getRoot(setting); + view.editNodeBlur = false; + if (data.isSelectedNode(setting, node) && root.curEditNode == node && node.editNameFlag) { + setTimeout(function () { + tools.inputFocus(root.curEditInput); + }, 0); + return; + } + node.editNameFlag = true; + view.removeTreeDom(setting, node); + view.cancelCurEditNode(setting); + view.selectNode(setting, node, false); + $$(node, consts.id.SPAN, setting).html(""); + var inputObj = $$(node, consts.id.INPUT, setting); + inputObj.attr("value", data.nodeName(setting, node)); + if (setting.edit.editNameSelectAll) { + tools.inputSelect(inputObj); + } else { + tools.inputFocus(inputObj); + } + + inputObj.bind('blur', function (event) { + if (!view.editNodeBlur) { + view.cancelCurEditNode(setting); + } + }).bind('keydown', function (event) { + if (event.keyCode == "13") { + view.editNodeBlur = true; + view.cancelCurEditNode(setting); + } else if (event.keyCode == "27") { + view.cancelCurEditNode(setting, null, true); + } + }).bind('click', function (event) { + return false; + }).bind('dblclick', function (event) { + return false; + }); + + $$(node, consts.id.A, setting).addClass(consts.node.CURSELECTED_EDIT); + root.curEditInput = inputObj; + root.noSelection = false; + root.curEditNode = node; + }, + moveNode: function (setting, targetNode, node, moveType, animateFlag, isSilent) { + var root = data.getRoot(setting); + if (targetNode == node) return; + if (setting.data.keep.leaf && targetNode && !data.nodeIsParent(setting, targetNode) && moveType == consts.move.TYPE_INNER) return; + var oldParentNode = (node.parentTId ? node.getParentNode() : root), + targetNodeIsRoot = (targetNode === null || targetNode == root); + if (targetNodeIsRoot && targetNode === null) targetNode = root; + if (targetNodeIsRoot) moveType = consts.move.TYPE_INNER; + var targetParentNode = (targetNode.parentTId ? targetNode.getParentNode() : root); + + if (moveType != consts.move.TYPE_PREV && moveType != consts.move.TYPE_NEXT) { + moveType = consts.move.TYPE_INNER; + } + + if (moveType == consts.move.TYPE_INNER) { + if (targetNodeIsRoot) { + //parentTId of root node is null + node.parentTId = null; + } else { + if (!data.nodeIsParent(setting, targetNode)) { + data.nodeIsParent(setting, targetNode, true); + targetNode.open = !!targetNode.open; + view.setNodeLineIcos(setting, targetNode); + } + node.parentTId = targetNode.tId; + } + } + + //move node Dom + var targetObj, target_ulObj; + if (targetNodeIsRoot) { + targetObj = setting.treeObj; + target_ulObj = targetObj; + } else { + if (!isSilent && moveType == consts.move.TYPE_INNER) { + view.expandCollapseNode(setting, targetNode, true, false); + } else if (!isSilent) { + view.expandCollapseNode(setting, targetNode.getParentNode(), true, false); + } + targetObj = $$(targetNode, setting); + target_ulObj = $$(targetNode, consts.id.UL, setting); + if (!!targetObj.get(0) && !target_ulObj.get(0)) { + var ulstr = []; + view.makeUlHtml(setting, targetNode, ulstr, ''); + targetObj.append(ulstr.join('')); + } + target_ulObj = $$(targetNode, consts.id.UL, setting); + } + var nodeDom = $$(node, setting); + if (!nodeDom.get(0)) { + nodeDom = view.appendNodes(setting, node.level, [node], null, -1, false, true).join(''); + } else if (!targetObj.get(0)) { + nodeDom.remove(); + } + if (target_ulObj.get(0) && moveType == consts.move.TYPE_INNER) { + target_ulObj.append(nodeDom); + } else if (targetObj.get(0) && moveType == consts.move.TYPE_PREV) { + targetObj.before(nodeDom); + } else if (targetObj.get(0) && moveType == consts.move.TYPE_NEXT) { + targetObj.after(nodeDom); + } + + //repair the data after move + var i, l, + tmpSrcIndex = -1, + tmpTargetIndex = 0, + oldNeighbor = null, + newNeighbor = null, + oldLevel = node.level; + var oldChildren = data.nodeChildren(setting, oldParentNode); + var targetParentChildren = data.nodeChildren(setting, targetParentNode); + var targetChildren = data.nodeChildren(setting, targetNode); + if (node.isFirstNode) { + tmpSrcIndex = 0; + if (oldChildren.length > 1) { + oldNeighbor = oldChildren[1]; + oldNeighbor.isFirstNode = true; + } + } else if (node.isLastNode) { + tmpSrcIndex = oldChildren.length - 1; + oldNeighbor = oldChildren[tmpSrcIndex - 1]; + oldNeighbor.isLastNode = true; + } else { + for (i = 0, l = oldChildren.length; i < l; i++) { + if (oldChildren[i].tId == node.tId) { + tmpSrcIndex = i; + break; + } + } + } + if (tmpSrcIndex >= 0) { + oldChildren.splice(tmpSrcIndex, 1); + } + if (moveType != consts.move.TYPE_INNER) { + for (i = 0, l = targetParentChildren.length; i < l; i++) { + if (targetParentChildren[i].tId == targetNode.tId) tmpTargetIndex = i; + } + } + if (moveType == consts.move.TYPE_INNER) { + if (!targetChildren) { + targetChildren = data.nodeChildren(setting, targetNode, []); + } + if (targetChildren.length > 0) { + newNeighbor = targetChildren[targetChildren.length - 1]; + newNeighbor.isLastNode = false; + } + targetChildren.splice(targetChildren.length, 0, node); + node.isLastNode = true; + node.isFirstNode = (targetChildren.length == 1); + } else if (targetNode.isFirstNode && moveType == consts.move.TYPE_PREV) { + targetParentChildren.splice(tmpTargetIndex, 0, node); + newNeighbor = targetNode; + newNeighbor.isFirstNode = false; + node.parentTId = targetNode.parentTId; + node.isFirstNode = true; + node.isLastNode = false; + + } else if (targetNode.isLastNode && moveType == consts.move.TYPE_NEXT) { + targetParentChildren.splice(tmpTargetIndex + 1, 0, node); + newNeighbor = targetNode; + newNeighbor.isLastNode = false; + node.parentTId = targetNode.parentTId; + node.isFirstNode = false; + node.isLastNode = true; + + } else { + if (moveType == consts.move.TYPE_PREV) { + targetParentChildren.splice(tmpTargetIndex, 0, node); + } else { + targetParentChildren.splice(tmpTargetIndex + 1, 0, node); + } + node.parentTId = targetNode.parentTId; + node.isFirstNode = false; + node.isLastNode = false; + } + data.fixPIdKeyValue(setting, node); + data.setSonNodeLevel(setting, node.getParentNode(), node); + + //repair node what been moved + view.setNodeLineIcos(setting, node); + view.repairNodeLevelClass(setting, node, oldLevel); + + //repair node's old parentNode dom + if (!setting.data.keep.parent && oldChildren.length < 1) { + //old parentNode has no child nodes + data.nodeIsParent(setting, oldParentNode, false); + oldParentNode.open = false; + var tmp_ulObj = $$(oldParentNode, consts.id.UL, setting), + tmp_switchObj = $$(oldParentNode, consts.id.SWITCH, setting), + tmp_icoObj = $$(oldParentNode, consts.id.ICON, setting); + view.replaceSwitchClass(oldParentNode, tmp_switchObj, consts.folder.DOCU); + view.replaceIcoClass(oldParentNode, tmp_icoObj, consts.folder.DOCU); + tmp_ulObj.css("display", "none"); + + } else if (oldNeighbor) { + //old neighbor node + view.setNodeLineIcos(setting, oldNeighbor); + } + + //new neighbor node + if (newNeighbor) { + view.setNodeLineIcos(setting, newNeighbor); + } + + //repair checkbox / radio + if (!!setting.check && setting.check.enable && view.repairChkClass) { + view.repairChkClass(setting, oldParentNode); + view.repairParentChkClassWithSelf(setting, oldParentNode); + if (oldParentNode != node.parent) + view.repairParentChkClassWithSelf(setting, node); + } + + //expand parents after move + if (!isSilent) { + view.expandCollapseParentNode(setting, node.getParentNode(), true, animateFlag); + } + }, + removeEditBtn: function (setting, node) { + $$(node, consts.id.EDIT, setting).unbind().remove(); + }, + removeRemoveBtn: function (setting, node) { + $$(node, consts.id.REMOVE, setting).unbind().remove(); + }, + removeTreeDom: function (setting, node) { + node.isHover = false; + view.removeEditBtn(setting, node); + view.removeRemoveBtn(setting, node); + tools.apply(setting.view.removeHoverDom, [setting.treeId, node]); + }, + repairNodeLevelClass: function (setting, node, oldLevel) { + if (oldLevel === node.level) return; + var liObj = $$(node, setting), + aObj = $$(node, consts.id.A, setting), + ulObj = $$(node, consts.id.UL, setting), + oldClass = consts.className.LEVEL + oldLevel, + newClass = consts.className.LEVEL + node.level; + liObj.removeClass(oldClass); + liObj.addClass(newClass); + aObj.removeClass(oldClass); + aObj.addClass(newClass); + ulObj.removeClass(oldClass); + ulObj.addClass(newClass); + }, + selectNodes: function (setting, nodes) { + for (var i = 0, l = nodes.length; i < l; i++) { + view.selectNode(setting, nodes[i], i > 0); + } + } + }, + + _z = { + tools: _tools, + view: _view, + event: _event, + data: _data + }; + $.extend(true, $.fn.zTree.consts, _consts); + $.extend(true, $.fn.zTree._z, _z); + + var zt = $.fn.zTree, + tools = zt._z.tools, + consts = zt.consts, + view = zt._z.view, + data = zt._z.data, + event = zt._z.event, + $$ = tools.$; + + data.exSetting(_setting); + data.addInitBind(_bindEvent); + data.addInitUnBind(_unbindEvent); + data.addInitCache(_initCache); + data.addInitNode(_initNode); + data.addInitProxy(_eventProxy); + data.addInitRoot(_initRoot); + data.addZTreeTools(_zTreeTools); + + var _cancelPreSelectedNode = view.cancelPreSelectedNode; + view.cancelPreSelectedNode = function (setting, node) { + var list = data.getRoot(setting).curSelectedList; + for (var i = 0, j = list.length; i < j; i++) { + if (!node || node === list[i]) { + view.removeTreeDom(setting, list[i]); + if (node) break; + } + } + if (_cancelPreSelectedNode) _cancelPreSelectedNode.apply(view, arguments); + } + + var _createNodes = view.createNodes; + view.createNodes = function (setting, level, nodes, parentNode, index) { + if (_createNodes) { + _createNodes.apply(view, arguments); + } + if (!nodes) return; + if (view.repairParentChkClassWithSelf) { + view.repairParentChkClassWithSelf(setting, parentNode); + } + } + + var _makeNodeUrl = view.makeNodeUrl; + view.makeNodeUrl = function (setting, node) { + return setting.edit.enable ? null : (_makeNodeUrl.apply(view, arguments)); + } + + var _removeNode = view.removeNode; + view.removeNode = function (setting, node) { + var root = data.getRoot(setting); + if (root.curEditNode === node) root.curEditNode = null; + if (_removeNode) { + _removeNode.apply(view, arguments); + } + } + + var _selectNode = view.selectNode; + view.selectNode = function (setting, node, addFlag) { + var root = data.getRoot(setting); + if (data.isSelectedNode(setting, node) && root.curEditNode == node && node.editNameFlag) { + return false; + } + if (_selectNode) _selectNode.apply(view, arguments); + view.addHoverDom(setting, node); + return true; + } + + var _uCanDo = tools.uCanDo; + tools.uCanDo = function (setting, e) { + var root = data.getRoot(setting); + if (e && (tools.eqs(e.type, "mouseover") || tools.eqs(e.type, "mouseout") || tools.eqs(e.type, "mousedown") || tools.eqs(e.type, "mouseup"))) { + return true; + } + if (root.curEditNode) { + view.editNodeBlur = false; + root.curEditInput.focus(); + } + return (!root.curEditNode) && (_uCanDo ? _uCanDo.apply(view, arguments) : true); + } +})(jQuery); \ No newline at end of file diff --git a/public/self/ztree/js/jquery.ztree.exhide.js b/public/self/ztree/js/jquery.ztree.exhide.js new file mode 100644 index 000000000..8cfe174bb --- /dev/null +++ b/public/self/ztree/js/jquery.ztree.exhide.js @@ -0,0 +1,405 @@ +/* + * JQuery zTree exHideNodes + * v3.5.48 + * http://treejs.cn/ + * + * Copyright (c) 2010 Hunter.z + * + * Licensed same as jquery - MIT License + * http://www.opensource.org/licenses/mit-license.php + * + * Date: 2020-11-21 + */ + +(function ($) { + var _setting = { + data: { + key: { + isHidden: "isHidden" + } + } + }; + //default init node of exLib + var _initNode = function (setting, level, n, parentNode, isFirstNode, isLastNode, openFlag) { + var isHidden = data.isHidden(setting, n); + data.isHidden(setting, n, isHidden); + data.initHideForExCheck(setting, n); + }, + //add dom for check + _beforeA = function (setting, node, html) { + }, + //update zTreeObj, add method of exLib + _zTreeTools = function (setting, zTreeTools) { + zTreeTools.showNodes = function (nodes, options) { + view.showNodes(setting, nodes, options); + } + zTreeTools.showNode = function (node, options) { + if (!node) { + return; + } + view.showNodes(setting, [node], options); + } + zTreeTools.hideNodes = function (nodes, options) { + view.hideNodes(setting, nodes, options); + } + zTreeTools.hideNode = function (node, options) { + if (!node) { + return; + } + view.hideNodes(setting, [node], options); + } + + var _checkNode = zTreeTools.checkNode; + if (_checkNode) { + zTreeTools.checkNode = function (node, checked, checkTypeFlag, callbackFlag) { + if (!!node && !!data.isHidden(setting, node)) { + return; + } + _checkNode.apply(zTreeTools, arguments); + } + } + }, + //method of operate data + _data = { + initHideForExCheck: function (setting, n) { + var isHidden = data.isHidden(setting, n); + if (isHidden && setting.check && setting.check.enable) { + if (typeof n._nocheck == "undefined") { + n._nocheck = !!n.nocheck + n.nocheck = true; + } + n.check_Child_State = -1; + if (view.repairParentChkClassWithSelf) { + view.repairParentChkClassWithSelf(setting, n); + } + } + }, + initShowForExCheck: function (setting, n) { + var isHidden = data.isHidden(setting, n); + if (!isHidden && setting.check && setting.check.enable) { + if (typeof n._nocheck != "undefined") { + n.nocheck = n._nocheck; + delete n._nocheck; + } + if (view.setChkClass) { + var checkObj = $$(n, consts.id.CHECK, setting); + view.setChkClass(setting, checkObj, n); + } + if (view.repairParentChkClassWithSelf) { + view.repairParentChkClassWithSelf(setting, n); + } + } + } + }, + //method of operate ztree dom + _view = { + clearOldFirstNode: function (setting, node) { + var n = node.getNextNode(); + while (!!n) { + if (n.isFirstNode) { + n.isFirstNode = false; + view.setNodeLineIcos(setting, n); + break; + } + if (n.isLastNode) { + break; + } + n = n.getNextNode(); + } + }, + clearOldLastNode: function (setting, node, openFlag) { + var n = node.getPreNode(); + while (!!n) { + if (n.isLastNode) { + n.isLastNode = false; + if (openFlag) { + view.setNodeLineIcos(setting, n); + } + break; + } + if (n.isFirstNode) { + break; + } + n = n.getPreNode(); + } + }, + makeDOMNodeMainBefore: function (html, setting, node) { + var isHidden = data.isHidden(setting, node); + html.push("
      • "); + }, + showNode: function (setting, node, options) { + data.isHidden(setting, node, false); + data.initShowForExCheck(setting, node); + $$(node, setting).show(); + }, + showNodes: function (setting, nodes, options) { + if (!nodes || nodes.length == 0) { + return; + } + var pList = {}, i, j; + for (i = 0, j = nodes.length; i < j; i++) { + var n = nodes[i]; + if (!pList[n.parentTId]) { + var pn = n.getParentNode(); + pList[n.parentTId] = (pn === null) ? data.getRoot(setting) : n.getParentNode(); + } + view.showNode(setting, n, options); + } + for (var tId in pList) { + var children = data.nodeChildren(setting, pList[tId]); + view.setFirstNodeForShow(setting, children); + view.setLastNodeForShow(setting, children); + } + }, + hideNode: function (setting, node, options) { + data.isHidden(setting, node, true); + node.isFirstNode = false; + node.isLastNode = false; + data.initHideForExCheck(setting, node); + view.cancelPreSelectedNode(setting, node); + $$(node, setting).hide(); + }, + hideNodes: function (setting, nodes, options) { + if (!nodes || nodes.length == 0) { + return; + } + var pList = {}, i, j; + for (i = 0, j = nodes.length; i < j; i++) { + var n = nodes[i]; + if ((n.isFirstNode || n.isLastNode) && !pList[n.parentTId]) { + var pn = n.getParentNode(); + pList[n.parentTId] = (pn === null) ? data.getRoot(setting) : n.getParentNode(); + } + view.hideNode(setting, n, options); + } + for (var tId in pList) { + var children = data.nodeChildren(setting, pList[tId]); + view.setFirstNodeForHide(setting, children); + view.setLastNodeForHide(setting, children); + } + }, + setFirstNode: function (setting, parentNode) { + var children = data.nodeChildren(setting, parentNode); + var isHidden = data.isHidden(setting, children[0], false); + if (children.length > 0 && !isHidden) { + children[0].isFirstNode = true; + } else if (children.length > 0) { + view.setFirstNodeForHide(setting, children); + } + }, + setLastNode: function (setting, parentNode) { + var children = data.nodeChildren(setting, parentNode); + var isHidden = data.isHidden(setting, children[0]); + if (children.length > 0 && !isHidden) { + children[children.length - 1].isLastNode = true; + } else if (children.length > 0) { + view.setLastNodeForHide(setting, children); + } + }, + setFirstNodeForHide: function (setting, nodes) { + var n, i, j; + for (i = 0, j = nodes.length; i < j; i++) { + n = nodes[i]; + if (n.isFirstNode) { + break; + } + var isHidden = data.isHidden(setting, n); + if (!isHidden && !n.isFirstNode) { + n.isFirstNode = true; + view.setNodeLineIcos(setting, n); + break; + } else { + n = null; + } + } + return n; + }, + setFirstNodeForShow: function (setting, nodes) { + var n, i, j, first, old; + for (i = 0, j = nodes.length; i < j; i++) { + n = nodes[i]; + var isHidden = data.isHidden(setting, n); + if (!first && !isHidden && n.isFirstNode) { + first = n; + break; + } else if (!first && !isHidden && !n.isFirstNode) { + n.isFirstNode = true; + first = n; + view.setNodeLineIcos(setting, n); + } else if (first && n.isFirstNode) { + n.isFirstNode = false; + old = n; + view.setNodeLineIcos(setting, n); + break; + } else { + n = null; + } + } + return {"new": first, "old": old}; + }, + setLastNodeForHide: function (setting, nodes) { + var n, i; + for (i = nodes.length - 1; i >= 0; i--) { + n = nodes[i]; + if (n.isLastNode) { + break; + } + var isHidden = data.isHidden(setting, n); + if (!isHidden && !n.isLastNode) { + n.isLastNode = true; + view.setNodeLineIcos(setting, n); + break; + } else { + n = null; + } + } + return n; + }, + setLastNodeForShow: function (setting, nodes) { + var n, i, j, last, old; + for (i = nodes.length - 1; i >= 0; i--) { + n = nodes[i]; + var isHidden = data.isHidden(setting, n); + if (!last && !isHidden && n.isLastNode) { + last = n; + break; + } else if (!last && !isHidden && !n.isLastNode) { + n.isLastNode = true; + last = n; + view.setNodeLineIcos(setting, n); + } else if (last && n.isLastNode) { + n.isLastNode = false; + old = n; + view.setNodeLineIcos(setting, n); + break; + } else { + n = null; + } + } + return {"new": last, "old": old}; + } + }, + + _z = { + view: _view, + data: _data + }; + $.extend(true, $.fn.zTree._z, _z); + + var zt = $.fn.zTree, + tools = zt._z.tools, + consts = zt.consts, + view = zt._z.view, + data = zt._z.data, + event = zt._z.event, + $$ = tools.$; + + data.isHidden = function (setting, node, newIsHidden) { + if (!node) { + return false; + } + var key = setting.data.key.isHidden; + if (typeof newIsHidden !== 'undefined') { + if (typeof newIsHidden === "string") { + newIsHidden = tools.eqs(newIsHidden, "true"); + } + newIsHidden = !!newIsHidden; + node[key] = newIsHidden; + } else if (typeof node[key] == "string"){ + node[key] = tools.eqs(node[key], "true"); + } else { + node[key] = !!node[key]; + } + return node[key]; + }; + + data.exSetting(_setting); + data.addInitNode(_initNode); + data.addBeforeA(_beforeA); + data.addZTreeTools(_zTreeTools); + +// Override method in core + var _dInitNode = data.initNode; + data.initNode = function (setting, level, node, parentNode, isFirstNode, isLastNode, openFlag) { + var tmpPNode = (parentNode) ? parentNode : data.getRoot(setting), + children = tmpPNode[setting.data.key.children]; + data.tmpHideFirstNode = view.setFirstNodeForHide(setting, children); + data.tmpHideLastNode = view.setLastNodeForHide(setting, children); + if (openFlag) { + view.setNodeLineIcos(setting, data.tmpHideFirstNode); + view.setNodeLineIcos(setting, data.tmpHideLastNode); + } + isFirstNode = (data.tmpHideFirstNode === node); + isLastNode = (data.tmpHideLastNode === node); + if (_dInitNode) _dInitNode.apply(data, arguments); + if (openFlag && isLastNode) { + view.clearOldLastNode(setting, node, openFlag); + } + }; + + var _makeChkFlag = data.makeChkFlag; + if (!!_makeChkFlag) { + data.makeChkFlag = function (setting, node) { + if (!!node && !!data.isHidden(setting, node)) { + return; + } + _makeChkFlag.apply(data, arguments); + } + } + + var _getTreeCheckedNodes = data.getTreeCheckedNodes; + if (!!_getTreeCheckedNodes) { + data.getTreeCheckedNodes = function (setting, nodes, checked, results) { + if (!!nodes && nodes.length > 0) { + var p = nodes[0].getParentNode(); + if (!!p && !!data.isHidden(setting, p)) { + return []; + } + } + return _getTreeCheckedNodes.apply(data, arguments); + } + } + + var _getTreeChangeCheckedNodes = data.getTreeChangeCheckedNodes; + if (!!_getTreeChangeCheckedNodes) { + data.getTreeChangeCheckedNodes = function (setting, nodes, results) { + if (!!nodes && nodes.length > 0) { + var p = nodes[0].getParentNode(); + if (!!p && !!data.isHidden(setting, p)) { + return []; + } + } + return _getTreeChangeCheckedNodes.apply(data, arguments); + } + } + + var _expandCollapseSonNode = view.expandCollapseSonNode; + if (!!_expandCollapseSonNode) { + view.expandCollapseSonNode = function (setting, node, expandFlag, animateFlag, callback) { + if (!!node && !!data.isHidden(setting, node)) { + return; + } + _expandCollapseSonNode.apply(view, arguments); + } + } + + var _setSonNodeCheckBox = view.setSonNodeCheckBox; + if (!!_setSonNodeCheckBox) { + view.setSonNodeCheckBox = function (setting, node, value, srcNode) { + if (!!node && !!data.isHidden(setting, node)) { + return; + } + _setSonNodeCheckBox.apply(view, arguments); + } + } + + var _repairParentChkClassWithSelf = view.repairParentChkClassWithSelf; + if (!!_repairParentChkClassWithSelf) { + view.repairParentChkClassWithSelf = function (setting, node) { + if (!!node && !!data.isHidden(setting, node)) { + return; + } + _repairParentChkClassWithSelf.apply(view, arguments); + } + } +})(jQuery); \ No newline at end of file diff --git a/routers/admin/cloudbrains.go b/routers/admin/cloudbrains.go index 8cfe10795..ec0034f4f 100755 --- a/routers/admin/cloudbrains.go +++ b/routers/admin/cloudbrains.go @@ -10,6 +10,7 @@ import ( "github.com/360EntSecGroup-Skylar/excelize/v2" "code.gitea.io/gitea/modules/modelarts" + "code.gitea.io/gitea/routers/repo" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/base" @@ -34,10 +35,14 @@ func CloudBrains(ctx *context.Context) { listType := ctx.Query("listType") jobType := ctx.Query("jobType") jobStatus := ctx.Query("jobStatus") + aiCenter := ctx.Query("aiCenter") + cluster := ctx.Query("cluster") ctx.Data["ListType"] = listType ctx.Data["JobType"] = jobType ctx.Data["JobStatus"] = jobStatus + ctx.Data["aiCenter"] = aiCenter + ctx.Data["cluster"] = cluster page := ctx.QueryInt("page") if page <= 0 { @@ -79,6 +84,8 @@ func CloudBrains(ctx *context.Context) { IsLatestVersion: modelarts.IsLatestVersion, ComputeResource: listType, Type: models.TypeCloudBrainAll, + AiCenter: aiCenter, + Cluster: cluster, }) if err != nil { ctx.ServerError("Get job failed:", err) @@ -89,6 +96,10 @@ func CloudBrains(ctx *context.Context) { ciTasks[i].CanDebug = true ciTasks[i].CanDel = true ciTasks[i].Cloudbrain.ComputeResource = task.ComputeResource + ciTasks[i].Cloudbrain.AiCenter = repo.GetCloudbrainAiCenter(task.Cloudbrain, ctx) + _, cardType, _ := repo.GetCloudbrainCardNumAndType(task.Cloudbrain) + ciTasks[i].Cloudbrain.CardType = cardType + ciTasks[i].Cloudbrain.Cluster = repo.GetCloudbrainCluster(task.Cloudbrain, ctx) } pager := context.NewPagination(int(count), setting.UI.IssuePagingNum, page, getTotalPage(count, setting.UI.IssuePagingNum)) @@ -188,11 +199,19 @@ func DownloadCloudBrains(ctx *context.Context) { } func allValues(row int, rs *models.CloudbrainInfo, ctx *context.Context) map[string]string { - return map[string]string{getCellName("A", row): rs.DisplayJobName, getCellName("B", row): rs.JobType, getCellName("C", row): rs.Status, getCellName("D", row): time.Unix(int64(rs.Cloudbrain.CreatedUnix), 0).Format(CREATE_TIME_FORMAT), getCellName("E", row): getDurationTime(rs), - getCellName("F", row): rs.ComputeResource, getCellName("G", row): rs.Name, getCellName("H", row): getRepoPathName(rs), getCellName("I", row): rs.JobName, + return map[string]string{getCellName("A", row): rs.DisplayJobName, getCellName("B", row): repo.GetCloudbrainCluster(rs.Cloudbrain, ctx), + getCellName("C", row): rs.JobType, getCellName("D", row): rs.Status, getCellName("E", row): time.Unix(int64(rs.Cloudbrain.CreatedUnix), 0).Format(CREATE_TIME_FORMAT), + getCellName("F", row): getDurationTime(rs), getCellName("G", row): rs.ComputeResource, + getCellName("H", row): repo.GetCloudbrainAiCenter(rs.Cloudbrain, ctx), getCellName("I", row): getCloudbrainCardType(rs), + getCellName("J", row): rs.Name, getCellName("K", row): getRepoPathName(rs), getCellName("L", row): rs.JobName, } } +func getCloudbrainCardType(rs *models.CloudbrainInfo) string { + _, cardType, _ := repo.GetCloudbrainCardNumAndType(rs.Cloudbrain) + return cardType +} + func getRepoPathName(rs *models.CloudbrainInfo) string { if rs.Repo != nil { return rs.Repo.OwnerName + "/" + rs.Repo.Alias @@ -225,7 +244,11 @@ func getTotalPage(total int64, pageSize int) int { func allHeader(ctx *context.Context) map[string]string { - return map[string]string{"A1": ctx.Tr("repo.cloudbrain_task"), "B1": ctx.Tr("repo.cloudbrain_task_type"), "C1": ctx.Tr("repo.modelarts.status"), "D1": ctx.Tr("repo.modelarts.createtime"), "E1": ctx.Tr("repo.modelarts.train_job.dura_time"), "F1": ctx.Tr("repo.modelarts.computing_resources"), "G1": ctx.Tr("repo.cloudbrain_creator"), "H1": ctx.Tr("repo.repo_name"), "I1": ctx.Tr("repo.cloudbrain_task_name")} + return map[string]string{"A1": ctx.Tr("repo.cloudbrain_task"), "B1": ctx.Tr("repo.modelarts.cluster"), + "C1": ctx.Tr("repo.cloudbrain_task_type"), "D1": ctx.Tr("repo.modelarts.status"), "E1": ctx.Tr("repo.modelarts.createtime"), + "F1": ctx.Tr("repo.modelarts.train_job.dura_time"), "G1": ctx.Tr("repo.modelarts.computing_resources"), + "H1": ctx.Tr("repo.modelarts.ai_center"), "I1": ctx.Tr("repo.modelarts.card_type"), "J1": ctx.Tr("repo.cloudbrain_creator"), + "K1": ctx.Tr("repo.repo_name"), "L1": ctx.Tr("repo.cloudbrain_task_name")} } diff --git a/routers/admin/resources.go b/routers/admin/resources.go new file mode 100644 index 000000000..808c44f3f --- /dev/null +++ b/routers/admin/resources.go @@ -0,0 +1,284 @@ +package admin + +import ( + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/routers/response" + "code.gitea.io/gitea/services/cloudbrain/resource" + "net/http" + "strconv" + "strings" +) + +const ( + tplResourceQueue base.TplName = "admin/resources/queue" + tplResourceSpecification base.TplName = "admin/resources/specification" + tplResourceScene base.TplName = "admin/resources/scene" +) + +func GetQueuePage(ctx *context.Context) { + ctx.Data["PageIsAdmin"] = true + ctx.Data["PageIsAdminResources"] = true + ctx.Data["PageIsAdminResourcesQueue"] = true + ctx.HTML(200, tplResourceQueue) +} + +func GetSpecificationPage(ctx *context.Context) { + ctx.Data["PageIsAdmin"] = true + ctx.Data["PageIsAdminResources"] = true + ctx.Data["PageIsAdminResourcesSpecification"] = true + ctx.HTML(200, tplResourceSpecification) +} + +func GetScenePage(ctx *context.Context) { + ctx.Data["PageIsAdmin"] = true + ctx.Data["PageIsAdminResources"] = true + ctx.Data["PageIsAdminResourcesScene"] = true + ctx.HTML(200, tplResourceScene) +} + +func GetResourceQueueList(ctx *context.Context) { + page := ctx.QueryInt("page") + cluster := ctx.Query("cluster") + aiCenterCode := ctx.Query("center") + computeResource := ctx.Query("resource") + accCardType := ctx.Query("card") + list, err := resource.GetResourceQueueList(models.SearchResourceQueueOptions{ + ListOptions: models.ListOptions{Page: page, PageSize: 10}, + Cluster: cluster, + AiCenterCode: aiCenterCode, + ComputeResource: computeResource, + AccCardType: accCardType, + }) + if err != nil { + log.Error("GetResourceQueueList error.%v", err) + ctx.JSON(http.StatusOK, response.ServerError(err.Error())) + return + } + ctx.JSON(http.StatusOK, response.SuccessWithData(list)) +} + +func GetResourceQueueCodes(ctx *context.Context) { + cluster := ctx.Query("cluster") + list, err := resource.GetResourceQueueCodes(models.GetQueueCodesOptions{Cluster: cluster}) + if err != nil { + log.Error("GetResourceQueueCodes error.%v", err) + ctx.JSON(http.StatusOK, response.ServerError(err.Error())) + return + } + ctx.JSON(http.StatusOK, response.SuccessWithData(list)) +} + +func GetResourceAiCenters(ctx *context.Context) { + list, err := resource.GetResourceAiCenters() + if err != nil { + log.Error("GetResourceAiCenters error.%v", err) + ctx.JSON(http.StatusOK, response.ServerError(err.Error())) + return + } + ctx.JSON(http.StatusOK, response.SuccessWithData(list)) +} + +func AddResourceQueue(ctx *context.Context, req models.ResourceQueueReq) { + req.IsAutomaticSync = false + req.CreatorId = ctx.User.ID + err := resource.AddResourceQueue(req) + if err != nil { + log.Error("AddResourceQueue error. %v", err) + ctx.JSON(http.StatusOK, response.ServerError(err.Error())) + return + } + ctx.JSON(http.StatusOK, response.Success()) +} + +func UpdateResourceQueue(ctx *context.Context, req models.ResourceQueueReq) { + queueId := ctx.ParamsInt64(":id") + //only CardsTotalNum permitted to change + err := resource.UpdateResourceQueue(queueId, req) + if err != nil { + log.Error("UpdateResourceQueue error. %v", err) + ctx.JSON(http.StatusOK, response.ServerError(err.Error())) + return + } + ctx.JSON(http.StatusOK, response.Success()) +} + +func SyncGrampusQueue(ctx *context.Context) { + err := resource.SyncGrampusQueue(ctx.User.ID) + if err != nil { + log.Error("AddResourceQueue error. %v", err) + ctx.JSON(http.StatusOK, response.ServerError(err.Error())) + return + } + ctx.JSON(http.StatusOK, response.Success()) +} + +func GetResourceSpecificationList(ctx *context.Context) { + page := ctx.QueryInt("page") + queue := ctx.QueryInt64("queue") + status := ctx.QueryInt("status") + cluster := ctx.Query("cluster") + list, err := resource.GetResourceSpecificationList(models.SearchResourceSpecificationOptions{ + ListOptions: models.ListOptions{Page: page, PageSize: 10}, + QueueId: queue, + Status: status, + Cluster: cluster, + }) + if err != nil { + log.Error("GetResourceSpecificationList error.%v", err) + ctx.JSON(http.StatusOK, response.ServerError(err.Error())) + return + } + ctx.JSON(http.StatusOK, response.SuccessWithData(list)) +} + +func GetResourceSpecificationScenes(ctx *context.Context) { + specId := ctx.ParamsInt64(":id") + list, err := resource.GetResourceSpecificationScenes(specId) + if err != nil { + log.Error("GetResourceSpecificationScenes error.%v", err) + ctx.JSON(http.StatusOK, response.ServerError(err.Error())) + return + } + r := make(map[string]interface{}) + r["List"] = list + ctx.JSON(http.StatusOK, response.SuccessWithData(r)) +} + +func AddResourceSpecification(ctx *context.Context, req models.ResourceSpecificationReq) { + req.IsAutomaticSync = false + req.CreatorId = ctx.User.ID + err := resource.AddResourceSpecification(ctx.User.ID, req) + if err != nil { + log.Error("AddResourceQueue error. %v", err) + ctx.JSON(http.StatusOK, response.ServerError(err.Error())) + return + } + ctx.JSON(http.StatusOK, response.Success()) +} + +func UpdateResourceSpecification(ctx *context.Context, req models.ResourceSpecificationReq) { + id := ctx.ParamsInt64(":id") + action := ctx.Query("action") + + var err *response.BizError + switch action { + case "edit": + if req.UnitPrice < 0 { + ctx.JSON(http.StatusOK, response.ServerError("param error")) + return + } + //only UnitPrice and permitted to change + err = resource.UpdateSpecUnitPrice(ctx.User.ID, id, req.UnitPrice) + case "on-shelf": + err = resource.ResourceSpecOnShelf(ctx.User.ID, id, req.UnitPrice) + case "off-shelf": + err = resource.ResourceSpecOffShelf(ctx.User.ID, id) + } + + if err != nil { + log.Error("UpdateResourceSpecification error. %v", err) + ctx.JSON(http.StatusOK, response.ResponseError(err)) + return + } + ctx.JSON(http.StatusOK, response.Success()) +} + +func SyncGrampusSpecs(ctx *context.Context) { + err := resource.SyncGrampusSpecs(ctx.User.ID) + if err != nil { + log.Error("AddResourceQueue error. %v", err) + ctx.JSON(http.StatusOK, response.ServerError(err.Error())) + return + } + ctx.JSON(http.StatusOK, response.Success()) +} + +func GetResourceSceneList(ctx *context.Context) { + page := ctx.QueryInt("page") + jobType := ctx.Query("jobType") + aiCenterCode := ctx.Query("center") + queueId := ctx.QueryInt64("queue") + isExclusive := ctx.QueryInt("IsExclusive") + list, err := resource.GetResourceSceneList(models.SearchResourceSceneOptions{ + ListOptions: models.ListOptions{Page: page, PageSize: 10}, + JobType: jobType, + IsExclusive: isExclusive, + AiCenterCode: aiCenterCode, + QueueId: queueId, + }) + if err != nil { + log.Error("GetResourceSceneList error.%v", err) + ctx.JSON(http.StatusOK, response.ServerError(err.Error())) + return + } + ctx.JSON(http.StatusOK, response.SuccessWithData(list)) +} + +func AddResourceScene(ctx *context.Context, req models.ResourceSceneReq) { + req.CreatorId = ctx.User.ID + err := resource.AddResourceScene(req) + if err != nil { + log.Error("AddResourceScene error. %v", err) + ctx.JSON(http.StatusOK, response.ServerError(err.Error())) + return + } + ctx.JSON(http.StatusOK, response.Success()) +} + +func UpdateResourceScene(ctx *context.Context, req models.ResourceSceneReq) { + id := ctx.ParamsInt64(":id") + action := ctx.Query("action") + + req.ID = id + var err error + switch action { + case "edit": + err = resource.UpdateResourceScene(req) + case "delete": + err = resource.DeleteResourceScene(id) + } + + if err != nil { + log.Error("UpdateResourceScene error. %v", err) + ctx.JSON(http.StatusOK, response.ServerError(err.Error())) + return + } + ctx.JSON(http.StatusOK, response.Success()) +} + +func RefreshHistorySpec(ctx *context.Context) { + scope := ctx.Query("scope") + list := ctx.Query("list") + + var scopeAll = false + if scope == "all" { + scopeAll = true + } + var ids = make([]int64, 0) + if list != "" { + strs := strings.Split(list, "|") + for _, s := range strs { + i, err := strconv.ParseInt(s, 10, 64) + if err != nil { + ctx.JSON(http.StatusOK, response.ServerError(err.Error())) + return + } + ids = append(ids, i) + } + + } + + total, success, err := resource.RefreshHistorySpec(scopeAll, ids) + if err != nil { + log.Error("RefreshHistorySpec error. %v", err) + ctx.JSON(http.StatusOK, response.ServerError(err.Error())) + return + } + r := make(map[string]interface{}, 0) + r["success"] = success + r["total"] = total + ctx.JSON(http.StatusOK, response.SuccessWithData(r)) +} diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index e6c572e73..0b941b400 100755 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -570,6 +570,8 @@ func RegisterRoutes(m *macaron.Macaron) { m.Get("/query_user_last_month", operationReq, repo_ext.QueryUserStaticLastMonth) m.Get("/query_user_yesterday", operationReq, repo_ext.QueryUserStaticYesterday) m.Get("/query_user_all", operationReq, repo_ext.QueryUserStaticAll) + m.Get("/query_user_activity", operationReq, repo_ext.QueryUserActivity) + m.Get("/query_user_login", operationReq, repo_ext.QueryUserLoginInfo) //cloudbrain board m.Group("/cloudbrainboard", func() { m.Get("/downloadAll", repo.DownloadCloudBrainBoard) @@ -914,6 +916,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/cloudbrain", func() { m.Get("/:id", repo.GetCloudbrainTask) m.Get("/:id/log", repo.CloudbrainGetLog) + m.Get("/:id/download_log_file", repo.CloudbrainDownloadLogFile) m.Group("/train-job", func() { m.Group("/:jobid", func() { m.Get("", repo.GetModelArtsTrainJobVersion) @@ -921,7 +924,20 @@ func RegisterRoutes(m *macaron.Macaron) { m.Post("/stop_version", cloudbrain.AdminOrOwnerOrJobCreaterRightForTrain, repo_ext.CloudBrainStop) }) }) + m.Group("/inference-job", func() { + m.Group("/:jobid", func() { + m.Get("", repo.GetCloudBrainInferenceJob) + m.Post("/del", cloudbrain.AdminOrOwnerOrJobCreaterRightForTrain, repo.DelCloudBrainJob) + m.Get("/result_list", repo.InferencJobResultList) + }) + }) }, reqRepoReader(models.UnitTypeCloudBrain)) + m.Group("/modelmanage", func() { + m.Get("/:id", repo.GetCloudbrainModelConvertTask) + m.Get("/:id/log", repo.CloudbrainForModelConvertGetLog) + m.Get("/:id/modelartlog", repo.TrainJobForModelConvertGetLog) + m.Get("/:id/model_list", repo.CloudBrainModelConvertList) + }, reqRepoReader(models.UnitTypeModelManage)) m.Group("/modelarts", func() { m.Group("/notebook", func() { //m.Get("/:jobid", repo.GetModelArtsNotebook) @@ -1056,6 +1072,8 @@ func RegisterRoutes(m *macaron.Macaron) { m.Post("/prd/event", authentication.AcceptWechatEvent) }) m.Get("/wechat/material", authentication.GetMaterial) + m.Get("/cloudbrain/get_newest_job", repo.GetNewestJobs) + m.Get("/cloudbrain/get_center_info", repo.GetAICenterInfo) }, securityHeaders(), context.APIContexter(), sudo()) } diff --git a/routers/api/v1/repo/cloudbrain.go b/routers/api/v1/repo/cloudbrain.go index a81263823..b450b2e26 100755 --- a/routers/api/v1/repo/cloudbrain.go +++ b/routers/api/v1/repo/cloudbrain.go @@ -6,16 +6,24 @@ package repo import ( + "bufio" "encoding/json" + "io" "net/http" + "os" "sort" "strings" "time" + "code.gitea.io/gitea/modules/notification" + + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/cloudbrain" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/modelarts" "code.gitea.io/gitea/modules/storage" routerRepo "code.gitea.io/gitea/routers/repo" ) @@ -71,7 +79,7 @@ func GetCloudbrainTask(ctx *context.APIContext) { log.Error("ConvertToJobResultPayload failed:", err) return } - + oldStatus := job.Status job.Status = result.JobStatus.State taskRoles := result.TaskRoles taskRes, _ := models.ConvertToTaskPod(taskRoles[cloudbrain.SubTaskName].(map[string]interface{})) @@ -83,6 +91,9 @@ func GetCloudbrainTask(ctx *context.APIContext) { if result.JobStatus.State != string(models.JobWaiting) { models.ParseAndSetDurationFromCloudBrainOne(result, job) + if oldStatus != job.Status { + notification.NotifyChangeCloudbrainStatus(job, oldStatus) + } err = models.UpdateJob(job) if err != nil { log.Error("UpdateJob failed:", err) @@ -96,25 +107,211 @@ func GetCloudbrainTask(ctx *context.APIContext) { "SubState": result.JobStatus.SubState, "CreatedTime": time.Unix(result.JobStatus.CreatedTime/1000, 0).Format("2006-01-02 15:04:05"), "CompletedTime": time.Unix(result.JobStatus.CompletedTime/1000, 0).Format("2006-01-02 15:04:05"), + "JobDuration": job.TrainJobDuration, }) } -func CloudbrainGetLog(ctx *context.Context) { +func GetCloudBrainInferenceJob(ctx *context.APIContext) { + + jobID := ctx.Params(":jobid") + job, err := models.GetCloudbrainByJobID(jobID) + if err != nil { + ctx.NotFound(err) + return + } + jobResult, err := cloudbrain.GetJob(job.JobID) + if err != nil { + ctx.NotFound(err) + log.Error("GetJob failed:", err) + return + } + result, err := models.ConvertToJobResultPayload(jobResult.Payload) + if err != nil { + ctx.NotFound(err) + log.Error("ConvertToJobResultPayload failed:", err) + return + } + oldStatus := job.Status + job.Status = result.JobStatus.State + if result.JobStatus.State != string(models.JobWaiting) && result.JobStatus.State != string(models.JobFailed) { + taskRoles := result.TaskRoles + taskRes, _ := models.ConvertToTaskPod(taskRoles[cloudbrain.SubTaskName].(map[string]interface{})) + + job.ContainerIp = taskRes.TaskStatuses[0].ContainerIP + job.ContainerID = taskRes.TaskStatuses[0].ContainerID + job.Status = taskRes.TaskStatuses[0].State + } + + if result.JobStatus.State != string(models.JobWaiting) { + models.ParseAndSetDurationFromCloudBrainOne(result, job) + if oldStatus != job.Status { + notification.NotifyChangeCloudbrainStatus(job, oldStatus) + } + err = models.UpdateJob(job) + if err != nil { + log.Error("UpdateJob failed:", err) + } + } + + ctx.JSON(http.StatusOK, map[string]interface{}{ + "JobID": jobID, + "JobStatus": job.Status, + "JobDuration": job.TrainJobDuration, + }) + +} + +func DelCloudBrainJob(ctx *context.APIContext) { + jobID := ctx.Params(":jobid") + + errStr := cloudbrain.DelCloudBrainJob(jobID) + + if errStr != "" { + ctx.JSON(http.StatusOK, map[string]interface{}{ + "Message": ctx.Tr(errStr), + "VersionName": "1", + "Code": 1, + }) + } else { + ctx.JSON(http.StatusOK, map[string]interface{}{ + "Message": "", + "VersionName": "1", + "Code": 0, + }) + } + +} + +func InferencJobResultList(ctx *context.APIContext) { + jobID := ctx.Params(":jobid") + parentDir := ctx.Query("parentDir") + dirArray := strings.Split(parentDir, "/") + + task, err := models.GetCloudbrainByJobID(jobID) + if err != nil { + log.Error("get cloud brain err:", err) + ctx.ServerError("get cloud brain information failed:", err) + + } + + //get dirs + dirs, err := routerRepo.GetResultDirs(task.JobName, parentDir) + if err != nil { + log.Error("GetModelDirs failed:%v", err.Error(), ctx.Data["msgID"]) + ctx.ServerError("GetModelDirs failed:", err) + return + } + + var fileInfos []storage.FileInfo + err = json.Unmarshal([]byte(dirs), &fileInfos) + if err != nil { + log.Error("json.Unmarshal failed:%v", err.Error(), ctx.Data["msgID"]) + ctx.ServerError("json.Unmarshal failed:", err) + return + } + + for i, fileInfo := range fileInfos { + temp, _ := time.Parse("2006-01-02 15:04:05", fileInfo.ModTime) + fileInfos[i].ModTime = temp.Local().Format("2006-01-02 15:04:05") + } + + sort.Slice(fileInfos, func(i, j int) bool { + return fileInfos[i].ModTime > fileInfos[j].ModTime + }) + + ctx.JSON(http.StatusOK, map[string]interface{}{ + "JobID": jobID, + "StatusOK": 0, + "Path": dirArray, + "Dirs": fileInfos, + "task": task, + "PageIsCloudBrain": true, + }) + +} + +func GetCloudbrainModelConvertTask(ctx *context.APIContext) { + var ( + err error + ) ID := ctx.Params(":id") - job, err := models.GetCloudbrainByID(ID) + job, err := models.QueryModelConvertById(ID) if err != nil { - log.Error("GetCloudbrainByJobName failed: %v", err, ctx.Data["MsgID"]) - ctx.ServerError(err.Error(), err) + ctx.NotFound(err) + log.Error("GetCloudbrainByID failed:", err) return } + if job.IsGpuTrainTask() { + jobResult, err := cloudbrain.GetJob(job.CloudBrainTaskId) + if err != nil { + ctx.NotFound(err) + log.Error("GetJob failed:", err) + return + } + result, _ := models.ConvertToJobResultPayload(jobResult.Payload) + if err != nil { + ctx.NotFound(err) + log.Error("ConvertToJobResultPayload failed:", err) + return + } + + job.Status = result.JobStatus.State + taskRoles := result.TaskRoles + taskRes, _ := models.ConvertToTaskPod(taskRoles[cloudbrain.SubTaskName].(map[string]interface{})) + if result.JobStatus.State != string(models.JobWaiting) && result.JobStatus.State != string(models.JobFailed) { + job.ContainerIp = taskRes.TaskStatuses[0].ContainerIP + job.ContainerID = taskRes.TaskStatuses[0].ContainerID + job.Status = taskRes.TaskStatuses[0].State + } + + if result.JobStatus.State != string(models.JobWaiting) { + models.ModelComputeAndSetDuration(job, result) + err = models.UpdateModelConvert(job) + if err != nil { + log.Error("UpdateJob failed:", err) + } + } + ctx.JSON(http.StatusOK, map[string]interface{}{ + "ID": ID, + "JobName": result.Config.JobName, + "JobStatus": result.JobStatus.State, + "SubState": result.JobStatus.SubState, + "CreatedTime": time.Unix(result.JobStatus.CreatedTime/1000, 0).Format("2006-01-02 15:04:05"), + "CompletedTime": time.Unix(result.JobStatus.CompletedTime/1000, 0).Format("2006-01-02 15:04:05"), + }) + } else { + + result, err := modelarts.GetTrainJob(job.CloudBrainTaskId, job.ModelArtsVersionId) + if err != nil { + log.Error("get modelart job failed:", err) + ctx.NotFound(err) + return + } + job.Status = modelarts.TransTrainJobStatus(result.IntStatus) + job.RunTime = result.Duration / 1000 + job.TrainJobDuration = models.ConvertDurationToStr(job.RunTime) + err = models.UpdateModelConvert(job) + if err != nil { + log.Error("UpdateJob failed:", err) + } + + ctx.JSON(http.StatusOK, map[string]interface{}{ + "ID": ID, + "JobStatus": job.Status, + }) + + } + +} + +func CloudbrainGetLogByJobId(jobId string, jobName string) map[string]interface{} { var hits []models.Hits - result, err := cloudbrain.GetJobLog(job.JobID) + result, err := cloudbrain.GetJobLog(jobId) if err != nil { - log.Error("GetJobLog failed: %v", err, ctx.Data["MsgID"]) - ctx.ServerError(err.Error(), err) - return + log.Error("GetJobLog failed: %v", err) + return nil } hits = result.Hits.Hits @@ -123,7 +320,7 @@ func CloudbrainGetLog(ctx *context.Context) { for { resultNext, err := cloudbrain.GetJobAllLog(result.ScrollID) if err != nil { - log.Error("GetJobAllLog failed: %v", err, ctx.Data["MsgID"]) + log.Error("GetJobAllLog failed: %v", err) } else { for _, hit := range resultNext.Hits.Hits { hits = append(hits, hit) @@ -148,12 +345,233 @@ func CloudbrainGetLog(ctx *context.Context) { content += log.Source.Message + "\n" } - ctx.JSON(http.StatusOK, map[string]interface{}{ - "JobName": job.JobName, + return map[string]interface{}{ + "JobName": jobName, "Content": content, - }) + } + +} + +func CloudbrainForModelConvertGetLog(ctx *context.Context) { + ID := ctx.Params(":id") + job, err := models.QueryModelConvertById(ID) + if err != nil { + log.Error("GetCloudbrainByJobName failed: %v", err, ctx.Data["MsgID"]) + ctx.ServerError(err.Error(), err) + return + } + + result := CloudbrainGetLogByJobId(job.CloudBrainTaskId, job.Name) + if result == nil { + log.Error("GetJobLog failed: %v", err, ctx.Data["MsgID"]) + ctx.ServerError(err.Error(), err) + return + } + ctx.JSON(http.StatusOK, result) +} + +func CloudbrainDownloadLogFile(ctx *context.Context) { + ID := ctx.Params(":id") + job, err := models.GetCloudbrainByID(ID) + if err != nil { + log.Error("GetCloudbrainByJobName failed: %v", err, ctx.Data["MsgID"]) + ctx.ServerError(err.Error(), err) + return + } + + prefix := "/" + setting.CBCodePathPrefix + job.JobName + "/model" + files, err := storage.GetOneLevelAllObjectUnderDirMinio(setting.Attachment.Minio.Bucket, prefix, "") + if err != nil { + log.Error("query cloudbrain model failed: %v", err) + return + } + fileName := "" + for _, file := range files { + if strings.HasSuffix(file.FileName, "log.txt") { + fileName = file.FileName + break + } + } + if fileName != "" { + url, err := storage.Attachments.PresignedGetURL(prefix+"/"+fileName, fileName) + if err != nil { + log.Error("Get minio get SignedUrl failed: %v", err.Error(), ctx.Data["msgID"]) + ctx.ServerError("Get minio get SignedUrl failed", err) + return + } + http.Redirect(ctx.Resp, ctx.Req.Request, url, http.StatusTemporaryRedirect) + } +} + +func CloudbrainGetLog(ctx *context.Context) { + ID := ctx.Params(":id") + startLine := ctx.QueryInt("base_line") + lines := ctx.QueryInt("lines") + endLine := startLine + lines + order := ctx.Query("order") + if order == "asc" { + endLine = startLine + startLine = endLine - lines + if startLine < 0 { + startLine = 0 + } + } + job, err := models.GetCloudbrainByID(ID) + if err != nil { + log.Error("GetCloudbrainByJobName failed: %v", err, ctx.Data["MsgID"]) + ctx.ServerError(err.Error(), err) + return + } + result := getLogFromModelDir(job.JobName, startLine, endLine) + if result == nil { + log.Error("GetJobLog failed: %v", err, ctx.Data["MsgID"]) + ctx.ServerError(err.Error(), err) + return + } + + re := map[string]interface{}{ + "JobID": ID, + "LogFileName": result["FileName"], + "StartLine": startLine, + "EndLine": result["endLine"], + "Content": result["Content"], + "Lines": result["lines"], + "CanLogDownload": result["FileName"] != "", + } + //result := CloudbrainGetLogByJobId(job.JobID, job.JobName) + + ctx.JSON(http.StatusOK, re) +} + +func getLogFromModelDir(jobName string, startLine int, endLine int) map[string]interface{} { + prefix := "/" + setting.CBCodePathPrefix + jobName + "/model" + files, err := storage.GetOneLevelAllObjectUnderDirMinio(setting.Attachment.Minio.Bucket, prefix, "") + if err != nil { + log.Error("query cloudbrain model failed: %v", err) + return nil + } + + re := "" + fileName := "" + count := 0 + fileEndLine := endLine + for _, file := range files { + if strings.HasSuffix(file.FileName, "log.txt") { + fileName = file.FileName + path := storage.GetMinioPath(jobName+"/model/", file.FileName) + log.Info("path=" + path) + reader, err := os.Open(path) + defer reader.Close() + if err == nil { + r := bufio.NewReader(reader) + for i := 0; i < endLine; i++ { + line, error := r.ReadString('\n') + log.Info("line=" + line) + fileEndLine = i + if error == io.EOF { + log.Info("read file completed.") + break + } + if error != nil { + log.Info("read file error." + error.Error()) + break + } + if error == nil { + if i >= startLine { + re = re + line + count++ + } + } + } + } else { + log.Info("error:" + err.Error()) + } + break + } + } + + return map[string]interface{}{ + "JobName": jobName, + "Content": re, + "FileName": fileName, + "lines": count, + "endLine": fileEndLine, + } +} + +func CloudBrainModelConvertList(ctx *context.APIContext) { + var ( + err error + ) + + ID := ctx.Params(":id") + parentDir := ctx.Query("parentDir") + dirArray := strings.Split(parentDir, "/") + + job, err := models.QueryModelConvertById(ID) + if err != nil { + log.Error("GetCloudbrainByJobID(%s) failed:%v", job.Name, err.Error()) + return + } + if job.IsGpuTrainTask() { + //get dirs + dirs, err := routerRepo.GetModelDirs(job.ID, parentDir) + if err != nil { + log.Error("GetModelDirs failed:%v", err.Error(), ctx.Data["msgID"]) + ctx.ServerError("GetModelDirs failed:", err) + return + } + + var fileInfos []storage.FileInfo + err = json.Unmarshal([]byte(dirs), &fileInfos) + if err != nil { + log.Error("json.Unmarshal failed:%v", err.Error(), ctx.Data["msgID"]) + ctx.ServerError("json.Unmarshal failed:", err) + return + } + + for i, fileInfo := range fileInfos { + temp, _ := time.Parse("2006-01-02 15:04:05", fileInfo.ModTime) + fileInfos[i].ModTime = temp.Local().Format("2006-01-02 15:04:05") + } + + sort.Slice(fileInfos, func(i, j int) bool { + return fileInfos[i].ModTime > fileInfos[j].ModTime + }) + + ctx.JSON(http.StatusOK, map[string]interface{}{ + "JobID": ID, + "VersionName": "", + "StatusOK": 0, + "Path": dirArray, + "Dirs": fileInfos, + "task": job, + "PageIsCloudBrain": true, + }) + } else { + var jobID = ctx.Params(":id") + var versionName = "V0001" + parentDir := ctx.Query("parentDir") + dirArray := strings.Split(parentDir, "/") + + models, err := storage.GetObsListObject(job.ID, "output/", parentDir, versionName) + if err != nil { + log.Info("get TrainJobListModel failed:", err) + ctx.ServerError("GetObsListObject:", err) + return + } + + ctx.JSON(http.StatusOK, map[string]interface{}{ + "JobID": jobID, + "VersionName": versionName, + "StatusOK": 0, + "Path": dirArray, + "Dirs": models, + "task": job, + "PageIsCloudBrain": true, + }) + } - return } func CloudBrainModelList(ctx *context.APIContext) { @@ -207,3 +625,94 @@ func CloudBrainModelList(ctx *context.APIContext) { "PageIsCloudBrain": true, }) } + +type JobInfo struct { + JobName string `json:"job_name"` + AiCenterId int `json:"ai_center_id"` +} + +func GetNewestJobs(ctx *context.APIContext) { + idsC2Net, err := models.GetNewestJobsByAiCenter() + if err != nil { + log.Error("GetNewestJobsByAiCenter(%s) failed:%v", err.Error()) + return + } + + idsCloudbrain, err := models.GetNewestJobsByType() + if err != nil { + log.Error("GetNewestJobsByType(%s) failed:%v", err.Error()) + return + } + + ids := make([]int64, len(idsC2Net), cap(idsC2Net)*2) + copy(ids, idsC2Net) + for _, id := range idsCloudbrain { + ids = append(ids, id) + } + + jobs, err := models.GetCloudbrainByIDs(ids) + if err != nil { + log.Error("GetCloudbrainByIDs(%s) failed:%v", err.Error()) + return + } + + jobInfos := make([]JobInfo, 0) + for _, job := range jobs { + var id int + var content string + switch job.Type { + case models.TypeCloudBrainOne: + id, content = getAICenterID("cloudbrain_one") + if content == "" { + log.Error("job(%s) has no match config info", job.DisplayJobName) + continue + } + case models.TypeCloudBrainTwo: + id, content = getAICenterID("cloudbrain_two") + if content == "" { + log.Error("job(%s) has no match config info", job.DisplayJobName) + continue + } + case models.TypeC2Net: + centerInfo := strings.Split(job.AiCenter, "+") + if len(centerInfo) != 2 { + log.Error("job(%s):ai_center(%s) is wrong", job.DisplayJobName, job.AiCenter) + continue + } + id, content = getAICenterID(centerInfo[0]) + if content == "" { + log.Error("job(%s) has no match config info", job.DisplayJobName) + continue + } + default: + log.Error("no match info") + continue + } + + jobInfos = append(jobInfos, JobInfo{ + JobName: job.DisplayJobName, + AiCenterId: id, + }) + } + + ctx.JSON(http.StatusOK, jobInfos) +} + +func GetAICenterInfo(ctx *context.APIContext) { + if setting.C2NetInfos == nil { + log.Error("C2NET_SEQUENCE is incorrect") + return + } + + ctx.JSON(http.StatusOK, setting.C2NetInfos.C2NetSqInfo) +} + +func getAICenterID(name string) (int, string) { + for _, info := range setting.C2NetInfos.C2NetSqInfo { + if name == info.Name { + return info.ID, info.Content + } + } + + return 0, "" +} diff --git a/routers/api/v1/repo/cloudbrain_dashboard.go b/routers/api/v1/repo/cloudbrain_dashboard.go index eb86a8293..55d7400b7 100755 --- a/routers/api/v1/repo/cloudbrain_dashboard.go +++ b/routers/api/v1/repo/cloudbrain_dashboard.go @@ -10,6 +10,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/routers/repo" "github.com/360EntSecGroup-Skylar/excelize/v2" ) @@ -678,6 +679,8 @@ func GetCloudbrainsDetailData(ctx *context.Context) { jobType := ctx.Query("jobType") jobStatus := ctx.Query("jobStatus") cloudBrainType := ctx.QueryInt("Type") + aiCenter := ctx.Query("aiCenter") + needDeleteInfo := ctx.Query("needDeleteInfo") page := ctx.QueryInt("page") pageSize := ctx.QueryInt("pagesize") @@ -723,6 +726,8 @@ func GetCloudbrainsDetailData(ctx *context.Context) { NeedRepoInfo: true, BeginTimeUnix: int64(recordBeginTime), EndTimeUnix: endTime.Unix(), + AiCenter: aiCenter, + NeedDeleteInfo: needDeleteInfo, }) if err != nil { ctx.ServerError("Get job failed:", err) @@ -735,6 +740,7 @@ func GetCloudbrainsDetailData(ctx *context.Context) { var taskDetail models.TaskDetail taskDetail.ID = ciTasks[i].Cloudbrain.ID + taskDetail.JobID = ciTasks[i].Cloudbrain.JobID taskDetail.JobName = ciTasks[i].JobName taskDetail.DisplayJobName = ciTasks[i].DisplayJobName taskDetail.Status = ciTasks[i].Status @@ -751,48 +757,14 @@ func GetCloudbrainsDetailData(ctx *context.Context) { taskDetail.RepoName = ciTasks[i].Repo.OwnerName + "/" + ciTasks[i].Repo.Name taskDetail.RepoAlias = ciTasks[i].Repo.OwnerName + "/" + ciTasks[i].Repo.Alias } - if ciTasks[i].Cloudbrain.Status == string(models.JobWaiting) { - if ciTasks[i].Cloudbrain.DeletedAt != nilTime { - WaitTimeInt := ciTasks[i].Cloudbrain.UpdatedUnix.AsTime().Unix() - ciTasks[i].Cloudbrain.CreatedUnix.AsTime().Unix() - taskDetail.WaitTime = models.ConvertDurationToStr(WaitTimeInt) - if WaitTimeInt < 0 { - taskDetail.WaitTime = "00:00:00" - } - } else { - if ciTasks[i].Cloudbrain.StartTime.AsTime().Unix() == 0 { - WaitTimeInt := time.Now().Unix() - ciTasks[i].Cloudbrain.CreatedUnix.AsTime().Unix() - taskDetail.WaitTime = models.ConvertDurationToStr(WaitTimeInt) - if WaitTimeInt < 0 { - taskDetail.WaitTime = "00:00:00" - } - } else { - WaitTimeInt := ciTasks[i].Cloudbrain.StartTime.AsTime().Unix() - ciTasks[i].Cloudbrain.CreatedUnix.AsTime().Unix() - taskDetail.WaitTime = models.ConvertDurationToStr(WaitTimeInt) - if WaitTimeInt < 0 { - taskDetail.WaitTime = "00:00:00" - } - } - } - } else if ciTasks[i].Cloudbrain.Status == string(models.JobStopped) && ciTasks[i].Cloudbrain.StartTime.AsTime().Unix() == 0 { - WaitTimeInt := ciTasks[i].Cloudbrain.EndTime.AsTime().Unix() - ciTasks[i].Cloudbrain.CreatedUnix.AsTime().Unix() - taskDetail.WaitTime = models.ConvertDurationToStr(WaitTimeInt) - if WaitTimeInt < 0 { - taskDetail.WaitTime = "00:00:00" - - } - } else { - WaitTimeInt := ciTasks[i].Cloudbrain.StartTime.AsTime().Unix() - ciTasks[i].Cloudbrain.CreatedUnix.AsTime().Unix() - taskDetail.WaitTime = models.ConvertDurationToStr(WaitTimeInt) - if WaitTimeInt < 0 { - taskDetail.WaitTime = "00:00:00" - } - } + taskDetail.CardNum, taskDetail.CardType, _ = repo.GetCloudbrainCardNumAndType(ciTasks[i].Cloudbrain) + taskDetail.CardDuration = repo.GetCloudbrainCardDuration(ciTasks[i].Cloudbrain) + taskDetail.AiCenter = repo.GetCloudbrainAiCenter(ciTasks[i].Cloudbrain, ctx) + taskDetail.FlavorName, _ = repo.GetCloudbrainFlavorName(ciTasks[i].Cloudbrain) - if ciTasks[i].Cloudbrain.Type == models.TypeCloudBrainTwo || (ciTasks[i].Cloudbrain.Type == models.TypeCloudBrainOne && ciTasks[i].Cloudbrain.JobType == "TRAIN") { - taskDetail.JobID = ciTasks[i].Cloudbrain.JobID - } + taskDetail.WaitTime = repo.GetCloudbrainWaitTime(ciTasks[i].Cloudbrain) - if ciTasks[i].Cloudbrain.DeletedAt != nilTime { + if ciTasks[i].Cloudbrain.DeletedAt != nilTime || ciTasks[i].Repo == nil { taskDetail.IsDelete = true } else { taskDetail.IsDelete = false @@ -813,6 +785,17 @@ func GetCloudbrainsDetailData(ctx *context.Context) { }) } +func getCloudbrainAiCenter(task models.Cloudbrain, ctx *context.Context) string { + if task.Type == models.TypeCloudBrainOne { + return ctx.Tr("repo.cloudbrain1") + } else if task.Type == models.TypeCloudBrainTwo { + return ctx.Tr("repo.cloudbrain2") + } else if task.Type == models.TypeC2Net { + return task.AiCenter + } + return "" +} + func GetCloudbrainsCreateHoursData(ctx *context.Context) { recordCloudbrain, err := models.GetRecordBeginTime() if err != nil { @@ -1247,18 +1230,23 @@ func allCloudbrainHeader(ctx *context.Context) map[string]string { return map[string]string{"A1": ctx.Tr("repo.cloudbrain_task"), "B1": ctx.Tr("repo.cloudbrain_type"), "C1": ctx.Tr("repo.modelarts.status"), "D1": ctx.Tr("repo.cloudbrain_task_type"), "E1": ctx.Tr("repo.modelarts.createtime"), "F1": ctx.Tr("repo.modelarts.train_job.wait_time"), "G1": ctx.Tr("repo.modelarts.train_job.dura_time"), - "H1": ctx.Tr("repo.modelarts.train_job.start_time"), - "I1": ctx.Tr("repo.modelarts.train_job.end_time"), "J1": ctx.Tr("repo.modelarts.computing_resources"), - "K1": ctx.Tr("repo.cloudbrain_creator"), "L1": ctx.Tr("repo.repo_name"), "M1": ctx.Tr("repo.cloudbrain_task_name"), "N1": ctx.Tr("repo.modelarts.deletetime")} + "H1": ctx.Tr("cloudbrain.card_duration"), + "I1": ctx.Tr("repo.modelarts.train_job.start_time"), "J1": ctx.Tr("repo.modelarts.train_job.end_time"), + "K1": ctx.Tr("repo.modelarts.computing_resources"), "L1": ctx.Tr("cloudbrain.card_type"), + "M1": ctx.Tr("repo.grampus.train_job.ai_center"), "N1": ctx.Tr("cloudbrain.resource_specification"), + "O1": ctx.Tr("repo.cloudbrain_creator"), "P1": ctx.Tr("repo.repo_name"), "Q1": ctx.Tr("repo.cloudbrain_task_name"), + "R1": ctx.Tr("repo.modelarts.deletetime")} } func allCloudbrainValues(row int, rs *models.CloudbrainInfo, ctx *context.Context) map[string]string { return map[string]string{getCellName("A", row): rs.DisplayJobName, getCellName("B", row): getCloudbrainType(rs, ctx), getCellName("C", row): rs.Status, getCellName("D", row): rs.JobType, - getCellName("E", row): time.Unix(int64(rs.Cloudbrain.CreatedUnix), 0).Format(CREATE_TIME_FORMAT), getCellName("F", row): getBrainWaitTime(rs), - getCellName("G", row): rs.TrainJobDuration, getCellName("H", row): getBrainStartTime(rs), - getCellName("I", row): getBrainEndTime(rs), - getCellName("J", row): rs.ComputeResource, getCellName("K", row): rs.Name, getCellName("L", row): getBrainRepo(rs), - getCellName("M", row): rs.JobName, getCellName("N", row): getBrainDeleteTime(rs), + getCellName("E", row): time.Unix(int64(rs.Cloudbrain.CreatedUnix), 0).Format(CREATE_TIME_FORMAT), getCellName("F", row): repo.GetCloudbrainWaitTime(rs.Cloudbrain), + getCellName("G", row): rs.TrainJobDuration, getCellName("H", row): repo.GetCloudbrainCardDuration(rs.Cloudbrain), + getCellName("I", row): getBrainStartTime(rs), + getCellName("J", row): getBrainEndTime(rs), getCellName("K", row): rs.ComputeResource, getCellName("L", row): getCloudbrainCardType(rs), + getCellName("M", row): repo.GetCloudbrainAiCenter(rs.Cloudbrain, ctx), getCellName("N", row): getCloudbrainFlavorName(rs), + getCellName("O", row): rs.Name, getCellName("P", row): getBrainRepo(rs), + getCellName("Q", row): rs.JobName, getCellName("R", row): getBrainDeleteTime(rs), } } func getBrainRepo(rs *models.CloudbrainInfo) string { @@ -1285,19 +1273,6 @@ func getBrainEndTime(rs *models.CloudbrainInfo) string { } } -func getBrainWaitTime(rs *models.CloudbrainInfo) string { - var waitTime int64 - if rs.Cloudbrain.Status == string(models.JobWaiting) { - waitTime = time.Now().Unix() - rs.Cloudbrain.CreatedUnix.AsTime().Unix() - } else { - waitTime = int64(rs.Cloudbrain.StartTime - rs.Cloudbrain.CreatedUnix) - } - if waitTime <= 0 { - return "00:00:00" - } else { - return models.ConvertDurationToStr(waitTime) - } -} func getCloudbrainType(rs *models.CloudbrainInfo, ctx *context.Context) string { if rs.Cloudbrain.Type == models.TypeCloudBrainOne { return ctx.Tr("repo.cloudbrain1") @@ -1309,6 +1284,14 @@ func getCloudbrainType(rs *models.CloudbrainInfo, ctx *context.Context) string { return ctx.Tr("repo.cloudbrain_untype") } } +func getCloudbrainCardType(rs *models.CloudbrainInfo) string { + _, cardType, _ := repo.GetCloudbrainCardNumAndType(rs.Cloudbrain) + return cardType +} +func getCloudbrainFlavorName(rs *models.CloudbrainInfo) string { + flavorName, _ := repo.GetCloudbrainFlavorName(rs.Cloudbrain) + return flavorName +} func getBrainDeleteTime(rs *models.CloudbrainInfo) string { nilTime := time.Time{} diff --git a/routers/api/v1/repo/modelarts.go b/routers/api/v1/repo/modelarts.go index 751e240bf..79e35812e 100755 --- a/routers/api/v1/repo/modelarts.go +++ b/routers/api/v1/repo/modelarts.go @@ -6,14 +6,17 @@ package repo import ( - "code.gitea.io/gitea/modules/grampus" - "code.gitea.io/gitea/modules/setting" "encoding/json" "net/http" "path" "strconv" "strings" + "code.gitea.io/gitea/modules/notification" + + "code.gitea.io/gitea/modules/grampus" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/cloudbrain" "code.gitea.io/gitea/modules/context" @@ -24,37 +27,6 @@ import ( routerRepo "code.gitea.io/gitea/routers/repo" ) -func GetModelArtsNotebook(ctx *context.APIContext) { - var ( - err error - ) - - jobID := ctx.Params(":jobid") - repoID := ctx.Repo.Repository.ID - job, err := models.GetRepoCloudBrainByJobID(repoID, jobID) - if err != nil { - ctx.NotFound(err) - return - } - result, err := modelarts.GetJob(jobID) - if err != nil { - ctx.NotFound(err) - return - } - - job.Status = result.Status - err = models.UpdateJob(job) - if err != nil { - log.Error("UpdateJob failed:", err) - } - - ctx.JSON(http.StatusOK, map[string]interface{}{ - "JobID": jobID, - "JobStatus": result.Status, - }) - -} - func GetModelArtsNotebook2(ctx *context.APIContext) { var ( err error @@ -66,29 +38,17 @@ func GetModelArtsNotebook2(ctx *context.APIContext) { ctx.NotFound(err) return } - result, err := modelarts.GetNotebook2(job.JobID) + err = modelarts.HandleNotebookInfo(job) if err != nil { ctx.NotFound(err) return } - if job.StartTime == 0 && result.Lease.UpdateTime > 0 { - job.StartTime = timeutil.TimeStamp(result.Lease.UpdateTime / 1000) - } - job.Status = result.Status - if job.EndTime == 0 && models.IsModelArtsDebugJobTerminal(job.Status) { - job.EndTime = timeutil.TimeStampNow() - } - job.CorrectCreateUnix() - job.ComputeAndSetDuration() - err = models.UpdateJob(job) - if err != nil { - log.Error("UpdateJob failed:", err) - } ctx.JSON(http.StatusOK, map[string]interface{}{ - "ID": ID, - "JobName": job.JobName, - "JobStatus": result.Status, + "ID": ID, + "JobName": job.JobName, + "JobStatus": job.Status, + "JobDuration": job.TrainJobDuration, }) } @@ -110,10 +70,13 @@ func GetModelArtsTrainJob(ctx *context.APIContext) { ctx.NotFound(err) return } - + oldStatus := job.Status job.Status = modelarts.TransTrainJobStatus(result.IntStatus) job.Duration = result.Duration job.TrainJobDuration = result.TrainJobDuration + if oldStatus != job.Status { + notification.NotifyChangeCloudbrainStatus(job, oldStatus) + } err = models.UpdateJob(job) if err != nil { log.Error("UpdateJob failed:", err) @@ -154,7 +117,7 @@ func GetModelArtsTrainJobVersion(ctx *context.APIContext) { log.Error("ConvertToJobResultPayload failed:", err) return } - + oldStatus := job.Status job.Status = result.JobStatus.State if result.JobStatus.State != string(models.JobWaiting) && result.JobStatus.State != string(models.JobFailed) { taskRoles := result.TaskRoles @@ -167,33 +130,20 @@ func GetModelArtsTrainJobVersion(ctx *context.APIContext) { if result.JobStatus.State != string(models.JobWaiting) { models.ParseAndSetDurationFromCloudBrainOne(result, job) + if oldStatus != job.Status { + notification.NotifyChangeCloudbrainStatus(job, oldStatus) + } err = models.UpdateJob(job) if err != nil { log.Error("UpdateJob failed:", err) } } } else if job.Type == models.TypeCloudBrainTwo { - result, err := modelarts.GetTrainJob(jobID, strconv.FormatInt(job.VersionID, 10)) + err := modelarts.HandleTrainJobInfo(job) if err != nil { ctx.NotFound(err) return } - - if job.StartTime == 0 && result.StartTime > 0 { - job.StartTime = timeutil.TimeStamp(result.StartTime / 1000) - } - job.Status = modelarts.TransTrainJobStatus(result.IntStatus) - job.Duration = result.Duration / 1000 - job.TrainJobDuration = models.ConvertDurationToStr(job.Duration) - - if job.EndTime == 0 && models.IsTrainJobTerminal(job.Status) && job.StartTime > 0 { - job.EndTime = job.StartTime.Add(job.Duration) - } - job.CorrectCreateUnix() - err = models.UpdateTrainJobVersion(job) - if err != nil { - log.Error("UpdateJob failed:", err) - } } else if job.Type == models.TypeC2Net { result, err := grampus.GetJob(jobID) if err != nil { @@ -205,6 +155,7 @@ func GetModelArtsTrainJobVersion(ctx *context.APIContext) { if job.StartTime == 0 && result.JobInfo.StartedAt > 0 { job.StartTime = timeutil.TimeStamp(result.JobInfo.StartedAt) } + oldStatus := job.Status job.Status = grampus.TransTrainJobStatus(result.JobInfo.Status) job.Duration = result.JobInfo.RunSec job.TrainJobDuration = models.ConvertDurationToStr(job.Duration) @@ -227,6 +178,9 @@ func GetModelArtsTrainJobVersion(ctx *context.APIContext) { aiCenterName = temp[1] } } + if oldStatus != job.Status { + notification.NotifyChangeCloudbrainStatus(job, oldStatus) + } err = models.UpdateTrainJobVersion(job) if err != nil { log.Error("UpdateJob failed:", err) @@ -242,6 +196,75 @@ func GetModelArtsTrainJobVersion(ctx *context.APIContext) { } +func TrainJobForModelConvertGetLog(ctx *context.APIContext) { + var ( + err error + ) + + var jobID = ctx.Params(":id") + var baseLine = ctx.Query("base_line") + var order = ctx.Query("order") + var lines = ctx.Query("lines") + lines_int, err := strconv.Atoi(lines) + if err != nil { + log.Error("change lines(%d) string to int failed", lines_int) + } + + if order != modelarts.OrderDesc && order != modelarts.OrderAsc { + log.Error("order(%s) check failed", order) + ctx.JSON(http.StatusBadRequest, map[string]interface{}{ + "err_msg": "order check failed", + }) + return + } + + resultLogFile, result, err := trainJobForModelConvertGetLogContent(jobID, baseLine, order, lines_int) + if err != nil { + log.Error("trainJobGetLog(%s) failed:%v", jobID, err.Error()) + // ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobShow, nil) + ctx.JSON(http.StatusOK, map[string]interface{}{ + "JobID": jobID, + "LogFileName": "", + "StartLine": "0", + "EndLine": "0", + "Content": "", + "Lines": 0, + }) + return + } + + ctx.Data["log_file_name"] = resultLogFile.LogFileList[0] + + ctx.JSON(http.StatusOK, map[string]interface{}{ + "JobID": jobID, + "LogFileName": resultLogFile.LogFileList[0], + "StartLine": result.StartLine, + "EndLine": result.EndLine, + "Content": result.Content, + "Lines": result.Lines, + }) +} + +func trainJobForModelConvertGetLogContent(jobID string, baseLine string, order string, lines int) (*models.GetTrainJobLogFileNamesResult, *models.GetTrainJobLogResult, error) { + task, err := models.QueryModelConvertById(jobID) + if err != nil { + log.Error("GetCloudbrainByJobID(%s) failed:%v", jobID, err.Error()) + return nil, nil, err + } + resultLogFile, err := modelarts.GetTrainJobLogFileNames(task.CloudBrainTaskId, task.ModelArtsVersionId) + if err != nil { + log.Error("GetTrainJobLogFileNames(%s) failed:%v", task.CloudBrainTaskId, err.Error()) + return nil, nil, err + } + result, err := modelarts.GetTrainJobLog(task.CloudBrainTaskId, task.ModelArtsVersionId, baseLine, resultLogFile.LogFileList[0], order, lines) + if err != nil { + log.Error("GetTrainJobLog(%s) failed:%v", task.CloudBrainTaskId, err.Error()) + return nil, nil, err + } + + return resultLogFile, result, err +} + func TrainJobGetLog(ctx *context.APIContext) { var ( err error @@ -330,6 +353,14 @@ func DelTrainJobVersion(ctx *context.APIContext) { return } + if task.Status != string(models.ModelArtsTrainJobImageFailed) && task.Status != string(models.ModelArtsTrainJobSubmitFailed) && task.Status != string(models.ModelArtsTrainJobDeleteFailed) && + task.Status != string(models.ModelArtsTrainJobCompleted) && task.Status != string(models.ModelArtsTrainJobFailed) && + task.Status != string(models.ModelArtsTrainJobKilled) && task.Status != string(models.ModelArtsTrainJobCanceled) && task.Status != string(models.ModelArtsTrainJobLost) { + log.Error("the job(%s) version has not been stopped", task.JobName) + ctx.NotFound(err) + return + } + //删除modelarts上的记录 _, err = modelarts.DelTrainJobVersion(jobID, strconv.FormatInt(task.VersionID, 10)) if err != nil { @@ -473,26 +504,11 @@ func GetModelArtsInferenceJob(ctx *context.APIContext) { ctx.NotFound(err) return } - result, err := modelarts.GetTrainJob(jobID, strconv.FormatInt(job.VersionID, 10)) + err = modelarts.HandleTrainJobInfo(job) if err != nil { ctx.NotFound(err) return } - if job.StartTime == 0 && result.StartTime > 0 { - job.StartTime = timeutil.TimeStamp(result.StartTime / 1000) - } - job.Status = modelarts.TransTrainJobStatus(result.IntStatus) - job.Duration = result.Duration / 1000 - job.TrainJobDuration = models.ConvertDurationToStr(job.Duration) - - if job.EndTime == 0 && models.IsTrainJobTerminal(job.Status) && job.StartTime > 0 { - job.EndTime = job.StartTime.Add(job.Duration) - } - job.CorrectCreateUnix() - err = models.UpdateInferenceJob(job) - if err != nil { - log.Error("UpdateJob failed:", err) - } ctx.JSON(http.StatusOK, map[string]interface{}{ "JobID": jobID, diff --git a/routers/home.go b/routers/home.go index 85057c3a1..026491156 100755 --- a/routers/home.go +++ b/routers/home.go @@ -40,7 +40,7 @@ const ( tplExploreImages base.TplName = "explore/images" tplExploreExploreDataAnalysis base.TplName = "explore/data_analysis" tplHomeTerm base.TplName = "terms" - tplHomePrivacy base.TplName = "privacy" + tplHomePrivacy base.TplName = "privacy" ) // Home render home page @@ -93,6 +93,7 @@ func setRecommendURL(ctx *context.Context) { ctx.Data["page_dev_yunlao_desc2"] = ctx.Tr("home.page_dev_yunlao_desc2") ctx.Data["page_dev_yunlao_desc3"] = ctx.Tr("home.page_dev_yunlao_desc3") ctx.Data["page_dev_yunlao_desc4"] = ctx.Tr("home.page_dev_yunlao_desc4") + ctx.Data["page_dev_yunlao_desc5"] = ctx.Tr("home.page_dev_yunlao_desc5") ctx.Data["page_dev_yunlao_apply"] = ctx.Tr("home.page_dev_yunlao_apply") ctx.Data["page_recommend_activity"] = ctx.Tr("home.page_recommend_activity") ctx.Data["page_recommend_activity_desc"] = ctx.Tr("home.page_recommend_activity_desc") @@ -295,11 +296,10 @@ func ExploreDatasets(ctx *context.Context) { // ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled var ( - datasets []*models.Dataset - datasetsWithStar []*models.DatasetWithStar - count int64 - err error - orderBy models.SearchOrderBy + datasets []*models.Dataset + count int64 + err error + orderBy models.SearchOrderBy ) page := ctx.QueryInt("page") if page <= 0 { @@ -378,14 +378,6 @@ func ExploreDatasets(ctx *context.Context) { ctx.ServerError("SearchDatasets", err) return } - for _, dataset := range datasets { - if !ctx.IsSigned { - datasetsWithStar = append(datasetsWithStar, &models.DatasetWithStar{Dataset: *dataset, IsStaring: false}) - } else { - datasetsWithStar = append(datasetsWithStar, &models.DatasetWithStar{Dataset: *dataset, IsStaring: models.IsDatasetStaring(ctx.User.ID, dataset.ID)}) - } - - } pager := context.NewPagination(int(count), opts.PageSize, page, 5) ctx.Data["Keyword"] = opts.Keyword @@ -396,7 +388,7 @@ func ExploreDatasets(ctx *context.Context) { pager.SetDefaultParams(ctx) ctx.Data["Page"] = pager - ctx.Data["Datasets"] = datasetsWithStar + ctx.Data["Datasets"] = repository.ConvertToDatasetWithStar(ctx, datasets) ctx.Data["Total"] = count ctx.Data["PageIsDatasets"] = true ctx.HTML(200, tplExploreDataset) diff --git a/routers/private/internal.go b/routers/private/internal.go index 4731463b1..3e2eeab31 100755 --- a/routers/private/internal.go +++ b/routers/private/internal.go @@ -6,6 +6,7 @@ package private import ( + "code.gitea.io/gitea/routers/admin" "strings" "code.gitea.io/gitea/routers/repo" @@ -51,6 +52,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Get("/tool/org_stat", OrgStatisticManually) m.Post("/tool/update_repo_visit/:date", UpdateRepoVisit) m.Post("/task/history_handle/duration", repo.HandleTaskWithNoDuration) + m.Post("/resources/specification/handle_historical_task", admin.RefreshHistorySpec) }, CheckInternalToken) } diff --git a/routers/repo/ai_model_convert.go b/routers/repo/ai_model_convert.go new file mode 100644 index 000000000..9a5874956 --- /dev/null +++ b/routers/repo/ai_model_convert.go @@ -0,0 +1,795 @@ +package repo + +import ( + "bufio" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "net/http" + "os" + "path" + "strings" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/cloudbrain" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/modelarts" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/storage" + "code.gitea.io/gitea/modules/timeutil" + uuid "github.com/satori/go.uuid" +) + +const ( + tplModelManageConvertIndex = "repo/modelmanage/convertIndex" + tplModelConvertInfo = "repo/modelmanage/convertshowinfo" + PYTORCH_ENGINE = 0 + TENSORFLOW_ENGINE = 1 + MINDSPORE_ENGIN = 2 + ModelMountPath = "/model" + CodeMountPath = "/code" + DataSetMountPath = "/dataset" + LogFile = "log.txt" + DefaultBranchName = "master" + SubTaskName = "task1" + //GpuQueue = "openidgx" + Success = "S000" + //GPU_PYTORCH_IMAGE = "dockerhub.pcl.ac.cn:5000/user-images/openi:tensorRT_7_zouap" + //GPU_TENSORFLOW_IMAGE = "dockerhub.pcl.ac.cn:5000/user-images/openi:tf2onnx" + //NPU_MINDSPORE_16_IMAGE = "swr.cn-south-222.ai.pcl.cn/openi/mindspore1.6.1_train_v1_openi:v3_ascend" + //PytorchOnnxBootFile = "convert_pytorch.py" + //PytorchTrTBootFile = "convert_pytorch_tensorrt.py" + //MindsporeBootFile = "convert_mindspore.py" + //TensorFlowNpuBootFile = "convert_tensorflow.py" + //TensorFlowGpuBootFile = "convert_tensorflow_gpu.py" + + //ConvertRepoPath = "https://git.openi.org.cn/zouap/npu_test" + + CONVERT_FORMAT_ONNX = 0 + CONVERT_FORMAT_TRT = 1 + + NetOutputFormat_FP32 = 0 + NetOutputFormat_FP16 = 1 + + NPU_MINDSPORE_IMAGE_ID = 35 + NPU_TENSORFLOW_IMAGE_ID = 121 + + //GPU_Resource_Specs_ID = 1 //cpu 1, gpu 1 + + //NPU_FlavorCode = "modelarts.bm.910.arm.public.1" + //NPU_PoolID = "pool7908321a" +) + +var ( + TrainResourceSpecs *models.ResourceSpecs +) + +func SaveModelConvert(ctx *context.Context) { + log.Info("save model convert start.") + if !ctx.Repo.CanWrite(models.UnitTypeModelManage) { + ctx.JSON(200, map[string]string{ + "result_code": "1", + "message": ctx.Tr("repo.modelconvert.manage.no_operate_right"), + }) + return + } + name := ctx.Query("name") + desc := ctx.Query("desc") + modelId := ctx.Query("modelId") + modelPath := ctx.Query("ModelFile") + SrcEngine := ctx.QueryInt("SrcEngine") + InputShape := ctx.Query("inputshape") + InputDataFormat := ctx.Query("inputdataformat") + DestFormat := ctx.QueryInt("DestFormat") + NetOutputFormat := ctx.QueryInt("NetOutputFormat") + + task, err := models.QueryModelById(modelId) + if err != nil { + log.Error("no such model!", err.Error()) + ctx.JSON(200, map[string]string{ + "result_code": "1", + "message": ctx.Tr("repo.modelconvert.manage.model_not_exist"), + }) + return + } + + convertList, err := models.QueryModelConvertByRepoID(ctx.Repo.Repository.ID) + if err == nil { + for _, convert := range convertList { + if convert.Name == name { + log.Info("convert.Name=" + name + " convert.id=" + convert.ID) + ctx.JSON(200, map[string]string{ + "result_code": "1", + "message": ctx.Tr("repo.modelconvert.manage.create_error1"), + }) + return + } + } + } + + convertList, err = models.QueryModelConvertByUserID(ctx.User.ID) + if err == nil { + for _, convert := range convertList { + if isRunningTask(convert.Status) { + log.Info("convert.Status=" + convert.Status + " convert.id=" + convert.ID) + ctx.JSON(200, map[string]string{ + "result_code": "1", + "message": ctx.Tr("repo.modelconvert.manage.create_error2"), + }) + return + } + } + } + + uuid := uuid.NewV4() + id := uuid.String() + modelConvert := &models.AiModelConvert{ + ID: id, + Name: name, + Description: desc, + Status: string(models.JobWaiting), + SrcEngine: SrcEngine, + RepoId: ctx.Repo.Repository.ID, + ModelName: task.Name, + ModelVersion: task.Version, + ModelId: modelId, + ModelPath: modelPath, + DestFormat: DestFormat, + NetOutputFormat: NetOutputFormat, + InputShape: InputShape, + InputDataFormat: InputDataFormat, + UserId: ctx.User.ID, + } + models.SaveModelConvert(modelConvert) + go goCreateTask(modelConvert, ctx, task) + + ctx.JSON(200, map[string]string{ + "result_code": "0", + }) +} + +func isRunningTask(status string) bool { + stopStatus := []string{"COMPLETED", "STOPPED", "FAILED", "START_FAILED", "STOPPING", "SUCCEEDED"} + for _, sta := range stopStatus { + if sta == status { + return false + } + } + return true +} + +func goCreateTask(modelConvert *models.AiModelConvert, ctx *context.Context, task *models.AiModelManage) error { + if modelConvert.IsGpuTrainTask() { + log.Info("create gpu train job.") + return createGpuTrainJob(modelConvert, ctx, task) + } else { + //create npu job + log.Info("create npu train job.") + return createNpuTrainJob(modelConvert, ctx, task.Path) + } +} + +func createNpuTrainJob(modelConvert *models.AiModelConvert, ctx *context.Context, modelRelativePath string) error { + VersionOutputPath := "V0001" + codeLocalPath := setting.JobPath + modelConvert.ID + modelarts.CodePath + codeObsPath := "/" + setting.Bucket + modelarts.JobPath + modelConvert.ID + modelarts.CodePath + outputObsPath := "/" + setting.Bucket + modelarts.JobPath + modelConvert.ID + modelarts.OutputPath + VersionOutputPath + "/" + logObsPath := "/" + setting.Bucket + modelarts.JobPath + modelConvert.ID + modelarts.LogPath + VersionOutputPath + "/" + dataPath := "/" + modelRelativePath + + _, err := ioutil.ReadDir(codeLocalPath) + if err == nil { + deleteLocalDir(codeLocalPath) + } + if err := downloadConvertCode(setting.ModelConvert.ConvertRepoPath, codeLocalPath, DefaultBranchName); err != nil { + log.Error("downloadCode failed, server timed out: %s (%v)", setting.ModelConvert.ConvertRepoPath, err) + return err + } + if err := obsMkdir(setting.CodePathPrefix + modelConvert.ID + modelarts.OutputPath + VersionOutputPath + "/"); err != nil { + log.Error("Failed to obsMkdir_output: %s (%v)", modelConvert.ID+modelarts.OutputPath, err) + return err + } + if err := obsMkdir(setting.CodePathPrefix + modelConvert.ID + modelarts.LogPath + VersionOutputPath + "/"); err != nil { + log.Error("Failed to obsMkdir_log: %s (%v)", modelConvert.ID+modelarts.LogPath, err) + return err + } + if err := uploadCodeToObs(codeLocalPath, modelConvert.ID, ""); err != nil { + log.Error("Failed to uploadCodeToObs: %s (%v)", modelConvert.ID, err) + return err + } + deleteLocalDir(codeLocalPath) + + intputshape := strings.Split(modelConvert.InputShape, ",") + n := "256" + c := "1" + h := "28" + w := "28" + if len(intputshape) == 4 { + n = intputshape[0] + c = intputshape[1] + h = intputshape[2] + w = intputshape[3] + } + + var engineId int64 + engineId = int64(NPU_MINDSPORE_IMAGE_ID) + bootfile := setting.ModelConvert.MindsporeBootFile + if modelConvert.SrcEngine == TENSORFLOW_ENGINE { + engineId = int64(NPU_TENSORFLOW_IMAGE_ID) + bootfile = setting.ModelConvert.TensorFlowNpuBootFile + } + userCommand := "/bin/bash /home/work/run_train.sh 's3://" + codeObsPath + "' 'code/" + bootfile + "' '/tmp/log/train.log' --'data_url'='s3://" + dataPath + "' --'train_url'='s3://" + outputObsPath + "'" + userCommand += " --'model'='" + modelConvert.ModelPath + "'" + userCommand += " --'n'='" + fmt.Sprint(n) + "'" + userCommand += " --'c'='" + fmt.Sprint(c) + "'" + userCommand += " --'h'='" + fmt.Sprint(h) + "'" + userCommand += " --'w'='" + fmt.Sprint(w) + "'" + + req := &modelarts.GenerateTrainJobReq{ + JobName: modelConvert.ID, + DisplayJobName: modelConvert.Name, + DataUrl: dataPath, + Description: modelConvert.Description, + CodeObsPath: codeObsPath, + BootFileUrl: codeObsPath + bootfile, + BootFile: bootfile, + TrainUrl: outputObsPath, + FlavorCode: setting.ModelConvert.NPU_FlavorCode, + WorkServerNumber: 1, + IsLatestVersion: modelarts.IsLatestVersion, + EngineID: engineId, + LogUrl: logObsPath, + PoolID: setting.ModelConvert.NPU_PoolID, + //Parameters: param, + BranchName: DefaultBranchName, + UserImageUrl: setting.ModelConvert.NPU_MINDSPORE_16_IMAGE, + UserCommand: userCommand, + } + result, err := modelarts.GenerateModelConvertTrainJob(req) + if err == nil { + log.Info("jobId=" + fmt.Sprint(result.JobID) + " versionid=" + fmt.Sprint(result.VersionID)) + models.UpdateModelConvertModelArts(modelConvert.ID, fmt.Sprint(result.JobID), fmt.Sprint(result.VersionID)) + } else { + log.Info("create modelarts taks failed.error=" + err.Error()) + models.UpdateModelConvertFailed(modelConvert.ID, "FAILED", err.Error()) + } + return err +} + +func downloadConvertCode(repopath string, codePath, branchName string) error { + //add "file:///" prefix to make the depth valid + if err := git.Clone(repopath, codePath, git.CloneRepoOptions{Branch: branchName, Depth: 1}); err != nil { + log.Error("Failed to clone repository: %s (%v)", repopath, err) + return err + } + log.Info("srcPath=" + repopath + " codePath=" + codePath) + configFile, err := os.OpenFile(codePath+"/.git/config", os.O_RDWR, 0666) + if err != nil { + log.Error("open file(%s) failed:%v", codePath+"/,git/config", err) + return err + } + + defer configFile.Close() + + pos := int64(0) + reader := bufio.NewReader(configFile) + for { + line, err := reader.ReadString('\n') + if err != nil { + if err == io.EOF { + log.Error("not find the remote-url") + return nil + } else { + log.Error("read error: %v", err) + return err + } + } + + if strings.Contains(line, "url") && strings.Contains(line, ".git") { + originUrl := "\turl = " + repopath + "\n" + if len(line) > len(originUrl) { + originUrl += strings.Repeat(" ", len(line)-len(originUrl)) + } + bytes := []byte(originUrl) + _, err := configFile.WriteAt(bytes, pos) + if err != nil { + log.Error("WriteAt failed:%v", err) + return err + } + break + } + + pos += int64(len(line)) + } + + return nil +} + +func downloadFromObsToLocal(task *models.AiModelManage, localPath string) error { + path := Model_prefix + models.AttachmentRelativePath(task.ID) + "/" + allFile, err := storage.GetAllObjectByBucketAndPrefix(setting.Bucket, path) + if err == nil { + _, errState := os.Stat(localPath) + if errState != nil { + if err = os.MkdirAll(localPath, os.ModePerm); err != nil { + return err + } + } + for _, oneFile := range allFile { + if oneFile.IsDir { + log.Info(" dir name:" + oneFile.FileName) + } else { + allFileName := localPath + "/" + oneFile.FileName + index := strings.LastIndex(allFileName, "/") + if index != -1 { + parentDir := allFileName[0:index] + if err = os.MkdirAll(parentDir, os.ModePerm); err != nil { + log.Info("make dir may be error," + err.Error()) + } + } + fDest, err := os.Create(allFileName) + if err != nil { + log.Info("create file error, download file failed: %s\n", err.Error()) + return err + } + body, err := storage.ObsDownloadAFile(setting.Bucket, path+oneFile.FileName) + if err != nil { + log.Info("download file failed: %s\n", err.Error()) + return err + } else { + defer body.Close() + p := make([]byte, 1024) + var readErr error + var readCount int + // 读取对象内容 + for { + readCount, readErr = body.Read(p) + if readCount > 0 { + fDest.Write(p[:readCount]) + } + if readErr != nil { + break + } + } + } + } + } + } else { + log.Info("error,msg=" + err.Error()) + return err + } + return nil +} + +func createGpuTrainJob(modelConvert *models.AiModelConvert, ctx *context.Context, model *models.AiModelManage) error { + modelRelativePath := model.Path + command := "" + IMAGE_URL := setting.ModelConvert.GPU_PYTORCH_IMAGE + dataActualPath := setting.Attachment.Minio.RealPath + modelRelativePath + + if modelConvert.SrcEngine == PYTORCH_ENGINE { + if modelConvert.DestFormat == CONVERT_FORMAT_ONNX { + command = getGpuModelConvertCommand(modelConvert.ID, modelConvert.ModelPath, modelConvert, setting.ModelConvert.PytorchOnnxBootFile) + } else if modelConvert.DestFormat == CONVERT_FORMAT_TRT { + command = getGpuModelConvertCommand(modelConvert.ID, modelConvert.ModelPath, modelConvert, setting.ModelConvert.PytorchTrTBootFile) + } else { + return errors.New("Not support the format.") + } + } else if modelConvert.SrcEngine == TENSORFLOW_ENGINE { + IMAGE_URL = setting.ModelConvert.GPU_TENSORFLOW_IMAGE + if modelConvert.DestFormat == CONVERT_FORMAT_ONNX { + command = getGpuModelConvertCommand(modelConvert.ID, modelConvert.ModelPath, modelConvert, setting.ModelConvert.TensorFlowGpuBootFile) + } else { + return errors.New("Not support the format.") + } + //如果模型在OBS上,需要下载到本地,并上传到minio中 + if model.Type == models.TypeCloudBrainTwo { + relatetiveModelPath := setting.JobPath + modelConvert.ID + "/dataset" + log.Info("local dataset path:" + relatetiveModelPath) + downloadFromObsToLocal(model, relatetiveModelPath) + uploadCodeToMinio(relatetiveModelPath+"/", modelConvert.ID, "/dataset/") + deleteLocalDir(relatetiveModelPath) + dataActualPath = setting.Attachment.Minio.RealPath + setting.Attachment.Minio.Bucket + "/" + setting.CBCodePathPrefix + modelConvert.ID + "/dataset" + } + } + log.Info("dataActualPath=" + dataActualPath) + + log.Info("command=" + command) + + codePath := setting.JobPath + modelConvert.ID + CodeMountPath + downloadConvertCode(setting.ModelConvert.ConvertRepoPath, codePath, DefaultBranchName) + + uploadCodeToMinio(codePath+"/", modelConvert.ID, CodeMountPath+"/") + deleteLocalDir(codePath) + + minioCodePath := setting.Attachment.Minio.RealPath + setting.Attachment.Minio.Bucket + "/" + setting.CBCodePathPrefix + modelConvert.ID + "/code" + log.Info("minio codePath=" + minioCodePath) + + modelPath := setting.JobPath + modelConvert.ID + ModelMountPath + "/" + log.Info("local modelPath=" + modelPath) + mkModelPath(modelPath) + + uploadCodeToMinio(modelPath, modelConvert.ID, ModelMountPath+"/") + deleteLocalDir(modelPath) + + minioModelPath := setting.Attachment.Minio.RealPath + setting.Attachment.Minio.Bucket + "/" + setting.CBCodePathPrefix + modelConvert.ID + "/model" + log.Info("minio model path=" + minioModelPath) + + if TrainResourceSpecs == nil { + json.Unmarshal([]byte(setting.TrainResourceSpecs), &TrainResourceSpecs) + } + resourceSpec := TrainResourceSpecs.ResourceSpec[setting.ModelConvert.GPU_Resource_Specs_ID] + jobResult, err := cloudbrain.CreateJob(modelConvert.ID, models.CreateJobParams{ + JobName: modelConvert.ID, + RetryCount: 1, + GpuType: setting.ModelConvert.GpuQueue, + Image: IMAGE_URL, + TaskRoles: []models.TaskRole{ + { + Name: SubTaskName, + TaskNumber: 1, + MinSucceededTaskCount: 1, + MinFailedTaskCount: 1, + CPUNumber: resourceSpec.CpuNum, + GPUNumber: resourceSpec.GpuNum, + MemoryMB: resourceSpec.MemMiB, + ShmMB: resourceSpec.ShareMemMiB, + Command: command, + NeedIBDevice: false, + IsMainRole: false, + UseNNI: false, + }, + }, + Volumes: []models.Volume{ + { + HostPath: models.StHostPath{ + Path: minioCodePath, + MountPath: CodeMountPath, + ReadOnly: false, + }, + }, + { + HostPath: models.StHostPath{ + Path: dataActualPath, + MountPath: DataSetMountPath, + ReadOnly: true, + }, + }, + { + HostPath: models.StHostPath{ + Path: minioModelPath, + MountPath: ModelMountPath, + ReadOnly: false, + }, + }, + }, + }) + if err != nil { + log.Error("CreateJob failed:", err.Error(), ctx.Data["MsgID"]) + models.UpdateModelConvertFailed(modelConvert.ID, "FAILED", err.Error()) + return err + } + if jobResult.Code != Success { + log.Error("CreateJob(%s) failed:%s", modelConvert.ID, jobResult.Msg, ctx.Data["MsgID"]) + models.UpdateModelConvertFailed(modelConvert.ID, "FAILED", err.Error()) + return errors.New(jobResult.Msg) + } + + var jobID = jobResult.Payload["jobId"].(string) + log.Info("jobId=" + jobID) + models.UpdateModelConvertCBTI(modelConvert.ID, jobID) + + return nil +} + +func deleteLocalDir(dirpath string) { + //TODO delete + _err := os.RemoveAll(dirpath) + if _err == nil { + log.Info("Delete local file:" + dirpath) + } else { + log.Info("Delete local file error: path=" + dirpath) + } +} + +func getGpuModelConvertCommand(name string, modelFile string, modelConvert *models.AiModelConvert, bootfile string) string { + var command string + + inputshape := strings.Split(modelConvert.InputShape, ",") + n := "256" + c := "1" + h := "28" + w := "28" + if len(inputshape) == 4 { + n = inputshape[0] + c = inputshape[1] + h = inputshape[2] + w = inputshape[3] + } + command += "python3 /code/" + bootfile + " --model " + modelFile + " --n " + n + " --c " + c + " --h " + h + " --w " + w + if modelConvert.DestFormat == CONVERT_FORMAT_TRT { + if modelConvert.NetOutputFormat == NetOutputFormat_FP16 { + command += " --fp16 True" + } else { + command += " --fp16 False" + } + } + command += " > " + ModelMountPath + "/" + name + "-" + LogFile + return command +} + +func DeleteModelConvert(ctx *context.Context) { + log.Info("delete model convert start.") + id := ctx.Params(":id") + task, err := models.QueryModelConvertById(id) + if err == nil { + go deleteCloudBrainTask(task) + } + err = models.DeleteModelConvertById(id) + //TODO delete OBS文件及云脑任务 + if err != nil { + ctx.JSON(500, err.Error()) + } else { + ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelmanage/convert_model") + } +} + +func deleteCloudBrainTask(task *models.AiModelConvert) { + if task.IsGpuTrainTask() { + log.Info("delete cloudbrain one resource.") + dirPath := setting.CBCodePathPrefix + task.ID + "/" + err := storage.Attachments.DeleteDir(dirPath) + if err != nil { + log.Error("DeleteDir(%s) failed:%v", dirPath, err) + } + } else { + log.Info("delete cloudbrain two resource.") + _, err := modelarts.DelTrainJob(task.CloudBrainTaskId) + if err != nil { + log.Error("DelTrainJob(%s) failed:%v", task.CloudBrainTaskId, err.Error()) + } + DeleteJobStorage(task.ID) + } +} + +func StopModelConvert(ctx *context.Context) { + id := ctx.Params(":id") + log.Info("stop model convert start.id=" + id) + job, err := models.QueryModelConvertById(id) + if err != nil { + ctx.ServerError("Not found task.", err) + return + } + if job.IsGpuTrainTask() { + err = cloudbrain.StopJob(job.CloudBrainTaskId) + if err != nil { + log.Error("Stop cloudbrain Job(%s) failed:%v", job.CloudBrainTaskId, err) + } + } else { + _, err = modelarts.StopTrainJob(job.CloudBrainTaskId, job.ModelArtsVersionId) + if err != nil { + log.Error("Stop modelarts Job(%s) failed:%v", job.CloudBrainTaskId, err) + } + } + job.Status = string(models.JobStopped) + if job.EndTime == 0 { + job.EndTime = timeutil.TimeStampNow() + } + models.ModelConvertSetDuration(job) + err = models.UpdateModelConvert(job) + if err != nil { + log.Error("UpdateModelConvert failed:", err) + } + ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelmanage/convert_model") +} + +func ShowModelConvertInfo(ctx *context.Context) { + ctx.Data["ID"] = ctx.Query("ID") + ctx.Data["isModelManage"] = true + ctx.Data["ModelManageAccess"] = ctx.Repo.CanWrite(models.UnitTypeModelManage) + + job, err := models.QueryModelConvertById(ctx.Query("ID")) + if err == nil { + if job.TrainJobDuration == "" { + job.TrainJobDuration = "00:00:00" + } + ctx.Data["task"] = job + } else { + ctx.ServerError("Not found task.", err) + return + } + ctx.Data["Name"] = job.Name + ctx.Data["canDownload"] = isOper(ctx, job.UserId) + user, err := models.GetUserByID(job.UserId) + if err == nil { + job.UserName = user.Name + job.UserRelAvatarLink = user.RelAvatarLink() + } + + if job.IsGpuTrainTask() { + ctx.Data["npu_display"] = "none" + ctx.Data["gpu_display"] = "block" + if job.CloudBrainTaskId == "" { + ctx.Data["ExitDiagnostics"] = "" + ctx.Data["AppExitDiagnostics"] = "" + ctx.HTML(200, tplModelConvertInfo) + return + } + result, err := cloudbrain.GetJob(job.CloudBrainTaskId) + if err != nil { + log.Info("error:" + err.Error()) + ctx.Data["error"] = err.Error() + ctx.HTML(200, tplModelConvertInfo) + return + } + if result != nil { + jobRes, _ := models.ConvertToJobResultPayload(result.Payload) + ctx.Data["result"] = jobRes + taskRoles := jobRes.TaskRoles + taskRes, _ := models.ConvertToTaskPod(taskRoles[cloudbrain.SubTaskName].(map[string]interface{})) + ctx.Data["taskRes"] = taskRes + ctx.Data["ExitDiagnostics"] = taskRes.TaskStatuses[0].ExitDiagnostics + ctx.Data["AppExitDiagnostics"] = jobRes.JobStatus.AppExitDiagnostics + + job.Status = jobRes.JobStatus.State + + if jobRes.JobStatus.State != string(models.JobWaiting) && jobRes.JobStatus.State != string(models.JobFailed) { + job.ContainerIp = taskRes.TaskStatuses[0].ContainerIP + job.ContainerID = taskRes.TaskStatuses[0].ContainerID + job.Status = taskRes.TaskStatuses[0].State + } + if jobRes.JobStatus.State != string(models.JobWaiting) { + models.ModelComputeAndSetDuration(job, jobRes) + err = models.UpdateModelConvert(job) + if err != nil { + log.Error("UpdateModelConvert failed:", err) + } + } + } + } else { + if job.CloudBrainTaskId != "" { + result, err := modelarts.GetTrainJob(job.CloudBrainTaskId, job.ModelArtsVersionId) + if err != nil { + log.Info("error:" + err.Error()) + ctx.Data["error"] = err.Error() + return + } + job.Status = modelarts.TransTrainJobStatus(result.IntStatus) + job.RunTime = result.Duration / 1000 + job.TrainJobDuration = models.ConvertDurationToStr(job.RunTime) + err = models.UpdateModelConvert(job) + if err != nil { + log.Error("UpdateJob failed:", err) + } + } + ctx.Data["npu_display"] = "block" + ctx.Data["gpu_display"] = "none" + ctx.Data["ExitDiagnostics"] = "" + ctx.Data["AppExitDiagnostics"] = "" + } + + ctx.HTML(200, tplModelConvertInfo) +} + +func ConvertModelTemplate(ctx *context.Context) { + ctx.Data["isModelManage"] = true + ctx.Data["TRAIN_COUNT"] = 0 + SetModelCount(ctx) + ctx.Data["ModelManageAccess"] = ctx.Repo.CanWrite(models.UnitTypeModelManage) + ShowModelConvertPageInfo(ctx) + ctx.HTML(200, tplModelManageConvertIndex) +} + +func ShowModelConvertPageInfo(ctx *context.Context) { + log.Info("ShowModelConvertInfo start.") + if !isQueryRight(ctx) { + log.Info("no right.") + ctx.NotFound(ctx.Req.URL.RequestURI(), nil) + return + } + page := ctx.QueryInt("page") + if page <= 0 { + page = 1 + } + pageSize := ctx.QueryInt("pageSize") + if pageSize <= 0 { + pageSize = setting.UI.IssuePagingNum + } + repoId := ctx.Repo.Repository.ID + modelResult, count, err := models.QueryModelConvert(&models.AiModelQueryOptions{ + ListOptions: models.ListOptions{ + Page: page, + PageSize: pageSize, + }, + RepoID: repoId, + }) + if err != nil { + log.Info("query db error." + err.Error()) + ctx.ServerError("Cloudbrain", err) + return + } + ctx.Data["MODEL_CONVERT_COUNT"] = count + userIds := make([]int64, len(modelResult)) + for i, model := range modelResult { + model.IsCanOper = isOper(ctx, model.UserId) + model.IsCanDelete = isCanDelete(ctx, model.UserId) + userIds[i] = model.UserId + } + userNameMap := queryUserName(userIds) + for _, model := range modelResult { + value := userNameMap[model.UserId] + if value != nil { + model.UserName = value.Name + model.UserRelAvatarLink = value.RelAvatarLink() + } + } + pager := context.NewPagination(int(count), page, pageSize, 5) + ctx.Data["Page"] = pager + ctx.Data["Tasks"] = modelResult + +} + +func ModelConvertDownloadModel(ctx *context.Context) { + log.Info("enter here......") + id := ctx.Params(":id") + job, err := models.QueryModelConvertById(id) + if err != nil { + ctx.ServerError("Not found task.", err) + return + } + AllDownload := ctx.QueryBool("AllDownload") + if AllDownload { + if job.IsGpuTrainTask() { + path := setting.CBCodePathPrefix + job.ID + "/model/" + allFile, err := storage.GetAllObjectByBucketAndPrefixMinio(setting.Attachment.Minio.Bucket, path) + if err == nil { + returnFileName := job.Name + ".zip" + MinioDownloadManyFile(path, ctx, returnFileName, allFile) + } else { + log.Info("error,msg=" + err.Error()) + ctx.ServerError("no file to download.", err) + } + } else { + Prefix := path.Join(setting.TrainJobModelPath, job.ID, "output/", "V0001", "") + "/" + log.Info("bucket=" + setting.Bucket + "prefix=" + Prefix) + allFile, err := storage.GetAllObjectByBucketAndPrefix(setting.Bucket, Prefix) + if err == nil { + returnFileName := job.Name + ".zip" + ObsDownloadManyFile(Prefix, ctx, returnFileName, allFile) + } else { + log.Info("error,msg=" + err.Error()) + ctx.ServerError("no file to download.", err) + } + } + } else { + parentDir := ctx.Query("parentDir") + fileName := ctx.Query("fileName") + jobName := ctx.Query("jobName") + if job.IsGpuTrainTask() { + filePath := "jobs/" + jobName + "/model/" + parentDir + url, err := storage.Attachments.PresignedGetURL(filePath, fileName) + if err != nil { + log.Error("PresignedGetURL failed: %v", err.Error(), ctx.Data["msgID"]) + ctx.ServerError("PresignedGetURL", err) + return + } + //ctx.JSON(200, url) + http.Redirect(ctx.Resp, ctx.Req.Request, url, http.StatusTemporaryRedirect) + } else { + ObjectKey := path.Join(setting.TrainJobModelPath, job.ID, "output/", "V0001", parentDir, fileName) + log.Info("ObjectKey=" + ObjectKey) + url, err := storage.GetObsCreateSignedUrlByBucketAndKey(setting.Bucket, ObjectKey) + if err != nil { + log.Error("GetObsCreateSignedUrl failed: %v", err.Error(), ctx.Data["msgID"]) + ctx.ServerError("GetObsCreateSignedUrl", err) + return + } + http.Redirect(ctx.Resp, ctx.Req.Request, url, http.StatusTemporaryRedirect) + } + } + +} diff --git a/routers/repo/ai_model_manage.go b/routers/repo/ai_model_manage.go index 2c24527ce..d01539a75 100644 --- a/routers/repo/ai_model_manage.go +++ b/routers/repo/ai_model_manage.go @@ -26,6 +26,7 @@ const ( tplModelInfo = "repo/modelmanage/showinfo" MODEL_LATEST = 1 MODEL_NOT_LATEST = 0 + MODEL_MAX_SIZE = 1024 * 1024 * 1024 ) func saveModelByParameters(jobId string, versionName string, name string, version string, label string, description string, engine int, ctx *context.Context) error { @@ -58,14 +59,16 @@ func saveModelByParameters(jobId string, versionName string, name string, versio } } cloudType := aiTask.Type + modelSelectedFile := ctx.Query("modelSelectedFile") //download model zip //train type - if cloudType == models.TypeCloudBrainTwo { - modelPath, modelSize, err = downloadModelFromCloudBrainTwo(id, aiTask.JobName, "", aiTask.TrainUrl) + if aiTask.ComputeResource == models.NPUResource { + modelPath, modelSize, err = downloadModelFromCloudBrainTwo(id, aiTask.JobName, "", aiTask.TrainUrl, modelSelectedFile) if err != nil { log.Info("download model from CloudBrainTwo faild." + err.Error()) return err } - } else if cloudType == models.TypeCloudBrainOne { + cloudType = models.TypeCloudBrainTwo + } else if aiTask.ComputeResource == models.GPUResource { var ResourceSpecs *models.ResourceSpecs json.Unmarshal([]byte(setting.ResourceSpecs), &ResourceSpecs) for _, tmp := range ResourceSpecs.ResourceSpec { @@ -74,12 +77,24 @@ func saveModelByParameters(jobId string, versionName string, name string, versio aiTask.FlavorName = flaverName } } - modelPath, modelSize, err = downloadModelFromCloudBrainOne(id, aiTask.JobName, "", aiTask.TrainUrl) + modelPath, modelSize, err = downloadModelFromCloudBrainOne(id, aiTask.JobName, "", aiTask.TrainUrl, modelSelectedFile) if err != nil { log.Info("download model from CloudBrainOne faild." + err.Error()) return err } - } + cloudType = models.TypeCloudBrainOne + } + // else if cloudType == models.TypeC2Net { + // if aiTask.ComputeResource == models.NPUResource { + // modelPath, modelSize, err = downloadModelFromCloudBrainTwo(id, aiTask.JobName, "", aiTask.TrainUrl, modelSelectedFile) + // if err != nil { + // log.Info("download model from CloudBrainTwo faild." + err.Error()) + // return err + // } + // } else if aiTask.ComputeResource == models.GPUResource { + + // } + // } accuracy := make(map[string]string) accuracy["F1"] = "" accuracy["Recall"] = "" @@ -137,6 +152,10 @@ func saveModelByParameters(jobId string, versionName string, name string, versio } func SaveNewNameModel(ctx *context.Context) { + if !ctx.Repo.CanWrite(models.UnitTypeModelManage) { + ctx.Error(403, ctx.Tr("repo.model_noright")) + return + } name := ctx.Query("Name") if name == "" { ctx.Error(500, fmt.Sprintf("name or version is null.")) @@ -154,6 +173,10 @@ func SaveNewNameModel(ctx *context.Context) { } func SaveModel(ctx *context.Context) { + if !ctx.Repo.CanWrite(models.UnitTypeModelManage) { + ctx.Error(403, ctx.Tr("repo.model_noright")) + return + } log.Info("save model start.") JobId := ctx.Query("JobId") VersionName := ctx.Query("VersionName") @@ -162,20 +185,17 @@ func SaveModel(ctx *context.Context) { label := ctx.Query("Label") description := ctx.Query("Description") engine := ctx.QueryInt("Engine") - trainTaskCreate := ctx.QueryBool("trainTaskCreate") - log.Info("engine=" + fmt.Sprint(engine)) - if !trainTaskCreate { - if !ctx.Repo.CanWrite(models.UnitTypeModelManage) { - //ctx.NotFound(ctx.Req.URL.RequestURI(), nil) - ctx.JSON(403, ctx.Tr("repo.model_noright")) - return - } - } + modelSelectedFile := ctx.Query("modelSelectedFile") + log.Info("engine=" + fmt.Sprint(engine) + " modelSelectedFile=" + modelSelectedFile) if JobId == "" || VersionName == "" { ctx.Error(500, fmt.Sprintf("JobId or VersionName is null.")) return } + if modelSelectedFile == "" { + ctx.Error(500, fmt.Sprintf("Not selected model file.")) + return + } if name == "" || version == "" { ctx.Error(500, fmt.Sprintf("name or version is null.")) @@ -193,11 +213,22 @@ func SaveModel(ctx *context.Context) { log.Info("save model end.") } -func downloadModelFromCloudBrainTwo(modelUUID string, jobName string, parentDir string, trainUrl string) (string, int64, error) { +func downloadModelFromCloudBrainTwo(modelUUID string, jobName string, parentDir string, trainUrl string, modelSelectedFile string) (string, int64, error) { objectkey := strings.TrimPrefix(path.Join(setting.TrainJobModelPath, jobName, setting.OutPutPath, parentDir), "/") if trainUrl != "" { objectkey = strings.Trim(trainUrl[len(setting.Bucket)+1:], "/") } + prefix := objectkey + "/" + + filterFiles := strings.Split(modelSelectedFile, ";") + Files := make([]string, 0) + for _, shortFile := range filterFiles { + Files = append(Files, prefix+shortFile) + } + totalSize := storage.ObsGetFilesSize(setting.Bucket, Files) + if float64(totalSize) > setting.MaxModelSize*MODEL_MAX_SIZE { + return "", 0, errors.New("Cannot create model, as model is exceed " + fmt.Sprint(setting.MaxModelSize) + "G.") + } modelDbResult, err := storage.GetOneLevelAllObjectUnderDir(setting.Bucket, objectkey, "") log.Info("bucket=" + setting.Bucket + " objectkey=" + objectkey) @@ -206,26 +237,33 @@ func downloadModelFromCloudBrainTwo(modelUUID string, jobName string, parentDir return "", 0, err } if len(modelDbResult) == 0 { - return "", 0, errors.New("cannot create model, as model is empty.") + return "", 0, errors.New("Cannot create model, as model is empty.") } - prefix := objectkey + "/" destKeyNamePrefix := Model_prefix + models.AttachmentRelativePath(modelUUID) + "/" - - size, err := storage.ObsCopyManyFile(setting.Bucket, prefix, setting.Bucket, destKeyNamePrefix) + size, err := storage.ObsCopyManyFile(setting.Bucket, prefix, setting.Bucket, destKeyNamePrefix, filterFiles) dataActualPath := setting.Bucket + "/" + destKeyNamePrefix return dataActualPath, size, nil } -func downloadModelFromCloudBrainOne(modelUUID string, jobName string, parentDir string, trainUrl string) (string, int64, error) { +func downloadModelFromCloudBrainOne(modelUUID string, jobName string, parentDir string, trainUrl string, modelSelectedFile string) (string, int64, error) { modelActualPath := storage.GetMinioPath(jobName, "/model/") log.Info("modelActualPath=" + modelActualPath) modelSrcPrefix := setting.CBCodePathPrefix + jobName + "/model/" destKeyNamePrefix := Model_prefix + models.AttachmentRelativePath(modelUUID) + "/" bucketName := setting.Attachment.Minio.Bucket log.Info("destKeyNamePrefix=" + destKeyNamePrefix + " modelSrcPrefix=" + modelSrcPrefix + " bucket=" + bucketName) - size, err := storage.MinioPathCopy(bucketName, modelSrcPrefix, destKeyNamePrefix) + filterFiles := strings.Split(modelSelectedFile, ";") + Files := make([]string, 0) + for _, shortFile := range filterFiles { + Files = append(Files, modelSrcPrefix+shortFile) + } + totalSize := storage.MinioGetFilesSize(bucketName, Files) + if float64(totalSize) > setting.MaxModelSize*MODEL_MAX_SIZE { + return "", 0, errors.New("Cannot create model, as model is exceed " + fmt.Sprint(setting.MaxModelSize) + "G.") + } + size, err := storage.MinioCopyFiles(bucketName, modelSrcPrefix, destKeyNamePrefix, filterFiles) if err == nil { dataActualPath := bucketName + "/" + destKeyNamePrefix return dataActualPath, size, nil @@ -459,6 +497,35 @@ func QueryTrainJobList(ctx *context.Context) { } +func QueryTrainModelList(ctx *context.Context) { + log.Info("query train job list. start.") + jobName := ctx.Query("jobName") + taskType := ctx.QueryInt("type") + VersionName := ctx.Query("VersionName") + if taskType == models.TypeCloudBrainTwo { + objectkey := path.Join(setting.TrainJobModelPath, jobName, setting.OutPutPath, VersionName) + "/" + modelDbResult, err := storage.GetAllObjectByBucketAndPrefix(setting.Bucket, objectkey) + log.Info("bucket=" + setting.Bucket + " objectkey=" + objectkey) + if err != nil { + log.Info("get TypeCloudBrainTwo TrainJobListModel failed:", err) + } else { + ctx.JSON(200, modelDbResult) + return + } + } else if taskType == models.TypeCloudBrainOne { + modelSrcPrefix := setting.CBCodePathPrefix + jobName + "/model/" + bucketName := setting.Attachment.Minio.Bucket + modelDbResult, err := storage.GetAllObjectByBucketAndPrefixMinio(bucketName, modelSrcPrefix) + if err != nil { + log.Info("get TypeCloudBrainOne TrainJobListModel failed:", err) + } else { + ctx.JSON(200, modelDbResult) + return + } + } + ctx.JSON(200, "") +} + func DownloadSingleModelFile(ctx *context.Context) { log.Info("DownloadSingleModelFile start.") id := ctx.Params(":ID") @@ -766,7 +833,7 @@ func QueryModelListForPredict(ctx *context.Context) { PageSize: -1, }, RepoID: repoId, - Type: -1, + Type: ctx.QueryInt("type"), New: -1, }) if err != nil { diff --git a/routers/repo/cloudbrain.go b/routers/repo/cloudbrain.go index d5df01e79..2c78d37dd 100755 --- a/routers/repo/cloudbrain.go +++ b/routers/repo/cloudbrain.go @@ -3,6 +3,7 @@ package repo import ( "bufio" "code.gitea.io/gitea/services/reward/point/account" + "code.gitea.io/gitea/services/cloudbrain/resource" "encoding/json" "errors" "fmt" @@ -16,6 +17,8 @@ import ( "time" "unicode/utf8" + "code.gitea.io/gitea/modules/notification" + "code.gitea.io/gitea/modules/grampus" "code.gitea.io/gitea/modules/timeutil" @@ -48,6 +51,9 @@ const ( tplCloudBrainTrainJobNew base.TplName = "repo/cloudbrain/trainjob/new" tplCloudBrainTrainJobShow base.TplName = "repo/cloudbrain/trainjob/show" + + tplCloudBrainInferenceJobNew base.TplName = "repo/cloudbrain/inference/new" + tplCloudBrainInferenceJobShow base.TplName = "repo/cloudbrain/inference/show" ) var ( @@ -57,6 +63,7 @@ var ( benchmarkGpuInfos *models.GpuInfos benchmarkResourceSpecs *models.ResourceSpecs trainGpuInfos *models.GpuInfos + inferenceGpuInfos *models.GpuInfos ) const BENCHMARK_TYPE_CODE = "repo.cloudbrain.benchmark.types" @@ -98,46 +105,7 @@ func cloudBrainNewDataPrepare(ctx *context.Context) error { var displayJobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:] ctx.Data["display_job_name"] = displayJobName - result, err := cloudbrain.GetImages() - if err != nil { - ctx.Data["error"] = err.Error() - log.Error("cloudbrain.GetImages failed:", err.Error(), ctx.Data["MsgID"]) - } - - for i, payload := range result.Payload.ImageInfo { - if strings.HasPrefix(result.Payload.ImageInfo[i].Place, "192.168") { - result.Payload.ImageInfo[i].PlaceView = payload.Place[strings.Index(payload.Place, "/"):len(payload.Place)] - } else { - result.Payload.ImageInfo[i].PlaceView = payload.Place - } - } - - ctx.Data["images"] = result.Payload.ImageInfo - - resultPublic, err := cloudbrain.GetPublicImages() - if err != nil { - ctx.Data["error"] = err.Error() - log.Error("cloudbrain.GetPublicImages failed:", err.Error(), ctx.Data["MsgID"]) - } - - for i, payload := range resultPublic.Payload.ImageInfo { - if strings.HasPrefix(resultPublic.Payload.ImageInfo[i].Place, "192.168") { - resultPublic.Payload.ImageInfo[i].PlaceView = payload.Place[strings.Index(payload.Place, "/"):len(payload.Place)] - } else { - resultPublic.Payload.ImageInfo[i].PlaceView = payload.Place - } - } - - ctx.Data["public_images"] = resultPublic.Payload.ImageInfo - - attachs, err := models.GetAllUserAttachments(ctx.User.ID) - if err != nil { - log.Error("GetAllUserAttachments failed: %v", err, ctx.Data["MsgID"]) - return err - } - - ctx.Data["attachments"] = attachs - ctx.Data["command"] = cloudbrain.Command + ctx.Data["command"] = cloudbrain.GetCloudbrainDebugCommand() ctx.Data["code_path"] = cloudbrain.CodeMountPath ctx.Data["dataset_path"] = cloudbrain.DataSetMountPath ctx.Data["model_path"] = cloudbrain.ModelMountPath @@ -150,76 +118,12 @@ func cloudBrainNewDataPrepare(ctx *context.Context) error { ctx.Data["benchmark_categories"] = categories.Category ctx.Data["benchmark_types"] = GetBenchmarkTypes(ctx).BenchmarkType - - cloudbrain.InitSpecialPool() - - if gpuInfos == nil { - json.Unmarshal([]byte(setting.GpuTypes), &gpuInfos) - } - ctx.Data["gpu_types"] = gpuInfos.GpuInfo - - if trainGpuInfos == nil { - json.Unmarshal([]byte(setting.TrainGpuTypes), &trainGpuInfos) - } - ctx.Data["train_gpu_types"] = trainGpuInfos.GpuInfo - - if benchmarkGpuInfos == nil { - json.Unmarshal([]byte(setting.BenchmarkGpuTypes), &benchmarkGpuInfos) - } - ctx.Data["benchmark_gpu_types"] = benchmarkGpuInfos.GpuInfo - - if benchmarkResourceSpecs == nil { - json.Unmarshal([]byte(setting.BenchmarkResourceSpecs), &benchmarkResourceSpecs) + queuesDetail, _ := cloudbrain.GetQueuesDetail() + if queuesDetail != nil { + ctx.Data["QueuesDetail"] = queuesDetail } - ctx.Data["benchmark_resource_specs"] = benchmarkResourceSpecs.ResourceSpec - if cloudbrain.ResourceSpecs == nil { - json.Unmarshal([]byte(setting.ResourceSpecs), &cloudbrain.ResourceSpecs) - } - ctx.Data["resource_specs"] = cloudbrain.ResourceSpecs.ResourceSpec - - if cloudbrain.TrainResourceSpecs == nil { - json.Unmarshal([]byte(setting.TrainResourceSpecs), &cloudbrain.TrainResourceSpecs) - } - ctx.Data["train_resource_specs"] = cloudbrain.TrainResourceSpecs.ResourceSpec - - if cloudbrain.SpecialPools != nil { - var debugGpuTypes []*models.GpuInfo - var trainGpuTypes []*models.GpuInfo - - for _, pool := range cloudbrain.SpecialPools.Pools { - org, _ := models.GetOrgByName(pool.Org) - if org != nil { - isOrgMember, _ := models.IsOrganizationMember(org.ID, ctx.User.ID) - if isOrgMember { - for _, jobType := range pool.JobType { - if jobType == string(models.JobTypeDebug) { - debugGpuTypes = append(debugGpuTypes, pool.Pool...) - if pool.ResourceSpec != nil { - ctx.Data["resource_specs"] = pool.ResourceSpec - } - } else if jobType == string(models.JobTypeTrain) { - trainGpuTypes = append(trainGpuTypes, pool.Pool...) - if pool.ResourceSpec != nil { - ctx.Data["train_resource_specs"] = pool.ResourceSpec - } - } - } - break - } - } - - } - - if len(debugGpuTypes) > 0 { - ctx.Data["gpu_types"] = debugGpuTypes - } - - if len(trainGpuTypes) > 0 { - ctx.Data["train_gpu_types"] = trainGpuTypes - } - - } + prepareCloudbrainOneSpecs(ctx) ctx.Data["params"] = "" ctx.Data["branchName"] = ctx.Repo.BranchName @@ -237,12 +141,47 @@ func cloudBrainNewDataPrepare(ctx *context.Context) error { return nil } +func prepareCloudbrainOneSpecs(ctx *context.Context) { + debugSpecs, _ := resource.FindAvailableSpecs(ctx.User.ID, models.FindSpecsOptions{ + JobType: models.JobTypeDebug, + ComputeResource: models.GPU, + Cluster: models.OpenICluster, + AiCenterCode: models.AICenterOfCloudBrainOne, + }) + ctx.Data["debug_specs"] = debugSpecs + + trainSpecs, _ := resource.FindAvailableSpecs(ctx.User.ID, models.FindSpecsOptions{ + JobType: models.JobTypeTrain, + ComputeResource: models.GPU, + Cluster: models.OpenICluster, + AiCenterCode: models.AICenterOfCloudBrainOne, + }) + ctx.Data["train_specs"] = trainSpecs + + inferenceSpecs, _ := resource.FindAvailableSpecs(ctx.User.ID, models.FindSpecsOptions{ + JobType: models.JobTypeInference, + ComputeResource: models.GPU, + Cluster: models.OpenICluster, + AiCenterCode: models.AICenterOfCloudBrainOne, + }) + ctx.Data["inference_specs"] = inferenceSpecs + + benchmarkSpecs, _ := resource.FindAvailableSpecs(ctx.User.ID, models.FindSpecsOptions{ + JobType: models.JobTypeBenchmark, + ComputeResource: models.GPU, + Cluster: models.OpenICluster, + AiCenterCode: models.AICenterOfCloudBrainOne, + }) + ctx.Data["benchmark_specs"] = benchmarkSpecs +} + func CloudBrainNew(ctx *context.Context) { err := cloudBrainNewDataPrepare(ctx) if err != nil { ctx.ServerError("get new cloudbrain info failed", err) return } + ctx.Data["PageIsGPUDebug"] = true ctx.HTML(200, tplCloudBrainNew) } @@ -253,10 +192,9 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) { image := strings.TrimSpace(form.Image) uuids := form.Attachment jobType := form.JobType - gpuQueue := form.GpuType codePath := setting.JobPath + jobName + cloudbrain.CodeMountPath - resourceSpecId := form.ResourceSpecId branchName := form.BranchName + bootFile := strings.TrimSpace(form.BootFile) repo := ctx.Repo.Repository tpl := tplCloudBrainNew @@ -289,6 +227,7 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) { } if !jobNamePattern.MatchString(displayJobName) { + cloudBrainNewDataPrepare(ctx) ctx.RenderWithErr(ctx.Tr("repo.cloudbrain_jobname_err"), tpl, &form) return } @@ -314,21 +253,33 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) { return } } - - datasetInfos, datasetNames, err := models.GetDatasetInfo(uuids) - if err != nil { - log.Error("GetDatasetInfo failed: %v", err, ctx.Data["MsgID"]) - cloudBrainNewDataPrepare(ctx) - ctx.RenderWithErr(ctx.Tr("cloudbrain.error.dataset_select"), tpl, &form) - return + var datasetInfos map[string]models.DatasetInfo + var datasetNames string + //var + if uuids != "" { + datasetInfos, datasetNames, err = models.GetDatasetInfo(uuids) + if err != nil { + log.Error("GetDatasetInfo failed: %v", err, ctx.Data["MsgID"]) + cloudBrainNewDataPrepare(ctx) + ctx.RenderWithErr(ctx.Tr("cloudbrain.error.dataset_select"), tpl, &form) + return + } } - command := cloudbrain.Command + command := cloudbrain.GetCloudbrainDebugCommand() if jobType == string(models.JobTypeTrain) { + bootFileExist, err := ctx.Repo.FileExists(bootFile, branchName) + if err != nil || !bootFileExist { + log.Error("Get bootfile error:", err, ctx.Data["MsgID"]) + cloudBrainNewDataPrepare(ctx) + ctx.RenderWithErr(ctx.Tr("repo.cloudbrain_bootfile_err"), tpl, &form) + return + } tpl = tplCloudBrainTrainJobNew commandTrain, err := getTrainJobCommand(form) if err != nil { log.Error("getTrainJobCommand failed: %v", err) + cloudBrainNewDataPrepare(ctx) ctx.RenderWithErr(err.Error(), tpl, &form) return } @@ -336,26 +287,29 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) { command = commandTrain } - errStr := checkCloudBrainSpecialPool(ctx, jobType, gpuQueue, resourceSpecId) - + if branchName == "" { + branchName = cloudbrain.DefaultBranchName + } + errStr := loadCodeAndMakeModelPath(repo, codePath, branchName, jobName, cloudbrain.ModelMountPath) if errStr != "" { cloudBrainNewDataPrepare(ctx) - ctx.RenderWithErr(errStr, tpl, &form) + ctx.RenderWithErr(ctx.Tr(errStr), tpl, &form) return } - if branchName == "" { - branchName = cloudbrain.DefaultBranchName - } - downloadCode(repo, codePath, branchName) - uploadCodeToMinio(codePath+"/", jobName, cloudbrain.CodeMountPath+"/") - - modelPath := setting.JobPath + jobName + cloudbrain.ModelMountPath + "/" - mkModelPath(modelPath) - uploadCodeToMinio(modelPath, jobName, cloudbrain.ModelMountPath+"/") - commitID, _ := ctx.Repo.GitRepo.GetBranchCommitID(branchName) + spec, err := resource.GetAndCheckSpec(ctx.User.ID, form.SpecId, models.FindSpecsOptions{ + JobType: models.JobType(jobType), + ComputeResource: models.GPU, + Cluster: models.OpenICluster, + AiCenterCode: models.AICenterOfCloudBrainOne}) + if err != nil || spec == nil { + cloudBrainNewDataPrepare(ctx) + ctx.RenderWithErr("Resource specification not available", tpl, &form) + return + } + req := cloudbrain.GenerateCloudBrainTaskReq{ Ctx: ctx, DisplayJobName: displayJobName, @@ -371,7 +325,6 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) { Snn4ImageNetPath: storage.GetMinioPath(jobName, cloudbrain.Snn4imagenetMountPath+"/"), BrainScorePath: storage.GetMinioPath(jobName, cloudbrain.BrainScoreMountPath+"/"), JobType: jobType, - GpuQueue: gpuQueue, Description: form.Description, BranchName: branchName, BootFile: form.BootFile, @@ -379,7 +332,8 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) { CommitID: commitID, BenchmarkTypeID: 0, BenchmarkChildTypeID: 0, - ResourceSpecId: resourceSpecId, + ResultPath: storage.GetMinioPath(jobName, cloudbrain.ResultPath+"/"), + Spec: spec, } err = cloudbrain.GenerateTask(req) @@ -396,6 +350,169 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) { } } +func loadCodeAndMakeModelPath(repo *models.Repository, codePath string, branchName string, jobName string, resultPath string) string { + err := downloadCode(repo, codePath, branchName) + if err != nil { + return "cloudbrain.load_code_failed" + } + + err = uploadCodeToMinio(codePath+"/", jobName, cloudbrain.CodeMountPath+"/") + if err != nil { + return "cloudbrain.load_code_failed" + } + + modelPath := setting.JobPath + jobName + resultPath + "/" + err = mkModelPath(modelPath) + if err != nil { + return "cloudbrain.load_code_failed" + } + err = uploadCodeToMinio(modelPath, jobName, resultPath+"/") + if err != nil { + return "cloudbrain.load_code_failed" + } + + return "" +} + +func CloudBrainInferenceJobCreate(ctx *context.Context, form auth.CreateCloudBrainInferencForm) { + ctx.Data["PageIsCloudBrain"] = true + displayJobName := form.DisplayJobName + jobName := util.ConvertDisplayJobNameToJobName(displayJobName) + image := strings.TrimSpace(form.Image) + uuid := form.Attachment + jobType := string(models.JobTypeInference) + codePath := setting.JobPath + jobName + cloudbrain.CodeMountPath + branchName := form.BranchName + bootFile := strings.TrimSpace(form.BootFile) + labelName := form.LabelName + repo := ctx.Repo.Repository + + ckptUrl := setting.Attachment.Minio.RealPath + form.TrainUrl + form.CkptName + log.Info("ckpt url:" + ckptUrl) + tpl := tplCloudBrainInferenceJobNew + command, err := getInferenceJobCommand(form) + if err != nil { + log.Error("getTrainJobCommand failed: %v", err) + cloudBrainNewDataPrepare(ctx) + ctx.RenderWithErr(err.Error(), tpl, &form) + return + } + + tasks, err := models.GetCloudbrainsByDisplayJobName(repo.ID, jobType, displayJobName) + if err == nil { + if len(tasks) != 0 { + log.Error("the job name did already exist", ctx.Data["MsgID"]) + cloudBrainNewDataPrepare(ctx) + ctx.RenderWithErr("the job name did already exist", tpl, &form) + return + } + } else { + if !models.IsErrJobNotExist(err) { + log.Error("system error, %v", err, ctx.Data["MsgID"]) + cloudBrainNewDataPrepare(ctx) + ctx.RenderWithErr("system error", tpl, &form) + return + } + } + + if !jobNamePattern.MatchString(displayJobName) { + cloudBrainNewDataPrepare(ctx) + ctx.RenderWithErr(ctx.Tr("repo.cloudbrain_jobname_err"), tpl, &form) + return + } + + bootFileExist, err := ctx.Repo.FileExists(bootFile, branchName) + if err != nil || !bootFileExist { + log.Error("Get bootfile error:", err, ctx.Data["MsgID"]) + cloudBrainNewDataPrepare(ctx) + ctx.RenderWithErr(ctx.Tr("repo.cloudbrain_bootfile_err"), tpl, &form) + return + } + + count, err := models.GetCloudbrainCountByUserID(ctx.User.ID, jobType) + if err != nil { + log.Error("GetCloudbrainCountByUserID failed:%v", err, ctx.Data["MsgID"]) + cloudBrainNewDataPrepare(ctx) + ctx.RenderWithErr("system error", tpl, &form) + return + } else { + if count >= 1 { + log.Error("the user already has running or waiting task", ctx.Data["MsgID"]) + cloudBrainNewDataPrepare(ctx) + ctx.RenderWithErr(ctx.Tr("repo.cloudbrain.morethanonejob"), tpl, &form) + return + } + } + + if branchName == "" { + branchName = cloudbrain.DefaultBranchName + } + errStr := loadCodeAndMakeModelPath(repo, codePath, branchName, jobName, cloudbrain.ResultPath) + if errStr != "" { + cloudBrainNewDataPrepare(ctx) + ctx.RenderWithErr(ctx.Tr(errStr), tpl, &form) + return + } + + commitID, _ := ctx.Repo.GitRepo.GetBranchCommitID(branchName) + + datasetInfos, datasetNames, err := models.GetDatasetInfo(uuid) + if err != nil { + log.Error("GetDatasetInfo failed: %v", err, ctx.Data["MsgID"]) + cloudBrainNewDataPrepare(ctx) + ctx.RenderWithErr(ctx.Tr("cloudbrain.error.dataset_select"), tpl, &form) + return + } + spec, err := resource.GetAndCheckSpec(ctx.User.ID, form.SpecId, models.FindSpecsOptions{ + JobType: models.JobTypeInference, + ComputeResource: models.GPU, + Cluster: models.OpenICluster, + AiCenterCode: models.AICenterOfCloudBrainOne}) + if err != nil || spec == nil { + cloudBrainNewDataPrepare(ctx) + ctx.RenderWithErr("Resource specification not available", tpl, &form) + return + } + req := cloudbrain.GenerateCloudBrainTaskReq{ + Ctx: ctx, + DisplayJobName: displayJobName, + JobName: jobName, + Image: image, + Command: command, + Uuids: uuid, + DatasetNames: datasetNames, + DatasetInfos: datasetInfos, + CodePath: storage.GetMinioPath(jobName, cloudbrain.CodeMountPath+"/"), + ModelPath: setting.Attachment.Minio.RealPath + form.TrainUrl, + BenchmarkPath: storage.GetMinioPath(jobName, cloudbrain.BenchMarkMountPath+"/"), + Snn4ImageNetPath: storage.GetMinioPath(jobName, cloudbrain.Snn4imagenetMountPath+"/"), + BrainScorePath: storage.GetMinioPath(jobName, cloudbrain.BrainScoreMountPath+"/"), + JobType: jobType, + Description: form.Description, + BranchName: branchName, + BootFile: form.BootFile, + Params: form.Params, + CommitID: commitID, + ResultPath: storage.GetMinioPath(jobName, cloudbrain.ResultPath+"/"), + ModelName: form.ModelName, + ModelVersion: form.ModelVersion, + CkptName: form.CkptName, + TrainUrl: form.TrainUrl, + LabelName: labelName, + Spec: spec, + } + + err = cloudbrain.GenerateTask(req) + if err != nil { + cloudBrainNewDataPrepare(ctx) + ctx.RenderWithErr(err.Error(), tpl, &form) + return + } + + ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/inference-job") + +} + /** 检查用户传输的参数是否符合专属资源池 */ @@ -467,23 +584,25 @@ func CloudBrainRestart(ctx *context.Context) { break } - var hasSameResource bool - if gpuInfos == nil { - json.Unmarshal([]byte(setting.GpuTypes), &gpuInfos) - } - for _, resourceType := range gpuInfos.GpuInfo { - if resourceType.Queue == task.GpuQueue { - hasSameResource = true - continue - } + specOld, err := resource.GetCloudbrainSpec(task.ID) + if err != nil || specOld == nil { + log.Error("CloudBrainRestart GetCloudbrainSpec error.task.id = %d", task.ID) + resultCode = "-1" + errorMsg = "Resource specification not support any more" + break } - - if !hasSameResource { - log.Error("has no same resource, can not restart", ctx.Data["MsgID"]) + spec, err := resource.GetAndCheckSpec(ctx.User.ID, specOld.ID, models.FindSpecsOptions{ + JobType: models.JobType(task.JobType), + ComputeResource: models.GPU, + Cluster: models.OpenICluster, + AiCenterCode: models.AICenterOfCloudBrainOne}) + if err != nil || spec == nil { + log.Error("CloudBrainRestart GetAndCheckSpec error.task.id = %d", task.ID) resultCode = "-1" - errorMsg = "the job's version is too old and can not be restarted" + errorMsg = "Resource specification not support any more" break } + task.Spec = spec count, err := models.GetCloudbrainCountByUserID(ctx.User.ID, string(models.JobTypeDebug)) if err != nil { @@ -534,10 +653,11 @@ func CloudBrainTrainJobShow(ctx *context.Context) { func cloudBrainShow(ctx *context.Context, tpName base.TplName, jobType models.JobType) { ctx.Data["PageIsCloudBrain"] = true debugListType := ctx.Query("debugListType") + cloudbrain.InitSpecialPool() var task *models.Cloudbrain var err error - if jobType == models.JobTypeTrain { + if jobType == models.JobTypeTrain || jobType == models.JobTypeInference { task, err = models.GetCloudbrainByJobID(ctx.Params(":jobid")) } else { task, err = models.GetCloudbrainByIDWithDeleted(ctx.Params(":id")) @@ -545,88 +665,37 @@ func cloudBrainShow(ctx *context.Context, tpName base.TplName, jobType models.Jo if err != nil { log.Info("error:" + err.Error()) - ctx.Data["error"] = err.Error() + ctx.NotFound(ctx.Req.URL.RequestURI(), nil) return } result, err := cloudbrain.GetJob(task.JobID) if err != nil { log.Info("error:" + err.Error()) - ctx.Data["error"] = err.Error() + ctx.NotFound(ctx.Req.URL.RequestURI(), nil) return } - - if task.JobType == string(models.JobTypeTrain) { - if cloudbrain.TrainResourceSpecs == nil { - json.Unmarshal([]byte(setting.TrainResourceSpecs), &cloudbrain.TrainResourceSpecs) - } - for _, tmp := range cloudbrain.TrainResourceSpecs.ResourceSpec { - if tmp.Id == task.ResourceSpecId { - ctx.Data["GpuNum"] = tmp.GpuNum - ctx.Data["CpuNum"] = tmp.CpuNum - ctx.Data["MemMiB"] = tmp.MemMiB - ctx.Data["ShareMemMiB"] = tmp.ShareMemMiB - } - } - } else { - if cloudbrain.ResourceSpecs == nil { - json.Unmarshal([]byte(setting.ResourceSpecs), &cloudbrain.ResourceSpecs) - } - for _, tmp := range cloudbrain.ResourceSpecs.ResourceSpec { - if tmp.Id == task.ResourceSpecId { - ctx.Data["GpuNum"] = tmp.GpuNum - ctx.Data["CpuNum"] = tmp.CpuNum - ctx.Data["MemMiB"] = tmp.MemMiB - ctx.Data["ShareMemMiB"] = tmp.ShareMemMiB - } - } + prepareSpec4Show(ctx, task) + if ctx.Written() { + return } if result != nil { jobRes, _ := models.ConvertToJobResultPayload(result.Payload) - jobRes.Resource.Memory = strings.ReplaceAll(jobRes.Resource.Memory, "Mi", "MB") - spec := "GPU数:" + strconv.Itoa(jobRes.Resource.NvidiaComGpu) + ",CPU数:" + strconv.Itoa(jobRes.Resource.CPU) + ",内存(MB):" + jobRes.Resource.Memory - ctx.Data["resource_spec"] = spec - if task.JobType == string(models.JobTypeTrain) { - if trainGpuInfos == nil { - json.Unmarshal([]byte(setting.TrainGpuTypes), &trainGpuInfos) - } - for _, resourceType := range trainGpuInfos.GpuInfo { - if resourceType.Queue == jobRes.Config.GpuType { - ctx.Data["resource_type"] = resourceType.Value - } - } - } else if cloudbrain.IsBenchmarkJob(task.JobType) { - if benchmarkGpuInfos == nil { - json.Unmarshal([]byte(setting.BenchmarkGpuTypes), &benchmarkGpuInfos) - } - - for _, resourceType := range benchmarkGpuInfos.GpuInfo { - if resourceType.Queue == jobRes.Config.GpuType { - ctx.Data["resource_type"] = resourceType.Value - } - } - - } else { - if gpuInfos == nil { - json.Unmarshal([]byte(setting.GpuTypes), &gpuInfos) - } - for _, resourceType := range gpuInfos.GpuInfo { - if resourceType.Queue == jobRes.Config.GpuType { - ctx.Data["resource_type"] = resourceType.Value - } - } - } taskRoles := jobRes.TaskRoles taskRes, _ := models.ConvertToTaskPod(taskRoles[cloudbrain.SubTaskName].(map[string]interface{})) ctx.Data["taskRes"] = taskRes ctx.Data["ExitDiagnostics"] = taskRes.TaskStatuses[0].ExitDiagnostics + oldStatus := task.Status task.Status = taskRes.TaskStatuses[0].State task.ContainerID = taskRes.TaskStatuses[0].ContainerID task.ContainerIp = taskRes.TaskStatuses[0].ContainerIP models.ParseAndSetDurationFromCloudBrainOne(jobRes, task) if task.DeletedAt.IsZero() { //normal record + if oldStatus != task.Status { + notification.NotifyChangeCloudbrainStatus(task, oldStatus) + } err = models.UpdateJob(task) if err != nil { ctx.Data["error"] = err.Error() @@ -685,53 +754,134 @@ func cloudBrainShow(ctx *context.Context, tpName base.TplName, jobType models.Jo } else { duration = int64(task.UpdatedUnix) - int64(task.CreatedUnix) } - task.Duration = duration + task.Duration = duration + } + task.TrainJobDuration = models.ConvertDurationToStr(task.Duration) + } + ctx.Data["duration"] = task.TrainJobDuration + + if len(task.Parameters) > 0 { + var parameters models.Parameters + + err := json.Unmarshal([]byte(task.Parameters), ¶meters) + if err != nil { + log.Error("Failed to Unmarshal Parameters: %s (%v)", task.Parameters, err) + task.Parameters = "" + } else { + if len(parameters.Parameter) > 0 { + paramTemp := "" + for _, Parameter := range parameters.Parameter { + param := Parameter.Label + " = " + Parameter.Value + "; " + paramTemp = paramTemp + param + } + task.Parameters = paramTemp[:len(paramTemp)-2] + } else { + task.Parameters = "" + } + } + + } + ctx.Data["datasetDownload"] = GetCloudBrainDataSetInfo(task.Uuid, false) + ctx.Data["task"] = task + labelName := strings.Fields(task.LabelName) + ctx.Data["LabelName"] = labelName + ctx.Data["jobName"] = task.JobName + ctx.Data["displayJobName"] = task.DisplayJobName + version_list_task := make([]*models.Cloudbrain, 0) + version_list_task = append(version_list_task, task) + ctx.Data["version_list_task"] = version_list_task + ctx.Data["debugListType"] = debugListType + ctx.Data["code_path"] = cloudbrain.CodeMountPath + ctx.Data["dataset_path"] = cloudbrain.DataSetMountPath + ctx.Data["model_path"] = cloudbrain.ModelMountPath + ctx.Data["canDownload"] = cloudbrain.CanModifyJob(ctx, task) + ctx.Data["branchName"] = task.BranchName + ctx.HTML(200, tpName) +} + +func CloudBrainDebug(ctx *context.Context) { + task := ctx.Cloudbrain + debugUrl := setting.DebugServerHost + "jpylab_" + task.JobID + "_" + task.SubTaskName + ctx.Redirect(debugUrl) +} + +func prepareSpec4Show(ctx *context.Context, task *models.Cloudbrain) { + s, err := resource.GetCloudbrainSpec(task.ID) + if err != nil { + log.Info("error:" + err.Error()) + ctx.NotFound(ctx.Req.URL.RequestURI(), nil) + return + } + ctx.Data["Spec"] = s +} + +func oldPrepareSpec4Show(ctx *context.Context, task *models.Cloudbrain) { + hasSpec := false + if task.JobType == string(models.JobTypeTrain) { + if cloudbrain.TrainResourceSpecs == nil { + json.Unmarshal([]byte(setting.TrainResourceSpecs), &cloudbrain.TrainResourceSpecs) + } + + for _, tmp := range cloudbrain.TrainResourceSpecs.ResourceSpec { + if tmp.Id == task.ResourceSpecId { + hasSpec = true + ctx.Data["GpuNum"] = tmp.GpuNum + ctx.Data["CpuNum"] = tmp.CpuNum + ctx.Data["MemMiB"] = tmp.MemMiB + ctx.Data["ShareMemMiB"] = tmp.ShareMemMiB + break + } + } + + } else if task.JobType == string(models.JobTypeInference) { + if cloudbrain.InferenceResourceSpecs == nil { + json.Unmarshal([]byte(setting.InferenceResourceSpecs), &cloudbrain.InferenceResourceSpecs) + } + for _, tmp := range cloudbrain.InferenceResourceSpecs.ResourceSpec { + if tmp.Id == task.ResourceSpecId { + hasSpec = true + ctx.Data["GpuNum"] = tmp.GpuNum + ctx.Data["CpuNum"] = tmp.CpuNum + ctx.Data["MemMiB"] = tmp.MemMiB + ctx.Data["ShareMemMiB"] = tmp.ShareMemMiB + break + } + } + } else { + if cloudbrain.ResourceSpecs == nil { + json.Unmarshal([]byte(setting.ResourceSpecs), &cloudbrain.ResourceSpecs) + } + for _, tmp := range cloudbrain.ResourceSpecs.ResourceSpec { + if tmp.Id == task.ResourceSpecId { + hasSpec = true + ctx.Data["GpuNum"] = tmp.GpuNum + ctx.Data["CpuNum"] = tmp.CpuNum + ctx.Data["MemMiB"] = tmp.MemMiB + ctx.Data["ShareMemMiB"] = tmp.ShareMemMiB + break + + } } - task.TrainJobDuration = models.ConvertDurationToStr(task.Duration) } - ctx.Data["duration"] = task.TrainJobDuration - if len(task.Parameters) > 0 { - var parameters models.Parameters + if !hasSpec && cloudbrain.SpecialPools != nil { - err := json.Unmarshal([]byte(task.Parameters), ¶meters) - if err != nil { - log.Error("Failed to Unmarshal Parameters: %s (%v)", task.Parameters, err) - task.Parameters = "" - } else { - if len(parameters.Parameter) > 0 { - paramTemp := "" - for _, Parameter := range parameters.Parameter { - param := Parameter.Label + " = " + Parameter.Value + "; " - paramTemp = paramTemp + param + for _, specialPool := range cloudbrain.SpecialPools.Pools { + + if specialPool.ResourceSpec != nil { + + for _, spec := range specialPool.ResourceSpec { + if task.ResourceSpecId == spec.Id { + ctx.Data["GpuNum"] = spec.GpuNum + ctx.Data["CpuNum"] = spec.CpuNum + ctx.Data["MemMiB"] = spec.MemMiB + ctx.Data["ShareMemMiB"] = spec.ShareMemMiB + break + } } - task.Parameters = paramTemp[:len(paramTemp)-2] - } else { - task.Parameters = "" } } - } - - ctx.Data["task"] = task - ctx.Data["jobName"] = task.JobName - ctx.Data["displayJobName"] = task.DisplayJobName - version_list_task := make([]*models.Cloudbrain, 0) - version_list_task = append(version_list_task, task) - ctx.Data["version_list_task"] = version_list_task - ctx.Data["debugListType"] = debugListType - ctx.Data["code_path"] = cloudbrain.CodeMountPath - ctx.Data["dataset_path"] = cloudbrain.DataSetMountPath - ctx.Data["model_path"] = cloudbrain.ModelMountPath - ctx.Data["canDownload"] = cloudbrain.CanModifyJob(ctx, task) - ctx.Data["branchName"] = task.BranchName - ctx.HTML(200, tpName) -} - -func CloudBrainDebug(ctx *context.Context) { - task := ctx.Cloudbrain - debugUrl := setting.DebugServerHost + "jpylab_" + task.JobID + "_" + task.SubTaskName - ctx.Redirect(debugUrl) } func CloudBrainCommitImageShow(ctx *context.Context) { @@ -740,6 +890,20 @@ func CloudBrainCommitImageShow(ctx *context.Context) { ctx.HTML(200, tplCloudBrainImageSubmit) } +func GetImage(ctx *context.Context) { + + var ID = ctx.Params(":id") + id, _ := strconv.ParseInt(ID, 10, 64) + + image, err := models.GetImageByID(id) + if err != nil { + log.Error("GetImageByID failed:%v", err.Error()) + ctx.JSON(http.StatusNotFound, nil) + } + ctx.JSON(http.StatusOK, image) + +} + func CloudBrainImageEdit(ctx *context.Context) { ctx.Data["PageIsImageEdit"] = true ctx.Data["PageFrom"] = ctx.Params(":from") @@ -951,6 +1115,8 @@ func CloudBrainStop(ctx *context.Context) { for { if task.Status == string(models.JobStopped) || task.Status == string(models.JobFailed) || task.Status == string(models.JobSucceeded) { log.Error("the job(%s) has been stopped", task.JobName, ctx.Data["msgID"]) + resultCode = "-1" + errorMsg = "cloudbrain.Already_stopped" resultCode = task.Status break } @@ -959,20 +1125,23 @@ func CloudBrainStop(ctx *context.Context) { if err != nil { log.Error("StopJob(%s) failed:%v", task.JobName, err, ctx.Data["msgID"]) resultCode = "-1" - errorMsg = "system error" + errorMsg = "cloudbrain.Stopped_failed" break } - + oldStatus := task.Status task.Status = string(models.JobStopped) if task.EndTime == 0 { task.EndTime = timeutil.TimeStampNow() } task.ComputeAndSetDuration() + if oldStatus != task.Status { + notification.NotifyChangeCloudbrainStatus(task, oldStatus) + } err = models.UpdateJob(task) if err != nil { log.Error("UpdateJob(%s) failed:%v", task.JobName, err, ctx.Data["msgID"]) resultCode = "-1" - errorMsg = "system error" + errorMsg = "cloudbrain.Stopped_success_update_status_fail" break } status = task.Status @@ -981,7 +1150,7 @@ func CloudBrainStop(ctx *context.Context) { ctx.JSON(200, map[string]interface{}{ "result_code": resultCode, - "error_msg": errorMsg, + "error_msg": ctx.Tr(errorMsg), "status": status, "id": ID, "StatusOK": 0, @@ -1007,6 +1176,15 @@ func StopJobsByRepoID(repoID int64) { StopJobs(cloudBrains) } +func DeleteJobsByRepoID(repoID int64) { + cloudBrains, err := models.GetCloudbrainsNeededDeleteByRepoID(repoID) + if err != nil { + log.Warn("Failed to get cloudBrain info", err) + return + } + DeleteJobs(cloudBrains) +} + /** */ @@ -1042,6 +1220,36 @@ func StopJobs(cloudBrains []*models.Cloudbrain) { } } +func DeleteJobs(cloudBrains []*models.Cloudbrain) { + for _, taskInfo := range cloudBrains { + err := models.DeleteJob(taskInfo) + if err != nil { + log.Warn("Failed to DeleteJob:", err) + return + } + if taskInfo.Type == models.TypeCloudBrainOne { + cloudbrain.DelCloudBrainJob(taskInfo.JobName) + DeleteCloudbrainJobStorage(taskInfo.JobName, models.TypeCloudBrainOne) + } + if taskInfo.Type == models.TypeCloudBrainTwo { + if taskInfo.JobType == string(models.JobTypeTrain) || taskInfo.JobType == string(models.JobTypeInference) { + + modelarts.DelTrainJob(taskInfo.JobID) + DeleteJobStorage(taskInfo.JobName) + } + if taskInfo.JobType == string(models.JobTypeDebug) { + modelarts.DelNotebook2(taskInfo.JobID) + } + } + if taskInfo.Type == models.TypeC2Net { + if taskInfo.JobType == string(models.JobTypeTrain) { + cloudbrain.DelCloudBrainJob(taskInfo.JobName) + DeleteCloudbrainJobStorage(taskInfo.JobName, models.TypeCloudBrainOne) + } + } + } +} + func retry(attempts int, sleep time.Duration, f func() error) (err error) { for i := 0; i < attempts; i++ { if i > 0 { @@ -1060,11 +1268,15 @@ func logErrorAndUpdateJobStatus(err error, taskInfo *models.Cloudbrain) { if err != nil { log.Warn("Failed to stop cloudBrain job:"+taskInfo.JobID, err) } else { + oldStatus := taskInfo.Status taskInfo.Status = string(models.JobStopped) if taskInfo.EndTime == 0 { taskInfo.EndTime = timeutil.TimeStampNow() } taskInfo.ComputeAndSetDuration() + if oldStatus != taskInfo.Status { + notification.NotifyChangeCloudbrainStatus(taskInfo, oldStatus) + } err = models.UpdateJob(taskInfo) if err != nil { log.Warn("UpdateJob failed", err) @@ -1105,7 +1317,7 @@ func deleteCloudbrainJob(ctx *context.Context) error { return err } - deleteJobStorage(task.JobName, models.TypeCloudBrainOne) + DeleteCloudbrainJobStorage(task.JobName, models.TypeCloudBrainOne) return nil } @@ -1271,6 +1483,18 @@ func GetModelDirs(jobName string, parentDir string) (string, error) { return getDirs(req) } +func GetResultDirs(jobName string, parentDir string) (string, error) { + var req string + modelActualPath := storage.GetMinioPath(jobName, cloudbrain.ResultPath+"/") + if parentDir == "" { + req = "baseDir=" + modelActualPath + } else { + req = "baseDir=" + modelActualPath + "&parentDir=" + parentDir + } + + return getDirs(req) +} + func CloudBrainDownloadModel(ctx *context.Context) { parentDir := ctx.Query("parentDir") fileName := ctx.Query("fileName") @@ -1285,6 +1509,20 @@ func CloudBrainDownloadModel(ctx *context.Context) { ctx.Resp.Header().Set("Cache-Control", "max-age=0") http.Redirect(ctx.Resp, ctx.Req.Request, url, http.StatusMovedPermanently) } +func CloudBrainDownloadInferenceResult(ctx *context.Context) { + parentDir := ctx.Query("parentDir") + fileName := ctx.Query("fileName") + jobName := ctx.Query("jobName") + filePath := "jobs/" + jobName + "/result/" + parentDir + url, err := storage.Attachments.PresignedGetURL(filePath, fileName) + if err != nil { + log.Error("PresignedGetURL failed: %v", err.Error(), ctx.Data["msgID"]) + ctx.ServerError("PresignedGetURL", err) + return + } + ctx.Resp.Header().Set("Cache-Control", "max-age=0") + http.Redirect(ctx.Resp, ctx.Req.Request, url, http.StatusMovedPermanently) +} func GetRate(ctx *context.Context) { isObjectDetcionAll := ctx.QueryBool("isObjectDetcionAll") @@ -1438,13 +1676,17 @@ func uploadCodeToMinio(codePath, jobName, parentDir string) error { } func mkModelPath(modelPath string) error { - err := os.MkdirAll(modelPath, os.ModePerm) + return mkPathAndReadMeFile(modelPath, "You can put the files into this directory and download the files by the web page.") +} + +func mkPathAndReadMeFile(path string, text string) error { + err := os.MkdirAll(path, os.ModePerm) if err != nil { - log.Error("MkdirAll(%s) failed:%v", modelPath, err) + log.Error("MkdirAll(%s) failed:%v", path, err) return err } - fileName := modelPath + "README" + fileName := path + "README" f, err := os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm) if err != nil { log.Error("OpenFile failed", err.Error()) @@ -1453,7 +1695,7 @@ func mkModelPath(modelPath string) error { defer f.Close() - _, err = f.WriteString("You can put the model file into this directory and download it by the web page.") + _, err = f.WriteString(text) if err != nil { log.Error("WriteString failed", err.Error()) return err @@ -1462,7 +1704,7 @@ func mkModelPath(modelPath string) error { return nil } -func deleteJobStorage(jobName string, cloudbrainType int) error { +func DeleteCloudbrainJobStorage(jobName string, cloudbrainType int) error { //delete local localJobPath := setting.JobPath + jobName err := os.RemoveAll(localJobPath) @@ -1510,9 +1752,13 @@ func SyncCloudbrainStatus() { jobRes, _ := models.ConvertToJobResultPayload(result.Payload) taskRoles := jobRes.TaskRoles taskRes, _ := models.ConvertToTaskPod(taskRoles[cloudbrain.SubTaskName].(map[string]interface{})) + oldStatus := task.Status task.Status = taskRes.TaskStatuses[0].State if task.Status != string(models.JobWaiting) { models.ParseAndSetDurationFromCloudBrainOne(jobRes, task) + if oldStatus != task.Status { + notification.NotifyChangeCloudbrainStatus(task, oldStatus) + } err = models.UpdateJob(task) if err != nil { log.Error("UpdateJob(%s) failed:%v", task.JobName, err) @@ -1539,6 +1785,9 @@ func SyncCloudbrainStatus() { task.EndTime = timeutil.TimeStampNow() } task.ComputeAndSetDuration() + if oldStatus != task.Status { + notification.NotifyChangeCloudbrainStatus(task, oldStatus) + } err = models.UpdateJob(task) if err != nil { log.Error("UpdateJob(%s) failed:%v", task.DisplayJobName, err) @@ -1549,62 +1798,24 @@ func SyncCloudbrainStatus() { } } else if task.Type == models.TypeCloudBrainTwo { if task.JobType == string(models.JobTypeDebug) { - //result, err := modelarts.GetJob(task.JobID) - result, err := modelarts.GetNotebook2(task.JobID) + err := modelarts.HandleNotebookInfo(task) if err != nil { - log.Error("GetJob(%s) failed:%v", task.JobName, err) + log.Error("HandleNotebookInfo(%s) failed:%v", task.DisplayJobName, err) continue } - - if result != nil { - task.Status = result.Status - if task.StartTime == 0 && result.Lease.UpdateTime > 0 { - task.StartTime = timeutil.TimeStamp(result.Lease.UpdateTime / 1000) - } - if task.EndTime == 0 && models.IsModelArtsDebugJobTerminal(task.Status) { - task.EndTime = timeutil.TimeStampNow() - } - task.CorrectCreateUnix() - task.ComputeAndSetDuration() - err = models.UpdateJob(task) - if err != nil { - log.Error("UpdateJob(%s) failed:%v", task.JobName, err) - continue - } - } } else if task.JobType == string(models.JobTypeTrain) || task.JobType == string(models.JobTypeInference) { - result, err := modelarts.GetTrainJob(task.JobID, strconv.FormatInt(task.VersionID, 10)) + err := modelarts.HandleTrainJobInfo(task) if err != nil { - log.Error("GetTrainJob(%s) failed:%v", task.JobName, err) + log.Error("HandleTrainJobInfo(%s) failed:%v", task.DisplayJobName, err) continue } - - if result != nil { - task.Status = modelarts.TransTrainJobStatus(result.IntStatus) - task.Duration = result.Duration / 1000 - task.TrainJobDuration = result.TrainJobDuration - - if task.StartTime == 0 && result.StartTime > 0 { - task.StartTime = timeutil.TimeStamp(result.StartTime / 1000) - } - task.TrainJobDuration = models.ConvertDurationToStr(task.Duration) - if task.EndTime == 0 && models.IsTrainJobTerminal(task.Status) && task.StartTime > 0 { - task.EndTime = task.StartTime.Add(task.Duration) - } - task.CorrectCreateUnix() - err = models.UpdateJob(task) - if err != nil { - log.Error("UpdateJob(%s) failed:%v", task.JobName, err) - continue - } - } } else { - log.Error("task.JobType(%s) is error:%s", task.JobName, task.JobType) + log.Error("task.JobType(%s) is error:%s", task.DisplayJobName, task.JobType) } } else if task.Type == models.TypeC2Net { result, err := grampus.GetJob(task.JobID) if err != nil { - log.Error("GetTrainJob(%s) failed:%v", task.JobName, err) + log.Error("GetTrainJob(%s) failed:%v", task.DisplayJobName, err) continue } @@ -1612,6 +1823,7 @@ func SyncCloudbrainStatus() { if len(result.JobInfo.Tasks[0].CenterID) == 1 && len(result.JobInfo.Tasks[0].CenterName) == 1 { task.AiCenter = result.JobInfo.Tasks[0].CenterID[0] + "+" + result.JobInfo.Tasks[0].CenterName[0] } + oldStatus := task.Status task.Status = grampus.TransTrainJobStatus(result.JobInfo.Status) task.Duration = result.JobInfo.RunSec task.TrainJobDuration = models.ConvertDurationToStr(task.Duration) @@ -1623,6 +1835,9 @@ func SyncCloudbrainStatus() { task.EndTime = task.StartTime.Add(task.Duration) } task.CorrectCreateUnix() + if oldStatus != task.Status { + notification.NotifyChangeCloudbrainStatus(task, oldStatus) + } err = models.UpdateJob(task) if err != nil { log.Error("UpdateJob(%s) failed:%v", task.JobName, err) @@ -2001,10 +2216,8 @@ func BenchMarkAlgorithmCreate(ctx *context.Context, form auth.CreateCloudBrainFo displayJobName := form.DisplayJobName jobName := util.ConvertDisplayJobNameToJobName(displayJobName) image := strings.TrimSpace(form.Image) - gpuQueue := form.GpuType command := cloudbrain.CommandBenchmark codePath := setting.JobPath + jobName + cloudbrain.CodeMountPath - resourceSpecId := cloudbrain.BenchMarkResourceID benchmarkTypeID := form.BenchmarkTypeID benchmarkChildTypeID := form.BenchmarkChildTypeID @@ -2052,19 +2265,14 @@ func BenchMarkAlgorithmCreate(ctx *context.Context, form auth.CreateCloudBrainFo return } - _, err = getBenchmarkGpuQueue(gpuQueue) - if err != nil { - log.Error("getBenchmarkGpuQueue failed:%v", err, ctx.Data["MsgID"]) - cloudBrainNewDataPrepare(ctx) - ctx.RenderWithErr("gpu queue error", tplCloudBrainBenchmarkNew, &form) - return - } - - _, err = getBenchmarkResourceSpec(resourceSpecId) - if err != nil { - log.Error("getBenchmarkResourceSpec failed:%v", err, ctx.Data["MsgID"]) + spec, err := resource.GetAndCheckSpec(ctx.User.ID, form.SpecId, models.FindSpecsOptions{ + JobType: models.JobTypeBenchmark, + ComputeResource: models.GPU, + Cluster: models.OpenICluster, + AiCenterCode: models.AICenterOfCloudBrainOne}) + if err != nil || spec == nil { cloudBrainNewDataPrepare(ctx) - ctx.RenderWithErr("resource spec error", tplCloudBrainBenchmarkNew, &form) + ctx.RenderWithErr("Resource specification not available", tplCloudBrainBenchmarkNew, &form) return } @@ -2125,14 +2333,8 @@ func BenchMarkAlgorithmCreate(ctx *context.Context, form auth.CreateCloudBrainFo } benchmarkPath := setting.JobPath + jobName + cloudbrain.BenchMarkMountPath - var gpuType string - for _, gpuInfo := range gpuInfos.GpuInfo { - if gpuInfo.Queue == gpuQueue { - gpuType = gpuInfo.Value - } - } - if err := downloadRateCode(repo, jobName, childInfo.Owner, childInfo.RepoName, benchmarkPath, form.BenchmarkCategory, gpuType, ctx.User.Name); err != nil { + if err := downloadRateCode(repo, jobName, childInfo.Owner, childInfo.RepoName, benchmarkPath, form.BenchmarkCategory, spec.AccCardType, ctx.User.Name); err != nil { log.Error("downloadRateCode failed, %v", err, ctx.Data["MsgID"]) //cloudBrainNewDataPrepare(ctx) //ctx.RenderWithErr("system error", tplCloudBrainBenchmarkNew, &form) @@ -2170,7 +2372,6 @@ func BenchMarkAlgorithmCreate(ctx *context.Context, form auth.CreateCloudBrainFo Snn4ImageNetPath: storage.GetMinioPath(jobName, cloudbrain.Snn4imagenetMountPath+"/"), BrainScorePath: storage.GetMinioPath(jobName, cloudbrain.BrainScoreMountPath+"/"), JobType: string(models.JobTypeBenchmark), - GpuQueue: gpuQueue, Description: form.Description, BranchName: cloudbrain.DefaultBranchName, BootFile: "", @@ -2178,7 +2379,8 @@ func BenchMarkAlgorithmCreate(ctx *context.Context, form auth.CreateCloudBrainFo CommitID: "", BenchmarkTypeID: benchmarkTypeID, BenchmarkChildTypeID: benchmarkChildTypeID, - ResourceSpecId: resourceSpecId, + ResultPath: storage.GetMinioPath(jobName, cloudbrain.ResultPath+"/"), + Spec: spec, } err = cloudbrain.GenerateTask(req) @@ -2198,14 +2400,12 @@ func ModelBenchmarkCreate(ctx *context.Context, form auth.CreateCloudBrainForm) image := form.Image uuid := form.Attachment jobType := form.JobType - gpuQueue := form.GpuType codePath := setting.JobPath + jobName + cloudbrain.CodeMountPath - resourceSpecId := form.ResourceSpecId branchName := cloudbrain.DefaultBranchName repo := ctx.Repo.Repository tpl := tplCloudBrainBenchmarkNew - command := cloudbrain.Command + command := cloudbrain.GetCloudbrainDebugCommand() if !account.IsPointBalanceEnough(ctx.User.ID, jobType, models.TypeCloudBrainOne, resourceSpecId, "") { log.Error("point balance is not enough,userId=%d jobType=%s resourceSpecId=%d", ctx.User.ID, jobType, resourceSpecId) @@ -2289,7 +2489,16 @@ func ModelBenchmarkCreate(ctx *context.Context, form auth.CreateCloudBrainForm) ctx.RenderWithErr(ctx.Tr("cloudbrain.error.dataset_select"), tpl, &form) return } - + spec, err := resource.GetAndCheckSpec(ctx.User.ID, form.SpecId, models.FindSpecsOptions{ + JobType: models.JobTypeBenchmark, + ComputeResource: models.GPU, + Cluster: models.OpenICluster, + AiCenterCode: models.AICenterOfCloudBrainOne}) + if err != nil || spec == nil { + cloudBrainNewDataPrepare(ctx) + ctx.RenderWithErr("Resource specification not available", tpl, &form) + return + } req := cloudbrain.GenerateCloudBrainTaskReq{ Ctx: ctx, DisplayJobName: displayJobName, @@ -2305,7 +2514,6 @@ func ModelBenchmarkCreate(ctx *context.Context, form auth.CreateCloudBrainForm) Snn4ImageNetPath: storage.GetMinioPath(jobName, cloudbrain.Snn4imagenetMountPath+"/"), BrainScorePath: storage.GetMinioPath(jobName, cloudbrain.BrainScoreMountPath+"/"), JobType: jobType, - GpuQueue: gpuQueue, Description: form.Description, BranchName: branchName, BootFile: form.BootFile, @@ -2313,7 +2521,8 @@ func ModelBenchmarkCreate(ctx *context.Context, form auth.CreateCloudBrainForm) CommitID: "", BenchmarkTypeID: 0, BenchmarkChildTypeID: benchmarkChildTypeID, - ResourceSpecId: resourceSpecId, + ResultPath: storage.GetMinioPath(jobName, cloudbrain.ResultPath+"/"), + Spec: spec, } err = cloudbrain.GenerateTask(req) @@ -2364,6 +2573,64 @@ func CloudBrainTrainJobNew(ctx *context.Context) { ctx.HTML(http.StatusOK, tplCloudBrainTrainJobNew) } +func InferenceCloudBrainJobNew(ctx *context.Context) { + err := cloudBrainNewDataPrepare(ctx) + if err != nil { + ctx.ServerError("get new train-job info failed", err) + return + } + ctx.HTML(http.StatusOK, tplCloudBrainInferenceJobNew) +} + +func InferenceCloudBrainJobShow(ctx *context.Context) { + cloudBrainShow(ctx, tplCloudBrainInferenceJobShow, models.JobTypeInference) +} + +func DownloadInferenceResultFile(ctx *context.Context) { + var jobID = ctx.Params(":jobid") + var versionName = ctx.Query("version_name") + task, err := models.GetCloudbrainByJobIDAndVersionName(jobID, versionName) + if err != nil { + log.Error("GetCloudbrainByJobID(%s) failed:%v", task.JobName, err.Error()) + return + } + + allFile, err := storage.GetAllObjectByBucketAndPrefixMinio(setting.Attachment.Minio.Bucket, task.ResultUrl) + returnFileName := task.DisplayJobName + ".zip" + MinioDownloadManyFile(task.ResultUrl, ctx, returnFileName, allFile) +} + +func getInferenceJobCommand(form auth.CreateCloudBrainInferencForm) (string, error) { + var command string + bootFile := strings.TrimSpace(form.BootFile) + params := form.Params + + if !strings.HasSuffix(bootFile, ".py") { + log.Error("bootFile(%s) format error", bootFile) + return command, errors.New("bootFile format error") + } + + var parameters models.Parameters + var param string + if len(params) != 0 { + err := json.Unmarshal([]byte(params), ¶meters) + if err != nil { + log.Error("Failed to Unmarshal params: %s (%v)", params, err) + return command, err + } + + for _, parameter := range parameters.Parameter { + param += " --" + parameter.Label + "=" + parameter.Value + } + } + + param += " --modelname" + "=" + form.CkptName + + command += "python /code/" + bootFile + param + " > " + cloudbrain.ResultPath + "/" + form.DisplayJobName + "-" + cloudbrain.LogFile + + return command, nil +} + func getTrainJobCommand(form auth.CreateCloudBrainForm) (string, error) { var command string bootFile := strings.TrimSpace(form.BootFile) @@ -2388,7 +2655,7 @@ func getTrainJobCommand(form auth.CreateCloudBrainForm) (string, error) { } } - command += "python /code/" + bootFile + param + " > " + cloudbrain.ModelMountPath + "/" + form.DisplayJobName + "-" + cloudbrain.LogFile + command += "python /code/" + bootFile + param + " | tee " + cloudbrain.ModelMountPath + "/" + form.DisplayJobName + "-" + cloudbrain.LogFile return command, nil } @@ -2427,3 +2694,170 @@ func GetBenchmarkTypes(ctx *context.Context) *models.BenchmarkTypes { } return benchmarkTypesMap[lang] } + +func GetCloudbrainAiCenter(task models.Cloudbrain, ctx *context.Context) string { + if task.Type == models.TypeCloudBrainOne { + return ctx.Tr("repo.cloudbrain1") + } else if task.Type == models.TypeCloudBrainTwo { + return ctx.Tr("repo.cloudbrain2") + } else if task.Type == models.TypeC2Net { + return getCutStringAiCenterByAiCenter(task.AiCenter) + } + return "" +} +func getCutStringAiCenterByAiCenter(aiCenter string) string { + if aiCenter == "" { + return "" + } + index := strings.LastIndex(aiCenter, "+") + return aiCenter[index+1:] + +} +func GetCloudbrainCluster(task models.Cloudbrain, ctx *context.Context) string { + if task.Type == models.TypeCloudBrainOne || task.Type == models.TypeCloudBrainTwo { + return ctx.Tr("cloudbrain.resource_cluster_openi") + } else if task.Type == models.TypeC2Net { + return ctx.Tr("cloudbrain.resource_cluster_c2net") + } + return "" +} +func GetCloudbrainCardDuration(task models.Cloudbrain) string { + cardNum, _, _ := GetCloudbrainCardNumAndType(task) + cardDuration := models.ConvertDurationToStr(int64(cardNum) * task.Duration) + return cardDuration +} +func GetCloudbrainWaitTime(task models.Cloudbrain) string { + var waitTime string + if task.Status == string(models.JobWaiting) { + waitTimeInt := time.Now().Unix() - task.CreatedUnix.AsTime().Unix() + waitTime = models.ConvertDurationToStr(waitTimeInt) + if waitTimeInt < 0 { + waitTime = "00:00:00" + } + } else if task.Status == string(models.JobStopped) && task.StartTime.AsTime().Unix() == 0 { + waitTimeInt := task.EndTime.AsTime().Unix() - task.CreatedUnix.AsTime().Unix() + waitTime = models.ConvertDurationToStr(waitTimeInt) + if waitTimeInt < 0 { + waitTime = "00:00:00" + + } + } else { + waitTimeInt := task.StartTime.AsTime().Unix() - task.CreatedUnix.AsTime().Unix() + waitTime = models.ConvertDurationToStr(waitTimeInt) + if waitTimeInt < 0 { + waitTime = "00:00:00" + } + } + return waitTime +} + +func GetCloudbrainCardNumAndType(task models.Cloudbrain) (int, string, error) { + if !models.SpecsMapInitFlag { + models.InitCloudbrainOneResourceSpecMap() + } + if !models.GpuInfosMapInitFlag { + models.InitCloudbrainOneGpuInfoMap() + } + flavorName, err := GetCloudbrainFlavorName(task) + if err != nil { + return 0, "", nil + } + return getCardNumAndTypeByFlavorname(flavorName) +} + +func getCardNumAndTypeByFlavorname(FlavorName string) (int, string, error) { + if FlavorName == "" { + return 0, "", nil + } else { + var beginIndex = strings.Index(FlavorName, ":") + var lastIndex = strings.LastIndex(FlavorName, ":") + var endIndex = strings.Index(FlavorName, "*") + if endIndex >= (beginIndex+1) && lastIndex >= (endIndex+1) { + cardNum, err := strconv.Atoi(strings.TrimSpace(FlavorName[beginIndex+1 : endIndex])) + if err != nil { + log.Error("strconv.Atoi failed: %v", err) + return 0, "", err + } + cardType := strings.TrimSpace(FlavorName[endIndex+1 : lastIndex]) + return cardNum, cardType, err + } + return 0, "", nil + } +} + +func GetCloudbrainFlavorName(task models.Cloudbrain) (string, error) { + if task.Type == models.TypeCloudBrainOne { + resourceSpec, gpuInfo, err := getCloudBrainOneResourceSpec(task) + if err != nil { + log.Info("getCloudBrainOneResourceSpec err:", err) + return "", err + } else { + if resourceSpec == nil || gpuInfo == nil { + err := errors.New("resourceSpec or gpuInfo is nil") + return "", err + } else { + CloudbrainOneFlavorName := "GPU:" + strconv.Itoa(resourceSpec.GpuNum) + "*Nvidia-" + gpuInfo.Value + + " | CPU:" + strconv.Itoa(resourceSpec.CpuNum) + "核" + strconv.Itoa(resourceSpec.MemMiB) + "MB" + return CloudbrainOneFlavorName, nil + } + } + } else if (task.Type == models.TypeCloudBrainTwo || task.Type == models.TypeC2Net) && task.FlavorName != "" { + replaceFlavorName := strings.ReplaceAll(task.FlavorName, ":", ":") + return replaceFlavorName, nil + } else if task.Type == models.TypeCloudBrainTwo && task.FlavorName == "" && task.FlavorCode != "" { + cloudbrainTwoFlavorName := getFlavorNameByFlavorCode(task.FlavorCode) + return cloudbrainTwoFlavorName, nil + } else if task.Type == models.TypeCloudBrainTwo && task.JobType == string(models.JobTypeDebug) && task.FlavorName == "" && task.FlavorCode == "" { + tasks, err := models.GetModelartsReDebugTaskByJobId(task.JobID) + if err != nil { + return "", err + } + if len(tasks) >= 1 { + return getFlavorNameByFlavorCode(tasks[0].FlavorCode), nil + } + return "", nil + } + return "", nil +} + +func getCloudBrainOneResourceSpec(task models.Cloudbrain) (*models.ResourceSpec, *models.GpuInfo, error) { + gpuQueueDefault := "openidebug" + if task.GpuQueue != "" { + gpuQueueDefault = task.GpuQueue + } + if task.ResourceSpecId >= 0 { + if task.JobType == string(models.JobTypeTrain) { + if models.CloudbrainTrainResourceSpecsMap[task.ResourceSpecId] != nil { + return models.CloudbrainTrainResourceSpecsMap[task.ResourceSpecId], models.CloudbrainTrainGpuInfosMap[gpuQueueDefault], nil + } else { + return models.CloudbrainSpecialResourceSpecsMap[task.ResourceSpecId], models.CloudbrainSpecialGpuInfosMap[gpuQueueDefault], nil + } + } else if task.JobType == string(models.JobTypeDebug) { + if models.CloudbrainDebugResourceSpecsMap[task.ResourceSpecId] != nil { + return models.CloudbrainDebugResourceSpecsMap[task.ResourceSpecId], models.CloudbrainDebugGpuInfosMap[gpuQueueDefault], nil + } else { + return models.CloudbrainSpecialResourceSpecsMap[task.ResourceSpecId], models.CloudbrainSpecialGpuInfosMap[gpuQueueDefault], nil + } + } else if task.JobType == string(models.JobTypeInference) { + return models.CloudbrainInferenceResourceSpecsMap[task.ResourceSpecId], models.CloudbrainInferenceGpuInfosMap[gpuQueueDefault], nil + } else if task.JobType == string(models.JobTypeBenchmark) || task.JobType == string(models.JobTypeSnn4imagenet) || task.JobType == string(models.JobTypeBrainScore) { + return models.CloudbrainBenchmarkResourceSpecsMap[task.ResourceSpecId], models.CloudbrainBenchmarkGpuInfosMap[gpuQueueDefault], nil + } + } else { + err := errors.New("ResourceSpecId is null") + return nil, nil, err + } + return nil, nil, nil +} +func getFlavorNameByFlavorCode(flavorCode string) string { + index := strings.LastIndex(flavorCode, ".") + cardNum, err := strconv.Atoi(strings.TrimSpace(flavorCode[index+1 : len(flavorCode)])) + if err != nil { + log.Error("strconv.Atoi failed: %v", err) + return "" + } + cloudbrainTwoFlavorName := "Ascend:" + strings.TrimSpace(flavorCode[index+1:len(flavorCode)]) + + "*Ascend-910(" + strconv.Itoa(cardNum*32) + "GB)|ARM:" + strconv.Itoa(cardNum*24) + + "核" + strconv.Itoa(cardNum*256) + "GB" + return cloudbrainTwoFlavorName +} diff --git a/routers/repo/dataset.go b/routers/repo/dataset.go index 4dde646a6..f047cdaa9 100755 --- a/routers/repo/dataset.go +++ b/routers/repo/dataset.go @@ -9,6 +9,8 @@ import ( "strings" "unicode/utf8" + "code.gitea.io/gitea/services/repository" + "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" @@ -22,6 +24,7 @@ const ( tplDatasetCreate base.TplName = "repo/datasets/create" tplDatasetEdit base.TplName = "repo/datasets/edit" taskstplIndex base.TplName = "repo/datasets/tasks/index" + tplReference base.TplName = "repo/datasets/reference" ) // MustEnableDataset check if repository enable internal dataset @@ -266,6 +269,37 @@ func CreateDatasetPost(ctx *context.Context, form auth.CreateDatasetForm) { ctx.JSON(http.StatusOK, models.BaseOKMessage) } +} +func ReferenceDatasetDelete(ctx *context.Context) { + repoID := ctx.Repo.Repository.ID + datasetId, _ := strconv.ParseInt(ctx.Params(":id"), 10, 64) + + oldDatasetIds := models.GetDatasetIdsByRepoID(repoID) + + var newDatasetIds []int64 + + for _, tempDatasetId := range oldDatasetIds { + if datasetId != tempDatasetId { + newDatasetIds = append(newDatasetIds, tempDatasetId) + } + } + err := models.NewDatasetIdsByRepoID(repoID, newDatasetIds) + if err != nil { + ctx.JSON(http.StatusOK, models.BaseErrorMessage("dataset.cancel_reference_dataset_fail")) + } + ctx.JSON(http.StatusOK, models.BaseOKMessage) + +} + +func ReferenceDatasetPost(ctx *context.Context, form auth.ReferenceDatasetForm) { + repoID := ctx.Repo.Repository.ID + err := models.NewDatasetIdsByRepoID(repoID, form.DatasetID) + if err != nil { + ctx.JSON(http.StatusOK, models.BaseErrorMessage("dataset.reference_dataset_fail")) + } + + ctx.JSON(http.StatusOK, models.BaseOKMessage) + } func EditDatasetPost(ctx *context.Context, form auth.EditDatasetForm) { @@ -412,18 +446,17 @@ func MyDatasets(ctx *context.Context) { func datasetMultiple(ctx *context.Context, opts *models.SearchDatasetOptions) { page := ctx.QueryInt("page") - cloudbrainType := ctx.QueryInt("type") keyword := strings.Trim(ctx.Query("q"), " ") - orderBy := models.SearchOrderByRecentUpdated opts.Keyword = keyword - opts.SearchOrderBy = orderBy + if opts.SearchOrderBy.String() == "" { + opts.SearchOrderBy = models.SearchOrderByRecentUpdated + } + opts.RecommendOnly = ctx.QueryBool("recommend") - opts.CloudBrainType = cloudbrainType opts.ListOptions = models.ListOptions{ Page: page, PageSize: setting.UI.DatasetPagingNum, } - opts.NeedAttachment = true opts.JustNeedZipFile = true opts.User = ctx.User @@ -449,22 +482,52 @@ func datasetMultiple(ctx *context.Context, opts *models.SearchDatasetOptions) { "data": string(data), "count": strconv.FormatInt(count, 10), }) - } func CurrentRepoDatasetMultiple(ctx *context.Context) { - + datasetIds := models.GetDatasetIdsByRepoID(ctx.Repo.Repository.ID) + searchOrderBy := getSearchOrderByInValues(datasetIds) opts := &models.SearchDatasetOptions{ - RepoID: ctx.Repo.Repository.ID, + RepoID: ctx.Repo.Repository.ID, + NeedAttachment: true, + CloudBrainType: ctx.QueryInt("type"), + DatasetIDs: datasetIds, + SearchOrderBy: searchOrderBy, } + datasetMultiple(ctx, opts) } +func getSearchOrderByInValues(datasetIds []int64) models.SearchOrderBy { + if len(datasetIds) == 0 { + return "" + } + searchOrderBy := "CASE id " + for i, id := range datasetIds { + searchOrderBy += fmt.Sprintf(" WHEN %d THEN %d", id, i+1) + } + searchOrderBy += " ELSE 0 END" + return models.SearchOrderBy(searchOrderBy) +} + func MyDatasetsMultiple(ctx *context.Context) { opts := &models.SearchDatasetOptions{ UploadAttachmentByMe: true, + NeedAttachment: true, + CloudBrainType: ctx.QueryInt("type"), + } + datasetMultiple(ctx, opts) + +} + +func ReferenceDatasetAvailable(ctx *context.Context) { + + opts := &models.SearchDatasetOptions{ + PublicOnly: true, + NeedAttachment: false, + CloudBrainType: models.TypeCloudBrainAll, } datasetMultiple(ctx, opts) @@ -473,7 +536,9 @@ func MyDatasetsMultiple(ctx *context.Context) { func PublicDatasetMultiple(ctx *context.Context) { opts := &models.SearchDatasetOptions{ - PublicOnly: true, + PublicOnly: true, + NeedAttachment: true, + CloudBrainType: ctx.QueryInt("type"), } datasetMultiple(ctx, opts) @@ -482,11 +547,50 @@ func PublicDatasetMultiple(ctx *context.Context) { func MyFavoriteDatasetMultiple(ctx *context.Context) { opts := &models.SearchDatasetOptions{ - StarByMe: true, - DatasetIDs: models.GetDatasetIdsStarByUser(ctx.User.ID), + StarByMe: true, + DatasetIDs: models.GetDatasetIdsStarByUser(ctx.User.ID), + NeedAttachment: true, + CloudBrainType: ctx.QueryInt("type"), } datasetMultiple(ctx, opts) } +func ReferenceDataset(ctx *context.Context) { + MustEnableDataset(ctx) + ctx.Data["PageIsDataset"] = true + ctx.Data["MaxReferenceDatasetNum"] = setting.RepoMaxReferenceDatasetNum + ctx.Data["CanWrite"] = ctx.Repo.CanWrite(models.UnitTypeDatasets) + ctx.HTML(200, tplReference) + +} + +func ReferenceDatasetData(ctx *context.Context) { + MustEnableDataset(ctx) + datasetIds := models.GetDatasetIdsByRepoID(ctx.Repo.Repository.ID) + var datasets models.DatasetList + var err error + if len(datasetIds) > 0 { + + opts := &models.SearchDatasetOptions{ + DatasetIDs: datasetIds, + NeedAttachment: false, + CloudBrainType: models.TypeCloudBrainAll, + ListOptions: models.ListOptions{ + Page: 1, + PageSize: setting.RepoMaxReferenceDatasetNum, + }, + SearchOrderBy: getSearchOrderByInValues(datasetIds), + QueryReference: true, + } + datasets, _, err = models.SearchDataset(opts) + if err != nil { + ctx.ServerError("SearchDatasets", err) + return + } + } + + ctx.JSON(http.StatusOK, repository.ConvertToDatasetWithStar(ctx, datasets)) + +} func PublicDataset(ctx *context.Context) { page := ctx.QueryInt("page") diff --git a/routers/repo/grampus.go b/routers/repo/grampus.go index 2e084b535..f40b56007 100755 --- a/routers/repo/grampus.go +++ b/routers/repo/grampus.go @@ -1,15 +1,10 @@ package repo import ( - "code.gitea.io/gitea/modules/auth" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/grampus" - "code.gitea.io/gitea/modules/modelarts" - "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/services/cloudbrain/resource" "encoding/json" "errors" - "github.com/unknwon/com" + "fmt" "io/ioutil" "net/http" "os" @@ -18,6 +13,15 @@ import ( "strings" "time" + "code.gitea.io/gitea/modules/auth" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/grampus" + "code.gitea.io/gitea/modules/modelarts" + "code.gitea.io/gitea/modules/notification" + "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/util" + "github.com/unknwon/com" + "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/cloudbrain" @@ -43,6 +47,7 @@ func GrampusTrainJobGPUNew(ctx *context.Context) { ctx.ServerError("get new train-job info failed", err) return } + ctx.HTML(http.StatusOK, tplGrampusTrainJobGPUNew) } @@ -60,7 +65,7 @@ func grampusTrainJobNewDataPrepare(ctx *context.Context, processType string) err ctx.Data["PageIsCloudBrain"] = true t := time.Now() - var displayJobName = cutString(ctx.User.Name, 5) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:] + var displayJobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:] ctx.Data["display_job_name"] = displayJobName //get valid images @@ -102,15 +107,11 @@ func grampusTrainJobNewDataPrepare(ctx *context.Context, processType string) err } } - //get valid resource specs - specs, err := grampus.GetResourceSpecs(processType) - - grampusSpecs := getFilterSpecBySpecialPool(specs, includeCenters, excludeCenters) - - if err != nil { - log.Error("GetResourceSpecs failed:", err.Error()) - } else { - ctx.Data["flavor_infos"] = grampusSpecs + //prepare available specs + if processType == grampus.ProcessorTypeNPU { + prepareGrampusTrainSpecs(ctx, models.NPU) + } else if processType == grampus.ProcessorTypeGPU { + prepareGrampusTrainSpecs(ctx, models.GPU) } //get branches @@ -125,13 +126,26 @@ func grampusTrainJobNewDataPrepare(ctx *context.Context, processType string) err if processType == grampus.ProcessorTypeGPU { ctx.Data["datasetType"] = models.TypeCloudBrainOne + waitCount := cloudbrain.GetWaitingCloudbrainCount(models.TypeC2Net, models.GPUResource, models.JobTypeTrain) + ctx.Data["WaitCount"] = waitCount } else if processType == grampus.ProcessorTypeNPU { ctx.Data["datasetType"] = models.TypeCloudBrainTwo + waitCount := cloudbrain.GetWaitingCloudbrainCount(models.TypeC2Net, models.NPUResource, models.JobTypeTrain) + ctx.Data["WaitCount"] = waitCount } return nil } +func prepareGrampusTrainSpecs(ctx *context.Context, computeResource string) { + noteBookSpecs, _ := resource.FindAvailableSpecs(ctx.User.ID, models.FindSpecsOptions{ + JobType: models.JobTypeTrain, + ComputeResource: computeResource, + Cluster: models.C2NetCluster, + }) + ctx.Data["Specs"] = noteBookSpecs +} + func getFilterSpecBySpecialPool(specs *models.GetGrampusResourceSpecsResult, includeCenters map[string]struct{}, excludeCenters map[string]struct{}) []models.GrampusSpec { if len(includeCenters) == 0 && len(excludeCenters) == 0 { return specs.Infos @@ -198,7 +212,6 @@ func GrampusTrainJobGpuCreate(ctx *context.Context, form auth.CreateGrampusTrain codeMinioPath := setting.CBCodePathPrefix + jobName + cloudbrain.CodeMountPath + "/" dataMinioPath := setting.Attachment.Minio.BasePath + path.Join(uuid[0:1], uuid[1:2]) + "/" + uuid branchName := form.BranchName - flavorName := form.FlavorName image := strings.TrimSpace(form.Image) if !jobNamePattern.MatchString(displayJobName) { @@ -207,6 +220,14 @@ func GrampusTrainJobGpuCreate(ctx *context.Context, form auth.CreateGrampusTrain return } + bootFileExist, err := ctx.Repo.FileExists(bootFile, branchName) + if err != nil || !bootFileExist { + log.Error("Get bootfile error:", err, ctx.Data["MsgID"]) + grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeGPU) + ctx.RenderWithErr(ctx.Tr("repo.cloudbrain_bootfile_err"), tplGrampusTrainJobGPUNew, &form) + return + } + errStr := checkSpecialPool(ctx, "GPU") if errStr != "" { grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeGPU) @@ -256,6 +277,18 @@ func GrampusTrainJobGpuCreate(ctx *context.Context, form auth.CreateGrampusTrain } } + //check specification + spec, err := resource.GetAndCheckSpec(ctx.User.ID, form.SpecId, models.FindSpecsOptions{ + JobType: models.JobTypeTrain, + ComputeResource: models.GPU, + Cluster: models.C2NetCluster, + }) + if err != nil || spec == nil { + grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeGPU) + ctx.RenderWithErr("Resource specification not available", tplGrampusTrainJobGPUNew, &form) + return + } + //check dataset attachment, err := models.GetAttachmentByUUID(uuid) if err != nil { @@ -274,7 +307,7 @@ func GrampusTrainJobGpuCreate(ctx *context.Context, form auth.CreateGrampusTrain if err := downloadZipCode(ctx, codeLocalPath, branchName); err != nil { log.Error("downloadZipCode failed, server timed out: %s (%v)", repo.FullName(), err, ctx.Data["MsgID"]) grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeGPU) - ctx.RenderWithErr("Create task failed, internal error", tplGrampusTrainJobGPUNew, &form) + ctx.RenderWithErr(ctx.Tr("cloudbrain.load_code_failed"), tplGrampusTrainJobGPUNew, &form) return } @@ -283,7 +316,7 @@ func GrampusTrainJobGpuCreate(ctx *context.Context, form auth.CreateGrampusTrain if err := uploadCodeToMinio(codeLocalPath+"/", jobName, cloudbrain.CodeMountPath+"/"); err != nil { log.Error("Failed to uploadCodeToMinio: %s (%v)", repo.FullName(), err, ctx.Data["MsgID"]) grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeGPU) - ctx.RenderWithErr("Create task failed, internal error", tplGrampusTrainJobGPUNew, &form) + ctx.RenderWithErr(ctx.Tr("cloudbrain.load_code_failed"), tplGrampusTrainJobGPUNew, &form) return } @@ -291,7 +324,7 @@ func GrampusTrainJobGpuCreate(ctx *context.Context, form auth.CreateGrampusTrain if err := mkModelPath(modelPath); err != nil { log.Error("Failed to mkModelPath: %s (%v)", repo.FullName(), err, ctx.Data["MsgID"]) grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeGPU) - ctx.RenderWithErr("Create task failed, internal error", tplGrampusTrainJobGPUNew, &form) + ctx.RenderWithErr(ctx.Tr("cloudbrain.load_code_failed"), tplGrampusTrainJobGPUNew, &form) return } @@ -299,7 +332,7 @@ func GrampusTrainJobGpuCreate(ctx *context.Context, form auth.CreateGrampusTrain if err := uploadCodeToMinio(modelPath, jobName, cloudbrain.ModelMountPath+"/"); err != nil { log.Error("Failed to uploadCodeToMinio: %s (%v)", repo.FullName(), err, ctx.Data["MsgID"]) grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeGPU) - ctx.RenderWithErr("Create task failed, internal error", tplGrampusTrainJobGPUNew, &form) + ctx.RenderWithErr(ctx.Tr("cloudbrain.load_code_failed"), tplGrampusTrainJobGPUNew, &form) return } @@ -320,7 +353,6 @@ func GrampusTrainJobGpuCreate(ctx *context.Context, form auth.CreateGrampusTrain ComputeResource: models.GPUResource, ProcessType: grampus.ProcessorTypeGPU, Command: command, - ResourceSpecId: form.FlavorID, ImageUrl: image, Description: description, BootFile: bootFile, @@ -328,12 +360,12 @@ func GrampusTrainJobGpuCreate(ctx *context.Context, form auth.CreateGrampusTrain CommitID: commitID, BranchName: branchName, Params: form.Params, - FlavorName: flavorName, EngineName: image, DatasetName: attachment.Name, IsLatestVersion: modelarts.IsLatestVersion, - VersionCount: modelarts.VersionCount, + VersionCount: modelarts.VersionCountOne, WorkServerNumber: 1, + Spec: spec, } err = grampus.GenerateTrainJob(ctx, req) @@ -381,8 +413,7 @@ func GrampusTrainJobNpuCreate(ctx *context.Context, form auth.CreateGrampusTrain dataObsPath := setting.BasePath + path.Join(uuid[0:1], uuid[1:2]) + "/" + uuid + "/" branchName := form.BranchName isLatestVersion := modelarts.IsLatestVersion - flavorName := form.FlavorName - versionCount := modelarts.VersionCount + versionCount := modelarts.VersionCountOne engineName := form.EngineName if !jobNamePattern.MatchString(displayJobName) { @@ -391,6 +422,14 @@ func GrampusTrainJobNpuCreate(ctx *context.Context, form auth.CreateGrampusTrain return } + bootFileExist, err := ctx.Repo.FileExists(bootFile, branchName) + if err != nil || !bootFileExist { + log.Error("Get bootfile error:", err, ctx.Data["MsgID"]) + grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeNPU) + ctx.RenderWithErr(ctx.Tr("repo.cloudbrain_bootfile_err"), tplGrampusTrainJobNPUNew, &form) + return + } + errStr := checkSpecialPool(ctx, "NPU") if errStr != "" { grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeNPU) @@ -440,6 +479,18 @@ func GrampusTrainJobNpuCreate(ctx *context.Context, form auth.CreateGrampusTrain } } + //check specification + spec, err := resource.GetAndCheckSpec(ctx.User.ID, form.SpecId, models.FindSpecsOptions{ + JobType: models.JobTypeTrain, + ComputeResource: models.NPU, + Cluster: models.C2NetCluster, + }) + if err != nil || spec == nil { + grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeNPU) + ctx.RenderWithErr("Resource specification not available", tplGrampusTrainJobNPUNew, &form) + return + } + //check dataset attachment, err := models.GetAttachmentByUUID(uuid) if err != nil { @@ -458,7 +509,7 @@ func GrampusTrainJobNpuCreate(ctx *context.Context, form auth.CreateGrampusTrain if err := downloadZipCode(ctx, codeLocalPath, branchName); err != nil { log.Error("downloadZipCode failed, server timed out: %s (%v)", repo.FullName(), err) grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeNPU) - ctx.RenderWithErr("Create task failed, server timed out", tplGrampusTrainJobNPUNew, &form) + ctx.RenderWithErr(ctx.Tr("cloudbrain.load_code_failed"), tplGrampusTrainJobNPUNew, &form) return } @@ -466,14 +517,14 @@ func GrampusTrainJobNpuCreate(ctx *context.Context, form auth.CreateGrampusTrain if err := obsMkdir(setting.CodePathPrefix + jobName + modelarts.OutputPath); err != nil { log.Error("Failed to obsMkdir_output: %s (%v)", repo.FullName(), err) grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeNPU) - ctx.RenderWithErr("Failed to obsMkdir_output", tplGrampusTrainJobNPUNew, &form) + ctx.RenderWithErr(ctx.Tr("cloudbrain.load_code_failed"), tplGrampusTrainJobNPUNew, &form) return } if err := uploadCodeToObs(codeLocalPath, jobName, ""); err != nil { log.Error("Failed to uploadCodeToObs: %s (%v)", repo.FullName(), err) grampusTrainJobNewDataPrepare(ctx, grampus.ProcessorTypeNPU) - ctx.RenderWithErr("Failed to uploadCodeToObs", tplGrampusTrainJobNPUNew, &form) + ctx.RenderWithErr(ctx.Tr("cloudbrain.load_code_failed"), tplGrampusTrainJobNPUNew, &form) return } @@ -494,7 +545,6 @@ func GrampusTrainJobNpuCreate(ctx *context.Context, form auth.CreateGrampusTrain ComputeResource: models.NPUResource, ProcessType: grampus.ProcessorTypeNPU, Command: command, - ResourceSpecId: form.FlavorID, ImageId: form.ImageID, DataUrl: dataObsPath, Description: description, @@ -507,11 +557,11 @@ func GrampusTrainJobNpuCreate(ctx *context.Context, form auth.CreateGrampusTrain IsLatestVersion: isLatestVersion, BranchName: branchName, Params: form.Params, - FlavorName: flavorName, EngineName: engineName, VersionCount: versionCount, TotalVersionCount: modelarts.TotalVersionCount, DatasetName: attachment.Name, + Spec: spec, } err = grampus.GenerateTrainJob(ctx, req) @@ -546,12 +596,15 @@ func GrampusStopJob(ctx *context.Context) { errorMsg = res.ErrorMsg break } - + oldStatus := task.Status task.Status = string(models.GrampusStatusStopped) if task.EndTime == 0 { task.EndTime = timeutil.TimeStampNow() } task.ComputeAndSetDuration() + if oldStatus != task.Status { + notification.NotifyChangeCloudbrainStatus(task, oldStatus) + } err = models.UpdateJob(task) if err != nil { log.Error("UpdateJob(%s) failed:%v", task.JobName, err, ctx.Data["msgID"]) @@ -610,7 +663,7 @@ func deleteGrampusJob(ctx *context.Context) error { if task.ComputeResource == models.NPUResource { storageType = models.TypeCloudBrainTwo } - deleteJobStorage(task.JobName, storageType) + DeleteCloudbrainJobStorage(task.JobName, storageType) return nil } @@ -622,7 +675,7 @@ func GrampusTrainJobShow(ctx *context.Context) { task, err := models.GetCloudbrainByJobIDWithDeleted(ctx.Params(":jobid")) if err != nil { log.Error("GetCloudbrainByJobID failed:" + err.Error()) - ctx.ServerError("system error", err) + ctx.NotFound(ctx.Req.URL.RequestURI(), nil) return } @@ -630,14 +683,15 @@ func GrampusTrainJobShow(ctx *context.Context) { result, err := grampus.GetJob(task.JobID) if err != nil { log.Error("GetJob failed:" + err.Error()) - //ctx.ServerError("GetJob failed", err) - //return + ctx.NotFound(ctx.Req.URL.RequestURI(), nil) + return } if result != nil { if len(result.JobInfo.Tasks[0].CenterID) == 1 && len(result.JobInfo.Tasks[0].CenterName) == 1 { task.AiCenter = result.JobInfo.Tasks[0].CenterID[0] + "+" + result.JobInfo.Tasks[0].CenterName[0] } + oldStatus := task.Status task.Status = grampus.TransTrainJobStatus(result.JobInfo.Status) if task.Status != result.JobInfo.Status || result.JobInfo.Status == models.GrampusStatusRunning { task.Duration = result.JobInfo.RunSec @@ -650,6 +704,9 @@ func GrampusTrainJobShow(ctx *context.Context) { task.EndTime = task.StartTime.Add(task.Duration) } task.CorrectCreateUnix() + if oldStatus != task.Status { + notification.NotifyChangeCloudbrainStatus(task, oldStatus) + } err = models.UpdateJob(task) if err != nil { log.Error("UpdateJob failed:" + err.Error()) @@ -681,8 +738,9 @@ func GrampusTrainJobShow(ctx *context.Context) { taskList := make([]*models.Cloudbrain, 0) taskList = append(taskList, task) + prepareSpec4Show(ctx, task) ctx.Data["version_list_task"] = taskList - + ctx.Data["datasetDownload"] = GetCloudBrainDataSetInfo(task.Uuid, false) ctx.Data["canDownload"] = cloudbrain.CanModifyJob(ctx, task) ctx.Data["displayJobName"] = task.DisplayJobName @@ -736,10 +794,6 @@ func generateCommand(repoName, processorType, codeRemotePath, dataRemotePath, bo command += commandDownload } - //check download result - commandCheckRes := "bash -c \"[[ $? -eq 0 ]] && exit 0 || exit -1;\";" - command += commandCheckRes - //unzip code & dataset toolUnzip := "unzip -q '" if strings.HasSuffix(datasetName, ".tar.gz") { @@ -748,16 +802,22 @@ func generateCommand(repoName, processorType, codeRemotePath, dataRemotePath, bo commandUnzip := "cd " + workDir + "code;unzip -q master.zip;echo \"start to unzip dataset\";cd " + workDir + "dataset;" + toolUnzip + datasetName + "';" command += commandUnzip - //check unzip result - commandCheckRes = "bash -c \"[[ $? -eq 0 ]] && exit 0 || exit -1;\";" - command += commandCheckRes - command += "echo \"unzip finished;start to exec code;\";" + // set export + var commandExport string + if processorType == grampus.ProcessorTypeNPU { + commandExport = "export bucket=" + setting.Bucket + " && export remote_path=" + outputRemotePath + ";" + } else if processorType == grampus.ProcessorTypeGPU { + commandExport = "export env=" + setting.Grampus.Env + " && export remote_path=" + outputRemotePath + ";" + } + + command += commandExport + //exec code var parameters models.Parameters var paramCode string - param := make([]models.Parameter, 0) + if len(paramSrc) != 0 { err := json.Unmarshal([]byte(paramSrc), ¶meters) if err != nil { @@ -766,15 +826,17 @@ func generateCommand(repoName, processorType, codeRemotePath, dataRemotePath, bo } for _, parameter := range parameters.Parameter { - param = append(param, models.Parameter{ - Label: parameter.Label, - Value: parameter.Value, - }) paramCode += " --" + parameter.Label + "=" + parameter.Value } } - commandCode := "cd " + workDir + "code/" + strings.ToLower(repoName) + ";python " + bootFile + paramCode + ";" + var commandCode string + if processorType == grampus.ProcessorTypeNPU { + commandCode = "/bin/bash /home/work/run_train_for_openi.sh " + workDir + "code/" + strings.ToLower(repoName) + "/" + bootFile + " /tmp/log/train.log" + paramCode + ";" + } else if processorType == grampus.ProcessorTypeGPU { + commandCode = "cd " + workDir + "code/" + strings.ToLower(repoName) + ";python " + bootFile + paramCode + ";" + } + command += commandCode //get exec result @@ -783,15 +845,15 @@ func generateCommand(repoName, processorType, codeRemotePath, dataRemotePath, bo //upload models if processorType == grampus.ProcessorTypeNPU { - commandUpload := "cd " + workDir + "script_for_grampus/;./uploader_for_obs " + setting.Bucket + " " + outputRemotePath + " " + workDir + "output/;" + commandUpload := "cd " + workDir + "script_for_grampus/;./uploader_for_npu " + setting.Bucket + " " + outputRemotePath + " " + workDir + "output/;" command += commandUpload } else if processorType == grampus.ProcessorTypeGPU { - commandUpload := "cd " + workDir + "script_for_grampus/;./uploader_for_minio " + setting.Grampus.Env + " " + outputRemotePath + " " + workDir + "output/;" + commandUpload := "cd " + workDir + "script_for_grampus/;./uploader_for_gpu " + setting.Grampus.Env + " " + outputRemotePath + " " + workDir + "output/;" command += commandUpload } //check exec result - commandCheckRes = "bash -c \"[[ $result -eq 0 ]] && exit 0 || exit -1\"" + commandCheckRes := "bash -c \"[[ $result -eq 0 ]] && exit 0 || exit -1\"" command += commandCheckRes return command, nil @@ -826,6 +888,9 @@ func downloadZipCode(ctx *context.Context, codePath, branchName string) error { log.Error("GetBranchCommit failed:" + err.Error()) return err } + } else { + log.Error("the branch is not exist: " + branchName) + return fmt.Errorf("The branch does not exist.") } archivePath = path.Join(archivePath, grampus.CodeArchiveName) diff --git a/routers/repo/modelarts.go b/routers/repo/modelarts.go index 084f38ad7..6dd793f81 100755 --- a/routers/repo/modelarts.go +++ b/routers/repo/modelarts.go @@ -3,9 +3,9 @@ package repo import ( "archive/zip" "code.gitea.io/gitea/services/reward/point/account" + "code.gitea.io/gitea/services/cloudbrain/resource" "encoding/json" "errors" - "fmt" "io" "io/ioutil" "net/http" @@ -16,9 +16,6 @@ import ( "time" "unicode/utf8" - "code.gitea.io/gitea/modules/notification" - "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/base" @@ -27,9 +24,11 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/modelarts" + "code.gitea.io/gitea/modules/notification" "code.gitea.io/gitea/modules/obs" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/storage" + "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" ) @@ -142,16 +141,26 @@ func notebookNewDataPrepare(ctx *context.Context) error { } ctx.Data["images"] = modelarts.ImageInfos.ImageInfo - if modelarts.FlavorInfos == nil { - json.Unmarshal([]byte(setting.FlavorInfos), &modelarts.FlavorInfos) - } - ctx.Data["flavors"] = modelarts.FlavorInfos.FlavorInfo + prepareCloudbrainTwoDebugSpecs(ctx) ctx.Data["datasetType"] = models.TypeCloudBrainTwo + waitCount := cloudbrain.GetWaitingCloudbrainCount(models.TypeCloudBrainTwo, "") + ctx.Data["WaitCount"] = waitCount + return nil } +func prepareCloudbrainTwoDebugSpecs(ctx *context.Context) { + noteBookSpecs, _ := resource.FindAvailableSpecs(ctx.User.ID, models.FindSpecsOptions{ + JobType: models.JobTypeDebug, + ComputeResource: models.NPU, + Cluster: models.OpenICluster, + AiCenterCode: models.AICenterOfCloudBrainTwo, + }) + ctx.Data["Specs"] = noteBookSpecs +} + func NotebookCreate(ctx *context.Context, form auth.CreateModelArtsNotebookForm) { ctx.Data["PageIsNotebook"] = true jobName := form.JobName @@ -202,7 +211,6 @@ func Notebook2Create(ctx *context.Context, form auth.CreateModelArtsNotebookForm jobName := util.ConvertDisplayJobNameToJobName(displayJobName) uuid := form.Attachment description := form.Description - flavor := form.Flavor imageId := form.ImageId repo := ctx.Repo.Repository @@ -244,7 +252,17 @@ func Notebook2Create(ctx *context.Context, form auth.CreateModelArtsNotebookForm } } - err = modelarts.GenerateNotebook2(ctx, displayJobName, jobName, uuid, description, flavor, imageId) + spec, err := resource.GetAndCheckSpec(ctx.User.ID, form.SpecId, models.FindSpecsOptions{ + JobType: models.JobTypeDebug, + ComputeResource: models.NPU, + Cluster: models.OpenICluster, + AiCenterCode: models.AICenterOfCloudBrainTwo}) + if err != nil || spec == nil { + notebookNewDataPrepare(ctx) + ctx.RenderWithErr("Resource specification not available", tplModelArtsNotebookNew, &form) + return + } + err = modelarts.GenerateNotebook2(ctx, displayJobName, jobName, uuid, description, imageId, spec) if err != nil { log.Error("GenerateNotebook2 failed, %v", err, ctx.Data["MsgID"]) notebookNewDataPrepare(ctx) @@ -263,84 +281,33 @@ func NotebookShow(ctx *context.Context) { var ID = ctx.Params(":id") task, err := models.GetCloudbrainByIDWithDeleted(ID) if err != nil { - ctx.Data["error"] = err.Error() - ctx.RenderWithErr(err.Error(), tplModelArtsNotebookShow, nil) - return - } - - result, err := modelarts.GetNotebook2(task.JobID) - if err != nil { - ctx.Data["error"] = err.Error() - ctx.RenderWithErr(err.Error(), tplModelArtsNotebookShow, nil) + log.Error("GET job error", err.Error()) + ctx.NotFound(ctx.Req.URL.RequestURI(), nil) return } - if result != nil { - if task.DeletedAt.IsZero() { //normal record - if task.Status != result.Status { - task.Status = result.Status - models.ParseAndSetDurationFromModelArtsNotebook(result, task) - err = models.UpdateJob(task) - if err != nil { - ctx.Data["error"] = err.Error() - ctx.RenderWithErr(err.Error(), tplModelArtsNotebookShow, nil) - return - } - } - } else { //deleted record - + if task.DeletedAt.IsZero() { //normal record + err := modelarts.HandleNotebookInfo(task) + if err != nil { + ctx.Data["error"] = err.Error() + ctx.RenderWithErr(err.Error(), tplModelArtsNotebookShow, nil) + return } + } else { //deleted record + } datasetDownload := make([]models.DatasetDownload, 0) if ctx.IsSigned { if task.Uuid != "" && task.UserID == ctx.User.ID { - uuidList := strings.Split(task.Uuid, ";") - for _, uuidStr := range uuidList { - attachment, err := models.GetAttachmentByUUID(uuidStr) - if err != nil { - log.Error("GetAttachmentByUUID failed:%v", err.Error()) - return - } - dataset, err := models.GetDatasetByID(attachment.DatasetID) - if err != nil { - log.Error("GetDatasetByID failed:%v", err.Error()) - return - } - repo, err := models.GetRepositoryByID(dataset.RepoID) - if err != nil { - log.Error("GetRepositoryByID failed:%v", err.Error()) - return - } - datasetDownload = append(datasetDownload, models.DatasetDownload{ - DatasetName: attachment.Name, - DatasetDownloadLink: attachment.S3DownloadURL(), - RepositoryLink: repo.Link() + "/datasets", - }) - - } - // datasetName, err := GetDatasetNameByUUID(task.Uuid) - // if err == nil { - // task.DatasetName = datasetName - // } + datasetDownload = GetCloudBrainDataSetInfo(task.Uuid, true) } } user, err := models.GetUserByID(task.UserID) if err == nil { task.User = user } - if modelarts.FlavorInfos == nil { - json.Unmarshal([]byte(setting.FlavorInfos), &modelarts.FlavorInfos) - } - if modelarts.FlavorInfos != nil { - ctx.Data["resource_spec"] = modelarts.FlavorInfos.FlavorInfo[0].Desc - for _, f := range modelarts.FlavorInfos.FlavorInfo { - if fmt.Sprint(f.Value) == task.FlavorCode { - ctx.Data["resource_spec"] = f.Desc - break - } - } - } + prepareSpec4Show(ctx, task) if task.TrainJobDuration == "" { if task.Duration == 0 { var duration int64 @@ -362,6 +329,53 @@ func NotebookShow(ctx *context.Context) { ctx.HTML(200, tplModelArtsNotebookShow) } +func GetCloudBrainDataSetInfo(uuid string, isNeedDown bool) []models.DatasetDownload { + datasetDownload := make([]models.DatasetDownload, 0) + + uuidList := strings.Split(uuid, ";") + for _, uuidStr := range uuidList { + attachment, err := models.GetAttachmentByUUID(uuidStr) + if err != nil { + log.Error("GetAttachmentByUUID failed:%v", err.Error()) + return datasetDownload + } + dataset, err := models.GetDatasetByID(attachment.DatasetID) + if err != nil { + log.Error("GetDatasetByID failed:%v", err.Error()) + return datasetDownload + } + repo, err := models.GetRepositoryByID(dataset.RepoID) + if err != nil { + log.Error("GetRepositoryByID failed:%v", err.Error()) + return datasetDownload + } + url := "" + if isNeedDown { + url = attachment.S3DownloadURL() + } + datasetDownload = append(datasetDownload, models.DatasetDownload{ + DatasetName: attachment.Name, + DatasetDownloadLink: url, + RepositoryLink: repo.Link() + "/datasets", + }) + } + return datasetDownload +} + +func setShowSpecBySpecialPoolConfig(ctx *context.Context, findSpec bool, task *models.Cloudbrain) { + modelarts.InitSpecialPool() + if modelarts.SpecialPools != nil && !findSpec { + for _, pool := range modelarts.SpecialPools.Pools { + for _, flavor := range pool.Flavor { + if flavor.Value == task.FlavorCode { + ctx.Data["resource_spec"] = flavor.Desc + } + } + } + + } +} + func NotebookDebug(ctx *context.Context) { var jobID = ctx.Params(":jobid") @@ -400,89 +414,154 @@ func NotebookDebug2(ctx *context.Context) { ctx.Redirect(result.Url + "?token=" + result.Token) } -func NotebookManage(ctx *context.Context) { - var ID = ctx.Params(":id") - var action = ctx.Params(":action") - var resultCode = "0" +func NotebookRestart(ctx *context.Context) { + var id = ctx.Params(":id") + var resultCode = "-1" var errorMsg = "" var status = "" + var spec *models.Specification + + task := ctx.Cloudbrain for { - task, err := models.GetCloudbrainByID(ID) - if err != nil { - log.Error("get task(%s) failed:%v", task.JobName, err.Error(), ctx.Data["MsgID"]) + ctx.CheckWechatBind() + if ctx.Written() { + return + } + if task.Status != string(models.ModelArtsStopped) && task.Status != string(models.ModelArtsStartFailed) && task.Status != string(models.ModelArtsCreateFailed) { + log.Error("the job(%s) is not stopped", task.JobName, ctx.Data["MsgID"]) + errorMsg = "the job is not stopped" + break + } + + if !account.IsPointBalanceEnough(ctx.User.ID, task.JobType, task.Type, task.ResourceSpecId, task.FlavorCode) { + log.Error("point balance is not enough,userId=%d jobType=%s resourceSpecId=%d", ctx.User.ID, task.JobType, task.ResourceSpecId) resultCode = "-1" - errorMsg = "system error" + errorMsg = ctx.Tr("points.insufficient_points_balance") break + return } - if action == models.ActionStop { - if task.Status != string(models.ModelArtsRunning) { - log.Error("the job(%s) is not running", task.JobName, ctx.Data["MsgID"]) - resultCode = "-1" - errorMsg = "the job is not running" + count, err := models.GetCloudbrainNotebookCountByUserID(ctx.User.ID) + if err != nil { + log.Error("GetCloudbrainNotebookCountByUserID failed:%v", err, ctx.Data["MsgID"]) + errorMsg = "system error" + break + } else { + if count >= 1 { + log.Error("the user already has running or waiting task", ctx.Data["MsgID"]) + errorMsg = "you have already a running or waiting task, can not create more" break } + } - if !ctx.IsSigned || (ctx.User.ID != task.UserID && !ctx.IsUserSiteAdmin() && !ctx.IsUserRepoOwner()) { - log.Error("the user has no right ro stop the job", task.JobName, ctx.Data["MsgID"]) - resultCode = "-1" - errorMsg = "you have no right to stop the job" - break - } - } else if action == models.ActionRestart { - ctx.CheckWechatBind() - if ctx.Written() { - return - } - if task.Status != string(models.ModelArtsStopped) && task.Status != string(models.ModelArtsStartFailed) && task.Status != string(models.ModelArtsCreateFailed) { - log.Error("the job(%s) is not stopped", task.JobName, ctx.Data["MsgID"]) - resultCode = "-1" - errorMsg = "the job is not stopped" - break - } + oldSpec, err := resource.GetCloudbrainSpec(task.ID) + if err != nil || oldSpec == nil { + log.Error("NotebookManage GetCloudbrainSpec error.%v", err) + errorMsg = "Resource specification not available" + break + } + spec, err = resource.GetAndCheckSpec(ctx.User.ID, oldSpec.ID, models.FindSpecsOptions{ + JobType: models.JobType(task.JobType), + ComputeResource: models.NPU, + Cluster: models.OpenICluster, + AiCenterCode: models.AICenterOfCloudBrainTwo}) + if err != nil || spec == nil { + log.Error("NotebookManage GetAndCheckSpec error.task.id = %d", task.ID) + errorMsg = "Resource specification not support any more" + break + } - if !ctx.IsSigned || (ctx.User.ID != task.UserID && !ctx.IsUserSiteAdmin()) { - log.Error("the user has no right ro restart the job", task.JobName, ctx.Data["MsgID"]) - resultCode = "-1" - errorMsg = "you have no right to restart the job" - break - } - if !account.IsPointBalanceEnough(ctx.User.ID, task.JobType, task.Type, task.ResourceSpecId, task.FlavorCode) { - log.Error("point balance is not enough,userId=%d jobType=%s resourceSpecId=%d", ctx.User.ID, task.JobType, task.ResourceSpecId) - resultCode = "-1" - errorMsg = ctx.Tr("points.insufficient_points_balance") - break - return - } + createTime := timeutil.TimeStampNow() + param := models.NotebookAction{ + Action: models.ActionStart, + } - count, err := models.GetCloudbrainNotebookCountByUserID(ctx.User.ID) - if err != nil { - log.Error("GetCloudbrainNotebookCountByUserID failed:%v", err, ctx.Data["MsgID"]) - resultCode = "-1" - errorMsg = "system error" - break - } else { - if count >= 1 { - log.Error("the user already has running or waiting task", ctx.Data["MsgID"]) - resultCode = "-1" - errorMsg = "you have already a running or waiting task, can not create more" - break + res, err := modelarts.ManageNotebook2(task.JobID, param) + if err != nil { + log.Error("ManageNotebook2(%s) failed:%v", task.DisplayJobName, err.Error(), ctx.Data["MsgID"]) + /* 暂不处理再次调试502的场景,详情见方案 + if strings.HasPrefix(err.Error(), modelarts.UnknownErrorPrefix) { + log.Info("(%s)unknown error, set temp status", task.DisplayJobName) + errTemp := models.InsertCloudbrainTemp(&models.CloudbrainTemp{ + JobID: task.JobID, + VersionID: models.TempVersionId, + Status: models.TempJobStatus, + Type: task.Type, + JobName: task.JobName, + JobType: task.JobType, + }) + if errTemp != nil { + log.Error("InsertCloudbrainTemp failed: %v", errTemp.Error()) } } + */ + errorMsg = err.Error() + break + } - action = models.ActionStart - } else { - log.Error("the action(%s) is illegal", action, ctx.Data["MsgID"]) + newTask := &models.Cloudbrain{ + Status: res.Status, + UserID: task.UserID, + RepoID: task.RepoID, + JobID: task.JobID, + JobName: task.JobName, + DisplayJobName: task.DisplayJobName, + JobType: task.JobType, + Type: task.Type, + Uuid: task.Uuid, + Image: task.Image, + ComputeResource: task.ComputeResource, + Description: task.Description, + CreatedUnix: createTime, + UpdatedUnix: createTime, + Spec: spec, + } + + err = models.RestartCloudbrain(task, newTask) + if err != nil { + log.Error("RestartCloudbrain(%s) failed:%v", task.JobName, err.Error(), ctx.Data["MsgID"]) + errorMsg = "system error" + break + } + + id = strconv.FormatInt(newTask.ID, 10) + + status = res.Status + resultCode = "0" + notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, id, newTask.DisplayJobName, models.ActionCreateDebugNPUTask) + + break + } + + ctx.JSON(200, map[string]string{ + "result_code": resultCode, + "error_msg": errorMsg, + "status": status, + "id": id, + }) +} + +func NotebookStop(ctx *context.Context) { + var id = ctx.Params(":id") + var resultCode = "0" + var errorMsg = "" + var status = "" + + task := ctx.Cloudbrain + + for { + if task.Status != string(models.ModelArtsRunning) { + log.Error("the job(%s) is not running", task.JobName, ctx.Data["MsgID"]) resultCode = "-1" - errorMsg = "非法操作" + errorMsg = "the job is not running" break } param := models.NotebookAction{ - Action: action, + Action: models.ActionStop, } - createTime := timeutil.TimeStampNow() + res, err := modelarts.ManageNotebook2(task.JobID, param) if err != nil { log.Error("ManageNotebook2(%s) failed:%v", task.JobName, err.Error(), ctx.Data["MsgID"]) @@ -495,46 +574,21 @@ func NotebookManage(ctx *context.Context) { } status = res.Status - if action == models.ActionStart { - newTask := &models.Cloudbrain{ - Status: status, - UserID: task.UserID, - RepoID: task.RepoID, - JobID: task.JobID, - JobName: task.JobName, - DisplayJobName: task.DisplayJobName, - JobType: task.JobType, - Type: task.Type, - Uuid: task.Uuid, - Image: task.Image, - ComputeResource: task.ComputeResource, - Description: task.Description, - CreatedUnix: createTime, - UpdatedUnix: createTime, - } - - err = models.RestartCloudbrain(task, newTask) - if err != nil { - log.Error("RestartCloudbrain(%s) failed:%v", task.JobName, err.Error(), ctx.Data["MsgID"]) - resultCode = "-1" - errorMsg = "system error" - break - } - ID = strconv.FormatInt(newTask.ID, 10) - notification.NotifyOtherTask(ctx.User, ctx.Repo.Repository, ID, task.DisplayJobName, models.ActionCreateDebugNPUTask) - } else { - task.Status = res.Status - if task.EndTime == 0 && models.IsModelArtsDebugJobTerminal(task.Status) { - task.EndTime = timeutil.TimeStampNow() - } - task.ComputeAndSetDuration() - err = models.UpdateJob(task) - if err != nil { - log.Error("UpdateJob(%s) failed:%v", task.JobName, err.Error(), ctx.Data["MsgID"]) - resultCode = "-1" - errorMsg = "system error" - break - } + oldStatus := task.Status + task.Status = res.Status + if task.EndTime == 0 && models.IsModelArtsDebugJobTerminal(task.Status) { + task.EndTime = timeutil.TimeStampNow() + } + task.ComputeAndSetDuration() + if oldStatus != task.Status { + notification.NotifyChangeCloudbrainStatus(task, oldStatus) + } + err = models.UpdateJob(task) + if err != nil { + log.Error("UpdateJob(%s) failed:%v", task.JobName, err.Error(), ctx.Data["MsgID"]) + resultCode = "-1" + errorMsg = "system error" + break } break @@ -544,7 +598,7 @@ func NotebookManage(ctx *context.Context) { "result_code": resultCode, "error_msg": errorMsg, "status": status, - "id": ID, + "id": id, }) } @@ -552,7 +606,7 @@ func NotebookDel(ctx *context.Context) { var listType = ctx.Query("debugListType") task := ctx.Cloudbrain - if task.Status != string(models.ModelArtsCreateFailed) && task.Status != string(models.ModelArtsStartFailed) && task.Status != string(models.ModelArtsStopped) { + if task.Status != string(models.ModelArtsCreateFailed) && task.Status != string(models.ModelArtsStartFailed) && task.Status != string(models.ModelArtsStopped) && task.Status != string(models.ModelArtsDeleted) { log.Error("the job(%s) has not been stopped", task.JobName) ctx.RenderWithErr("the job has not been stopped", tplDebugJobIndex, nil) return @@ -663,7 +717,7 @@ func trainJobNewDataPrepare(ctx *context.Context) error { //} t := time.Now() - var displayJobName = cutString(ctx.User.Name, 5) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:] + var displayJobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:] ctx.Data["display_job_name"] = displayJobName attachs, err := models.GetModelArtsTrainAttachments(ctx.User.ID) @@ -694,12 +748,7 @@ func trainJobNewDataPrepare(ctx *context.Context) error { } ctx.Data["engine_versions"] = versionInfos.Version - var flavorInfos modelarts.Flavor - if err = json.Unmarshal([]byte(setting.TrainJobFLAVORINFOS), &flavorInfos); err != nil { - ctx.ServerError("json.Unmarshal failed:", err) - return err - } - ctx.Data["flavor_infos"] = flavorInfos.Info + prepareCloudbrainTwoTrainSpecs(ctx) ctx.Data["params"] = "" ctx.Data["branchName"] = ctx.Repo.BranchName @@ -711,10 +760,57 @@ func trainJobNewDataPrepare(ctx *context.Context) error { } ctx.Data["config_list"] = configList.ParaConfigs ctx.Data["datasetType"] = models.TypeCloudBrainTwo + waitCount := cloudbrain.GetWaitingCloudbrainCount(models.TypeCloudBrainTwo, "") + ctx.Data["WaitCount"] = waitCount return nil } +func prepareCloudbrainTwoTrainSpecs(ctx *context.Context) { + noteBookSpecs, _ := resource.FindAvailableSpecs(ctx.User.ID, models.FindSpecsOptions{ + JobType: models.JobTypeTrain, + ComputeResource: models.NPU, + Cluster: models.OpenICluster, + AiCenterCode: models.AICenterOfCloudBrainTwo, + }) + ctx.Data["Specs"] = noteBookSpecs +} + +func setSpecBySpecialPoolConfig(ctx *context.Context, jobType string) { + modelarts.InitSpecialPool() + + if modelarts.SpecialPools != nil { + for _, specialPool := range modelarts.SpecialPools.Pools { + if cloudbrain.IsElementExist(specialPool.JobType, jobType) { + + if isInOrg, _ := models.IsOrganizationMemberByOrgName(specialPool.Org, ctx.User.ID); isInOrg { + var specialFlavor []struct { + Code string + Value string + } + + if jobType == string(models.JobTypeDebug) { + ctx.Data["flavors"] = specialPool.Flavor + } else { + + for _, tempFlavor := range specialPool.Flavor { + specialFlavor = append(specialFlavor, struct { + Code string + Value string + }{Code: tempFlavor.Value, Value: tempFlavor.Desc}) + } + + ctx.Data["flavor_infos"] = specialFlavor + } + + } + + } + } + + } +} + func trainJobErrorNewDataPrepare(ctx *context.Context, form auth.CreateModelArtsTrainJobForm) error { ctx.Data["PageIsCloudBrain"] = true @@ -731,7 +827,7 @@ func trainJobErrorNewDataPrepare(ctx *context.Context, form auth.CreateModelArts //} t := time.Now() - var displayJobName = cutString(ctx.User.Name, 5) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:] + var displayJobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:] ctx.Data["display_job_name"] = displayJobName attachs, err := models.GetModelArtsTrainAttachments(ctx.User.ID) @@ -762,12 +858,7 @@ func trainJobErrorNewDataPrepare(ctx *context.Context, form auth.CreateModelArts } ctx.Data["engine_versions"] = versionInfos.Version - var flavorInfos modelarts.Flavor - if err = json.Unmarshal([]byte(setting.TrainJobFLAVORINFOS), &flavorInfos); err != nil { - ctx.ServerError("json.Unmarshal failed:", err) - return err - } - ctx.Data["flavor_infos"] = flavorInfos.Info + prepareCloudbrainTwoTrainSpecs(ctx) configList, err := getConfigList(modelarts.PerPage, 1, modelarts.SortByCreateTime, "desc", "", modelarts.ConfigTypeCustom) if err != nil { @@ -783,8 +874,16 @@ func trainJobErrorNewDataPrepare(ctx *context.Context, form auth.CreateModelArts ctx.Data["config_list"] = configList.ParaConfigs ctx.Data["bootFile"] = form.BootFile ctx.Data["uuid"] = form.Attachment + _, datasetNames, err := models.GetDatasetInfo(form.Attachment) + if err != nil { + log.Error("GetDatasetInfo failed: %v", err, ctx.Data["MsgID"]) + return nil + } + ctx.Data["dataset_name"] = datasetNames ctx.Data["branch_name"] = form.BranchName ctx.Data["datasetType"] = models.TypeCloudBrainTwo + waitCount := cloudbrain.GetWaitingCloudbrainCount(models.TypeCloudBrainTwo, "") + ctx.Data["WaitCount"] = waitCount return nil } @@ -819,12 +918,12 @@ func trainJobNewVersionDataPrepare(ctx *context.Context) error { ctx.Data["display_job_name"] = task.DisplayJobName ctx.Data["job_name"] = task.JobName - attachs, err := models.GetModelArtsTrainAttachments(ctx.User.ID) - if err != nil { - ctx.ServerError("GetAllUserAttachments failed:", err) - return err - } - ctx.Data["attachments"] = attachs + // attachs, err := models.GetModelArtsTrainAttachments(ctx.User.ID) + // if err != nil { + // ctx.ServerError("GetAllUserAttachments failed:", err) + // return err + // } + // ctx.Data["attachments"] = attachs var resourcePools modelarts.ResourcePool if err = json.Unmarshal([]byte(setting.ResourcePools), &resourcePools); err != nil { @@ -847,12 +946,7 @@ func trainJobNewVersionDataPrepare(ctx *context.Context) error { } ctx.Data["engine_versions"] = versionInfos.Version - var flavorInfos modelarts.Flavor - if err = json.Unmarshal([]byte(setting.TrainJobFLAVORINFOS), &flavorInfos); err != nil { - ctx.ServerError("json.Unmarshal failed:", err) - return err - } - ctx.Data["flavor_infos"] = flavorInfos.Info + prepareCloudbrainTwoTrainSpecs(ctx) var Parameters modelarts.Parameters if err = json.Unmarshal([]byte(task.Parameters), &Parameters); err != nil { @@ -866,12 +960,16 @@ func trainJobNewVersionDataPrepare(ctx *context.Context) error { ctx.ServerError("GetBranches error:", err) return err } - + _, _, datasetNames, _, err := getDatasUrlListByUUIDS(task.Uuid) + if err != nil { + ctx.ServerError("GetAllUserAttachments failed:", err) + return err + } ctx.Data["branches"] = branches ctx.Data["branch_name"] = task.BranchName ctx.Data["description"] = task.Description ctx.Data["boot_file"] = task.BootFile - ctx.Data["dataset_name"] = task.DatasetName + ctx.Data["dataset_name"] = datasetNames ctx.Data["work_server_number"] = task.WorkServerNumber ctx.Data["flavor_name"] = task.FlavorName ctx.Data["engine_name"] = task.EngineName @@ -886,6 +984,8 @@ func trainJobNewVersionDataPrepare(ctx *context.Context) error { return err } ctx.Data["config_list"] = configList.ParaConfigs + waitCount := cloudbrain.GetWaitingCloudbrainCount(models.TypeCloudBrainTwo, "") + ctx.Data["WaitCount"] = waitCount return nil } @@ -903,7 +1003,7 @@ func versionErrorDataPrepare(ctx *context.Context, form auth.CreateModelArtsTrai } t := time.Now() - var jobName = cutString(ctx.User.Name, 5) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:] + var jobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:] ctx.Data["job_name"] = task.JobName attachs, err := models.GetModelArtsTrainAttachments(ctx.User.ID) @@ -934,12 +1034,7 @@ func versionErrorDataPrepare(ctx *context.Context, form auth.CreateModelArtsTrai } ctx.Data["engine_versions"] = versionInfos.Version - var flavorInfos modelarts.Flavor - if err = json.Unmarshal([]byte(setting.TrainJobFLAVORINFOS), &flavorInfos); err != nil { - ctx.ServerError("json.Unmarshal failed:", err) - return err - } - ctx.Data["flavor_infos"] = flavorInfos.Info + prepareCloudbrainTwoTrainSpecs(ctx) var Parameters modelarts.Parameters if err = json.Unmarshal([]byte(form.Params), &Parameters); err != nil { @@ -976,6 +1071,8 @@ func versionErrorDataPrepare(ctx *context.Context, form auth.CreateModelArtsTrai } ctx.Data["config_list"] = configList.ParaConfigs ctx.Data["datasetType"] = models.TypeCloudBrainTwo + waitCount := cloudbrain.GetWaitingCloudbrainCount(models.TypeCloudBrainTwo, "") + ctx.Data["WaitCount"] = waitCount return nil } @@ -990,20 +1087,19 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm) workServerNumber := form.WorkServerNumber engineID := form.EngineID bootFile := strings.TrimSpace(form.BootFile) - flavorCode := form.Flavor params := form.Params poolID := form.PoolID - isSaveParam := form.IsSaveParam + //isSaveParam := form.IsSaveParam repo := ctx.Repo.Repository codeLocalPath := setting.JobPath + jobName + modelarts.CodePath codeObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.CodePath outputObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.OutputPath + VersionOutputPath + "/" logObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.LogPath + VersionOutputPath + "/" // dataPath := "/" + setting.Bucket + "/" + setting.BasePath + path.Join(uuid[0:1], uuid[1:2]) + "/" + uuid + uuid + "/" - branch_name := form.BranchName + branchName := form.BranchName isLatestVersion := modelarts.IsLatestVersion FlavorName := form.FlavorName - VersionCount := modelarts.VersionCount + VersionCount := modelarts.VersionCountOne EngineName := form.EngineName if !account.IsPointBalanceEnough(ctx.User.ID, string(models.JobTypeTrain), models.TypeCloudBrainTwo, 0, flavorCode) { @@ -1033,6 +1129,25 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm) ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobNew, &form) return } + + bootFileExist, err := ctx.Repo.FileExists(bootFile, branchName) + if err != nil || !bootFileExist { + log.Error("Get bootfile error:", err) + trainJobErrorNewDataPrepare(ctx, form) + ctx.RenderWithErr(ctx.Tr("repo.cloudbrain_bootfile_err"), tplModelArtsTrainJobNew, &form) + return + } + + spec, err := resource.GetAndCheckSpec(ctx.User.ID, form.SpecId, models.FindSpecsOptions{ + JobType: models.JobTypeTrain, + ComputeResource: models.NPU, + Cluster: models.OpenICluster, + AiCenterCode: models.AICenterOfCloudBrainTwo}) + if err != nil || spec == nil { + trainJobErrorNewDataPrepare(ctx, form) + ctx.RenderWithErr("Resource specification not available", tplModelArtsTrainJobNew, &form) + return + } //Determine whether the task name of the task in the project is duplicated tasks, err := models.GetCloudbrainsByDisplayJobName(repo.ID, string(models.JobTypeTrain), displayJobName) if err == nil { @@ -1058,12 +1173,12 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm) } gitRepo, _ := git.OpenRepository(repo.RepoPath()) - commitID, _ := gitRepo.GetBranchCommitID(branch_name) + commitID, _ := gitRepo.GetBranchCommitID(branchName) - if err := downloadCode(repo, codeLocalPath, branch_name); err != nil { + if err := downloadCode(repo, codeLocalPath, branchName); err != nil { log.Error("downloadCode failed, server timed out: %s (%v)", repo.FullName(), err) trainJobErrorNewDataPrepare(ctx, form) - ctx.RenderWithErr("Create task failed, server timed out", tplModelArtsTrainJobNew, &form) + ctx.RenderWithErr(ctx.Tr("cloudbrain.load_code_failed"), tplModelArtsTrainJobNew, &form) return } @@ -1087,7 +1202,7 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm) // if err := uploadCodeToObs(codeLocalPath, jobName, parentDir); err != nil { log.Error("Failed to uploadCodeToObs: %s (%v)", repo.FullName(), err) trainJobErrorNewDataPrepare(ctx, form) - ctx.RenderWithErr("Failed to uploadCodeToObs", tplModelArtsTrainJobNew, &form) + ctx.RenderWithErr(ctx.Tr("cloudbrain.load_code_failed"), tplModelArtsTrainJobNew, &form) return } @@ -1144,45 +1259,45 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm) } //save param config - if isSaveParam == "on" { - saveparams := append(param, models.Parameter{ - Label: modelarts.TrainUrl, - Value: outputObsPath, - }, models.Parameter{ - Label: modelarts.DataUrl, - Value: dataPath, - }) - if form.ParameterTemplateName == "" { - log.Error("ParameterTemplateName is empty") - trainJobNewDataPrepare(ctx) - ctx.RenderWithErr("保存作业参数时,作业参数名称不能为空", tplModelArtsTrainJobNew, &form) - return - } - - _, err := modelarts.CreateTrainJobConfig(models.CreateConfigParams{ - ConfigName: form.ParameterTemplateName, - Description: form.PrameterDescription, - DataUrl: dataPath, - AppUrl: codeObsPath, - BootFileUrl: codeObsPath + bootFile, - TrainUrl: outputObsPath, - Flavor: models.Flavor{ - Code: flavorCode, - }, - WorkServerNum: workServerNumber, - EngineID: int64(engineID), - LogUrl: logObsPath, - PoolID: poolID, - Parameter: saveparams, - }) - - if err != nil { - log.Error("Failed to CreateTrainJobConfig: %v", err) - trainJobErrorNewDataPrepare(ctx, form) - ctx.RenderWithErr("保存作业参数失败:"+err.Error(), tplModelArtsTrainJobNew, &form) - return - } - } + // if isSaveParam == "on" { + // saveparams := append(param, models.Parameter{ + // Label: modelarts.TrainUrl, + // Value: outputObsPath, + // }, models.Parameter{ + // Label: modelarts.DataUrl, + // Value: dataPath, + // }) + // if form.ParameterTemplateName == "" { + // log.Error("ParameterTemplateName is empty") + // trainJobNewDataPrepare(ctx) + // ctx.RenderWithErr("保存作业参数时,作业参数名称不能为空", tplModelArtsTrainJobNew, &form) + // return + // } + + // _, err := modelarts.CreateTrainJobConfig(models.CreateConfigParams{ + // ConfigName: form.ParameterTemplateName, + // Description: form.PrameterDescription, + // DataUrl: dataPath, + // AppUrl: codeObsPath, + // BootFileUrl: codeObsPath + bootFile, + // TrainUrl: outputObsPath, + // Flavor: models.Flavor{ + // Code: flavorCode, + // }, + // WorkServerNum: workServerNumber, + // EngineID: int64(engineID), + // LogUrl: logObsPath, + // PoolID: poolID, + // Parameter: saveparams, + // }) + + // if err != nil { + // log.Error("Failed to CreateTrainJobConfig: %v", err) + // trainJobErrorNewDataPrepare(ctx, form) + // ctx.RenderWithErr("保存作业参数失败:"+err.Error(), tplModelArtsTrainJobNew, &form) + // return + // } + // } req := &modelarts.GenerateTrainJobReq{ JobName: jobName, @@ -1193,7 +1308,6 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm) BootFileUrl: codeObsPath + bootFile, BootFile: bootFile, TrainUrl: outputObsPath, - FlavorCode: flavorCode, WorkServerNumber: workServerNumber, EngineID: int64(engineID), LogUrl: logObsPath, @@ -1202,14 +1316,18 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm) Parameters: param, CommitID: commitID, IsLatestVersion: isLatestVersion, - BranchName: branch_name, + BranchName: branchName, Params: form.Params, FlavorName: FlavorName, EngineName: EngineName, VersionCount: VersionCount, TotalVersionCount: modelarts.TotalVersionCount, DatasetName: datasetNames, + Spec: spec, } + userCommand, userImageUrl := getUserCommand(engineID, req) + req.UserCommand = userCommand + req.UserImageUrl = userImageUrl //将params转换Parameters.Parameter,出错时返回给前端 var Parameters modelarts.Parameters @@ -1228,6 +1346,36 @@ func TrainJobCreate(ctx *context.Context, form auth.CreateModelArtsTrainJobForm) ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/train-job") } +func getUserCommand(engineId int, req *modelarts.GenerateTrainJobReq) (string, string) { + userImageUrl := "" + userCommand := "" + if engineId < 0 { + tmpCodeObsPath := strings.Trim(req.CodeObsPath, "/") + tmpCodeObsPaths := strings.Split(tmpCodeObsPath, "/") + lastCodeDir := "code" + if len(tmpCodeObsPaths) > 0 { + lastCodeDir = tmpCodeObsPaths[len(tmpCodeObsPaths)-1] + } + userCommand = "/bin/bash /home/work/run_train.sh 's3://" + req.CodeObsPath + "' '" + lastCodeDir + "/" + req.BootFile + "' '/tmp/log/train.log' --'data_url'='s3://" + req.DataUrl + "' --'train_url'='s3://" + req.TrainUrl + "'" + var versionInfos modelarts.VersionInfo + if err := json.Unmarshal([]byte(setting.EngineVersions), &versionInfos); err != nil { + log.Info("json parse err." + err.Error()) + } else { + for _, engine := range versionInfos.Version { + if engine.ID == engineId { + userImageUrl = engine.Url + break + } + } + } + for _, param := range req.Parameters { + userCommand += " --'" + param.Label + "'='" + param.Value + "'" + } + return userCommand, userImageUrl + } + return userCommand, userImageUrl +} + func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJobForm) { ctx.Data["PageIsTrainJob"] = true var jobID = ctx.Params(":jobid") @@ -1261,17 +1409,16 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ workServerNumber := form.WorkServerNumber engineID := form.EngineID bootFile := strings.TrimSpace(form.BootFile) - flavorCode := form.Flavor params := form.Params poolID := form.PoolID - isSaveParam := form.IsSaveParam + //isSaveParam := form.IsSaveParam repo := ctx.Repo.Repository codeLocalPath := setting.JobPath + jobName + modelarts.CodePath codeObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.CodePath + VersionOutputPath + "/" outputObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.OutputPath + VersionOutputPath + "/" logObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.LogPath + VersionOutputPath + "/" // dataPath := "/" + setting.Bucket + "/" + setting.BasePath + path.Join(uuid[0:1], uuid[1:2]) + "/" + uuid + uuid + "/" - branch_name := form.BranchName + branchName := form.BranchName PreVersionName := form.VersionName FlavorName := form.FlavorName EngineName := form.EngineName @@ -1279,6 +1426,7 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ canNewJob, _ := canUserCreateTrainJobVersion(ctx, latestTask.UserID) if !canNewJob { + versionErrorDataPrepare(ctx, form) ctx.RenderWithErr("user cann't new trainjob", tplModelArtsTrainJobVersionNew, &form) return } @@ -1290,6 +1438,25 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ return } + bootFileExist, err := ctx.Repo.FileExists(bootFile, branchName) + if err != nil || !bootFileExist { + log.Error("Get bootfile error:", err) + versionErrorDataPrepare(ctx, form) + ctx.RenderWithErr(ctx.Tr("repo.cloudbrain_bootfile_err"), tplModelArtsTrainJobVersionNew, &form) + return + } + + spec, err := resource.GetAndCheckSpec(ctx.User.ID, form.SpecId, models.FindSpecsOptions{ + JobType: models.JobTypeTrain, + ComputeResource: models.NPU, + Cluster: models.OpenICluster, + AiCenterCode: models.AICenterOfCloudBrainTwo}) + if err != nil || spec == nil { + versionErrorDataPrepare(ctx, form) + ctx.RenderWithErr("Resource specification not available", tplModelArtsTrainJobVersionNew, &form) + return + } + //todo: del the codeLocalPath _, err = ioutil.ReadDir(codeLocalPath) if err == nil { @@ -1297,11 +1464,11 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ } gitRepo, _ := git.OpenRepository(repo.RepoPath()) - commitID, _ := gitRepo.GetBranchCommitID(branch_name) - if err := downloadCode(repo, codeLocalPath, branch_name); err != nil { + commitID, _ := gitRepo.GetBranchCommitID(branchName) + if err := downloadCode(repo, codeLocalPath, branchName); err != nil { log.Error("Failed git clone repo to local(!: %s (%v)", repo.FullName(), err) versionErrorDataPrepare(ctx, form) - ctx.RenderWithErr("Failed git clone repo to local!", tplModelArtsTrainJobVersionNew, &form) + ctx.RenderWithErr(ctx.Tr("cloudbrain.load_code_failed"), tplModelArtsTrainJobVersionNew, &form) return } @@ -1326,7 +1493,7 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ if err := uploadCodeToObs(codeLocalPath, jobName, parentDir); err != nil { log.Error("Failed to uploadCodeToObs: %s (%v)", repo.FullName(), err) versionErrorDataPrepare(ctx, form) - ctx.RenderWithErr("Failed to uploadCodeToObs", tplModelArtsTrainJobVersionNew, &form) + ctx.RenderWithErr(ctx.Tr("cloudbrain.load_code_failed"), tplModelArtsTrainJobVersionNew, &form) return } @@ -1384,46 +1551,46 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ }) } - //save param config - if isSaveParam == "on" { - saveparams := append(param, models.Parameter{ - Label: modelarts.TrainUrl, - Value: outputObsPath, - }, models.Parameter{ - Label: modelarts.DataUrl, - Value: dataPath, - }) - if form.ParameterTemplateName == "" { - log.Error("ParameterTemplateName is empty") - versionErrorDataPrepare(ctx, form) - ctx.RenderWithErr("保存作业参数时,作业参数名称不能为空", tplModelArtsTrainJobVersionNew, &form) - return - } - - _, err := modelarts.CreateTrainJobConfig(models.CreateConfigParams{ - ConfigName: form.ParameterTemplateName, - Description: form.PrameterDescription, - DataUrl: dataPath, - AppUrl: codeObsPath, - BootFileUrl: codeObsPath + bootFile, - TrainUrl: outputObsPath, - Flavor: models.Flavor{ - Code: flavorCode, - }, - WorkServerNum: workServerNumber, - EngineID: int64(engineID), - LogUrl: logObsPath, - PoolID: poolID, - Parameter: saveparams, - }) - - if err != nil { - log.Error("Failed to CreateTrainJobConfig: %v", err) - versionErrorDataPrepare(ctx, form) - ctx.RenderWithErr("保存作业参数失败:"+err.Error(), tplModelArtsTrainJobVersionNew, &form) - return - } - } + // //save param config + // if isSaveParam == "on" { + // saveparams := append(param, models.Parameter{ + // Label: modelarts.TrainUrl, + // Value: outputObsPath, + // }, models.Parameter{ + // Label: modelarts.DataUrl, + // Value: dataPath, + // }) + // if form.ParameterTemplateName == "" { + // log.Error("ParameterTemplateName is empty") + // versionErrorDataPrepare(ctx, form) + // ctx.RenderWithErr("保存作业参数时,作业参数名称不能为空", tplModelArtsTrainJobVersionNew, &form) + // return + // } + + // _, err := modelarts.CreateTrainJobConfig(models.CreateConfigParams{ + // ConfigName: form.ParameterTemplateName, + // Description: form.PrameterDescription, + // DataUrl: dataPath, + // AppUrl: codeObsPath, + // BootFileUrl: codeObsPath + bootFile, + // TrainUrl: outputObsPath, + // Flavor: models.Flavor{ + // Code: flavorCode, + // }, + // WorkServerNum: workServerNumber, + // EngineID: int64(engineID), + // LogUrl: logObsPath, + // PoolID: poolID, + // Parameter: saveparams, + // }) + + // if err != nil { + // log.Error("Failed to CreateTrainJobConfig: %v", err) + // versionErrorDataPrepare(ctx, form) + // ctx.RenderWithErr("保存作业参数失败:"+err.Error(), tplModelArtsTrainJobVersionNew, &form) + // return + // } + // } task, err := models.GetCloudbrainByJobIDAndVersionName(jobID, PreVersionName) if err != nil { @@ -1440,7 +1607,6 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ BootFileUrl: codeObsPath + bootFile, BootFile: bootFile, TrainUrl: outputObsPath, - FlavorCode: flavorCode, WorkServerNumber: workServerNumber, IsLatestVersion: isLatestVersion, EngineID: int64(engineID), @@ -1451,13 +1617,17 @@ func TrainJobCreateVersion(ctx *context.Context, form auth.CreateModelArtsTrainJ Parameters: param, PreVersionId: task.VersionID, CommitID: commitID, - BranchName: branch_name, + BranchName: branchName, FlavorName: FlavorName, EngineName: EngineName, PreVersionName: PreVersionName, TotalVersionCount: latestTask.TotalVersionCount + 1, DatasetName: datasetNames, + Spec: spec, } + userCommand, userImageUrl := getUserCommand(engineID, req) + req.UserCommand = userCommand + req.UserImageUrl = userImageUrl err = modelarts.GenerateTrainJobVersion(ctx, req, jobID) if err != nil { @@ -1622,7 +1792,11 @@ func TrainJobShow(ctx *context.Context) { if err != nil { log.Error("GetVersionListTasks(%s) failed:%v", jobID, err.Error()) - ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobShow, nil) + ctx.NotFound(ctx.Req.URL.RequestURI(), nil) + return + } + if len(VersionListTasks) == 0 { + ctx.NotFound(ctx.Req.URL.RequestURI(), nil) return } //设置权限 @@ -1632,12 +1806,11 @@ func TrainJobShow(ctx *context.Context) { return } ctx.Data["canNewJob"] = canNewJob - + datasetList := make([][]models.DatasetDownload, 0) //将运行参数转化为epoch_size = 3, device_target = Ascend的格式 for i, task := range VersionListTasks { var parameters models.Parameters - err := json.Unmarshal([]byte(VersionListTasks[i].Parameters), ¶meters) if err != nil { log.Error("Failed to Unmarshal Parameters: %s (%v)", VersionListTasks[i].Parameters, err) @@ -1655,9 +1828,17 @@ func TrainJobShow(ctx *context.Context) { } else { VersionListTasks[i].Parameters = "" } - + datasetList = append(datasetList, GetCloudBrainDataSetInfo(task.Uuid, false)) VersionListTasks[i].CanDel = cloudbrain.CanDeleteJob(ctx, &task.Cloudbrain) VersionListTasks[i].CanModify = cloudbrain.CanModifyJob(ctx, &task.Cloudbrain) + + //add spec + s, err := resource.GetCloudbrainSpec(task.Cloudbrain.ID) + if err != nil { + log.Error("TrainJobShow GetCloudbrainSpec error:" + err.Error()) + continue + } + VersionListTasks[i].Cloudbrain.Spec = s } pager := context.NewPagination(VersionListCount, setting.UI.IssuePagingNum, page, 5) @@ -1667,64 +1848,11 @@ func TrainJobShow(ctx *context.Context) { ctx.Data["displayJobName"] = VersionListTasks[0].DisplayJobName ctx.Data["version_list_task"] = VersionListTasks ctx.Data["version_list_count"] = VersionListCount + ctx.Data["datasetList"] = datasetList ctx.Data["canDownload"] = cloudbrain.CanModifyJob(ctx, &VersionListTasks[0].Cloudbrain) ctx.HTML(http.StatusOK, tplModelArtsTrainJobShow) } -func TrainJobGetLog(ctx *context.Context) { - ctx.Data["PageIsTrainJob"] = true - - var jobID = ctx.Params(":jobid") - var logFileName = ctx.Query("file_name") - var baseLine = ctx.Query("base_line") - var order = ctx.Query("order") - - if order != modelarts.OrderDesc && order != modelarts.OrderAsc { - log.Error("order(%s) check failed", order) - ctx.HTML(http.StatusBadRequest, tplModelArtsTrainJobShow) - return - } - - task, err := models.GetCloudbrainByJobID(jobID) - if err != nil { - log.Error("GetCloudbrainByJobID(%s) failed:%v", jobID, err.Error()) - ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobShow, nil) - return - } - - result, err := modelarts.GetTrainJobLog(jobID, strconv.FormatInt(task.VersionID, 10), baseLine, logFileName, order, modelarts.Lines) - if err != nil { - log.Error("GetTrainJobLog(%s) failed:%v", jobID, err.Error()) - ctx.RenderWithErr(err.Error(), tplModelArtsTrainJobShow, nil) - return - } - - ctx.Data["log"] = result - //ctx.HTML(http.StatusOK, tplModelArtsTrainJobShow) -} - -func trainJobGetLog(jobID string) (*models.GetTrainJobLogFileNamesResult, *models.GetTrainJobLogResult, error) { - task, err := models.GetCloudbrainByJobID(jobID) - if err != nil { - log.Error("GetCloudbrainByJobID(%s) failed:%v", jobID, err.Error()) - return nil, nil, err - } - - resultLogFile, err := modelarts.GetTrainJobLogFileNames(jobID, strconv.FormatInt(task.VersionID, 10)) - if err != nil { - log.Error("GetTrainJobLogFileNames(%s) failed:%v", jobID, err.Error()) - return nil, nil, err - } - - result, err := modelarts.GetTrainJobLog(jobID, strconv.FormatInt(task.VersionID, 10), "", resultLogFile.LogFileList[0], modelarts.OrderDesc, modelarts.Lines) - if err != nil { - log.Error("GetTrainJobLog(%s) failed:%v", jobID, err.Error()) - return nil, nil, err - } - - return resultLogFile, result, err -} - func TrainJobDel(ctx *context.Context) { var jobID = ctx.Params(":jobid") var listType = ctx.Query("listType") @@ -1743,6 +1871,16 @@ func TrainJobDel(ctx *context.Context) { return } + for _, task := range VersionListTasks { + if task.Status != string(models.ModelArtsTrainJobImageFailed) && task.Status != string(models.ModelArtsTrainJobSubmitFailed) && task.Status != string(models.ModelArtsTrainJobDeleteFailed) && + task.Status != string(models.ModelArtsTrainJobCompleted) && task.Status != string(models.ModelArtsTrainJobFailed) && + task.Status != string(models.ModelArtsTrainJobKilled) && task.Status != string(models.ModelArtsTrainJobCanceled) && task.Status != string(models.ModelArtsTrainJobLost) { + log.Error("the job(%s) version has not been stopped", task.JobName) + ctx.RenderWithErr("the job version has not been stopped", tplModelArtsTrainJobIndex, nil) + return + } + } + //删除modelarts上的任务记录 _, err = modelarts.DelTrainJob(jobID) if err != nil { @@ -1790,15 +1928,6 @@ func TrainJobStop(ctx *context.Context) { ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/train-job?listType=" + listType) } -func canUserCreateTrainJob(uid int64) (bool, error) { - org, err := models.GetOrgByName(setting.AllowedOrg) - if err != nil { - log.Error("get allowed org failed: ", setting.AllowedOrg) - return false, err - } - - return org.IsOrgMember(uid) -} func canUserCreateTrainJobVersion(ctx *context.Context, userID int64) (bool, error) { if ctx == nil || ctx.User == nil { log.Error("user unlogin!") @@ -1876,7 +2005,6 @@ func InferenceJobCreate(ctx *context.Context, form auth.CreateModelArtsInference workServerNumber := form.WorkServerNumber engineID := form.EngineID bootFile := strings.TrimSpace(form.BootFile) - flavorCode := form.Flavor params := form.Params poolID := form.PoolID repo := ctx.Repo.Repository @@ -1884,19 +2012,19 @@ func InferenceJobCreate(ctx *context.Context, form auth.CreateModelArtsInference codeObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.CodePath resultObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.ResultPath + VersionOutputPath + "/" logObsPath := "/" + setting.Bucket + modelarts.JobPath + jobName + modelarts.LogPath + VersionOutputPath + "/" - dataPath := "/" + setting.Bucket + "/" + setting.BasePath + path.Join(uuid[0:1], uuid[1:2]) + "/" + uuid + uuid + "/" - branch_name := form.BranchName + //dataPath := "/" + setting.Bucket + "/" + setting.BasePath + path.Join(uuid[0:1], uuid[1:2]) + "/" + uuid + uuid + "/" + branchName := form.BranchName FlavorName := form.FlavorName EngineName := form.EngineName LabelName := form.LabelName isLatestVersion := modelarts.IsLatestVersion - VersionCount := modelarts.VersionCount + VersionCount := modelarts.VersionCountOne trainUrl := form.TrainUrl modelName := form.ModelName modelVersion := form.ModelVersion ckptName := form.CkptName - - ckptUrl := form.TrainUrl + form.CkptName + ckptUrl := "/" + form.TrainUrl + form.CkptName + log.Info("ckpt url:" + ckptUrl) if !account.IsPointBalanceEnough(ctx.User.ID, string(models.JobTypeInference), models.TypeCloudBrainTwo, 0, flavorCode) { log.Error("point balance is not enough,userId=%d jobType=%s ", ctx.User.ID, string(models.JobTypeBenchmark)) @@ -1926,6 +2054,14 @@ func InferenceJobCreate(ctx *context.Context, form auth.CreateModelArtsInference return } + bootFileExist, err := ctx.Repo.FileExists(bootFile, branchName) + if err != nil || !bootFileExist { + log.Error("Get bootfile error:", err) + inferenceJobErrorNewDataPrepare(ctx, form) + ctx.RenderWithErr(ctx.Tr("repo.cloudbrain_bootfile_err"), tplModelArtsInferenceJobNew, &form) + return + } + //Determine whether the task name of the task in the project is duplicated tasks, err := models.GetCloudbrainsByDisplayJobName(repo.ID, string(models.JobTypeInference), displayJobName) if err == nil { @@ -1944,6 +2080,16 @@ func InferenceJobCreate(ctx *context.Context, form auth.CreateModelArtsInference } } + spec, err := resource.GetAndCheckSpec(ctx.User.ID, form.SpecId, models.FindSpecsOptions{ + JobType: models.JobTypeInference, + ComputeResource: models.NPU, + Cluster: models.OpenICluster, + AiCenterCode: models.AICenterOfCloudBrainTwo}) + if err != nil || spec == nil { + inferenceJobErrorNewDataPrepare(ctx, form) + ctx.RenderWithErr("Resource specification not available", tplModelArtsInferenceJobNew, &form) + return + } //todo: del the codeLocalPath _, err = ioutil.ReadDir(codeLocalPath) if err == nil { @@ -1951,12 +2097,12 @@ func InferenceJobCreate(ctx *context.Context, form auth.CreateModelArtsInference } gitRepo, _ := git.OpenRepository(repo.RepoPath()) - commitID, _ := gitRepo.GetBranchCommitID(branch_name) + commitID, _ := gitRepo.GetBranchCommitID(branchName) - if err := downloadCode(repo, codeLocalPath, branch_name); err != nil { + if err := downloadCode(repo, codeLocalPath, branchName); err != nil { log.Error("Create task failed, server timed out: %s (%v)", repo.FullName(), err) inferenceJobErrorNewDataPrepare(ctx, form) - ctx.RenderWithErr("Create task failed, server timed out", tplModelArtsInferenceJobNew, &form) + ctx.RenderWithErr(ctx.Tr("cloudbrain.load_code_failed"), tplModelArtsInferenceJobNew, &form) return } @@ -1978,7 +2124,7 @@ func InferenceJobCreate(ctx *context.Context, form auth.CreateModelArtsInference if err := uploadCodeToObs(codeLocalPath, jobName, ""); err != nil { log.Error("Failed to uploadCodeToObs: %s (%v)", repo.FullName(), err) inferenceJobErrorNewDataPrepare(ctx, form) - ctx.RenderWithErr("Failed to uploadCodeToObs", tplModelArtsInferenceJobNew, &form) + ctx.RenderWithErr(ctx.Tr("cloudbrain.load_code_failed"), tplModelArtsInferenceJobNew, &form) return } @@ -1991,6 +2137,28 @@ func InferenceJobCreate(ctx *context.Context, form auth.CreateModelArtsInference Label: modelarts.CkptUrl, Value: "s3:/" + ckptUrl, }) + + datasUrlList, dataUrl, datasetNames, isMultiDataset, err := getDatasUrlListByUUIDS(uuid) + if err != nil { + inferenceJobErrorNewDataPrepare(ctx, form) + ctx.RenderWithErr(err.Error(), tplModelArtsInferenceJobNew, &form) + return + } + dataPath := dataUrl + jsondatas, err := json.Marshal(datasUrlList) + if err != nil { + log.Error("Failed to Marshal: %v", err) + inferenceJobErrorNewDataPrepare(ctx, form) + ctx.RenderWithErr("json error:"+err.Error(), tplModelArtsInferenceJobNew, &form) + return + } + if isMultiDataset { + param = append(param, models.Parameter{ + Label: modelarts.MultiDataUrl, + Value: string(jsondatas), + }) + } + existDeviceTarget := false if len(params) != 0 { err := json.Unmarshal([]byte(params), ¶meters) @@ -2029,7 +2197,6 @@ func InferenceJobCreate(ctx *context.Context, form auth.CreateModelArtsInference BootFileUrl: codeObsPath + bootFile, BootFile: bootFile, TrainUrl: trainUrl, - FlavorCode: flavorCode, WorkServerNumber: workServerNumber, EngineID: int64(engineID), LogUrl: logObsPath, @@ -2037,7 +2204,7 @@ func InferenceJobCreate(ctx *context.Context, form auth.CreateModelArtsInference Uuid: uuid, Parameters: param, //modelarts train parameters CommitID: commitID, - BranchName: branch_name, + BranchName: branchName, Params: form.Params, FlavorName: FlavorName, EngineName: EngineName, @@ -2049,6 +2216,8 @@ func InferenceJobCreate(ctx *context.Context, form auth.CreateModelArtsInference ModelVersion: modelVersion, CkptName: ckptName, ResultUrl: resultObsPath, + Spec: spec, + DatasetName: datasetNames, } err = modelarts.GenerateInferenceJob(ctx, req) @@ -2060,6 +2229,65 @@ func InferenceJobCreate(ctx *context.Context, form auth.CreateModelArtsInference } ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/modelarts/inference-job") } + +func checkModelArtsSpecialPool(ctx *context.Context, flavorCode string, jobType string) string { + if modelarts.SpecialPools != nil { + isMatchPool := false + + for _, specialPool := range modelarts.SpecialPools.Pools { + if cloudbrain.IsElementExist(specialPool.JobType, jobType) { + if isInOrg, _ := models.IsOrganizationMemberByOrgName(specialPool.Org, ctx.User.ID); isInOrg { + isMatchPool = true + isMatchSpec := false + for _, flavor := range specialPool.Flavor { + if flavor.Value == flavorCode { + isMatchSpec = true + break + } + } + + if !isMatchSpec { + return "cloudbrain.wrong_specification" + + } + + } + + } + } + + if !isMatchPool { + isMatchSpec := false + if jobType == string(models.JobTypeDebug) { + for _, flavor := range modelarts.FlavorInfos.FlavorInfo { + if flavor.Value == flavorCode { + isMatchSpec = true + break + } + } + } else { + + var flavorInfos modelarts.Flavor + json.Unmarshal([]byte(setting.TrainJobFLAVORINFOS), &flavorInfos) + + for _, flavor := range flavorInfos.Info { + if flavor.Code == flavorCode { + isMatchSpec = true + break + } + } + } + + if !isMatchSpec { + + return "cloudbrain.wrong_specification" + } + + } + + } + return "" +} func InferenceJobIndex(ctx *context.Context) { MustEnableModelArts(ctx) @@ -2069,6 +2297,13 @@ func InferenceJobIndex(ctx *context.Context) { page = 1 } + listType := ctx.Query("listType") + ctx.Data["ListType"] = listType + + if listType == models.AllResource { + listType = "" + } + var jobTypes []string jobTypes = append(jobTypes, string(models.JobTypeInference)) tasks, count, err := models.Cloudbrains(&models.CloudbrainsOptions{ @@ -2076,9 +2311,10 @@ func InferenceJobIndex(ctx *context.Context) { Page: page, PageSize: setting.UI.IssuePagingNum, }, - RepoID: repo.ID, - Type: models.TypeCloudBrainTwo, - JobTypes: jobTypes, + RepoID: repo.ID, + ComputeResource: listType, + JobTypes: jobTypes, + Type: models.TypeCloudBrainAll, }) if err != nil { ctx.ServerError("Cloudbrain", err) @@ -2088,7 +2324,9 @@ func InferenceJobIndex(ctx *context.Context) { for i, task := range tasks { tasks[i].CanDel = cloudbrain.CanDeleteJob(ctx, &task.Cloudbrain) tasks[i].CanModify = cloudbrain.CanModifyJob(ctx, &task.Cloudbrain) - tasks[i].ComputeResource = models.NPUResource + if tasks[i].ComputeResource == "" { + tasks[i].ComputeResource = models.NPUResource + } } repoId := ctx.Repo.Repository.ID @@ -2120,6 +2358,7 @@ func InferenceJobNew(ctx *context.Context) { ctx.ServerError("get new inference-job info failed", err) return } + ctx.HTML(200, tplModelArtsInferenceJobNew) } func inferenceJobNewDataPrepare(ctx *context.Context) error { @@ -2127,7 +2366,7 @@ func inferenceJobNewDataPrepare(ctx *context.Context) error { ctx.Data["newInference"] = true t := time.Now() - var displayJobName = cutString(ctx.User.Name, 5) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:] + var displayJobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:] ctx.Data["display_job_name"] = displayJobName attachs, err := models.GetModelArtsTrainAttachments(ctx.User.ID) @@ -2158,13 +2397,8 @@ func inferenceJobNewDataPrepare(ctx *context.Context) error { } ctx.Data["engine_versions"] = versionInfos.Version - var flavorInfos modelarts.Flavor - if err = json.Unmarshal([]byte(setting.TrainJobFLAVORINFOS), &flavorInfos); err != nil { - ctx.ServerError("json.Unmarshal failed:", err) - return err - } + prepareCloudbrainTwoInferenceSpecs(ctx) - ctx.Data["flavor_infos"] = flavorInfos.Info ctx.Data["params"] = "" ctx.Data["branchName"] = ctx.Repo.BranchName @@ -2188,10 +2422,22 @@ func inferenceJobNewDataPrepare(ctx *context.Context) error { }) ctx.Data["MODEL_COUNT"] = model_count ctx.Data["datasetType"] = models.TypeCloudBrainTwo + waitCount := cloudbrain.GetWaitingCloudbrainCount(models.TypeCloudBrainTwo, "") + ctx.Data["WaitCount"] = waitCount return nil } +func prepareCloudbrainTwoInferenceSpecs(ctx *context.Context) { + noteBookSpecs, _ := resource.FindAvailableSpecs(ctx.User.ID, models.FindSpecsOptions{ + JobType: models.JobTypeInference, + ComputeResource: models.NPU, + Cluster: models.OpenICluster, + AiCenterCode: models.AICenterOfCloudBrainTwo, + }) + ctx.Data["Specs"] = noteBookSpecs +} + func inferenceJobErrorNewDataPrepare(ctx *context.Context, form auth.CreateModelArtsInferenceJobForm) error { ctx.Data["PageIsCloudBrain"] = true @@ -2233,6 +2479,7 @@ func inferenceJobErrorNewDataPrepare(ctx *context.Context, form auth.CreateModel return err } ctx.Data["flavor_infos"] = flavorInfos.Info + setSpecBySpecialPoolConfig(ctx, string(models.JobTypeInference)) configList, err := getConfigList(modelarts.PerPage, 1, modelarts.SortByCreateTime, "desc", "", modelarts.ConfigTypeCustom) if err != nil { @@ -2248,12 +2495,20 @@ func inferenceJobErrorNewDataPrepare(ctx *context.Context, form auth.CreateModel ctx.Data["config_list"] = configList.ParaConfigs ctx.Data["bootFile"] = form.BootFile ctx.Data["uuid"] = form.Attachment + _, datasetNames, err := models.GetDatasetInfo(form.Attachment) + if err != nil { + log.Error("GetDatasetInfo failed: %v", err, ctx.Data["MsgID"]) + return nil + } + ctx.Data["dataset_name"] = datasetNames ctx.Data["branch_name"] = form.BranchName ctx.Data["model_name"] = form.ModelName ctx.Data["model_version"] = form.ModelVersion ctx.Data["ckpt_name"] = form.CkptName ctx.Data["train_url"] = form.TrainUrl ctx.Data["datasetType"] = models.TypeCloudBrainTwo + waitCount := cloudbrain.GetWaitingCloudbrainCount(models.TypeCloudBrainTwo, "") + ctx.Data["WaitCount"] = waitCount return nil } @@ -2269,7 +2524,7 @@ func InferenceJobShow(ctx *context.Context) { if err != nil { log.Error("GetInferenceTask(%s) failed:%v", jobID, err.Error()) - ctx.RenderWithErr(err.Error(), tplModelArtsInferenceJobShow, nil) + ctx.NotFound(ctx.Req.URL.RequestURI(), nil) return } //设置权限 @@ -2299,7 +2554,7 @@ func InferenceJobShow(ctx *context.Context) { } else { task.Parameters = "" } - + prepareSpec4Show(ctx, task) LabelName := strings.Fields(task.LabelName) ctx.Data["labelName"] = LabelName ctx.Data["jobID"] = jobID @@ -2307,7 +2562,7 @@ func InferenceJobShow(ctx *context.Context) { ctx.Data["displayJobName"] = task.DisplayJobName ctx.Data["task"] = task ctx.Data["canDownload"] = cloudbrain.CanModifyJob(ctx, task) - + ctx.Data["datasetDownload"] = GetCloudBrainDataSetInfo(task.Uuid, false) tempUids := []int64{} tempUids = append(tempUids, task.UserID) JobCreater, err := models.GetUserNamesByIDs(tempUids) @@ -2520,11 +2775,23 @@ func getDatasUrlListByUUIDS(uuidStr string) ([]models.Datasurl, string, string, datasetInfos := make(map[string]models.DatasetInfo) attachs, err := models.GetAttachmentsByUUIDs(uuids) - if err != nil { + if err != nil || len(attachs) != len(uuids) { log.Error("GetAttachmentsByUUIDs failed: %v", err) return datasUrlList, dataUrl, datasetNames, isMultiDataset, errors.New("GetAttachmentsByUUIDs failed") } - for i, attach := range attachs { + + for i, tmpUuid := range uuids { + var attach *models.Attachment + for _, tmpAttach := range attachs { + if tmpAttach.UUID == tmpUuid { + attach = tmpAttach + break + } + } + if attach == nil { + log.Error("GetAttachmentsByUUIDs failed: %v", err) + return datasUrlList, dataUrl, datasetNames, isMultiDataset, errors.New("GetAttachmentsByUUIDs failed") + } fileName := strings.TrimSuffix(strings.TrimSuffix(strings.TrimSuffix(attach.Name, ".zip"), ".tar.gz"), ".tgz") for _, datasetInfo := range datasetInfos { if fileName == datasetInfo.Name { diff --git a/routers/repo/repo.go b/routers/repo/repo.go index c0803ed06..03d2d832a 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -195,8 +195,11 @@ func CreatePost(ctx *context.Context, form auth.CreateRepoForm) { } ctx.Data["ContextUser"] = ctxUser if !form.AutoAgree { + ctx.Data["CheckedAutoAgree"] = "" ctx.RenderWithErr(ctx.Tr("repo.template.one_promise"), tplCreate, form) return + } else { + ctx.Data["CheckedAutoAgree"] = "checked" } if ctx.HasError() { ctx.HTML(200, tplCreate) diff --git a/routers/repo/setting.go b/routers/repo/setting.go index fed89513a..11efdf275 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -464,6 +464,16 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { } return } + dataset, err := models.GetDatasetByRepo(repo) + if err == nil { + if dataset != nil { + models.UpdateDatasetCreateUser(dataset.ID, newOwner) + } else { + log.Info("not found the dataset") + } + } else { + log.Info("error=" + err.Error()) + } log.Trace("Repository transferred: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newOwner) ctx.Flash.Success(ctx.Tr("repo.settings.transfer_succeed")) @@ -494,7 +504,8 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { return } log.Trace("Repository deleted: %s/%s", ctx.Repo.Owner.Name, repo.Name) - go StopJobsByRepoID(repo.ID) + // go StopJobsByRepoID(repo.ID) + go DeleteJobsByRepoID(repo.ID) ctx.Flash.Success(ctx.Tr("repo.settings.deletion_success")) ctx.Redirect(ctx.Repo.Owner.DashboardLink()) diff --git a/routers/repo/user_data_analysis.go b/routers/repo/user_data_analysis.go index 207727af1..508addf75 100755 --- a/routers/repo/user_data_analysis.go +++ b/routers/repo/user_data_analysis.go @@ -5,6 +5,8 @@ import ( "net/http" "net/url" "os" + "strconv" + "strings" "time" "code.gitea.io/gitea/models" @@ -112,6 +114,7 @@ func getExcelHeader(ctx *context.Context) map[string]string { excelHeader = append(excelHeader, ctx.Tr("user.static.RecommendImage")) excelHeader = append(excelHeader, ctx.Tr("user.static.email")) + excelHeader = append(excelHeader, ctx.Tr("user.static.phone")) excelHeader = append(excelHeader, ctx.Tr("user.static.location")) excelHeader = append(excelHeader, ctx.Tr("user.static.registdate")) @@ -193,6 +196,9 @@ func writeExcel(row int, xlsx *excelize.File, sheetName string, userRecord *mode xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.Email) tmp = tmp + 1 + xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.Phone) + tmp = tmp + 1 + xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.UserLocation) tmp = tmp + 1 @@ -268,6 +274,9 @@ func writeExcelPage(row int, xlsx *excelize.File, sheetName string, userRecord * xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.Email) tmp = tmp + 1 + xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.Phone) + tmp = tmp + 1 + xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.UserLocation) tmp = tmp + 1 @@ -397,7 +406,7 @@ func queryMetrics(ctx *context.Context, tableName string, startTime time.Time, e if tableName == "public.user_business_analysis_yesterday" { mapInterface["datarecordbegintime"] = setting.RadarMap.GrowthBeginTime if len(result) > 0 { - dateTime := time.Unix(result[0].CountDate, 0) + dateTime := time.Unix(result[0].CountDate, 0).AddDate(0, 0, 1) mapInterface["lastUpdatedTime"] = dateTime.Format("2006-01-02 15:04:05") } else { mapInterface["lastUpdatedTime"] = "" @@ -443,7 +452,7 @@ func DownloadUserDefineFile(ctx *context.Context) { func QueryUserMetricsCurrentMonth(ctx *context.Context) { currentTimeNow := time.Now() - pageEndTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 23, 59, 59, 0, currentTimeNow.Location()) + pageEndTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 0, 0, 0, 0, currentTimeNow.Location()) pageStartTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), 1, 0, 0, 0, 0, currentTimeNow.Location()) pageStartTime = getStartTime(pageStartTime) queryMetrics(ctx, "public.user_business_analysis_current_month", pageStartTime, pageEndTime) @@ -469,7 +478,7 @@ func QueryUserMetricsCurrentWeek(ctx *context.Context) { } pageStartTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 0, 0, 0, 0, time.Local).AddDate(0, 0, offset) pageStartTime = getStartTime(pageStartTime) - pageEndTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 23, 59, 59, 0, currentTimeNow.Location()) + pageEndTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 0, 0, 0, 0, currentTimeNow.Location()) queryMetrics(ctx, "public.user_business_analysis_current_week", pageStartTime, pageEndTime) } func QueryUserStaticCurrentWeek(ctx *context.Context) { @@ -483,7 +492,7 @@ func QueryUserMetricsCurrentYear(ctx *context.Context) { currentTimeNow := time.Now() pageStartTime := time.Date(currentTimeNow.Year(), 1, 1, 0, 0, 0, 0, currentTimeNow.Location()) pageStartTime = getStartTime(pageStartTime) - pageEndTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 23, 59, 59, 0, currentTimeNow.Location()) + pageEndTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 0, 0, 0, 0, currentTimeNow.Location()) queryMetrics(ctx, "public.user_business_analysis_current_year", pageStartTime, pageEndTime) } func QueryUserStaticCurrentYear(ctx *context.Context) { @@ -493,7 +502,7 @@ func QueryUserMetricsLast30Day(ctx *context.Context) { currentTimeNow := time.Now() pageStartTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 0, 0, 0, 0, time.Local).AddDate(0, 0, -30) pageStartTime = getStartTime(pageStartTime) - pageEndTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 23, 59, 59, 0, currentTimeNow.Location()) + pageEndTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 0, 0, 0, 0, currentTimeNow.Location()) queryMetrics(ctx, "public.user_business_analysis_last30_day", pageStartTime, pageEndTime) } func QueryUserStaticLast30Day(ctx *context.Context) { @@ -511,7 +520,7 @@ func QueryUserStaticLastMonth(ctx *context.Context) { queryUserDataPage(ctx, "public.user_business_analysis_last_month", new(models.UserBusinessAnalysisLastMonth)) } func QueryUserMetricsYesterday(ctx *context.Context) { - currentTimeNow := time.Now() + currentTimeNow := time.Now().AddDate(0, 0, -1) pageStartTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 0, 0, 0, 0, time.Local) pageStartTime = getStartTime(pageStartTime) pageEndTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 23, 59, 59, 0, currentTimeNow.Location()) @@ -524,7 +533,7 @@ func QueryUserMetricsAll(ctx *context.Context) { currentTimeNow := time.Now() pageStartTime := time.Date(2022, 4, 5, 0, 0, 0, 0, currentTimeNow.Location()) pageStartTime = getStartTime(pageStartTime) - pageEndTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 23, 59, 59, 0, currentTimeNow.Location()) + pageEndTime := time.Date(currentTimeNow.Year(), currentTimeNow.Month(), currentTimeNow.Day(), 0, 0, 0, 0, currentTimeNow.Location()) queryMetrics(ctx, "public.user_business_analysis_all", pageStartTime, pageEndTime) } func QueryUserStaticAll(ctx *context.Context) { @@ -601,10 +610,18 @@ func QueryUserStaticDataPage(ctx *context.Context) { filename := sheetName + "_" + startDate + "_" + endDate + ".xlsx" os.Remove(setting.AppDataPath + Excel_File_Path + filename) go writeFileToDisk(ctx, count, re, filename) - ctx.JSON(http.StatusOK, ctx.Tr("user.static.downloadinfo")+setting.AppURL+"api/v1/download_user_define_file?filename="+filename) + ctx.JSON(http.StatusOK, ctx.Tr("user.static.downloadinfo")+"/api/v1/download_user_define_file?filename="+filename) } else { mapInterface := make(map[string]interface{}) - re, count := models.QueryUserStaticDataPage(pageOpts) + key := startTime.Format("2006-01-02") + endTime.Format("2006-01-02") + log.Info("db key =" + key) + re, count := models.QueryDataForUserDefineFromDb(pageOpts, key) + if count == 0 { + wikiMap, _ := queryWikiCountMap(startTime, endTime) + re, count = models.QueryUserStaticDataForUserDefine(pageOpts, wikiMap) + models.WriteDataToDb(re, key) + } + re, count = models.QueryDataForUserDefineFromDb(pageOpts, key) mapInterface["data"] = re mapInterface["count"] = count ctx.JSON(http.StatusOK, mapInterface) @@ -721,3 +738,172 @@ func TimingCountData() { startTime := currentTimeNow.AddDate(0, 0, -1).Format("2006-01-02") TimingCountDataByDateAndReCount(startTime, false) } + +func QueryUserActivity(ctx *context.Context) { + startDate := ctx.Query("beginTime") + endDate := ctx.Query("endTime") + + t, _ := time.Parse("2006-01-02", startDate) + startTime := time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location()) + startTime = startTime.UTC() + + t, _ = time.Parse("2006-01-02", endDate) + endTime := time.Date(t.Year(), t.Month(), t.Day(), 23, 59, 59, 0, t.Location()) + endTime = endTime.UTC() + + sheetName := ctx.Tr("user.static.sheetname") + filename := sheetName + "_" + startDate + "_" + endDate + ".xlsx" + filePath := setting.AppDataPath + Excel_File_Path + filename + os.Remove(setting.AppDataPath + Excel_File_Path + filename) + + go writeUserActivityToExcel(startTime, endTime, filePath, ctx) + + ctx.JSON(http.StatusOK, ctx.Tr("user.static.downloadinfo")+"/api/v1/download_user_define_file?filename="+filename) + +} + +func writeUserActivityToExcel(startTime time.Time, endTime time.Time, filePath string, ctx *context.Context) { + re := models.QueryDataForActivity(startTime, endTime) + log.Info("return count=" + fmt.Sprint(len(re))) + //writer exec file. + xlsx := excelize.NewFile() + sheetName := ctx.Tr("user.static.sheetname") + index := xlsx.NewSheet(sheetName) + xlsx.DeleteSheet("Sheet1") + + excelHeader := make([]string, 0) + excelHeader = append(excelHeader, ctx.Tr("user.static.id")) + excelHeader = append(excelHeader, ctx.Tr("user.static.name")) + excelHeader = append(excelHeader, ctx.Tr("user.static.codemergecount")) + excelHeader = append(excelHeader, ctx.Tr("user.static.commitcount")) + excelHeader = append(excelHeader, ctx.Tr("user.static.issuecount")) + excelHeader = append(excelHeader, ctx.Tr("user.static.commentcount")) + excelHeader = append(excelHeader, ctx.Tr("user.static.watchedcount")) + excelHeader = append(excelHeader, ctx.Tr("user.static.commitcodesize")) + excelHeader = append(excelHeader, ctx.Tr("user.static.solveissuecount")) + + excelHeader = append(excelHeader, ctx.Tr("user.static.CommitDatasetNum")) + excelHeader = append(excelHeader, ctx.Tr("user.static.CommitModelCount")) + excelHeader = append(excelHeader, ctx.Tr("user.static.email")) + excelHeader = append(excelHeader, ctx.Tr("user.static.phone")) + excelHeader = append(excelHeader, ctx.Tr("user.static.registdate")) + + excelHeaderMap := make(map[string]string, 0) + var j byte + j = 0 + for _, value := range excelHeader { + excelColumn := getColumn(j) + fmt.Sprint(1) + log.Info("excelColumn=" + excelColumn) + excelHeaderMap[excelColumn] = value + j++ + } + + for k, v := range excelHeaderMap { + //设置单元格的值 + xlsx.SetCellValue(sheetName, k, v) + } + + for i, userRecord := range re { + row := i + 2 + rows := fmt.Sprint(row) + var tmp byte + tmp = 0 + xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.ID) + tmp = tmp + 1 + xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.Name) + tmp = tmp + 1 + xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.CodeMergeCount) + tmp = tmp + 1 + xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.CommitCount) + tmp = tmp + 1 + xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.IssueCount) + tmp = tmp + 1 + xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.CommentCount) + tmp = tmp + 1 + xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.WatchedCount) + tmp = tmp + 1 + xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.CommitCodeSize) + tmp = tmp + 1 + xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.SolveIssueCount) + tmp = tmp + 1 + xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.CommitDatasetNum) + tmp = tmp + 1 + xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.CommitModelCount) + tmp = tmp + 1 + xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.Email) + tmp = tmp + 1 + xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userRecord.Phone) + tmp = tmp + 1 + formatTime := userRecord.RegistDate.Format("2006-01-02 15:04:05") + xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, formatTime[0:len(formatTime)-3]) + tmp = tmp + 1 + } + + //设置默认打开的表单 + xlsx.SetActiveSheet(index) + + os.Mkdir(setting.AppDataPath+Excel_File_Path, 0755) + if err := xlsx.SaveAs(filePath); err != nil { + log.Info("writer exel error." + err.Error()) + } else { + log.Info("write to file succeed, filepath=" + filePath) + } +} + +// URL: /api/v1/query_user_login?userId=1,2,3,4 +func QueryUserLoginInfo(ctx *context.Context) { + userId := ctx.Query("userId") + userIds := strings.Split(userId, ",") + userIdInt := make([]int64, 0) + for _, id := range userIds { + idInt, err := strconv.ParseInt(id, 10, 64) + if err == nil { + userIdInt = append(userIdInt, idInt) + } + } + result := models.QueryUserLoginInfo(userIdInt) + + xlsx := excelize.NewFile() + sheetName := ctx.Tr("用户登录信息") + index := xlsx.NewSheet(sheetName) + xlsx.DeleteSheet("Sheet1") + + excelHeader := make([]string, 0) + excelHeader = append(excelHeader, "用户ID") + excelHeader = append(excelHeader, "登录IP") + excelHeader = append(excelHeader, "登录时间") + + excelHeaderMap := make(map[string]string, 0) + var j byte + j = 0 + for _, value := range excelHeader { + excelColumn := getColumn(j) + fmt.Sprint(1) + log.Info("excelColumn=" + excelColumn) + excelHeaderMap[excelColumn] = value + j++ + } + for k, v := range excelHeaderMap { + //设置单元格的值 + xlsx.SetCellValue(sheetName, k, v) + } + for i, userLogin := range result { + row := i + 2 + rows := fmt.Sprint(row) + var tmp byte + tmp = 0 + xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userLogin.UId) + tmp = tmp + 1 + xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, userLogin.IpAddr) + tmp = tmp + 1 + formatTime := userLogin.CreatedUnix.Format("2006-01-02 15:04:05") + xlsx.SetCellValue(sheetName, getColumn(tmp)+rows, formatTime) + } + //设置默认打开的表单 + xlsx.SetActiveSheet(index) + filename := sheetName + "_" + time.Now().Format("2006-01-02 15:04:05") + ".xlsx" + ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+url.QueryEscape(filename)) + ctx.Resp.Header().Set("Content-Type", "application/octet-stream") + if _, err := xlsx.WriteTo(ctx.Resp); err != nil { + log.Info("writer exel error." + err.Error()) + } +} diff --git a/routers/repo/view.go b/routers/repo/view.go index 02004fa06..3a18e4ddf 100755 --- a/routers/repo/view.go +++ b/routers/repo/view.go @@ -484,6 +484,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st ) } else if isNoteBook { ctx.Data["FileContent"] = string(buf) + ctx.Data["FileParentURL"] = path.Dir(rawLink+"/"+ctx.Repo.TreePath) + "/" } else { // Building code view blocks with line number on server side. var fileContent string diff --git a/routers/response/error.go b/routers/response/error.go new file mode 100644 index 000000000..685267da9 --- /dev/null +++ b/routers/response/error.go @@ -0,0 +1,14 @@ +package response + +type BizError struct { + Code int + Err string +} + +func (b BizError) Error() string { + return b.Err +} + +func NewBizError(err error) *BizError { + return &BizError{Code: RESPONSE_CODE_ERROR_DEFAULT, Err: err.Error()} +} diff --git a/routers/response/response.go b/routers/response/response.go index e87471d4c..ccd6be445 100644 --- a/routers/response/response.go +++ b/routers/response/response.go @@ -24,6 +24,10 @@ func ServerError(msg string) *AiforgeResponse { return &AiforgeResponse{Code: RESPONSE_CODE_ERROR_DEFAULT, Msg: msg} } +func ResponseError(err *BizError) *AiforgeResponse { + return &AiforgeResponse{Code: err.Code, Msg: err.Err} +} + func SuccessWithData(data interface{}) *AiforgeResponse { return &AiforgeResponse{Code: RESPONSE_CODE_SUCCESS, Msg: RESPONSE_MSG_SUCCESS, Data: data} } diff --git a/routers/response/response_list.go b/routers/response/response_list.go new file mode 100644 index 000000000..5e057bfd0 --- /dev/null +++ b/routers/response/response_list.go @@ -0,0 +1,4 @@ +package response + +var RESOURCE_QUEUE_NOT_AVAILABLE = &BizError{Code: 1001, Err: "resource queue not available"} +var SPECIFICATION_NOT_EXIST = &BizError{Code: 1002, Err: "specification not exist"} diff --git a/routers/routes/routes.go b/routers/routes/routes.go index 55b65b40b..0e42b6347 100755 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -503,7 +503,7 @@ func RegisterRoutes(m *macaron.Macaron) { m.Get("/email2user", user.Email2User) m.Get("/recover_account", user.ResetPasswd) m.Post("/recover_account", user.ResetPasswdPost) - m.Post("/recover_account_by_phone",bindIgnErr(auth.ResetPassWordByPhoneForm{}), user.ResetPasswdByPhonePost) + m.Post("/recover_account_by_phone", bindIgnErr(auth.ResetPassWordByPhoneForm{}), user.ResetPasswdByPhonePost) m.Get("/forgot_password", user.ForgotPasswd) m.Post("/forgot_password", user.ForgotPasswdPost) m.Post("/logout", user.SignOut) @@ -608,6 +608,32 @@ func RegisterRoutes(m *macaron.Macaron) { m.Post("/delete", admin.DeleteNotices) m.Post("/empty", admin.EmptyNotices) }) + + m.Group("/resources", func() { + m.Group("/queue", func() { + m.Get("", admin.GetQueuePage) + m.Get("/list", admin.GetResourceQueueList) + m.Post("/grampus/sync", admin.SyncGrampusQueue) + m.Get("/codes", admin.GetResourceQueueCodes) + m.Get("/centers", admin.GetResourceAiCenters) + m.Post("/add", binding.Bind(models.ResourceQueueReq{}), admin.AddResourceQueue) + m.Post("/update/:id", binding.BindIgnErr(models.ResourceQueueReq{}), admin.UpdateResourceQueue) + }) + m.Group("/specification", func() { + m.Get("", admin.GetSpecificationPage) + m.Get("/list", admin.GetResourceSpecificationList) + m.Get("/scenes/:id", admin.GetResourceSpecificationScenes) + m.Post("/grampus/sync", admin.SyncGrampusSpecs) + m.Post("/add", binding.Bind(models.ResourceSpecificationReq{}), admin.AddResourceSpecification) + m.Post("/update/:id", binding.BindIgnErr(models.ResourceSpecificationReq{}), admin.UpdateResourceSpecification) + }) + m.Group("/scene", func() { + m.Get("", admin.GetScenePage) + m.Get("/list", admin.GetResourceSceneList) + m.Post("/add", binding.Bind(models.ResourceSceneReq{}), admin.AddResourceScene) + m.Post("/update/:id", binding.BindIgnErr(models.ResourceSceneReq{}), admin.UpdateResourceScene) + }) + }) }, adminReq) // ***** END: Admin ***** @@ -693,6 +719,9 @@ func RegisterRoutes(m *macaron.Macaron) { reqRepoIssuesOrPullsReader := context.RequireRepoReaderOr(models.UnitTypeIssues, models.UnitTypePullRequests) reqRepoDatasetReader := context.RequireRepoReader(models.UnitTypeDatasets) reqRepoDatasetWriter := context.RequireRepoWriter(models.UnitTypeDatasets) + reqRepoDatasetReaderJson := context.RequireRepoReaderJson(models.UnitTypeDatasets) + reqRepoDatasetWriterJson := context.RequireRepoWriterJson(models.UnitTypeDatasets) + reqRepoCloudBrainReader := context.RequireRepoReader(models.UnitTypeCloudBrain) reqRepoCloudBrainWriter := context.RequireRepoWriter(models.UnitTypeCloudBrain) reqRepoModelManageReader := context.RequireRepoReader(models.UnitTypeModelManage) @@ -1033,6 +1062,7 @@ func RegisterRoutes(m *macaron.Macaron) { }, context.RepoAssignment(), context.RepoMustNotBeArchived(), reqRepoAdmin) m.Group("/image/:id", func() { + m.Get("", repo.GetImage) m.Get("/:from", cloudbrain.AdminOrImageCreaterRight, repo.CloudBrainImageEdit) m.Post("", cloudbrain.AdminOrImageCreaterRight, bindIgnErr(auth.EditImageCloudBrainForm{}), repo.CloudBrainImageEditPost) m.Delete("", cloudbrain.AdminOrImageCreaterRight, repo.CloudBrainImageDelete) @@ -1048,10 +1078,14 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/datasets", func() { m.Get("", reqRepoDatasetReader, repo.DatasetIndex) + m.Get("/reference_datasets", reqRepoDatasetReader, repo.ReferenceDataset) + m.Get("/reference_datasets_data", reqRepoDatasetReaderJson, repo.ReferenceDatasetData) + m.Delete("/reference_datasets/:id", reqRepoDatasetWriterJson, repo.ReferenceDatasetDelete) m.Put("/:id/:action", reqRepoDatasetReader, repo.DatasetAction) m.Get("/create", reqRepoDatasetWriter, repo.CreateDataset) m.Post("/create", reqRepoDatasetWriter, bindIgnErr(auth.CreateDatasetForm{}), repo.CreateDatasetPost) m.Get("/edit/:id", reqRepoDatasetWriter, repo.EditDataset) + m.Post("/reference_datasets", reqRepoDatasetWriterJson, bindIgnErr(auth.ReferenceDatasetForm{}), repo.ReferenceDatasetPost) m.Post("/edit", reqRepoDatasetWriter, bindIgnErr(auth.EditDatasetForm{}), repo.EditDatasetPost) m.Get("/current_repo", repo.CurrentRepoDataset) m.Get("/my_datasets", repo.MyDatasets) @@ -1061,6 +1095,8 @@ func RegisterRoutes(m *macaron.Macaron) { m.Get("/current_repo_m", repo.CurrentRepoDatasetMultiple) m.Get("/my_datasets_m", repo.MyDatasetsMultiple) m.Get("/public_datasets_m", repo.PublicDatasetMultiple) + + m.Get("/reference_datasets_available", repo.ReferenceDatasetAvailable) m.Get("/my_favorite_m", repo.MyFavoriteDatasetMultiple) m.Group("/status", func() { @@ -1116,12 +1152,22 @@ func RegisterRoutes(m *macaron.Macaron) { m.Post("/del", cloudbrain.AdminOrOwnerOrJobCreaterRightForTrain, repo.CloudBrainTrainJobDel) //m.Get("/models", reqRepoCloudBrainReader, repo.CloudBrainShowModels) m.Get("/download_model", cloudbrain.AdminOrOwnerOrJobCreaterRightForTrain, repo.CloudBrainDownloadModel) - //m.Get("/create_version", reqWechatBind, cloudbrain.AdminOrJobCreaterRightForTrain, repo.TrainJobNewVersion) + //m.Get("/get_log", cloudbrain.AdminOrJobCreaterRightForTrain, repo.GetLogFromModelDir) //m.Post("/create_version", reqWechatBind, cloudbrain.AdminOrJobCreaterRightForTrain, bindIgnErr(auth.CreateModelArtsTrainJobForm{}), repo.TrainJobCreateVersion) }) m.Get("/create", reqWechatBind, reqRepoCloudBrainWriter, context.PointAccount(), repo.CloudBrainTrainJobNew) m.Post("/create", reqWechatBind, reqRepoCloudBrainWriter, bindIgnErr(auth.CreateCloudBrainForm{}), repo.CloudBrainCreate) }) + m.Group("/inference-job", func() { + m.Group("/:jobid", func() { + m.Get("", reqRepoCloudBrainReader, repo.InferenceCloudBrainJobShow) + m.Get("/result_download", cloudbrain.AdminOrJobCreaterRightForTrain, repo.CloudBrainDownloadInferenceResult) + + m.Get("/downloadall", repo.DownloadInferenceResultFile) + }) + m.Get("/create", reqWechatBind, reqRepoCloudBrainWriter, repo.InferenceCloudBrainJobNew) + m.Post("/create", reqWechatBind, reqRepoCloudBrainWriter, bindIgnErr(auth.CreateCloudBrainInferencForm{}), repo.CloudBrainInferenceJobCreate) + }) }, context.RepoRef()) m.Group("/grampus", func() { m.Group("/train-job", func() { @@ -1142,20 +1188,27 @@ func RegisterRoutes(m *macaron.Macaron) { }) }, context.RepoRef()) m.Group("/modelmanage", func() { - m.Post("/create_model", reqRepoModelManageWriter, repo.SaveModel) + m.Post("/create_model", repo.SaveModel) + m.Post("/create_model_convert", reqRepoModelManageWriter, repo.SaveModelConvert) m.Post("/create_new_model", repo.SaveNewNameModel) m.Delete("/delete_model", repo.DeleteModel) + m.Post("/delete_model_convert/:id", repo.DeleteModelConvert) + m.Post("/convert_stop/:id", repo.StopModelConvert) m.Put("/modify_model", repo.ModifyModelInfo) m.Get("/show_model", reqRepoModelManageReader, repo.ShowModelTemplate) + m.Get("/convert_model", reqRepoModelManageReader, repo.ConvertModelTemplate) m.Get("/show_model_info", repo.ShowModelInfo) + m.Get("/show_model_convert_info", repo.ShowModelConvertInfo) m.Get("/show_model_info_api", repo.ShowSingleModel) m.Get("/show_model_api", repo.ShowModelPageInfo) m.Get("/show_model_child_api", repo.ShowOneVersionOtherModel) m.Get("/query_train_job", reqRepoCloudBrainReader, repo.QueryTrainJobList) + m.Get("/query_train_model", reqRepoCloudBrainReader, repo.QueryTrainModelList) m.Get("/query_train_job_version", reqRepoCloudBrainReader, repo.QueryTrainJobVersionList) m.Get("/query_model_for_predict", reqRepoModelManageReader, repo.QueryModelListForPredict) m.Get("/query_modelfile_for_predict", reqRepoModelManageReader, repo.QueryModelFileForPredict) m.Get("/query_onelevel_modelfile", reqRepoModelManageReader, repo.QueryOneLevelModelFile) + m.Get("/download_model_convert/:id", reqRepoModelManageReader, repo.ModelConvertDownloadModel) m.Group("/:ID", func() { m.Get("", repo.ShowSingleModel) m.Get("/downloadsingle", repo.DownloadSingleModelFile) @@ -1182,7 +1235,8 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/:id", func() { m.Get("", reqRepoCloudBrainReader, repo.NotebookShow) m.Get("/debug", cloudbrain.AdminOrJobCreaterRight, repo.NotebookDebug2) - m.Post("/:action", reqRepoCloudBrainWriter, repo.NotebookManage) + m.Post("/restart", cloudbrain.AdminOrJobCreaterRight, repo.NotebookRestart) + m.Post("/stop", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.NotebookStop) m.Post("/del", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.NotebookDel) }) m.Get("/create", reqWechatBind, reqRepoCloudBrainWriter, context.PointAccount(), repo.NotebookNew) diff --git a/routers/search.go b/routers/search.go index 05074df55..8453d5c18 100644 --- a/routers/search.go +++ b/routers/search.go @@ -313,9 +313,8 @@ func searchRepo(ctx *context.Context, TableName string, Key string, Page int, Pa res, err := client.Search(TableName).Query(boolQ).SortBy(getSort(SortBy, ascending, "num_stars", false)...).From(from).Size(Size).Highlight(queryHighlight("alias", "description", "topics")).Do(ctx.Req.Context()) if err == nil { - searchJson, _ := json.Marshal(res) - log.Info("searchJson=" + string(searchJson)) esresult := makeRepoResult(res, Key, OnlyReturnNum, language) + setForkRepoOrder(esresult, SortBy) resultObj.Total = resultObj.PrivateTotal + esresult.Total isNeedSort := false if len(resultObj.Result) > 0 { @@ -348,6 +347,32 @@ func searchRepo(ctx *context.Context, TableName string, Key string, Page int, Pa } } +func setForkRepoOrder(esresult *SearchRes, SortBy string) { + if SortBy == "default" || SortBy == "" { + forkidMap := make(map[string]int, 0) + for index, re := range esresult.Result { + if re["fork_id"] != nil { + fork_id := re["fork_id"].(string) + if _, ok := forkidMap[fork_id]; !ok { + forkidMap[fork_id] = index + } + } + } + for key, value := range forkidMap { + for index, re := range esresult.Result { + if re["id"].(string) == key { + if value < index { //swap + tmp := esresult.Result[index] + esresult.Result[index] = esresult.Result[value] + esresult.Result[value] = tmp + break + } + } + } + } + } +} + func sortRepo(Result []map[string]interface{}, SortBy string, ascending bool) { orderBy := "" switch SortBy { @@ -479,6 +504,7 @@ func makeRepoResult(sRes *elastic.SearchResult, Key string, OnlyReturnNum bool, record["num_stars"] = recordSource["num_stars"] record["num_forks"] = recordSource["num_forks"] record["lower_alias"] = recordSource["lower_alias"] + record["fork_id"] = recordSource["fork_id"] if recordSource["topics"] != nil { topicsStr := recordSource["topics"].(string) log.Info("topicsStr=" + topicsStr) diff --git a/routers/user/home.go b/routers/user/home.go index ab64e707f..d8c2565c6 100755 --- a/routers/user/home.go +++ b/routers/user/home.go @@ -23,6 +23,7 @@ import ( "code.gitea.io/gitea/modules/modelarts" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/routers/repo" issue_service "code.gitea.io/gitea/services/issue" pull_service "code.gitea.io/gitea/services/pull" @@ -760,10 +761,14 @@ func Cloudbrains(ctx *context.Context) { listType := ctx.Query("listType") jobType := ctx.Query("jobType") jobStatus := ctx.Query("jobStatus") + aiCenter := ctx.Query("aiCenter") + cluster := ctx.Query("cluster") ctx.Data["ListType"] = listType ctx.Data["JobType"] = jobType ctx.Data["JobStatus"] = jobStatus + ctx.Data["aiCenter"] = aiCenter + ctx.Data["cluster"] = cluster page := ctx.QueryInt("page") if page <= 0 { @@ -824,6 +829,8 @@ func Cloudbrains(ctx *context.Context) { RepoIDList: repoIDList, ComputeResource: listType, Type: models.TypeCloudBrainAll, + AiCenter: aiCenter, + Cluster: cluster, }) if err != nil { ctx.ServerError("Get job failed:", err) @@ -834,6 +841,11 @@ func Cloudbrains(ctx *context.Context) { ciTasks[i].CanDebug = true ciTasks[i].CanDel = true ciTasks[i].Cloudbrain.ComputeResource = task.ComputeResource + ciTasks[i].Cloudbrain.AiCenter = repo.GetCloudbrainAiCenter(task.Cloudbrain, ctx) + _, cardType, _ := repo.GetCloudbrainCardNumAndType(task.Cloudbrain) + ciTasks[i].Cloudbrain.CardType = cardType + ciTasks[i].Cloudbrain.Cluster = repo.GetCloudbrainCluster(task.Cloudbrain, ctx) + } pager := context.NewPagination(int(count), setting.UI.IssuePagingNum, page, getTotalPage(count, setting.UI.IssuePagingNum)) diff --git a/routers/user/profile.go b/routers/user/profile.go index 30808f235..42cdfd1a8 100755 --- a/routers/user/profile.go +++ b/routers/user/profile.go @@ -116,8 +116,8 @@ func Profile(ctx *context.Context) { } var opts = models.FindOrgMembersOpts{ - OrgID: org.ID, - PublicOnly: true, + OrgID: org.ID, + PublicOnly: true, } if ctx.User != nil { @@ -261,7 +261,7 @@ func Profile(ctx *context.Context) { IsOwner: isOwner, ListOptions: models.ListOptions{ Page: page, - PageSize: setting.UI.ExplorePagingNum, + PageSize: setting.UI.User.RepoPagingNum, }, CloudBrainType: -1, } diff --git a/services/admin/operate_log/operate_log.go b/services/admin/operate_log/operate_log.go new file mode 100644 index 000000000..7b72ec2e2 --- /dev/null +++ b/services/admin/operate_log/operate_log.go @@ -0,0 +1,14 @@ +package operate_log + +import ( + "code.gitea.io/gitea/models" +) + +func Log(log models.AdminOperateLog) error { + _, err := models.InsertAdminOperateLog(log) + return err +} + +func NewLogValues() *models.LogValues { + return &models.LogValues{Params: make([]models.LogValue, 0)} +} diff --git a/services/cloudbrain/resource/resource_queue.go b/services/cloudbrain/resource/resource_queue.go new file mode 100644 index 000000000..2798a2b11 --- /dev/null +++ b/services/cloudbrain/resource/resource_queue.go @@ -0,0 +1,122 @@ +package resource + +import ( + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/grampus" + "code.gitea.io/gitea/modules/log" + "fmt" + "strings" +) + +func AddResourceQueue(req models.ResourceQueueReq) error { + if _, err := models.InsertResourceQueue(req.ToDTO()); err != nil { + return err + } + return nil +} + +func UpdateResourceQueue(queueId int64, req models.ResourceQueueReq) error { + if _, err := models.UpdateResourceQueueById(queueId, models.ResourceQueue{ + CardsTotalNum: req.CardsTotalNum, + Remark: req.Remark, + }); err != nil { + return err + } + return nil +} + +func GetResourceQueueList(opts models.SearchResourceQueueOptions) (*models.ResourceQueueListRes, error) { + n, r, err := models.SearchResourceQueue(opts) + if err != nil { + return nil, err + } + + return models.NewResourceQueueListRes(n, r), nil +} + +func GetResourceQueueCodes(opts models.GetQueueCodesOptions) ([]*models.ResourceQueueCodesRes, error) { + r, err := models.GetResourceQueueCodes(opts) + if err != nil { + return nil, err + } + + return r, nil +} + +func GetResourceAiCenters() ([]models.ResourceAiCenterRes, error) { + r, err := models.GetResourceAiCenters() + if err != nil { + return nil, err + } + + return r, nil +} + +func SyncGrampusQueue(doerId int64) error { + r, err := grampus.GetAiCenters(1, 100) + if err != nil { + return err + } + log.Info("SyncGrampusQueue result = %+v", r) + queueUpdateList := make([]models.ResourceQueue, 0) + queueInsertList := make([]models.ResourceQueue, 0) + existIds := make([]int64, 0) + + for _, center := range r.Infos { + for _, device := range center.AccDevices { + computeResource := models.ParseComputeResourceFormGrampus(device.Kind) + accCardType := strings.ToUpper(device.Model) + if computeResource == "" { + continue + } + //Determine if this quque already exists.if exist,update params + //if not exist,insert a new record + oldQueue, err := models.GetResourceQueue(&models.ResourceQueue{ + Cluster: models.C2NetCluster, + AiCenterCode: center.Id, + ComputeResource: computeResource, + AccCardType: accCardType, + }) + if err != nil { + return err + } + + if oldQueue == nil { + queueInsertList = append(queueInsertList, models.ResourceQueue{ + Cluster: models.C2NetCluster, + AiCenterCode: center.Id, + AiCenterName: center.Name, + ComputeResource: computeResource, + AccCardType: accCardType, + IsAutomaticSync: true, + CreatedBy: doerId, + UpdatedBy: doerId, + }) + } else { + existIds = append(existIds, oldQueue.ID) + queueUpdateList = append(queueUpdateList, models.ResourceQueue{ + ID: oldQueue.ID, + ComputeResource: computeResource, + AiCenterName: center.Name, + AccCardType: accCardType, + UpdatedBy: doerId, + }) + } + + } + } + return models.SyncGrampusQueues(queueUpdateList, queueInsertList, existIds) +} + +func SyncGrampusQueueAndSpecs() { + defer func() { + if err := recover(); err != nil { + combinedErr := fmt.Errorf("%s\n%s", err, log.Stack(2)) + log.Error("PANIC:", combinedErr) + } + }() + log.Info("start to sync grampus queue and specs") + SyncGrampusQueue(0) + SyncGrampusSpecs(0) + log.Info("sync grampus queue and specs finished") +} diff --git a/services/cloudbrain/resource/resource_scene.go b/services/cloudbrain/resource/resource_scene.go new file mode 100644 index 000000000..4cc840e76 --- /dev/null +++ b/services/cloudbrain/resource/resource_scene.go @@ -0,0 +1,35 @@ +package resource + +import ( + "code.gitea.io/gitea/models" +) + +func AddResourceScene(req models.ResourceSceneReq) error { + if err := models.InsertResourceScene(req); err != nil { + return err + } + return nil +} + +func UpdateResourceScene(req models.ResourceSceneReq) error { + if err := models.UpdateResourceScene(req); err != nil { + return err + } + return nil +} + +func DeleteResourceScene(id int64) error { + if err := models.DeleteResourceScene(id); err != nil { + return err + } + return nil +} + +func GetResourceSceneList(opts models.SearchResourceSceneOptions) (*models.ResourceSceneListRes, error) { + n, r, err := models.SearchResourceScene(opts) + if err != nil { + return nil, err + } + + return models.NewResourceSceneListRes(n, r), nil +} diff --git a/services/cloudbrain/resource/resource_specification.go b/services/cloudbrain/resource/resource_specification.go new file mode 100644 index 000000000..9692c1d0b --- /dev/null +++ b/services/cloudbrain/resource/resource_specification.go @@ -0,0 +1,639 @@ +package resource + +import ( + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/cloudbrain" + "code.gitea.io/gitea/modules/grampus" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/modelarts" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/routers/response" + "code.gitea.io/gitea/services/admin/operate_log" + "encoding/json" + "errors" + "fmt" + "strconv" + "strings" + "time" +) + +func AddResourceSpecification(doerId int64, req models.ResourceSpecificationReq) error { + if req.Status == 0 { + req.Status = models.SpecNotVerified + } + spec := req.ToDTO() + if _, err := models.InsertResourceSpecification(spec); err != nil { + return err + } + return nil +} + +func UpdateSpecUnitPrice(doerId int64, specId int64, unitPrice int) *response.BizError { + oldSpec, err := models.GetResourceSpecification(&models.ResourceSpecification{ID: specId}) + if err != nil { + return response.NewBizError(err) + } + if oldSpec == nil { + return response.SPECIFICATION_NOT_EXIST + } + err = models.UpdateSpecUnitPriceById(specId, unitPrice) + if err != nil { + return response.NewBizError(err) + } + + if oldSpec.UnitPrice != unitPrice { + AddSpecOperateLog(doerId, "edit", operate_log.NewLogValues().Add("unitPrice", unitPrice), operate_log.NewLogValues().Add("unitPrice", oldSpec.UnitPrice), specId, fmt.Sprintf("修改资源规格单价从%d积分到%d积分", oldSpec.UnitPrice, unitPrice)) + } + return nil +} + +func SyncGrampusSpecs(doerId int64) error { + r, err := grampus.GetResourceSpecs("") + if err != nil { + return err + } + log.Info("SyncGrampusSpecs result = %+v", r) + specUpdateList := make([]models.ResourceSpecification, 0) + specInsertList := make([]models.ResourceSpecification, 0) + existIds := make([]int64, 0) + for _, spec := range r.Infos { + for _, c := range spec.Centers { + computeResource := models.ParseComputeResourceFormGrampus(spec.SpecInfo.AccDeviceKind) + if computeResource == "" { + continue + } + accCardType := strings.ToUpper(spec.SpecInfo.AccDeviceModel) + memGiB, err := models.ParseMemSizeFromGrampus(spec.SpecInfo.MemorySize) + gpuMemGiB, err := models.ParseMemSizeFromGrampus(spec.SpecInfo.AccDeviceMemory) + if err != nil { + log.Error("ParseMemSizeFromGrampus error. MemorySize=%s AccDeviceMemory=%s", spec.SpecInfo.MemorySize, spec.SpecInfo.AccDeviceMemory) + } + // get resource queue.if queue not exist,skip it + r, err := models.GetResourceQueue(&models.ResourceQueue{ + Cluster: models.C2NetCluster, + AiCenterCode: c.ID, + ComputeResource: computeResource, + AccCardType: accCardType, + }) + if err != nil || r == nil { + continue + } + + //Determine if this specification already exists.if exist,update params + //if not exist,insert a new record and status is SpecNotVerified + oldSpec, err := models.GetResourceSpecification(&models.ResourceSpecification{ + QueueId: r.ID, + SourceSpecId: spec.ID, + }) + if err != nil { + return err + } + + if oldSpec == nil { + specInsertList = append(specInsertList, models.ResourceSpecification{ + QueueId: r.ID, + SourceSpecId: spec.ID, + AccCardsNum: spec.SpecInfo.AccDeviceNum, + CpuCores: spec.SpecInfo.CpuCoreNum, + MemGiB: memGiB, + GPUMemGiB: gpuMemGiB, + Status: models.SpecNotVerified, + IsAutomaticSync: true, + CreatedBy: doerId, + UpdatedBy: doerId, + }) + } else { + existIds = append(existIds, oldSpec.ID) + specUpdateList = append(specUpdateList, models.ResourceSpecification{ + ID: oldSpec.ID, + AccCardsNum: spec.SpecInfo.AccDeviceNum, + CpuCores: spec.SpecInfo.CpuCoreNum, + MemGiB: memGiB, + GPUMemGiB: gpuMemGiB, + UpdatedBy: doerId, + }) + } + + } + } + return models.SyncGrampusSpecs(specUpdateList, specInsertList, existIds) +} + +//GetResourceSpecificationList returns specification and queue +func GetResourceSpecificationList(opts models.SearchResourceSpecificationOptions) (*models.ResourceSpecAndQueueListRes, error) { + n, r, err := models.SearchResourceSpecification(opts) + if err != nil { + return nil, err + } + + return models.NewResourceSpecAndQueueListRes(n, r), nil +} + +func GetResourceSpecificationScenes(specId int64) ([]models.ResourceSceneBriefRes, error) { + r, err := models.GetSpecScenes(specId) + if err != nil { + return nil, err + } + + return r, nil +} + +func ResourceSpecOnShelf(doerId int64, id int64, unitPrice int) *response.BizError { + spec, err := models.GetResourceSpecification(&models.ResourceSpecification{ID: id}) + if err != nil { + return response.NewBizError(err) + } + if spec == nil { + return response.SPECIFICATION_NOT_EXIST + } + if q, err := models.GetResourceQueue(&models.ResourceQueue{ID: spec.QueueId}); err != nil || q == nil { + return response.RESOURCE_QUEUE_NOT_AVAILABLE + } + + err = models.ResourceSpecOnShelf(id, unitPrice) + if err != nil { + return response.NewBizError(err) + } + if spec.UnitPrice != unitPrice { + AddSpecOperateLog(doerId, "on-shelf", operate_log.NewLogValues().Add("UnitPrice", unitPrice), operate_log.NewLogValues().Add("UnitPrice", spec.UnitPrice), id, fmt.Sprintf("定价上架资源规格,单价为%d", unitPrice)) + } else { + AddSpecOperateLog(doerId, "on-shelf", nil, nil, id, "上架资源规格") + } + return nil + +} +func ResourceSpecOffShelf(doerId int64, id int64) *response.BizError { + _, err := models.ResourceSpecOffShelf(id) + if err != nil { + return response.NewBizError(err) + } + AddSpecOperateLog(doerId, "off-shelf", nil, nil, id, "下架资源规格") + return nil + +} + +func AddSpecOperateLog(doerId int64, operateType string, newValue, oldValue *models.LogValues, specId int64, comment string) { + var newString = "" + var oldString = "" + if newValue != nil { + newString = newValue.JsonString() + } + if oldValue != nil { + oldString = oldValue.JsonString() + } + operate_log.Log(models.AdminOperateLog{ + BizType: "SpecOperate", + OperateType: operateType, + OldValue: oldString, + NewValue: newString, + RelatedId: fmt.Sprint(specId), + CreatedBy: doerId, + Comment: comment, + }) +} + +func FindAvailableSpecs(userId int64, opts models.FindSpecsOptions) ([]*models.Specification, error) { + r, err := models.FindSpecs(opts) + if err != nil { + log.Error("FindAvailableSpecs error.%v", err) + return nil, err + } + //filter exclusive specs + specs := filterExclusiveSpecs(r, userId) + + //distinct by sourceSpecId + specs = distinctSpecs(specs) + return specs, err +} + +func filterExclusiveSpecs(r []*models.Specification, userId int64) []*models.Specification { + specs := make([]*models.Specification, 0, len(r)) + specMap := make(map[int64]string, 0) + for i := 0; i < len(r); i++ { + spec := r[i] + if _, has := specMap[spec.ID]; has { + continue + } + if !spec.IsExclusive { + specs = append(specs, spec) + specMap[spec.ID] = "" + continue + } + orgs := strings.Split(spec.ExclusiveOrg, ";") + for _, org := range orgs { + isMember, _ := models.IsOrganizationMemberByOrgName(org, userId) + if isMember { + specs = append(specs, spec) + specMap[spec.ID] = "" + } + } + } + return specs +} + +func distinctSpecs(r []*models.Specification) []*models.Specification { + specs := make([]*models.Specification, 0, len(r)) + sourceSpecIdMap := make(map[string]string, 0) + for i := 0; i < len(r); i++ { + spec := r[i] + if spec.SourceSpecId == "" { + specs = append(specs, spec) + continue + } + if _, has := sourceSpecIdMap[spec.SourceSpecId]; has { + continue + } + specs = append(specs, spec) + sourceSpecIdMap[spec.SourceSpecId] = "" + } + return specs +} + +func GetAndCheckSpec(userId int64, specId int64, opts models.FindSpecsOptions) (*models.Specification, error) { + if specId == 0 { + return nil, nil + } + opts.SpecId = specId + r, err := FindAvailableSpecs(userId, opts) + if err != nil { + return nil, err + } + if r == nil || len(r) == 0 { + return nil, nil + } + return r[0], nil +} + +func InsertCloudbrainSpec(cloudbrainId int64, s *models.Specification) error { + c := models.CloudbrainSpec{ + CloudbrainID: cloudbrainId, + SpecId: s.ID, + SourceSpecId: s.SourceSpecId, + AccCardsNum: s.AccCardsNum, + AccCardType: s.AccCardType, + CpuCores: s.CpuCores, + MemGiB: s.MemGiB, + GPUMemGiB: s.GPUMemGiB, + ShareMemGiB: s.ShareMemGiB, + ComputeResource: s.ComputeResource, + UnitPrice: s.UnitPrice, + QueueId: s.QueueId, + QueueCode: s.QueueCode, + Cluster: s.Cluster, + AiCenterCode: s.AiCenterCode, + AiCenterName: s.AiCenterName, + IsExclusive: s.IsExclusive, + ExclusiveOrg: s.ExclusiveOrg, + } + _, err := models.InsertCloudbrainSpec(c) + if err != nil { + log.Error("InsertCloudbrainSpec error.CloudbrainSpec=%v. err=%v", c, err) + return err + } + return nil +} + +func GetCloudbrainSpec(cloudbrainId int64) (*models.Specification, error) { + c, err := models.GetCloudbrainSpecByID(cloudbrainId) + if err != nil { + return nil, err + } + if c == nil { + return nil, nil + } + return c.ConvertToSpecification(), nil +} + +func RefreshHistorySpec(scopeAll bool, ids []int64) (int64, int64, error) { + var success int64 + var total int64 + + if !scopeAll { + if ids == nil || len(ids) == 0 { + return 0, 0, nil + } + total = int64(len(ids)) + tasks, err := models.GetCloudbrainWithDeletedByIDs(ids) + if err != nil { + return total, 0, err + } + for _, task := range tasks { + err = RefreshOneHistorySpec(task) + if err != nil { + log.Error("RefreshOneHistorySpec error.%v", err) + continue + } + success++ + } + + } else { + page := 1 + pageSize := 100 + n, err := models.CountNoSpecHistoricTask() + if err != nil { + log.Error("FindNoSpecHistoricTask CountNoSpecHistoricTask error. e=%v", err) + return 0, 0, err + } + total = n + for i := 0; i < 500; i++ { + list, err := models.FindCloudbrainTask(page, pageSize) + page++ + if err != nil { + log.Error("FindCloudbrainTask error.page=%d pageSize=%d e=%v", page, pageSize, err) + return total, success, err + } + if len(list) == 0 { + log.Info("RefreshHistorySpec. list is empty") + break + } + for _, task := range list { + s, err := GetCloudbrainSpec(task.ID) + if err != nil { + log.Error("RefreshHistorySpec GetCloudbrainSpec error.%v", err) + continue + } + if s != nil { + continue + } + err = RefreshOneHistorySpec(task) + if err != nil { + log.Error("RefreshOneHistorySpec error.%v", err) + continue + } + success++ + } + if len(list) < pageSize { + log.Info("RefreshHistorySpec. list < pageSize") + break + } + } + } + return total, success, nil + +} + +func RefreshOneHistorySpec(task *models.Cloudbrain) error { + var spec *models.Specification + var err error + switch task.Type { + case models.TypeCloudBrainOne: + spec, err = getCloudbrainOneSpec(task) + case models.TypeCloudBrainTwo: + spec, err = getCloudbrainTwoSpec(task) + case models.TypeC2Net: + spec, err = getGrampusSpec(task) + } + if err != nil { + log.Error("find spec error,task.ID=%d err=%v", task.ID, err) + return err + } + if spec == nil { + log.Error("find spec failed,task.ID=%d", task.ID) + return errors.New("find spec failed") + } + return InsertCloudbrainSpec(task.ID, spec) +} + +func getCloudbrainOneSpec(task *models.Cloudbrain) (*models.Specification, error) { + if task.GpuQueue == "" { + log.Info("gpu queue is empty.task.ID = %d", task.ID) + return nil, nil + } + //find from config + spec, err := findCloudbrainOneSpecFromConfig(task) + if err != nil { + log.Error("getCloudbrainOneSpec findCloudbrainOneSpecFromConfig error.%v", err) + return nil, err + } + if spec != nil { + return spec, nil + } + //find from remote + return findCloudbrainOneSpecFromRemote(task) + +} + +func findCloudbrainOneSpecFromRemote(task *models.Cloudbrain) (*models.Specification, error) { + time.Sleep(200 * time.Millisecond) + log.Info("start findCloudbrainOneSpecFromRemote") + result, err := cloudbrain.GetJob(task.JobID) + if err != nil { + log.Error("getCloudbrainOneSpec error. %v", err) + return nil, err + } + + if result == nil { + log.Info("findCloudbrainOneSpecFromRemote failed,result is empty.task.ID=%d", task.ID) + return nil, nil + } + jobRes, _ := models.ConvertToJobResultPayload(result.Payload) + memSize, _ := models.ParseMemSizeFromGrampus(jobRes.Resource.Memory) + if task.ComputeResource == "CPU/GPU" { + task.ComputeResource = models.GPU + } + var shmMB float32 + if jobRes.Config.TaskRoles != nil && len(jobRes.Config.TaskRoles) > 0 { + shmMB = float32(jobRes.Config.TaskRoles[0].ShmMB) / 1024 + } + + opt := models.FindSpecsOptions{ + ComputeResource: task.ComputeResource, + Cluster: models.OpenICluster, + AiCenterCode: models.AICenterOfCloudBrainOne, + QueueCode: task.GpuQueue, + AccCardsNum: jobRes.Resource.NvidiaComGpu, + UseAccCardsNum: true, + CpuCores: jobRes.Resource.CPU, + UseCpuCores: true, + MemGiB: memSize, + UseMemGiB: memSize > 0, + ShareMemGiB: shmMB, + UseShareMemGiB: shmMB > 0, + RequestAll: true, + } + specs, err := models.FindSpecs(opt) + if err != nil { + log.Error("getCloudbrainOneSpec from remote error,%v", err) + return nil, err + } + if len(specs) == 1 { + return specs[0], nil + } + if len(specs) == 0 { + s, err := InitQueueAndSpec(opt, "云脑一", "处理历史云脑任务时自动添加") + if err != nil { + log.Error("getCloudbrainOneSpec InitQueueAndSpec error.err=%v", err) + return nil, nil + } + return s, nil + } + log.Error("Too many results matched.size=%d opt=%+v", len(specs), opt) + return nil, nil +} + +func findCloudbrainOneSpecFromConfig(task *models.Cloudbrain) (*models.Specification, error) { + //find from config + var specConfig *models.ResourceSpec + hasSpec := false + if task.JobType == string(models.JobTypeTrain) { + if cloudbrain.TrainResourceSpecs == nil { + json.Unmarshal([]byte(setting.TrainResourceSpecs), &cloudbrain.TrainResourceSpecs) + } + for _, tmp := range cloudbrain.TrainResourceSpecs.ResourceSpec { + if tmp.Id == task.ResourceSpecId { + hasSpec = true + specConfig = tmp + break + } + } + } else if task.JobType == string(models.JobTypeInference) { + if cloudbrain.InferenceResourceSpecs == nil { + json.Unmarshal([]byte(setting.InferenceResourceSpecs), &cloudbrain.InferenceResourceSpecs) + } + for _, tmp := range cloudbrain.InferenceResourceSpecs.ResourceSpec { + if tmp.Id == task.ResourceSpecId { + hasSpec = true + specConfig = tmp + break + } + } + } else { + if cloudbrain.ResourceSpecs == nil { + json.Unmarshal([]byte(setting.ResourceSpecs), &cloudbrain.ResourceSpecs) + } + for _, tmp := range cloudbrain.ResourceSpecs.ResourceSpec { + if tmp.Id == task.ResourceSpecId { + hasSpec = true + specConfig = tmp + break + + } + } + } + if !hasSpec && cloudbrain.SpecialPools != nil { + + for _, specialPool := range cloudbrain.SpecialPools.Pools { + + if specialPool.ResourceSpec != nil { + + for _, spec := range specialPool.ResourceSpec { + if task.ResourceSpecId == spec.Id { + hasSpec = true + specConfig = spec + break + } + } + } + } + } + if specConfig == nil { + log.Error("getCloudbrainOneSpec from config failed,task.ResourceSpecId=%d", task.ResourceSpecId) + return nil, nil + } + if task.ComputeResource == "CPU/GPU" { + task.ComputeResource = models.GPU + } + + opt := models.FindSpecsOptions{ + JobType: models.JobType(task.JobType), + ComputeResource: task.ComputeResource, + Cluster: models.OpenICluster, + AiCenterCode: models.AICenterOfCloudBrainOne, + QueueCode: task.GpuQueue, + AccCardsNum: specConfig.GpuNum, + UseAccCardsNum: true, + CpuCores: specConfig.CpuNum, + UseCpuCores: true, + MemGiB: float32(specConfig.MemMiB) / 1024, + UseMemGiB: true, + ShareMemGiB: float32(specConfig.ShareMemMiB) / 1024, + UseShareMemGiB: true, + RequestAll: true, + } + specs, err := models.FindSpecs(opt) + if err != nil { + log.Error("getCloudbrainOneSpec from config error,%v", err) + return nil, err + } + if len(specs) > 1 { + log.Error("Too many results matched.size=%d opt=%+v", len(specs), opt) + return nil, nil + } + if len(specs) == 0 { + s, err := InitQueueAndSpec(opt, "云脑一", "处理历史云脑任务时自动添加") + if err != nil { + log.Error("getCloudbrainOneSpec InitQueueAndSpec error.err=%v", err) + return nil, nil + } + return s, nil + } + return specs[0], nil +} + +func getCloudbrainTwoSpec(task *models.Cloudbrain) (*models.Specification, error) { + specMap, err := models.GetCloudbrainTwoSpecs() + if err != nil { + log.Error("InitCloudbrainTwoSpecs err.%v", err) + return nil, err + } + if task.FlavorCode != "" { + return specMap[task.FlavorCode], nil + } + time.Sleep(200 * time.Millisecond) + log.Info("start getCloudbrainTwoSpec FromRemote") + if task.JobType == string(models.JobTypeDebug) { + result, err := modelarts.GetNotebook2(task.JobID) + if err != nil { + log.Error("getCloudbrainTwoSpec GetNotebook2 error.%v", err) + return nil, err + } + if result != nil { + return specMap[result.Flavor], nil + } + } else if task.JobType == string(models.JobTypeTrain) || task.JobType == string(models.JobTypeInference) { + result, err := modelarts.GetTrainJob(task.JobID, strconv.FormatInt(task.VersionID, 10)) + if err != nil { + log.Error("getCloudbrainTwoSpec GetTrainJob error:%v", task.JobName, err) + return nil, err + } + if result != nil { + return specMap[result.Flavor.Code], nil + } + } + return nil, nil +} + +func getGrampusSpec(task *models.Cloudbrain) (*models.Specification, error) { + specMap, err := models.GetGrampusSpecs() + if err != nil { + log.Error("GetGrampusSpecs err.%v", err) + return nil, err + } + if task.AiCenter != "" { + c := strings.Split(task.AiCenter, "+") + spec := specMap[task.FlavorCode+"_"+c[0]] + if spec != nil { + return spec, nil + } + } + return specMap[task.FlavorCode], nil +} + +func InitQueueAndSpec(opt models.FindSpecsOptions, aiCenterName string, remark string) (*models.Specification, error) { + return models.InitQueueAndSpec(models.ResourceQueue{ + QueueCode: opt.QueueCode, + Cluster: opt.Cluster, + AiCenterCode: opt.AiCenterCode, + AiCenterName: aiCenterName, + ComputeResource: opt.ComputeResource, + AccCardType: models.GetCloudbrainOneAccCardType(opt.QueueCode), + Remark: remark, + }, models.ResourceSpecification{ + AccCardsNum: opt.AccCardsNum, + CpuCores: opt.CpuCores, + MemGiB: opt.MemGiB, + GPUMemGiB: opt.GPUMemGiB, + ShareMemGiB: opt.ShareMemGiB, + Status: models.SpecOffShelf, + }) +} diff --git a/services/repository/dataset.go b/services/repository/dataset.go new file mode 100644 index 000000000..ffe3c5466 --- /dev/null +++ b/services/repository/dataset.go @@ -0,0 +1,19 @@ +package repository + +import ( + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/modules/context" +) + +func ConvertToDatasetWithStar(ctx *context.Context, datasets []*models.Dataset) []*models.DatasetWithStar { + var datasetsWithStar []*models.DatasetWithStar + for _, dataset := range datasets { + if !ctx.IsSigned { + datasetsWithStar = append(datasetsWithStar, &models.DatasetWithStar{Dataset: *dataset, IsStaring: false}) + } else { + datasetsWithStar = append(datasetsWithStar, &models.DatasetWithStar{Dataset: *dataset, IsStaring: models.IsDatasetStaring(ctx.User.ID, dataset.ID)}) + } + + } + return datasetsWithStar +} diff --git a/templates/admin/cloudbrain/images.tmpl b/templates/admin/cloudbrain/images.tmpl index 7d800eb64..d79bd5437 100644 --- a/templates/admin/cloudbrain/images.tmpl +++ b/templates/admin/cloudbrain/images.tmpl @@ -13,10 +13,9 @@
        {{template "admin/navbar" .}} -
        - -
        - +
        +
        +
        diff --git a/templates/admin/cloudbrain/list.tmpl b/templates/admin/cloudbrain/list.tmpl index 347b5658d..83510f268 100755 --- a/templates/admin/cloudbrain/list.tmpl +++ b/templates/admin/cloudbrain/list.tmpl @@ -14,54 +14,70 @@
        {{template "admin/navbar" .}} -
        +
        {{template "base/alert" .}} -
        -
        +
        +
        {{template "admin/cloudbrain/search" .}} -
        +
        -
        +
        -
        - {{$.i18n.Tr "repo.cloudbrain_task"}} +
        + {{$.i18n.Tr "repo.cloudbrain_task"}}
        -
        - {{$.i18n.Tr "repo.cloudbrain_task_type"}} + +
        + {{$.i18n.Tr "repo.modelarts.cluster"}}
        -
        +
        {{$.i18n.Tr "repo.modelarts.status"}}
        -
        +
        + {{$.i18n.Tr "repo.cloudbrain_task_type"}} +
        +
        {{$.i18n.Tr "repo.modelarts.createtime"}}
        -
        +
        {{$.i18n.Tr "repo.cloudbrain_status_runtime"}}
        -
        +
        {{$.i18n.Tr "repo.modelarts.computing_resources"}}
        -
        + +
        + {{$.i18n.Tr "repo.modelarts.ai_center"}} +
        + +
        + {{$.i18n.Tr "repo.modelarts.card_type"}} +
        +
        {{$.i18n.Tr "repo.cloudbrain_creator"}}
        -
        +
        {{$.i18n.Tr "repository"}}
        -
        +
        {{.i18n.Tr "admin.cloudbrain.cloudbrain_name"}}
        -
        +
        {{$.i18n.Tr "repo.cloudbrain_operate"}}
        @@ -78,24 +94,24 @@ {{$JobID = .JobID}} {{end}} -
        + - -
        - {{.JobType}} + +
        + {{if .Cluster}}{{.Cluster}}{{else}}--{{end}}
        + style="width: 6% !important;"> @@ -131,23 +148,39 @@ style="margin-left: 0.4em;font-size: 12px;">{{.Status}}
        + +
        + {{.JobType}} +
        + -
        +
        {{TimeSinceUnix1 .Cloudbrain.CreatedUnix}}
        -
        +
        {{if .TrainJobDuration}}{{.TrainJobDuration}}{{else}}--{{end}}
        -
        +
        {{if .ComputeResource}}{{.ComputeResource}}{{else}}--{{end}}
        + +
        + {{if .AiCenter}}{{.AiCenter}}{{else}}--{{end}} +
        + +
        + + {{if .CardType}}{{.CardType}}{{else}}--{{end}} + +
        -
        +
        {{if .User.Name}} @@ -157,16 +190,24 @@ {{end}}
        -
        +
        - {{.JobName}} + style="overflow: hidden;text-overflow:ellipsis;width:10% !important;"> + + {{.JobName}} +
        -
        +
        {{if eq .JobType "DEBUG"}}
        @@ -204,7 +245,7 @@ {{else}} {{$.i18n.Tr "repo.stop"}} @@ -212,7 +253,7 @@
        {{$.CsrfTokenHtml}}
        -
        +
        {{if eq .JobType "DEBUG"}} - + {{.DisplayJobName}} {{else if eq .JobType "INFERENCE"}} - + {{.DisplayJobName}} {{else if eq .JobType "TRAIN"}} - + {{.DisplayJobName}} {{else if eq .JobType "BENCHMARK"}} - + {{.DisplayJobName}} {{end}}
        - -
        - {{.JobType}} + +
        + {{if .Cluster}}{{.Cluster}}{{else}}--{{end}}
        + style="width: 6% !important;"> {{.Status}}
        + +
        + {{.JobType}} +
        + -
        +
        {{TimeSinceUnix1 .Cloudbrain.CreatedUnix}}
        -
        +
        {{if .TrainJobDuration}}{{.TrainJobDuration}}{{else}}--{{end}}
        -
        +
        {{if .ComputeResource}}{{.ComputeResource}}{{else}}--{{end}}
        + +
        + {{if .AiCenter}}{{.AiCenter}}{{else}}--{{end}} +
        + +
        + + {{if .CardType}}{{.CardType}}{{else}}--{{end}} + +
        -
        +
        {{if .User.Name}} @@ -298,15 +356,24 @@ {{end}}
        -
        +
        --
        - {{.JobName}} + style="overflow: hidden;text-overflow:ellipsis;width:10% !important;"> + + {{.JobName}} + +
        -
        +
        {{if eq .JobType "DEBUG"}}
        @@ -345,17 +412,16 @@
        {{end}} - {{end}} -
        -
        - - -
        -
        + {{end}} +
        +
        +
        +
        + +
        -
        diff --git a/templates/admin/cloudbrain/search.tmpl b/templates/admin/cloudbrain/search.tmpl index ddd616eb4..72077b8a2 100644 --- a/templates/admin/cloudbrain/search.tmpl +++ b/templates/admin/cloudbrain/search.tmpl @@ -7,44 +7,68 @@
        + + diff --git a/templates/admin/cloudbrain/search_dashboard.tmpl b/templates/admin/cloudbrain/search_dashboard.tmpl index 61f02f182..c2d3d139e 100644 --- a/templates/admin/cloudbrain/search_dashboard.tmpl +++ b/templates/admin/cloudbrain/search_dashboard.tmpl @@ -15,48 +15,72 @@
        -
        +
        + + diff --git a/templates/admin/dataset/list.tmpl b/templates/admin/dataset/list.tmpl index 9e4e72b68..9712f2e7b 100644 --- a/templates/admin/dataset/list.tmpl +++ b/templates/admin/dataset/list.tmpl @@ -35,7 +35,7 @@ {{range .Datasets}}
      • - + @@ -48,4 +48,4 @@ {{template "base/paginate" .}} -{{template "base/footer" .}} +{{template "base/footer" .}} \ No newline at end of file diff --git a/templates/admin/navbar.tmpl b/templates/admin/navbar.tmpl index d04915df1..5dd430466 100644 --- a/templates/admin/navbar.tmpl +++ b/templates/admin/navbar.tmpl @@ -1,44 +1,64 @@ - diff --git a/templates/base/head_navbar_home.tmpl b/templates/base/head_navbar_home.tmpl index cfc3859d0..2b1a482b5 100644 --- a/templates/base/head_navbar_home.tmpl +++ b/templates/base/head_navbar_home.tmpl @@ -34,7 +34,8 @@ {{.i18n.Tr "explore.organizations"}} {{.i18n.Tr "explore.images"}} {{if .IsOperator}} - {{.i18n.Tr "explore.data_analysis"}} + {{/* {{.i18n.Tr "explore.data_analysis"}} */}} + {{.i18n.Tr "explore.data_analysis"}} {{end}} {{.i18n.Tr "custom.head.openi.repo"}} @@ -66,7 +67,8 @@ {{.i18n.Tr "explore.organizations"}} {{.i18n.Tr "explore.images"}} {{if .IsOperator}} - {{.i18n.Tr "explore.data_analysis"}} + {{/* {{.i18n.Tr "explore.data_analysis"}} */}} + {{.i18n.Tr "explore.data_analysis"}} {{end}} {{.i18n.Tr "custom.head.openi.repo"}} diff --git a/templates/base/head_navbar_pro.tmpl b/templates/base/head_navbar_pro.tmpl index d345665c5..8aa9861bb 100644 --- a/templates/base/head_navbar_pro.tmpl +++ b/templates/base/head_navbar_pro.tmpl @@ -44,7 +44,8 @@ {{.i18n.Tr "explore.organizations"}} {{.i18n.Tr "explore.images"}} {{if .IsOperator}} - {{.i18n.Tr "explore.data_analysis"}} + {{/* {{.i18n.Tr "explore.data_analysis"}} */}} + {{.i18n.Tr "explore.data_analysis"}} {{end}} {{.i18n.Tr "custom.head.openi.repo"}} @@ -76,7 +77,8 @@ {{.i18n.Tr "explore.organizations"}} {{.i18n.Tr "explore.images"}} {{if .IsOperator}} - {{.i18n.Tr "explore.data_analysis"}} + {{/* {{.i18n.Tr "explore.data_analysis"}} */}} + {{.i18n.Tr "explore.data_analysis"}} {{end}} {{.i18n.Tr "custom.head.openi.repo"}} diff --git a/templates/base/head_pro.tmpl b/templates/base/head_pro.tmpl index 75292b6fc..c643ea873 100644 --- a/templates/base/head_pro.tmpl +++ b/templates/base/head_pro.tmpl @@ -109,6 +109,11 @@ ], {{end}} }; + {{if .IsSigned}} + window.sessionStorage.setItem('_csrf', '{{.CsrfToken}}'); + {{else}} + window.sessionStorage.removeItem('_csrf'); + {{end}} diff --git a/templates/custom/global_mask.tmpl b/templates/custom/global_mask.tmpl new file mode 100644 index 000000000..fd2dfcbe6 --- /dev/null +++ b/templates/custom/global_mask.tmpl @@ -0,0 +1,21 @@ +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        + 任务正在准备中,喝杯水回来再看看~ +
        +
        +
        diff --git a/templates/custom/select_dataset_train.tmpl b/templates/custom/select_dataset_train.tmpl index a6ad0d873..caa8f46f9 100755 --- a/templates/custom/select_dataset_train.tmpl +++ b/templates/custom/select_dataset_train.tmpl @@ -1,19 +1,21 @@
        - {{if or (.benchmarkMode) (.newInference)}} + + + {{if .benchmarkMode}}{{.i18n.Tr "repo.modelarts.infer_job.select_model"}}{{else}}{{.i18n.Tr "dataset.select_dataset"}}{{end}} {{if .benchmarkMode}} - 说明:先使用数据集功能上传模型,然后从数据集列表选模型。 + {{.i18n.Tr "dataset.benchmark_dataset_tip"}} {{end}}
        @@ -47,13 +49,13 @@ 解压中 + data-variation="mini" data-position="left center">{{$.i18n.Tr "dataset.unzip_stared"}} 解压失败 + data-tooltip="{{$.i18n.Tr "dataset.unzip_failed"}}" data-inverted="" + data-variation="mini" data-position="left center">{{$.i18n.Tr "dataset.unzip_failed"}}
        @@ -83,13 +85,13 @@ 解压中 + data-variation="mini" data-position="left center">{{$.i18n.Tr "dataset.unzip_stared"}} 解压失败 + data-variation="mini" data-position="left center">{{$.i18n.Tr "dataset.unzip_failed"}} @@ -118,13 +120,13 @@ 解压中 + data-variation="mini" data-position="left center">{{$.i18n.Tr "dataset.unzip_stared"}} 解压失败 + data-variation="mini" data-position="left center">{{$.i18n.Tr "dataset.unzip_failed"}} @@ -153,13 +155,13 @@ 解压中 + data-variation="mini" data-position="left center">{{$.i18n.Tr "dataset.unzip_stared"}} 解压失败 + data-variation="mini" data-position="left center">{{$.i18n.Tr "dataset.unzip_failed"}} diff --git a/templates/custom/task_wait_count.tmpl b/templates/custom/task_wait_count.tmpl new file mode 100644 index 000000000..fb8ee71fb --- /dev/null +++ b/templates/custom/task_wait_count.tmpl @@ -0,0 +1,25 @@ +
        +
        + + {{.i18n.Tr "repo.wait_count_start"}} {{.WaitCount}} {{.i18n.Tr "repo.wait_count_end"}} +
        +
        + diff --git a/templates/custom/wait_count.tmpl b/templates/custom/wait_count.tmpl new file mode 100644 index 000000000..072d1d40d --- /dev/null +++ b/templates/custom/wait_count.tmpl @@ -0,0 +1,27 @@ +
        + {{$queue := ""}} + {{$gpuQueue := 0}} + {{range $k,$v :=.gpu_types}} + {{if eq $k 0}} + {{ $queue = $v.Queue }} + {{ end }} + {{ end }} + {{ range $k,$v :=.QueuesDetail }} + {{if eq $k $queue}} + {{$gpuQueue =$v}} + {{ end }} + {{ end }} + + {{.i18n.Tr "repo.wait_count_start"}} + {{if .QueuesDetail}} + {{ $gpuQueue }} + {{else}} + {{.WaitCount}} + {{ end }} + {{.i18n.Tr "repo.wait_count_end"}} +
        diff --git a/templates/custom/wait_count_train.tmpl b/templates/custom/wait_count_train.tmpl new file mode 100644 index 000000000..4b7e2dac3 --- /dev/null +++ b/templates/custom/wait_count_train.tmpl @@ -0,0 +1,28 @@ +
        + {{$queue := ""}} + {{$gpuQueue := 0}} + {{range $k,$v :=.type}} + {{if eq $k 0}} + {{ $queue = $v.Queue }} + {{ end }} + {{ end }} + + {{ range $k,$v :=.ctx.QueuesDetail }} + {{if eq $k $queue}} + {{$gpuQueue =$v}} + {{ end }} + {{ end }} + + {{.ctx.i18n.Tr "repo.wait_count_start"}} + {{if .type}} + {{ $gpuQueue }} + {{else}} + {{.ctx.WaitCount}} + {{ end }} + {{.ctx.i18n.Tr "repo.wait_count_end"}} +
        diff --git a/templates/explore/dataset_list.tmpl b/templates/explore/dataset_list.tmpl index 1d22b62ec..a0f171195 100755 --- a/templates/explore/dataset_list.tmpl +++ b/templates/explore/dataset_list.tmpl @@ -24,9 +24,9 @@
        {{range .Datasets}}
        -
        - - {{.Repo.OwnerName}} / {{.Repo.Alias}} +
        + + {{.Title}}
        {{if .Task}} @@ -40,7 +40,7 @@
        {{if .Description}} -

        {{.Description}}

        +

        {{.Description}}

        {{else if .Repo.DescriptionHTML}}

        {{.Repo.DescriptionHTML}}

        {{end}} @@ -53,4 +53,7 @@
        {{end}}
        -
        \ No newline at end of file +
        + \ No newline at end of file diff --git a/templates/home.tmpl b/templates/home.tmpl index 36210d336..c7485a214 100755 --- a/templates/home.tmpl +++ b/templates/home.tmpl @@ -89,22 +89,16 @@
        -

        智算网络

        -

        人工智能算力网络推进联盟已接入10家智算中心,算力总规模1542P

        +

        {{.i18n.Tr "home.c2net_title"}}

        +

        {{.i18n.Tr "home.c2net_desc"}}

        -
        - +
        @@ -129,13 +123,13 @@ -
        +
        -
        +
        @@ -206,22 +200,23 @@
        -
        +

        {{.page_dev_yunlao}}

        {{.page_dev_yunlao_desc1}}
        {{.page_dev_yunlao_desc2}}
        {{.page_dev_yunlao_desc3}}
        - {{.page_dev_yunlao_desc4}}
        + {{.page_dev_yunlao_desc4}}
        + {{.page_dev_yunlao_desc5}}

        {{if .IsSigned}} - {{.page_use}} + {{.page_use}} {{else}} - {{.page_use}} + {{.page_use}} {{end}} - {{.page_dev_yunlao_apply}} + {{.page_dev_yunlao_apply}}
        diff --git a/templates/org/create.tmpl b/templates/org/create.tmpl index 872140efa..21b6ebdb4 100644 --- a/templates/org/create.tmpl +++ b/templates/org/create.tmpl @@ -48,7 +48,7 @@ - {{.i18n.Tr "cancel"}} + {{.i18n.Tr "cancel"}}
        diff --git a/templates/repo/attachment/upload.tmpl b/templates/repo/attachment/upload.tmpl index a78931b13..5161d93a1 100644 --- a/templates/repo/attachment/upload.tmpl +++ b/templates/repo/attachment/upload.tmpl @@ -3,7 +3,7 @@ {{template "repo/header" .}}
        -
        +

        {{$.i18n.Tr "dataset.upload_dataset_file"}}

        @@ -47,14 +47,15 @@ data-uploading='{{.i18n.Tr "dropzone.uploading"}}' data-failed='{{.i18n.Tr "dropzone.failed"}}' data-repopath='{{AppSubUrl}}{{$.RepoLink}}/datasets' data-cancel='{{.i18n.Tr "cancel"}}' - data-upload='{{.i18n.Tr "dataset.dataset_upload"}}'> + data-upload='{{.i18n.Tr "dataset.dataset_upload"}}' + data-upload-status='{{.i18n.Tr "dataset.dataset_upload_status"}}'>
        -
        +
        -
        +
        {{$.i18n.Tr "repo.cloudbrain_task"}} diff --git a/templates/repo/cloudbrain/benchmark/new.tmpl b/templates/repo/cloudbrain/benchmark/new.tmpl index ac0481b5f..18516a765 100755 --- a/templates/repo/cloudbrain/benchmark/new.tmpl +++ b/templates/repo/cloudbrain/benchmark/new.tmpl @@ -10,11 +10,9 @@ padding-left: 3rem !important; } - .min_title { + .min_title{ font-size: 14px !important; - padding-left: 6rem !important; margin-bottom: 2rem !important; - } .width81 { @@ -30,18 +28,7 @@ white-space: nowrap !important; } - -
        -
        -
        -
        -
        -
        -
        -
        -
        +{{template "custom/global_mask" .}}
        {{template "repo/header" .}}
        @@ -53,11 +40,11 @@ {{if eq .benchmarkMode "model"}} -
        + {{.CsrfTokenHtml}} -
        - +
        +
        -
        - +
        + + {{template "custom/task_wait_count" .}} +
        +
        + - {{.i18n.Tr "cloudbrain.job_name_rule"}} + tabindex="3" autofocus required maxlength="36"> + {{.i18n.Tr "repo.cloudbrain_jobname_err"}}
        -
        -
        -
        - + +
        - +   + + - {{if .CloudBrainPaySwitch}} -
        - {{$.i18n.Tr "points.balance_of_points"}}{{.PointAccount.Balance}}{{$.i18n.Tr "points.points"}}{{$.i18n.Tr "points.expected_time"}}{{$.i18n.Tr "points.hours"}} - - - {{$.i18n.Tr "points.points_acquisition_instructions"}} - - {{end}} -
        +
        --> +
        + +
        -
        +
        + @@ -157,12 +144,12 @@
        {{else}} -
        + {{.CsrfTokenHtml}} -
        - +
        +
        -
        - +
        + + {{template "custom/task_wait_count" .}} +
        +
        + - {{.i18n.Tr "cloudbrain.job_name_rule"}} + tabindex="3" autofocus required maxlength="36"> + {{.i18n.Tr "repo.cloudbrain_jobname_err"}}
        -
        - +
        +
        -
        - + +
         
        - -   + +
        - - - + +
        -
        - + + +
        + +
        -
        - +
        + {{.i18n.Tr "repo.cloudbrain.benchmark.evaluate_train"}}
        -
        - +
        + + @@ -284,8 +266,14 @@
        {{template "base/footer" .}} - + \ No newline at end of file diff --git a/templates/repo/cloudbrain/benchmark/show.tmpl b/templates/repo/cloudbrain/benchmark/show.tmpl index 59ce3c471..1ba2d5890 100755 --- a/templates/repo/cloudbrain/benchmark/show.tmpl +++ b/templates/repo/cloudbrain/benchmark/show.tmpl @@ -232,12 +232,7 @@
        - {{if not (eq .StartTime 0)}} -
        - {{else}} - {{$.i18n.Tr "repo.modelarts.status"}}: @@ -358,7 +353,15 @@ @@ -450,7 +453,7 @@ {{$.i18n.Tr "cloudbrain.gpu_type"}} - - @@ -568,7 +571,22 @@ {{template "base/footer" .}} - + + \ No newline at end of file diff --git a/templates/repo/cloudbrain/inference/new.tmpl b/templates/repo/cloudbrain/inference/new.tmpl new file mode 100644 index 000000000..c0facf8b4 --- /dev/null +++ b/templates/repo/cloudbrain/inference/new.tmpl @@ -0,0 +1,530 @@ +{{template "base/head" .}} + +{{template "custom/global_mask" .}} +
        + {{template "repo/header" .}} +
        + {{template "base/alert" .}} + +

        + {{.i18n.Tr "repo.modelarts.train_job.new_infer"}} +

        +
        + + + {{.CsrfTokenHtml}} + + + + + + + + + + + +

        {{.i18n.Tr "repo.modelarts.train_job.basic_info"}}:

        +
        +
        + + {{template "custom/task_wait_count" .}} +
        + + {{.i18n.Tr "cloudbrain.new_infer_gpu_tooltips" "/dataset" "/model" "/result" | Safe}} +
        +
        +
        + + + {{.i18n.Tr "repo.cloudbrain_jobname_err"}} +
        + +
        + + {{if .description}} + + {{else}} + + {{end}} +
        +
        + + +

        {{.i18n.Tr "repo.modelarts.train_job.parameter_setting"}}:

        +
        +
        + + +
        +
        + +
        +
        + + +
        + + + + +
        + +
        + +
        + +
        + + +
        + + + +
        + +
        + +
        + + {{if .boot_file}} + + {{else}} + + {{end}} + + + + {{.i18n.Tr "cloudbrain.view_sample"}} +
        + + +
        + + {{.i18n.Tr "repo.modelarts.train_job.add_run_parameter"}} + +
        + +
        +
        + + + + +
        + + +
        + +
        + + + {{.i18n.Tr "repo.cloudbrain.cancel"}} +
        + + +
        +
        +
        +{{template "base/footer" .}} + + diff --git a/templates/repo/cloudbrain/inference/show.tmpl b/templates/repo/cloudbrain/inference/show.tmpl new file mode 100644 index 000000000..1d19627dd --- /dev/null +++ b/templates/repo/cloudbrain/inference/show.tmpl @@ -0,0 +1,632 @@ +{{template "base/head" .}} + +
        +
        +
        +
        +
        +
        +
        +
        +
        +
        + {{template "repo/header" .}} +
        +

        + +

        + {{with .task}} +
        + +
        +
        +
        + + + +
        + {{TimeSinceUnix1 .CreatedUnix}} + + {{$.i18n.Tr "repo.modelarts.status"}}: + {{.Status}} + + {{$.i18n.Tr "repo.modelarts.train_job.dura_time"}}: + {{$.duration}} + + +
        +
        +
        +
        +
        +
        +
        {{.ID}}{{.Title}}{{if .Recommend}}{{end}}{{.Title}}{{if .Recommend}}{{end}} {{.CreatedUnix.FormatShort}} {{if .Recommend}}{{$.i18n.Tr "admin.datasets.unrecommend"}}{{else}}{{$.i18n.Tr "admin.datasets.recommend"}}{{end}}{{TimeSinceUnix1 .StartTime}}{{TimeSinceUnix1 .CreatedUnix}} - - {{end}} + {{TimeSinceUnix1 .CreatedUnix}}
        - {{.Image}} + + {{.Image}} +
        +
        {{$.resource_type}}
        @@ -461,9 +464,9 @@ {{$.i18n.Tr "repo.modelarts.train_job.standard"}}
        +
        - {{$.resource_spec}} + {{$.resource_spec}}
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        + {{$.i18n.Tr "repo.cloudbrain_task"}} + +
        + {{.DisplayJobName}} +
        +
        + {{$.i18n.Tr "repo.modelarts.status"}} + +
        + {{.Status}} +
        +
        + {{$.i18n.Tr "repo.modelarts.train_job.start_time"}} + +
        + + {{if not (eq .StartTime 0)}} + {{TimeSinceUnix1 .StartTime}} + {{else}} + -- + {{end}} + +
        +
        + {{$.i18n.Tr "repo.modelarts.train_job.dura_time"}} + +
        + {{.TrainJobDuration}} +
        +
        + {{$.i18n.Tr "repo.modelarts.train_job.resource_type"}} + +
        + {{$.resource_type}} +
        +
        + {{$.i18n.Tr "repo.model.manage.description"}} + +
        + {{if .Description}} + {{.Description}} + {{else}} + -- + {{end}} +
        +
        + 创建人 + +
        + {{.User.Name}} +
        +
        + {{$.i18n.Tr "cloudbrain.mirror"}} + +
        + + {{.Image}} + +
        +
        +
        +
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        + {{$.i18n.Tr "repo.modelarts.infer_job_model"}} + +
        + {{.ModelName}}   + {{$.i18n.Tr "repo.modelarts.version"}}:{{.ModelVersion}}   + +
        +
        + {{$.i18n.Tr "repo.modelarts.infer_job_model_file"}} + +
        + {{.CkptName}} +
        +
        + {{$.i18n.Tr "repo.modelarts.model_label"}} + +
        + {{if .LabelName}} + {{range $.LabelName}} + {{.}} + {{end}} + {{else}} + -- + {{end}} +
        +
        + {{$.i18n.Tr "repo.modelarts.code_version"}} + +
        + {{.BranchName}} +
        +
        + {{$.i18n.Tr "repo.modelarts.train_job.start_file"}} + +
        + {{.BootFile}} +
        +
        + {{$.i18n.Tr "repo.modelarts.train_job.run_parameter"}} + +
        + {{if .Parameters}} + {{.Parameters}} + {{else}} + -- + {{end}} +
        +
        + {{$.i18n.Tr "repo.modelarts.train_job.standard"}} + +
        + {{$.i18n.Tr "cloudbrain.gpu_num"}}:{{$.GpuNum}},{{$.i18n.Tr "cloudbrain.cpu_num"}}:{{$.CpuNum}},{{$.i18n.Tr "cloudbrain.memory"}}(MB):{{$.MemMiB}},{{$.i18n.Tr "cloudbrain.shared_memory"}}(MB):{{$.ShareMemMiB}} +
        +
        +
        + +
        + + + + + + {{range $m ,$n := $.datasetDownload}} + + + + + {{end}} + + +
        {{$.i18n.Tr "dataset.file"}}
        {{.DatasetName}}
        +
        + + + + + +
        +
        + +
        + + + + + +
        + +
        + +
        + + + +
        + + + +
        + +
        +
        + + + + + + {{end}} + + + +{{template "base/footer" .}} + + + \ No newline at end of file diff --git a/templates/repo/cloudbrain/new.tmpl b/templates/repo/cloudbrain/new.tmpl index c3fccbf34..cac971eaf 100755 --- a/templates/repo/cloudbrain/new.tmpl +++ b/templates/repo/cloudbrain/new.tmpl @@ -1,91 +1,7 @@ {{template "base/head" .}} -
        -
        -
        -
        -
        -
        -
        -
        -
        +{{template "custom/global_mask" .}}
        {{template "repo/header" .}} -
        - - {{template "base/alert" .}} -

        - {{.i18n.Tr "repo.cloudbrain.new"}} -

        - -
        +
        +
        + + {{template "base/alert" .}} +
        {{.CsrfTokenHtml}} -
        - - +
        {{template "base/footer" .}} + \ No newline at end of file diff --git a/templates/repo/cloudbrain/show.tmpl b/templates/repo/cloudbrain/show.tmpl index 9ae3b9445..8096e1e8a 100755 --- a/templates/repo/cloudbrain/show.tmpl +++ b/templates/repo/cloudbrain/show.tmpl @@ -240,12 +240,7 @@
        - {{if not (eq .StartTime 0)}} - {{TimeSinceUnix1 .StartTime}} - {{else}} - {{TimeSinceUnix1 .CreatedUnix}} - - {{end}} + {{TimeSinceUnix1 .CreatedUnix}} {{$.i18n.Tr "repo.modelarts.status"}}: @@ -257,9 +252,9 @@ {{$.duration}} - + + +
        @@ -350,7 +345,7 @@ {{$.i18n.Tr "cloudbrain.gpu_type"}} - +
        {{$.resource_type}}
        @@ -369,18 +364,7 @@ - - - {{$.i18n.Tr "repo.modelarts.train_job.dura_time"}} - - - -
        - {{$.duration}} -
        - - + @@ -397,32 +381,27 @@
        - {{.Image}} -
        - - - - - {{$.i18n.Tr "repo.modelarts.train_job.dataset"}} - - - -
        - {{.DatasetName}} + + {{.Image}} +
        + {{$.i18n.Tr "repo.modelarts.train_job.standard"}} - -
        - {{$.i18n.Tr "cloudbrain.gpu_num"}}:{{$.GpuNum}},{{$.i18n.Tr "cloudbrain.cpu_num"}}:{{$.CpuNum}},{{$.i18n.Tr "cloudbrain.memory"}}(MB):{{$.MemMiB}},{{$.i18n.Tr "cloudbrain.shared_memory"}}(MB):{{$.ShareMemMiB}} -
        + +
        @@ -491,14 +470,40 @@
        + + + {{$.i18n.Tr "repo.modelarts.train_job.dura_time"}} + + +
        + {{$.duration}} +
        + +
        - +
        + + + + + + {{range $m ,$n := $.datasetDownload}} + + + + + {{end}} + + +
        {{$.i18n.Tr "dataset.file"}}
        {{.DatasetName}}
        +
        @@ -547,7 +552,7 @@
        {{template "base/footer" .}} - + \ No newline at end of file diff --git a/templates/repo/cloudbrain/trainjob/new.tmpl b/templates/repo/cloudbrain/trainjob/new.tmpl index de7ca766c..9d680cd70 100755 --- a/templates/repo/cloudbrain/trainjob/new.tmpl +++ b/templates/repo/cloudbrain/trainjob/new.tmpl @@ -14,7 +14,9 @@ .width { width: 100% !important; } - + .width48 { + width: 48.5% !important; + } .width80 { width: 80.7% !important; margin-left: 10px; @@ -25,16 +27,12 @@ margin-left: -2px; } - .width485 { - width: 48.5% !important; - } - .width85 { width: 85% !important; margin-left: 10.5rem !important; align-items: center; } - + .width81 { margin-left: 1.5rem !important; width: 81% !important; @@ -68,33 +66,21 @@ } - -
        -
        -
        -
        -
        -
        -
        -
        -
        +{{template "custom/global_mask" .}}
        {{template "repo/header" .}}
        - + {{template "base/alert" .}}

        {{.i18n.Tr "repo.modelarts.train_job.new"}}

        -
        + {{.CsrfTokenHtml}} - - +

        {{.i18n.Tr "repo.modelarts.train_job.basic_info"}}:

        @@ -131,23 +117,31 @@ Ascend NPU
        +
        + + {{template "custom/task_wait_count" .}} +
        + + {{.i18n.Tr "cloudbrain.new_train_gpu_tooltips" "/code" "/dataset" "/model" | Safe}} +
        +
        - {{.i18n.Tr "cloudbrain.job_name_rule"}} + maxlength="36"> + {{.i18n.Tr "repo.cloudbrain_jobname_err"}}
        - + {{if .description}} + + {{else}} + + {{end}}
        @@ -182,24 +176,36 @@
        - -
        +
        - {{if .bootFile}} - {{else}} - 查看样例 + {{.i18n.Tr "cloudbrain.view_sample"}}
        - - - - - 训练脚本存储在/code中,数据集存储在/dataset中,训练输出请存储在/model中以供后续下载。 +
        {{.i18n.Tr "repo.modelarts.train_job.add_run_parameter"}} -
        - {{if .params}} - {{if ne 0 (len .params)}} - {{range $k ,$v := .params}} -
        -
        - -
        -
        - -
        - - - - -
        - {{end}} - {{end}} - {{end}} +
        +
        - -
        + + +
        + +
        @@ -289,10 +279,14 @@
        {{template "base/footer" .}} - + \ No newline at end of file + diff --git a/templates/repo/cloudbrain/trainjob/show.tmpl b/templates/repo/cloudbrain/trainjob/show.tmpl old mode 100755 new mode 100644 index 05d678016..1dba0b7f0 --- a/templates/repo/cloudbrain/trainjob/show.tmpl +++ b/templates/repo/cloudbrain/trainjob/show.tmpl @@ -1,4 +1,5 @@ {{template "base/head" .}} +
        @@ -236,6 +250,16 @@ +
        + {{if and ($.canDownload) (eq .Status "SUCCEEDED") ($.Permission.CanWrite $.UnitTypeModelManage) }} + {{$.i18n.Tr "repo.modelarts.create_model"}} + {{else}} + {{$.i18n.Tr "repo.modelarts.create_model"}} + {{end}} + +
        {{TimeSinceUnix1 .CreatedUnix}} @@ -248,6 +272,8 @@ class="cti-mgRight-sm">{{$.i18n.Tr "repo.modelarts.train_job.dura_time"}}: {{$.duration}} +
        @@ -260,12 +286,15 @@
        @@ -303,8 +332,13 @@
        - {{TimeSinceUnix1 .CreatedUnix}} + + {{if not (eq .StartTime 0)}} + {{TimeSinceUnix1 .StartTime}} + {{else}} + -- + {{end}} +
        @@ -325,7 +359,7 @@ {{$.i18n.Tr "repo.modelarts.train_job.resource_type"}} - +
        {{$.resource_type}}
        @@ -336,7 +370,7 @@ {{$.i18n.Tr "repo.modelarts.train_job.standard"}} - +
        {{$.i18n.Tr "cloudbrain.gpu_num"}}:{{$.GpuNum}},{{$.i18n.Tr "cloudbrain.cpu_num"}}:{{$.CpuNum}},{{$.i18n.Tr "cloudbrain.memory"}}(MB):{{$.MemMiB}},{{$.i18n.Tr "cloudbrain.shared_memory"}}(MB):{{$.ShareMemMiB}}
        @@ -356,7 +390,15 @@
        - {{.Image}} + + {{.Image}} +
        @@ -385,17 +427,7 @@ - - - {{$.i18n.Tr "repo.modelarts.train_job.train_dataset"}} - - - -
        - {{.DatasetName}} -
        - - + @@ -424,6 +456,22 @@
        +
        + + + + + + {{range $m ,$n := $.datasetDownload}} + + + + + {{end}} + + +
        {{$.i18n.Tr "dataset.file"}}
        {{.DatasetName}}
        +
        @@ -434,7 +482,7 @@ -
        @@ -448,18 +496,41 @@
        - @@ -467,7 +538,7 @@ @@ -505,12 +576,299 @@
        + + +
        + +
        {{template "base/footer" .}} + + + \ No newline at end of file diff --git a/templates/repo/create.tmpl b/templates/repo/create.tmpl index 773f398f2..4d51b0120 100644 --- a/templates/repo/create.tmpl +++ b/templates/repo/create.tmpl @@ -80,6 +80,15 @@ +
        +
        + + +
        +
        @@ -152,7 +161,7 @@
        diff --git a/templates/repo/datasets/index.tmpl b/templates/repo/datasets/index.tmpl index 22ee1f4c6..405758f50 100755 --- a/templates/repo/datasets/index.tmpl +++ b/templates/repo/datasets/index.tmpl @@ -20,7 +20,6 @@ .wrapper { display: flex; overflow: hidden; - padding: 0 1rem; } .exp { @@ -40,7 +39,8 @@ } .exp:checked+.text .btn::after { - content:'{{$.i18n.Tr "org.fold"}}' + content:'{{$.i18n.Tr "org.fold"}}'; + color: #3291f8; } .wrapper>.text { @@ -80,7 +80,7 @@ margin-left: 20px; font-size: 14px; padding: 0 8px; - background: #3F51B5; + background-color: transparent; line-height: 20px; border-radius: 4px; color: #fff; @@ -89,7 +89,8 @@ } .btn::after { - content:'{{$.i18n.Tr "org.unfold"}}' + content:'{{$.i18n.Tr "org.unfold"}}'; + color: #3291f8; } .btn::before { @@ -132,6 +133,15 @@ border: 5px solid transparent; border-top-color: #c0c4cc; } + .dataset-flavor-button{ + display: flex; + align-items: center; + padding: 0.5rem; + border: 1px solid rgba(34,36,38,0.15); + border-top-right-radius: 0; + border-bottom-right-radius: 0; + box-shadow: none + }
        {{template "repo/header" .}} @@ -142,44 +152,59 @@
        {{end}}
        -
        -
        -

        {{.dataset.Title}}

        + -
        - - {{if $.IsSigned}} -
        - - - - -
        - ${num_stars} - {{else}} -
        - - - - -
        - ${num_stars} - {{end}} - {{.i18n.Tr "repo.modelarts.modify"}} -
        +
        +
        +

        {{.dataset.Title}}

        + +
        + {{if $.IsSigned}} + + + {{else}} + + + {{end}}
        + + ${num_stars} + + {{.i18n.Tr "repo.modelarts.modify"}} +
        + {{if .dataset.Description}} +
        +
        + +
        + + {{.dataset.Description}} +
        +
        +
        + {{end}} +
        {{if or (.dataset.Category) (.dataset.Task) (.dataset.License)}} -
        +
        {{if .dataset.Category}} {{$category := .dataset.Category}} {{end}} -
        - {{if .dataset.Description}} -
        -
        - -
        - - {{.dataset.Description}} -
        -
        -
        - {{end}} -
        -
        -
        +
        @@ -222,7 +233,7 @@ @click="gotoUpload('{{.RepoLink}}',{{.dataset.ID}})">{{$.i18n.Tr "dataset.dataset_upload"}}
        -
        +
        @@ -387,16 +398,27 @@
        {{else}} -
        -
        -
        {{.i18n.Tr "dataset.dataset_no_create"}}
        - {{if $.CanWrite}} -
        {{.i18n.Tr "dataset.create_new_dataset"}} - {{end}} -
        -
        {{.i18n.Tr "dataset.dataset_explain"}}
        -
        {{.i18n.Tr "dataset.dataset_instructions_for_use"}}{{.i18n.Tr "dataset.dataset_camp_course"}}
        +
        + + +
        +
        +
        {{.i18n.Tr "dataset.dataset_no_create"}}
        +
        +
        {{.i18n.Tr "dataset.dataset_explain"}}
        +
        {{.i18n.Tr "dataset.dataset_instructions_for_use"}}{{.i18n.Tr "dataset.dataset_camp_course"}}
        +
        {{end}} diff --git a/templates/repo/datasets/reference.tmpl b/templates/repo/datasets/reference.tmpl new file mode 100644 index 000000000..422a2ff8f --- /dev/null +++ b/templates/repo/datasets/reference.tmpl @@ -0,0 +1,8 @@ +{{template "base/head" .}} + +
        + {{template "repo/header" .}} +
        +
        +
        +{{template "base/footer" .}} diff --git a/templates/repo/debugjob/index.tmpl b/templates/repo/debugjob/index.tmpl index 37961cf36..912a615c2 100755 --- a/templates/repo/debugjob/index.tmpl +++ b/templates/repo/debugjob/index.tmpl @@ -19,90 +19,6 @@ height: 100%; } - /* 弹窗 */ - - #mask { - position: fixed; - top: 0px; - left: 0px; - right: 0px; - bottom: 0px; - filter: alpha(opacity=60); - background-color: #777; - z-index: 1000; - display: none; - opacity: 0.8; - -moz-opacity: 0.5; - padding-top: 100px; - color: #000000 - } - - #loadingPage { - margin: 200px auto; - width: 50px; - height: 40px; - text-align: center; - font-size: 10px; - display: block; - } - - #loadingPage>div { - background-color: green; - height: 100%; - width: 6px; - display: inline-block; - -webkit-animation: sk-stretchdelay 1.2s infinite ease-in-out; - animation: sk-stretchdelay 1.2s infinite ease-in-out; - } - - #loadingPage .rect2 { - -webkit-animation-delay: -1.1s; - animation-delay: -1.1s; - } - - #loadingPage .rect3 { - -webkit-animation-delay: -1.0s; - animation-delay: -1.0s; - } - - #loadingPage .rect4 { - -webkit-animation-delay: -0.9s; - animation-delay: -0.9s; - } - - #loadingPage .rect5 { - -webkit-animation-delay: -0.8s; - animation-delay: -0.8s; - } - - @-webkit-keyframes sk-stretchdelay { - - 0%, - 40%, - 100% { - -webkit-transform: scaleY(0.4) - } - - 20% { - -webkit-transform: scaleY(1.0) - } - } - - @keyframes sk-stretchdelay { - - 0%, - 40%, - 100% { - transform: scaleY(0.4); - -webkit-transform: scaleY(0.4); - } - - 20% { - transform: scaleY(1.0); - -webkit-transform: scaleY(1.0); - } - } - /* 消息框 */ .alert { @@ -203,17 +119,6 @@ } - -
        -
        -
        -
        -
        -
        -
        -
        -
        -
        {{template "repo/header" .}} @@ -230,7 +135,7 @@ {{$.i18n.Tr "repo.modelarts.notebook"}} {{$.i18n.Tr "repo.modelarts.train_job"}} + href="{{.RepoLink}}/modelarts/train-job?listType=all">{{$.i18n.Tr "repo.modelarts.train_job"}} {{$.i18n.Tr "repo.modelarts.infer_job"}} {{.ComputeResource}} + class=""> + {{.ComputeResource}}
        +
        {{template "base/footer" .}} \ No newline at end of file + + ;(function() { + var SPECS = {{ .Specs }}; + var showPoint = true; + renderSpecsSelect($('#__specs__'), SPECS, showPoint, { + gpu_memory: {{$.i18n.Tr "cloudbrain.gpu_memory"}}, + free: {{$.i18n.Tr "cloudbrain.free"}}, + point_hr: {{$.i18n.Tr "cloudbrain.point_hr"}}, + memory: {{$.i18n.Tr "cloudbrain.memory"}}, + shared_memory: {{$.i18n.Tr "cloudbrain.shared_memory"}}, + }); + })(); + diff --git a/templates/repo/grampus/trainjob/npu/new.tmpl b/templates/repo/grampus/trainjob/npu/new.tmpl index d318777a4..6849528dc 100755 --- a/templates/repo/grampus/trainjob/npu/new.tmpl +++ b/templates/repo/grampus/trainjob/npu/new.tmpl @@ -25,7 +25,9 @@ .width81{ width: 81% !important; } - +.width48 { + width: 48.5% !important; +} .add{font-size: 18px; padding: 0.5rem; border: 1px solid rgba(187, 187, 187, 100); @@ -51,18 +53,7 @@ font-size: 14px !important; } - -
        -
        -
        -
        -
        -
        -
        -
        -
        +{{template "custom/global_mask" .}}
        {{template "repo/header" .}}
        @@ -72,11 +63,13 @@ +
        + + {{template "custom/task_wait_count" .}} +
        + + {{.i18n.Tr "cloudbrain.new_train_gpu_tooltips" "/cache/code" "/cache/dataset" "/cache/output" | Safe}} +
        +
        - - {{.i18n.Tr "cloudbrain.job_name_rule"}} + + {{.i18n.Tr "repo.cloudbrain_jobname_err"}}
        + {{if .description}} + + {{else}} + {{end}}
        @@ -144,20 +149,32 @@ {{end}}
        -
        - {{if .bootFile}} - + {{if .boot_file}} + {{else}} {{end}} @@ -166,24 +183,42 @@ {{.i18n.Tr "cloudbrain.view_sample"}}
        - + {{template "custom/select_dataset_train" .}} - {{.i18n.Tr "repo.grampus.dataset_path_rule"}} +
        {{.i18n.Tr "repo.modelarts.train_job.add_run_parameter"}} -
        +
        +
        -
        + +
        + +
        @@ -215,135 +250,75 @@
        {{template "base/footer" .}} + diff --git a/templates/repo/grampus/trainjob/show.tmpl b/templates/repo/grampus/trainjob/show.tmpl index 579e83693..6e9e0d6dc 100755 --- a/templates/repo/grampus/trainjob/show.tmpl +++ b/templates/repo/grampus/trainjob/show.tmpl @@ -1,4 +1,5 @@ {{template "base/head" .}} + -
        -
        -
        -
        -
        -
        -
        -
        -
        +{{template "custom/global_mask" .}}
        {{template "repo/header" .}}
        @@ -222,7 +228,7 @@ {{range $k ,$v := .version_list_task}}
        @@ -234,14 +240,10 @@ {{$.CsrfTokenHtml}}
        +
        - - {{if not (eq .StartTime 0)}} - {{TimeSinceUnix1 .StartTime}} - {{else}} - {{TimeSinceUnix1 .CreatedUnix}} - {{end}} + {{TimeSinceUnix1 .CreatedUnix}} {{$.i18n.Tr "repo.modelarts.current_version"}}:{{.VersionName}} @@ -255,9 +257,19 @@ class="cti-mgRight-sm">{{$.i18n.Tr "repo.modelarts.train_job.dura_time"}}: {{.TrainJobDuration}} - + + + + +
        +
        + {{if and ($.canDownload) (eq .Status "SUCCEEDED") ($.Permission.CanWrite $.UnitTypeModelManage) }} + {{$.i18n.Tr "repo.modelarts.create_model"}} + {{else}} + {{$.i18n.Tr "repo.modelarts.create_model"}} + {{end}}
        @@ -269,12 +281,9 @@
        @@ -326,7 +335,7 @@ {{if not (eq .StartTime 0)}} {{TimeSinceUnix1 .StartTime}} {{else}} - {{TimeSinceUnix1 .CreatedUnix}} + -- {{end}}
        @@ -348,7 +357,7 @@ {{$.i18n.Tr "repo.modelarts.train_job.standard"}} - +
        {{.FlavorName}}
        @@ -409,7 +418,9 @@
        - {{.DatasetName}} + {{range $m ,$n := $.datasetDownload}} + {{.DatasetName}} + {{end}}
        @@ -458,11 +469,23 @@
        + + + + + + -
        +
        +
        +
        
        @@ -480,8 +503,13 @@
         
                                 
        -
        + {{if eq .ComputeResource "CPU/GPU"}} +
        + + {{$.i18n.Tr "repo.file_limit_100"}} +
        + {{end}}
        @@ -510,10 +538,10 @@
        -
        +