You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nlpcraft.apache.org by se...@apache.org on 2022/07/13 12:29:55 UTC

[incubator-nlpcraft] branch NLPCRAFT-491 updated: Pizzeria examples refactoring.

This is an automated email from the ASF dual-hosted git repository.

sergeykamov pushed a commit to branch NLPCRAFT-491
in repository https://gitbox.apache.org/repos/asf/incubator-nlpcraft.git


The following commit(s) were added to refs/heads/NLPCRAFT-491 by this push:
     new 226ff4b5 Pizzeria examples refactoring.
226ff4b5 is described below

commit 226ff4b547a7af00a7fe1b1b013214de5edf61d2
Author: Sergey Kamov <sk...@gmail.com>
AuthorDate: Wed Jul 13 15:29:45 2022 +0300

    Pizzeria examples refactoring.
---
 .../nlpcraft/examples/pizzeria/PizzeriaModel.scala | 17 +++----
 .../nlpcraft/examples/pizzeria/PizzeriaOrder.scala | 14 +++---
 .../{ => components}/PizzeriaModelPipeline.scala   | 20 +++-----
 ...derExtender.scala => PizzeriaOrderMapper.scala} | 58 +++++++++++-----------
 .../components/PizzeriaOrderValidator.scala        |  2 +-
 .../org/apache/nlpcraft/NCPropertyMapAdapter.scala |  2 +-
 6 files changed, 52 insertions(+), 61 deletions(-)

diff --git a/nlpcraft-examples/pizzeria/src/main/java/org/apache/nlpcraft/examples/pizzeria/PizzeriaModel.scala b/nlpcraft-examples/pizzeria/src/main/java/org/apache/nlpcraft/examples/pizzeria/PizzeriaModel.scala
index 3a46d562..bb4a5f73 100644
--- a/nlpcraft-examples/pizzeria/src/main/java/org/apache/nlpcraft/examples/pizzeria/PizzeriaModel.scala
+++ b/nlpcraft-examples/pizzeria/src/main/java/org/apache/nlpcraft/examples/pizzeria/PizzeriaModel.scala
@@ -23,12 +23,14 @@ import org.apache.nlpcraft.NCResultType.*
 import org.apache.nlpcraft.annotations.*
 import org.apache.nlpcraft.examples.pizzeria.{PizzeriaOrder as Order, PizzeriaOrderState as State}
 import org.apache.nlpcraft.examples.pizzeria.PizzeriaOrderState.*
+import org.apache.nlpcraft.examples.pizzeria.components.PizzeriaModelPipeline
 import org.apache.nlpcraft.nlp.*
 
 /**
   * * Pizza model helper.
   */
 object PizzeriaModel extends LazyLogging:
+    type Result = (NCResult, State)
     private val UNEXPECTED_REQUEST = new NCRejection("Unexpected request for current dialog context.")
 
     private def extractPizzaSize(e: NCEntity): String = e.get[String]("ord:pizza:size:value")
@@ -41,13 +43,12 @@ object PizzeriaModel extends LazyLogging:
     private def getOrder(ctx: NCContext): Order =
         val data = ctx.getConversation.getData
         val usrId = ctx.getRequest.getUserId
-        var o: Order = data.get(usrId)
-
-        if o == null then
-            o = new Order()
-            data.put(usrId, o)
-
-        o
+        data.getOpt[Order](usrId) match
+            case Some(ord) => ord
+            case None =>
+                val ord = new Order()
+                data.put(usrId, ord)
+                ord
 
     private def mkResult(msg: String): NCResult = NCResult(msg, ASK_RESULT)
     private def mkDialog(msg: String): NCResult = NCResult(msg, ASK_DIALOG)
@@ -111,8 +112,6 @@ object PizzeriaModel extends LazyLogging:
     private def askIsReadyOrAskSpecify(o: Order): Result = if o.isValid then askIsReady() else askSpecify(o)
     private def askStopOrDoStop(o: Order)(using ctx: NCContext, im: NCIntentMatch): Result = if o.isValid then askShouldStop() else doStop(o)
 
-    type Result = (NCResult, State)
-
 import org.apache.nlpcraft.examples.pizzeria.PizzeriaModel.*
 
 /**
diff --git a/nlpcraft-examples/pizzeria/src/main/java/org/apache/nlpcraft/examples/pizzeria/PizzeriaOrder.scala b/nlpcraft-examples/pizzeria/src/main/java/org/apache/nlpcraft/examples/pizzeria/PizzeriaOrder.scala
index 66cc2c06..f510accd 100644
--- a/nlpcraft-examples/pizzeria/src/main/java/org/apache/nlpcraft/examples/pizzeria/PizzeriaOrder.scala
+++ b/nlpcraft-examples/pizzeria/src/main/java/org/apache/nlpcraft/examples/pizzeria/PizzeriaOrder.scala
@@ -25,12 +25,12 @@ import scala.collection.mutable
 enum PizzeriaOrderState:
     case DIALOG_EMPTY, DIALOG_IS_READY, DIALOG_SHOULD_CANCEL, DIALOG_SPECIFY, DIALOG_CONFIRM
 
-private object OrderElement:
+private object OrderPosition:
     val DFLT_QTY = 1
 /**
   *
   */
-private abstract class OrderElement:
+private trait OrderPosition:
     val name: String
     var qty: Option[Int]
     require(name != null && name.nonEmpty)
@@ -42,8 +42,8 @@ private abstract class OrderElement:
   * @param size Optional size.
   * @param qty Optional quantity.
   */
-case class Pizza(name: String, var size: Option[String], var qty: Option[Int]) extends OrderElement:
-    override def toString = s"$name '${size.getOrElse("undefined size")}' ${qty.getOrElse(OrderElement.DFLT_QTY)} pcs"
+case class Pizza(name: String, var size: Option[String], var qty: Option[Int]) extends OrderPosition:
+    override def toString = s"$name '${size.getOrElse("undefined size")}' ${qty.getOrElse(OrderPosition.DFLT_QTY)} pcs"
 
 /**
   * Drink order data holder.
@@ -51,8 +51,8 @@ case class Pizza(name: String, var size: Option[String], var qty: Option[Int]) e
   * @param name Name.
   * @param qty Optional quantity.
   */
-case class Drink(name: String, var qty: Option[Int]) extends OrderElement:
-    override def toString = s"$name ${qty.getOrElse(OrderElement.DFLT_QTY)} pcs"
+case class Drink(name: String, var qty: Option[Int]) extends OrderPosition:
+    override def toString = s"$name ${qty.getOrElse(OrderPosition.DFLT_QTY)} pcs"
 
 import org.apache.nlpcraft.examples.pizzeria.PizzeriaOrderState.*
 
@@ -82,7 +82,7 @@ class PizzeriaOrder:
       * @param ds
       */
     def add(ps: Seq[Pizza], ds: Seq[Drink]): Unit =
-        def setByName[T <: OrderElement](buf: mutable.ArrayBuffer[T], t: T): Unit =
+        def setByName[T <: OrderPosition](buf: mutable.ArrayBuffer[T], t: T): Unit =
             buf.find(_.name == t.name) match
                 case Some(found) => if t.qty.nonEmpty then found.qty = t.qty
                 case None => buf += t
diff --git a/nlpcraft-examples/pizzeria/src/main/java/org/apache/nlpcraft/examples/pizzeria/PizzeriaModelPipeline.scala b/nlpcraft-examples/pizzeria/src/main/java/org/apache/nlpcraft/examples/pizzeria/components/PizzeriaModelPipeline.scala
similarity index 71%
rename from nlpcraft-examples/pizzeria/src/main/java/org/apache/nlpcraft/examples/pizzeria/PizzeriaModelPipeline.scala
rename to nlpcraft-examples/pizzeria/src/main/java/org/apache/nlpcraft/examples/pizzeria/components/PizzeriaModelPipeline.scala
index ca233a4d..73fafbc0 100644
--- a/nlpcraft-examples/pizzeria/src/main/java/org/apache/nlpcraft/examples/pizzeria/PizzeriaModelPipeline.scala
+++ b/nlpcraft-examples/pizzeria/src/main/java/org/apache/nlpcraft/examples/pizzeria/components/PizzeriaModelPipeline.scala
@@ -1,19 +1,17 @@
-package org.apache.nlpcraft.examples.pizzeria
+package org.apache.nlpcraft.examples.pizzeria.components
 
 import edu.stanford.nlp.pipeline.StanfordCoreNLP
-import java.util.Properties
 import opennlp.tools.stemmer.PorterStemmer
-import org.apache.nlpcraft.examples.pizzeria.components.*
 import org.apache.nlpcraft.nlp.entity.parser.semantic.*
+import org.apache.nlpcraft.nlp.entity.parser.stanford.NCStanfordNLPEntityParser
 import org.apache.nlpcraft.nlp.token.enricher.NCEnStopWordsTokenEnricher
 import org.apache.nlpcraft.nlp.token.parser.stanford.NCStanfordNLPTokenParser
 import org.apache.nlpcraft.*
-import org.apache.nlpcraft.nlp.entity.parser.stanford.NCStanfordNLPEntityParser
 
+import java.util.Properties
 
 /**
-  * PizzeriaModel pipeline, based on Stanford NLP engine, including model custom components.
-  */
+  * PizzeriaModel pipeline, based on Stanford NLP engine, including model custom components. */
 object PizzeriaModelPipeline:
     val PIPELINE: NCPipeline =
         val stanford =
@@ -25,18 +23,14 @@ object PizzeriaModelPipeline:
             private val ps = new PorterStemmer
             override def stem(txt: String): String = ps.synchronized { ps.stem(txt) }
 
-        import PizzeriaOrderExtender as Ex, EntityData as D
+        import MapperDesc as D
 
         new NCPipelineBuilder().
             withTokenParser(tokParser).
             withTokenEnricher(new NCEnStopWordsTokenEnricher()).
             withEntityParser(new NCStanfordNLPEntityParser(stanford, Set("number"))).
             withEntityParser(NCSemanticEntityParser(stemmer, tokParser, "pizzeria_model.yaml")).
-            withEntityMappers(
-                List(
-                    Ex(Seq(D("ord:pizza", "ord:pizza:size")), D("ord:pizza:size", "ord:pizza:size:value")),
-                    Ex(Seq(D("ord:pizza", "ord:pizza:qty"), D("ord:drink", "ord:drink:qty")), D("stanford:number", "stanford:number:nne")),
-                )
-            ).
+            withEntityMapper(PizzeriaOrderMapper(extra = D("ord:pizza:size", "ord:pizza:size:value"), dests = D("ord:pizza", "ord:pizza:size"))).
+            withEntityMapper(PizzeriaOrderMapper(extra = D("stanford:number", "stanford:number:nne"), dests = D("ord:pizza", "ord:pizza:qty"), D("ord:drink", "ord:drink:qty"))).
             withEntityValidator(new PizzeriaOrderValidator()).
             build
\ No newline at end of file
diff --git a/nlpcraft-examples/pizzeria/src/main/java/org/apache/nlpcraft/examples/pizzeria/components/PizzeriaOrderExtender.scala b/nlpcraft-examples/pizzeria/src/main/java/org/apache/nlpcraft/examples/pizzeria/components/PizzeriaOrderMapper.scala
similarity index 54%
rename from nlpcraft-examples/pizzeria/src/main/java/org/apache/nlpcraft/examples/pizzeria/components/PizzeriaOrderExtender.scala
rename to nlpcraft-examples/pizzeria/src/main/java/org/apache/nlpcraft/examples/pizzeria/components/PizzeriaOrderMapper.scala
index 54816210..a16bf476 100644
--- a/nlpcraft-examples/pizzeria/src/main/java/org/apache/nlpcraft/examples/pizzeria/components/PizzeriaOrderExtender.scala
+++ b/nlpcraft-examples/pizzeria/src/main/java/org/apache/nlpcraft/examples/pizzeria/components/PizzeriaOrderMapper.scala
@@ -25,14 +25,14 @@ import scala.collection.*
 
 /**
   *
-  * @param id
-  * @param property
+  * @param elementId Element.
+  * @param propertyName Element's property name.
   */
-case class EntityData(id: String, property: String)
+case class MapperDesc(elementId: String, propertyName: String)
 
 /**
   * Element extender.
-  * For each 'main' element it tries to find related extra element and convert this pair to new complex element.
+  * For each 'main' dest element it tries to find related extra element and convert this pair to new complex element.
   * New element:
   * 1. Gets same ID as main element, also all main element properties copied into this new one.
   * 2. Gets tokens from both elements.
@@ -41,52 +41,50 @@ case class EntityData(id: String, property: String)
   * Note that it is simple example implementation.
   * It just tries to unite nearest neighbours and doesn't check intermediate words, order correctness etc.
   */
-object PizzeriaOrderExtender:
+object PizzeriaOrderMapper:
     extension(entity: NCEntity)
         def position: Double =
             val toks = entity.getTokens
             (toks.head.getIndex + toks.last.getIndex) / 2.0
         def tokens: List[NCToken] = entity.getTokens
 
-import PizzeriaOrderExtender.*
+    private def str(es: Iterable[NCEntity]): String =
+        es.map(e => s"id=${e.getId}(${e.tokens.map(_.getIndex).mkString("[", ",", "]")})").mkString("{", ", ", "}")
 
-/**
-  *
-  * @param mainDataSeq
-  * @param extraData
-  */
-case class PizzeriaOrderExtender(mainDataSeq: Seq[EntityData], extraData: EntityData) extends NCEntityMapper with LazyLogging:
+    def apply(extra: MapperDesc, dests: MapperDesc*): PizzeriaOrderMapper = new PizzeriaOrderMapper(extra, dests)
+
+import PizzeriaOrderMapper.*
+
+case class PizzeriaOrderMapper(extra: MapperDesc, dests: Seq[MapperDesc]) extends NCEntityMapper with LazyLogging:
     override def map(req: NCRequest, cfg: NCModelConfig, ents: List[NCEntity]): List[NCEntity] =
-        def combine(mainEnt: NCEntity, mainProp: String, extraEnt: NCEntity): NCEntity =
+        def map(destEnt: NCEntity, destProp: String, extraEnt: NCEntity): NCEntity =
             new NCPropertyMapAdapter with NCEntity:
-                mainEnt.keysSet.foreach(k => put(k, mainEnt.get(k)))
-                put[String](mainProp, extraEnt.get[String](extraData.property).toLowerCase)
-                override val getTokens: List[NCToken] = (mainEnt.tokens ++ extraEnt.tokens).sortBy(_.getIndex)
+                destEnt.keysSet.foreach(k => put(k, destEnt.get(k)))
+                put[String](destProp, extraEnt.get[String](extra.propertyName).toLowerCase)
+                override val getTokens: List[NCToken] = (destEnt.tokens ++ extraEnt.tokens).sortBy(_.getIndex)
                 override val getRequestId: String = req.getRequestId
-                override val getId: String = mainEnt.getId
+                override val getId: String = destEnt.getId
 
-        val mainById = mainDataSeq.map(p => p.id -> p).toMap
-        val main = mutable.HashSet.empty ++ ents.filter(e => mainById.contains(e.getId))
-        val extra = ents.filter(_.getId == extraData.id)
+        val mainById = dests.map(p => p.elementId -> p).toMap
+        val descEnts = mutable.HashSet.empty ++ ents.filter(e => mainById.contains(e.getId))
+        val extraEnts = ents.filter(_.getId == extra.elementId)
 
-        if main.nonEmpty && extra.nonEmpty && main.size >= extra.size then
-            val used = (main ++ extra).toSet
+        if descEnts.nonEmpty && extraEnts.nonEmpty && descEnts.size >= extraEnts.size then
+            val used = (descEnts ++ extraEnts).toSet
             val main2Extra = mutable.HashMap.empty[NCEntity, NCEntity]
 
-            for (e <- extra)
-                val m = main.minBy(m => Math.abs(m.position - e.position))
-                main -= m
+            for (e <- extraEnts)
+                val m = descEnts.minBy(m => Math.abs(m.position - e.position))
+                descEnts -= m
                 main2Extra += m -> e
 
             val unrelated = ents.filter(e => !used.contains(e))
-            val artificial = for ((m, e) <- main2Extra) yield combine(m, mainById(m.getId).property, e)
-            val unused = main
+            val artificial = for ((m, e) <- main2Extra) yield map(m, mainById(m.getId).propertyName, e)
+            val unused = descEnts
 
             val res = (unrelated ++ artificial ++ unused).sortBy(_.tokens.head.getIndex)
 
-            def s(es: Iterable[NCEntity]) =
-                es.map(e => s"id=${e.getId}(${e.tokens.map(_.getIndex).mkString("[", ",", "]")})").mkString("{", ", ", "}")
-            logger.debug(s"Elements mapped [input=${s(ents)}, output=${s(res)}]")
+            logger.debug(s"Elements mapped [input=${str(ents)}, output=${str(res)}]")
 
             res
         else ents
\ No newline at end of file
diff --git a/nlpcraft-examples/pizzeria/src/main/java/org/apache/nlpcraft/examples/pizzeria/components/PizzeriaOrderValidator.scala b/nlpcraft-examples/pizzeria/src/main/java/org/apache/nlpcraft/examples/pizzeria/components/PizzeriaOrderValidator.scala
index 09f653dd..b0d2f8b3 100644
--- a/nlpcraft-examples/pizzeria/src/main/java/org/apache/nlpcraft/examples/pizzeria/components/PizzeriaOrderValidator.scala
+++ b/nlpcraft-examples/pizzeria/src/main/java/org/apache/nlpcraft/examples/pizzeria/components/PizzeriaOrderValidator.scala
@@ -31,7 +31,7 @@ class PizzeriaOrderValidator extends NCEntityValidator:
         val cntNums = count("stanford:number")
         val cntSize = count("ord:pizza:size")
 
-        // Single size  - it is order specification request.
+        // Single size - it is order specification request.
         if cntSize != 1 && cntSize > cntPizza then
             throw new NCRejection("There are unrecognized pizza sizes in the request, maybe because some misprints.")
             
diff --git a/nlpcraft/src/main/scala/org/apache/nlpcraft/NCPropertyMapAdapter.scala b/nlpcraft/src/main/scala/org/apache/nlpcraft/NCPropertyMapAdapter.scala
index 1c8c8e86..44430183 100644
--- a/nlpcraft/src/main/scala/org/apache/nlpcraft/NCPropertyMapAdapter.scala
+++ b/nlpcraft/src/main/scala/org/apache/nlpcraft/NCPropertyMapAdapter.scala
@@ -44,6 +44,6 @@ class NCPropertyMapAdapter extends NCPropertyMap:
 
     override def remove(key: String, obj: Any): Boolean = map.remove(key, obj)
 
-    override def keysSet = map.keys().asScala.toSet
+    override def keysSet: Set[String] = map.keys().asScala.toSet
 
     override def clear(): Unit = map.clear()
\ No newline at end of file