Repository: fniessink/toisto (language: python)

Issue:
Add support for past perfect



Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cadd282f..2c3c7e24 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
 ### Added
 
 - Print the progress report dramatically (character by character) to make it stand out more.
+- Add support for the past perfect tense. Closes [#660](https://github.com/fniessink/toisto/issues/660).
 - Add word order quizzes for sentences with five or more words. Closes [#757](https://github.com/fniessink/toisto/issues/757).
 
 ## 0.22.0 - 2024-07-29
diff --git a/docs/software.md b/docs/software.md
index 7aa3e9cb..59b6921e 100644
--- a/docs/software.md
+++ b/docs/software.md
@@ -517,7 +517,7 @@ When concepts are verbs, infinitives can be specified as follows:
 
 ### Tenses
 
-When concepts are verbs, the present tense, past tense, and present perfect tense can be specified as follows:
+When concepts are verbs, the present tense, past tense, present perfect, and past perfect tense can be specified as follows:
 
 ```json
 {
@@ -552,6 +552,16 @@ When concepts are verbs, the present tense, past tense, and present perfect tens
                 "second person": "..."
             },
             "plural": "..."
+        },
+        "past perfect tense": {
+            "singular": {
+                "first person": {
+                    "en": "I had been",
+                    "fi": "minä olin ollut",
+                },
+                "second person": "..."
+            },
+            "plural": "..."
         }
     }
 }
diff --git a/src/concepts/verbs/be.json b/src/concepts/verbs/be.json
index c2c82034..1f4e7c13 100644
--- a/src/concepts/verbs/be.json
+++ b/src/concepts/verbs/be.json
@@ -150,6 +150,48 @@
                             "nl": "zij zijn geweest"
                         }
                     }
+                },
+                "past perfect tense": {
+                    "singular": {
+                        "first person": {
+                            "en": "I had been",
+                            "fi": "minä olin ollut",
+                            "nl": "ik was geweest"
+                        },
+                        "second person": {
+                            "en": "you had been",
+                            "fi": "sinä olit ollut",
+                            "nl": "jij was geweest"
+                        },
+                        "third person": {
+                            "fi": "hän oli ollut",
+                            "female": {
+                                "en": "she had been",
+                                "nl": "zij was geweest"
+                            },
+                            "male": {
+                                "en": "he had been",
+                                "nl": "hij was geweest"
+                            }
+                        }
+                    },
+                    "plural": {
+                        "first person": {
+                            "en": "we had been",
+                            "fi": "me olimme olleet",
+                            "nl": "wij waren geweest"
+                        },
+                        "second person": {
+                            "en": "you had been",
+                            "fi": "te olitte olleet",
+                            "nl": "jullie waren geweest"
+                        },
+                        "third person": {
+                            "en": "they had been",
+                            "fi": "he olivat olleet",
+                            "nl": "zij waren geweest"
+                        }
+                    }
                 }
             },
             "negative": {
@@ -341,6 +383,69 @@
                             ]
                         }
                     }
+                },
+                "past perfect tense": {
+                    "singular": {
+                        "first person": {
+                            "en": "I had not been",
+                            "fi": "minä en ollut ollut",
+                            "nl": [
+                                "ik was niet geweest",
+                                "ik was geen geweest"
+                            ]
+                        },
+                        "second person": {
+                            "en": "you had not been",
+                            "fi": "sinä et ollut ollut",
+                            "nl": [
+                                "jij was niet geweest",
+                                "jij was geen geweest"
+                            ]
+                        },
+                        "third person": {
+                            "fi": "hän ei ollut ollut",
+                            "female": {
+                                "en": "she had not been",
+                                "nl": [
+                                    "zij was niet geweest",
+                                    "zij was geen geweest"
+                                ]
+                            },
+                            "male": {
+                                "en": "he had not been",
+                                "nl": [
+                                    "hij was niet geweest",
+                                    "hij was geen geweest"
+                                ]
+                            }
+                        }
+                    },
+                    "plural": {
+                        "first person": {
+                            "en": "we had not been",
+                            "fi": "me emme olleet olleet",
+                            "nl": [
+                                "wij waren niet geweest",
+                                "wij waren geen geweest"
+                            ]
+                        },
+                        "second person": {
+                            "en": "you had not been",
+                            "fi": "te ette olleet olleet",
+                            "nl": [
+                                "jullie waren niet geweest",
+                                "jullie waren geen geweest"
+                            ]
+                        },
+                        "third person": {
+                            "en": "they had not been",
+                            "fi": "he eivät olleet olleet",
+                            "nl": [
+                                "zij waren niet geweest",
+                                "zij waren geen geweest"
+                            ]
+                        }
+                    }
                 }
             }
         },
@@ -471,6 +576,48 @@
                             "nl": "zijn zij geweest?"
                         }
                     }
+                },
+                "past perfect tense": {
+                    "singular": {
+                        "first person": {
+                            "en": "had I been?",
+                            "fi": "olinko minä ollut?",
+                            "nl": "was ik geweest?"
+                        },
+                        "second person": {
+                            "en": "had you been?",
+                            "fi": "olitko sinä ollut?",
+                            "nl": "was jij geweest?"
+                        },
+                        "third person": {
+                            "fi": "oliko hän ollut?",
+                            "female": {
+                                "en": "had she been?",
+                                "nl": "was zij geweest?"
+                            },
+                            "male": {
+                                "en": "had he been?",
+                                "nl": "was hij geweest?"
+                            }
+                        }
+                    },
+                    "plural": {
+                        "first person": {
+                            "en": "had we been?",
+                            "fi": "olimmeko me olleet?",
+                            "nl": "waren wij geweest?"
+                        },
+                        "second person": {
+                            "en": "had you been?",
+                            "fi": "olitteko te olleet?",
+                            "nl": "waren jullie geweest?"
+                        },
+                        "third person": {
+                            "en": "had they been?",
+                            "fi": "olivatko he olleet?",
+                            "nl": "waren zij geweest?"
+                        }
+                    }
                 }
             },
             "negative": {
@@ -611,7 +758,7 @@
                             ]
                         },
                         "second person": {
-                            "en": "are you not?",
+                            "en": "have you not been?",
                             "fi": "etkö sinä ole ollut?",
                             "nl": [
                                 "ben jij niet geweest?",
@@ -662,6 +809,69 @@
                             ]
                         }
                     }
+                },
+                "past perfect tense": {
+                    "singular": {
+                        "first person": {
+                            "en": "had I not been?",
+                            "fi": "enkö minä ollut ollut?",
+                            "nl": [
+                                "was ik niet geweest?",
+                                "was ik geen geweest?"
+                            ]
+                        },
+                        "second person": {
+                            "en": "had you not been?",
+                            "fi": "etkö sinä ollut ollut?",
+                            "nl": [
+                                "was jij niet geweest?",
+                                "was jij geen geweest?"
+                            ]
+                        },
+                        "third person": {
+                            "fi": "eikö hän ollut ollut?",
+                            "female": {
+                                "en": "had she not been?",
+                                "nl": [
+                                    "was zij niet geweest?",
+                                    "was zij geen geweest?"
+                                ]
+                            },
+                            "male": {
+                                "en": "had he not been?",
+                                "nl": [
+                                    "was hij niet geweest?",
+                                    "was hij geen geweest?"
+                                ]
+                            }
+                        }
+                    },
+                    "plural": {
+                        "first person": {
+                            "en": "had we not been?",
+                            "fi": "emmekö me olleet olleet?",
+                            "nl": [
+                                "waren wij niet geweest?",
+                                "waren we geen geweest?"
+                            ]
+                        },
+                        "second person": {
+                            "en": "had you not been?",
+                            "fi": "ettekö te olleet olleet?",
+                            "nl": [
+                                "waren jullie niet geweest?",
+                                "waren jullie geen geweest?"
+                            ]
+                        },
+                        "third person": {
+                            "en": "had they not been?",
+                            "fi": "eivätkö he olleet olleet?",
+                            "nl": [
+                                "waren zij niet geweest?",
+                                "waren zij geen geweest?"
+                            ]
+                        }
+                    }
                 }
             }
         }
diff --git a/src/toisto/model/language/grammar.py b/src/toisto/model/language/grammar.py
index adb010f3..1c4b0fbd 100644
--- a/src/toisto/model/language/grammar.py
+++ b/src/toisto/model/language/grammar.py
@@ -7,7 +7,7 @@
 GrammaticalPerson = Literal["first person", "second person", "third person"]
 DegreeOfComparison = Literal["positive degree", "comparative degree", "superlative degree"]
 Diminutive = Literal["root", "diminutive"]
-Tense = Literal["present tense", "past tense", "present perfect tense"]
+Tense = Literal["present tense", "past tense", "present perfect tense", "past perfect tense"]
 GrammaticalMood = Literal["declarative", "interrogative", "imperative"]
 GrammaticalPolarity = Literal["affirmative", "negative"]
 Number = Literal["cardinal", "ordinal"]
diff --git a/src/toisto/model/quiz/quiz_type.py b/src/toisto/model/quiz/quiz_type.py
index 39e2c1f1..6afc21a5 100644
--- a/src/toisto/model/quiz/quiz_type.py
+++ b/src/toisto/model/quiz/quiz_type.py
@@ -192,6 +192,7 @@ def question_notes_applicable(self, question: Label, answer: Label) -> bool:  #
 PRESENT_TENSE = GrammaticalQuizType("present tense")
 PAST_TENSE = GrammaticalQuizType("past tense")
 PRESENT_PERFECT_TENSE = GrammaticalQuizType("present perfect tense")
+PAST_PERFECT_TENSE = GrammaticalQuizType("past perfect tense")
 INFINITIVE = GrammaticalQuizType("infinitive")
 POSITIVE_DEGREE = GrammaticalQuizType("positive degree")
 COMPARATIVE_DEGREE = GrammaticalQuizType("comparative degree")


Test patch:
diff --git a/tests/toisto/model/quiz/test_quiz.py b/tests/toisto/model/quiz/test_quiz.py
index 714ef54d..9b8752f8 100644
--- a/tests/toisto/model/quiz/test_quiz.py
+++ b/tests/toisto/model/quiz/test_quiz.py
@@ -24,6 +24,7 @@
     NEUTER,
     ORDER,
     ORDINAL,
+    PAST_PERFECT_TENSE,
     PAST_TENSE,
     PLURAL,
     POSITIVE_DEGREE,
@@ -220,6 +221,7 @@ def test_instructions(self):
             PRESENT_TENSE: "Give the [underline]present tense[/underline] in Finnish",
             PAST_TENSE: "Give the [underline]past tense[/underline] in Finnish",
             PRESENT_PERFECT_TENSE: "Give the [underline]present perfect tense[/underline] in Finnish",
+            PAST_PERFECT_TENSE: "Give the [underline]past perfect tense[/underline] in Finnish",
             DECLARATIVE: "Give the [underline]declarative[/underline] in Finnish",
             INTERROGATIVE: "Give the [underline]interrogative[/underline] in Finnish",
             IMPERATIVE: "Give the [underline]imperative[/underline] in Finnish",
diff --git a/tests/toisto/model/quiz/test_quiz_factory.py b/tests/toisto/model/quiz/test_quiz_factory.py
index 9223d59f..73e69911 100644
--- a/tests/toisto/model/quiz/test_quiz_factory.py
+++ b/tests/toisto/model/quiz/test_quiz_factory.py
@@ -27,6 +27,7 @@
     NEUTER,
     ORDER,
     ORDINAL,
+    PAST_PERFECT_TENSE,
     PAST_TENSE,
     PLURAL,
     POSITIVE_DEGREE,
@@ -77,6 +78,11 @@ def create_verb_with_tense_and_person(self, *tense: Tense) -> Concept:
                 "singular": dict(en="I have eaten", nl="ik heb gegeten"),
                 "plural": dict(en="we have eaten", nl="wij hebben gegeten"),
             }
+        if "past perfect tense" in tense:
+            concept_dict["past perfect tense"] = {
+                "singular": dict(en="I had eaten", nl="ik had gegeten"),
+                "plural": dict(en="we had eaten", nl="wij hadden gegeten"),
+            }
         return self.create_concept("to eat", concept_dict)
 
     def create_verb_with_infinitive_and_person(self) -> Concept:
@@ -1040,14 +1046,23 @@ def test_present_and_past_tense_nested_with_grammatical_person(self):
             create_quizzes(NL_EN, concept),
         )
 
-    def test_present_past_and_perfect_tense_nested_with_grammatical_person(self):
-        """Test that quizzes can be generated for present, past, and perfect tense nested with grammatical person."""
+    def test_all_tenses_nested_with_grammatical_person(self):
+        """Test that quizzes can be generated for all tenses nested with grammatical person."""
         self.language_pair = NL_EN
-        concept = self.create_verb_with_tense_and_person("present tense", "past tense", "present perfect tense")
-        present, past, perfect = concept.constituents
-        present_singular, present_plural, past_singular, past_plural, perfect_singular, perfect_plural = (
-            concept.leaf_concepts(NL)
+        concept = self.create_verb_with_tense_and_person(
+            "present tense", "past tense", "present perfect tense", "past perfect tense"
         )
+        present, past, perfect, past_perfect = concept.constituents
+        (
+            present_singular,
+            present_plural,
+            past_singular,
+            past_plural,
+            perfect_singular,
+            perfect_plural,
+            past_perfect_singular,
+            past_perfect_plural,
+        ) = concept.leaf_concepts(NL)
         self.assertSetEqual(
             {
                 self.create_quiz(present_singular, "ik eet", ["I eat"], READ),
@@ -1080,18 +1095,40 @@ def test_present_past_and_perfect_tense_nested_with_grammatical_person(self):
                 self.create_quiz(perfect_plural, "we have eaten", ["wij hebben gegeten"], WRITE),
                 self.create_quiz(perfect, "ik heb gegeten", ["wij hebben gegeten"], PLURAL),
                 self.create_quiz(perfect, "wij hebben gegeten", ["ik heb gegeten"], SINGULAR),
+                self.create_quiz(past_perfect_singular, "ik had gegeten", ["I had eaten"], READ),
+                self.create_quiz(past_perfect_singular, "ik had gegeten", ["ik had gegeten"], DICTATE),
+                self.create_quiz(past_perfect_singular, "ik had gegeten", ["I had eaten"], INTERPRET),
+                self.create_quiz(past_perfect_singular, "I had eaten", ["ik had gegeten"], WRITE),
+                self.create_quiz(past_perfect_plural, "wij hadden gegeten", ["we had eaten"], READ),
+                self.create_quiz(past_perfect_plural, "wij hadden gegeten", ["wij hadden gegeten"], DICTATE),
+                self.create_quiz(past_perfect_plural, "wij hadden gegeten", ["we had eaten"], INTERPRET),
+                self.create_quiz(past_perfect_plural, "we had eaten", ["wij hadden gegeten"], WRITE),
+                self.create_quiz(past_perfect, "ik had gegeten", ["wij hadden gegeten"], PLURAL),
+                self.create_quiz(past_perfect, "wij hadden gegeten", ["ik had gegeten"], SINGULAR),
                 self.create_quiz(concept, "ik eet", ["ik at"], PAST_TENSE),
+                self.create_quiz(concept, "ik eet", ["ik heb gegeten"], PRESENT_PERFECT_TENSE),
+                self.create_quiz(concept, "ik eet", ["ik had gegeten"], PAST_PERFECT_TENSE),
                 self.create_quiz(concept, "wij eten", ["wij aten"], PAST_TENSE),
+                self.create_quiz(concept, "wij eten", ["wij hebben gegeten"], PRESENT_PERFECT_TENSE),
+                self.create_quiz(concept, "wij eten", ["wij hadden gegeten"], PAST_PERFECT_TENSE),
                 self.create_quiz(concept, "ik at", ["ik eet"], PRESENT_TENSE),
+                self.create_quiz(concept, "ik at", ["ik heb gegeten"], PRESENT_PERFECT_TENSE),
+                self.create_quiz(concept, "ik at", ["ik had gegeten"], PAST_PERFECT_TENSE),
                 self.create_quiz(concept, "wij aten", ["wij eten"], PRESENT_TENSE),
+                self.create_quiz(concept, "wij aten", ["wij hebben gegeten"], PRESENT_PERFECT_TENSE),
+                self.create_quiz(concept, "wij aten", ["wij hadden gegeten"], PAST_PERFECT_TENSE),
                 self.create_quiz(concept, "ik heb gegeten", ["ik eet"], PRESENT_TENSE),
-                self.create_quiz(concept, "wij hebben gegeten", ["wij eten"], PRESENT_TENSE),
+                self.create_quiz(concept, "ik heb gegeten", ["ik had gegeten"], PAST_PERFECT_TENSE),
                 self.create_quiz(concept, "ik heb gegeten", ["ik at"], PAST_TENSE),
+                self.create_quiz(concept, "wij hebben gegeten", ["wij eten"], PRESENT_TENSE),
                 self.create_quiz(concept, "wij hebben gegeten", ["wij aten"], PAST_TENSE),
-                self.create_quiz(concept, "ik eet", ["ik heb gegeten"], PRESENT_PERFECT_TENSE),
-                self.create_quiz(concept, "wij eten", ["wij hebben gegeten"], PRESENT_PERFECT_TENSE),
-                self.create_quiz(concept, "ik at", ["ik heb gegeten"], PRESENT_PERFECT_TENSE),
-                self.create_quiz(concept, "wij aten", ["wij hebben gegeten"], PRESENT_PERFECT_TENSE),
+                self.create_quiz(concept, "wij hebben gegeten", ["wij hadden gegeten"], PAST_PERFECT_TENSE),
+                self.create_quiz(concept, "ik had gegeten", ["ik eet"], PRESENT_TENSE),
+                self.create_quiz(concept, "ik had gegeten", ["ik at"], PAST_TENSE),
+                self.create_quiz(concept, "ik had gegeten", ["ik heb gegeten"], PRESENT_PERFECT_TENSE),
+                self.create_quiz(concept, "wij hadden gegeten", ["wij eten"], PRESENT_TENSE),
+                self.create_quiz(concept, "wij hadden gegeten", ["wij aten"], PAST_TENSE),
+                self.create_quiz(concept, "wij hadden gegeten", ["wij hebben gegeten"], PRESENT_PERFECT_TENSE),
             },
             create_quizzes(NL_EN, concept),
         )