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