Repository: XiaoMi/MiNLP (language: python)

Issue:
类似于 “第一集” 支持 第一(季|部|期|集|番|期|幕|卷|幕|卷|册|辑|弹|场) 



Discussion / hints:

Fix patch:
diff --git a/duckling-fork-chinese/core/src/main/resources/reference.conf b/duckling-fork-chinese/core/src/main/resources/reference.conf
index fb4865fd..15d18053 100644
--- a/duckling-fork-chinese/core/src/main/resources/reference.conf
+++ b/duckling-fork-chinese/core/src/main/resources/reference.conf
@@ -13,6 +13,7 @@ duckling.core {
   model.bayes {
     file = "naive_bayes.kryo"
     dims = [
+      Act,
       Age,
       Area,
       BloodType,
diff --git a/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/EnumeratedDimension.java b/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/EnumeratedDimension.java
index fc81f3d5..c7946609 100644
--- a/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/EnumeratedDimension.java
+++ b/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/EnumeratedDimension.java
@@ -18,6 +18,7 @@
 
 
 public enum EnumeratedDimension {
+    Act(com.xiaomi.duckling.dimension.act.Act$.MODULE$),
     Age(com.xiaomi.duckling.dimension.age.Age$.MODULE$),
     Area(com.xiaomi.duckling.dimension.quantity.area.Area$.MODULE$),
     BloodType(com.xiaomi.duckling.dimension.bloodtype.BloodType$.MODULE$),
diff --git a/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/FullDimensions.scala b/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/FullDimensions.scala
index 435ac5dd..b71551b5 100644
--- a/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/FullDimensions.scala
+++ b/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/FullDimensions.scala
@@ -16,6 +16,7 @@
 
 package com.xiaomi.duckling.dimension
 
+import com.xiaomi.duckling.dimension.act.Act
 import com.xiaomi.duckling.dimension.age.Age
 import com.xiaomi.duckling.dimension.bloodtype.BloodType
 import com.xiaomi.duckling.dimension.constellation.Constellation
@@ -48,6 +49,7 @@ import com.xiaomi.duckling.dimension.url.DuckURL
 
 class FullDimensions extends Dimensions {
   override val dims: List[Dimension] = List(
+    Act,
     Age,
     Area,
     BloodType,
diff --git a/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/act/Act.scala b/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/act/Act.scala
new file mode 100644
index 00000000..8afa5637
--- /dev/null
+++ b/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/act/Act.scala
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2020, Xiaomi and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.xiaomi.duckling.dimension.act
+
+import com.xiaomi.duckling.dimension.Dimension
+import com.xiaomi.duckling.dimension.ordinal.Ordinal
+
+case object Act extends Dimension with Rules {
+  override val name: String = "Act"
+  override val dimDependents: List[Dimension] = List(Ordinal)
+}
diff --git a/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/act/Rules.scala b/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/act/Rules.scala
new file mode 100644
index 00000000..d1fed880
--- /dev/null
+++ b/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/act/Rules.scala
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020, Xiaomi and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.xiaomi.duckling.dimension.act
+
+import com.xiaomi.duckling.Types._
+import com.xiaomi.duckling.dimension.DimRules
+import com.xiaomi.duckling.dimension.implicits._
+import com.xiaomi.duckling.dimension.numeral.Predicates._
+import com.xiaomi.duckling.dimension.ordinal.notEndsWithGe
+
+trait Rules extends DimRules {
+  private val actPattern = "(番|幕|弹|场)".regex
+
+  val numeratedAct = Rule(
+    name = "<ordinal> act",
+    pattern = List(notEndsWithGe.predicate, actPattern),
+    prod = tokens {
+      case t1 :: _ =>
+        for (i <- getIntValue(t1)) yield act(i.toInt)
+    }
+  )
+
+  val integerAct = Rule(
+    name = "<positive integer> act",
+    pattern = List(isDigits.predicate, actPattern),
+    prod = tokens {
+      case t1 :: _ =>
+        for (i <- getIntValue(t1)) yield act(i.toInt, true)
+    }
+  )
+
+  val reverseAct = Rule(
+    name = "reverse act",
+    pattern = List(reversePattern, isNatural.predicate, actPattern),
+    prod = tokens {
+      case _ :: t1 :: _ =>
+        for (i <- getIntValue(t1)) yield act(-i.toInt)
+    }
+  )
+}
diff --git a/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/act/package.scala b/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/act/package.scala
new file mode 100644
index 00000000..6afe968f
--- /dev/null
+++ b/duckling-fork-chinese/core/src/main/scala/com/xiaomi/duckling/dimension/act/package.scala
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2020, Xiaomi and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.xiaomi.duckling.dimension
+
+import com.xiaomi.duckling.Types._
+import com.xiaomi.duckling.dimension.implicits._
+import com.xiaomi.duckling.dimension.quantity.QuantityData
+
+package object act {
+  val reversePattern = "(最后|最新|倒数第)".regex
+
+  def act(i: Int, isLatent: Boolean = false): Token = Token(Act, QuantityData(i, "场", "场", isLatent))
+}
diff --git "a/duckling-fork-chinese/doc/\346\224\257\346\214\201\347\232\204Dimension.md" "b/duckling-fork-chinese/doc/\346\224\257\346\214\201\347\232\204Dimension.md"
index aed27e8b..56766826 100644
--- "a/duckling-fork-chinese/doc/\346\224\257\346\214\201\347\232\204Dimension.md"
+++ "b/duckling-fork-chinese/doc/\346\224\257\346\214\201\347\232\204Dimension.md"
@@ -92,9 +92,14 @@ case class OpenIntervalValue(start: Double, direction: IntervalDirection) extend
 |          | 一万两千零3十四  | {<br/>  "n": 12034<br/>}   |
 
 
+## Act
 
-## Age
+| 示例           | 解析结果示例                                                 |
+| -------------- | ------------------------------------------------------------ |
+| 倒数第一幕     | {<br/>  "v": -1,<br/>  "unit": "场",<br/>  "dim": "场"<br/>} |
+| 第一百一十一场 | {<br/>  "v": 111,<br/>  "unit": "场",<br/>  "dim": "场"<br/>} |
 
+## Age
 
 
 | 示例       | 解析结果示例（示意）                                     |
diff --git a/duckling-fork-chinese/learning/src/main/scala/com/xiaomi/duckling/dimension/act/Examples.scala b/duckling-fork-chinese/learning/src/main/scala/com/xiaomi/duckling/dimension/act/Examples.scala
new file mode 100644
index 00000000..e17a10f6
--- /dev/null
+++ b/duckling-fork-chinese/learning/src/main/scala/com/xiaomi/duckling/dimension/act/Examples.scala
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2020, Xiaomi and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.xiaomi.duckling.dimension.act
+
+import com.xiaomi.duckling.Types.ResolvedValue
+import com.xiaomi.duckling.dimension.quantity.QuantityValue
+import com.xiaomi.duckling.dimension.{DimExamples, Dimension}
+
+object Examples extends DimExamples {
+  override def pairs: List[(ResolvedValue, List[String])] = List(
+    (QuantityValue(-1, "场", "场"), List("倒数第一场", "最后一番", "最新一场")),
+    (QuantityValue(3, "场", "场"), List("第三场", "第三番", "3场", "三弹"))
+  )
+
+  override val dimension: Dimension = Act
+}
diff --git a/duckling-fork-chinese/server/src/main/resources/static/js/duckling-extract.js b/duckling-fork-chinese/server/src/main/resources/static/js/duckling-extract.js
index 58b29dff..77f8eb5a 100644
--- a/duckling-fork-chinese/server/src/main/resources/static/js/duckling-extract.js
+++ b/duckling-fork-chinese/server/src/main/resources/static/js/duckling-extract.js
@@ -7,6 +7,7 @@ new Vue({
                 dims:[]
             },
             dimList: [
+                {value: 'Act', label: 'Act'},
                 {value: 'Age', label: '年龄'},
                 {value: 'Area', label: '面积'},
                 {value: 'BloodType', label: '血型'},


Test patch:
diff --git a/duckling-fork-chinese/core/src/test/scala/com/xiaomi/duckling/dimension/act/ActTest.scala b/duckling-fork-chinese/core/src/test/scala/com/xiaomi/duckling/dimension/act/ActTest.scala
new file mode 100644
index 00000000..e1cd6f02
--- /dev/null
+++ b/duckling-fork-chinese/core/src/test/scala/com/xiaomi/duckling/dimension/act/ActTest.scala
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2020, Xiaomi and/or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.xiaomi.duckling.dimension.act
+
+import com.xiaomi.duckling.Api.analyze
+import com.xiaomi.duckling.UnitSpec
+import com.xiaomi.duckling.dimension.quantity.QuantityValue
+import com.xiaomi.duckling.ranking.Testing.{testContext, testOptions}
+
+class ActTest extends UnitSpec {
+
+	val options = testOptions.copy(targets = Set(Act), full = false)
+
+	describe("SeasonSchemaTest") {
+		val testCases = List(
+			("倒数第三场", Some("-3.0")),
+			("第三幕", Some("3.0")),
+			("第一百一十一弹", Some("111.0")),
+			("第九弹", Some("9.0")),
+			("第三番", Some("3.0")),
+			("第十九番", Some("19.0")),
+			("最新一幕", Some("-1.0"))
+		)
+
+		it("schema eq") {
+			testCases.foreach{
+				case (query, target) =>
+					val answers = analyze(query, testContext, options)
+					answers(0).token.value match {
+						case data: QuantityValue  => data.schema shouldBe target
+						case _ => true shouldBe (false)
+					}
+			}
+		}
+	}
+
+	describe("SeasonLatentTest") {
+		val testCases = List(
+			("倒数第三场", false),
+			("十九幕", true),
+			("第三幕", false),
+			("倒第三弹", false),
+			("第一百一十一幕", false),
+			("九番", true),
+			("一百一十一场", true)
+		)
+
+		it("Latent eq") {
+			testCases.foreach{
+				case (query, target) =>
+					val answers = analyze(query, testContext, options)
+					answers(0).token.isLatent shouldBe (target)
+			}
+		}
+	}
+}