Repository: fniessink/toisto (language: python)

Issue:
Add imperative verb forms



Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9d1623d8..84de2f08 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,12 @@ All notable changes to Toisto will be documented in this file.
 
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## [Unreleased]
+
+### Added
+
+- Add support for the imperative grammatical mood. Closes [#618](https://github.com/fniessink/toisto/issues/618).
+
 ## 0.16.0 - 2024-03-30
 
 ### Fixed
diff --git a/docs/software.md b/docs/software.md
index f0ca011d..d9c0ba9e 100644
--- a/docs/software.md
+++ b/docs/software.md
@@ -516,9 +516,32 @@ When there are synonyms, they need to be in the same order in every degree. This
 }
 ```
 
-### Sentence forms
+### Grammatical mood
 
-When the concept file contains both the declarative and the interrogative form of a sentence, Toisto can generate quizzes to change one into the other. Sentence forms are specified as follows:
+When the concept file contains different grammatical moods, Toisto can generate quizzes to change one into another. Toisto supports the declarative, interrogative, and imperative mood.
+
+All three moods can be combined as follows:
+
+```json
+{
+    "you run": {
+        "declarative": {
+            "en": "You run.;singular",
+            "nl": "Jij rent.",
+        },
+        "interrogative": {
+            "en": "Do you run?",
+            "nl": "Ren jij?",
+        },
+        "imperative": {
+            "en": "Run!",
+            "nl": "Ren!",
+        }
+    }
+}
+```
+
+It is also possible to include just two of the three grammatical moods:
 
 ```json
 {
@@ -853,7 +876,7 @@ Toisto uses the concepts to generate quizzes. Currently, the following types of
 9. Change the gender of a concept.
 9. Provide the positive, comparative, or superlative degree of comparison, given an adjective in another degree.
 10. Change the tense of a concept between infinitive, present tense, and past tense.
-11. Change the sentence form from declarative to interrogative and vice versa.
+11. Change the grammatical mood of a concept between declarative, interrogative, and imperative mood.
 12. Change the polarity from affirmative to negative and vice versa.
 13. Change cardinal numbers into ordinal numbers and vice versa.
 14. Give the antonym.
diff --git a/src/concepts/verbs/run.json b/src/concepts/verbs/run.json
index d9026ebf..d4c1fbb5 100644
--- a/src/concepts/verbs/run.json
+++ b/src/concepts/verbs/run.json
@@ -5,87 +5,100 @@
             "fi": "juosta",
             "nl": "rennen"
         },
-        "present tense": {
-            "singular": {
-                "first person": {
-                    "en": "I run",
-                    "fi": "minä juoksen",
-                    "nl": "ik ren"
-                },
-                "second person": {
-                    "en": "you run;singular",
-                    "fi": "sinä juokset",
-                    "nl": "jij rent"
-                },
-                "third person": {
-                    "fi": "hän juoksee",
-                    "female": {
-                        "en": "she runs",
-                        "nl": "zij rent"
-                    },
-                    "male": {
-                        "en": "he runs",
-                        "nl": "hij rent"
+        "imperative": {
+            "present tense": {
+                "singular": {
+                    "second person": {
+                        "en": "Run!",
+                        "fi": "Juokse!",
+                        "nl": "Ren!"
                     }
                 }
-            },
-            "plural": {
-                "first person": {
-                    "en": "we run",
-                    "fi": "me juoksemme",
-                    "nl": "wij rennen"
-                },
-                "second person": {
-                    "en": "you run;plural",
-                    "fi": "te juoksette",
-                    "nl": "jullie rennen"
-                },
-                "third person": {
-                    "en": "they run",
-                    "fi": "he juoksevat",
-                    "nl": "zij rennen"
-                }
             }
         },
-        "past tense": {
-            "singular": {
-                "first person": {
-                    "en": "I ran",
-                    "fi": "minä juoksin",
-                    "nl": "ik rende"
-                },
-                "second person": {
-                    "en": "you ran;singular",
-                    "fi": "sinä juoksit",
-                    "nl": "jij rende"
+        "declarative": {
+            "present tense": {
+                "singular": {
+                    "first person": {
+                        "en": "I run",
+                        "fi": "minä juoksen",
+                        "nl": "ik ren"
+                    },
+                    "second person": {
+                        "en": "you run;singular",
+                        "fi": "sinä juokset",
+                        "nl": "jij rent"
+                    },
+                    "third person": {
+                        "fi": "hän juoksee",
+                        "female": {
+                            "en": "she runs",
+                            "nl": "zij rent"
+                        },
+                        "male": {
+                            "en": "he runs",
+                            "nl": "hij rent"
+                        }
+                    }
                 },
-                "third person": {
-                    "fi": "hän juoksi",
-                    "female": {
-                        "en": "she ran",
-                        "nl": "zij rende"
+                "plural": {
+                    "first person": {
+                        "en": "we run",
+                        "fi": "me juoksemme",
+                        "nl": "wij rennen"
+                    },
+                    "second person": {
+                        "en": "you run;plural",
+                        "fi": "te juoksette",
+                        "nl": "jullie rennen"
                     },
-                    "male": {
-                        "en": "he ran",
-                        "nl": "hij rende"
+                    "third person": {
+                        "en": "they run",
+                        "fi": "he juoksevat",
+                        "nl": "zij rennen"
                     }
                 }
             },
-            "plural": {
-                "first person": {
-                    "en": "we ran",
-                    "fi": "me juoksimme",
-                    "nl": "wij renden"
-                },
-                "second person": {
-                    "en": "you ran;plural",
-                    "fi": "te juoksitte",
-                    "nl": "jullie renden"
+            "past tense": {
+                "singular": {
+                    "first person": {
+                        "en": "I ran",
+                        "fi": "minä juoksin",
+                        "nl": "ik rende"
+                    },
+                    "second person": {
+                        "en": "you ran;singular",
+                        "fi": "sinä juoksit",
+                        "nl": "jij rende"
+                    },
+                    "third person": {
+                        "fi": "hän juoksi",
+                        "female": {
+                            "en": "she ran",
+                            "nl": "zij rende"
+                        },
+                        "male": {
+                            "en": "he ran",
+                            "nl": "hij rende"
+                        }
+                    }
                 },
-                "third person": {
-                    "en": "they ran",
-                    "fi": "he juoksivat",
-                    "nl": "zij renden"
+                "plural": {
+                    "first person": {
+                        "en": "we ran",
+                        "fi": "me juoksimme",
+                        "nl": "wij renden"
+                    },
+                    "second person": {
+                        "en": "you ran;plural",
+                        "fi": "te juoksitte",
+                        "nl": "jullie renden"
+                    },
+                    "third person": {
+                        "en": "they ran",
+                        "fi": "he juoksivat",
+                        "nl": "zij renden"
+                    }
                 }
             }
         }
diff --git a/src/toisto/model/language/grammar.py b/src/toisto/model/language/grammar.py
index 2e64235d..864c5cb2 100644
--- a/src/toisto/model/language/grammar.py
+++ b/src/toisto/model/language/grammar.py
@@ -8,7 +8,7 @@
 DegreeOfComparison = Literal["positive degree", "comparative degree", "superlative degree"]
 Diminutive = Literal["root", "diminutive"]
 Tense = Literal["present tense", "past tense"]
-SentenceForm = Literal["declarative", "interrogative"]
+GrammaticalMood = Literal["declarative", "interrogative", "imperative"]
 GrammaticalPolarity = Literal["affirmative", "negative"]
 Number = Literal["cardinal", "ordinal"]
 Abbreviation = Literal["abbreviation", "full form"]
@@ -19,7 +19,7 @@
     DegreeOfComparison,
     Diminutive,
     Tense,
-    SentenceForm,
+    GrammaticalMood,
     GrammaticalPolarity,
     Number,
     Abbreviation,
diff --git a/src/toisto/model/quiz/quiz.py b/src/toisto/model/quiz/quiz.py
index 9dbddee0..0418d0bf 100644
--- a/src/toisto/model/quiz/quiz.py
+++ b/src/toisto/model/quiz/quiz.py
@@ -36,6 +36,7 @@
     "give past tense",
     "make declarative",
     "make interrogative",
+    "make imperative",
     "affirm",
     "negate",
     "make cardinal",
@@ -62,6 +63,7 @@
     "past tense": "give past tense",
     "declarative": "make declarative",
     "interrogative": "make interrogative",
+    "imperative": "make imperative",
     "affirmative": "affirm",
     "negative": "negate",
     "cardinal": "make cardinal",


Test patch:
diff --git a/tests/toisto/model/quiz/test_quiz.py b/tests/toisto/model/quiz/test_quiz.py
index 1785d158..f88f17e4 100644
--- a/tests/toisto/model/quiz/test_quiz.py
+++ b/tests/toisto/model/quiz/test_quiz.py
@@ -100,6 +100,7 @@ def test_instructions(self):
             "Give the [underline]past tense[/underline] in",
             "Give the [underline]declarative[/underline] in",
             "Give the [underline]interrogative[/underline] in",
+            "Give the [underline]imperative[/underline] in",
             "Give the [underline]affirmative[/underline] in",
             "Give the [underline]negative[/underline] in",
             "Give the [underline]cardinal[/underline] in",
diff --git a/tests/toisto/model/quiz/test_quiz_factory.py b/tests/toisto/model/quiz/test_quiz_factory.py
index 2c9f56de..cfa2988e 100644
--- a/tests/toisto/model/quiz/test_quiz_factory.py
+++ b/tests/toisto/model/quiz/test_quiz_factory.py
@@ -1004,11 +1004,11 @@ def test_tense_nested_with_grammatical_person_and_infinitive(self):
         )
 
 
-class SentenceFormTest(ToistoTestCase):
-    """Unit tests for concepts with different sentence forms."""
+class GrammaticalMoodTest(ToistoTestCase):
+    """Unit tests for concepts with different grammatical moods."""
 
-    def test_declarative_and_interrogative_sentence_types(self):
-        """Test that quizzes can be generated for the declarative and interrogative sentence forms."""
+    def test_declarative_and_interrogative_moods(self):
+        """Test that quizzes can be generated for the declarative and interrogative moods."""
         concept = self.create_concept(
             "car",
             {
@@ -1030,7 +1030,7 @@ def test_declarative_and_interrogative_sentence_types(self):
                     interrogative, self.nl, self.nl, "Is de auto zwart?", ["Is de auto zwart?"], "dictate"
                 ),
                 self.create_quiz(
-                    interrogative, self.nl, self.en, "Is de auto zwart?", ["Is the cat black?"], "interpret"
+                    interrogative, self.nl, self.en, "Is de auto zwart?", ["Is the car black?"], "interpret"
                 ),
                 self.create_quiz(interrogative, self.en, self.nl, "Is the car black?", ["Is de auto zwart?"], "write"),
                 self.create_quiz(
@@ -1043,6 +1043,67 @@ def test_declarative_and_interrogative_sentence_types(self):
             create_quizzes(self.nl, self.en, concept),
         )
 
+    def test_declarative_and_imperative_moods(self):
+        """Test that quizzes can be generated for the declarative and imperative moods."""
+        concept = self.create_concept(
+            "you run",
+            {
+                "declarative": dict(en="You run.", nl="Jij rent."),
+                "imperative": dict(en="Run!", nl="Ren!"),
+            },
+        )
+        declarative, imperative = concept.constituents
+        self.assertSetEqual(
+            {
+                self.create_quiz(declarative, self.nl, self.en, "Jij rent.", ["You run."], "read"),
+                self.create_quiz(declarative, self.nl, self.nl, "Jij rent.", ["Jij rent."], "dictate"),
+                self.create_quiz(declarative, self.nl, self.en, "Jij rent.", ["You run."], "interpret"),
+                self.create_quiz(declarative, self.en, self.nl, "You run.", ["Jij rent."], "write"),
+                self.create_quiz(imperative, self.nl, self.en, "Ren!", ["Run!"], "read"),
+                self.create_quiz(imperative, self.nl, self.nl, "Ren!", ["Ren!"], "dictate"),
+                self.create_quiz(imperative, self.nl, self.en, "Ren!", ["Run!"], "interpret"),
+                self.create_quiz(imperative, self.en, self.nl, "Run!", ["Ren!"], "write"),
+                self.create_quiz(concept, self.nl, self.nl, "Jij rent.", ["Ren!"], "make imperative"),
+                self.create_quiz(concept, self.nl, self.nl, "Ren!", ["Jij rent."], "make declarative"),
+            },
+            create_quizzes(self.nl, self.en, concept),
+        )
+
+    def test_declarative_interrogative_and_imperative_moods(self):
+        """Test that quizzes can be generated for the declarative, interrogative, and imperative moods."""
+        concept = self.create_concept(
+            "you run",
+            {
+                "declarative": dict(en="You run.", nl="Jij rent."),
+                "interrogative": dict(en="Do you run?", nl="Ren jij?"),
+                "imperative": dict(en="Run!", nl="Ren!"),
+            },
+        )
+        declarative, interrogative, imperative = concept.constituents
+        self.assertSetEqual(
+            {
+                self.create_quiz(declarative, self.nl, self.en, "Jij rent.", ["You run."], "read"),
+                self.create_quiz(declarative, self.nl, self.nl, "Jij rent.", ["Jij rent."], "dictate"),
+                self.create_quiz(declarative, self.nl, self.en, "Jij rent.", ["You run."], "interpret"),
+                self.create_quiz(declarative, self.en, self.nl, "You run.", ["Jij rent."], "write"),
+                self.create_quiz(interrogative, self.nl, self.en, "Ren jij?", ["Ren jij?"], "read"),
+                self.create_quiz(interrogative, self.nl, self.nl, "Ren jij?", ["Ren jij?"], "dictate"),
+                self.create_quiz(interrogative, self.nl, self.en, "Ren jij?", ["Do you run?"], "interpret"),
+                self.create_quiz(interrogative, self.en, self.nl, "Do you run?", ["Ren jij?"], "write"),
+                self.create_quiz(imperative, self.nl, self.en, "Ren!", ["Run!"], "read"),
+                self.create_quiz(imperative, self.nl, self.nl, "Ren!", ["Ren!"], "dictate"),
+                self.create_quiz(imperative, self.nl, self.en, "Ren!", ["Run!"], "interpret"),
+                self.create_quiz(imperative, self.en, self.nl, "Run!", ["Ren!"], "write"),
+                self.create_quiz(concept, self.nl, self.nl, "Jij rent.", ["Ren!"], "make imperative"),
+                self.create_quiz(concept, self.nl, self.nl, "Jij rent.", ["Ren jij?"], "make interrogative"),
+                self.create_quiz(concept, self.nl, self.nl, "Ren!", ["Jij rent."], "make declarative"),
+                self.create_quiz(concept, self.nl, self.nl, "Ren!", ["Ren jij?"], "make interrogative"),
+                self.create_quiz(concept, self.nl, self.nl, "Ren jij?", ["Ren!"], "make imperative"),
+                self.create_quiz(concept, self.nl, self.nl, "Ren jij?", ["Jij rent."], "make declarative"),
+            },
+            create_quizzes(self.nl, self.en, concept),
+        )
+
 
 class GrammaticalPolarityTest(ToistoTestCase):
     """Unit tests for concepts with different grammatical polarities."""