Skip to content

Commit

Permalink
Batch processing: simplify API and code cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
jpsacha committed Jul 12, 2024
1 parent 642a084 commit 66cbd77
Show file tree
Hide file tree
Showing 11 changed files with 234 additions and 236 deletions.
2 changes: 1 addition & 1 deletion ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ to parent size.
### Batch Processing and Progress Dialog

Work in progress
* Helper UI for running batch processing tasks, see `BatchRunnerProgressHelperDemoApp` for example of use
* Helper UI for running batch processing tasks, see `BatchRunnerProgressDemoApp` for example of use
* Component for display of progress of batch processing tasks, see `ProgressStatusDemoApp` for example of use


Expand Down
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import scala.xml.{Node => XmlNode, NodeSeq => XmlNodeSeq, _}
// JAR_BUILT_BY - Name to be added to Jar metadata field "Built-By" (defaults to System.getProperty("user.name")
//

val projectVersion = "0.9.0.5-SNAPSHOT"
val projectVersion = "0.9.0.6-SNAPSHOT"
val versionTagDir = if (projectVersion.endsWith("SNAPSHOT")) "master" else "v." + projectVersion
val _scalaVersions = Seq("3.3.3", "2.13.14", "2.12.19")
val _scalaVersion = _scalaVersions.head
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright (c) 2000-2023 Jarek Sacha. All Rights Reserved.
* Author's e-mail: jpsacha at gmail.com
*/

package org.scalafx.extras.batch

import org.scalafx.extras.BusyWorker
import org.scalafx.extras.BusyWorker.SimpleTask
import scalafx.application.JFXApp3
import scalafx.application.JFXApp3.PrimaryStage
import scalafx.geometry.Insets
import scalafx.scene.Scene
import scalafx.scene.control.{Button, Label}
import scalafx.scene.layout.VBox
import scalafx.stage.Window

import scala.util.Random

/**
* Example of a task that for batch execution.
*/
private class MyTask(val i: Int) extends ItemTask[String] {

import MyTask.*

val name = s"Task #$i"

def run(): String =
val t = minTime + new Random().nextInt(maxTime - minTime)
Thread.sleep(t)
if t % 6 != 0 then
s"name t = $t"
else
throw new Exception(s"Do not like $t")
}

object MyTask {
val minTime = 500
val maxTime = 1000
}

/**
* Demo of `BatchRunnerWithProgress` GUI
* @author Jarek Sacha
*/
object BatchRunnerProgressDemoApp extends JFXApp3:

private lazy val busyWorker = new BusyWorker(Title, parentWindow)
private val Title = "Batch Processing / Progress Dialog Demo"
private val nTasks = 10

override def start(): Unit =
stage = new PrimaryStage:
title = Title
scene = new Scene:
content = new VBox:
padding = Insets(21)
spacing = 14
children = Seq(
new Label(
s"""Press "Run x" to initiate processing.
|Wait till processing finished or press "Abort".
|There will be $nTasks processed.
|Tasks will have randomly generated execution time,
|between ${MyTask.minTime} and ${MyTask.maxTime} ms.
|If task's time is divisible by 6, that task will fail.
|""".stripMargin
),
new Button("Run as Sequence"):
onAction = _ => onStart(false)
prefWidth = 120
,
new Button("Run in Parallel"):
onAction = _ => onStart(true)
prefWidth = 120
)

private def onStart(runInParallel: Boolean): Unit = busyWorker.doTask("Start") { () =>
type ResultType = String
val helper =
new BatchRunnerWithProgress[ResultType]("Sample batch processing", Option(stage), runInParallel):
def createTasks(): Seq[ItemTask[ResultType]] = (1 to nTasks).map { i => new MyTask(i) }

helper.run()
}

private def parentWindow: Option[Window] = Option(stage)

end BatchRunnerProgressDemoApp
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,16 @@ package org.scalafx.extras.batch

import scala.util.{Failure, Success}

import scala.util.{Failure, Success, Try}

/**
* Example of using `ParallelBatchRunner` and `ItemTask`.
* Results are printed to standard output.
*
* @author Jarek Sacha
*/
object ParallelBatchRunnerDemo {
class DemoTaskItem(n: Int) extends ItemTask[Int] {
private class DemoTaskItem(n: Int) extends ItemTask[Int] {
val name = s"Demo TaskItem $n"

def run(): Int = {
Expand All @@ -46,13 +53,12 @@ object ParallelBatchRunnerDemo {
}
}


def main(args: Array[String]): Unit = {
val items: Seq[DemoTaskItem] = Range(0, 10).map { i => new DemoTaskItem(i) }

val batchHelper = new ParallelBatchRunner(items, progressUpdate, useParallelProcessing = true)

val results = batchHelper.execute()
val results: Seq[(String, Try[Option[Int]])] = batchHelper.execute()

println()
println("Summarize processing")
Expand All @@ -62,19 +68,21 @@ object ParallelBatchRunnerDemo {
}
}

def progressUpdate(running : Long,
successful: Long,
failed : Long,
canceled : Long,
executed : Long,
total : Long,
isCanceled : Boolean,
perc : Double,
message : String) : Unit = {
@FunctionalInterface
private def progressUpdate(
running: Long,
successful: Long,
failed: Long,
canceled: Long,
executed: Long,
total: Long,
isCanceled: Boolean,
perc: Double,
message: String
): Unit = {
val m =
f"R:$running%2d, S:$successful%2d, F:$failed%2d, E:$executed%2d, C:$canceled, T:$total%d, " +
f"C:$isCanceled, perc:${perc.toInt}%3d, $message"
println(m)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -32,51 +32,44 @@ import org.scalafx.extras.{initFX, offFX, onFX, onFXAndWait}
import scalafx.application.Platform

/**
* Example showing use of ProgressStatusDialog
*/
object ProgressStatusDemoApp {
* Example showing use of ProgressStatusDialog
*/
object ProgressStatusDialogDemoApp:

// TODO implement simulated processing using batch processing backend

def main(args: Array[String]): Unit = {
def main(args: Array[String]): Unit =

initFX()
Platform.implicitExit = true

val progressStatusDialog = onFXAndWait {
new ProgressStatusDialog("Processing sample tasks", None)
}
val progressStatusDialog = onFXAndWait:
new ProgressStatusDialog("Progress Status Dialog Demo", None)

progressStatusDialog.abortFlag.onChange { (_, _, newValue) =>
if newValue then {
if newValue then
// Do not block UI, but wait till shutdown completed
offFX {
// Simulate delay due to shut down
offFX:
// Simulate delay due to shutdown
Thread.sleep(3000)
onFX {
onFX:
progressStatusDialog.close()
}
}
}
}

onFXAndWait {
onFXAndWait:
progressStatusDialog.show()
}

val n = 500
for i <- 1 to n if !progressStatusDialog.abortFlag.value do {
onFX {
for i <- 1 to n if !progressStatusDialog.abortFlag.value do
onFX:
progressStatusDialog.statusText.value = s"Processing item $i / $n"
progressStatusDialog.progress.value = i / n.toDouble
}

Thread.sleep(250)
}

// In case of abort leave to abort handler o close the dialog when shutdown actions are complete
if !progressStatusDialog.abortFlag.value then
onFX {
onFX:
progressStatusDialog.close()
}
}

}
end ProgressStatusDialogDemoApp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

package org.scalafx.extras.batch

object BatchRunner {
object BatchRunner:

@FunctionalInterface
trait ProgressUpdater:
Expand All @@ -42,14 +42,13 @@ object BatchRunner {
perc: Double,
message: String
): Unit
}
trait BatchRunner[T, I <: ItemTask[T]] {

import BatchRunner.ProgressUpdater
trait BatchRunner[T, I <: ItemTask[T]]:

// TODO Is BatchRunner trait needed? It is not used on its own?

// TODO: Is this trait needed
import BatchRunner.ProgressUpdater

protected def itemTasks: Seq[I]

protected def progressUpdater: ProgressUpdater
}
Loading

0 comments on commit 66cbd77

Please sign in to comment.