2
# card_type.py <Peter.Bienstman@UGent.be>
5
from mnemosyne.libmnemosyne.card import Card
6
from mnemosyne.libmnemosyne.utils import CompareOnId
7
from mnemosyne.libmnemosyne.component import Component
10
class CardType(Component, CompareOnId):
12
"""A card type groups a number of fact views on a certain fact, thereby
13
forming a set of related cards.
15
A card type needs an id as well as a name, because the name can change
16
for different translations. It is best to keep the id short, as it will
17
show up in the card id as well.
19
Inherited card types should have ids where :: separates the different
20
levels of the hierarchy, e.g. parent_id::child_id.
22
The keys from the fact are also given more verbose names here, as well
23
as an optional language code, e.g. for text-to-speech processing.
24
This is not done in fact.py, on one hand to save space in the database,
25
and on the other hand to allow the possibility that different card types
26
give different names to the same key. (E.g. foreign word' could be
27
called 'French' in a French card type, or 'pronunciation' could be
28
called 'reading' in a Kanji card type.) This in done in self.fields,
29
which is a list of the form [(fact_key, fact_key_name, language_code)].
30
It is tempting to use a dictionary here, but we can't do that since
31
ordering is important.
33
Fields which need to be different for all facts belonging to this card
34
type are listed in unique_fields.
36
Note that a fact could contain more data than those listed in the card
37
type's 'fields' variable, which could be useful for card types needing
40
We could use the component manager to track fact views, but this is
43
The function 'fact_data' typically just returns a dictionary which is
44
typically just fact.data, butwhich can also be generated on the fly,
45
as e.g. in the cloze card type.
47
The functions 'create_related_cards' and 'edit_related_cards' can be
48
overridden by card types which can have a varying number of fact views,
49
e.g. the cloze card type.
55
component_type = "card_type"
61
required_fields = None
62
keyboard_shortcuts = {}
66
return set(fact_key for (fact_key, fact_key_name,
67
fact_key_language) in self.fields)
70
return [fact_key_name for (fact_key, fact_key_name,
71
fact_key_language) in self.fields]
73
def key_with_name(self, key_name):
74
for fact_key, fact_key_name, fact_key_language in self.fields:
75
if fact_key_name == key_name:
78
def is_data_valid(self, fact_data):
79
for required in self.required_fields:
80
if not fact_data[required]:
84
# Note: we don't call render_chain in card.question because Card is not
85
# a Component and has no access to the render chains.
87
def render_question(self, card, render_chain="default", **render_args):
88
return self.render_chain(render_chain).\
89
render_question(card, **render_args)
91
def render_answer(self, card, render_chain="default", **render_args):
92
return self.render_chain(render_chain).\
93
render_answer(card, **render_args)
95
# The following functions can be overridden by speciality card types.
97
def fact_data(self, card):
100
def create_related_cards(self, fact):
102
"""Initial grading of cards and storing in the database should not happen
103
here, but is done in the main controller.
107
return [Card(self, fact, fact_view) for fact_view in self.fact_views]
109
def edit_related_cards(self, fact, new_fact_data):
111
"""If for the card type this operation results in edited, added or
112
deleted card data apart from the edited fact data from which they
113
derive, these should be returned here, so that they can be taken into
114
account in the database storage.
116
Initial grading of cards and storing in the database should not happen
117
here, but is done in the main controller.
121
new_cards, edited_cards, deleted_cards = [], [], []
122
return new_cards, edited_cards, deleted_cards
124
def before_repetition(self, card):
127
def after_repetition(self, card):