From 2406a75b56369bd80a2937dc8695f19e4df4526a Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 28 Dec 2023 20:50:13 +0000 Subject: [PATCH 001/144] - --- bin/publish-local | 2 +- build.sbt | 2 +- example-scripts/project.scala | 2 +- example-scripts/server.sc | 2 +- .../src/main/scala/org/terminal21/server/Terminal21Server.scala | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bin/publish-local b/bin/publish-local index 3ab1bc07..6bfcc82b 100755 --- a/bin/publish-local +++ b/bin/publish-local @@ -4,4 +4,4 @@ cd ../terminal21-ui bin/build-and-copy-to-restapi cd ../terminal21-restapi -sbt clean compile publishLocal +sbt clean terminal21-server-client-common/publishLocal terminal21-ui-std-exports/publishLocal compile publishLocal diff --git a/build.sbt b/build.sbt index 5f266381..e3af9b28 100644 --- a/build.sbt +++ b/build.sbt @@ -3,7 +3,7 @@ */ val scala3Version = "3.3.1" -ThisBuild / version := "0.1" +ThisBuild / version := "0.11" ThisBuild / organization := "io.github.kostaskougios" name := "rest-api" ThisBuild / scalaVersion := scala3Version diff --git a/example-scripts/project.scala b/example-scripts/project.scala index d2324063..1103e496 100644 --- a/example-scripts/project.scala +++ b/example-scripts/project.scala @@ -1,4 +1,4 @@ //> using jvm "21" //> using scala 3 -//> using dep io.github.kostaskougios::terminal21-ui-std:0.1 +//> using dep io.github.kostaskougios::terminal21-ui-std:0.11 diff --git a/example-scripts/server.sc b/example-scripts/server.sc index 40f22515..23a0b561 100755 --- a/example-scripts/server.sc +++ b/example-scripts/server.sc @@ -2,7 +2,7 @@ //> using jvm "21" //> using scala 3 -//> using dep io.github.kostaskougios::terminal21-server:0.1 +//> using dep io.github.kostaskougios::terminal21-server:0.11 import org.terminal21.server.Terminal21Server diff --git a/terminal21-server/src/main/scala/org/terminal21/server/Terminal21Server.scala b/terminal21-server/src/main/scala/org/terminal21/server/Terminal21Server.scala index ef907e8d..70e8be52 100644 --- a/terminal21-server/src/main/scala/org/terminal21/server/Terminal21Server.scala +++ b/terminal21-server/src/main/scala/org/terminal21/server/Terminal21Server.scala @@ -29,7 +29,7 @@ object Terminal21Server: if !server.isRunning then throw new IllegalStateException("Server failed to start") try - logger.info(s"Terminal 21 Server started and listening on http://localhost:$portV") + logger.info(s"Terminal 21 Server started. Please open http://localhost:$portV/ui for the user interface") val hostname = InetAddress.getLocalHost.getHostName logger.info(s""" |Files under ~/.terminal21/web will be served under /web From c9f39752f692f97e0052a003d30a05df007600c5 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Mon, 1 Jan 2024 19:36:13 +0000 Subject: [PATCH 002/144] - --- Readme.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Readme.md b/Readme.md index 8ce971ba..b775bab4 100644 --- a/Readme.md +++ b/Readme.md @@ -6,6 +6,8 @@ of what can be done at the [terminal 21 youtube channel](https://www.youtube.com For scala 3 and jdk21 or better. If you have scala-cli installed, you won't need to download scala 3 or jdk21, see below for instructions on how to quickly start with terminal21. +Note: feel free to ask questions in the "Discussions" board at the top of this github page. + Terminal21 consist of : - a web server that can be easily deployed on your laptop, home network etc - scala apps (scala-cli/ammonite scripts or just normal scala apps) that use the terminal21 UI libs to create user interfaces From b6a820ddf16ec068a8ef847ad9c93ab0e72656b5 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Tue, 2 Jan 2024 13:54:50 +0000 Subject: [PATCH 003/144] - --- .../org/terminal21/client/components/chakra/ChakraElement.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala index 75284446..fd10efb5 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala @@ -954,7 +954,6 @@ case class Tfoot(key: String = Keys.nextKey, @volatile var children: Seq[UiEleme with HasChildren[Tfoot] case class Tr( key: String = Keys.nextKey, - isNumeric: Boolean = false, @volatile var children: Seq[UiElement] = Nil, @volatile var style: Map[String, String] = Map.empty ) extends ChakraElement From 0522c5db4829ed399b80b9311293e2110d35ee2d Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Tue, 2 Jan 2024 14:03:31 +0000 Subject: [PATCH 004/144] - --- .../terminal21/client/components/chakra/ChakraElement.scala | 3 --- 1 file changed, 3 deletions(-) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala index fd10efb5..c7a106d9 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala @@ -908,7 +908,6 @@ case class Switch( case class Select( key: String = Keys.nextKey, placeholder: String = "", - defaultValue: String = "", @volatile var value: String = "", @volatile var bg: Option[String] = None, @volatile var color: Option[String] = None, @@ -919,8 +918,6 @@ case class Select( with HasEventHandler with HasChildren[Select] with OnChangeEventHandler.CanHandleOnChangeEvent[Select]: - if value == "" then value = defaultValue - override def defaultEventHandler: OnChangeEventHandler = newValue => value = newValue case class Option_( From b8fc236815dc9b963cf3808a7322ed20824c016b Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Tue, 2 Jan 2024 17:18:05 +0000 Subject: [PATCH 005/144] - --- examples/src/main/scala/tests/chakra/Overlay.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/src/main/scala/tests/chakra/Overlay.scala b/examples/src/main/scala/tests/chakra/Overlay.scala index 13358751..caa9aa9b 100644 --- a/examples/src/main/scala/tests/chakra/Overlay.scala +++ b/examples/src/main/scala/tests/chakra/Overlay.scala @@ -12,7 +12,7 @@ object Overlay: commonBox(text = "Menus"), HStack().withChildren( Menu().withChildren( - MenuButton(text = "Actions", size = Some("sm"), colorScheme = Some("teal")).withChildren( + MenuButton(text = "Actions menu0001", size = Some("sm"), colorScheme = Some("teal")).withChildren( ChevronDownIcon() ), MenuList().withChildren( From eb7362df3adec858b7486285377e96a1f5d20106 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Tue, 2 Jan 2024 18:42:10 +0000 Subject: [PATCH 006/144] - --- examples/src/main/scala/tests/chakra/Overlay.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/src/main/scala/tests/chakra/Overlay.scala b/examples/src/main/scala/tests/chakra/Overlay.scala index caa9aa9b..4792157a 100644 --- a/examples/src/main/scala/tests/chakra/Overlay.scala +++ b/examples/src/main/scala/tests/chakra/Overlay.scala @@ -16,7 +16,7 @@ object Overlay: ChevronDownIcon() ), MenuList().withChildren( - MenuItem(text = "Download") + MenuItem(text = "Download menu-download") .onClick: () => box1.text = "'Download' clicked" session.render() From 3644a71026c6298acbed9cef77feacafd283cc7e Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Tue, 2 Jan 2024 18:50:09 +0000 Subject: [PATCH 007/144] - --- examples/src/main/scala/tests/chakra/Overlay.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/src/main/scala/tests/chakra/Overlay.scala b/examples/src/main/scala/tests/chakra/Overlay.scala index 4792157a..75e672cb 100644 --- a/examples/src/main/scala/tests/chakra/Overlay.scala +++ b/examples/src/main/scala/tests/chakra/Overlay.scala @@ -9,7 +9,7 @@ object Overlay: def components(using session: ConnectedSession): Seq[UiElement] = val box1 = Box(text = "Clicks will be reported here.") Seq( - commonBox(text = "Menus"), + commonBox(text = "Menus box0001"), HStack().withChildren( Menu().withChildren( MenuButton(text = "Actions menu0001", size = Some("sm"), colorScheme = Some("teal")).withChildren( From 9c7e201f0d703ba8eeacc8cf51cb20f7052160c3 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Tue, 2 Jan 2024 20:52:45 +0000 Subject: [PATCH 008/144] - --- .../main/scala/tests/ChakraComponents.scala | 35 +++++++++++++------ .../org/terminal21/client/Sessions.scala | 2 -- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/examples/src/main/scala/tests/ChakraComponents.scala b/examples/src/main/scala/tests/ChakraComponents.scala index 3b03ccb8..1b2728d7 100644 --- a/examples/src/main/scala/tests/ChakraComponents.scala +++ b/examples/src/main/scala/tests/ChakraComponents.scala @@ -1,23 +1,36 @@ package tests import org.terminal21.client.* +import org.terminal21.client.components.chakra.* import org.terminal21.client.components.{Paragraph, render} import tests.chakra.* +import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.{CountDownLatch, TimeUnit} @main def chakraComponents(): Unit = - Sessions.withNewSession("chakra-components", "Chakra Components"): session => - given ConnectedSession = session + val keepRunning = new AtomicBoolean(true) - val latch = new CountDownLatch(1) + while keepRunning.get() do + println("Starting new session") + Sessions.withNewSession("chakra-components", "Chakra Components"): session => + keepRunning.set(false) + given ConnectedSession = session - (Overlay.components ++ Forms.components ++ Editables.components ++ Stacks.components ++ Grids.components ++ Buttons.components( - latch - ) ++ Etc.components ++ MediaAndIcons.components ++ DataDisplay.components) - .render() + val latch = new CountDownLatch(1) - println("Waiting for button to be pressed for 1 hour") - latch.await(1, TimeUnit.HOURS) - session.clear() - Paragraph(text = "Terminated").render() + // react tests reset the session to clear state + val krButton = Button(text = "Keep Running").onClick: () => + keepRunning.set(true) + latch.countDown() + + (Overlay.components ++ Forms.components ++ Editables.components ++ Stacks.components ++ Grids.components ++ Buttons.components( + latch + ) ++ Etc.components ++ MediaAndIcons.components ++ DataDisplay.components ++ Seq(krButton)) + .render() + + println("Waiting for button to be pressed for 1 hour") + latch.await(1, TimeUnit.HOURS) + session.clear() + Paragraph(text = "Terminated").render() + Thread.sleep(2000) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/Sessions.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/Sessions.scala index 00370517..4757ebe8 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/Sessions.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/Sessions.scala @@ -23,11 +23,9 @@ object Sessions: .baseUri(s"ws://${config.host}:${config.port}") .build - val currentThread = Thread.currentThread() val isStopped = new AtomicBoolean(false) def terminate(): Unit = isStopped.set(true) - currentThread.interrupt() val connectedSession = ConnectedSession(session, serverUrl, sessionsService, terminate) FiberExecutor.withFiberExecutor: executor => From 088559808cd406106f1e0b3dc7f75dcf6d7fa308 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Tue, 2 Jan 2024 21:06:05 +0000 Subject: [PATCH 009/144] - --- example-scripts/csv-editor.sc | 2 -- example-scripts/csv-viewer.sc | 2 -- example-scripts/project.scala | 1 + example-scripts/textedit.sc | 2 -- .../main/scala/tests/ChakraComponents.scala | 9 ++++---- .../terminal21/client/ConnectedSession.scala | 21 +++++++++++++++++-- 6 files changed, 25 insertions(+), 12 deletions(-) diff --git a/example-scripts/csv-editor.sc b/example-scripts/csv-editor.sc index 558679bc..85330975 100755 --- a/example-scripts/csv-editor.sc +++ b/example-scripts/csv-editor.sc @@ -4,8 +4,6 @@ // A quick and dirty casv file editor for small csv files. // ------------------------------------------------------------------------------ -//> using dep commons-io:commons-io:2.15.1 - // always import these import org.terminal21.client.* // std components, https://github.com/kostaskougios/terminal21-restapi/blob/main/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdElement.scala diff --git a/example-scripts/csv-viewer.sc b/example-scripts/csv-viewer.sc index 0408d078..2ecd5a2d 100755 --- a/example-scripts/csv-viewer.sc +++ b/example-scripts/csv-viewer.sc @@ -4,8 +4,6 @@ // A csv file viewer // ------------------------------------------------------------------------------ -//> using dep commons-io:commons-io:2.15.1 - // always import these import org.terminal21.client.* // std components, https://github.com/kostaskougios/terminal21-restapi/blob/main/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdElement.scala diff --git a/example-scripts/project.scala b/example-scripts/project.scala index 1103e496..b8b2b135 100644 --- a/example-scripts/project.scala +++ b/example-scripts/project.scala @@ -2,3 +2,4 @@ //> using scala 3 //> using dep io.github.kostaskougios::terminal21-ui-std:0.11 +//> using dep commons-io:commons-io:2.15.1 diff --git a/example-scripts/textedit.sc b/example-scripts/textedit.sc index db1177e4..dc894124 100755 --- a/example-scripts/textedit.sc +++ b/example-scripts/textedit.sc @@ -4,8 +4,6 @@ // A text file editor for small files. // ------------------------------------------------------------------------------ -//> using dep commons-io:commons-io:2.15.1 - import org.apache.commons.io.FileUtils import java.io.File diff --git a/examples/src/main/scala/tests/ChakraComponents.scala b/examples/src/main/scala/tests/ChakraComponents.scala index 1b2728d7..5ebc8890 100644 --- a/examples/src/main/scala/tests/ChakraComponents.scala +++ b/examples/src/main/scala/tests/ChakraComponents.scala @@ -30,7 +30,8 @@ import java.util.concurrent.{CountDownLatch, TimeUnit} .render() println("Waiting for button to be pressed for 1 hour") - latch.await(1, TimeUnit.HOURS) - session.clear() - Paragraph(text = "Terminated").render() - Thread.sleep(2000) + session.waitTillUserClosesSessionOr(latch.getCount == 0) + if !session.isClosed then + session.clear() + Paragraph(text = "Terminated").render() + Thread.sleep(2000) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala index 4e32c05e..b62e8182 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala @@ -10,7 +10,8 @@ import org.terminal21.client.components.UiElementEncoding.uiElementEncoder import org.terminal21.model.* import org.terminal21.ui.std.SessionsService -import java.util.concurrent.CountDownLatch +import java.util.concurrent.{CountDownLatch, TimeUnit} +import scala.annotation.tailrec class ConnectedSession(val session: Session, val serverUrl: String, sessionsService: SessionsService, onCloseHandler: () => Unit): private val logger = LoggerFactory.getLogger(getClass) @@ -39,11 +40,27 @@ class ConnectedSession(val session: Session, val serverUrl: String, sessionsServ val handlers = eventHandlers.getOrElse(key, Nil) eventHandlers += key -> (handlers :+ handler) - private val exitLatch = new CountDownLatch(1) + private val exitLatch = new CountDownLatch(1) + + /** Waits till user closes the session by clicking the session close [X] button. + */ def waitTillUserClosesSession(): Unit = try exitLatch.await() catch case _: Throwable => () // nop + /** Waits till user closes the session or a custom condition becomes true + * @param condition + * if true then this returns otherwise it waits. + */ + @tailrec final def waitTillUserClosesSessionOr(condition: => Boolean): Unit = + exitLatch.await(100, TimeUnit.MILLISECONDS) + if exitLatch.getCount == 0 || condition then () else waitTillUserClosesSessionOr(condition) + + /** @return + * true if user closed the session via the close button + */ + def isClosed: Boolean = exitLatch.getCount == 0 + def fireEvent(event: CommandEvent): Unit = event match case SessionClosed(_) => From 44e3f2864d9d5ccb246be840e0f3553af3d577da Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Wed, 3 Jan 2024 13:58:52 +0000 Subject: [PATCH 010/144] - --- examples/src/main/scala/tests/ChakraComponents.scala | 2 +- examples/src/main/scala/tests/chakra/Etc.scala | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/src/main/scala/tests/ChakraComponents.scala b/examples/src/main/scala/tests/ChakraComponents.scala index 5ebc8890..8185ffec 100644 --- a/examples/src/main/scala/tests/ChakraComponents.scala +++ b/examples/src/main/scala/tests/ChakraComponents.scala @@ -34,4 +34,4 @@ import java.util.concurrent.{CountDownLatch, TimeUnit} if !session.isClosed then session.clear() Paragraph(text = "Terminated").render() - Thread.sleep(2000) + Thread.sleep(1000) diff --git a/examples/src/main/scala/tests/chakra/Etc.scala b/examples/src/main/scala/tests/chakra/Etc.scala index f2470cb2..42b930c1 100644 --- a/examples/src/main/scala/tests/chakra/Etc.scala +++ b/examples/src/main/scala/tests/chakra/Etc.scala @@ -10,11 +10,11 @@ object Etc: Seq( commonBox(text = "Center"), Center(text = "Center demo, not styled"), - Center(text = "Center demo", bg = Some("tomato"), color = Some("white"), w = Some("100px"), h = Some("100px")), + Center(text = "Center demo center-demo-0001", bg = Some("tomato"), color = Some("white"), w = Some("100px"), h = Some("100px")), commonBox(text = "Circle"), Circle(text = "Circle demo, not styled"), - Circle(text = "Circle demo", bg = Some("tomato"), color = Some("white"), w = Some("100px"), h = Some("100px")), + Circle(text = "Circle demo circle-demo-0001", bg = Some("tomato"), color = Some("white"), w = Some("100px"), h = Some("100px")), commonBox(text = "Square"), Square(text = "Square demo, not styled"), - Square(text = "Square demo", bg = Some("tomato"), color = Some("white"), w = Some("100px"), h = Some("100px")) + Square(text = "Square demo square-demo-0001", bg = Some("tomato"), color = Some("white"), w = Some("100px"), h = Some("100px")) ) From 651758a54a9d7b90e1f9bc29d21758522c64f86c Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Wed, 3 Jan 2024 14:23:47 +0000 Subject: [PATCH 011/144] - --- examples/src/main/scala/tests/chakra/DataDisplay.scala | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/src/main/scala/tests/chakra/DataDisplay.scala b/examples/src/main/scala/tests/chakra/DataDisplay.scala index ff7ceedb..4265da5a 100644 --- a/examples/src/main/scala/tests/chakra/DataDisplay.scala +++ b/examples/src/main/scala/tests/chakra/DataDisplay.scala @@ -26,7 +26,7 @@ object DataDisplay: commonBox(text = "Tables"), TableContainer().withChildren( Table(variant = "striped", colorScheme = Some("teal"), size = "lg").withChildren( - TableCaption(text = "Imperial to metric conversion factors"), + TableCaption(text = "Imperial to metric conversion factors (table-caption-0001)"), Thead().withChildren( headAndFoot ), @@ -45,6 +45,11 @@ object DataDisplay: Td(text = "yards"), Td(text = "metres (m)"), Td(text = "0.91444", isNumeric = true) + ), + Tr().withChildren( + Td(text = "td0001"), + Td(text = "td0002"), + Td(text = "td0003", isNumeric = true) ) ), Tfoot().withChildren( From a338ec028316b3d149c7ca1e11b11cde2e3cb424 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Wed, 3 Jan 2024 15:58:15 +0000 Subject: [PATCH 012/144] - --- examples/src/main/scala/tests/StdComponents.scala | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/src/main/scala/tests/StdComponents.scala b/examples/src/main/scala/tests/StdComponents.scala index d0c0447b..7f633f7b 100644 --- a/examples/src/main/scala/tests/StdComponents.scala +++ b/examples/src/main/scala/tests/StdComponents.scala @@ -28,6 +28,4 @@ import org.terminal21.client.components.* output ).render() - for i <- 1 to 400 do - println(s"i = $i, input value = ${input.value}") - Thread.sleep(1000) + session.waitTillUserClosesSession() From 2904c7beda14c8b6738eefa85083e64a62a89bb2 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Wed, 3 Jan 2024 20:15:09 +0000 Subject: [PATCH 013/144] - --- build.sbt | 23 +++++- .../org/sparkonscala3/lib/Sessions.scala | 18 +++++ .../sparkonscala3/lib/SparkSessionExt.scala | 16 ++++ .../org/sparkonscala3/lib/steps/Steps.scala | 40 ++++++++++ .../sparkonscala3/lib/util/Environment.scala | 8 ++ .../src/test/resources/logback-test.xml | 19 +++++ .../lib/AbstractSparkSuite.scala | 11 +++ .../org/sparkonscala3/lib/SessionsTest.scala | 74 +++++++++++++++++++ .../lib/SparkSessionExtTest.scala | 32 ++++++++ .../sparkonscala3/lib/steps/StepsTest.scala | 66 +++++++++++++++++ .../sparkonscala3/lib/testmodel/Person.scala | 3 + 11 files changed, 308 insertions(+), 2 deletions(-) create mode 100644 terminal21-spark/src/main/scala/org/sparkonscala3/lib/Sessions.scala create mode 100644 terminal21-spark/src/main/scala/org/sparkonscala3/lib/SparkSessionExt.scala create mode 100644 terminal21-spark/src/main/scala/org/sparkonscala3/lib/steps/Steps.scala create mode 100644 terminal21-spark/src/main/scala/org/sparkonscala3/lib/util/Environment.scala create mode 100644 terminal21-spark/src/test/resources/logback-test.xml create mode 100644 terminal21-spark/src/test/scala/org/sparkonscala3/lib/AbstractSparkSuite.scala create mode 100644 terminal21-spark/src/test/scala/org/sparkonscala3/lib/SessionsTest.scala create mode 100644 terminal21-spark/src/test/scala/org/sparkonscala3/lib/SparkSessionExtTest.scala create mode 100644 terminal21-spark/src/test/scala/org/sparkonscala3/lib/steps/StepsTest.scala create mode 100644 terminal21-spark/src/test/scala/org/sparkonscala3/lib/testmodel/Person.scala diff --git a/build.sbt b/build.sbt index e3af9b28..9f6b3a2d 100644 --- a/build.sbt +++ b/build.sbt @@ -43,8 +43,15 @@ val HelidonClientWebSocket = "io.helidon.webclient" % "helidon-webclient-websock val HelidonClient = "io.helidon.webclient" % "helidon-webclient-http2" % HelidonVersion val HelidonServerLogging = "io.helidon.logging" % "helidon-logging-jul" % HelidonVersion -val LogBack = "ch.qos.logback" % "logback-classic" % "1.4.14" -val Slf4jApi = "org.slf4j" % "slf4j-api" % "2.0.9" +val LogBack = "ch.qos.logback" % "logback-classic" % "1.4.14" +val Slf4jApi = "org.slf4j" % "slf4j-api" % "2.0.9" + +val SparkSql = ("org.apache.spark" %% "spark-sql" % "3.5.0" % "provided").cross(CrossVersion.for3Use2_13).exclude("org.scala-lang.modules", "scala-xml_2.13") +val SparkScala3Fix = Seq( + "io.github.vincenzobaz" %% "spark-scala3-encoders" % "0.2.5", + "io.github.vincenzobaz" %% "spark-scala3-udf" % "0.2.5" +).map(_.exclude("org.scala-lang.modules", "scala-xml_2.13")) + // ----------------------------------------------------------------------------------------------- // Modules // ----------------------------------------------------------------------------------------------- @@ -138,3 +145,15 @@ lazy val examples = project libraryDependencies ++= Seq(ScalaTest, LogBack) ) .dependsOn(`terminal21-ui-std`) + +lazy val `terminal21-spark` = project + .settings( + commonSettings, + Test / fork := true, + Test / javaOptions ++= Seq("--add-opens", "java.base/sun.nio.ch=ALL-UNNAMED"), + libraryDependencies ++= Seq( + ScalaTest, + SparkSql, + LogBack % Test + ) ++ SparkScala3Fix + ) diff --git a/terminal21-spark/src/main/scala/org/sparkonscala3/lib/Sessions.scala b/terminal21-spark/src/main/scala/org/sparkonscala3/lib/Sessions.scala new file mode 100644 index 00000000..e66f8c8f --- /dev/null +++ b/terminal21-spark/src/main/scala/org/sparkonscala3/lib/Sessions.scala @@ -0,0 +1,18 @@ +package org.sparkonscala3.lib + +import org.apache.spark.sql.SparkSession + +object Sessions: + def newSparkSession( + appName: String = "spark-app", + master: String = "local[*]", + bindAddress: String = "localhost", + sparkUiEnabled: Boolean = false + ): SparkSession = + SparkSession + .builder() + .appName(appName) + .master(master) + .config("spark.driver.bindAddress", bindAddress) + .config("spark.ui.enabled", sparkUiEnabled) + .getOrCreate() diff --git a/terminal21-spark/src/main/scala/org/sparkonscala3/lib/SparkSessionExt.scala b/terminal21-spark/src/main/scala/org/sparkonscala3/lib/SparkSessionExt.scala new file mode 100644 index 00000000..04b8a230 --- /dev/null +++ b/terminal21-spark/src/main/scala/org/sparkonscala3/lib/SparkSessionExt.scala @@ -0,0 +1,16 @@ +package org.sparkonscala3.lib + +import org.apache.spark.sql.{DataFrame, Dataset, Encoder, SparkSession} + +import scala.reflect.ClassTag + +class SparkSessionExt(spark: SparkSession): + import spark.implicits.* + + def schemaOf[P: Encoder] = summon[Encoder[P]].schema + + def toDF[P: ClassTag: Encoder](s: Seq[P], numSlices: Int = spark.sparkContext.defaultParallelism): DataFrame = + spark.sparkContext.parallelize(s, numSlices).toDF() + + def toDS[P: ClassTag: Encoder](s: Seq[P], numSlices: Int = spark.sparkContext.defaultParallelism): Dataset[P] = + spark.sparkContext.parallelize(s, numSlices).toDS() diff --git a/terminal21-spark/src/main/scala/org/sparkonscala3/lib/steps/Steps.scala b/terminal21-spark/src/main/scala/org/sparkonscala3/lib/steps/Steps.scala new file mode 100644 index 00000000..704b6224 --- /dev/null +++ b/terminal21-spark/src/main/scala/org/sparkonscala3/lib/steps/Steps.scala @@ -0,0 +1,40 @@ +package org.sparkonscala3.lib.steps + +import org.apache.commons.io.FileUtils +import org.apache.spark.sql.{DataFrame, Dataset, Encoder, SparkSession} +import org.sparkonscala3.lib.util.Environment + +import java.io.File + +class Steps(spark: SparkSession, name: String): + private val rootFolder = s"${Environment.tmpDirectory}/steps/$name" + + def step(name: String): Step = new Step(spark, rootFolder, name) + +class Step(spark: SparkSession, rootFolder: String, name: String): + val targetDir = s"$rootFolder/$name" + + def invalidateCache(): Unit = + FileUtils.deleteDirectory(new File(targetDir)) + + private def cache[A](reader: => A, writer: => A): A = + if new File(targetDir).exists() then reader + else writer + + def calculateOnce(f: => DataFrame): DataFrame = + cache( + spark.read.parquet(targetDir), { + val df = f + df.write.parquet(targetDir) + df + } + ) + + def calculateOnce[A: Encoder](f: => Dataset[A]): Dataset[A] = + cache( + spark.read.parquet(targetDir).as[A], { + val ds = f + ds.write.parquet(targetDir) + ds + } + ) diff --git a/terminal21-spark/src/main/scala/org/sparkonscala3/lib/util/Environment.scala b/terminal21-spark/src/main/scala/org/sparkonscala3/lib/util/Environment.scala new file mode 100644 index 00000000..eca4917a --- /dev/null +++ b/terminal21-spark/src/main/scala/org/sparkonscala3/lib/util/Environment.scala @@ -0,0 +1,8 @@ +package org.sparkonscala3.lib.util + +import org.apache.commons.lang3.StringUtils + +object Environment: + val tmpDirectory = + val t = System.getProperty("java.io.tmpdir") + if (t.endsWith("/")) StringUtils.substringBeforeLast(t, "/") else t diff --git a/terminal21-spark/src/test/resources/logback-test.xml b/terminal21-spark/src/test/resources/logback-test.xml new file mode 100644 index 00000000..f037a072 --- /dev/null +++ b/terminal21-spark/src/test/resources/logback-test.xml @@ -0,0 +1,19 @@ + + + + + + + + %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + diff --git a/terminal21-spark/src/test/scala/org/sparkonscala3/lib/AbstractSparkSuite.scala b/terminal21-spark/src/test/scala/org/sparkonscala3/lib/AbstractSparkSuite.scala new file mode 100644 index 00000000..01e366ac --- /dev/null +++ b/terminal21-spark/src/test/scala/org/sparkonscala3/lib/AbstractSparkSuite.scala @@ -0,0 +1,11 @@ +package org.sparkonscala3.lib + +import org.scalatest.funsuite.AnyFunSuiteLike +import org.scalatest.matchers.should.Matchers +import org.sparkonscala3.lib.util.Environment + +import java.util.UUID + +class AbstractSparkSuite extends AnyFunSuiteLike with Matchers: + protected def randomString: String = UUID.randomUUID().toString + protected def randomTmpFilename: String = s"${Environment.tmpDirectory}/AbstractSparkSuite-" + UUID.randomUUID().toString diff --git a/terminal21-spark/src/test/scala/org/sparkonscala3/lib/SessionsTest.scala b/terminal21-spark/src/test/scala/org/sparkonscala3/lib/SessionsTest.scala new file mode 100644 index 00000000..b647b54e --- /dev/null +++ b/terminal21-spark/src/test/scala/org/sparkonscala3/lib/SessionsTest.scala @@ -0,0 +1,74 @@ +package org.sparkonscala3.lib + +import org.sparkonscala3.lib.testmodel.Person + +import scala.util.Using + +class SessionsTest extends AbstractSparkSuite: + val people = for (i <- 1 to 10) yield Person(i.toString, s"text for row $i") + + test("creates/destroys session"): + Using.resource(Sessions.newSparkSession()): spark => + () + + test("Can convert to Dataframe"): + Using.resource(Sessions.newSparkSession()): spark => + import scala3encoders.given + import spark.implicits.* + val df = spark.sparkContext.parallelize(people, 16).toDF() + df.as[Person].collect() should be(people.toArray) + + test("Can convert to Dataset"): + Using.resource(Sessions.newSparkSession()): spark => + import scala3encoders.given + import spark.implicits.* + val ds = spark.sparkContext.parallelize(people, 16).toDS() + ds.collect() should be(people.toArray) + + test("Can write parquet"): + Using.resource(Sessions.newSparkSession()): spark => + import scala3encoders.given + import spark.implicits.* + val ds = spark.sparkContext.parallelize(people, 16).toDS() + val f = randomTmpFilename + ds.write.parquet(f) + val rds = spark.read.parquet(f).as[Person] + rds.collect() should be(rds.collect()) + + test("Can write csv"): + Using.resource(Sessions.newSparkSession()): spark => + import scala3encoders.given + import spark.implicits.* + val ds = spark.sparkContext.parallelize(people, 16).toDS() + val f = randomTmpFilename + ds.write.option("header", true).csv(f) + val rds = spark.read.option("header", true).csv(f).as[Person] + rds.collect() should be(rds.collect()) + + test("Can write json"): + Using.resource(Sessions.newSparkSession()): spark => + import scala3encoders.given + import spark.implicits.* + val ds = spark.sparkContext.parallelize(people, 16).toDS() + val f = randomTmpFilename + ds.write.json(f) + val rds = spark.read.json(f).as[Person] + rds.collect() should be(rds.collect()) + + test("Can write orc"): + Using.resource(Sessions.newSparkSession()): spark => + import scala3encoders.given + import spark.implicits.* + val ds = spark.sparkContext.parallelize(people, 16).toDS() + val f = randomTmpFilename + ds.write.orc(f) + val rds = spark.read.orc(f).as[Person] + rds.collect() should be(rds.collect()) + + test("Can mount as tmp table"): + Using.resource(Sessions.newSparkSession()): spark => + import scala3encoders.given + import spark.implicits.* + val ds = spark.sparkContext.parallelize(people, 16).toDS() + ds.createOrReplaceTempView("people") + spark.sql("select * from people").count() should be(10) diff --git a/terminal21-spark/src/test/scala/org/sparkonscala3/lib/SparkSessionExtTest.scala b/terminal21-spark/src/test/scala/org/sparkonscala3/lib/SparkSessionExtTest.scala new file mode 100644 index 00000000..cca0d385 --- /dev/null +++ b/terminal21-spark/src/test/scala/org/sparkonscala3/lib/SparkSessionExtTest.scala @@ -0,0 +1,32 @@ +package org.sparkonscala3.lib + +import org.sparkonscala3.lib.testmodel.Person + +import scala.util.Using + +class SparkSessionExtTest extends AbstractSparkSuite: + val people = for (i <- 1 to 10) yield Person(i.toString, s"text for row $i") + + test("schemaOf"): + Using.resource(Sessions.newSparkSession()): spark => + val sp = new SparkSessionExt(spark) + import scala3encoders.given + import spark.implicits.* + val schema = sp.schemaOf[Person] + schema.toList.size should be(2) + + test("toDF"): + Using.resource(Sessions.newSparkSession()): spark => + val sp = new SparkSessionExt(spark) + import scala3encoders.given + import spark.implicits.* + val df = sp.toDF(people) + df.as[Person].collect() should be(people.toArray) + + test("toDS"): + Using.resource(Sessions.newSparkSession()): spark => + val sp = new SparkSessionExt(spark) + import scala3encoders.given + import spark.implicits.* + val ds = sp.toDS(people) + ds.collect() should be(people.toArray) diff --git a/terminal21-spark/src/test/scala/org/sparkonscala3/lib/steps/StepsTest.scala b/terminal21-spark/src/test/scala/org/sparkonscala3/lib/steps/StepsTest.scala new file mode 100644 index 00000000..35a735bb --- /dev/null +++ b/terminal21-spark/src/test/scala/org/sparkonscala3/lib/steps/StepsTest.scala @@ -0,0 +1,66 @@ +package org.sparkonscala3.lib.steps + +import org.sparkonscala3.lib.{AbstractSparkSuite, Sessions, SparkSessionExt} + +import java.util.concurrent.atomic.AtomicInteger +import scala.util.Using + +class StepsTest extends AbstractSparkSuite: + test("returns the correct dataframe"): + Using.resource(Sessions.newSparkSession()): spark => + import spark.implicits.* + + val steps = Steps(spark, "StepsTest-correct-df") + val budgetStep = steps.step("budget") + budgetStep.invalidateCache() + + def calculations() = Seq(1, 2, 3).toDF() + + for _ <- 1 to 3 do budgetStep.calculateOnce(calculations()).as[Int].collect().sorted should be(Array(1, 2, 3)) + + test("returns the correct dataset"): + Using.resource(Sessions.newSparkSession()): spark => + import spark.implicits.* + + val steps = Steps(spark, "StepsTest-correct-ds") + val budgetStep = steps.step("budget") + budgetStep.invalidateCache() + + def calculations() = Seq(1, 2, 3).toDS() + + for _ <- 1 to 3 do budgetStep.calculateOnce(calculations()).as[Int].collect().sorted should be(Array(1, 2, 3)) + + test("doesn't re-evaluate cached dataframes"): + Using.resource(Sessions.newSparkSession()): spark => + import spark.implicits.* + + val steps = Steps(spark, "StepsTest-cachedDF") + val budgetStep = steps.step("budget") + budgetStep.invalidateCache() + + val calculated = new AtomicInteger(0) + + def calculations() = + calculated.incrementAndGet() + Seq(1, 2, 3).toDF() + + budgetStep.calculateOnce(calculations()) + budgetStep.calculateOnce(calculations()) + calculated.get() should be(1) + + test("doesn't re-evaluate cached datasets"): + Using.resource(Sessions.newSparkSession()): spark => + import spark.implicits.* + + val steps = Steps(spark, "StepsTest-cachedDS") + val budgetStep = steps.step("budget") + budgetStep.invalidateCache() + + val calculated = new AtomicInteger(0) + + def calculations() = + calculated.incrementAndGet() + Seq(1, 2, 3).toDS() + budgetStep.calculateOnce(calculations()) + budgetStep.calculateOnce(calculations()) + calculated.get() should be(1) diff --git a/terminal21-spark/src/test/scala/org/sparkonscala3/lib/testmodel/Person.scala b/terminal21-spark/src/test/scala/org/sparkonscala3/lib/testmodel/Person.scala new file mode 100644 index 00000000..78722122 --- /dev/null +++ b/terminal21-spark/src/test/scala/org/sparkonscala3/lib/testmodel/Person.scala @@ -0,0 +1,3 @@ +package org.sparkonscala3.lib.testmodel + +case class Person(id: String, name: String) From 6197ffc71f1f0aba176ab42f72744a8493c984b9 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Wed, 3 Jan 2024 20:17:12 +0000 Subject: [PATCH 014/144] - --- build.sbt | 1 + .../src/test/scala/org/sparkonscala3/lib/steps/StepsTest.scala | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 9f6b3a2d..1509e35d 100644 --- a/build.sbt +++ b/build.sbt @@ -157,3 +157,4 @@ lazy val `terminal21-spark` = project LogBack % Test ) ++ SparkScala3Fix ) + .dependsOn(`terminal21-ui-std`) diff --git a/terminal21-spark/src/test/scala/org/sparkonscala3/lib/steps/StepsTest.scala b/terminal21-spark/src/test/scala/org/sparkonscala3/lib/steps/StepsTest.scala index 35a735bb..5c731db0 100644 --- a/terminal21-spark/src/test/scala/org/sparkonscala3/lib/steps/StepsTest.scala +++ b/terminal21-spark/src/test/scala/org/sparkonscala3/lib/steps/StepsTest.scala @@ -1,6 +1,6 @@ package org.sparkonscala3.lib.steps -import org.sparkonscala3.lib.{AbstractSparkSuite, Sessions, SparkSessionExt} +import org.sparkonscala3.lib.{AbstractSparkSuite, Sessions} import java.util.concurrent.atomic.AtomicInteger import scala.util.Using From d4245670168e8cb61e71d6e41243b80bc5d06b2a Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Wed, 3 Jan 2024 20:21:59 +0000 Subject: [PATCH 015/144] - --- .../{Sessions.scala => SparkSessions.scala} | 2 +- .../lib/SparkSessionExtTest.scala | 6 +++--- ...sionsTest.scala => SparkSessionsTest.scala} | 18 +++++++++--------- .../lib/endtoend/SparkBasics.scala | 9 +++++++++ .../sparkonscala3/lib/steps/StepsTest.scala | 10 +++++----- 5 files changed, 27 insertions(+), 18 deletions(-) rename terminal21-spark/src/main/scala/org/sparkonscala3/lib/{Sessions.scala => SparkSessions.scala} (95%) rename terminal21-spark/src/test/scala/org/sparkonscala3/lib/{SessionsTest.scala => SparkSessionsTest.scala} (80%) create mode 100644 terminal21-spark/src/test/scala/org/sparkonscala3/lib/endtoend/SparkBasics.scala diff --git a/terminal21-spark/src/main/scala/org/sparkonscala3/lib/Sessions.scala b/terminal21-spark/src/main/scala/org/sparkonscala3/lib/SparkSessions.scala similarity index 95% rename from terminal21-spark/src/main/scala/org/sparkonscala3/lib/Sessions.scala rename to terminal21-spark/src/main/scala/org/sparkonscala3/lib/SparkSessions.scala index e66f8c8f..76f6152f 100644 --- a/terminal21-spark/src/main/scala/org/sparkonscala3/lib/Sessions.scala +++ b/terminal21-spark/src/main/scala/org/sparkonscala3/lib/SparkSessions.scala @@ -2,7 +2,7 @@ package org.sparkonscala3.lib import org.apache.spark.sql.SparkSession -object Sessions: +object SparkSessions: def newSparkSession( appName: String = "spark-app", master: String = "local[*]", diff --git a/terminal21-spark/src/test/scala/org/sparkonscala3/lib/SparkSessionExtTest.scala b/terminal21-spark/src/test/scala/org/sparkonscala3/lib/SparkSessionExtTest.scala index cca0d385..7637206f 100644 --- a/terminal21-spark/src/test/scala/org/sparkonscala3/lib/SparkSessionExtTest.scala +++ b/terminal21-spark/src/test/scala/org/sparkonscala3/lib/SparkSessionExtTest.scala @@ -8,7 +8,7 @@ class SparkSessionExtTest extends AbstractSparkSuite: val people = for (i <- 1 to 10) yield Person(i.toString, s"text for row $i") test("schemaOf"): - Using.resource(Sessions.newSparkSession()): spark => + Using.resource(SparkSessions.newSparkSession()): spark => val sp = new SparkSessionExt(spark) import scala3encoders.given import spark.implicits.* @@ -16,7 +16,7 @@ class SparkSessionExtTest extends AbstractSparkSuite: schema.toList.size should be(2) test("toDF"): - Using.resource(Sessions.newSparkSession()): spark => + Using.resource(SparkSessions.newSparkSession()): spark => val sp = new SparkSessionExt(spark) import scala3encoders.given import spark.implicits.* @@ -24,7 +24,7 @@ class SparkSessionExtTest extends AbstractSparkSuite: df.as[Person].collect() should be(people.toArray) test("toDS"): - Using.resource(Sessions.newSparkSession()): spark => + Using.resource(SparkSessions.newSparkSession()): spark => val sp = new SparkSessionExt(spark) import scala3encoders.given import spark.implicits.* diff --git a/terminal21-spark/src/test/scala/org/sparkonscala3/lib/SessionsTest.scala b/terminal21-spark/src/test/scala/org/sparkonscala3/lib/SparkSessionsTest.scala similarity index 80% rename from terminal21-spark/src/test/scala/org/sparkonscala3/lib/SessionsTest.scala rename to terminal21-spark/src/test/scala/org/sparkonscala3/lib/SparkSessionsTest.scala index b647b54e..d10207e2 100644 --- a/terminal21-spark/src/test/scala/org/sparkonscala3/lib/SessionsTest.scala +++ b/terminal21-spark/src/test/scala/org/sparkonscala3/lib/SparkSessionsTest.scala @@ -4,29 +4,29 @@ import org.sparkonscala3.lib.testmodel.Person import scala.util.Using -class SessionsTest extends AbstractSparkSuite: +class SparkSessionsTest extends AbstractSparkSuite: val people = for (i <- 1 to 10) yield Person(i.toString, s"text for row $i") test("creates/destroys session"): - Using.resource(Sessions.newSparkSession()): spark => + Using.resource(SparkSessions.newSparkSession()): spark => () test("Can convert to Dataframe"): - Using.resource(Sessions.newSparkSession()): spark => + Using.resource(SparkSessions.newSparkSession()): spark => import scala3encoders.given import spark.implicits.* val df = spark.sparkContext.parallelize(people, 16).toDF() df.as[Person].collect() should be(people.toArray) test("Can convert to Dataset"): - Using.resource(Sessions.newSparkSession()): spark => + Using.resource(SparkSessions.newSparkSession()): spark => import scala3encoders.given import spark.implicits.* val ds = spark.sparkContext.parallelize(people, 16).toDS() ds.collect() should be(people.toArray) test("Can write parquet"): - Using.resource(Sessions.newSparkSession()): spark => + Using.resource(SparkSessions.newSparkSession()): spark => import scala3encoders.given import spark.implicits.* val ds = spark.sparkContext.parallelize(people, 16).toDS() @@ -36,7 +36,7 @@ class SessionsTest extends AbstractSparkSuite: rds.collect() should be(rds.collect()) test("Can write csv"): - Using.resource(Sessions.newSparkSession()): spark => + Using.resource(SparkSessions.newSparkSession()): spark => import scala3encoders.given import spark.implicits.* val ds = spark.sparkContext.parallelize(people, 16).toDS() @@ -46,7 +46,7 @@ class SessionsTest extends AbstractSparkSuite: rds.collect() should be(rds.collect()) test("Can write json"): - Using.resource(Sessions.newSparkSession()): spark => + Using.resource(SparkSessions.newSparkSession()): spark => import scala3encoders.given import spark.implicits.* val ds = spark.sparkContext.parallelize(people, 16).toDS() @@ -56,7 +56,7 @@ class SessionsTest extends AbstractSparkSuite: rds.collect() should be(rds.collect()) test("Can write orc"): - Using.resource(Sessions.newSparkSession()): spark => + Using.resource(SparkSessions.newSparkSession()): spark => import scala3encoders.given import spark.implicits.* val ds = spark.sparkContext.parallelize(people, 16).toDS() @@ -66,7 +66,7 @@ class SessionsTest extends AbstractSparkSuite: rds.collect() should be(rds.collect()) test("Can mount as tmp table"): - Using.resource(Sessions.newSparkSession()): spark => + Using.resource(SparkSessions.newSparkSession()): spark => import scala3encoders.given import spark.implicits.* val ds = spark.sparkContext.parallelize(people, 16).toDS() diff --git a/terminal21-spark/src/test/scala/org/sparkonscala3/lib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/sparkonscala3/lib/endtoend/SparkBasics.scala new file mode 100644 index 00000000..ae307dcc --- /dev/null +++ b/terminal21-spark/src/test/scala/org/sparkonscala3/lib/endtoend/SparkBasics.scala @@ -0,0 +1,9 @@ +package org.sparkonscala3.lib.endtoend + +import org.sparkonscala3.lib.SparkSessions + +import scala.util.Using + +@main def sparkBasics() = + Using.resource(SparkSessions.newSparkSession()): spark => + () diff --git a/terminal21-spark/src/test/scala/org/sparkonscala3/lib/steps/StepsTest.scala b/terminal21-spark/src/test/scala/org/sparkonscala3/lib/steps/StepsTest.scala index 5c731db0..9eaa1fc8 100644 --- a/terminal21-spark/src/test/scala/org/sparkonscala3/lib/steps/StepsTest.scala +++ b/terminal21-spark/src/test/scala/org/sparkonscala3/lib/steps/StepsTest.scala @@ -1,13 +1,13 @@ package org.sparkonscala3.lib.steps -import org.sparkonscala3.lib.{AbstractSparkSuite, Sessions} +import org.sparkonscala3.lib.{AbstractSparkSuite, SparkSessions} import java.util.concurrent.atomic.AtomicInteger import scala.util.Using class StepsTest extends AbstractSparkSuite: test("returns the correct dataframe"): - Using.resource(Sessions.newSparkSession()): spark => + Using.resource(SparkSessions.newSparkSession()): spark => import spark.implicits.* val steps = Steps(spark, "StepsTest-correct-df") @@ -19,7 +19,7 @@ class StepsTest extends AbstractSparkSuite: for _ <- 1 to 3 do budgetStep.calculateOnce(calculations()).as[Int].collect().sorted should be(Array(1, 2, 3)) test("returns the correct dataset"): - Using.resource(Sessions.newSparkSession()): spark => + Using.resource(SparkSessions.newSparkSession()): spark => import spark.implicits.* val steps = Steps(spark, "StepsTest-correct-ds") @@ -31,7 +31,7 @@ class StepsTest extends AbstractSparkSuite: for _ <- 1 to 3 do budgetStep.calculateOnce(calculations()).as[Int].collect().sorted should be(Array(1, 2, 3)) test("doesn't re-evaluate cached dataframes"): - Using.resource(Sessions.newSparkSession()): spark => + Using.resource(SparkSessions.newSparkSession()): spark => import spark.implicits.* val steps = Steps(spark, "StepsTest-cachedDF") @@ -49,7 +49,7 @@ class StepsTest extends AbstractSparkSuite: calculated.get() should be(1) test("doesn't re-evaluate cached datasets"): - Using.resource(Sessions.newSparkSession()): spark => + Using.resource(SparkSessions.newSparkSession()): spark => import spark.implicits.* val steps = Steps(spark, "StepsTest-cachedDS") From c40dfc8432d59071f4116cb916388fe41378d91b Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Wed, 3 Jan 2024 20:32:49 +0000 Subject: [PATCH 016/144] - --- .../lib => terminal21/sparklib}/SparkSessionExt.scala | 2 +- .../lib => terminal21/sparklib}/SparkSessions.scala | 2 +- .../lib => terminal21/sparklib}/steps/Steps.scala | 4 ++-- .../lib => terminal21/sparklib}/util/Environment.scala | 2 +- .../lib => terminal21/sparklib}/AbstractSparkSuite.scala | 4 ++-- .../lib => terminal21/sparklib}/SparkSessionExtTest.scala | 4 ++-- .../lib => terminal21/sparklib}/SparkSessionsTest.scala | 4 ++-- .../lib => terminal21/sparklib}/endtoend/SparkBasics.scala | 5 +++-- .../lib => terminal21/sparklib}/steps/StepsTest.scala | 4 ++-- .../lib => terminal21/sparklib}/testmodel/Person.scala | 2 +- 10 files changed, 17 insertions(+), 16 deletions(-) rename terminal21-spark/src/main/scala/org/{sparkonscala3/lib => terminal21/sparklib}/SparkSessionExt.scala (94%) rename terminal21-spark/src/main/scala/org/{sparkonscala3/lib => terminal21/sparklib}/SparkSessions.scala (93%) rename terminal21-spark/src/main/scala/org/{sparkonscala3/lib => terminal21/sparklib}/steps/Steps.scala (92%) rename terminal21-spark/src/main/scala/org/{sparkonscala3/lib => terminal21/sparklib}/util/Environment.scala (84%) rename terminal21-spark/src/test/scala/org/{sparkonscala3/lib => terminal21/sparklib}/AbstractSparkSuite.scala (82%) rename terminal21-spark/src/test/scala/org/{sparkonscala3/lib => terminal21/sparklib}/SparkSessionExtTest.scala (92%) rename terminal21-spark/src/test/scala/org/{sparkonscala3/lib => terminal21/sparklib}/SparkSessionsTest.scala (97%) rename terminal21-spark/src/test/scala/org/{sparkonscala3/lib => terminal21/sparklib}/endtoend/SparkBasics.scala (53%) rename terminal21-spark/src/test/scala/org/{sparkonscala3/lib => terminal21/sparklib}/steps/StepsTest.scala (95%) rename terminal21-spark/src/test/scala/org/{sparkonscala3/lib => terminal21/sparklib}/testmodel/Person.scala (51%) diff --git a/terminal21-spark/src/main/scala/org/sparkonscala3/lib/SparkSessionExt.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/SparkSessionExt.scala similarity index 94% rename from terminal21-spark/src/main/scala/org/sparkonscala3/lib/SparkSessionExt.scala rename to terminal21-spark/src/main/scala/org/terminal21/sparklib/SparkSessionExt.scala index 04b8a230..f07fd00b 100644 --- a/terminal21-spark/src/main/scala/org/sparkonscala3/lib/SparkSessionExt.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/SparkSessionExt.scala @@ -1,4 +1,4 @@ -package org.sparkonscala3.lib +package org.terminal21.sparklib import org.apache.spark.sql.{DataFrame, Dataset, Encoder, SparkSession} diff --git a/terminal21-spark/src/main/scala/org/sparkonscala3/lib/SparkSessions.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/SparkSessions.scala similarity index 93% rename from terminal21-spark/src/main/scala/org/sparkonscala3/lib/SparkSessions.scala rename to terminal21-spark/src/main/scala/org/terminal21/sparklib/SparkSessions.scala index 76f6152f..26ee8ca3 100644 --- a/terminal21-spark/src/main/scala/org/sparkonscala3/lib/SparkSessions.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/SparkSessions.scala @@ -1,4 +1,4 @@ -package org.sparkonscala3.lib +package org.terminal21.sparklib import org.apache.spark.sql.SparkSession diff --git a/terminal21-spark/src/main/scala/org/sparkonscala3/lib/steps/Steps.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/Steps.scala similarity index 92% rename from terminal21-spark/src/main/scala/org/sparkonscala3/lib/steps/Steps.scala rename to terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/Steps.scala index 704b6224..7e38c8b4 100644 --- a/terminal21-spark/src/main/scala/org/sparkonscala3/lib/steps/Steps.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/Steps.scala @@ -1,8 +1,8 @@ -package org.sparkonscala3.lib.steps +package org.terminal21.sparklib.steps import org.apache.commons.io.FileUtils import org.apache.spark.sql.{DataFrame, Dataset, Encoder, SparkSession} -import org.sparkonscala3.lib.util.Environment +import org.terminal21.sparklib.util.Environment import java.io.File diff --git a/terminal21-spark/src/main/scala/org/sparkonscala3/lib/util/Environment.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/util/Environment.scala similarity index 84% rename from terminal21-spark/src/main/scala/org/sparkonscala3/lib/util/Environment.scala rename to terminal21-spark/src/main/scala/org/terminal21/sparklib/util/Environment.scala index eca4917a..ae00d0d0 100644 --- a/terminal21-spark/src/main/scala/org/sparkonscala3/lib/util/Environment.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/util/Environment.scala @@ -1,4 +1,4 @@ -package org.sparkonscala3.lib.util +package org.terminal21.sparklib.util import org.apache.commons.lang3.StringUtils diff --git a/terminal21-spark/src/test/scala/org/sparkonscala3/lib/AbstractSparkSuite.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/AbstractSparkSuite.scala similarity index 82% rename from terminal21-spark/src/test/scala/org/sparkonscala3/lib/AbstractSparkSuite.scala rename to terminal21-spark/src/test/scala/org/terminal21/sparklib/AbstractSparkSuite.scala index 01e366ac..61a8dcd7 100644 --- a/terminal21-spark/src/test/scala/org/sparkonscala3/lib/AbstractSparkSuite.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/AbstractSparkSuite.scala @@ -1,8 +1,8 @@ -package org.sparkonscala3.lib +package org.terminal21.sparklib import org.scalatest.funsuite.AnyFunSuiteLike import org.scalatest.matchers.should.Matchers -import org.sparkonscala3.lib.util.Environment +import org.terminal21.sparklib.util.Environment import java.util.UUID diff --git a/terminal21-spark/src/test/scala/org/sparkonscala3/lib/SparkSessionExtTest.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/SparkSessionExtTest.scala similarity index 92% rename from terminal21-spark/src/test/scala/org/sparkonscala3/lib/SparkSessionExtTest.scala rename to terminal21-spark/src/test/scala/org/terminal21/sparklib/SparkSessionExtTest.scala index 7637206f..951daccd 100644 --- a/terminal21-spark/src/test/scala/org/sparkonscala3/lib/SparkSessionExtTest.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/SparkSessionExtTest.scala @@ -1,6 +1,6 @@ -package org.sparkonscala3.lib +package org.terminal21.sparklib -import org.sparkonscala3.lib.testmodel.Person +import org.terminal21.sparklib.testmodel.Person import scala.util.Using diff --git a/terminal21-spark/src/test/scala/org/sparkonscala3/lib/SparkSessionsTest.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/SparkSessionsTest.scala similarity index 97% rename from terminal21-spark/src/test/scala/org/sparkonscala3/lib/SparkSessionsTest.scala rename to terminal21-spark/src/test/scala/org/terminal21/sparklib/SparkSessionsTest.scala index d10207e2..096668da 100644 --- a/terminal21-spark/src/test/scala/org/sparkonscala3/lib/SparkSessionsTest.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/SparkSessionsTest.scala @@ -1,6 +1,6 @@ -package org.sparkonscala3.lib +package org.terminal21.sparklib -import org.sparkonscala3.lib.testmodel.Person +import org.terminal21.sparklib.testmodel.Person import scala.util.Using diff --git a/terminal21-spark/src/test/scala/org/sparkonscala3/lib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala similarity index 53% rename from terminal21-spark/src/test/scala/org/sparkonscala3/lib/endtoend/SparkBasics.scala rename to terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index ae307dcc..73173f92 100644 --- a/terminal21-spark/src/test/scala/org/sparkonscala3/lib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -1,9 +1,10 @@ package org.sparkonscala3.lib.endtoend -import org.sparkonscala3.lib.SparkSessions +import org.terminal21.sparklib.SparkSessions +import org.terminal21.sparklib.steps.Steps import scala.util.Using @main def sparkBasics() = Using.resource(SparkSessions.newSparkSession()): spark => - () + val steps = Steps(spark, "spark-basics") diff --git a/terminal21-spark/src/test/scala/org/sparkonscala3/lib/steps/StepsTest.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StepsTest.scala similarity index 95% rename from terminal21-spark/src/test/scala/org/sparkonscala3/lib/steps/StepsTest.scala rename to terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StepsTest.scala index 9eaa1fc8..3f708615 100644 --- a/terminal21-spark/src/test/scala/org/sparkonscala3/lib/steps/StepsTest.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StepsTest.scala @@ -1,6 +1,6 @@ -package org.sparkonscala3.lib.steps +package org.terminal21.sparklib.steps -import org.sparkonscala3.lib.{AbstractSparkSuite, SparkSessions} +import org.terminal21.sparklib.{AbstractSparkSuite, SparkSessions} import java.util.concurrent.atomic.AtomicInteger import scala.util.Using diff --git a/terminal21-spark/src/test/scala/org/sparkonscala3/lib/testmodel/Person.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/testmodel/Person.scala similarity index 51% rename from terminal21-spark/src/test/scala/org/sparkonscala3/lib/testmodel/Person.scala rename to terminal21-spark/src/test/scala/org/terminal21/sparklib/testmodel/Person.scala index 78722122..286580a5 100644 --- a/terminal21-spark/src/test/scala/org/sparkonscala3/lib/testmodel/Person.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/testmodel/Person.scala @@ -1,3 +1,3 @@ -package org.sparkonscala3.lib.testmodel +package org.terminal21.sparklib.testmodel case class Person(id: String, name: String) From 659b8c8971ed535ea0f6cd24b2cda0ae060c4bf2 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Wed, 3 Jan 2024 22:24:14 +0000 Subject: [PATCH 017/144] - --- .../main/scala/tests/ChakraComponents.scala | 4 +-- .../main/scala/tests/chakra/Typography.scala | 10 ++++++++ .../terminal21/sparklib/SparkSessions.scala | 22 ++++++++++++++++ .../sparklib/endtoend/SparkBasics.scala | 20 ++++++++++++--- .../sparklib/endtoend/model/CodeFile.scala | 25 +++++++++++++++++++ .../components/chakra/ChakraElement.scala | 14 +++++++++++ .../client/components/chakra/QuickTable.scala | 20 +++++++++++++++ 7 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 examples/src/main/scala/tests/chakra/Typography.scala create mode 100644 terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/model/CodeFile.scala create mode 100644 terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala diff --git a/examples/src/main/scala/tests/ChakraComponents.scala b/examples/src/main/scala/tests/ChakraComponents.scala index 8185ffec..5940e29e 100644 --- a/examples/src/main/scala/tests/ChakraComponents.scala +++ b/examples/src/main/scala/tests/ChakraComponents.scala @@ -5,8 +5,8 @@ import org.terminal21.client.components.chakra.* import org.terminal21.client.components.{Paragraph, render} import tests.chakra.* +import java.util.concurrent.CountDownLatch import java.util.concurrent.atomic.AtomicBoolean -import java.util.concurrent.{CountDownLatch, TimeUnit} @main def chakraComponents(): Unit = val keepRunning = new AtomicBoolean(true) @@ -26,7 +26,7 @@ import java.util.concurrent.{CountDownLatch, TimeUnit} (Overlay.components ++ Forms.components ++ Editables.components ++ Stacks.components ++ Grids.components ++ Buttons.components( latch - ) ++ Etc.components ++ MediaAndIcons.components ++ DataDisplay.components ++ Seq(krButton)) + ) ++ Etc.components ++ MediaAndIcons.components ++ DataDisplay.components ++ Typography.components ++ Seq(krButton)) .render() println("Waiting for button to be pressed for 1 hour") diff --git a/examples/src/main/scala/tests/chakra/Typography.scala b/examples/src/main/scala/tests/chakra/Typography.scala new file mode 100644 index 00000000..d645fa75 --- /dev/null +++ b/examples/src/main/scala/tests/chakra/Typography.scala @@ -0,0 +1,10 @@ +package tests.chakra + +import org.terminal21.client.components.UiElement +import org.terminal21.client.components.chakra.* + +object Typography: + def components: Seq[UiElement] = + Seq( + Text(text = "typography-text-0001", color = Some("tomato")) + ) diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/SparkSessions.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/SparkSessions.scala index 26ee8ca3..683583c8 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/SparkSessions.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/SparkSessions.scala @@ -1,6 +1,9 @@ package org.terminal21.sparklib import org.apache.spark.sql.SparkSession +import org.terminal21.client.{ConnectedSession, Sessions} + +import scala.util.Using object SparkSessions: def newSparkSession( @@ -16,3 +19,22 @@ object SparkSessions: .config("spark.driver.bindAddress", bindAddress) .config("spark.ui.enabled", sparkUiEnabled) .getOrCreate() + + /** Will create a terminal21 session and use the provided spark session + * @param spark + * the spark session, will be closed before this call returns. Use #newSparkSession to quickly create one. + * @param id + * the id of the terminal21 session + * @param name + * the name of the terminal21 session + * @param f + * the code to run + * @tparam R + * if f returns some value, this will be returned by the method + * @return + * whatever f returns + */ + def newTerminal21WithSparkSession[R](spark: SparkSession, id: String, name: String)(f: (SparkSession, ConnectedSession) => R): R = + Sessions.withNewSession(id, name): terminal21Session => + Using.resource(spark): sp => + f(spark, terminal21Session) diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index 73173f92..3aae0835 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -1,10 +1,22 @@ -package org.sparkonscala3.lib.endtoend +package org.terminal21.sparklib.endtoend +import org.terminal21.client.ConnectedSession import org.terminal21.sparklib.SparkSessions +import org.terminal21.sparklib.endtoend.model.CodeFile.createDatasetFromProjectsSourceFiles import org.terminal21.sparklib.steps.Steps -import scala.util.Using +import java.time.LocalDate -@main def sparkBasics() = - Using.resource(SparkSessions.newSparkSession()): spark => +@main def sparkBasics(): Unit = + SparkSessions.newTerminal21WithSparkSession(SparkSessions.newSparkSession(), "spark-basics", "Spark Basics"): (spark, session) => + given ConnectedSession = session + + import scala3encoders.given + import spark.implicits.* val steps = Steps(spark, "spark-basics") + + val step1 = steps.step("query-dataset") + println(step1.targetDir) + + val sourceCodeDs = step1.calculateOnce: + createDatasetFromProjectsSourceFiles.toDS diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/model/CodeFile.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/model/CodeFile.scala new file mode 100644 index 00000000..7deff6ca --- /dev/null +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/model/CodeFile.scala @@ -0,0 +1,25 @@ +package org.terminal21.sparklib.endtoend.model + +import org.apache.commons.io.FileUtils + +import java.io.File +import java.nio.file.Files +import java.time.{Instant, LocalDate, ZoneId} + +case class CodeFile(id: Int, name: String, path: String, numOfLines: Int, numOfWords: Int, createdDate: LocalDate) + +object CodeFile: + import scala.jdk.CollectionConverters.* + def createDatasetFromProjectsSourceFiles: Seq[CodeFile] = + val availableFiles = FileUtils.listFiles(new File("."), Array("scala"), true).asScala.toArray + for i <- 1 to 10000 yield + val f = availableFiles(i % availableFiles.length) + val code = Files.readString(f.toPath) + CodeFile( + i, + f.getName, + f.getPath, + code.split("\n").size, + code.split(" ").size, + LocalDate.ofInstant(Instant.ofEpochMilli(f.lastModified()), ZoneId.systemDefault()) + ) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala index c7a106d9..403d4dda 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala @@ -1011,3 +1011,17 @@ case class Image( @volatile var borderRadius: Option[String] = None, @volatile var style: Map[String, String] = Map.empty ) extends ChakraElement + +/** https://chakra-ui.com/docs/components/text + */ +case class Text( + key: String = Keys.nextKey, + @volatile var text: String = "text.text is empty", + @volatile var fontSize: Option[String] = None, + @volatile var noOfLines: Option[Int] = None, + @volatile var color: Option[String] = None, + @volatile var as: Option[String] = None, + @volatile var align: Option[String] = None, + @volatile var casing: Option[String] = None, + @volatile var decoration: Option[String] = None +) extends ChakraElement diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala new file mode 100644 index 00000000..ec0d5d2a --- /dev/null +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala @@ -0,0 +1,20 @@ +package org.terminal21.client.components.chakra + +import org.terminal21.client.components.UiElement + +object QuickTable: + def quickTable(data: Seq[Seq[UiElement]], caption: Option[String] = None, variant: String = "striped", colorScheme: String = "teal", size: String = "mg") = + TableContainer().withChildren( + Table(variant = variant, colorScheme = Some(colorScheme), size = size) + .withChildren( + Thead(), + Tbody( + children = data.map: row => + Tr( + children = row.map: column => + Td().withChildren(column) + ) + ) + ) + .addChildren(caption.map(c => TableCaption(text = c)).toList: _*) + ) From 5977a57a3d4842ddcb5716ebcf53041b59c38c13 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Wed, 3 Jan 2024 22:36:52 +0000 Subject: [PATCH 018/144] - --- .../sparklib/endtoend/SparkBasics.scala | 8 ++++++++ .../sparklib/endtoend/model/CodeFile.scala | 4 +++- .../client/components/chakra/QuickTable.scala | 15 +++++++++++++-- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index 3aae0835..cf8385bf 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -1,6 +1,8 @@ package org.terminal21.sparklib.endtoend import org.terminal21.client.ConnectedSession +import org.terminal21.client.components.* +import org.terminal21.client.components.chakra.* import org.terminal21.sparklib.SparkSessions import org.terminal21.sparklib.endtoend.model.CodeFile.createDatasetFromProjectsSourceFiles import org.terminal21.sparklib.steps.Steps @@ -20,3 +22,9 @@ import java.time.LocalDate val sourceCodeDs = step1.calculateOnce: createDatasetFromProjectsSourceFiles.toDS + + val tableData = sourceCodeDs.take(10).toList + Seq( + QuickTable.quickTable(Seq("id", "name", "path", "numOfLines", "numOfWords", "createdDate"), tableData.map(d => d.toData.map(c => Text(text = c)))) + ).render() + session.waitTillUserClosesSession() diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/model/CodeFile.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/model/CodeFile.scala index 7deff6ca..bc24b533 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/model/CodeFile.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/model/CodeFile.scala @@ -6,7 +6,9 @@ import java.io.File import java.nio.file.Files import java.time.{Instant, LocalDate, ZoneId} -case class CodeFile(id: Int, name: String, path: String, numOfLines: Int, numOfWords: Int, createdDate: LocalDate) +case class CodeFile(id: Int, name: String, path: String, numOfLines: Int, numOfWords: Int, createdDate: LocalDate): + def toColumnNames: Seq[String] = productElementNames.toList + def toData: Seq[String] = productIterator.map(_.toString).toList object CodeFile: import scala.jdk.CollectionConverters.* diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala index ec0d5d2a..07405b14 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala @@ -3,11 +3,22 @@ package org.terminal21.client.components.chakra import org.terminal21.client.components.UiElement object QuickTable: - def quickTable(data: Seq[Seq[UiElement]], caption: Option[String] = None, variant: String = "striped", colorScheme: String = "teal", size: String = "mg") = + def quickTable( + headers: Seq[UiElement], + data: Seq[Seq[UiElement]], + caption: Option[String] = None, + variant: String = "striped", + colorScheme: String = "teal", + size: String = "mg" + ) = + val hs = headers.map: h => + Th().withChildren(h) TableContainer().withChildren( Table(variant = variant, colorScheme = Some(colorScheme), size = size) .withChildren( - Thead(), + Thead().withChildren( + Tr().withChildren(hs: _*) + ), Tbody( children = data.map: row => Tr( From fb7fb401958732e680690cab1712f82dc378338c Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Wed, 3 Jan 2024 22:45:34 +0000 Subject: [PATCH 019/144] - --- .../sparklib/endtoend/SparkBasics.scala | 3 +- .../client/components/chakra/QuickTable.scala | 51 ++++++++++++------- 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index cf8385bf..037fb641 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -21,10 +21,11 @@ import java.time.LocalDate println(step1.targetDir) val sourceCodeDs = step1.calculateOnce: + println("*** Recalculating source codes dataset ***") createDatasetFromProjectsSourceFiles.toDS val tableData = sourceCodeDs.take(10).toList Seq( - QuickTable.quickTable(Seq("id", "name", "path", "numOfLines", "numOfWords", "createdDate"), tableData.map(d => d.toData.map(c => Text(text = c)))) + QuickTable.quickTable().withStringHeaders("id", "name", "path", "numOfLines", "numOfWords", "createdDate").withStringData(tableData.map(_.toData)).build ).render() session.waitTillUserClosesSession() diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala index 07405b14..e8e4ef76 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala @@ -3,29 +3,42 @@ package org.terminal21.client.components.chakra import org.terminal21.client.components.UiElement object QuickTable: - def quickTable( + case class QuickTableBuilder( headers: Seq[UiElement], data: Seq[Seq[UiElement]], caption: Option[String] = None, variant: String = "striped", colorScheme: String = "teal", size: String = "mg" - ) = - val hs = headers.map: h => - Th().withChildren(h) - TableContainer().withChildren( - Table(variant = variant, colorScheme = Some(colorScheme), size = size) - .withChildren( - Thead().withChildren( - Tr().withChildren(hs: _*) - ), - Tbody( - children = data.map: row => - Tr( - children = row.map: column => - Td().withChildren(column) - ) + ): + def withHeaders(hs: Seq[UiElement]): QuickTableBuilder = copy(headers = hs) + def withStringHeaders(hs: String*): QuickTableBuilder = copy(headers = hs.map(h => Text(text = h))) + + def withData(dt: Seq[Seq[UiElement]]) = copy(data = dt) + def withStringData(dt: Seq[Seq[String]]) = copy(data = dt.map(row => row.map(c => Text(text = c)))) + + def build: TableContainer = + val hs = headers.map: h => + Th().withChildren(h) + TableContainer().withChildren( + Table(variant = variant, colorScheme = Some(colorScheme), size = size) + .withChildren( + Thead().withChildren( + Tr().withChildren(hs: _*) + ), + Tbody( + children = data.map: row => + Tr( + children = row.map: column => + Td().withChildren(column) + ) + ) ) - ) - .addChildren(caption.map(c => TableCaption(text = c)).toList: _*) - ) + .addChildren(caption.map(c => TableCaption(text = c)).toList: _*) + ) + def quickTable( + caption: Option[String] = None, + variant: String = "striped", + colorScheme: String = "teal", + size: String = "mg" + ) = QuickTableBuilder(Nil, Nil, caption, variant, colorScheme, size) From f5bacb90b848ea6460eff2b55a591dd79cda80d6 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 4 Jan 2024 13:18:12 +0000 Subject: [PATCH 020/144] - --- .../org/terminal21/sparklib/endtoend/SparkBasics.scala | 8 ++++---- .../org/terminal21/sparklib/endtoend/model/CodeFile.scala | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index 037fb641..9a9b0c4d 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -15,17 +15,17 @@ import java.time.LocalDate import scala3encoders.given import spark.implicits.* - val steps = Steps(spark, "spark-basics") + val steps = Steps(spark, "spark-basics") val step1 = steps.step("query-dataset") - println(step1.targetDir) val sourceCodeDs = step1.calculateOnce: - println("*** Recalculating source codes dataset ***") - createDatasetFromProjectsSourceFiles.toDS + createDatasetFromProjectsSourceFiles.toDS.orderBy($"createdDate".desc) val tableData = sourceCodeDs.take(10).toList Seq( + Button(text = "Code files").onClick: () => + step1.invalidateCache(), QuickTable.quickTable().withStringHeaders("id", "name", "path", "numOfLines", "numOfWords", "createdDate").withStringData(tableData.map(_.toData)).build ).render() session.waitTillUserClosesSession() diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/model/CodeFile.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/model/CodeFile.scala index bc24b533..c819aa90 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/model/CodeFile.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/model/CodeFile.scala @@ -13,7 +13,7 @@ case class CodeFile(id: Int, name: String, path: String, numOfLines: Int, numOfW object CodeFile: import scala.jdk.CollectionConverters.* def createDatasetFromProjectsSourceFiles: Seq[CodeFile] = - val availableFiles = FileUtils.listFiles(new File("."), Array("scala"), true).asScala.toArray + val availableFiles = FileUtils.listFiles(new File(".."), Array("scala"), true).asScala.toArray for i <- 1 to 10000 yield val f = availableFiles(i % availableFiles.length) val code = Files.readString(f.toPath) From 1fef5ebc200183a97d95e1b1aa7786efa92d0486 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 4 Jan 2024 14:34:52 +0000 Subject: [PATCH 021/144] - --- .../org/terminal21/client/components/chakra/QuickTable.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala index e8e4ef76..cdd7dd4f 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala @@ -14,8 +14,8 @@ object QuickTable: def withHeaders(hs: Seq[UiElement]): QuickTableBuilder = copy(headers = hs) def withStringHeaders(hs: String*): QuickTableBuilder = copy(headers = hs.map(h => Text(text = h))) - def withData(dt: Seq[Seq[UiElement]]) = copy(data = dt) - def withStringData(dt: Seq[Seq[String]]) = copy(data = dt.map(row => row.map(c => Text(text = c)))) + def withData(dt: Seq[Seq[UiElement]]): QuickTableBuilder = copy(data = dt) + def withStringData(dt: Seq[Seq[String]]): QuickTableBuilder = copy(data = dt.map(row => row.map(c => Text(text = c)))) def build: TableContainer = val hs = headers.map: h => From b6814687a3bfa0dea5eaf7bcd20c9de482599d62 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 4 Jan 2024 15:25:49 +0000 Subject: [PATCH 022/144] - --- .../org/terminal21/client/Calculation.scala | 29 +++++++++++++++++ .../terminal21/client/CalculationTest.scala | 32 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala create mode 100644 terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala new file mode 100644 index 00000000..e3998b5f --- /dev/null +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala @@ -0,0 +1,29 @@ +package org.terminal21.client + +import functions.fibers.FiberExecutor + +class Calculation[IN, OUT] private ( + executor: FiberExecutor, + calc: IN => OUT, + uiUpdater: OUT => Unit, + notifyWhenCalcReady: Seq[Calculation[OUT, _]] +): + def apply(in: IN): OUT = + val f = executor.submit: + val out = calc(in) + executor.submit: + uiUpdater(out) + + for c <- notifyWhenCalcReady do + executor.submit: + try c(out) + catch case t: Throwable => t.printStackTrace() + out + f.get() + + def notifyCalc(calcToNotify: Calculation[OUT, _]): Calculation[IN, OUT] = + new Calculation(executor, calc, uiUpdater, notifyWhenCalcReady :+ calcToNotify) + +object Calculation: + def apply[IN, OUT](calc: IN => OUT, uiUpdater: OUT => Unit, notify: Seq[Calculation[OUT, _]] = Nil)(using executor: FiberExecutor): Calculation[IN, OUT] = + new Calculation(executor, calc, uiUpdater, notify) diff --git a/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala b/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala new file mode 100644 index 00000000..4d3b2fe9 --- /dev/null +++ b/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala @@ -0,0 +1,32 @@ +package org.terminal21.client + +import functions.fibers.FiberExecutor +import org.scalatest.funsuite.AnyFunSuiteLike +import org.scalatest.matchers.should.Matchers.* + +import java.util.concurrent.atomic.AtomicInteger +import org.scalatest.concurrent.Eventually.* + +class CalculationTest extends AnyFunSuiteLike: + given executor: FiberExecutor = FiberExecutor() + def testCalc(i: Int) = i + 1 + def testCalcString(i: Int): String = (i + 10).toString + + test("calculates"): + val calc = Calculation(testCalc, _ => (), Nil) + calc(1) should be(2) + + test("calls the ui updater with the calculated value"): + val c = new AtomicInteger(-1) + val calc = Calculation(testCalc, i => c.set(i), Nil) + calc(1) + eventually: + c.get() should be(2) + + test("notifies"): + val c = new AtomicInteger(-1) + val calc2 = Calculation(testCalcString, i => c.set(i.toInt), Nil) + val calc1 = Calculation(testCalc, _ => ()).notifyCalc(calc2) + calc1(1) + eventually: + c.get() should be(12) From 7f7187e7cab354ed6f69bef0ba7c4d5d0a54b25a Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 4 Jan 2024 15:44:24 +0000 Subject: [PATCH 023/144] - --- .../sparklib/endtoend/SparkBasics.scala | 18 +++++++++++++++--- .../scala/org/terminal21/client/Globals.scala | 5 +++++ .../components/chakra/ChakraElement.scala | 18 +++++++++++++++++- 3 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 terminal21-ui-std/src/main/scala/org/terminal21/client/Globals.scala diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index 9a9b0c4d..0e7c81fc 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -1,9 +1,12 @@ package org.terminal21.sparklib.endtoend -import org.terminal21.client.ConnectedSession +import org.apache.spark.sql.{Dataset, SparkSession} +import org.terminal21.client.* +import org.terminal21.client.given import org.terminal21.client.components.* import org.terminal21.client.components.chakra.* import org.terminal21.sparklib.SparkSessions +import org.terminal21.sparklib.endtoend.model.CodeFile import org.terminal21.sparklib.endtoend.model.CodeFile.createDatasetFromProjectsSourceFiles import org.terminal21.sparklib.steps.Steps @@ -22,10 +25,19 @@ import java.time.LocalDate val sourceCodeDs = step1.calculateOnce: createDatasetFromProjectsSourceFiles.toDS.orderBy($"createdDate".desc) - val tableData = sourceCodeDs.take(10).toList + val tableData = sourceCodeDs.take(10).toList + val codeFilesTable = QuickTable.quickTable().withStringHeaders("id", "name", "path", "numOfLines", "numOfWords", "createdDate").build Seq( Button(text = "Code files").onClick: () => step1.invalidateCache(), - QuickTable.quickTable().withStringHeaders("id", "name", "path", "numOfLines", "numOfWords", "createdDate").withStringData(tableData.map(_.toData)).build + codeFilesTable ).render() session.waitTillUserClosesSession() + +def calculateSourceCodeFiles(spark: SparkSession, codeFilesTable: TableContainer): Calculation[Unit, Dataset[CodeFile]] = + import spark.implicits.* + import scala3encoders.given + Calculation( + _ => createDatasetFromProjectsSourceFiles.toDS.orderBy($"createdDate".desc), + tableData => codeFilesTable.withTBodyStringData(tableData.take(10).map(_.toData)) + ) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/Globals.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/Globals.scala new file mode 100644 index 00000000..1adb69b7 --- /dev/null +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/Globals.scala @@ -0,0 +1,5 @@ +package org.terminal21.client + +import functions.fibers.FiberExecutor + +given FiberExecutor = FiberExecutor() diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala index 403d4dda..e7d49a42 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala @@ -929,7 +929,23 @@ case class Option_( /** https://chakra-ui.com/docs/components/table/usage */ -case class TableContainer(key: String = Keys.nextKey, @volatile var children: Seq[UiElement] = Nil) extends ChakraElement with HasChildren[TableContainer] +case class TableContainer(key: String = Keys.nextKey, @volatile var children: Seq[UiElement] = Nil) extends ChakraElement with HasChildren[TableContainer]: + def withTBodyStringData(data: Seq[Seq[String]]): TableContainer = withTBodyData(data.map(_.map(c => Text(text = c)))) + def withTBodyData(data: Seq[Seq[UiElement]]): TableContainer = + val tableBodies = children + .collect: + case t: Table => + t.children.collect: + case b: Tbody => b + .flatten + val newTrs = data.map: row => + Tr( + children = row.map: column => + Td().withChildren(column) + ) + for b <- tableBodies do b.withChildren(newTrs: _*) + this + case class Table( key: String = Keys.nextKey, @volatile var variant: String = "simple", From dc637b6f1995f333b326266d9895d3f7effc6df8 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 4 Jan 2024 16:01:35 +0000 Subject: [PATCH 024/144] - --- .../sparklib/endtoend/SparkBasics.scala | 22 +++++++++++-------- .../org/terminal21/client/Calculation.scala | 21 +++++++++++++----- .../terminal21/client/CalculationTest.scala | 8 +++---- 3 files changed, 33 insertions(+), 18 deletions(-) diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index 0e7c81fc..cb451179 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -16,28 +16,32 @@ import java.time.LocalDate SparkSessions.newTerminal21WithSparkSession(SparkSessions.newSparkSession(), "spark-basics", "Spark Basics"): (spark, session) => given ConnectedSession = session - import scala3encoders.given - import spark.implicits.* - val steps = Steps(spark, "spark-basics") val step1 = steps.step("query-dataset") - val sourceCodeDs = step1.calculateOnce: - createDatasetFromProjectsSourceFiles.toDS.orderBy($"createdDate".desc) - - val tableData = sourceCodeDs.take(10).toList val codeFilesTable = QuickTable.quickTable().withStringHeaders("id", "name", "path", "numOfLines", "numOfWords", "createdDate").build + Seq( Button(text = "Code files").onClick: () => step1.invalidateCache(), codeFilesTable ).render() + + val calcSrcCodeFiles = calculateSourceCodeFiles(spark, session, codeFilesTable) + calcSrcCodeFiles(()) + session.waitTillUserClosesSession() -def calculateSourceCodeFiles(spark: SparkSession, codeFilesTable: TableContainer): Calculation[Unit, Dataset[CodeFile]] = +def calculateSourceCodeFiles(spark: SparkSession, session: ConnectedSession, codeFilesTable: TableContainer): Calculation[Unit, Dataset[CodeFile]] = import spark.implicits.* import scala3encoders.given Calculation( _ => createDatasetFromProjectsSourceFiles.toDS.orderBy($"createdDate".desc), - tableData => codeFilesTable.withTBodyStringData(tableData.take(10).map(_.toData)) + () => + codeFilesTable.withTBodyStringData(Nil) + session.render() + , + tableData => + codeFilesTable.withTBodyStringData(tableData.take(10).map(_.toData)) + session.render() ) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala index e3998b5f..0b940971 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala @@ -5,14 +5,18 @@ import functions.fibers.FiberExecutor class Calculation[IN, OUT] private ( executor: FiberExecutor, calc: IN => OUT, - uiUpdater: OUT => Unit, + uiUpdaterWhenResultsNotReady: () => Unit, + uiUpdaterWhenResultsReady: OUT => Unit, notifyWhenCalcReady: Seq[Calculation[OUT, _]] ): def apply(in: IN): OUT = val f = executor.submit: + executor.submit: + uiUpdaterWhenResultsNotReady() + val out = calc(in) executor.submit: - uiUpdater(out) + uiUpdaterWhenResultsReady(out) for c <- notifyWhenCalcReady do executor.submit: @@ -22,8 +26,15 @@ class Calculation[IN, OUT] private ( f.get() def notifyCalc(calcToNotify: Calculation[OUT, _]): Calculation[IN, OUT] = - new Calculation(executor, calc, uiUpdater, notifyWhenCalcReady :+ calcToNotify) + new Calculation(executor, calc, uiUpdaterWhenResultsNotReady, uiUpdaterWhenResultsReady, notifyWhenCalcReady :+ calcToNotify) object Calculation: - def apply[IN, OUT](calc: IN => OUT, uiUpdater: OUT => Unit, notify: Seq[Calculation[OUT, _]] = Nil)(using executor: FiberExecutor): Calculation[IN, OUT] = - new Calculation(executor, calc, uiUpdater, notify) + def apply[IN, OUT]( + calc: IN => OUT, + uiUpdaterWhenResultsNotReady: () => Unit, + uiUpdaterWhenResultsReady: OUT => Unit, + notify: Seq[Calculation[OUT, _]] = Nil + )(using + executor: FiberExecutor + ): Calculation[IN, OUT] = + new Calculation(executor, calc, uiUpdaterWhenResultsNotReady, uiUpdaterWhenResultsReady, notify) diff --git a/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala b/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala index 4d3b2fe9..9888aed5 100644 --- a/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala +++ b/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala @@ -13,20 +13,20 @@ class CalculationTest extends AnyFunSuiteLike: def testCalcString(i: Int): String = (i + 10).toString test("calculates"): - val calc = Calculation(testCalc, _ => (), Nil) + val calc = Calculation(testCalc, () => (), _ => (), Nil) calc(1) should be(2) test("calls the ui updater with the calculated value"): val c = new AtomicInteger(-1) - val calc = Calculation(testCalc, i => c.set(i), Nil) + val calc = Calculation(testCalc, () => (), i => c.set(i), Nil) calc(1) eventually: c.get() should be(2) test("notifies"): val c = new AtomicInteger(-1) - val calc2 = Calculation(testCalcString, i => c.set(i.toInt), Nil) - val calc1 = Calculation(testCalc, _ => ()).notifyCalc(calc2) + val calc2 = Calculation(testCalcString, () => (), i => c.set(i.toInt), Nil) + val calc1 = Calculation(testCalc, () => (), _ => ()).notifyCalc(calc2) calc1(1) eventually: c.get() should be(12) From 5929bbcc0d3a2740c75ef575970e58c258098a7e Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 4 Jan 2024 16:02:25 +0000 Subject: [PATCH 025/144] - --- .../test/scala/org/terminal21/client/CalculationTest.scala | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala b/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala index 9888aed5..973db796 100644 --- a/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala +++ b/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala @@ -4,7 +4,7 @@ import functions.fibers.FiberExecutor import org.scalatest.funsuite.AnyFunSuiteLike import org.scalatest.matchers.should.Matchers.* -import java.util.concurrent.atomic.AtomicInteger +import java.util.concurrent.atomic.{AtomicBoolean, AtomicInteger} import org.scalatest.concurrent.Eventually.* class CalculationTest extends AnyFunSuiteLike: @@ -18,8 +18,10 @@ class CalculationTest extends AnyFunSuiteLike: test("calls the ui updater with the calculated value"): val c = new AtomicInteger(-1) - val calc = Calculation(testCalc, () => (), i => c.set(i), Nil) + val b = new AtomicBoolean(false) + val calc = Calculation(testCalc, () => b.set(true), i => c.set(i), Nil) calc(1) + b.get() should be(true) eventually: c.get() should be(2) From d8fb3808e91ac99f145e7671936827989d04cd64 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 4 Jan 2024 16:06:20 +0000 Subject: [PATCH 026/144] - --- .../scala/org/terminal21/sparklib/endtoend/SparkBasics.scala | 4 ++-- .../terminal21/client/components/chakra/ChakraElement.scala | 4 ++-- .../org/terminal21/client/components/chakra/QuickTable.scala | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index cb451179..7003cc17 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -38,10 +38,10 @@ def calculateSourceCodeFiles(spark: SparkSession, session: ConnectedSession, cod Calculation( _ => createDatasetFromProjectsSourceFiles.toDS.orderBy($"createdDate".desc), () => - codeFilesTable.withTBodyStringData(Nil) + codeFilesTable.withRowStringData(Nil) session.render() , tableData => - codeFilesTable.withTBodyStringData(tableData.take(10).map(_.toData)) + codeFilesTable.withRowStringData(tableData.take(10).map(_.toData)) session.render() ) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala index e7d49a42..ee5b00b6 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala @@ -930,8 +930,8 @@ case class Option_( /** https://chakra-ui.com/docs/components/table/usage */ case class TableContainer(key: String = Keys.nextKey, @volatile var children: Seq[UiElement] = Nil) extends ChakraElement with HasChildren[TableContainer]: - def withTBodyStringData(data: Seq[Seq[String]]): TableContainer = withTBodyData(data.map(_.map(c => Text(text = c)))) - def withTBodyData(data: Seq[Seq[UiElement]]): TableContainer = + def withRowStringData(data: Seq[Seq[String]]): TableContainer = withRowData(data.map(_.map(c => Text(text = c)))) + def withRowData(data: Seq[Seq[UiElement]]): TableContainer = val tableBodies = children .collect: case t: Table => diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala index cdd7dd4f..5cf8784e 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala @@ -14,8 +14,8 @@ object QuickTable: def withHeaders(hs: Seq[UiElement]): QuickTableBuilder = copy(headers = hs) def withStringHeaders(hs: String*): QuickTableBuilder = copy(headers = hs.map(h => Text(text = h))) - def withData(dt: Seq[Seq[UiElement]]): QuickTableBuilder = copy(data = dt) - def withStringData(dt: Seq[Seq[String]]): QuickTableBuilder = copy(data = dt.map(row => row.map(c => Text(text = c)))) + def withRowData(dt: Seq[Seq[UiElement]]): QuickTableBuilder = copy(data = dt) + def withRowStringData(dt: Seq[Seq[String]]): QuickTableBuilder = copy(data = dt.map(row => row.map(c => Text(text = c)))) def build: TableContainer = val hs = headers.map: h => From a8dbe88802e4f75ed52d1c5ab369750bec7882f2 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 4 Jan 2024 16:28:27 +0000 Subject: [PATCH 027/144] - --- .../scala/org/terminal21/client/ConnectedSession.scala | 2 +- .../org/terminal21/client/ConnectedSessionMock.scala | 10 ++++++++++ .../org/terminal21/client/ConnectedSessionTest.scala | 6 +----- 3 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionMock.scala diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala index b62e8182..10b1336b 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala @@ -61,7 +61,7 @@ class ConnectedSession(val session: Session, val serverUrl: String, sessionsServ */ def isClosed: Boolean = exitLatch.getCount == 0 - def fireEvent(event: CommandEvent): Unit = + private[client] def fireEvent(event: CommandEvent): Unit = event match case SessionClosed(_) => exitLatch.countDown() diff --git a/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionMock.scala b/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionMock.scala new file mode 100644 index 00000000..1723192a --- /dev/null +++ b/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionMock.scala @@ -0,0 +1,10 @@ +package org.terminal21.client + +import org.mockito.Mockito.mock +import org.terminal21.model.CommonModelBuilders.session +import org.terminal21.ui.std.SessionsService + +object ConnectedSessionMock: + def newConnectedSessionMock: ConnectedSession = + val sessionsService = mock(classOf[SessionsService]) + new ConnectedSession(session(), "test", sessionsService, () => ()) diff --git a/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionTest.scala b/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionTest.scala index 0d5a423e..42706244 100644 --- a/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionTest.scala +++ b/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionTest.scala @@ -1,17 +1,13 @@ package org.terminal21.client -import org.mockito.Mockito.* import org.scalatest.funsuite.AnyFunSuiteLike import org.scalatest.matchers.should.Matchers.* import org.terminal21.client.components.chakra.Editable -import org.terminal21.model.CommonModelBuilders.* import org.terminal21.model.OnChange -import org.terminal21.ui.std.SessionsService class ConnectedSessionTest extends AnyFunSuiteLike: test("default event handlers are invoked before user handlers"): - val sessionsService = mock(classOf[SessionsService]) - given connectedSession: ConnectedSession = new ConnectedSession(session(), "test", sessionsService, () => ()) + given connectedSession: ConnectedSession = ConnectedSessionMock.newConnectedSessionMock val editable = Editable() editable.onChange: newValue => editable.value should be(newValue) From b6821a2ebb3530a36e3795672392251cd4bce72a Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 4 Jan 2024 16:56:56 +0000 Subject: [PATCH 028/144] - --- .../sparklib/endtoend/SparkBasics.scala | 16 ++++++------ .../org/terminal21/client/Calculation.scala | 25 +++++++++++-------- .../terminal21/client/CalculationTest.scala | 8 +++--- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index 7003cc17..bf90390f 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -32,16 +32,16 @@ import java.time.LocalDate session.waitTillUserClosesSession() -def calculateSourceCodeFiles(spark: SparkSession, session: ConnectedSession, codeFilesTable: TableContainer): Calculation[Unit, Dataset[CodeFile]] = +def calculateSourceCodeFiles(spark: SparkSession, session: ConnectedSession, codeFilesTable: TableContainer) = import spark.implicits.* import scala3encoders.given - Calculation( - _ => createDatasetFromProjectsSourceFiles.toDS.orderBy($"createdDate".desc), - () => + Calculation + .newOutOnlyCalculation: + createDatasetFromProjectsSourceFiles.toDS.orderBy($"createdDate".desc) + .whenStartingCalculationUpdateUi: codeFilesTable.withRowStringData(Nil) session.render() - , - tableData => - codeFilesTable.withRowStringData(tableData.take(10).map(_.toData)) + .whenCalculatedUpdateUi: tableData => + codeFilesTable.withRowStringData(tableData.take(10).toList.map(_.toData)) session.render() - ) + .build diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala index 0b940971..765d10c1 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala @@ -25,16 +25,21 @@ class Calculation[IN, OUT] private ( out f.get() - def notifyCalc(calcToNotify: Calculation[OUT, _]): Calculation[IN, OUT] = - new Calculation(executor, calc, uiUpdaterWhenResultsNotReady, uiUpdaterWhenResultsReady, notifyWhenCalcReady :+ calcToNotify) - object Calculation: - def apply[IN, OUT]( + class CalcBuilder[IN, OUT]( + executor: FiberExecutor, calc: IN => OUT, - uiUpdaterWhenResultsNotReady: () => Unit, - uiUpdaterWhenResultsReady: OUT => Unit, + uiUpdater: () => Unit = () => (), + uiReadyUpdater: OUT => Unit = (_: OUT) => (), notify: Seq[Calculation[OUT, _]] = Nil - )(using - executor: FiberExecutor - ): Calculation[IN, OUT] = - new Calculation(executor, calc, uiUpdaterWhenResultsNotReady, uiUpdaterWhenResultsReady, notify) + ): + def whenStartingCalculationUpdateUi(uiUpdater: => Unit) = new CalcBuilder(executor, calc, () => uiUpdater, uiReadyUpdater, notify) + def whenCalculatedUpdateUi(uiReadyUpdater: OUT => Unit) = new CalcBuilder(executor, calc, uiUpdater, uiReadyUpdater, notify) + def notifyAfterCalculated(other: Calculation[OUT, _]) = new CalcBuilder(executor, calc, uiUpdater, uiReadyUpdater, notify :+ other) + def build: Calculation[IN, OUT] = new Calculation[IN, OUT](executor, calc, uiUpdater, uiReadyUpdater, notify) + + def newOutOnlyCalculation[OUT](calc: => OUT)(using executor: FiberExecutor): CalcBuilder[Unit, OUT] = + new CalcBuilder(executor, _ => calc) + + def newCalculation[IN, OUT](calc: IN => OUT)(using executor: FiberExecutor): CalcBuilder[IN, OUT] = + new CalcBuilder(executor, calc) diff --git a/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala b/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala index 973db796..1654b7a7 100644 --- a/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala +++ b/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala @@ -13,13 +13,13 @@ class CalculationTest extends AnyFunSuiteLike: def testCalcString(i: Int): String = (i + 10).toString test("calculates"): - val calc = Calculation(testCalc, () => (), _ => (), Nil) + val calc = Calculation.newCalculation(testCalc).build calc(1) should be(2) test("calls the ui updater with the calculated value"): val c = new AtomicInteger(-1) val b = new AtomicBoolean(false) - val calc = Calculation(testCalc, () => b.set(true), i => c.set(i), Nil) + val calc = Calculation.newCalculation(testCalc).whenStartingCalculationUpdateUi(b.set(true)).whenCalculatedUpdateUi(i => c.set(i)).build calc(1) b.get() should be(true) eventually: @@ -27,8 +27,8 @@ class CalculationTest extends AnyFunSuiteLike: test("notifies"): val c = new AtomicInteger(-1) - val calc2 = Calculation(testCalcString, () => (), i => c.set(i.toInt), Nil) - val calc1 = Calculation(testCalc, () => (), _ => ()).notifyCalc(calc2) + val calc2 = Calculation.newCalculation(testCalcString).whenCalculatedUpdateUi(i => c.set(i.toInt)).build + val calc1 = Calculation.newCalculation(testCalc).notifyAfterCalculated(calc2).build calc1(1) eventually: c.get() should be(12) From f1ab45f43997202e1a7e39c3e2c1898a6588ee0c Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 4 Jan 2024 17:05:12 +0000 Subject: [PATCH 029/144] - --- .../sparklib/endtoend/SparkBasics.scala | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index bf90390f..d8def5e0 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -16,32 +16,31 @@ import java.time.LocalDate SparkSessions.newTerminal21WithSparkSession(SparkSessions.newSparkSession(), "spark-basics", "Spark Basics"): (spark, session) => given ConnectedSession = session - val steps = Steps(spark, "spark-basics") - val step1 = steps.step("query-dataset") - val codeFilesTable = QuickTable.quickTable().withStringHeaders("id", "name", "path", "numOfLines", "numOfWords", "createdDate").build + val codeFilesBadge = Badge() Seq( - Button(text = "Code files").onClick: () => - step1.invalidateCache(), + codeFilesBadge, codeFilesTable ).render() - val calcSrcCodeFiles = calculateSourceCodeFiles(spark, session, codeFilesTable) + val calcSrcCodeFiles = calculateSourceCodeFiles(spark, session, codeFilesTable, codeFilesBadge) calcSrcCodeFiles(()) session.waitTillUserClosesSession() -def calculateSourceCodeFiles(spark: SparkSession, session: ConnectedSession, codeFilesTable: TableContainer) = +def calculateSourceCodeFiles(spark: SparkSession, session: ConnectedSession, table: TableContainer, badge: Badge) = import spark.implicits.* import scala3encoders.given Calculation .newOutOnlyCalculation: - createDatasetFromProjectsSourceFiles.toDS.orderBy($"createdDate".desc) + createDatasetFromProjectsSourceFiles.toDS .whenStartingCalculationUpdateUi: - codeFilesTable.withRowStringData(Nil) + table.withRowStringData(Nil) + badge.text = "Calculating..." session.render() .whenCalculatedUpdateUi: tableData => - codeFilesTable.withRowStringData(tableData.take(10).toList.map(_.toData)) + table.withRowStringData(tableData.take(10).toList.map(_.toData)) + badge.text = "Ready" session.render() .build From a032a8445070a7e571b568affe1b487e85e821e9 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 4 Jan 2024 17:07:00 +0000 Subject: [PATCH 030/144] - --- .../scala/org/terminal21/sparklib/endtoend/SparkBasics.scala | 2 +- .../src/main/scala/org/terminal21/client/Calculation.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index d8def5e0..200ff8b9 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -33,7 +33,7 @@ def calculateSourceCodeFiles(spark: SparkSession, session: ConnectedSession, tab import spark.implicits.* import scala3encoders.given Calculation - .newOutOnlyCalculation: + .newCalculationNoIn: createDatasetFromProjectsSourceFiles.toDS .whenStartingCalculationUpdateUi: table.withRowStringData(Nil) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala index 765d10c1..512a595d 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala @@ -38,7 +38,7 @@ object Calculation: def notifyAfterCalculated(other: Calculation[OUT, _]) = new CalcBuilder(executor, calc, uiUpdater, uiReadyUpdater, notify :+ other) def build: Calculation[IN, OUT] = new Calculation[IN, OUT](executor, calc, uiUpdater, uiReadyUpdater, notify) - def newOutOnlyCalculation[OUT](calc: => OUT)(using executor: FiberExecutor): CalcBuilder[Unit, OUT] = + def newCalculationNoIn[OUT](calc: => OUT)(using executor: FiberExecutor): CalcBuilder[Unit, OUT] = new CalcBuilder(executor, _ => calc) def newCalculation[IN, OUT](calc: IN => OUT)(using executor: FiberExecutor): CalcBuilder[IN, OUT] = From f9dbc99ba59c80f5138b4d0771650c4e481aa267 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 4 Jan 2024 17:35:26 +0000 Subject: [PATCH 031/144] - --- .../sparklib/endtoend/SparkBasics.scala | 35 +++++++++++++++---- .../components/chakra/ChakraElement.scala | 1 + 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index 200ff8b9..31643890 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -15,21 +15,39 @@ import java.time.LocalDate @main def sparkBasics(): Unit = SparkSessions.newTerminal21WithSparkSession(SparkSessions.newSparkSession(), "spark-basics", "Spark Basics"): (spark, session) => given ConnectedSession = session + given SparkSession = spark - val codeFilesTable = QuickTable.quickTable().withStringHeaders("id", "name", "path", "numOfLines", "numOfWords", "createdDate").build - val codeFilesBadge = Badge() + val headers = Seq("id", "name", "path", "numOfLines", "numOfWords", "createdDate") + val codeFilesTable = QuickTable.quickTable().withStringHeaders(headers: _*).build + val codeFilesBadge = Badge() + val sortedFilesTable = QuickTable.quickTable().withStringHeaders(headers: _*).build + val sortedFilesBadge = Badge() Seq( + Box(text = "Code files", bg = "green", p = 4), codeFilesBadge, - codeFilesTable + codeFilesTable, + Box(text = "Code files sorted by date", bg = "green", p = 4), + sortedFilesBadge, + sortedFilesTable ).render() - val calcSrcCodeFiles = calculateSourceCodeFiles(spark, session, codeFilesTable, codeFilesBadge) - calcSrcCodeFiles(()) + val sortedCalc = Calculation + .newCalculation(sortedSourceFiles) + .whenStartingCalculationUpdateUi: + sortedFilesBadge.text = "Calculating..." + session.render() + .whenCalculatedUpdateUi: data => + sortedFilesBadge.text = "Ready" + sortedFilesTable.withRowStringData(data.take(10).toList.map(_.toData)) + session.render() + .build + val calcSrcCodeFiles = calculateSourceCodeFiles(codeFilesTable, codeFilesBadge) + calcSrcCodeFiles.notifyAfterCalculated(sortedCalc).build.apply(()) session.waitTillUserClosesSession() -def calculateSourceCodeFiles(spark: SparkSession, session: ConnectedSession, table: TableContainer, badge: Badge) = +def calculateSourceCodeFiles(table: TableContainer, badge: Badge)(using spark: SparkSession, session: ConnectedSession) = import spark.implicits.* import scala3encoders.given Calculation @@ -43,4 +61,7 @@ def calculateSourceCodeFiles(spark: SparkSession, session: ConnectedSession, tab table.withRowStringData(tableData.take(10).toList.map(_.toData)) badge.text = "Ready" session.render() - .build + +def sortedSourceFiles(sourceFiles: Dataset[CodeFile])(using spark: SparkSession) = + import spark.implicits.* + sourceFiles.sort($"createdDate".desc) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala index ee5b00b6..fab69d16 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala @@ -44,6 +44,7 @@ case class Box( @volatile var p: Int = 0, @volatile var color: String = "", @volatile var style: Map[String, String] = Map.empty, + @volatile var as: Option[String] = None, @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement with HasChildren[Box] From 080ae4d0ca1860b5f6bf40f5b4a11034d2cbf840 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 4 Jan 2024 21:35:50 +0000 Subject: [PATCH 032/144] - --- .../sparklib/steps/StepComponent.scala | 27 ++++ .../org/terminal21/sparklib/steps/Steps.scala | 60 ++++---- .../sparklib/steps/StepsCalculation.scala | 28 ++++ .../sparklib/endtoend/SparkBasics.scala | 55 ++++---- .../terminal21/sparklib/steps/StepsTest.scala | 132 +++++++++--------- .../org/terminal21/client/Calculation.scala | 4 +- .../terminal21/client/ConnectedSession.scala | 10 +- .../client/components/UiComponent.scala | 5 + .../terminal21/client/CalculationTest.scala | 6 +- .../client/ConnectedSessionTest.scala | 14 +- 10 files changed, 207 insertions(+), 134 deletions(-) create mode 100644 terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/StepComponent.scala create mode 100644 terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/StepsCalculation.scala create mode 100644 terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/StepComponent.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/StepComponent.scala new file mode 100644 index 00000000..a35635ef --- /dev/null +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/StepComponent.scala @@ -0,0 +1,27 @@ +package org.terminal21.sparklib.steps + +import org.terminal21.client.ConnectedSession +import org.terminal21.client.components.chakra.{Badge, Box} +import org.terminal21.client.components.{Keys, UiComponent, UiElement} + +abstract class StepComponent(val key: String = Keys.nextKey, @volatile var children: Seq[UiElement]) extends UiComponent: + def calculating(): Unit + def ready(): Unit + +object StepComponent: + def stdStep(name: String, dataUi: UiElement)(using session: ConnectedSession) = + val badge = Badge() + + new StepComponent( + children = Seq( + Box(text = name, bg = "green", p = 4), + badge, + dataUi + ) + ): + override def calculating(): Unit = + badge.text = "Calculating" + session.render() + override def ready(): Unit = + badge.text = "Ready" + session.render() diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/Steps.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/Steps.scala index 7e38c8b4..f9b72edd 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/Steps.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/Steps.scala @@ -2,39 +2,39 @@ package org.terminal21.sparklib.steps import org.apache.commons.io.FileUtils import org.apache.spark.sql.{DataFrame, Dataset, Encoder, SparkSession} +import org.terminal21.client.Calculation +import org.terminal21.client.components.UiElement import org.terminal21.sparklib.util.Environment import java.io.File -class Steps(spark: SparkSession, name: String): +class Steps(spark: SparkSession, name: String, calculations: Seq[Calculation[_, _]]): private val rootFolder = s"${Environment.tmpDirectory}/steps/$name" - def step(name: String): Step = new Step(spark, rootFolder, name) - -class Step(spark: SparkSession, rootFolder: String, name: String): - val targetDir = s"$rootFolder/$name" - - def invalidateCache(): Unit = - FileUtils.deleteDirectory(new File(targetDir)) - - private def cache[A](reader: => A, writer: => A): A = - if new File(targetDir).exists() then reader - else writer - - def calculateOnce(f: => DataFrame): DataFrame = - cache( - spark.read.parquet(targetDir), { - val df = f - df.write.parquet(targetDir) - df - } - ) - - def calculateOnce[A: Encoder](f: => Dataset[A]): Dataset[A] = - cache( - spark.read.parquet(targetDir).as[A], { - val ds = f - ds.write.parquet(targetDir) - ds - } - ) +//class Step(spark: SparkSession, rootFolder: String, name: String, ui: Seq[UiElement]): +// val targetDir = s"$rootFolder/$name" +// +// def invalidateCache(): Unit = +// FileUtils.deleteDirectory(new File(targetDir)) +// +// private def cache[A](reader: => A, writer: => A): A = +// if new File(targetDir).exists() then reader +// else writer +// +// def calculateOnce(f: => DataFrame): DataFrame = +// cache( +// spark.read.parquet(targetDir), { +// val df = f +// df.write.parquet(targetDir) +// df +// } +// ) +// +// def calculateOnce[A: Encoder](f: => Dataset[A]): Dataset[A] = +// cache( +// spark.read.parquet(targetDir).as[A], { +// val ds = f +// ds.write.parquet(targetDir) +// ds +// } +// ) diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/StepsCalculation.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/StepsCalculation.scala new file mode 100644 index 00000000..94022927 --- /dev/null +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/StepsCalculation.scala @@ -0,0 +1,28 @@ +package org.terminal21.sparklib.steps + +import org.terminal21.client.{Calculation, ConnectedSession} +import org.terminal21.client.components.chakra.* +import org.terminal21.client.given + +class StepsCalculation(session: ConnectedSession): + def calc[IN, OUT](name: String)(renderer: OUT => Unit)(calc: IN => OUT): Calculation[IN, OUT] = + val badge = Badge() + val ui = Seq( + Box(text = name, bg = "green", p = 4), + badge + ) + session.add(ui: _*) + session.render() + + val calculation = Calculation + .newCalculation(calc) + .whenStartingCalculationUpdateUi: + badge.text = "Calculating..." + session.render() + .whenCalculatedUpdateUi: data => + badge.text = "Ready" + renderer(data) + session.render() + .build + + calculation diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index 31643890..5e6da71c 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -1,38 +1,38 @@ package org.terminal21.sparklib.endtoend import org.apache.spark.sql.{Dataset, SparkSession} -import org.terminal21.client.* -import org.terminal21.client.given import org.terminal21.client.components.* import org.terminal21.client.components.chakra.* +import org.terminal21.client.{*, given} import org.terminal21.sparklib.SparkSessions import org.terminal21.sparklib.endtoend.model.CodeFile import org.terminal21.sparklib.endtoend.model.CodeFile.createDatasetFromProjectsSourceFiles -import org.terminal21.sparklib.steps.Steps - -import java.time.LocalDate +import org.terminal21.sparklib.steps.StepComponent @main def sparkBasics(): Unit = SparkSessions.newTerminal21WithSparkSession(SparkSessions.newSparkSession(), "spark-basics", "Spark Basics"): (spark, session) => given ConnectedSession = session given SparkSession = spark - val headers = Seq("id", "name", "path", "numOfLines", "numOfWords", "createdDate") - val codeFilesTable = QuickTable.quickTable().withStringHeaders(headers: _*).build - val codeFilesBadge = Badge() + import scala3encoders.given + import spark.implicits.* + + val headers = Seq("id", "name", "path", "numOfLines", "numOfWords", "createdDate") + + val codeFilesTable = QuickTable.quickTable().withStringHeaders(headers: _*).build + val codeFilesComponent = StepComponent.stdStep("Code files", codeFilesTable) + val sortedFilesTable = QuickTable.quickTable().withStringHeaders(headers: _*).build val sortedFilesBadge = Badge() Seq( - Box(text = "Code files", bg = "green", p = 4), - codeFilesBadge, - codeFilesTable, + codeFilesComponent, Box(text = "Code files sorted by date", bg = "green", p = 4), sortedFilesBadge, sortedFilesTable ).render() - val sortedCalc = Calculation + val sortedCalc = Calculation .newCalculation(sortedSourceFiles) .whenStartingCalculationUpdateUi: sortedFilesBadge.text = "Calculating..." @@ -42,25 +42,22 @@ import java.time.LocalDate sortedFilesTable.withRowStringData(data.take(10).toList.map(_.toData)) session.render() .build - val calcSrcCodeFiles = calculateSourceCodeFiles(codeFilesTable, codeFilesBadge) - calcSrcCodeFiles.notifyAfterCalculated(sortedCalc).build.apply(()) - session.waitTillUserClosesSession() + Calculation + .newCalculationNoIn: + createDatasetFromProjectsSourceFiles.toDS + .whenStartingCalculationUpdateUi: + sortedFilesTable.withRowStringData(Nil) + codeFilesComponent.calculating() + .whenCalculatedUpdateUi: tableData => + val dt = tableData.take(10).toList + codeFilesTable.withRowStringData(dt.map(_.toData)) + codeFilesComponent.ready() + .notifyAfterCalculated(sortedCalc) + .build + .run(()) -def calculateSourceCodeFiles(table: TableContainer, badge: Badge)(using spark: SparkSession, session: ConnectedSession) = - import spark.implicits.* - import scala3encoders.given - Calculation - .newCalculationNoIn: - createDatasetFromProjectsSourceFiles.toDS - .whenStartingCalculationUpdateUi: - table.withRowStringData(Nil) - badge.text = "Calculating..." - session.render() - .whenCalculatedUpdateUi: tableData => - table.withRowStringData(tableData.take(10).toList.map(_.toData)) - badge.text = "Ready" - session.render() + session.waitTillUserClosesSession() def sortedSourceFiles(sourceFiles: Dataset[CodeFile])(using spark: SparkSession) = import spark.implicits.* diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StepsTest.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StepsTest.scala index 3f708615..5d9fd22f 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StepsTest.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StepsTest.scala @@ -1,66 +1,66 @@ -package org.terminal21.sparklib.steps - -import org.terminal21.sparklib.{AbstractSparkSuite, SparkSessions} - -import java.util.concurrent.atomic.AtomicInteger -import scala.util.Using - -class StepsTest extends AbstractSparkSuite: - test("returns the correct dataframe"): - Using.resource(SparkSessions.newSparkSession()): spark => - import spark.implicits.* - - val steps = Steps(spark, "StepsTest-correct-df") - val budgetStep = steps.step("budget") - budgetStep.invalidateCache() - - def calculations() = Seq(1, 2, 3).toDF() - - for _ <- 1 to 3 do budgetStep.calculateOnce(calculations()).as[Int].collect().sorted should be(Array(1, 2, 3)) - - test("returns the correct dataset"): - Using.resource(SparkSessions.newSparkSession()): spark => - import spark.implicits.* - - val steps = Steps(spark, "StepsTest-correct-ds") - val budgetStep = steps.step("budget") - budgetStep.invalidateCache() - - def calculations() = Seq(1, 2, 3).toDS() - - for _ <- 1 to 3 do budgetStep.calculateOnce(calculations()).as[Int].collect().sorted should be(Array(1, 2, 3)) - - test("doesn't re-evaluate cached dataframes"): - Using.resource(SparkSessions.newSparkSession()): spark => - import spark.implicits.* - - val steps = Steps(spark, "StepsTest-cachedDF") - val budgetStep = steps.step("budget") - budgetStep.invalidateCache() - - val calculated = new AtomicInteger(0) - - def calculations() = - calculated.incrementAndGet() - Seq(1, 2, 3).toDF() - - budgetStep.calculateOnce(calculations()) - budgetStep.calculateOnce(calculations()) - calculated.get() should be(1) - - test("doesn't re-evaluate cached datasets"): - Using.resource(SparkSessions.newSparkSession()): spark => - import spark.implicits.* - - val steps = Steps(spark, "StepsTest-cachedDS") - val budgetStep = steps.step("budget") - budgetStep.invalidateCache() - - val calculated = new AtomicInteger(0) - - def calculations() = - calculated.incrementAndGet() - Seq(1, 2, 3).toDS() - budgetStep.calculateOnce(calculations()) - budgetStep.calculateOnce(calculations()) - calculated.get() should be(1) +//package org.terminal21.sparklib.steps +// +//import org.terminal21.sparklib.{AbstractSparkSuite, SparkSessions} +// +//import java.util.concurrent.atomic.AtomicInteger +//import scala.util.Using +// +//class StepsTest extends AbstractSparkSuite: +// test("returns the correct dataframe"): +// Using.resource(SparkSessions.newSparkSession()): spark => +// import spark.implicits.* +// +// val steps = Steps(spark, "StepsTest-correct-df") +// val budgetStep = steps.step("budget") +// budgetStep.invalidateCache() +// +// def calculations() = Seq(1, 2, 3).toDF() +// +// for _ <- 1 to 3 do budgetStep.calculateOnce(calculations()).as[Int].collect().sorted should be(Array(1, 2, 3)) +// +// test("returns the correct dataset"): +// Using.resource(SparkSessions.newSparkSession()): spark => +// import spark.implicits.* +// +// val steps = Steps(spark, "StepsTest-correct-ds") +// val budgetStep = steps.step("budget") +// budgetStep.invalidateCache() +// +// def calculations() = Seq(1, 2, 3).toDS() +// +// for _ <- 1 to 3 do budgetStep.calculateOnce(calculations()).as[Int].collect().sorted should be(Array(1, 2, 3)) +// +// test("doesn't re-evaluate cached dataframes"): +// Using.resource(SparkSessions.newSparkSession()): spark => +// import spark.implicits.* +// +// val steps = Steps(spark, "StepsTest-cachedDF") +// val budgetStep = steps.step("budget") +// budgetStep.invalidateCache() +// +// val calculated = new AtomicInteger(0) +// +// def calculations() = +// calculated.incrementAndGet() +// Seq(1, 2, 3).toDF() +// +// budgetStep.calculateOnce(calculations()) +// budgetStep.calculateOnce(calculations()) +// calculated.get() should be(1) +// +// test("doesn't re-evaluate cached datasets"): +// Using.resource(SparkSessions.newSparkSession()): spark => +// import spark.implicits.* +// +// val steps = Steps(spark, "StepsTest-cachedDS") +// val budgetStep = steps.step("budget") +// budgetStep.invalidateCache() +// +// val calculated = new AtomicInteger(0) +// +// def calculations() = +// calculated.incrementAndGet() +// Seq(1, 2, 3).toDS() +// budgetStep.calculateOnce(calculations()) +// budgetStep.calculateOnce(calculations()) +// calculated.get() should be(1) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala index 512a595d..760742e4 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala @@ -9,7 +9,7 @@ class Calculation[IN, OUT] private ( uiUpdaterWhenResultsReady: OUT => Unit, notifyWhenCalcReady: Seq[Calculation[OUT, _]] ): - def apply(in: IN): OUT = + def run(in: IN): OUT = val f = executor.submit: executor.submit: uiUpdaterWhenResultsNotReady() @@ -20,7 +20,7 @@ class Calculation[IN, OUT] private ( for c <- notifyWhenCalcReady do executor.submit: - try c(out) + try c.run(out) catch case t: Throwable => t.printStackTrace() out f.get() diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala index 10b1336b..6bf23074 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala @@ -4,7 +4,7 @@ import io.circe.* import io.circe.generic.auto.* import io.circe.syntax.* import org.slf4j.LoggerFactory -import org.terminal21.client.components.UiElement +import org.terminal21.client.components.{UiComponent, UiElement} import org.terminal21.client.components.UiElement.{HasEventHandler, allDeep} import org.terminal21.client.components.UiElementEncoding.uiElementEncoder import org.terminal21.model.* @@ -28,7 +28,9 @@ class ConnectedSession(val session: Session, val serverUrl: String, sessionsServ for e <- withEvents do addEventHandlerAtTheTop(e.key, e.defaultEventHandler) synchronized: - elements = elements ::: es.toList + elements = elements ::: es.toList.flatMap: + case c: UiComponent => c.children + case e => Seq(e) private val eventHandlers = collection.concurrent.TrieMap.empty[String, List[EventHandler]] @@ -82,8 +84,10 @@ class ConnectedSession(val session: Session, val serverUrl: String, sessionsServ val j = toJson sessionsService.setSessionJsonState(session, j.toJson.noSpaces) + def allElements: Seq[UiElement] = synchronized(elements) + private def toJson: JsonObject = - val elementsCopy = synchronized(elements) + val elementsCopy = allElements val json = for e <- elementsCopy yield e.asJson.deepDropNullValues diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala new file mode 100644 index 00000000..4eb8ef70 --- /dev/null +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala @@ -0,0 +1,5 @@ +package org.terminal21.client.components + +import org.terminal21.client.components.UiElement.HasChildren + +trait UiComponent extends UiElement with HasChildren[UiComponent] diff --git a/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala b/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala index 1654b7a7..614cd83e 100644 --- a/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala +++ b/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala @@ -14,13 +14,13 @@ class CalculationTest extends AnyFunSuiteLike: test("calculates"): val calc = Calculation.newCalculation(testCalc).build - calc(1) should be(2) + calc.run(1) should be(2) test("calls the ui updater with the calculated value"): val c = new AtomicInteger(-1) val b = new AtomicBoolean(false) val calc = Calculation.newCalculation(testCalc).whenStartingCalculationUpdateUi(b.set(true)).whenCalculatedUpdateUi(i => c.set(i)).build - calc(1) + calc.run(1) b.get() should be(true) eventually: c.get() should be(2) @@ -29,6 +29,6 @@ class CalculationTest extends AnyFunSuiteLike: val c = new AtomicInteger(-1) val calc2 = Calculation.newCalculation(testCalcString).whenCalculatedUpdateUi(i => c.set(i.toInt)).build val calc1 = Calculation.newCalculation(testCalc).notifyAfterCalculated(calc2).build - calc1(1) + calc1.run(1) eventually: c.get() should be(12) diff --git a/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionTest.scala b/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionTest.scala index 42706244..c1b7c4c2 100644 --- a/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionTest.scala +++ b/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionTest.scala @@ -2,7 +2,8 @@ package org.terminal21.client import org.scalatest.funsuite.AnyFunSuiteLike import org.scalatest.matchers.should.Matchers.* -import org.terminal21.client.components.chakra.Editable +import org.terminal21.client.components.UiComponent +import org.terminal21.client.components.chakra.{Box, Editable} import org.terminal21.model.OnChange class ConnectedSessionTest extends AnyFunSuiteLike: @@ -14,3 +15,14 @@ class ConnectedSessionTest extends AnyFunSuiteLike: connectedSession.add(editable) connectedSession.fireEvent(OnChange(editable.key, "new value")) + + test("components have their children added"): + given session: ConnectedSession = ConnectedSessionMock.newConnectedSessionMock + + val box = Box(key = "1") + val component = new UiComponent { + var children = Seq(box) + override def key = "2" + } + session.add(component) + session.allElements should be(Seq(box)) From 628a8992c8836340d3063e7e80bd9d0b71896e8a Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 4 Jan 2024 23:24:41 +0000 Subject: [PATCH 033/144] - --- .../sparklib/steps/SparkCalculation.scala | 36 ++++++++++++++ .../sparklib/steps/StepComponent.scala | 27 ----------- .../sparklib/steps/StepsCalculation.scala | 4 +- .../sparklib/endtoend/SparkBasics.scala | 47 ++++++++----------- .../org/terminal21/client/Calculation.scala | 45 +++++++++--------- .../terminal21/client/CalculationTest.scala | 4 +- 6 files changed, 84 insertions(+), 79 deletions(-) create mode 100644 terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala delete mode 100644 terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/StepComponent.scala diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala new file mode 100644 index 00000000..486f99b6 --- /dev/null +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala @@ -0,0 +1,36 @@ +package org.terminal21.sparklib.steps + +import functions.fibers.FiberExecutor +import org.terminal21.client.{Calculation, ConnectedSession} +import org.terminal21.client.components.chakra.{Badge, Box} +import org.terminal21.client.components.{Keys, UiComponent, UiElement} + +abstract class SparkCalculation[IN, OUT]( + val key: String = Keys.nextKey, + @volatile var children: Seq[UiElement], + notifyWhenCalcReady: Seq[Calculation[OUT, _]] +)(using executor: FiberExecutor) + extends Calculation[IN, OUT](notifyWhenCalcReady) + with UiComponent + +abstract class StdSparkCalculation[IN, OUT]( + key: String = Keys.nextKey, + name: String, + dataUi: UiElement, + notifyWhenCalcReady: Seq[Calculation[OUT, _]] +)(using session: ConnectedSession, executor: FiberExecutor) + extends SparkCalculation[IN, OUT](key, Nil, notifyWhenCalcReady): + val badge = Badge() + children = Seq( + Box(text = name, bg = "green", p = 4), + badge, + dataUi + ) + + override protected def whenResultsNotReady(): Unit = + badge.text = "Calculating" + session.render() + + override protected def whenResultsReady(results: OUT): Unit = + badge.text = "Ready" + session.render() diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/StepComponent.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/StepComponent.scala deleted file mode 100644 index a35635ef..00000000 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/StepComponent.scala +++ /dev/null @@ -1,27 +0,0 @@ -package org.terminal21.sparklib.steps - -import org.terminal21.client.ConnectedSession -import org.terminal21.client.components.chakra.{Badge, Box} -import org.terminal21.client.components.{Keys, UiComponent, UiElement} - -abstract class StepComponent(val key: String = Keys.nextKey, @volatile var children: Seq[UiElement]) extends UiComponent: - def calculating(): Unit - def ready(): Unit - -object StepComponent: - def stdStep(name: String, dataUi: UiElement)(using session: ConnectedSession) = - val badge = Badge() - - new StepComponent( - children = Seq( - Box(text = name, bg = "green", p = 4), - badge, - dataUi - ) - ): - override def calculating(): Unit = - badge.text = "Calculating" - session.render() - override def ready(): Unit = - badge.text = "Ready" - session.render() diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/StepsCalculation.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/StepsCalculation.scala index 94022927..3f67c8c7 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/StepsCalculation.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/StepsCalculation.scala @@ -16,10 +16,10 @@ class StepsCalculation(session: ConnectedSession): val calculation = Calculation .newCalculation(calc) - .whenStartingCalculationUpdateUi: + .whenResultsNotReady: badge.text = "Calculating..." session.render() - .whenCalculatedUpdateUi: data => + .whenResultsReady: data => badge.text = "Ready" renderer(data) session.render() diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index 5e6da71c..a85ccb12 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -7,7 +7,7 @@ import org.terminal21.client.{*, given} import org.terminal21.sparklib.SparkSessions import org.terminal21.sparklib.endtoend.model.CodeFile import org.terminal21.sparklib.endtoend.model.CodeFile.createDatasetFromProjectsSourceFiles -import org.terminal21.sparklib.steps.StepComponent +import org.terminal21.sparklib.steps.{SparkCalculation, StdSparkCalculation} @main def sparkBasics(): Unit = SparkSessions.newTerminal21WithSparkSession(SparkSessions.newSparkSession(), "spark-basics", "Spark Basics"): (spark, session) => @@ -19,44 +19,37 @@ import org.terminal21.sparklib.steps.StepComponent val headers = Seq("id", "name", "path", "numOfLines", "numOfWords", "createdDate") - val codeFilesTable = QuickTable.quickTable().withStringHeaders(headers: _*).build - val codeFilesComponent = StepComponent.stdStep("Code files", codeFilesTable) - val sortedFilesTable = QuickTable.quickTable().withStringHeaders(headers: _*).build val sortedFilesBadge = Badge() - - Seq( - codeFilesComponent, - Box(text = "Code files sorted by date", bg = "green", p = 4), - sortedFilesBadge, - sortedFilesTable - ).render() - - val sortedCalc = Calculation + val sortedCalc = Calculation .newCalculation(sortedSourceFiles) - .whenStartingCalculationUpdateUi: + .whenResultsNotReady: sortedFilesBadge.text = "Calculating..." session.render() - .whenCalculatedUpdateUi: data => + .whenResultsReady: data => sortedFilesBadge.text = "Ready" sortedFilesTable.withRowStringData(data.take(10).toList.map(_.toData)) session.render() .build - Calculation - .newCalculationNoIn: - createDatasetFromProjectsSourceFiles.toDS - .whenStartingCalculationUpdateUi: - sortedFilesTable.withRowStringData(Nil) - codeFilesComponent.calculating() - .whenCalculatedUpdateUi: tableData => - val dt = tableData.take(10).toList + val codeFilesTable = QuickTable.quickTable().withStringHeaders(headers: _*).build + + object CodeFilesCalculation + extends StdSparkCalculation[Unit, Dataset[CodeFile]](name = "Code files", dataUi = codeFilesTable, notifyWhenCalcReady = Seq(sortedCalc)): + override protected def calculation(in: Unit) = createDatasetFromProjectsSourceFiles.toDS + override protected def whenResultsReady(results: Dataset[CodeFile]): Unit = + val dt = results.take(10).toList codeFilesTable.withRowStringData(dt.map(_.toData)) - codeFilesComponent.ready() - .notifyAfterCalculated(sortedCalc) - .build - .run(()) + super.whenResultsReady(results) + + Seq( + CodeFilesCalculation, + Box(text = "Code files sorted by date", bg = "green", p = 4), + sortedFilesBadge, + sortedFilesTable + ).render() + CodeFilesCalculation.run(()) session.waitTillUserClosesSession() def sortedSourceFiles(sourceFiles: Dataset[CodeFile])(using spark: SparkSession) = diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala index 760742e4..aa2ca032 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala @@ -2,21 +2,21 @@ package org.terminal21.client import functions.fibers.FiberExecutor -class Calculation[IN, OUT] private ( - executor: FiberExecutor, - calc: IN => OUT, - uiUpdaterWhenResultsNotReady: () => Unit, - uiUpdaterWhenResultsReady: OUT => Unit, +abstract class Calculation[IN, OUT]( notifyWhenCalcReady: Seq[Calculation[OUT, _]] -): +)(using executor: FiberExecutor): + protected def calculation(in: IN): OUT + protected def whenResultsNotReady(): Unit + protected def whenResultsReady(results: OUT): Unit + def run(in: IN): OUT = val f = executor.submit: executor.submit: - uiUpdaterWhenResultsNotReady() + whenResultsNotReady() - val out = calc(in) + val out = calculation(in) executor.submit: - uiUpdaterWhenResultsReady(out) + whenResultsReady(out) for c <- notifyWhenCalcReady do executor.submit: @@ -26,20 +26,23 @@ class Calculation[IN, OUT] private ( f.get() object Calculation: - class CalcBuilder[IN, OUT]( - executor: FiberExecutor, + class Builder[IN, OUT]( calc: IN => OUT, - uiUpdater: () => Unit = () => (), + uiNotReadyUpdater: () => Unit = () => (), uiReadyUpdater: OUT => Unit = (_: OUT) => (), notify: Seq[Calculation[OUT, _]] = Nil - ): - def whenStartingCalculationUpdateUi(uiUpdater: => Unit) = new CalcBuilder(executor, calc, () => uiUpdater, uiReadyUpdater, notify) - def whenCalculatedUpdateUi(uiReadyUpdater: OUT => Unit) = new CalcBuilder(executor, calc, uiUpdater, uiReadyUpdater, notify) - def notifyAfterCalculated(other: Calculation[OUT, _]) = new CalcBuilder(executor, calc, uiUpdater, uiReadyUpdater, notify :+ other) - def build: Calculation[IN, OUT] = new Calculation[IN, OUT](executor, calc, uiUpdater, uiReadyUpdater, notify) + )(using executor: FiberExecutor): + def whenResultsNotReady(uiUpdater: => Unit) = new Builder(calc, () => uiUpdater, uiReadyUpdater, notify) + def whenResultsReady(uiReadyUpdater: OUT => Unit) = new Builder(calc, uiNotReadyUpdater, uiReadyUpdater, notify) + def notifyAfterCalculated(other: Calculation[OUT, _]) = new Builder(calc, uiNotReadyUpdater, uiReadyUpdater, notify :+ other) + def build: Calculation[IN, OUT] = + new Calculation[IN, OUT](notify): + override protected def calculation(in: IN): OUT = calc(in) + override protected def whenResultsNotReady(): Unit = uiNotReadyUpdater() + override protected def whenResultsReady(results: OUT): Unit = uiReadyUpdater(results) - def newCalculationNoIn[OUT](calc: => OUT)(using executor: FiberExecutor): CalcBuilder[Unit, OUT] = - new CalcBuilder(executor, _ => calc) + def newCalculationNoIn[OUT](calc: => OUT)(using executor: FiberExecutor): Builder[Unit, OUT] = + new Builder(_ => calc) - def newCalculation[IN, OUT](calc: IN => OUT)(using executor: FiberExecutor): CalcBuilder[IN, OUT] = - new CalcBuilder(executor, calc) + def newCalculation[IN, OUT](calc: IN => OUT)(using executor: FiberExecutor): Builder[IN, OUT] = + new Builder(calc) diff --git a/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala b/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala index 614cd83e..2d88db72 100644 --- a/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala +++ b/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala @@ -19,7 +19,7 @@ class CalculationTest extends AnyFunSuiteLike: test("calls the ui updater with the calculated value"): val c = new AtomicInteger(-1) val b = new AtomicBoolean(false) - val calc = Calculation.newCalculation(testCalc).whenStartingCalculationUpdateUi(b.set(true)).whenCalculatedUpdateUi(i => c.set(i)).build + val calc = Calculation.newCalculation(testCalc).whenResultsNotReady(b.set(true)).whenResultsReady(i => c.set(i)).build calc.run(1) b.get() should be(true) eventually: @@ -27,7 +27,7 @@ class CalculationTest extends AnyFunSuiteLike: test("notifies"): val c = new AtomicInteger(-1) - val calc2 = Calculation.newCalculation(testCalcString).whenCalculatedUpdateUi(i => c.set(i.toInt)).build + val calc2 = Calculation.newCalculation(testCalcString).whenResultsReady(i => c.set(i.toInt)).build val calc1 = Calculation.newCalculation(testCalc).notifyAfterCalculated(calc2).build calc1.run(1) eventually: From 9f371b0e2c9e5a50b79b8dd81c8aac552e25d435 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 4 Jan 2024 23:36:35 +0000 Subject: [PATCH 034/144] - --- .../sparklib/steps/SparkCalculation.scala | 18 ++++++++++++++++++ .../sparklib/endtoend/SparkBasics.scala | 13 ++++++------- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala index 486f99b6..ca8d5398 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala @@ -34,3 +34,21 @@ abstract class StdSparkCalculation[IN, OUT]( override protected def whenResultsReady(results: OUT): Unit = badge.text = "Ready" session.render() + +object SparkCalculation: + class Builder[IN, OUT](name: String, dataUi: UiElement, notifyWhenCalcReady: Seq[Calculation[OUT, _]], calc: IN => OUT)(using + session: ConnectedSession, + executor: FiberExecutor + ): + def whenResultsReady(ready: OUT => Unit) = + new StdSparkCalculation[IN, OUT](name = name, dataUi = dataUi, notifyWhenCalcReady): + override protected def calculation(in: IN): OUT = calc(in) + override protected def whenResultsReady(results: OUT): Unit = + ready(results) + super.whenResultsReady(results) + + def stdSparkCalculation[IN, OUT](name: String, dataUi: UiElement, notifyWhenCalcReady: Seq[Calculation[OUT, _]] = Nil)(calc: IN => OUT)(using + session: ConnectedSession, + executor: FiberExecutor + ) = + new Builder[IN, OUT](name, dataUi, notifyWhenCalcReady, calc) diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index a85ccb12..ad90cf36 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -34,22 +34,21 @@ import org.terminal21.sparklib.steps.{SparkCalculation, StdSparkCalculation} val codeFilesTable = QuickTable.quickTable().withStringHeaders(headers: _*).build - object CodeFilesCalculation - extends StdSparkCalculation[Unit, Dataset[CodeFile]](name = "Code files", dataUi = codeFilesTable, notifyWhenCalcReady = Seq(sortedCalc)): - override protected def calculation(in: Unit) = createDatasetFromProjectsSourceFiles.toDS - override protected def whenResultsReady(results: Dataset[CodeFile]): Unit = + val codeFilesCalculation = SparkCalculation + .stdSparkCalculation("Code files", codeFilesTable, Seq(sortedCalc)): _ => + createDatasetFromProjectsSourceFiles.toDS + .whenResultsReady: results => val dt = results.take(10).toList codeFilesTable.withRowStringData(dt.map(_.toData)) - super.whenResultsReady(results) Seq( - CodeFilesCalculation, + codeFilesCalculation, Box(text = "Code files sorted by date", bg = "green", p = 4), sortedFilesBadge, sortedFilesTable ).render() - CodeFilesCalculation.run(()) + codeFilesCalculation.run(()) session.waitTillUserClosesSession() def sortedSourceFiles(sourceFiles: Dataset[CodeFile])(using spark: SparkSession) = From afef877891e1223e1da4219a01606edfe5deb02e Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 5 Jan 2024 00:07:43 +0000 Subject: [PATCH 035/144] - --- .../sparklib/steps/SparkCalculation.scala | 2 +- .../sparklib/endtoend/SparkBasics.scala | 33 +++++++------------ 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala index ca8d5398..08a3e87a 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala @@ -47,7 +47,7 @@ object SparkCalculation: ready(results) super.whenResultsReady(results) - def stdSparkCalculation[IN, OUT](name: String, dataUi: UiElement, notifyWhenCalcReady: Seq[Calculation[OUT, _]] = Nil)(calc: IN => OUT)(using + def stdSparkCalculation[IN, OUT](name: String, dataUi: UiElement, notifyWhenCalcReady: Calculation[OUT, _]*)(calc: IN => OUT)(using session: ConnectedSession, executor: FiberExecutor ) = diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index ad90cf36..1d7e48be 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -7,6 +7,7 @@ import org.terminal21.client.{*, given} import org.terminal21.sparklib.SparkSessions import org.terminal21.sparklib.endtoend.model.CodeFile import org.terminal21.sparklib.endtoend.model.CodeFile.createDatasetFromProjectsSourceFiles +import org.terminal21.sparklib.steps.SparkCalculation.stdSparkCalculation import org.terminal21.sparklib.steps.{SparkCalculation, StdSparkCalculation} @main def sparkBasics(): Unit = @@ -20,32 +21,20 @@ import org.terminal21.sparklib.steps.{SparkCalculation, StdSparkCalculation} val headers = Seq("id", "name", "path", "numOfLines", "numOfWords", "createdDate") val sortedFilesTable = QuickTable.quickTable().withStringHeaders(headers: _*).build - val sortedFilesBadge = Badge() - val sortedCalc = Calculation - .newCalculation(sortedSourceFiles) - .whenResultsNotReady: - sortedFilesBadge.text = "Calculating..." - session.render() - .whenResultsReady: data => - sortedFilesBadge.text = "Ready" - sortedFilesTable.withRowStringData(data.take(10).toList.map(_.toData)) - session.render() - .build - - val codeFilesTable = QuickTable.quickTable().withStringHeaders(headers: _*).build - - val codeFilesCalculation = SparkCalculation - .stdSparkCalculation("Code files", codeFilesTable, Seq(sortedCalc)): _ => - createDatasetFromProjectsSourceFiles.toDS + val sortedCalc = stdSparkCalculation("Sorted files", sortedFilesTable)(sortedSourceFiles) .whenResultsReady: results => - val dt = results.take(10).toList - codeFilesTable.withRowStringData(dt.map(_.toData)) + sortedFilesTable.withRowStringData(results.take(10).toList.map(_.toData)) + + val codeFilesTable = QuickTable.quickTable().withStringHeaders(headers: _*).build + val codeFilesCalculation = stdSparkCalculation("Code files", codeFilesTable, sortedCalc): _ => + createDatasetFromProjectsSourceFiles.toDS + .whenResultsReady: results => + val dt = results.take(10).toList + codeFilesTable.withRowStringData(dt.map(_.toData)) Seq( codeFilesCalculation, - Box(text = "Code files sorted by date", bg = "green", p = 4), - sortedFilesBadge, - sortedFilesTable + sortedCalc ).render() codeFilesCalculation.run(()) From 8c52d99e8dfc6f272ae5db8b0d55af437b849202 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 5 Jan 2024 17:01:06 +0000 Subject: [PATCH 036/144] - --- .../sparklib/steps/SparkCalculation.scala | 22 ++++++--- .../sparklib/endtoend/SparkBasics.scala | 2 +- .../client/components/StdElement.scala | 6 +-- .../client/components/UiElement.scala | 3 ++ .../components/chakra/ChakraElement.scala | 48 ++++++++++++++----- 5 files changed, 58 insertions(+), 23 deletions(-) diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala index 08a3e87a..e646c92a 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala @@ -1,8 +1,9 @@ package org.terminal21.sparklib.steps import functions.fibers.FiberExecutor +import org.terminal21.client.components.UiElement.HasStyle import org.terminal21.client.{Calculation, ConnectedSession} -import org.terminal21.client.components.chakra.{Badge, Box} +import org.terminal21.client.components.chakra.{Badge, Box, Button, HStack} import org.terminal21.client.components.{Keys, UiComponent, UiElement} abstract class SparkCalculation[IN, OUT]( @@ -16,27 +17,36 @@ abstract class SparkCalculation[IN, OUT]( abstract class StdSparkCalculation[IN, OUT]( key: String = Keys.nextKey, name: String, - dataUi: UiElement, + dataUi: UiElement with HasStyle, notifyWhenCalcReady: Seq[Calculation[OUT, _]] )(using session: ConnectedSession, executor: FiberExecutor) extends SparkCalculation[IN, OUT](key, Nil, notifyWhenCalcReady): - val badge = Badge() + val badge = Badge() + val recalc = Button(text = "Recalculate").onClick: () => + for i <- in do run(i) children = Seq( Box(text = name, bg = "green", p = 4), - badge, + HStack().withChildren(badge, recalc), dataUi ) + private var in: Option[IN] = None + override def run(in: IN) = + this.in = Some(in) + super.run(in) + override protected def whenResultsNotReady(): Unit = badge.text = "Calculating" + dataUi.style = dataUi.style + ("filter" -> "grayscale(100%)") session.render() override protected def whenResultsReady(results: OUT): Unit = badge.text = "Ready" + dataUi.style = dataUi.style - "filter" session.render() object SparkCalculation: - class Builder[IN, OUT](name: String, dataUi: UiElement, notifyWhenCalcReady: Seq[Calculation[OUT, _]], calc: IN => OUT)(using + class Builder[IN, OUT](name: String, dataUi: UiElement with HasStyle, notifyWhenCalcReady: Seq[Calculation[OUT, _]], calc: IN => OUT)(using session: ConnectedSession, executor: FiberExecutor ): @@ -47,7 +57,7 @@ object SparkCalculation: ready(results) super.whenResultsReady(results) - def stdSparkCalculation[IN, OUT](name: String, dataUi: UiElement, notifyWhenCalcReady: Calculation[OUT, _]*)(calc: IN => OUT)(using + def stdSparkCalculation[IN, OUT](name: String, dataUi: UiElement with HasStyle, notifyWhenCalcReady: Calculation[OUT, _]*)(calc: IN => OUT)(using session: ConnectedSession, executor: FiberExecutor ) = diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index 1d7e48be..ba91dcf4 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -42,4 +42,4 @@ import org.terminal21.sparklib.steps.{SparkCalculation, StdSparkCalculation} def sortedSourceFiles(sourceFiles: Dataset[CodeFile])(using spark: SparkSession) = import spark.implicits.* - sourceFiles.sort($"createdDate".desc) + sourceFiles.sort($"createdDate".desc, $"numOfWords".desc) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdElement.scala index 6712b29c..c40239d6 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdElement.scala @@ -1,12 +1,12 @@ package org.terminal21.client.components -import org.terminal21.client.components.UiElement.{HasChildren, HasEventHandler} +import org.terminal21.client.components.UiElement.{HasChildren, HasEventHandler, HasStyle} import org.terminal21.client.{ConnectedSession, OnChangeEventHandler} -sealed trait StdElement extends UiElement +sealed trait StdElement extends UiElement with HasStyle case class Span(key: String = Keys.nextKey, @volatile var text: String, @volatile var style: Map[String, String] = Map.empty) extends StdElement -case class NewLine(key: String = Keys.nextKey) extends StdElement +case class NewLine(key: String = Keys.nextKey, @volatile var style: Map[String, String] = Map.empty) extends StdElement case class Em(key: String = Keys.nextKey, @volatile var text: String, @volatile var style: Map[String, String] = Map.empty) extends StdElement case class Header1(key: String = Keys.nextKey, @volatile var text: String, @volatile var style: Map[String, String] = Map.empty) extends StdElement diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala index 9d63a2ad..61f03dd9 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala @@ -30,3 +30,6 @@ object UiElement: trait HasEventHandler: def defaultEventHandler: EventHandler + + trait HasStyle: + var style: Map[String, String] diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala index fab69d16..25cf0bf9 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala @@ -1,10 +1,10 @@ package org.terminal21.client.components.chakra -import org.terminal21.client.components.UiElement.{HasChildren, HasEventHandler} +import org.terminal21.client.components.UiElement.{HasChildren, HasEventHandler, HasStyle} import org.terminal21.client.components.{Keys, UiElement} import org.terminal21.client.{OnChangeBooleanEventHandler, OnChangeEventHandler, OnClickEventHandler} -sealed trait ChakraElement extends UiElement +sealed trait ChakraElement extends UiElement with HasStyle /** https://chakra-ui.com/docs/components/button */ @@ -184,8 +184,13 @@ case class Checkbox( /** https://chakra-ui.com/docs/components/radio */ -case class Radio(key: String = Keys.nextKey, value: String, @volatile var text: String = "", @volatile var colorScheme: Option[String] = None) - extends ChakraElement +case class Radio( + key: String = Keys.nextKey, + value: String, + @volatile var text: String = "", + @volatile var colorScheme: Option[String] = None, + @volatile var style: Map[String, String] = Map.empty +) extends ChakraElement case class RadioGroup( key: String = Keys.nextKey, defaultValue: String = "", @@ -930,7 +935,9 @@ case class Option_( /** https://chakra-ui.com/docs/components/table/usage */ -case class TableContainer(key: String = Keys.nextKey, @volatile var children: Seq[UiElement] = Nil) extends ChakraElement with HasChildren[TableContainer]: +case class TableContainer(key: String = Keys.nextKey, @volatile var children: Seq[UiElement] = Nil, @volatile var style: Map[String, String] = Map.empty) + extends ChakraElement + with HasChildren[TableContainer]: def withRowStringData(data: Seq[Seq[String]]): TableContainer = withRowData(data.map(_.map(c => Text(text = c)))) def withRowData(data: Seq[Seq[UiElement]]): TableContainer = val tableBodies = children @@ -956,7 +963,7 @@ case class Table( @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement with HasChildren[Table] -case class TableCaption(key: String = Keys.nextKey, @volatile var text: String = "") extends ChakraElement +case class TableCaption(key: String = Keys.nextKey, @volatile var text: String = "", @volatile var style: Map[String, String] = Map.empty) extends ChakraElement case class Thead(key: String = Keys.nextKey, @volatile var children: Seq[UiElement] = Nil, @volatile var style: Map[String, String] = Map.empty) extends ChakraElement with HasChildren[Thead] @@ -980,28 +987,42 @@ case class Th( @volatile var style: Map[String, String] = Map.empty ) extends ChakraElement with HasChildren[Th] -case class Td(key: String = Keys.nextKey, @volatile var text: String = "", isNumeric: Boolean = false, @volatile var children: Seq[UiElement] = Nil) - extends ChakraElement +case class Td( + key: String = Keys.nextKey, + @volatile var text: String = "", + isNumeric: Boolean = false, + @volatile var style: Map[String, String] = Map.empty, + @volatile var children: Seq[UiElement] = Nil +) extends ChakraElement with HasChildren[Td] /** https://chakra-ui.com/docs/components/menu/usage */ -case class Menu(key: String = Keys.nextKey, @volatile var children: Seq[UiElement] = Nil) extends ChakraElement with HasChildren[Menu] +case class Menu(key: String = Keys.nextKey, @volatile var style: Map[String, String] = Map.empty, @volatile var children: Seq[UiElement] = Nil) + extends ChakraElement + with HasChildren[Menu] case class MenuButton( key: String = Keys.nextKey, @volatile var text: String = "", @volatile var size: Option[String] = None, @volatile var colorScheme: Option[String] = None, + @volatile var style: Map[String, String] = Map.empty, @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement with HasChildren[MenuButton] -case class MenuList(key: String = Keys.nextKey, @volatile var children: Seq[UiElement] = Nil) extends ChakraElement with HasChildren[MenuList] -case class MenuItem(key: String = Keys.nextKey, @volatile var text: String = "", @volatile var children: Seq[UiElement] = Nil) +case class MenuList(key: String = Keys.nextKey, @volatile var style: Map[String, String] = Map.empty, @volatile var children: Seq[UiElement] = Nil) extends ChakraElement + with HasChildren[MenuList] +case class MenuItem( + key: String = Keys.nextKey, + @volatile var style: Map[String, String] = Map.empty, + @volatile var text: String = "", + @volatile var children: Seq[UiElement] = Nil +) extends ChakraElement with HasChildren[MenuItem] with OnClickEventHandler.CanHandleOnClickEvent[MenuItem] -case class MenuDivider(key: String = Keys.nextKey) extends ChakraElement +case class MenuDivider(key: String = Keys.nextKey, @volatile var style: Map[String, String] = Map.empty) extends ChakraElement case class Badge( key: String = Keys.nextKey, @@ -1040,5 +1061,6 @@ case class Text( @volatile var as: Option[String] = None, @volatile var align: Option[String] = None, @volatile var casing: Option[String] = None, - @volatile var decoration: Option[String] = None + @volatile var decoration: Option[String] = None, + @volatile var style: Map[String, String] = Map.empty ) extends ChakraElement From 1ffebecafe5ad5a6b0d4b5a0db1396873aa3d65d Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 5 Jan 2024 17:25:39 +0000 Subject: [PATCH 037/144] - --- .../sparklib/steps/SparkCalculation.scala | 14 ++++++++++---- .../sparklib/endtoend/model/CodeFile.scala | 5 ++--- .../client/components/chakra/ChakraElement.scala | 3 ++- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala index e646c92a..ec0a174a 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala @@ -3,7 +3,7 @@ package org.terminal21.sparklib.steps import functions.fibers.FiberExecutor import org.terminal21.client.components.UiElement.HasStyle import org.terminal21.client.{Calculation, ConnectedSession} -import org.terminal21.client.components.chakra.{Badge, Box, Button, HStack} +import org.terminal21.client.components.chakra.{Badge, Box, Button, HStack, RepeatIcon, Text} import org.terminal21.client.components.{Keys, UiComponent, UiElement} abstract class SparkCalculation[IN, OUT]( @@ -22,11 +22,17 @@ abstract class StdSparkCalculation[IN, OUT]( )(using session: ConnectedSession, executor: FiberExecutor) extends SparkCalculation[IN, OUT](key, Nil, notifyWhenCalcReady): val badge = Badge() - val recalc = Button(text = "Recalculate").onClick: () => + val recalc = Button(text = "Recalculate", size = Some("sm"), leftIcon = Some(RepeatIcon())).onClick: () => for i <- in do run(i) + children = Seq( - Box(text = name, bg = "green", p = 4), - HStack().withChildren(badge, recalc), + Box(bg = "green", p = 4).withChildren( + HStack().withChildren( + Text(text = name), + badge, + recalc + ) + ), dataUi ) diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/model/CodeFile.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/model/CodeFile.scala index c819aa90..e8139c8a 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/model/CodeFile.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/model/CodeFile.scala @@ -13,9 +13,8 @@ case class CodeFile(id: Int, name: String, path: String, numOfLines: Int, numOfW object CodeFile: import scala.jdk.CollectionConverters.* def createDatasetFromProjectsSourceFiles: Seq[CodeFile] = - val availableFiles = FileUtils.listFiles(new File(".."), Array("scala"), true).asScala.toArray - for i <- 1 to 10000 yield - val f = availableFiles(i % availableFiles.length) + val availableFiles = FileUtils.listFiles(new File(".."), Array("scala"), true).asScala.toList + availableFiles.zipWithIndex.map: (f, i) => val code = Files.readString(f.toPath) CodeFile( i, diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala index 25cf0bf9..b6fff647 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala @@ -14,7 +14,8 @@ case class Button( @volatile var size: Option[String] = None, @volatile var variant: Option[String] = None, @volatile var colorScheme: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, String] = Map.empty, + @volatile var leftIcon: Option[UiElement] = None ) extends ChakraElement with OnClickEventHandler.CanHandleOnClickEvent[Button] From 0dbd9a233d5e55cc459445a477fcf46c1ded269a Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 5 Jan 2024 19:25:26 +0000 Subject: [PATCH 038/144] - --- .../terminal21/client/components/chakra/ChakraElement.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala index b6fff647..329a6a6b 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala @@ -15,7 +15,8 @@ case class Button( @volatile var variant: Option[String] = None, @volatile var colorScheme: Option[String] = None, @volatile var style: Map[String, String] = Map.empty, - @volatile var leftIcon: Option[UiElement] = None + @volatile var leftIcon: Option[UiElement] = None, + @volatile var rightIcon: Option[UiElement] = None ) extends ChakraElement with OnClickEventHandler.CanHandleOnClickEvent[Button] From 9b34afe6db0ce2f68e996c789cb26a1bdd96a7ec Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 5 Jan 2024 19:27:36 +0000 Subject: [PATCH 039/144] - --- .../scala/org/terminal21/sparklib/steps/SparkCalculation.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala index ec0a174a..bc002458 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala @@ -43,11 +43,13 @@ abstract class StdSparkCalculation[IN, OUT]( override protected def whenResultsNotReady(): Unit = badge.text = "Calculating" + badge.colorScheme = Some("purple") dataUi.style = dataUi.style + ("filter" -> "grayscale(100%)") session.render() override protected def whenResultsReady(results: OUT): Unit = badge.text = "Ready" + badge.colorScheme = None dataUi.style = dataUi.style - "filter" session.render() From 4d6a2ce2848029ad73432a120c9e175d215cb1f9 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 5 Jan 2024 19:34:49 +0000 Subject: [PATCH 040/144] - --- .../org/terminal21/sparklib/steps/SparkCalculation.scala | 1 + .../src/main/scala/org/terminal21/client/Calculation.scala | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala index bc002458..db0f7bba 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala @@ -46,6 +46,7 @@ abstract class StdSparkCalculation[IN, OUT]( badge.colorScheme = Some("purple") dataUi.style = dataUi.style + ("filter" -> "grayscale(100%)") session.render() + super.whenResultsNotReady() override protected def whenResultsReady(results: OUT): Unit = badge.text = "Ready" diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala index aa2ca032..01097780 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala @@ -6,7 +6,9 @@ abstract class Calculation[IN, OUT]( notifyWhenCalcReady: Seq[Calculation[OUT, _]] )(using executor: FiberExecutor): protected def calculation(in: IN): OUT - protected def whenResultsNotReady(): Unit + protected def whenResultsNotReady(): Unit = + for c <- notifyWhenCalcReady do c.whenResultsNotReady() + protected def whenResultsReady(results: OUT): Unit def run(in: IN): OUT = From e4e1e8ce3c00e761c8794bbd83e9f1c26502113a Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 5 Jan 2024 19:49:55 +0000 Subject: [PATCH 041/144] - --- .../org/terminal21/sparklib/steps/SparkCalculation.scala | 2 ++ .../client/components/chakra/ChakraElement.scala | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala index db0f7bba..bcde346b 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala @@ -44,6 +44,7 @@ abstract class StdSparkCalculation[IN, OUT]( override protected def whenResultsNotReady(): Unit = badge.text = "Calculating" badge.colorScheme = Some("purple") + recalc.isDisabled = Some(true) dataUi.style = dataUi.style + ("filter" -> "grayscale(100%)") session.render() super.whenResultsNotReady() @@ -51,6 +52,7 @@ abstract class StdSparkCalculation[IN, OUT]( override protected def whenResultsReady(results: OUT): Unit = badge.text = "Ready" badge.colorScheme = None + recalc.isDisabled = Some(false) dataUi.style = dataUi.style - "filter" session.render() diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala index 329a6a6b..131df8bc 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala @@ -16,7 +16,12 @@ case class Button( @volatile var colorScheme: Option[String] = None, @volatile var style: Map[String, String] = Map.empty, @volatile var leftIcon: Option[UiElement] = None, - @volatile var rightIcon: Option[UiElement] = None + @volatile var rightIcon: Option[UiElement] = None, + @volatile var isActive: Option[Boolean] = None, + @volatile var isDisabled: Option[Boolean] = None, + @volatile var isLoading: Option[Boolean] = None, + @volatile var isAttached: Option[Boolean] = None, + @volatile var spacing: Option[String] = None ) extends ChakraElement with OnClickEventHandler.CanHandleOnClickEvent[Button] From 310ee4ab079079e6c8ead5b5aca5ff3e7fe6c23d Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 5 Jan 2024 21:05:08 +0000 Subject: [PATCH 042/144] - --- build.sbt | 2 +- .../sparklib/steps/StepsCalculation.scala | 28 ------------- .../steps/StdSparkCalculationTest.scala | 39 +++++++++++++++++++ 3 files changed, 40 insertions(+), 29 deletions(-) delete mode 100644 terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/StepsCalculation.scala create mode 100644 terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StdSparkCalculationTest.scala diff --git a/build.sbt b/build.sbt index 1509e35d..fe02696c 100644 --- a/build.sbt +++ b/build.sbt @@ -157,4 +157,4 @@ lazy val `terminal21-spark` = project LogBack % Test ) ++ SparkScala3Fix ) - .dependsOn(`terminal21-ui-std`) + .dependsOn(`terminal21-ui-std` % "compile->compile;test->test") diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/StepsCalculation.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/StepsCalculation.scala deleted file mode 100644 index 3f67c8c7..00000000 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/StepsCalculation.scala +++ /dev/null @@ -1,28 +0,0 @@ -package org.terminal21.sparklib.steps - -import org.terminal21.client.{Calculation, ConnectedSession} -import org.terminal21.client.components.chakra.* -import org.terminal21.client.given - -class StepsCalculation(session: ConnectedSession): - def calc[IN, OUT](name: String)(renderer: OUT => Unit)(calc: IN => OUT): Calculation[IN, OUT] = - val badge = Badge() - val ui = Seq( - Box(text = name, bg = "green", p = 4), - badge - ) - session.add(ui: _*) - session.render() - - val calculation = Calculation - .newCalculation(calc) - .whenResultsNotReady: - badge.text = "Calculating..." - session.render() - .whenResultsReady: data => - badge.text = "Ready" - renderer(data) - session.render() - .build - - calculation diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StdSparkCalculationTest.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StdSparkCalculationTest.scala new file mode 100644 index 00000000..f702063d --- /dev/null +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StdSparkCalculationTest.scala @@ -0,0 +1,39 @@ +package org.terminal21.sparklib.steps + +import org.scalatest.concurrent.Eventually.eventually +import org.scalatest.funsuite.AnyFunSuiteLike +import org.scalatest.matchers.should.Matchers.* +import org.terminal21.client.components.chakra.* +import org.terminal21.client.{ConnectedSession, ConnectedSessionMock, given} + +import java.util.concurrent.atomic.AtomicBoolean + +class StdSparkCalculationTest extends AnyFunSuiteLike: + test("whenResultsNotReady"): + given ConnectedSession = ConnectedSessionMock.newConnectedSessionMock + val dataUi = Box() + val called = new AtomicBoolean(false) + val calc = new StdSparkCalculation[Int, Int]("key", "name", dataUi, Nil): + override protected def calculation(in: Int) = in + 1 + + override protected def whenResultsNotReady(): Unit = + called.set(true) + super.whenResultsNotReady() + + calc.run(1) + called.get() should be(true) + + test("whenResultsReady"): + given ConnectedSession = ConnectedSessionMock.newConnectedSessionMock + val dataUi = Box() + val called = new AtomicBoolean(false) + val calc = new StdSparkCalculation[Int, Int]("key", "name", dataUi, Nil): + override protected def calculation(in: Int) = in + 1 + + override protected def whenResultsReady(results: Int): Unit = + called.set(true) + super.whenResultsReady(results) + + calc.run(1) + eventually: + called.get() should be(true) From 0d7ce82cf16f37d3f4ec79db17e14d72f56231fb Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 5 Jan 2024 21:16:17 +0000 Subject: [PATCH 043/144] - --- .../sparklib/steps/SparkCalculation.scala | 20 ++++--- .../steps/StdSparkCalculationTest.scala | 60 +++++++++++-------- 2 files changed, 47 insertions(+), 33 deletions(-) diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala index bcde346b..d4397af2 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala @@ -1,6 +1,7 @@ package org.terminal21.sparklib.steps import functions.fibers.FiberExecutor +import org.apache.spark.sql.Dataset import org.terminal21.client.components.UiElement.HasStyle import org.terminal21.client.{Calculation, ConnectedSession} import org.terminal21.client.components.chakra.{Badge, Box, Button, HStack, RepeatIcon, Text} @@ -9,16 +10,16 @@ import org.terminal21.client.components.{Keys, UiComponent, UiElement} abstract class SparkCalculation[IN, OUT]( val key: String = Keys.nextKey, @volatile var children: Seq[UiElement], - notifyWhenCalcReady: Seq[Calculation[OUT, _]] + notifyWhenCalcReady: Seq[Calculation[Dataset[OUT], _]] )(using executor: FiberExecutor) - extends Calculation[IN, OUT](notifyWhenCalcReady) + extends Calculation[IN, Dataset[OUT]](notifyWhenCalcReady) with UiComponent abstract class StdSparkCalculation[IN, OUT]( key: String = Keys.nextKey, name: String, dataUi: UiElement with HasStyle, - notifyWhenCalcReady: Seq[Calculation[OUT, _]] + notifyWhenCalcReady: Seq[Calculation[Dataset[OUT], _]] )(using session: ConnectedSession, executor: FiberExecutor) extends SparkCalculation[IN, OUT](key, Nil, notifyWhenCalcReady): val badge = Badge() @@ -49,7 +50,7 @@ abstract class StdSparkCalculation[IN, OUT]( session.render() super.whenResultsNotReady() - override protected def whenResultsReady(results: OUT): Unit = + override protected def whenResultsReady(results: Dataset[OUT]): Unit = badge.text = "Ready" badge.colorScheme = None recalc.isDisabled = Some(false) @@ -57,18 +58,19 @@ abstract class StdSparkCalculation[IN, OUT]( session.render() object SparkCalculation: - class Builder[IN, OUT](name: String, dataUi: UiElement with HasStyle, notifyWhenCalcReady: Seq[Calculation[OUT, _]], calc: IN => OUT)(using + class Builder[IN, OUT](name: String, dataUi: UiElement with HasStyle, notifyWhenCalcReady: Seq[Calculation[Dataset[OUT], _]], calc: IN => Dataset[OUT])(using session: ConnectedSession, executor: FiberExecutor ): - def whenResultsReady(ready: OUT => Unit) = + def whenResultsReady(ready: Dataset[OUT] => Unit) = new StdSparkCalculation[IN, OUT](name = name, dataUi = dataUi, notifyWhenCalcReady): - override protected def calculation(in: IN): OUT = calc(in) - override protected def whenResultsReady(results: OUT): Unit = + override protected def calculation(in: IN): Dataset[OUT] = calc(in) + override protected def whenResultsReady(results: Dataset[OUT]): Unit = ready(results) super.whenResultsReady(results) - def stdSparkCalculation[IN, OUT](name: String, dataUi: UiElement with HasStyle, notifyWhenCalcReady: Calculation[OUT, _]*)(calc: IN => OUT)(using + def stdSparkCalculation[IN, OUT](name: String, dataUi: UiElement with HasStyle, notifyWhenCalcReady: Calculation[Dataset[OUT], _]*)(calc: IN => Dataset[OUT])( + using session: ConnectedSession, executor: FiberExecutor ) = diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StdSparkCalculationTest.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StdSparkCalculationTest.scala index f702063d..fef7f345 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StdSparkCalculationTest.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StdSparkCalculationTest.scala @@ -1,39 +1,51 @@ package org.terminal21.sparklib.steps +import org.apache.spark.sql.Dataset import org.scalatest.concurrent.Eventually.eventually import org.scalatest.funsuite.AnyFunSuiteLike import org.scalatest.matchers.should.Matchers.* import org.terminal21.client.components.chakra.* import org.terminal21.client.{ConnectedSession, ConnectedSessionMock, given} +import org.terminal21.sparklib.SparkSessions import java.util.concurrent.atomic.AtomicBoolean +import scala.util.Using class StdSparkCalculationTest extends AnyFunSuiteLike: - test("whenResultsNotReady"): - given ConnectedSession = ConnectedSessionMock.newConnectedSessionMock - val dataUi = Box() - val called = new AtomicBoolean(false) - val calc = new StdSparkCalculation[Int, Int]("key", "name", dataUi, Nil): - override protected def calculation(in: Int) = in + 1 + test("calculates the correct result"): + Using.resource(SparkSessions.newSparkSession()): spark => + import spark.implicits.* + given ConnectedSession = ConnectedSessionMock.newConnectedSessionMock - override protected def whenResultsNotReady(): Unit = - called.set(true) - super.whenResultsNotReady() + val calc = new StdSparkCalculation[Int, Int]("key", "name", Box(), Nil): + override protected def calculation(in: Int) = Seq(in + 1).toDS - calc.run(1) - called.get() should be(true) + calc.run(1).collect().toList should be(List(2)) - test("whenResultsReady"): - given ConnectedSession = ConnectedSessionMock.newConnectedSessionMock - val dataUi = Box() - val called = new AtomicBoolean(false) - val calc = new StdSparkCalculation[Int, Int]("key", "name", dataUi, Nil): - override protected def calculation(in: Int) = in + 1 - - override protected def whenResultsReady(results: Int): Unit = - called.set(true) - super.whenResultsReady(results) - - calc.run(1) - eventually: + test("whenResultsNotReady"): + Using.resource(SparkSessions.newSparkSession()): spark => + import spark.implicits.* + given ConnectedSession = ConnectedSessionMock.newConnectedSessionMock + val called = new AtomicBoolean(false) + val calc = new StdSparkCalculation[Int, Int]("key", "name", Box(), Nil): + override protected def calculation(in: Int) = Seq(in + 1).toDS + override protected def whenResultsNotReady(): Unit = + called.set(true) + + calc.run(1) called.get() should be(true) + + test("whenResultsReady"): + Using.resource(SparkSessions.newSparkSession()): spark => + import spark.implicits.* + given ConnectedSession = ConnectedSessionMock.newConnectedSessionMock + val called = new AtomicBoolean(false) + val calc = new StdSparkCalculation[Int, Int]("key", "name", Box(), Nil): + override protected def calculation(in: Int) = Seq(in + 1).toDS() + override protected def whenResultsReady(results: Dataset[Int]): Unit = + results.collect().toList should be(List(2)) + called.set(true) + + calc.run(1) + eventually: + called.get() should be(true) From 1ad31a91866f783a39d4a2d1d5eb2eff3437a4a5 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Sat, 6 Jan 2024 01:02:13 +0000 Subject: [PATCH 044/144] - --- .../org/terminal21/sparklib/steps/SparkCalculation.scala | 2 +- .../org/terminal21/sparklib/endtoend/SparkBasics.scala | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala index d4397af2..97136c60 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala @@ -69,7 +69,7 @@ object SparkCalculation: ready(results) super.whenResultsReady(results) - def stdSparkCalculation[IN, OUT](name: String, dataUi: UiElement with HasStyle, notifyWhenCalcReady: Calculation[Dataset[OUT], _]*)(calc: IN => Dataset[OUT])( + def sparkCalculation[IN, OUT](name: String, dataUi: UiElement with HasStyle, notifyWhenCalcReady: Calculation[Dataset[OUT], _]*)(calc: IN => Dataset[OUT])( using session: ConnectedSession, executor: FiberExecutor diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index ba91dcf4..227cac81 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -7,7 +7,7 @@ import org.terminal21.client.{*, given} import org.terminal21.sparklib.SparkSessions import org.terminal21.sparklib.endtoend.model.CodeFile import org.terminal21.sparklib.endtoend.model.CodeFile.createDatasetFromProjectsSourceFiles -import org.terminal21.sparklib.steps.SparkCalculation.stdSparkCalculation +import org.terminal21.sparklib.steps.SparkCalculation.sparkCalculation import org.terminal21.sparklib.steps.{SparkCalculation, StdSparkCalculation} @main def sparkBasics(): Unit = @@ -21,12 +21,12 @@ import org.terminal21.sparklib.steps.{SparkCalculation, StdSparkCalculation} val headers = Seq("id", "name", "path", "numOfLines", "numOfWords", "createdDate") val sortedFilesTable = QuickTable.quickTable().withStringHeaders(headers: _*).build - val sortedCalc = stdSparkCalculation("Sorted files", sortedFilesTable)(sortedSourceFiles) + val sortedCalc = sparkCalculation("Sorted files", sortedFilesTable)(sortedSourceFiles) .whenResultsReady: results => sortedFilesTable.withRowStringData(results.take(10).toList.map(_.toData)) val codeFilesTable = QuickTable.quickTable().withStringHeaders(headers: _*).build - val codeFilesCalculation = stdSparkCalculation("Code files", codeFilesTable, sortedCalc): _ => + val codeFilesCalculation = sparkCalculation("Code files", codeFilesTable, sortedCalc): _ => createDatasetFromProjectsSourceFiles.toDS .whenResultsReady: results => val dt = results.take(10).toList From 88341137754cc9e8cb3efe33796be16d8ab97549 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Sat, 6 Jan 2024 01:37:46 +0000 Subject: [PATCH 045/144] - --- .../sparklib/endtoend/SparkBasics.scala | 8 +- .../terminal21/client/ConnectedSession.scala | 4 +- .../client/components/UiComponent.scala | 2 + .../client/components/UiElementEncoding.scala | 5 +- .../client/components/chakra/QuickTable.scala | 81 +++++++++---------- .../client/ConnectedSessionTest.scala | 14 +--- 6 files changed, 51 insertions(+), 63 deletions(-) diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index 227cac81..1e59dd30 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -20,17 +20,17 @@ import org.terminal21.sparklib.steps.{SparkCalculation, StdSparkCalculation} val headers = Seq("id", "name", "path", "numOfLines", "numOfWords", "createdDate") - val sortedFilesTable = QuickTable.quickTable().withStringHeaders(headers: _*).build + val sortedFilesTable = QuickTable().headers(headers: _*).caption("Files sorted by createdDate and numOfWords") val sortedCalc = sparkCalculation("Sorted files", sortedFilesTable)(sortedSourceFiles) .whenResultsReady: results => - sortedFilesTable.withRowStringData(results.take(10).toList.map(_.toData)) + sortedFilesTable.rows(results.take(10).toList.map(_.toData)) - val codeFilesTable = QuickTable.quickTable().withStringHeaders(headers: _*).build + val codeFilesTable = QuickTable().headers(headers: _*).caption("Unsorted files") val codeFilesCalculation = sparkCalculation("Code files", codeFilesTable, sortedCalc): _ => createDatasetFromProjectsSourceFiles.toDS .whenResultsReady: results => val dt = results.take(10).toList - codeFilesTable.withRowStringData(dt.map(_.toData)) + codeFilesTable.rows(dt.map(_.toData)) Seq( codeFilesCalculation, diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala index 6bf23074..ad9c1e83 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala @@ -28,9 +28,7 @@ class ConnectedSession(val session: Session, val serverUrl: String, sessionsServ for e <- withEvents do addEventHandlerAtTheTop(e.key, e.defaultEventHandler) synchronized: - elements = elements ::: es.toList.flatMap: - case c: UiComponent => c.children - case e => Seq(e) + elements = elements ::: es.toList private val eventHandlers = collection.concurrent.TrieMap.empty[String, List[EventHandler]] diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala index 4eb8ef70..bf121453 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiComponent.scala @@ -2,4 +2,6 @@ package org.terminal21.client.components import org.terminal21.client.components.UiElement.HasChildren +/** A UiComponent is a UI element that is composed of a seq of other ui elements + */ trait UiComponent extends UiElement with HasChildren[UiComponent] diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElementEncoding.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElementEncoding.scala index 6ea4a877..1161256c 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElementEncoding.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElementEncoding.scala @@ -3,9 +3,12 @@ package org.terminal21.client.components import io.circe.* import io.circe.generic.auto.* import io.circe.syntax.* -import org.terminal21.client.components.chakra.ChakraElement +import org.terminal21.client.components.chakra.{Box, ChakraElement} object UiElementEncoding: given uiElementEncoder: Encoder[UiElement] = case std: StdElement => std.asJson.mapObject(o => o.add("type", "Std".asJson)) case c: ChakraElement => c.asJson.mapObject(o => o.add("type", "Chakra".asJson)) + case c: UiComponent => + val b: ChakraElement = Box(children = c.children) + b.asJson.mapObject(o => o.add("type", "Chakra".asJson)) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala index 5cf8784e..3a62181e 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala @@ -1,44 +1,41 @@ package org.terminal21.client.components.chakra -import org.terminal21.client.components.UiElement - -object QuickTable: - case class QuickTableBuilder( - headers: Seq[UiElement], - data: Seq[Seq[UiElement]], - caption: Option[String] = None, - variant: String = "striped", - colorScheme: String = "teal", - size: String = "mg" - ): - def withHeaders(hs: Seq[UiElement]): QuickTableBuilder = copy(headers = hs) - def withStringHeaders(hs: String*): QuickTableBuilder = copy(headers = hs.map(h => Text(text = h))) - - def withRowData(dt: Seq[Seq[UiElement]]): QuickTableBuilder = copy(data = dt) - def withRowStringData(dt: Seq[Seq[String]]): QuickTableBuilder = copy(data = dt.map(row => row.map(c => Text(text = c)))) - - def build: TableContainer = - val hs = headers.map: h => - Th().withChildren(h) - TableContainer().withChildren( - Table(variant = variant, colorScheme = Some(colorScheme), size = size) - .withChildren( - Thead().withChildren( - Tr().withChildren(hs: _*) - ), - Tbody( - children = data.map: row => - Tr( - children = row.map: column => - Td().withChildren(column) - ) - ) - ) - .addChildren(caption.map(c => TableCaption(text = c)).toList: _*) - ) - def quickTable( - caption: Option[String] = None, - variant: String = "striped", - colorScheme: String = "teal", - size: String = "mg" - ) = QuickTableBuilder(Nil, Nil, caption, variant, colorScheme, size) +import org.terminal21.client.components.UiElement.HasStyle +import org.terminal21.client.components.{Keys, UiComponent, UiElement} + +case class QuickTable( + key: String = Keys.nextKey, + variant: String = "striped", + colorScheme: String = "teal", + size: String = "mg" +) extends UiComponent + with HasStyle: + val head = Thead() + val body = Tbody() + + val table = Table(variant = variant, colorScheme = Some(colorScheme), size = size) + .withChildren( + head, + body + ) + @volatile var tableContainer = TableContainer().withChildren(table) + @volatile var children: Seq[UiElement] = Seq(tableContainer) + + def headers(headers: String*): QuickTable = headersElements(headers.map(h => Text(text = h)): _*) + def headersElements(headers: UiElement*): QuickTable = + head.children = headers.map(h => Th(children = Seq(h))) + this + + def rows(data: Seq[Seq[String]]): QuickTable = rowsElements(data.map(_.map(c => Text(text = c)))) + + def rowsElements(data: Seq[Seq[UiElement]]): QuickTable = + body.children = data.map: row => + Tr(children = row.map(c => Td().withChildren(c))) + this + + def style: Map[String, String] = tableContainer.style + def style_=(s: Map[String, String]): Unit = tableContainer.style = s + + def caption(text: String): QuickTable = + table.addChildren(TableCaption(text = text)) + this diff --git a/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionTest.scala b/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionTest.scala index c1b7c4c2..42706244 100644 --- a/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionTest.scala +++ b/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionTest.scala @@ -2,8 +2,7 @@ package org.terminal21.client import org.scalatest.funsuite.AnyFunSuiteLike import org.scalatest.matchers.should.Matchers.* -import org.terminal21.client.components.UiComponent -import org.terminal21.client.components.chakra.{Box, Editable} +import org.terminal21.client.components.chakra.Editable import org.terminal21.model.OnChange class ConnectedSessionTest extends AnyFunSuiteLike: @@ -15,14 +14,3 @@ class ConnectedSessionTest extends AnyFunSuiteLike: connectedSession.add(editable) connectedSession.fireEvent(OnChange(editable.key, "new value")) - - test("components have their children added"): - given session: ConnectedSession = ConnectedSessionMock.newConnectedSessionMock - - val box = Box(key = "1") - val component = new UiComponent { - var children = Seq(box) - override def key = "2" - } - session.add(component) - session.allElements should be(Seq(box)) From 156876a0e90ff54e1c049fae69dc2914ece20d6a Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Sun, 7 Jan 2024 15:55:54 +0000 Subject: [PATCH 046/144] - --- .../sparklib/steps/SparkCalculation.scala | 54 ++++++++++++++----- .../steps/StdSparkCalculationTest.scala | 34 +++++++++--- .../terminal21/client/ConnectedSession.scala | 2 +- 3 files changed, 71 insertions(+), 19 deletions(-) diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala index 97136c60..24dbeae4 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala @@ -1,11 +1,15 @@ package org.terminal21.sparklib.steps import functions.fibers.FiberExecutor -import org.apache.spark.sql.Dataset +import org.apache.commons.io.FileUtils +import org.apache.spark.sql.{Dataset, Encoder, SparkSession} import org.terminal21.client.components.UiElement.HasStyle import org.terminal21.client.{Calculation, ConnectedSession} import org.terminal21.client.components.chakra.{Badge, Box, Button, HStack, RepeatIcon, Text} import org.terminal21.client.components.{Keys, UiComponent, UiElement} +import org.terminal21.sparklib.util.Environment + +import java.io.File abstract class SparkCalculation[IN, OUT]( val key: String = Keys.nextKey, @@ -15,12 +19,12 @@ abstract class SparkCalculation[IN, OUT]( extends Calculation[IN, Dataset[OUT]](notifyWhenCalcReady) with UiComponent -abstract class StdSparkCalculation[IN, OUT]( - key: String = Keys.nextKey, +abstract class StdSparkCalculation[IN, OUT: Encoder]( name: String, dataUi: UiElement with HasStyle, - notifyWhenCalcReady: Seq[Calculation[Dataset[OUT], _]] -)(using session: ConnectedSession, executor: FiberExecutor) + notifyWhenCalcReady: Seq[Calculation[Dataset[OUT], _]], + key: String = Keys.nextKey +)(using session: ConnectedSession, executor: FiberExecutor, spark: SparkSession) extends SparkCalculation[IN, OUT](key, Nil, notifyWhenCalcReady): val badge = Badge() val recalc = Button(text = "Recalculate", size = Some("sm"), leftIcon = Some(RepeatIcon())).onClick: () => @@ -37,10 +41,28 @@ abstract class StdSparkCalculation[IN, OUT]( dataUi ) + private val rootFolder = s"${Environment.tmpDirectory}/steps/$name" + private val targetDir = s"$rootFolder/$name" + private def cache[A](reader: => A, writer: => A): A = + if new File(targetDir).exists() then reader + else writer + + def invalidateCache(): Unit = + FileUtils.deleteDirectory(new File(targetDir)) + + private def calculateOnce(f: => Dataset[OUT]): Dataset[OUT] = + cache( + spark.read.parquet(targetDir).as[OUT], { + val ds = f + ds.write.parquet(targetDir) + ds + } + ) + private var in: Option[IN] = None override def run(in: IN) = this.in = Some(in) - super.run(in) + calculateOnce(super.run(in)) override protected def whenResultsNotReady(): Unit = badge.text = "Calculating" @@ -58,20 +80,28 @@ abstract class StdSparkCalculation[IN, OUT]( session.render() object SparkCalculation: - class Builder[IN, OUT](name: String, dataUi: UiElement with HasStyle, notifyWhenCalcReady: Seq[Calculation[Dataset[OUT], _]], calc: IN => Dataset[OUT])(using + class Builder[IN, OUT: Encoder]( + name: String, + dataUi: UiElement with HasStyle, + notifyWhenCalcReady: Seq[Calculation[Dataset[OUT], _]], + calc: IN => Dataset[OUT] + )(using session: ConnectedSession, - executor: FiberExecutor + executor: FiberExecutor, + spark: SparkSession ): def whenResultsReady(ready: Dataset[OUT] => Unit) = - new StdSparkCalculation[IN, OUT](name = name, dataUi = dataUi, notifyWhenCalcReady): + new StdSparkCalculation[IN, OUT](name, dataUi, notifyWhenCalcReady): override protected def calculation(in: IN): Dataset[OUT] = calc(in) override protected def whenResultsReady(results: Dataset[OUT]): Unit = ready(results) super.whenResultsReady(results) - def sparkCalculation[IN, OUT](name: String, dataUi: UiElement with HasStyle, notifyWhenCalcReady: Calculation[Dataset[OUT], _]*)(calc: IN => Dataset[OUT])( - using + def sparkCalculation[IN, OUT: Encoder](name: String, dataUi: UiElement with HasStyle, notifyWhenCalcReady: Calculation[Dataset[OUT], _]*)( + calc: IN => Dataset[OUT] + )(using session: ConnectedSession, - executor: FiberExecutor + executor: FiberExecutor, + spark: SparkSession ) = new Builder[IN, OUT](name, dataUi, notifyWhenCalcReady, calc) diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StdSparkCalculationTest.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StdSparkCalculationTest.scala index fef7f345..6e9ca47f 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StdSparkCalculationTest.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StdSparkCalculationTest.scala @@ -1,6 +1,6 @@ package org.terminal21.sparklib.steps -import org.apache.spark.sql.Dataset +import org.apache.spark.sql.{Dataset, SparkSession} import org.scalatest.concurrent.Eventually.eventually import org.scalatest.funsuite.AnyFunSuiteLike import org.scalatest.matchers.should.Matchers.* @@ -8,7 +8,7 @@ import org.terminal21.client.components.chakra.* import org.terminal21.client.{ConnectedSession, ConnectedSessionMock, given} import org.terminal21.sparklib.SparkSessions -import java.util.concurrent.atomic.AtomicBoolean +import java.util.concurrent.atomic.{AtomicBoolean, AtomicInteger} import scala.util.Using class StdSparkCalculationTest extends AnyFunSuiteLike: @@ -16,22 +16,25 @@ class StdSparkCalculationTest extends AnyFunSuiteLike: Using.resource(SparkSessions.newSparkSession()): spark => import spark.implicits.* given ConnectedSession = ConnectedSessionMock.newConnectedSessionMock + given SparkSession = spark - val calc = new StdSparkCalculation[Int, Int]("key", "name", Box(), Nil): + val calc = new StdSparkCalculation[Int, Int]("name", Box(), Nil): override protected def calculation(in: Int) = Seq(in + 1).toDS + calc.invalidateCache() calc.run(1).collect().toList should be(List(2)) test("whenResultsNotReady"): Using.resource(SparkSessions.newSparkSession()): spark => import spark.implicits.* given ConnectedSession = ConnectedSessionMock.newConnectedSessionMock + given SparkSession = spark val called = new AtomicBoolean(false) - val calc = new StdSparkCalculation[Int, Int]("key", "name", Box(), Nil): + val calc = new StdSparkCalculation[Int, Int]("name", Box(), Nil): override protected def calculation(in: Int) = Seq(in + 1).toDS override protected def whenResultsNotReady(): Unit = called.set(true) - + calc.invalidateCache() calc.run(1) called.get() should be(true) @@ -39,13 +42,32 @@ class StdSparkCalculationTest extends AnyFunSuiteLike: Using.resource(SparkSessions.newSparkSession()): spark => import spark.implicits.* given ConnectedSession = ConnectedSessionMock.newConnectedSessionMock + given SparkSession = spark val called = new AtomicBoolean(false) - val calc = new StdSparkCalculation[Int, Int]("key", "name", Box(), Nil): + val calc = new StdSparkCalculation[Int, Int]("name", Box(), Nil): override protected def calculation(in: Int) = Seq(in + 1).toDS() override protected def whenResultsReady(results: Dataset[Int]): Unit = results.collect().toList should be(List(2)) called.set(true) + calc.invalidateCache() calc.run(1) eventually: called.get() should be(true) + + test("caches results"): + Using.resource(SparkSessions.newSparkSession()): spark => + import spark.implicits.* + given ConnectedSession = ConnectedSessionMock.newConnectedSessionMock + given SparkSession = spark + + val called = new AtomicInteger(0) + val calc = new StdSparkCalculation[Int, Int]("name", Box(), Nil): + override protected def calculation(in: Int) = + called.incrementAndGet() + Seq(in + 1).toDS + + calc.invalidateCache() + calc.run(1).collect().toList should be(List(2)) + calc.run(1).collect().toList should be(List(2)) + called.get() should be(1) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala index ad9c1e83..2be63bf0 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala @@ -4,7 +4,7 @@ import io.circe.* import io.circe.generic.auto.* import io.circe.syntax.* import org.slf4j.LoggerFactory -import org.terminal21.client.components.{UiComponent, UiElement} +import org.terminal21.client.components.UiElement import org.terminal21.client.components.UiElement.{HasEventHandler, allDeep} import org.terminal21.client.components.UiElementEncoding.uiElementEncoder import org.terminal21.model.* From f2878271a37c93172701a351edcac422e0304f31 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Sun, 7 Jan 2024 16:07:19 +0000 Subject: [PATCH 047/144] - --- .../sparklib/steps/SparkCalculation.scala | 3 ++ .../org/terminal21/sparklib/steps/Steps.scala | 40 ------------------- .../steps/StdSparkCalculationTest.scala | 35 ++++++++-------- 3 files changed, 19 insertions(+), 59 deletions(-) delete mode 100644 terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/Steps.scala diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala index 24dbeae4..0c176c63 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala @@ -28,6 +28,9 @@ abstract class StdSparkCalculation[IN, OUT: Encoder]( extends SparkCalculation[IN, OUT](key, Nil, notifyWhenCalcReady): val badge = Badge() val recalc = Button(text = "Recalculate", size = Some("sm"), leftIcon = Some(RepeatIcon())).onClick: () => + badge.text = "Invalidating cache ..." + session.render() + invalidateCache() for i <- in do run(i) children = Seq( diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/Steps.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/Steps.scala deleted file mode 100644 index f9b72edd..00000000 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/Steps.scala +++ /dev/null @@ -1,40 +0,0 @@ -package org.terminal21.sparklib.steps - -import org.apache.commons.io.FileUtils -import org.apache.spark.sql.{DataFrame, Dataset, Encoder, SparkSession} -import org.terminal21.client.Calculation -import org.terminal21.client.components.UiElement -import org.terminal21.sparklib.util.Environment - -import java.io.File - -class Steps(spark: SparkSession, name: String, calculations: Seq[Calculation[_, _]]): - private val rootFolder = s"${Environment.tmpDirectory}/steps/$name" - -//class Step(spark: SparkSession, rootFolder: String, name: String, ui: Seq[UiElement]): -// val targetDir = s"$rootFolder/$name" -// -// def invalidateCache(): Unit = -// FileUtils.deleteDirectory(new File(targetDir)) -// -// private def cache[A](reader: => A, writer: => A): A = -// if new File(targetDir).exists() then reader -// else writer -// -// def calculateOnce(f: => DataFrame): DataFrame = -// cache( -// spark.read.parquet(targetDir), { -// val df = f -// df.write.parquet(targetDir) -// df -// } -// ) -// -// def calculateOnce[A: Encoder](f: => Dataset[A]): Dataset[A] = -// cache( -// spark.read.parquet(targetDir).as[A], { -// val ds = f -// ds.write.parquet(targetDir) -// ds -// } -// ) diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StdSparkCalculationTest.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StdSparkCalculationTest.scala index 6e9ca47f..2b7dd62d 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StdSparkCalculationTest.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StdSparkCalculationTest.scala @@ -1,6 +1,7 @@ package org.terminal21.sparklib.steps -import org.apache.spark.sql.{Dataset, SparkSession} +import functions.fibers.FiberExecutor +import org.apache.spark.sql.{Dataset, Encoder, SparkSession} import org.scalatest.concurrent.Eventually.eventually import org.scalatest.funsuite.AnyFunSuiteLike import org.scalatest.matchers.should.Matchers.* @@ -17,11 +18,7 @@ class StdSparkCalculationTest extends AnyFunSuiteLike: import spark.implicits.* given ConnectedSession = ConnectedSessionMock.newConnectedSessionMock given SparkSession = spark - - val calc = new StdSparkCalculation[Int, Int]("name", Box(), Nil): - override protected def calculation(in: Int) = Seq(in + 1).toDS - - calc.invalidateCache() + val calc = new TestingCalculation calc.run(1).collect().toList should be(List(2)) test("whenResultsNotReady"): @@ -30,11 +27,9 @@ class StdSparkCalculationTest extends AnyFunSuiteLike: given ConnectedSession = ConnectedSessionMock.newConnectedSessionMock given SparkSession = spark val called = new AtomicBoolean(false) - val calc = new StdSparkCalculation[Int, Int]("name", Box(), Nil): - override protected def calculation(in: Int) = Seq(in + 1).toDS + val calc = new TestingCalculation: override protected def whenResultsNotReady(): Unit = called.set(true) - calc.invalidateCache() calc.run(1) called.get() should be(true) @@ -44,13 +39,11 @@ class StdSparkCalculationTest extends AnyFunSuiteLike: given ConnectedSession = ConnectedSessionMock.newConnectedSessionMock given SparkSession = spark val called = new AtomicBoolean(false) - val calc = new StdSparkCalculation[Int, Int]("name", Box(), Nil): - override protected def calculation(in: Int) = Seq(in + 1).toDS() + val calc = new TestingCalculation: override protected def whenResultsReady(results: Dataset[Int]): Unit = results.collect().toList should be(List(2)) called.set(true) - calc.invalidateCache() calc.run(1) eventually: called.get() should be(true) @@ -61,13 +54,17 @@ class StdSparkCalculationTest extends AnyFunSuiteLike: given ConnectedSession = ConnectedSessionMock.newConnectedSessionMock given SparkSession = spark - val called = new AtomicInteger(0) - val calc = new StdSparkCalculation[Int, Int]("name", Box(), Nil): - override protected def calculation(in: Int) = - called.incrementAndGet() - Seq(in + 1).toDS + val calc = new TestingCalculation - calc.invalidateCache() calc.run(1).collect().toList should be(List(2)) calc.run(1).collect().toList should be(List(2)) - called.get() should be(1) + calc.calcCalledTimes.get() should be(1) + +class TestingCalculation(using session: ConnectedSession, executor: FiberExecutor, spark: SparkSession, intEncoder: Encoder[Int]) + extends StdSparkCalculation[Int, Int]("testing-calc", Box(), Nil): + val calcCalledTimes = new AtomicInteger(0) + invalidateCache() + override protected def calculation(in: Int) = + import spark.implicits.* + calcCalledTimes.incrementAndGet() + Seq(in + 1).toDS From 24e19697a266e6677a7a9115ea172681eadacdde Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Sun, 7 Jan 2024 16:13:42 +0000 Subject: [PATCH 048/144] - --- .../sparklib/steps/SparkCalculation.scala | 2 +- .../sparklib/steps/StdSparkCalculationTest.scala | 14 +++++++++++--- .../org/terminal21/client/ConnectedSession.scala | 2 ++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala index 0c176c63..f4f5b063 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala @@ -44,7 +44,7 @@ abstract class StdSparkCalculation[IN, OUT: Encoder]( dataUi ) - private val rootFolder = s"${Environment.tmpDirectory}/steps/$name" + private val rootFolder = s"${Environment.tmpDirectory}/spark-calculations/$name" private val targetDir = s"$rootFolder/$name" private def cache[A](reader: => A, writer: => A): A = if new File(targetDir).exists() then reader diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StdSparkCalculationTest.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StdSparkCalculationTest.scala index 2b7dd62d..90799ce0 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StdSparkCalculationTest.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StdSparkCalculationTest.scala @@ -53,13 +53,21 @@ class StdSparkCalculationTest extends AnyFunSuiteLike: import spark.implicits.* given ConnectedSession = ConnectedSessionMock.newConnectedSessionMock given SparkSession = spark - - val calc = new TestingCalculation - + val calc = new TestingCalculation calc.run(1).collect().toList should be(List(2)) calc.run(1).collect().toList should be(List(2)) calc.calcCalledTimes.get() should be(1) + test("refresh button invalidates cache and runs calculations"): + Using.resource(SparkSessions.newSparkSession()): spark => + import spark.implicits.* + given session: ConnectedSession = ConnectedSessionMock.newConnectedSessionMock + given SparkSession = spark + val calc = new TestingCalculation + calc.run(1).collect().toList should be(List(2)) + session.click(calc.recalc) + calc.calcCalledTimes.get() should be(2) + class TestingCalculation(using session: ConnectedSession, executor: FiberExecutor, spark: SparkSession, intEncoder: Encoder[Int]) extends StdSparkCalculation[Int, Int]("testing-calc", Box(), Nil): val calcCalledTimes = new AtomicInteger(0) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala index 2be63bf0..e8722f5c 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala @@ -61,6 +61,8 @@ class ConnectedSession(val session: Session, val serverUrl: String, sessionsServ */ def isClosed: Boolean = exitLatch.getCount == 0 + def click(e: UiElement): Unit = fireEvent(OnClick(e.key)) + private[client] def fireEvent(event: CommandEvent): Unit = event match case SessionClosed(_) => From ce669f3b426f00240adc235f5364c95beacc0fe1 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Sun, 7 Jan 2024 18:17:44 +0000 Subject: [PATCH 049/144] - --- .../sparklib/steps/SparkCalculation.scala | 8 +++++-- .../steps/StdSparkCalculationTest.scala | 23 +++++++++++++++++-- .../org/terminal21/client/Calculation.scala | 17 ++++++++------ 3 files changed, 37 insertions(+), 11 deletions(-) diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala index f4f5b063..5a7e2430 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala @@ -46,8 +46,9 @@ abstract class StdSparkCalculation[IN, OUT: Encoder]( private val rootFolder = s"${Environment.tmpDirectory}/spark-calculations/$name" private val targetDir = s"$rootFolder/$name" + def isCached: Boolean = new File(targetDir).exists() private def cache[A](reader: => A, writer: => A): A = - if new File(targetDir).exists() then reader + if isCached then reader else writer def invalidateCache(): Unit = @@ -65,7 +66,10 @@ abstract class StdSparkCalculation[IN, OUT: Encoder]( private var in: Option[IN] = None override def run(in: IN) = this.in = Some(in) - calculateOnce(super.run(in)) + val isC = isCached + val out = calculateOnce(super.run(in)) + if isC then postRun(out) + out override protected def whenResultsNotReady(): Unit = badge.text = "Calculating" diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StdSparkCalculationTest.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StdSparkCalculationTest.scala index 90799ce0..5bddf171 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StdSparkCalculationTest.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StdSparkCalculationTest.scala @@ -2,9 +2,10 @@ package org.terminal21.sparklib.steps import functions.fibers.FiberExecutor import org.apache.spark.sql.{Dataset, Encoder, SparkSession} -import org.scalatest.concurrent.Eventually.eventually +import org.scalatest.concurrent.Eventually import org.scalatest.funsuite.AnyFunSuiteLike import org.scalatest.matchers.should.Matchers.* +import org.scalatest.time.{Millis, Span} import org.terminal21.client.components.chakra.* import org.terminal21.client.{ConnectedSession, ConnectedSessionMock, given} import org.terminal21.sparklib.SparkSessions @@ -12,7 +13,9 @@ import org.terminal21.sparklib.SparkSessions import java.util.concurrent.atomic.{AtomicBoolean, AtomicInteger} import scala.util.Using -class StdSparkCalculationTest extends AnyFunSuiteLike: +class StdSparkCalculationTest extends AnyFunSuiteLike with Eventually: + given PatienceConfig = PatienceConfig(scaled(Span(3000, Millis))) + test("calculates the correct result"): Using.resource(SparkSessions.newSparkSession()): spark => import spark.implicits.* @@ -48,6 +51,22 @@ class StdSparkCalculationTest extends AnyFunSuiteLike: eventually: called.get() should be(true) + test("whenResultsReady called even when cached"): + Using.resource(SparkSessions.newSparkSession()): spark => + import spark.implicits.* + given ConnectedSession = ConnectedSessionMock.newConnectedSessionMock + given SparkSession = spark + val called = new AtomicInteger(0) + val calc = new TestingCalculation: + override protected def whenResultsReady(results: Dataset[Int]): Unit = + results.collect().toList should be(List(2)) + called.incrementAndGet() + + calc.run(1) + calc.run(1) + eventually: + called.get() should be(2) + test("caches results"): Using.resource(SparkSessions.newSparkSession()): spark => import spark.implicits.* diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala index 01097780..040953cf 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala @@ -17,16 +17,19 @@ abstract class Calculation[IN, OUT]( whenResultsNotReady() val out = calculation(in) - executor.submit: - whenResultsReady(out) - - for c <- notifyWhenCalcReady do - executor.submit: - try c.run(out) - catch case t: Throwable => t.printStackTrace() + postRun(out) out f.get() + def postRun(out: OUT): Unit = + executor.submit: + whenResultsReady(out) + + for c <- notifyWhenCalcReady do + executor.submit: + try c.run(out) + catch case t: Throwable => t.printStackTrace() + object Calculation: class Builder[IN, OUT]( calc: IN => OUT, From cadc37e7cfc399e35622d96a44b35afeaa3b6867 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Sun, 7 Jan 2024 18:21:19 +0000 Subject: [PATCH 050/144] - --- .../org/terminal21/sparklib/steps/SparkCalculation.scala | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala index 5a7e2430..c70503cf 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala @@ -22,7 +22,7 @@ abstract class SparkCalculation[IN, OUT]( abstract class StdSparkCalculation[IN, OUT: Encoder]( name: String, dataUi: UiElement with HasStyle, - notifyWhenCalcReady: Seq[Calculation[Dataset[OUT], _]], + notifyWhenCalcReady: Seq[StdSparkCalculation[Dataset[OUT], _]], key: String = Keys.nextKey )(using session: ConnectedSession, executor: FiberExecutor, spark: SparkSession) extends SparkCalculation[IN, OUT](key, Nil, notifyWhenCalcReady): @@ -53,6 +53,7 @@ abstract class StdSparkCalculation[IN, OUT: Encoder]( def invalidateCache(): Unit = FileUtils.deleteDirectory(new File(targetDir)) + for n <- notifyWhenCalcReady do n.invalidateCache() private def calculateOnce(f: => Dataset[OUT]): Dataset[OUT] = cache( @@ -90,7 +91,7 @@ object SparkCalculation: class Builder[IN, OUT: Encoder]( name: String, dataUi: UiElement with HasStyle, - notifyWhenCalcReady: Seq[Calculation[Dataset[OUT], _]], + notifyWhenCalcReady: Seq[StdSparkCalculation[Dataset[OUT], _]], calc: IN => Dataset[OUT] )(using session: ConnectedSession, @@ -104,7 +105,7 @@ object SparkCalculation: ready(results) super.whenResultsReady(results) - def sparkCalculation[IN, OUT: Encoder](name: String, dataUi: UiElement with HasStyle, notifyWhenCalcReady: Calculation[Dataset[OUT], _]*)( + def sparkCalculation[IN, OUT: Encoder](name: String, dataUi: UiElement with HasStyle, notifyWhenCalcReady: StdSparkCalculation[Dataset[OUT], _]*)( calc: IN => Dataset[OUT] )(using session: ConnectedSession, From 8b0cafc880557ac083e4b819bb54d166c0c04a81 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Sun, 7 Jan 2024 20:53:38 +0000 Subject: [PATCH 051/144] - --- .../sparklib/steps/SparkCalculation.scala | 81 ++++++++++--------- .../sparklib/endtoend/SparkBasics.scala | 2 +- .../sparklib/endtoend/model/CodeFile.scala | 4 +- ....scala => StdUiSparkCalculationTest.scala} | 4 +- .../terminal21/client/CachedCalculation.scala | 10 +++ 5 files changed, 58 insertions(+), 43 deletions(-) rename terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/{StdSparkCalculationTest.scala => StdUiSparkCalculationTest.scala} (96%) create mode 100644 terminal21-ui-std/src/main/scala/org/terminal21/client/CachedCalculation.scala diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala index c70503cf..846bc63a 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala @@ -4,56 +4,35 @@ import functions.fibers.FiberExecutor import org.apache.commons.io.FileUtils import org.apache.spark.sql.{Dataset, Encoder, SparkSession} import org.terminal21.client.components.UiElement.HasStyle -import org.terminal21.client.{Calculation, ConnectedSession} +import org.terminal21.client.{CachedCalculation, Calculation, ConnectedSession} import org.terminal21.client.components.chakra.{Badge, Box, Button, HStack, RepeatIcon, Text} import org.terminal21.client.components.{Keys, UiComponent, UiElement} import org.terminal21.sparklib.util.Environment import java.io.File -abstract class SparkCalculation[IN, OUT]( +abstract class SparkCalculation[IN, OUT: Encoder]( val key: String = Keys.nextKey, + name: String, @volatile var children: Seq[UiElement], notifyWhenCalcReady: Seq[Calculation[Dataset[OUT], _]] -)(using executor: FiberExecutor) - extends Calculation[IN, Dataset[OUT]](notifyWhenCalcReady) - with UiComponent +)(using executor: FiberExecutor, spark: SparkSession) + extends CachedCalculation[IN, Dataset[OUT]](notifyWhenCalcReady) + with UiComponent: + private val rootFolder = s"${Environment.tmpDirectory}/spark-calculations/$name" + private val targetDir = s"$rootFolder/$name" -abstract class StdSparkCalculation[IN, OUT: Encoder]( - name: String, - dataUi: UiElement with HasStyle, - notifyWhenCalcReady: Seq[StdSparkCalculation[Dataset[OUT], _]], - key: String = Keys.nextKey -)(using session: ConnectedSession, executor: FiberExecutor, spark: SparkSession) - extends SparkCalculation[IN, OUT](key, Nil, notifyWhenCalcReady): - val badge = Badge() - val recalc = Button(text = "Recalculate", size = Some("sm"), leftIcon = Some(RepeatIcon())).onClick: () => - badge.text = "Invalidating cache ..." - session.render() - invalidateCache() - for i <- in do run(i) + def isCached: Boolean = new File(targetDir).exists() - children = Seq( - Box(bg = "green", p = 4).withChildren( - HStack().withChildren( - Text(text = name), - badge, - recalc - ) - ), - dataUi - ) - - private val rootFolder = s"${Environment.tmpDirectory}/spark-calculations/$name" - private val targetDir = s"$rootFolder/$name" - def isCached: Boolean = new File(targetDir).exists() private def cache[A](reader: => A, writer: => A): A = if isCached then reader else writer def invalidateCache(): Unit = FileUtils.deleteDirectory(new File(targetDir)) - for n <- notifyWhenCalcReady do n.invalidateCache() + val ccs = notifyWhenCalcReady.collect: + case cc: CachedCalculation[_, _] => cc + for n <- ccs do n.invalidateCache() private def calculateOnce(f: => Dataset[OUT]): Dataset[OUT] = cache( @@ -64,14 +43,40 @@ abstract class StdSparkCalculation[IN, OUT: Encoder]( } ) - private var in: Option[IN] = None - override def run(in: IN) = + protected var in: Option[IN] = None + + override def run(in: IN) = this.in = Some(in) val isC = isCached val out = calculateOnce(super.run(in)) if isC then postRun(out) out +abstract class StdUiSparkCalculation[IN, OUT: Encoder]( + name: String, + dataUi: UiElement with HasStyle, + notifyWhenCalcReady: Seq[Calculation[Dataset[OUT], _]], + key: String = Keys.nextKey +)(using session: ConnectedSession, executor: FiberExecutor, spark: SparkSession) + extends SparkCalculation[IN, OUT](name, key, Nil, notifyWhenCalcReady): + val badge = Badge() + val recalc = Button(text = "Recalculate", size = Some("sm"), leftIcon = Some(RepeatIcon())).onClick: () => + badge.text = "Invalidating cache ..." + session.render() + invalidateCache() + for i <- in do run(i) + + children = Seq( + Box(bg = "green", p = 4).withChildren( + HStack().withChildren( + Text(text = name), + badge, + recalc + ) + ), + dataUi + ) + override protected def whenResultsNotReady(): Unit = badge.text = "Calculating" badge.colorScheme = Some("purple") @@ -91,7 +96,7 @@ object SparkCalculation: class Builder[IN, OUT: Encoder]( name: String, dataUi: UiElement with HasStyle, - notifyWhenCalcReady: Seq[StdSparkCalculation[Dataset[OUT], _]], + notifyWhenCalcReady: Seq[Calculation[Dataset[OUT], _]], calc: IN => Dataset[OUT] )(using session: ConnectedSession, @@ -99,13 +104,13 @@ object SparkCalculation: spark: SparkSession ): def whenResultsReady(ready: Dataset[OUT] => Unit) = - new StdSparkCalculation[IN, OUT](name, dataUi, notifyWhenCalcReady): + new StdUiSparkCalculation[IN, OUT](name, dataUi, notifyWhenCalcReady): override protected def calculation(in: IN): Dataset[OUT] = calc(in) override protected def whenResultsReady(results: Dataset[OUT]): Unit = ready(results) super.whenResultsReady(results) - def sparkCalculation[IN, OUT: Encoder](name: String, dataUi: UiElement with HasStyle, notifyWhenCalcReady: StdSparkCalculation[Dataset[OUT], _]*)( + def sparkCalculation[IN, OUT: Encoder](name: String, dataUi: UiElement with HasStyle, notifyWhenCalcReady: Calculation[Dataset[OUT], _]*)( calc: IN => Dataset[OUT] )(using session: ConnectedSession, diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index 1e59dd30..9d29a97c 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -8,7 +8,7 @@ import org.terminal21.sparklib.SparkSessions import org.terminal21.sparklib.endtoend.model.CodeFile import org.terminal21.sparklib.endtoend.model.CodeFile.createDatasetFromProjectsSourceFiles import org.terminal21.sparklib.steps.SparkCalculation.sparkCalculation -import org.terminal21.sparklib.steps.{SparkCalculation, StdSparkCalculation} +import org.terminal21.sparklib.steps.{SparkCalculation, StdUiSparkCalculation} @main def sparkBasics(): Unit = SparkSessions.newTerminal21WithSparkSession(SparkSessions.newSparkSession(), "spark-basics", "Spark Basics"): (spark, session) => diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/model/CodeFile.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/model/CodeFile.scala index e8139c8a..ae1aa07b 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/model/CodeFile.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/model/CodeFile.scala @@ -20,7 +20,7 @@ object CodeFile: i, f.getName, f.getPath, - code.split("\n").size, - code.split(" ").size, + code.split("\n").length, + code.split(" ").length, LocalDate.ofInstant(Instant.ofEpochMilli(f.lastModified()), ZoneId.systemDefault()) ) diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StdSparkCalculationTest.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StdUiSparkCalculationTest.scala similarity index 96% rename from terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StdSparkCalculationTest.scala rename to terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StdUiSparkCalculationTest.scala index 5bddf171..3931e3b7 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StdSparkCalculationTest.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StdUiSparkCalculationTest.scala @@ -13,7 +13,7 @@ import org.terminal21.sparklib.SparkSessions import java.util.concurrent.atomic.{AtomicBoolean, AtomicInteger} import scala.util.Using -class StdSparkCalculationTest extends AnyFunSuiteLike with Eventually: +class StdUiSparkCalculationTest extends AnyFunSuiteLike with Eventually: given PatienceConfig = PatienceConfig(scaled(Span(3000, Millis))) test("calculates the correct result"): @@ -88,7 +88,7 @@ class StdSparkCalculationTest extends AnyFunSuiteLike with Eventually: calc.calcCalledTimes.get() should be(2) class TestingCalculation(using session: ConnectedSession, executor: FiberExecutor, spark: SparkSession, intEncoder: Encoder[Int]) - extends StdSparkCalculation[Int, Int]("testing-calc", Box(), Nil): + extends StdUiSparkCalculation[Int, Int]("testing-calc", Box(), Nil): val calcCalledTimes = new AtomicInteger(0) invalidateCache() override protected def calculation(in: Int) = diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/CachedCalculation.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/CachedCalculation.scala new file mode 100644 index 00000000..32ab459e --- /dev/null +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/CachedCalculation.scala @@ -0,0 +1,10 @@ +package org.terminal21.client + +import functions.fibers.FiberExecutor + +abstract class CachedCalculation[IN, OUT]( + notifyWhenCalcReady: Seq[Calculation[OUT, _]] +)(using executor: FiberExecutor) + extends Calculation[IN, OUT](notifyWhenCalcReady): + def isCached: Boolean + def invalidateCache(): Unit From 8ec9aabcd3cd7d9f3b9c2e92db19f9040a70aaa0 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Sun, 7 Jan 2024 20:56:30 +0000 Subject: [PATCH 052/144] - --- .../org/terminal21/sparklib/steps/SparkCalculation.scala | 6 ++---- .../scala/org/terminal21/client/CachedCalculation.scala | 5 ++++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala index 846bc63a..c0c8b30e 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala @@ -28,11 +28,9 @@ abstract class SparkCalculation[IN, OUT: Encoder]( if isCached then reader else writer - def invalidateCache(): Unit = + override def invalidateCache(): Unit = FileUtils.deleteDirectory(new File(targetDir)) - val ccs = notifyWhenCalcReady.collect: - case cc: CachedCalculation[_, _] => cc - for n <- ccs do n.invalidateCache() + super.invalidateCache() private def calculateOnce(f: => Dataset[OUT]): Dataset[OUT] = cache( diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/CachedCalculation.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/CachedCalculation.scala index 32ab459e..fe073779 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/CachedCalculation.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/CachedCalculation.scala @@ -7,4 +7,7 @@ abstract class CachedCalculation[IN, OUT]( )(using executor: FiberExecutor) extends Calculation[IN, OUT](notifyWhenCalcReady): def isCached: Boolean - def invalidateCache(): Unit + def invalidateCache(): Unit = + val ccs = notifyWhenCalcReady.collect: + case cc: CachedCalculation[_, _] => cc + for n <- ccs do n.invalidateCache() From 256e0309fc14b1a31bd8ba521cf5405cd70ba128 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Sun, 7 Jan 2024 21:01:10 +0000 Subject: [PATCH 053/144] - --- .../org/terminal21/sparklib/steps/SparkCalculation.scala | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala index c0c8b30e..43428a43 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala @@ -11,6 +11,15 @@ import org.terminal21.sparklib.util.Environment import java.io.File +/** A UI component that takes a spark calculation (i.e. a spark query) that results in a Dataset. It caches the results by storing them as parquet into the tmp + * folder/spark-calculations/$name. Next time the calculation runs it reads the cache if available. A button should allow the user to clear the cache and rerun + * the spark calculations in case the data changed. + * + * Because the cache is stored in the disk, it is available even if the jvm running the code restarts. This allows the user to run and rerun their code without + * having to rerun the spark calculation. + * + * Subclass this to create your own UI for a spark calculation, see StdUiSparkCalculation below. + */ abstract class SparkCalculation[IN, OUT: Encoder]( val key: String = Keys.nextKey, name: String, From 263c135d13488038d62943cae5ae420f37999654 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Sun, 7 Jan 2024 21:09:44 +0000 Subject: [PATCH 054/144] - --- .../sparklib/steps/SparkCalculation.scala | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala index 43428a43..63ad974c 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala @@ -117,6 +117,30 @@ object SparkCalculation: ready(results) super.whenResultsReady(results) + /** builder for a spark calculation + * @param name + * the name of the calculation, it will show in the UI but also used as a directory name for storing the calculated Dataset. This storage is used as cache, + * the dataset is not recalculated even if the jvm is restarted. There is a button for the user to invalidate this cache so that the spark calculation to + * be rerun in case of changes in the data. + * @param dataUi + * The UiElement that is used to display the calculated data, i.e. a table. This will be grayed out whenever the calculation is running + * @param notifyWhenCalcReady + * if other spark calculations depend on the outcome of this calculation, they should be passed here so that calculations cascade across all of them. + * @param calc + * the function that calculates the dataset. + * @param session + * terminal21 session + * @param executor + * a fiber executor, used for UI rendering + * @param spark + * the spark session + * @tparam IN + * the input data type, can be Unit + * @tparam OUT + * the output data type for the Dataset. + * @return + * a ui component that should be rendered. + */ def sparkCalculation[IN, OUT: Encoder](name: String, dataUi: UiElement with HasStyle, notifyWhenCalcReady: Calculation[Dataset[OUT], _]*)( calc: IN => Dataset[OUT] )(using From a4474a44f2204649c10d432fdb13a0f0fa2fdca3 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Mon, 8 Jan 2024 13:03:06 +0000 Subject: [PATCH 055/144] - --- .../{steps => calculations}/SparkCalculation.scala | 2 +- .../StdUiSparkCalculationTest.scala | 4 ++-- .../sparklib/{steps => calculations}/StepsTest.scala | 0 .../org/terminal21/sparklib/endtoend/SparkBasics.scala | 7 ++++--- 4 files changed, 7 insertions(+), 6 deletions(-) rename terminal21-spark/src/main/scala/org/terminal21/sparklib/{steps => calculations}/SparkCalculation.scala (99%) rename terminal21-spark/src/test/scala/org/terminal21/sparklib/{steps => calculations}/StdUiSparkCalculationTest.scala (95%) rename terminal21-spark/src/test/scala/org/terminal21/sparklib/{steps => calculations}/StepsTest.scala (100%) diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala similarity index 99% rename from terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala rename to terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala index 63ad974c..95f9fd31 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/steps/SparkCalculation.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala @@ -1,4 +1,4 @@ -package org.terminal21.sparklib.steps +package org.terminal21.sparklib.calculations import functions.fibers.FiberExecutor import org.apache.commons.io.FileUtils diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StdUiSparkCalculationTest.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/calculations/StdUiSparkCalculationTest.scala similarity index 95% rename from terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StdUiSparkCalculationTest.scala rename to terminal21-spark/src/test/scala/org/terminal21/sparklib/calculations/StdUiSparkCalculationTest.scala index 3931e3b7..74e18e5b 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StdUiSparkCalculationTest.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/calculations/StdUiSparkCalculationTest.scala @@ -1,4 +1,4 @@ -package org.terminal21.sparklib.steps +package org.terminal21.sparklib.calculations import functions.fibers.FiberExecutor import org.apache.spark.sql.{Dataset, Encoder, SparkSession} @@ -87,7 +87,7 @@ class StdUiSparkCalculationTest extends AnyFunSuiteLike with Eventually: session.click(calc.recalc) calc.calcCalledTimes.get() should be(2) -class TestingCalculation(using session: ConnectedSession, executor: FiberExecutor, spark: SparkSession, intEncoder: Encoder[Int]) +class TestingCalculation(using session: ConnectedSession, spark: SparkSession, intEncoder: Encoder[Int]) extends StdUiSparkCalculation[Int, Int]("testing-calc", Box(), Nil): val calcCalledTimes = new AtomicInteger(0) invalidateCache() diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StepsTest.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/calculations/StepsTest.scala similarity index 100% rename from terminal21-spark/src/test/scala/org/terminal21/sparklib/steps/StepsTest.scala rename to terminal21-spark/src/test/scala/org/terminal21/sparklib/calculations/StepsTest.scala diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index 9d29a97c..0fcba7b1 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -7,8 +7,8 @@ import org.terminal21.client.{*, given} import org.terminal21.sparklib.SparkSessions import org.terminal21.sparklib.endtoend.model.CodeFile import org.terminal21.sparklib.endtoend.model.CodeFile.createDatasetFromProjectsSourceFiles -import org.terminal21.sparklib.steps.SparkCalculation.sparkCalculation -import org.terminal21.sparklib.steps.{SparkCalculation, StdUiSparkCalculation} +import org.terminal21.sparklib.calculations.SparkCalculation.sparkCalculation +import org.terminal21.sparklib.calculations.{SparkCalculation, StdUiSparkCalculation} @main def sparkBasics(): Unit = SparkSessions.newTerminal21WithSparkSession(SparkSessions.newSparkSession(), "spark-basics", "Spark Basics"): (spark, session) => @@ -23,7 +23,8 @@ import org.terminal21.sparklib.steps.{SparkCalculation, StdUiSparkCalculation} val sortedFilesTable = QuickTable().headers(headers: _*).caption("Files sorted by createdDate and numOfWords") val sortedCalc = sparkCalculation("Sorted files", sortedFilesTable)(sortedSourceFiles) .whenResultsReady: results => - sortedFilesTable.rows(results.take(10).toList.map(_.toData)) + val tableRows = results.take(10).toList.map(_.toData) + sortedFilesTable.rows(tableRows) val codeFilesTable = QuickTable().headers(headers: _*).caption("Unsorted files") val codeFilesCalculation = sparkCalculation("Code files", codeFilesTable, sortedCalc): _ => From c114e1332a45306eb305412091be70454d22ba63 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Mon, 8 Jan 2024 13:44:05 +0000 Subject: [PATCH 056/144] - --- .../org/terminal21/sparklib/Extensions.scala | 23 +++++++ .../StdUiSparkCalculationTest.scala | 1 - .../sparklib/calculations/StepsTest.scala | 66 ------------------- .../sparklib/endtoend/SparkBasics.scala | 19 ++++-- .../sparklib/endtoend/model/CodeFile.scala | 7 +- .../org/terminal21/client/Calculation.scala | 11 +++- 6 files changed, 48 insertions(+), 79 deletions(-) create mode 100644 terminal21-spark/src/test/scala/org/terminal21/sparklib/Extensions.scala delete mode 100644 terminal21-spark/src/test/scala/org/terminal21/sparklib/calculations/StepsTest.scala diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/Extensions.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/Extensions.scala new file mode 100644 index 00000000..26dcc670 --- /dev/null +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/Extensions.scala @@ -0,0 +1,23 @@ +package org.terminal21.sparklib + +import functions.fibers.FiberExecutor +import org.apache.spark.sql.{Dataset, Encoder, SparkSession} +import org.terminal21.client.{Calculation, ConnectedSession} +import org.terminal21.client.components.UiElement +import org.terminal21.client.components.UiElement.HasStyle +import org.terminal21.sparklib.calculations.StdUiSparkCalculation + +extension [OUT: Encoder](ds: Dataset[OUT]) + def visualize(name: String, dataUi: UiElement with HasStyle, notifyWhenReady: Calculation[Dataset[OUT], _]*)( + toUi: Dataset[OUT] => Unit + )(using + session: ConnectedSession, + executor: FiberExecutor, + spark: SparkSession + ) = + new StdUiSparkCalculation[Unit, OUT](name, dataUi, notifyWhenReady): + override protected def whenResultsReady(results: Dataset[OUT]): Unit = + toUi(ds) + super.whenResultsReady(results) + + override protected def calculation(in: Unit): Dataset[OUT] = ds diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/calculations/StdUiSparkCalculationTest.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/calculations/StdUiSparkCalculationTest.scala index 74e18e5b..5b973f7b 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/calculations/StdUiSparkCalculationTest.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/calculations/StdUiSparkCalculationTest.scala @@ -1,6 +1,5 @@ package org.terminal21.sparklib.calculations -import functions.fibers.FiberExecutor import org.apache.spark.sql.{Dataset, Encoder, SparkSession} import org.scalatest.concurrent.Eventually import org.scalatest.funsuite.AnyFunSuiteLike diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/calculations/StepsTest.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/calculations/StepsTest.scala deleted file mode 100644 index 5d9fd22f..00000000 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/calculations/StepsTest.scala +++ /dev/null @@ -1,66 +0,0 @@ -//package org.terminal21.sparklib.steps -// -//import org.terminal21.sparklib.{AbstractSparkSuite, SparkSessions} -// -//import java.util.concurrent.atomic.AtomicInteger -//import scala.util.Using -// -//class StepsTest extends AbstractSparkSuite: -// test("returns the correct dataframe"): -// Using.resource(SparkSessions.newSparkSession()): spark => -// import spark.implicits.* -// -// val steps = Steps(spark, "StepsTest-correct-df") -// val budgetStep = steps.step("budget") -// budgetStep.invalidateCache() -// -// def calculations() = Seq(1, 2, 3).toDF() -// -// for _ <- 1 to 3 do budgetStep.calculateOnce(calculations()).as[Int].collect().sorted should be(Array(1, 2, 3)) -// -// test("returns the correct dataset"): -// Using.resource(SparkSessions.newSparkSession()): spark => -// import spark.implicits.* -// -// val steps = Steps(spark, "StepsTest-correct-ds") -// val budgetStep = steps.step("budget") -// budgetStep.invalidateCache() -// -// def calculations() = Seq(1, 2, 3).toDS() -// -// for _ <- 1 to 3 do budgetStep.calculateOnce(calculations()).as[Int].collect().sorted should be(Array(1, 2, 3)) -// -// test("doesn't re-evaluate cached dataframes"): -// Using.resource(SparkSessions.newSparkSession()): spark => -// import spark.implicits.* -// -// val steps = Steps(spark, "StepsTest-cachedDF") -// val budgetStep = steps.step("budget") -// budgetStep.invalidateCache() -// -// val calculated = new AtomicInteger(0) -// -// def calculations() = -// calculated.incrementAndGet() -// Seq(1, 2, 3).toDF() -// -// budgetStep.calculateOnce(calculations()) -// budgetStep.calculateOnce(calculations()) -// calculated.get() should be(1) -// -// test("doesn't re-evaluate cached datasets"): -// Using.resource(SparkSessions.newSparkSession()): spark => -// import spark.implicits.* -// -// val steps = Steps(spark, "StepsTest-cachedDS") -// val budgetStep = steps.step("budget") -// budgetStep.invalidateCache() -// -// val calculated = new AtomicInteger(0) -// -// def calculations() = -// calculated.incrementAndGet() -// Seq(1, 2, 3).toDS() -// budgetStep.calculateOnce(calculations()) -// budgetStep.calculateOnce(calculations()) -// calculated.get() should be(1) diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index 0fcba7b1..0a636b7b 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -6,19 +6,21 @@ import org.terminal21.client.components.chakra.* import org.terminal21.client.{*, given} import org.terminal21.sparklib.SparkSessions import org.terminal21.sparklib.endtoend.model.CodeFile -import org.terminal21.sparklib.endtoend.model.CodeFile.createDatasetFromProjectsSourceFiles +import org.terminal21.sparklib.endtoend.model.CodeFile.scanSourceFiles import org.terminal21.sparklib.calculations.SparkCalculation.sparkCalculation import org.terminal21.sparklib.calculations.{SparkCalculation, StdUiSparkCalculation} +import org.terminal21.sparklib.* + +import java.util.concurrent.atomic.AtomicInteger @main def sparkBasics(): Unit = SparkSessions.newTerminal21WithSparkSession(SparkSessions.newSparkSession(), "spark-basics", "Spark Basics"): (spark, session) => given ConnectedSession = session given SparkSession = spark - import scala3encoders.given import spark.implicits.* - val headers = Seq("id", "name", "path", "numOfLines", "numOfWords", "createdDate") + val headers = Seq("id", "name", "path", "numOfLines", "numOfWords", "createdDate", "timestamp") val sortedFilesTable = QuickTable().headers(headers: _*).caption("Files sorted by createdDate and numOfWords") val sortedCalc = sparkCalculation("Sorted files", sortedFilesTable)(sortedSourceFiles) @@ -27,9 +29,7 @@ import org.terminal21.sparklib.calculations.{SparkCalculation, StdUiSparkCalcula sortedFilesTable.rows(tableRows) val codeFilesTable = QuickTable().headers(headers: _*).caption("Unsorted files") - val codeFilesCalculation = sparkCalculation("Code files", codeFilesTable, sortedCalc): _ => - createDatasetFromProjectsSourceFiles.toDS - .whenResultsReady: results => + val codeFilesCalculation = sourceFiles().visualize("Code files", codeFilesTable, sortedCalc): results => val dt = results.take(10).toList codeFilesTable.rows(dt.map(_.toData)) @@ -41,6 +41,13 @@ import org.terminal21.sparklib.calculations.{SparkCalculation, StdUiSparkCalcula codeFilesCalculation.run(()) session.waitTillUserClosesSession() +def sourceFiles()(using spark: SparkSession) = + import spark.implicits.* + import scala3encoders.given + val runId = new AtomicInteger(0) + scanSourceFiles.toDS.map: cf => + cf.copy(timestamp = System.currentTimeMillis()) + def sortedSourceFiles(sourceFiles: Dataset[CodeFile])(using spark: SparkSession) = import spark.implicits.* sourceFiles.sort($"createdDate".desc, $"numOfWords".desc) diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/model/CodeFile.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/model/CodeFile.scala index ae1aa07b..ed011604 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/model/CodeFile.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/model/CodeFile.scala @@ -6,13 +6,13 @@ import java.io.File import java.nio.file.Files import java.time.{Instant, LocalDate, ZoneId} -case class CodeFile(id: Int, name: String, path: String, numOfLines: Int, numOfWords: Int, createdDate: LocalDate): +case class CodeFile(id: Int, name: String, path: String, numOfLines: Int, numOfWords: Int, createdDate: LocalDate, timestamp: Long): def toColumnNames: Seq[String] = productElementNames.toList def toData: Seq[String] = productIterator.map(_.toString).toList object CodeFile: import scala.jdk.CollectionConverters.* - def createDatasetFromProjectsSourceFiles: Seq[CodeFile] = + def scanSourceFiles: Seq[CodeFile] = val availableFiles = FileUtils.listFiles(new File(".."), Array("scala"), true).asScala.toList availableFiles.zipWithIndex.map: (f, i) => val code = Files.readString(f.toPath) @@ -22,5 +22,6 @@ object CodeFile: f.getPath, code.split("\n").length, code.split(" ").length, - LocalDate.ofInstant(Instant.ofEpochMilli(f.lastModified()), ZoneId.systemDefault()) + LocalDate.ofInstant(Instant.ofEpochMilli(f.lastModified()), ZoneId.systemDefault()), + System.currentTimeMillis() ) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala index 040953cf..a123b0c5 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala @@ -2,6 +2,8 @@ package org.terminal21.client import functions.fibers.FiberExecutor +import java.util.concurrent.CountDownLatch + abstract class Calculation[IN, OUT]( notifyWhenCalcReady: Seq[Calculation[OUT, _]] )(using executor: FiberExecutor): @@ -12,16 +14,19 @@ abstract class Calculation[IN, OUT]( protected def whenResultsReady(results: OUT): Unit def run(in: IN): OUT = - val f = executor.submit: + val refreshInOrder = new CountDownLatch(1) + val f = executor.submit: executor.submit: - whenResultsNotReady() + try whenResultsNotReady() + finally refreshInOrder.countDown() val out = calculation(in) + refreshInOrder.await() postRun(out) out f.get() - def postRun(out: OUT): Unit = + protected def postRun(out: OUT): Unit = executor.submit: whenResultsReady(out) From 2f672c7980c9adb93f2eb4f63230916dc55a40fe Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Mon, 8 Jan 2024 13:48:57 +0000 Subject: [PATCH 057/144] - --- .../calculations/SparkCalculation.scala | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala index 95f9fd31..22559bf2 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala @@ -10,6 +10,7 @@ import org.terminal21.client.components.{Keys, UiComponent, UiElement} import org.terminal21.sparklib.util.Environment import java.io.File +import java.util.concurrent.atomic.AtomicBoolean /** A UI component that takes a spark calculation (i.e. a spark query) that results in a Dataset. It caches the results by storing them as parquet into the tmp * folder/spark-calculations/$name. Next time the calculation runs it reads the cache if available. A button should allow the user to clear the cache and rerun @@ -66,12 +67,16 @@ abstract class StdUiSparkCalculation[IN, OUT: Encoder]( key: String = Keys.nextKey )(using session: ConnectedSession, executor: FiberExecutor, spark: SparkSession) extends SparkCalculation[IN, OUT](name, key, Nil, notifyWhenCalcReady): - val badge = Badge() - val recalc = Button(text = "Recalculate", size = Some("sm"), leftIcon = Some(RepeatIcon())).onClick: () => - badge.text = "Invalidating cache ..." - session.render() - invalidateCache() - for i <- in do run(i) + val badge = Badge() + private val running = new AtomicBoolean(false) + val recalc = Button(text = "Recalculate", size = Some("sm"), leftIcon = Some(RepeatIcon())).onClick: () => + if running.compareAndSet(false, true) then + try + badge.text = "Invalidating cache ..." + session.render() + invalidateCache() + for i <- in do run(i) + finally running.set(false) children = Seq( Box(bg = "green", p = 4).withChildren( From f903549ebb9fd7f79c4f29b274ec656ef03ed2cc Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Mon, 8 Jan 2024 13:51:42 +0000 Subject: [PATCH 058/144] - --- .../terminal21/sparklib/endtoend/SparkBasics.scala | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index 0a636b7b..64782837 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -4,14 +4,10 @@ import org.apache.spark.sql.{Dataset, SparkSession} import org.terminal21.client.components.* import org.terminal21.client.components.chakra.* import org.terminal21.client.{*, given} -import org.terminal21.sparklib.SparkSessions +import org.terminal21.sparklib.* +import org.terminal21.sparklib.calculations.SparkCalculation.sparkCalculation import org.terminal21.sparklib.endtoend.model.CodeFile import org.terminal21.sparklib.endtoend.model.CodeFile.scanSourceFiles -import org.terminal21.sparklib.calculations.SparkCalculation.sparkCalculation -import org.terminal21.sparklib.calculations.{SparkCalculation, StdUiSparkCalculation} -import org.terminal21.sparklib.* - -import java.util.concurrent.atomic.AtomicInteger @main def sparkBasics(): Unit = SparkSessions.newTerminal21WithSparkSession(SparkSessions.newSparkSession(), "spark-basics", "Spark Basics"): (spark, session) => @@ -42,9 +38,8 @@ import java.util.concurrent.atomic.AtomicInteger session.waitTillUserClosesSession() def sourceFiles()(using spark: SparkSession) = - import spark.implicits.* import scala3encoders.given - val runId = new AtomicInteger(0) + import spark.implicits.* scanSourceFiles.toDS.map: cf => cf.copy(timestamp = System.currentTimeMillis()) From ef41324c420449e8b3a9c250eaf51bc7bdb7d331 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Mon, 8 Jan 2024 13:58:06 +0000 Subject: [PATCH 059/144] - --- .../sparklib/calculations/SparkCalculation.scala | 15 ++++++++------- .../org/terminal21/sparklib/Extensions.scala | 6 +++++- .../sparklib/endtoend/SparkBasics.scala | 1 - 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala index 22559bf2..216188c1 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala @@ -78,14 +78,15 @@ abstract class StdUiSparkCalculation[IN, OUT: Encoder]( for i <- in do run(i) finally running.set(false) + val header = Box(bg = "green", p = 4).withChildren( + HStack().withChildren( + Text(text = name), + badge, + recalc + ) + ) children = Seq( - Box(bg = "green", p = 4).withChildren( - HStack().withChildren( - Text(text = name), - badge, - recalc - ) - ), + header, dataUi ) diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/Extensions.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/Extensions.scala index 26dcc670..09a022c1 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/Extensions.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/Extensions.scala @@ -15,9 +15,13 @@ extension [OUT: Encoder](ds: Dataset[OUT]) executor: FiberExecutor, spark: SparkSession ) = - new StdUiSparkCalculation[Unit, OUT](name, dataUi, notifyWhenReady): + val ui = new StdUiSparkCalculation[Unit, OUT](name, dataUi, notifyWhenReady): override protected def whenResultsReady(results: Dataset[OUT]): Unit = toUi(ds) super.whenResultsReady(results) override protected def calculation(in: Unit): Dataset[OUT] = ds + + executor.submit: + ui.run(()) + ui diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index 64782837..0e22aa4b 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -34,7 +34,6 @@ import org.terminal21.sparklib.endtoend.model.CodeFile.scanSourceFiles sortedCalc ).render() - codeFilesCalculation.run(()) session.waitTillUserClosesSession() def sourceFiles()(using spark: SparkSession) = From af32f29a8ad539d28316f66919fce5ddf3be97b4 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Mon, 8 Jan 2024 15:14:41 +0000 Subject: [PATCH 060/144] - --- .../calculations/SparkCalculation.scala | 83 +++---------------- .../org/terminal21/sparklib/Extensions.scala | 18 ++-- .../StdUiSparkCalculationTest.scala | 29 ++++--- .../sparklib/endtoend/SparkBasics.scala | 17 ++-- .../terminal21/client/CachedCalculation.scala | 11 +-- .../org/terminal21/client/Calculation.scala | 55 +++--------- .../terminal21/client/CalculationTest.scala | 32 +++---- 7 files changed, 75 insertions(+), 170 deletions(-) diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala index 216188c1..ff9cb405 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala @@ -21,15 +21,14 @@ import java.util.concurrent.atomic.AtomicBoolean * * Subclass this to create your own UI for a spark calculation, see StdUiSparkCalculation below. */ -abstract class SparkCalculation[IN, OUT: Encoder]( - val key: String = Keys.nextKey, +abstract class SparkCalculation[OUT: Encoder]( + val key: String, name: String, - @volatile var children: Seq[UiElement], - notifyWhenCalcReady: Seq[Calculation[Dataset[OUT], _]] + @volatile var children: Seq[UiElement] )(using executor: FiberExecutor, spark: SparkSession) - extends CachedCalculation[IN, Dataset[OUT]](notifyWhenCalcReady) + extends CachedCalculation[Dataset[OUT]] with UiComponent: - private val rootFolder = s"${Environment.tmpDirectory}/spark-calculations/$name" + private val rootFolder = s"${Environment.tmpDirectory}/spark-calculations" private val targetDir = s"$rootFolder/$name" def isCached: Boolean = new File(targetDir).exists() @@ -40,7 +39,6 @@ abstract class SparkCalculation[IN, OUT: Encoder]( override def invalidateCache(): Unit = FileUtils.deleteDirectory(new File(targetDir)) - super.invalidateCache() private def calculateOnce(f: => Dataset[OUT]): Dataset[OUT] = cache( @@ -51,22 +49,14 @@ abstract class SparkCalculation[IN, OUT: Encoder]( } ) - protected var in: Option[IN] = None + override protected def calculation(): Dataset[OUT] = calculateOnce(nonCachedCalculation) - override def run(in: IN) = - this.in = Some(in) - val isC = isCached - val out = calculateOnce(super.run(in)) - if isC then postRun(out) - out - -abstract class StdUiSparkCalculation[IN, OUT: Encoder]( +abstract class StdUiSparkCalculation[OUT: Encoder]( + key: String, name: String, - dataUi: UiElement with HasStyle, - notifyWhenCalcReady: Seq[Calculation[Dataset[OUT], _]], - key: String = Keys.nextKey + dataUi: UiElement with HasStyle )(using session: ConnectedSession, executor: FiberExecutor, spark: SparkSession) - extends SparkCalculation[IN, OUT](name, key, Nil, notifyWhenCalcReady): + extends SparkCalculation[OUT](key, name, Nil): val badge = Badge() private val running = new AtomicBoolean(false) val recalc = Button(text = "Recalculate", size = Some("sm"), leftIcon = Some(RepeatIcon())).onClick: () => @@ -75,7 +65,7 @@ abstract class StdUiSparkCalculation[IN, OUT: Encoder]( badge.text = "Invalidating cache ..." session.render() invalidateCache() - for i <- in do run(i) + run() finally running.set(false) val header = Box(bg = "green", p = 4).withChildren( @@ -104,54 +94,3 @@ abstract class StdUiSparkCalculation[IN, OUT: Encoder]( recalc.isDisabled = Some(false) dataUi.style = dataUi.style - "filter" session.render() - -object SparkCalculation: - class Builder[IN, OUT: Encoder]( - name: String, - dataUi: UiElement with HasStyle, - notifyWhenCalcReady: Seq[Calculation[Dataset[OUT], _]], - calc: IN => Dataset[OUT] - )(using - session: ConnectedSession, - executor: FiberExecutor, - spark: SparkSession - ): - def whenResultsReady(ready: Dataset[OUT] => Unit) = - new StdUiSparkCalculation[IN, OUT](name, dataUi, notifyWhenCalcReady): - override protected def calculation(in: IN): Dataset[OUT] = calc(in) - override protected def whenResultsReady(results: Dataset[OUT]): Unit = - ready(results) - super.whenResultsReady(results) - - /** builder for a spark calculation - * @param name - * the name of the calculation, it will show in the UI but also used as a directory name for storing the calculated Dataset. This storage is used as cache, - * the dataset is not recalculated even if the jvm is restarted. There is a button for the user to invalidate this cache so that the spark calculation to - * be rerun in case of changes in the data. - * @param dataUi - * The UiElement that is used to display the calculated data, i.e. a table. This will be grayed out whenever the calculation is running - * @param notifyWhenCalcReady - * if other spark calculations depend on the outcome of this calculation, they should be passed here so that calculations cascade across all of them. - * @param calc - * the function that calculates the dataset. - * @param session - * terminal21 session - * @param executor - * a fiber executor, used for UI rendering - * @param spark - * the spark session - * @tparam IN - * the input data type, can be Unit - * @tparam OUT - * the output data type for the Dataset. - * @return - * a ui component that should be rendered. - */ - def sparkCalculation[IN, OUT: Encoder](name: String, dataUi: UiElement with HasStyle, notifyWhenCalcReady: Calculation[Dataset[OUT], _]*)( - calc: IN => Dataset[OUT] - )(using - session: ConnectedSession, - executor: FiberExecutor, - spark: SparkSession - ) = - new Builder[IN, OUT](name, dataUi, notifyWhenCalcReady, calc) diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/Extensions.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/Extensions.scala index 09a022c1..04cefa4e 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/Extensions.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/Extensions.scala @@ -2,26 +2,26 @@ package org.terminal21.sparklib import functions.fibers.FiberExecutor import org.apache.spark.sql.{Dataset, Encoder, SparkSession} -import org.terminal21.client.{Calculation, ConnectedSession} -import org.terminal21.client.components.UiElement +import org.terminal21.client.ConnectedSession import org.terminal21.client.components.UiElement.HasStyle +import org.terminal21.client.components.{Keys, UiElement} import org.terminal21.sparklib.calculations.StdUiSparkCalculation extension [OUT: Encoder](ds: Dataset[OUT]) - def visualize(name: String, dataUi: UiElement with HasStyle, notifyWhenReady: Calculation[Dataset[OUT], _]*)( + def visualize(name: String, dataUi: UiElement with HasStyle)( toUi: Dataset[OUT] => Unit )(using session: ConnectedSession, executor: FiberExecutor, spark: SparkSession ) = - val ui = new StdUiSparkCalculation[Unit, OUT](name, dataUi, notifyWhenReady): + val ui = new StdUiSparkCalculation[OUT](Keys.nextKey, name, dataUi): override protected def whenResultsReady(results: Dataset[OUT]): Unit = - toUi(ds) + toUi(results) super.whenResultsReady(results) + override def nonCachedCalculation: Dataset[OUT] = + println("nonCachedCalculation") + ds - override protected def calculation(in: Unit): Dataset[OUT] = ds - - executor.submit: - ui.run(()) + ui.run() ui diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/calculations/StdUiSparkCalculationTest.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/calculations/StdUiSparkCalculationTest.scala index 5b973f7b..c726aad6 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/calculations/StdUiSparkCalculationTest.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/calculations/StdUiSparkCalculationTest.scala @@ -5,6 +5,7 @@ import org.scalatest.concurrent.Eventually import org.scalatest.funsuite.AnyFunSuiteLike import org.scalatest.matchers.should.Matchers.* import org.scalatest.time.{Millis, Span} +import org.terminal21.client.components.Keys import org.terminal21.client.components.chakra.* import org.terminal21.client.{ConnectedSession, ConnectedSessionMock, given} import org.terminal21.sparklib.SparkSessions @@ -21,7 +22,7 @@ class StdUiSparkCalculationTest extends AnyFunSuiteLike with Eventually: given ConnectedSession = ConnectedSessionMock.newConnectedSessionMock given SparkSession = spark val calc = new TestingCalculation - calc.run(1).collect().toList should be(List(2)) + calc.run().get().collect().toList should be(List(2)) test("whenResultsNotReady"): Using.resource(SparkSessions.newSparkSession()): spark => @@ -32,7 +33,7 @@ class StdUiSparkCalculationTest extends AnyFunSuiteLike with Eventually: val calc = new TestingCalculation: override protected def whenResultsNotReady(): Unit = called.set(true) - calc.run(1) + calc.run().get() called.get() should be(true) test("whenResultsReady"): @@ -46,7 +47,7 @@ class StdUiSparkCalculationTest extends AnyFunSuiteLike with Eventually: results.collect().toList should be(List(2)) called.set(true) - calc.run(1) + calc.run() eventually: called.get() should be(true) @@ -61,8 +62,8 @@ class StdUiSparkCalculationTest extends AnyFunSuiteLike with Eventually: results.collect().toList should be(List(2)) called.incrementAndGet() - calc.run(1) - calc.run(1) + calc.run().get() + calc.run() eventually: called.get() should be(2) @@ -72,8 +73,8 @@ class StdUiSparkCalculationTest extends AnyFunSuiteLike with Eventually: given ConnectedSession = ConnectedSessionMock.newConnectedSessionMock given SparkSession = spark val calc = new TestingCalculation - calc.run(1).collect().toList should be(List(2)) - calc.run(1).collect().toList should be(List(2)) + calc.run().get().collect().toList should be(List(2)) + calc.run().get().collect().toList should be(List(2)) calc.calcCalledTimes.get() should be(1) test("refresh button invalidates cache and runs calculations"): @@ -82,15 +83,17 @@ class StdUiSparkCalculationTest extends AnyFunSuiteLike with Eventually: given session: ConnectedSession = ConnectedSessionMock.newConnectedSessionMock given SparkSession = spark val calc = new TestingCalculation - calc.run(1).collect().toList should be(List(2)) + calc.run().get().collect().toList should be(List(2)) session.click(calc.recalc) - calc.calcCalledTimes.get() should be(2) + eventually: + calc.calcCalledTimes.get() should be(2) class TestingCalculation(using session: ConnectedSession, spark: SparkSession, intEncoder: Encoder[Int]) - extends StdUiSparkCalculation[Int, Int]("testing-calc", Box(), Nil): - val calcCalledTimes = new AtomicInteger(0) + extends StdUiSparkCalculation[Int](Keys.nextKey, "testing-calc", Box()): + val calcCalledTimes = new AtomicInteger(0) invalidateCache() - override protected def calculation(in: Int) = + + override def nonCachedCalculation: Dataset[Int] = import spark.implicits.* calcCalledTimes.incrementAndGet() - Seq(in + 1).toDS + Seq(2).toDS diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index 0e22aa4b..561af796 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -5,7 +5,6 @@ import org.terminal21.client.components.* import org.terminal21.client.components.chakra.* import org.terminal21.client.{*, given} import org.terminal21.sparklib.* -import org.terminal21.sparklib.calculations.SparkCalculation.sparkCalculation import org.terminal21.sparklib.endtoend.model.CodeFile import org.terminal21.sparklib.endtoend.model.CodeFile.scanSourceFiles @@ -19,19 +18,21 @@ import org.terminal21.sparklib.endtoend.model.CodeFile.scanSourceFiles val headers = Seq("id", "name", "path", "numOfLines", "numOfWords", "createdDate", "timestamp") val sortedFilesTable = QuickTable().headers(headers: _*).caption("Files sorted by createdDate and numOfWords") - val sortedCalc = sparkCalculation("Sorted files", sortedFilesTable)(sortedSourceFiles) - .whenResultsReady: results => - val tableRows = results.take(10).toList.map(_.toData) - sortedFilesTable.rows(tableRows) val codeFilesTable = QuickTable().headers(headers: _*).caption("Unsorted files") - val codeFilesCalculation = sourceFiles().visualize("Code files", codeFilesTable, sortedCalc): results => + val codeFilesCalculation = sourceFiles().visualize("Code files", codeFilesTable): results => val dt = results.take(10).toList + println("results") codeFilesTable.rows(dt.map(_.toData)) + println("results 2") + +// val sortedCalc = sortedSourceFiles(sourceFiles()).visualize("Sorted files", sortedFilesTable): results => +// val tableRows = results.take(10).toList.map(_.toData) +// sortedFilesTable.rows(tableRows) Seq( - codeFilesCalculation, - sortedCalc + codeFilesCalculation +// sortedCalc ).render() session.waitTillUserClosesSession() diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/CachedCalculation.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/CachedCalculation.scala index fe073779..cb5eb5e0 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/CachedCalculation.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/CachedCalculation.scala @@ -2,12 +2,7 @@ package org.terminal21.client import functions.fibers.FiberExecutor -abstract class CachedCalculation[IN, OUT]( - notifyWhenCalcReady: Seq[Calculation[OUT, _]] -)(using executor: FiberExecutor) - extends Calculation[IN, OUT](notifyWhenCalcReady): +abstract class CachedCalculation[OUT](using executor: FiberExecutor) extends Calculation[OUT]: def isCached: Boolean - def invalidateCache(): Unit = - val ccs = notifyWhenCalcReady.collect: - case cc: CachedCalculation[_, _] => cc - for n <- ccs do n.invalidateCache() + def invalidateCache(): Unit + def nonCachedCalculation: OUT diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala index a123b0c5..11f11d4e 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala @@ -1,58 +1,23 @@ package org.terminal21.client -import functions.fibers.FiberExecutor +import functions.fibers.{Fiber, FiberExecutor} import java.util.concurrent.CountDownLatch -abstract class Calculation[IN, OUT]( - notifyWhenCalcReady: Seq[Calculation[OUT, _]] -)(using executor: FiberExecutor): - protected def calculation(in: IN): OUT - protected def whenResultsNotReady(): Unit = - for c <- notifyWhenCalcReady do c.whenResultsNotReady() +abstract class Calculation[OUT](using executor: FiberExecutor): + protected def calculation(): OUT + protected def whenResultsNotReady(): Unit = () + protected def whenResultsReady(results: OUT): Unit = () - protected def whenResultsReady(results: OUT): Unit - - def run(in: IN): OUT = + def run(): Fiber[OUT] = val refreshInOrder = new CountDownLatch(1) - val f = executor.submit: + executor.submit: executor.submit: try whenResultsNotReady() finally refreshInOrder.countDown() - val out = calculation(in) + val out = calculation() refreshInOrder.await() - postRun(out) - out - f.get() - - protected def postRun(out: OUT): Unit = - executor.submit: - whenResultsReady(out) - - for c <- notifyWhenCalcReady do executor.submit: - try c.run(out) - catch case t: Throwable => t.printStackTrace() - -object Calculation: - class Builder[IN, OUT]( - calc: IN => OUT, - uiNotReadyUpdater: () => Unit = () => (), - uiReadyUpdater: OUT => Unit = (_: OUT) => (), - notify: Seq[Calculation[OUT, _]] = Nil - )(using executor: FiberExecutor): - def whenResultsNotReady(uiUpdater: => Unit) = new Builder(calc, () => uiUpdater, uiReadyUpdater, notify) - def whenResultsReady(uiReadyUpdater: OUT => Unit) = new Builder(calc, uiNotReadyUpdater, uiReadyUpdater, notify) - def notifyAfterCalculated(other: Calculation[OUT, _]) = new Builder(calc, uiNotReadyUpdater, uiReadyUpdater, notify :+ other) - def build: Calculation[IN, OUT] = - new Calculation[IN, OUT](notify): - override protected def calculation(in: IN): OUT = calc(in) - override protected def whenResultsNotReady(): Unit = uiNotReadyUpdater() - override protected def whenResultsReady(results: OUT): Unit = uiReadyUpdater(results) - - def newCalculationNoIn[OUT](calc: => OUT)(using executor: FiberExecutor): Builder[Unit, OUT] = - new Builder(_ => calc) - - def newCalculation[IN, OUT](calc: IN => OUT)(using executor: FiberExecutor): Builder[IN, OUT] = - new Builder(calc) + whenResultsReady(out) + out diff --git a/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala b/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala index 2d88db72..d2f30117 100644 --- a/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala +++ b/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala @@ -12,23 +12,25 @@ class CalculationTest extends AnyFunSuiteLike: def testCalc(i: Int) = i + 1 def testCalcString(i: Int): String = (i + 10).toString + class Calc extends Calculation[Int]: + val whenResultsNotReadyCalled = new AtomicBoolean(false) + val whenResultsReadyValue = new AtomicInteger(-1) + override protected def whenResultsNotReady(): Unit = whenResultsNotReadyCalled.set(true) + override protected def whenResultsReady(results: Int): Unit = whenResultsReadyValue.set(results) + override protected def calculation() = 2 + test("calculates"): - val calc = Calculation.newCalculation(testCalc).build - calc.run(1) should be(2) + val calc = new Calc + calc.run().get() should be(2) - test("calls the ui updater with the calculated value"): - val c = new AtomicInteger(-1) - val b = new AtomicBoolean(false) - val calc = Calculation.newCalculation(testCalc).whenResultsNotReady(b.set(true)).whenResultsReady(i => c.set(i)).build - calc.run(1) - b.get() should be(true) + test("calls whenResultsNotReady"): + val calc = new Calc + calc.run() eventually: - c.get() should be(2) + calc.whenResultsNotReadyCalled.get() should be(true) - test("notifies"): - val c = new AtomicInteger(-1) - val calc2 = Calculation.newCalculation(testCalcString).whenResultsReady(i => c.set(i.toInt)).build - val calc1 = Calculation.newCalculation(testCalc).notifyAfterCalculated(calc2).build - calc1.run(1) + test("calls whenResultsReady"): + val calc = new Calc + calc.run() eventually: - c.get() should be(12) + calc.whenResultsReadyValue.get() should be(2) From 8b563bfba54e00b3a02510b67034c9cec56288c7 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Mon, 8 Jan 2024 15:42:24 +0000 Subject: [PATCH 061/144] - --- .../scala/org/terminal21/sparklib/Extensions.scala | 4 +--- .../terminal21/sparklib/endtoend/SparkBasics.scala | 12 +++++------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/Extensions.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/Extensions.scala index 04cefa4e..63a88a59 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/Extensions.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/Extensions.scala @@ -19,9 +19,7 @@ extension [OUT: Encoder](ds: Dataset[OUT]) override protected def whenResultsReady(results: Dataset[OUT]): Unit = toUi(results) super.whenResultsReady(results) - override def nonCachedCalculation: Dataset[OUT] = - println("nonCachedCalculation") - ds + override def nonCachedCalculation: Dataset[OUT] = ds ui.run() ui diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index 561af796..2b7915df 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -22,17 +22,15 @@ import org.terminal21.sparklib.endtoend.model.CodeFile.scanSourceFiles val codeFilesTable = QuickTable().headers(headers: _*).caption("Unsorted files") val codeFilesCalculation = sourceFiles().visualize("Code files", codeFilesTable): results => val dt = results.take(10).toList - println("results") codeFilesTable.rows(dt.map(_.toData)) - println("results 2") -// val sortedCalc = sortedSourceFiles(sourceFiles()).visualize("Sorted files", sortedFilesTable): results => -// val tableRows = results.take(10).toList.map(_.toData) -// sortedFilesTable.rows(tableRows) + val sortedCalc = sortedSourceFiles(sourceFiles()).visualize("Sorted files", sortedFilesTable): results => + val tableRows = results.take(10).toList.map(_.toData) + sortedFilesTable.rows(tableRows) Seq( - codeFilesCalculation -// sortedCalc + codeFilesCalculation, + sortedCalc ).render() session.waitTillUserClosesSession() From ec6c8ef741ebce798c9eb646f95e75d2fca7f2f9 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Mon, 8 Jan 2024 15:50:01 +0000 Subject: [PATCH 062/144] - --- Readme.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Readme.md b/Readme.md index b775bab4..71cfd4aa 100644 --- a/Readme.md +++ b/Readme.md @@ -99,6 +99,27 @@ ls *.sc bouncing-ball.sc csv-editor.sc csv-viewer.sc hello-world.sc postit.sc server.sc textedit.sc ``` +# Mutability + +terminal21 ui components are shamelessly mutable. This is a decision choice (for now) because of how much more simple code is this way. I.e. +changing the text of a paragraph on an event handler is as simple as : + +```scala + p.text = "new text" +``` + +The equivalent immutable code would be (at least) +```scala + p.copy(text= "new text") +``` + +In the immutable version, the graph of components wouldn't reflect the change and there would be more work needed to do that. + +Also by default some component values (like input boxes) are changed by the user. These changes are reflected in the component graph, something that +would be a lot harder if the graph was immutable. + +If there is a reasonable way to refactor to have immutability without compromising simplicity, it will be done. + # Need help? Please use the discussions of the project to post any questions, comments or ideas. From 737531c7e8b295486c881633b14e7d789fff8622 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Mon, 8 Jan 2024 15:53:24 +0000 Subject: [PATCH 063/144] - --- .../org/terminal21/sparklib/endtoend/SparkBasics.scala | 9 +++++---- .../scala/org/terminal21/client/CachedCalculation.scala | 5 ++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index 2b7915df..76079594 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -19,15 +19,16 @@ import org.terminal21.sparklib.endtoend.model.CodeFile.scanSourceFiles val sortedFilesTable = QuickTable().headers(headers: _*).caption("Files sorted by createdDate and numOfWords") - val codeFilesTable = QuickTable().headers(headers: _*).caption("Unsorted files") - val codeFilesCalculation = sourceFiles().visualize("Code files", codeFilesTable): results => - val dt = results.take(10).toList - codeFilesTable.rows(dt.map(_.toData)) + val codeFilesTable = QuickTable().headers(headers: _*).caption("Unsorted files") val sortedCalc = sortedSourceFiles(sourceFiles()).visualize("Sorted files", sortedFilesTable): results => val tableRows = results.take(10).toList.map(_.toData) sortedFilesTable.rows(tableRows) + val codeFilesCalculation = sourceFiles().visualize("Code files", codeFilesTable): results => + val dt = results.take(10).toList + codeFilesTable.rows(dt.map(_.toData)) + Seq( codeFilesCalculation, sortedCalc diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/CachedCalculation.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/CachedCalculation.scala index cb5eb5e0..cde68f8c 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/CachedCalculation.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/CachedCalculation.scala @@ -1,8 +1,11 @@ package org.terminal21.client -import functions.fibers.FiberExecutor +import functions.fibers.{Fiber, FiberExecutor} abstract class CachedCalculation[OUT](using executor: FiberExecutor) extends Calculation[OUT]: def isCached: Boolean def invalidateCache(): Unit def nonCachedCalculation: OUT + def reCalculate(): Fiber[OUT] = + invalidateCache() + run() From 6d6c6f7c1ec0275316dc20f62f4649c1c4aed5ba Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Mon, 8 Jan 2024 16:43:50 +0000 Subject: [PATCH 064/144] - --- .../sparklib/calculations/ReadWriter.scala | 16 +++++++++++++ .../calculations/SparkCalculation.scala | 23 ++++++++++--------- ...ons.scala => CalculationsExtensions.scala} | 15 ++++++------ .../sparklib/DataframeExtensions.scala | 7 ++++++ .../StdUiSparkCalculationTest.scala | 4 ++-- .../sparklib/endtoend/SparkBasics.scala | 18 +++++++++++---- 6 files changed, 58 insertions(+), 25 deletions(-) create mode 100644 terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/ReadWriter.scala rename terminal21-spark/src/test/scala/org/terminal21/sparklib/{Extensions.scala => CalculationsExtensions.scala} (57%) create mode 100644 terminal21-spark/src/test/scala/org/terminal21/sparklib/DataframeExtensions.scala diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/ReadWriter.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/ReadWriter.scala new file mode 100644 index 00000000..5ec57428 --- /dev/null +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/ReadWriter.scala @@ -0,0 +1,16 @@ +package org.terminal21.sparklib.calculations + +import org.apache.spark.sql.{DataFrame, Dataset, Encoder, SparkSession} + +trait ReadWriter[A]: + def read(spark: SparkSession, file: String): A + def write(file: String, ds: A): Unit + +object ReadWriter: + given datasetReadWriter[A](using Encoder[A]): ReadWriter[Dataset[A]] = new ReadWriter[Dataset[A]]: + override def read(spark: SparkSession, file: String) = spark.read.parquet(file).as[A] + override def write(file: String, ds: Dataset[A]): Unit = ds.write.parquet(file) + + given dataframeReadWriter: ReadWriter[DataFrame] = new ReadWriter[DataFrame]: + override def read(spark: SparkSession, file: String) = spark.read.parquet(file) + override def write(file: String, ds: DataFrame): Unit = ds.write.parquet(file) diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala index ff9cb405..3679b95e 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala @@ -4,9 +4,9 @@ import functions.fibers.FiberExecutor import org.apache.commons.io.FileUtils import org.apache.spark.sql.{Dataset, Encoder, SparkSession} import org.terminal21.client.components.UiElement.HasStyle -import org.terminal21.client.{CachedCalculation, Calculation, ConnectedSession} -import org.terminal21.client.components.chakra.{Badge, Box, Button, HStack, RepeatIcon, Text} -import org.terminal21.client.components.{Keys, UiComponent, UiElement} +import org.terminal21.client.components.chakra.* +import org.terminal21.client.components.{UiComponent, UiElement} +import org.terminal21.client.{CachedCalculation, ConnectedSession} import org.terminal21.sparklib.util.Environment import java.io.File @@ -21,13 +21,14 @@ import java.util.concurrent.atomic.AtomicBoolean * * Subclass this to create your own UI for a spark calculation, see StdUiSparkCalculation below. */ -abstract class SparkCalculation[OUT: Encoder]( +abstract class SparkCalculation[OUT: ReadWriter]( val key: String, name: String, @volatile var children: Seq[UiElement] )(using executor: FiberExecutor, spark: SparkSession) - extends CachedCalculation[Dataset[OUT]] + extends CachedCalculation[OUT] with UiComponent: + private val rw = implicitly[ReadWriter[OUT]] private val rootFolder = s"${Environment.tmpDirectory}/spark-calculations" private val targetDir = s"$rootFolder/$name" @@ -40,18 +41,18 @@ abstract class SparkCalculation[OUT: Encoder]( override def invalidateCache(): Unit = FileUtils.deleteDirectory(new File(targetDir)) - private def calculateOnce(f: => Dataset[OUT]): Dataset[OUT] = + private def calculateOnce(f: => OUT): OUT = cache( - spark.read.parquet(targetDir).as[OUT], { + rw.read(spark, targetDir), { val ds = f - ds.write.parquet(targetDir) + rw.write(targetDir, ds) ds } ) - override protected def calculation(): Dataset[OUT] = calculateOnce(nonCachedCalculation) + override protected def calculation(): OUT = calculateOnce(nonCachedCalculation) -abstract class StdUiSparkCalculation[OUT: Encoder]( +abstract class StdUiSparkCalculation[OUT: ReadWriter]( key: String, name: String, dataUi: UiElement with HasStyle @@ -88,7 +89,7 @@ abstract class StdUiSparkCalculation[OUT: Encoder]( session.render() super.whenResultsNotReady() - override protected def whenResultsReady(results: Dataset[OUT]): Unit = + override protected def whenResultsReady(results: OUT): Unit = badge.text = "Ready" badge.colorScheme = None recalc.isDisabled = Some(false) diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/Extensions.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/CalculationsExtensions.scala similarity index 57% rename from terminal21-spark/src/test/scala/org/terminal21/sparklib/Extensions.scala rename to terminal21-spark/src/test/scala/org/terminal21/sparklib/CalculationsExtensions.scala index 63a88a59..55e63694 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/Extensions.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/CalculationsExtensions.scala @@ -1,25 +1,26 @@ package org.terminal21.sparklib import functions.fibers.FiberExecutor -import org.apache.spark.sql.{Dataset, Encoder, SparkSession} +import org.apache.spark.sql.SparkSession import org.terminal21.client.ConnectedSession import org.terminal21.client.components.UiElement.HasStyle import org.terminal21.client.components.{Keys, UiElement} -import org.terminal21.sparklib.calculations.StdUiSparkCalculation +import org.terminal21.sparklib.calculations.{ReadWriter, StdUiSparkCalculation} -extension [OUT: Encoder](ds: Dataset[OUT]) +extension [OUT: ReadWriter](ds: OUT) def visualize(name: String, dataUi: UiElement with HasStyle)( - toUi: Dataset[OUT] => Unit + toUi: OUT => Unit )(using session: ConnectedSession, executor: FiberExecutor, spark: SparkSession ) = val ui = new StdUiSparkCalculation[OUT](Keys.nextKey, name, dataUi): - override protected def whenResultsReady(results: Dataset[OUT]): Unit = - toUi(results) + override protected def whenResultsReady(results: OUT): Unit = + try toUi(results) + catch case t: Throwable => t.printStackTrace() super.whenResultsReady(results) - override def nonCachedCalculation: Dataset[OUT] = ds + override def nonCachedCalculation: OUT = ds ui.run() ui diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/DataframeExtensions.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/DataframeExtensions.scala new file mode 100644 index 00000000..0ad266e8 --- /dev/null +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/DataframeExtensions.scala @@ -0,0 +1,7 @@ +package org.terminal21.sparklib + +import org.apache.spark.sql.{DataFrame, Row} + +extension (rows: Seq[Row]) + def toUiTable: Seq[Seq[String]] = rows.map: row => + row.toSeq.map(_.toString) diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/calculations/StdUiSparkCalculationTest.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/calculations/StdUiSparkCalculationTest.scala index c726aad6..13c7aadd 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/calculations/StdUiSparkCalculationTest.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/calculations/StdUiSparkCalculationTest.scala @@ -88,8 +88,8 @@ class StdUiSparkCalculationTest extends AnyFunSuiteLike with Eventually: eventually: calc.calcCalledTimes.get() should be(2) -class TestingCalculation(using session: ConnectedSession, spark: SparkSession, intEncoder: Encoder[Int]) - extends StdUiSparkCalculation[Int](Keys.nextKey, "testing-calc", Box()): +class TestingCalculation(using session: ConnectedSession, spark: SparkSession, enc: Encoder[Int]) + extends StdUiSparkCalculation[Dataset[Int]](Keys.nextKey, "testing-calc", Box()): val calcCalledTimes = new AtomicInteger(0) invalidateCache() diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index 76079594..6e3e5823 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -18,20 +18,28 @@ import org.terminal21.sparklib.endtoend.model.CodeFile.scanSourceFiles val headers = Seq("id", "name", "path", "numOfLines", "numOfWords", "createdDate", "timestamp") val sortedFilesTable = QuickTable().headers(headers: _*).caption("Files sorted by createdDate and numOfWords") - - val codeFilesTable = QuickTable().headers(headers: _*).caption("Unsorted files") + val codeFilesTable = QuickTable().headers(headers: _*).caption("Unsorted files") val sortedCalc = sortedSourceFiles(sourceFiles()).visualize("Sorted files", sortedFilesTable): results => - val tableRows = results.take(10).toList.map(_.toData) + val tableRows = results.take(3).toList.map(_.toData) sortedFilesTable.rows(tableRows) val codeFilesCalculation = sourceFiles().visualize("Code files", codeFilesTable): results => - val dt = results.take(10).toList + val dt = results.take(3).toList codeFilesTable.rows(dt.map(_.toData)) + val sortedFilesTableDF = QuickTable().headers(headers: _*).caption("Files sorted by createdDate and numOfWords ASC and as DF") + val sortedCalcDF = sourceFiles() + .sort($"createdDate".asc, $"numOfWords".asc) + .toDF() + .visualize("Sorted files DF", sortedFilesTableDF): results => + val tableRows = results.take(4).toList + sortedFilesTableDF.rows(tableRows.toUiTable) + Seq( codeFilesCalculation, - sortedCalc + sortedCalc, + sortedCalcDF ).render() session.waitTillUserClosesSession() From 17d640ecf90e2875d9b222713cbbb46e20cdc46a Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Mon, 8 Jan 2024 18:04:48 +0000 Subject: [PATCH 065/144] - --- .../org/terminal21/sparklib/calculations/ReadWriter.scala | 3 +++ 1 file changed, 3 insertions(+) diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/ReadWriter.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/ReadWriter.scala index 5ec57428..1aa716bd 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/ReadWriter.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/ReadWriter.scala @@ -2,6 +2,9 @@ package org.terminal21.sparklib.calculations import org.apache.spark.sql.{DataFrame, Dataset, Encoder, SparkSession} +import scala.annotation.implicitNotFound + +@implicitNotFound("Unable to find ReadWriter for type ${A}. Dataset of case classes and Dataframes are supported.") trait ReadWriter[A]: def read(spark: SparkSession, file: String): A def write(file: String, ds: A): Unit From b6c016a129a96c9886d237326cd267ec6a31e47c Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Tue, 9 Jan 2024 13:03:38 +0000 Subject: [PATCH 066/144] - --- .../terminal21/sparklib/calculations/SparkCalculation.scala | 6 +++--- .../client/{ => components}/CachedCalculation.scala | 2 +- .../terminal21/client/{ => components}/Calculation.scala | 2 +- .../test/scala/org/terminal21/client/CalculationTest.scala | 1 + 4 files changed, 6 insertions(+), 5 deletions(-) rename terminal21-ui-std/src/main/scala/org/terminal21/client/{ => components}/CachedCalculation.scala (87%) rename terminal21-ui-std/src/main/scala/org/terminal21/client/{ => components}/Calculation.scala (93%) diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala index 3679b95e..ca4b22ed 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala @@ -2,11 +2,11 @@ package org.terminal21.sparklib.calculations import functions.fibers.FiberExecutor import org.apache.commons.io.FileUtils -import org.apache.spark.sql.{Dataset, Encoder, SparkSession} +import org.apache.spark.sql.SparkSession import org.terminal21.client.components.UiElement.HasStyle import org.terminal21.client.components.chakra.* -import org.terminal21.client.components.{UiComponent, UiElement} -import org.terminal21.client.{CachedCalculation, ConnectedSession} +import org.terminal21.client.components.{CachedCalculation, UiComponent, UiElement} +import org.terminal21.client.ConnectedSession import org.terminal21.sparklib.util.Environment import java.io.File diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/CachedCalculation.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/CachedCalculation.scala similarity index 87% rename from terminal21-ui-std/src/main/scala/org/terminal21/client/CachedCalculation.scala rename to terminal21-ui-std/src/main/scala/org/terminal21/client/components/CachedCalculation.scala index cde68f8c..f06a232e 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/CachedCalculation.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/CachedCalculation.scala @@ -1,4 +1,4 @@ -package org.terminal21.client +package org.terminal21.client.components import functions.fibers.{Fiber, FiberExecutor} diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/Calculation.scala similarity index 93% rename from terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala rename to terminal21-ui-std/src/main/scala/org/terminal21/client/components/Calculation.scala index 11f11d4e..1358e221 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/Calculation.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/Calculation.scala @@ -1,4 +1,4 @@ -package org.terminal21.client +package org.terminal21.client.components import functions.fibers.{Fiber, FiberExecutor} diff --git a/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala b/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala index d2f30117..9fea22ac 100644 --- a/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala +++ b/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala @@ -6,6 +6,7 @@ import org.scalatest.matchers.should.Matchers.* import java.util.concurrent.atomic.{AtomicBoolean, AtomicInteger} import org.scalatest.concurrent.Eventually.* +import org.terminal21.client.components.Calculation class CalculationTest extends AnyFunSuiteLike: given executor: FiberExecutor = FiberExecutor() From 467b4806684a46ff8f45ec6aa4b9dab219604519 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Tue, 9 Jan 2024 13:20:09 +0000 Subject: [PATCH 067/144] - --- .../calculations/SparkCalculation.scala | 52 ++----------------- .../client/components/Calculation.scala | 2 +- .../client/components/StdUiCalculation.scala | 49 +++++++++++++++++ 3 files changed, 55 insertions(+), 48 deletions(-) create mode 100644 terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala index ca4b22ed..36ab24fe 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala @@ -5,7 +5,7 @@ import org.apache.commons.io.FileUtils import org.apache.spark.sql.SparkSession import org.terminal21.client.components.UiElement.HasStyle import org.terminal21.client.components.chakra.* -import org.terminal21.client.components.{CachedCalculation, UiComponent, UiElement} +import org.terminal21.client.components.{CachedCalculation, StdUiCalculation, UiComponent, UiElement} import org.terminal21.client.ConnectedSession import org.terminal21.sparklib.util.Environment @@ -21,13 +21,7 @@ import java.util.concurrent.atomic.AtomicBoolean * * Subclass this to create your own UI for a spark calculation, see StdUiSparkCalculation below. */ -abstract class SparkCalculation[OUT: ReadWriter]( - val key: String, - name: String, - @volatile var children: Seq[UiElement] -)(using executor: FiberExecutor, spark: SparkSession) - extends CachedCalculation[OUT] - with UiComponent: +trait SparkCalculation[OUT: ReadWriter](name: String)(using executor: FiberExecutor, spark: SparkSession) extends CachedCalculation[OUT] with UiComponent: private val rw = implicitly[ReadWriter[OUT]] private val rootFolder = s"${Environment.tmpDirectory}/spark-calculations" private val targetDir = s"$rootFolder/$name" @@ -53,45 +47,9 @@ abstract class SparkCalculation[OUT: ReadWriter]( override protected def calculation(): OUT = calculateOnce(nonCachedCalculation) abstract class StdUiSparkCalculation[OUT: ReadWriter]( - key: String, + val key: String, name: String, dataUi: UiElement with HasStyle )(using session: ConnectedSession, executor: FiberExecutor, spark: SparkSession) - extends SparkCalculation[OUT](key, name, Nil): - val badge = Badge() - private val running = new AtomicBoolean(false) - val recalc = Button(text = "Recalculate", size = Some("sm"), leftIcon = Some(RepeatIcon())).onClick: () => - if running.compareAndSet(false, true) then - try - badge.text = "Invalidating cache ..." - session.render() - invalidateCache() - run() - finally running.set(false) - - val header = Box(bg = "green", p = 4).withChildren( - HStack().withChildren( - Text(text = name), - badge, - recalc - ) - ) - children = Seq( - header, - dataUi - ) - - override protected def whenResultsNotReady(): Unit = - badge.text = "Calculating" - badge.colorScheme = Some("purple") - recalc.isDisabled = Some(true) - dataUi.style = dataUi.style + ("filter" -> "grayscale(100%)") - session.render() - super.whenResultsNotReady() - - override protected def whenResultsReady(results: OUT): Unit = - badge.text = "Ready" - badge.colorScheme = None - recalc.isDisabled = Some(false) - dataUi.style = dataUi.style - "filter" - session.render() + extends SparkCalculation[OUT](name) + with StdUiCalculation[OUT](name, dataUi) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/Calculation.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/Calculation.scala index 1358e221..47f23619 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/Calculation.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/Calculation.scala @@ -4,7 +4,7 @@ import functions.fibers.{Fiber, FiberExecutor} import java.util.concurrent.CountDownLatch -abstract class Calculation[OUT](using executor: FiberExecutor): +trait Calculation[OUT](using executor: FiberExecutor): protected def calculation(): OUT protected def whenResultsNotReady(): Unit = () protected def whenResultsReady(results: OUT): Unit = () diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala new file mode 100644 index 00000000..ac7d499f --- /dev/null +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala @@ -0,0 +1,49 @@ +package org.terminal21.client.components + +import functions.fibers.FiberExecutor +import org.terminal21.client.ConnectedSession +import org.terminal21.client.components.UiElement.HasStyle +import org.terminal21.client.components.chakra.* + +import java.util.concurrent.atomic.AtomicBoolean + +trait StdUiCalculation[OUT]( + name: String, + dataUi: UiElement with HasStyle +)(using session: ConnectedSession, executor: FiberExecutor) + extends Calculation[OUT] + with UiComponent: + val badge = Badge() + private val running = new AtomicBoolean(false) + val recalc = Button(text = "Recalculate", size = Some("sm"), leftIcon = Some(RepeatIcon())).onClick: () => + if running.compareAndSet(false, true) then + try + run() + finally running.set(false) + + val header = Box(bg = "green", p = 4).withChildren( + HStack().withChildren( + Text(text = name), + badge, + recalc + ) + ) + @volatile var children: Seq[UiElement] = Seq( + header, + dataUi + ) + + override protected def whenResultsNotReady(): Unit = + badge.text = "Calculating" + badge.colorScheme = Some("purple") + recalc.isDisabled = Some(true) + dataUi.style = dataUi.style + ("filter" -> "grayscale(100%)") + session.render() + super.whenResultsNotReady() + + override protected def whenResultsReady(results: OUT): Unit = + badge.text = "Ready" + badge.colorScheme = None + recalc.isDisabled = Some(false) + dataUi.style = dataUi.style - "filter" + session.render() From 844770901ee34fe01e3a3f5e80bebedcfd42286d Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Tue, 9 Jan 2024 13:22:21 +0000 Subject: [PATCH 068/144] - --- .../org/terminal21/client/components/StdUiCalculation.scala | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala index ac7d499f..017767d8 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala @@ -7,6 +7,11 @@ import org.terminal21.client.components.chakra.* import java.util.concurrent.atomic.AtomicBoolean +/** Creates a standard UI for a calculation which may take time. While the calculation runs, the UI is grayed out, including the dataUi component. When the + * calculation completes, it allows for updating the dataUi component. + * @tparam OUT + * the return value of the calculation. + */ trait StdUiCalculation[OUT]( name: String, dataUi: UiElement with HasStyle From 12c0d46a34cf104947d77a9de9c868cb915a2a7c Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Tue, 9 Jan 2024 13:24:51 +0000 Subject: [PATCH 069/144] - --- .../terminal21/client/components/chakra/ChakraElement.scala | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala index 131df8bc..a85a2dfe 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala @@ -4,6 +4,10 @@ import org.terminal21.client.components.UiElement.{HasChildren, HasEventHandler, import org.terminal21.client.components.{Keys, UiElement} import org.terminal21.client.{OnChangeBooleanEventHandler, OnChangeEventHandler, OnClickEventHandler} +/** The chakra-react based components, for a complete (though bit rough) example please see + * https://github.com/kostaskougios/terminal21-restapi/blob/main/examples/src/main/scala/tests/ChakraComponents.scala and it's related scala files under + * https://github.com/kostaskougios/terminal21-restapi/tree/main/examples/src/main/scala/tests/chakra + */ sealed trait ChakraElement extends UiElement with HasStyle /** https://chakra-ui.com/docs/components/button From ed1c87ca1ad75d3aa3282132d1345b6c31cad861 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Tue, 9 Jan 2024 13:28:52 +0000 Subject: [PATCH 070/144] - --- img.png | Bin 54743 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 img.png diff --git a/img.png b/img.png deleted file mode 100644 index ba3ef1278d25759172f68d2dee7e66027cd7ec11..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54743 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%0A=!U;~><};$I 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 Q_z&m;gGmRg1HZWVUjcgdHUIzs From 2ee2eb4552a2c189bc2c4b132c75886a9dae56a4 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Tue, 9 Jan 2024 15:04:51 +0000 Subject: [PATCH 071/144] - --- build.sbt | 11 +++++++++- .../client/components/nivo/Axis.scala | 10 +++++++++ .../client/components/nivo/Margin.scala | 3 +++ .../client/components/nivo/NivoElement.scala | 22 +++++++++++++++++++ .../client/components/nivo/NivoLib.scala | 11 ++++++++++ .../components/nivo/ResponsiveLineData.scala | 12 ++++++++++ .../client/components/nivo/Scale.scala | 12 ++++++++++ .../terminal21/client/ConnectedSession.scala | 5 +++-- .../org/terminal21/client/Sessions.scala | 6 +++-- .../client/components/ComponentLib.scala | 6 +++++ .../client/components/UiElementEncoding.scala | 9 +++++++- .../client/ConnectedSessionMock.scala | 3 ++- 12 files changed, 103 insertions(+), 7 deletions(-) create mode 100644 terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Axis.scala create mode 100644 terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Margin.scala create mode 100644 terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala create mode 100644 terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoLib.scala create mode 100644 terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/ResponsiveLineData.scala create mode 100644 terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Scale.scala create mode 100644 terminal21-ui-std/src/main/scala/org/terminal21/client/components/ComponentLib.scala diff --git a/build.sbt b/build.sbt index fe02696c..357af24c 100644 --- a/build.sbt +++ b/build.sbt @@ -144,7 +144,16 @@ lazy val examples = project commonSettings, libraryDependencies ++= Seq(ScalaTest, LogBack) ) - .dependsOn(`terminal21-ui-std`) + .dependsOn(`terminal21-ui-std`, `terminal21-nivo`) + +lazy val `terminal21-nivo` = project + .settings( + commonSettings, + libraryDependencies ++= Seq( + ScalaTest + ) + ) + .dependsOn(`terminal21-ui-std` % "compile->compile;test->test") lazy val `terminal21-spark` = project .settings( diff --git a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Axis.scala b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Axis.scala new file mode 100644 index 00000000..4bad6fcc --- /dev/null +++ b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Axis.scala @@ -0,0 +1,10 @@ +package org.terminal21.client.components.nivo + +case class Axis( + tickSize: Int = 5, + tickPadding: Int = 5, + tickRotation: Int = 0, + legend: String = "CHANGEME axis.legend", + legendOffset: Int = 0, + legendPosition: String = "middle" +) diff --git a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Margin.scala b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Margin.scala new file mode 100644 index 00000000..d30a7519 --- /dev/null +++ b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Margin.scala @@ -0,0 +1,3 @@ +package org.terminal21.client.components.nivo + +case class Margin(top: Int = 50, right: Int = 50, bottom: Int = 50, left: Int = 50) diff --git a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala new file mode 100644 index 00000000..532c1d5d --- /dev/null +++ b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala @@ -0,0 +1,22 @@ +package org.terminal21.client.components.nivo + +import org.terminal21.client.components.{Keys, UiElement} + +sealed trait NivoElement extends UiElement + +case class ResponsiveLine( + key: String = Keys.nextKey, + data: Seq[ResponsiveLineData] = Nil, + margin: Margin = Margin(), + xScale: Scale = Scale.Point, + yScale: Scale = Scale(), + yFormat: String = " >-.2f", + axisTop: Option[Axis] = None, + axisRight: Option[Axis] = None, + axisBottom: Option[Axis] = Some(Axis(legend = "y", legendOffset = 36)), + axisLeft: Option[Axis] = Some(Axis(legend = "x", legendOffset = -40)), + pointSize: Int = 10, + pointBorderWidth: Int = 2, + pointLabelYOffset: Int = -12, + useMesh: Boolean = true +) extends NivoElement diff --git a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoLib.scala b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoLib.scala new file mode 100644 index 00000000..7af0ffa2 --- /dev/null +++ b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoLib.scala @@ -0,0 +1,11 @@ +package org.terminal21.client.components.nivo + +import io.circe.{Encoder, Json} +import org.terminal21.client.components.{ComponentLib, UiElement} +import io.circe.* +import io.circe.generic.auto.* +import io.circe.syntax.* + +object NivoLib extends ComponentLib: + override def toJson(using Encoder[UiElement]): PartialFunction[UiElement, Json] = + case n: NivoElement => n.asJson.mapObject(o => o.add("type", "Nivo".asJson)) diff --git a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/ResponsiveLineData.scala b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/ResponsiveLineData.scala new file mode 100644 index 00000000..1dbe70bd --- /dev/null +++ b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/ResponsiveLineData.scala @@ -0,0 +1,12 @@ +package org.terminal21.client.components.nivo + +case class ResponsiveLineData( + id: String, + color: String = "hsl(88, 70%, 50%)", + data: Seq[XYCoordinates] = Nil +) + +case class XYCoordinates( + x: String, + y: Float +) diff --git a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Scale.scala b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Scale.scala new file mode 100644 index 00000000..1e94b645 --- /dev/null +++ b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Scale.scala @@ -0,0 +1,12 @@ +package org.terminal21.client.components.nivo + +case class Scale( + `type`: String = "linear", + min: Option[String] = Some("auto"), + max: Option[String] = Some("auto"), + stacked: Option[Boolean] = Some(false), + reverse: Option[Boolean] = Some(false) +) + +object Scale: + val Point = Scale(`type` = "point", None, None, None, None) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala index e8722f5c..ff25e342 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala @@ -6,14 +6,14 @@ import io.circe.syntax.* import org.slf4j.LoggerFactory import org.terminal21.client.components.UiElement import org.terminal21.client.components.UiElement.{HasEventHandler, allDeep} -import org.terminal21.client.components.UiElementEncoding.uiElementEncoder +import org.terminal21.client.components.UiElementEncoding import org.terminal21.model.* import org.terminal21.ui.std.SessionsService import java.util.concurrent.{CountDownLatch, TimeUnit} import scala.annotation.tailrec -class ConnectedSession(val session: Session, val serverUrl: String, sessionsService: SessionsService, onCloseHandler: () => Unit): +class ConnectedSession(val session: Session, encoding: UiElementEncoding, val serverUrl: String, sessionsService: SessionsService, onCloseHandler: () => Unit): private val logger = LoggerFactory.getLogger(getClass) private var elements = List.empty[UiElement] @@ -87,6 +87,7 @@ class ConnectedSession(val session: Session, val serverUrl: String, sessionsServ def allElements: Seq[UiElement] = synchronized(elements) private def toJson: JsonObject = + import encoding.given val elementsCopy = allElements val json = for e <- elementsCopy diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/Sessions.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/Sessions.scala index 4757ebe8..bccf1592 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/Sessions.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/Sessions.scala @@ -4,13 +4,14 @@ import functions.fibers.FiberExecutor import functions.helidon.transport.HelidonTransport import io.helidon.webclient.api.WebClient import io.helidon.webclient.websocket.WsClient +import org.terminal21.client.components.{ComponentLib, StdElementEncoding, UiElementEncoding} import org.terminal21.config.Config import org.terminal21.ui.std.SessionsServiceCallerFactory import java.util.concurrent.atomic.AtomicBoolean object Sessions: - def withNewSession[R](id: String, name: String)(f: ConnectedSession => R): R = + def withNewSession[R](id: String, name: String, componentLibs: ComponentLib*)(f: ConnectedSession => R): R = val config = Config.Default val serverUrl = s"http://${config.host}:${config.port}" val client = WebClient.builder @@ -27,7 +28,8 @@ object Sessions: def terminate(): Unit = isStopped.set(true) - val connectedSession = ConnectedSession(session, serverUrl, sessionsService, terminate) + val encoding = new UiElementEncoding(Seq(StdElementEncoding) ++ componentLibs) + val connectedSession = ConnectedSession(session, encoding, serverUrl, sessionsService, terminate) FiberExecutor.withFiberExecutor: executor => val listener = new ClientEventsWsListener(wsClient, connectedSession, executor) listener.start() diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/ComponentLib.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/ComponentLib.scala new file mode 100644 index 00000000..dd47bf23 --- /dev/null +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/ComponentLib.scala @@ -0,0 +1,6 @@ +package org.terminal21.client.components + +import io.circe.{Encoder, Json} + +trait ComponentLib: + def toJson(using Encoder[UiElement]): PartialFunction[UiElement, Json] diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElementEncoding.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElementEncoding.scala index 1161256c..54495b3f 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElementEncoding.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElementEncoding.scala @@ -5,8 +5,15 @@ import io.circe.generic.auto.* import io.circe.syntax.* import org.terminal21.client.components.chakra.{Box, ChakraElement} -object UiElementEncoding: +class UiElementEncoding(libs: Seq[ComponentLib]): given uiElementEncoder: Encoder[UiElement] = + a => + val cl = libs.find(_.toJson.isDefinedAt(a)).getOrElse(throw new IllegalStateException(s"Unknown element $a , did you register a ComponentLib for it?")) + cl.toJson(a) + +object StdElementEncoding extends ComponentLib: + + override def toJson(using Encoder[UiElement]): PartialFunction[UiElement, Json] = case std: StdElement => std.asJson.mapObject(o => o.add("type", "Std".asJson)) case c: ChakraElement => c.asJson.mapObject(o => o.add("type", "Chakra".asJson)) case c: UiComponent => diff --git a/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionMock.scala b/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionMock.scala index 1723192a..ca667af7 100644 --- a/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionMock.scala +++ b/terminal21-ui-std/src/test/scala/org/terminal21/client/ConnectedSessionMock.scala @@ -1,10 +1,11 @@ package org.terminal21.client import org.mockito.Mockito.mock +import org.terminal21.client.components.{StdElementEncoding, UiElementEncoding} import org.terminal21.model.CommonModelBuilders.session import org.terminal21.ui.std.SessionsService object ConnectedSessionMock: def newConnectedSessionMock: ConnectedSession = val sessionsService = mock(classOf[SessionsService]) - new ConnectedSession(session(), "test", sessionsService, () => ()) + new ConnectedSession(session(), new UiElementEncoding(Seq(StdElementEncoding)), "test", sessionsService, () => ()) From 1d60acdf8a335e3ee4dcbe0ba99229c111d32e49 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Tue, 9 Jan 2024 15:16:15 +0000 Subject: [PATCH 072/144] - --- .../src/main/scala/tests/NivoComponents.scala | 22 +++++++++++++++ .../client/components/nivo/NivoElement.scala | 28 +++++++++---------- .../{ResponsiveLineData.scala => Serie.scala} | 8 +++--- 3 files changed, 40 insertions(+), 18 deletions(-) create mode 100644 examples/src/main/scala/tests/NivoComponents.scala rename terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/{ResponsiveLineData.scala => Serie.scala} (54%) diff --git a/examples/src/main/scala/tests/NivoComponents.scala b/examples/src/main/scala/tests/NivoComponents.scala new file mode 100644 index 00000000..9b11e0d8 --- /dev/null +++ b/examples/src/main/scala/tests/NivoComponents.scala @@ -0,0 +1,22 @@ +package tests + +import org.terminal21.client.components.nivo.* +import org.terminal21.client.* +import org.terminal21.client.components.* + +@main def nivoComponents(): Unit = + Sessions.withNewSession("nivo-components", "Nivo Components", NivoLib): session => + given ConnectedSession = session + Seq( + ResponsiveLine( + data = Seq( + Serie( + "japan", + data = Seq( + Datum("plane", "262.5f") + ) + ) + ) + ) + ).render() + session.waitTillUserClosesSession() diff --git a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala index 532c1d5d..6954abce 100644 --- a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala +++ b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala @@ -5,18 +5,18 @@ import org.terminal21.client.components.{Keys, UiElement} sealed trait NivoElement extends UiElement case class ResponsiveLine( - key: String = Keys.nextKey, - data: Seq[ResponsiveLineData] = Nil, - margin: Margin = Margin(), - xScale: Scale = Scale.Point, - yScale: Scale = Scale(), - yFormat: String = " >-.2f", - axisTop: Option[Axis] = None, - axisRight: Option[Axis] = None, - axisBottom: Option[Axis] = Some(Axis(legend = "y", legendOffset = 36)), - axisLeft: Option[Axis] = Some(Axis(legend = "x", legendOffset = -40)), - pointSize: Int = 10, - pointBorderWidth: Int = 2, - pointLabelYOffset: Int = -12, - useMesh: Boolean = true + key: String = Keys.nextKey, + data: Seq[Serie] = Nil, + margin: Margin = Margin(), + xScale: Scale = Scale.Point, + yScale: Scale = Scale(), + yFormat: String = " >-.2f", + axisTop: Option[Axis] = None, + axisRight: Option[Axis] = None, + axisBottom: Option[Axis] = Some(Axis(legend = "y", legendOffset = 36)), + axisLeft: Option[Axis] = Some(Axis(legend = "x", legendOffset = -40)), + pointSize: Int = 10, + pointBorderWidth: Int = 2, + pointLabelYOffset: Int = -12, + useMesh: Boolean = true ) extends NivoElement diff --git a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/ResponsiveLineData.scala b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Serie.scala similarity index 54% rename from terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/ResponsiveLineData.scala rename to terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Serie.scala index 1dbe70bd..c485ab4f 100644 --- a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/ResponsiveLineData.scala +++ b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Serie.scala @@ -1,12 +1,12 @@ package org.terminal21.client.components.nivo -case class ResponsiveLineData( +case class Serie( id: String, color: String = "hsl(88, 70%, 50%)", - data: Seq[XYCoordinates] = Nil + data: Seq[Datum] = Nil ) -case class XYCoordinates( +case class Datum( x: String, - y: Float + y: String ) From 1e60ecee14788bc28b23e3994cbe3d44c597c660 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Tue, 9 Jan 2024 16:43:45 +0000 Subject: [PATCH 073/144] - --- .../src/main/scala/tests/NivoComponents.scala | 4 ++- .../client/components/nivo/NivoElement.scala | 28 +++++++++---------- .../client/components/nivo/Serie.scala | 21 ++++++++++++-- 3 files changed, 36 insertions(+), 17 deletions(-) diff --git a/examples/src/main/scala/tests/NivoComponents.scala b/examples/src/main/scala/tests/NivoComponents.scala index 9b11e0d8..9217efae 100644 --- a/examples/src/main/scala/tests/NivoComponents.scala +++ b/examples/src/main/scala/tests/NivoComponents.scala @@ -13,7 +13,9 @@ import org.terminal21.client.components.* Serie( "japan", data = Seq( - Datum("plane", "262.5f") + StringIntDatum("plane", 262), + StringIntDatum("helicopter", 26), + StringIntDatum("boat", 43) ) ) ) diff --git a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala index 6954abce..6d8fd653 100644 --- a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala +++ b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala @@ -5,18 +5,18 @@ import org.terminal21.client.components.{Keys, UiElement} sealed trait NivoElement extends UiElement case class ResponsiveLine( - key: String = Keys.nextKey, - data: Seq[Serie] = Nil, - margin: Margin = Margin(), - xScale: Scale = Scale.Point, - yScale: Scale = Scale(), - yFormat: String = " >-.2f", - axisTop: Option[Axis] = None, - axisRight: Option[Axis] = None, - axisBottom: Option[Axis] = Some(Axis(legend = "y", legendOffset = 36)), - axisLeft: Option[Axis] = Some(Axis(legend = "x", legendOffset = -40)), - pointSize: Int = 10, - pointBorderWidth: Int = 2, - pointLabelYOffset: Int = -12, - useMesh: Boolean = true + key: String = Keys.nextKey, + data: Seq[Serie] = Nil, + margin: Margin = Margin(), + xScale: Scale = Scale.Point, + yScale: Scale = Scale(), + yFormat: String = " >-.2f", + axisTop: Option[Axis] = None, + axisRight: Option[Axis] = None, + axisBottom: Option[Axis] = Some(Axis(legend = "y", legendOffset = 36)), + axisLeft: Option[Axis] = Some(Axis(legend = "x", legendOffset = -40)), + pointSize: Int = 10, + pointBorderWidth: Int = 2, + pointLabelYOffset: Int = -12, + useMesh: Boolean = true ) extends NivoElement diff --git a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Serie.scala b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Serie.scala index c485ab4f..41b5a122 100644 --- a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Serie.scala +++ b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Serie.scala @@ -6,7 +6,24 @@ case class Serie( data: Seq[Datum] = Nil ) -case class Datum( +sealed trait Datum + +case class StringDatum( x: String, y: String -) +) extends Datum + +case class IntDatum( + x: Int, + y: Int +) extends Datum + +case class FloatDatum( + x: Float, + y: Float +) extends Datum + +case class StringIntDatum( + x: String, + y: Int +) extends Datum From f83d6c09af75ee003a564061b0616e380b7e606d Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Tue, 9 Jan 2024 17:01:42 +0000 Subject: [PATCH 074/144] - --- .../src/main/scala/tests/NivoComponents.scala | 6 ++-- .../client/components/nivo/NivoElement.scala | 31 +++++++++-------- .../client/components/nivo/Serie.scala | 33 ++++++++----------- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/examples/src/main/scala/tests/NivoComponents.scala b/examples/src/main/scala/tests/NivoComponents.scala index 9217efae..c40b343d 100644 --- a/examples/src/main/scala/tests/NivoComponents.scala +++ b/examples/src/main/scala/tests/NivoComponents.scala @@ -13,9 +13,9 @@ import org.terminal21.client.components.* Serie( "japan", data = Seq( - StringIntDatum("plane", 262), - StringIntDatum("helicopter", 26), - StringIntDatum("boat", 43) + Datum("plane", 262), + Datum("helicopter", 26), + Datum("boat", 43) ) ) ) diff --git a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala index 6d8fd653..2abb6815 100644 --- a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala +++ b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala @@ -4,19 +4,24 @@ import org.terminal21.client.components.{Keys, UiElement} sealed trait NivoElement extends UiElement +/** https://nivo.rocks/line/ + */ case class ResponsiveLine( key: String = Keys.nextKey, - data: Seq[Serie] = Nil, - margin: Margin = Margin(), - xScale: Scale = Scale.Point, - yScale: Scale = Scale(), - yFormat: String = " >-.2f", - axisTop: Option[Axis] = None, - axisRight: Option[Axis] = None, - axisBottom: Option[Axis] = Some(Axis(legend = "y", legendOffset = 36)), - axisLeft: Option[Axis] = Some(Axis(legend = "x", legendOffset = -40)), - pointSize: Int = 10, - pointBorderWidth: Int = 2, - pointLabelYOffset: Int = -12, - useMesh: Boolean = true + // to give width and height, we wrap the component in a wrapper element. Height must be provided + // for nivo components to be visible + @volatile var wrapperStyle: Map[String, String] = Map("height" -> "400px"), + @volatile var data: Seq[Serie] = Nil, + @volatile var margin: Margin = Margin(), + @volatile var xScale: Scale = Scale.Point, + @volatile var yScale: Scale = Scale(), + @volatile var yFormat: String = " >-.2f", + @volatile var axisTop: Option[Axis] = None, + @volatile var axisRight: Option[Axis] = None, + @volatile var axisBottom: Option[Axis] = Some(Axis(legend = "y", legendOffset = 36)), + @volatile var axisLeft: Option[Axis] = Some(Axis(legend = "x", legendOffset = -40)), + @volatile var pointSize: Int = 10, + @volatile var pointBorderWidth: Int = 2, + @volatile var pointLabelYOffset: Int = -12, + @volatile var useMesh: Boolean = true ) extends NivoElement diff --git a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Serie.scala b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Serie.scala index 41b5a122..8b94cea0 100644 --- a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Serie.scala +++ b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Serie.scala @@ -1,29 +1,24 @@ package org.terminal21.client.components.nivo +import io.circe.{Encoder, Json} + case class Serie( id: String, color: String = "hsl(88, 70%, 50%)", data: Seq[Datum] = Nil ) -sealed trait Datum - -case class StringDatum( - x: String, - y: String -) extends Datum - -case class IntDatum( - x: Int, - y: Int -) extends Datum +case class Datum( + x: String | Int | Float, + y: String | Int | Float +) -case class FloatDatum( - x: Float, - y: Float -) extends Datum +object Datum: + private def toJson(name: String, x: String | Int | Float): (String, Json) = x match + case s: String => (name, Json.fromString(s)) + case i: Int => (name, Json.fromInt(i)) + case f: Float => (name, Json.fromFloat(f).get) -case class StringIntDatum( - x: String, - y: Int -) extends Datum + given Encoder[Datum] = + case Datum(x, y) => + Json.obj(toJson("x", x), toJson("y", y)) From 4a13c99f4658fb872b4a5664ce30a5b606024103 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Tue, 9 Jan 2024 17:15:18 +0000 Subject: [PATCH 075/144] - --- examples/src/main/scala/tests/NivoComponents.scala | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/examples/src/main/scala/tests/NivoComponents.scala b/examples/src/main/scala/tests/NivoComponents.scala index c40b343d..ef8e0593 100644 --- a/examples/src/main/scala/tests/NivoComponents.scala +++ b/examples/src/main/scala/tests/NivoComponents.scala @@ -17,8 +17,18 @@ import org.terminal21.client.components.* Datum("helicopter", 26), Datum("boat", 43) ) + ), + Serie( + "france", + "hsl(186, 70%, 50%)", + Seq( + Datum("plane", 271), + Datum("helicopter", 31), + Datum("boat", 27) + ) ) - ) + ), + yScale = Scale(stacked = Some(true)) ) ).render() session.waitTillUserClosesSession() From 437d02c324e168db9cadc1b61f04e9534ba487d7 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Tue, 9 Jan 2024 17:24:59 +0000 Subject: [PATCH 076/144] - --- examples/src/main/scala/tests/NivoComponents.scala | 4 +++- .../org/terminal21/client/components/nivo/NivoElement.scala | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/src/main/scala/tests/NivoComponents.scala b/examples/src/main/scala/tests/NivoComponents.scala index ef8e0593..367e4aae 100644 --- a/examples/src/main/scala/tests/NivoComponents.scala +++ b/examples/src/main/scala/tests/NivoComponents.scala @@ -28,7 +28,9 @@ import org.terminal21.client.components.* ) ) ), - yScale = Scale(stacked = Some(true)) + yScale = Scale(stacked = Some(true)), + axisBottom = Some(Axis(tickSize = 5, tickPadding = 5, tickRotation = 0, legend = "transportation", legendOffset = 36, legendPosition = "middle")), + axisLeft = Some(Axis(tickSize = 5, tickPadding = 5, tickRotation = 0, legend = "count", legendOffset = -40, legendPosition = "middle")) ) ).render() session.waitTillUserClosesSession() diff --git a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala index 2abb6815..ab6eefb6 100644 --- a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala +++ b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala @@ -21,7 +21,9 @@ case class ResponsiveLine( @volatile var axisBottom: Option[Axis] = Some(Axis(legend = "y", legendOffset = 36)), @volatile var axisLeft: Option[Axis] = Some(Axis(legend = "x", legendOffset = -40)), @volatile var pointSize: Int = 10, + @volatile var pointColor: Map[String, String] = Map("theme" -> "background"), @volatile var pointBorderWidth: Int = 2, + @volatile var pointBorderColor: Map[String, String] = Map("from" -> "serieColor"), @volatile var pointLabelYOffset: Int = -12, @volatile var useMesh: Boolean = true ) extends NivoElement From 8fe8fa05dca7dc50784a697181f741bfce0d03dd Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Tue, 9 Jan 2024 18:55:14 +0000 Subject: [PATCH 077/144] - --- Readme.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Readme.md b/Readme.md index 71cfd4aa..bb182964 100644 --- a/Readme.md +++ b/Readme.md @@ -101,7 +101,7 @@ bouncing-ball.sc csv-editor.sc csv-viewer.sc hello-world.sc postit.sc # Mutability -terminal21 ui components are shamelessly mutable. This is a decision choice (for now) because of how much more simple code is this way. I.e. +terminal21 ui components are mutable. This is a decision choice (for now) because of how much more simple code is this way. I.e. changing the text of a paragraph on an event handler is as simple as : ```scala @@ -113,8 +113,6 @@ The equivalent immutable code would be (at least) p.copy(text= "new text") ``` -In the immutable version, the graph of components wouldn't reflect the change and there would be more work needed to do that. - Also by default some component values (like input boxes) are changed by the user. These changes are reflected in the component graph, something that would be a lot harder if the graph was immutable. From 794bcf2858979fb83df2a560f7fcdb018a8aae69 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Tue, 9 Jan 2024 20:13:18 +0000 Subject: [PATCH 078/144] - --- .../src/main/scala/tests/NivoComponents.scala | 5 ++++- .../client/components/nivo/Effect.scala | 9 +++++++++ .../client/components/nivo/Legend.scala | 18 ++++++++++++++++++ .../client/components/nivo/NivoElement.scala | 5 +++-- 4 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Effect.scala create mode 100644 terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Legend.scala diff --git a/examples/src/main/scala/tests/NivoComponents.scala b/examples/src/main/scala/tests/NivoComponents.scala index 367e4aae..57e084ce 100644 --- a/examples/src/main/scala/tests/NivoComponents.scala +++ b/examples/src/main/scala/tests/NivoComponents.scala @@ -30,7 +30,10 @@ import org.terminal21.client.components.* ), yScale = Scale(stacked = Some(true)), axisBottom = Some(Axis(tickSize = 5, tickPadding = 5, tickRotation = 0, legend = "transportation", legendOffset = 36, legendPosition = "middle")), - axisLeft = Some(Axis(tickSize = 5, tickPadding = 5, tickRotation = 0, legend = "count", legendOffset = -40, legendPosition = "middle")) + axisLeft = Some(Axis(tickSize = 5, tickPadding = 5, tickRotation = 0, legend = "count", legendOffset = -40, legendPosition = "middle")), + legends = Seq( + Legend() + ) ) ).render() session.waitTillUserClosesSession() diff --git a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Effect.scala b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Effect.scala new file mode 100644 index 00000000..2a7edaf4 --- /dev/null +++ b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Effect.scala @@ -0,0 +1,9 @@ +package org.terminal21.client.components.nivo + +case class Effect( + on: String = "hover", + style: Map[String, String] = Map( + "itemBackground" -> "rgba(0, 0, 0, .03)", + "itemOpacity" -> "1" + ) +) diff --git a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Legend.scala b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Legend.scala new file mode 100644 index 00000000..7f1647ab --- /dev/null +++ b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Legend.scala @@ -0,0 +1,18 @@ +package org.terminal21.client.components.nivo + +case class Legend( + anchor: String = "bottom-right", + direction: String = "column", + justify: Boolean = false, + translateX: Int = 100, + translateY: Int = 0, + itemsSpacing: Int = 0, + itemDirection: String = "left-to-right", + itemWidth: Int = 80, + itemHeight: Int = 20, + itemOpacity: Float = 0.75, + symbolSize: Int = 12, + symbolShape: String = "circle", + symbolBorderColor: String = "rgba(0, 0, 0, .5)", + effects: Seq[Effect] = Seq(Effect()) +) diff --git a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala index ab6eefb6..cabef707 100644 --- a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala +++ b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala @@ -12,7 +12,7 @@ case class ResponsiveLine( // for nivo components to be visible @volatile var wrapperStyle: Map[String, String] = Map("height" -> "400px"), @volatile var data: Seq[Serie] = Nil, - @volatile var margin: Margin = Margin(), + @volatile var margin: Margin = Margin(right = 110), @volatile var xScale: Scale = Scale.Point, @volatile var yScale: Scale = Scale(), @volatile var yFormat: String = " >-.2f", @@ -25,5 +25,6 @@ case class ResponsiveLine( @volatile var pointBorderWidth: Int = 2, @volatile var pointBorderColor: Map[String, String] = Map("from" -> "serieColor"), @volatile var pointLabelYOffset: Int = -12, - @volatile var useMesh: Boolean = true + @volatile var useMesh: Boolean = true, + @volatile var legends: Seq[Legend] = Nil ) extends NivoElement From e5b5d36b49863408a62880a8c99031c7994a0e86 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Tue, 9 Jan 2024 20:36:26 +0000 Subject: [PATCH 079/144] - --- build.sbt | 2 +- .../client/components/nivo/NivoElement.scala | 4 ++- .../terminal21/sparklib/SparkSessions.scala | 9 +++-- .../sparklib/endtoend/SparkBasics.scala | 33 +++++++++++++++++-- .../client/components/UiElementEncoding.scala | 5 ++- 5 files changed, 44 insertions(+), 9 deletions(-) diff --git a/build.sbt b/build.sbt index 357af24c..e3a8a61b 100644 --- a/build.sbt +++ b/build.sbt @@ -166,4 +166,4 @@ lazy val `terminal21-spark` = project LogBack % Test ) ++ SparkScala3Fix ) - .dependsOn(`terminal21-ui-std` % "compile->compile;test->test") + .dependsOn(`terminal21-ui-std` % "compile->compile;test->test", `terminal21-nivo` % Test) diff --git a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala index cabef707..29f5a3ab 100644 --- a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala +++ b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala @@ -1,5 +1,6 @@ package org.terminal21.client.components.nivo +import org.terminal21.client.components.UiElement.HasStyle import org.terminal21.client.components.{Keys, UiElement} sealed trait NivoElement extends UiElement @@ -10,7 +11,7 @@ case class ResponsiveLine( key: String = Keys.nextKey, // to give width and height, we wrap the component in a wrapper element. Height must be provided // for nivo components to be visible - @volatile var wrapperStyle: Map[String, String] = Map("height" -> "400px"), + @volatile var style: Map[String, String] = Map("height" -> "400px"), @volatile var data: Seq[Serie] = Nil, @volatile var margin: Margin = Margin(right = 110), @volatile var xScale: Scale = Scale.Point, @@ -28,3 +29,4 @@ case class ResponsiveLine( @volatile var useMesh: Boolean = true, @volatile var legends: Seq[Legend] = Nil ) extends NivoElement + with HasStyle diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/SparkSessions.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/SparkSessions.scala index 683583c8..f7306ac1 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/SparkSessions.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/SparkSessions.scala @@ -1,6 +1,7 @@ package org.terminal21.sparklib import org.apache.spark.sql.SparkSession +import org.terminal21.client.components.ComponentLib import org.terminal21.client.{ConnectedSession, Sessions} import scala.util.Using @@ -34,7 +35,9 @@ object SparkSessions: * @return * whatever f returns */ - def newTerminal21WithSparkSession[R](spark: SparkSession, id: String, name: String)(f: (SparkSession, ConnectedSession) => R): R = - Sessions.withNewSession(id, name): terminal21Session => - Using.resource(spark): sp => + def newTerminal21WithSparkSession[R](spark: SparkSession, id: String, name: String, componentLibs: ComponentLib*)( + f: (SparkSession, ConnectedSession) => R + ): R = + Sessions.withNewSession(id, name, componentLibs: _*): terminal21Session => + Using.resource(spark): _ => f(spark, terminal21Session) diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index 6e3e5823..34be7955 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -1,15 +1,17 @@ package org.terminal21.sparklib.endtoend +import org.apache.commons.lang3.StringUtils import org.apache.spark.sql.{Dataset, SparkSession} import org.terminal21.client.components.* import org.terminal21.client.components.chakra.* +import org.terminal21.client.components.nivo.* import org.terminal21.client.{*, given} import org.terminal21.sparklib.* import org.terminal21.sparklib.endtoend.model.CodeFile import org.terminal21.sparklib.endtoend.model.CodeFile.scanSourceFiles @main def sparkBasics(): Unit = - SparkSessions.newTerminal21WithSparkSession(SparkSessions.newSparkSession(), "spark-basics", "Spark Basics"): (spark, session) => + SparkSessions.newTerminal21WithSparkSession(SparkSessions.newSparkSession(), "spark-basics", "Spark Basics", NivoLib): (spark, session) => given ConnectedSession = session given SparkSession = spark import scala3encoders.given @@ -20,7 +22,8 @@ import org.terminal21.sparklib.endtoend.model.CodeFile.scanSourceFiles val sortedFilesTable = QuickTable().headers(headers: _*).caption("Files sorted by createdDate and numOfWords") val codeFilesTable = QuickTable().headers(headers: _*).caption("Unsorted files") - val sortedCalc = sortedSourceFiles(sourceFiles()).visualize("Sorted files", sortedFilesTable): results => + val sortedSourceFilesDS = sortedSourceFiles(sourceFiles()) + val sortedCalc = sortedSourceFilesDS.visualize("Sorted files", sortedFilesTable): results => val tableRows = results.take(3).toList.map(_.toData) sortedFilesTable.rows(tableRows) @@ -36,10 +39,34 @@ import org.terminal21.sparklib.endtoend.model.CodeFile.scanSourceFiles val tableRows = results.take(4).toList sortedFilesTableDF.rows(tableRows.toUiTable) + val chart = ResponsiveLine( + data = Seq( + Serie( + "Scala", + data = Seq( + Datum("plane", 262), + Datum("helicopter", 26), + Datum("boat", 43) + ) + ) + ), + axisBottom = Some(Axis(tickSize = 5, tickPadding = 5, tickRotation = 0, legend = "Class", legendOffset = 36, legendPosition = "middle")), + axisLeft = Some(Axis(tickSize = 5, tickPadding = 5, tickRotation = 0, legend = "Count", legendOffset = -40, legendPosition = "middle")), + legends = Seq( + Legend() + ) + ) + + val sourceFileChart = sortedSourceFilesDS.visualize("Biggest Code Files", chart): results => + val data = results.take(10).map(cf => Datum(StringUtils.substringBeforeLast(cf.name, ".scala"), cf.numOfLines)).toList + chart.data = Seq(Serie("Scala", data = data)) + session.render() + Seq( codeFilesCalculation, sortedCalc, - sortedCalcDF + sortedCalcDF, + sourceFileChart ).render() session.waitTillUserClosesSession() diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElementEncoding.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElementEncoding.scala index 54495b3f..f046b397 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElementEncoding.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElementEncoding.scala @@ -8,7 +8,10 @@ import org.terminal21.client.components.chakra.{Box, ChakraElement} class UiElementEncoding(libs: Seq[ComponentLib]): given uiElementEncoder: Encoder[UiElement] = a => - val cl = libs.find(_.toJson.isDefinedAt(a)).getOrElse(throw new IllegalStateException(s"Unknown element $a , did you register a ComponentLib for it?")) + val cl = + libs + .find(_.toJson.isDefinedAt(a)) + .getOrElse(throw new IllegalStateException(s"Unknown ui element, did you forget to register a Lib when creating a session? Component: $a")) cl.toJson(a) object StdElementEncoding extends ComponentLib: From dc4bd64bcb478860a251831e7c6f4fadbd783a47 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Wed, 10 Jan 2024 16:26:16 +0000 Subject: [PATCH 080/144] - --- .../scala/org/terminal21/sparklib/endtoend/SparkBasics.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index 34be7955..39f35530 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -50,8 +50,8 @@ import org.terminal21.sparklib.endtoend.model.CodeFile.scanSourceFiles ) ) ), - axisBottom = Some(Axis(tickSize = 5, tickPadding = 5, tickRotation = 0, legend = "Class", legendOffset = 36, legendPosition = "middle")), - axisLeft = Some(Axis(tickSize = 5, tickPadding = 5, tickRotation = 0, legend = "Count", legendOffset = -40, legendPosition = "middle")), + axisBottom = Some(Axis(legend = "Class", legendOffset = 36)), + axisLeft = Some(Axis(legend = "Count", legendOffset = -40)), legends = Seq( Legend() ) From c54c77c470ab827725e6ac77d35b772e7d4e7bb7 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Wed, 10 Jan 2024 18:11:27 +0000 Subject: [PATCH 081/144] - --- Readme.md | 2 ++ docs/images/terminal21-architecture.png | Bin 0 -> 32309 bytes 2 files changed, 2 insertions(+) create mode 100644 docs/images/terminal21-architecture.png diff --git a/Readme.md b/Readme.md index bb182964..4cb78305 100644 --- a/Readme.md +++ b/Readme.md @@ -1,3 +1,5 @@ +Note: for Table Of Contents, just click the burger icon top right of this document. + # Terminal 21 Terminal 21 is a library and server that give scala command line programs (i.e. scala-cli scripts) the ability to easily diff --git a/docs/images/terminal21-architecture.png b/docs/images/terminal21-architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..47e1b3d5fec3264ed10f0f3a4a583045d7f29b83 GIT binary patch literal 32309 zcmc$`2{@I}_b9xNgClViQD#cWT$GuEQpgZ8lu$BfmU%cLQ-fJ$CR0j;GINTI;g}xu1c*yG0{|OPx+67{G5vpC!K$g7&^o{Fm4@FSMO_{Rzt{rs2>385mnJ*p*E-32mmN-S4+vUWShK){ciQdKul#C;Ar-#BfwA0KH7r2)P3zAdm96(zdopalDbp z{(8%^?*M3Do5oCyABPWQ?CC;k!db}yXu=m+DiVI3ke?yIBCo zN^pP3w=9yT&f9K9OTK;rd3ZSlkC0Kew0GNoIgJ?NZ(~zG0kHSq7$UAyKRd4_WNfnk ze0sjGy>AiX-Z2B-xgdj8O(z)d!R?-^p?+?LQdMmP+4(H;Y7j>7h>eVT)S6V@TU06B z9QOu4?6#U3gem`o z1kSZrqQ-N93!-&{n)TS{tu{knn zH1`WtTL_0|evBCerwyZkvHib)%MAeaU_F*Im zz%{_pVO}CPpIb_$$nT|O2%twJfiY8QUpMr=)@xFw*((3*2Hi)+zt3|!1YYZVu>R)v zD8))kyU{&&5&>ZAqy(nykEh-b-}wXU6|CgDLr#PbDBMxF6n;i4WlX1<^K!l70qqiP=3-CH;_f&@Yg zsYs^2ACfnc@2^-|n=-PxY)%|p86^1~#Z|p#ly`RJr~gOR{dLxUCO|+K*I9K%=HUf~ zFww}14r1#i%$~z5K{P??HQ9a~M8H_DIs*lMOH9X?3`8#DBE#v65Bs(2h{r$gii(!V z1cu!GrOI?g*6YKvPt~_x0(e{!%p-3HXLlwkp4~$gmu)qQ`m(E-zTQs?z8Cv$lO{;3 zKEHL56w;u52A~X|K8O`;>ttO#-%IhVqDF>0U64X7Od%lfAtL92ju zg{$6p_hi<1FetjZMJ6ea>eIH;+*6B>V0u;Q(Y4x+bO8Qbod(xk=CXt7=|cQV4SENY z@3w2aTprh;Vwqb_RjbB@7K`(RaXSED{0fhleO&5>esN7E=2c=$qH7#;lBIn%J88~+ z3IMupP6H!8n)zo^i)n?CKJEJ5ckE@ZDTwAy&!;1SJqF&LkBw*-<|`PEbriX_A(1wJ zSaj~Y@P*<=V!#F^D`(Bn+%S92#T+Gak9DZRWSYdtT(9=U)YeT}bta$1+{feqCkDB1 z{L>hZ%9zGgi?c#z)pY_vL;xEI8M$${cmAX>-zq0MH=Q<1TXK-Cb%ms-Gl>Y`SyPc5 z7aWD31kN@VT=pG!ds>R5oNCbaO9x&90UB;hV}7NbcJ``Uv?BRTYl#YyD5D;F+|AsychsNe5pvU;!~a{O{W&GICV$0iCSFcLN`+-t6P!NZMO-g9Ron0jCrKo zJt@mFw8X*8Vr&`9#XI`j^E~mO(rA|ld0$xF%E6>J9{|D2(464|&8u*B8IgP1_hjv( z4XHjIxZg(Eh$h@Qu-x~3#a!wxs|}U@8$3d7t83N1$%J;dpwD0!$NGq;tl7A5-#lkA z`uB# zkeE|krax&A+Ny!Cr#{tq%-*Yjg%L9GpX*sjg9sn<+RL=_<%6Y2DlT-ntZTuT*cSkP zL)B=Ba+<%LfBHk1Ii*8QO$}t^m&ce#LN^)dLm3yd$GLA`g1Afv;t>LO+VX#@GHK_> z+_e=Sy?Hr(t3?$YsWI1>dy&}qgb$o0+av(`(x{r zZsJ=akR6Uk*t*AP&XHfeOjO?Cgi=2Xu(t5IvUP^o@~%jw4`8kTi;?Ckxl0K!68{6M{lCu= z<035Ml&KIP<@Uc!3xxFlpi5{mB%+#k_rMSU^Vtg8pyXPuEJd9r;H%*?;o~NBb=jU>}S3Q`5T-J;szP(4j2$ z+Tg^#5iO&6-raA$&rEHWir| z4wVJyg8U6X|8^Sx)C}^%ft<6>DXg)NfxJ6(MF_t6-${7?-db|k7U-)U0da!~t3mhj zKTQZR4ZJqv2lnx@5R)hfp35U@)EiEKBaugyT~dv*sv3{0@T6z(e$le?9#wzr<8EJ{^zb=Lf8L9(dyTdOH8g9lT!>6|<OX%1sE53l*FI7 zhp9eThOSt?inTikaN_zj#*S|eWON;NMF|2Rbrb$Jlt!d?NlaB|02cHeym>791v8K<0EATK?@I%n$QQ!};< z1eP9)m$jbm5M8HetA9$IO8rp@fW;FM_$*V^pk@b!owrhNv$Cuv{N#req`O+-c7b?5 znFSpW)4YxP(6%x4S&ydqF=$``DVKAKLVTL2r@umiN7H)0h{Fzqn0cs!Ve*$ zk?x#_kIBKuS{ori+|b{}$Gazq%0@^+u7)9Rioz1Xt(Q`t213LH2b*w(=ZbePetOc5n_lSa^7w7An2BBK;Vi+B;4g#5CDgSKl-|K8d*_iKfj;@ z*aH}SFXnhb=lf`}hLnCKx;O&(H>N>&z2;D_PGd83iRGOO(sUr@B1CSFyvMA2!vLSQ znIiWGfCLCZLuSkqUtzV&9OY;Mie3s34xc@dmXmX{38!x)NUeJkgpU=&xn-ByoM^uP zG)sV}bY#k%EHAcb^z8HU7%RDiNU?+_GEF;1#>>Vf+o~w~bLp8Yr`2nvAE-_MtRXyM z(#xx-8)knZ>qBb3QVGf$49wVfQXUa5T)%b`r(bb8^lk2XzJOfJ+snaX2j!w$&S{90 zQ>jP{RqPg9WeGzr#comcTjn4wv;@PXyU+gI1hQl|XpPm#SJ*q=T$Bhre8{eZ-Ro%< z(Wz22nc!8Pykns8m41A+>0L5)kfgsuEFwi70=adaGUiurDODEf_Ca&}Qit`=31tJZ zQ>P=JMSW>YY8&F~<>QJ(*YgFtm z+W-Xy8X}GM&`1EJJ3?Xgi_js{GKDi%*y?(Dqae<>K>a=&*KfNPL7 zh;COUj5`S8OprdtYCi9tIAtpanv~by_x5U46W@*#^V;D8BTCRZ$?#|jnH%Zn8;@y> zI|zF?Mm$qLj4MeCszrcV7=i|`>lM}{lAP{k?Azlgo1gzIv*$cekdUcC^_+i&9gXgK zcV1ro1tn-8q*`Q?+L+~O--%P%)~xIq+mcJ{8uiYXcvVN5;$E=WgX!mZ1U9$cF(~*- z(AY_(9UHMbCtpcc3=dtJ(91BmqRI-Vz_^^v;C8c8rt23wA5E`u)sipVW;V2|QNQ2l zUb0;{$OKqENV0iPG1qD1(s~r!={5fD8?t<0%mkU7OS^kM+;%O)!Rh>;D5Ok`n1};q zblFoLfTw{Bg;?*hyKwHY+W>uyLwJ_~hK66@TlT0t5s-oQLMnZoN5pwgj!{#$3Dcvc2+9)cQJchHP!0(_n2oDK-geH@E1xDMS`}m2BbI=WP#M zXZfwknCRh~%x6J($Yo}rTu`78UK>Lu^LSVCLVN7FikZ|Br{%=UM_O>il3sVRIjxsr zcT3E*v{_DGuy^zE#W9xSz%2=?w|qnE0|o<`e6^;u94rN}cnQ}d*TDBo?mw5(dZg=f zzJfKN2}N;a*ENy^qpUS}T(f&d%BhYFF8AAc(YnvxM)z#I?=Ilvz02IM+PtC!8nTe! zv`uuy*I1-=N#Y%rlWol|s{OIgFN7v}gy^^u(J8H8HhfLWM&%Ap_}E>(Dj%Q1D?YLp zQop;L=hgA4L|dQX0QfW>nK?i)*SNE>)vi*>%*1!wjXk2V{B$#Ta)t5J%JG+BPAj7b zpxQYDG&H4K&@o;U2U>DH%Q!P$@tJy_QWk*Cgi44-)o32_82#j51@B|JI2 z^P(#%-98aR8Gr?-4-npyypqe^Nb@Gw*Wn-gr<%=->3fZqeGmy_i?kqyr<|pvy8)qG zO7n1j!{CGO>&=NlNs{H58CpR5&fFB`)-HlJ_4Y=q9eS8)gBb$EDuqa3y=g)sIFG9# z{LaWRLm9<&Px=Xur7M(~(>d$0WCYfx_Fv}1TFp3v?Eabs(z)+-p%hs1Qqqz8S%#rh zNl8RRDWp)4W-ELL_;#oB4(E{84*g8V_J%=zbTB`rQ9dDU=GOV_wxLg6Z3K3Zm z!FC2}vWiTgW5}fE>uu1O+i^Z8Z9SH8mmvfmw>5WROvf@=p>TfI{gOb0+)j9=FTZRE z!&wa&x?_Z!hOQSzMuaC`L1i1gRsW%GNB3e8ZQo)yg8=pqlgw#loNrFoKOfNu63jx*eo7{Z zf760tTFeiwFcqc(yM(Lg-8ecHYzdErT)5Cs@II#I+dr9k3S^6XBgpyq+!h25AM_ITV7{e^_QKQ5fkC}E)57u)}rD- zmtJA?e%SNtl?;c~;)&b4NhC+UO!}k@wnuPcD72pSZDMqwbGVpuT_-9<7aoIIm)83( z;YhJ_^CrGesDacq+W$li0ec z9x$jp(?!DiN~}xkdoCBfjjjFL?tB^jks zr`Zc~N;)f8zJ126Lid(Q(cbskNlYfc3XDQCL?j-uor9GBrMS!1DtyOowrbM)%_}#i zBwrnuZ3+B@+WA>d2RG4G%cu9DHtN@E%WCsbbd-CW>^&1KReYwZI;}jzco7_Fm$2t`>TlOTxq?vsbQ5z(UHiz zBr5y$=i||I$K%bN?H>w=NHP_R&ksqDy3N%7u7064Ix{EZ;O6Qs=%LDl{Yvm@!M!(` zmf`A+_lEL{W8N+PuwdEMmKe=jz5#P;}LE4HT8DImgmw|9p3!PMkGW zgoCw>w1fuaer!4E1q!L)eo`~=Gmb#kFVN-s;gMF<>ziS9`8Ro@OEY0+!J~aCeOsG{F2-r>fnlh;mMEe~vY+B@8+6H!}U zVAOYw^~dJYkffxyx%8Lawk}U|CqJ#XJN+dQ9epR;wKbbQ-*YQDoZvE(>&&AfryxWj zUp&n8L^kDwO>q~RE8hLIcCpFzL#d6K3=1WDKQMGVYp3Zy;d{=IX4cnd;*OQbE7cUO zhcitw)aOSo-Ae&2@3X|kmsUNgN_24PUDH~m>Q7*(>z@f(qM#i8L+BpJT|y;e=c zqs0_NmBRGuHVpKmR;%Gp1eAJoqKv!`0QdIheOHRYUxn>L$kBsq!$a(w48La#9q;! zjeBg(nd05Hgb5e*;j_(qPoEFd1`WyOk9gnaHf6RTXTa`2;quV9oeF^nB~wO){>Ufu z{=i00)wLffBjTVZt9*AVIR0h|U>M^kPoarpwu|=e>ZN$j0bu(}JV`KiJ^Biif~&SLqbS6e z_#b23p{^cfV^f$FAt)k3^bIv5y8BB)x)~h1`0ufUZoEDrmL*hBurhE0$5H(WET14m zz;B@~rI33{1&#uy!5sU*r-bKI|5XykCgn;V=dt9)!`NB~{jb1YC|tRpTm;6|6tJ*} zFC~8!MGTA~2!Q-(A`y&%^=2KgCT%| zMOsB37bOC)9fwZ^!>4k&j{*Ee_=7vn!Ubl!xCrYKf_pqPinx@VoQ22t;krsNj1Skb z+RY(434j0$92~xz;&G=uAhoDf9cW&!!*zfJi^%GcK1du@mX+GJ*LQt>95Km zOoa6}SWC|N;<|eiV3Yoh^~r9j8hPl3VMO5sZQY-}2_djV4ILe!>_GC}{!4I2YDnX) z?RR;1-m_5z%MJHNRm!g=&`1bq5D%&HI)B9Cs%JWX94MGz{IV0o=mzEENR96TrJ>*3IY zFi$tINeF}Dp^QOajOe2*#&SE2jUAQ3psw|!*&Et$kKLKD350q_&3Ef zPs%D&LE3Q;IPO9-0%ZF^&Q_lr7+t-3OqdUrY2gO=TfQU`X$sSEkZ_DKOZw&gk&I8KuI0VlL%Z1TA>569HB&a zRB#1no?SbS0B$fAH5SWqL=m{zfD;n!*2cisoG6$|f~!!>su5PX0EzKiB3_cOT2?j6 z9X{^`{f#qmbTD5!c2mZdh{ATT(0)ply%*AdI#3OTZ7&UhwGKJ>SOXL;!dZ+=U56`S z0HpOH^m_rDAiVNzgm+URhn#}`Oy6HChN40U zt*hLVK1)HKgkC20oa-HEi3xroRaOHV1vQ%qtD6w?#fQ|V&hY^ z!hK(@=v_jklFbb|?r*{;k0>ma^Qg~Ys@(9^E%~QGJ6mE|6mG7^H#dvEA5ZzVb{vxk z4}qi4`FxQcr;XGadTWN1oR$T{daPe_kJ|Ao2!|`D9ArxzezC$t>>W>Pl_n@m%MNUJO~YFEu^l zZjoS4Lem!LQXMW?D`SUmYoG1r?A2wNFkaY=$pPb!cjvlKBoyIfrZ;f8}hF_k4IqFwqVF-0Q0;1aW z{!ycMKGyV4e{6G|b?`l?9ANAZ?uKL>*b2)Jp-JEzOeb`paI!KjSzAS4zU^!lI&+Ii zoF)YXqJphCX}+J@2hE3k+mTTZb>#$IscFzVALUg`Ad`Yfd#(M zG9#hv?Cn*2KEu(oXLk2*_X{po0Qol;wXB}}t*`d}=102T2gf&a0gyNc%Mj&1;MGs4 zFQ$_tx9;zp0>BWi86E}mYQ~V`vDE@sNg0&DQQGMr1YPd!`|`A9DgG|hyig-ypWXZb zMVfHsD{)FQEx}*VqoQ`2*m_VlCrXc;|=h%-FS{ zB8FvojIe+R+rXw};mKxczGKG$h82RZJ?itR^3mw_M`CYilL^*k|NZ#xTJK))gs%eY zG?y5e+lZTtcp@y_2g8F8`k-rGQt?#%@K04seb0jvkRiQb+Id7AwHYWz%gwasPALt! zrvmD8w>uWrIEh{|54KWRO-VachRm@VKehcvpQ?}b`$NjV)?93~@d}Ec7GUP!)?FX_ z*BP8)Hw>&T08CE5CM?S|5r{)>^A9qyHJaF<$0&}PsIZ@2vxPr3fEpM!6@V(59>~8i zS&@RcJ&2Q!hZY;=Zh+S(H3=3qCMz=izcvR`>3kaiRk%Jb;t65ZTzH8O%z;I$zw5{l z+DML+vYFJtyhcujJTD61UmTqPX@Zp#p+N_>VgW4k=_me!=%+1IrYB%xA1dIK^N$6% z#8tJ%+%jd;f{6bz*`q`&r;-FVuYCoZlYf!4zh^K8Z9W3}nfTZ4Nk+80BC01VL5K}H zSrfSc?Ic03C(tm6PGbS^fIFq!muo=_#`Ck*BN$+@i%?^+XuDK%wtTZC_Riw1P+04R zrwev99Vy$PzD2;t4cKj=Btl8YM5Qg=Uzw3hc4q@v8F(rdb-bp*ck!dwAtQ!&6Ovuv zonZ*3UNpwQMRfxK{>Z_H7+sF9x+h7#Z`=KK4j4a#HA8%$?uCQ6yY1HcLvua|P(u)I z6KzPT-2c!vX3)FR{}Bv84uqfo1tU~qn=}Qhp(%z?6yT}+ueK;o5C82k!v#YY`=6GW zae9;P0@B1OHnzdY{~wC)&bEImzs4H>1=*2+qmnb$?8uIKtyJ+njYENyV{qc^x+U$8 ziK!0gPR79$9wtL`v3zt0GRI}e%@paWRlt%6U{D06l(_Ir>;@F6S%P8^2x(TV@)1*o zB{Ry$_C?lChCJevI-5|+2&;6n2ykY}CVAnT7Ffa=(Cr=+m!G46TNd;=JeaxAc@o09 zJfGg(N#1)<-JdsidlleRAuK$OIZZ8^X4ge~sWPR!Xa6_3^q>OLC-3@<#G=hKee9(zG~zS7;&4q6rx z`Bx4jCfb)cI73uk5P{2(&4d#C9bJ8*_SwmmFRaU+M{lx9PgiA82aU~E^dn%~B*K`@ z%3mFQHc-oGDbIIpzP&2hnMvi%VE(VG`Sj{Tqt|}e!wLx0)W&@6E!!cb3N&+UW~|+d zq@7O0G`t{193n%ydb4}yvTMWcWABf-(oy~ft;`%~fG22@w{8W(hbb%{3*A1Bj{uMe z&8E;t>+Yq#YKf0i$@B_!3wf?nt3s^;Jo@IE!k6k#h^^c`?d`F(x6$N_qBMRSC59Gk&VTt%encO|Cr{U+a}spF`tP)AV0CNNCK*zB6* z?YVc=l}@OZigS&-)AYMntA<_{h5YgYAQ_6ZM6XT+-NpX++t#t?DOgSnDqPG9kimOg zkF%f{t$#x_BHYOfvU^2TP3QLTnmMgxzqfB?s260f1tS0oT2+l#_;0JN(E+>2XG!=&vEKN=7DFTog4UNLDGply6LDN5a9Ep&4RSPs#0EP3%e3TCwAc|ym9^*$*>x2 zT}Y~#ZfCWv8bb?krVw+hQ?De`-8dZ@K5K1;-R^r^DH&MnO9XfbeCTA(JKHNLWpCGD zZV;cPq3(Z^)f@o=p>0jkOpW40e~Fd0bxV2FIy7%@bME;M5+qIlhDeyW-3~F`#r*Q$ z-n}>Kzbfx27EnwUOn%fm3S=-UfXH=y3>z4w-&{gC46Eg156>Shxcz)Hdqkr6iAaM%*8O^B^RCqtb?tphm6@0pQc zTOt2&&p76ug?)e7$#>0&G=6-pW$(3N^CK%`0ea96!_c4Rq0xud^e$ZJls%6__+{9V z3r!0?H#0tggc5(kT8W6ze2XPAeDh626{dHwFZFLw8T1cQlXuHAcb*TS`#0*^F$ev;M z2vfu01Q=988xPnLAPRN=>JWy*e;sgnJr!ygYGS6mrlIaXf*KsPaN}qOa70I`oYw7V z$kG>}$o+G1?w$e?LNG~KoB;L+yUrWL85mRkbKsvO`S0WL&!@x}>)r3 z*j_^R)!sZl*{*Dh9wqEeN9XwW7g=3NSDY_C8fSL5(PaR{ z5KjA6+fU8|nFpV%`lFqes_hPZMO%{w)|ZMI^(rJk3x2#0`Uw@Qpn_hrA--%a^^3Ka zbEQP9NuPl~1i8!l^WX;~Vt?CXpX&3-wUsHi7S}cswm8UkcXcL%o_Ht=Xc{WsJcA(g z@q(1I(iybLTYJZjQg#?&iC_+|E#fl-)=Z(fe4SdxiRd{@p8=s*RKc-ip`(%-uz&g3 z3PEUB$3D`2aMZvEO6E%R2k-J9^~VAO=lc&Titkn3`N*q#956%Mj1%8HJ-Rm8%oYE> zIfLlPnGc_}9>}kFpGizJ^u)c`bD~81Z$>MUX`IYx_~z}%^m3;R1jgp*#2D;_FT3qe z?z_`d*v5I?BgU9ODYPG#zM!#wEBsdh5kB@PewdB9=#Y*jDJmyNT2+Z^Rkc zxr809$lFMkb_lo7Fqr#XW=?q&fKl*2_}b9(Oqyq9J(Jc*aL`uZyR8c`L9jucqdnKR z`Cnn4r|LxREanfDXSxy_KZG|(3k>7b=rH?3c^&PJC~!w~yMYA9#-i746p!p%P$Vty z?RF@^FJ_z>OeDr|!+1D6sMe3H{GRxXQte8z$XAZz*vzBakJPB&6;yDcL78igZ5t6t zBDi`7(#s3kS5Q{rj@_Ss^1+00%00N{j_4sQeQP}JncL{8r#jLK0~sxT9vp_AE!_)A zTHpl~avXmPtz8lC%7b6IZ8W-XKfHk%I)1|VEJpw4eBCs%M72ZP$N-_n(doofOWWq$Cr6l5Oac~ zj6;VYe@|OJkXPR&T9(~dw7L73F@%WwkJ#a1^B<&mfc?d)8#=cwTZ33;9wG}qO?1e| zS`!|7|0SemJ|U=U?_|nO z?TC0(fIJ#98?PhRd)J{iSbEG}1|D>$rHqxbG=+a|6VtNk6=Jqa(AFyVa=rZ(nZ(Fz zte^3=tfQ6^dkW$=b0JQ2#>0d}`?lo=!wU+CU|7w-Rm6o!Q<$+Ob|J_yUeLK9Kh?sk z--2-AmFi<({{o1i(YetmI4kMwt!Ne|O1$InjVOVTbU)>>7dg{=%S_Ad2`0k^K?XY% zH3CB2qbBFB?{r6tXDqstn{xvPcmUbz82jzr*WK;8Y?TkP)Zqz`5&8jR?zm^LgeVN1 zQdq<}Jmn(OhNr*=p?u8!YB(`JS|PM{w0-ha?o(mfRt<0#o~}mE?#TVE@ZFusgf`}? z(NkKUDTeUnkvhnC8d_PkqYWZKVdv;o_;L1{;#l}f5D2L}-4qBznja2gNn#)N@+igS zEi!&S40;fcJVD4=@7y_$F0W^=K?(IZW0N8Mr6IIAx44rF@xLRAD+g`O-f3VeZE^)8 zwI6vY>;r4*-klo8V9X-G6O<@<87DfBDIa%&84G)bvE-b|^Y&(Ac)K`c^er~m@N`fF z*^2u?KlOG|InS4>wYJ*(NlU@2%A{7EE;+ykvSCwP>ea0dsA4%O$WD1kfx{Ju!6VQxXr$f_s7)R9 z5q|6EF4m85gZ!7Br?+VDGiDrzoYxs~7V1Nc5Z<~gB{?*`?6#I7E~*cf;p;QuL-ni0 zRv`oS4)toRx6E{FYzwXc76=5bL0)gN$sm#cC=GScX_V|Y{4>k$X6ZSblQ?mByOjS_ z)px~;spu_~Y=+V~N;5XjQX~!y$?&A=J7tXWI961rf}3s!rxQ30U_UeHs|aOMt^fw*iw}(K)pmb#8yq5cju{dc~UW27q1C_ z-dNg5WUL0o{DG3l@f0Pk@034G0-<$24TlaM?^`=796W9n3HqH}aKJ3TU?`@)&0a>s zGy;=_#%G%+4xDnOcs$NHntZQPpDFWwewX{h5dX3DFJvj~@WfuJyd^>Re;F;xU?Q!u z**5aNPu*&YwS(xNq@9c8bjWiS~Uy6sxGyl)-;L1rXS!7Y0^Wrz+j53{?kd*ug& z_H@e|M0KR9y|!LHVStD>b6o(t5L36yjW1|x)}Fi~rLdz)XoQ`{oRD!wj86KLwSA6O zsJ?R5Ldf~{C092cD4rVxcd?Y>1!G{>L``ALnrca7t?p93a})^nCygFR=tv!SDVFmhp-Len@l=9LOV~Aq2APphaH)*Q>#U; z?-=fHNvn&?w8ehYS$9N1?vx@N#fkxOeMQDMksy^oI(e$~mwO&}iSuEsVtlr^T2XYI zwbFNA<&{{)8uCIXkIVAuUrg9_NSIXGk6NBwL{D7@JzR)FN}#>sfDk&LpX-X65;M?L zb-K}|ZSj8StC8I^@C9le;#C{&p&55Nhd&>iwR-nIS9|E`%x?)oTS4H5R;PXI&E{WU z%%h|1K9Ic@+k2#myHX?GH2e@D#0b~rB*~Q0Yq}rwo6+>A!r67lAoG|nB*v7G%K&eo zza8?szc43hL3E9W`(1QBZ8P%+?qfe7QS6hLA5X0|2+3ZDX3SB;U;ARSd4&q+NYvAgE;j}lv2{{xi|02qVuRs|;pNRLg*3MuF zcfYXH^3C3qsQwZo(K4Z+{ks3Rdf0owkmDTnl=i&eJGcTviD6z&ojG-o#ypc1cY-81 z1tGRDyZ&eS)8Y@q=N_0ze^1_&T;@W&mMKXelJEdcn0hfp&MtOcomCJ~gl!M{`O7 zVQ6ib9w&F%OSzU<_ZMj;CX1yH-NQK1qZS-dDN>L&K7yMXUMiGk+-E)QOg3sq_j+3G zyDpX*Bm!I!B#vZd9;^Fd&^!&D%QE(dYu%nb5oFC;sEjQ}q%B@&1^>a`G-fMBV(cLd z(%J#8Ea9b!bypP5I-;-*@cHZBjGAhCa%u-9BA-c1-N{ zRYauxpgLJ#rvr@S{p6zDb>*Ro;8%32mD2|;9cdO6*i=fAc%Rly(hd>jqhAuWE^ zjYVJ{!d(b`{I8lP94ieywI8qZd|#spaxN%lCFc&8tY{eWF*bvM`92fBJ*X4w2&UzF zQPEJujdRzQJTzR8yQf~yk8mDOA%%Mjey8IUyYCdb@Ts{(RY=V5*#LqKj^libcxFOl z0=XWkRn{WTz|yr44ChC}!+165?!Ly2Ma;$Yo#0l0!?7^z`+8z$$4K=KZSDDQF|b7S z_wNFPgY%CSkyo|YT-_on_i@?~;yfQZr@KM5?m_q|4z%ru>H5}y^7&GKCwfAww|(iK z4^yi;q1339$oWC&kYUr|Bc21c#mVb)an;AHZ;S!^Oy_&y1Biy{wrNsu)w$BJ_xY#f z!%4%v303-}7k9`C>xPiu$_8x(_wSZ{+%=`qG0X*|m*7hhtf;v!U26<1G6Dg8mgNI_ z?IEnP_rKPEtkXy0p<==MdhwN?M`JGbIT4glDH$_X-#}LXV7IfMu17osiEA83gDiN0 z>-#@WvzQ6|Rvf(DzT^++8la+plnve03CcBiazFBtt?>yq2cX!PBni@?8_MbES+FcB zc$n#h@Gu)Qz7QnbSGlY;JMi@(ij5={snJ(O{wQbQzVR41;WvOtflVbs&s0rjlpKRT zh4>GT`mxEhVw*b_nwhaMD}>QU9nY&EnN~K@0wD@q-(PBqX${#LErP31xH9wYg5!(9 zV5&8_@dXsy(@TRs9cIY$$C$Bkce?N14yrfL1Xf0D5$UB0aGW3_dm48Avp?NYx1AC! z=FNXD{01^UQ-ZGG0o0A_%&gwG8LAT##D2J#T-&A$P~wn&6?KI%8#>?lP4%B!I>onj z^ZKZRo(Fq7M? zD0~1MWrL#{9Son6A?F^m`v7v3JyhY#F>(aTXBV@msWD}qw>t8w%LypRyxi23TKiD? zDeS%2D2=;$vVs$o0&fCMhU@NW#TeAsISK8MG~MV=ZDhuNg5##cELSczMhC(5N9pVJ zOsI0`(U!rS&F-*ef|P(E|4nyp12;-_CNQhy5%vArB6tpqteML;O*I)7e@?JFytau$ z3LS&`l2aaKeD-+Fi1KIM0GkN0pGXZDgA*K)a2T!lFKPKS)r8Zdq68;Syx6DW;&w`- zEm=muR!b~#l1duq#I5dWcKIRpqRV0YQwebpDSJ6l@Jb&^g8~9? zoKV=0P$-kj6(o>m14xt62gf07-ot$9hZ`FI5UJj z6*y0Kjz0Lp%oxEIw7}b5{c7&Pc3X95JB;22MLwIQ46iHbhtzEMF5kc3f62m)RRc4HR8hl^ta#V5P-4m%v!pU?gr4?^CEBH}ow}i_OID;CNPZA6-Tfx172c?r*w-6s-<(^#!ZVL3`?=6B4 z1U21oNgFNGK-GPwXE{dBs!x4%egUNh)%4jN{aOz15|g4@_8D8^?DP&VzpfW338?UA zOzC-0>P$CkT~VJWBQTQR9C6I1O?j*U3uNQ}%N48V$V-l@LaV3ydtwv=5o+ z2h<#!=Fm0b7eQ3PeO2f}0|VD<*Y5IDr5RF(>rhT}8ILUsn7s#PQI(>xl7JO5_P$OJ zO>R#1Ddd*>AkXZtwR7Aqv@(4p+nS zIuOWOux_;dZabK;JUK;J(u*sr&;VvW@IQo_- zOYw&cYPue>hOn$EvEXa+==A5*;~EZms2?@&C@0{74nKLW@F29>X{=Z%QTeudC7^y7rIi#qL#?R%q z6DE0%o{seG4dS~JPu@pJR&eJZ!(As(Eb@FSCpBt_vad19t8mFFU%i!?#ntIYPj7_0 z-?LSYd-PBou59UzrN4KK3)bOLVrmd@Q>U(Q|MAXnG5q5`jsY$bAEwYppBBU(c4L6w z=lK0MCTDuWCBX_rprwkEl>9XLS5f{gZ`PaBI}ay@We+Rp)=(4<~=ZIp1?m&LM zcB#TLyLY2^YdN3MpItUz?dIgW1$mTVQPTbTRI;Ws7g@&98n8+E=ZyD@;`09DJ69P! z^_u)NsuFj(HjhzyrrI$x2dpkQdikg@G!6NTz@8B%O`j(J3?{c*lRs_ero?>reI(t^ z>^^y|bBp~zv5iEJAEVNH{Qdvh$fWC7LHuYQv&nChzETe)yGuTu-O^#AulOn#Prmb1Znfu}OT8*xyIQFK zg1fs*U<9q|sWU~s0|Cx=5d`S>&h?jkmp~`j)nELw9H~REKPvaVsdJzqlv8;U{VL!t zq7bSRPvT^@e9iF;rlIe4?1>Z~vNolQS3fQ^MVFG&4!blooif`us`9e|f6zld0}eEk?vnc(1)1rriQ7EMhju?A>cl-S zO@5&aTRg<+VSsm;;wpo&lzQsPRpsq25y=rY}_e< zs>kgmX8_EjXRMGY3dJa!tOrJL+YcnSQ-W#kdt)bZpWYn1!S7Q(SXH^VvZSjP2Qw4b)%?Qv#kZ1p zZ3g{vV{u{!oXhk391>F$v-gdIK!s2z;`Oy635Bun8=u+-*IU|N@v$bzQVnt@7u2n6 zqY*TrK6XS)R&A~&4~Litp=&g)`$~iB%H4*08+KOLzgC(>^$t)vsO7TWfBi{l{kL0x z$r3$lzTu~V)Q(AvrfnXuA~)0|u3kd2Z18_rULlrR5c3`Q;INmg^Z&H>m2FXVVcUBE zrH4|G1|=jU1Y`h_Arz1_P(lf%M7lv@=#r9d5Cl|GrEBQ!k{n<}KpLd$S##g-m-h!e zAD-j5_m_b^JJwqJ+E<>}IqGfa2p-Sg6RCrc&qJQH3HB-YD`3XHz7#8dgx6c_&a;O8 z7}I+VrFjb;G)>Zy3clI--GAY9n4VWMg6~v?PX<)j7v%(m!dv#TULmA3qW_hmM~Qs1 zhhl^>#U8xSM&!eneSgXzF=kZP!gK0vJ*CobCp!mROJCacccrApSRp#>=E4zaB?j|7H*M%V7P-XFX1meL3v=3XUVcgq(A&V0o~9dsp}0}<3K zZVyA~)ID+sUzp>ZM4rpLBy>>dXYq%{756DYNjjg+6lir7DVU+`zlQ-DDCEQbPspC= z(>-@JVLE)sIsf0I0o<#D+iL+y-lc}Rv?tJLPq+-M5J$|y&c$qpdmKGft7I3h&*g%1 zppaZV*4j~s*k39HuFvg*_3g6_ffb2*0BR&JHrjK?CXX zNGJ$aJrV-9Q9>)RK-c-)-o#7coF2BM4wG=(9O^huG?ke9qbht{$nddy2c@b-bcU!# z9e#=E|7uN_3K&!T<2k{yd@bYOwZbcSC=s-)K9wJrc10-{y&;o-hGw>*=Y z9IM3wI&qFZc(%OT$b?X0VAu*F)Ily)x}=LQGu~U1_Pk1Wg9V6_<6N@AORUXb?>yWo zi=Y4M{|G2o*0&#Sq;OG~Tm}P7RR~k82TlN^ssKDs>`vTT=(}tAZN#oMGYmy~cHR{X z?=It9+)^c`;I?sw;cjLc)@(wjyYID}9(Ae=tJO4=^rZU$AC@gjA*8v+kvuSmHup~c zBs<8Yxm%ZI@`9N4ei5nFpHBkp#Tm@^i_!^9*<619~!rUzN2XVahuV!)y4dsj68qrQEtat=SSBVhCXE zc{U-DtnRyPo2!qZvBmwLId3SHDwUT)-NQ+FNv+4XrPHnC<+@E;j7{$TYX0M zz%jZOf?v`$Rxg(XkG@*(&XTb+@H&9#8YrZ`X9sOm5~E)F^1IVU!`5Ll5p%PML@@n=0f zF3jOJ1iVQ`GYVDspw5EsbH=CZyNyA{F5Ntq1+t$5kGB38EI^m?zh{+5I0VfK`o{ndE1vq+BFOEb z4!L@mr;TNCqoNPNcjFoP(B&jUH_$>Ry-zmP0W3d~k($t4rIpxr$%uBi=pK7DuC&Q%~v#DQBalkc0-tigoE;;tZwSNPxZq?cr;N9(sk`kS0dsP!K0;Ir;C*B^+JEp4u z;uh=4_SmqUC=0KaZNj-y%1k$TI_4i0qpbS`e3N=Xo_H!*s;5Ll323cYPB5)6T}yB^ z-uPE1{vhv)X4%e@P-4m~iAg9qtE9dqlsawHs}j0Sa~oVM#YC0qobmq+RgFS358|o3{p&ghW7aqK5vO+v4+Z z!OTN;T4T4oMr9mkQtL?d+99aBQkV1Tc@wA*pebP788R^lG>5*rvmO4{@NU$}ukHPQ z!;x?boB;AiBcRr0PD5UNa8|s84ypI|>EG?^j9gt*AYXpS`*H!gQeK<`|LFqbW=xTK z0hj69H8h1a11akVvxH3%r>e?8weLEa1+q^<5s~};G++ThuRRjkPZO86gXW4(czPFc zb)kc>kr*${5=q(k{LZ%seDX^@<9K(7GvCkM?|ZoUJXDjO1n13YQ?KK3*%`7#SfPGj z?|0GOtm~z(@O3-2al}VyU%%-36QS7U5QYC2_fGOLF$?|$`Q-Df5_X3-@85u-%zwdc z0VHKBaxTP=kq>Yn>e~|z;yZ#Ww*Yu6AtBbtBHZvG+}}J^_~2yJgfeKjNz&1!uHNZT zP1LRK{sO?T%kz(3-!0zE+(Kb{98qp{9B`Q6*A>ve$R_ojB@+bHs=TikC`+OTG`HTR zLBH)zRaBz+O7!7(AK>kye-D_cP z1HfJLL8fvKi1w>;&Or@Hv}^SX=qy(@n>j7Q+){1B4%c*P#9(+`wG4RtZYjBkAOe((vPRpjtClv z14;$=1R+B?Il>O!K~Z2zC`0k7AzV^ViN|SyvbGTn8K7D^fYku4qhQF!U?gCjegUl} zm@7e<3XaYc&%!tUV4e&#Il$6NA{nR&5q;U6=SR#Gp86gy=(bx&@h2LfmAbrWtnPo7V zyfEOju;qhkoxVa!MpF!~?lDxkAsB#AY-gSbMQW`9APluVHpnVzA`O{sS%v0 zdKJK*QniWI6?=U4zy^HHIbL>}#7C=xcm}&Y#%Q)b%PPZ%RoA=G4xTjfF-9A< z2Hu?1CrIw-cZqYOePK3*BM6cR0lYTrKM6ztg-1{ifn@$=AJQIa5@K zY3)8yJmejg_%=MP=>e2}QwS!yYBD;)k2Y~SyLx$$wN3D)oJ3^}t$zK=V0zK}nzL!A zzF<1Afb32U#jh`+_&1)fFD`P>c}<>amnyhYb0>szPIM`~!ic9-f0Gv2J6&XPq!iLG zig@!R7%t1ipROMDl@&5|h`c&|(AW7VfX*SE$5aw+SoMHmui6VT43At{#~LKBAt_|x zUji@L7Iig%JfQ+r1Ew?r4)v@|U6tvejvE*~4s{t`*<$*23$qZrSO7qIhQ1A=jA zbs0htIh#?6Ro3BMjsoi0lcfDn3=98YpQ25nYBUwU-E&Ji6ssY$>S6M`};)Az9@oM~nGejwpTm8LjO{L#E9yhulIyg#t7K55TGsGIr+Y?^Q$aO%RDN$> z-3Us2QTJ*N6h@I$%8KXd+OSJk4#^3i{4P~O^GrYp2)cd~%#o#_l&|}6L>6SznoQv% zsU47}>V6Z56%5OW0Y}uC1qa*+ zLqk%T!9dC#4C+CXMc(1oM9oCZhIzL`nz!3(LMgkjC{@Q-8>VB_Q1oYNJ>@#?0J!bb zKfDb_3sKd4IUj2CjA!HV(NeAKKxZ2^o%~@mhtW-XUp4DTyOyn0^w0WE=~FDkFP}V?N*R(Dt7OEZfd%Nqo3{^#@6(+abaFP0(39K`5} z*bY{zCKP?7>Hx8QyCNo%cLb^_lrKXU&Yf$V)~6fl+j_h0gxvCm_@RS8r^7@eGD~W$|LM*Aa!WNL|;vMHcmYnms3Kb#9tYo`CEb zgrWgBs~4a8Ku8hlnxs`EF~%T?=BG)@U(sbw)M|e4Cr7+!wfGzH3T1w zsP7Y?d>YxlH!;71iSRloX&=SYmAEZ@yS2I;LdsvF?WC1N@@&=(HL7>zUrxTMDGWt* zU_WNZzXmzit*%z$YWIw_lHWNZy)aZW5Z0m*Xw!{jp0|3w5B_RzZiMAusZt;3ow?6{kp)&0 z09hj1wF*40Leo_il>PwaYKTe*(kVDP>AQu8+P_^Si_`-JE2LwW7#lzHmA~<4{2~Bz z+NOOZ6t?#1GMYHO=E)4#KQ}OncOY~UL2GQ*<#Pwf-vrmbrmRC&(!XOYJcv(V1QfiU;&4>04#sH2}N$6l?aU@W#;z zCX(TTE|}$8g$y%*bU{%MQmL?eo%IQuCoLecmvx{Yx_e9Yx>LO=Ze%Dc{RcC8840C= zcFPC91@+4pv5f8tM122rd5j(3$=Q@7@;(~KVEu6erlWnfeQv#ZA@*(qRJ=lf)&%VC zE2qlm()UW&2V5{DRBz7#f{0J(q08%zBT%u0kPP_kb(p{fwOO+>48S#-MYLtmHAUgY zF71^T;p;5=)S=HxNM#}Ol zR05(PIfds|RU^3Olz+K~4)U@{K`{Mr(pAtSbr6_kWvf1Y))*Fb7$RSk5dnN`*KU_% z&OeVGI-RQUP?3Eenv09m-#A7r@YLGoc}OB74ct$!T?eo}j`HcVaL-d}BWVK!01X>E z_OS^)T`1Wrt6N1U92lHGpuWnO#Z?XvForpv6<(v|cWtbOIiIPb#bFLX3~?k-KzJRC z)$w2UhP!d8@JymNa?5C;%?dBIch3YJ2lQ;B{Nm#I{EB?$8iQgCo&r|s@#1A&5k#cs z&aKnwyT4sp3+rgLyMpw#n^-qPqE$e1X>41~ODxi4fH=%`l(2G!5DnIXsHTFMR z?LU||##nzleCAwmlzf?GJu)(V%U05%aL3`^6~mbF294BIM6LL}wPhDIl@EP(u2g0j z<7|$Gi2(v{FmYIu<#)f42dDLAPo7uW{46&OojJcVIGo$cd~eo)s-z^**ew|K?!0e= z1NC9-V4dC}$dSD7{xVu=lV*iAK11uf(42Bz-@?{IPgi^d=ADV{d0j3aMJ<*!*+oDL zeNJLnpnhY@Ay=YS)Qi2S*R{9$R}V8w)5dP?JFR83D_?)c7jb_7_3^}s?$=M;Ypt3S zSap)@SNBe^WIvyo*^S@t9;@}v+hk@7GgpyF{oErWC_Zs)S*bGfau7JIlI}^-SuxkM zJxh<16i>RS_7vM$Kb4+jOX=uizIL8?T(RJWLbaGy84bcUA=Wy#vI_<5@1| zg?giq`oy(zQxewav1F^WO%JQ&Pd%+~DbF!ko$Rx(`U)1W+1eW%+a=|3ZKf_(M}3T@ zCuSzHSmZsvzRrcGPs~eBwHo65qu|Ghk8w7< zkHWpxdK@NR$Ero1yPr3-bGti+s=>JE3KMmji}|x_*N#vP(occbtQKkc_yHSa*GVHg zL-y2u$G#)&xF5@-+Qwv7*Pbp|8!r1#7#FP`jQ?I9La7gt)3J)Eo_Mxwcz&=pV{xgO z;hO%l3j)jT#R79A1RAVqYKIW)c8Ct5R8W6HFH)Z{&{SEW6{A%wG7}e_e6iZvvTP&% z!_UGWHEe?-&zynLTQNv`<*=;aXw-H%`*Wno95-;b2Oj0+N#jG)o=p7)7ZxYwC$rp6 z5+k-UqHYaue<$5$H$Hfhv%FP$=_7sj9=x8+wJ{G z%21U#CMmD?6K5yA(RB3}-D>?05kEiQ)33*RGVCu2kiQT}z{T#TTdsk&)ry>X>8eEX zu7(J-FZKzAK$z!R_VV$5Re!N(?bJrn70h|ARi}6@s&?~wLU|Ghh+t`AJ!tN7Wwp{nF=6N}oNL=qbNQ_Ma zK8f5EW{jnP29~owD{Q?!5aEbA+0Y~!T@17!a{w#DEXCE}#PG}GG&9D4h}?v!q9Jv1 z$+GFqOA?{Tga{L*=c!*W^285PEEo4ll-7=jM@c-Ff7#imxy6g=WSwCf#-BrO^eeIt zfOERw-*tX;mU0?6QQlaC=?xA}68oH&rhpVKe5Q?>Ve$gO(@kroZRuYwgIuf!cb)L^ zjSNeRL|{Dh7yC3^F6#%&X-o#p_#ljE;)7D`38ML<6(jBB*cZXEygcX0jyu0~Ihv<-3(c<03PDO;zPTsb!#);Xv(mHs z?ag~CA_Y~2p6#Jzq~xv_8mu<^ckt$!BC|{9*(=Tq1NmKc%=M%278j$%Wpyv%BJ|8k zb#YQlVBp_&=%w{2H1GyWZWBhl>X)4FAB)j{Na_zZv7l}84}vXgFNU7Y#NM(g~z zikP+8H%`#t@cW{R_C%H?bF<}3G77mB7X##!RAgJx(zo-qF~BmhBsA>vxLSSco>`i2 zry`yk&5PqV*P1^6kCjLxZLx-L%_jT<4bV zyEDoBH@`cD22H3!$eX5;nu=eyo6#*sG3{qDf?A`6OoP4{&-9$RABwZ@xDBJrmC4=yBiA#%A={ zuDFbw`$$v;o@-u=&+sovP|_KpDj(Q9PT$;2{Yv5MQip9&kU5u1^QonFD(80)4rSXT zHepu=EjxF5eLCw3_-M^Sx`J5NkIETZ`{PAr>;Mhc+HNK9{Tf?tYUhY0;N`@YIl1mm z3pz^nC#3wa>7a5eU4AvgndcVCD|yTeD#-=jZl)rEd3TYVS=FAV!|z^1xlG#u z$0N?Fx=)%S{PNiJgkaCxvB)2795Qu;!Svl@n&jtI_*&{h3U!Ia4+zyIi(Y|d-Td77 zmNm8FGiTWu$)i^X0Y}@iR08Xp8aGe#^*_Z;s%(tyXAw(rN#&$hUKwtew&9j1pemYq z8HYgz2741r-M;+hB>gop^G$P?mUFwP-P6fMfw?(K$)^@2{oPNa#p1aYIA$nLe{9Ca zK4z}inf?*ub0eosbYxTe*tJyZ+^)SiYpL5+9kK~1Sm0&WheMW{k|R-qJHr(BSvtJRe#o}c=9?>z6B zO&Od0JoOjh_um9v4xNuvg?39GwAQ}iOZ_6GSw|zZBiBSWzDKgE6!rkT-7+@5+8K`J z3_p7Y4rW}&vEGW}LBW&1;bsp#pB7K}xK+S)-DK3%5rWtM+L%3@N%%HJOjw2G1y9B4 zZ{>4|=X7^!ovSvBUPy(jPqKs__;tAx)JRJPu~&dhFn)1*S%2ev95FYkeHZ~K5$9+2 z{F=Wp{(CN^?XJ7v;ZqysrO0HP4lH^1+V$n${+&Fkz~8TaEC*&_p&WtdW zMQ13qNgj^$IE?Xo0MwM&tAmL;N^ErVPaP!f%l>+zFV^Fo_wEImPqa5|JCV8UJmJV! z03J?Swa^(|>dI*zhoxuCQj<%&;%4{We%-@^`yQ<bPb{rLy`5RIvBH&=WCKRU$qXzl!&<@5rd96PU zVO1<>pIrGuHQvYdl#&DqyI`CFaaBABniU~- zRsuFU5Q(`inrU6zkbXfa4n;*L!I6juwUE0UWC8NsP(!Ai0_aO6!Tk+*)IefLVM*~Y z=EyG2k4X+f`+=6oe|LiqM*s{=Ok>mrJ3#b?ZkP_I0MRsv9Rb}RjtOTV`osm~qHw4& zD1VMhpi&u7k^o4CYH#rX)AshEohA&T0EZijbmBp{P6?EE;?;if{t9*QgDd|url2R2 zNI||DhxLap0Q`VU>WYKz1KSMPHp0u82Er8<4J`z}FK%9(GD&-=UiX6Y1kjl=Aey|- zKVmM9&TR1@Qvgk#8|`LuZci-Vr{}H>17L$^l2959_U`{yx$N=VP$RR9!gxb-M8IV_^@^EW7yIc( z8LgR&FzDNsqiEZ^qTg87D5^FVnS-JB2Lg9dY;)G$6aJ`M{@s5Z$RR^rQ%TCfcE!Ks zZ`zi*Jj#oWNQ>RoS$)?$lp1l%0K^t?0NdKBthUNxz4HQrsYtUiz63#QKtAK+uorRO z<+k#T#Jsoi=#Czcfe@U3H=mdtJ9m0m6=V0+;O{EZzVI)2NFK(y_ywaL%kQ?xE8yJ; z7xkTzR6}6@Tv8I;f6Hp^cn488`vgA~zPZ1?TRx2R5Cma%?10bXJK<5xrzvad9{cNN zxY+v&4z9GE2Jq$Ju|k0Q=v^%VY*mD)A4>qCfV815Z!|VGj^6wP%(v$IDL$0G?yb|$ zDR>Yn2jBqRCp<|3L!TxP97N}v$a`QWzCZ-b-lfCXQ6PM70^HjzJJj;a*Izf_yIc_($5ni8wN;|4%x>fB&8K-;(?% zB|rxLdvU-b;7k_S{! Date: Wed, 10 Jan 2024 18:17:01 +0000 Subject: [PATCH 082/144] - --- Readme.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Readme.md b/Readme.md index 4cb78305..9fe89bd1 100644 --- a/Readme.md +++ b/Readme.md @@ -20,6 +20,10 @@ the state in the client scripts. The best and easiest way to start with terminal 21 is via scala-cli and a simple example. +First start the server with a scala-cli script like this (better clone this repository as explained below at [Quick start with terminal21](#Quick start with terminal21) ): + +[server.sc](example-scripts/server.sc) + Let's create a simple hello world script in scala-cli that uses terminal21 server to render the UI. [hello-world.sc](example-scripts/hello-world.sc) @@ -101,6 +105,9 @@ ls *.sc bouncing-ball.sc csv-editor.sc csv-viewer.sc hello-world.sc postit.sc server.sc textedit.sc ``` +# Architecture + +Terminal21 consists of a server (that can easily be started with the provided ) # Mutability terminal21 ui components are mutable. This is a decision choice (for now) because of how much more simple code is this way. I.e. From cd2b4e8011811072eb8450b9e16534daef2edd37 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Wed, 10 Jan 2024 18:23:49 +0000 Subject: [PATCH 083/144] - --- Readme.md | 83 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 42 insertions(+), 41 deletions(-) diff --git a/Readme.md b/Readme.md index 9fe89bd1..44a3aa3f 100644 --- a/Readme.md +++ b/Readme.md @@ -20,9 +20,46 @@ the state in the client scripts. The best and easiest way to start with terminal 21 is via scala-cli and a simple example. -First start the server with a scala-cli script like this (better clone this repository as explained below at [Quick start with terminal21](#Quick start with terminal21) ): +# Quick start with terminal21 + +The easiest way to start with terminal21 is to clone this repository. There is a scala-cli +script that starts the server (all deps and jdk21 will be downloaded automatically by scala-cli). + +```shell +git clone https://github.com/kostaskougios/terminal21-restapi.git +cd terminal21-restapi/example-scripts + +# start the server +./server.sc +# ... it will download dependencies & jdk and start the server. +``` +Now open your browser to http://localhost:8080/ui/ . You'll have the terminal21 UI, will be a bit empty for now, just the settings tab. But we will shortly run some scripts with UI's. + +Let's run some example scripts. All scripts use project.scala with some common settings and dependencies. + +[csv-editor.sc](example-scripts/csv-editor.sc) : edit csv files. + +```shell +./csv-editor.sc -- /tmp/wargame.csv +``` +(note the "--": this is a scala-cli parameter needed before passing actual arguments to a script. The actual argument is the csv filename.) + +Terminal21 UI will now have the csv editor's tab: +![csv editor](docs/images/csv-editor.png) + +If we click in a cell, we will be able to change a value. And then use the "Save & Exit" button to save the file and exit. -[server.sc](example-scripts/server.sc) +![csv editor](docs/images/csv-editor-change.png) + +Now feel free to examine and run the rest of the scripts or create your own! You can have the server running and develop your +scripts with your favorite IDE. Run the scripts within the IDE and view the UI in a browser. + +# Example scripts + +```shell +ls *.sc +bouncing-ball.sc csv-editor.sc csv-viewer.sc hello-world.sc postit.sc server.sc textedit.sc +``` Let's create a simple hello world script in scala-cli that uses terminal21 server to render the UI. @@ -66,48 +103,12 @@ can be used for things like: - logs can be viewed and searched via scripts - ... and so on -# Quick start with terminal21 - -The easiest way to start with terminal21 is to clone this repository. There is a scala-cli -script that starts the server (all deps and jdk21 will be downloaded automatically by scala-cli). - -```shell -git clone https://github.com/kostaskougios/terminal21-restapi.git -cd terminal21-restapi/example-scripts - -# start the server -./server.sc -# ... it will download dependencies & jdk and start the server. -``` -Now open your browser to http://localhost:8080/ui/ . You'll have the terminal21 UI, will be a bit empty for now, just the settings tab. But we will shortly run some scripts with UI's. - -Let's run some example scripts. All scripts use project.scala with some common settings and dependencies. - -[csv-editor.sc](example-scripts/csv-editor.sc) : edit csv files. - -```shell -./csv-editor.sc -- /tmp/wargame.csv -``` -(note the "--": this is a scala-cli parameter needed before passing actual arguments to a script. The actual argument is the csv filename.) - -Terminal21 UI will now have the csv editor's tab: -![csv editor](docs/images/csv-editor.png) - -If we click in a cell, we will be able to change a value. And then use the "Save & Exit" button to save the file and exit. - -![csv editor](docs/images/csv-editor-change.png) - -Now feel free to examine and run the rest of the scripts or create your own! I found out MS code works better for scala-cli scripts but -please make sure you include the terminal21 libs in the script rather than in `project.scala`. This will make autocomplete work better. +# Architecture -```shell -ls *.sc -bouncing-ball.sc csv-editor.sc csv-viewer.sc hello-world.sc postit.sc server.sc textedit.sc -``` +Terminal21 consists of a scala server, a react frontend on the server and a client library that can be imported by scripts/scala projects to create UI's. -# Architecture +![architecture](docs/images/terminal21-architecture.png) -Terminal21 consists of a server (that can easily be started with the provided ) # Mutability terminal21 ui components are mutable. This is a decision choice (for now) because of how much more simple code is this way. I.e. From 797bfa1b10dda19e557c3742985f4b606fce18e9 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Wed, 10 Jan 2024 18:26:29 +0000 Subject: [PATCH 084/144] - --- Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index 44a3aa3f..7261ba45 100644 --- a/Readme.md +++ b/Readme.md @@ -1,4 +1,4 @@ -Note: for Table Of Contents, just click the burger icon top right of this document. +Note: for Table Of Contents, just click the burger icon top right of this document, just above this text on the right. # Terminal 21 From 36c98df23b2b598e86996f9a58fc44926f442568 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Wed, 10 Jan 2024 18:41:37 +0000 Subject: [PATCH 085/144] - --- Readme.md | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/Readme.md b/Readme.md index 7261ba45..b1fd13f5 100644 --- a/Readme.md +++ b/Readme.md @@ -10,16 +10,6 @@ For scala 3 and jdk21 or better. If you have scala-cli installed, you won't need Note: feel free to ask questions in the "Discussions" board at the top of this github page. -Terminal21 consist of : -- a web server that can be easily deployed on your laptop, home network etc -- scala apps (scala-cli/ammonite scripts or just normal scala apps) that use the terminal21 UI libs to create user interfaces - -The terminal21 libs have a websocket open with the server, and they send / receive instructions and events. Similarly, the server -has a websocket open with the React frontend to do the same. Events like clicks or changes to input boxes instantly update -the state in the client scripts. - -The best and easiest way to start with terminal 21 is via scala-cli and a simple example. - # Quick start with terminal21 The easiest way to start with terminal21 is to clone this repository. There is a scala-cli @@ -105,7 +95,13 @@ can be used for things like: # Architecture -Terminal21 consists of a scala server, a react frontend on the server and a client library that can be imported by scripts/scala projects to create UI's. +Terminal21 consist of : +- a scala/react based web server that can be easily deployed on your laptop, home network etc +- scala apps (scala-cli/ammonite scripts or just normal scala apps) that use the terminal21 UI libs to create user interfaces + +The terminal21 libs have a websocket open with the server, and they send / receive instructions and events. Similarly, the server +has a websocket open with the React frontend on the browser to do the same. Events like clicks or changes to input boxes instantly update +the state in the client scripts. ![architecture](docs/images/terminal21-architecture.png) From 6971223d5f12f74e014eb8098ebfeea7c1069d66 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Wed, 10 Jan 2024 18:43:02 +0000 Subject: [PATCH 086/144] - --- Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index b1fd13f5..e3a8fe5b 100644 --- a/Readme.md +++ b/Readme.md @@ -25,7 +25,7 @@ cd terminal21-restapi/example-scripts ``` Now open your browser to http://localhost:8080/ui/ . You'll have the terminal21 UI, will be a bit empty for now, just the settings tab. But we will shortly run some scripts with UI's. -Let's run some example scripts. All scripts use project.scala with some common settings and dependencies. +Let's run some example scripts. All scripts use [project.scala](example-scripts/project.scala) with some common settings and dependencies. [csv-editor.sc](example-scripts/csv-editor.sc) : edit csv files. From c6aa30bc389e84150c6375cf18eeb0bcaaff5c43 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Wed, 10 Jan 2024 18:45:06 +0000 Subject: [PATCH 087/144] - --- Readme.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Readme.md b/Readme.md index e3a8fe5b..41ddc979 100644 --- a/Readme.md +++ b/Readme.md @@ -27,6 +27,12 @@ Now open your browser to http://localhost:8080/ui/ . You'll have the terminal21 Let's run some example scripts. All scripts use [project.scala](example-scripts/project.scala) with some common settings and dependencies. +Start with a hello world example: + +[hello-world.sc](example-scripts/hello-world.sc) + +And then continue with a more complicated csv editor: + [csv-editor.sc](example-scripts/csv-editor.sc) : edit csv files. ```shell From 432f3b5740418491d183ef41d3874573de3bd14d Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Wed, 10 Jan 2024 18:46:21 +0000 Subject: [PATCH 088/144] - --- Readme.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Readme.md b/Readme.md index 41ddc979..2c626e7e 100644 --- a/Readme.md +++ b/Readme.md @@ -31,6 +31,10 @@ Start with a hello world example: [hello-world.sc](example-scripts/hello-world.sc) +```shell +./hello-world.sc +``` + And then continue with a more complicated csv editor: [csv-editor.sc](example-scripts/csv-editor.sc) : edit csv files. From cb1f976453fbc670d379d6c3cfe4b5df6bfc164d Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Wed, 10 Jan 2024 18:46:35 +0000 Subject: [PATCH 089/144] - --- Readme.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Readme.md b/Readme.md index 2c626e7e..86604e7e 100644 --- a/Readme.md +++ b/Readme.md @@ -31,10 +31,13 @@ Start with a hello world example: [hello-world.sc](example-scripts/hello-world.sc) +Run it ```shell ./hello-world.sc ``` +and check your browser. + And then continue with a more complicated csv editor: [csv-editor.sc](example-scripts/csv-editor.sc) : edit csv files. From 19a8c0d105f955ccd8d4ca53b4a9bd9a7780eeac Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Wed, 10 Jan 2024 18:46:55 +0000 Subject: [PATCH 090/144] - --- Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index 86604e7e..33962849 100644 --- a/Readme.md +++ b/Readme.md @@ -36,7 +36,7 @@ Run it ./hello-world.sc ``` -and check your browser. +and check your browser. Changes in the terminal21 UI will reflect instantly. And then continue with a more complicated csv editor: From a00a4e28304553eaa860c8db0e42a114ad64483e Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 11 Jan 2024 00:53:26 +0000 Subject: [PATCH 091/144] - --- .../src/main/scala/examples/HelloWorld.scala | 38 ------------------- .../src/main/scala/tests/NivoComponents.scala | 4 +- 2 files changed, 2 insertions(+), 40 deletions(-) delete mode 100644 examples/src/main/scala/examples/HelloWorld.scala diff --git a/examples/src/main/scala/examples/HelloWorld.scala b/examples/src/main/scala/examples/HelloWorld.scala deleted file mode 100644 index 2a5530cf..00000000 --- a/examples/src/main/scala/examples/HelloWorld.scala +++ /dev/null @@ -1,38 +0,0 @@ -package examples - -import org.terminal21.client.components.* -import org.terminal21.client.components.chakra.* -import org.terminal21.client.{ConnectedSession, Sessions} - -@main def helloWorld(): Unit = - Sessions.withNewSession("hello-world", "Hello World"): session => - given ConnectedSession = session - println(session.session.id) - - val h1 = Header1(key = "header", text = "Welcome to the Hello World Program!") - val b1 = Box(text = "First box", bg = "green", p = 4, color = "black") - b1.withChildren( - Button(text = "Click me!").onClick: () => - b1.text = "Clicked!" - session.render() - ) - val p1 = Paragraph( - key = "status", - text = s"Hello there mr X", - children = Seq( - b1, - Box(text = "Second box", bg = "tomato", p = 4, color = "black") - ) - ) - val grid = SimpleGrid(spacing = Some("8px"), columns = 4) - session.add(h1, p1, grid) - session.render() - - for i <- 1 to 25 do - Thread.sleep(1000) - p1.text = s"i = $i" - grid.addChildren(Box(text = s"counting i = $i", bg = "green")) - session.render() - - h1.text = "Done!" - session.render() diff --git a/examples/src/main/scala/tests/NivoComponents.scala b/examples/src/main/scala/tests/NivoComponents.scala index 57e084ce..4212557f 100644 --- a/examples/src/main/scala/tests/NivoComponents.scala +++ b/examples/src/main/scala/tests/NivoComponents.scala @@ -29,8 +29,8 @@ import org.terminal21.client.components.* ) ), yScale = Scale(stacked = Some(true)), - axisBottom = Some(Axis(tickSize = 5, tickPadding = 5, tickRotation = 0, legend = "transportation", legendOffset = 36, legendPosition = "middle")), - axisLeft = Some(Axis(tickSize = 5, tickPadding = 5, tickRotation = 0, legend = "count", legendOffset = -40, legendPosition = "middle")), + axisBottom = Some(Axis(legend = "transportation", legendOffset = 36)), + axisLeft = Some(Axis(legend = "count", legendOffset = -40)), legends = Seq( Legend() ) From b86c2e6fdb5ffc33eeabbb3286979ebd00bf4f79 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 11 Jan 2024 12:44:33 +0000 Subject: [PATCH 092/144] - --- .../src/main/scala/tests/NivoComponents.scala | 31 +------------- .../scala/tests/nivo/ResponsiveBarChart.scala | 25 +++++++++++ .../tests/nivo/ResponsiveLineChart.scala | 41 +++++++++++++++++++ .../client/components/nivo/BarDatum.scala | 23 +++++++++++ .../client/components/nivo/Effect.scala | 4 +- .../client/components/nivo/Legend.scala | 1 + .../client/components/nivo/NivoElement.scala | 24 +++++++++++ .../client/components/nivo/NivoLib.scala | 13 ++++++ .../client/components/nivo/Scale.scala | 3 +- 9 files changed, 133 insertions(+), 32 deletions(-) create mode 100644 examples/src/main/scala/tests/nivo/ResponsiveBarChart.scala create mode 100644 examples/src/main/scala/tests/nivo/ResponsiveLineChart.scala create mode 100644 terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/BarDatum.scala diff --git a/examples/src/main/scala/tests/NivoComponents.scala b/examples/src/main/scala/tests/NivoComponents.scala index 4212557f..2eb2aaa6 100644 --- a/examples/src/main/scala/tests/NivoComponents.scala +++ b/examples/src/main/scala/tests/NivoComponents.scala @@ -3,37 +3,10 @@ package tests import org.terminal21.client.components.nivo.* import org.terminal21.client.* import org.terminal21.client.components.* +import tests.nivo.{ResponsiveBarChart, ResponsiveLineChart} @main def nivoComponents(): Unit = Sessions.withNewSession("nivo-components", "Nivo Components", NivoLib): session => given ConnectedSession = session - Seq( - ResponsiveLine( - data = Seq( - Serie( - "japan", - data = Seq( - Datum("plane", 262), - Datum("helicopter", 26), - Datum("boat", 43) - ) - ), - Serie( - "france", - "hsl(186, 70%, 50%)", - Seq( - Datum("plane", 271), - Datum("helicopter", 31), - Datum("boat", 27) - ) - ) - ), - yScale = Scale(stacked = Some(true)), - axisBottom = Some(Axis(legend = "transportation", legendOffset = 36)), - axisLeft = Some(Axis(legend = "count", legendOffset = -40)), - legends = Seq( - Legend() - ) - ) - ).render() + (ResponsiveBarChart() ++ ResponsiveLineChart()).render() session.waitTillUserClosesSession() diff --git a/examples/src/main/scala/tests/nivo/ResponsiveBarChart.scala b/examples/src/main/scala/tests/nivo/ResponsiveBarChart.scala new file mode 100644 index 00000000..a5b4e9a8 --- /dev/null +++ b/examples/src/main/scala/tests/nivo/ResponsiveBarChart.scala @@ -0,0 +1,25 @@ +package tests.nivo + +import org.terminal21.client.components.nivo.* +import tests.chakra.Common.commonBox + +object ResponsiveBarChart: + def apply() = Seq( + commonBox("ResponsiveBar"), + ResponsiveBar( + data = Seq( + Seq( + BarDatum("country", "AD"), + BarDatum("hot dog", 150), + BarDatum("hot dogColor", "hsl(202, 70%, 50%)"), + BarDatum("burger", 189), + BarDatum("burgerColor", "hsl(106, 70%, 50%)"), + BarDatum("sandwich", 170), + BarDatum("sandwichColor", "hsl(115, 70%, 50%)") + ) + ), + keys = Seq("hot dog", "burger", "sandwich", "kebab", "fries", "donut"), + valueScale = Scale(`type` = "linear"), + indexScale = Scale(`type` = "band", round = Some(true)) + ) + ) diff --git a/examples/src/main/scala/tests/nivo/ResponsiveLineChart.scala b/examples/src/main/scala/tests/nivo/ResponsiveLineChart.scala new file mode 100644 index 00000000..0cd78448 --- /dev/null +++ b/examples/src/main/scala/tests/nivo/ResponsiveLineChart.scala @@ -0,0 +1,41 @@ +package tests.nivo + +import org.terminal21.client.* +import org.terminal21.client.components.* +import org.terminal21.client.components.nivo.* +import tests.chakra.Common +import tests.chakra.Common.commonBox + +import scala.collection.immutable.Seq + +object ResponsiveLineChart: + def apply() = Seq( + commonBox("ResponsiveLine"), + ResponsiveLine( + data = Seq( + Serie( + "japan", + data = Seq( + Datum("plane", 262), + Datum("helicopter", 26), + Datum("boat", 43) + ) + ), + Serie( + "france", + "hsl(186, 70%, 50%)", + Seq( + Datum("plane", 271), + Datum("helicopter", 31), + Datum("boat", 27) + ) + ) + ), + yScale = Scale(stacked = Some(true)), + axisBottom = Some(Axis(legend = "transportation", legendOffset = 36)), + axisLeft = Some(Axis(legend = "count", legendOffset = -40)), + legends = Seq( + Legend() + ) + ) + ) diff --git a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/BarDatum.scala b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/BarDatum.scala new file mode 100644 index 00000000..3452a5ed --- /dev/null +++ b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/BarDatum.scala @@ -0,0 +1,23 @@ +package org.terminal21.client.components.nivo + +import io.circe.{Encoder, Json} + +case class BarDatum( + name: String, + value: Any // String, Int, Float etc, see the encoder below for supported types +) + +object BarDatum: + given Encoder[Seq[BarDatum]] = s => + val vs = s.map: bd => + ( + bd.name, + bd.value match + case s: String => Json.fromString(s) + case i: Int => Json.fromInt(i) + case f: Float => Json.fromFloat(f).get + case d: Double => Json.fromDouble(d).get + case b: Boolean => Json.fromBoolean(b) + case _ => throw new IllegalArgumentException(s"type $bd not supported, either use one of the supported ones or open a bug request") + ) + Json.obj(vs: _*) diff --git a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Effect.scala b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Effect.scala index 2a7edaf4..7fb5c73a 100644 --- a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Effect.scala +++ b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Effect.scala @@ -2,8 +2,8 @@ package org.terminal21.client.components.nivo case class Effect( on: String = "hover", - style: Map[String, String] = Map( + style: Map[String, Any] = Map( "itemBackground" -> "rgba(0, 0, 0, .03)", - "itemOpacity" -> "1" + "itemOpacity" -> 1 ) ) diff --git a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Legend.scala b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Legend.scala index 7f1647ab..769c658f 100644 --- a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Legend.scala +++ b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Legend.scala @@ -1,6 +1,7 @@ package org.terminal21.client.components.nivo case class Legend( + dataFrom: String = "keys", anchor: String = "bottom-right", direction: String = "column", justify: Boolean = false, diff --git a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala index 29f5a3ab..f1b36915 100644 --- a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala +++ b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala @@ -30,3 +30,27 @@ case class ResponsiveLine( @volatile var legends: Seq[Legend] = Nil ) extends NivoElement with HasStyle + +/** https://nivo.rocks/bar/ + */ +case class ResponsiveBar( + key: String = Keys.nextKey, + // to give width and height, we wrap the component in a wrapper element. Height must be provided + // for nivo components to be visible + @volatile var style: Map[String, String] = Map("height" -> "400px"), + @volatile var data: Seq[Seq[BarDatum]] = Nil, + @volatile var keys: Seq[String] = Nil, + @volatile var indexBy: String = "", + @volatile var margin: Margin = Margin(right = 110), + @volatile var padding: Float = 0, + @volatile var valueScale: Scale = Scale(), + @volatile var indexScale: Scale = Scale(), + @volatile var colors: Map[String, String] = Map("scheme" -> "nivo"), + @volatile var axisTop: Option[Axis] = None, + @volatile var axisRight: Option[Axis] = None, + @volatile var axisBottom: Option[Axis] = Some(Axis(legend = "y", legendOffset = 36)), + @volatile var axisLeft: Option[Axis] = Some(Axis(legend = "x", legendOffset = -40)), + @volatile var legends: Seq[Legend] = Nil, + @volatile var ariaLabel: String = "Chart Label" +) extends NivoElement + with HasStyle diff --git a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoLib.scala b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoLib.scala index 7af0ffa2..68295cbd 100644 --- a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoLib.scala +++ b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoLib.scala @@ -7,5 +7,18 @@ import io.circe.generic.auto.* import io.circe.syntax.* object NivoLib extends ComponentLib: + given Encoder[Map[String, Any]] = m => + val vs = m.toSeq.map: (k, v) => + ( + k, + v match + case s: String => Json.fromString(s) + case i: Int => Json.fromInt(i) + case f: Float => Json.fromFloat(f).get + case d: Double => Json.fromDouble(d).get + case _ => throw new IllegalArgumentException(s"type $v not supported, either use one of the supported ones or open a bug request") + ) + Json.obj(vs: _*) + override def toJson(using Encoder[UiElement]): PartialFunction[UiElement, Json] = case n: NivoElement => n.asJson.mapObject(o => o.add("type", "Nivo".asJson)) diff --git a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Scale.scala b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Scale.scala index 1e94b645..89af9a26 100644 --- a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Scale.scala +++ b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Scale.scala @@ -5,7 +5,8 @@ case class Scale( min: Option[String] = Some("auto"), max: Option[String] = Some("auto"), stacked: Option[Boolean] = Some(false), - reverse: Option[Boolean] = Some(false) + reverse: Option[Boolean] = Some(false), + round: Option[Boolean] = None ) object Scale: From f8ecf6808106c008aa732b7a32d0b533a7f2feaa Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 11 Jan 2024 12:51:50 +0000 Subject: [PATCH 093/144] - --- .../org/terminal21/client/ui/UiLib.scala | 3 - .../client/components/nivo/NivoElement.scala | 4 +- .../client/components/nivo/NivoLib.scala | 14 +- .../client/components/StdElement.scala | 12 +- .../client/components/UiElement.scala | 2 +- .../client/components/UiElementEncoding.scala | 12 ++ .../components/chakra/ChakraElement.scala | 204 +++++++++--------- .../client/components/chakra/QuickTable.scala | 4 +- 8 files changed, 126 insertions(+), 129 deletions(-) delete mode 100644 terminal21-client-common/src/main/scala/org/terminal21/client/ui/UiLib.scala diff --git a/terminal21-client-common/src/main/scala/org/terminal21/client/ui/UiLib.scala b/terminal21-client-common/src/main/scala/org/terminal21/client/ui/UiLib.scala deleted file mode 100644 index e9cfe8cd..00000000 --- a/terminal21-client-common/src/main/scala/org/terminal21/client/ui/UiLib.scala +++ /dev/null @@ -1,3 +0,0 @@ -package org.terminal21.client.ui - -trait UiLib diff --git a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala index f1b36915..9126a983 100644 --- a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala +++ b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala @@ -11,7 +11,7 @@ case class ResponsiveLine( key: String = Keys.nextKey, // to give width and height, we wrap the component in a wrapper element. Height must be provided // for nivo components to be visible - @volatile var style: Map[String, String] = Map("height" -> "400px"), + @volatile var style: Map[String, Any] = Map("height" -> "400px"), @volatile var data: Seq[Serie] = Nil, @volatile var margin: Margin = Margin(right = 110), @volatile var xScale: Scale = Scale.Point, @@ -37,7 +37,7 @@ case class ResponsiveBar( key: String = Keys.nextKey, // to give width and height, we wrap the component in a wrapper element. Height must be provided // for nivo components to be visible - @volatile var style: Map[String, String] = Map("height" -> "400px"), + @volatile var style: Map[String, Any] = Map("height" -> "400px"), @volatile var data: Seq[Seq[BarDatum]] = Nil, @volatile var keys: Seq[String] = Nil, @volatile var indexBy: String = "", diff --git a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoLib.scala b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoLib.scala index 68295cbd..d4030b54 100644 --- a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoLib.scala +++ b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoLib.scala @@ -7,18 +7,6 @@ import io.circe.generic.auto.* import io.circe.syntax.* object NivoLib extends ComponentLib: - given Encoder[Map[String, Any]] = m => - val vs = m.toSeq.map: (k, v) => - ( - k, - v match - case s: String => Json.fromString(s) - case i: Int => Json.fromInt(i) - case f: Float => Json.fromFloat(f).get - case d: Double => Json.fromDouble(d).get - case _ => throw new IllegalArgumentException(s"type $v not supported, either use one of the supported ones or open a bug request") - ) - Json.obj(vs: _*) - + import org.terminal21.client.components.StdElementEncoding.given override def toJson(using Encoder[UiElement]): PartialFunction[UiElement, Json] = case n: NivoElement => n.asJson.mapObject(o => o.add("type", "Nivo".asJson)) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdElement.scala index c40239d6..1c4d316b 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdElement.scala @@ -5,16 +5,16 @@ import org.terminal21.client.{ConnectedSession, OnChangeEventHandler} sealed trait StdElement extends UiElement with HasStyle -case class Span(key: String = Keys.nextKey, @volatile var text: String, @volatile var style: Map[String, String] = Map.empty) extends StdElement -case class NewLine(key: String = Keys.nextKey, @volatile var style: Map[String, String] = Map.empty) extends StdElement -case class Em(key: String = Keys.nextKey, @volatile var text: String, @volatile var style: Map[String, String] = Map.empty) extends StdElement +case class Span(key: String = Keys.nextKey, @volatile var text: String, @volatile var style: Map[String, Any] = Map.empty) extends StdElement +case class NewLine(key: String = Keys.nextKey, @volatile var style: Map[String, Any] = Map.empty) extends StdElement +case class Em(key: String = Keys.nextKey, @volatile var text: String, @volatile var style: Map[String, Any] = Map.empty) extends StdElement -case class Header1(key: String = Keys.nextKey, @volatile var text: String, @volatile var style: Map[String, String] = Map.empty) extends StdElement +case class Header1(key: String = Keys.nextKey, @volatile var text: String, @volatile var style: Map[String, Any] = Map.empty) extends StdElement case class Paragraph( key: String = Keys.nextKey, @volatile var text: String = "", - @volatile var style: Map[String, String] = Map.empty, + @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil ) extends StdElement with HasChildren[Paragraph] @@ -23,7 +23,7 @@ case class Input( key: String = Keys.nextKey, `type`: String = "text", defaultValue: String = "", - @volatile var style: Map[String, String] = Map.empty, + @volatile var style: Map[String, Any] = Map.empty, @volatile var value: String = "" ) extends StdElement with HasEventHandler: diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala index 61f03dd9..eca8cb17 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElement.scala @@ -32,4 +32,4 @@ object UiElement: def defaultEventHandler: EventHandler trait HasStyle: - var style: Map[String, String] + var style: Map[String, Any] diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElementEncoding.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElementEncoding.scala index f046b397..1d312f19 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElementEncoding.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/UiElementEncoding.scala @@ -15,6 +15,18 @@ class UiElementEncoding(libs: Seq[ComponentLib]): cl.toJson(a) object StdElementEncoding extends ComponentLib: + given Encoder[Map[String, Any]] = m => + val vs = m.toSeq.map: (k, v) => + ( + k, + v match + case s: String => Json.fromString(s) + case i: Int => Json.fromInt(i) + case f: Float => Json.fromFloat(f).get + case d: Double => Json.fromDouble(d).get + case _ => throw new IllegalArgumentException(s"type $v not supported, either use one of the supported ones or open a bug request") + ) + Json.obj(vs: _*) override def toJson(using Encoder[UiElement]): PartialFunction[UiElement, Json] = case std: StdElement => std.asJson.mapObject(o => o.add("type", "Std".asJson)) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala index a85a2dfe..1679a965 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala @@ -18,7 +18,7 @@ case class Button( @volatile var size: Option[String] = None, @volatile var variant: Option[String] = None, @volatile var colorScheme: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty, + @volatile var style: Map[String, Any] = Map.empty, @volatile var leftIcon: Option[UiElement] = None, @volatile var rightIcon: Option[UiElement] = None, @volatile var isActive: Option[Boolean] = None, @@ -40,7 +40,7 @@ case class ButtonGroup( @volatile var height: Option[String] = None, @volatile var border: Option[String] = None, @volatile var borderColor: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty, + @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement with HasChildren[ButtonGroup] @@ -54,7 +54,7 @@ case class Box( @volatile var w: String = "", @volatile var p: Int = 0, @volatile var color: String = "", - @volatile var style: Map[String, String] = Map.empty, + @volatile var style: Map[String, Any] = Map.empty, @volatile var as: Option[String] = None, @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement @@ -66,7 +66,7 @@ case class HStack( key: String = Keys.nextKey, @volatile var spacing: Option[String] = None, @volatile var align: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty, + @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement with HasChildren[HStack] @@ -74,7 +74,7 @@ case class VStack( key: String = Keys.nextKey, @volatile var spacing: Option[String] = None, @volatile var align: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty, + @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement with HasChildren[VStack] @@ -86,7 +86,7 @@ case class SimpleGrid( @volatile var spacingY: Option[String] = None, @volatile var columns: Int = 2, @volatile var children: Seq[UiElement] = Nil, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement with HasChildren[SimpleGrid] @@ -96,7 +96,7 @@ case class Editable( key: String = Keys.nextKey, defaultValue: String = "", @volatile var value: String = "", - @volatile var style: Map[String, String] = Map.empty, + @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement with HasEventHandler @@ -105,16 +105,16 @@ case class Editable( if value == "" then value = defaultValue override def defaultEventHandler: OnChangeEventHandler = newValue => value = newValue -case class EditablePreview(key: String = Keys.nextKey, @volatile var style: Map[String, String] = Map.empty) extends ChakraElement -case class EditableInput(key: String = Keys.nextKey, @volatile var style: Map[String, String] = Map.empty) extends ChakraElement -case class EditableTextarea(key: String = Keys.nextKey, @volatile var style: Map[String, String] = Map.empty) extends ChakraElement +case class EditablePreview(key: String = Keys.nextKey, @volatile var style: Map[String, Any] = Map.empty) extends ChakraElement +case class EditableInput(key: String = Keys.nextKey, @volatile var style: Map[String, Any] = Map.empty) extends ChakraElement +case class EditableTextarea(key: String = Keys.nextKey, @volatile var style: Map[String, Any] = Map.empty) extends ChakraElement /** https://chakra-ui.com/docs/components/form-control */ case class FormControl( key: String = Keys.nextKey, as: String = "", - @volatile var style: Map[String, String] = Map.empty, + @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement with HasChildren[FormControl] @@ -124,7 +124,7 @@ case class FormControl( case class FormLabel( key: String = Keys.nextKey, @volatile var text: String, - @volatile var style: Map[String, String] = Map.empty, + @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement with HasChildren[FormLabel] @@ -134,7 +134,7 @@ case class FormLabel( case class FormHelperText( key: String = Keys.nextKey, @volatile var text: String, - @volatile var style: Map[String, String] = Map.empty, + @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement with HasChildren[FormHelperText] @@ -148,7 +148,7 @@ case class Input( @volatile var size: String = "md", @volatile var variant: Option[String] = None, @volatile var value: String = "", - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement with HasEventHandler with OnChangeEventHandler.CanHandleOnChangeEvent[Input]: @@ -157,7 +157,7 @@ case class Input( case class InputGroup( key: String = Keys.nextKey, @volatile var size: String = "md", - @volatile var style: Map[String, String] = Map.empty, + @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement with HasChildren[InputGroup] @@ -165,7 +165,7 @@ case class InputGroup( case class InputLeftAddon( key: String = Keys.nextKey, @volatile var text: String = "", - @volatile var style: Map[String, String] = Map.empty, + @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement with HasChildren[InputLeftAddon] @@ -173,7 +173,7 @@ case class InputLeftAddon( case class InputRightAddon( key: String = Keys.nextKey, @volatile var text: String = "", - @volatile var style: Map[String, String] = Map.empty, + @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement with HasChildren[InputRightAddon] @@ -185,7 +185,7 @@ case class Checkbox( @volatile var text: String = "", defaultChecked: Boolean = false, @volatile var isDisabled: Boolean = false, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement with HasEventHandler with OnChangeBooleanEventHandler.CanHandleOnChangeEvent[Checkbox]: @@ -200,13 +200,13 @@ case class Radio( value: String, @volatile var text: String = "", @volatile var colorScheme: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement case class RadioGroup( key: String = Keys.nextKey, defaultValue: String = "", @volatile var value: String = "", - @volatile var style: Map[String, String] = Map.empty, + @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement with HasEventHandler @@ -224,7 +224,7 @@ case class Center( @volatile var w: Option[String] = None, @volatile var h: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement with HasChildren[Center] @@ -236,7 +236,7 @@ case class Circle( @volatile var w: Option[String] = None, @volatile var h: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement with HasChildren[Circle] @@ -248,7 +248,7 @@ case class Square( @volatile var w: Option[String] = None, @volatile var h: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement with HasChildren[Square] @@ -260,7 +260,7 @@ case class AddIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -271,7 +271,7 @@ case class ArrowBackIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -282,7 +282,7 @@ case class ArrowDownIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -293,7 +293,7 @@ case class ArrowForwardIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -304,7 +304,7 @@ case class ArrowLeftIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -315,7 +315,7 @@ case class ArrowRightIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -326,7 +326,7 @@ case class ArrowUpIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -337,7 +337,7 @@ case class ArrowUpDownIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -348,7 +348,7 @@ case class AtSignIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -359,7 +359,7 @@ case class AttachmentIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -370,7 +370,7 @@ case class BellIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -381,7 +381,7 @@ case class CalendarIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -392,7 +392,7 @@ case class ChatIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -403,7 +403,7 @@ case class CheckIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -414,7 +414,7 @@ case class CheckCircleIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -425,7 +425,7 @@ case class ChevronDownIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -436,7 +436,7 @@ case class ChevronLeftIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -447,7 +447,7 @@ case class ChevronRightIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -458,7 +458,7 @@ case class ChevronUpIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -469,7 +469,7 @@ case class CloseIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -480,7 +480,7 @@ case class CopyIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -491,7 +491,7 @@ case class DeleteIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -502,7 +502,7 @@ case class DownloadIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -513,7 +513,7 @@ case class DragHandleIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -524,7 +524,7 @@ case class EditIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -535,7 +535,7 @@ case class EmailIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -546,7 +546,7 @@ case class ExternalLinkIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -557,7 +557,7 @@ case class HamburgerIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -568,7 +568,7 @@ case class InfoIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -579,7 +579,7 @@ case class InfoOutlineIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -590,7 +590,7 @@ case class LinkIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -601,7 +601,7 @@ case class LockIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -612,7 +612,7 @@ case class MinusIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -623,7 +623,7 @@ case class MoonIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -634,7 +634,7 @@ case class NotAllowedIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -645,7 +645,7 @@ case class PhoneIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -656,7 +656,7 @@ case class PlusSquareIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -667,7 +667,7 @@ case class QuestionIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -678,7 +678,7 @@ case class QuestionOutlineIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -689,7 +689,7 @@ case class RepeatIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -700,7 +700,7 @@ case class RepeatClockIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -711,7 +711,7 @@ case class SearchIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -722,7 +722,7 @@ case class Search2Icon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -733,7 +733,7 @@ case class SettingsIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -744,7 +744,7 @@ case class SmallAddIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -755,7 +755,7 @@ case class SmallCloseIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -766,7 +766,7 @@ case class SpinnerIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -777,7 +777,7 @@ case class StarIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -788,7 +788,7 @@ case class SunIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -799,7 +799,7 @@ case class TimeIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -810,7 +810,7 @@ case class TriangleDownIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -821,7 +821,7 @@ case class TriangleUpIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -832,7 +832,7 @@ case class UnlockIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -843,7 +843,7 @@ case class UpDownIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -854,7 +854,7 @@ case class ViewIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -865,7 +865,7 @@ case class ViewOffIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -876,7 +876,7 @@ case class WarningIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** generated by generateIconsCode() , https://chakra-ui.com/docs/components/icon @@ -887,7 +887,7 @@ case class WarningTwoIcon( @volatile var h: Option[String] = None, @volatile var boxSize: Option[String] = None, @volatile var color: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** https://chakra-ui.com/docs/components/textarea @@ -899,7 +899,7 @@ case class Textarea( @volatile var size: String = "md", @volatile var variant: Option[String] = None, @volatile var value: String = "", - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement with HasEventHandler with OnChangeEventHandler.CanHandleOnChangeEvent[Textarea]: @@ -912,7 +912,7 @@ case class Switch( @volatile var text: String = "", defaultChecked: Boolean = false, @volatile var isDisabled: Boolean = false, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement with HasEventHandler with OnChangeBooleanEventHandler.CanHandleOnChangeEvent[Switch]: @@ -929,7 +929,7 @@ case class Select( @volatile var bg: Option[String] = None, @volatile var color: Option[String] = None, @volatile var borderColor: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty, + @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement with HasEventHandler @@ -941,12 +941,12 @@ case class Option_( key: String = Keys.nextKey, value: String, @volatile var text: String = "", - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** https://chakra-ui.com/docs/components/table/usage */ -case class TableContainer(key: String = Keys.nextKey, @volatile var children: Seq[UiElement] = Nil, @volatile var style: Map[String, String] = Map.empty) +case class TableContainer(key: String = Keys.nextKey, @volatile var children: Seq[UiElement] = Nil, @volatile var style: Map[String, Any] = Map.empty) extends ChakraElement with HasChildren[TableContainer]: def withRowStringData(data: Seq[Seq[String]]): TableContainer = withRowData(data.map(_.map(c => Text(text = c)))) @@ -970,24 +970,24 @@ case class Table( @volatile var variant: String = "simple", @volatile var size: String = "md", @volatile var colorScheme: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty, + @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement with HasChildren[Table] -case class TableCaption(key: String = Keys.nextKey, @volatile var text: String = "", @volatile var style: Map[String, String] = Map.empty) extends ChakraElement -case class Thead(key: String = Keys.nextKey, @volatile var children: Seq[UiElement] = Nil, @volatile var style: Map[String, String] = Map.empty) +case class TableCaption(key: String = Keys.nextKey, @volatile var text: String = "", @volatile var style: Map[String, Any] = Map.empty) extends ChakraElement +case class Thead(key: String = Keys.nextKey, @volatile var children: Seq[UiElement] = Nil, @volatile var style: Map[String, Any] = Map.empty) extends ChakraElement with HasChildren[Thead] -case class Tbody(key: String = Keys.nextKey, @volatile var children: Seq[UiElement] = Nil, @volatile var style: Map[String, String] = Map.empty) +case class Tbody(key: String = Keys.nextKey, @volatile var children: Seq[UiElement] = Nil, @volatile var style: Map[String, Any] = Map.empty) extends ChakraElement with HasChildren[Tbody] -case class Tfoot(key: String = Keys.nextKey, @volatile var children: Seq[UiElement] = Nil, @volatile var style: Map[String, String] = Map.empty) +case class Tfoot(key: String = Keys.nextKey, @volatile var children: Seq[UiElement] = Nil, @volatile var style: Map[String, Any] = Map.empty) extends ChakraElement with HasChildren[Tfoot] case class Tr( key: String = Keys.nextKey, @volatile var children: Seq[UiElement] = Nil, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement with HasChildren[Tr] case class Th( @@ -995,21 +995,21 @@ case class Th( @volatile var text: String = "", isNumeric: Boolean = false, @volatile var children: Seq[UiElement] = Nil, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement with HasChildren[Th] case class Td( key: String = Keys.nextKey, @volatile var text: String = "", isNumeric: Boolean = false, - @volatile var style: Map[String, String] = Map.empty, + @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement with HasChildren[Td] /** https://chakra-ui.com/docs/components/menu/usage */ -case class Menu(key: String = Keys.nextKey, @volatile var style: Map[String, String] = Map.empty, @volatile var children: Seq[UiElement] = Nil) +case class Menu(key: String = Keys.nextKey, @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil) extends ChakraElement with HasChildren[Menu] case class MenuButton( @@ -1017,23 +1017,23 @@ case class MenuButton( @volatile var text: String = "", @volatile var size: Option[String] = None, @volatile var colorScheme: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty, + @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement with HasChildren[MenuButton] -case class MenuList(key: String = Keys.nextKey, @volatile var style: Map[String, String] = Map.empty, @volatile var children: Seq[UiElement] = Nil) +case class MenuList(key: String = Keys.nextKey, @volatile var style: Map[String, Any] = Map.empty, @volatile var children: Seq[UiElement] = Nil) extends ChakraElement with HasChildren[MenuList] case class MenuItem( key: String = Keys.nextKey, - @volatile var style: Map[String, String] = Map.empty, + @volatile var style: Map[String, Any] = Map.empty, @volatile var text: String = "", @volatile var children: Seq[UiElement] = Nil ) extends ChakraElement with HasChildren[MenuItem] with OnClickEventHandler.CanHandleOnClickEvent[MenuItem] -case class MenuDivider(key: String = Keys.nextKey, @volatile var style: Map[String, String] = Map.empty) extends ChakraElement +case class MenuDivider(key: String = Keys.nextKey, @volatile var style: Map[String, Any] = Map.empty) extends ChakraElement case class Badge( key: String = Keys.nextKey, @@ -1042,7 +1042,7 @@ case class Badge( @volatile var variant: Option[String] = None, @volatile var size: String = "md", @volatile var children: Seq[UiElement] = Nil, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement with HasChildren[Badge] @@ -1058,7 +1058,7 @@ case class Image( @volatile var alt: String = "", @volatile var boxSize: Option[String] = None, @volatile var borderRadius: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement /** https://chakra-ui.com/docs/components/text @@ -1073,5 +1073,5 @@ case class Text( @volatile var align: Option[String] = None, @volatile var casing: Option[String] = None, @volatile var decoration: Option[String] = None, - @volatile var style: Map[String, String] = Map.empty + @volatile var style: Map[String, Any] = Map.empty ) extends ChakraElement diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala index 3a62181e..5307ed74 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala @@ -33,8 +33,8 @@ case class QuickTable( Tr(children = row.map(c => Td().withChildren(c))) this - def style: Map[String, String] = tableContainer.style - def style_=(s: Map[String, String]): Unit = tableContainer.style = s + def style: Map[String, Any] = tableContainer.style + def style_=(s: Map[String, Any]): Unit = tableContainer.style = s def caption(text: String): QuickTable = table.addChildren(TableCaption(text = text)) From 73acb8e4b39a48bbaf3411d89814a5953bd03765 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 11 Jan 2024 12:56:05 +0000 Subject: [PATCH 094/144] - --- Readme.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index 33962849..8aa80ce6 100644 --- a/Readme.md +++ b/Readme.md @@ -139,4 +139,16 @@ If there is a reasonable way to refactor to have immutability without compromisi # Need help? -Please use the discussions of the project to post any questions, comments or ideas. +Please use the [discussions](https://github.com/kostaskougios/terminal21-restapi/discussions) of the project to post any questions, comments or ideas. + +# Changelog + +## Version 0.2 + +- Quick* classes to simplify UI creation +- A few Nivo visualisation components +- spark integration, terminal21 acts as a spark notebook + +## Version 0.1 + +- initial release with std and chakra components From a0e2ade8d4c79c251431b7e97d8ba68d02a1c72e Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 11 Jan 2024 13:13:25 +0000 Subject: [PATCH 095/144] - --- Readme.md | 2 +- .../scala/tests/nivo/ResponsiveBarChart.scala | 34 ++++++++++++++----- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/Readme.md b/Readme.md index 8aa80ce6..a274c522 100644 --- a/Readme.md +++ b/Readme.md @@ -143,7 +143,7 @@ Please use the [discussions](https://github.com/kostaskougios/terminal21-restapi # Changelog -## Version 0.2 +## Version 0.11 - Quick* classes to simplify UI creation - A few Nivo visualisation components diff --git a/examples/src/main/scala/tests/nivo/ResponsiveBarChart.scala b/examples/src/main/scala/tests/nivo/ResponsiveBarChart.scala index a5b4e9a8..54bf7bcd 100644 --- a/examples/src/main/scala/tests/nivo/ResponsiveBarChart.scala +++ b/examples/src/main/scala/tests/nivo/ResponsiveBarChart.scala @@ -3,23 +3,39 @@ package tests.nivo import org.terminal21.client.components.nivo.* import tests.chakra.Common.commonBox +import scala.util.Random + object ResponsiveBarChart: def apply() = Seq( commonBox("ResponsiveBar"), ResponsiveBar( data = Seq( - Seq( - BarDatum("country", "AD"), - BarDatum("hot dog", 150), - BarDatum("hot dogColor", "hsl(202, 70%, 50%)"), - BarDatum("burger", 189), - BarDatum("burgerColor", "hsl(106, 70%, 50%)"), - BarDatum("sandwich", 170), - BarDatum("sandwichColor", "hsl(115, 70%, 50%)") - ) + dataFor("AD"), + dataFor("AE"), + dataFor("GB"), + dataFor("GR"), + dataFor("IT"), + dataFor("FR"), + dataFor("GE"), + dataFor("US") ), keys = Seq("hot dog", "burger", "sandwich", "kebab", "fries", "donut"), + indexBy = "country", + padding = 0.3, + axisLeft = Some(Axis(legend = "food", legendOffset = -40)), + axisBottom = Some(Axis(legend = "country", legendOffset = 32)), valueScale = Scale(`type` = "linear"), indexScale = Scale(`type` = "band", round = Some(true)) ) ) + + def dataFor(country: String) = + Seq( + BarDatum("country", country), + BarDatum("hot dog", Random.nextInt(500) + 50), + BarDatum("hot dogColor", "hsl(202, 70%, 50%)"), + BarDatum("burger", Random.nextInt(500) + 50), + BarDatum("burgerColor", "hsl(106, 70%, 50%)"), + BarDatum("sandwich", Random.nextInt(500) + 50), + BarDatum("sandwichColor", "hsl(115, 70%, 50%)") + ) From 17ba220ff7f8bf329c6f15a88a70888e6384a1f3 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 11 Jan 2024 13:38:56 +0000 Subject: [PATCH 096/144] - --- .../scala/tests/nivo/ResponsiveBarChart.scala | 20 +++++++++++++++---- .../client/components/nivo/Defs.scala | 14 +++++++++++++ .../client/components/nivo/Fill.scala | 8 ++++++++ .../client/components/nivo/NivoElement.scala | 2 ++ 4 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Defs.scala create mode 100644 terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Fill.scala diff --git a/examples/src/main/scala/tests/nivo/ResponsiveBarChart.scala b/examples/src/main/scala/tests/nivo/ResponsiveBarChart.scala index 54bf7bcd..417a1ca4 100644 --- a/examples/src/main/scala/tests/nivo/ResponsiveBarChart.scala +++ b/examples/src/main/scala/tests/nivo/ResponsiveBarChart.scala @@ -22,6 +22,11 @@ object ResponsiveBarChart: keys = Seq("hot dog", "burger", "sandwich", "kebab", "fries", "donut"), indexBy = "country", padding = 0.3, + defs = Seq( + Defs("dots", "patternDots", "inherit", "#38bcb2", size = Some(4), padding = Some(1), stagger = Some(true)), + Defs("lines", "patternLines", "inherit", "#eed312", rotation = Some(-45), lineWidth = Some(6), spacing = Some(10)) + ), + fill = Seq(Fill("dots", Match("fries")), Fill("lines", Match("sandwich"))), axisLeft = Some(Axis(legend = "food", legendOffset = -40)), axisBottom = Some(Axis(legend = "country", legendOffset = 32)), valueScale = Scale(`type` = "linear"), @@ -30,12 +35,19 @@ object ResponsiveBarChart: ) def dataFor(country: String) = + def r = Random.nextInt(500) + 50 Seq( BarDatum("country", country), - BarDatum("hot dog", Random.nextInt(500) + 50), + BarDatum("hot dog", r), BarDatum("hot dogColor", "hsl(202, 70%, 50%)"), - BarDatum("burger", Random.nextInt(500) + 50), + BarDatum("burger", r), BarDatum("burgerColor", "hsl(106, 70%, 50%)"), - BarDatum("sandwich", Random.nextInt(500) + 50), - BarDatum("sandwichColor", "hsl(115, 70%, 50%)") + BarDatum("sandwich", r), + BarDatum("sandwichColor", "hsl(115, 70%, 50%)"), + BarDatum("kebab", r), + BarDatum("kebabColor", "hsl(113, 70%, 50%)"), + BarDatum("fries", r), + BarDatum("friesColor", "hsl(209, 70%, 50%)"), + BarDatum("donut", r), + BarDatum("donutColor", "hsl(47, 70%, 50%)") ) diff --git a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Defs.scala b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Defs.scala new file mode 100644 index 00000000..5e7a3c8a --- /dev/null +++ b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Defs.scala @@ -0,0 +1,14 @@ +package org.terminal21.client.components.nivo + +case class Defs( + id: String = "dots", + `type`: String = "patternDots", + background: String = "inherit", + color: String = "#38bcb2", + size: Option[Int] = None, + padding: Option[Float] = None, + stagger: Option[Boolean] = None, + rotation: Option[Int] = None, + lineWidth: Option[Int] = None, + spacing: Option[Int] = None +) diff --git a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Fill.scala b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Fill.scala new file mode 100644 index 00000000..b7bd47f9 --- /dev/null +++ b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/Fill.scala @@ -0,0 +1,8 @@ +package org.terminal21.client.components.nivo + +case class Fill( + id: String, + `match`: Match +) + +case class Match(id: String) diff --git a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala index 9126a983..7200fc36 100644 --- a/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala +++ b/terminal21-nivo/src/main/scala/org/terminal21/client/components/nivo/NivoElement.scala @@ -46,6 +46,8 @@ case class ResponsiveBar( @volatile var valueScale: Scale = Scale(), @volatile var indexScale: Scale = Scale(), @volatile var colors: Map[String, String] = Map("scheme" -> "nivo"), + @volatile var defs: Seq[Defs] = Nil, + @volatile var fill: Seq[Fill] = Nil, @volatile var axisTop: Option[Axis] = None, @volatile var axisRight: Option[Axis] = None, @volatile var axisBottom: Option[Axis] = Some(Axis(legend = "y", legendOffset = 36)), From 6a3b6c1138a29ea97c4abfc8f66a2665c62df961 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 11 Jan 2024 13:44:01 +0000 Subject: [PATCH 097/144] - --- .../main/scala/tests/nivo/ResponsiveBarChart.scala | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/examples/src/main/scala/tests/nivo/ResponsiveBarChart.scala b/examples/src/main/scala/tests/nivo/ResponsiveBarChart.scala index 417a1ca4..9f734117 100644 --- a/examples/src/main/scala/tests/nivo/ResponsiveBarChart.scala +++ b/examples/src/main/scala/tests/nivo/ResponsiveBarChart.scala @@ -30,7 +30,18 @@ object ResponsiveBarChart: axisLeft = Some(Axis(legend = "food", legendOffset = -40)), axisBottom = Some(Axis(legend = "country", legendOffset = 32)), valueScale = Scale(`type` = "linear"), - indexScale = Scale(`type` = "band", round = Some(true)) + indexScale = Scale(`type` = "band", round = Some(true)), + legends = Seq( + Legend( + dataFrom = "keys", + translateX = 120, + itemsSpacing = 2, + itemWidth = 100, + itemHeight = 20, + symbolSize = 20, + symbolShape = "square" + ) + ) ) ) From ced90c789a4ab1da6573638b2a0e5c15338f1e70 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 11 Jan 2024 13:50:20 +0000 Subject: [PATCH 098/144] - --- .../scala/tests/nivo/ResponsiveBarChart.scala | 13 ++++--- .../tests/nivo/ResponsiveLineChart.scala | 34 +++++++++---------- .../src/main/scala/tests/nivo/common.scala | 5 +++ 3 files changed, 28 insertions(+), 24 deletions(-) create mode 100644 examples/src/main/scala/tests/nivo/common.scala diff --git a/examples/src/main/scala/tests/nivo/ResponsiveBarChart.scala b/examples/src/main/scala/tests/nivo/ResponsiveBarChart.scala index 9f734117..787a9c88 100644 --- a/examples/src/main/scala/tests/nivo/ResponsiveBarChart.scala +++ b/examples/src/main/scala/tests/nivo/ResponsiveBarChart.scala @@ -46,19 +46,18 @@ object ResponsiveBarChart: ) def dataFor(country: String) = - def r = Random.nextInt(500) + 50 Seq( BarDatum("country", country), - BarDatum("hot dog", r), + BarDatum("hot dog", rnd), BarDatum("hot dogColor", "hsl(202, 70%, 50%)"), - BarDatum("burger", r), + BarDatum("burger", rnd), BarDatum("burgerColor", "hsl(106, 70%, 50%)"), - BarDatum("sandwich", r), + BarDatum("sandwich", rnd), BarDatum("sandwichColor", "hsl(115, 70%, 50%)"), - BarDatum("kebab", r), + BarDatum("kebab", rnd), BarDatum("kebabColor", "hsl(113, 70%, 50%)"), - BarDatum("fries", r), + BarDatum("fries", rnd), BarDatum("friesColor", "hsl(209, 70%, 50%)"), - BarDatum("donut", r), + BarDatum("donut", rnd), BarDatum("donutColor", "hsl(47, 70%, 50%)") ) diff --git a/examples/src/main/scala/tests/nivo/ResponsiveLineChart.scala b/examples/src/main/scala/tests/nivo/ResponsiveLineChart.scala index 0cd78448..2646b092 100644 --- a/examples/src/main/scala/tests/nivo/ResponsiveLineChart.scala +++ b/examples/src/main/scala/tests/nivo/ResponsiveLineChart.scala @@ -13,23 +13,11 @@ object ResponsiveLineChart: commonBox("ResponsiveLine"), ResponsiveLine( data = Seq( - Serie( - "japan", - data = Seq( - Datum("plane", 262), - Datum("helicopter", 26), - Datum("boat", 43) - ) - ), - Serie( - "france", - "hsl(186, 70%, 50%)", - Seq( - Datum("plane", 271), - Datum("helicopter", 31), - Datum("boat", 27) - ) - ) + dataFor("Japan"), + dataFor("France"), + dataFor("Greece"), + dataFor("UK"), + dataFor("Germany") ), yScale = Scale(stacked = Some(true)), axisBottom = Some(Axis(legend = "transportation", legendOffset = 36)), @@ -39,3 +27,15 @@ object ResponsiveLineChart: ) ) ) + + def dataFor(country: String) = + Serie( + country, + data = Seq( + Datum("plane", rnd), + Datum("helicopter", rnd), + Datum("boat", rnd), + Datum("car", rnd), + Datum("submarine", rnd) + ) + ) diff --git a/examples/src/main/scala/tests/nivo/common.scala b/examples/src/main/scala/tests/nivo/common.scala new file mode 100644 index 00000000..5a5c849c --- /dev/null +++ b/examples/src/main/scala/tests/nivo/common.scala @@ -0,0 +1,5 @@ +package tests.nivo + +import scala.util.Random + +def rnd = Random.nextInt(500) + 50 From ec174240c5b919a0e700403399718f033c483f78 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 11 Jan 2024 14:02:46 +0000 Subject: [PATCH 099/144] - --- Readme.md | 3 +++ docs/chakra.md | 21 +++++++++++++++++++++ docs/images/chakra/box.png | Bin 0 -> 1328 bytes docs/images/chakra/button.png | Bin 0 -> 3324 bytes 4 files changed, 24 insertions(+) create mode 100644 docs/chakra.md create mode 100644 docs/images/chakra/box.png create mode 100644 docs/images/chakra/button.png diff --git a/Readme.md b/Readme.md index a274c522..b91aab41 100644 --- a/Readme.md +++ b/Readme.md @@ -105,6 +105,9 @@ can be used for things like: - POC code at the office can be presented via a scala-cli script + terminal21 UI. The POC code can be imported as a lib in a script. - logs can be viewed and searched via scripts - ... and so on +# Available UI Components + +[Chakra](docs/chakra.md) # Architecture diff --git a/docs/chakra.md b/docs/chakra.md new file mode 100644 index 00000000..8c390e94 --- /dev/null +++ b/docs/chakra.md @@ -0,0 +1,21 @@ +# Chakra components + + +[The case classes](../terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala) + +### Button + +![Button](images/chakra/button.png) +```scala +val b = Button(text = "Keep Running") +b.onClick: () => + b.text = "Clicked" + session.render() +``` + +### Box + +![Box](images/chakra/box.png) +```scala +Box(text = "Badges", bg = "green", p = 4, color = "black") +``` \ No newline at end of file diff --git a/docs/images/chakra/box.png b/docs/images/chakra/box.png new file mode 100644 index 0000000000000000000000000000000000000000..2178f7759cd008820a88fc1982a1e90a8cd29f70 GIT binary patch literal 1328 zcmb7E>sQhT03}o7q$p4_AFX_4*3?AIM`~dSN`{uTlC7G6L1bFIvx@u`Wh=CTqykW2jg2G(*wcuu0fv?(*>F@p9 ziA%P2*%&k?w%f&a3^S$Yz1x3OytuG;j>W|m|Gq(~)hcpZ>XQ$DD;UmcN!4Bi7kcly zQM}LJd+n9~DWy=(InHU$Nxfx>sA>M>;H~Y6GzC(x1Es({Knc_VH?0i0h%5xm6;z=E zOx>P8s9SyP%+>8fQA3eK+XYej`=_lf4uuV4h6eH5S-MxJD{TO;BkQtthjk{M9HD-7 z$G@9hgaLWOlH47SFk0^Nd*2!tvy@NaGMRq2T6Lu1@155#gUOEg5lR!( zy+dlq)Gf?cy_xQjjO(0C+e~{|n@m2yv9pw_R)l&diGe4PwW<}t{rgK)Id!=d#|JI? z;koG7?0+Wua$FhDCefRzBiObcT5+)Y@hyVMxl*DT!sp$l*~Zl*N=B;|12n)22QpJ< zQt9@3Fp3xg#(lYtgm=tSDbyrR_4s7;i-2=Gq^W{xrVulOy4;k%S)9{<*R&4w2I}GR zgqke?lW*Z3Rj~q-*84=b*o>0OtGi5;l6I!BZ@|zfi|Ei-2?O?2BzmsQ?dS|$fRAjZ ziVMXJ{Br|@=K=K6)OGNT@o6@RsJV~Gg#e0EqE!ca)%t+R$TZ|udx6`=VR4Sn_QkznU5@^lUrwTOM#xdxl;#vP{uNxYqCO0Sbzg0`yy*c2J2ib(o!vSjokKg-(K1inJiQNU$L zv!Co|x`B1A>lz1y8vF4?x?i}fTYiVx%a}HpgYN3!$8(=-bxTfcXKVfBT*ys@mw2kV z&szW3=W}%qj4HW*nma_a*7!8xs5%91wo|8k=c@VBT#K29xz;6o@=QICuwF4(8Y?nL zHp5&+RA304F^yw~I8QaZ5d?{swjq64PFeK>8@O&Nu6QDh0xgz8gKgw+jwur>jVx_v z>N3lWZ+d%;t`o6jl-Y@M0B$*4Kou?g0tmbFPKdYQq?3TSXfG=6}3k+n5XfMlHak+ddxjLqC%4US#iw+X{gXs8s6I);$UO& zH`m|4UK%aV+E@T1AS$-B&Cos-#(I9y;dm`Utm&b~KYXS;m!A-X!z9K1wJWXQKOWtG AHUIzs literal 0 HcmV?d00001 diff --git a/docs/images/chakra/button.png b/docs/images/chakra/button.png new file mode 100644 index 0000000000000000000000000000000000000000..0dff59dab21f71f281b30e511343a4cb01e97971 GIT binary patch literal 3324 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 Date: Thu, 11 Jan 2024 14:10:49 +0000 Subject: [PATCH 100/144] - --- docs/chakra.md | 36 +++++++++++++++++++++++++++++++++- docs/images/chakra/hstack.png | Bin 0 -> 4081 bytes docs/images/chakra/menu.png | Bin 0 -> 14792 bytes 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 docs/images/chakra/hstack.png create mode 100644 docs/images/chakra/menu.png diff --git a/docs/chakra.md b/docs/chakra.md index 8c390e94..6a6d6c2c 100644 --- a/docs/chakra.md +++ b/docs/chakra.md @@ -1,8 +1,9 @@ # Chakra components - [The case classes](../terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala) +Note: only a fraction of the available properties of each component is documented here. +See the case class for a full list as well as the chakra react documentation (links are available in the scaladocs of each case class). ### Button ![Button](images/chakra/button.png) @@ -18,4 +19,37 @@ b.onClick: () => ![Box](images/chakra/box.png) ```scala Box(text = "Badges", bg = "green", p = 4, color = "black") +``` + +## HStack / VStack +Horizontal / Vertical stack of elements + +![HStack](images/chakra/hstack.png) +```scala +HStack().withChildren( + checkbox1, + checkbox2 +) +``` + +### Menus + +![Menu](images/chakra/menu.png) + +```scala +Menu().withChildren( + MenuButton(text = "Actions menu0001", size = Some("sm"), colorScheme = Some("teal")).withChildren( + ChevronDownIcon() + ), + MenuList().withChildren( + MenuItem(text = "Download menu-download") + .onClick: () => + box1.text = "'Download' clicked" + session.render() + , + MenuItem(text = "Copy").onClick: () => + box1.text = "'Copy' clicked" + session.render() + ) +) ``` \ No newline at end of file diff --git a/docs/images/chakra/hstack.png b/docs/images/chakra/hstack.png new file mode 100644 index 0000000000000000000000000000000000000000..8d284e2b0953cbdaf8741b19476a9b7ed0c6f465 GIT binary patch literal 4081 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 Kx>C{<{J#LIN#sfZ literal 0 HcmV?d00001 From b259a0e6a1e41de4020ba13a7949575512d97c7d Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 11 Jan 2024 14:18:31 +0000 Subject: [PATCH 101/144] - --- docs/chakra.md | 24 +++++++++++++++++++++++- docs/images/chakra/forms.png | Bin 0 -> 70819 bytes 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 docs/images/chakra/forms.png diff --git a/docs/chakra.md b/docs/chakra.md index 6a6d6c2c..b8322789 100644 --- a/docs/chakra.md +++ b/docs/chakra.md @@ -52,4 +52,26 @@ Menu().withChildren( session.render() ) ) -``` \ No newline at end of file +``` + +### Forms + +see [Forms](../examples/src/main/scala/tests/chakra/Forms.scala) as an example on how to create forms. + +![Forms](images/chakra/forms.png) + +Use FormControl to wrap your form elements: + +```scala +FormControl().withChildren( + FormLabel(text = "Email address"), + InputGroup().withChildren( + InputLeftAddon().withChildren(EmailIcon()), + Input(`type` = "email", value = "my@email.com"), + InputRightAddon().withChildren(CheckCircleIcon(color = Some("green"))) + ), + FormHelperText(text = "We'll never share your email.") +) +``` + + diff --git a/docs/images/chakra/forms.png b/docs/images/chakra/forms.png new file mode 100644 index 0000000000000000000000000000000000000000..00fc91fb745125f876c3d396ddd7a61fb95ea7b7 GIT binary patch literal 70819 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�prW0;6G;Y>;0Qm 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%0DsXC+O26T)MWxoZ#=jxjuF%C`?57FeR5qxQi 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 literal 0 HcmV?d00001 From c207cf19a8860c60b660fe6c6c93b5fd6f23b863 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 11 Jan 2024 14:28:10 +0000 Subject: [PATCH 102/144] - --- Readme.md | 1 + docs/chakra.md | 46 ++++++++++++++++++++++++ docs/images/chakra/ccs.png | Bin 0 -> 38746 bytes docs/images/chakra/editable-editing.png | Bin 0 -> 25880 bytes docs/images/chakra/editable.png | Bin 0 -> 22915 bytes docs/images/chakra/simplegrid.png | Bin 0 -> 2703 bytes 6 files changed, 47 insertions(+) create mode 100644 docs/images/chakra/ccs.png create mode 100644 docs/images/chakra/editable-editing.png create mode 100644 docs/images/chakra/editable.png create mode 100644 docs/images/chakra/simplegrid.png diff --git a/Readme.md b/Readme.md index b91aab41..1a814b5e 100644 --- a/Readme.md +++ b/Readme.md @@ -151,6 +151,7 @@ Please use the [discussions](https://github.com/kostaskougios/terminal21-restapi - Quick* classes to simplify UI creation - A few Nivo visualisation components - spark integration, terminal21 acts as a spark notebook +- ui component documentation ## Version 0.1 diff --git a/docs/chakra.md b/docs/chakra.md index b8322789..2b9d1bc3 100644 --- a/docs/chakra.md +++ b/docs/chakra.md @@ -74,4 +74,50 @@ FormControl().withChildren( ) ``` +### Editable +Editable is a textarea but looks like normal text until the user clicks it. + +```scala +val editable1 = Editable(defaultValue = "Please type here").withChildren( + EditablePreview(), + EditableInput() +) + +editable1.onChange: newValue => + status.text = s"editable1 newValue = $newValue, verify editable1.value = ${editable1.value}" + session.render() + +// Use text area for longer texts +val editable2 = Editable(defaultValue = "For longer maybe-editable texts\nUse an EditableTextarea\nIt uses a textarea control.").withChildren( + EditablePreview(), + EditableTextarea() +) + +``` +![Editable](images/chakra/editable.png) +![Editable](images/chakra/editable-editing.png) + +### SimpleGrid + +```scala +SimpleGrid(spacing = Some("8px"), columns = 4).withChildren( + Box(text = "One", bg = "yellow", color = "black"), + Box(text = "Two", bg = "tomato", color = "black"), + Box(text = "Three", bg = "blue", color = "black") +) +``` + +![SimpleGrid](images/chakra/simplegrid.png) + +### Center / Circle / Square +```scala +Center(text = "Center demo, not styled"), +Center(text = "Center demo center-demo-0001", bg = Some("tomato"), color = Some("white"), w = Some("100px"), h = Some("100px")), +Circle(text = "Circle demo, not styled"), +Circle(text = "Circle demo circle-demo-0001", bg = Some("tomato"), color = Some("white"), w = Some("100px"), h = Some("100px")), +Square(text = "Square demo, not styled"), +Square(text = "Square demo square-demo-0001", bg = Some("tomato"), color = Some("white"), w = Some("100px"), h = Some("100px")) +``` + +![CCS](images/chakra/ccs.png) \ No newline at end of file diff --git a/docs/images/chakra/ccs.png b/docs/images/chakra/ccs.png new file mode 100644 index 0000000000000000000000000000000000000000..ea2541950902e48658576325732d634b058be10f GIT binary patch literal 38746 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 dS(q*VWA|jYVj|^w&jHSH$4N|O literal 0 HcmV?d00001 diff --git a/docs/images/chakra/editable-editing.png b/docs/images/chakra/editable-editing.png new file mode 100644 index 0000000000000000000000000000000000000000..f707171666e6cd840672957706356ec776c67661 GIT binary patch literal 25880 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 JiM-{@{{?Hh2`K;o literal 0 HcmV?d00001 diff --git a/docs/images/chakra/editable.png b/docs/images/chakra/editable.png new file mode 100644 index 0000000000000000000000000000000000000000..6a7c64eb8b52a613b55e4285e90cd4c417debea4 GIT binary patch literal 22915 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 literal 0 HcmV?d00001 diff --git a/docs/images/chakra/simplegrid.png b/docs/images/chakra/simplegrid.png new file mode 100644 index 0000000000000000000000000000000000000000..da3fd24408acf32c245e049f0ce8e4a86e5b019b GIT binary patch literal 2703 zcmV;A3UKv_P)N000*V1^@s6ytBPr000VBNkl}uyN-a_h`=)GdImK4sv=-rjfOSJkH(EVV zY4uRRwrDx_9Pog$BRzmXQKVMcp}>(n0rKs=zc)iN^J6k021q9NkI(t#z2BWT^T(Vw zAMU;H2gN3&qiIYAzBkMXfE1-#Q(WvoR@W3XYmou@?v^EnNl47V@ezH2r}_a;DoQnF zVt>r+mW_xOX}0gN5SQpgXromi{@N=_btX7uC*qt*x4*ljQlZo&R4SB!K&3(n5L7CZ z8jVVY5)i0VC;@^>g;JwYsZas}l?o+5P^nOAG!>DG6C+tFCJq25l*3c~?Hj$K+%Hrr zlmMZ^QgQt{VdF-^vgL#~-z4nVK`1FHy{{+_0)JBBWU2VHFc-VO+XXux%fT*-F8G)+ zf9GJwcXDuiST7X|<-y@sDiT>LR`RBOh}p^Pa`ADQzh(ZQYjgjA`4Mx;W5ZXJdrgI; zV#^jnr%r^hFhYDhAtr_p8cN8^BOE?#Vie^;;7=-&+qA=9^Ro~WS|5$;2O)xKST6`+ zL3MAB8}aystYlzPfqk=Al>32Ssc6Ge@kidY2QcAGgzFmWHSXiu+?Ozon6n-mzM|Y~ z{7S{%y@aSJLYFRtt=k9}E)dS0Bdl9TNJt>0rV>t{CfjDODE9+@QX!d&BSU-RKZOtD z$P>)4o*0`Phr~t=aNzN7IKuOv59xtRqx;&WLdqgiHYsmTvK#U`H=6BGDuZ9Ckn+c0 zc+)=4e9jzU{>0Q{WM0NHf6TSH|AvWX&UtM3igK^Q2H9_%@9!$ za&pXjOSoe5>y9Y7WTB}N;p0@ zCK5po$L)TL5`O(d(Ty=aIS!ce2-fxMgqYA^#DxW;Yx4+1h15eTue)bZR}~B8?x>Vh zNZ*7=#TMJfPV`$QiW$#jF+E_)Ap>C~LSmj{n!rIr`09iytz2h5Winu-q>JmC?wWV6 zFM=YOkk-sYFl!Hnr?e=w5uZ{qVyx53L=Z=Ae=l&Xx^OAwry#z z#hEjN^mIagz8OpTBss}sK;&v zhN4u2Dwm3-*4|C@6Xq&Y!o262TkDFM=Niw3k%1d9*@ir(19Q|BCs&b*0@qkdFwK|= zC|z5!CeqXKk}F23t@xCRk>i}$zTHU0)~#gQ%-y{Hx@qA2;tNwEkxa#c1)ed_JY#w< zZrvih|Grss(}#r1mkAdyn$pLlNrc2iGxmNDMe#+INQKPvbwGSHCZ@IkelXA~W%P}0 zis=~%7~VPtnDQw8p4Y+3Ok8H6SktGy^*#RN@ouF(7LyBDO4{?k{2bV;*$U-usB)>0 zOhp(>pM`twGSdR4Tp{B*OjDF*E(AGUQioZ_T!E1dxxNCk-brRT(}39nbGk%gL@pj< zGGYHLN^QcYRE!wo#DV<;hl8+oE!j46H}mG1?8Mo#CQ~69hvmyXW9G~uBqtMY-X#3t z_eLsOwC9@W+Oea zA&SQ3V@9SE{o zB#$Fp(pxb790o){=Yf7%8{NoVzbWsg3xQio%gj zT)Ieziz7^#LblD^O^+U?e?qblA_O88%a(b@%${8;6)RR4QIY1%-Mb09b{Y2VBYgIm z>CNzd4@L1sl}QB)f%HOrE3`fi4(^7`$VS-0LUDa;9+De}V#}l1=ou4`KOE zdFw5cb&wK>jE^2IUn+`=&54nqAi~O(o-xvcv3WD0$;YW`2^+xT;88WsZIEm3Mp%J?rh2vFTQBx z;-ilU)29=Hg9%R*60Th%D^`w)Hfch5;|()*Gi#RVm$-bzWHiQ%A%uq$UV53ZX%k_| z60`n{8J@3G6n|7kDyF8jL{fODO)BKx^bF~OCl-qqMieFuuo^XGPELME=0aX0B@*ei z5P^6#I|UmGvhcHZiKy#vV0MSr+E1a}9hH&_DOJdi`)1q5ZjO33fn6%(w8qa}W04Cv z*-=(11j$6m^Dll1BNB3?Gy&#xhh!n-gore4O0(v#nGczTOk?I%*guO>oA4_Yf;3=G zol1y_HPRrbJ)BO$^Ye`;xZ|X!B0HO~W=-jwAkV9ir*B?$#Y!{g%$er6sgzezQV0td z>a!}8yQeZzF~4&X`o~A%+E}|(tnc615)Ns~w4{RXA067$%52Q;(5AGc!Si2_?t|%> z3245{ctFXFDfM!+hF=0cH1^~qCYd)%!M*@(oC5TQ`(U6 zFPZM}?5Pk$GDHrNVR|V&Ij({FGKsa>B zoZRrfFGwcB`~L)yvCMnN%EPimSfniCeNRy;!d*)RBhQ<-B|rS$8@r2JcQ_7Oa;JC^ z2bPR0WBTIM@LnjAEQTc_cZgM#@~D(l$WcyT{R_VC$(-CPFUF1ZPe|G0+8yq>k<%Yi zGEtdO40k0Jic;OEj8s%PvbJU>ly5?%q(V`uA1W0}jiy>ig`#{@R4SAJL8U^e(Wq1? z0f9<|5+JBlC^Z_D3MC*=sZat0l?tUsqf(&+1S%CufZ#0^{|B7VyOpy06I}oR002ov JPDHLkV1mc(6>$In literal 0 HcmV?d00001 From 62f148e5dcfe85d50956ed2a4b8f78c2253cb436 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 11 Jan 2024 14:32:30 +0000 Subject: [PATCH 103/144] - --- docs/chakra.md | 41 +++++++++++++++++++++++++++++++++- docs/images/chakra/images.png | Bin 0 -> 110262 bytes 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 docs/images/chakra/images.png diff --git a/docs/chakra.md b/docs/chakra.md index 2b9d1bc3..d8468a75 100644 --- a/docs/chakra.md +++ b/docs/chakra.md @@ -120,4 +120,43 @@ Square(text = "Square demo, not styled"), Square(text = "Square demo square-demo-0001", bg = Some("tomato"), color = Some("white"), w = Some("100px"), h = Some("100px")) ``` -![CCS](images/chakra/ccs.png) \ No newline at end of file +![CCS](images/chakra/ccs.png) + +### Image + +```scala +Image( + src = "https://bit.ly/dan-abramov", + alt = "Dan Abramov", + boxSize = Some("150px") +), +Image( + src = "https://bit.ly/dan-abramov", + alt = "Dan Abramov", + boxSize = Some("150px"), + borderRadius = Some("full") +) +``` + +![Images](images/chakra/images.png) + +Images can be hosted in the server under ~/.terminal21: + +```shell + ~/.terminal21  tree +. +└── web + └── images + ├── ball.png + ├── logo1.png + └── logo2.png +``` +and then used like: +```scala +Image( + src = "/web/images/logo1.png", + alt = "logo no 1", + boxSize = Some("150px"), + borderRadius = Some("full") +) +``` \ No newline at end of file diff --git a/docs/images/chakra/images.png b/docs/images/chakra/images.png new file mode 100644 index 0000000000000000000000000000000000000000..e91bf154924afbaaa8c483981e8987a10a8d7346 GIT binary patch literal 110262 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 nN9b^m!{+ha{*M)DqxA+ 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 KMNUMnLSTX|2}auh literal 0 HcmV?d00001 From d0571d81a5a6d81dfabf5784b5241ef4d47a104f Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 11 Jan 2024 15:49:41 +0000 Subject: [PATCH 104/144] - --- docs/chakra.md | 71 ++++++++++++++++++++++++++++++++++- docs/images/chakra/icons.png | Bin 0 -> 23971 bytes 2 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 docs/images/chakra/icons.png diff --git a/docs/chakra.md b/docs/chakra.md index d8468a75..731f83fa 100644 --- a/docs/chakra.md +++ b/docs/chakra.md @@ -159,4 +159,73 @@ Image( boxSize = Some("150px"), borderRadius = Some("full") ) -``` \ No newline at end of file +``` + +### Icons + +see [Chakra Icons](https://chakra-ui.com/docs/components/icon). + +```scala +InfoIcon(color = Some("tomato")), +MoonIcon(color = Some("green")), +AddIcon(), +ArrowBackIcon(), +ArrowDownIcon(), +ArrowForwardIcon(), +ArrowLeftIcon(), +ArrowRightIcon(), +ArrowUpIcon(), +ArrowUpDownIcon(), +AtSignIcon(), +AttachmentIcon(), +BellIcon(), +CalendarIcon(), +ChatIcon(), +CheckIcon(), +CheckCircleIcon(), +ChevronDownIcon(), +ChevronLeftIcon(), +ChevronRightIcon(), +ChevronUpIcon(), +CloseIcon(), +CopyIcon(), +DeleteIcon(), +DownloadIcon(), +DragHandleIcon(), +EditIcon(), +EmailIcon(), +ExternalLinkIcon(), +HamburgerIcon(), +InfoIcon(), +InfoOutlineIcon(), +LinkIcon(), +LockIcon(), +MinusIcon(), +MoonIcon(), +NotAllowedIcon(), +PhoneIcon(), +PlusSquareIcon(), +QuestionIcon(), +QuestionOutlineIcon(), +RepeatIcon(), +RepeatClockIcon(), +SearchIcon(), +Search2Icon(), +SettingsIcon(), +SmallAddIcon(), +SmallCloseIcon(), +SpinnerIcon(), +StarIcon(), +SunIcon(), +TimeIcon(), +TriangleDownIcon(), +TriangleUpIcon(), +UnlockIcon(), +UpDownIcon(), +ViewIcon(), +ViewOffIcon(), +WarningIcon(), +WarningTwoIcon() +``` + +![Icons](images/chakra/icons.png) \ No newline at end of file diff --git a/docs/images/chakra/icons.png b/docs/images/chakra/icons.png new file mode 100644 index 0000000000000000000000000000000000000000..35bd3616677750cc8f01cc527cc5691ca7924f27 GIT binary patch literal 23971 zcmV)hK%>8jP)9CJ2OUd&N=6ta|Q{Lkqm+eh=SxC1W`beBne6q$&!yc zSkzAoeh2RB{>T`a$m#bWgCSABd$0Km2?ykj+>!s#sNdb6503f_o$`L>ZX?*W?(yYw z_w|1U*#6UZz`x_8|F#WeKQNIaAB#WJ{7nd;hv9SuxdHFrgTbw%k$~}tuQ88r*m>*i zZ#dcgg+CS;C+58Mf!@!9A)M{@!fVTdAwKk-@7sCo#UI(vd+mg~uij^JUmu@v`v&gk z{roS)4el{c2>Y4P?FWO~j@Wy}cCH1o6$#n*gaXgt{o5ZA?y>tL#~gm(SN~%R+rE`m zu=f8<^0WE7_wSLy&Re%^?7#iQ2;mTJY(wRVk;1lv4Ws?|=>LBL0*!G($%po6H=%5j zeU|u~v%%zJbu^L~`#%+K86~7_5?X(LPq?2YlIL>kd_wD|y;r1pM5^?8Bt^EIlB9Sh z#UsTs%7aBSO0f#rq+g@Fvb0@c+10g}tnXArrnM?4O={+pR3$RW1Lm3}#WF<>?qhx? zEu2{rmoRMnz(9x9YJ#x;K-;qb5gsiaz$1~PZsYf`W0 z33CE z&G_c1RYaz&nJ&*veO?k9oqV)NhG-r8LxcXCXHHoryG(j(icDDXk|ZycMV~3w{V(pw z>wyAkw5`&us!8j?9i{ukKGJb?52?|up~~-p0;%IyPNp5^)`JDpnsH30c(7m^{TUzk zpdH3GE^X1bf8|dtRpl-f@(<{5ome&9{XP$`eOCRNybBXfSX7}#Ex zzPn5|oAx(-vMDGwpV%yGkFAqFQ~FD~in-%9=JAOrodl&@Rh9lThp6pb|KWOByyp!W zv2d*FP*TR*$ny`;=f4mV*xXVrtH@i2S4;B&?bS9|eGP>N@~4riUFyrKBdeu;uVy-b zyw^m-=)t2!Gf27$xutx&T2irnZK>da{>^;GHGb|N3g~C?W) zOW#{AqZW^ss-5erOk&l3Fcf$EC$aSK1!H8*(X}#p^)xBbyprPwyw`_9lny2qb(pbh)m8V^u13S1691Pa~;JPTHt%E19tTC0X~udRh132K8t07FU6&xyF1N zY35bts`-un2j&1`3w&kSHr3^o^)vOEG>;2#oo}do(yHCB+q0?B{i(9j#GG3`+oJZN z-;6;fF0G_8jE{KnPl+holeT;==``jkCq6m3Ri>_;A(cATx#z>Y--{R-)oxZadO3K` z2uW2oNBqV-zbA@2`4nAhlzg6CQ2k?E^^-oD zHqw;KDY@$wk7({DN|hmxKs;0NoGeXV$&w+TOs`l*uC=Nz(xIkEdl&eg0lZNs&5tjL+f=3DN(=NwZ0##_i;bQ(vn5S1eyEk0!~g_ez!` zr}XbXQm$OOCi08uzk`iU@>IE`M5&tc-8bjt?(JV>!`dy9Gj}O%gYj+FytDjv=Qp{2 z?S@R6G(+1@8PBpx2>I|_88a7_#f#pQJ3rr*J2&sh{CRIkx{L)Rh55{Ty&v%0DN^N@ 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%0AHrvbx`!Lg8O}{dM`@pU z4pN)lJIw7k%E^#w{S7|JfwTy|bL%7?DW+*6;sF(xRCpb?SVQDt+F2RnI=076Z;h9yHFJE#)YM z1;Nf&QNcb&h~M3x0|Eh#8VD9NxPcNwtPyB3VVAF5Ei+!7FLUNE7PkFc%zbUKp3$Ob z*@iG^UeG@08nLyXDlrVGn{-CO07nlDA2m^(x_9D*0_}4?872Jg{sLA8c^0b32r$@g z&x8!uzLVTID<1^q5RkmUg9%Ij>o;zt%E*M{MKKVbp(d#x&?mbBkE6voA#)rAMl6k) z%(Le%(#g$s6p{-+<4f|3{Lv$f0^5#A)Tz29apO#6Ucf`I=12D;|13cFbN3`?9h?-jR82LRTBgj7`K1jdRj*AOoxkE~kf%vaJF*L=F=XYBw%llE!#{VNRlBs z$=<$mx7y86fnYe;Ims4G`q-&kFW-wEk%Dp zlLRV0>?ih+`M@|ch9m*#FSaMr{_~wcq%-G{dfT!=Sfr#gAn@aiV&)9TG`%`!p;mXX z@JO=Dc02SEKlJ+2l`5x5>@$6ZY+&Jx^CekyME1ORb*}O8B<^f$WQT}umyJ+P^oJvx zNJ6m}ELtNAXZvbiJ&J+$8Izjzn)w=Mp3h4<(;g%wgcY`cv7tJW?S~(Kazf`ZXGr_0 z6tko=2`+ZvhaWCTM`JIZFo`4ymD@%*N$+z?!jN2`Q`ni8$YBlU|b>YkPmT!H}(kxuEVZztczEN-N4^+7 zI1UxIhI}dFhAM!VjopQ%+$c-3_U7FZ43Msh8-B+H29fs`c2-ZIOfNp$b>=*LKwzGi5@giPw z$Bq?eZ4-}uW9BuBgWl`Bzz#y{=o~Wl;Qclf_Hb|f0i-i$vLk!!GsF$^mRKJ7u>cV) zKjImg_r#toeg%6@a}`|-Mr1PQDA2(Mp+neah?RE6h6j6YFOVbpw$a26#B1mcdP}+FUhrY9j!MW(}=OCYE;r$1bkFrP$;Ykj{ViWvP zFgy6Du|Lf54I8(_n1|>DFE?f(-y1j6XG_L$wz{8j;XA||=nT5*m5G&$b7pnuF$xBJ z2fha0%Ud*nzT5d`y@l(iwqCW~4i9PW>@ILH#;lu>%M_K&*Ru_~glnC4>`htJp`K@0< z4)`jL&mc};Z*&V?CkDoL+Og+8yu3A342oVsSbKM2>GzFocy-PKT?h_DIukGP0$Y8t zW01u}H_(4#65ho?TiU*vKg8wexf9dfQ2sKM4GBp)D>~PdZpDjA?RLOMqg z&;K>xZ}MQ>+zM}dZlHjiYsZ;}u_m2I#GZ79>32xydmz}FHgjIA zNN18R`l(gaxr-@51L+L0g_1(FvRX1`{L9e+uD6|Fa7gDtQKa+zeZ~N@5=ezhoJi*n z0+G(nOBx)~xkyN(;Rp07K}cuwEOi>Rlo>`2oP)`C2&72d*?z91zxf-a3PH6W`FLTC zVyIv#K1kV^;sz zmUs`<@0}NGKrn?|BO#p=>P%I?`T=P8%ptMa?lPY*%$xDd1$L&Gb(?1D=PugFex3NEME-VWEd$HWGaa z1Un}!>5RUH((kw+zDZ~L4zbL(K*)VwGUS}s_?RZ?_=RxWdBeflpg$YwjE+(9j}60a z^)dGuF?xa$wa5!{fWU_!_m!D*)#iEI2nU{p#YU&{;g%As=s#z$@jJ)Lu(KpKI%-L0 z2-$=row0Eoxp3atE)pLID-s#b1cv0nXK=0@M`?vh@`0OlwiOsu{v=P6Q+LP^%pw>3 zPsc93w9g#Xg*|b`B6g%Rf1~d_7qJ}+oY-29q+*LEHrq=Y1Oqx~NoQW%;*EU*ff&v= z={!0n=?r*&7F~&ts1^+5)9kr=wBv1C+m8p*nS?(a>5Lvw(Q`K1jydm>C|{+no|{PZ zEiZ1~vv^9-32TMp1Yog~=+40^OTTzYeW;cW%(iw0) zB&YjaNA!+eBUCGT^(I_VgcqSm=e6rMnV8~7eW~h}LX0GeetDm0B zOy60IVF8rSLg^@Dg-@d3hgg+4W7}t5j~qKueGc-09AFWHamOZwBb~{K@a@E&y3;Md zafS2^zrz{2%s-BE@oO7+=BJJP$l)MQyluO|v*2^^E${4kH!R@^@H@mAPNl80ZQeKO ze8YLk9dpfZ{1{C-Z{DVM^nTKLlM>IB5}0)MQ(=D2unJjzn^r5Gz&#K<2RQqdn+L6Y-4D~ z7b2J(+3J*%&ZQmFnVq|S7H0uApWKeQ8i*7|r-=7L#U#Oi&awN=dFg84F(AI1IB8ng z9Tslztfk7=lCOnoyXQaTAFUpa(ViDn6xbI^{QHjA_*Bmc;-2w&yaw%4 zi3+jA`9`c#ka)4$N^ry0AS!E^c(G|8qj-Nmogg;)cnuR24IMFFlRm%g)0g{?*Z9OZ zR@dR84L~% zHINMK@fvI)iS&34&dR3$^mB+AL(XxwMh7B*g_qkVxL`FC$4f;7^5X>?p-65VGK3V{ zG9mCDOp-yjagj)5&QVzB0F^VJefGIJcCNW{<*GVjdoHK9&0xUrlDq{<&~?0TQW?BV z_0{{`KnPUn452f<;%?^K5%fApE3%)@Y`vn6eL0SDVo?0I1p?q_%F4qF`wa0&KE z&VsdXLGZ;J-!bmUhJ~*hb(?C%Iep}B*a-qbKMur#ad#iDL7+o`T&Q?OO(3c2guLZi zf@UwdLhusF@fsG$g=C59RE~jJxq7{leW3!1NPw)l$HbRj(U;bBf4X1T05($4p7Yk= zZ}fPLO!08M2G5UwdH>jlN*r2J5WTHbqoIzm9)}aacK88u!R{o)@fs|aCM4<1Tqa?+ zFW+F>_2_Yvl&EDPl@}qhAj@;GXc~;j3kQ3~8@AKhpJaZrU$#)@UksV|KWBSiW(&@ZrJi;BX;ckZ=ZTM^Cj+}KH@#3ZW5>LhxoxwI! zlt95uxPDu>NoViz8XTz--^XiMAa9cLF5Ua6?s^vmbW!8drTfk}_9D>n8WxNTM`J)b zA!E0ktse__yoS9tRmQx!qYZK#xda+B{>BbhUS>tC&KaWQe9VtP*SmoXc?lnjC|-Fw zd2cc2g^Lt2W#^c#?7oiI0E{m$RU}TMa*kuB@U1*E^20`vV=x93NTE-D7cJ;33yJ6g zj16+Y77;VE`0OXnRhi~4u0<3an-_h&Mz=7>Yn;EJM*x&FcGb@@5^mU- z2f6(FkJpIoW!a1$6~a6-@`r4r-`?xopnd#Jed9CGIpPmLL^s4M1QmY8e#^F1@Y-Bzg1dB+Guzew<4kQWJFY+FrQ7bzlD^A?nI zZK}#|0Z8Xqzeq3;_am6Khv`YQSeZtU8JCW zDlQ2sIa?BfNnd1V93d}VVDiPL-+>VGMS^2g$s=t)^os;@=>g+fd3-CrUnICc-d`kW z!KCq$&faSx0R{qtNxw;V(g2*a1FA}U$qmd>pBw@4XFER$=ss?G?lS&0D zz%d%UU=Sz3*0VGjgP(m$qEBE!r9{7h!=oyuoNICLjN?0I4(K-SGS#Bax4f!JHi29y zhaLz78!y$@_~r3FMp$=0>|$s z5O;%d_r6FF8MEj`^23oST*r8N31{?(MXK1mND$aS=!s4}qF$I3yLlQ6w9nihH_>^K zAeA_N=93?wf6ke0&Wi*yn7}(&wM1+X>_v;b?8r~`Tp&31GvQt&cqV~KXLOsbis&8z zTrkoZ+exzSx4I+{7)wYu-jeupobj!dn9uhdEQ4lC(WonkIGB} zM95{fyV)v9=lX(LA;|fAzDVaGqts4O(aSjRKX^p#&&7+E^n7(Wjj(iz`cw_!^qQ6Zb`-}xB{=?u6g;a((|MQvK3 z#Lf$d0&ElUCvzdxf`S{)iv-u{Gh-j!iwnq`^SSZy5YG^~klTJ9{3O;_1k zU8S$sV&YQBEEbrF3$YuPObsW0FX?RchrhEhP977d7YS;7buZa<&mVS3EqHyIE}D8j zkL`;D-JdfS5D#9T%d`1?ksv^Z00PFDLoww_8^qUC8gdK`@hWq}dtERfD`(eC%zL|Y zWAvFtJ6?_!?nQ!tenC_(S-w(pXk<3)nRv&@OD z+jc3@;&*;|Vdb14FA^LV>_vjkjx}}{`?b+LA1^L-h_37UuA5MPkI%?yIzRc0E@oCY zb0=J-vm1;j$GzBUQ7_2F+ID=(w{7-<*okMSikaPM>}+DUt-eU`d>mgS*x>#b2}a-3 z=qH5)_*!f@wgO+o0tPs(Zs$M(k1vw`|{~{)wX>IVOk`2=HahVHO2AvV|QH;W#Hg zXCAs`>z46z1BPj_0`C&I_T&8vw>%3wH^a$D{fBu5?5}&l0zJYOA9??S`-(33`^C$b zq+ye`0rfQz|Hsi7Jxdl7>3Bcse7AjdX;!GPBr~5UH#e@ByP#Zb?W&0GR6~w6sVJE; z=am!zI_n0+^FLZX67*$)F%Wg+MzZ{7P}BK0YHskrY+<*gb6k>8FBohl+K0zK(U-H4 zjQKTWen9)Ih7i=SqKP4(Is=0mPD%WNc9@u>OyZ8Q=rK>Is)@6$j61J5eBU9G}-;&jqvzL&7Z2HcZ? zmDSPsAf0Isa*Zk&&Iu)Xhp44pbd}>BsE)yCzPW0hCTih=S%6$>)o-p7ALmMdsOQm{ zajaISi5h6eg%v%V4-t_RHI*1$|@t9V`Fz)jA3O&N)uJK#=MN2-Y6Goz+=h zasz1-s@*`qc~LbCnW5)E>A6r‚$IRo;hjy*3Lq5>hDt_C8VAwwWhIXlmS-&rKF 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 O0000 Date: Thu, 11 Jan 2024 15:57:17 +0000 Subject: [PATCH 105/144] - --- docs/chakra.md | 21 ++++++++++++++++++++- docs/images/chakra/table.png | Bin 0 -> 44654 bytes 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 docs/images/chakra/table.png diff --git a/docs/chakra.md b/docs/chakra.md index 731f83fa..53269c0f 100644 --- a/docs/chakra.md +++ b/docs/chakra.md @@ -228,4 +228,23 @@ WarningIcon(), WarningTwoIcon() ``` -![Icons](images/chakra/icons.png) \ No newline at end of file +![Icons](images/chakra/icons.png) + +### Tables + +There are a lot of elements to create a table but QuickTable also helps simplify things. First lets see the code +for a table [here](../examples/src/main/scala/tests/chakra/DataDisplay.scala) + +![Table](images/chakra/table.png) + +Quicktable helps creating all those table elements for the most common usecases: + +```scala +val sortedFilesTable = QuickTable().headers("id", "name", "path") + .caption("Files sorted") +val tableRows:Seq[Seq[String]] = Seq( + Seq("file1","File1.scala","/tmp/scala"), + ... +) +sortedFilesTable.rows(tableRows) +``` \ No newline at end of file diff --git a/docs/images/chakra/table.png b/docs/images/chakra/table.png new file mode 100644 index 0000000000000000000000000000000000000000..63d1bcb8841f224878ad655a46fc1e4a8b644964 GIT binary patch literal 44654 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 literal 0 HcmV?d00001 From eb42191d604a16dfc4aed18b04ab3dab33f1c941 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 11 Jan 2024 15:59:51 +0000 Subject: [PATCH 106/144] - --- docs/chakra.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/chakra.md b/docs/chakra.md index 53269c0f..3cbd6a9c 100644 --- a/docs/chakra.md +++ b/docs/chakra.md @@ -232,19 +232,19 @@ WarningTwoIcon() ### Tables -There are a lot of elements to create a table but QuickTable also helps simplify things. First lets see the code +There are a lot of elements to create a table but `QuickTable` component also helps simplify things. First lets see the code for a table [here](../examples/src/main/scala/tests/chakra/DataDisplay.scala) ![Table](images/chakra/table.png) -Quicktable helps creating all those table elements for the most common usecases: +`Quicktable` helps creating all those table elements for the most common usecases: ```scala -val sortedFilesTable = QuickTable().headers("id", "name", "path") - .caption("Files sorted") +val conversionTable = QuickTable().headers("To convert", "into", "multiply by") + .caption("Imperial to metric conversion factors") val tableRows:Seq[Seq[String]] = Seq( - Seq("file1","File1.scala","/tmp/scala"), + Seq("inches","millimetres (mm)","25.4"), ... ) -sortedFilesTable.rows(tableRows) +conversionTable.rows(tableRows) ``` \ No newline at end of file From badc6986f61e975f21ae4a8645c7fc726d18be08 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 11 Jan 2024 16:01:46 +0000 Subject: [PATCH 107/144] - --- docs/chakra.md | 10 +++++++++- docs/images/chakra/text.png | Bin 0 -> 3867 bytes 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 docs/images/chakra/text.png diff --git a/docs/chakra.md b/docs/chakra.md index 3cbd6a9c..d40b1989 100644 --- a/docs/chakra.md +++ b/docs/chakra.md @@ -247,4 +247,12 @@ val tableRows:Seq[Seq[String]] = Seq( ... ) conversionTable.rows(tableRows) -``` \ No newline at end of file +``` + +### Text + +```scala +Text(text = "typography-text-0001", color = Some("tomato")) +``` + +![Text](images/chakra/text.png) \ No newline at end of file diff --git a/docs/images/chakra/text.png b/docs/images/chakra/text.png new file mode 100644 index 0000000000000000000000000000000000000000..470e3058c5075b93e88323b774dc1dc5f863be8b GIT binary patch literal 3867 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 dkV!eL Date: Thu, 11 Jan 2024 16:02:23 +0000 Subject: [PATCH 108/144] - --- docs/chakra.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/chakra.md b/docs/chakra.md index d40b1989..5ac72b38 100644 --- a/docs/chakra.md +++ b/docs/chakra.md @@ -4,6 +4,15 @@ Note: only a fraction of the available properties of each component is documented here. See the case class for a full list as well as the chakra react documentation (links are available in the scaladocs of each case class). + +### Text + +```scala +Text(text = "typography-text-0001", color = Some("tomato")) +``` + +![Text](images/chakra/text.png) + ### Button ![Button](images/chakra/button.png) @@ -248,11 +257,3 @@ val tableRows:Seq[Seq[String]] = Seq( ) conversionTable.rows(tableRows) ``` - -### Text - -```scala -Text(text = "typography-text-0001", color = Some("tomato")) -``` - -![Text](images/chakra/text.png) \ No newline at end of file From 6d3f049b3d00fe2419403255ae449fc3326bf9aa Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 11 Jan 2024 16:07:41 +0000 Subject: [PATCH 109/144] - --- docs/chakra.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/chakra.md b/docs/chakra.md index 5ac72b38..2c65a068 100644 --- a/docs/chakra.md +++ b/docs/chakra.md @@ -1,5 +1,9 @@ # Chakra components +Chakra components build on top of standard html elements to provide nice UI for text, forms, tables etc. +See [Chakra Components for React](https://chakra-ui.com/docs/components). + +These case classes can be used to render chakra components (note: not all chakra elements are available). [The case classes](../terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala) Note: only a fraction of the available properties of each component is documented here. From 39d086d8652b46f3c964b2e7aa3d685e2642d598 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 11 Jan 2024 16:09:10 +0000 Subject: [PATCH 110/144] - --- docs/chakra.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/chakra.md b/docs/chakra.md index 2c65a068..304905a5 100644 --- a/docs/chakra.md +++ b/docs/chakra.md @@ -3,10 +3,10 @@ Chakra components build on top of standard html elements to provide nice UI for text, forms, tables etc. See [Chakra Components for React](https://chakra-ui.com/docs/components). -These case classes can be used to render chakra components (note: not all chakra elements are available). +These case classes can be used to render chakra components. [The case classes](../terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala) -Note: only a fraction of the available properties of each component is documented here. +Note: not all chakra elements are available and also only a fraction of the available properties of each component is documented here. See the case class for a full list as well as the chakra react documentation (links are available in the scaladocs of each case class). ### Text From 318d55a0ac1d31c4d59cd44cd8b29e3badc24c1b Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 11 Jan 2024 16:11:51 +0000 Subject: [PATCH 111/144] - --- docs/chakra.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/chakra.md b/docs/chakra.md index 304905a5..214aa1f2 100644 --- a/docs/chakra.md +++ b/docs/chakra.md @@ -9,6 +9,7 @@ These case classes can be used to render chakra components. Note: not all chakra elements are available and also only a fraction of the available properties of each component is documented here. See the case class for a full list as well as the chakra react documentation (links are available in the scaladocs of each case class). +If you want a chakra component that is not supported yet, please add a comment here: [Chakra support discussion](https://github.com/kostaskougios/terminal21-restapi/discussions/2) ### Text ```scala From 96892a44bba054b9ed48bf76e68d978909b93783 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 11 Jan 2024 16:22:42 +0000 Subject: [PATCH 112/144] - --- Readme.md | 5 +++++ docs/chakra.md | 3 +++ docs/images/nivo/responsivebar.png | Bin 0 -> 82122 bytes docs/images/nivo/responsiveline.png | Bin 0 -> 70769 bytes docs/nivo.md | 18 ++++++++++++++++++ 5 files changed, 26 insertions(+) create mode 100644 docs/images/nivo/responsivebar.png create mode 100644 docs/images/nivo/responsiveline.png create mode 100644 docs/nivo.md diff --git a/Readme.md b/Readme.md index 1a814b5e..2a2347a4 100644 --- a/Readme.md +++ b/Readme.md @@ -107,8 +107,13 @@ can be used for things like: - ... and so on # Available UI Components +Generic components for buttons, menus, forms, text, grids, tables: [Chakra](docs/chakra.md) +Charts and visualisation: +[Nivo](docs/nivo.md) + + # Architecture Terminal21 consist of : diff --git a/docs/chakra.md b/docs/chakra.md index 214aa1f2..e6dd1d5b 100644 --- a/docs/chakra.md +++ b/docs/chakra.md @@ -10,6 +10,9 @@ Note: not all chakra elements are available and also only a fraction of the avai See the case class for a full list as well as the chakra react documentation (links are available in the scaladocs of each case class). If you want a chakra component that is not supported yet, please add a comment here: [Chakra support discussion](https://github.com/kostaskougios/terminal21-restapi/discussions/2) + +[Examples](https://github.com/kostaskougios/terminal21-restapi/tree/main/example-scripts) + ### Text ```scala diff --git a/docs/images/nivo/responsivebar.png b/docs/images/nivo/responsivebar.png new file mode 100644 index 0000000000000000000000000000000000000000..971feaf18b1c254755c6c16ad48e299190e0a547 GIT binary patch literal 82122 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 literal 0 HcmV?d00001 diff --git a/docs/images/nivo/responsiveline.png b/docs/images/nivo/responsiveline.png new file mode 100644 index 0000000000000000000000000000000000000000..594107a43665d424477d302cbc19ded2ac05332d GIT binary patch literal 70769 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 Date: Thu, 11 Jan 2024 16:23:44 +0000 Subject: [PATCH 113/144] - --- docs/nivo.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/nivo.md b/docs/nivo.md index 643c0ea4..f662a4a8 100644 --- a/docs/nivo.md +++ b/docs/nivo.md @@ -2,7 +2,8 @@ [Nivo](https://nivo.rocks/), "nivo provides a rich set of dataviz components, built on top of D3 and React". -terminal21 supports a few of these, but please add a comment [here](https://github.com/kostaskougios/terminal21-restapi/discussions/3). +terminal21 supports a few of these, but please add a comment [here](https://github.com/kostaskougios/terminal21-restapi/discussions/3) if you +would like support for a particular component. ### ResponsiveLine From 9cb6602ddab4776acbd657755933cba664a447e8 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 11 Jan 2024 16:25:05 +0000 Subject: [PATCH 114/144] - --- docs/nivo.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/nivo.md b/docs/nivo.md index f662a4a8..f5051484 100644 --- a/docs/nivo.md +++ b/docs/nivo.md @@ -7,13 +7,13 @@ would like support for a particular component. ### ResponsiveLine -code [ResponsiveLine](../examples/src/main/scala/tests/nivo/ResponsiveLineChart.scala) +Code: [ResponsiveLine](../examples/src/main/scala/tests/nivo/ResponsiveLineChart.scala) ![RL](images/nivo/responsiveline.png) ### ResponsiveBar -code [ResponsiveBar](../examples/src/main/scala/tests/nivo/ResponsiveBarChart.scala) +Code: [ResponsiveBar](../examples/src/main/scala/tests/nivo/ResponsiveBarChart.scala) ![RB](images/nivo/responsivebar.png) From cb48b54f22f58eee96faf6559ae386433cc0e7c0 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 11 Jan 2024 16:32:12 +0000 Subject: [PATCH 115/144] - --- Readme.md | 5 +++++ docs/std.md | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 docs/std.md diff --git a/Readme.md b/Readme.md index 2a2347a4..547a2f46 100644 --- a/Readme.md +++ b/Readme.md @@ -107,6 +107,11 @@ can be used for things like: - ... and so on # Available UI Components +Std + +Some standard html elements +[Std](docs/std.md) + Generic components for buttons, menus, forms, text, grids, tables: [Chakra](docs/chakra.md) diff --git a/docs/std.md b/docs/std.md new file mode 100644 index 00000000..d21f64df --- /dev/null +++ b/docs/std.md @@ -0,0 +1,33 @@ +# Std + +These are standard html elements but please prefer the more flexible chakra component if it exists. + +[Example](../examples/src/main/scala/tests/StdComponents.scala) + +### Paragraph, NewLine, Span, Em + +```scala +Paragraph(text = "Hello World!").withChildren( + NewLine(), + Span(text = "Some more text"), + Em(text = " emphasized!"), + NewLine(), + Span(text = "And the last line") +) +``` +### Header + +```scala +Header1(text = "Welcome to the std components demo/test") +``` + +### Input + +```scala + val input = Input(defaultValue = "Please enter your name") + val output = Paragraph(text = "This will reflect what you type in the input") + input.onChange: newValue => + output.text = newValue + session.render() + +``` \ No newline at end of file From de2f83303283de35d00a35da84166bfa18adc2c1 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 11 Jan 2024 16:35:34 +0000 Subject: [PATCH 116/144] - --- docs/chakra.md | 2 ++ docs/nivo.md | 2 ++ docs/std.md | 2 ++ example-scripts/project.scala | 1 + 4 files changed, 7 insertions(+) diff --git a/docs/chakra.md b/docs/chakra.md index e6dd1d5b..7a4bf1a4 100644 --- a/docs/chakra.md +++ b/docs/chakra.md @@ -13,6 +13,8 @@ If you want a chakra component that is not supported yet, please add a comment h [Examples](https://github.com/kostaskougios/terminal21-restapi/tree/main/example-scripts) +Dependency: `io.github.kostaskougios::terminal21-ui-std:$VERSION` + ### Text ```scala diff --git a/docs/nivo.md b/docs/nivo.md index f5051484..f6b05adb 100644 --- a/docs/nivo.md +++ b/docs/nivo.md @@ -5,6 +5,8 @@ terminal21 supports a few of these, but please add a comment [here](https://github.com/kostaskougios/terminal21-restapi/discussions/3) if you would like support for a particular component. +Dependency: `io.github.kostaskougios::terminal21-nivo:$VERSION` + ### ResponsiveLine Code: [ResponsiveLine](../examples/src/main/scala/tests/nivo/ResponsiveLineChart.scala) diff --git a/docs/std.md b/docs/std.md index d21f64df..2080d6c7 100644 --- a/docs/std.md +++ b/docs/std.md @@ -4,6 +4,8 @@ These are standard html elements but please prefer the more flexible chakra comp [Example](../examples/src/main/scala/tests/StdComponents.scala) +Dependency: `io.github.kostaskougios::terminal21-ui-std:$VERSION` + ### Paragraph, NewLine, Span, Em ```scala diff --git a/example-scripts/project.scala b/example-scripts/project.scala index b8b2b135..a2226d02 100644 --- a/example-scripts/project.scala +++ b/example-scripts/project.scala @@ -2,4 +2,5 @@ //> using scala 3 //> using dep io.github.kostaskougios::terminal21-ui-std:0.11 +//> using dep io.github.kostaskougios::nivo:0.11 //> using dep commons-io:commons-io:2.15.1 From 55f832a3a45cca4c99b0e7f43b90c8015827242f Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 11 Jan 2024 16:38:22 +0000 Subject: [PATCH 117/144] - --- Readme.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Readme.md b/Readme.md index 547a2f46..42ab03e9 100644 --- a/Readme.md +++ b/Readme.md @@ -107,9 +107,7 @@ can be used for things like: - ... and so on # Available UI Components -Std - -Some standard html elements +Standard html elements [Std](docs/std.md) Generic components for buttons, menus, forms, text, grids, tables: From 5df85039c3b4ab62ddb8d353d06e58f10c77cee0 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 11 Jan 2024 17:11:22 +0000 Subject: [PATCH 118/144] - --- example-scripts/project.scala | 2 +- .../org/terminal21/client/components/CachedCalculation.scala | 4 ++++ .../scala/org/terminal21/client/components/Calculation.scala | 2 ++ .../org/terminal21/client/components/StdUiCalculation.scala | 2 +- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/example-scripts/project.scala b/example-scripts/project.scala index a2226d02..5e17c8a6 100644 --- a/example-scripts/project.scala +++ b/example-scripts/project.scala @@ -2,5 +2,5 @@ //> using scala 3 //> using dep io.github.kostaskougios::terminal21-ui-std:0.11 -//> using dep io.github.kostaskougios::nivo:0.11 +//> using dep io.github.kostaskougios::terminal21-nivo:0.11 //> using dep commons-io:commons-io:2.15.1 diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/CachedCalculation.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/CachedCalculation.scala index f06a232e..cc98214f 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/CachedCalculation.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/CachedCalculation.scala @@ -9,3 +9,7 @@ abstract class CachedCalculation[OUT](using executor: FiberExecutor) extends Cal def reCalculate(): Fiber[OUT] = invalidateCache() run() + + override protected def reRunRequested() = + invalidateCache() + super.reRunRequested() diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/Calculation.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/Calculation.scala index 47f23619..7387b30e 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/Calculation.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/Calculation.scala @@ -9,6 +9,8 @@ trait Calculation[OUT](using executor: FiberExecutor): protected def whenResultsNotReady(): Unit = () protected def whenResultsReady(results: OUT): Unit = () + protected def reRunRequested(): Fiber[OUT] = run() + def run(): Fiber[OUT] = val refreshInOrder = new CountDownLatch(1) executor.submit: diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala index 017767d8..a5c4a9de 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala @@ -23,7 +23,7 @@ trait StdUiCalculation[OUT]( val recalc = Button(text = "Recalculate", size = Some("sm"), leftIcon = Some(RepeatIcon())).onClick: () => if running.compareAndSet(false, true) then try - run() + reRunRequested() finally running.set(false) val header = Box(bg = "green", p = 4).withChildren( From 7ad7778aa2524a1c203f4c26043f41abc1d6d796 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 11 Jan 2024 17:13:19 +0000 Subject: [PATCH 119/144] - --- .../terminal21/client/components/CachedCalculation.scala | 8 ++------ .../org/terminal21/client/components/Calculation.scala | 2 +- .../terminal21/client/components/StdUiCalculation.scala | 2 +- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/CachedCalculation.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/CachedCalculation.scala index cc98214f..98a95d8d 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/CachedCalculation.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/CachedCalculation.scala @@ -6,10 +6,6 @@ abstract class CachedCalculation[OUT](using executor: FiberExecutor) extends Cal def isCached: Boolean def invalidateCache(): Unit def nonCachedCalculation: OUT - def reCalculate(): Fiber[OUT] = + override def reCalculate(): Fiber[OUT] = invalidateCache() - run() - - override protected def reRunRequested() = - invalidateCache() - super.reRunRequested() + super.reCalculate() diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/Calculation.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/Calculation.scala index 7387b30e..be3c8d06 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/Calculation.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/Calculation.scala @@ -9,7 +9,7 @@ trait Calculation[OUT](using executor: FiberExecutor): protected def whenResultsNotReady(): Unit = () protected def whenResultsReady(results: OUT): Unit = () - protected def reRunRequested(): Fiber[OUT] = run() + def reCalculate(): Fiber[OUT] = run() def run(): Fiber[OUT] = val refreshInOrder = new CountDownLatch(1) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala index a5c4a9de..8e57cd8a 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala @@ -23,7 +23,7 @@ trait StdUiCalculation[OUT]( val recalc = Button(text = "Recalculate", size = Some("sm"), leftIcon = Some(RepeatIcon())).onClick: () => if running.compareAndSet(false, true) then try - reRunRequested() + reCalculate() finally running.set(false) val header = Box(bg = "green", p = 4).withChildren( From c2d9a01ea6f4e9be86f85a93a018349ac3bacc45 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 11 Jan 2024 18:52:22 +0000 Subject: [PATCH 120/144] - --- example-scripts/nivo-line-chart.sc | 56 +++++++++++++++++++ example-scripts/postit.sc | 2 +- example-scripts/server.sc | 2 +- .../scala/org/terminal21/client/Globals.scala | 2 +- 4 files changed, 59 insertions(+), 3 deletions(-) create mode 100755 example-scripts/nivo-line-chart.sc diff --git a/example-scripts/nivo-line-chart.sc b/example-scripts/nivo-line-chart.sc new file mode 100755 index 00000000..664f5698 --- /dev/null +++ b/example-scripts/nivo-line-chart.sc @@ -0,0 +1,56 @@ +#!/usr/bin/env -S scala-cli project.scala + +import org.terminal21.client.* +import org.terminal21.client.fiberExecutor +import org.terminal21.client.components.* +import org.terminal21.client.components.nivo.* + +import scala.util.Random +import NivoLineChart.* + +Sessions.withNewSession("nivo-line-chart", "Nivo Line Chart", NivoLib /* note we need to register the NivoLib in order to use it */ ): session => + given ConnectedSession = session + + val chart = ResponsiveLine( + data = createRandomData, + yScale = Scale(stacked = Some(true)), + axisBottom = Some(Axis(legend = "transportation", legendOffset = 36)), + axisLeft = Some(Axis(legend = "count", legendOffset = -40)), + legends = Seq(Legend()) + ) + + Seq( + Paragraph(text = "Means of transportation for various countries", style = Map("margin" -> 20)), + chart + ).render() + + fiberExecutor.submit: + while !session.isClosed do + Thread.sleep(2000) + chart.data = createRandomData + session.render() + + session.waitTillUserClosesSession() + +object NivoLineChart: + def createRandomData: Seq[Serie] = + Seq( + dataFor("Japan"), + dataFor("France"), + dataFor("Greece"), + dataFor("UK"), + dataFor("Germany") + ) + def dataFor(country: String): Serie = + Serie( + country, + data = Seq( + Datum("plane", rnd), // rnd = random int, see below + Datum("helicopter", rnd), + Datum("boat", rnd), + Datum("car", rnd), + Datum("submarine", rnd) + ) + ) + + def rnd = Random.nextInt(500) + 50 diff --git a/example-scripts/postit.sc b/example-scripts/postit.sc index cf5dceba..cca81ef6 100755 --- a/example-scripts/postit.sc +++ b/example-scripts/postit.sc @@ -25,7 +25,7 @@ Sessions.withNewSession("postit", "Post-It"): session => src = "https://upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Apple_Notes_icon.svg/2048px-Apple_Notes_icon.svg.png", boxSize = Some("32px") ), - Box(text = editor.value) + Box(text = editor.value) ) ) // always render after adding/modifying something diff --git a/example-scripts/server.sc b/example-scripts/server.sc index 23a0b561..b93acb6e 100755 --- a/example-scripts/server.sc +++ b/example-scripts/server.sc @@ -6,4 +6,4 @@ import org.terminal21.server.Terminal21Server -Terminal21Server.start() \ No newline at end of file +Terminal21Server.start() diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/Globals.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/Globals.scala index 1adb69b7..74a3af9b 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/Globals.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/Globals.scala @@ -2,4 +2,4 @@ package org.terminal21.client import functions.fibers.FiberExecutor -given FiberExecutor = FiberExecutor() +given fiberExecutor: FiberExecutor = FiberExecutor() From d67c6758af597a6c0474dd6d668563049e55c71b Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Thu, 11 Jan 2024 22:52:55 +0000 Subject: [PATCH 121/144] - --- Readme.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Readme.md b/Readme.md index 42ab03e9..a690aca6 100644 --- a/Readme.md +++ b/Readme.md @@ -164,3 +164,16 @@ Please use the [discussions](https://github.com/kostaskougios/terminal21-restapi ## Version 0.1 - initial release with std and chakra components + + +# Our thanks + +![yourkit](https://www.yourkit.com/images/yklogo.png) + +To yourkit for their excellent profiler. + +YourKit supports open source projects with innovative and intelligent tools +for monitoring and profiling Java and .NET applications. +YourKit is the creator of [YourKit Java Profiler](https://www.yourkit.com/java/profiler/), +[YourKit .NET Profiler](https://www.yourkit.com/dotnet-profiler/), +and [YourKit YouMonitor](https://www.yourkit.com/youmonitor/). \ No newline at end of file From 308038af60542682f140aebed15254e17d2c3c34 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 12 Jan 2024 14:44:12 +0000 Subject: [PATCH 122/144] - --- build.sbt | 13 +++++++++++-- docs/chakra.md | 4 ++-- docs/nivo.md | 4 ++-- docs/std.md | 2 +- .../src/main/scala/tests/ChakraComponents.scala | 0 .../src/main/scala/tests/MathJaxComponents.scala | 11 +++++++++++ .../src/main/scala/tests/NivoComponents.scala | 0 .../src/main/scala/tests/StdComponents.scala | 0 .../src/main/scala/tests/chakra/Buttons.scala | 0 .../src/main/scala/tests/chakra/Common.scala | 0 .../src/main/scala/tests/chakra/DataDisplay.scala | 0 .../src/main/scala/tests/chakra/Editables.scala | 0 .../src/main/scala/tests/chakra/Etc.scala | 0 .../src/main/scala/tests/chakra/Forms.scala | 0 .../src/main/scala/tests/chakra/Grids.scala | 0 .../src/main/scala/tests/chakra/MediaAndIcons.scala | 0 .../src/main/scala/tests/chakra/Overlay.scala | 0 .../src/main/scala/tests/chakra/Stacks.scala | 0 .../src/main/scala/tests/chakra/Typography.scala | 0 .../main/scala/tests/nivo/ResponsiveBarChart.scala | 0 .../main/scala/tests/nivo/ResponsiveLineChart.scala | 0 .../src/main/scala/tests/nivo/common.scala | 0 .../client/components/mathjax/MathJax.scala | 7 +++++++ .../client/components/mathjax/MathJaxLib.scala | 10 ++++++++++ 24 files changed, 44 insertions(+), 7 deletions(-) rename {examples => end-to-end-tests}/src/main/scala/tests/ChakraComponents.scala (100%) create mode 100644 end-to-end-tests/src/main/scala/tests/MathJaxComponents.scala rename {examples => end-to-end-tests}/src/main/scala/tests/NivoComponents.scala (100%) rename {examples => end-to-end-tests}/src/main/scala/tests/StdComponents.scala (100%) rename {examples => end-to-end-tests}/src/main/scala/tests/chakra/Buttons.scala (100%) rename {examples => end-to-end-tests}/src/main/scala/tests/chakra/Common.scala (100%) rename {examples => end-to-end-tests}/src/main/scala/tests/chakra/DataDisplay.scala (100%) rename {examples => end-to-end-tests}/src/main/scala/tests/chakra/Editables.scala (100%) rename {examples => end-to-end-tests}/src/main/scala/tests/chakra/Etc.scala (100%) rename {examples => end-to-end-tests}/src/main/scala/tests/chakra/Forms.scala (100%) rename {examples => end-to-end-tests}/src/main/scala/tests/chakra/Grids.scala (100%) rename {examples => end-to-end-tests}/src/main/scala/tests/chakra/MediaAndIcons.scala (100%) rename {examples => end-to-end-tests}/src/main/scala/tests/chakra/Overlay.scala (100%) rename {examples => end-to-end-tests}/src/main/scala/tests/chakra/Stacks.scala (100%) rename {examples => end-to-end-tests}/src/main/scala/tests/chakra/Typography.scala (100%) rename {examples => end-to-end-tests}/src/main/scala/tests/nivo/ResponsiveBarChart.scala (100%) rename {examples => end-to-end-tests}/src/main/scala/tests/nivo/ResponsiveLineChart.scala (100%) rename {examples => end-to-end-tests}/src/main/scala/tests/nivo/common.scala (100%) create mode 100644 terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJax.scala create mode 100644 terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJaxLib.scala diff --git a/build.sbt b/build.sbt index e3a8a61b..f9bf9122 100644 --- a/build.sbt +++ b/build.sbt @@ -139,12 +139,12 @@ lazy val `terminal21-ui-std` = project ) .enablePlugins(FunctionsRemotePlugin) -lazy val examples = project +lazy val `end-to-end-tests` = project .settings( commonSettings, libraryDependencies ++= Seq(ScalaTest, LogBack) ) - .dependsOn(`terminal21-ui-std`, `terminal21-nivo`) + .dependsOn(`terminal21-ui-std`, `terminal21-nivo`, `terminal21-mathjax`) lazy val `terminal21-nivo` = project .settings( @@ -167,3 +167,12 @@ lazy val `terminal21-spark` = project ) ++ SparkScala3Fix ) .dependsOn(`terminal21-ui-std` % "compile->compile;test->test", `terminal21-nivo` % Test) + +lazy val `terminal21-mathjax` = project + .settings( + commonSettings, + libraryDependencies ++= Seq( + ScalaTest + ) + ) + .dependsOn(`terminal21-ui-std` % "compile->compile;test->test") diff --git a/docs/chakra.md b/docs/chakra.md index 7a4bf1a4..db5f3d92 100644 --- a/docs/chakra.md +++ b/docs/chakra.md @@ -75,7 +75,7 @@ Menu().withChildren( ### Forms -see [Forms](../examples/src/main/scala/tests/chakra/Forms.scala) as an example on how to create forms. +see [Forms](../end-to-end-tests/src/main/scala/tests/chakra/Forms.scala) as an example on how to create forms. ![Forms](images/chakra/forms.png) @@ -252,7 +252,7 @@ WarningTwoIcon() ### Tables There are a lot of elements to create a table but `QuickTable` component also helps simplify things. First lets see the code -for a table [here](../examples/src/main/scala/tests/chakra/DataDisplay.scala) +for a table [here](../end-to-end-tests/src/main/scala/tests/chakra/DataDisplay.scala) ![Table](images/chakra/table.png) diff --git a/docs/nivo.md b/docs/nivo.md index f6b05adb..b20ff6ec 100644 --- a/docs/nivo.md +++ b/docs/nivo.md @@ -9,13 +9,13 @@ Dependency: `io.github.kostaskougios::terminal21-nivo:$VERSION` ### ResponsiveLine -Code: [ResponsiveLine](../examples/src/main/scala/tests/nivo/ResponsiveLineChart.scala) +Code: [ResponsiveLine](../end-to-end-tests/src/main/scala/tests/nivo/ResponsiveLineChart.scala) ![RL](images/nivo/responsiveline.png) ### ResponsiveBar -Code: [ResponsiveBar](../examples/src/main/scala/tests/nivo/ResponsiveBarChart.scala) +Code: [ResponsiveBar](../end-to-end-tests/src/main/scala/tests/nivo/ResponsiveBarChart.scala) ![RB](images/nivo/responsivebar.png) diff --git a/docs/std.md b/docs/std.md index 2080d6c7..60577914 100644 --- a/docs/std.md +++ b/docs/std.md @@ -2,7 +2,7 @@ These are standard html elements but please prefer the more flexible chakra component if it exists. -[Example](../examples/src/main/scala/tests/StdComponents.scala) +[Example](../end-to-end-tests/src/main/scala/tests/StdComponents.scala) Dependency: `io.github.kostaskougios::terminal21-ui-std:$VERSION` diff --git a/examples/src/main/scala/tests/ChakraComponents.scala b/end-to-end-tests/src/main/scala/tests/ChakraComponents.scala similarity index 100% rename from examples/src/main/scala/tests/ChakraComponents.scala rename to end-to-end-tests/src/main/scala/tests/ChakraComponents.scala diff --git a/end-to-end-tests/src/main/scala/tests/MathJaxComponents.scala b/end-to-end-tests/src/main/scala/tests/MathJaxComponents.scala new file mode 100644 index 00000000..a22fe050 --- /dev/null +++ b/end-to-end-tests/src/main/scala/tests/MathJaxComponents.scala @@ -0,0 +1,11 @@ +package tests + +import org.terminal21.client.* +import org.terminal21.client.components.* +import org.terminal21.client.components.mathjax.* + +@main def mathJaxComponents(): Unit = + Sessions.withNewSession("mathjax-components", "MathJax Components", MathJaxLib): session => + given ConnectedSession = session + Seq(MathJax(expression = "\\[\\sum_{n = 200}^{1000}\\left(\\frac{10\\sqrt{n}}{n}\\right)\\]")).render() + session.waitTillUserClosesSession() diff --git a/examples/src/main/scala/tests/NivoComponents.scala b/end-to-end-tests/src/main/scala/tests/NivoComponents.scala similarity index 100% rename from examples/src/main/scala/tests/NivoComponents.scala rename to end-to-end-tests/src/main/scala/tests/NivoComponents.scala diff --git a/examples/src/main/scala/tests/StdComponents.scala b/end-to-end-tests/src/main/scala/tests/StdComponents.scala similarity index 100% rename from examples/src/main/scala/tests/StdComponents.scala rename to end-to-end-tests/src/main/scala/tests/StdComponents.scala diff --git a/examples/src/main/scala/tests/chakra/Buttons.scala b/end-to-end-tests/src/main/scala/tests/chakra/Buttons.scala similarity index 100% rename from examples/src/main/scala/tests/chakra/Buttons.scala rename to end-to-end-tests/src/main/scala/tests/chakra/Buttons.scala diff --git a/examples/src/main/scala/tests/chakra/Common.scala b/end-to-end-tests/src/main/scala/tests/chakra/Common.scala similarity index 100% rename from examples/src/main/scala/tests/chakra/Common.scala rename to end-to-end-tests/src/main/scala/tests/chakra/Common.scala diff --git a/examples/src/main/scala/tests/chakra/DataDisplay.scala b/end-to-end-tests/src/main/scala/tests/chakra/DataDisplay.scala similarity index 100% rename from examples/src/main/scala/tests/chakra/DataDisplay.scala rename to end-to-end-tests/src/main/scala/tests/chakra/DataDisplay.scala diff --git a/examples/src/main/scala/tests/chakra/Editables.scala b/end-to-end-tests/src/main/scala/tests/chakra/Editables.scala similarity index 100% rename from examples/src/main/scala/tests/chakra/Editables.scala rename to end-to-end-tests/src/main/scala/tests/chakra/Editables.scala diff --git a/examples/src/main/scala/tests/chakra/Etc.scala b/end-to-end-tests/src/main/scala/tests/chakra/Etc.scala similarity index 100% rename from examples/src/main/scala/tests/chakra/Etc.scala rename to end-to-end-tests/src/main/scala/tests/chakra/Etc.scala diff --git a/examples/src/main/scala/tests/chakra/Forms.scala b/end-to-end-tests/src/main/scala/tests/chakra/Forms.scala similarity index 100% rename from examples/src/main/scala/tests/chakra/Forms.scala rename to end-to-end-tests/src/main/scala/tests/chakra/Forms.scala diff --git a/examples/src/main/scala/tests/chakra/Grids.scala b/end-to-end-tests/src/main/scala/tests/chakra/Grids.scala similarity index 100% rename from examples/src/main/scala/tests/chakra/Grids.scala rename to end-to-end-tests/src/main/scala/tests/chakra/Grids.scala diff --git a/examples/src/main/scala/tests/chakra/MediaAndIcons.scala b/end-to-end-tests/src/main/scala/tests/chakra/MediaAndIcons.scala similarity index 100% rename from examples/src/main/scala/tests/chakra/MediaAndIcons.scala rename to end-to-end-tests/src/main/scala/tests/chakra/MediaAndIcons.scala diff --git a/examples/src/main/scala/tests/chakra/Overlay.scala b/end-to-end-tests/src/main/scala/tests/chakra/Overlay.scala similarity index 100% rename from examples/src/main/scala/tests/chakra/Overlay.scala rename to end-to-end-tests/src/main/scala/tests/chakra/Overlay.scala diff --git a/examples/src/main/scala/tests/chakra/Stacks.scala b/end-to-end-tests/src/main/scala/tests/chakra/Stacks.scala similarity index 100% rename from examples/src/main/scala/tests/chakra/Stacks.scala rename to end-to-end-tests/src/main/scala/tests/chakra/Stacks.scala diff --git a/examples/src/main/scala/tests/chakra/Typography.scala b/end-to-end-tests/src/main/scala/tests/chakra/Typography.scala similarity index 100% rename from examples/src/main/scala/tests/chakra/Typography.scala rename to end-to-end-tests/src/main/scala/tests/chakra/Typography.scala diff --git a/examples/src/main/scala/tests/nivo/ResponsiveBarChart.scala b/end-to-end-tests/src/main/scala/tests/nivo/ResponsiveBarChart.scala similarity index 100% rename from examples/src/main/scala/tests/nivo/ResponsiveBarChart.scala rename to end-to-end-tests/src/main/scala/tests/nivo/ResponsiveBarChart.scala diff --git a/examples/src/main/scala/tests/nivo/ResponsiveLineChart.scala b/end-to-end-tests/src/main/scala/tests/nivo/ResponsiveLineChart.scala similarity index 100% rename from examples/src/main/scala/tests/nivo/ResponsiveLineChart.scala rename to end-to-end-tests/src/main/scala/tests/nivo/ResponsiveLineChart.scala diff --git a/examples/src/main/scala/tests/nivo/common.scala b/end-to-end-tests/src/main/scala/tests/nivo/common.scala similarity index 100% rename from examples/src/main/scala/tests/nivo/common.scala rename to end-to-end-tests/src/main/scala/tests/nivo/common.scala diff --git a/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJax.scala b/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJax.scala new file mode 100644 index 00000000..72a66eb6 --- /dev/null +++ b/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJax.scala @@ -0,0 +1,7 @@ +package org.terminal21.client.components.mathjax + +import org.terminal21.client.components.{Keys, UiElement} + +sealed trait MathJaxElement extends UiElement + +case class MathJax(key: String = Keys.nextKey, expression: String = "\\[\\sum_{n = 100}^{1000}\\left(\\frac{10\\sqrt{n}}{n}\\right)\\]") extends MathJaxElement diff --git a/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJaxLib.scala b/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJaxLib.scala new file mode 100644 index 00000000..564b0d79 --- /dev/null +++ b/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJaxLib.scala @@ -0,0 +1,10 @@ +package org.terminal21.client.components.mathjax + +import io.circe.generic.auto.* +import io.circe.syntax.* +import io.circe.* +import org.terminal21.client.components.{ComponentLib, UiElement} + +object MathJaxLib extends ComponentLib: + override def toJson(using Encoder[UiElement]): PartialFunction[UiElement, Json] = + case n: MathJaxElement => n.asJson.mapObject(o => o.add("type", "MathJax".asJson)) From 46736946e5d945fa7a4e36f1309fa7ec95ab6b1d Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 12 Jan 2024 15:17:32 +0000 Subject: [PATCH 123/144] - --- .../src/main/scala/tests/MathJaxComponents.scala | 16 +++++++++++++++- .../client/components/mathjax/MathJax.scala | 8 +++++++- .../client/components/mathjax/MathJaxLib.scala | 1 + .../client/components/chakra/ChakraElement.scala | 2 +- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/end-to-end-tests/src/main/scala/tests/MathJaxComponents.scala b/end-to-end-tests/src/main/scala/tests/MathJaxComponents.scala index a22fe050..2c5a8450 100644 --- a/end-to-end-tests/src/main/scala/tests/MathJaxComponents.scala +++ b/end-to-end-tests/src/main/scala/tests/MathJaxComponents.scala @@ -2,10 +2,24 @@ package tests import org.terminal21.client.* import org.terminal21.client.components.* +import org.terminal21.client.components.chakra.* import org.terminal21.client.components.mathjax.* @main def mathJaxComponents(): Unit = Sessions.withNewSession("mathjax-components", "MathJax Components", MathJaxLib): session => given ConnectedSession = session - Seq(MathJax(expression = "\\[\\sum_{n = 200}^{1000}\\left(\\frac{10\\sqrt{n}}{n}\\right)\\]")).render() + Seq( + HStack().withChildren( + Text(text = "Lets write some math expressions that will wow everybody!"), + MathJax(expression = """\[\sum_{n = 200}^{1000}\left(\frac{20\sqrt{n}}{n}\right)\]""") + ), + HStack().withChildren( + Text(text = "Everyone knows this one"), + MathJax(expression = """\(ax^2 + bx + c = 0\)""") + ), + MathJax( + expression = """Does it align correctly? \(ax^2 + bx + c = 0\) It does provided CHTML renderer is used.""", + style = Map("backgroundColor" -> "gray") + ) + ).render() session.waitTillUserClosesSession() diff --git a/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJax.scala b/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJax.scala index 72a66eb6..168764fe 100644 --- a/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJax.scala +++ b/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJax.scala @@ -1,7 +1,13 @@ package org.terminal21.client.components.mathjax +import org.terminal21.client.components.UiElement.HasStyle import org.terminal21.client.components.{Keys, UiElement} sealed trait MathJaxElement extends UiElement -case class MathJax(key: String = Keys.nextKey, expression: String = "\\[\\sum_{n = 100}^{1000}\\left(\\frac{10\\sqrt{n}}{n}\\right)\\]") extends MathJaxElement +case class MathJax( + key: String = Keys.nextKey, + @volatile var expression: String = "\\[\\sum_{n = 100}^{1000}\\left(\\frac{10\\sqrt{n}}{n}\\right)\\]", + @volatile var style: Map[String, Any] = Map.empty // Note: some of the styles are ignored by mathjax lib +) extends MathJaxElement + with HasStyle diff --git a/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJaxLib.scala b/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJaxLib.scala index 564b0d79..5946f253 100644 --- a/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJaxLib.scala +++ b/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJaxLib.scala @@ -6,5 +6,6 @@ import io.circe.* import org.terminal21.client.components.{ComponentLib, UiElement} object MathJaxLib extends ComponentLib: + import org.terminal21.client.components.StdElementEncoding.given override def toJson(using Encoder[UiElement]): PartialFunction[UiElement, Json] = case n: MathJaxElement => n.asJson.mapObject(o => o.add("type", "MathJax".asJson)) diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala index 1679a965..54be2d48 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/ChakraElement.scala @@ -1065,7 +1065,7 @@ case class Image( */ case class Text( key: String = Keys.nextKey, - @volatile var text: String = "text.text is empty", + @volatile var text: String = "text.text is empty. Did you accidentally assigned the text to the `key` field?", @volatile var fontSize: Option[String] = None, @volatile var noOfLines: Option[Int] = None, @volatile var color: Option[String] = None, From 27250d3f56dc8d3f60d6d5035ed13423c36770f6 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 12 Jan 2024 15:40:13 +0000 Subject: [PATCH 124/144] - --- .../src/main/scala/tests/MathJaxComponents.scala | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/end-to-end-tests/src/main/scala/tests/MathJaxComponents.scala b/end-to-end-tests/src/main/scala/tests/MathJaxComponents.scala index 2c5a8450..a905426c 100644 --- a/end-to-end-tests/src/main/scala/tests/MathJaxComponents.scala +++ b/end-to-end-tests/src/main/scala/tests/MathJaxComponents.scala @@ -13,10 +13,7 @@ import org.terminal21.client.components.mathjax.* Text(text = "Lets write some math expressions that will wow everybody!"), MathJax(expression = """\[\sum_{n = 200}^{1000}\left(\frac{20\sqrt{n}}{n}\right)\]""") ), - HStack().withChildren( - Text(text = "Everyone knows this one"), - MathJax(expression = """\(ax^2 + bx + c = 0\)""") - ), + MathJax(expression = """Everyone knows this one : \(ax^2 + bx + c = 0\)"""), MathJax( expression = """Does it align correctly? \(ax^2 + bx + c = 0\) It does provided CHTML renderer is used.""", style = Map("backgroundColor" -> "gray") From 4608b92b249bb16441230fcf0641ba5dd54cd289 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 12 Jan 2024 15:48:25 +0000 Subject: [PATCH 125/144] - --- Readme.md | 2 ++ docs/images/mathjax/mathjax.png | Bin 0 -> 6722 bytes docs/images/mathjax/mathjaxbig.png | Bin 0 -> 6008 bytes docs/mathjax.md | 21 +++++++++++++++++++++ 4 files changed, 23 insertions(+) create mode 100644 docs/images/mathjax/mathjax.png create mode 100644 docs/images/mathjax/mathjaxbig.png create mode 100644 docs/mathjax.md diff --git a/Readme.md b/Readme.md index a690aca6..3a5016b5 100644 --- a/Readme.md +++ b/Readme.md @@ -116,6 +116,8 @@ Generic components for buttons, menus, forms, text, grids, tables: Charts and visualisation: [Nivo](docs/nivo.md) +Maths: +[MathJax](docs/mathjax.md) # Architecture diff --git a/docs/images/mathjax/mathjax.png b/docs/images/mathjax/mathjax.png new file mode 100644 index 0000000000000000000000000000000000000000..08a825377d70a04f9ba0f2f8999c70c163d84b1a GIT binary patch literal 6722 zcmV-I8olL-P)3at29)D%0DJ8(G|Da*A@Y!XOF?(T?+osL0 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 YKhKoCEXotSvH$=807*qoM6N<$f?;9x>i_@% literal 0 HcmV?d00001 diff --git a/docs/images/mathjax/mathjaxbig.png b/docs/images/mathjax/mathjaxbig.png new file mode 100644 index 0000000000000000000000000000000000000000..940c12a3e881466d21e7386802ef99b4cbfecef2 GIT binary patch literal 6008 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 mg7|QRPY@rD@Co9>5&sXbq4bs?ySeuO0000 Date: Fri, 12 Jan 2024 15:51:13 +0000 Subject: [PATCH 126/144] - --- docs/mathjax.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/mathjax.md b/docs/mathjax.md index 3eab914e..91a5cf62 100644 --- a/docs/mathjax.md +++ b/docs/mathjax.md @@ -9,7 +9,7 @@ Dependency: `io.github.kostaskougios::terminal21-mathjax:$VERSION` ### MathJax -Code: [MathJax](../end-to-end-tests/src/main/scala/tests/MathJaxComponents.scala) +Example: [MathJax](../end-to-end-tests/src/main/scala/tests/MathJaxComponents.scala) ```scala MathJax( From ed73f43d6bef2b1baba3fc81f3c11416649d89bc Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 12 Jan 2024 16:13:46 +0000 Subject: [PATCH 127/144] - --- example-scripts/mathjax.sc | 28 ++++++++++++++++++++++++++++ example-scripts/project.scala | 1 + 2 files changed, 29 insertions(+) create mode 100755 example-scripts/mathjax.sc diff --git a/example-scripts/mathjax.sc b/example-scripts/mathjax.sc new file mode 100755 index 00000000..1710e8cc --- /dev/null +++ b/example-scripts/mathjax.sc @@ -0,0 +1,28 @@ +//#!/usr/bin/env -S scala-cli project.scala + +import org.terminal21.client.* +import org.terminal21.client.components.* +import org.terminal21.client.components.mathjax.* + +import scala.util.Random + +Sessions.withNewSession("mathjax", "MathJax Example", MathJaxLib /* note we need to register the MathJaxLib in order to use it */ ): session => + given ConnectedSession = session + Seq( + MathJax( + expression = """When \(a \ne 0\), there are two solutions to \(ax^2 + bx + c = 0\) and they are $$x = {-b \pm \sqrt{b^2-4ac} \over 2a}.$$""" + ), + MathJax( + expression = """ + |when \(a \ne 0\), there are two solutions to \(x = {-b \pm \sqrt{b^2-4ac} \over 2a}.\) + |Aenean vel velit a lacus lacinia pulvinar. Morbi eget ex et tellus aliquam molestie sit amet eu diam. + |Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas tellus enim, tempor non efficitur et, rutrum efficitur metus. + |Nulla scelerisque, mauris sit amet accumsan iaculis, elit ipsum suscipit lorem, sed fermentum nunc purus non tellus. + |Aenean congue accumsan tempor. \(x = {-b \pm \sqrt{b^2-4ac} \over 2a}.\) maecenas vitae commodo tortor. Aliquam erat volutpat. Etiam laoreet malesuada elit sed vestibulum. + |Etiam consequat congue fermentum. Vivamus dapibus scelerisque ipsum eu tempus. Integer non pulvinar nisi. + |Morbi ultrices sem quis nisl convallis, ac cursus nunc condimentum. Orci varius natoque penatibus et magnis dis parturient montes, + |nascetur ridiculus mus. + |""".stripMargin + ) + ).render() + session.waitTillUserClosesSession() diff --git a/example-scripts/project.scala b/example-scripts/project.scala index 5e17c8a6..dec52579 100644 --- a/example-scripts/project.scala +++ b/example-scripts/project.scala @@ -3,4 +3,5 @@ //> using dep io.github.kostaskougios::terminal21-ui-std:0.11 //> using dep io.github.kostaskougios::terminal21-nivo:0.11 +//> using dep io.github.kostaskougios::terminal21-mathjax:0.11 //> using dep commons-io:commons-io:2.15.1 From b24ef5c201caaa70502aa6f1e1ac38eb43a61ab0 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 12 Jan 2024 16:19:29 +0000 Subject: [PATCH 128/144] - --- Readme.md | 10 ++++++++-- example-scripts/mathjax.sc | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Readme.md b/Readme.md index 3a5016b5..a190ad99 100644 --- a/Readme.md +++ b/Readme.md @@ -61,7 +61,7 @@ scripts with your favorite IDE. Run the scripts within the IDE and view the UI i ```shell ls *.sc -bouncing-ball.sc csv-editor.sc csv-viewer.sc hello-world.sc postit.sc server.sc textedit.sc +bouncing-ball.sc csv-editor.sc csv-viewer.sc hello-world.sc mathjax.sc nivo-line-chart.sc postit.sc server.sc textedit.sc ``` Let's create a simple hello world script in scala-cli that uses terminal21 server to render the UI. @@ -104,7 +104,13 @@ can be used for things like: - even small web based games, maybe starting with [bouncing-ball.sc](example-scripts/bouncing-ball.sc) - POC code at the office can be presented via a scala-cli script + terminal21 UI. The POC code can be imported as a lib in a script. - logs can be viewed and searched via scripts -- ... and so on + +![notebooks, spark notebooks and maths](docs/images/nivo/responsiveline.png) +![notebooks, spark notebooks and maths](docs/images/mathjax/mathjaxbig.png) + +- notebooks with charts, maths like [notebooks](example-scripts/nivo-line-chart.sc) and [maths](example-scripts/mathjax.sc) +- spark notebooks + # Available UI Components Standard html elements diff --git a/example-scripts/mathjax.sc b/example-scripts/mathjax.sc index 1710e8cc..7d698138 100755 --- a/example-scripts/mathjax.sc +++ b/example-scripts/mathjax.sc @@ -1,4 +1,4 @@ -//#!/usr/bin/env -S scala-cli project.scala +#!/usr/bin/env -S scala-cli project.scala import org.terminal21.client.* import org.terminal21.client.components.* From d71d6859a8567c2c2d31952962254d679bc919f5 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 12 Jan 2024 16:20:29 +0000 Subject: [PATCH 129/144] - --- docs/mathjax.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/mathjax.md b/docs/mathjax.md index 91a5cf62..d43d4ea5 100644 --- a/docs/mathjax.md +++ b/docs/mathjax.md @@ -9,7 +9,7 @@ Dependency: `io.github.kostaskougios::terminal21-mathjax:$VERSION` ### MathJax -Example: [MathJax](../end-to-end-tests/src/main/scala/tests/MathJaxComponents.scala) +Example: [mathjax.sc](../example-scripts/mathjax.sc) [MathJax](../end-to-end-tests/src/main/scala/tests/MathJaxComponents.scala) ```scala MathJax( From 360adfbbdd969088d22e59cce463356a6788bbd3 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 12 Jan 2024 16:21:08 +0000 Subject: [PATCH 130/144] - --- example-scripts/mathjax.sc | 2 -- 1 file changed, 2 deletions(-) diff --git a/example-scripts/mathjax.sc b/example-scripts/mathjax.sc index 7d698138..902ff2e2 100755 --- a/example-scripts/mathjax.sc +++ b/example-scripts/mathjax.sc @@ -4,8 +4,6 @@ import org.terminal21.client.* import org.terminal21.client.components.* import org.terminal21.client.components.mathjax.* -import scala.util.Random - Sessions.withNewSession("mathjax", "MathJax Example", MathJaxLib /* note we need to register the MathJaxLib in order to use it */ ): session => given ConnectedSession = session Seq( From ca99aea15b6e13d1deb5d97eb1bf5cd47ad4b5ab Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 12 Jan 2024 16:26:32 +0000 Subject: [PATCH 131/144] - --- .../src/main/scala/tests/MathJaxComponents.scala | 2 +- .../org/terminal21/client/components/mathjax/MathJax.scala | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/end-to-end-tests/src/main/scala/tests/MathJaxComponents.scala b/end-to-end-tests/src/main/scala/tests/MathJaxComponents.scala index a905426c..91d05c20 100644 --- a/end-to-end-tests/src/main/scala/tests/MathJaxComponents.scala +++ b/end-to-end-tests/src/main/scala/tests/MathJaxComponents.scala @@ -13,7 +13,7 @@ import org.terminal21.client.components.mathjax.* Text(text = "Lets write some math expressions that will wow everybody!"), MathJax(expression = """\[\sum_{n = 200}^{1000}\left(\frac{20\sqrt{n}}{n}\right)\]""") ), - MathJax(expression = """Everyone knows this one : \(ax^2 + bx + c = 0\)"""), + MathJax(expression = """Everyone knows this one : \(ax^2 + bx + c = 0\). But how about this? \(sum_(i=1)^n i^3=((n(n+1))/2)^2\)"""), MathJax( expression = """Does it align correctly? \(ax^2 + bx + c = 0\) It does provided CHTML renderer is used.""", style = Map("backgroundColor" -> "gray") diff --git a/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJax.scala b/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJax.scala index 168764fe..d3c2a460 100644 --- a/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJax.scala +++ b/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJax.scala @@ -5,9 +5,13 @@ import org.terminal21.client.components.{Keys, UiElement} sealed trait MathJaxElement extends UiElement +/** see https://asciimath.org/ + */ case class MathJax( key: String = Keys.nextKey, - @volatile var expression: String = "\\[\\sum_{n = 100}^{1000}\\left(\\frac{10\\sqrt{n}}{n}\\right)\\]", + /** expression should be like """ text \( asciimath \) text""", i.e. """When \(a \ne 0\), there are two solutions to \(ax^2 + bx + c = 0\)""" + */ + @volatile var expression: String = """fill in the expression as per https://asciimath.org/""", @volatile var style: Map[String, Any] = Map.empty // Note: some of the styles are ignored by mathjax lib ) extends MathJaxElement with HasStyle From 6d913b1f2268eacc9c389b6af48909380191b8b4 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 12 Jan 2024 16:31:55 +0000 Subject: [PATCH 132/144] - --- end-to-end-tests/src/main/scala/tests/MathJaxComponents.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/end-to-end-tests/src/main/scala/tests/MathJaxComponents.scala b/end-to-end-tests/src/main/scala/tests/MathJaxComponents.scala index 91d05c20..c34fe57d 100644 --- a/end-to-end-tests/src/main/scala/tests/MathJaxComponents.scala +++ b/end-to-end-tests/src/main/scala/tests/MathJaxComponents.scala @@ -13,7 +13,7 @@ import org.terminal21.client.components.mathjax.* Text(text = "Lets write some math expressions that will wow everybody!"), MathJax(expression = """\[\sum_{n = 200}^{1000}\left(\frac{20\sqrt{n}}{n}\right)\]""") ), - MathJax(expression = """Everyone knows this one : \(ax^2 + bx + c = 0\). But how about this? \(sum_(i=1)^n i^3=((n(n+1))/2)^2\)"""), + MathJax(expression = """Everyone knows this one : \(ax^2 + bx + c = 0\). But how about this? \(\sum_{i=1}^n i^3 = ((n(n+1))/2)^2 \)"""), MathJax( expression = """Does it align correctly? \(ax^2 + bx + c = 0\) It does provided CHTML renderer is used.""", style = Map("backgroundColor" -> "gray") From 1231338d6f1175157afd75013d90ab8554f43667 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 12 Jan 2024 16:44:58 +0000 Subject: [PATCH 133/144] - --- Readme.md | 2 +- example-scripts/nivo-bar-chart.sc | 84 +++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) create mode 100755 example-scripts/nivo-bar-chart.sc diff --git a/Readme.md b/Readme.md index a190ad99..2ac34676 100644 --- a/Readme.md +++ b/Readme.md @@ -108,7 +108,7 @@ can be used for things like: ![notebooks, spark notebooks and maths](docs/images/nivo/responsiveline.png) ![notebooks, spark notebooks and maths](docs/images/mathjax/mathjaxbig.png) -- notebooks with charts, maths like [notebooks](example-scripts/nivo-line-chart.sc) and [maths](example-scripts/mathjax.sc) +- notebooks with charts like [notebooks](example-scripts/nivo-line-chart.sc) and with maths like [maths](example-scripts/mathjax.sc) - spark notebooks # Available UI Components diff --git a/example-scripts/nivo-bar-chart.sc b/example-scripts/nivo-bar-chart.sc new file mode 100755 index 00000000..47ee7837 --- /dev/null +++ b/example-scripts/nivo-bar-chart.sc @@ -0,0 +1,84 @@ +#!/usr/bin/env -S scala-cli project.scala + +import org.terminal21.client.* +import org.terminal21.client.fiberExecutor +import org.terminal21.client.components.* +import org.terminal21.client.components.nivo.* + +import scala.util.Random +import NivoBarChart.* + +Sessions.withNewSession("nivo-bar-chart", "Nivo Bar Chart", NivoLib /* note we need to register the NivoLib in order to use it */ ): session => + given ConnectedSession = session + + val chart = ResponsiveBar( + data = createRandomData, + keys = Seq("hot dog", "burger", "sandwich", "kebab", "fries", "donut"), + indexBy = "country", + padding = 0.3, + defs = Seq( + Defs("dots", "patternDots", "inherit", "#38bcb2", size = Some(4), padding = Some(1), stagger = Some(true)), + Defs("lines", "patternLines", "inherit", "#eed312", rotation = Some(-45), lineWidth = Some(6), spacing = Some(10)) + ), + fill = Seq(Fill("dots", Match("fries")), Fill("lines", Match("sandwich"))), + axisLeft = Some(Axis(legend = "food", legendOffset = -40)), + axisBottom = Some(Axis(legend = "country", legendOffset = 32)), + valueScale = Scale(`type` = "linear"), + indexScale = Scale(`type` = "band", round = Some(true)), + legends = Seq( + Legend( + dataFrom = "keys", + translateX = 120, + itemsSpacing = 2, + itemWidth = 100, + itemHeight = 20, + symbolSize = 20, + symbolShape = "square" + ) + ) + ) + + Seq( + Paragraph(text = "Means of transportation for various countries", style = Map("margin" -> 20)), + chart + ).render() + + fiberExecutor.submit: + while !session.isClosed do + Thread.sleep(2000) + chart.data = createRandomData + session.render() + + session.waitTillUserClosesSession() + +object NivoBarChart: + def createRandomData: Seq[Seq[BarDatum]] = + Seq( + dataFor("AD"), + dataFor("AE"), + dataFor("GB"), + dataFor("GR"), + dataFor("IT"), + dataFor("FR"), + dataFor("GE"), + dataFor("US") + ) + + def dataFor(country: String) = + Seq( + BarDatum("country", country), + BarDatum("hot dog", rnd), + BarDatum("hot dogColor", "hsl(202, 70%, 50%)"), + BarDatum("burger", rnd), + BarDatum("burgerColor", "hsl(106, 70%, 50%)"), + BarDatum("sandwich", rnd), + BarDatum("sandwichColor", "hsl(115, 70%, 50%)"), + BarDatum("kebab", rnd), + BarDatum("kebabColor", "hsl(113, 70%, 50%)"), + BarDatum("fries", rnd), + BarDatum("friesColor", "hsl(209, 70%, 50%)"), + BarDatum("donut", rnd), + BarDatum("donutColor", "hsl(47, 70%, 50%)") + ) + + def rnd = Random.nextInt(500) + 50 From af4feb4849fc9513be52354a68c8f661673a2e5b Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 12 Jan 2024 20:04:30 +0000 Subject: [PATCH 134/144] - --- example-scripts/bin/start-server | 2 - example-spark/model/Person.scala | 2 + example-spark/project.scala | 12 ++++ example-spark/spark-notebook.sc | 68 +++++++++++++++++++ .../client/components/mathjax/MathJax.scala | 2 +- .../sparklib/CalculationsExtensions.scala | 0 .../sparklib/DataframeExtensions.scala | 0 .../calculations/SparkCalculation.scala | 5 +- .../client/components/Calculation.scala | 24 ++++--- .../client/components/StdUiCalculation.scala | 7 ++ .../client/components/chakra/QuickTable.scala | 2 +- 11 files changed, 109 insertions(+), 15 deletions(-) delete mode 100755 example-scripts/bin/start-server create mode 100644 example-spark/model/Person.scala create mode 100644 example-spark/project.scala create mode 100755 example-spark/spark-notebook.sc rename terminal21-spark/src/{test => main}/scala/org/terminal21/sparklib/CalculationsExtensions.scala (100%) rename terminal21-spark/src/{test => main}/scala/org/terminal21/sparklib/DataframeExtensions.scala (100%) diff --git a/example-scripts/bin/start-server b/example-scripts/bin/start-server deleted file mode 100755 index b0dd2898..00000000 --- a/example-scripts/bin/start-server +++ /dev/null @@ -1,2 +0,0 @@ -#! /bin/sh -scala-cli server.sc diff --git a/example-spark/model/Person.scala b/example-spark/model/Person.scala new file mode 100644 index 00000000..d13eafba --- /dev/null +++ b/example-spark/model/Person.scala @@ -0,0 +1,2 @@ +case class Person(id: Int, name: String, age: Int) + diff --git a/example-spark/project.scala b/example-spark/project.scala new file mode 100644 index 00000000..b37b5a0e --- /dev/null +++ b/example-spark/project.scala @@ -0,0 +1,12 @@ +//> using jvm "21" +//> using scala 3 + +// these java params are needed for spark to work with jdk 21 +//> using javaOpt --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/sun.util.calendar=ALL-UNNAMED --add-opens java.base/sun.security.action=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED + +//> using dep io.github.kostaskougios::terminal21-ui-std:0.11 +//> using dep io.github.kostaskougios::terminal21-spark:0.11 +//> using dep io.github.kostaskougios::terminal21-nivo:0.11 +//> using dep io.github.kostaskougios::terminal21-mathjax:0.11 + +//> using file model diff --git a/example-spark/spark-notebook.sc b/example-spark/spark-notebook.sc new file mode 100755 index 00000000..81b8f9c9 --- /dev/null +++ b/example-spark/spark-notebook.sc @@ -0,0 +1,68 @@ +//#!/usr/bin/env -S scala-cli project.scala + +import org.apache.spark.sql.{Dataset, SparkSession} +import org.terminal21.client.components.* +import org.terminal21.client.components.chakra.* +import org.terminal21.client.components.nivo.* +import org.terminal21.client.{*, given} +import org.terminal21.sparklib.* + +import java.util.concurrent.atomic.AtomicInteger +import scala.util.Random +import SparkNotebook.* +import org.terminal21.client.components.mathjax.{MathJax, MathJaxLib} + +SparkSessions.newTerminal21WithSparkSession(SparkSessions.newSparkSession(), "spark-basics", "Spark Basics", NivoLib, MathJaxLib): (spark, session) => + given ConnectedSession = session + given SparkSession = spark + import scala3encoders.given + import spark.implicits.* + + // lets get a Dataset, the data are random so that when we click refresh we can see the data actually + // been refreshed. Better have spark code in an object to avoid accidentally capturing any script variables. + val peopleDS = createPeople + + // We will display the data in a table + val peopleTable = QuickTable().headers("Id", "Name", "Age").caption("People") + + val peopleTableCalc = peopleDS.visualize("People sample", peopleTable): data => + peopleTable.rows(data.take(10).map(p => Seq(p.id, p.name, p.age))) + + /** The calculation above uses a directory to store the dataset results. This way we can restart this script without loosing datasets that may take long to + * calculate, making our script behave more like a notebook. When we click "Recalculate" in the UI, the cache directory is deleted and the dataset is + * re-evaluated. If the Dataset schema changes, please click "Recalculate" or manually delete this folder. + * + * The key for the cache is "People sample" + */ + println(s"Cache path: ${peopleTableCalc.cachePath}") + + Seq( + // just make it look a bit more like a proper notebook by adding some fake maths + MathJax( + expression = """ + |The following is total nonsense but it simulates some explanation that would normally be here if we had + |a proper notebook. When \(a \ne 0\), there are two solutions to \(x = {-b \pm \sqrt{b^2-4ac} \over 2a}.\) + |Aenean vel velit a lacus lacinia pulvinar. Morbi eget ex et tellus aliquam molestie sit amet eu diam. + |Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas tellus enim, tempor non efficitur et, rutrum efficitur metus. + |Nulla scelerisque, mauris sit amet accumsan iaculis, elit ipsum suscipit lorem, sed fermentum nunc purus non tellus. + |Aenean congue accumsan tempor. \(x = {-b \pm \sqrt{b^2-4ac} \over 2a}.\) maecenas vitae commodo tortor. Aliquam erat volutpat. Etiam laoreet malesuada elit sed vestibulum. + |Etiam consequat congue fermentum. Vivamus dapibus scelerisque ipsum eu tempus. Integer non pulvinar nisi. + |Morbi ultrices sem quis nisl convallis, ac cursus nunc condimentum. Orci varius natoque penatibus et magnis dis parturient montes, + |nascetur ridiculus mus. + |""".stripMargin + ), + peopleTableCalc + ).render() + + session.waitTillUserClosesSession() + +object SparkNotebook: + private val names = Array("Andy", "Kostas", "Alex", "Andreas", "George", "Jack") + private val surnames = Array("Papadopoulos", "Rex", "Dylan", "Johnson", "Regan") + def randomName: String = names(Random.nextInt(names.length)) + " " + surnames(Random.nextInt(surnames.length)) + def randomPerson(id: Int): Person = Person(id, randomName, Random.nextInt(100)) + + def createPeople(using spark: SparkSession): Dataset[Person] = + import spark.implicits.* + import scala3encoders.given + (1 to 100).toDS.map(randomPerson) diff --git a/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJax.scala b/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJax.scala index d3c2a460..10fad50f 100644 --- a/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJax.scala +++ b/terminal21-mathjax/src/main/scala/org/terminal21/client/components/mathjax/MathJax.scala @@ -5,7 +5,7 @@ import org.terminal21.client.components.{Keys, UiElement} sealed trait MathJaxElement extends UiElement -/** see https://asciimath.org/ +/** see https://asciimath.org/ and https://github.com/fast-reflexes/better-react-mathjax */ case class MathJax( key: String = Keys.nextKey, diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/CalculationsExtensions.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/CalculationsExtensions.scala similarity index 100% rename from terminal21-spark/src/test/scala/org/terminal21/sparklib/CalculationsExtensions.scala rename to terminal21-spark/src/main/scala/org/terminal21/sparklib/CalculationsExtensions.scala diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/DataframeExtensions.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/DataframeExtensions.scala similarity index 100% rename from terminal21-spark/src/test/scala/org/terminal21/sparklib/DataframeExtensions.scala rename to terminal21-spark/src/main/scala/org/terminal21/sparklib/DataframeExtensions.scala diff --git a/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala b/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala index 36ab24fe..10b20321 100644 --- a/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala +++ b/terminal21-spark/src/main/scala/org/terminal21/sparklib/calculations/SparkCalculation.scala @@ -3,14 +3,12 @@ package org.terminal21.sparklib.calculations import functions.fibers.FiberExecutor import org.apache.commons.io.FileUtils import org.apache.spark.sql.SparkSession +import org.terminal21.client.ConnectedSession import org.terminal21.client.components.UiElement.HasStyle -import org.terminal21.client.components.chakra.* import org.terminal21.client.components.{CachedCalculation, StdUiCalculation, UiComponent, UiElement} -import org.terminal21.client.ConnectedSession import org.terminal21.sparklib.util.Environment import java.io.File -import java.util.concurrent.atomic.AtomicBoolean /** A UI component that takes a spark calculation (i.e. a spark query) that results in a Dataset. It caches the results by storing them as parquet into the tmp * folder/spark-calculations/$name. Next time the calculation runs it reads the cache if available. A button should allow the user to clear the cache and rerun @@ -27,6 +25,7 @@ trait SparkCalculation[OUT: ReadWriter](name: String)(using executor: FiberExecu private val targetDir = s"$rootFolder/$name" def isCached: Boolean = new File(targetDir).exists() + def cachePath: String = targetDir private def cache[A](reader: => A, writer: => A): A = if isCached then reader diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/Calculation.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/Calculation.scala index be3c8d06..30c933e0 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/Calculation.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/Calculation.scala @@ -11,15 +11,23 @@ trait Calculation[OUT](using executor: FiberExecutor): def reCalculate(): Fiber[OUT] = run() + def onError(t: Throwable): Unit = + t.printStackTrace() + def run(): Fiber[OUT] = val refreshInOrder = new CountDownLatch(1) executor.submit: - executor.submit: - try whenResultsNotReady() - finally refreshInOrder.countDown() + try + executor.submit: + try whenResultsNotReady() + finally refreshInOrder.countDown() - val out = calculation() - refreshInOrder.await() - executor.submit: - whenResultsReady(out) - out + val out = calculation() + refreshInOrder.await() + executor.submit: + whenResultsReady(out) + out + catch + case t: Throwable => + onError(t) + throw t diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala index 8e57cd8a..c46bec4e 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/StdUiCalculation.scala @@ -38,6 +38,13 @@ trait StdUiCalculation[OUT]( dataUi ) + override def onError(t: Throwable): Unit = + badge.text = s"Error: ${t.getMessage}" + badge.colorScheme = Some("red") + recalc.isDisabled = None + session.render() + super.onError(t) + override protected def whenResultsNotReady(): Unit = badge.text = "Calculating" badge.colorScheme = Some("purple") diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala index 5307ed74..fb7dd571 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/components/chakra/QuickTable.scala @@ -26,7 +26,7 @@ case class QuickTable( head.children = headers.map(h => Th(children = Seq(h))) this - def rows(data: Seq[Seq[String]]): QuickTable = rowsElements(data.map(_.map(c => Text(text = c)))) + def rows(data: Seq[Seq[Any]]): QuickTable = rowsElements(data.map(_.map(c => Text(text = c.toString)))) def rowsElements(data: Seq[Seq[UiElement]]): QuickTable = body.children = data.map: row => From 069e1d4247fbabfc459e9c963a2aeabf56059dea Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 12 Jan 2024 20:12:30 +0000 Subject: [PATCH 135/144] - --- example-spark/spark-notebook.sc | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/example-spark/spark-notebook.sc b/example-spark/spark-notebook.sc index 81b8f9c9..0bba3979 100755 --- a/example-spark/spark-notebook.sc +++ b/example-spark/spark-notebook.sc @@ -36,6 +36,24 @@ SparkSessions.newTerminal21WithSparkSession(SparkSessions.newSparkSession(), "sp */ println(s"Cache path: ${peopleTableCalc.cachePath}") + val oldestPeopleChart = ResponsiveLine( + axisBottom = Some(Axis(legend = "Person", legendOffset = 36)), + axisLeft = Some(Axis(legend = "Age", legendOffset = -40)), + legends = Seq( + Legend() + ) + ) + + val oldestPeopleChartCalc = peopleDS + .orderBy($"age".desc) + .visualize("Oldest people", oldestPeopleChart): data => + oldestPeopleChart.data = Seq( + Serie( + "Person", + data = data.take(5).map(person => Datum(person.name, person.age)) + ) + ) + Seq( // just make it look a bit more like a proper notebook by adding some fake maths MathJax( @@ -51,7 +69,8 @@ SparkSessions.newTerminal21WithSparkSession(SparkSessions.newSparkSession(), "sp |nascetur ridiculus mus. |""".stripMargin ), - peopleTableCalc + peopleTableCalc, + oldestPeopleChartCalc ).render() session.waitTillUserClosesSession() @@ -60,7 +79,7 @@ object SparkNotebook: private val names = Array("Andy", "Kostas", "Alex", "Andreas", "George", "Jack") private val surnames = Array("Papadopoulos", "Rex", "Dylan", "Johnson", "Regan") def randomName: String = names(Random.nextInt(names.length)) + " " + surnames(Random.nextInt(surnames.length)) - def randomPerson(id: Int): Person = Person(id, randomName, Random.nextInt(100)) + def randomPerson(id: Int): Person = Person(id, randomName + s" ($id)", Random.nextInt(100)) def createPeople(using spark: SparkSession): Dataset[Person] = import spark.implicits.* From c46ca4ffa304e1f49ecb9e4261bed1c890c49b72 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 12 Jan 2024 22:54:35 +0000 Subject: [PATCH 136/144] - --- example-spark/spark-notebook.sc | 18 ++++++++++++------ .../sparklib/endtoend/SparkBasics.scala | 4 ++-- .../sparklib/endtoend/model/CodeFile.scala | 2 +- .../terminal21/client/ConnectedSession.scala | 10 ++++++++++ .../scala/org/terminal21/client/Sessions.scala | 2 +- .../terminal21/client/CalculationTest.scala | 2 +- 6 files changed, 27 insertions(+), 11 deletions(-) diff --git a/example-spark/spark-notebook.sc b/example-spark/spark-notebook.sc index 0bba3979..7080a44a 100755 --- a/example-spark/spark-notebook.sc +++ b/example-spark/spark-notebook.sc @@ -1,4 +1,12 @@ -//#!/usr/bin/env -S scala-cli project.scala +#!/usr/bin/env -S scala-cli --restart project.scala + +/** + * note we use the --restart param for scala-cli. This means every time we change this file, scala-cli will terminate + * and rerun it with the changes. This way we get the notebook feel when we use spark scripts. + * + * terminal21 spark lib caches datasets by storing them into disk. This way complex queries won't have to be re-evaluated + * on each restart of the script. We can force re-evaluation by clicking the "Recalculate" buttons in the UI. + */ import org.apache.spark.sql.{Dataset, SparkSession} import org.terminal21.client.components.* @@ -12,7 +20,7 @@ import scala.util.Random import SparkNotebook.* import org.terminal21.client.components.mathjax.{MathJax, MathJaxLib} -SparkSessions.newTerminal21WithSparkSession(SparkSessions.newSparkSession(), "spark-basics", "Spark Basics", NivoLib, MathJaxLib): (spark, session) => +SparkSessions.newTerminal21WithSparkSession(SparkSessions.newSparkSession(), "spark-notebook", "Spark Notebook", NivoLib, MathJaxLib): (spark, session) => given ConnectedSession = session given SparkSession = spark import scala3encoders.given @@ -25,7 +33,7 @@ SparkSessions.newTerminal21WithSparkSession(SparkSessions.newSparkSession(), "sp // We will display the data in a table val peopleTable = QuickTable().headers("Id", "Name", "Age").caption("People") - val peopleTableCalc = peopleDS.visualize("People sample", peopleTable): data => + val peopleTableCalc = peopleDS.sort($"id").visualize("People sample", peopleTable): data => peopleTable.rows(data.take(10).map(p => Seq(p.id, p.name, p.age))) /** The calculation above uses a directory to store the dataset results. This way we can restart this script without loosing datasets that may take long to @@ -39,9 +47,7 @@ SparkSessions.newTerminal21WithSparkSession(SparkSessions.newSparkSession(), "sp val oldestPeopleChart = ResponsiveLine( axisBottom = Some(Axis(legend = "Person", legendOffset = 36)), axisLeft = Some(Axis(legend = "Age", legendOffset = -40)), - legends = Seq( - Legend() - ) + legends = Seq(Legend()) ) val oldestPeopleChartCalc = peopleDS diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index 39f35530..e57c52ca 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -32,7 +32,7 @@ import org.terminal21.sparklib.endtoend.model.CodeFile.scanSourceFiles codeFilesTable.rows(dt.map(_.toData)) val sortedFilesTableDF = QuickTable().headers(headers: _*).caption("Files sorted by createdDate and numOfWords ASC and as DF") - val sortedCalcDF = sourceFiles() + val sortedCalcAsDF = sourceFiles() .sort($"createdDate".asc, $"numOfWords".asc) .toDF() .visualize("Sorted files DF", sortedFilesTableDF): results => @@ -65,7 +65,7 @@ import org.terminal21.sparklib.endtoend.model.CodeFile.scanSourceFiles Seq( codeFilesCalculation, sortedCalc, - sortedCalcDF, + sortedCalcAsDF, sourceFileChart ).render() diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/model/CodeFile.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/model/CodeFile.scala index ed011604..0113b1ef 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/model/CodeFile.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/model/CodeFile.scala @@ -13,7 +13,7 @@ case class CodeFile(id: Int, name: String, path: String, numOfLines: Int, numOfW object CodeFile: import scala.jdk.CollectionConverters.* def scanSourceFiles: Seq[CodeFile] = - val availableFiles = FileUtils.listFiles(new File(".."), Array("scala"), true).asScala.toList + val availableFiles = FileUtils.listFiles(new File(".."), Array("scala"), true).asScala.filterNot(_.getPath.contains("/.scala-build/")).toList availableFiles.zipWithIndex.map: (f, i) => val code = Files.readString(f.toPath) CodeFile( diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala index ff25e342..b5a73373 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/ConnectedSession.scala @@ -10,6 +10,7 @@ import org.terminal21.client.components.UiElementEncoding import org.terminal21.model.* import org.terminal21.ui.std.SessionsService +import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.{CountDownLatch, TimeUnit} import scala.annotation.tailrec @@ -48,6 +49,15 @@ class ConnectedSession(val session: Session, encoding: UiElementEncoding, val se try exitLatch.await() catch case _: Throwable => () // nop + private val leaveSessionOpen = new AtomicBoolean(false) + + /** Doesn't close the session upon exiting. In the UI the session seems active but events are not working because the event handlers are not available. + */ + def leaveSessionOpenAfterExiting(): Unit = + leaveSessionOpen.set(true) + + def isLeaveSessionOpen: Boolean = leaveSessionOpen.get() + /** Waits till user closes the session or a custom condition becomes true * @param condition * if true then this returns otherwise it waits. diff --git a/terminal21-ui-std/src/main/scala/org/terminal21/client/Sessions.scala b/terminal21-ui-std/src/main/scala/org/terminal21/client/Sessions.scala index bccf1592..5b29b5fd 100644 --- a/terminal21-ui-std/src/main/scala/org/terminal21/client/Sessions.scala +++ b/terminal21-ui-std/src/main/scala/org/terminal21/client/Sessions.scala @@ -37,5 +37,5 @@ object Sessions: try { f(connectedSession) } finally - if !isStopped.get() then sessionsService.terminateSession(session) + if !isStopped.get() && !connectedSession.isLeaveSessionOpen then sessionsService.terminateSession(session) listener.close() diff --git a/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala b/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala index 9fea22ac..a22df69a 100644 --- a/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala +++ b/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala @@ -22,7 +22,7 @@ class CalculationTest extends AnyFunSuiteLike: test("calculates"): val calc = new Calc - calc.run().get() should be(2) + calc.run() should be(2) test("calls whenResultsNotReady"): val calc = new Calc From d9a7566712c93b5bccea553aaf5f6299d206d971 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 12 Jan 2024 22:56:40 +0000 Subject: [PATCH 137/144] - --- example-spark/spark-notebook.sc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example-spark/spark-notebook.sc b/example-spark/spark-notebook.sc index 7080a44a..6db04dc1 100755 --- a/example-spark/spark-notebook.sc +++ b/example-spark/spark-notebook.sc @@ -27,7 +27,7 @@ SparkSessions.newTerminal21WithSparkSession(SparkSessions.newSparkSession(), "sp import spark.implicits.* // lets get a Dataset, the data are random so that when we click refresh we can see the data actually - // been refreshed. Better have spark code in an object to avoid accidentally capturing any script variables. + // been refreshed. val peopleDS = createPeople // We will display the data in a table From 8f34569a634f6e3371b472df8d31978f9cc0994b Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 12 Jan 2024 23:14:03 +0000 Subject: [PATCH 138/144] - --- Readme.md | 2 + docs/images/spark/spark-notebook.png | Bin 0 -> 141108 bytes docs/images/spark/sparkbasics.png | Bin 0 -> 167051 bytes docs/spark.md | 36 ++++++++++++++++++ example-spark/spark-notebook.sc | 2 +- .../sparklib/endtoend/SparkBasics.scala | 4 +- 6 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 docs/images/spark/spark-notebook.png create mode 100644 docs/images/spark/sparkbasics.png create mode 100644 docs/spark.md diff --git a/Readme.md b/Readme.md index 2ac34676..ac92296e 100644 --- a/Readme.md +++ b/Readme.md @@ -125,6 +125,8 @@ Charts and visualisation: Maths: [MathJax](docs/mathjax.md) +Spark: +[Spark](docs/spark.md) # Architecture Terminal21 consist of : diff --git a/docs/images/spark/spark-notebook.png b/docs/images/spark/spark-notebook.png new file mode 100644 index 0000000000000000000000000000000000000000..e70e79135ff7f1f39cc2469e0baa23601949d404 GIT binary patch literal 141108 zcmZ7dcRZVa^goQpriv4-}gV)uONld3Xl^08nYZRDA;g5TgMA zB5rch+b0jqlUx9R2!N)lieZ2ydeN`ZzQ0NI<{SY%@A6-Z3nogG+~41y4p`HzUB10Z zx%Zr$I&yiRy62O;s)t8)C#uvcbW^SgA-9Y6x%8c+(s`>mz`EFZKF8Zo9Oi0Vg_j&M zZ1|b}&ZVp<{oQ5#4u9%sW|$1-gzgE>DP?D}&tPo9b3wT2?;Gv^y{zTjm>hrl|Go1N zbuu{=QxcW_R4*IS8eFT!5EG(+Kce0PGCviNQmuftxuB=t4_za+PM z)D&Uye>9<@+`7^r{Pj$6Io5Zg%(2LH^Q{cf6-d^X?R*wB@mHc0xQiOIUSDeO&#+M3 z^IxXjxuVm_NVjm#^PLcMEXm*NvTD3az11Aa0}EhYa8Hr*7&iTtiItD-TxbmuQLSsU zq?|VzG%d@hsa;cRd=15h{y$9cpDsUZ{r`3O=3cj3+>V3Z6^E}~(9VX*#7KkaUxwPN z9@X9nFmyldfvNX~80jyC@a`UsqNoREVnSP+nf_mV;TGq`$D-{DLc4~jQCa*OXA}{2 zk{0X*wUS7l&r<)bBJ!H8%swTzZSOLgn5T_Fm+MbD^Alzd%>Hjf{EtbxwEou`>*PC@ zS8OTOZ8w`2{?f|S*5?h1=i2eYoh~y6WL#5zl%jRsa))0F*HFrmzB7vFC8Ny?f}vY* z1ApKo{F%g9UiI~8lk;raG>;3r)r7?1_HQSD=7Hw< zg**DRybw8v>}AG?DvAJtp|e?hE#R_oNBUMhAwg$tj1%Rq?(}fml1vX(!&zt7t;I8) zikb$mY!@V}MPyUtpp4ND8)%(kXX1Cwt1U>lGg_cDeM&0~D(LZivy0{ox7a-zd+P0u2GFKxJa z$01v~^DriZaC;5}U!3%;=@b*L-M-@-ZMvmLDbj4SAiCwz_996E_}^Z{tdi${FLTVS zFz9SPx#L=#qcA%{_e~;YH;{#&DkD&nmmBcpg2Bdd=sPF zQCU3*3!iTi^r2&eZ!$I%XzQE(`6Yc9!@q7FzlF|eo?K(-fq=UIsxSZk^GjE zjTFC3mrL0--uwGSyBgSI=NJUtnM=`>M)9p*0lOtVJ#eQ}GipV1DZi^|HS9Y9HW`cO zIejblC^kNQh)xxD}9akZ2o=*1$GQjwvWvcx`yD zkhy_>OH(~BQ|H*Pd5 zY2z<(f)iLq%k(ke!RkqkQl@xWHPMnR*HsmVF0&D+rt2&y+qR2$f`*U%0VnEqKRld@ z6hepLa&|w8S~14l=oH*Bgz%3x{1L|iJ%RHRjNE|cTY*hCuby9@+GL9!U3t)V*|WCD z(%Vq7wj88azLFZPtjiK}+YDT5yH!Tsru(3?cISzG)%RTMF>J6e9;y4?xpav@H~#~962J$bItG@P8ph4>Zj$KS+oE7G0G`1;q!P>QtNUra!o z9?XE~-q=&^G}N)?jnb7uyvCEam)GUf6@Ov_#c3Q^!WEeANCqyWB*vPRkwE-_d`OPh zG#2(p1~)wbw35a)PVIcFNr}_jcc2S5hia9ugl`V|WI8Jh@3;P>xL?a{XrQB{v6VUT z`sHfN@BOQytNm~R;=e2;qHWhEI}=vs%#<@BVIisg8JyZqow!<(y?2+zuy0KFEaKZU!^12ueX!uL5oz zCs25#XzN9s^8t=pN3`SO4bt|^vzjDbD~8=GgzP%bv4lB!zw@A{5j!nk@?Y8k3|WS+ zjgmj@i1>A2IGxrkJsT3^J4g#ZWl@sbuC<+b9$!PRvcQ{Rm1S)1j*|%rK9!XIiMl?! ze70*I|C2Yvg1htdOy2p6y-8A=2Xqp$(>#9=cXs=1!^BdX#iQN*B1z~eEA(f15k>^vQVCX>IDmx`{ca~Rz+dnnI!zC;*`BBCi;JO>KwH@E>M3?Fjr66KPSK2zez zSDi){Q;M`JR5i{G0=?Lvq{xuI`er`aWqgd_a6b2M=a`XMzQPnb@zVp)np<9w0rpqCB zoJ%<=Ynnno-QE7e55JbY!l8#RADng7FgT>($&e;oL+11d#2)-!g>93k<$79spgipyWQZ@q1U z)XW3w8nitQpLFvPzDye~Td@6}>zVuEC~N2EzgTT6WZxkje zG53>Mede%wpz|SuZ4&`Ktl!xWKG1WmhB}k0A7^>+I4^8P^iFDv!dRWk><%tj!ZSQt zccx2ooq$SRnki*k>q_`trO9K_n@8Z(&^)bNGnbwCdqjd+H#j!`l4KT7hRZ)8#YLhS z#1C@+Dz67eUdwJ-<$@K?ex6iuyvo|NvTHl}=jz$F=-ovLa1|0QW=aRm76$DQ=j=5E z@fMwK-n9zZ*=U?c(8M-_MiHX0;UG3R6SV{xEkfbyx#9PaEe!*;sQEm96l0{fbb*_% z&(rj_h6T1!*R6^7kj!Tmipu*k`bT!cv$_qq*NOjbRqjb^!XDJ;HI#2< ziOm^4J4FgMKhHgfw=98OY#BOSD;u2CCy#$z_MMNObq(=k8IlOA14nT5x|yfmJi6BGum(GF!E-Ybz2hevj;o(`b}Ivb&K+jux30AZShgei zV_3t?!TeTnjLBFx{HLT>O7o$pKb|NK|5fIldMnG*qT@;chz5A{*1YM+{<2W6N4XCw zRZ3tmz-v6`GH)@N49{g%W+*rwD#_P`=YAMwOmPj}Yup9yoGNuOGXz^nA$IN9S=`$F zE|c)rNhixMl1MpC-F{>qX0GW`w$a1!t8o?eib`TPR?~ZbxRc^LtY2BVoEyD1=^j(n zGWrDLbpg3QErzJ3Z4J+#grJC#Pt{+q9bEW(#1yg*$`Z|(6W{DO<`*YQ&5MLS=J)C( z7$3oyxigt{66XSLt@nN>iV^>Z09|k}v-m-~(KN9s9F|hQDdN1-yY(s2@97cL$6Ng3 zL;_J{Vi~OHUf)lRF~wyCS#=)o?EQ9RKvcevoGEp1M*p1-e4nQ<>6ypZq}%EKcgsA$ zpvwPXA;)z#akY(LPR$-XU^HNAlJNaZc)2tzo~A z&Q)k?&_)Lk(KT$b`5wKv$&5?)`-+ zBTyE-IN&qC&cXN2^T&%PWq)Y+A&*8oyWi~K&Xr!?Vjb7ZX_}x@BuXc%w8qS|!7{5f zVE=f+(=?{NtFJZa!pQD>P%-S)v(xHogZ}ii8?tO$={U^kVU0_8F>uWpsPsL#TO*2% z>;P4g9~dULapGeuLMRal5YJFNSClC~Vv#B9v=QVuCt*Sys`j-Thx7W(3yVgKY5}I`UkWM$(owDjYHX> zLCsMh8S8`dW5tU@Tj!O2lQ>Btt~ugME;zyN<+~w|q%IKJlvfmY+B6gGJE>uawoaaL zV40H8fle&~V=B*1Oi^!zjinTp3Be=}9FTCc#e4bTbP$=@d+D-HqMJ)m{GsmV zI#MpKZ)UMw7)?BiVIJw9n0@`&mEQescYJ%*n=e0q4UV4?1qr#4LFT)Asr2t$>pQSr zDd(pXQY`1)FOq%+AA}2Zo(xya!cPhNE{TwxX9FCzsjFnB)bY8~Ge6j2^vp-57!~`( z923nlL0oNa(~or z!O8yBfxmA5vn#bJ)@+QiJ<%N{IiFvJT&Z=2ORn! zU`)0Jg_osHZxd9)_nmoTgb?oOSxH?(KOC=;yaHfiKbG1aFqfAeLQT#tkMA?nbW^{* zlPestxP2)cdKJPMY_#)=mjxo6f1Vr#X)1&Jm(C2F&E;{mgl%1Hb z?qc25q@=EnN`OE^@5uK3^kC7dHOZO9%<3|uBtLYv?CGGtJ@oRZeb!+mqdDg~9;0WB zshw4a>&;xe!Rw4G9`T>IUUiX8m~L7A&2p@<+8)K`T#o;&=esuX(K+l}gd;i+ZL&cF z|C~ktq@HMD>@oFPlm=V7U>6u29bibaIyEX~n>{JsM@W)pA6PaR(*ghhxhtEdZ zLLjV?13%>9Ua6MRK07;xl=GhQcdJdg1*!6GpYW*+Yd?HYLt-g)gz_B0t7X1_z4+md zY-~HGh$YO~sJwo1PkpnLk~IrS?PFI9yiS%zmcNE$uwCO}UE|`FX}Bp{tO8~2)%IEv zcXSITNtA&q{w(3eJdM(2K>K!zRrG%Z?33g#DYc^?I#__~(V&D18&ZbN_Z6-Nk<#p(R+WJvrI##Da= zTi7RX>;c;(SMIPFr4K0~THuWJSR_jXwp!kJa^wp7JdBdx!U`5u-Y6*$#uS3(c^AEA zI_o~!OE)cOOsr2SubZ9=QfxObdL=32_DE+KB34&=2x3^%_Q#HCnQ{Y$O`UIREU?%2 z|Jak^1m1q|*;0Of-5R`SW~(4&C!s+!L3yHek&Z-18eGrai7DM%I|_h)fVKb2Zallp zfrrpWoMf0-=B2*{HObUFRE7J`v(+^dS-gI1&4mQ*R(IYMpD0_>`0FMv1nfFVY-YkZ zq>dGK3VOj{$zaZ!EfPi?mvDXGu;Oz?1>suco$;1+Y;B?LOaH~taJ1VkokPtANi8WW z$(Pt4_g=@|avwt-v)tu=hsIAyUwz=QKCbfLIh;jRRCRi_%N!@XVl0}PDS}=2N$n@8 zTM<$myA9h)BK|du*59@AS=1TFGX(yJGc+Bk8s#^98^};YcMYvJ#<;YrCYa+L7bynH zR(f?{SD}OTcSuofX6PT<)v?@Er%g?d1OM%8k=Cw=2%melZHLb&o*_aPSu$2dVMp!B zZ`lG*G?QOmjL6Xwx!u*Ss�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 Ct(2z# literal 0 HcmV?d00001 diff --git a/docs/images/spark/sparkbasics.png b/docs/images/spark/sparkbasics.png new file mode 100644 index 0000000000000000000000000000000000000000..878fc768d1981cee95f238f92d24b5740818f1b3 GIT binary patch literal 167051 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 ROFrIVYvpA5`hri=e*ykpN)`YB literal 0 HcmV?d00001 diff --git a/docs/spark.md b/docs/spark.md new file mode 100644 index 00000000..df3287c9 --- /dev/null +++ b/docs/spark.md @@ -0,0 +1,36 @@ +# Spark + +Dependency: `io.github.kostaskougios::terminal21-spark:$VERSION` + +Terminal 21 spark integration allows using datasets and dataframes inside terminal21 scripts/code. +It also provides caching of datasets in order for scripts to be used as notebooks. The caching +has also a UI component to allow invalidating the cache and re-evaluating the datasets. + +```scala +import org.terminal21.sparklib.* + +val sourceFiles:Dataset[...] +// the following is a UI component that also caches the dataset. +val codeFilesCalculation = sourceFiles.visualize("Code files", codeFilesTable): results => + val dt = results.take(3).toList + codeFilesTable.rows(dt.map(_.toData)) + + Seq(codeFilesCalculation).render() +``` + +## Using terminal21 as notebook within an ide + +Create a scala project (i.e. using sbt), add the terminal21 dependencies and run the terminal21 server. Create your notebook code, i.e. +see [SparkBasics](../terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala). Run it. Let it run while +you interact with the UI. Change the code and rerun it. Click "Recalculate" if you want the datasets to be re-evaluated. + +![SparkBasics](images/spark/sparkbasics.png) + +## Using terminal21 as notebook with scala-cli + +See [spark-notebook.sc](../example-spark/spark-notebook.sc) +On top of the file, `scala-cli` is configured to run with the `--restart` option. This will terminate and restart the script +whenever a change in the file is detected. Edit the script with your favorite IDE and when saving it, it will automatically +re-run. If you want to re-evaluate the datasets, click "Recalculate" on the UI components. + +![SparkNotebook](images/spark/spark-notebook.png) \ No newline at end of file diff --git a/example-spark/spark-notebook.sc b/example-spark/spark-notebook.sc index 6db04dc1..f97b3bbe 100755 --- a/example-spark/spark-notebook.sc +++ b/example-spark/spark-notebook.sc @@ -34,7 +34,7 @@ SparkSessions.newTerminal21WithSparkSession(SparkSessions.newSparkSession(), "sp val peopleTable = QuickTable().headers("Id", "Name", "Age").caption("People") val peopleTableCalc = peopleDS.sort($"id").visualize("People sample", peopleTable): data => - peopleTable.rows(data.take(10).map(p => Seq(p.id, p.name, p.age))) + peopleTable.rows(data.take(5).map(p => Seq(p.id, p.name, p.age))) /** The calculation above uses a directory to store the dataset results. This way we can restart this script without loosing datasets that may take long to * calculate, making our script behave more like a notebook. When we click "Recalculate" in the UI, the cache directory is deleted and the dataset is diff --git a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala index e57c52ca..1b391b16 100644 --- a/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala +++ b/terminal21-spark/src/test/scala/org/terminal21/sparklib/endtoend/SparkBasics.scala @@ -52,9 +52,7 @@ import org.terminal21.sparklib.endtoend.model.CodeFile.scanSourceFiles ), axisBottom = Some(Axis(legend = "Class", legendOffset = 36)), axisLeft = Some(Axis(legend = "Count", legendOffset = -40)), - legends = Seq( - Legend() - ) + legends = Seq(Legend()) ) val sourceFileChart = sortedSourceFilesDS.visualize("Biggest Code Files", chart): results => From 0bea638515a66ae70d084b503e1a1400269c3bc9 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 12 Jan 2024 23:17:54 +0000 Subject: [PATCH 139/144] - --- Readme.md | 2 +- docs/spark.md | 23 +++++++++++++++-------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/Readme.md b/Readme.md index ac92296e..55436361 100644 --- a/Readme.md +++ b/Readme.md @@ -109,7 +109,7 @@ can be used for things like: ![notebooks, spark notebooks and maths](docs/images/mathjax/mathjaxbig.png) - notebooks with charts like [notebooks](example-scripts/nivo-line-chart.sc) and with maths like [maths](example-scripts/mathjax.sc) -- spark notebooks +- spark notebooks like [spark-notebook.sc](example-spark/spark-notebook.sc) # Available UI Components diff --git a/docs/spark.md b/docs/spark.md index df3287c9..712bfb40 100644 --- a/docs/spark.md +++ b/docs/spark.md @@ -6,18 +6,25 @@ Terminal 21 spark integration allows using datasets and dataframes inside termin It also provides caching of datasets in order for scripts to be used as notebooks. The caching has also a UI component to allow invalidating the cache and re-evaluating the datasets. -```scala -import org.terminal21.sparklib.* +To give it a go, please checkout this repo and try the examples: -val sourceFiles:Dataset[...] -// the following is a UI component that also caches the dataset. -val codeFilesCalculation = sourceFiles.visualize("Code files", codeFilesTable): results => - val dt = results.take(3).toList - codeFilesTable.rows(dt.map(_.toData)) +```shell +git clone https://github.com/kostaskougios/terminal21-restapi.git +cd terminal21-restapi/example-scripts - Seq(codeFilesCalculation).render() +# start the server +./server.sc +# ... it will download dependencies & jdk and start the server. + +# Now lets run a spark notebook + +cd terminal21-restapi/example-spark +./spark-notebook.sc ``` +Leave `spark-notebook.sc` running and edit it with your preferred editor. When you save your changes, it will automatically be rerun and +the changes will be reflected in the UI. + ## Using terminal21 as notebook within an ide Create a scala project (i.e. using sbt), add the terminal21 dependencies and run the terminal21 server. Create your notebook code, i.e. From 894079e7e21e822f6e595885b4a6a39a56874883 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 12 Jan 2024 23:20:18 +0000 Subject: [PATCH 140/144] - --- docs/spark.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/spark.md b/docs/spark.md index 712bfb40..666de99e 100644 --- a/docs/spark.md +++ b/docs/spark.md @@ -6,7 +6,7 @@ Terminal 21 spark integration allows using datasets and dataframes inside termin It also provides caching of datasets in order for scripts to be used as notebooks. The caching has also a UI component to allow invalidating the cache and re-evaluating the datasets. -To give it a go, please checkout this repo and try the examples: +To give it a go, please checkout this repo and try the examples. Only requirement to do this is that you have `scala-cli` installed: ```shell git clone https://github.com/kostaskougios/terminal21-restapi.git From 8998617106c1eec19dd39b6cd86e34ec7ac491f6 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Fri, 12 Jan 2024 23:22:21 +0000 Subject: [PATCH 141/144] - --- docs/spark.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/spark.md b/docs/spark.md index 666de99e..daa92dc7 100644 --- a/docs/spark.md +++ b/docs/spark.md @@ -35,7 +35,7 @@ you interact with the UI. Change the code and rerun it. Click "Recalculate" if y ## Using terminal21 as notebook with scala-cli -See [spark-notebook.sc](../example-spark/spark-notebook.sc) +See [spark-notebook.sc](../example-spark/spark-notebook.sc). On top of the file, `scala-cli` is configured to run with the `--restart` option. This will terminate and restart the script whenever a change in the file is detected. Edit the script with your favorite IDE and when saving it, it will automatically re-run. If you want to re-evaluate the datasets, click "Recalculate" on the UI components. From f0009247d4babda75f15687ac2c754049cda342b Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Sun, 14 Jan 2024 17:52:30 +0000 Subject: [PATCH 142/144] - --- example-spark/etc/logback.xml | 19 +++++++++++++++++++ example-spark/project.scala | 6 ++++++ example-spark/spark-notebook.sc | 5 +++-- 3 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 example-spark/etc/logback.xml diff --git a/example-spark/etc/logback.xml b/example-spark/etc/logback.xml new file mode 100644 index 00000000..f037a072 --- /dev/null +++ b/example-spark/etc/logback.xml @@ -0,0 +1,19 @@ + + + + + + + + %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + diff --git a/example-spark/project.scala b/example-spark/project.scala index b37b5a0e..f921305a 100644 --- a/example-spark/project.scala +++ b/example-spark/project.scala @@ -4,9 +4,15 @@ // these java params are needed for spark to work with jdk 21 //> using javaOpt --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/sun.util.calendar=ALL-UNNAMED --add-opens java.base/sun.security.action=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED +// configure logback +//> using javaOpt -Dlogback.configurationFile=file:etc/logback.xml + +// terminal21 dependencies //> using dep io.github.kostaskougios::terminal21-ui-std:0.11 //> using dep io.github.kostaskougios::terminal21-spark:0.11 //> using dep io.github.kostaskougios::terminal21-nivo:0.11 //> using dep io.github.kostaskougios::terminal21-mathjax:0.11 +//> using dep ch.qos.logback:logback-classic:1.4.14 + //> using file model diff --git a/example-spark/spark-notebook.sc b/example-spark/spark-notebook.sc index f97b3bbe..c0cce5e2 100755 --- a/example-spark/spark-notebook.sc +++ b/example-spark/spark-notebook.sc @@ -8,7 +8,8 @@ * on each restart of the script. We can force re-evaluation by clicking the "Recalculate" buttons in the UI. */ -import org.apache.spark.sql.{Dataset, SparkSession} +// We need these imports +import org.apache.spark.sql.* import org.terminal21.client.components.* import org.terminal21.client.components.chakra.* import org.terminal21.client.components.nivo.* @@ -20,7 +21,7 @@ import scala.util.Random import SparkNotebook.* import org.terminal21.client.components.mathjax.{MathJax, MathJaxLib} -SparkSessions.newTerminal21WithSparkSession(SparkSessions.newSparkSession(), "spark-notebook", "Spark Notebook", NivoLib, MathJaxLib): (spark, session) => +SparkSessions.newTerminal21WithSparkSession(SparkSessions.newSparkSession(/* configure your spark session here */), "spark-notebook", "Spark Notebook", NivoLib, MathJaxLib): (spark, session) => given ConnectedSession = session given SparkSession = spark import scala3encoders.given From 95df261265f19497f4a0b7fe63be4aa5635eacc9 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Sun, 14 Jan 2024 18:06:27 +0000 Subject: [PATCH 143/144] - --- bin/publish-maven-central-signed | 8 ++++++++ .../scala/org/terminal21/client/CalculationTest.scala | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100755 bin/publish-maven-central-signed diff --git a/bin/publish-maven-central-signed b/bin/publish-maven-central-signed new file mode 100755 index 00000000..d163eb01 --- /dev/null +++ b/bin/publish-maven-central-signed @@ -0,0 +1,8 @@ +#! /bin/sh + +cd ../terminal21-ui +bin/build-and-copy-to-restapi + +cd ../terminal21-restapi +sbt clean terminal21-server-client-common/publishLocal terminal21-ui-std-exports/publishLocal compile publishSigned + diff --git a/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala b/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala index a22df69a..9fea22ac 100644 --- a/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala +++ b/terminal21-ui-std/src/test/scala/org/terminal21/client/CalculationTest.scala @@ -22,7 +22,7 @@ class CalculationTest extends AnyFunSuiteLike: test("calculates"): val calc = new Calc - calc.run() should be(2) + calc.run().get() should be(2) test("calls whenResultsNotReady"): val calc = new Calc From 704c0d35ae1a9a34ef1c919b45f13f409dfff741 Mon Sep 17 00:00:00 2001 From: Kostas Kougios Date: Sun, 14 Jan 2024 18:09:59 +0000 Subject: [PATCH 144/144] - --- Readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Readme.md b/Readme.md index 55436361..d6afb51f 100644 --- a/Readme.md +++ b/Readme.md @@ -1,5 +1,6 @@ Note: for Table Of Contents, just click the burger icon top right of this document, just above this text on the right. +![artifact](https://img.shields.io/maven-central/v/io.github.kostaskougios/terminal21-server_3) # Terminal 21 Terminal 21 is a library and server that give scala command line programs (i.e. scala-cli scripts) the ability to easily