├── .gitignore
├── Global.scala
├── README.md
├── app
├── controllers
│ ├── Application.scala
│ ├── apps
│ │ ├── SparkList.scala
│ │ └── YarnList.scala
│ ├── auth
│ │ ├── Authentication.scala
│ │ ├── Secured.scala
│ │ └── userName.scala
│ └── spark
│ │ ├── SparkJar.scala
│ │ └── TaskManager.scala
├── models
│ ├── Mail
│ │ ├── Email.scala
│ │ └── RegisterExecption.scala
│ ├── actor
│ │ ├── ActorStack.scala
│ │ ├── InstrumentedActor.scala
│ │ ├── Slf4jLogging.scala
│ │ └── WebSocketChannel.scala
│ ├── depend
│ │ └── Depend.scala
│ ├── deploy
│ │ ├── CommonMessages.scala
│ │ ├── CreateBatchRequest.scala
│ │ ├── Execute.scala
│ │ ├── JobManager.scala
│ │ ├── MatchEngine.scala
│ │ ├── MessagePool.scala
│ │ ├── StatusListener.scala
│ │ ├── package.scala
│ │ ├── process
│ │ │ ├── LineBufferedProcess.scala
│ │ │ ├── LineBufferedStream.scala
│ │ │ └── SparkProcessBuilder.scala
│ │ └── task
│ │ │ ├── TaskDataProvider.scala
│ │ │ └── TaskProvider.scala
│ ├── io
│ │ ├── JobDAO.scala
│ │ ├── JobFileDAO.scala
│ │ ├── Message.scala
│ │ ├── MetricsData.scala
│ │ ├── TaskDao.scala
│ │ ├── TaskInfoDao.scala
│ │ └── package.scala
│ ├── metrics
│ │ ├── BaseInfo.scala
│ │ ├── HadoopMetricsProvider.scala
│ │ ├── MetricsFactory.scala
│ │ └── MetricsProvider.scala
│ ├── shell
│ │ └── kill_job.sh
│ ├── user
│ │ ├── User.scala
│ │ └── package.scala
│ └── utils
│ │ ├── Captcha.scala
│ │ ├── Config.scala
│ │ ├── Configuration.scala
│ │ ├── JsonParse.scala
│ │ └── MD5Utils.scala
└── views
│ ├── copyright.scala.html
│ ├── error.scala.html
│ ├── errors
│ └── fzf.scala.html
│ ├── findpwd.scala.html
│ ├── index.scala.html
│ ├── login.scala.html
│ ├── main.scala.html
│ ├── nothing.scala.html
│ ├── plain.scala.html
│ ├── registed.scala.html
│ ├── register.scala.html
│ ├── setpwd.scala.html
│ ├── sparklist.scala.html
│ ├── tasklist.scala.html
│ ├── upload.scala.html
│ └── yarnlist.scala.html
├── build.sbt
├── conf
├── application.conf
├── evolutions
│ └── default
│ │ ├── 1.sql
│ │ ├── 2.sql
│ │ ├── 3.sql
│ │ ├── 4.sql
│ │ ├── 5.sql
│ │ └── 6.sql
├── routes
└── web-site.conf
├── db
├── play.h2.db
├── play.h2.db.trace.db
└── play.trace.db
├── project
├── .sbtserver
├── .sbtserver.lock
├── build.properties
├── plugins.sbt
└── sbt-ui.sbt
├── public
├── fonts
│ ├── FontAwesome.otf
│ ├── fontawesome-webfont.eot
│ ├── fontawesome-webfont.svg
│ ├── fontawesome-webfont.ttf
│ ├── fontawesome-webfont.woff
│ ├── fontawesome-webfont.woff2
│ ├── glyphicons-halflings-regular.eot
│ ├── glyphicons-halflings-regular.svg
│ ├── glyphicons-halflings-regular.ttf
│ ├── glyphicons-halflings-regular.woff
│ ├── glyphicons-halflings-regular.woff2
│ ├── linecons.eot
│ ├── linecons.svg
│ ├── linecons.ttf
│ └── linecons.woff
├── images
│ ├── 1.png
│ ├── 2.png
│ ├── 3.png
│ ├── 4.png
│ ├── 5.png
│ ├── 522641-46907f6c8f3b2b0b.png
│ ├── QQ20170313-162151@2x.png
│ ├── QQ20170313-171517@2x.png
│ ├── album-image-full.jpg
│ ├── album-img-1.png
│ ├── album-img-2.png
│ ├── album-img-3.png
│ ├── album-img-4.png
│ ├── album-img-5.png
│ ├── album-img-6.png
│ ├── album-img-7.png
│ ├── album-img-8.png
│ ├── arrow-left.png
│ ├── arrow-right.png
│ ├── attach-1.png
│ ├── attach-2.png
│ ├── back_disabled.png
│ ├── back_enabled.png
│ ├── back_enabled_hover.png
│ ├── bg.jpg
│ ├── chosen-sprite.png
│ ├── chosen-sprite@2x.png
│ ├── cloud.png
│ ├── clouds.png
│ ├── eiffel.jpg
│ ├── forward_disabled.png
│ ├── forward_enabled.png
│ ├── forward_enabled_hover.png
│ ├── image-1.jpg
│ ├── image-2.jpg
│ ├── image-3.jpg
│ ├── image-4.jpg
│ ├── image-5.jpg
│ ├── logo-collapsed.png
│ ├── logo-collapsed2x.png
│ ├── logo-white-bg.png
│ ├── logo-white-bg@2x.png
│ ├── logo.png
│ ├── logo2x.png
│ ├── minus.png
│ ├── news-image-widget-2.png
│ ├── news-image-widget-3.png
│ ├── news-image-widget-4.png
│ ├── news-image-widget.png
│ ├── notes-lines.png
│ ├── ok-white-full.png
│ ├── ok-white.png
│ ├── ok.png
│ ├── pic1.png
│ ├── pic2.png
│ ├── pic3.jpg
│ ├── pic4.jpg
│ ├── plus-white.png
│ ├── plus.png
│ ├── sample.jpg
│ ├── sort_asc.png
│ ├── sort_asc_disabled.png
│ ├── sort_both.png
│ ├── sort_desc.png
│ ├── sort_desc_disabled.png
│ ├── spritemap.png
│ ├── spritemap@2x.png
│ ├── switch.png
│ ├── user-1.png
│ ├── user-2.png
│ ├── user-3.png
│ ├── user-4.png
│ └── user-5.png
├── javascripts
│ ├── .DS_Store
│ ├── TweenMax.min.js
│ ├── bootstrap.min.js
│ ├── dropzone.min.js
│ ├── globalize.min.js
│ ├── highchartThemes.js
│ ├── highcharts.js
│ ├── initialize-tooltips.js
│ ├── joinable.js
│ ├── jquery-1.11.1.min.js
│ ├── jquery-migrate-1.2.1.min.js
│ ├── jquery-ui-1.9.2.custom.min.js
│ ├── jquery.dataTables.js
│ ├── jquery.dataTables.min.js
│ ├── jquery.edatatables.js
│ ├── jquery.form.js
│ ├── jquery.min.js
│ ├── jquery.nicescroll.js
│ ├── progress.js
│ ├── push.js
│ ├── resizeable.js
│ ├── scripts.js
│ ├── submited.js
│ ├── sweetalert.min.js
│ ├── task.js
│ ├── tooltipster.bundle.min.js
│ ├── xenon-api.js
│ ├── xenon-custom.js
│ ├── xenon-toggles.js
│ ├── xenon-widgets.js
│ └── zeroModal.js
└── stylesheets
│ ├── .DS_Store
│ ├── DT_bootstrap.css
│ ├── app.css
│ ├── bootstrap-reset.css
│ ├── bootstrap.css
│ ├── bootstrap.min.css
│ ├── custom-ico-fonts.css
│ ├── custom.css
│ ├── datapp.css
│ ├── dropzone.css
│ ├── error.css
│ ├── font-awesome.css
│ ├── font-awesome.min.css
│ ├── google.css
│ ├── jarcustom.css
│ ├── jquery-ui.min.css
│ ├── lines.css
│ ├── main.css
│ ├── material.min.css
│ ├── meteocons.css
│ ├── style-responsive.css
│ ├── style.css
│ ├── sweetalert.css
│ ├── tooltipster-sideTip-punk.min.css
│ ├── tooltipster.bundle.min.css
│ ├── xenon-components.css
│ ├── xenon-core.css
│ ├── xenon-forms.css
│ ├── xenon-skins.css
│ └── zeroModal.css
├── spark-submit-ui.iml
└── test
├── ApplicationSpec.scala
└── IntegrationSpec.scala
/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore Play! working directory #
2 | bin/
3 | .eclipse
4 | /lib/
5 | /logs/
6 | # Created by .ignore support plugin (hsz.mobi)
7 | ### PlayFramework template
8 | # Ignore Play! working directory #
9 | bin/
10 | .eclipse
11 | /lib/
12 | /modules
13 | /project/target
14 | tmp/
15 | test-result
16 | server.pid
17 | *.eml
18 | /dist/
19 | .cache
20 | target/
21 | ### JetBrains template
22 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
23 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
24 |
25 | # User-specific stuff:
26 | .idea/workspace.xml
27 | .idea/tasks.xml
28 | .idea/dictionaries
29 | .idea/vcs.xml
30 | .idea/jsLibraryMappings.xml
31 | .idea
32 |
33 |
34 | # Sensitive or high-churn files:
35 | .idea/dataSources.ids
36 | .idea/dataSources.xml
37 | .idea/dataSources.local.xml
38 | .idea/sqlDataSources.xml
39 | .idea/dynamic.xml
40 | .idea/uiDesigner.xml
41 |
42 | # Gradle:
43 | .idea/gradle.xml
44 | .idea/libraries
45 |
46 | # Mongo Explorer plugin:
47 | .idea/mongoSettings.xml
48 |
49 | ## File-based project format:
50 | *.iws
51 |
52 | ## Plugin-specific files:
53 |
54 | # IntelliJ
55 | /out/
56 |
57 | # mpeltonen/sbt-idea plugin
58 | .idea_modules/
59 |
60 | # JIRA plugin
61 | atlassian-ide-plugin.xml
62 |
63 | # Crashlytics plugin (for Android Studio and IntelliJ)
64 | com_crashlytics_export_strings.xml
65 | crashlytics.properties
66 | crashlytics-build.properties
67 | fabric.properties
68 |
69 | ### SBT template
70 | # Simple Build Tool
71 | # http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control
72 |
73 | /.idea
74 | /.settings
75 | /.sbtserver
76 | target/
77 | /app-2.10
78 | lib_managed/
79 | src_managed/
80 | project/boot/
81 | .history
82 | .cache
83 | ### JetBrains template
84 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
85 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
86 |
87 | # User-specific stuff:
88 | .idea/workspace.xml
89 | .idea/tasks.xml
90 | .idea/dictionaries
91 | .idea/vcs.xml
92 | .idea/jsLibraryMappings.xml
93 |
94 | # Sensitive or high-churn files:
95 | .idea/dataSources.ids
96 | .idea/dataSources.xml
97 | .idea/dataSources.local.xml
98 | .idea/sqlDataSources.xml
99 | .idea/dynamic.xml
100 | .idea/uiDesigner.xml
101 |
102 | # Gradle:
103 | .idea/gradle.xml
104 | .idea/libraries
105 |
106 | # Mongo Explorer plugin:
107 | .idea/mongoSettings.xml
108 |
109 | ## File-based project format:
110 | *.iws
111 |
112 | ## Plugin-specific files:
113 |
114 | # IntelliJ
115 | /out/
116 |
117 | # mpeltonen/sbt-idea plugin
118 | .idea_modules/
119 |
120 | # JIRA plugin
121 | atlassian-ide-plugin.xml
122 |
123 | # Crashlytics plugin (for Android Studio and IntelliJ)
124 | com_crashlytics_export_strings.xml
125 | crashlytics.properties
126 | crashlytics-build.properties
127 | fabric.properties
128 | ### PlayFramework template
129 | # Ignore Play! working directory #
130 | bin/
131 | .eclipse
132 | /lib/
133 | /modules
134 | /project/target
135 | /target
136 | tmp/
137 | test-result
138 | server.pid
139 | *.eml
140 | /dist/
141 | .cache
142 | ### Scala template
143 | *.class
144 | *.log
145 |
146 | # sbt specific
147 | .cache
148 | .history
149 | .lib/
150 | dist/*
151 | target/
152 | lib_managed/
153 | src_managed/
154 | project/boot/
155 | project/plugins/project/
156 |
157 | # Scala-IDE specific
158 | .scala_dependencies
159 | .worksheet
160 |
161 |
162 | #/conf/web-site.conf
163 | project/project/
164 |
165 | /db/
166 |
--------------------------------------------------------------------------------
/Global.scala:
--------------------------------------------------------------------------------
1 | import play.api._
2 | import play.api.mvc._
3 | import play.api.mvc.Results._
4 |
5 | import scala.concurrent.Future
6 | import play.api.Play.current
7 | import com.google.inject._
8 | import models._
9 |
10 | object Global extends GlobalSettings {
11 |
12 | private lazy val injector = {
13 | Play.isProd match {
14 | case _ => {
15 | Guice.createInjector(new Depend)
16 | }
17 | }
18 | }
19 |
20 |
21 | // 500 - internal server error
22 | override def onError(request: RequestHeader,ex: Throwable) = {
23 | Future.successful(
24 | InternalServerError(
25 | views.html.errors.fzf()
26 | )
27 | )
28 | }
29 |
30 | // 404 - page not found error
31 | override def onHandlerNotFound(request: RequestHeader)= {
32 | Future.successful(
33 | NotFound(
34 | views.html.errors.fzf()
35 | )
36 | )
37 | }
38 |
39 | override def getControllerInstance[A](clazz: Class[A]) = {
40 | injector.getInstance(clazz)
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # spark-submit-ui
3 | >这是一个基于playframwork开发,web管理的spark应用程序
4 |
5 | >你需要安装SBT和Java以及PlayFramowrk。项目基于2.2.x 版本开发,需要PlayFramowrk 2.2或更高版本。
6 |
7 | #### 测试环境
8 | * JDK8
9 | * Center OS 6.5
10 | * Spark 1.5.2
11 | * Hadoop 2.6.0
12 | * Scala 2.11
13 |
14 | #### 主要功能
15 | * hadoop metrics 数据监控
16 | * spark 集群状态信息展示
17 | * 完善的spark app 提交与管理
18 | * 任务状态监控,状态推送
19 |
20 | #### 并下载并安装Play Framework 编译环境
21 | [Installing Play](https://www.playframework.com/documentation/2.5.x/Installing")
22 |
23 |
24 | #### 修改配置文件,将集群地址替换为你的
25 | 文件路径在
26 |
conf/web-site.conf
27 | ### 编译与运行
28 | 然后去 http://localhost:9000 查看正在运行的服务器。
29 |
30 | 如果运行有这个界面提示,点击Apply this script now 初始化数据表
31 | 
32 |
33 | #### 编译与运行
34 | activator run
35 |
36 | #### 项目默认使用H2数据库
37 | 这是Play 内嵌的一个数据库 H2
38 | H2官方介绍 http://www.h2database.com/html/main.html
39 |
40 | 如果想要换成Mysql或者是其他的存储可以参考指引
41 | MySQL 数据库引擎连接属性
42 | 配置文件 conf/application.conf
43 |
44 |
45 | db.default.driver=com.mysql.jdbc.Driver
46 | db.default.url="jdbc:mysql://localhost/playdb"
47 | db.default.user=playdbuser
48 | db.default.pass="a strong password"
49 |
50 |
51 | #其他
52 |
53 | 通过界面管理,kill或者rerun任务
54 |
55 | 
56 |
57 | 如果你的提交参数或配置导致异常,可以在提交时查看相关的错误输出
58 |
59 | 
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/app/controllers/Application.scala:
--------------------------------------------------------------------------------
1 | package controllers
2 | import java.text.SimpleDateFormat
3 | import java.util.Date
4 |
5 | import akka.actor.ActorRef
6 | import com.google.inject.Inject
7 | import models._
8 | import models.io.{Message, MessageList, TaskMessage}
9 | import models.metrics.MetricsProvider
10 | import play.api.libs.iteratee.Concurrent.Channel
11 | import play.api.libs.iteratee.{Concurrent, Enumerator, Iteratee}
12 | import play.api.libs.json.{JsValue, Json}
13 | import play.api.mvc._
14 | import play.libs.Akka
15 | import akka.actor._
16 | import akka.event.Logging
17 | import akka.pattern.ask
18 | import akka.util.Timeout
19 | import play.api.Logger
20 | import play.api.mvc._
21 |
22 | import scala.concurrent.duration._
23 | import scala.concurrent.{ExecutionContext, Future}
24 | import scala.collection.mutable
25 | import scala.concurrent.ExecutionContext.Implicits.global
26 | import scala.concurrent.stm.{Sink, Source}
27 |
28 | /**
29 | * WebSocket and Index
30 | */
31 | class Application @Inject()(metricsProvider: MetricsProvider)(execute: Execute) extends Controller with Secured {
32 |
33 |
34 | def index = IsAuthenticated { username => implicit request =>
35 | val rpcInfo: RPCInfo = metricsProvider.getMetricMouled[RPCInfo](RPCInfo.getClass)
36 | val dfsInfo: DFSInfo = metricsProvider.getMetricMouled[DFSInfo](DFSInfo.getClass)
37 | val memInfo: MEMInfo = metricsProvider.getMetricMouled[MEMInfo](MEMInfo.getClass)
38 | Ok(views.html.index(rpcInfo,dfsInfo,memInfo,getNowDate))
39 | }
40 |
41 |
42 | def startpush = WebSocket.using[String] { implicit request =>
43 | val user: String = request.session.get("email").get
44 | val (out,channel) = Concurrent.broadcast[String]
45 | val in = Iteratee.foreach[String] { msg =>
46 | execute.register(user,request.id.toString,channel)
47 | }
48 | (in,out)
49 | }
50 |
51 |
52 |
53 |
54 |
55 |
56 | def msglist =IsAuthenticated { username => implicit request =>
57 | implicit val residentWrites = Json.writes[TaskMessage]
58 | implicit val clusterListWrites = Json.writes[MessageList]
59 | val json: JsValue = Json.toJson(MessageList(Message.getMessages(username)))
60 | Ok(json)
61 | }
62 |
63 | def read(appId:String)=Action{
64 | Message.deleteMessage(appId)
65 | Ok(views.html.tasklist())
66 | }
67 |
68 | def readall=IsAuthenticated{ username => implicit request =>
69 | Message.deleteAllMessage(username)
70 | Ok(views.html.tasklist())
71 | }
72 |
73 | private[this] def getNowDate:String ={
74 | val before = 3*60*60*1000
75 | val currentTime = new Date(System.currentTimeMillis()-before)
76 | currentTime.setMinutes(0)
77 | val formatter = new SimpleDateFormat("yyyy,MM,dd,HH,mm,ss")
78 | formatter.format(currentTime)
79 | }
80 |
81 |
82 |
83 |
84 |
85 | }
86 |
--------------------------------------------------------------------------------
/app/controllers/apps/SparkList.scala:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import play.api.mvc.{Action, BodyParsers, Controller}
4 | import com.google.inject.Inject
5 | import models.TaskDataProvider.{AppDataObject, SparkTaskList, TaskData, YarnTaskInfoList}
6 | import models.TaskInfo
7 | import models.utils.Config
8 | import play.api.libs.json._
9 |
10 | import scala.language.postfixOps
11 | import scala.io.Source
12 |
13 | /**
14 | * Created by kinge
15 | */
16 | class SparkList @Inject()(conf:Config) extends Controller {
17 |
18 |
19 | def getSparkInfo=Action{
20 | Ok(Source.fromURL("http://"+conf.getString("spark.master.host")+"/json").mkString)
21 | }
22 |
23 | def sparklist=Action{
24 | Ok(views.html.sparklist())
25 | }
26 |
27 |
28 |
29 | import models.TaskDataProvider.taskListReads
30 | implicit val taskWrites = Json.writes[TaskInfo]
31 | implicit val residentWrites = Json.writes[SparkTaskList]
32 |
33 |
34 | def getSaprkApps=Action{
35 | val json= Source.fromURL("http://"+conf.getString("spark.master.host")+"/json").mkString
36 | val taskData: TaskData = Json.parse(json).as[TaskData]
37 | val apps: Seq[TaskInfo] = taskData.activeapps ++ taskData.completedapps
38 | Ok(Json.toJson(SparkTaskList(apps)))
39 | }
40 |
41 |
42 |
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/app/controllers/apps/YarnList.scala:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 |
4 | import com.google.inject.Inject
5 | import models.utils.Config
6 | import play.api.mvc._
7 | import scala.io.Source
8 |
9 | /**
10 | * Created by kinge
11 | */
12 | class YarnList @Inject()(conf:Config) extends Controller {
13 |
14 | def getYarnInfo=Action{
15 | Ok(Source.fromURL("http://"+conf.getString("hadoop.yarn.host")+"/ws/v1/cluster/apps").mkString)
16 | }
17 |
18 | def yarnlist=Action{
19 | Ok(views.html.yarnlist())
20 | }
21 |
22 | }
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/controllers/auth/Secured.scala:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import play.api.mvc._
4 |
5 | /**
6 | * Provide security features
7 | */
8 | trait Secured extends {
9 |
10 | /**
11 | * Retrieve the connected user's email
12 | */
13 | private def username(request: RequestHeader) = request.session.get("email")
14 |
15 | /**
16 | * Not authorized, forward to login
17 | */
18 | private def onUnauthorized(request: RequestHeader) = {
19 | Results.Redirect(routes.Authentication.login)
20 | }
21 |
22 | /**
23 | * Action for authenticated users.
24 | */
25 | def IsAuthenticated(f: => String => Request[AnyContent] => Result) = {
26 | Security.Authenticated(username, onUnauthorized) { user =>
27 | Action(request => f(user)(request))
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/controllers/auth/userName.scala:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import play.api.mvc.Controller
4 | import play.api.libs.json._
5 |
6 |
7 |
8 | object UserName extends Controller with Secured {
9 |
10 | def userName = IsAuthenticated { username => implicit request =>
11 | var shortName = username
12 | if (username.contains("@")){
13 | shortName = username.split("@")(0)
14 | }
15 | Ok(Json.obj("user"->username,"shortName"->shortName))
16 |
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/controllers/spark/SparkJar.scala:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 |
4 |
5 | import com.google.inject.Inject
6 | import models.JobManagerActor.{InvalidJar, JarStored}
7 | import models.TaskDataProvider.AppDataObject
8 | import models._
9 | import play.api.Logger
10 | import play.api.data.Forms._
11 | import play.api.data._
12 | import play.api.mvc._
13 |
14 |
15 | /**
16 | * Created by kinge on 16/6/22.
17 | */
18 | class SparkJar @Inject() (taskDao: TaskDao,taskProvider: TaskProvider[AppDataObject],execute: Execute) extends Controller with Secured {
19 |
20 | val executeForm:Form[ExecuteModel] = Form{
21 | mapping (
22 | "master"->text,
23 | "executeClass"->text,
24 | "numExecutors"->text,
25 | "driverMemory"->text,
26 | "executorMemory"->text,
27 | "total-executor-cores"->text,
28 | "jarLocation"->text,
29 | "args1"->text
30 | )(ExecuteModel.apply)(ExecuteModel.unapply)
31 | }
32 |
33 |
34 | def upload = Action(parse.multipartFormData) { implicit request =>
35 | request.body.file("file").map { jobFile =>
36 | session.get("email").map { user =>
37 | Logger.info("username=>"+user)
38 | execute.storeJar(user,jobFile) match {
39 | case JarStored(uri) => Redirect(routes.SparkJar.executejarpage())
40 | case InvalidJar(error) => Logger.info(error); BadRequest(error)
41 | case _ => NotFound
42 | }
43 | }.getOrElse {
44 | Unauthorized("The user does not exist, please login again!")
45 | }
46 | }.getOrElse {
47 | Redirect(routes.SparkJar.errorpage("Upload failed"))
48 | }
49 | }
50 |
51 | def executejarpage = Action { implicit request =>
52 | Ok(views.html.upload(executeForm))
53 | }
54 |
55 | def executejar = IsAuthenticated { username => implicit request =>
56 | executeForm.bindFromRequest.fold(
57 | formWithErrors => {
58 | formWithErrors.errors.map(x => Logger.info(x.message))
59 | formWithErrors.globalError.map(x => Logger.info(x.message))
60 | BadRequest(views.html.error(formWithErrors.toString))
61 | },
62 | executeArguments => {
63 | Logger.info("execution mode=>"+executeArguments.master)
64 | execute.main(executeArguments)
65 | match {
66 | case JobSubmitSuccess(id) => {
67 | taskDao.saveTaskArgs(executeArguments)(id)
68 | taskProvider.loadTaskInfo(AppDataObject(id,username))
69 | Ok("Task submitted successfully!")
70 | }
71 | case JobRunExecption(error) => Ok(error);
72 | case _ => NotFound
73 | }
74 | }
75 | )
76 | }
77 |
78 |
79 | def errorpage(error:String) =Action {
80 | Ok(views.html.error(error))
81 | }
82 |
83 |
84 | }
85 |
86 |
87 |
88 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/app/controllers/spark/TaskManager.scala:
--------------------------------------------------------------------------------
1 | package controllers
2 |
3 | import com.google.inject.Inject
4 | import models.TaskDataProvider.AppDataObject
5 | import models._
6 | import models.utils.Config
7 | import play.api.Logger
8 | import play.api.libs.ws.WS
9 | import play.api.mvc.{Action, Controller}
10 |
11 | import scala.concurrent.ExecutionContext.Implicits.global
12 | import scala.language.postfixOps
13 |
14 | /**
15 | * Created by kinge on 16/8/18.
16 | * Management related tasks run time
17 | */
18 | class TaskManager @Inject() (config: Config,taskProvider:TaskProvider[AppDataObject],taskDao: TaskDao,execute: Execute) extends Controller with Secured{
19 |
20 | import play.api.libs.json._
21 | import play.api.libs.functional.syntax._
22 |
23 | def tasklist=Action{
24 | Ok(views.html.tasklist())
25 | }
26 |
27 |
28 | def standaloneInfo =IsAuthenticated{
29 | username => implicit request =>
30 | implicit val residentWrites = Json.writes[TaskInfo]
31 | implicit val clusterWrites = Json.writes[TaskList]
32 | val json: JsValue = Json.toJson(TaskList(taskDao.getTaskInfoList(username)))
33 | Ok(json)
34 | }
35 |
36 |
37 |
38 | def yarnInfo =IsAuthenticated{
39 | username => implicit request =>
40 |
41 | implicit val yarnWrites: Writes[YarnTaskInfo] = (
42 | (__ \ "application_id").write[String] and
43 | (__ \ "name").write[String] and
44 | (__ \ "apptype").write[String] and
45 | (__ \ "queue").write[String] and
46 | (__ \ "starttime").write[Long] and
47 | (__ \ "state").write[String] and
48 | (__ \ "finishtime").write[Long]
49 | )(unlift(YarnTaskInfo.unapply))
50 |
51 | implicit val clusterWrites = Json.writes[YarnTaskList]
52 | val json: JsValue = Json.toJson(YarnTaskList(taskDao.getYarnTaskList(username)))
53 | Ok(json)
54 |
55 | }
56 |
57 | def killTask(appId:String): Unit ={
58 | //curl -v -X PUT -H "Content-Type: application/json" -d '{"state": "KILLED"}' 'http://localhost:8088/application_1489377540859_0013/state'
59 | WS.url(s"http://${config.getString("hadoop.yarn.host")}/ws/v1/cluster/apps/${appId}/state").withHeaders("Content-Type"->"application/json").put(Json.obj("state"->"KILLED")) map{
60 | response => response.status match {
61 | case 200 => Some{
62 | Logger.debug(s"post to kill ${appId} success" )
63 | }
64 | case _ => None
65 | }
66 | }
67 | }
68 |
69 |
70 |
71 | def kill(appId:String) =IsAuthenticated{
72 | username => implicit request =>
73 | if(appId.startsWith("application")){
74 | killTask(appId)
75 | }else{
76 | val spark_master = config.getString("spark.master.host")
77 | WS.url("http://"+spark_master+"/app/kill/").withQueryString(("terminate","true")).withQueryString(("id",appId)).post("content") map{
78 | response => response.status match {
79 | case 200 => Some{
80 | Logger.debug(s"post to kill ${appId} success" )
81 | }
82 | case _ => None
83 | }
84 | }
85 | }
86 | Ok("KILLED")
87 | }
88 |
89 |
90 | def rerun(appId:String) =IsAuthenticated{
91 | username => implicit request =>
92 | val executeModel: ExecuteModel = taskDao.getTaskArgs(appId)
93 | execute.main(executeModel) match {
94 | case JobSubmitSuccess(id) => {
95 | Logger.info(s"old Id====> $appId,new id====> $id")
96 | /**
97 | *Save the new task parameters add to the List
98 | */
99 | taskDao.saveTaskArgs(executeModel)(id)
100 | taskProvider.coverTask(appId)
101 | taskProvider.loadTaskInfo(AppDataObject(id,username));
102 | Ok(id)
103 | }
104 | case JobRunExecption(error) => Ok(error)
105 | case _ => NotFound
106 | }
107 | }
108 |
109 |
110 |
111 |
112 |
113 | }
114 |
--------------------------------------------------------------------------------
/app/models/Mail/RegisterExecption.scala:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | /**
4 | * Created by kinge on 16/6/17.
5 | */
6 | abstract class EmailStatus(val ex:String) extends Exception(ex){
7 | def unapply(arg: EmailStatus): Option[String] ={ Some(ex) }
8 | }
9 |
10 | case class EmailExecption(name:String) extends EmailStatus(name)
11 | case class VerifyException(name:String) extends EmailStatus(name)
12 | case class Success(name:String) extends EmailStatus(name)
13 | case class Failure(name:String) extends EmailStatus(name)
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/models/actor/ActorStack.scala:
--------------------------------------------------------------------------------
1 | package models.actor
2 |
3 | import akka.actor.Actor
4 |
5 | /**
6 | * Created by kinge on 16/7/4.
7 | */
8 | trait ActorStack extends Actor {
9 | def wrappedReceive: Receive
10 |
11 | def receive: Receive = {
12 | case x => if (wrappedReceive.isDefinedAt(x)) wrappedReceive(x) else unhandled(x)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app/models/actor/InstrumentedActor.scala:
--------------------------------------------------------------------------------
1 | package models.actor
2 |
3 |
4 | /**
5 | * Created by kinge on 16/7/4.
6 | */
7 | abstract class InstrumentedActor extends ActorStack with Slf4jLogging {
8 |
9 | override def preRestart(reason: Throwable, message: Option[Any]) {
10 | super.preRestart(reason, message)
11 | }
12 |
13 | override def postStop() { logger.warn(getClass.getName) }
14 | }
15 |
--------------------------------------------------------------------------------
/app/models/actor/Slf4jLogging.scala:
--------------------------------------------------------------------------------
1 | package models.actor
2 |
3 | import org.slf4j.LoggerFactory
4 |
5 | /**
6 | * Created by kinge 16/7/4.
7 | */
8 | trait Slf4jLogging extends ActorStack {
9 | val logger = LoggerFactory.getLogger(getClass)
10 | private[this] val myPath = self.path.toString
11 |
12 | withAkkaSourceLogging {
13 | logger.info("Starting actor " + getClass.getName)
14 | }
15 |
16 | override def receive: Receive = {
17 | case x =>
18 | withAkkaSourceLogging {
19 | super.receive(x)
20 | }
21 | }
22 |
23 | private def withAkkaSourceLogging(fn: => Unit) {
24 | try {
25 | org.slf4j.MDC.put("akkaSource", myPath)
26 | fn
27 | } finally {
28 | org.slf4j.MDC.remove("akkaSource")
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/models/actor/WebSocketChannel.scala:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import akka.actor.{ActorPath, ActorRef, Props}
4 | import models.actor.InstrumentedActor
5 | import play.api.libs.iteratee.Concurrent.Channel
6 | import play.api.libs.iteratee.{Concurrent, Enumerator}
7 |
8 | case class Send(data:String)
9 |
10 | /**
11 | * Created by kinge on 16/8/18.
12 | */
13 | object WebSocketChannel {
14 |
15 |
16 | def props(channel: Concurrent.Channel[String]):Props ={
17 | Props(new WebSocketChannel(channel))
18 | }
19 | }
20 |
21 | class WebSocketChannel(channel: Concurrent.Channel[String]) extends InstrumentedActor {
22 |
23 | override def wrappedReceive: Receive = {
24 | case Send(data) => channel.push(data)
25 |
26 | }
27 | }
--------------------------------------------------------------------------------
/app/models/depend/Depend.scala:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import com.tzavellas.sse.guice.ScalaModule
4 | import TaskDataProvider.AppDataObject
5 | import models.metrics.{HadoopMetricsProvider, MetricsProvider}
6 | import models.utils.{Config, Configuration}
7 | /**
8 | * Created by kinge on 16/8/22.
9 | */
10 | class Depend extends ScalaModule{
11 | override def configure(): Unit = {
12 | bind[TaskDao].to[TaskInfoDao]
13 | bind[TaskProvider[AppDataObject]].to[TaskDataProvider]
14 | bind[Config].to[Configuration]
15 | bind[MetricsProvider].to[HadoopMetricsProvider]
16 | bind[Execute].toInstance(new Execute)
17 |
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/app/models/deploy/CommonMessages.scala:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | /**
4 | * Created by kinge on 16/7/11.
5 | *
6 | */
7 | sealed trait CommonMessages
8 |
9 | /** Task submitted abnormal */
10 | case class JobSubmitExecption(msg:String) extends RuntimeException(msg) with CommonMessages
11 |
12 | /** End of the task to run */
13 | case class JobRunFinish(msg:String) extends RuntimeException(msg) with CommonMessages
14 |
15 | /** Task to run abnormal */
16 | case class JobRunExecption(msg:String) extends RuntimeException(msg) with CommonMessages
17 |
18 | /** Task submitted successfully */
19 | case class JobSubmitSuccess(msg:String) extends RuntimeException(msg) with CommonMessages
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/models/deploy/CreateBatchRequest.scala:
--------------------------------------------------------------------------------
1 | package models.deploy
2 |
3 | /**
4 | * Created by kinge on 16/7/13.
5 | */
6 | class CreateBatchRequest {
7 |
8 | var master :Option[String]=None
9 | var file: String = _
10 | var proxyUser: Option[String] = None
11 | var args: List[String] = List()
12 | var className: Option[String] = None
13 | var jars: List[String] = List()
14 | var pyFiles: List[String] = List()
15 | var files: List[String] = List()
16 | var driverMemory: Option[String] = None
17 | var driverCores: Option[String] = None
18 | var executorMemory: Option[String] = None
19 | var executorCores: Option[String] = None
20 | var total_executor_cores:Option[String] =None
21 | var numExecutors: Option[String] = None
22 | var jarLocation :Option[String] =None
23 | var archives: List[String] = List()
24 | var queue: Option[String] = None
25 | var name: Option[String] = None
26 | var conf: Map[String, String] = Map()
27 |
28 | }
29 |
30 | object CreateBatchRequest{
31 | def apply(): CreateBatchRequest = new CreateBatchRequest()
32 | }
--------------------------------------------------------------------------------
/app/models/deploy/Execute.scala:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import java.util.UUID
4 | import java.util.concurrent.TimeUnit
5 |
6 | import akka.actor.{ActorPath, ActorRef, ActorSelection, ActorSystem, Props}
7 | import akka.util.Timeout
8 | import models.JobManagerActor.{Initializ, StoreJar, SubmitJob}
9 | import play.api.libs.Files.TemporaryFile
10 | import play.api.mvc.MultipartFormData.FilePart
11 | import akka.pattern.ask
12 | import models.deploy.StatusListener.PushStack
13 | import models.deploy.{CreateBatchRequest, StatusListener}
14 | import models.utils.{Config, Configuration}
15 | import play.api.libs.iteratee.Concurrent
16 |
17 | import scala.concurrent.duration.Duration
18 | import scala.concurrent._
19 | /**
20 | * Created by kinge on 16/4/21.
21 | */
22 | class Execute {
23 |
24 | private[this] var _actorSystem :ActorSystem= _
25 | private[this] var _jobMange:ActorRef= _
26 | private[this] val _config:Config =new Configuration
27 | private[this] val _task_dao :TaskDao =new TaskInfoDao
28 | private[this] val _dao: JobFileDAO = new JobFileDAO(_config)
29 | private[this] var _statusLister :ActorRef =_
30 |
31 | def makeSystem ={
32 | _actorSystem = ActorSystem("jobSystem")
33 | _statusLister=_actorSystem.actorOf(StatusListener.props(_config,_dao,_task_dao),"StatusLinster")
34 | _jobMange=_actorSystem.actorOf((JobManagerActor.props(_config,_dao,_task_dao,_statusLister)), "JobManger")
35 | _actorSystem.registerOnTermination(System.exit(0))
36 | }
37 |
38 | makeSystem
39 |
40 |
41 | def register(user:String,id:String,channel: Concurrent.Channel[String])={
42 | val webSocketChannel = _actorSystem.actorOf(WebSocketChannel.props(channel))
43 | val actorPath: ActorPath = _actorSystem.actorOf(MessagePool.props(webSocketChannel),s"UserActor_$id").path
44 | _statusLister ! PushStack(user,actorPath)
45 | }
46 |
47 |
48 | private def getRequest(executeModel: ExecuteModel): CreateBatchRequest ={
49 | val timeoutSecs: Long = _config.getLong("job.request.timeout.seconds")
50 | Await.result( (_jobMange ? Initializ(executeModel))(Timeout(timeoutSecs,TimeUnit.SECONDS)) ,
51 | new Timeout(Duration.create(timeoutSecs,"seconds")).duration).asInstanceOf[CreateBatchRequest]
52 | }
53 |
54 |
55 | def main(executeModel: ExecuteModel)={
56 | val timeoutSecs: Long = _config.getLong("job.submit.timeout.seconds")
57 | Await.result(
58 | (_jobMange ? SubmitJob(getRequest(executeModel)))
59 | (Timeout(timeoutSecs,TimeUnit.SECONDS)),
60 | new Timeout(Duration.create(timeoutSecs,"seconds")).duration)
61 | }
62 |
63 |
64 | def storeJar(userName:String,filePart: FilePart[TemporaryFile]):Any = {
65 | val timeoutSecs: Long = _config.getLong("job.upload.timeout.seconds")
66 | Await.result( (_jobMange ? StoreJar(userName,filePart))
67 | (Timeout(timeoutSecs,TimeUnit.SECONDS)),
68 | new Timeout(Duration.create(timeoutSecs,"seconds")).duration);
69 | }
70 |
71 |
72 |
73 | }
--------------------------------------------------------------------------------
/app/models/deploy/MatchEngine.scala:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import models.utils.Configuration
4 |
5 | import scala.util.matching.Regex
6 |
7 | /**
8 | * Created by kinge on 16/8/18.
9 | */
10 | object MatchEngine {
11 |
12 | val config: Configuration = new Configuration
13 |
14 | /**
15 | * on_local pattern matching
16 | */
17 | val regex_on_local = """Starting executor ID driver on host localhost(.*)""".r.unanchored
18 |
19 | /**
20 | * on yarn-cluster pattern matching
21 | */
22 | val regex_on_yarn = """Submitted application (.*)""".r.unanchored
23 |
24 |
25 | /**
26 | * standalone pattern matching
27 | */
28 | val regex_on_standalone = """Spark cluster with app ID (.*)""".r.unanchored
29 |
30 |
31 | val spark_uri ="http://"+config.getString("spark.master.host")+"/json/"
32 |
33 | val yarn_uri="http://"+config.getString("hadoop.yarn.host")+"/ws/v1/cluster/apps"
34 |
35 |
36 |
37 | def matchMode :PartialFunction[String,Regex]={
38 | case m if m.startsWith("yarn") => regex_on_yarn
39 | case m if m.startsWith("spark") => regex_on_standalone
40 | case m if m.startsWith("local") => regex_on_local
41 | }
42 |
43 | def matchURI : PartialFunction[String,Option[(String,String)]]={
44 | case m if m.startsWith("application") => Some(("yarn",yarn_uri))
45 | case m if m.startsWith("app") => Some(("standalone",spark_uri))
46 | case m if m.startsWith("local") => None
47 |
48 | }
49 |
50 | }
51 |
52 |
53 |
--------------------------------------------------------------------------------
/app/models/deploy/MessagePool.scala:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import akka.actor.{ActorRef, AllForOneStrategy, PoisonPill, Props, Terminated}
4 | import akka.actor.SupervisorStrategy._
5 | import models.actor.InstrumentedActor
6 |
7 |
8 |
9 | sealed trait State
10 | object RUNNING extends State
11 | object FINISHED extends State
12 | object KILLED extends State
13 | object FAILED extends State
14 | object NEW extends State
15 |
16 |
17 |
18 | /**
19 | * Created by kinge on 16/8/31.
20 | */
21 | object MessagePool {
22 | def props(webSocketChannel: ActorRef): Props = Props(new MessagePool(webSocketChannel))
23 | }
24 |
25 |
26 | class MessagePool(webSocketChannel: ActorRef) extends InstrumentedActor {
27 |
28 | override val supervisorStrategy = AllForOneStrategy() {
29 | case anyException => Stop
30 | }
31 | override def preStart()={
32 | context.watch(webSocketChannel)
33 | }
34 |
35 |
36 |
37 | override def wrappedReceive: Receive = {
38 | case RUNNING => webSocketChannel ! Send("Job is running!")
39 | case FINISHED => webSocketChannel ! Send("Job finish!")
40 | case KILLED => webSocketChannel ! Send("Job is killed");
41 | case FAILED => webSocketChannel ! Send("Job failure!")
42 |
43 |
44 | case t: Terminated => self ! PoisonPill
45 |
46 | }
47 | }
--------------------------------------------------------------------------------
/app/models/deploy/StatusListener.scala:
--------------------------------------------------------------------------------
1 | package models.deploy
2 |
3 | import akka.actor.{ActorPath, PoisonPill, Props, Terminated}
4 | import models.actor.InstrumentedActor
5 | import models.deploy.StatusListener.{AppFinish, PushStack}
6 | import models.io.{Message, TaskMessage}
7 | import models.{FAILED, KILLED, _}
8 | import models.utils.Config
9 | import play.api.Logger
10 |
11 |
12 |
13 | /**
14 | * Created by kinge on 16/7/11.
15 | * Task status process
16 | */
17 | object StatusListener{
18 |
19 | var _paths =Map.empty[String,ActorPath]
20 |
21 | case class AppFinish(appId: String)
22 | case class PushStack(user:String,actorPath: ActorPath)
23 | case class AppRuning(msg:String)
24 |
25 |
26 | def props(config:Config,jobDAO: JobDAO,taskDao: TaskDao): Props = Props(classOf[StatusListener], config,jobDAO,taskDao)
27 |
28 | }
29 |
30 |
31 | class StatusListener(config:Config,jobDAO: JobDAO,taskDao: TaskDao) extends InstrumentedActor{
32 |
33 |
34 |
35 | override def wrappedReceive: Receive = {
36 | case AppFinish(appId) => {
37 |
38 | if (appId.startsWith("application")) {
39 | val user: String = taskDao.findyarnTaskUser(appId)
40 | val act = context.system.actorSelection(StatusListener._paths(user))
41 | taskDao.queryYarnState(appId).map {
42 | info =>
43 | Message.addMessage(TaskMessage(info.application_id, info.state, user))
44 | act ! Sealing(info.state);
45 | Logger.info(s"job finsh,current state==>" + info.state);
46 | }
47 | } else {
48 | val user = taskDao.findTaskUser(appId)
49 | val act = context.system.actorSelection(StatusListener._paths(user))
50 | taskDao.queryState(appId).map {
51 | info =>
52 | Message.addMessage(TaskMessage(info.app_id, info.state, user));
53 | act ! Sealing(info.state);
54 | Logger.info(s"job finsh,current state==>" + info.state);
55 | }
56 | }
57 | }
58 | case PushStack(user,actorPath) => StatusListener._paths+=(user->actorPath)
59 |
60 | case t: Terminated => self ! PoisonPill
61 | }
62 |
63 |
64 | def Sealing:PartialFunction[String,State] ={
65 | case m if(m.equals("RUNNING")) => RUNNING
66 | case m if(m.equals("FINISHED")) => FINISHED
67 | case m if(m.equals("KILLED")) => KILLED
68 | case m if(m.equals("FAILED")) => FAILED
69 | case _ => FINISHED
70 |
71 | }
72 |
73 |
74 |
75 |
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/app/models/deploy/package.scala:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | /**
4 | * Created by kinge on 16/7/22.
5 | * Task submitted, deployment operations
6 | */
7 | package object deploy {
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/app/models/deploy/process/LineBufferedProcess.scala:
--------------------------------------------------------------------------------
1 | package models.deploy.process
2 |
3 | import akka.actor.ActorRef
4 | import org.apache.spark.Logging
5 |
6 |
7 | /**
8 | * Created by kinge on 16/7/12.
9 | */
10 | class LineBufferedProcess(master: Option[String],act:ActorRef,process: Process) extends Logging {
11 |
12 | private[this] val _inputStream = new LineBufferedStream(master,act,process.getInputStream)
13 | private[this] val _errorStream = new LineBufferedStream(master,act,process.getErrorStream)
14 |
15 | def inputLines: IndexedSeq[String] = _inputStream.lines
16 | def errorLines: IndexedSeq[String] = _errorStream.lines
17 |
18 | def inputIterator: Iterator[String] = _inputStream.iterator
19 | def errorIterator: Iterator[String] = _errorStream.iterator
20 |
21 | def destroy(): Unit = {
22 | process.destroy()
23 | }
24 |
25 | def isAlive: Boolean = isProcessAlive(process)
26 |
27 | def exitValue(): Int = {
28 | process.exitValue()
29 | }
30 |
31 | def waitFor(): Int = {
32 | val returnCode = process.waitFor()
33 | _inputStream.waitUntilClose()
34 | _errorStream.waitUntilClose()
35 | returnCode
36 | }
37 |
38 | def isProcessAlive(process: Process): Boolean = {
39 | try {
40 | process.exitValue()
41 | false
42 | } catch {
43 | case _: IllegalThreadStateException =>
44 | true
45 | }
46 | }
47 | }
48 |
49 |
--------------------------------------------------------------------------------
/app/models/deploy/process/LineBufferedStream.scala:
--------------------------------------------------------------------------------
1 | package models.deploy.process
2 |
3 | import java.io.InputStream
4 | import java.util.UUID
5 | import java.util.concurrent.atomic.AtomicInteger
6 | import java.util.concurrent.locks.ReentrantLock
7 |
8 | import akka.actor.ActorRef
9 | import models.{JobSubmitSuccess, MatchEngine}
10 | import org.apache.spark.Logging
11 | import play.api.{Logger, cache}
12 |
13 | import scala.io.Source
14 | import scala.util.matching.Regex
15 | /**
16 | * Created by kinge on 16/7/12.
17 | *
18 | */
19 | class LineBufferedStream(master:Option[String],act:ActorRef, inputStream: InputStream) extends Logging {
20 |
21 | private[this] var _lines: IndexedSeq[String] = IndexedSeq()
22 |
23 | private[this] val _lock = new ReentrantLock()
24 | private[this] val _condition = _lock.newCondition()
25 | private[this] var _finished = false
26 | private val atomicCounter = new AtomicInteger()
27 |
28 | def nextCount(): Int = atomicCounter.getAndIncrement()
29 |
30 | private val thread = new Thread {
31 | override def run() = {
32 | val lines:Iterator[String] = Source.fromInputStream(inputStream).getLines()
33 | val uid: String = UUID.randomUUID().toString
34 | val regex: Regex = MatchEngine.matchMode(master.get)
35 |
36 | for (line <- lines) {
37 | _lock.lock()
38 | line match {
39 | case regex(id) => act ! JobSubmitSuccess(id);
40 | case _ => Logger.info(line)
41 | }
42 | try {
43 | _lines = _lines :+ line
44 | _condition.signalAll()
45 | } finally {
46 | _lock.unlock()
47 | }
48 | }
49 | _lock.lock()
50 | try {
51 | _finished = true
52 | _condition.signalAll()
53 | } finally {
54 | _lock.unlock()
55 | }
56 | }
57 | }
58 | thread.setDaemon(true)
59 | thread.start()
60 |
61 | def lines: IndexedSeq[String] = _lines
62 |
63 | def iterator: Iterator[String] = {
64 | new LinesIterator
65 | }
66 |
67 | def waitUntilClose(): Unit = thread.join()
68 |
69 | private class LinesIterator extends Iterator[String] {
70 | private[this] var index = 0
71 |
72 |
73 | override def hasNext: Boolean = {
74 | if (index < _lines.length) {
75 | true
76 | } else {
77 | _lock.lock()
78 | try {
79 | if (_finished) {
80 | false
81 | } else {
82 | _condition.await()
83 | index < _lines.length
84 | }
85 | } finally {
86 | _lock.unlock()
87 | }
88 | }
89 | }
90 |
91 | override def next(): String = {
92 | val line = _lines(index)
93 | index += 1
94 | line
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/app/models/deploy/process/SparkProcessBuilder.scala:
--------------------------------------------------------------------------------
1 |
2 | package models.deploy.process
3 |
4 | import akka.actor.ActorRef
5 | import org.apache.spark.Logging
6 | import play.api.Logger
7 |
8 | import scala.collection.JavaConverters._
9 | import scala.collection.immutable.HashMap.HashMap1
10 | import scala.collection.mutable
11 | import scala.collection.mutable.ArrayBuffer
12 |
13 |
14 | /**
15 | * Created by kinge on 16/7/12.
16 | */
17 | class SparkProcessBuilder(act:ActorRef) extends Logging {
18 |
19 | private[this] var _executable: String = _
20 | private[this] var _master: Option[String] = None
21 | private[this] var _deployMode: Option[String] = None
22 | private[this] var _className: Option[String] = None
23 | private[this] var _executor_memory: Option[String]=None
24 | private[this] var _num_executors: Option[String] = None
25 | private[this] var _driver_memory: Option[String] = None
26 | private[this] var _total_executor_cores: Option[String] = None
27 | private[this] var _name: Option[String] = Some("app")
28 | private[this] var _jarLocation:Option[String] =None
29 | private[this] var _queue: Option[String] = None
30 | private[this] var _redirectOutput: Option[ProcessBuilder.Redirect] = None
31 | private[this] var _redirectError: Option[ProcessBuilder.Redirect] = None
32 | private[this] var _redirectErrorStream: Option[Boolean] = None
33 |
34 | def jarLocation(jarLocation:String) :SparkProcessBuilder ={
35 | _jarLocation =Some(jarLocation)
36 | this
37 | }
38 |
39 | def executable(executable: String): SparkProcessBuilder = {
40 | _executable = executable
41 | this
42 | }
43 |
44 | def master(masterUrl: String): SparkProcessBuilder = {
45 | _master = Some(masterUrl)
46 | this
47 | }
48 |
49 | def deployMode(deployMode: String): SparkProcessBuilder = {
50 | _deployMode = Some(deployMode)
51 | this
52 | }
53 |
54 | def className(className: String): SparkProcessBuilder = {
55 | _className = Some(className)
56 | this
57 | }
58 |
59 | def name(name: String): SparkProcessBuilder = {
60 | _name = Some(name)
61 | this
62 | }
63 |
64 | def executorCores(executorCores: String): SparkProcessBuilder = {
65 | _total_executor_cores = Some(executorCores)
66 | this
67 | }
68 |
69 | def executorMemory(executorMemory: String): SparkProcessBuilder = {
70 | _executor_memory =Some(executorMemory)
71 | this
72 | }
73 |
74 | def numExecutors(numExecutors: String): SparkProcessBuilder = {
75 | _num_executors=Some(numExecutors)
76 | this
77 | }
78 |
79 | def driverMemory(driverMemory: String): SparkProcessBuilder = {
80 | _driver_memory=Some(driverMemory)
81 | this
82 | }
83 |
84 | def queue(queue: String): SparkProcessBuilder = {
85 | _queue = Some(queue)
86 | this
87 | }
88 |
89 | def redirectOutput(redirect: ProcessBuilder.Redirect): SparkProcessBuilder = {
90 | _redirectOutput = Some(redirect)
91 | this
92 | }
93 |
94 | def redirectError(redirect: ProcessBuilder.Redirect): SparkProcessBuilder = {
95 | _redirectError = Some(redirect)
96 | this
97 | }
98 |
99 | def redirectErrorStream(redirect: Boolean): SparkProcessBuilder = {
100 | _redirectErrorStream = Some(redirect)
101 | this
102 | }
103 |
104 | def start(file: Option[String], args: Traversable[String]): LineBufferedProcess = {
105 | executable(file.get)
106 | var arguments = ArrayBuffer[String](_executable)
107 |
108 | def addOpt(option: String, value: Option[String]): Unit = {
109 | value.foreach { v =>
110 | arguments += option
111 | arguments += v
112 | }
113 | }
114 |
115 | def addArg(value: Option[String]): Unit = {
116 | value.foreach{
117 | v=> arguments += v
118 | }
119 | }
120 |
121 | def addList(values:Traversable[String]): Unit ={
122 | if(values.nonEmpty){
123 | values.foreach{ v =>
124 | arguments += v
125 | }
126 | }
127 | }
128 |
129 |
130 | addOpt("--master", _master)
131 | addOpt("--class", _className)
132 | addOpt("--num-executors",_num_executors)
133 | addOpt("--executor-memory", _executor_memory)
134 | addOpt("--driver-memory",_driver_memory)
135 | addOpt("--total-executor-cores", _total_executor_cores)
136 | addArg(_jarLocation)
137 | addList(args)
138 | Logger.info(s"args $arguments")
139 |
140 | val pb = new ProcessBuilder(arguments.asJava)
141 | pb.redirectErrorStream(true)
142 | pb.redirectInput(java.lang.ProcessBuilder.Redirect.PIPE)
143 | val env = pb.environment()
144 |
145 | env.put("TEST_CLASSPATH", sys.props("java.class.path"))
146 |
147 | _redirectOutput.foreach(pb.redirectOutput)
148 | _redirectError.foreach(pb.redirectError)
149 | _redirectErrorStream.foreach(pb.redirectErrorStream)
150 |
151 | val process: LineBufferedProcess = new LineBufferedProcess(_master,act,pb.start())
152 | process.waitFor()
153 | process
154 | }
155 |
156 | }
157 |
--------------------------------------------------------------------------------
/app/models/deploy/task/TaskProvider.scala:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | /**
4 | * Created by king on 16/8/22.
5 | */
6 | trait TaskProvider[T] {
7 |
8 | def coverTask(appId: String)
9 |
10 | def loadTaskInfo(app: T)
11 |
12 | def proTaskOnMaster(app: T)
13 |
14 | def proTaskOnYarn(app: T)
15 |
16 | }
17 |
18 |
--------------------------------------------------------------------------------
/app/models/io/JobDAO.scala:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import org.joda.time.DateTime
4 | import play.api.libs.Files.TemporaryFile
5 | import play.api.mvc.MultipartFormData.FilePart
6 |
7 | /**
8 | * Created by kinge on 16/7/11.
9 | */
10 | case class JarInfo(userName: String, uploadTime: String, location:String)
11 |
12 |
13 | trait JobDAO {
14 |
15 | def saveJar(userName: String, uploadTime: DateTime, filePart: FilePart[TemporaryFile]):String
16 |
17 |
18 | }
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/models/io/JobFileDAO.scala:
--------------------------------------------------------------------------------
1 | package models
2 | import java.io.File
3 |
4 | import org.joda.time.DateTime
5 | import play.api.db.DB
6 | import play.api.libs.Files.TemporaryFile
7 | import play.api.mvc.MultipartFormData.FilePart
8 | import anorm._
9 | import com.google.inject.Inject
10 | import models.utils.Config
11 | import play.api.Play.current
12 |
13 | import scala.collection.mutable
14 |
15 | /**
16 | * Created by kinge on 16/7/11.
17 | */
18 | class JobFileDAO(config: Config) extends JobDAO {
19 |
20 |
21 | private val rootDir = config.getString("job.upload.path");
22 | private val rootDirFile = new File(rootDir)
23 | private val rootOV = true
24 | private val apps = mutable.HashMap.empty[String, Seq[DateTime]]
25 |
26 |
27 | init
28 |
29 | private def init() {
30 | if (!rootDirFile.exists()) {
31 | if (!rootDirFile.mkdirs()) {
32 | throw new RuntimeException("Could not create directory " + rootDir)
33 | }
34 | }
35 | }
36 |
37 |
38 | override def saveJar(userName: String, uploadTime: DateTime, filePart: FilePart[TemporaryFile]):String= {
39 | val jobName:String=filePart.filename
40 | val file: File = new File(s"$rootDir$jobName")
41 | filePart.ref.moveTo(file,rootOV)
42 | addJar(jobName,uploadTime)
43 | file.getAbsoluteFile.getAbsolutePath
44 | }
45 |
46 | private def addJar(jobName: String, uploadTime: DateTime) {
47 | if (apps.contains(jobName)) {
48 | apps(jobName) = uploadTime +: apps(jobName)
49 | } else {
50 | apps(jobName) = Seq(uploadTime)
51 | }
52 | }
53 |
54 |
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/app/models/io/Message.scala:
--------------------------------------------------------------------------------
1 | package models.io
2 |
3 | import anorm.SqlParser._
4 | import anorm._
5 | import play.api.Play.current
6 | import play.api.db.DB
7 |
8 | /**
9 | * Created by kinge on 16/9/1.
10 | */
11 |
12 | case class TaskMessage(id:String,state:String,user:String)
13 | case class MessageList(list:Seq[TaskMessage])
14 | object Message {
15 | val taskmsg = {
16 | get[String]("task_msg.id") ~
17 | get[String]("task_msg.state") ~
18 | get[String]("task_msg.user") map {
19 | case id ~ state ~ user => TaskMessage(id,state,user)
20 | }
21 | }
22 |
23 |
24 | def addMessage(taskMessage: TaskMessage): TaskMessage ={
25 | DB.withConnection { implicit connection =>
26 | SQL(
27 | """
28 | insert into task_msg values (
29 | {id}, {state}, {user}
30 | )
31 | """).on(
32 | 'id -> taskMessage.id,
33 | 'state -> taskMessage.state,
34 | 'user -> taskMessage.user
35 | ).executeUpdate()
36 | }
37 | taskMessage
38 | }
39 |
40 | def getMessages(user:String):Seq[TaskMessage]={
41 | DB.withConnection { implicit connection =>
42 | play.api.db.DB.withConnection { implicit connection =>
43 | SQL("select * from task_msg where user={user}").on(
44 | 'user -> user
45 | ).as(taskmsg *)
46 | }
47 | }
48 | }
49 |
50 | def deleteMessage(appId:String)={
51 | DB.withConnection { implicit connection =>
52 | SQL(
53 | """
54 | delete from task_msg where id={id}
55 | """).on(
56 | 'id -> appId
57 | ).executeUpdate()
58 | }
59 | }
60 |
61 | def deleteAllMessage(user:String)={
62 | DB.withConnection { implicit connection =>
63 | SQL(
64 | """
65 | delete from task_msg where user={user}
66 | """).on(
67 | 'user -> user
68 | ).executeUpdate()
69 | }
70 | }
71 |
72 |
73 |
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/app/models/io/MetricsData.scala:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import anorm.SqlParser._
4 | import anorm._
5 | import models.utils.Configuration
6 | import play.api.db.DB
7 | import play.api.Play.current
8 |
9 | /**
10 | * Created by kinge on 2017/2/17.
11 | */
12 | case class MetricsData(
13 | receivedBytes:Long,
14 | sentBytes:Long,
15 | timestamp:Long,
16 | host:String,
17 | CapacityUsed:Long,
18 | CapacityRemaining:Long,
19 | CapacityUsedNonDFS:Long,
20 | MemNonHeapUsedM:Double,
21 | MemHeapUsedM:Double
22 | )
23 |
24 | object MetricsData {
25 |
26 | val config: Configuration = new Configuration
27 |
28 | val metricsData ={
29 | get[Long]("hadoop_metrics.ReceivedBytes")~
30 | get[Long]("hadoop_metrics.SentBytes") ~
31 | get[Long]("hadoop_metrics.timestamp") ~
32 | get[String]("hadoop_metrics.host") ~
33 | get[Long]("hadoop_metrics.CapacityUsed") ~
34 | get[Long]("hadoop_metrics.CapacityRemaining") ~
35 | get[Long]("hadoop_metrics.CapacityUsedNonDFS") ~
36 | get[Double]("hadoop_metrics.MemNonHeapUsedM") ~
37 | get[Double]("hadoop_metrics.MemHeapUsedM") map {
38 | case receivedBytes ~
39 | sentBytes ~
40 | timestamp ~
41 | host ~
42 | capacityUsed ~
43 | capacityRemaining ~
44 | capacityUsedNonDFS ~
45 | memNonHeapUsedM ~
46 | memHeapUsedM
47 | =>
48 | MetricsData(receivedBytes,sentBytes,timestamp,host,capacityUsed,capacityRemaining,capacityUsedNonDFS,memNonHeapUsedM,memHeapUsedM)
49 | }
50 | }
51 |
52 | def updateMetrics(metricsData: MetricsData)={
53 | play.api.db.DB.withConnection { implicit connection =>
54 | SQL(
55 | """
56 | insert into hadoop_metrics values (
57 | {ReceivedBytes},{SentBytes},{host},{CapacityUsed},{CapacityRemaining},{CapacityUsedNonDFS},{MemHeapUsedM},{MemNonHeapUsedM},{timestamp}
58 | )
59 | """).on(
60 | 'ReceivedBytes -> metricsData.receivedBytes,
61 | 'SentBytes -> metricsData.sentBytes,
62 | 'host -> metricsData.host,
63 | 'CapacityUsed -> metricsData.CapacityUsed,
64 | 'CapacityRemaining -> metricsData.CapacityRemaining,
65 | 'CapacityUsedNonDFS -> metricsData.CapacityUsedNonDFS,
66 | 'MemNonHeapUsedM -> metricsData.MemNonHeapUsedM,
67 | 'MemHeapUsedM -> metricsData.MemHeapUsedM,
68 | 'timestamp -> metricsData.timestamp
69 | ).executeUpdate()
70 | }
71 | metricsData
72 | }
73 |
74 |
75 | def getMetrics():Seq[MetricsData]={
76 | DB.withConnection { implicit connection =>
77 | play.api.db.DB.withConnection { implicit connection =>
78 | SQL("select * from hadoop_metrics where host = {host} order by timestamp desc limit 0,36 ")
79 | .on('host -> config.getString("hadoop.metrics.host")).as(metricsData *)
80 | }
81 | }
82 | }
83 |
84 |
85 |
86 |
87 |
88 |
89 | }
90 |
--------------------------------------------------------------------------------
/app/models/io/TaskDao.scala:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import anorm.SqlParser._
4 | import anorm.~
5 |
6 |
7 | /**
8 | * Created by kinge on 16/8/22.
9 | */
10 | case class TaskInfo(
11 | app_id:String,
12 | name:String,
13 | cores: Option[Int],
14 | memoryperslave:Long,
15 | state:String,
16 | starttime:Long,
17 | duration:Long
18 | )
19 |
20 | case class YarnTaskInfo(
21 | application_id:String,
22 | name:String,
23 | apptype:String,
24 | queue:String,
25 | starttime:Long,
26 | state:String,
27 | finishtime:Long
28 | )
29 | case class YarnTaskList(list:Seq[YarnTaskInfo])
30 | case class TaskList(list:Seq[TaskInfo])
31 |
32 |
33 | trait TaskDao{
34 |
35 | val yarn = {
36 | get[String]("task_yarn.application_id") ~
37 | get[String]("task_yarn.name") ~
38 | get[String]("task_yarn.apptype")~
39 | get[String]("task_yarn.queue") ~
40 | get[Long]("task_yarn.starttime")~
41 | get[String]("task_yarn.state")~
42 | get[Long]("task_yarn.finishtime") map {
43 | case application_id ~ name ~ apptype ~ queue ~starttime ~state~finishtime=> YarnTaskInfo(application_id,name,apptype,queue,starttime,state,finishtime)
44 | }
45 | }
46 |
47 | val standalone = {
48 | get[String]("task_standalone.app_id") ~
49 | get[String]("task_standalone.name") ~
50 | get[Int]("task_standalone.cores")~
51 | get[Long]("task_standalone.memoryperslave")~
52 | get[String]("task_standalone.state") ~
53 | get[Long]("task_standalone.starttime")~
54 | get[Long]("task_standalone.duration") map {
55 | case app_id ~ name ~ cores ~ memoryperslave ~ state ~ starttime ~ duration => TaskInfo(app_id,name,Some(cores),memoryperslave,state,starttime,duration)
56 | }
57 | }
58 |
59 |
60 | val args = {
61 | get[String]("task_args.master") ~
62 | get[String]("task_args.executeClass") ~
63 | get[String]("task_args.numExecutors")~
64 | get[String]("task_args.driverMemory")~
65 | get[String]("task_args.executorMemory") ~
66 | get[String]("task_args.total_executor_cores") ~
67 | get[String]("task_args.jarLocation")~
68 | get[String]("task_args.args1") map {
69 | case master ~ executeClass ~ numExecutors ~ driverMemory ~ executorMemory ~ total_executor_cores ~ jarLocation ~ args1 => ExecuteModel(master,executeClass,numExecutors,driverMemory,executorMemory,total_executor_cores,jarLocation,args1)
70 | }
71 | }
72 |
73 |
74 | def saveTaskArgs(executeModel: ExecuteModel)(appId:String) : ExecuteModel
75 |
76 |
77 | def getTaskArgs(appId:String):ExecuteModel
78 |
79 |
80 | def saveTask(task: TaskInfo)(user:String): TaskInfo
81 |
82 | def saveYarnTask(yarnTask: YarnTaskInfo)(user:String): YarnTaskInfo
83 |
84 | def getTaskInfoList(user:String): Seq[TaskInfo]
85 |
86 | def getYarnTaskList(user:String): Seq[YarnTaskInfo]
87 |
88 | def updateYarnTaskList(tasks: Seq[YarnTaskInfo])
89 |
90 | def updateTaskList(tasks:Seq[TaskInfo])
91 |
92 | def queryYarnState(appId:String):Option[YarnTaskInfo]
93 |
94 | def queryState(appId:String):Option[TaskInfo]
95 |
96 | def rmYarnTaskInfo(appId:String)
97 |
98 | def rmTaskInfo(appId:String)
99 |
100 | def findTaskUser(appId:String):String
101 |
102 | def findyarnTaskUser(appId:String):String
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 | }
--------------------------------------------------------------------------------
/app/models/io/package.scala:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | /**
4 | * Database related operations
5 | * Created by kinge on 16/7/6.
6 | */
7 | package object io {
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/app/models/metrics/BaseInfo.scala:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import play.api.libs.json.JsValue
4 |
5 | /**
6 | * Created by kinge on 2016/8/21.
7 | */
8 | sealed class BaseInfo;
9 | /**
10 | * RPC INFO
11 | * @param rec_rpc
12 | * @param sent_rpc
13 | */
14 | case class RPCInfo(rec_rpc:JsValue,
15 | sent_rpc :JsValue
16 | ) extends BaseInfo
17 |
18 | /**
19 | * HDFS
20 | * @param capacityUsed
21 | * @param capacityRemaining
22 | * @param capacityUsedNonDFS
23 | */
24 | case class DFSInfo(capacityUsed:JsValue,
25 | capacityRemaining:JsValue,
26 | capacityUsedNonDFS:JsValue
27 | ) extends BaseInfo
28 |
29 | /**
30 | * MEM
31 | * @param memHeapUsedM
32 | * @param memNonHeapUsedM
33 | */
34 | case class MEMInfo(memHeapUsedM:JsValue,memNonHeapUsedM:JsValue) extends BaseInfo
35 |
36 |
37 | case class BadInfo(msg:String)extends BaseInfo{
38 |
39 | override def toString: String = this.msg
40 |
41 |
42 | }
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/app/models/metrics/HadoopMetricsProvider.scala:
--------------------------------------------------------------------------------
1 | package models.metrics
2 |
3 | import akka.actor.Cancellable
4 | import models.{BaseInfo, MetricsData, RPCInfo}
5 | import models.TaskDataProvider.{AppDataObject, TaskData, YarnTaskInfoList}
6 |
7 | import scala.concurrent.ExecutionContext.Implicits.global
8 | import scala.language.postfixOps
9 | import models.utils.{Config, Configuration}
10 | import play.api.libs.json.{JsValue, Json}
11 | import play.api.libs.ws.WS
12 | import play.libs.Akka
13 |
14 | import scala.io.{BufferedSource, Source}
15 |
16 | /**
17 | * Created by kinge on 2016/6/7.
18 | */
19 | class HadoopMetricsProvider extends MetricsProvider{
20 |
21 | private[this] val _config:Config =new Configuration
22 | private[this] val _factory: Factory = new MetricsFactory
23 |
24 | val startMetrics: Cancellable = scheduleMetricsDate
25 |
26 |
27 |
28 | private def getMENInfo(json:JsValue)={
29 | val memNonHeapUsedM = (json \\ "MemNonHeapUsedM")(0).as[Double]
30 | val memHeapUsedM = (json \\ "MemHeapUsedM")(0).as[Double]
31 | (memNonHeapUsedM,memHeapUsedM)
32 | }
33 |
34 |
35 | private def getDFSInfo(json:JsValue)={
36 | val CapacityUsed = (json \\ "CapacityUsed")(0).as[Long]
37 | val CapacityRemaining = (json \\ "CapacityRemaining")(0).as[Long]
38 | val CapacityUsedNonDFS=(json \\ "CapacityUsedNonDFS")(0).as[Long]
39 | (CapacityUsed,CapacityRemaining,CapacityUsedNonDFS)
40 | }
41 |
42 |
43 |
44 | private def getRpcInfo(json:JsValue)={
45 | val receivedBytes = (json \\ "ReceivedBytes")(0).as[Long]
46 | val sentBytes = (json \\ "SentBytes")(0).as[Long]
47 | (receivedBytes,sentBytes)
48 | }
49 |
50 |
51 |
52 | import scala.concurrent.duration._
53 |
54 | private[this] def scheduleMetricsDate={
55 | Akka.system.scheduler.schedule(0.second, _config.getLong("metrics.data-update.interval-ms") millis, new Runnable {
56 | override def run(): Unit = {
57 | val url="http://"+_config.getString("hadoop.metrics.host")+":50070/jmx"
58 | WS.url(url).get() map{
59 | response => response.status match {
60 | case 200 => Some{
61 |
62 | val (receivedBytes,sentBytes) = getRpcInfo(response.json)
63 | val (capacityUsed,capacityRemaining,capacityUsedNonDFS)=getDFSInfo(response.json)
64 | val (memNonHeapUsedM,memHeapUsedM)=getMENInfo(response.json)
65 |
66 |
67 | MetricsData.updateMetrics(MetricsData(
68 | receivedBytes,
69 | sentBytes,
70 | System.currentTimeMillis(),
71 | _config.getString("hadoop.metrics.host"),
72 | capacityUsed,
73 | capacityRemaining,
74 | capacityUsedNonDFS,
75 | memNonHeapUsedM,
76 | memHeapUsedM))
77 | }
78 | case _ => None
79 | }
80 | }
81 |
82 | }
83 | })
84 | }
85 |
86 | override def getMetricMouled[T](clz: Class[_]): T = {
87 | _factory.queryMetrics(clz)
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/app/models/metrics/MetricsFactory.scala:
--------------------------------------------------------------------------------
1 | package models.metrics
2 |
3 | import models._
4 | import play.api.Logger
5 | import play.api.libs.json.{JsValue, Json}
6 |
7 | /**
8 | * Created by kinge on 2016/7/23.
9 | * metric data computing related @host:port/jmx
10 | *
11 | */
12 | private [metrics] abstract class Factory {
13 | var metrics: Seq[MetricsData] =_
14 |
15 |
16 | def queryMetrics[T](clz:Class[_]) :T={
17 | metrics = MetricsData.getMetrics()
18 | clz.getClass match {
19 | case _=> queryMetricsModels(clz).asInstanceOf[T]
20 | }
21 | }
22 |
23 | def queryMetricsModels:PartialFunction[Class[_],BaseInfo] = ???
24 |
25 | }
26 |
27 | private[metrics] class MetricsFactory extends Factory{
28 |
29 | private[this] val BYTE_NUMBER=1024
30 |
31 |
32 | override def queryMetricsModels:PartialFunction[Class[_],BaseInfo]={
33 | case x if x == RPCInfo.getClass =>
34 | RPCInfo(ReceivedInterval, SentInterval)
35 |
36 | case x if x == DFSInfo.getClass =>
37 | DFSInfo(CapacityUsed,CapacityRemaining,CapacityUsedNonDFS)
38 |
39 | case x if x==MEMInfo.getClass =>
40 | MEMInfo(MemHeapUsedM,MemNonHeapUsedM)
41 |
42 | case _ => BadInfo("error")
43 | }
44 |
45 |
46 |
47 | private [this] def MemNonHeapUsedM={
48 | val result:Seq[Double]={
49 | for(me<- metrics) yield (me.MemNonHeapUsedM)
50 | }
51 | Json.toJson(result.reverse)
52 | }
53 |
54 |
55 | private [this] def MemHeapUsedM ={
56 | val result:Seq[Double]={
57 | for(me<- metrics) yield (me.MemHeapUsedM)
58 | }
59 | Json.toJson(result.reverse)
60 | }
61 |
62 |
63 | private [this] def CapacityUsed = {
64 | val result:Seq[Long]={
65 | for(me<- metrics) yield (me.CapacityUsed / (BYTE_NUMBER*BYTE_NUMBER*BYTE_NUMBER))
66 | }
67 | Json.toJson(result.reverse)
68 | }
69 |
70 | private [this] def CapacityRemaining ={
71 | val result:Seq[Long]={
72 | for(me<- metrics) yield (me.CapacityRemaining / (BYTE_NUMBER*BYTE_NUMBER*BYTE_NUMBER))
73 | }
74 | Json.toJson(result.reverse)
75 | }
76 |
77 | private [this] def CapacityUsedNonDFS ={
78 | val result:Seq[Long]={
79 | for(me<- metrics) yield (me.CapacityUsedNonDFS / (BYTE_NUMBER*BYTE_NUMBER*BYTE_NUMBER))
80 | }
81 | Json.toJson(result.reverse)
82 | }
83 |
84 |
85 |
86 | private[this] def SentInterval ={
87 | val result:Seq[Long]={
88 | for(i<- 0 until metrics.length)
89 | yield if(i==metrics.length-1){
90 | 0
91 | }else{
92 | (metrics(i).sentBytes-metrics(i+1).sentBytes)/BYTE_NUMBER
93 | }
94 | }.dropRight(1)
95 | Json.toJson(result.reverse)
96 | }
97 |
98 |
99 | private[this] def ReceivedInterval ={
100 | val result:Seq[Long]={
101 | for(i<- 0 until metrics.length)
102 | yield if(i==metrics.length-1){
103 | 0
104 | }else{
105 | (metrics(i).receivedBytes-metrics(i+1).receivedBytes)/BYTE_NUMBER
106 | }
107 | }.dropRight(1)
108 | Json.toJson(result.reverse)
109 | }
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 | }
123 |
--------------------------------------------------------------------------------
/app/models/metrics/MetricsProvider.scala:
--------------------------------------------------------------------------------
1 | package models.metrics
2 |
3 | /**
4 | * Created by kinge on 2016/6/2.
5 | */
6 | trait MetricsProvider {
7 |
8 | def getMetricMouled[T](clz:Class[_]):T
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/app/models/shell/kill_job.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | arg1=$1
4 | echo "will kill the job with id =$arg1"
5 | exec yarn application -kill $arg1
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/models/user/User.scala:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import anorm.SqlParser._
4 | import anorm._
5 | import play.api.Play.current
6 | import play.api.db.DB
7 |
8 | case class User(email: String, name: String, password: String,status:Int =0,audit:Int=0)
9 | case class Registration(email:String,name:String,password:String,repassword:String)
10 | case class Verify(email:String,captcha:String,name:String)
11 | case class UserList(all:Seq[User])
12 |
13 | object User {
14 |
15 | /**
16 | * Parse a User from a ResultSet
17 | */
18 | val simple = {
19 | get[String]("user.email") ~
20 | get[String]("user.name") ~
21 | get[String]("user.password")~
22 | get[Int]("user.status") map {
23 | case email ~ name ~ password ~ status => User(email, name, password,status)
24 | }
25 | }
26 |
27 |
28 |
29 | /**
30 | * Retrieve all users.
31 | */
32 | def findAll: Seq[User] = {
33 | DB.withConnection { implicit connection =>
34 | play.api.db.DB.withConnection { implicit connection =>
35 | SQL("select * from user").as(User.simple *)
36 | }
37 | }
38 | }
39 |
40 | /**
41 | * Authenticate a User.
42 | */
43 | def authenticate(email: String, password: String): Option[User] = {
44 | DB.withConnection { implicit connection =>
45 | play.api.db.DB.withConnection { implicit connection =>
46 | SQL(
47 | """
48 | select * from user where
49 | email = {email} and password = {password}
50 | """).on(
51 | 'email -> email,
52 | 'password -> MD5Utils.encode2hex(password)).as(User.simple.singleOpt)
53 | }
54 | }
55 | }
56 |
57 |
58 |
59 | /**
60 | * Authenticate a User.
61 | */
62 | def hasUser(email: String): Boolean = {
63 | DB.withConnection { implicit connection =>
64 | play.api.db.DB.withConnection { implicit connection =>
65 | SQL(
66 | """
67 | select * from user where
68 | email = {email}
69 | """).on(
70 | 'email -> email
71 | ).as(User.simple.singleOpt)
72 | }
73 | }.isDefined
74 | }
75 |
76 |
77 | def isActivate(email: String): Option[User] ={
78 | play.api.db.DB.withConnection { implicit connection =>
79 | SQL(
80 | """
81 | select * from user where
82 | email = {email} and status = 1
83 | """).on(
84 | 'email -> email
85 | ).
86 | as(User.simple.singleOpt)
87 | }
88 | }
89 |
90 |
91 |
92 | def verifying(registration : Registration): User={
93 | val newUser = User(registration.email, registration.name,registration.password)
94 | create(newUser)
95 | }
96 |
97 | /**
98 | * Retrieve a User from email.
99 | */
100 | def findByEmail(email: String): Option[User] = {
101 | play.api.db.DB.withConnection { implicit connection =>
102 | SQL("select * from user where email = {email}").on(
103 | 'email -> email).as(User.simple.singleOpt)
104 | }
105 | }
106 |
107 | def findNameByEmail(email: String): String= {
108 | play.api.db.DB.withConnection { implicit connection =>
109 | SQL("select name from user where email = {email}").on(
110 | 'email -> email).as(SqlParser.scalar[String].single)
111 | }
112 | }
113 |
114 |
115 | def findByStatusByEmail(email: String): Int = {
116 | play.api.db.DB.withConnection { implicit connection =>
117 | SQL("select status from user where email = {email}").on(
118 | 'email -> email).as(SqlParser.scalar[Int].single)
119 | }
120 | }
121 |
122 |
123 | def updateStatus(email:String): Int ={
124 | play.api.db.DB.withConnection { implicit connection =>
125 | SQL(
126 | """
127 | update user set status=1 where email={email}
128 | """).on(
129 | 'email -> email
130 | ).executeUpdate()
131 | }
132 | }
133 |
134 |
135 | def updatePWD(email: String, password: String): Int={
136 | play.api.db.DB.withConnection { implicit connection =>
137 | SQL(
138 | """
139 | update user set password={password} where
140 | email = {email}
141 | """).on(
142 | 'email -> email,
143 | 'password -> MD5Utils.encode2hex(password)).executeUpdate()
144 | }
145 | }
146 |
147 | /**
148 | *
149 | *Create a User.
150 | */
151 | def create(user: User): User = {
152 | play.api.db.DB.withConnection { implicit connection =>
153 | SQL(
154 | """
155 | insert into user values (
156 | {email}, {name}, {password},{status}
157 | )
158 | """).on(
159 | 'email -> user.email,
160 | 'name -> user.name,
161 | 'password -> MD5Utils.encode2hex(user.password),
162 | 'status->user.status
163 | ).executeUpdate()
164 | user
165 | }
166 | }
167 |
168 |
169 |
170 |
171 |
172 | }
173 |
174 |
175 |
--------------------------------------------------------------------------------
/app/models/user/package.scala:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | /**
4 | * Created by kinge on 16/7/22.
5 | * The user related operations
6 | */
7 | package object user {
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/app/models/utils/Captcha.scala:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import java.awt.geom.AffineTransform
4 | import java.awt._
5 | import java.awt.image.BufferedImage
6 | import java.io._
7 | import java.{lang, util}
8 | import javax.imageio.ImageIO
9 |
10 | import scala.util.Random
11 |
12 | /**
13 | * Created by kinge on 16/6/17.
14 | */
15 | class CaptchaUtils(width:Int=160 ,height:Int=40 ,codeCount:Int=5 ,lineCount:Int=150)
16 | object CaptchaUtils{
17 |
18 | val VERIFY_CODES = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ"
19 | val random = new Random();
20 |
21 |
22 | def generateVerifyCode(verifySize:Int):String={
23 | generateVerifyCode(verifySize, VERIFY_CODES);
24 | }
25 | def generateVerifyCode(verifySize:Int, sources:String):String={
26 | val codesLen = sources.length();
27 | val rand = new Random(System.currentTimeMillis());
28 | val verifyCode = new StringBuilder(verifySize);
29 | for(i<- 0 until verifySize){
30 | verifyCode.append(sources.charAt(rand.nextInt(codesLen-1)))
31 | }
32 | return verifyCode.toString();
33 | }
34 |
35 |
36 | def outputImage(w:Int, h:Int,code:String)={
37 | val verifySize = code.length()
38 | val image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB)
39 | val rand = new Random()
40 | val g2 = image.createGraphics()
41 | g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON)
42 | val colors = new Array[Color](5)
43 | val colorSpaces = Array[Color](Color.WHITE, Color.CYAN,
44 | Color.GRAY, Color.LIGHT_GRAY, Color.MAGENTA, Color.ORANGE,
45 | Color.PINK, Color.YELLOW )
46 | val fractions = new Array[Float](colors.length);
47 | for(i <- 0 until colors.length){
48 | colors(i) = colorSpaces(rand.nextInt(colorSpaces.length));
49 | fractions(i) = rand.nextFloat();
50 | }
51 | util.Arrays.sort(fractions);
52 |
53 | g2.setColor(Color.GRAY);
54 | g2.fillRect(0, 0, w, h);
55 |
56 | val c = getRandColor(200, 250);
57 | g2.setColor(c);
58 | g2.fillRect(0, 2, w, h-4);
59 |
60 |
61 | val random = new Random();
62 | g2.setColor(getRandColor(160, 200));
63 | for (i <- 0 until 20) {
64 | val x = random.nextInt(w - 1);
65 | val y = random.nextInt(h - 1);
66 | val xl = random.nextInt(6) + 1;
67 | val yl = random.nextInt(12) + 1;
68 | g2.drawLine(x, y, x + xl + 40, y + yl + 20);
69 | }
70 |
71 |
72 | val yawpRate = 0.05f;
73 | val area = (yawpRate * w * h).asInstanceOf[Int];
74 | for (j <- 0 until area) {
75 | val x = random.nextInt(w);
76 | val y = random.nextInt(h);
77 | val rgb = getRandomIntColor();
78 | image.setRGB(x, y, rgb);
79 | }
80 |
81 | shear(g2, w, h, c);
82 |
83 | g2.setColor(getRandColor(100, 160));
84 | val fontSize = h-4;
85 | val font = new Font("Algerian", Font.ITALIC, fontSize);
86 | g2.setFont(font);
87 | val chars = code.toCharArray();
88 | for(i <- 0 until verifySize){
89 | val affine = new AffineTransform();
90 | affine.setToRotation(Math.PI / 4 * rand.nextDouble() * (if (rand.nextBoolean()) 1 else -1), (w / verifySize) * i + fontSize/2, h/2);
91 | g2.setTransform(affine);
92 | g2.drawChars(chars, i, 1, ((w-10) / verifySize) * i + 5, h/2 + fontSize/2 - 10);
93 | }
94 | g2.dispose();
95 | val baos = new ByteArrayOutputStream();
96 | ImageIO.write(image, "jpg", baos);
97 | baos.flush()
98 | baos.toByteArray
99 | }
100 |
101 | def getRandColor(fcd:Int, bcd:Int):Color={
102 | var fc=fcd
103 | var bc=bcd
104 | if (fc > 255)
105 | fc=255
106 | if (bc > 255)
107 | bc = 255;
108 | val r = fc + random.nextInt(bc - fc);
109 | val g = fc + random.nextInt(bc - fc);
110 | val b = fc + random.nextInt(bc - fc);
111 | new Color(r, g, b);
112 | }
113 |
114 | def getRandomIntColor():Int= {
115 | val rgb = getRandomRgb();
116 | var color = 0;
117 | for (c <-rgb) {
118 | color = color << 8;
119 | color = color | c;
120 | }
121 | color;
122 | }
123 |
124 | def getRandomRgb() :Array[Int]={
125 | val rgb = new Array[Int](3);
126 | for (i <- 0 to 2) {
127 | rgb(i) = random.nextInt(255);
128 | }
129 | return rgb;
130 | }
131 |
132 | def shear( g:Graphics, w1:Int, h1:Int, color:Color):Unit= {
133 | shearX(g, w1, h1, color);
134 | shearY(g, w1, h1, color);
135 | }
136 |
137 | def shearX(g:Graphics, w1:Int, h1:Int, color:Color):Unit={
138 | val period = random.nextInt(2);
139 | val borderGap = true;
140 | val frames :Double= 1;
141 | val phase:Double = random.nextInt(2);
142 |
143 | for (i <- 0 until h1) {
144 | val d = (period >> 1).asInstanceOf[Double] * lang.Math.sin(i / period.asInstanceOf[Double]
145 | + (6.2831853071795862D * phase) / frames)
146 | g.copyArea(0, i, w1, 1, d.asInstanceOf[Int], 0)
147 | if (borderGap) {
148 | g.setColor(color);
149 | g.drawLine( d.asInstanceOf[Int], i, 0, i);
150 | g.drawLine(d.asInstanceOf[Int] + w1, i, w1, i);
151 | }
152 | }
153 |
154 | }
155 |
156 | def shearY( g:Graphics, w1:Int, h1:Int, color:Color)={
157 | val period = random.nextInt(40) + 10; // 50;
158 | val borderGap = true;
159 | val frames:Double = 20;
160 | val phase:Double = 7;
161 | for (i <- 0 until w1) {
162 | val d = (period >> 1).asInstanceOf[Double] * lang.Math.sin(i / period.asInstanceOf[Double]
163 | + (6.2831853071795862D * phase) / frames)
164 |
165 | g.copyArea(i, 0, 1, h1, 0, d.asInstanceOf[Int]);
166 | if (borderGap) {
167 | g.setColor(color);
168 | g.drawLine(i, d.asInstanceOf[Int], i, 0);
169 | g.drawLine(i, d.asInstanceOf[Int] + h1, i, h1);
170 | }
171 |
172 | }
173 | }
174 | }
175 |
176 |
--------------------------------------------------------------------------------
/app/models/utils/Config.scala:
--------------------------------------------------------------------------------
1 | package models.utils
2 |
3 | /**
4 | * Created by kinge on 16/5/2.
5 | */
6 | trait Config {
7 |
8 |
9 | def getString(path:String):String
10 |
11 | def getBoolean(path:String):Boolean
12 |
13 | def getLong(path:String):Long
14 |
15 | def getInt(path:String):Int
16 |
17 | def getDouble(path:String):Double
18 |
19 |
20 |
21 |
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/app/models/utils/Configuration.scala:
--------------------------------------------------------------------------------
1 | package models.utils
2 |
3 | import com.typesafe.config.ConfigFactory
4 |
5 | /**
6 | * Created by kinge on 16/5/2.
7 | */
8 | class Configuration extends Config{
9 |
10 | val configName = "web-site"
11 |
12 | val config = ConfigFactory.load(configName)
13 |
14 | override def getString(path: String): String = config.getString(path)
15 |
16 | override def getDouble(path: String): Double = config.getDouble(path)
17 |
18 | override def getLong(path: String): Long = config.getLong(path)
19 |
20 | override def getBoolean(path: String): Boolean = config.getBoolean(path)
21 |
22 | override def getInt(path: String): Int = config.getInt(path)
23 | }
24 |
--------------------------------------------------------------------------------
/app/models/utils/JsonParse.scala:
--------------------------------------------------------------------------------
1 | package models.utils
2 |
3 | import com.fasterxml.jackson.databind.ObjectMapper
4 | import play.libs.Json
5 |
6 | import scala.collection.mutable.ArrayBuffer
7 |
8 | /**
9 | * Created by kinge on 16/8/24.
10 | */
11 | object JsonParse {
12 |
13 | def jsonParse(jobQueueList :ArrayBuffer[Any]):String = {
14 |
15 | val mapper = new ObjectMapper()
16 | mapper.registerModule(com.fasterxml.jackson.module.scala.DefaultScalaModule)
17 | Json.setObjectMapper(mapper)
18 |
19 | val resultCSV = Json.toJson(
20 | Map[String, Any](
21 | "jobs" -> jobQueueList
22 | )
23 | ).toString
24 | resultCSV
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/models/utils/MD5Utils.scala:
--------------------------------------------------------------------------------
1 | package models
2 |
3 | import java.io.UnsupportedEncodingException
4 | import java.security.{MessageDigest, NoSuchAlgorithmException}
5 |
6 | object MD5Utils {
7 |
8 | private[this] def encode2bytes(source: String): Array[Byte] = {
9 | var result: Array[Byte] = null
10 | try {
11 | val md = MessageDigest.getInstance("MD5");
12 | md.reset();
13 | md.update(source.getBytes("UTF-8"));
14 | result = md.digest()
15 | } catch {
16 | case e: NoSuchAlgorithmException => e.printStackTrace()
17 | case e: UnsupportedEncodingException => e.printStackTrace()
18 | }
19 | result;
20 | }
21 |
22 | def encode2hex(source: String): String = {
23 | val data = encode2bytes(source);
24 | val hexString = new StringBuffer();
25 | for (byte <- data) {
26 | val hex: String = Integer.toHexString(0xff & byte);
27 | if (hex.length() == 1) {
28 | hexString.append('0');
29 | }
30 | hexString.append(hex);
31 | }
32 | hexString.toString
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/app/views/copyright.scala.html:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/app/views/error.scala.html:
--------------------------------------------------------------------------------
1 | @(mess:String)
2 |
3 | @mess
4 |
--------------------------------------------------------------------------------
/app/views/errors/fzf.scala.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Page not found : 404
5 |
6 |
7 |
8 |
9 |
10 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/app/views/findpwd.scala.html:
--------------------------------------------------------------------------------
1 | @(form: Form[(String, String)])
2 |
3 |
4 | @plain("Password back") {
5 |
6 |
7 |
8 |
13 |
14 | @helper.form(routes.Authentication.resetpwd) {
15 |
16 | @if(form.hasErrors && !form.error("email").isEmpty) {
17 |
@form.error("email").get.message
18 | }
19 |
20 |
23 |
24 |
25 |
26 | @if(form.hasErrors && !form.error("captcha").isEmpty) {
27 |
@form.error("captcha").get.message
28 | }
29 |
30 |
31 |
37 | }
38 |
39 |
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/app/views/login.scala.html:
--------------------------------------------------------------------------------
1 | @(form: Form[(String, String)])(implicit flash: Flash)
2 |
3 | @plain("Neptune Login") {
4 |
5 |
6 |
7 | @helper.form(routes.Authentication.authenticate) {
8 | @form.globalError.map { error =>
9 |
10 | Wrong username or password
11 |
12 | }
13 |
14 | @if(form.hasErrors && !form.error("email").isEmpty) {
15 |
@form.error("email").get.message
16 | }
17 |
18 | @flash.get("success").map { message =>
19 |
20 | @message
21 |
22 | }
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
40 | }
41 |
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/app/views/nothing.scala.html:
--------------------------------------------------------------------------------
1 | @(args:String)
2 |
3 | @args
4 |
--------------------------------------------------------------------------------
/app/views/plain.scala.html:
--------------------------------------------------------------------------------
1 | @(title: String)(content: Html)
2 |
3 |
4 |
5 |
6 | @title
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | @content
29 |
30 |
31 |
--------------------------------------------------------------------------------
/app/views/registed.scala.html:
--------------------------------------------------------------------------------
1 | @(result: Option[String])
2 |
3 |
4 |
5 |
6 |
7 | Registration results
8 |
9 |
10 | @if(!result.isEmpty) {
11 | @result.get
12 | }
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/views/register.scala.html:
--------------------------------------------------------------------------------
1 | @(form: Form[Registration])
2 |
3 | @plain("register"){
4 |
5 |
6 |
7 | @helper.form(routes.Authentication.verifying){
8 |
9 | @if(form.hasErrors && !form.error("email").isEmpty){
10 | @if(form.error("email").get.message.equals("error.required")){
11 |
Email cannot be empty
12 | }else{
13 |
@form.error("email").get.message
14 | }
15 | }
16 |
17 | @if(!form.error("name").isEmpty){
18 | @if(form.error("name").get.message.equals("error.required")){
19 |
The name cannot be empty
20 | }else{
21 |
@form.error("name").get.message
22 | }
23 | }
24 |
25 |
26 | @if(form.hasErrors && !form.error("repassword").isEmpty){
27 | @if(form.error("repassword").get.message.equals("error.required")){
28 |
Password cannot be empty
29 | }else{
30 |
@form.error("repassword").get.message
31 | }
32 | }
33 |
34 | @if(form.hasErrors){
35 | @form.globalError.map { error =>
36 |
37 | @error.message
38 |
39 | }
40 | }
41 |
42 |
43 |
44 |
45 |
46 | -
47 |
Login
48 |
49 |
50 |
51 |
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/app/views/setpwd.scala.html:
--------------------------------------------------------------------------------
1 | @(form: Form[(String,String)])
2 |
3 | @plain("Set the password"){
4 |
5 |
6 |
7 | @if(form.hasErrors){
8 | @form.globalError.map { error =>
9 |
10 | @error.message
11 |
12 | }
13 | }
14 |
15 | @helper.form(routes.Authentication.updatepwd){
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | -
26 |
Login
27 |
28 |
29 |
30 |
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/views/sparklist.scala.html:
--------------------------------------------------------------------------------
1 | @sidebar = {
2 |
76 | }
77 | @main("Spark-Apps")(sidebar) {
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
Spark apps information list
96 |
Spark apps for all users
97 |
98 |
99 |
100 |
101 |
102 | -
103 | Home
104 |
105 | -
106 | App-Data
107 |
108 | -
109 | Spark-Apps
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
Spark-Apps
124 |
125 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 | APP ID |
139 | Name |
140 | Duration |
141 | State |
142 | Memoryperslave |
143 | Starttime |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 | @copyright()
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 | }
161 |
--------------------------------------------------------------------------------
/app/views/yarnlist.scala.html:
--------------------------------------------------------------------------------
1 | @sidebar = {
2 |
74 | }
75 | @main("Yarn-Apps")(sidebar) {
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
Yarn apps information list
94 |
yarn apps for all users
95 |
96 |
97 |
98 |
99 |
100 | -
101 | Home
102 |
103 | -
104 | App-Data
105 |
106 | -
107 | Yarn-Apps
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
Yarn-Apps
121 |
122 |
128 |
129 |
130 |
131 |
132 |
133 |
134 | JobID |
135 | User |
136 | Queue |
137 | State |
138 | FinalStatus |
139 | Progress |
140 | StartedTime |
141 | FinishedTime |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 | @copyright()
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 | }
159 |
--------------------------------------------------------------------------------
/build.sbt:
--------------------------------------------------------------------------------
1 | name := "spark-submit-ui"
2 |
3 | version := "v1.01
4 |
5 | scalaVersion := "2.11.8"
6 |
7 | libraryDependencies ++= Seq(
8 | jdbc,
9 | anorm,
10 | cache,
11 | "org.apache.commons" % "commons-email" % "1.4",
12 | "org.apache.spark" % "spark-core_2.10" % "1.4.1",
13 | "com.google.inject" % "guice" % "3.0",
14 | "com.tzavellas" % "sse-guice" % "0.7.1",
15 | "com.jolbox" % "bonecp" % "0.8.0.RELEASE"
16 | )
17 |
18 | play.Project.playScalaSettings
19 |
20 | resolvers ++= Seq(
21 | "Apache Repository" at "https://repository.apache.org/content/repositories/releases/",
22 | "scalaz-bintray" at "http://dl.bintray.com/scalaz/releases",
23 | Resolver.sonatypeRepo("public")
24 | )
25 |
26 | ivyScala := ivyScala.value map { _.copy(overrideScalaVersion = true) }
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/conf/application.conf:
--------------------------------------------------------------------------------
1 | # This is the main configuration file for the application.
2 | # ~~~~~
3 |
4 | # Secret key
5 | # ~~~~~
6 | # The secret key is used to secure cryptographics functions.
7 | # If you deploy your application to several instances be sure to use the same key!
8 | application.secret="VD]<6q
acNmRhB1c[pQxZ6cS5rf[JrVi2wJgj?a@iWyyg"
9 |
10 | # The application languages
11 | # ~~~~~
12 | application.langs="en"
13 |
14 | # Global object class
15 | # ~~~~~
16 | # Define the Global object class for this application.
17 | # Default to Global in the root package.
18 | # application.global=Global
19 |
20 | # Router
21 | # ~~~~~
22 | # Define the Router object to use for this application.
23 | # This router will be looked up first when the application is starting up,
24 | # so make sure this is the entry point.
25 | # Furthermore, it's assumed your route file is named properly.
26 | # So for an application router like `my.application.Router`,
27 | # you may need to define a router file `conf/my.application.routes`.
28 | # Default to Routes in the root package (and conf/routes)
29 | # application.router=my.application.Routes
30 |
31 | # Database configuration
32 | # ~~~~~
33 | # You can declare as many datasources as you want.
34 | # By convention, the default datasource is named `default`
35 | #
36 | # db.default.driver=org.h2.Driver
37 | # db.default.url="jdbc:h2:mem:play"
38 | # db.default.user=sa
39 | # db.default.password=""
40 |
41 | db.default.driver=org.h2.Driver
42 | db.default.url="jdbc:h2:db/play;FILE_LOCK=NO"
43 |
44 |
45 |
46 |
47 | # Ebean configuration
48 | #ebean.default="models.*"
49 |
50 | # Evolutions
51 | # ~~~~~
52 | # You can disable evolutions if needed
53 | # evolutionplugin=disabled
54 |
55 | # Logger
56 | # ~~~~~
57 | # You can also configure logback (http://logback.qos.ch/),
58 | # by providing an application-logger.xml file in the conf directory.
59 |
60 | # Root logger:
61 | logger.root=ERROR
62 |
63 | # Logger used by the framework:
64 | logger.play=INFO
65 |
66 | # Logger provided to your application:
67 | logger.application=DEBUG
68 |
69 | akka.default-dispatcher.fork-join-executor.pool-size-max =64
70 | akka.actor.debug.receive = on
71 |
72 |
73 | #Database connection pool configuration
74 | # Set a connection's default isolation level
75 | db.default.isolation=READ_COMMITTED
76 |
77 | # In order to reduce lock contention and thus improve performance,
78 | # each incoming connection request picks off a connection from a
79 | # pool that has thread-affinity.
80 | # The higher this number, the better your performance will be for the
81 | # case when you have plenty of short-lived threads.
82 | # Beyond a certain threshold, maintenance of these pools will start
83 | # to have a negative effect on performance (and only for the case
84 | # when connections on a partition start running out).
85 | db.default.partitionCount=2
86 |
87 | # The number of connections to create per partition. Setting this to
88 | # 5 with 3 partitions means you will have 15 unique connections to the
89 | # database. Note that BoneCP will not create all these connections in
90 | # one go but rather start off with minConnectionsPerPartition and
91 | # gradually increase connections as required.
92 | db.default.maxConnectionsPerPartition=100
93 |
94 | # The number of initial connections, per partition.
95 | db.default.minConnectionsPerPartition=10
96 |
97 | # When the available connections are about to run out, BoneCP will
98 | # dynamically create new ones in batches. This property controls
99 | # how many new connections to create in one go (up to a maximum of
100 | # maxConnectionsPerPartition). Note: This is a per-partition setting.
101 | db.default.acquireIncrement=10
102 |
103 | # After attempting to acquire a connection and failing, try to
104 | # connect this number of times before giving up.
105 | db.default.acquireRetryAttempts=10
106 |
107 | # How long to wait before attempting to obtain a
108 | # connection again after a failure.
109 | db.default.acquireRetryDelay=10 minute
110 |
111 | # The maximum time to wait before a call
112 | # to getConnection is timed out.
113 | db.default.connectionTimeout=10 second
114 |
115 | # Idle max age
116 | db.default.idleMaxAge=10 minute
117 |
118 | # This sets the time for a connection to remain idle before sending a test query to the DB.
119 | # This is useful to prevent a DB from timing out connections on its end.
120 | db.default.idleConnectionTestPeriod=5 minutes
121 |
122 | # An initial SQL statement that is run only when
123 | # a connection is first created.
124 | #db.default.initSQL="SELECT 1"
125 |
126 | # If enabled, log SQL statements being executed.
127 | db.default.logStatements=false
128 |
129 | # The maximum connection age.
130 | db.default.maxConnectionAge=10 minute
131 |
--------------------------------------------------------------------------------
/conf/evolutions/default/1.sql:
--------------------------------------------------------------------------------
1 | -- MySQL dump 10.13 Distrib 5.7.12, for osx10.9 (x86_64)
2 | --
3 | -- Host: 10.73.33.41 Database: playdb
4 | -- ------------------------------------------------------
5 | -- Server version 5.6.30-76.3
6 |
7 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
8 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
9 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
10 | /*!40101 SET NAMES utf8 */;
11 | /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
12 | /*!40103 SET TIME_ZONE='+00:00' */;
13 | /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
14 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
15 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
16 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
17 |
18 | --
19 | -- Table structure for table `user`
20 | --
21 |
22 | DROP TABLE IF EXISTS `user`;
23 | /*!40101 SET @saved_cs_client = @@character_set_client */;
24 | /*!40101 SET character_set_client = utf8 */;
25 | CREATE TABLE `user` (
26 | `email` varchar(255) NOT NULL,
27 | `name` varchar(255) NOT NULL,
28 | `password` varchar(255) NOT NULL,
29 | `status` int(4) NOT NULL DEFAULT '0'
30 | PRIMARY KEY (`email`)
31 | )
32 | /*!40101 SET character_set_client = @saved_cs_client */;
33 |
34 |
35 |
--------------------------------------------------------------------------------
/conf/evolutions/default/2.sql:
--------------------------------------------------------------------------------
1 | -- MySQL dump 10.13 Distrib 5.7.12, for osx10.9 (x86_64)
2 | --
3 | -- Host: 10.73.33.41 Database: playdb
4 | -- ------------------------------------------------------
5 | -- Server version 5.6.30-76.3
6 |
7 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
8 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
9 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
10 | /*!40101 SET NAMES utf8 */;
11 | /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
12 | /*!40103 SET TIME_ZONE='+00:00' */;
13 | /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
14 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
15 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
16 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
17 |
18 | --
19 | -- Table structure for table `hadoop_metrics`
20 | --
21 |
22 | DROP TABLE IF EXISTS `hadoop_metrics`;
23 | /*!40101 SET @saved_cs_client = @@character_set_client */;
24 | /*!40101 SET character_set_client = utf8 */;
25 | CREATE TABLE `hadoop_metrics` (
26 | `ReceivedBytes` bigint(255) NOT NULL,
27 | `SentBytes` bigint(255) NOT NULL,
28 | `host` varchar(45) NOT NULL,
29 | `CapacityUsed` bigint(225) NOT NULL,
30 | `CapacityRemaining` bigint(255) NOT NULL,
31 | `CapacityUsedNonDFS` bigint(255) NOT NULL,
32 | `MemHeapUsedM` double NOT NULL,
33 | `MemNonHeapUsedM` double NOT NULL,
34 | `timestamp` bigint(255) NOT NULL
35 | )
36 | /*!40101 SET character_set_client = @saved_cs_client */;
37 |
38 |
39 |
--------------------------------------------------------------------------------
/conf/evolutions/default/3.sql:
--------------------------------------------------------------------------------
1 | -- MySQL dump 10.13 Distrib 5.7.12, for osx10.9 (x86_64)
2 | --
3 | -- Host: 10.73.33.41 Database: playdb
4 | -- ------------------------------------------------------
5 | -- Server version 5.6.30-76.3
6 |
7 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
8 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
9 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
10 | /*!40101 SET NAMES utf8 */;
11 | /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
12 | /*!40103 SET TIME_ZONE='+00:00' */;
13 | /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
14 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
15 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
16 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
17 |
18 | --
19 | -- Table structure for table `task_msg`
20 | --
21 |
22 | DROP TABLE IF EXISTS `task_msg`;
23 | /*!40101 SET @saved_cs_client = @@character_set_client */;
24 | /*!40101 SET character_set_client = utf8 */;
25 | CREATE TABLE `task_msg` (
26 | `id` varchar(255) NOT NULL,
27 | `state` varchar(45) DEFAULT NULL,
28 | `user` varchar(255) DEFAULT NULL,
29 | PRIMARY KEY (`id`)
30 | )
31 | /*!40101 SET character_set_client = @saved_cs_client */;
32 |
33 |
--------------------------------------------------------------------------------
/conf/evolutions/default/4.sql:
--------------------------------------------------------------------------------
1 | -- MySQL dump 10.13 Distrib 5.7.12, for osx10.9 (x86_64)
2 | --
3 | -- Host: 10.73.33.41 Database: playdb
4 | -- ------------------------------------------------------
5 | -- Server version 5.6.30-76.3
6 |
7 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
8 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
9 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
10 | /*!40101 SET NAMES utf8 */;
11 | /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
12 | /*!40103 SET TIME_ZONE='+00:00' */;
13 | /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
14 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
15 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
16 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
17 |
18 | --
19 | -- Table structure for table `task_standalone`
20 | --
21 |
22 | DROP TABLE IF EXISTS `task_standalone`;
23 | /*!40101 SET @saved_cs_client = @@character_set_client */;
24 | /*!40101 SET character_set_client = utf8 */;
25 | CREATE TABLE `task_standalone` (
26 | `app_id` varchar(255) NOT NULL,
27 | `name` varchar(45) DEFAULT NULL,
28 | `cores` int(11) DEFAULT NULL,
29 | `memoryperslave` int(10) DEFAULT NULL,
30 | `state` varchar(45) DEFAULT NULL,
31 | `starttime` bigint(255) DEFAULT NULL,
32 | `duration` bigint(255) DEFAULT NULL,
33 | `user` varchar(45) DEFAULT NULL,
34 | PRIMARY KEY (`app_id`)
35 | )
36 | /*!40101 SET character_set_client = @saved_cs_client */;
37 |
38 |
39 |
--------------------------------------------------------------------------------
/conf/evolutions/default/5.sql:
--------------------------------------------------------------------------------
1 | -- MySQL dump 10.13 Distrib 5.7.12, for osx10.9 (x86_64)
2 | --
3 | -- Host: 10.73.33.41 Database: playdb
4 | -- ------------------------------------------------------
5 | -- Server version 5.6.30-76.3
6 |
7 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
8 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
9 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
10 | /*!40101 SET NAMES utf8 */;
11 | /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
12 | /*!40103 SET TIME_ZONE='+00:00' */;
13 | /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
14 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
15 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
16 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
17 |
18 | --
19 | -- Table structure for table `task_yarn`
20 | --
21 |
22 | DROP TABLE IF EXISTS `task_yarn`;
23 | /*!40101 SET @saved_cs_client = @@character_set_client */;
24 | /*!40101 SET character_set_client = utf8 */;
25 | CREATE TABLE `task_yarn` (
26 | `application_id` varchar(255) NOT NULL,
27 | `name` varchar(45) DEFAULT NULL,
28 | `apptype` varchar(45) DEFAULT NULL,
29 | `queue` varchar(45) DEFAULT NULL,
30 | `starttime` bigint(255) DEFAULT NULL,
31 | `finishtime` bigint(255) DEFAULT NULL,
32 | `state` varchar(45) DEFAULT NULL,
33 | `user` varchar(45) DEFAULT NULL,
34 | PRIMARY KEY (`application_id`)
35 | )
36 | /*!40101 SET character_set_client = @saved_cs_client */;
37 |
38 |
--------------------------------------------------------------------------------
/conf/evolutions/default/6.sql:
--------------------------------------------------------------------------------
1 | -- MySQL dump 10.13 Distrib 5.7.12, for osx10.9 (x86_64)
2 | --
3 | -- Host: 10.73.33.41 Database: playdb
4 | -- ------------------------------------------------------
5 | -- Server version 5.6.30-76.3
6 |
7 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
8 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
9 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
10 | /*!40101 SET NAMES utf8 */;
11 | /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
12 | /*!40103 SET TIME_ZONE='+00:00' */;
13 | /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
14 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
15 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
16 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
17 |
18 | --
19 | -- Table structure for table `task_args`
20 | --
21 |
22 | DROP TABLE IF EXISTS `task_args`;
23 | /*!40101 SET @saved_cs_client = @@character_set_client */;
24 | /*!40101 SET character_set_client = utf8 */;
25 | CREATE TABLE `task_args` (
26 | `id` varchar(100) NOT NULL,
27 | `master` varchar(45) DEFAULT NULL,
28 | `executeClass` varchar(45) DEFAULT NULL,
29 | `numExecutors` varchar(45) DEFAULT NULL,
30 | `driverMemory` varchar(45) DEFAULT NULL,
31 | `executorMemory` varchar(45) DEFAULT NULL,
32 | `total_executor_cores` varchar(45) DEFAULT NULL,
33 | `jarLocation` varchar(500) DEFAULT NULL,
34 | `args1` varchar(45) DEFAULT NULL,
35 | PRIMARY KEY (`id`)
36 | )
37 | /*!40101 SET character_set_client = @saved_cs_client */;
38 |
39 |
--------------------------------------------------------------------------------
/conf/routes:
--------------------------------------------------------------------------------
1 | # Routes
2 | # This file defines all application routes (Higher priority routes first)
3 | # ~~~~
4 |
5 | # Home page
6 | GET / @controllers.Application.index
7 | GET /login controllers.Authentication.login
8 | POST /login controllers.Authentication.authenticate
9 | GET /logout controllers.Authentication.logout
10 |
11 | #Dashboard
12 |
13 | GET /spark_info @controllers.SparkList.getSparkInfo
14 | GET /yarn_info @controllers.YarnList.getYarnInfo
15 | GET /yarnlist @controllers.YarnList.yarnlist
16 | GET /sparklist @controllers.SparkList.sparklist
17 | GET /spark_apps @controllers.SparkList.getSaprkApps
18 |
19 |
20 | # Map static resources from the /public folder to the /assets URL path
21 | GET /assets/*file controllers.Assets.at(path="/public", file)
22 |
23 | #User
24 | GET /registration controllers.Authentication.registration
25 | POST /registration controllers.Authentication.verifying
26 | GET /mail controllers.Authentication.mail(user)
27 | GET /mail/verifyingmail controllers.Authentication.verifyingmail(email,validateCode)
28 | GET /findpwd controllers.Authentication.findpwd
29 | GET /captcha controllers.Authentication.captcha
30 | POST /findpwd controllers.Authentication.resetpwd
31 | GET /mail/setpwd controllers.Authentication.setpwd(email,pwdToken)
32 | POST /updatepwd controllers.Authentication.updatepwd
33 |
34 |
35 |
36 | # Spark Jar
37 | POST /sparkjar @controllers.SparkJar.upload
38 | GET /executejar @controllers.SparkJar.executejarpage
39 | POST /executejar @controllers.SparkJar.executejar
40 | GET /errorlog @controllers.SparkJar.errorpage(error:String)
41 |
42 |
43 |
44 |
45 | #return User
46 | GET /whoiam controllers.UserName.userName
47 |
48 | #Job
49 | GET /tasklist @controllers.TaskManager.tasklist
50 | GET /standalone @controllers.TaskManager.standaloneInfo
51 | GET /yarn @controllers.TaskManager.yarnInfo
52 | GET /kill @controllers.TaskManager.kill(appId:String)
53 | GET /rerun @controllers.TaskManager.rerun(appId:String)
54 |
55 |
56 | #PUSH
57 | GET /startpush @controllers.Application.startpush
58 | GET /msglist @controllers.Application.msglist
59 | GET /read @controllers.Application.read(appId:String)
60 | GET /readall @controllers.Application.readall
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/conf/web-site.conf:
--------------------------------------------------------------------------------
1 | #spark home
2 | spark.home="/usr/local/spark/"
3 |
4 | #spark master url
5 | spark.master.url ="localhost:7077"
6 |
7 | #spark rest api url
8 | spark.master.host="localhost:8080"
9 |
10 |
11 | #yarn rest api url
12 | hadoop.yarn.host="localhost:8088"
13 |
14 |
15 | hadoop.metrics.host ="localhost"
16 |
17 | # Monitoring data update interval
18 | #ms
19 | #metrics.data-update.interval-ms = 300000
20 | metrics.data-update.interval-ms = 60000
21 |
22 | #Receive mail address, the deployment of the host address *
23 | email.default.host="localhost:9000"
24 |
25 |
26 |
27 | ########## Task run configuration ##########
28 |
29 | #The task status update interval
30 | #ms
31 | task.data-update.interval-ms = 5000
32 |
33 | #Start threads
34 | task.pool.max = 20
35 |
36 | # Jar file path
37 | job.upload.path="/tmp/file/"
38 |
39 | # File upload timeout
40 | # seconds
41 | job.upload.timeout.seconds= 100
42 |
43 | # Task submitted timeout
44 | #seconds
45 | job.submit.timeout.seconds = 300
46 |
47 | #The initialization tasks parameter timeout
48 | #seconds
49 | job.request.timeout.seconds = 50
50 |
51 | ##### Email configuration ########
52 | email.server.host=""
53 | email.default.username=""
54 | email.default.password=""
55 | email.default.port=
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/db/play.h2.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/db/play.h2.db
--------------------------------------------------------------------------------
/db/play.h2.db.trace.db:
--------------------------------------------------------------------------------
1 | 03-07 12:10:45 jdbc[4]: java.lang.Exception: Open Stack Trace
2 | at org.h2.util.CloseWatcher.register(CloseWatcher.java:99)
3 | at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:117)
4 | at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:90)
5 | at org.h2.Driver.connect(Driver.java:73)
6 | at org.h2.server.web.WebServer.getConnection(WebServer.java:655)
7 | at org.h2.server.web.WebApp.login(WebApp.java:900)
8 | at org.h2.server.web.WebApp.process(WebApp.java:205)
9 | at org.h2.server.web.WebApp.processRequest(WebApp.java:164)
10 | at org.h2.server.web.WebThread.process(WebThread.java:138)
11 | at org.h2.server.web.WebThread.run(WebThread.java:94)
12 | at java.lang.Thread.run(Thread.java:744)
13 |
14 | org.h2.message.DbException: The connection was not closed by the application and is garbage collected [90018-172]
15 | at org.h2.message.DbException.get(DbException.java:169)
16 | at org.h2.message.DbException.get(DbException.java:146)
17 | at org.h2.message.DbException.get(DbException.java:135)
18 | at org.h2.jdbc.JdbcConnection.closeOld(JdbcConnection.java:171)
19 | at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:116)
20 | at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:90)
21 | at org.h2.Driver.connect(Driver.java:73)
22 | at org.h2.server.web.WebServer.getConnection(WebServer.java:655)
23 | at org.h2.server.web.WebApp.test(WebApp.java:840)
24 | at org.h2.server.web.WebApp.process(WebApp.java:215)
25 | at org.h2.server.web.WebApp.processRequest(WebApp.java:164)
26 | at org.h2.server.web.WebThread.process(WebThread.java:138)
27 | at org.h2.server.web.WebThread.run(WebThread.java:94)
28 | at java.lang.Thread.run(Thread.java:744)
29 | Caused by: org.h2.jdbc.JdbcSQLException: The connection was not closed by the application and is garbage collected [90018-172]
30 | at org.h2.message.DbException.getJdbcSQLException(DbException.java:329)
31 | ... 14 more
32 | 03-07 12:22:19 jdbc[7]: java.lang.Exception: Open Stack Trace
33 | at org.h2.util.CloseWatcher.register(CloseWatcher.java:99)
34 | at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:117)
35 | at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:90)
36 | at org.h2.Driver.connect(Driver.java:73)
37 | at org.h2.server.web.WebServer.getConnection(WebServer.java:655)
38 | at org.h2.server.web.WebApp.login(WebApp.java:900)
39 | at org.h2.server.web.WebApp.process(WebApp.java:205)
40 | at org.h2.server.web.WebApp.processRequest(WebApp.java:164)
41 | at org.h2.server.web.WebThread.process(WebThread.java:138)
42 | at org.h2.server.web.WebThread.run(WebThread.java:94)
43 | at java.lang.Thread.run(Thread.java:744)
44 |
45 | org.h2.message.DbException: The connection was not closed by the application and is garbage collected [90018-172]
46 | at org.h2.message.DbException.get(DbException.java:169)
47 | at org.h2.message.DbException.get(DbException.java:146)
48 | at org.h2.message.DbException.get(DbException.java:135)
49 | at org.h2.jdbc.JdbcConnection.closeOld(JdbcConnection.java:171)
50 | at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:116)
51 | at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:90)
52 | at org.h2.Driver.connect(Driver.java:73)
53 | at org.h2.server.web.WebServer.getConnection(WebServer.java:655)
54 | at org.h2.server.web.WebApp.test(WebApp.java:840)
55 | at org.h2.server.web.WebApp.process(WebApp.java:215)
56 | at org.h2.server.web.WebApp.processRequest(WebApp.java:164)
57 | at org.h2.server.web.WebThread.process(WebThread.java:138)
58 | at org.h2.server.web.WebThread.run(WebThread.java:94)
59 | at java.lang.Thread.run(Thread.java:744)
60 | Caused by: org.h2.jdbc.JdbcSQLException: The connection was not closed by the application and is garbage collected [90018-172]
61 | at org.h2.message.DbException.getJdbcSQLException(DbException.java:329)
62 | ... 14 more
63 | 03-07 12:22:19 jdbc[7]: java.lang.Exception: Open Stack Trace
64 | at org.h2.util.CloseWatcher.register(CloseWatcher.java:99)
65 | at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:117)
66 | at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:90)
67 | at org.h2.Driver.connect(Driver.java:73)
68 | at org.h2.server.web.WebServer.getConnection(WebServer.java:655)
69 | at org.h2.server.web.WebApp.login(WebApp.java:900)
70 | at org.h2.server.web.WebApp.process(WebApp.java:205)
71 | at org.h2.server.web.WebApp.processRequest(WebApp.java:164)
72 | at org.h2.server.web.WebThread.process(WebThread.java:138)
73 | at org.h2.server.web.WebThread.run(WebThread.java:94)
74 | at java.lang.Thread.run(Thread.java:744)
75 |
76 | org.h2.message.DbException: The connection was not closed by the application and is garbage collected [90018-172]
77 | at org.h2.message.DbException.get(DbException.java:169)
78 | at org.h2.message.DbException.get(DbException.java:146)
79 | at org.h2.message.DbException.get(DbException.java:135)
80 | at org.h2.jdbc.JdbcConnection.closeOld(JdbcConnection.java:171)
81 | at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:116)
82 | at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:90)
83 | at org.h2.Driver.connect(Driver.java:73)
84 | at org.h2.server.web.WebServer.getConnection(WebServer.java:655)
85 | at org.h2.server.web.WebApp.test(WebApp.java:840)
86 | at org.h2.server.web.WebApp.process(WebApp.java:215)
87 | at org.h2.server.web.WebApp.processRequest(WebApp.java:164)
88 | at org.h2.server.web.WebThread.process(WebThread.java:138)
89 | at org.h2.server.web.WebThread.run(WebThread.java:94)
90 | at java.lang.Thread.run(Thread.java:744)
91 | Caused by: org.h2.jdbc.JdbcSQLException: The connection was not closed by the application and is garbage collected [90018-172]
92 | at org.h2.message.DbException.getJdbcSQLException(DbException.java:329)
93 | ... 14 more
94 |
--------------------------------------------------------------------------------
/project/.sbtserver:
--------------------------------------------------------------------------------
1 | #Server Startup at 2017-03-06T09:15+0000
2 | #Mon Mar 06 17:15:49 HKT 2017
3 | server.uri=http\://0.0.0.0\:49360
4 |
--------------------------------------------------------------------------------
/project/.sbtserver.lock:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/project/.sbtserver.lock
--------------------------------------------------------------------------------
/project/build.properties:
--------------------------------------------------------------------------------
1 | sbt.version=0.13.11
2 |
--------------------------------------------------------------------------------
/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | // Comment to get more information during initialization
2 | logLevel := Level.Warn
3 |
4 | // The Typesafe repository
5 | resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/"
6 |
7 | // Use the Play sbt plugin for Play projects
8 | addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.2.6")
--------------------------------------------------------------------------------
/project/sbt-ui.sbt:
--------------------------------------------------------------------------------
1 | // This plugin represents functionality that is to be added to sbt in the future
2 |
3 | addSbtPlugin("org.scala-sbt" % "sbt-core-next" % "0.1.1")
--------------------------------------------------------------------------------
/public/fonts/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/fonts/FontAwesome.otf
--------------------------------------------------------------------------------
/public/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/public/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/public/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/public/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/public/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/public/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/public/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/public/fonts/glyphicons-halflings-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/fonts/glyphicons-halflings-regular.woff2
--------------------------------------------------------------------------------
/public/fonts/linecons.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/fonts/linecons.eot
--------------------------------------------------------------------------------
/public/fonts/linecons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/fonts/linecons.ttf
--------------------------------------------------------------------------------
/public/fonts/linecons.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/fonts/linecons.woff
--------------------------------------------------------------------------------
/public/images/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/1.png
--------------------------------------------------------------------------------
/public/images/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/2.png
--------------------------------------------------------------------------------
/public/images/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/3.png
--------------------------------------------------------------------------------
/public/images/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/4.png
--------------------------------------------------------------------------------
/public/images/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/5.png
--------------------------------------------------------------------------------
/public/images/522641-46907f6c8f3b2b0b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/522641-46907f6c8f3b2b0b.png
--------------------------------------------------------------------------------
/public/images/QQ20170313-162151@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/QQ20170313-162151@2x.png
--------------------------------------------------------------------------------
/public/images/QQ20170313-171517@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/QQ20170313-171517@2x.png
--------------------------------------------------------------------------------
/public/images/album-image-full.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/album-image-full.jpg
--------------------------------------------------------------------------------
/public/images/album-img-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/album-img-1.png
--------------------------------------------------------------------------------
/public/images/album-img-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/album-img-2.png
--------------------------------------------------------------------------------
/public/images/album-img-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/album-img-3.png
--------------------------------------------------------------------------------
/public/images/album-img-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/album-img-4.png
--------------------------------------------------------------------------------
/public/images/album-img-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/album-img-5.png
--------------------------------------------------------------------------------
/public/images/album-img-6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/album-img-6.png
--------------------------------------------------------------------------------
/public/images/album-img-7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/album-img-7.png
--------------------------------------------------------------------------------
/public/images/album-img-8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/album-img-8.png
--------------------------------------------------------------------------------
/public/images/arrow-left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/arrow-left.png
--------------------------------------------------------------------------------
/public/images/arrow-right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/arrow-right.png
--------------------------------------------------------------------------------
/public/images/attach-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/attach-1.png
--------------------------------------------------------------------------------
/public/images/attach-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/attach-2.png
--------------------------------------------------------------------------------
/public/images/back_disabled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/back_disabled.png
--------------------------------------------------------------------------------
/public/images/back_enabled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/back_enabled.png
--------------------------------------------------------------------------------
/public/images/back_enabled_hover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/back_enabled_hover.png
--------------------------------------------------------------------------------
/public/images/bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/bg.jpg
--------------------------------------------------------------------------------
/public/images/chosen-sprite.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/chosen-sprite.png
--------------------------------------------------------------------------------
/public/images/chosen-sprite@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/chosen-sprite@2x.png
--------------------------------------------------------------------------------
/public/images/cloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/cloud.png
--------------------------------------------------------------------------------
/public/images/clouds.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/clouds.png
--------------------------------------------------------------------------------
/public/images/eiffel.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/eiffel.jpg
--------------------------------------------------------------------------------
/public/images/forward_disabled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/forward_disabled.png
--------------------------------------------------------------------------------
/public/images/forward_enabled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/forward_enabled.png
--------------------------------------------------------------------------------
/public/images/forward_enabled_hover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/forward_enabled_hover.png
--------------------------------------------------------------------------------
/public/images/image-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/image-1.jpg
--------------------------------------------------------------------------------
/public/images/image-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/image-2.jpg
--------------------------------------------------------------------------------
/public/images/image-3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/image-3.jpg
--------------------------------------------------------------------------------
/public/images/image-4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/image-4.jpg
--------------------------------------------------------------------------------
/public/images/image-5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/image-5.jpg
--------------------------------------------------------------------------------
/public/images/logo-collapsed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/logo-collapsed.png
--------------------------------------------------------------------------------
/public/images/logo-collapsed2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/logo-collapsed2x.png
--------------------------------------------------------------------------------
/public/images/logo-white-bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/logo-white-bg.png
--------------------------------------------------------------------------------
/public/images/logo-white-bg@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/logo-white-bg@2x.png
--------------------------------------------------------------------------------
/public/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/logo.png
--------------------------------------------------------------------------------
/public/images/logo2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/logo2x.png
--------------------------------------------------------------------------------
/public/images/minus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/minus.png
--------------------------------------------------------------------------------
/public/images/news-image-widget-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/news-image-widget-2.png
--------------------------------------------------------------------------------
/public/images/news-image-widget-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/news-image-widget-3.png
--------------------------------------------------------------------------------
/public/images/news-image-widget-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/news-image-widget-4.png
--------------------------------------------------------------------------------
/public/images/news-image-widget.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/news-image-widget.png
--------------------------------------------------------------------------------
/public/images/notes-lines.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/notes-lines.png
--------------------------------------------------------------------------------
/public/images/ok-white-full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/ok-white-full.png
--------------------------------------------------------------------------------
/public/images/ok-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/ok-white.png
--------------------------------------------------------------------------------
/public/images/ok.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/ok.png
--------------------------------------------------------------------------------
/public/images/pic1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/pic1.png
--------------------------------------------------------------------------------
/public/images/pic2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/pic2.png
--------------------------------------------------------------------------------
/public/images/pic3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/pic3.jpg
--------------------------------------------------------------------------------
/public/images/pic4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/pic4.jpg
--------------------------------------------------------------------------------
/public/images/plus-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/plus-white.png
--------------------------------------------------------------------------------
/public/images/plus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/plus.png
--------------------------------------------------------------------------------
/public/images/sample.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/sample.jpg
--------------------------------------------------------------------------------
/public/images/sort_asc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/sort_asc.png
--------------------------------------------------------------------------------
/public/images/sort_asc_disabled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/sort_asc_disabled.png
--------------------------------------------------------------------------------
/public/images/sort_both.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/sort_both.png
--------------------------------------------------------------------------------
/public/images/sort_desc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/sort_desc.png
--------------------------------------------------------------------------------
/public/images/sort_desc_disabled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/sort_desc_disabled.png
--------------------------------------------------------------------------------
/public/images/spritemap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/spritemap.png
--------------------------------------------------------------------------------
/public/images/spritemap@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/spritemap@2x.png
--------------------------------------------------------------------------------
/public/images/switch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/switch.png
--------------------------------------------------------------------------------
/public/images/user-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/user-1.png
--------------------------------------------------------------------------------
/public/images/user-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/user-2.png
--------------------------------------------------------------------------------
/public/images/user-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/user-3.png
--------------------------------------------------------------------------------
/public/images/user-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/user-4.png
--------------------------------------------------------------------------------
/public/images/user-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/images/user-5.png
--------------------------------------------------------------------------------
/public/javascripts/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/javascripts/.DS_Store
--------------------------------------------------------------------------------
/public/javascripts/globalize.min.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/javascripts/globalize.min.js
--------------------------------------------------------------------------------
/public/javascripts/highchartThemes.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by jhazheng on 16/7/25.
3 | */
4 |
5 | Highcharts.createElement('link', {
6 | // href: 'http://fonts.googleapis.com/css?family=Unica+One',
7 | rel: 'stylesheet',
8 | type: 'text/css'
9 | }, null, document.getElementsByTagName('head')[0]);
10 |
11 | Highcharts.theme = {
12 | colors: ["#2b908f", "#90ee7e", "#f45b5b", "#7798BF", "#aaeeee", "#ff0066", "#eeaaee",
13 | "#55BF3B", "#DF5353", "#7798BF", "#aaeeee"],
14 | chart: {
15 | backgroundColor: {
16 | linearGradient: { x1: 0, y1: 0, x2: 1, y2: 1 },
17 | stops: [
18 | [0, '#FFFFFF'],
19 | [1, '#FFFFFF']
20 | ]
21 | },
22 | style: {
23 | fontFamily: "'Unica One', sans-serif"
24 | },
25 | plotBorderColor: '#606063'
26 | },
27 | title: {
28 | style: {
29 | color: '#050505',
30 | textTransform: 'uppercase',
31 | fontSize: '20px'
32 | }
33 | },
34 | subtitle: {
35 | style: {
36 | color: '#050505',
37 | textTransform: 'uppercase'
38 | }
39 | },
40 | xAxis: {
41 | gridLineColor: '#43CD80',
42 | labels: {
43 | style: {
44 | color: '#050505'
45 | }
46 | },
47 | lineColor: '#43CD80',
48 | minorGridLineColor: '#505053',
49 | tickColor: '#43CD80',
50 | title: {
51 | style: {
52 | color: '#A0A0A3'
53 |
54 | }
55 | }
56 | },
57 | yAxis: {
58 | gridLineColor: '#43CD80',
59 | labels: {
60 | style: {
61 | color: '#050505'
62 | }
63 | },
64 | lineColor: '#43CD80',
65 | minorGridLineColor: '#505053',
66 | tickColor: '#43CD80',
67 | tickWidth: 1,
68 | title: {
69 | style: {
70 | color: '#A0A0A3'
71 | }
72 | }
73 | },
74 | tooltip: {
75 | backgroundColor: 'rgba(0, 0, 0, 0.85)',
76 | style: {
77 | color: '#F0F0F0'
78 | }
79 | },
80 | plotOptions: {
81 | series: {
82 | dataLabels: {
83 | color: '#282828'
84 | },
85 | marker: {
86 | lineColor: '#333'
87 | }
88 | },
89 | boxplot: {
90 | fillColor: '#505053'
91 | },
92 | candlestick: {
93 | lineColor: 'white'
94 | },
95 | errorbar: {
96 | color: 'white'
97 | }
98 | },
99 | legend: {
100 | itemStyle: {
101 | color: '#050505'
102 | },
103 | itemHoverStyle: {
104 | color: '#FFF'
105 | },
106 | itemHiddenStyle: {
107 | color: '#606063'
108 | }
109 | },
110 | credits: {
111 | style: {
112 | color: '#666'
113 | }
114 | },
115 | labels: {
116 | style: {
117 | color: '#43CD80'
118 | }
119 | },
120 |
121 | drilldown: {
122 | activeAxisLabelStyle: {
123 | color: '#F0F0F3'
124 | },
125 | activeDataLabelStyle: {
126 | color: '#F0F0F3'
127 | }
128 | },
129 |
130 | navigation: {
131 | buttonOptions: {
132 | symbolStroke: '#DDDDDD',
133 | theme: {
134 | fill: '#505053'
135 | }
136 | }
137 | },
138 |
139 | // scroll charts
140 | rangeSelector: {
141 | buttonTheme: {
142 | fill: '#505053',
143 | stroke: '#000000',
144 | style: {
145 | color: '#CCC'
146 | },
147 | states: {
148 | hover: {
149 | fill: '#43CD80',
150 | stroke: '#000000',
151 | style: {
152 | color: 'white'
153 | }
154 | },
155 | select: {
156 | fill: '#000003',
157 | stroke: '#000000',
158 | style: {
159 | color: 'white'
160 | }
161 | }
162 | }
163 | },
164 | inputBoxBorderColor: '#505053',
165 | inputStyle: {
166 | backgroundColor: '#333',
167 | color: 'silver'
168 | },
169 | labelStyle: {
170 | color: 'silver'
171 | }
172 | },
173 |
174 | navigator: {
175 | handles: {
176 | backgroundColor: '#666',
177 | borderColor: '#AAA'
178 | },
179 | outlineColor: '#CCC',
180 | maskFill: 'rgba(255,255,255,0.1)',
181 | series: {
182 | color: '#7798BF',
183 | lineColor: '#A6C7ED'
184 | },
185 | xAxis: {
186 | gridLineColor: '#505053'
187 | }
188 | },
189 |
190 | scrollbar: {
191 | barBackgroundColor: '#808083',
192 | barBorderColor: '#808083',
193 | buttonArrowColor: '#CCC',
194 | buttonBackgroundColor: '#606063',
195 | buttonBorderColor: '#606063',
196 | rifleColor: '#FFF',
197 | trackBackgroundColor: '#404043',
198 | trackBorderColor: '#404043'
199 | },
200 |
201 | // special colors for some of the
202 | legendBackgroundColor: 'rgba(0, 0, 0, 0.5)',
203 | background2: '#505053',
204 | dataLabelsColor: '#B0B0B3',
205 | textColor: '#000000',
206 | contrastTextColor: '#F0F0F3',
207 | maskColor: 'rgba(255,255,255,0.3)'
208 | };
209 |
210 | // Apply the theme
211 | Highcharts.setOptions(Highcharts.theme);
212 |
--------------------------------------------------------------------------------
/public/javascripts/initialize-tooltips.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the Apache Software Foundation (ASF) under one or more
3 | * contributor license agreements. See the NOTICE file distributed with
4 | * this work for additional information regarding copyright ownership.
5 | * The ASF licenses this file to You under the Apache License, Version 2.0
6 | * (the "License"); you may not use this file except in compliance with
7 | * the License. You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | $(document).ready(function(){
19 | $("[data-toggle=tooltip]").tooltip({container: 'body'});
20 | });
21 |
22 |
--------------------------------------------------------------------------------
/public/javascripts/jquery.edatatables.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by jhazheng on 16/9/14.
3 | */
4 |
5 | /* Set the defaults for DataTables initialisation */
6 | $.extend( true, $.fn.dataTable.defaults, {
7 | "sDom":
8 | "<'row'<'col-xs-6'l><'col-xs-6'f>r>"+
9 | "t"+
10 | "<'row'<'col-xs-6'i><'col-xs-6'p>>",
11 | "sPaginationType": "bootstrap",
12 | "oLanguage": {
13 | "sLengthMenu": "_MENU_ 记录/每页"
14 | }
15 | } );
16 |
17 |
18 | /* Default class modification */
19 | $.extend( $.fn.dataTableExt.oStdClasses, {
20 | "sWrapper": "dataTables_wrapper form-inline",
21 | "sFilterInput": "form-control input-sm",
22 | "sLengthSelect": "form-control input-sm"
23 | } );
24 |
25 |
26 | /* API method to get paging information */
27 | $.fn.dataTableExt.oApi.fnPagingInfo = function ( oSettings )
28 | {
29 | return {
30 | "iStart": oSettings._iDisplayStart,
31 | "iEnd": oSettings.fnDisplayEnd(),
32 | "iLength": oSettings._iDisplayLength,
33 | "iTotal": oSettings.fnRecordsTotal(),
34 | "iFilteredTotal": oSettings.fnRecordsDisplay(),
35 | "iPage": oSettings._iDisplayLength === -1 ?
36 | 0 : Math.ceil( oSettings._iDisplayStart / oSettings._iDisplayLength ),
37 | "iTotalPages": oSettings._iDisplayLength === -1 ?
38 | 0 : Math.ceil( oSettings.fnRecordsDisplay() / oSettings._iDisplayLength )
39 | };
40 | };
41 |
42 |
43 | /* Bootstrap style pagination control */
44 | $.extend( $.fn.dataTableExt.oPagination, {
45 | "bootstrap": {
46 | "fnInit": function( oSettings, nPaging, fnDraw ) {
47 | var oLang = oSettings.oLanguage.oPaginate;
48 | var fnClickHandler = function ( e ) {
49 | e.preventDefault();
50 | if ( oSettings.oApi._fnPageChange(oSettings, e.data.action) ) {
51 | fnDraw( oSettings );
52 | }
53 | };
54 |
55 | $(nPaging).addClass('pagination').append(
56 | ''
60 | );
61 | var els = $('a', nPaging);
62 | $(els[0]).bind( 'click.DT', { action: "previous" }, fnClickHandler );
63 | $(els[1]).bind( 'click.DT', { action: "next" }, fnClickHandler );
64 | },
65 |
66 | "fnUpdate": function ( oSettings, fnDraw ) {
67 | var iListLength = 5;
68 | var oPaging = oSettings.oInstance.fnPagingInfo();
69 | var an = oSettings.aanFeatures.p;
70 | var i, ien, j, sClass, iStart, iEnd, iHalf=Math.floor(iListLength/2);
71 |
72 | if ( oPaging.iTotalPages < iListLength) {
73 | iStart = 1;
74 | iEnd = oPaging.iTotalPages;
75 | }
76 | else if ( oPaging.iPage <= iHalf ) {
77 | iStart = 1;
78 | iEnd = iListLength;
79 | } else if ( oPaging.iPage >= (oPaging.iTotalPages-iHalf) ) {
80 | iStart = oPaging.iTotalPages - iListLength + 1;
81 | iEnd = oPaging.iTotalPages;
82 | } else {
83 | iStart = oPaging.iPage - iHalf + 1;
84 | iEnd = iStart + iListLength - 1;
85 | }
86 |
87 | for ( i=0, ien=an.length ; i'+j+'')
95 | .insertBefore( $('li:last', an[i])[0] )
96 | .bind('click', function (e) {
97 | e.preventDefault();
98 | oSettings._iDisplayStart = (parseInt($('a', this).text(),10)-1) * oPaging.iLength;
99 | fnDraw( oSettings );
100 | } );
101 | }
102 |
103 | // Add / remove disabled classes from the static elements
104 | if ( oPaging.iPage === 0 ) {
105 | $('li:first', an[i]).addClass('disabled');
106 | } else {
107 | $('li:first', an[i]).removeClass('disabled');
108 | }
109 |
110 | if ( oPaging.iPage === oPaging.iTotalPages-1 || oPaging.iTotalPages === 0 ) {
111 | $('li:last', an[i]).addClass('disabled');
112 | } else {
113 | $('li:last', an[i]).removeClass('disabled');
114 | }
115 | }
116 | }
117 | }
118 | } );
119 |
120 |
121 | /*
122 | * TableTools Bootstrap compatibility
123 | * Required TableTools 2.1+
124 | */
125 | if ( $.fn.DataTable.TableTools ) {
126 | // Set the classes that TableTools uses to something suitable for Bootstrap
127 | $.extend( true, $.fn.DataTable.TableTools.classes, {
128 | "container": "DTTT btn-group",
129 | "buttons": {
130 | "normal": "btn",
131 | "disabled": "disabled"
132 | },
133 | "collection": {
134 | "container": "DTTT_dropdown dropdown-menu",
135 | "buttons": {
136 | "normal": "",
137 | "disabled": "disabled"
138 | }
139 | },
140 | "print": {
141 | "info": "DTTT_print_info modal"
142 | },
143 | "select": {
144 | "row": "active"
145 | }
146 | } );
147 |
148 | // Have the collection use a bootstrap compatible dropdown
149 | $.extend( true, $.fn.DataTable.TableTools.DEFAULTS.oTags, {
150 | "collection": {
151 | "container": "ul",
152 | "button": "li",
153 | "liner": "a"
154 | }
155 | } );
156 | }
157 |
158 |
--------------------------------------------------------------------------------
/public/javascripts/progress.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 纯js进度条
3 | * Created by kiner on 15/3/22.
4 | */
5 |
6 | function progress(options){
7 |
8 | this.w = (options && options.width)?parseFloat(options.width) : parseFloat(this.options.width);
9 | this.h = (options && options.height)?parseFloat(options.height) : parseFloat(this.options.height);
10 | this.bgColor = (options && options.bgColor)?options.bgColor : this.options.bgColor;
11 | this.proColor = (options && options.proColor)?options.proColor : this.options.proColor;
12 | this.fontColor = (options && options.fontColor)?options.fontColor : this.options.fontColor;
13 | this.showPresent = (options && options.showPresent != undefined)?options.showPresent : this.options.showPresent;
14 | this.completeCallback = (options && options.completeCallback)?options.completeCallback : this.options.completeCallback;
15 | this.changeCallback = (options && options.changeCallback)?options.changeCallback : this.options.changeCallback;
16 | this.text = (options && options.text)?options.text : this.options.text;
17 | this.val = (options && options.val)?options.val : this.options.val;
18 |
19 | this.strTemp = this.text.substring(0,this.text.indexOf('#*'))+"{{pro}}"+this.text.substring(this.text.indexOf('*#')+2);
20 |
21 | this.init();
22 |
23 | }
24 | /**
25 | * 默认选项
26 | * @type {{width: number, height: number, bgColor: string, proColor: string, fontColor: string, val: number, text: string, showPresent: boolean, completeCallback: Function, changeCallback: Function}}
27 | */
28 | progress.prototype.options = {
29 |
30 | width : 200,
31 | height: 30,
32 | bgColor : "#005538",
33 | proColor : "#009988",
34 | fontColor : "#FFFFFF",
35 | val : 10,
36 | text:"当前进度为#*val*#%",
37 | showPresent : true,
38 | completeCallback:function(){},
39 | changeCallback:function(){}
40 |
41 | };
42 |
43 | /**
44 | * 初始化
45 | */
46 | progress.prototype.init = function(){
47 |
48 | this.proBox = document.createElement('div');
49 | this.proBg = document.createElement('div');
50 | this.proPre = document.createElement('div');
51 | this.proFont = document.createElement('div');
52 |
53 | addClass(this.proBox,'proBox');
54 | addClass(this.proBg,'proBg');
55 | addClass(this.proPre,'proPre');
56 | addClass(this.proFont,'proFont');
57 |
58 | this.proBox.setAttribute("style","width:"+this.w+"px; height:"+this.h+"px; position:relative; overflow:hidden; box-shadow:0 0 5px #FFFFFF; -moz-box-shadow:0 0 5px #FFFFFF; -webkit-box-shadow:0 0 5px #FFFFFF; -o-box-shadow:0 0 5px #FFFFFF;");
59 | this.proBg.setAttribute("style","background-color:"+this.bgColor+"; position:absolute; z-index:1; width:100%; height:100%; top:0; left:0;");
60 | this.proPre.setAttribute("style","transition:all 300ms; -moz-transition:all 300ms; -webkit-transition:all 300ms; -o-transition:all 300ms; width:"+this.val+"%; height:100%; background-color:"+this.proColor+"; position:absolute; z-index:2; top:0; left:0;");
61 | if(this.showPresent){
62 |
63 | this.proFont.setAttribute("style","overflow:hidden;text-overflow:ellipsis; white-space:nowrap; *white-space:nowrap; width:100%; height:100%; color:"+this.fontColor+"; text-align:center; line-height:"+this.h+"px; z-index:3; position:absolute; font-size:12px;");
64 |
65 | var text = this.parseText();
66 | this.proFont.innerHTML = text;
67 | this.proFont.setAttribute("title",text);
68 | this.proBox.appendChild(this.proFont);
69 | }
70 |
71 |
72 | this.proBox.appendChild(this.proBg);
73 | this.proBox.appendChild(this.proPre);
74 |
75 | };
76 |
77 | /**
78 | *
79 | */
80 | progress.prototype.refresh = function(){
81 | this.proPre.style.width = this.val+"%";
82 |
83 | this.proFont.innerHTML = this.parseText();
84 | };
85 |
86 | /**
87 | * 转换文字
88 | * @returns {options.text|*}
89 | */
90 | progress.prototype.parseText = function(){
91 | this.text = this.strTemp.replace("{{pro}}",this.val);
92 | return this.text;
93 | };
94 |
95 | /**
96 | * 更新进度条进度
97 | * @param val
98 | */
99 | progress.prototype.update = function(val){
100 |
101 | this.val = val;
102 | this.refresh();
103 |
104 | this.changeCallback.call(this,val);
105 | if(val==100){
106 |
107 | this.completeCallback.call(this,val);
108 |
109 | }
110 | };
111 | /**
112 | * 获取进度条本身的html对象,可直接将其塞入容器中
113 | * @returns {HTMLElement|*}
114 | */
115 | progress.prototype.getBody = function(){
116 | return this.proBox;
117 | };
118 | /**
119 | * 获取当前进度条的值
120 | * @returns {*}
121 | */
122 | progress.prototype.getVal = function(){
123 | return this.val;
124 | };
125 |
126 | function hasClass(obj, cls) {
127 | return obj.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'));
128 | }
129 |
130 | function addClass(obj, cls) {
131 | if (!this.hasClass(obj, cls)) obj.className += " " + cls;
132 | }
133 |
134 | function removeClass(obj, cls) {
135 | if (hasClass(obj, cls)) {
136 | var reg = new RegExp('(\\s|^)' + cls + '(\\s|$)');
137 | obj.className = obj.className.replace(reg, ' ');
138 | }
139 | }
140 |
141 | function toggleClass(obj,cls){
142 | if(hasClass(obj,cls)){
143 | removeClass(obj, cls);
144 | }else{
145 | addClass(obj, cls);
146 | }
147 | }
--------------------------------------------------------------------------------
/public/javascripts/push.js:
--------------------------------------------------------------------------------
1 | var host = window.location.host
2 | var wsUri = "ws://" + host + "/";
3 | var websocket = null
4 | var msg;
5 |
6 |
7 | function initWebSocket()
8 | {
9 | count = document.getElementById("count")
10 | createWebSocket();
11 | }
12 |
13 | function createWebSocket()
14 | {
15 | websocket = new WebSocket(wsUri+"startpush");
16 | websocket.onopen = function(evt) { onOpen(evt) };
17 | websocket.onclose = function(evt) { onClose(evt) };
18 | websocket.onmessage = function(evt) { onMessage(evt) };
19 | websocket.onerror = function(evt) { onError(evt) };
20 | }
21 |
22 | function onOpen(evt)
23 | {
24 | writeToScreen("CONNECTED");
25 | doSend("WebSocket rocks");
26 | }
27 |
28 | function onClose(evt)
29 | {
30 | writeToScreen("DISCONNECTED");
31 | }
32 |
33 | function onMessage(evt)
34 | {
35 | writeToScreen('RESPONSE: ' + evt.data+'');
36 | showmessage();
37 | }
38 |
39 | function showmessage(){
40 | $.ajax({
41 | type:"GET",
42 | url:"/msglist",
43 | dataType:"json",
44 | success:function(data){
45 | var head= ""
46 | var $count = 0;
47 | var myobj=eval(data);
48 | for (var p in myobj) {
49 | var datamid = eval(data[p]);
50 | for (var i = 0; i < datamid.length; i++) {
51 | $count++;
52 | head +=""+"End of the task to run, the current state "+datamid[i].state+""+"ID "+datamid[i].id+""
53 |
54 | };
55 | };
56 |
57 | $('#msglist').html(head);
58 | if($count!=0){
59 | $('#count').html($count);
60 | }
61 | $('#mes').html($count);
62 |
63 |
64 | }
65 | });
66 |
67 | }
68 |
69 | function onError(evt)
70 | {
71 | writeToScreen('ERROR: ' + evt.data);
72 | }
73 |
74 | function doSend(message)
75 | {
76 | writeToScreen("SENT: " + message);
77 | websocket.send(message);
78 | }
79 |
80 | function writeToScreen(message)
81 | {
82 | // var pre = document.createElement("p");
83 | // pre.style.wordWrap = "break-word";
84 | // pre.innerHTML = message;
85 | // output.appendChild(pre);
86 | }
87 |
88 | window.addEventListener("load", initWebSocket, false);
--------------------------------------------------------------------------------
/public/javascripts/resizeable.js:
--------------------------------------------------------------------------------
1 | /*
2 | This function will be called in the event when browser breakpoint changes
3 | */
4 |
5 | var public_vars = public_vars || {};
6 |
7 | jQuery.extend(public_vars, {
8 |
9 | breakpoints: {
10 | largescreen: [991, -1],
11 | tabletscreen: [768, 990],
12 | devicescreen: [420, 767],
13 | sdevicescreen: [0, 419]
14 | },
15 |
16 | lastBreakpoint: null
17 | });
18 |
19 |
20 | /* Main Function that will be called each time when the screen breakpoint changes */
21 | function resizable(breakpoint)
22 | {
23 | var sb_with_animation;
24 |
25 | // Large Screen Specific Script
26 | if(is('largescreen'))
27 | {
28 |
29 | }
30 |
31 |
32 | // Tablet or larger screen
33 | if(ismdxl())
34 | {
35 | }
36 |
37 |
38 | // Tablet Screen Specific Script
39 | if(is('tabletscreen'))
40 | {
41 | }
42 |
43 |
44 | // Tablet device screen
45 | if(is('tabletscreen'))
46 | {
47 | public_vars.$sidebarMenu.addClass('collapsed');
48 | ps_destroy();
49 | }
50 |
51 |
52 | // Tablet Screen Specific Script
53 | if(isxs())
54 | {
55 | }
56 |
57 |
58 | // Trigger Event
59 | jQuery(window).trigger('xenon.resize');
60 | }
61 |
62 |
63 |
64 | /* Functions */
65 |
66 | // Get current breakpoint
67 | function get_current_breakpoint()
68 | {
69 | var width = jQuery(window).width(),
70 | breakpoints = public_vars.breakpoints;
71 |
72 | for(var breakpont_label in breakpoints)
73 | {
74 | var bp_arr = breakpoints[breakpont_label],
75 | min = bp_arr[0],
76 | max = bp_arr[1];
77 |
78 | if(max == -1)
79 | max = width;
80 |
81 | if(min <= width && max >= width)
82 | {
83 | return breakpont_label;
84 | }
85 | }
86 |
87 | return null;
88 | }
89 |
90 |
91 | // Check current screen breakpoint
92 | function is(screen_label)
93 | {
94 | return get_current_breakpoint() == screen_label;
95 | }
96 |
97 |
98 | // Is xs device
99 | function isxs()
100 | {
101 | return is('devicescreen') || is('sdevicescreen');
102 | }
103 |
104 | // Is md or xl
105 | function ismdxl()
106 | {
107 | return is('tabletscreen') || is('largescreen');
108 | }
109 |
110 |
111 | // Trigger Resizable Function
112 | function trigger_resizable()
113 | {
114 | if(public_vars.lastBreakpoint != get_current_breakpoint())
115 | {
116 | public_vars.lastBreakpoint = get_current_breakpoint();
117 | resizable(public_vars.lastBreakpoint);
118 | }
119 |
120 |
121 | // Trigger Event (Repeated)
122 | jQuery(window).trigger('xenon.resized');
123 | }
--------------------------------------------------------------------------------
/public/javascripts/scripts.js:
--------------------------------------------------------------------------------
1 |
2 | (function() {
3 | "use strict";
4 |
5 | // custom scrollbar
6 |
7 | $("html").niceScroll({styler:"fb",cursorcolor:"#65cea7", cursorwidth: '6', cursorborderradius: '0px', background: '#424f63', spacebarenabled:false, cursorborder: '0', zindex: '1000'});
8 |
9 | $(".left-side").niceScroll({styler:"fb",cursorcolor:"#65cea7", cursorwidth: '3', cursorborderradius: '0px', background: '#424f63', spacebarenabled:false, cursorborder: '0'});
10 |
11 |
12 | $(".left-side").getNiceScroll();
13 | if ($('body').hasClass('left-side-collapsed')) {
14 | $(".left-side").getNiceScroll().hide();
15 | }
16 |
17 |
18 |
19 | // Toggle Left Menu
20 | jQuery('.menu-list > a').click(function() {
21 |
22 | var parent = jQuery(this).parent();
23 | var sub = parent.find('> ul');
24 |
25 | if(!jQuery('body').hasClass('left-side-collapsed')) {
26 | if(sub.is(':visible')) {
27 | sub.slideUp(200, function(){
28 | parent.removeClass('nav-active');
29 | jQuery('.main-content').css({height: ''});
30 | mainContentHeightAdjust();
31 | });
32 | } else {
33 | visibleSubMenuClose();
34 | parent.addClass('nav-active');
35 | sub.slideDown(200, function(){
36 | mainContentHeightAdjust();
37 | });
38 | }
39 | }
40 | return false;
41 | });
42 |
43 | function visibleSubMenuClose() {
44 | jQuery('.menu-list').each(function() {
45 | var t = jQuery(this);
46 | if(t.hasClass('nav-active')) {
47 | t.find('> ul').slideUp(200, function(){
48 | t.removeClass('nav-active');
49 | });
50 | }
51 | });
52 | }
53 |
54 | function mainContentHeightAdjust() {
55 | // Adjust main content height
56 | var docHeight = jQuery(document).height();
57 | if(docHeight > jQuery('.main-content').height())
58 | jQuery('.main-content').height(docHeight);
59 | }
60 |
61 | // class add mouse hover
62 | jQuery('.custom-nav > li').hover(function(){
63 | jQuery(this).addClass('nav-hover');
64 | }, function(){
65 | jQuery(this).removeClass('nav-hover');
66 | });
67 |
68 |
69 | // Menu Toggle
70 | jQuery('.toggle-btn').click(function(){
71 | $(".left-side").getNiceScroll().hide();
72 |
73 | if ($('body').hasClass('left-side-collapsed')) {
74 | $(".left-side").getNiceScroll().hide();
75 | }
76 | var body = jQuery('body');
77 | var bodyposition = body.css('position');
78 |
79 | if(bodyposition != 'relative') {
80 |
81 | if(!body.hasClass('left-side-collapsed')) {
82 | body.addClass('left-side-collapsed');
83 | jQuery('.custom-nav ul').attr('style','');
84 |
85 | jQuery(this).addClass('menu-collapsed');
86 |
87 | } else {
88 | body.removeClass('left-side-collapsed chat-view');
89 | jQuery('.custom-nav li.active ul').css({display: 'block'});
90 |
91 | jQuery(this).removeClass('menu-collapsed');
92 |
93 | }
94 | } else {
95 |
96 | if(body.hasClass('left-side-show'))
97 | body.removeClass('left-side-show');
98 | else
99 | body.addClass('left-side-show');
100 |
101 | mainContentHeightAdjust();
102 | }
103 |
104 | });
105 |
106 |
107 | searchform_reposition();
108 |
109 | jQuery(window).resize(function(){
110 |
111 | if(jQuery('body').css('position') == 'relative') {
112 |
113 | jQuery('body').removeClass('left-side-collapsed');
114 |
115 | } else {
116 |
117 | jQuery('body').css({left: '', marginRight: ''});
118 | }
119 |
120 | searchform_reposition();
121 |
122 | });
123 |
124 | function searchform_reposition() {
125 | if(jQuery('.searchform').css('position') == 'relative') {
126 | jQuery('.searchform').insertBefore('.left-side-inner .logged-user');
127 | } else {
128 | jQuery('.searchform').insertBefore('.menu-right');
129 | }
130 | }
131 |
132 | // panel collapsible
133 | $('.panel .tools .fa').click(function () {
134 | var el = $(this).parents(".panel").children(".panel-body");
135 | if ($(this).hasClass("fa-chevron-down")) {
136 | $(this).removeClass("fa-chevron-down").addClass("fa-chevron-up");
137 | el.slideUp(200);
138 | } else {
139 | $(this).removeClass("fa-chevron-up").addClass("fa-chevron-down");
140 | el.slideDown(200); }
141 | });
142 |
143 | $('.todo-check label').click(function () {
144 | $(this).parents('li').children('.todo-title').toggleClass('line-through');
145 | });
146 |
147 | $(document).on('click', '.todo-remove', function () {
148 | $(this).closest("li").remove();
149 | return false;
150 | });
151 |
152 | $("#sortable-todo").sortable();
153 |
154 |
155 | // panel close
156 | $('.panel .tools .fa-times').click(function () {
157 | $(this).parents(".panel").parent().remove();
158 | });
159 |
160 |
161 |
162 | // tool tips
163 |
164 | $('.tooltips').tooltip();
165 |
166 | // popovers
167 |
168 | $('.popovers').popover();
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 | })(jQuery);
--------------------------------------------------------------------------------
/public/javascripts/xenon-api.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Xenon API Functions
3 | *
4 | * Theme by: www.laborator.co
5 | **/
6 |
7 |
8 | function rtl() // checks whether the content is in RTL mode
9 | {
10 | if(typeof window.isRTL == 'boolean')
11 | return window.isRTL;
12 |
13 | window.isRTL = jQuery("html").get(0).dir == 'rtl' ? true : false;
14 |
15 | return window.isRTL;
16 | }
17 |
18 |
19 |
20 | // Page Loader
21 | function show_loading_bar(options)
22 | {
23 | var defaults = {
24 | pct: 0,
25 | delay: 1.3,
26 | wait: 0,
27 | before: function(){},
28 | finish: function(){},
29 | resetOnEnd: true
30 | };
31 |
32 | if(typeof options == 'object')
33 | defaults = jQuery.extend(defaults, options);
34 | else
35 | if(typeof options == 'number')
36 | defaults.pct = options;
37 |
38 |
39 | if(defaults.pct > 100)
40 | defaults.pct = 100;
41 | else
42 | if(defaults.pct < 0)
43 | defaults.pct = 0;
44 |
45 | var $ = jQuery,
46 | $loading_bar = $(".xenon-loading-bar");
47 |
48 | if($loading_bar.length == 0)
49 | {
50 | $loading_bar = $('
');
51 | public_vars.$body.append( $loading_bar );
52 | }
53 |
54 | var $pct = $loading_bar.find('span'),
55 | current_pct = $pct.data('pct'),
56 | is_regress = current_pct > defaults.pct;
57 |
58 |
59 | defaults.before(current_pct);
60 |
61 | TweenMax.to($pct, defaults.delay, {css: {width: defaults.pct + '%'}, delay: defaults.wait, ease: is_regress ? Expo.easeOut : Expo.easeIn,
62 | onStart: function()
63 | {
64 | $loading_bar.removeClass('progress-is-hidden');
65 | },
66 | onComplete: function()
67 | {
68 | var pct = $pct.data('pct');
69 |
70 | if(pct == 100 && defaults.resetOnEnd)
71 | {
72 | hide_loading_bar();
73 | }
74 |
75 | defaults.finish(pct);
76 | },
77 | onUpdate: function()
78 | {
79 | $pct.data('pct', parseInt($pct.get(0).style.width, 10));
80 | }});
81 | }
82 |
83 | function hide_loading_bar()
84 | {
85 | var $ = jQuery,
86 | $loading_bar = $(".xenon-loading-bar"),
87 | $pct = $loading_bar.find('span');
88 |
89 | $loading_bar.addClass('progress-is-hidden');
90 | $pct.width(0).data('pct', 0);
91 | }
--------------------------------------------------------------------------------
/public/javascripts/xenon-widgets.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Xenon Widgets
3 | *
4 | * Theme by: www.laborator.co
5 | **/
6 |
7 | ;(function($, window, undefined){
8 |
9 | "use strict";
10 |
11 | $(document).ready(function()
12 | {
13 | if($('.page-loading-overlay').length)
14 | {
15 | $(window).on('load', function()
16 | {
17 | setTimeout(setupWidgets, 200);
18 | });
19 | }
20 | else
21 | {
22 | setupWidgets();
23 | }
24 | });
25 |
26 |
27 | var setupWidgets = function()
28 | {
29 | // Count Anything
30 | $("[data-from][data-to]").each(function(i, el)
31 | {
32 | var $el = $(el),
33 | sm = scrollMonitor.create(el);
34 |
35 | sm.fullyEnterViewport(function()
36 | {
37 | var opts = {
38 | useEasing: attrDefault($el, 'easing', true),
39 | useGrouping: attrDefault($el, 'grouping', true),
40 | separator: attrDefault($el, 'separator', ','),
41 | decimal: attrDefault($el, 'decimal', '.'),
42 | prefix: attrDefault($el, 'prefix', ''),
43 | suffix: attrDefault($el, 'suffix', ''),
44 | },
45 | $count = attrDefault($el, 'count', 'this') == 'this' ? $el : $el.find($el.data('count')),
46 | from = attrDefault($el, 'from', 0),
47 | to = attrDefault($el, 'to', 100),
48 | duration = attrDefault($el, 'duration', 2.5),
49 | delay = attrDefault($el, 'delay', 0),
50 | decimals = new String(to).match(/\.([0-9]+)/) ? new String(to).match(/\.([0-9]+)$/)[1].length : 0,
51 | counter = new countUp($count.get(0), from, to, decimals, duration, opts);
52 |
53 | setTimeout(function(){ counter.start(); }, delay * 1000);
54 |
55 | sm.destroy();
56 | });
57 | });
58 |
59 |
60 | // Fill Anything
61 | $("[data-fill-from][data-fill-to]").each(function(i, el)
62 | {
63 | var $el = $(el),
64 | sm = scrollMonitor.create(el);
65 |
66 | sm.fullyEnterViewport(function()
67 | {
68 | var fill = {
69 | current: null,
70 | from: attrDefault($el, 'fill-from', 0),
71 | to: attrDefault($el, 'fill-to', 100),
72 | property: attrDefault($el, 'fill-property', 'width'),
73 | unit: attrDefault($el, 'fill-unit', '%'),
74 | },
75 | opts = {
76 | current: fill.to, onUpdate: function(){
77 | $el.css(fill.property, fill.current + fill.unit);
78 | },
79 | delay: attrDefault($el, 'delay', 0),
80 | },
81 | easing = attrDefault($el, 'fill-easing', true),
82 | duration = attrDefault($el, 'fill-duration', 2.5);
83 |
84 | if(easing)
85 | {
86 | opts.ease = Sine.easeOut;
87 | }
88 |
89 | // Set starting point
90 | fill.current = fill.from;
91 |
92 | TweenMax.to(fill, duration, opts);
93 |
94 | sm.destroy();
95 | });
96 | });
97 |
98 |
99 | // Todo List
100 | $(".xe-todo-list").on('change', 'input[type="checkbox"]', function(ev)
101 | {
102 | var $cb = $(this),
103 | $li = $cb.closest('li');
104 |
105 | $li.removeClass('done');
106 |
107 | if($cb.is(':checked'))
108 | {
109 | $li.addClass('done');
110 | }
111 | });
112 |
113 |
114 | $(".xe-status-update").each(function(i, el)
115 | {
116 | var $el = $(el),
117 | $nav = $el.find('.xe-nav a'),
118 | $status_list = $el.find('.xe-body li'),
119 | index = $status_list.filter('.active').index(),
120 | auto_switch = attrDefault($el, 'auto-switch', 0),
121 | as_interval = 0;
122 |
123 | if(auto_switch > 0)
124 | {
125 | as_interval = setInterval(function()
126 | {
127 | goTo(1);
128 |
129 | }, auto_switch * 1000);
130 |
131 | $el.hover(function()
132 | {
133 | window.clearInterval(as_interval);
134 | },
135 | function()
136 | {
137 | as_interval = setInterval(function()
138 | {
139 | goTo(1);
140 |
141 | }, auto_switch * 1000);;
142 | });
143 | }
144 |
145 | function goTo(plus_one)
146 | {
147 | index = (index + plus_one) % $status_list.length;
148 |
149 | if(index < 0)
150 | index = $status_list.length - 1;
151 |
152 | var $to_hide = $status_list.filter('.active'),
153 | $to_show = $status_list.eq(index);
154 |
155 | $to_hide.removeClass('active');
156 | $to_show.addClass('active').fadeTo(0,0).fadeTo(320,1);
157 | }
158 |
159 | $nav.on('click', function(ev)
160 | {
161 | ev.preventDefault();
162 |
163 | var plus_one = $(this).hasClass('xe-prev') ? -1 : 1;
164 |
165 | goTo(plus_one);
166 | });
167 | });
168 | }
169 |
170 |
171 | })(jQuery, window);
--------------------------------------------------------------------------------
/public/stylesheets/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kingekinge/spark-submit-ui/a794c8e682d72669f79f66f35b35a3b893393709/public/stylesheets/.DS_Store
--------------------------------------------------------------------------------
/public/stylesheets/DT_bootstrap.css:
--------------------------------------------------------------------------------
1 |
2 | div.dataTables_length label {
3 | float: left;
4 | text-align: left;
5 | }
6 |
7 | div.dataTables_length select {
8 | width: 75px;
9 | }
10 |
11 | div.dataTables_filter label {
12 | float: right;
13 | }
14 |
15 | div.dataTables_info {
16 | padding-top: 8px;
17 | }
18 |
19 | div.dataTables_paginate {
20 | float: right;
21 | margin: 0;
22 | }
23 |
24 | table.table {
25 | clear: both;
26 | margin-bottom: 6px !important;
27 | max-width: none !important;
28 | }
29 |
30 | table.table thead .sorting,
31 | table.table thead .sorting_asc,
32 | table.table thead .sorting_desc,
33 | table.table thead .sorting_asc_disabled,
34 | table.table thead .sorting_desc_disabled {
35 | cursor: pointer;
36 | *cursor: hand;
37 | }
38 |
39 | table.table thead .sorting { background: url('../images/sort_both.png') no-repeat center right; }
40 | table.table thead .sorting_asc { background: url('../images/sort_asc.png') no-repeat center right; }
41 | table.table thead .sorting_desc { background: url('../images/sort_desc.png') no-repeat center right; }
42 |
43 | table.table thead .sorting_asc_disabled { background: url('../images/sort_asc_disabled.png') no-repeat center right; }
44 | table.table thead .sorting_desc_disabled { background: url('../images/sort_desc_disabled.png') no-repeat center right; }
45 |
46 | table.dataTable th:active {
47 | outline: none;
48 | }
49 |
50 | /* Scrolling */
51 | div.dataTables_scrollHead table {
52 | margin-bottom: 0 !important;
53 | border-bottom-left-radius: 0;
54 | border-bottom-right-radius: 0;
55 | }
56 |
57 | div.dataTables_scrollHead table thead tr:last-child th:first-child,
58 | div.dataTables_scrollHead table thead tr:last-child td:first-child {
59 | border-bottom-left-radius: 0 !important;
60 | border-bottom-right-radius: 0 !important;
61 | }
62 |
63 | div.dataTables_scrollBody table {
64 | border-top: none;
65 | margin-bottom: 0 !important;
66 | }
67 |
68 | div.dataTables_scrollBody tbody tr:first-child th,
69 | div.dataTables_scrollBody tbody tr:first-child td {
70 | border-top: none;
71 | }
72 |
73 | div.dataTables_scrollFoot table {
74 | border-top: none;
75 | }
76 |
77 |
78 |
79 |
80 | /*
81 | * TableTools styles
82 | */
83 | .table tbody tr.active td,
84 | .table tbody tr.active th {
85 | background-color: #08C;
86 | color: white;
87 | }
88 |
89 | .table tbody tr.active:hover td,
90 | .table tbody tr.active:hover th {
91 | background-color: #0075b0 !important;
92 | }
93 |
94 | .table-striped tbody tr.active:nth-child(odd) td,
95 | .table-striped tbody tr.active:nth-child(odd) th {
96 | background-color: #017ebc;
97 | }
98 |
99 | table.DTTT_selectable tbody tr {
100 | cursor: pointer;
101 | *cursor: hand;
102 | }
103 |
104 | div.DTTT .btn {
105 | color: #333 !important;
106 | font-size: 12px;
107 | }
108 |
109 | div.DTTT .btn:hover {
110 | text-decoration: none !important;
111 | }
112 |
113 |
114 | ul.DTTT_dropdown.dropdown-menu a {
115 | color: #333 !important; /* needed only when demo_page.css is included */
116 | }
117 |
118 | ul.DTTT_dropdown.dropdown-menu li:hover a {
119 | background-color: #0088cc;
120 | color: white !important;
121 | }
122 |
123 | /* TableTools information display */
124 | div.DTTT_print_info.modal {
125 | height: 150px;
126 | margin-top: -75px;
127 | text-align: center;
128 | }
129 |
130 | div.DTTT_print_info h6 {
131 | font-weight: normal;
132 | font-size: 28px;
133 | line-height: 28px;
134 | margin: 1em;
135 | }
136 |
137 | div.DTTT_print_info p {
138 | font-size: 14px;
139 | line-height: 20px;
140 | }
141 |
142 |
143 |
144 | /*
145 | * FixedColumns styles
146 | */
147 | div.DTFC_LeftHeadWrapper table,
148 | div.DTFC_LeftFootWrapper table,
149 | table.DTFC_Cloned tr.even {
150 | background-color: white;
151 | }
152 |
153 | div.DTFC_LeftHeadWrapper table {
154 | margin-bottom: 0 !important;
155 | border-top-right-radius: 0 !important;
156 | border-bottom-left-radius: 0 !important;
157 | border-bottom-right-radius: 0 !important;
158 | }
159 |
160 | div.DTFC_LeftHeadWrapper table thead tr:last-child th:first-child,
161 | div.DTFC_LeftHeadWrapper table thead tr:last-child td:first-child {
162 | border-bottom-left-radius: 0 !important;
163 | border-bottom-right-radius: 0 !important;
164 | }
165 |
166 | div.DTFC_LeftBodyWrapper table {
167 | border-top: none;
168 | margin-bottom: 0 !important;
169 | }
170 |
171 | div.DTFC_LeftBodyWrapper tbody tr:first-child th,
172 | div.DTFC_LeftBodyWrapper tbody tr:first-child td {
173 | border-top: none;
174 | }
175 |
176 | div.DTFC_LeftFootWrapper table {
177 | border-top: none;
178 | }
179 |
--------------------------------------------------------------------------------
/public/stylesheets/bootstrap-reset.css:
--------------------------------------------------------------------------------
1 |
2 | .nav-stacked > li + li {
3 | margin-top: 1px;
4 | }
5 |
6 | .nav > li > a:hover, .nav > li > a:focus {
7 | background-color: #353F4F;
8 | text-decoration: none;
9 | }
10 |
11 | .panel {
12 | border: none;
13 | }
14 |
15 | .panel-heading {
16 | border-bottom: 1px dotted rgba(0, 0, 0, 0.2);
17 | padding: 15px;
18 | text-transform: uppercase;
19 | color: #535351;
20 | font-size: 14px;
21 | font-weight: bold;
22 | }
23 |
24 | /*dropdown shadow*/
25 |
26 | .btn-group.open .dropdown-toggle, .btn-white.active, .btn:active, .btn.active {
27 | box-shadow: none;
28 | }
29 |
30 | /*dropdown select bg*/
31 |
32 | .dropdown-menu {
33 | box-shadow: none;
34 | }
35 | .dropdown-menu > li > a:hover, .dropdown-menu > li > a:focus {
36 | background-color: #424F63;
37 | color: #FFFFFF;
38 | text-decoration: none;
39 | }
40 |
41 | /*progress*/
42 |
43 | .progress {
44 | box-shadow: none;
45 | background: #f0f2f7;
46 | height: 15px;
47 | border-radius: 3px;
48 | -webkit-border-radius: 3px;
49 | }
50 |
51 | .progress-bar {
52 | box-shadow: none;
53 | line-height: 15px;
54 | text-align: right;
55 | padding-right: 10px;
56 | font-size: 11px;
57 | }
58 |
59 | /*alert*/
60 |
61 | .alert-success, .alert-danger, .alert-info, .alert-warning {
62 | border: none;
63 | }
64 |
65 | /*table*/
66 |
67 | .table thead > tr > th, .table tbody > tr > th, .table tfoot > tr > th, .table thead > tr > td, .table tbody > tr > td, .table tfoot > tr > td {
68 | padding: 10px;
69 | }
70 |
71 | /*pagination*/
72 |
73 | .pagination > li > a, .pagination > li > span {
74 | background-color: #EFF2F7;
75 | border: 1px solid #EFF2F7;
76 | float: left;
77 | line-height: 1.42857;
78 | margin-left: 1px;
79 | padding: 6px 12px;
80 | position: relative;
81 | text-decoration: none;
82 | color: #535351;
83 | }
84 |
85 | .pagination > li > a:hover,
86 | .pagination > li > span:hover,
87 | .pagination > li > a:focus,
88 | .pagination > li > span:focus,
89 | .pagination > li.active > a,
90 | .pagination > li.active > a:hover,
91 |
92 | .pager li > a:hover, .pager li > a:focus{
93 | background-color: #65CEA7;
94 | border-color: #65CEA7;
95 | color: #fff;
96 | }
97 |
98 | .pager li > a, .pager li > span {
99 | background-color: #EFF2F7;
100 | border: 1px solid #EFF2F7;
101 | color: #535351;
102 | }
103 |
104 | /*button*/
105 |
106 | .btn-primary {
107 | background-color: #424F63;
108 | border-color: #374152;
109 | color: #FFFFFF;
110 | }
111 |
112 | .btn-primary:hover, .btn-primary:focus, .btn-primary:active, .btn-primary.active, .open .dropdown-toggle.btn-primary {
113 | background-color: #374152;
114 | border-color: #2e3644;
115 | color: #FFFFFF;
116 | }
117 |
118 |
119 | /*modal*/
120 |
121 | .modal-content {
122 | box-shadow: none;
123 | border: none;
124 | border-radius: 0;
125 | -webkit-border-radius: 0;
126 | }
127 |
128 | .modal-header {
129 | background: #65CEA7;
130 | color: #fff;
131 | border-radius: 0;
132 | -webkit-border-radius: 0;
133 | border: none;
134 | }
135 |
136 | .modal-header .close {
137 | color: #fff;
138 | opacity: .5;
139 | }
140 | .modal-header .close:hover {
141 | opacity: 1;
142 | }
143 |
144 | /*popover*/
145 |
146 | .popover {
147 | box-shadow: none;
148 | }
149 |
150 | /*form elements*/
151 |
152 | .form-group label {
153 | font-weight: normal;
154 | }
155 |
156 | .form-control {
157 | box-shadow: none;
158 | }
159 |
160 | .form-control:focus, #focusedInput {
161 | border: 1px solid #424F63;
162 | box-shadow: none;
163 | }
164 |
165 | .has-success .form-control:focus,
166 | .has-warning .form-control:focus,
167 | .has-error .form-control:focus{
168 | box-shadow: none;
169 | }
170 |
171 | /*panels*/
172 |
173 | .panel-default > .panel-heading {
174 | background-color: #424F63;
175 | border-color: #424F63;
176 | color: #fff;
177 | }
178 |
179 | .panel-success > .panel-heading {
180 | background-color: #5CB85C;
181 | border-color: #5CB85C;
182 | color: #fff;
183 | }
184 |
185 | .panel-info > .panel-heading {
186 | background-color: #46B8DA;
187 | border-color: #46B8DA;
188 | color: #fff;
189 | }
190 |
191 | .panel-warning > .panel-heading {
192 | background-color: #F0AD4E;
193 | border-color: #F0AD4E;
194 | color: #fff;
195 | }
196 |
197 | .panel-danger > .panel-heading {
198 | background-color: #D9534F;
199 | border-color: #D9534F;
200 | color: #fff;
201 | }
--------------------------------------------------------------------------------
/public/stylesheets/custom.css:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * Apply your custom CSS here
4 | *
5 | */
6 |
7 | #my-cc .nanobar .bar {
8 | background: #6bff63 ;;
9 | }
--------------------------------------------------------------------------------
/public/stylesheets/google.css:
--------------------------------------------------------------------------------
1 | .sweet-overlay {
2 | background: rgba(10, 10, 10, 0.6); }
3 |
4 | .sweet-alert {
5 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
6 | padding: 24px;
7 | padding-top: 64px;
8 | padding-bottom: 13px;
9 | text-align: right;
10 | border-radius: 0;
11 | box-shadow: 0 0 14px rgba(0, 0, 0, 0.24), 0 14px 28px rgba(0, 0, 0, 0.48); }
12 | .sweet-alert h2 {
13 | position: absolute;
14 | top: 0;
15 | left: 0;
16 | right: 0;
17 | height: auto;
18 | font-weight: 400;
19 | color: #212121;
20 | margin: 20px 0;
21 | font-size: 1.2em;
22 | line-height: 1.25;
23 | text-align: left;
24 | padding: 0 24px; }
25 | .sweet-alert p {
26 | display: block;
27 | text-align: center;
28 | color: #212121;
29 | font-weight: 400;
30 | font-size: 14px;
31 | margin: 20px 0; }
32 | .sweet-alert button {
33 | border-radius: 2px;
34 | box-shadow: none !important;
35 | background: none !important;
36 | border-radius: 2px;
37 | text-transform: uppercase;
38 | font-size: 14px;
39 | font-weight: 600;
40 | padding: 8px 16px;
41 | position: relative;
42 | margin-top: 0; }
43 | .sweet-alert button:hover, .sweet-alert button:focus {
44 | background-color: #f6f6f6 !important; }
45 | .sweet-alert button.confirm {
46 | color: #3c80f6; }
47 | .sweet-alert button.cancel {
48 | color: #757575; }
49 | .sweet-alert button.cancel:focus {
50 | box-shadow: none !important; }
51 | .sweet-alert .sa-icon:not(.sa-custom) {
52 | transform: scale(0.8);
53 | margin-bottom: -10px;
54 | margin-top: -10px; }
55 | .sweet-alert input {
56 | border: none;
57 | border-radius: 0;
58 | border-bottom: 1px solid #c9c9c9;
59 | color: #212121;
60 | margin-bottom: 8px;
61 | padding: 1px;
62 | padding-bottom: 8px;
63 | height: auto;
64 | box-shadow: none;
65 | font-size: 13px;
66 | margin: 10px 0; }
67 | .sweet-alert input:focus {
68 | border: none;
69 | border-bottom: 1px solid #3c80f6;
70 | box-shadow: inset 0 -1px 0 #3c80f6; }
71 | .sweet-alert fieldset {
72 | padding: 0; }
73 | .sweet-alert fieldset .sa-input-error {
74 | display: none; }
75 | .sweet-alert .sa-error-container {
76 | display: none;
77 | background: none;
78 | height: auto;
79 | padding: 0 24px;
80 | margin: 0 -20px;
81 | text-align: left; }
82 | .sweet-alert .sa-error-container.show {
83 | padding: 0 24px;
84 | display: block; }
85 | .sweet-alert .sa-error-container.show ~ fieldset input {
86 | background: red;
87 | border-bottom: 1px solid #d9453c;
88 | box-shadow: inset 0 -1px 0 #d9453c; }
89 | .sweet-alert .sa-error-container .icon {
90 | display: none; }
91 | .sweet-alert .sa-error-container p {
92 | color: #d9453c;
93 | margin-top: 0; }
94 |
95 | @-webkit-keyframes animateErrorIcon {
96 | 0% {
97 | transform: rotateX(100deg), scale(0.5);
98 | -webkit-transform: rotateX(100deg), scale(0.5);
99 | opacity: 0; }
100 |
101 | 100% {
102 | transform: rotateX(0deg), scale(0.5);
103 | -webkit-transform: rotateX(0deg), scale(0.5);
104 | opacity: 1; } }
105 |
106 | @keyframes animateErrorIcon {
107 | 0% {
108 | transform: rotateX(100deg), scale(0.5);
109 | -webkit-transform: rotateX(100deg), scale(0.5);
110 | opacity: 0; }
111 |
112 | 100% {
113 | transform: rotateX(0deg), scale(0.5);
114 | -webkit-transform: rotateX(0deg), scale(0.5);
115 | opacity: 1; } }
116 |
--------------------------------------------------------------------------------
/public/stylesheets/jquery-ui.min.css:
--------------------------------------------------------------------------------
1 | /*! jQuery UI - v1.11.1 - 2014-09-18
2 | * http://jqueryui.com
3 | * Includes: core.css, draggable.css, resizable.css, selectable.css, sortable.css, slider.css
4 | * Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */
5 |
6 | .ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{min-height:0}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-draggable-handle{-ms-touch-action:none;touch-action:none}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:0.1px;display:block;-ms-touch-action:none;touch-action:none}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-selectable{-ms-touch-action:none;touch-action:none}.ui-selectable-helper{position:absolute;z-index:100;border:1px dotted black}.ui-sortable-handle{-ms-touch-action:none;touch-action:none}.ui-slider{position:relative;text-align:left}.ui-slider .ui-slider-handle{position:absolute;z-index:2;width:1.2em;height:1.2em;cursor:default;-ms-touch-action:none;touch-action:none}.ui-slider .ui-slider-range{position:absolute;z-index:1;font-size:.7em;display:block;border:0;background-position:0 0}.ui-slider.ui-state-disabled .ui-slider-handle,.ui-slider.ui-state-disabled .ui-slider-range{filter:inherit}.ui-slider-horizontal{height:.8em}.ui-slider-horizontal .ui-slider-handle{top:-.3em;margin-left:-.6em}.ui-slider-horizontal .ui-slider-range{top:0;height:100%}.ui-slider-horizontal .ui-slider-range-min{left:0}.ui-slider-horizontal .ui-slider-range-max{right:0}.ui-slider-vertical{width:.8em;height:100px}.ui-slider-vertical .ui-slider-handle{left:-.3em;margin-left:0;margin-bottom:-.6em}.ui-slider-vertical .ui-slider-range{left:0;width:100%}.ui-slider-vertical .ui-slider-range-min{bottom:0}.ui-slider-vertical .ui-slider-range-max{top:0}
--------------------------------------------------------------------------------
/public/stylesheets/lines.css:
--------------------------------------------------------------------------------
1 |
2 | .rickshaw_graph .detail {
3 | pointer-events: none;
4 | position: absolute;
5 | top: 0;
6 | z-index: 2;
7 | background: rgba(0, 0, 0, 0.1);
8 | bottom: 0;
9 | width: 1px;
10 | transition: opacity 0.25s linear;
11 | -moz-transition: opacity 0.25s linear;
12 | -o-transition: opacity 0.25s linear;
13 | -webkit-transition: opacity 0.25s linear;
14 | }
15 | .rickshaw_graph .detail.inactive {
16 | opacity: 0;
17 | }
18 | .rickshaw_graph .detail .item.active {
19 | opacity: 1;
20 | }
21 | .rickshaw_graph .detail .x_label {
22 | font-family: Arial, sans-serif;
23 | border-radius: 3px;
24 | padding: 6px;
25 | opacity: 0.5;
26 | border: 1px solid #e0e0e0;
27 | font-size: 12px;
28 | position: absolute;
29 | background: white;
30 | white-space: nowrap;
31 | }
32 | .rickshaw_graph .detail .x_label.left {
33 | left: 0;
34 | }
35 | .rickshaw_graph .detail .x_label.right {
36 | right: 0;
37 | }
38 | .rickshaw_graph .detail .item {
39 | position: absolute;
40 | z-index: 2;
41 | border-radius: 3px;
42 |
43 | font-size: 12px;
44 | font-family: Arial, sans-serif;
45 | opacity: 0;
46 | background: rgba(0, 0, 0, 0.4);
47 | color: white;
48 | border: 1px solid rgba(0, 0, 0, 0.4);
49 | margin-left: 1em;
50 | margin-right: 1em;
51 | margin-top: -1em;
52 | white-space: nowrap;
53 | }
54 | .rickshaw_graph .detail .item.left {
55 | left: 0;
56 | }
57 | .rickshaw_graph .detail .item.right {
58 | right: 0;
59 | }
60 | .rickshaw_graph .detail .item.active {
61 | opacity: 1;
62 | background: rgba(0, 0, 0, 0.8);
63 | }
64 | .rickshaw_graph .detail .item:after {
65 | position: absolute;
66 | display: block;
67 | width: 0;
68 | height: 0;
69 |
70 | content: "";
71 |
72 | border: 5px solid transparent;
73 | }
74 | .rickshaw_graph .detail .item.left:after {
75 | top: 1em;
76 | left: -5px;
77 | margin-top: -5px;
78 | border-right-color: rgba(0, 0, 0, 0.8);
79 | border-left-width: 0;
80 | }
81 | .rickshaw_graph .detail .item.right:after {
82 | top: 1em;
83 | right: -5px;
84 | margin-top: -5px;
85 | border-left-color: rgba(0, 0, 0, 0.8);
86 | border-right-width: 0;
87 | }
88 | .rickshaw_graph .detail .dot {
89 | width: 4px;
90 | height: 4px;
91 | margin-left: -3px;
92 | margin-top: -3.5px;
93 | border-radius: 5px;
94 | position: absolute;
95 | box-shadow: 0 0 2px rgba(0, 0, 0, 0.6);
96 | box-sizing: content-box;
97 | -moz-box-sizing: content-box;
98 | background: white;
99 | border-width: 2px;
100 | border-style: solid;
101 | display: none;
102 | background-clip: padding-box;
103 | }
104 | .rickshaw_graph .detail .dot.active {
105 | display: block;
106 | }
107 | /* graph */
108 | #chart {
109 | width: 649px !important;
110 | height: 400px !important;
111 | background-color: #fff;
112 | }
113 | .rickshaw_graph {
114 | position: relative;
115 | }
116 | .rickshaw_graph svg {
117 | display: block;
118 | overflow: hidden;
119 | }
120 | @media (max-width:1366px){
121 | #chart {
122 | width:533px !important;
123 | }
124 | }
125 | @media (max-width:1280px){
126 | #chart {
127 | width:483px !important;
128 | }
129 | canvas#doughnut, canvas#line, canvas#polarArea, #bar, #pie, #radar {
130 | width: 308px !important;
131 | height: 220px !important;
132 | }
133 | }
134 | @media (max-width:1024px){
135 | #chart {
136 | width:360px !important;
137 | height:300px !important;
138 | }
139 | }
140 | @media (max-width:768px){
141 | #chart {
142 | width:468px !important;
143 | height: 300px !important;
144 | }
145 | }
146 | @media (max-width:736px){
147 | #chart {
148 | width:685px !important;
149 | height: 300px !important;
150 | }
151 | }
152 | @media (max-width:640px){
153 | #chart {
154 | width:580px !important;
155 | height:250px !important;
156 | }
157 | }
158 | @media (max-width:540px){
159 | #chart {
160 | width:464px !important;
161 | height:250px !important;
162 | }
163 | }
164 | @media (max-width:480px){
165 | #chart {
166 | width:447px !important;
167 | height:250px !important;
168 | }
169 | }
170 | @media (max-width:320px){
171 | #chart {
172 | width:298px !important;
173 | height:250px !important;
174 | }
175 | .copy {
176 | padding: 10px;
177 | }
178 | .copy p{
179 | font-size:13px;
180 | }
181 | .btn-group-lg>.btn, .btn-lg {
182 | padding: 6px 7px;
183 | font-size: 16px;
184 | }
185 | .label {
186 | font-size: 56%;
187 | }
188 | .pagination-lg>li>a, .pagination-lg>li>span {
189 | padding: 8px 13px;
190 | font-size: 15px;
191 | }
192 | .nav>li>a {
193 | padding: 7px 8px;
194 | }
195 | .modal-dialog {
196 | width: 245px !important;
197 | }
198 | }
199 |
--------------------------------------------------------------------------------
/public/stylesheets/meteocons.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'meteocons';
3 | font-weight: normal;
4 | font-style: normal;
5 | }
6 | /* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */
7 | /* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */
8 | /*
9 | @media screen and (-webkit-min-device-pixel-ratio:0) {
10 | @font-face {
11 | font-family: 'meteocons';
12 | src: url('../font/meteocons.svg?24967826#meteocons') format('svg');
13 | }
14 | }
15 | */
16 |
17 | [class^="meteocons-"]:before, [class*=" meteocons-"]:before {
18 | font-family: "meteocons";
19 | font-style: normal;
20 | font-weight: normal;
21 | speak: none;
22 |
23 | display: inline-block;
24 | text-decoration: inherit;
25 | width: 1em;
26 | margin-right: .2em;
27 | text-align: center;
28 | /* opacity: .8; */
29 |
30 | /* For safety - reset parent styles, that can break glyph codes*/
31 | font-variant: normal;
32 | text-transform: none;
33 |
34 | /* fix buttons height, for twitter bootstrap */
35 | line-height: 1em;
36 |
37 | /* Animation center compensation - margins should be symmetric */
38 | /* remove if not needed */
39 | margin-left: .2em;
40 |
41 | /* you can be more comfortable with increased icons size */
42 | /* font-size: 120%; */
43 |
44 | /* Uncomment for 3D effect */
45 | /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */
46 | }
47 |
48 | .meteocons-windy-rain-inv:before { content: '\e800'; } /* '' */
49 | .meteocons-snow-inv:before { content: '\e801'; } /* '' */
50 | .meteocons-snow-heavy-inv:before { content: '\e802'; } /* '' */
51 | .meteocons-hail-inv:before { content: '\e803'; } /* '' */
52 | .meteocons-clouds-inv:before { content: '\e804'; } /* '' */
53 | .meteocons-clouds-flash-inv:before { content: '\e805'; } /* '' */
54 | .meteocons-temperature:before { content: '\e806'; } /* '' */
55 | .meteocons-compass:before { content: '\e807'; } /* '' */
56 | .meteocons-na:before { content: '\e808'; } /* '' */
57 | .meteocons-celcius:before { content: '\e809'; } /* '' */
58 | .meteocons-fahrenheit:before { content: '\e80a'; } /* '' */
59 | .meteocons-clouds-flash-alt:before { content: '\e80b'; } /* '' */
60 | .meteocons-sun-inv:before { content: '\e80c'; } /* '' */
61 | .meteocons-moon-inv:before { content: '\e80d'; } /* '' */
62 | .meteocons-cloud-sun-inv:before { content: '\e80e'; } /* '' */
63 | .meteocons-cloud-moon-inv:before { content: '\e80f'; } /* '' */
64 | .meteocons-cloud-inv:before { content: '\e810'; } /* '' */
65 | .meteocons-cloud-flash-inv:before { content: '\e811'; } /* '' */
66 | .meteocons-drizzle-inv:before { content: '\e812'; } /* '' */
67 | .meteocons-rain-inv:before { content: '\e813'; } /* '' */
68 | .meteocons-windy-inv:before { content: '\e814'; } /* '' */
69 | .meteocons-sunrise:before { content: '\e815'; } /* '' */
70 | .meteocons-sun:before { content: '\e816'; } /* '' */
71 | .meteocons-moon:before { content: '\e817'; } /* '' */
72 | .meteocons-eclipse:before { content: '\e818'; } /* '' */
73 | .meteocons-mist:before { content: '\e819'; } /* '' */
74 | .meteocons-wind:before { content: '\e81a'; } /* '' */
75 | .meteocons-snowflake:before { content: '\e81b'; } /* '' */
76 | .meteocons-cloud-sun:before { content: '\e81c'; } /* '' */
77 | .meteocons-cloud-moon:before { content: '\e81d'; } /* '' */
78 | .meteocons-fog-sun:before { content: '\e81e'; } /* '' */
79 | .meteocons-fog-moon:before { content: '\e81f'; } /* '' */
80 | .meteocons-fog-cloud:before { content: '\e820'; } /* '' */
81 | .meteocons-fog:before { content: '\e821'; } /* '' */
82 | .meteocons-cloud:before { content: '\e822'; } /* '' */
83 | .meteocons-cloud-flash:before { content: '\e823'; } /* '' */
84 | .meteocons-cloud-flash-alt:before { content: '\e824'; } /* '' */
85 | .meteocons-drizzle:before { content: '\e825'; } /* '' */
86 | .meteocons-rain:before { content: '\e826'; } /* '' */
87 | .meteocons-windy:before { content: '\e827'; } /* '' */
88 | .meteocons-windy-rain:before { content: '\e828'; } /* '' */
89 | .meteocons-snow:before { content: '\e829'; } /* '' */
90 | .meteocons-snow-alt:before { content: '\e82a'; } /* '' */
91 | .meteocons-snow-heavy:before { content: '\e82b'; } /* '' */
92 | .meteocons-hail:before { content: '\e82c'; } /* '' */
93 | .meteocons-clouds:before { content: '\e82d'; } /* '' */
94 | .meteocons-clouds-flash:before { content: '\e82e'; } /* '' */
--------------------------------------------------------------------------------
/public/stylesheets/tooltipster-sideTip-punk.min.css:
--------------------------------------------------------------------------------
1 | .tooltipster-sidetip.tooltipster-punk .tooltipster-box{border-radius:5px;border:none;border-bottom:3px solid #f71169;background:#2a2a2a}.tooltipster-sidetip.tooltipster-punk.tooltipster-top .tooltipster-box{margin-bottom:7px}.tooltipster-sidetip.tooltipster-punk .tooltipster-content{color:#fff;padding:8px 16px}.tooltipster-sidetip.tooltipster-punk .tooltipster-arrow-background{display:none}.tooltipster-sidetip.tooltipster-punk.tooltipster-bottom .tooltipster-arrow-border{border-bottom-color:#2a2a2a}.tooltipster-sidetip.tooltipster-punk.tooltipster-left .tooltipster-arrow-border{border-left-color:#2a2a2a}.tooltipster-sidetip.tooltipster-punk.tooltipster-right .tooltipster-arrow-border{border-right-color:#2a2a2a}.tooltipster-sidetip.tooltipster-punk.tooltipster-top .tooltipster-arrow-border{border-top-color:#f71169}
--------------------------------------------------------------------------------
/public/stylesheets/tooltipster.bundle.min.css:
--------------------------------------------------------------------------------
1 | .tooltipster-fall,.tooltipster-grow.tooltipster-show{-webkit-transition-timing-function:cubic-bezier(.175,.885,.32,1);-moz-transition-timing-function:cubic-bezier(.175,.885,.32,1.15);-ms-transition-timing-function:cubic-bezier(.175,.885,.32,1.15);-o-transition-timing-function:cubic-bezier(.175,.885,.32,1.15)}.tooltipster-base{display:flex;pointer-events:none;position:absolute}.tooltipster-box{flex:1 1 auto}.tooltipster-content{box-sizing:border-box;max-height:100%;max-width:100%;overflow:auto}.tooltipster-ruler{bottom:0;left:0;overflow:hidden;position:fixed;right:0;top:0;visibility:hidden}.tooltipster-fade{opacity:0;-webkit-transition-property:opacity;-moz-transition-property:opacity;-o-transition-property:opacity;-ms-transition-property:opacity;transition-property:opacity}.tooltipster-fade.tooltipster-show{opacity:1}.tooltipster-grow{-webkit-transform:scale(0,0);-moz-transform:scale(0,0);-o-transform:scale(0,0);-ms-transform:scale(0,0);transform:scale(0,0);-webkit-transition-property:-webkit-transform;-moz-transition-property:-moz-transform;-o-transition-property:-o-transform;-ms-transition-property:-ms-transform;transition-property:transform;-webkit-backface-visibility:hidden}.tooltipster-grow.tooltipster-show{-webkit-transform:scale(1,1);-moz-transform:scale(1,1);-o-transform:scale(1,1);-ms-transform:scale(1,1);transform:scale(1,1);-webkit-transition-timing-function:cubic-bezier(.175,.885,.32,1.15);transition-timing-function:cubic-bezier(.175,.885,.32,1.15)}.tooltipster-swing{opacity:0;-webkit-transform:rotateZ(4deg);-moz-transform:rotateZ(4deg);-o-transform:rotateZ(4deg);-ms-transform:rotateZ(4deg);transform:rotateZ(4deg);-webkit-transition-property:-webkit-transform,opacity;-moz-transition-property:-moz-transform;-o-transition-property:-o-transform;-ms-transition-property:-ms-transform;transition-property:transform}.tooltipster-swing.tooltipster-show{opacity:1;-webkit-transform:rotateZ(0);-moz-transform:rotateZ(0);-o-transform:rotateZ(0);-ms-transform:rotateZ(0);transform:rotateZ(0);-webkit-transition-timing-function:cubic-bezier(.23,.635,.495,1);-webkit-transition-timing-function:cubic-bezier(.23,.635,.495,2.4);-moz-transition-timing-function:cubic-bezier(.23,.635,.495,2.4);-ms-transition-timing-function:cubic-bezier(.23,.635,.495,2.4);-o-transition-timing-function:cubic-bezier(.23,.635,.495,2.4);transition-timing-function:cubic-bezier(.23,.635,.495,2.4)}.tooltipster-fall{-webkit-transition-property:top;-moz-transition-property:top;-o-transition-property:top;-ms-transition-property:top;transition-property:top;-webkit-transition-timing-function:cubic-bezier(.175,.885,.32,1.15);transition-timing-function:cubic-bezier(.175,.885,.32,1.15)}.tooltipster-fall.tooltipster-initial{top:0!important}.tooltipster-fall.tooltipster-dying{-webkit-transition-property:all;-moz-transition-property:all;-o-transition-property:all;-ms-transition-property:all;transition-property:all;top:0!important;opacity:0}.tooltipster-slide{-webkit-transition-property:left;-moz-transition-property:left;-o-transition-property:left;-ms-transition-property:left;transition-property:left;-webkit-transition-timing-function:cubic-bezier(.175,.885,.32,1);-webkit-transition-timing-function:cubic-bezier(.175,.885,.32,1.15);-moz-transition-timing-function:cubic-bezier(.175,.885,.32,1.15);-ms-transition-timing-function:cubic-bezier(.175,.885,.32,1.15);-o-transition-timing-function:cubic-bezier(.175,.885,.32,1.15);transition-timing-function:cubic-bezier(.175,.885,.32,1.15)}.tooltipster-slide.tooltipster-initial{left:-40px!important}.tooltipster-slide.tooltipster-dying{-webkit-transition-property:all;-moz-transition-property:all;-o-transition-property:all;-ms-transition-property:all;transition-property:all;left:0!important;opacity:0}@keyframes tooltipster-fading{0%{opacity:0}100%{opacity:1}}.tooltipster-update-fade{animation:tooltipster-fading .4s}@keyframes tooltipster-rotating{25%{transform:rotate(-2deg)}75%{transform:rotate(2deg)}100%{transform:rotate(0)}}.tooltipster-update-rotate{animation:tooltipster-rotating .6s}@keyframes tooltipster-scaling{50%{transform:scale(1.1)}100%{transform:scale(1)}}.tooltipster-update-scale{animation:tooltipster-scaling .6s}.tooltipster-sidetip .tooltipster-box{background:#565656;border:2px solid #000;border-radius:4px}.tooltipster-sidetip.tooltipster-bottom .tooltipster-box{margin-top:8px}.tooltipster-sidetip.tooltipster-left .tooltipster-box{margin-right:8px}.tooltipster-sidetip.tooltipster-right .tooltipster-box{margin-left:8px}.tooltipster-sidetip.tooltipster-top .tooltipster-box{margin-bottom:8px}.tooltipster-sidetip .tooltipster-content{color:#fff;line-height:18px;padding:6px 14px}.tooltipster-sidetip .tooltipster-arrow{overflow:hidden;position:absolute}.tooltipster-sidetip.tooltipster-bottom .tooltipster-arrow{height:10px;margin-left:-10px;top:0;width:20px}.tooltipster-sidetip.tooltipster-left .tooltipster-arrow{height:20px;margin-top:-10px;right:0;top:0;width:10px}.tooltipster-sidetip.tooltipster-right .tooltipster-arrow{height:20px;margin-top:-10px;left:0;top:0;width:10px}.tooltipster-sidetip.tooltipster-top .tooltipster-arrow{bottom:0;height:10px;margin-left:-10px;width:20px}.tooltipster-sidetip .tooltipster-arrow-background,.tooltipster-sidetip .tooltipster-arrow-border{height:0;position:absolute;width:0}.tooltipster-sidetip .tooltipster-arrow-background{border:10px solid transparent}.tooltipster-sidetip.tooltipster-bottom .tooltipster-arrow-background{border-bottom-color:#565656;left:0;top:3px}.tooltipster-sidetip.tooltipster-left .tooltipster-arrow-background{border-left-color:#565656;left:-3px;top:0}.tooltipster-sidetip.tooltipster-right .tooltipster-arrow-background{border-right-color:#565656;left:3px;top:0}.tooltipster-sidetip.tooltipster-top .tooltipster-arrow-background{border-top-color:#565656;left:0;top:-3px}.tooltipster-sidetip .tooltipster-arrow-border{border:10px solid transparent;left:0;top:0}.tooltipster-sidetip.tooltipster-bottom .tooltipster-arrow-border{border-bottom-color:#000}.tooltipster-sidetip.tooltipster-left .tooltipster-arrow-border{border-left-color:#000}.tooltipster-sidetip.tooltipster-right .tooltipster-arrow-border{border-right-color:#000}.tooltipster-sidetip.tooltipster-top .tooltipster-arrow-border{border-top-color:#000}.tooltipster-sidetip .tooltipster-arrow-uncropped{position:relative}.tooltipster-sidetip.tooltipster-bottom .tooltipster-arrow-uncropped{top:-10px}.tooltipster-sidetip.tooltipster-right .tooltipster-arrow-uncropped{left:-10px}
--------------------------------------------------------------------------------
/spark-submit-ui.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/test/ApplicationSpec.scala:
--------------------------------------------------------------------------------
1 | import org.specs2.mutable._
2 | import org.specs2.runner._
3 | import org.junit.runner._
4 | import play.api.db.DB
5 | import play.api.test._
6 | import play.api.test.Helpers._
7 |
8 | /**
9 | * Add your spec here.
10 | * You can mock out a whole application including requests, plugins etc.
11 | * For more information, consult the wiki.
12 | */
13 | @RunWith(classOf[JUnitRunner])
14 | class ApplicationSpec extends Specification {
15 |
16 | "Application" should {
17 |
18 | "send 404 on a bad request" in new WithApplication{
19 | DB
20 | route(FakeRequest(GET, "/boum")) must beNone
21 | }
22 |
23 | "render the index page" in new WithApplication{
24 | val home = route(FakeRequest(GET, "/")).get
25 |
26 | status(home) must equalTo(OK)
27 | contentType(home) must beSome.which(_ == "text/html")
28 | contentAsString(home) must contain ("Your new application is ready.")
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/test/IntegrationSpec.scala:
--------------------------------------------------------------------------------
1 | import org.specs2.mutable._
2 | import org.specs2.runner._
3 | import org.junit.runner._
4 |
5 | import play.api.test._
6 | import play.api.test.Helpers._
7 |
8 | /**
9 | * add your integration spec here.
10 | * An integration test will fire up a whole play application in a real (or headless) browser
11 | */
12 | @RunWith(classOf[JUnitRunner])
13 | class IntegrationSpec extends Specification {
14 |
15 | "Application" should {
16 |
17 | "work from within a browser" in new WithBrowser {
18 |
19 | browser.goTo("http://localhost:" + port)
20 |
21 | browser.pageSource must contain("Your new application is ready.")
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------