Last active
April 2, 2023 10:10
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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