def test_two_mem_files_sharing_same_logs(self):
     filename = os.path.join(os.getcwd(), "tests", "files", "basedir_2_mem",
                             "deck1.mem")
     self.mem_importer().do_import(filename)
     assert self.database().con.execute(\
         "select count() from log where event_type=?",
         (EventTypes.REPETITION, )).fetchone()[0] == 1
     filename = os.path.join(os.getcwd(), "tests", "files", "basedir_2_mem",
                             "deck2.mem")
     self.mem_importer().do_import(filename)
     assert self.database().con.execute(\
         "select count() from log where event_type=?",
         (EventTypes.REPETITION, )).fetchone()[0] == 3
     assert self.database().con.execute(\
         "select count() from log where event_type=?",
         (EventTypes.ADDED_CARD, )).fetchone()[0] == 2
     card = self.database().card("4c8fff73", is_id_internal=False)
     assert self.database().average_thinking_time(card) == 1.5
     assert self.database().total_thinking_time(card) == 3.0
     assert self.database().card_count_for_grade(0, active_only=True) == 2
     tag = self.database().get_or_create_tag_with_name("666")
     assert self.database().card_count_for_grade_and_tag(
         0, tag, active_only=True) == 0
     from mnemosyne.libmnemosyne.statistics_pages.grades import Grades
     page = Grades(component_manager=self.mnemosyne.component_manager)
     from mnemosyne.libmnemosyne.tag_tree import TagTree
     self.tag_tree = TagTree(self.mnemosyne.component_manager,
                             count_cards=False)
     self.nodes = self.tag_tree.nodes()
     for index, node in enumerate(self.nodes):
         if node == "666":
             page.prepare_statistics(index)
             assert page.y == [0, 0, 0, 0, 0, 0, 0]
     page.prepare_statistics(-1)
     assert page.y == [0, 2, 0, 0, 0, 0, 0]
Ejemplo n.º 2
0
class Grades(PlotStatisticsPage):

    name = _("Grades")

    ALL_CARDS = -2
    ACTIVE_CARDS = -1

    def __init__(self, **kwds):
        super().__init__(**kwds)
        self.tag_tree = TagTree(self.component_manager, count_cards=False)
        self.nodes = self.tag_tree.nodes()
        self.variants = [(self.ALL_CARDS, _("All cards")),
                         (self.ACTIVE_CARDS, _("Active cards only"))]
        for index, node in enumerate(self.nodes):
            if node == "__UNTAGGED__":
                node = _("Untagged")
            self.variants.append((index, node))

    def prepare_statistics(self, variant):
        self.x = list(range(-1, 6))
        if variant == self.ALL_CARDS:
            self.y = [self.database().card_count_for_grade \
                (grade, active_only=False) for grade in self.x]
        elif variant == self.ACTIVE_CARDS:
            self.y = [self.database().card_count_for_grade \
                (grade, active_only=True) for grade in self.x]
        else:
            self.y = []
            for grade in self.x:
                self.y.append(0)
                for tag in self.tag_tree.tags_in_subtree(self.nodes[variant]):
                    self.y[-1] += self.database().card_count_for_grade_and_tag \
                        (grade, tag, active_only=False)
Ejemplo n.º 3
0
class Easiness(PlotStatisticsPage):

    name = _("Easiness")

    ALL_CARDS = -2
    ACTIVE_CARDS = -1

    def __init__(self, **kwds):
        super().__init__(**kwds)
        self.tag_tree = TagTree(self.component_manager, count_cards=False)
        self.nodes = self.tag_tree.nodes()
        self.variants = [(self.ALL_CARDS, _("All cards")),
                         (self.ACTIVE_CARDS, _("Active cards only"))]
        for index, node in enumerate(self.nodes):
            if node == "__UNTAGGED__":
                node = _("Untagged")
            self.variants.append((index, node))

    def prepare_statistics(self, variant):
        if variant == self.ALL_CARDS:
            self.data = self.database().easinesses(active_only=False)
        elif variant == self.ACTIVE_CARDS:
            self.data = self.database().easinesses(active_only=True)
        else:
            self.data = []
            for tag in self.tag_tree.tags_in_subtree(self.nodes[variant]):
                self.data.extend(self.database().easinesses_for_tag\
                                 (tag, active_only=False))
Ejemplo n.º 4
0
    def rebuild(self):
        """To be called when external events invalidate the tag tree,
        e.g. due to edits in the card browser widget.

        """

        self.save_criterion()
        self.store_tree_state()
        self.tag_tree = TagTree(self.component_manager)
        self.restore_criterion()
Ejemplo n.º 5
0
 def __init__(self, **kwds):
     super().__init__(**kwds)
     self.tag_tree = TagTree(self.component_manager, count_cards=False)
     self.nodes = self.tag_tree.nodes()
     self.variants = [(self.ALL_CARDS, _("All cards")),
                      (self.ACTIVE_CARDS, _("Active cards only"))]
     for index, node in enumerate(self.nodes):
         if node == "__UNTAGGED__":
             node = _("Untagged")
         self.variants.append((index, node))
Ejemplo n.º 6
0
    def test_delete_2(self):
        card_type = self.card_type_with_id("1")
        fact_data = {"f": "question4", "b": "answer4"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["a::b"])[0]
        fact_data = {"f": "question", "b": "answer"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["a"])[0]
        fact_data = {"f": "question2", "b": "answer2"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["Z"])[0]
        fact_data = {"f": "question3", "b": "answer3"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["a::c"])[0]
        fact_data = {"f": "question5", "b": "answer5"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["b::c::d",
                                                             "b"])[0]
        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)

        assert self.tree.card_count_for_node["__ALL__"] == 5
        assert self.tree.card_count_for_node["a"] == 3
        assert self.tree.card_count_for_node["Z"] == 1
        assert self.tree.card_count_for_node["a::b"] == 1
        assert self.tree.card_count_for_node["a::c"] == 1
        assert self.tree.card_count_for_node["b"] == 1
        assert self.tree.card_count_for_node["b::c"] == 1
        assert self.tree.card_count_for_node["b::c::d"] == 1

        self.tree.delete_subtree("b::c")
        card = self.database().card(card._id, is_id_internal=True)
        assert card.tag_string() == ""
        self.database().con.execute("select tags from cards where _id=?",
                                    (card._id, )).fetchone()[0] == "b"

        assert self.tree.card_count_for_node["__ALL__"] == 5
        assert self.tree.card_count_for_node["a"] == 3
        assert self.tree.card_count_for_node["Z"] == 1
        assert self.tree.card_count_for_node["a::b"] == 1
        assert self.tree.card_count_for_node["a::c"] == 1
        assert "b" not in self.tree.card_count_for_node
        assert "__UNTAGGED__" in self.tree.card_count_for_node
        assert "b::c" not in self.tree.card_count_for_node
        assert "b::c::d" not in self.tree.card_count_for_node
Ejemplo n.º 7
0
 def display(self, criterion=None):
     # Create criterion if needed.
     if criterion is None:
         criterion = DefaultCriterion(self.component_manager)
         for tag in self.database().tags():
             criterion._tag_ids_active.add(tag._id)
     # Create tree.
     self.tag_tree = TagTree(self.component_manager)
     self.tree_wdgt.clear()
     self.node_items = []
     self.tag_for_node_item = []
     self.nodes_which_can_be_deleted = []
     self.nodes_which_can_be_renamed = []
     node = "__ALL__"
     node_name = "%s (%d)" % (self.tag_tree.display_name_for_node[node],
         self.tag_tree.card_count_for_node[node])
     root = self.tag_tree[node]
     root_item = QtWidgets.QTreeWidgetItem(\
         self.tree_wdgt, [node_name, node], 0)
     root_item.setFlags(root_item.flags() | \
        QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsTristate)
     root_item.setCheckState(0, QtCore.Qt.Checked)
     self.create_tree(self.tag_tree[node], qt_parent=root_item)
     # Set forbidden tags.
     if len(criterion._tag_ids_forbidden):
         for i in range(len(self.node_items)):
             node_item = self.node_items[i]
             tag = self.tag_for_node_item[i]
             if tag._id in criterion._tag_ids_forbidden:
                 node_item.setCheckState(0, QtCore.Qt.Checked)
             else:
                 node_item.setCheckState(0, QtCore.Qt.Unchecked)
     # Set active tags.
     else:
         # We first set all the tags inactive. We cannot do this in the
         # second branch of the upcoming 'if' statement, as then an
         # inactive parent tag coming later in the list will deactivate
         # active child tags coming earlier in the list.
         for node_item in self.node_items:
             node_item.setCheckState(0, QtCore.Qt.Unchecked)
         for i in range(len(self.node_items)):
             node_item = self.node_items[i]
             tag = self.tag_for_node_item[i]
             if tag._id in criterion._tag_ids_active:
                 node_item.setCheckState(0, QtCore.Qt.Checked)
     # Restore state of the tree.
     self.tree_wdgt.expandAll()
     collapsed = self.config()["tag_tree_wdgt_state"]
     if collapsed is None:
         collapsed = []
     iterator = QtWidgets.QTreeWidgetItemIterator(self.tree_wdgt)
     while iterator.value():
         if iterator.value().text(1) in collapsed:
             iterator.value().setExpanded(False)
         iterator += 1
Ejemplo n.º 8
0
 def test_1(self):
     fact_data = {"f": "question", "b": "answer"}
     card_type = self.card_type_with_id("1")
     card = self.controller().create_new_cards(fact_data,
                                               card_type,
                                               grade=-1,
                                               tag_names=[])[0]
     self.controller().save_file()
     from mnemosyne.libmnemosyne.tag_tree import TagTree
     self.tree = TagTree(self.mnemosyne.component_manager)
     assert len(list(self.tree.keys())) == 2
     assert self.tree['__ALL__'] == ['__UNTAGGED__']
Ejemplo n.º 9
0
 def test_rename_to_empty_2(self):
     card_type = self.card_type_with_id("1")
     fact_data = {"f": "question4", "b": "answer4"}
     card = self.controller().create_new_cards(fact_data,
                                               card_type,
                                               grade=-1,
                                               tag_names=["A::tag1"])[0]
     from mnemosyne.libmnemosyne.tag_tree import TagTree
     self.tree = TagTree(self.mnemosyne.component_manager)
     self.tree.rename_node("A", "")
     assert self.tree.card_count_for_node["tag1"] == 1
     assert len(self.database().tags()) == 2
Ejemplo n.º 10
0
    def test_4(self):
        card_type = self.card_type_with_id("1")
        fact_data = {"f": "question4", "b": "answer4"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["a::b"])[0]
        fact_data = {"f": "question", "b": "answer"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["a"])[0]
        fact_data = {"f": "question2", "b": "answer2"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["Z"])[0]
        fact_data = {"f": "question3", "b": "answer3"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["a::c"])[0]
        fact_data = {"f": "question5", "b": "answer5"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["b::c::d"])[0]
        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)
        assert self.tree.card_count_for_node["__ALL__"] == 5
        assert self.tree.card_count_for_node["a"] == 3
        assert self.tree.card_count_for_node["Z"] == 1
        assert self.tree.card_count_for_node["a::b"] == 1
        assert self.tree.card_count_for_node["a::c"] == 1
        assert self.tree.card_count_for_node["b"] == 1
        assert self.tree.card_count_for_node["b::c"] == 1
        assert self.tree.card_count_for_node["b::c::d"] == 1

        self.tree.rename_node("Z", "Z::Z")
        self.tree.rename_node("b::c", "b::cc")
        assert self.tree.card_count_for_node["__ALL__"] == 5
        assert self.tree.card_count_for_node["a"] == 3
        assert self.tree.card_count_for_node["Z"] == 1
        assert self.tree.card_count_for_node["Z::Z"] == 1
        assert self.tree.card_count_for_node["a::b"] == 1
        assert self.tree.card_count_for_node["a::c"] == 1
        assert self.tree.card_count_for_node["b"] == 1
        assert self.tree.card_count_for_node["b::cc"] == 1
        assert self.tree.card_count_for_node["b::cc::d"] == 1
Ejemplo n.º 11
0
 def test_rename_to_forbidden(self):
     card_type = self.card_type_with_id("1")
     fact_data = {"f": "question4", "b": "answer4"}
     card = self.controller().create_new_cards(fact_data,
                                               card_type,
                                               grade=-1,
                                               tag_names=["A::tag1"])[0]
     fact_data = {"f": "question", "b": "answer"}
     card = self.controller().create_new_cards(fact_data,
                                               card_type,
                                               grade=-1,
                                               tag_names=["A"])[0]
     from mnemosyne.libmnemosyne.tag_tree import TagTree
     self.tree = TagTree(self.mnemosyne.component_manager)
     self.tree.rename_node("A", "__UNTAGGED__")
     assert "__UNTAGGED__" in self.tree.card_count_for_node
Ejemplo n.º 12
0
 def test_3(self):
     fact_data = {"f": "question", "b": "answer"}
     card_type = self.card_type_with_id("1")
     card = self.controller().create_new_cards(fact_data, card_type,
         grade=-1, tag_names=["a"])[0]
     fact_data = {"f": "question2", "b": "answer2"}
     card = self.controller().create_new_cards(fact_data, card_type,
         grade=-1, tag_names=["Z"])[0]
     fact_data = {"f": "question3",  "b": "answer3"}
     card = self.controller().create_new_cards(fact_data, card_type,
         grade=-1, tag_names=["a::b"])[0]
     fact_data = {"f": "question4",  "b": "answer4"}
     card = self.controller().create_new_cards(fact_data, card_type,
         grade=-1, tag_names=["a::c"])[0]
     fact_data = {"f": "question5",  "b": "answer5"}
     card = self.controller().create_new_cards(fact_data, card_type,
         grade=-1, tag_names=["b::c::d"])[0]
     from mnemosyne.libmnemosyne.tag_tree import TagTree
     self.tree = TagTree(self.mnemosyne.component_manager)
     assert self.tree.card_count_for_node["__ALL__"] == 5
     assert self.tree.card_count_for_node["a"] == 3
     assert self.tree.card_count_for_node["Z"] == 1
     assert self.tree.card_count_for_node["a::b"] == 1
     assert self.tree.card_count_for_node["a::c"] == 1
     assert self.tree.card_count_for_node["b"] == 1
     assert self.tree.card_count_for_node["b::c"] == 1
     assert self.tree.card_count_for_node["b::c::d"] == 1
Ejemplo n.º 13
0
    def test_count_1(self):
        card_type = self.card_type_with_id("1")
        fact_data = {"f": "question4", "b": "answer4"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["b"])[0]
        fact_data = {"f": "question", "b": "answer"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["b", "b"])[0]

        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)

        assert self.tree.card_count_for_node["__ALL__"] == 2
Ejemplo n.º 14
0
 def test_rename_to_empty_2(self):
     card_type = self.card_type_with_id("1")
     fact_data = {"f": "question4",  "b": "answer4"}
     card = self.controller().create_new_cards(fact_data, card_type,
         grade=-1, tag_names=["A::tag1"])[0]
     from mnemosyne.libmnemosyne.tag_tree import TagTree
     self.tree = TagTree(self.mnemosyne.component_manager)
     self.tree.rename_node("A", "")
     assert self.tree.card_count_for_node["tag1"] == 1
     assert len(self.database().tags()) == 2
Ejemplo n.º 15
0
 def test_rename_to_existing_tag_2(self):
     card_type = self.card_type_with_id("1")
     fact_data = {"f": "question4", "b": "answer4"}
     card = self.controller().create_new_cards(
         fact_data,
         card_type,
         grade=-1,
         tag_names=["Xx::vb::test", "Xx::aa::vb::test"])[0]
     from mnemosyne.libmnemosyne.tag_tree import TagTree
     self.tree = TagTree(self.mnemosyne.component_manager)
     assert len(self.database().tags()) == 3
     self.tree.rename_node("Xx::aa::vb::test", "Xx::vb::test")
     assert self.tree.card_count_for_node["Xx::vb::test"] == 1
     assert "Xx::aa::vb::test" not in self.tree.card_count_for_node
     assert "," not in  self.database().con.execute(\
         "select tags from cards where _id=?", (card._id,)).fetchone()[0]
     assert self.database().con.execute(\
         "select count() from tags_for_card").fetchone()[0] == 1
     assert len(self.database().tags()) == 2
Ejemplo n.º 16
0
 def test_2(self):
     fact_data = {"f": "question",
                  "b": "answer"}
     card_type = self.card_type_with_id("1")
     card = self.controller().create_new_cards(fact_data, card_type,
         grade=-1, tag_names=["tag_1"])[0]
     self.controller().save_file()
     from mnemosyne.libmnemosyne.tag_tree import TagTree
     self.tree = TagTree(self.mnemosyne.component_manager)
     assert len(self.tree.keys()) == 3
     assert self.tree['__ALL__'] == [u'tag_1', u'__UNTAGGED__']
Ejemplo n.º 17
0
    def rebuild(self):

        """To be called when external events invalidate the tag tree,
        e.g. due to edits in the card browser widget.

        """

        self.save_criterion()
        self.store_tree_state()
        self.tag_tree = TagTree(self.component_manager)
        self.restore_criterion()
Ejemplo n.º 18
0
 def test_rename_to_forbidden(self):
     card_type = self.card_type_with_id("1")
     fact_data = {"f": "question4",  "b": "answer4"}
     card = self.controller().create_new_cards(fact_data, card_type,
         grade=-1, tag_names=["A::tag1"])[0]
     fact_data = {"f": "question", "b": "answer"}
     card = self.controller().create_new_cards(fact_data, card_type,
         grade=-1, tag_names=["A"])[0]
     from mnemosyne.libmnemosyne.tag_tree import TagTree
     self.tree = TagTree(self.mnemosyne.component_manager)
     self.tree.rename_node("A", "__UNTAGGED__")
     assert "__UNTAGGED__" in self.tree.card_count_for_node
Ejemplo n.º 19
0
    def test_delete_forbidden(self):
        card_type = self.card_type_with_id("1")
        fact_data = {"f": "question", "b": "answer"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["forbidden"])[0]
        assert self.database().active_count() == 1

        c = DefaultCriterion(self.mnemosyne.component_manager)
        c.deactivated_card_type_fact_view_ids = set()
        c._tag_ids_active = set(
            [self.database().get_or_create_tag_with_name("active")._id, 1])
        c._tag_ids_forbidden = set(
            [self.database().get_or_create_tag_with_name("forbidden")._id])
        self.database().set_current_criterion(c)
        assert self.database().active_count() == 0

        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)
        self.tree.delete_subtree("forbidden")
        assert self.database().active_count() == 1
Ejemplo n.º 20
0
    def test_count_1(self):
        card_type = self.card_type_with_id("1")
        fact_data = {"f": "question4",  "b": "answer4"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["b"])[0]
        fact_data = {"f": "question", "b": "answer"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["b", "b"])[0]

        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)

        assert self.tree.card_count_for_node["__ALL__"] == 2
Ejemplo n.º 21
0
 def display(self, criterion=None):
     # Create criterion if needed.
     if criterion is None:
         criterion = DefaultCriterion(self.component_manager)
         for tag in self.database().tags():
             criterion._tag_ids_active.add(tag._id)
     # Create tree.
     self.tag_tree = TagTree(self.component_manager)
     self.tree_wdgt.clear()
     self.tag_for_node_item = {}
     self.nodes_which_can_be_deleted = []
     self.nodes_which_can_be_renamed = []
     node = "__ALL__"
     node_name = "%s (%d)" % (self.tag_tree.display_name_for_node[node],
         self.tag_tree.card_count_for_node[node])
     root = self.tag_tree[node]
     root_item = QtGui.QTreeWidgetItem(\
         self.tree_wdgt, [node_name, node], 0)
     root_item.setFlags(root_item.flags() | \
        QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsTristate)
     root_item.setCheckState(0, QtCore.Qt.Checked)
     self.create_tree(self.tag_tree[node], qt_parent=root_item)
     # Set forbidden tags.
     if len(criterion._tag_ids_forbidden):
         for node_item, tag in self.tag_for_node_item.iteritems():
             if tag._id in criterion._tag_ids_forbidden:
                 node_item.setCheckState(0, QtCore.Qt.Checked)
             else:
                 node_item.setCheckState(0, QtCore.Qt.Unchecked)
     # Set active tags.
     else:
         # We first set all the tags inactive. We cannot do this in the
         # second branch of the upcoming 'if' statement, as then an
         # inactive parent tag coming later in the list will deactivate
         # active child tags coming earlier in the list.
         for node_item in self.tag_for_node_item:
             node_item.setCheckState(0, QtCore.Qt.Unchecked)
         for node_item, tag in self.tag_for_node_item.iteritems():
             if tag._id in criterion._tag_ids_active:
                 node_item.setCheckState(0, QtCore.Qt.Checked)
     # Restore state of the tree.
     self.tree_wdgt.expandAll()
     collapsed = self.config()["tag_tree_wdgt_state"]
     if collapsed is None:
         collapsed = []
     iterator = QtGui.QTreeWidgetItemIterator(self.tree_wdgt)
     while iterator.value():
         if unicode(iterator.value().text(1)) in collapsed:
             iterator.value().setExpanded(False)
         iterator += 1
Ejemplo n.º 22
0
 def test_rename_to_existing_tag_2(self):
     card_type = self.card_type_with_id("1")
     fact_data = {"f": "question4",  "b": "answer4"}
     card = self.controller().create_new_cards(fact_data, card_type,
         grade=-1, tag_names=["Xx::vb::test", "Xx::aa::vb::test"])[0]
     from mnemosyne.libmnemosyne.tag_tree import TagTree
     self.tree = TagTree(self.mnemosyne.component_manager)
     assert len(self.database().tags()) == 3
     self.tree.rename_node("Xx::aa::vb::test", "Xx::vb::test")
     assert self.tree.card_count_for_node["Xx::vb::test"] == 1
     assert "Xx::aa::vb::test" not in self.tree.card_count_for_node
     assert "," not in  self.database().con.execute(\
         "select tags from cards where _id=?", (card._id,)).fetchone()[0]
     assert self.database().con.execute(\
         "select count() from tags_for_card").fetchone()[0] == 1
     assert len(self.database().tags()) == 2
Ejemplo n.º 23
0
    def test_delete_forbidden(self):
        card_type = self.card_type_with_id("1")
        fact_data = {"f": "question",  "b": "answer"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["forbidden"])[0]
        assert self.database().active_count() == 1

        c = DefaultCriterion(self.mnemosyne.component_manager)
        c.deactivated_card_type_fact_view_ids = set()
        c._tag_ids_active = set([self.database().get_or_create_tag_with_name("active")._id, 1])
        c._tag_ids_forbidden = set([self.database().get_or_create_tag_with_name("forbidden")._id])
        self.database().set_current_criterion(c)
        assert self.database().active_count() == 0

        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)
        self.tree.delete_subtree("forbidden")
        assert self.database().active_count() == 1
Ejemplo n.º 24
0
    def test_delete_2(self):
        card_type = self.card_type_with_id("1")
        fact_data = {"f": "question4",  "b": "answer4"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["a::b"])[0]
        fact_data = {"f": "question", "b": "answer"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["a"])[0]
        fact_data = {"f": "question2", "b": "answer2"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["Z"])[0]
        fact_data = {"f": "question3",  "b": "answer3"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["a::c"])[0]
        fact_data = {"f": "question5",  "b": "answer5"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["b::c::d", "b"])[0]
        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)

        assert self.tree.card_count_for_node["__ALL__"] == 5
        assert self.tree.card_count_for_node["a"] == 3
        assert self.tree.card_count_for_node["Z"] == 1
        assert self.tree.card_count_for_node["a::b"] == 1
        assert self.tree.card_count_for_node["a::c"] == 1
        assert self.tree.card_count_for_node["b"] == 1
        assert self.tree.card_count_for_node["b::c"] == 1
        assert self.tree.card_count_for_node["b::c::d"] == 1

        self.tree.delete_subtree("b::c")
        card = self.database().card(card._id, is_id_internal=True)
        assert card.tag_string() == "b"
        self.database().con.execute("select tags from cards where _id=?",
            (card._id, )).fetchone()[0] == "b"

        assert self.tree.card_count_for_node["__ALL__"] == 5
        assert self.tree.card_count_for_node["a"] == 3
        assert self.tree.card_count_for_node["Z"] == 1
        assert self.tree.card_count_for_node["a::b"] == 1
        assert self.tree.card_count_for_node["a::c"] == 1
        assert self.tree.card_count_for_node["b"] == 1
        assert "__UNTAGGED__" in self.tree.card_count_for_node
        assert "b::c" not in self.tree.card_count_for_node
        assert "b::c::d" not in self.tree.card_count_for_node
Ejemplo n.º 25
0
class TagsTreeWdgt(QtGui.QWidget, Component):

    """Displays all the tags in a tree together with check boxes. """

    tags_changed_signal = QtCore.pyqtSignal()

    def __init__(self, component_manager, parent, acquire_database=None):
        Component.__init__(self, component_manager)
        QtGui.QWidget.__init__(self, parent)
        self.layout = QtGui.QVBoxLayout(self)
        self.tree_wdgt = QtGui.QTreeWidget(self)
        self.tree_wdgt.setColumnCount(2)
        self.tree_wdgt.setColumnHidden(1, True)
        self.tree_wdgt.setColumnHidden(NODE, True)
        self.tree_wdgt.setHeaderHidden(True)
        self.tree_wdgt.setSelectionMode(\
            QtGui.QAbstractItemView.ExtendedSelection)
        self.delegate = TagDelegate(component_manager, self)
        self.tree_wdgt.setItemDelegate(self.delegate)
        self.delegate.rename_node.connect(self.rename_node)
        self.delegate.redraw_node.connect(self.redraw_node)
        self.layout.addWidget(self.tree_wdgt)
        self.tree_wdgt.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.tree_wdgt.customContextMenuRequested.connect(\
            self.context_menu)
        self.acquire_database = acquire_database

    def selected_nodes_which_can_be_renamed(self):
        nodes = []
        for index in self.tree_wdgt.selectedIndexes():
            node_index = \
                index.model().index(index.row(), NODE, index.parent())
            node = index.model().data(node_index).toString()
            if node in self.nodes_which_can_be_renamed:
                nodes.append(node)
        return nodes

    def selected_nodes_which_can_be_deleted(self):
        nodes = []
        for index in self.tree_wdgt.selectedIndexes():
            node_index = \
                index.model().index(index.row(), NODE, index.parent())
            node = index.model().data(node_index).toString()
            if node in self.nodes_which_can_be_deleted:
                nodes.append(node)
        return nodes

    def context_menu(self, point):
        menu = QtGui.QMenu(self)
        to_rename = self.selected_nodes_which_can_be_renamed()
        if len(to_rename) >= 1:
            rename_action = QtGui.QAction(_("&Rename"), menu)
            rename_action.triggered.connect(self.menu_rename)
            rename_action.setShortcut(QtCore.Qt.Key_Enter)
            menu.addAction(rename_action)
            if len(to_rename) > 1:
                rename_action.setEnabled(False)
        to_delete = self.selected_nodes_which_can_be_deleted()
        if len(to_delete) >= 1:
            delete_action = QtGui.QAction(_("&Delete"), menu)
            delete_action.triggered.connect(self.menu_delete)
            delete_action.setShortcut(QtGui.QKeySequence.Delete)
            menu.addAction(delete_action)
        if len(to_delete) + len(to_rename) >= 1:
            menu.exec_(self.tree_wdgt.mapToGlobal(point))

    def keyPressEvent(self, event):
        if event.key() in [QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return]:
            self.menu_rename()
        elif event.key() in [QtCore.Qt.Key_Delete, QtCore.Qt.Key_Backspace]:
            self.menu_delete()
        else:
            QtGui.QWidget.keyPressEvent(self, event)

    def menu_rename(self):
        nodes = self.selected_nodes_which_can_be_renamed()
        # If there are tags selected, this means that we could only have got
        # after pressing return on an actual edit, due to our custom
        # 'keyPressEvent'. We should not continue in that case.
        if len(nodes) == 0:
            return
        # We display the full node (i.e. all levels including ::), so that
        # the hierarchy can be changed upon editing.

        from mnemosyne.pyqt_ui.ui_rename_tag_dlg import Ui_RenameTagDlg

        class RenameDlg(QtGui.QDialog, Ui_RenameTagDlg):
            def __init__(self, old_tag_name):
                QtGui.QDialog.__init__(self)
                self.setupUi(self)
                self.tag_name.setText(\
                    old_tag_name.replace("::" + _("Untagged"), "" ))

        old_tag_name = nodes[0]
        dlg = RenameDlg(old_tag_name)
        if dlg.exec_() == QtGui.QDialog.Accepted:
            self.rename_node(nodes[0], unicode(dlg.tag_name.text()))

    def menu_delete(self):
        nodes = self.selected_nodes_which_can_be_deleted()
        if len(nodes) == 0:
            return
        if len(nodes) > 1:
            question = _("Delete these tags? Cards with these tags will not be deleted.")
        else:
            question = _("Delete this tag? Cards with this tag will not be deleted.")
        answer = self.main_widget().show_question\
            (question, _("&OK"), _("&Cancel"), "")
        if answer == 1: # Cancel.
            return
        self.delete_nodes(nodes)

    def create_tree(self, tree, qt_parent):
        for node in tree:
            node_name = "%s (%d)" % \
                (self.tag_tree.display_name_for_node[node],
                self.tag_tree.card_count_for_node[node])
            node_item = QtGui.QTreeWidgetItem(qt_parent, [node_name, node], 0)
            node_item.setFlags(node_item.flags() | \
                QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsTristate)
            if node not in ["__ALL__", "__UNTAGGED__"] and \
                not "::" + _("Untagged") in node:
                node_item.setFlags(node_item.flags() | \
                    QtCore.Qt.ItemIsEditable)
                self.nodes_which_can_be_renamed.append(node)
                self.nodes_which_can_be_deleted.append(node)
            if node in self.tag_tree.tag_for_node:
                self.tag_for_node_item[node_item] = \
                    self.tag_tree.tag_for_node[node]
            node_item.setData(NODE, QtCore.Qt.DisplayRole,
                    QtCore.QVariant(QtCore.QString(node)))
            self.create_tree(tree=self.tag_tree[node], qt_parent=node_item)

    def display(self, criterion=None):
        # Create criterion if needed.
        if criterion is None:
            criterion = DefaultCriterion(self.component_manager)
            for tag in self.database().tags():
                criterion._tag_ids_active.add(tag._id)
        # Create tree.
        self.tag_tree = TagTree(self.component_manager)
        self.tree_wdgt.clear()
        self.tag_for_node_item = {}
        self.nodes_which_can_be_deleted = []
        self.nodes_which_can_be_renamed = []
        node = "__ALL__"
        node_name = "%s (%d)" % (self.tag_tree.display_name_for_node[node],
            self.tag_tree.card_count_for_node[node])
        root = self.tag_tree[node]
        root_item = QtGui.QTreeWidgetItem(\
            self.tree_wdgt, [node_name, node], 0)
        root_item.setFlags(root_item.flags() | \
           QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsTristate)
        root_item.setCheckState(0, QtCore.Qt.Checked)
        self.create_tree(self.tag_tree[node], qt_parent=root_item)
        # Set forbidden tags.
        if len(criterion._tag_ids_forbidden):
            for node_item, tag in self.tag_for_node_item.iteritems():
                if tag._id in criterion._tag_ids_forbidden:
                    node_item.setCheckState(0, QtCore.Qt.Checked)
                else:
                    node_item.setCheckState(0, QtCore.Qt.Unchecked)
        # Set active tags.
        else:
            # We first set all the tags inactive. We cannot do this in the
            # second branch of the upcoming 'if' statement, as then an
            # inactive parent tag coming later in the list will deactivate
            # active child tags coming earlier in the list.
            for node_item in self.tag_for_node_item:
                node_item.setCheckState(0, QtCore.Qt.Unchecked)
            for node_item, tag in self.tag_for_node_item.iteritems():
                if tag._id in criterion._tag_ids_active:
                    node_item.setCheckState(0, QtCore.Qt.Checked)
        # Restore state of the tree.
        self.tree_wdgt.expandAll()
        collapsed = self.config()["tag_tree_wdgt_state"]
        if collapsed is None:
            collapsed = []
        iterator = QtGui.QTreeWidgetItemIterator(self.tree_wdgt)
        while iterator.value():
            if unicode(iterator.value().text(1)) in collapsed:
                iterator.value().setExpanded(False)
            iterator += 1

    def checked_to_active_tags_in_criterion(self, criterion):
        for item, tag in self.tag_for_node_item.iteritems():
            if item.checkState(0) == QtCore.Qt.Checked:
                criterion._tag_ids_active.add(tag._id)
        criterion._tag_ids_forbidden = set()
        return criterion

    def checked_to_forbidden_tags_in_criterion(self, criterion):
        for item, tag in self.tag_for_node_item.iteritems():
            if item.checkState(0) == QtCore.Qt.Checked:
                criterion._tag_ids_forbidden.add(tag._id)
        criterion._tag_ids_active = \
            set([tag._id for tag in self.tag_for_node_item.values()])
        return criterion
    
    def unchecked_to_forbidden_tags_in_criterion(self, criterion):
        for item, tag in self.tag_for_node_item.iteritems():
            if item.checkState(0) == QtCore.Qt.Unchecked:
                criterion._tag_ids_forbidden.add(tag._id)
        return criterion

    def save_criterion(self):
        self.saved_criterion = DefaultCriterion(self.component_manager)
        self.checked_to_active_tags_in_criterion(self.saved_criterion)
        # We also save the unchecked tags as this will allow us to identify
        # any new tags created afterwards.
        self.unchecked_to_forbidden_tags_in_criterion(self.saved_criterion)
        # Now we've saved the checked state of the tree.
        # Saving and restoring the selected state is less trivial, because
        # in the case of trees, the model indexes have parents which become
        # invalid when creating the widget.
        # The solution would be to save tags and reselect those in the new
        # widget.

    def restore_criterion(self):
        new_criterion = DefaultCriterion(self.component_manager)
        for tag in self.database().tags():
            if tag._id in self.saved_criterion._tag_ids_active or \
               tag._id not in self.saved_criterion._tag_ids_forbidden:
               # Second case deals with recently added tag.
                new_criterion._tag_ids_active.add(tag._id)
        self.display(new_criterion)

    def store_tree_state(self):

        """Store which nodes are collapsed. """

        collapsed = []
        iterator = QtGui.QTreeWidgetItemIterator(self.tree_wdgt)
        while iterator.value():
            if not iterator.value().isExpanded():
                collapsed.append(unicode(iterator.value().text(1)))
            iterator += 1
        self.config()["tag_tree_wdgt_state"] = collapsed

    def rename_node(self, node, new_name):
        if self.acquire_database:
            self.acquire_database()
        self.save_criterion()
        self.store_tree_state()
        self.tag_tree.rename_node(unicode(node), unicode(new_name))
        self.restore_criterion()
        self.tags_changed_signal.emit()

    def delete_nodes(self, nodes):
        if self.acquire_database:
            self.acquire_database()
        self.save_criterion()
        self.store_tree_state()
        for node in nodes:
            self.tag_tree.delete_subtree(unicode(node))
        self.restore_criterion()
        self.tags_changed_signal.emit()

    def redraw_node(self, node):

        """When renaming a tag to the same name, we need to redraw the node
        to show the card count again.

        """

        # We do the redrawing in a rather hackish way now, simply by
        # recreating the widget. Could be sped up, but at the expense of more
        # complicated code.
        self.save_criterion()
        self.restore_criterion()

    def rebuild(self):

        """To be called when external events invalidate the tag tree,
        e.g. due to edits in the card browser widget.

        """

        self.save_criterion()
        self.store_tree_state()
        self.tag_tree = TagTree(self.component_manager)
        self.restore_criterion()

    def closeEvent(self, event):
        self.store_tree_state()
Ejemplo n.º 26
0
class TestMemImport(MnemosyneTest):
    def setup(self):
        self.initialise_data_dir()
        self.mnemosyne = Mnemosyne(upload_science_logs=False,
                                   interested_in_old_reps=True,
                                   asynchronous_database=True)
        self.mnemosyne.components.insert(
            0,
            ("mnemosyne.libmnemosyne.gui_translators.gettext_gui_translator",
             "GetTextGuiTranslator"))
        self.mnemosyne.gui_for_component["ScheduledForgottenNew"] = \
            [("mnemosyne_test", "TestReviewWidget")]
        self.mnemosyne.components.append(\
            ("test_mem_import", "Widget"))
        self.mnemosyne.components.append(\
            ("test_mem_import", "MyImportDialog"))
        self.mnemosyne.initialise(os.path.abspath("dot_test"),
                                  automatic_upgrades=False)
        self.review_controller().reset()

    def mem_importer(self):
        for format in self.mnemosyne.component_manager.all("file_format"):
            if format.__class__.__name__ == "Mnemosyne1Mem":
                return format

    @mock.patch(
        "mnemosyne.libmnemosyne.file_formats.mnemosyne1_mem.open",
        mock.Mock(side_effect=[FileNotFoundError,
                               IndexError("Mocked Error")]))
    def test_exceptions(self):
        filename = os.path.join(os.getcwd(), "tests", "files", "nothere.mem")
        self.mem_importer().do_import(filename)
        assert last_error.startswith("Unable to open")

        self.mem_importer().do_import("name_does_not_matter")
        assert last_error.strip().endswith("IndexError: Mocked Error")

    @MnemosyneTest.set_timezone_utc
    def test_card_type_1(self):
        filename = os.path.join(os.getcwd(), "tests", "files", "1sided.mem")
        self.mem_importer().do_import(filename)
        self.review_controller().reset()
        assert self.database().card_count() == 1
        card = self.review_controller().card
        assert card.grade == 2
        assert card.easiness == 2.5
        assert card.acq_reps == 1
        assert card.ret_reps == 0
        assert card.lapses == 0
        assert card.acq_reps_since_lapse == 1
        assert card.ret_reps_since_lapse == 0
        assert [tag.name for tag in card.tags] == ["__UNTAGGED__"]
        assert card.last_rep == 1247529600
        assert card.next_rep == 1247616000
        assert card.id == "9cff728f"

    def test_card_type_1_unseen(self):
        filename = os.path.join(os.getcwd(), "tests", "files",
                                "1sided_unseen.mem")
        self.mem_importer().do_import(filename)
        self.review_controller().reset()
        assert self.database().card_count() == 1
        card = self.review_controller().card
        assert card.grade == -1
        assert card.easiness == 2.5
        assert card.acq_reps == 0
        assert card.ret_reps == 0
        assert card.lapses == 0
        assert card.acq_reps_since_lapse == 0
        assert card.ret_reps_since_lapse == 0
        assert card.last_rep == -1
        assert card.next_rep == -1
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.ADDED_CARD, )).fetchone()[0] == 1

    def test_card_type_1_edited(self):
        filename = os.path.join(os.getcwd(), "tests", "files", "1sided.mem")
        self.mem_importer().do_import(filename)
        self.review_controller().reset()
        assert self.database().card_count() == 1
        card = self.review_controller().card
        assert card.id == "9cff728f"
        assert "question" in card.question()
        filename = os.path.join(os.getcwd(), "tests", "files", "1sided.mem")
        self.mem_importer().do_import(filename)
        assert last_error.startswith(
            "These cards seem to have been imported before")

    def test_card_type_2(self):
        filename = os.path.join(os.getcwd(), "tests", "files", "2sided.mem")
        self.mem_importer().do_import(filename)
        self.review_controller().reset()
        assert self.database().card_count() == 2
        card_1 = self.review_controller().card
        assert "question" in card_1.question()
        assert "answer" in card_1.answer()
        cards = self.database().cards_from_fact(card_1.fact)
        if cards[0] == card_1:
            card_2 = cards[1]
        else:
            card_2 = cards[0]
        assert "question" in card_2.answer()
        assert "answer" in card_2.question()
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.ADDED_CARD, )).fetchone()[0] == 2

    def test_card_type_3(self):
        filename = os.path.join(os.getcwd(), "tests", "files", "3sided.mem")
        self.mem_importer().do_import(filename)
        self.review_controller().reset()
        assert self.database().card_count() == 2
        card_1 = self.review_controller().card
        assert card_1.fact.data == {"f": "f", "p_1": "p", "m_1": "t"}
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.ADDED_CARD, )).fetchone()[0] == 2

    def test_card_type_3_corrupt(self):
        filename = os.path.join(os.getcwd(), "tests", "files",
                                "3sided_corrupt.mem")
        self.mem_importer().do_import(filename)
        self.review_controller().reset()
        assert self.database().card_count() == 2
        card_1 = self.review_controller().card
        assert card_1.fact.data == {"f": "f", "m_1": "t"}
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.ADDED_CARD, )).fetchone()[0] == 2

    def test_card_type_3_missing(self):
        filename = os.path.join(os.getcwd(), "tests", "files",
                                "3sided_missing.mem")
        self.mem_importer().do_import(filename)
        self.review_controller().reset()
        assert self.database().card_count() == 1
        card_1 = self.review_controller().card
        assert card_1.fact.data == {"f": "t", "b": "f\np"}
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.ADDED_CARD, )).fetchone()[0] == 1

    def test_media(self):
        os.mkdir(os.path.join(os.getcwd(), "tests", "files", "figs"))
        os.mkdir(os.path.join(os.getcwd(), "tests", "files", "figs", "figs"))
        figures = [\
            os.path.join(os.getcwd(), "tests", "files", "a.png"),
            os.path.join(os.getcwd(), "tests", "files", "figs", "a.png"),
            os.path.join(os.getcwd(), "tests", "files", "figs", "figs", "a.png")]
        for filename in figures:
            open(filename, "w")
        filename = os.path.join(os.getcwd(), "tests", "files", "media.mem")
        self.mem_importer().do_import(filename)
        assert os.path.exists(os.path.join(\
            os.path.abspath("dot_test"), "default.db_media", "a.png"))
        assert os.path.exists(os.path.join(\
            os.path.abspath("dot_test"), "default.db_media", "figs", "a.png"))
        assert os.path.exists(os.path.join(\
            os.path.abspath("dot_test"), "default.db_media", "figs", "a.png"))
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.ADDED_MEDIA_FILE, )).fetchone()[0] == 3

    def test_media_missing(self):
        os.mkdir(os.path.join(os.getcwd(), "tests", "files", "figs"))
        os.mkdir(os.path.join(os.getcwd(), "tests", "files", "figs", "figs"))
        figures = [\
            os.path.join(os.getcwd(), "tests", "files", "a.png"),
            os.path.join(os.getcwd(), "tests", "files", "figs", "a.png")]
        for filename in figures:
            open(filename, "w")
        filename = os.path.join(os.getcwd(), "tests", "files", "media.mem")
        self.mem_importer().do_import(filename)
        assert os.path.exists(os.path.join(\
            os.path.abspath("dot_test"), "default.db_media", "a.png"))
        assert os.path.exists(os.path.join(\
            os.path.abspath("dot_test"), "default.db_media", "figs", "a.png"))
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.ADDED_MEDIA_FILE, )).fetchone()[0] == 2

    def test_media_missing_2(self):
        filename = os.path.join(os.getcwd(), "tests", "files", "media.mem")
        self.mem_importer().do_import(filename)
        assert not os.path.exists(os.path.join(\
            os.path.abspath("dot_test"), "default.db_media", "a.png"))
        assert not os.path.exists(os.path.join(\
            os.path.abspath("dot_test"), "default.db_media", "figs", "a.png"))
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.ADDED_MEDIA_FILE, )).fetchone()[0] == 0

    def test_media_slashes(self):
        os.mkdir(os.path.join(os.getcwd(), "tests", "files", "figs"))
        os.mkdir(os.path.join(os.getcwd(), "tests", "files", "figs", "figs"))
        figures = [\
            os.path.join(os.getcwd(), "tests", "files", "a.png"),
            os.path.join(os.getcwd(), "tests", "files", "figs", "a.png"),
            os.path.join(os.getcwd(), "tests", "files", "figs", "figs", "a.png")]
        for filename in figures:
            open(filename, "w")
        filename = os.path.join(os.getcwd(), "tests", "files",
                                "media_slashes.mem")
        self.mem_importer().do_import(filename)
        assert os.path.exists(os.path.join(\
            os.path.abspath("dot_test"), "default.db_media", "a.png"))
        assert os.path.exists(os.path.join(\
            os.path.abspath("dot_test"), "default.db_media", "figs", "a.png"))
        assert os.path.exists(os.path.join(\
            os.path.abspath("dot_test"), "default.db_media", "figs", "a.png"))
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.ADDED_MEDIA_FILE, )).fetchone()[0] == 3

    def test_media_quotes(self):
        filename = os.path.join(os.getcwd(), "tests", "files", "basedir_media",
                                "default.mem")
        self.mem_importer().do_import(filename)
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.ADDED_MEDIA_FILE, )).fetchone()[0] == 1

    def test_sound(self):
        os.mkdir(os.path.join(\
            os.getcwd(), "tests", "files", "soundfiles"))
        soundname = os.path.join(os.path.join(\
            os.getcwd(), "tests", "files", "soundfiles", "a.ogg"))
        open(soundname, "w")
        filename = os.path.join(os.getcwd(), "tests", "files", "sound.mem")
        self.mem_importer().do_import(filename)
        assert os.path.exists(os.path.join(\
            os.path.abspath("dot_test"), "default.db_media", "soundfiles", "a.ogg"))
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.ADDED_MEDIA_FILE, )).fetchone()[0] == 1
        self.review_controller().reset()
        card = self.review_controller().card
        assert card.fact["f"] == """<audio src="soundfiles/a.ogg">"""

    def test_map(self):
        filename = os.path.join(os.getcwd(), "tests", "files", "map.mem")
        self.mem_importer().do_import(filename)
        self.review_controller().reset()
        assert self.database().card_count() == 2
        card = self.review_controller().card
        assert card.fact["loc"] == "<b>Drenthe</b>"
        assert card.fact["marked"] == \
          """<img src_missing="maps/Netherlands-Provinces/Drenthe.png">"""
        assert card.fact["blank"] == \
          """<img src_missing="maps/Netherlands-Provinces/Netherlands-Provinces.png">"""

    def test_dups(self):
        filename = os.path.join(os.getcwd(), "tests", "files", "dups.mem")
        self.mem_importer().do_import(filename)
        self.review_controller().reset()
        assert self.review_controller().card.fact["loc"] == \
               """<b>Freistaat Th\xfcringen (Free State of Thuringia)</b>"""
        assert self.review_controller().card.tag_string(
        ) == "Germany: States, MISSING_MEDIA"

    def test_logs_new_1(self):
        self.database().update_card_after_log_import = (lambda x, y, z: 0)
        self.database().before_1x_log_import()
        filename = os.path.join(os.getcwd(), "tests", "files", "new_1.txt")
        ScienceLogParser(self.database()).parse(filename)
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.ADDED_CARD, )).fetchone()[0] == 1
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.REPETITION, )).fetchone()[0] == 10
        assert self.database().con.execute(\
            "select acq_reps from log where event_type=? and object_id='9525224f'",
            (EventTypes.REPETITION, )).fetchone()[0] == 1
        assert self.database().con.execute(\
            "select acq_reps_since_lapse from log where event_type=? and object_id='9525224f'",
            (EventTypes.REPETITION, )).fetchone()[0] == 1
        assert self.database().con.execute(\
            """select scheduled_interval from log where event_type=? and object_id='9525224f'
            order by _id desc limit 1""",
            (EventTypes.REPETITION, )).fetchone()[0] == (6)*60*60*24
        assert self.database().con.execute(\
            """select actual_interval from log where event_type=? and object_id='9525224f'
            order by _id desc limit 1""",
            (EventTypes.REPETITION, )).fetchone()[0] == 0 # This is an artificial log.
        timestamp = self.database().con.execute(\
            """select timestamp from log where event_type=? and object_id='9525224f'
            order by _id desc limit 1""",
            (EventTypes.REPETITION, )).fetchone()[0]
        next_rep = self.database().con.execute(\
            """select next_rep from log where event_type=? and object_id='9525224f'
            order by _id desc limit 1""",
            (EventTypes.REPETITION, )).fetchone()[0]
        assert next_rep - timestamp == (14 - 3) * 60 * 60 * 24
        assert self.database().con.execute(\
            "select count() from log").fetchone()[0] == 25
        assert self.database().con.execute(\
            "select acq_reps from log where event_type=? order by _id desc limit 1",
            (EventTypes.LOADED_DATABASE, )).fetchone()[0] == 0
        assert self.database().con.execute(\
            "select ret_reps from log where event_type=? order by _id desc limit 1",
            (EventTypes.LOADED_DATABASE, )).fetchone()[0] == 7
        assert self.database().con.execute(\
            "select lapses from log where event_type=? order by _id desc limit 1",
            (EventTypes.LOADED_DATABASE, )).fetchone()[0] == 336
        assert self.database().con.execute(\
            "select acq_reps from log where event_type=? order by _id desc limit 1",
            (EventTypes.SAVED_DATABASE, )).fetchone()[0] == 0
        assert self.database().con.execute(\
            "select ret_reps from log where event_type=? order by _id desc limit 1",
            (EventTypes.SAVED_DATABASE, )).fetchone()[0] == 12
        assert self.database().con.execute(\
            "select lapses from log where event_type=? order by _id desc limit 1",
            (EventTypes.SAVED_DATABASE, )).fetchone()[0] == 341

    def test_logs_new_2(self):
        self.database().update_card_after_log_import = (lambda x, y, z: 0)
        self.database().before_1x_log_import()
        filename = os.path.join(os.getcwd(), "tests", "files", "new_2.txt")
        ScienceLogParser(self.database()).parse(filename)
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.ADDED_CARD, )).fetchone()[0] == 1
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.REPETITION, )).fetchone()[0] == 1
        assert self.database().con.execute(\
            "select acq_reps from log where event_type=? and object_id='8da62cfb'",
            (EventTypes.REPETITION, )).fetchone()[0] == 1
        assert self.database().con.execute(\
            "select acq_reps_since_lapse from log where event_type=? and object_id='8da62cfb'",
            (EventTypes.REPETITION, )).fetchone()[0] == 1

    def test_logs_new_3(self):
        self.database().update_card_after_log_import = (lambda x, y, z: 0)
        self.database().before_1x_log_import()
        filename = os.path.join(os.getcwd(), "tests", "files", "new_3.txt")
        ScienceLogParser(self.database()).parse(filename)
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.ADDED_CARD, )).fetchone()[0] == 1
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.REPETITION, )).fetchone()[0] == 4
        assert self.database().con.execute(\
            "select acq_reps from log where event_type=? and object_id='5106b621'",
            (EventTypes.REPETITION, )).fetchone()[0] == 1
        assert self.database().con.execute(\
            "select acq_reps_since_lapse from log where event_type=? and object_id='5106b621'",
            (EventTypes.REPETITION, )).fetchone()[0] == 1
        assert self.database().con.execute(\
            """select acq_reps from log where event_type=? and object_id='5106b621'
             order by _id desc limit 1""",
            (EventTypes.REPETITION, )).fetchone()[0] == 1
        assert self.database().con.execute(\
            """select acq_reps_since_lapse from log where event_type=? and object_id='5106b621'
            order by _id desc limit 1""",
            (EventTypes.REPETITION, )).fetchone()[0] == 1

    def test_logs_new_4(self):
        self.database().update_card_after_log_import = (lambda x, y, z: 0)
        self.database().before_1x_log_import()
        filename = os.path.join(os.getcwd(), "tests", "files", "new_4.txt")
        ScienceLogParser(self.database()).parse(filename)
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.ADDED_CARD, )).fetchone()[0] == 1
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.REPETITION, )).fetchone()[0] == 2
        assert self.database().con.execute(\
            "select acq_reps from log where event_type=? and object_id='b7601e0c'",
            (EventTypes.REPETITION, )).fetchone()[0] == 1
        assert self.database().con.execute(\
            "select ret_reps from log where event_type=? and object_id='b7601e0c'",
            (EventTypes.REPETITION, )).fetchone()[0] == 0
        assert self.database().con.execute(\
            "select acq_reps_since_lapse from log where event_type=? and object_id='b7601e0c'",
            (EventTypes.REPETITION, )).fetchone()[0] == 1
        assert self.database().con.execute(\
            """select acq_reps from log where event_type=? and object_id='b7601e0c'
             order by _id desc limit 1""",
            (EventTypes.REPETITION, )).fetchone()[0] == 1
        assert self.database().con.execute(\
            """select ret_reps from log where event_type=? and object_id='b7601e0c'
             order by _id desc limit 1""",
            (EventTypes.REPETITION, )).fetchone()[0] == 1
        assert self.database().con.execute(\
            """select acq_reps_since_lapse from log where event_type=? and object_id='b7601e0c'
            order by _id desc limit 1""",
            (EventTypes.REPETITION, )).fetchone()[0] == 1

    def test_logs_new_5(self):
        self.database().update_card_after_log_import = (lambda x, y, z: 0)
        self.database().before_1x_log_import()
        filename = os.path.join(os.getcwd(), "tests", "files", "new_5.txt")
        ScienceLogParser(self.database()).parse(filename)
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.ADDED_CARD, )).fetchone()[0] == 1
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.REPETITION, )).fetchone()[0] == 2
        assert self.database().con.execute(\
            "select acq_reps from log where event_type=? and object_id='9c8ce28e-1a4b-4148-8287-b8a7790d86d0.1.1'",
            (EventTypes.REPETITION, )).fetchone()[0] == 1
        assert self.database().con.execute(\
            "select ret_reps from log where event_type=? and object_id='9c8ce28e-1a4b-4148-8287-b8a7790d86d0.1.1'",
            (EventTypes.REPETITION, )).fetchone()[0] == 0
        assert self.database().con.execute(\
            "select acq_reps_since_lapse from log where event_type=? and object_id='9c8ce28e-1a4b-4148-8287-b8a7790d86d0.1.1'",
            (EventTypes.REPETITION, )).fetchone()[0] == 1
        assert self.database().con.execute(\
            """select acq_reps from log where event_type=? and object_id='9c8ce28e-1a4b-4148-8287-b8a7790d86d0.1.1'
             order by _id desc limit 1""",
            (EventTypes.REPETITION, )).fetchone()[0] == 2
        assert self.database().con.execute(\
            """select ret_reps from log where event_type=? and object_id='9c8ce28e-1a4b-4148-8287-b8a7790d86d0.1.1'
             order by _id desc limit 1""",
            (EventTypes.REPETITION, )).fetchone()[0] == 0
        assert self.database().con.execute(\
            """select acq_reps_since_lapse from log where event_type=? and object_id='9c8ce28e-1a4b-4148-8287-b8a7790d86d0.1.1'
            order by _id desc limit 1""",
            (EventTypes.REPETITION, )).fetchone()[0] == 2
        assert self.database().con.execute(\
            """select object_id from log where event_type=?""",
            (EventTypes.STARTED_SCHEDULER, )).fetchone()[0] == "SM2 Mnemosyne"

    def test_logs_new_6(self):
        self.database().update_card_after_log_import = (lambda x, y, z: 0)
        self.database().before_1x_log_import()
        filename = os.path.join(os.getcwd(), "tests", "files", "new_6.txt")
        ScienceLogParser(self.database()).parse(filename)
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.ADDED_CARD, )).fetchone()[0] == 1
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.REPETITION, )).fetchone()[0] == 2
        sql_res = self.database().con.execute(\
            "select * from log where event_type=? and object_id='4c53e29a-f9e9-498b-8beb-d3a494f61bca.1.1'",
            (EventTypes.REPETITION, )).fetchone()
        assert sql_res[4] == 5
        assert sql_res[5] == 2.5
        assert sql_res[6] == 1
        assert sql_res[7] == 0
        assert sql_res[8] == 0
        assert sql_res[9] == 1
        assert sql_res[10] == 0
        assert sql_res[11] == 0
        assert sql_res[12] == 0
        assert sql_res[14] - sql_res[2] == 345600
        assert sql_res[13] == 0
        sql_res = self.database().con.execute(\
            """select * from log where event_type=? and object_id='4c53e29a-f9e9-498b-8beb-d3a494f61bca.1.1'
            order by _id desc limit 1""",
            (EventTypes.REPETITION, )).fetchone()
        assert sql_res[4] == 2
        assert sql_res[5] == 2.5
        assert sql_res[6] == 1
        assert sql_res[7] == 1
        assert sql_res[8] == 0
        assert sql_res[9] == 1
        assert sql_res[10] == 1
        assert sql_res[11] == 302986
        assert sql_res[12] == 10
        assert sql_res[14] - sql_res[2] == 475774
        assert sql_res[13] == 1

    def test_logs_imported_1(self):
        self.database().update_card_after_log_import = (lambda x, y, z: 0)
        self.database().before_1x_log_import()
        filename = os.path.join(os.getcwd(), "tests", "files",
                                "imported_1.txt")
        ScienceLogParser(self.database()).parse(filename)
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.ADDED_CARD, )).fetchone()[0] == 1
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.REPETITION, )).fetchone()[0] == 3
        assert self.database().con.execute(\
            "select acq_reps from log where event_type=? and object_id='f5d9bbe7'",
            (EventTypes.REPETITION, )).fetchone()[0] == 1
        assert self.database().con.execute(\
            "select ret_reps from log where event_type=? and object_id='f5d9bbe7'",
            (EventTypes.REPETITION, )).fetchone()[0] == 0
        assert self.database().con.execute(\
            "select acq_reps_since_lapse from log where event_type=? and object_id='f5d9bbe7'",
            (EventTypes.REPETITION, )).fetchone()[0] == 1
        assert self.database().con.execute(\
            """select acq_reps from log where event_type=? and object_id='f5d9bbe7'
             order by _id desc limit 1""",
            (EventTypes.REPETITION, )).fetchone()[0] == 1
        assert self.database().con.execute(\
            """select ret_reps from log where event_type=? and object_id='f5d9bbe7'
             order by _id desc limit 1""",
            (EventTypes.REPETITION, )).fetchone()[0] == 2
        assert self.database().con.execute(\
            """select acq_reps_since_lapse from log where event_type=? and object_id='f5d9bbe7'
            order by _id desc limit 1""",
            (EventTypes.REPETITION, )).fetchone()[0] == 1

    def test_logs_imported_2(self):
        self.database().update_card_after_log_import = (lambda x, y, z: 0)
        self.database().before_1x_log_import()
        filename = os.path.join(os.getcwd(), "tests", "files",
                                "imported_2.txt")
        ScienceLogParser(self.database()).parse(filename)
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.ADDED_CARD, )).fetchone()[0] == 1
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.REPETITION, )).fetchone()[0] == 1
        assert self.database().con.execute(\
            "select acq_reps from log where event_type=? and object_id='14670f10'",
            (EventTypes.REPETITION, )).fetchone()[0] == 1
        assert self.database().con.execute(\
            "select ret_reps from log where event_type=? and object_id='14670f10'",
            (EventTypes.REPETITION, )).fetchone()[0] == 0
        assert self.database().con.execute(\
            "select acq_reps_since_lapse from log where event_type=? and object_id='14670f10'",
            (EventTypes.REPETITION, )).fetchone()[0] == 1

    def test_logs_imported_3(self):
        self.database().update_card_after_log_import = (lambda x, y, z: 0)
        self.database().before_1x_log_import()
        filename = os.path.join(os.getcwd(), "tests", "files",
                                "imported_3.txt")
        ScienceLogParser(self.database()).parse(filename)
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.ADDED_CARD, )).fetchone()[0] == 1

    def test_restored_1(self):
        self.database().update_card_after_log_import = (lambda x, y, z: 0)
        self.database().before_1x_log_import()
        filename = os.path.join(os.getcwd(), "tests", "files",
                                "restored_1.txt")
        ScienceLogParser(self.database()).parse(filename)
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.ADDED_CARD, )).fetchone()[0] == 1
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.REPETITION, )).fetchone()[0] == 1
        sql_res = self.database().con.execute(\
            "select * from log where event_type=?",
            (EventTypes.REPETITION, )).fetchone()
        assert sql_res[4] == 1
        assert sql_res[5] == 2.36
        assert sql_res[6] == 23
        assert sql_res[7] == 8
        assert sql_res[8] == 2
        assert sql_res[9] == 0
        assert sql_res[10] == 0
        assert sql_res[11] == 89 * 24 * 60 * 60
        assert sql_res[12] == 0  # No last rep data.
        assert sql_res[14] - sql_res[2] == 0
        assert sql_res[13] == 5

    def test_restored_2(self):
        self.database().update_card_after_log_import = (lambda x, y, z: 0)
        self.database().before_1x_log_import()
        filename = os.path.join(os.getcwd(), "tests", "files",
                                "restored_2.txt")
        ScienceLogParser(self.database()).parse(filename)
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.ADDED_CARD, )).fetchone()[0] == 1

    def test_logs_act_interval(self):
        self.database().update_card_after_log_import = (lambda x, y, z: 0)
        self.database().before_1x_log_import()
        filename = os.path.join(os.getcwd(), "tests", "files",
                                "actinterval_1.txt")
        ScienceLogParser(self.database()).parse(filename)
        assert self.database().con.execute(\
            """select actual_interval from log where event_type=? and object_id='f1300e5a'
            order by _id desc limit 1""",
            (EventTypes.REPETITION, )).fetchone()[0] == 5

    def test_logs_deleted(self):
        self.database().update_card_after_log_import = (lambda x, y, z: 0)
        self.database().before_1x_log_import()
        filename = os.path.join(os.getcwd(), "tests", "files", "delete_1.txt")
        ScienceLogParser(self.database()).parse(filename)
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.ADDED_CARD, )).fetchone()[0] == 1
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.DELETED_CARD, )).fetchone()[0] == 1

    def test_logs_corrupt_1(self):  # Wrong data, missing creation event.
        self.database().update_card_after_log_import = (lambda x, y, z: 0)
        self.database().before_1x_log_import()
        filename = os.path.join(os.getcwd(), "tests", "files", "corrupt_1.txt")
        ScienceLogParser(self.database()).parse(filename)
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.ADDED_CARD, )).fetchone()[0] == 1
        assert self.database().con.execute(\
            "select count() from log where object_id=?",
            ("4b59b830", )).fetchone()[0] == 3

    def test_logs_corrupt_2(self):  # Wrong data, isolated deletion event.
        self.database().update_card_after_log_import = (lambda x, y, z: 0)
        self.database().before_1x_log_import()
        filename = os.path.join(os.getcwd(), "tests", "files", "corrupt_2.txt")
        ScienceLogParser(self.database()).parse(filename)
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.ADDED_CARD, )).fetchone()[0] == 0
        assert self.database().con.execute(\
            "select count() from log where object_id=?",
            ("4b59b830", )).fetchone()[0] == 0

    def test_two_mem_files_sharing_same_logs(self):
        filename = os.path.join(os.getcwd(), "tests", "files", "basedir_2_mem",
                                "deck1.mem")
        self.mem_importer().do_import(filename)
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.REPETITION, )).fetchone()[0] == 1
        filename = os.path.join(os.getcwd(), "tests", "files", "basedir_2_mem",
                                "deck2.mem")
        self.mem_importer().do_import(filename)
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.REPETITION, )).fetchone()[0] == 3
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.ADDED_CARD, )).fetchone()[0] == 2
        card = self.database().card("4c8fff73", is_id_internal=False)
        assert self.database().average_thinking_time(card) == 1.5
        assert self.database().total_thinking_time(card) == 3.0
        assert self.database().card_count_for_grade(0, active_only=True) == 2
        tag = self.database().get_or_create_tag_with_name("666")
        assert self.database().card_count_for_grade_and_tag(
            0, tag, active_only=True) == 0
        from mnemosyne.libmnemosyne.statistics_pages.grades import Grades
        page = Grades(component_manager=self.mnemosyne.component_manager)
        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tag_tree = TagTree(self.mnemosyne.component_manager,
                                count_cards=False)
        self.nodes = self.tag_tree.nodes()
        for index, node in enumerate(self.nodes):
            if node == "666":
                page.prepare_statistics(index)
                assert page.y == [0, 0, 0, 0, 0, 0, 0]
        page.prepare_statistics(-1)
        assert page.y == [0, 2, 0, 0, 0, 0, 0]

    def test_bz2(self):
        filename = os.path.join(os.getcwd(), "tests", "files", "basedir_bz2",
                                "default.mem")
        self.mem_importer().do_import(filename)
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.REPETITION, )).fetchone()[0] == 0
        assert self.database().con.execute(\
            "select count() from log where event_type=?",
            (EventTypes.ADDED_CARD, )).fetchone()[0] == 1
        assert self.database().con.execute(\
            "select count() from log where object_id=?",
            ("82f2ed0d", )).fetchone()[0] == 0

    def test_sch(self):
        self.controller().show_import_file_dialog()
        assert self.database().card_count_scheduled_n_days_ago(0) == 1

    def test_upgrade(self):
        old_data_dir = os.path.join(os.getcwd(), "tests", "files",
                                    "basedir_bz2")
        from mnemosyne.libmnemosyne.upgrades.upgrade1 import Upgrade1
        Upgrade1(self.mnemosyne.component_manager).upgrade_from_old_data_dir(
            old_data_dir)
        assert self.config()["dvipng"].rstrip() == \
               "dvipng -D 300 -T tight tmp.dvi\necho"
        assert "14pt" in self.config()["latex_preamble"]
        assert self.config()["user_id"] == "f3fb13c7"
        assert self.log().log_index_of_last_upload() == 2

        assert os.path.exists(
            os.path.join(old_data_dir,
                         "DIRECTORY_NO_LONGER_USED_BY_MNEMOSYNE2"))
        assert os.path.exists(
            os.path.join(self.mnemosyne.config().data_dir, "history",
                         "a_2.bz2"))
        log = open(os.path.join(self.mnemosyne.config().data_dir, "log.txt"))
        assert log.readline().strip() == \
               "2005-11-01 09:29:08 : Imported item 82f2ed0d 0 0 0 0 0"

    def teardown(self):
        filename = os.path.join(os.getcwd(), "tests", "files", "basedir_bz2",
                                "DIRECTORY_NO_LONGER_USED_BY_MNEMOSYNE2")
        if os.path.exists(filename):
            os.remove(filename)
        filename = os.path.join(os.getcwd(), "tests", "files", "a.png")
        if os.path.exists(filename):
            os.remove(filename)
        filename = os.path.join(os.getcwd(), "tests", "files", "a.ogg")
        if os.path.exists(filename):
            os.remove(filename)
        dirname = os.path.join(os.getcwd(), "tests", "files", "figs")
        if os.path.exists(dirname):
            shutil.rmtree(dirname)
        dirname = os.path.join(os.getcwd(), "tests", "files", "soundfiles")
        if os.path.exists(dirname):
            shutil.rmtree(dirname)
        MnemosyneTest.teardown(self)
Ejemplo n.º 27
0
class TagsTreeWdgt(Component, QtWidgets.QWidget):

    """Displays all the tags in a tree together with check boxes. """

    tags_changed_signal = QtCore.pyqtSignal()

    def __init__(self, acquire_database=None, **kwds):
        super().__init__(**kwds)
        self.layout = QtWidgets.QVBoxLayout(self)
        self.tree_wdgt = QtWidgets.QTreeWidget(self)
        self.tree_wdgt.setColumnCount(2)
        self.tree_wdgt.setColumnHidden(1, True)
        self.tree_wdgt.setColumnHidden(NODE, True)
        self.tree_wdgt.setHeaderHidden(True)
        self.tree_wdgt.setSelectionMode(\
            QtWidgets.QAbstractItemView.ExtendedSelection)
        self.delegate = TagDelegate(\
            component_manager=kwds["component_manager"], parent=self)
        self.tree_wdgt.setItemDelegate(self.delegate)
        self.delegate.rename_node.connect(self.rename_node)
        self.delegate.redraw_node.connect(self.redraw_node)
        self.layout.addWidget(self.tree_wdgt)
        self.tree_wdgt.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.tree_wdgt.customContextMenuRequested.connect(\
            self.context_menu)
        self.acquire_database = acquire_database

    def selected_nodes_which_can_be_renamed(self):
        nodes = []
        for index in self.tree_wdgt.selectedIndexes():
            node_index = \
                index.model().index(index.row(), NODE, index.parent())
            node = index.model().data(node_index)
            if node in self.nodes_which_can_be_renamed:
                nodes.append(node)
        return nodes

    def selected_nodes_which_can_be_deleted(self):
        nodes = []
        for index in self.tree_wdgt.selectedIndexes():
            node_index = \
                index.model().index(index.row(), NODE, index.parent())
            node = index.model().data(node_index)
            if node in self.nodes_which_can_be_deleted:
                nodes.append(node)
        return nodes

    def context_menu(self, point):
        menu = QtWidgets.QMenu(self)
        to_rename = self.selected_nodes_which_can_be_renamed()
        if len(to_rename) >= 1:
            rename_action = QtWidgets.QAction(_("&Rename"), menu)
            rename_action.triggered.connect(self.menu_rename)
            rename_action.setShortcut(QtCore.Qt.Key_Enter)
            menu.addAction(rename_action)
            if len(to_rename) > 1:
                rename_action.setEnabled(False)
        to_delete = self.selected_nodes_which_can_be_deleted()
        if len(to_delete) >= 1:
            delete_action = QtWidgets.QAction(_("&Delete"), menu)
            delete_action.triggered.connect(self.menu_delete)
            delete_action.setShortcut(QtGui.QKeySequence.Delete)
            menu.addAction(delete_action)
        if len(to_delete) + len(to_rename) >= 1:
            menu.exec_(self.tree_wdgt.mapToGlobal(point))

    def keyPressEvent(self, event):
        if event.key() in [QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return]:
            self.menu_rename()
        elif event.key() in [QtCore.Qt.Key_Delete, QtCore.Qt.Key_Backspace]:
            self.menu_delete()
        else:
            QtWidgets.QWidget.keyPressEvent(self, event)

    def menu_rename(self):
        nodes = self.selected_nodes_which_can_be_renamed()
        # If there are tags selected, this means that we could only have got
        # after pressing return on an actual edit, due to our custom
        # 'keyPressEvent'. We should not continue in that case.
        if len(nodes) == 0:
            return
        # We display the full node (i.e. all levels including ::), so that
        # the hierarchy can be changed upon editing.

        from mnemosyne.pyqt_ui.ui_rename_tag_dlg import Ui_RenameTagDlg

        class RenameDlg(QtWidgets.QDialog, Ui_RenameTagDlg):
            def __init__(self, old_tag_name):
                super().__init__()
                self.setupUi(self)
                self.tag_name.setText(\
                    old_tag_name.replace("::" + _("Untagged"), "" ))

        old_tag_name = nodes[0]
        dlg = RenameDlg(old_tag_name)
        if dlg.exec_() == QtWidgets.QDialog.Accepted:
            self.rename_node(nodes[0], dlg.tag_name.text())

    def menu_delete(self):
        nodes = self.selected_nodes_which_can_be_deleted()
        if len(nodes) == 0:
            return
        if len(nodes) > 1:
            question = _("Delete these tags? Cards with these tags will not be deleted.")
        else:
            question = _("Delete this tag? Cards with this tag will not be deleted.")
        answer = self.main_widget().show_question\
            (question, _("&OK"), _("&Cancel"), "")
        if answer == 1: # Cancel.
            return
        self.delete_nodes(nodes)

    def create_tree(self, tree, qt_parent):
        for node in tree:
            node_name = "%s (%d)" % \
                (self.tag_tree.display_name_for_node[node],
                self.tag_tree.card_count_for_node[node])
            node_item = QtWidgets.QTreeWidgetItem(qt_parent, [node_name, node], 0)
            node_item.setFlags(node_item.flags() | \
                QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsTristate)
            if node not in ["__ALL__", "__UNTAGGED__"] and \
                not "::" + _("Untagged") in node:
                node_item.setFlags(node_item.flags() | \
                    QtCore.Qt.ItemIsEditable)
                self.nodes_which_can_be_renamed.append(node)
                self.nodes_which_can_be_deleted.append(node)
            if node in self.tag_tree.tag_for_node:
                # Since node_item seems mutable, we cannot use a dict.
                self.node_items.append(node_item)
                self.tag_for_node_item.append(self.tag_tree.tag_for_node[node])
            node_item.setData(NODE, QtCore.Qt.DisplayRole,
                    QtCore.QVariant(node))
            self.create_tree(tree=self.tag_tree[node], qt_parent=node_item)

    def display(self, criterion=None):
        # Create criterion if needed.
        if criterion is None:
            criterion = DefaultCriterion(self.component_manager)
            for tag in self.database().tags():
                criterion._tag_ids_active.add(tag._id)
        # Create tree.
        self.tag_tree = TagTree(self.component_manager)
        self.tree_wdgt.clear()
        self.node_items = []
        self.tag_for_node_item = []
        self.nodes_which_can_be_deleted = []
        self.nodes_which_can_be_renamed = []
        node = "__ALL__"
        node_name = "%s (%d)" % (self.tag_tree.display_name_for_node[node],
            self.tag_tree.card_count_for_node[node])
        root = self.tag_tree[node]
        root_item = QtWidgets.QTreeWidgetItem(\
            self.tree_wdgt, [node_name, node], 0)
        root_item.setFlags(root_item.flags() | \
           QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsTristate)
        root_item.setCheckState(0, QtCore.Qt.Checked)
        self.create_tree(self.tag_tree[node], qt_parent=root_item)
        # Set forbidden tags.
        if len(criterion._tag_ids_forbidden):
            for i in range(len(self.node_items)):
                node_item = self.node_items[i]
                tag = self.tag_for_node_item[i]
                if tag._id in criterion._tag_ids_forbidden:
                    node_item.setCheckState(0, QtCore.Qt.Checked)
                else:
                    node_item.setCheckState(0, QtCore.Qt.Unchecked)
        # Set active tags.
        else:
            # We first set all the tags inactive. We cannot do this in the
            # second branch of the upcoming 'if' statement, as then an
            # inactive parent tag coming later in the list will deactivate
            # active child tags coming earlier in the list.
            for node_item in self.node_items:
                node_item.setCheckState(0, QtCore.Qt.Unchecked)
            for i in range(len(self.node_items)):
                node_item = self.node_items[i]
                tag = self.tag_for_node_item[i]
                if tag._id in criterion._tag_ids_active:
                    node_item.setCheckState(0, QtCore.Qt.Checked)
        # Restore state of the tree.
        self.tree_wdgt.expandAll()
        collapsed = self.config()["tag_tree_wdgt_state"]
        if collapsed is None:
            collapsed = []
        iterator = QtWidgets.QTreeWidgetItemIterator(self.tree_wdgt)
        while iterator.value():
            if iterator.value().text(1) in collapsed:
                iterator.value().setExpanded(False)
            iterator += 1

    def checked_to_active_tags_in_criterion(self, criterion):
        for i in range(len(self.node_items)):
            tag = self.tag_for_node_item[i]
            if self.node_items[i].checkState(0) == QtCore.Qt.Checked:
                criterion._tag_ids_active.add(tag._id)
        criterion._tag_ids_forbidden = set()
        return criterion

    def checked_to_forbidden_tags_in_criterion(self, criterion):
        for i in range(len(self.node_items)):
            tag = self.tag_for_node_item[i]
            if self.node_items[i].checkState(0) == QtCore.Qt.Checked:
                criterion._tag_ids_forbidden.add(tag._id)
        criterion._tag_ids_active = \
            set([tag._id for tag in self.tag_for_node_item])
        return criterion

    def unchecked_to_forbidden_tags_in_criterion(self, criterion):
        for i in range(len(self.node_items)):
            tag = self.tag_for_node_item[i]
            if self.node_items[i].checkState(0) == QtCore.Qt.Unchecked:
                criterion._tag_ids_forbidden.add(tag._id)
        return criterion

    def save_criterion(self):
        self.saved_criterion = DefaultCriterion(self.component_manager)
        self.checked_to_active_tags_in_criterion(self.saved_criterion)
        # We also save the unchecked tags as this will allow us to identify
        # any new tags created afterwards.
        self.unchecked_to_forbidden_tags_in_criterion(self.saved_criterion)
        # Now we've saved the checked state of the tree.
        # Saving and restoring the selected state is less trivial, because
        # in the case of trees, the model indexes have parents which become
        # invalid when creating the widget.
        # The solution would be to save tags and reselect those in the new
        # widget.

    def restore_criterion(self):
        new_criterion = DefaultCriterion(self.component_manager)
        for tag in self.database().tags():
            if tag._id in self.saved_criterion._tag_ids_active or \
               tag._id not in self.saved_criterion._tag_ids_forbidden:
               # Second case deals with recently added tag.
                new_criterion._tag_ids_active.add(tag._id)
        self.display(new_criterion)

    def store_tree_state(self):

        """Store which nodes are collapsed. """

        collapsed = []
        iterator = QtWidgets.QTreeWidgetItemIterator(self.tree_wdgt)
        while iterator.value():
            if not iterator.value().isExpanded():
                collapsed.append(iterator.value().text(1))
            iterator += 1
        self.config()["tag_tree_wdgt_state"] = collapsed

    def rename_node(self, node, new_name):
        if self.acquire_database:
            self.acquire_database()
        self.save_criterion()
        self.store_tree_state()
        self.tag_tree.rename_node(node, new_name)
        self.restore_criterion()
        self.tags_changed_signal.emit()

    def delete_nodes(self, nodes):
        if self.acquire_database:
            self.acquire_database()
        self.save_criterion()
        self.store_tree_state()
        for node in nodes:
            self.tag_tree.delete_subtree(node)
        self.restore_criterion()
        self.tags_changed_signal.emit()

    def redraw_node(self, node):

        """When renaming a tag to the same name, we need to redraw the node
        to show the card count again.

        """

        # We do the redrawing in a rather hackish way now, simply by
        # recreating the widget. Could be sped up, but at the expense of more
        # complicated code.
        self.save_criterion()
        self.restore_criterion()

    def rebuild(self):

        """To be called when external events invalidate the tag tree,
        e.g. due to edits in the card browser widget.

        """

        self.save_criterion()
        self.store_tree_state()
        self.tag_tree = TagTree(self.component_manager)
        self.restore_criterion()

    def closeEvent(self, event):
        self.store_tree_state()
Ejemplo n.º 28
0
class TestTagTree(MnemosyneTest):
    def test_1(self):
        fact_data = {"f": "question", "b": "answer"}
        card_type = self.card_type_with_id("1")
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=[])[0]
        self.controller().save_file()
        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)
        assert len(list(self.tree.keys())) == 2
        assert self.tree['__ALL__'] == ['__UNTAGGED__']

    def test_2(self):
        fact_data = {"f": "question", "b": "answer"}
        card_type = self.card_type_with_id("1")
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["tag_1"])[0]
        self.controller().save_file()
        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)
        assert len(list(self.tree.keys())) == 3
        assert self.tree['__ALL__'] == ['tag_1', '__UNTAGGED__']

    def test_3(self):
        fact_data = {"f": "question", "b": "answer"}
        card_type = self.card_type_with_id("1")
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["a"])[0]
        fact_data = {"f": "question2", "b": "answer2"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["Z"])[0]
        fact_data = {"f": "question3", "b": "answer3"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["a::b"])[0]
        fact_data = {"f": "question4", "b": "answer4"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["a::c"])[0]
        fact_data = {"f": "question5", "b": "answer5"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["b::c::d"])[0]
        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)
        assert self.tree.card_count_for_node["__ALL__"] == 5
        assert self.tree.card_count_for_node["a"] == 3
        assert self.tree.card_count_for_node["Z"] == 1
        assert self.tree.card_count_for_node["a::b"] == 1
        assert self.tree.card_count_for_node["a::c"] == 1
        assert self.tree.card_count_for_node["b"] == 1
        assert self.tree.card_count_for_node["b::c"] == 1
        assert self.tree.card_count_for_node["b::c::d"] == 1
        assert self.tree.nodes() == \
               ["a", "a::Untagged", "a::b", "a::c", "b", "b::c", "b::c::d",
                "Z", "__UNTAGGED__"]

    def test_4(self):
        card_type = self.card_type_with_id("1")
        fact_data = {"f": "question4", "b": "answer4"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["a::b"])[0]
        fact_data = {"f": "question", "b": "answer"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["a"])[0]
        fact_data = {"f": "question2", "b": "answer2"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["Z"])[0]
        fact_data = {"f": "question3", "b": "answer3"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["a::c"])[0]
        fact_data = {"f": "question5", "b": "answer5"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["b::c::d"])[0]
        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)
        assert self.tree.card_count_for_node["__ALL__"] == 5
        assert self.tree.card_count_for_node["a"] == 3
        assert self.tree.card_count_for_node["Z"] == 1
        assert self.tree.card_count_for_node["a::b"] == 1
        assert self.tree.card_count_for_node["a::c"] == 1
        assert self.tree.card_count_for_node["b"] == 1
        assert self.tree.card_count_for_node["b::c"] == 1
        assert self.tree.card_count_for_node["b::c::d"] == 1

        self.tree.rename_node("Z", "Z::Z")
        self.tree.rename_node("b::c", "b::cc")
        assert self.tree.card_count_for_node["__ALL__"] == 5
        assert self.tree.card_count_for_node["a"] == 3
        assert self.tree.card_count_for_node["Z"] == 1
        assert self.tree.card_count_for_node["Z::Z"] == 1
        assert self.tree.card_count_for_node["a::b"] == 1
        assert self.tree.card_count_for_node["a::c"] == 1
        assert self.tree.card_count_for_node["b"] == 1
        assert self.tree.card_count_for_node["b::cc"] == 1
        assert self.tree.card_count_for_node["b::cc::d"] == 1

    def test_5(self):
        card_type = self.card_type_with_id("1")
        fact_data = {"f": "question4", "b": "answer4"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["a::b"])[0]
        fact_data = {"f": "question", "b": "answer"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["a"])[0]
        fact_data = {"f": "question2", "b": "answer2"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["Z"])[0]
        fact_data = {"f": "question3", "b": "answer3"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["a::c"])[0]
        fact_data = {"f": "question5", "b": "answer5"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["b::c::d"])[0]
        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)
        assert self.tree.card_count_for_node["__ALL__"] == 5
        assert self.tree.card_count_for_node["a"] == 3
        assert self.tree.card_count_for_node["Z"] == 1
        assert self.tree.card_count_for_node["a::b"] == 1
        assert self.tree.card_count_for_node["a::c"] == 1
        assert self.tree.card_count_for_node["b"] == 1
        assert self.tree.card_count_for_node["b::c"] == 1
        assert self.tree.card_count_for_node["b::c::d"] == 1

        self.tree.rename_node("b::c", "b")
        assert self.tree.card_count_for_node["__ALL__"] == 5
        assert self.tree.card_count_for_node["a"] == 3
        assert self.tree.card_count_for_node["Z"] == 1
        assert self.tree.card_count_for_node["a::b"] == 1
        assert self.tree.card_count_for_node["a::c"] == 1
        assert self.tree.card_count_for_node["b"] == 1
        assert self.tree.card_count_for_node["b::d"] == 1

    def test_rename_to_existing_tag(self):
        card_type = self.card_type_with_id("1")
        fact_data = {"f": "question4", "b": "answer4"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["tag1"])[0]
        fact_data = {"f": "question", "b": "answer"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["tag2"])[0]
        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)
        assert len(self.database().tags()) == 3
        self.tree.rename_node("tag1", "tag2")
        assert self.tree.card_count_for_node["tag2"] == 2
        assert len(self.database().tags()) == 2

    def test_rename_to_existing_tag_2(self):
        card_type = self.card_type_with_id("1")
        fact_data = {"f": "question4", "b": "answer4"}
        card = self.controller().create_new_cards(
            fact_data,
            card_type,
            grade=-1,
            tag_names=["Xx::vb::test", "Xx::aa::vb::test"])[0]
        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)
        assert len(self.database().tags()) == 3
        self.tree.rename_node("Xx::aa::vb::test", "Xx::vb::test")
        assert self.tree.card_count_for_node["Xx::vb::test"] == 1
        assert "Xx::aa::vb::test" not in self.tree.card_count_for_node
        assert "," not in  self.database().con.execute(\
            "select tags from cards where _id=?", (card._id,)).fetchone()[0]
        assert self.database().con.execute(\
            "select count() from tags_for_card").fetchone()[0] == 1
        assert len(self.database().tags()) == 2

    def test_rename_to_empty(self):
        card_type = self.card_type_with_id("1")
        fact_data = {"f": "question4", "b": "answer4"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["tag1"])[0]
        fact_data = {"f": "question", "b": "answer"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["tag2"])[0]
        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)
        self.tree.rename_node("tag1", "")
        assert self.tree.card_count_for_node["__UNTAGGED__"] == 1
        assert len(self.database().tags()) == 2

    def test_rename_to_empty_2(self):
        card_type = self.card_type_with_id("1")
        fact_data = {"f": "question4", "b": "answer4"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["A::tag1"])[0]
        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)
        self.tree.rename_node("A", "")
        assert self.tree.card_count_for_node["tag1"] == 1
        assert len(self.database().tags()) == 2

    def test_rename_to_empty_3(self):
        card_type = self.card_type_with_id("1")
        fact_data = {"f": "question4", "b": "answer4"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["A::tag1"])[0]
        fact_data = {"f": "question", "b": "answer"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["A"])[0]
        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)
        self.tree.rename_node("A", "")
        assert self.tree.card_count_for_node["__UNTAGGED__"] == 1
        assert self.tree.card_count_for_node["tag1"] == 1
        assert len(self.database().tags()) == 2

    def test_rename_to_forbidden(self):
        card_type = self.card_type_with_id("1")
        fact_data = {"f": "question4", "b": "answer4"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["A::tag1"])[0]
        fact_data = {"f": "question", "b": "answer"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["A"])[0]
        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)
        self.tree.rename_node("A", "__UNTAGGED__")
        assert "__UNTAGGED__" in self.tree.card_count_for_node

    def test_delete(self):
        card_type = self.card_type_with_id("1")
        fact_data = {"f": "question4", "b": "answer4"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["a::b"])[0]
        fact_data = {"f": "question", "b": "answer"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["a"])[0]
        fact_data = {"f": "question2", "b": "answer2"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["Z"])[0]
        fact_data = {"f": "question3", "b": "answer3"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["a::c"])[0]
        fact_data = {"f": "question5", "b": "answer5"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["b::c::d"])[0]
        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)
        assert self.tree.card_count_for_node["__ALL__"] == 5
        assert self.tree.card_count_for_node["a"] == 3
        assert self.tree.card_count_for_node["Z"] == 1
        assert self.tree.card_count_for_node["a::b"] == 1
        assert self.tree.card_count_for_node["a::c"] == 1
        assert self.tree.card_count_for_node["b"] == 1
        assert self.tree.card_count_for_node["b::c"] == 1
        assert self.tree.card_count_for_node["b::c::d"] == 1

        self.tree.delete_subtree("b::c")

        card = self.database().card(card._id, is_id_internal=True)
        assert card.tag_string() == ""
        self.database().con.execute("select tags from cards where _id=?",
                                    (card._id, )).fetchone()[0] == ""

        assert self.tree.card_count_for_node["__ALL__"] == 5
        assert self.tree.card_count_for_node["a"] == 3
        assert self.tree.card_count_for_node["Z"] == 1
        assert self.tree.card_count_for_node["a::b"] == 1
        assert self.tree.card_count_for_node["a::c"] == 1
        assert self.tree.card_count_for_node["__UNTAGGED__"] == 1
        assert "b" not in self.tree.card_count_for_node
        assert "b::c" not in self.tree.card_count_for_node
        assert "b::c::d" not in self.tree.card_count_for_node

    def test_delete_2(self):
        card_type = self.card_type_with_id("1")
        fact_data = {"f": "question4", "b": "answer4"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["a::b"])[0]
        fact_data = {"f": "question", "b": "answer"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["a"])[0]
        fact_data = {"f": "question2", "b": "answer2"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["Z"])[0]
        fact_data = {"f": "question3", "b": "answer3"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["a::c"])[0]
        fact_data = {"f": "question5", "b": "answer5"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["b::c::d",
                                                             "b"])[0]
        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)

        assert self.tree.card_count_for_node["__ALL__"] == 5
        assert self.tree.card_count_for_node["a"] == 3
        assert self.tree.card_count_for_node["Z"] == 1
        assert self.tree.card_count_for_node["a::b"] == 1
        assert self.tree.card_count_for_node["a::c"] == 1
        assert self.tree.card_count_for_node["b"] == 1
        assert self.tree.card_count_for_node["b::c"] == 1
        assert self.tree.card_count_for_node["b::c::d"] == 1

        self.tree.delete_subtree("b::c")
        card = self.database().card(card._id, is_id_internal=True)
        assert card.tag_string() == ""
        self.database().con.execute("select tags from cards where _id=?",
                                    (card._id, )).fetchone()[0] == "b"

        assert self.tree.card_count_for_node["__ALL__"] == 5
        assert self.tree.card_count_for_node["a"] == 3
        assert self.tree.card_count_for_node["Z"] == 1
        assert self.tree.card_count_for_node["a::b"] == 1
        assert self.tree.card_count_for_node["a::c"] == 1
        assert "b" not in self.tree.card_count_for_node
        assert "__UNTAGGED__" in self.tree.card_count_for_node
        assert "b::c" not in self.tree.card_count_for_node
        assert "b::c::d" not in self.tree.card_count_for_node

    def test_count_1(self):
        card_type = self.card_type_with_id("1")
        fact_data = {"f": "question4", "b": "answer4"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["b"])[0]
        fact_data = {"f": "question", "b": "answer"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["b", "b"])[0]

        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)

        assert self.tree.card_count_for_node["__ALL__"] == 2

    def test_count_2(self):
        card_type = self.card_type_with_id("1")
        fact_data = {"f": "question4", "b": "answer4"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["X::a"])[0]
        fact_data = {"f": "question", "b": "answer"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["X::a",
                                                             "X::b"])[0]

        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)

        assert self.tree.card_count_for_node["__ALL__"] == 2
        assert self.tree.card_count_for_node["X"] == 2

    def test_delete_forbidden(self):
        card_type = self.card_type_with_id("1")
        fact_data = {"f": "question", "b": "answer"}
        card = self.controller().create_new_cards(fact_data,
                                                  card_type,
                                                  grade=-1,
                                                  tag_names=["forbidden"])[0]
        assert self.database().active_count() == 1

        c = DefaultCriterion(self.mnemosyne.component_manager)
        c.deactivated_card_type_fact_view_ids = set()
        c._tag_ids_active = set(
            [self.database().get_or_create_tag_with_name("active")._id, 1])
        c._tag_ids_forbidden = set(
            [self.database().get_or_create_tag_with_name("forbidden")._id])
        self.database().set_current_criterion(c)
        assert self.database().active_count() == 0

        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)
        self.tree.delete_subtree("forbidden")
        assert self.database().active_count() == 1
Ejemplo n.º 29
0
class TestTagTree(MnemosyneTest):

    def test_1(self):
        fact_data = {"f": "question",
                     "b": "answer"}
        card_type = self.card_type_with_id("1")
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=[])[0]
        self.controller().save_file()
        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)
        assert len(self.tree.keys()) == 2
        assert self.tree['__ALL__'] == [u'__UNTAGGED__']

    def test_2(self):
        fact_data = {"f": "question",
                     "b": "answer"}
        card_type = self.card_type_with_id("1")
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["tag_1"])[0]
        self.controller().save_file()
        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)
        assert len(self.tree.keys()) == 3
        assert self.tree['__ALL__'] == [u'tag_1', u'__UNTAGGED__']

    def test_3(self):
        fact_data = {"f": "question", "b": "answer"}
        card_type = self.card_type_with_id("1")
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["a"])[0]
        fact_data = {"f": "question2", "b": "answer2"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["Z"])[0]
        fact_data = {"f": "question3",  "b": "answer3"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["a::b"])[0]
        fact_data = {"f": "question4",  "b": "answer4"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["a::c"])[0]
        fact_data = {"f": "question5",  "b": "answer5"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["b::c::d"])[0]
        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)
        assert self.tree.card_count_for_node["__ALL__"] == 5
        assert self.tree.card_count_for_node["a"] == 3
        assert self.tree.card_count_for_node["Z"] == 1
        assert self.tree.card_count_for_node["a::b"] == 1
        assert self.tree.card_count_for_node["a::c"] == 1
        assert self.tree.card_count_for_node["b"] == 1
        assert self.tree.card_count_for_node["b::c"] == 1
        assert self.tree.card_count_for_node["b::c::d"] == 1

    def test_4(self):
        card_type = self.card_type_with_id("1")
        fact_data = {"f": "question4",  "b": "answer4"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["a::b"])[0]
        fact_data = {"f": "question", "b": "answer"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["a"])[0]
        fact_data = {"f": "question2", "b": "answer2"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["Z"])[0]
        fact_data = {"f": "question3",  "b": "answer3"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["a::c"])[0]
        fact_data = {"f": "question5",  "b": "answer5"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["b::c::d"])[0]
        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)
        assert self.tree.card_count_for_node["__ALL__"] == 5
        assert self.tree.card_count_for_node["a"] == 3
        assert self.tree.card_count_for_node["Z"] == 1
        assert self.tree.card_count_for_node["a::b"] == 1
        assert self.tree.card_count_for_node["a::c"] == 1
        assert self.tree.card_count_for_node["b"] == 1
        assert self.tree.card_count_for_node["b::c"] == 1
        assert self.tree.card_count_for_node["b::c::d"] == 1

        self.tree.rename_node("Z", "Z::Z")
        self.tree.rename_node("b::c", "b::cc")
        assert self.tree.card_count_for_node["__ALL__"] == 5
        assert self.tree.card_count_for_node["a"] == 3
        assert self.tree.card_count_for_node["Z"] == 1
        assert self.tree.card_count_for_node["Z::Z"] == 1
        assert self.tree.card_count_for_node["a::b"] == 1
        assert self.tree.card_count_for_node["a::c"] == 1
        assert self.tree.card_count_for_node["b"] == 1
        assert self.tree.card_count_for_node["b::cc"] == 1
        assert self.tree.card_count_for_node["b::cc::d"] == 1

    def test_5(self):
        card_type = self.card_type_with_id("1")
        fact_data = {"f": "question4",  "b": "answer4"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["a::b"])[0]
        fact_data = {"f": "question", "b": "answer"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["a"])[0]
        fact_data = {"f": "question2", "b": "answer2"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["Z"])[0]
        fact_data = {"f": "question3",  "b": "answer3"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["a::c"])[0]
        fact_data = {"f": "question5",  "b": "answer5"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["b::c::d"])[0]
        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)
        assert self.tree.card_count_for_node["__ALL__"] == 5
        assert self.tree.card_count_for_node["a"] == 3
        assert self.tree.card_count_for_node["Z"] == 1
        assert self.tree.card_count_for_node["a::b"] == 1
        assert self.tree.card_count_for_node["a::c"] == 1
        assert self.tree.card_count_for_node["b"] == 1
        assert self.tree.card_count_for_node["b::c"] == 1
        assert self.tree.card_count_for_node["b::c::d"] == 1

        self.tree.rename_node("b::c", "b")
        assert self.tree.card_count_for_node["__ALL__"] == 5
        assert self.tree.card_count_for_node["a"] == 3
        assert self.tree.card_count_for_node["Z"] == 1
        assert self.tree.card_count_for_node["a::b"] == 1
        assert self.tree.card_count_for_node["a::c"] == 1
        assert self.tree.card_count_for_node["b"] == 1
        assert self.tree.card_count_for_node["b::d"] == 1

    def test_rename_to_existing_tag(self):
        card_type = self.card_type_with_id("1")
        fact_data = {"f": "question4",  "b": "answer4"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["tag1"])[0]
        fact_data = {"f": "question", "b": "answer"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["tag2"])[0]
        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)
        assert len(self.database().tags()) == 3
        self.tree.rename_node("tag1", "tag2")
        assert self.tree.card_count_for_node["tag2"] == 2
        assert len(self.database().tags()) == 2

    def test_rename_to_existing_tag_2(self):
        card_type = self.card_type_with_id("1")
        fact_data = {"f": "question4",  "b": "answer4"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["Xx::vb::test", "Xx::aa::vb::test"])[0]
        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)
        assert len(self.database().tags()) == 3
        self.tree.rename_node("Xx::aa::vb::test", "Xx::vb::test")
        assert self.tree.card_count_for_node["Xx::vb::test"] == 1
        assert "Xx::aa::vb::test" not in self.tree.card_count_for_node
        assert "," not in  self.database().con.execute(\
            "select tags from cards where _id=?", (card._id,)).fetchone()[0]
        assert self.database().con.execute(\
            "select count() from tags_for_card").fetchone()[0] == 1
        assert len(self.database().tags()) == 2

    def test_rename_to_empty(self):
        card_type = self.card_type_with_id("1")
        fact_data = {"f": "question4",  "b": "answer4"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["tag1"])[0]
        fact_data = {"f": "question", "b": "answer"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["tag2"])[0]
        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)
        self.tree.rename_node("tag1", "")
        assert self.tree.card_count_for_node["__UNTAGGED__"] == 1
        assert len(self.database().tags()) == 2

    def test_rename_to_empty_2(self):
        card_type = self.card_type_with_id("1")
        fact_data = {"f": "question4",  "b": "answer4"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["A::tag1"])[0]
        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)
        self.tree.rename_node("A", "")
        assert self.tree.card_count_for_node["tag1"] == 1
        assert len(self.database().tags()) == 2

    def test_rename_to_empty_3(self):
        card_type = self.card_type_with_id("1")
        fact_data = {"f": "question4",  "b": "answer4"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["A::tag1"])[0]
        fact_data = {"f": "question", "b": "answer"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["A"])[0]
        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)
        self.tree.rename_node("A", "")
        assert self.tree.card_count_for_node["__UNTAGGED__"] == 1
        assert self.tree.card_count_for_node["tag1"] == 1
        assert len(self.database().tags()) == 2

    def test_rename_to_forbidden(self):
        card_type = self.card_type_with_id("1")
        fact_data = {"f": "question4",  "b": "answer4"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["A::tag1"])[0]
        fact_data = {"f": "question", "b": "answer"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["A"])[0]
        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)
        self.tree.rename_node("A", "__UNTAGGED__")
        assert "__UNTAGGED__" in self.tree.card_count_for_node

    def test_delete(self):
        card_type = self.card_type_with_id("1")
        fact_data = {"f": "question4",  "b": "answer4"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["a::b"])[0]
        fact_data = {"f": "question", "b": "answer"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["a"])[0]
        fact_data = {"f": "question2", "b": "answer2"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["Z"])[0]
        fact_data = {"f": "question3",  "b": "answer3"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["a::c"])[0]
        fact_data = {"f": "question5",  "b": "answer5"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["b::c::d"])[0]
        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)
        assert self.tree.card_count_for_node["__ALL__"] == 5
        assert self.tree.card_count_for_node["a"] == 3
        assert self.tree.card_count_for_node["Z"] == 1
        assert self.tree.card_count_for_node["a::b"] == 1
        assert self.tree.card_count_for_node["a::c"] == 1
        assert self.tree.card_count_for_node["b"] == 1
        assert self.tree.card_count_for_node["b::c"] == 1
        assert self.tree.card_count_for_node["b::c::d"] == 1

        self.tree.delete_subtree("b::c")

        card = self.database().card(card._id, is_id_internal=True)
        assert card.tag_string() == ""
        self.database().con.execute("select tags from cards where _id=?",
            (card._id, )).fetchone()[0] == ""

        assert self.tree.card_count_for_node["__ALL__"] == 5
        assert self.tree.card_count_for_node["a"] == 3
        assert self.tree.card_count_for_node["Z"] == 1
        assert self.tree.card_count_for_node["a::b"] == 1
        assert self.tree.card_count_for_node["a::c"] == 1
        assert self.tree.card_count_for_node["__UNTAGGED__"] == 1
        assert "b" not in self.tree.card_count_for_node
        assert "b::c" not in self.tree.card_count_for_node
        assert "b::c::d" not in self.tree.card_count_for_node

    def test_delete_2(self):
        card_type = self.card_type_with_id("1")
        fact_data = {"f": "question4",  "b": "answer4"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["a::b"])[0]
        fact_data = {"f": "question", "b": "answer"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["a"])[0]
        fact_data = {"f": "question2", "b": "answer2"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["Z"])[0]
        fact_data = {"f": "question3",  "b": "answer3"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["a::c"])[0]
        fact_data = {"f": "question5",  "b": "answer5"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["b::c::d", "b"])[0]
        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)

        assert self.tree.card_count_for_node["__ALL__"] == 5
        assert self.tree.card_count_for_node["a"] == 3
        assert self.tree.card_count_for_node["Z"] == 1
        assert self.tree.card_count_for_node["a::b"] == 1
        assert self.tree.card_count_for_node["a::c"] == 1
        assert self.tree.card_count_for_node["b"] == 1
        assert self.tree.card_count_for_node["b::c"] == 1
        assert self.tree.card_count_for_node["b::c::d"] == 1

        self.tree.delete_subtree("b::c")
        card = self.database().card(card._id, is_id_internal=True)
        assert card.tag_string() == "b"
        self.database().con.execute("select tags from cards where _id=?",
            (card._id, )).fetchone()[0] == "b"

        assert self.tree.card_count_for_node["__ALL__"] == 5
        assert self.tree.card_count_for_node["a"] == 3
        assert self.tree.card_count_for_node["Z"] == 1
        assert self.tree.card_count_for_node["a::b"] == 1
        assert self.tree.card_count_for_node["a::c"] == 1
        assert self.tree.card_count_for_node["b"] == 1
        assert "__UNTAGGED__" in self.tree.card_count_for_node
        assert "b::c" not in self.tree.card_count_for_node
        assert "b::c::d" not in self.tree.card_count_for_node

    def test_count_1(self):
        card_type = self.card_type_with_id("1")
        fact_data = {"f": "question4",  "b": "answer4"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["b"])[0]
        fact_data = {"f": "question", "b": "answer"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["b", "b"])[0]

        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)

        assert self.tree.card_count_for_node["__ALL__"] == 2

    def test_count_2(self):
        card_type = self.card_type_with_id("1")
        fact_data = {"f": "question4",  "b": "answer4"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["X::a"])[0]
        fact_data = {"f": "question", "b": "answer"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["X::a", "X::b"])[0]

        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)

        assert self.tree.card_count_for_node["__ALL__"] == 2
        assert self.tree.card_count_for_node["X"] == 2

    def test_delete_forbidden(self):
        card_type = self.card_type_with_id("1")
        fact_data = {"f": "question",  "b": "answer"}
        card = self.controller().create_new_cards(fact_data, card_type,
            grade=-1, tag_names=["forbidden"])[0]
        assert self.database().active_count() == 1

        c = DefaultCriterion(self.mnemosyne.component_manager)
        c.deactivated_card_type_fact_view_ids = set()
        c._tag_ids_active = set([self.database().get_or_create_tag_with_name("active")._id, 1])
        c._tag_ids_forbidden = set([self.database().get_or_create_tag_with_name("forbidden")._id])
        self.database().set_current_criterion(c)
        assert self.database().active_count() == 0

        from mnemosyne.libmnemosyne.tag_tree import TagTree
        self.tree = TagTree(self.mnemosyne.component_manager)
        self.tree.delete_subtree("forbidden")
        assert self.database().active_count() == 1