1#
2# database.py <Peter.Bienstman@UGent.be>
3#
4
5from mnemosyne.libmnemosyne.gui_translator import _
6from mnemosyne.libmnemosyne.component import Component
7
8
9class Database(Component):
10
11    """Interface class describing the functions to be implemented by the
12    actual database classes.
13
14    Apart from the basic interface defined here, depending on the situation
15    a database can also implement functions for logging, statistics and
16    syncing (see SQLite_logging.py, SQLite_statistics.py, SQLite_sync.py).
17
18    """
19
20    version = ""
21    default_name = "default"  # Without suffix, should not be translated.
22    default_criterion_name = "__DEFAULT__"
23    suffix = ""
24    component_type = "database"
25
26    def deactivate(self):
27        Component.deactivate(self)
28        self.unload()
29
30    def path(self):
31
32        """Returns full path of the database."""
33
34        raise NotImplementedError
35
36    def data_dir(self):
37
38        """Returns directory of the database."""
39
40        raise NotImplementedError
41
42    def name(self):
43
44        """Returns name of the database, without parent paths, but with
45        extensions.
46
47        """
48
49        raise NotImplementedError
50
51    def display_name(self):
52
53        """Returns bare name of the database, without parent paths and
54        without extension.
55
56        """
57
58        raise NotImplementedError
59
60    # File operations.
61
62    def release_connection(self):
63
64        """Release the connection, so that it may be recreated in a separate
65        thread.
66
67        """
68
69        raise NotImplementedError
70
71    def new(self, path):
72        raise NotImplementedError
73
74    def save(self, path=None):
75        raise NotImplementedError
76
77    def backup(self):
78        raise NotImplementedError
79
80    def restore(self, path):
81        raise NotImplementedError
82
83    def load(self, path):
84        raise NotImplementedError
85
86    def unload(self):
87        raise NotImplementedError
88
89    def abandon(self):
90        raise NotImplementedError
91
92    def is_loaded(self):
93        raise NotImplementedError
94
95    def is_empty(self):
96        raise NotImplementedError
97
98    # Functions to conform to openSM2sync API.
99
100    def user_id(self):
101        return self.config()["user_id"]
102
103    def change_user_id(self, user_id):
104        self.config().change_user_id(user_id)
105
106    # Tags.
107
108    def add_tag(self, tag):
109        raise NotImplementedError
110
111    def tag(self, id, is_id_internal):
112        raise NotImplementedError
113
114    def update_tag(self, tag):
115        raise NotImplementedError
116
117    def delete_tag(self, tag):
118        raise NotImplementedError
119
120    def get_or_create_tag_with_name(self, name):
121        raise NotImplementedError
122
123    def get_or_create_tags_with_names(self, names):
124        raise NotImplementedError
125
126    def delete_tag_if_unused(self, tag):
127        raise NotImplementedError
128
129    def tags(self):
130        raise NotImplementedError
131
132    def has_tag_with_id(self, id):
133        return NotImplementedError
134
135    # Facts.
136
137    def add_fact(self, fact):
138        raise NotImplementedError
139
140    def fact(self, id, is_id_internal):
141        raise NotImplementedError
142
143    def update_fact(self, fact):
144        raise NotImplementedError
145
146    def delete_fact(self, fact):
147        raise NotImplementedError
148
149    def has_fact_with_id(self, id):
150        return NotImplementedError
151
152    # Cards.
153
154    def add_card(self, card):
155        raise NotImplementedError
156
157    def card(self, id, is_id_internal):
158        raise NotImplementedError
159
160    def update_card(self, card, repetition_only=False):
161        raise NotImplementedError
162
163    def delete_card(self, card):
164        raise NotImplementedError
165
166    def tags_from_cards_with_internal_ids(self, _card_ids):
167        raise NotImplementedError
168
169    def add_tag_to_cards_with_internal_ids(self, tag, _card_ids):
170        raise NotImplementedError
171
172    def remove_tag_from_cards_with_internal_ids(self, tag, _card_ids):
173        raise NotImplementedError
174
175    def has_card_with_id(self, id):
176        return NotImplementedError
177
178    # Fact views.
179
180    def add_fact_view(self, fact_view):
181        raise NotImplementedError
182
183    def fact_view(self, id, is_id_internal):
184        raise NotImplementedError
185
186    def update_fact_view(self, fact_view):
187        raise NotImplementedError
188
189    def delete_fact_view(self, fact_view):
190        raise NotImplementedError
191
192    def has_fact_view_with_id(self, id):
193        return NotImplementedError
194
195    # Card types.
196
197    def add_card_type(self, card_type):
198        raise NotImplementedError
199
200    def card_type(self, id, is_id_internal):
201        raise NotImplementedError
202
203    def is_user_card_type(self, card_type):
204        raise NotImplementedError
205
206    def is_in_use(self, card_type):
207        raise NotImplementedError
208
209    def has_clone(self, card_type):
210        raise NotImplementedError
211
212    def update_card_type(self, card_type):
213        raise NotImplementedError
214
215    def delete_card_type(self, card_type):
216        raise NotImplementedError
217
218    def has_card_type_with_id(self, id):
219        return NotImplementedError
220
221    # Criteria.
222
223    def add_criterion(self, criterion):
224        raise NotImplementedError
225
226    def criterion(self, id, is_id_internal):
227        raise NotImplementedError
228
229    def update_criterion(self, criterion):
230        raise NotImplementedError
231
232    def delete_criterion(self, criterion):
233        raise NotImplementedError
234
235    def set_current_criterion(self, criterion):
236        raise NotImplementedError
237
238    def current_criterion(self):
239        raise NotImplementedError
240
241    def criteria(self):
242        raise NotImplementedError
243
244    def has_criterion_with_id(self, id):
245        return NotImplementedError
246
247    # Queries.
248
249    def cards_from_fact(self, fact):
250
251        """Return a list of the cards deriving from a fact."""
252
253        raise NotImplementedError
254
255    def duplicates_for_fact(self, fact, card_type):
256
257        """Return facts with same 'card_type.unique_fact_keys' data as 'fact'."""
258
259        raise NotImplementedError
260
261    def card_types_in_use(self):
262        raise NotImplementedError
263
264    # Card queries used by the scheduler. Returns tuples of internal ids
265    # (_card_id, _fact_id) Should function as an iterator in order to save
266    # memory. "sort_key" is a string of an attribute of Card to be used for
267    # sorting, with "" standing for the order in which the cards where added
268    # (no sorting), and "random" is used to shuffle the cards. "limit" is
269    # used to limit the number of cards returned by the iterator, with -1
270    # meaning no limit.
271
272    def cards(self, sort_key="", limit=-1):
273        raise NotImplementedError
274
275    def cards_due_for_ret_rep(self, now, sort_key="", limit=-1):
276        raise NotImplementedError
277
278    def cards_to_relearn(self, grade, sort_key="", limit=-1):
279        raise NotImplementedError
280
281    def cards_new_memorising(self, grade, sort_key="", limit=-1):
282        raise NotImplementedError
283
284    def cards_unseen(self, sort_key="", limit=-1):
285        raise NotImplementedError
286
287    def cards_learn_ahead(self, now, sort_key="", limit=-1):
288        raise NotImplementedError
289
290    def recently_memorised_count(self, max_ret_reps):
291        raise NotImplementedError
292
293    # Extra queries for custom schedulers.
294
295    def set_scheduler_data(self, scheduler_data):
296        raise NotImplementedError
297
298    def cards_with_scheduler_data(self, scheduler_data, sort_key="", limit=-1,
299                                  max_ret_reps=-1):
300        raise NotImplementedError
301
302    def scheduler_data_count(self, scheduler_data, max_ret_reps=-1):
303        raise NotImplementedError
304
305    #
306    # Extra queries for language analysis.
307    #
308
309    def known_recognition_questions_count_from_card_types_ids(\
310        self, card_type_ids):
311        raise NotImplementedError
312
313    def known_recognition_questions_from_card_types_ids(self, card_type_ids):
314        raise NotImplementedError
315
316    def sorted_card_types(self):
317
318        """Sorts card types so that all the built-in card types appear first,
319        in the order determined by their id, and then all the user card types
320        appear alphabetically.
321
322        """
323
324        result = []
325        user_card_types = []
326
327        for card_type in self.card_types():
328            if self.is_user_card_type(card_type):
329                user_card_types.append(card_type);
330            else:
331                result.append(card_type);
332
333        result.sort(key=lambda x: x.id)
334        user_card_types.sort(key=lambda x: x.name.lower())
335
336        result.extend(user_card_types)
337
338        return result
339
340
341
342class DatabaseMaintenance(Component):
343
344    """This component performs automatic database maintenance (like
345    archiving of old logs) and can be run from the UI or automatically from
346    the controller.
347
348    This version is unthreaded, and is OK for running on a headless server
349    (which has no UI to interrupt) and for Android (since the entire backend
350    runs in thread there anyhow).
351
352    """
353
354    component_type = "database_maintenance"
355
356    def run(self):
357        self.main_widget().set_progress_text(_("Compacting database..."))
358        self.database().archive_old_logs()
359        self.database().defragment()
360        self.main_widget().close_progress()
361
362