Browse Source

Merge remote-tracking branch 'origin/res-manage-v2' into point-v2

# Conflicts:
#	models/models.go
#	modules/cloudbrain/resty.go
#	modules/cron/tasks_basic.go
#	modules/notification/base/notifier.go
#	modules/notification/base/null.go
#	modules/notification/notification.go
#	modules/setting/setting.go
#	options/locale/locale_en-US.ini
#	options/locale/locale_zh-CN.ini
#	package-lock.json
#	routers/repo/cloudbrain.go
#	routers/repo/modelarts.go
#	templates/repo/cloudbrain/benchmark/new.tmpl
#	templates/repo/cloudbrain/new.tmpl
#	templates/repo/cloudbrain/trainjob/new.tmpl
#	templates/repo/modelarts/inferencejob/new.tmpl
#	templates/repo/modelarts/notebook/new.tmpl
#	templates/repo/modelarts/trainjob/new.tmpl
#	web_src/vuepages/components/BaseDialog.vue
#	web_src/vuepages/const/index.js
#	web_src/vuepages/langs/config/en-US.js
#	web_src/vuepages/langs/config/zh-CN.js
#	webpack_pro.config.js
tags/v1.22.9.2^2
chenyifan01 3 years ago
parent
commit
b3f02877f1
100 changed files with 16434 additions and 798 deletions
  1. +1
    -0
      .gitignore
  2. +4
    -1
      README.md
  3. +20
    -44
      custom/public/rotation3D/rotation3D.css
  4. +5
    -3
      go.mod
  5. +16
    -0
      go.sum
  6. +688
    -0
      index.html
  7. +49
    -0
      models/admin_operate_log.go
  8. +252
    -3
      models/ai_model_manage.go
  9. +433
    -20
      models/cloudbrain.go
  10. +4
    -0
      models/cloudbrain_image.go
  11. +109
    -0
      models/cloudbrain_spec.go
  12. +6
    -127
      models/cloudbrain_static.go
  13. +68
    -0
      models/cloudbrain_temp.go
  14. +28
    -13
      models/dataset.go
  15. +88
    -0
      models/dataset_reference.go
  16. +14
    -10
      models/dbsql/dataset_foreigntable_for_es.sql
  17. +1
    -1
      models/dbsql/repo_foreigntable_for_es.sql
  18. +9
    -0
      models/models.go
  19. +8
    -0
      models/org.go
  20. +20
    -8
      models/repo.go
  21. +39
    -0
      models/repo_activity_custom.go
  22. +349
    -0
      models/resource_queue.go
  23. +329
    -0
      models/resource_scene.go
  24. +550
    -0
      models/resource_specification.go
  25. +7
    -5
      models/user.go
  26. +451
    -0
      models/user_analysis_for_activity.go
  27. +72
    -15
      models/user_business_analysis.go
  28. +14
    -0
      models/user_business_struct.go
  29. +30
    -0
      modules/auth/cloudbrain.go
  30. +8
    -0
      modules/auth/dataset.go
  31. +2
    -2
      modules/auth/grampus.go
  32. +3
    -0
      modules/auth/modelarts.go
  33. +47
    -0
      modules/auth/wechat/client.go
  34. +167
    -0
      modules/auth/wechat/cloudbrain.go
  35. +63
    -0
      modules/auth/wechat/template.go
  36. +142
    -99
      modules/cloudbrain/cloudbrain.go
  37. +57
    -28
      modules/cloudbrain/resty.go
  38. +2
    -1
      modules/context/context.go
  39. +43
    -0
      modules/context/permission_json.go
  40. +26
    -0
      modules/cron/tasks_basic.go
  41. +18
    -0
      modules/dataset/dataset.go
  42. +1
    -1
      modules/git/repo_stats_custom.go
  43. +8
    -12
      modules/grampus/grampus.go
  44. +33
    -0
      modules/grampus/resty.go
  45. +41
    -6
      modules/markup/markdown/markdown.go
  46. +685
    -86
      modules/modelarts/modelarts.go
  47. +276
    -11
      modules/modelarts/resty.go
  48. +2
    -0
      modules/notification/base/notifier.go
  49. +4
    -0
      modules/notification/base/null.go
  50. +9
    -0
      modules/notification/notification.go
  51. +36
    -0
      modules/notification/wechat/wechat.go
  52. +2
    -0
      modules/setting/cloudbrain.go
  53. +5
    -2
      modules/setting/repository.go
  54. +118
    -28
      modules/setting/setting.go
  55. +57
    -6
      modules/storage/minio_ext.go
  56. +57
    -8
      modules/storage/obs.go
  57. +27
    -24
      modules/templates/helper.go
  58. +124
    -22
      options/locale/locale_en-US.ini
  59. +118
    -11
      options/locale/locale_zh-CN.ini
  60. +3
    -0
      package-lock.json
  61. +0
    -24
      public/home/home.js
  62. +10
    -2
      public/self/css/notebook/notebook.css
  63. +12
    -12
      public/self/dataset_preview.js
  64. +386
    -0
      public/self/ztree/css/awesomeStyle/awesome.css
  65. +146
    -0
      public/self/ztree/css/awesomeStyle/awesome.less
  66. +480
    -0
      public/self/ztree/css/awesomeStyle/fa.less
  67. BIN
      public/self/ztree/css/awesomeStyle/img/loading.gif
  68. +33
    -0
      public/self/ztree/css/demo.css
  69. BIN
      public/self/ztree/css/metroStyle/img/line_conn.png
  70. BIN
      public/self/ztree/css/metroStyle/img/loading.gif
  71. BIN
      public/self/ztree/css/metroStyle/img/metro.gif
  72. BIN
      public/self/ztree/css/metroStyle/img/metro.png
  73. +96
    -0
      public/self/ztree/css/metroStyle/metroStyle.css
  74. BIN
      public/self/ztree/css/zTreeStyle/img/diy/1_close.png
  75. BIN
      public/self/ztree/css/zTreeStyle/img/diy/1_open.png
  76. BIN
      public/self/ztree/css/zTreeStyle/img/diy/2.png
  77. BIN
      public/self/ztree/css/zTreeStyle/img/diy/3.png
  78. BIN
      public/self/ztree/css/zTreeStyle/img/diy/4.png
  79. BIN
      public/self/ztree/css/zTreeStyle/img/diy/5.png
  80. BIN
      public/self/ztree/css/zTreeStyle/img/diy/6.png
  81. BIN
      public/self/ztree/css/zTreeStyle/img/diy/7.png
  82. BIN
      public/self/ztree/css/zTreeStyle/img/diy/8.png
  83. BIN
      public/self/ztree/css/zTreeStyle/img/diy/9.png
  84. BIN
      public/self/ztree/css/zTreeStyle/img/line_conn.gif
  85. BIN
      public/self/ztree/css/zTreeStyle/img/loading.gif
  86. BIN
      public/self/ztree/css/zTreeStyle/img/zTreeStandard.gif
  87. BIN
      public/self/ztree/css/zTreeStyle/img/zTreeStandard.png
  88. +99
    -0
      public/self/ztree/css/zTreeStyle/zTreeStyle.css
  89. +167
    -0
      public/self/ztree/js/jquery-1.4.4.min.js
  90. +3878
    -0
      public/self/ztree/js/jquery.ztree.all.js
  91. +2019
    -0
      public/self/ztree/js/jquery.ztree.core.js
  92. +652
    -0
      public/self/ztree/js/jquery.ztree.excheck.js
  93. +1207
    -0
      public/self/ztree/js/jquery.ztree.exedit.js
  94. +405
    -0
      public/self/ztree/js/jquery.ztree.exhide.js
  95. +26
    -3
      routers/admin/cloudbrains.go
  96. +284
    -0
      routers/admin/resources.go
  97. +18
    -0
      routers/api/v1/api.go
  98. +523
    -14
      routers/api/v1/repo/cloudbrain.go
  99. +44
    -61
      routers/api/v1/repo/cloudbrain_dashboard.go
  100. +101
    -85
      routers/api/v1/repo/modelarts.go

+ 1
- 0
.gitignore View File

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


+ 4
- 1
README.md View File

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

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

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

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

.rotation3D__item.blue{ color: #01e9fc; }
.rotation3D__item.green{ color: #b4b3ca; }
.rotation3D__item.yellow{ color: #ffd200; }
@@ -90,14 +61,17 @@
---------------------------*/
.rotation3D__line{
position: absolute; left: 50%; top: 50%;
display: block; width: 1px; height: 50%;
display: block;
width: 30px;
height: 50%;
padding-top: 60px; color: #fff; font-size: 50px;
/*background: #fff;*/
/*原点设置在中间*/
transform-origin: 50% 0;
transform-style: preserve-3d;
}
.rotation3D__line .pos{ position: absolute; top: 0; }
overflow: hidden;
}
.rotation3D__line .pos{ position: absolute; top: 0; left: 15px;}
.rotation3D__line svg { position: absolute; top: 0; }
.rotation3D__line svg path {
stroke: #fff; fill: none;
@@ -139,8 +113,10 @@
position: absolute;
font-size: 12px;
color: #888;
transform: rotate(180deg)scale(0.80);
}
transform:scale(0.80);
transform-origin:left;
white-space: nowrap;
}

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


+ 5
- 3
go.mod View File

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


+ 16
- 0
go.sum View File

@@ -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=


+ 688
- 0
index.html View File

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

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

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

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

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

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

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



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

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

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

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



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

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

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

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

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


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


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

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




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


</div>


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



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

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

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

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

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

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

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

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

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

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





</div>


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

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

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


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










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

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


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


</body>
</html>


+ 49
- 0
models/admin_operate_log.go View File

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

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

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

type LogValues struct {
Params []LogValue
}

type LogValue struct {
Key string
Val interface{}
}

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

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

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

+ 252
- 3
models/ai_model_manage.go View File

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

import (
"fmt"
"time"

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

}

func ModifyModelDescription(id string, description string) error {
@@ -201,3 +380,73 @@ func QueryModel(opts *AiModelQueryOptions) ([]*AiModelManage, int64, error) {

return aiModelManages, count, nil
}

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

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

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

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

return aiModelManageConvert, count, nil
}

+ 433
- 20
models/cloudbrain.go View File

@@ -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
}

+ 4
- 0
models/cloudbrain_image.go View File

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


+ 109
- 0
models/cloudbrain_spec.go View File

@@ -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
}

+ 6
- 127
models/cloudbrain_static.go View File

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


+ 68
- 0
models/cloudbrain_temp.go View File

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

import (
"time"

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

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

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

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

return nil
}

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

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

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

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

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

+ 28
- 13
models/dataset.go View File

@@ -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
}

+ 88
- 0
models/dataset_reference.go View File

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

import (
"strconv"
"strings"

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

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

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

return datasets
}

func HasReferenceDataset(repoID int64) bool {

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

func getReferenceDatasetStr(repoID int64) string {

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

func DeleteReferenceDatasetIdsByRepoID(repoID int64) error {

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

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

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

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

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

}

+ 14
- 10
models/dbsql/dataset_foreigntable_for_es.sql View File

@@ -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$


+ 1
- 1
models/dbsql/repo_foreigntable_for_es.sql View File

@@ -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;



+ 9
- 0
models/models.go View File

@@ -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,


+ 8
- 0
models/org.go View File

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


+ 20
- 8
models/repo.go View File

@@ -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)",


+ 39
- 0
models/repo_activity_custom.go View File

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


+ 349
- 0
models/resource_queue.go View File

@@ -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
}

+ 329
- 0
models/resource_scene.go View File

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

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

const (
Exclusive = iota + 1
NotExclusive
)

type ResourceScene struct {
ID int64 `xorm:"pk autoincr"`
SceneName string
JobType string
IsExclusive bool
ExclusiveOrg string
CreatedTime timeutil.TimeStamp `xorm:"created"`
CreatedBy int64
UpdatedTime timeutil.TimeStamp `xorm:"updated"`
UpdatedBy int64
DeleteTime timeutil.TimeStamp `xorm:"deleted"`
DeletedBy int64
}

type ResourceSceneSpec struct {
ID int64 `xorm:"pk autoincr"`
SceneId int64 `xorm:"unique(idx_scene_spec)"`
SpecId int64 `xorm:"unique(idx_scene_spec)"`
CreatedTime timeutil.TimeStamp `xorm:"created"`
}

type ResourceSceneReq struct {
ID int64
SceneName string
JobType string
IsExclusive bool
ExclusiveOrg string
CreatorId int64
SpecIds []int64
}

type SearchResourceSceneOptions struct {
ListOptions
JobType string
IsExclusive int
AiCenterCode string
QueueId int64
}

type ResourceSceneListRes struct {
TotalSize int64
List []ResourceSceneRes
}

func NewResourceSceneListRes(totalSize int64, list []ResourceSceneRes) *ResourceSceneListRes {
return &ResourceSceneListRes{
TotalSize: totalSize,
List: list,
}
}

type ResourceSceneRes struct {
ID int64
SceneName string
JobType JobType
IsExclusive bool
ExclusiveOrg string
Specs []ResourceSpecWithSceneId
}

func (ResourceSceneRes) TableName() string {
return "resource_scene"
}

type ResourceSceneBriefRes struct {
ID int64
SceneName string
}

func (ResourceSceneBriefRes) TableName() string {
return "resource_scene"
}

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

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

func InsertResourceScene(r ResourceSceneReq) error {
sess := x.NewSession()
defer sess.Close()

//check
specs := make([]ResourceSpecification, 0)
cond := builder.In("id", r.SpecIds).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
}

+ 550
- 0
models/resource_specification.go View File

@@ -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(&param)
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(&param)
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
}

+ 7
- 5
models/user.go View File

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


+ 451
- 0
models/user_analysis_for_activity.go View File

@@ -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
}

+ 72
- 15
models/user_business_analysis.go View File

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


+ 14
- 0
models/user_business_struct.go View File

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

Phone string `xorm:"NULL"`
}

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

Phone string `xorm:"NULL"`
}

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

Phone string `xorm:"NULL"`
}

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

Phone string `xorm:"NULL"`
}

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

Phone string `xorm:"NULL"`
}

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

Phone string `xorm:"NULL"`
}

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

Phone string `xorm:"NULL"`
}

type UserAnalysisPara struct {


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

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

+ 8
- 0
modules/auth/dataset.go View File

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

+ 2
- 2
modules/auth/grampus.go View File

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


+ 3
- 0
modules/auth/modelarts.go View File

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


+ 47
- 0
modules/auth/wechat/client.go View File

@@ -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
}

+ 167
- 0
modules/auth/wechat/cloudbrain.go View File

@@ -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 ""
}

+ 63
- 0
modules/auth/wechat/template.go View File

@@ -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
}

+ 142
- 99
modules/cloudbrain/cloudbrain.go View File

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

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

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


+ 57
- 28
modules/cloudbrain/resty.go View File

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


+ 2
- 1
modules/context/context.go View File

@@ -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"


+ 43
- 0
modules/context/permission_json.go View File

@@ -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
}
}
}

+ 26
- 0
modules/cron/tasks_basic.go View File

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


+ 18
- 0
modules/dataset/dataset.go View File

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

}

+ 1
- 1
modules/git/repo_stats_custom.go View File

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


+ 8
- 12
modules/grampus/grampus.go View File

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


+ 33
- 0
modules/grampus/resty.go View File

@@ -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
}

+ 41
- 6
modules/markup/markdown/markdown.go View File

@@ -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(`<pre class="` + strings.Join(preClasses, " ") + `">`)
if err != nil {
return
}

// include language-x class as part of commonmark spec
_, err = w.WriteString(`<code class="chroma language-` + string(language) + `">`)
if err != nil {
return
}
} else {
_, err := w.WriteString("</code></pre>")
if err != nil {
return
}
}
}),
),
meta.Meta,


+ 685
- 86
modules/modelarts/modelarts.go View File

@@ -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
}

+ 276
- 11
modules/modelarts/resty.go View File

@@ -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
}

+ 2
- 0
modules/notification/base/notifier.go View File

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

+ 4
- 0
modules/notification/base/null.go View File

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

}

+ 9
- 0
modules/notification/notification.go View File

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

+ 36
- 0
modules/notification/wechat/wechat.go View File

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

+ 2
- 0
modules/setting/cloudbrain.go View File

@@ -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
}

+ 5
- 2
modules/setting/repository.go View File

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


+ 118
- 28
modules/setting/setting.go View File

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

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

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

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

var (
// AppVer settings
AppVer string
@@ -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() {


+ 57
- 6
modules/storage/minio_ext.go View File

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


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

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

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

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

var fileTotalSize int64

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

return fileTotalSize, nil
}

func ObsCopyAllFile(srcBucket string, srcPath string, destBucket string, destPath string) (int64, error) {
input := &obs.ListObjectsInput{}
input.Bucket = srcBucket
// 设置每页100个对象
@@ -330,6 +370,7 @@ func GetOneLevelAllObjectUnderDir(bucket string, prefixRootPath string, relative
output, err := ObsCli.ListObjects(input)
fileInfos := make([]FileInfo, 0)
prefixLen := len(input.Prefix)
fileMap := make(map[string]bool, 0)
if err == nil {
for _, val := range output.Contents {
log.Info("val key=" + val.Key)
@@ -338,23 +379,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
}

+ 27
- 24
modules/templates/helper.go View File

@@ -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)
},


+ 124
- 22
options/locale/locale_en-US.ini View File

@@ -99,6 +99,7 @@ error500= Sorry, the site has encountered some problems, we are trying to <stron
[error]
occurred = An error has occurred
report_message = An error has occurred
no_right=You have no right to do the operation.

[install]
install = Installation
@@ -253,13 +254,16 @@ page_dev_env_desc2_title=Model Management and Sharing
page_dev_env_desc2_desc=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.
page_dev_env_desc3_title=Once Configuration, Multiple Reuse
page_dev_env_desc3_desc=Provide execution environment sharing, Once Configuration, Multiple Reuse. Lower the threshold of model development, and avoid spending repetitive time configuring complex environments.
page_dev_yunlao=PengCheng Cloudbrain Open Source Collaboration
page_dev_yunlao_desc1=The platform has been connected with Pengcheng Cloudbrain and can use the rich computing resources of Pengcheng Cloudbrain to complete AI development tasks.
page_dev_yunlao_desc2=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.
page_dev_yunlao_desc3=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.
page_dev_yunlao_desc4=If your model requires more computing resources, you can also apply for it separately.
page_dev_yunlao=OpenI AI Collaboration Platform
page_dev_yunlao_desc1=OpenI AI collaboration platform has cooperated with Pengcheng cloud brain and China computing power network (C²NET) can be used to complete AI development tasks by using the rich computing resources of Pengcheng cloud brain and China computing network.
page_dev_yunlao_desc2=Pengcheng CloudBrain's existing AI computing power is 100p FLOPS@FP16 (billions of half precision floating-point calculations per second), the main hardware infrastructure consists of GPU servers equipped with NVIDIA Tesla V100 and A100, and Atlas 900 AI clusters equipped with Kunpeng and shengteng processors.
page_dev_yunlao_desc3=China computing power network (C²NET) phase I can realize high-speed network interconnection between different artificial intelligence computing centers, and realize reasonable scheduling of computing power and flexible allocation of resources. At present, 11 intelligent computing centers have been connected, and the total scale of computing power is 1924p OPS@FP16. OpenI AI collaboration platform has been connected to Pengcheng Cloud Computing Institute, Chengdu Intelligent Computing Center, Zhongyuan Intelligent Computing Center, Hefei brain and other nodes.
page_dev_yunlao_desc4=Developers can freely select the corresponding computing resources according to the use needs, and can test the adaptability, performance, stability, etc. of the model in different hardware environments.
page_dev_yunlao_desc5=If your model requires more computing resources, you can also apply for it separately.
page_dev_yunlao_apply=Apply Separately

c2net_title=China Computing Network
c2net_desc=The artificial intelligence computing power network promotion alliance has access to 11 intelligent computing centers, with a total scale of 1924p.
c2net_center=Center
search=Search
search_repo=Repository
search_dataset=DataSet
@@ -560,6 +564,7 @@ static.CollectImage=Collect Image Count
static.CollectedImage=Collected Image Count
static.RecommendImage=Recommended Image Count
static.email=Email
static.phone=Phone
static.location=Location
static.all=All
static.public.user_business_analysis_current_month=Current_Month
@@ -821,6 +826,9 @@ title_format_err=Name can only contain number,letter,'-','_' or '.', and can be
description = Description
description_format_err=Description's length can be up to %s characters long.
create_dataset = Create Dataset
download_url=Download Url
download_oper=Operation
download_copy=Copy URL
create_dataset_fail=Failed to create dataset.
query_dataset_fail=Failed to query dataset.
edit_attachment_fail=Failed to update description.
@@ -831,6 +839,8 @@ category = Category
no_category = No Category
task = Task
no_task = No Task
reference_dataset_fail=Failed to reference dataset, please try again later.
cancel_reference_dataset_fail=Failed to cancel reference dataset, please try again later.
license = License
no_license = No License
file = Dataset File
@@ -914,9 +924,10 @@ select_task = Select Research Direction/Application Area
dataset_name_tooltips = Please enter letters, numbers, _ and - up to 100 characters.
dataset_no_create = No dataset has been created yet
dataset_explain = Dataset: CloudBrain I provides CPU/GPU resources, Cloudbrain II provides Ascend NPU resources, and the data set used for debugging also needs to be uploaded to the corresponding environment;
dataset_instructions_for_use = Instructions for use: You can refer to Qizhi AI Collaboration Platform
dataset_camp_course = Newcomer Training Camp Course;
dataset_instructions_for_use = Instructions for use: You can refer to Openi AI Collaboration Platform
dataset_camp_course = Newcomer Training Camp Course;
dataset_upload = Upload
dataset_upload_status= Upload Status
dataset_file_name = File Name
dataset_available_clusters = Available Clusters
dataset_upload_time = Upload Time
@@ -943,6 +954,13 @@ unzip_failed=Unzip Failed
unzip_stared=Unzipping
unzip_status=Unzip Status
collection_num=Collection Nums
current_dataset=Current Dataset
linked_dataset=Linked Dataset
unfavorite=Unlike
favorite=Like
disassociate=Disassociate
benchmark_dataset_tip=Note: first use the dataset function to upload the model, and then select the model from the dataset list.

[repo]
owner = Owner
repo_name = Repository Name
@@ -1007,7 +1025,7 @@ cloudbrain.time.starttime=Start run time
cloudbrain.time.endtime=End run time
cloudbrain.datasetdownload=Dataset download url
model_manager = Model
model_noright=No right
model_noright=You have no right to do the operation.
model_rename=Duplicate model name, please modify model name.

date=Date
@@ -1037,8 +1055,9 @@ image_delete_fail=Failed to delete image, please try again later.
image_overwrite=You had submitted the same name image before, are you sure to overwrite the original image?
download=Download
score=Score
wait_count_start = There are currently
wait_count_end = tasks queued
file_limit_100 = Display up to 100 files or folders in a single directory

images.name = Image Tag
images.name_placerholder = Please enter the image name
image.label_tooltips = Example Python 3.7, Tensorflow 2.0, cuda 10, pytorch 1.6
@@ -1072,6 +1091,7 @@ cloudbrain_operate = Operate
cloudbrain_status_createtime = Status/Createtime
cloudbrain_status_runtime = Running Time
cloudbrain_jobname_err=Name must start with a lowercase letter or number,can include lowercase letter,number,_ and -,can not end with _, and can be up to 36 characters long.
cloudbrain_bootfile_err=The bootfile does not exist in the repository
cloudbrain_query_fail=Failed to query cloudbrain information.
cloudbrain.mirror_tag = Mirror Tag
cloudbrain.mirror_description = Mirror Description
@@ -1096,7 +1116,11 @@ modelarts.status=Status
modelarts.createtime=CreateTime
modelarts.version_nums = Version Nums
modelarts.version = Version
modelarts.computing_resources=compute Resources
modelarts.computing_resources=Compute Resources
modelarts.cluster.computing_resources=Cluster/Compute Resources
modelarts.ai_center=Ai Center
modelarts.card_type=Card Type
modelarts.cluster=Cluster
modelarts.notebook=Debug Task
modelarts.train_job=Train Task
modelarts.train_job.new_debug= New Debug Task
@@ -1186,7 +1210,7 @@ modelarts.infer_job_model_file = Model File
modelarts.infer_job = Inference Job
modelarts.infer_job.model_version = Model/Version
modelarts.infer_job.select_model = Select Model
modelarts.infer_job.boot_file_helper=The startup file is the entry file for your program execution and must end in.py.Such as inference.py, main.py, example/inference. Py, case/main.py.
modelarts.infer_job.boot_file_helper=The startup file is the entry file for your program execution and must end in.py.Such as inference.py, main.py, example/inference.py, case/main.py.
modelarts.infer_job.tooltip = The model has been deleted and cannot be viewed.
modelarts.download_log=Download log file

@@ -1198,7 +1222,7 @@ model_Evaluation_not_created = Model evaluation has not been created
repo_not_initialized = Code version: You have not initialized the code repository, please <a href="%s"> initialized </a> 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 <a href="https://git.openi.org.cn/zeizei/OpenI_Learning">Xiaobai training camp </a> course of Qizhi AI collaboration platform.
platform_instructions = Instructions for use: You can refer to the <a href="https://git.openi.org.cn/zeizei/OpenI_Learning">Xiaobai training camp </a> course of Openi AI collaboration platform.
model_not_exist = Model file: You do not have a model file yet, please generate and <a href="%s/modelmanage/show_model">export the model</a> through the <a href="%s/modelarts/train-job">training task</a> 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 <a href="%s">%s</a>
rename_repo = renamed repository from <code>%[1]s</code> to <a href="%[2]s">%[3]s</a>
@@ -2976,15 +3058,15 @@ mirror_sync_delete = synced and deleted reference <code>%[2]s</code> at <a href=
approve_pull_request = `approved <a href="%s/pulls/%s">%s#%[2]s</a>`
reject_pull_request = `suggested changes for <a href="%s/pulls/%s">%s#%[2]s</a>`
upload_dataset=`upload dataset <a href="%s/datasets">%s</a>`
task_gpudebugjob=`created CPU/GPU type debugging task<a href="%s/cloudbrain/%s">%s</a>`
task_gpudebugjob=`created CPU/GPU type debugging task <a href="%s/cloudbrain/%s">%s</a>`
task_npudebugjob=`created NPU type debugging task <a href="%s/modelarts/notebook/%s">%s</a>`
task_nputrainjob=`created NPU training task<a href="%s/modelarts/train-job/%s">%s</a>`
task_nputrainjob=`created NPU training task <a href="%s/modelarts/train-job/%s">%s</a>`
task_inferencejob=`created reasoning task <a href="%s/modelarts/inference-job/%s">%s</a>`
task_benchmark=`created profiling task <a href="%s/cloudbrain/benchmark/%s">%s</a>`
task_createmodel=`created new model <a href="%s/modelmanage/show_model_info?name=%s">%s</a>`
task_gputrainjob=`created CPU/GPU training task<a href="%s/cloudbrain/train-job/%s">%s</a>`
task_c2netnputrainjob=`created NPU training task<a href="%s/grampus/train-job/%s">%s</a>`
task_c2netgputrainjob=`created CPU/GPU training task<a href="%s/grampus/train-job/%s">%s</a>`
task_gputrainjob=`created CPU/GPU training task <a href="%s/cloudbrain/train-job/%s">%s</a>`
task_c2netnputrainjob=`created NPU training task <a href="%s/grampus/train-job/%s">%s</a>`
task_c2netgputrainjob=`created CPU/GPU training task <a href="%s/grampus/train-job/%s">%s</a>`

[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 <strong style="color:#010101">data_url</strong>, and the output path is stored in the environment variable <strong style="color:#010101">train_url</strong>.
infer_dataset_path_rule = The dataset location is stored in the environment variable <strong style="color:#010101">data_url</strong>, and the output path is stored in the environment variable <strong style="color:#010101">result_url</strong>.
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 <strong style="color:#010101">%s</strong>, the dataset is storaged in <strong style="color:#010101">%s</strong>, and please put your model into <strong style="color:#010101">%s</strong> then you can download it online
new_infer_gpu_tooltips = The dataset is stored in <strong style="color:#010101">%s</strong>, the model file is stored in <strong style="color:#010101">%s</strong>, please store the inference output in <strong style="color:#010101">%s</strong> for subsequent downloads.

[points]
points = points


+ 118
- 11
options/locale/locale_zh-CN.ini View File

@@ -100,6 +100,7 @@ error500=抱歉,站点遇到一些问题,我们正尝试<strong>修复网页
[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=创建了项目 <a href="%s">%s</a>
rename_repo=重命名项目 <code>%[1]s</code> 为 <a href="%[2]s">%[3]s</a>
@@ -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 = 数据集位置存储在环境变量<strong style="color:#010101">data_url</strong>中,训练输出路径存储在环境变量<strong style="color:#010101">train_url</strong>中。
infer_dataset_path_rule = 数据集位置存储在环境变量<strong style="color:#010101">data_url</strong>中,推理输出路径存储在环境变量<strong style="color:#010101">result_url</strong>中。
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 =训练脚本存储在<strong style="color:#010101">%s</strong>中,数据集存储在<strong style="color:#010101">%s</strong>中,训练输出请存储在<strong style="color:#010101">%s</strong>中以供后续下载。
new_infer_gpu_tooltips = 数据集存储在<strong style="color:#010101">%s</strong>中,模型文件存储在<strong style="color:#010101">%s</strong>中,推理输出请存储在<strong style="color:#010101">%s</strong>中以供后续下载。

+ 3
- 0
package-lock.json View File

@@ -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"
}


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

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

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

}

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


+ 10
- 2
public/self/css/notebook/notebook.css View File

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


+ 12
- 12
public/self/dataset_preview.js View File

@@ -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;


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

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

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

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

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

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

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

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

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

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

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

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

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

}
}


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

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

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


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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

}
},

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

+ 26
- 3
routers/admin/cloudbrains.go View File

@@ -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")}

}



+ 284
- 0
routers/admin/resources.go View File

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

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

@@ -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())
}



+ 523
- 14
routers/api/v1/repo/cloudbrain.go View File

@@ -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, ""
}

+ 44
- 61
routers/api/v1/repo/cloudbrain_dashboard.go View File

@@ -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{}


+ 101
- 85
routers/api/v1/repo/modelarts.go View File

@@ -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,


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

Loading…
Cancel
Save