Commit dd85f57d authored by Mathieu's avatar Mathieu
Browse files

Implement Client / Server application and build up API

parent 2dfb8f63
......@@ -8,5 +8,5 @@ Publish OpenMOLE artifacts locally:
git clone https://gitlab.openmole.org/openmole/openmole.git
cd openmole
git checkout 14-dev
./publishLocal
./publishLocal.sh
```
name := "visu"
val Organization = "org.openmole"
val Name = "Visu playground"
val Version = "0.4"
val ScalaVersion = "2.13.5"
val scalatraVersion = "2.7.1"
val jettyVersion = "11.0.2"
val json4sVersion = "3.6.11"
val autowireVersion = "0.3.3"
val boopickleVersion = "1.3.3"
val laminarVersion = "0.12.2"
val scaladgetVersion = "1.9.0"
val scalajsDomVersion = "1.1.0"
val scalatagsVersion = "0.9.4"
val plotlyVersion = "1.5.4"
val openmoleVersion = "14.0-SNAPSHOT"
version := "1.0-SNAPSHOT"
lazy val shared = project.in(file("shared")) settings (
scalaVersion := ScalaVersion,
libraryDependencies += "org.openmole" %% "org-openmole-plugin-method-evolution" % openmoleVersion
) enablePlugins (ScalaJSPlugin)
scalaVersion := "2.13.5"
libraryDependencies += "org.openmole" %% "org-openmole-plugin-method-evolution" % "14.0-SNAPSHOT"
lazy val go = taskKey[Unit]("go")
lazy val client = project.in(file("client")) enablePlugins(ScalaJSPlugin, ScalaJSBundlerPlugin) settings(
version := Version,
scalaVersion := ScalaVersion,
scalaJSUseMainModuleInitializer := true,
//skip in packageJSDependencies := false,
libraryDependencies ++= Seq(
"com.lihaoyi" %%% "autowire" % autowireVersion,
"io.suzaku" %%% "boopickle" % boopickleVersion,
"com.raquo" %%% "laminar" % laminarVersion,
"org.openmole" %%% "scala-js-plotlyjs" % plotlyVersion,
"org.openmole.scaladget" %%% "tools" % scaladgetVersion,
"org.openmole.scaladget" %%% "svg" % scaladgetVersion,
"org.openmole.scaladget" %%% "bootstrapnative" % scaladgetVersion,
"org.scala-js" %%% "scalajs-dom" % scalajsDomVersion,
"org.json4s" %% "json4s-jackson" % json4sVersion
)
) dependsOn (shared)
lazy val server = project.in(file("server")) settings(
organization := Organization,
name := Name,
version := Version,
scalaVersion := ScalaVersion,
libraryDependencies ++= Seq(
"com.lihaoyi" %% "autowire" % autowireVersion,
"io.suzaku" %% "boopickle" % boopickleVersion,
"com.lihaoyi" %% "scalatags" % scalatagsVersion,
"org.scalatra" %% "scalatra" % scalatraVersion,
"ch.qos.logback" % "logback-classic" % "1.2.3" % "runtime",
"javax.servlet" % "javax.servlet-api" % "4.0.1" % "provided",
"org.eclipse.jetty" % "jetty-webapp" % jettyVersion,
"org.eclipse.jetty" % "jetty-server" % jettyVersion,
"org.openmole" %% "org-openmole-plugin-method-evolution" % openmoleVersion
)
) dependsOn (shared) enablePlugins (ScalatraPlugin)
lazy val bootstrap = project.in(file("target/bootstrap")) enablePlugins(ScalaJSPlugin, ScalaJSBundlerPlugin) settings(
version := Version,
scalaVersion := ScalaVersion,
go := {
val jsBuild = (client / Compile / fullOptJS / webpack).value.head.data
val demoTarget = (server / Compile / target).value
val demoResource = (client / Compile / resourceDirectory).value
IO.copyFile(jsBuild, demoTarget / "webapp/js/demo.js")
IO.copyDirectory(demoResource, demoTarget)
}) dependsOn(client, server)
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<!--
This listener loads a class in the default package called ScalatraBootstrap.
That class should implement org.scalatra.LifeCycle. Your app can be
configured in Scala code there.
-->
<listener>
<listener-class>org.scalatra.servlet.ScalatraListener</listener-class>
</listener>
</web-app>
body{
margin: 0;
padding: 0;
overflow:hidden;
}
p{
text-align: center;
overflow: overlay;
position: relative;
}
body{
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
background-color: rgb(248, 248, 248)
}
#toolbox{
position: absolute;
bottom: 0;
left: 0;
margin-bottom: 0.5em;
margin-left: 1em;
border: 2px solid #EEEEEE;
border-radius: 5px;
padding: 1em;
z-index: 5;
}
#toolbox input{
width: 30px;
opacity: 0.4;
}
#toolbox input:hover{
opacity: 1;
cursor: pointer;
}
#hidden-file-upload{
display: none;
}
#download-input{
margin: 0 0.5em;
}
.conceptG text{
pointer-events: none;
}
marker{
fill: #333;
}
g.conceptG circle{
fill: #F6FBFF;
stroke: #333;
stroke-width: 2px;
z-index: 21;
}
g.conceptG:hover circle{
fill: rgb(200, 238, 1);
}
g.selected circle{
fill: rgb(0, 232, 255);
}
g.selected:hover circle{
fill: rgb(250, 232, 255);
}
path.link {
fill: none;
stroke: #333;
stroke-width: 6px;
cursor: default;
}
path.link:hover{
stroke: rgb(94, 196, 204);
}
g.connect-node circle{
fill: #BEFFFF;
}
path.link.hidden{
stroke-width: 0;
}
path.link.selected {
stroke: rgb(229, 172, 247);
}
This diff is collapsed.
package client
import java.nio.ByteBuffer
import org.scalajs.dom
import scala.concurrent.Future
import boopickle.Default._
import autowire._
import org.scalajs
import scala.concurrent.ExecutionContext.Implicits.global
import boopickle.Default._
import scala.scalajs.js.typedarray.{ArrayBuffer, TypedArrayBuffer}
import com.raquo.laminar.api.L._
import shared.DataUI
object App {
def main(args: Array[String]): Unit = {
val method = Var("")
lazy val d = div(
h2("My plot"),
Histogram.build
)
Post[shared.Api].convergence().call().foreach {c=>
println("C " + c)
}
Post[shared.Api].metadata().call().foreach {m=>
m match {
case nsga2: DataUI.NSGA2.Metadata=> println("M " + nsga2.OMRData.method)
case stNSGA2: DataUI.StochasticNSGA2.Metadata=> println("M " + stNSGA2.OMRData.method)
}
}
documentEvents.onDomContentLoaded.foreach { _ =>
render(scalajs.dom.document.body, d)
}(unsafeWindowOwner)
}
}
object Post extends autowire.Client[ByteBuffer, Pickler, Pickler] {
override def doCall(req: Request): Future[ByteBuffer] = {
dom.ext.Ajax.post(
url = req.path.mkString("/"),
data = Pickle.intoBytes(req.args),
responseType = "arraybuffer",
headers = Map("Content-Type" -> "application/octet-stream")
).map(r => TypedArrayBuffer.wrap(r.response.asInstanceOf[ArrayBuffer]))
}
override def read[Result: Pickler](p: ByteBuffer) = Unpickle[Result].fromBytes(p)
override def write[Result: Pickler](r: Result) = Pickle.intoBytes(r)
}
package client
import org.openmole.plotlyjs._
import org.openmole.plotlyjs.all._
import scala.scalajs.js.JSConverters._
import org.openmole.plotlyjs.PlotlyImplicits._
import com.raquo.laminar.api.L._
import org.openmole.plotlyjs.HistogramDataBuilder.HistogramDataBuilder
import scala.scalajs._
object Histogram {
def build = {
val plotDiv = div()
val layout = Layout
.title("My line plot")
.grid(grid.columns(3).rows(1).pattern(Pattern.coupled))
.showlegend(true)
val data1 = histogram
.x(Utils.randomInts(500))
.name("First serie")
.xbins(Bin.start(0.0).end(1000.0).size(25))
val data2 = histogram
.x(Utils.anArray.toJSArray)
.xaxis("x2")
.name("Second serie")
val data3 = histogram
.x(Utils.anArray.toJSArray)
.xaxis("x3")
.name("Second serie")
Plotly.newPlot(plotDiv.ref, js.Array(data1, data2, data3), layout = layout)
plotDiv
}
}
\ No newline at end of file
package client
/*
* Copyright (C) 24/03/16 // mathieu.leclaire@openmole.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU General Public License
*/
import scala.scalajs.js.JSConverters._
object Utils {
lazy val rng = scala.util.Random
val viridis = Seq(
"440154",
"481567",
"482677",
"453781",
"404788",
"39568C",
"33638D",
"2D708E",
"287D8E",
"238A8D",
"1F968B",
"20A387",
"29AF7F",
"3CBB75",
"55C667",
"73D055",
"95D840",
"B8DE29",
"DCE319",
"FDE725"
)
def randomDoubles(nb: Int = 100, ratio: Int = 1000) = Seq.fill(nb)(rng.nextDouble * ratio).toJSArray
def randomDoublesAsStrings(nb: Int = 100, ratio: Int = 1000) = Seq.fill(nb)((rng.nextDouble * ratio).toString).toJSArray
def randomInts(nb: Int = 100, ratio: Int = 1000) = Seq.fill(nb)(rng.nextInt(ratio)).toJSArray
def anArray = Array(
4.9,
4.7,
4.6,
5.0,
5.4,
4.6,
5.0,
4.4,
4.9,
5.4,
4.8,
4.8,
4.3,
5.8,
5.7,
5.4,
5.1,
5.7,
5.1,
5.4,
5.1,
4.6,
5.1,
4.8,
5.0,
5.0,
5.2,
5.2,
4.7,
4.8,
5.4,
5.2,
5.5,
4.9,
5.0,
5.5,
4.9,
4.4,
5.1,
5.0,
4.5,
4.4,
5.0,
5.1,
4.8,
5.1,
4.6,
5.3,
5.0,
7.0,
6.4,
6.9,
5.5,
6.5,
5.7,
6.3,
4.9,
6.6,
5.2,
5.0,
5.9,
6.0,
6.1,
5.6,
6.7,
5.6,
5.8,
6.2,
5.6,
5.9,
6.1,
6.3,
6.1,
6.4,
6.6,
6.8,
6.7,
6.0,
5.7,
5.5,
5.5,
5.8,
6.0,
5.4,
6.0,
6.7,
6.3,
5.6,
5.5,
5.5,
6.1,
5.8,
5.0,
5.6,
5.7,
5.7,
6.2,
5.1,
5.7,
6.3,
5.8,
7.1,
6.3,
6.5,
7.6,
4.9,
7.3,
6.7,
7.2,
6.5,
6.4,
6.8,
5.7,
5.8,
6.4,
6.5,
7.7,
7.7,
6.0,
6.9,
5.6,
7.7,
6.3,
6.7,
7.2,
6.2,
6.1,
6.4,
7.2,
7.4,
7.9,
6.4,
6.3,
6.1,
7.7,
6.3,
6.4,
6.0,
6.9,
6.7,
6.9,
5.8,
6.8,
6.7,
6.7,
6.3,
6.5,
6.2,
5.9).toJSArray
}
resolvers += "Typesafe repository" at "https://repo.typesafe.com/typesafe/releases/"
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.5.1")
addSbtPlugin("ch.epfl.scala" % "sbt-scalajs-bundler" % "0.20.0")
addSbtPlugin("org.scalatra.sbt" % "sbt-scalatra" % "1.0.4")
addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.5.3")
\ No newline at end of file
import server._
import org.scalatra._
import javax.servlet.ServletContext
class ScalatraBootstrap extends LifeCycle {
override def init(context: ServletContext) {
context.mount(new Servlet, "/*")
}
}
\ No newline at end of file
package server
object ApiImpl extends shared.Api {
def convergence()= OpenMOLEMethods.convergence
def metadata()= OpenMOLEMethods.metadata
}
package server
import org.openmole.core.workflow.test.Stubs
import org.openmole.plugin.hook.omr.OMROutputFormat.OMRData
import org.openmole.plugin.method.evolution.{Analysis, data}
import org.openmole.plugin.method.evolution.Analysis.NSGA2
import org.openmole.plugin.method.evolution.data.EvolutionMetadata.{ExactObjective, GenomeBoundData, NoisyObjective}
import org.openmole.plugin.method.evolution.data.EvolutionMetadata.GenomeBoundData._
import org.openmole.plugin.method.evolution.data.{AnalysisData, EvolutionMetadata, SaveOption}
import shared.DataUI
import java.io.File
object OpenMOLEMethods {
import Stubs._
implicit def toNSGA2ConvergenceDataUI(convergence: AnalysisData.NSGA2.Convergence): DataUI.Convergence =
DataUI.NSGA2.Convergence(convergence.generations.map { gc => DataUI.NSGA2.GenerationConvergence(gc.generation, gc.hypervolume, gc.minimums) })
implicit def toStochasticNSGA2ConvergenceDataUI(convergence: AnalysisData.StochasticNSGA2.Convergence): DataUI.Convergence =
DataUI.StochasticNSGA2.Convergence(convergence.generations.map { gc => DataUI.StochasticNSGA2.GenerationConvergence(gc.generation, gc.hypervolume, gc.minimums) })
implicit def toOMRDataUI(OMRData: OMRData): DataUI.OMRData = DataUI.OMRData(OMRData.method, OMRData.fileName, OMRData.version)
implicit def toGenomeUI(genomeBoundDatas: Seq[GenomeBoundData]): Seq[DataUI.GenomeBoundData] =
genomeBoundDatas.map {
_ match {
case DoubleBound(name, low, high) => DataUI.GenomeBoundData.DoubleBound(name, low, high)
case IntBound(name, low, high) => DataUI.GenomeBoundData.IntBound(name, low, high)
case DoubleSequenceBound(name, low, high) => DataUI.GenomeBoundData.DoubleSequenceBound(name, low, high)
case IntSequenceBound(name, low, high) => DataUI.GenomeBoundData.IntSequenceBound(name, low, high)
case Enumeration(name, values) => DataUI.GenomeBoundData.Enumeration(name, values)
}
}
implicit def toSaveOptionUI(saveOption: SaveOption): DataUI.SaveOption = DataUI.SaveOption(saveOption.frequency, saveOption.last)
implicit def toExactObjectiveUI(objectives: Seq[ExactObjective]): Seq[DataUI.ExactObjective] = objectives.map{ob=> DataUI.ExactObjective(ob.name, ob.delta, ob.negative)}
implicit def toNoisyObjectiveUI(objectives: Seq[NoisyObjective]): Seq[DataUI.NoisyObjective] = objectives.map{ob=> DataUI.NoisyObjective(ob.name, ob.delta, ob.negative)}
implicit def toNSGA2EvolutionMetadataUI(emd: data.EvolutionMetadata.NSGA2): DataUI.NSGA2.EvolutionMetadata =
DataUI.NSGA2.EvolutionMetadata(emd.genome, emd.objective, emd.populationSize, emd.generation, emd.saveOption)
implicit def toStochasticNSGA2EvolutionMetadataUI(emd: data.EvolutionMetadata.StochasticNSGA2): DataUI.StochasticNSGA2.EvolutionMetadata =
DataUI.StochasticNSGA2.EvolutionMetadata(emd.genome, emd.objective, emd.populationSize, emd.sample, emd.generation, emd.saveOption)