Skip to content

Instantly share code, notes, and snippets.

@dacr
Last active April 2, 2023 10:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dacr/ac41381121e0b7235e00533e79adf19d to your computer and use it in GitHub Desktop.
Save dacr/ac41381121e0b7235e00533e79adf19d to your computer and use it in GitHub Desktop.
Drools web based user enhanced interactions with a performance diagnostic knowledge base / published by https://github.com/dacr/code-examples-manager #0048f2a7-1b89-4a32-a0ad-05f785162fc7/db361f61fab0e3353230ffe72383957d1e80cea7
// summary : Drools web based user enhanced interactions with a performance diagnostic knowledge base
// keywords : scala, drools, mvel, scalatest, ai, knowledgebase, interactions, akka-http
// publish : gist
// authors : David Crosson
// license : Apache NON-AI License Version 2.0 (https://raw.githubusercontent.com/non-ai-licenses/non-ai-licenses/main/NON-AI-APACHE2)
// id : 0048f2a7-1b89-4a32-a0ad-05f785162fc7
// execution : scala ammonite script (http://ammonite.io/) - run as follow 'amm scriptname.sc'
// created-on : 2018-09-12T21:11:39+02:00
// managed-by : https://github.com/dacr/code-examples-manager
interp.resolutionHooks += { fetch =>
// -- This is mandatory with drools >= 7.0.46 because drools sources artifacts also brings kie.conf
// -- (it generates resources conflict at KIE init) and because by default ammonite also load sources artifact...
// -- WARN : YOU MAY HAVE TO CLEANUP YOUR LOCAL COURSIER CACHE ~/.cache/coursier to remove sources artifact
// -- WARN : find ~/.cache/coursier/ -name "*drools*-sources.jar" | xargs rm -f
import scala.jdk.CollectionConverters._
fetch.withClassifiers(fetch.getClassifiers.asScala.filter(_ != "sources").asJava)
}
@
import $ivy.`fr.janalyse::drools-scripting:1.0.15`
import fr.janalyse.droolscripting._
// TODO - IMPLEMENT SESSIONS TO ALLOW MULTIPLE USAGE AT THE SAME TIME
import $ivy.`com.typesafe.akka::akka-http:10.2.4`
import $ivy.`com.typesafe.akka::akka-stream:2.6.13`
import scala.collection.immutable.Queue
case class Question(reference:String, message:String) {
val possibleAnswersRE = """(\[[^]]+\])""".r
def possibleAnswers():List[String] = {
possibleAnswersRE.findAllMatchIn(message).map(_.group(1)).toList
}
}
class DroolsInternalHelper(engine:DroolsEngine) {
private var questions:Queue[Question] = Queue.empty
def current:Option[Question] = questions.headOption
def ask(reference:String, question:String):Unit = {
questions = questions.enqueue(Question(reference, question))
}
def respond(reference:String, answer:String):Unit = {
questions = questions.tail
val json = s"""{"reference":"$reference", "answer":"$answer"}"""
engine.insertJson(json,"interact.UserResponse")
}
def reset():Unit = {
questions = Queue.empty
}
}
class PerformanceExpertSystem {
val helperClassName = classOf[DroolsInternalHelper].getCanonicalName
val drl =
s"""package interact
|dialect "mvel"
|
|global org.slf4j.Logger logger
|global $helperClassName helper
|
|declare PerformanceIssue
| description: String
|end
|
|declare UserResponse
| reference: String
| answer: String
|end
|
|// -------------------------------------------------------------------------------
|rule "perf-issue" when then insert("reset"); end
|
|rule "perf-issue after reset" when str:String(this == "reset")
|then
| helper.ask("perf-issue", "Do you encounter a performance problem ? [yes] [no]");
| delete(str);
|end
|// -------------------------------------------------------------------------------
|rule "perf-context" when
| UserResponse(reference == "perf-issue", answer == "yes")
|then
| helper.ask("perf-context", "What is the nature of your performance issue ? [slow response time] [random crash] [high cpu]")
|end
|// -------------------------------------------------------------------------------
|rule "os-context" when
| UserResponse(reference == "perf-issue", answer == "yes")
|then
| helper.ask("os-context", "What is your operating system ? [Linux] [Windows] [MacOS]")
|end
|// -------------------------------------------------------------------------------
|rule "app-context" when
| UserResponse(reference == "perf-issue", answer == "yes")
|then
| helper.ask("app-context", "What is your technical context ? [java] [php] [javascript]")
|end
|// -------------------------------------------------------------------------------
|//rule "java-context" when
|// UserResponse(reference == "app-context", answer == "java")
|//then
|// helper.ask("java-vendor", "What is your java vendor ? [oracle] [openjdk] [ibm]")
|// helper.ask("java-release", "What is your java release ? <text>")
|//end
|// -------------------------------------------------------------------------------
|rule "javascript troll rule" when
| UserResponse(reference == "app-context", answer=="javascript")
|then
| insert(new PerformanceIssue("Oh NO you're using javascript !! do you know it means java scripting ?"));
|end
|// -------------------------------------------------------------------------------
|rule "php troll rule" when
| UserResponse(reference == "app-context", answer=="php")
|then
| insert(new PerformanceIssue("Seriously PHP !?!? A 20th century language..."));
|end
|// -------------------------------------------------------------------------------
|rule "java troll rule" when
| UserResponse(reference == "app-context", answer=="java")
|then
| insert(new PerformanceIssue("Java ? And did you consider cobol ?"));
|end
|// -------------------------------------------------------------------------------
|rule "windows/macos troll rule" when
| UserResponse(reference == "os-context", answer in ("Windows","MacOS") )
|then
| insert(new PerformanceIssue("What ! Using desktop Operating System to power your backend ?"));
|end
|""".stripMargin
val engineConfig = DroolsEngineConfig(equalsWithIdentity=false, withDroolsLogging=false)
val engine = DroolsEngine(drl, engineConfig)
val helper = new DroolsInternalHelper(engine)
engine.session.setGlobal("helper", helper)
def session = engine.session
def run() {
println("Ctrl-C to stop execution")
engine.session.fireUntilHalt()
}
}
class AIService(expertSystem:PerformanceExpertSystem) {
import akka.http.scaladsl._
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.model._
import scala.concurrent.Await
import scala.concurrent.duration.Duration
import akka.http.scaladsl.model.ContentTypes._
implicit val system = akka.actor.ActorSystem("MySystem")
implicit val materializer = akka.stream.ActorMaterializer()
implicit val executionContext = system.dispatcher
def render(question:Question): HttpResponse = {
val renderedQuestion =
question.possibleAnswers().foldLeft(question.message) { case (nquest,answer) =>
val cleanedAnswer = answer.tail.init // remove []
val lnk = s"/response/${question.reference}/$cleanedAnswer"
nquest.replace(answer, s"""<a href="$lnk">$answer</a> """)
}
val content =
s"""<html>
| <body>
| <h1>$renderedQuestion</h1>
| </body>
|</html>""".stripMargin
HttpResponse(entity = HttpEntity(`text/html(UTF-8)`, content))
}
def renderDiagnostic():HttpResponse = {
val issues = expertSystem.engine.getModelInstances("interact.PerformanceIssue")
val renderedIssues =
if (issues.isEmpty) {
"No issue found"
} else {
issues.map(issue => s"<p>$issue</p>").mkString("\n")
}
val content =
s"""<html>
| <body>
| <h1>Performance diagnostics results</h1>
| $renderedIssues
| <br/>
| <a href="/reset">Reset</a>
| </body>
|</html>""".stripMargin
HttpResponse(entity = HttpEntity(`text/html(UTF-8)`, content))
}
def stopRoute = path("shutdown") {
get { complete { expertSystem.engine.session.halt(); shutdown() ; "OK" } }
}
def homeRoute= path("") {
get {
complete {
expertSystem.helper.current match {
case Some(question) => render(question)
case None => renderDiagnostic()
}
}
}
}
def responseRoute = path("response" / Segment / Segment) {
case (reference, answer) => get {
expertSystem.helper.respond(reference, answer)
redirect("/", StatusCodes.TemporaryRedirect)
}
}
def resetRoute = path("reset") {
get {
for { ob <- expertSystem.engine.getObjects } {
val handle = expertSystem.engine.getFactHandle(ob)
expertSystem.session.delete(handle)
}
expertSystem.engine.insert("reset")
expertSystem.helper.reset()
redirect("/", StatusCodes.TemporaryRedirect)
}
}
val chosenPort = 6080
val chosenInterface = "0.0.0.0"
val routes = stopRoute ~ resetRoute ~ homeRoute ~ responseRoute
val bindingFuture = Http().bindAndHandle(routes, chosenInterface, chosenPort)
println(s"***** Local web server is running on $chosenInterface:$chosenPort *****")
def shutdown():Unit = bindingFuture.flatMap(_.unbind()).onComplete { _ => system.terminate() }
def waitForShutdown(): Unit = Await.ready(system.whenTerminated, Duration.Inf)
}
val expertSystem = new PerformanceExpertSystem
val aiService = new AIService(expertSystem)
expertSystem.run()
aiService.waitForShutdown()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment