def test_upgrade2(): p = "/tmp/alpha-upgrade.anki2" if os.path.exists(p): os.unlink(p) shutil.copy2(os.path.join(testDir, "support/anki2-alpha.anki2"), p) col = Collection(p) assert col.db.scalar("select ver from col") == SCHEMA_VERSION
def ReadCollectionCounts(path, deckName): col = Collection(path) col.sched._checkDay() deckIds = {} for key, value in col.decks.decks.items(): #print("Deck id -> " + value["name"]) if value["name"] == deckName or value["name"].startswith( deckName): #:: deckIds[value["name"]] = value["id"] print(value["name"]) #for value in deckIds.values(): # () #print(value) stat = col.stats() counts = 0 cardsRes = [] for name, deckId in deckIds.items(): cardsRes = stat.col.db.all(""" SELECT :today,due,* FROM cards WHERE due < :today AND did = :id AND reps != 0 AND queue >= 0 """ % (), today=stat.col.sched.today, id=deckId) #stat.col.db.all(""" PRAGMA table_info(cards);""") text = "count: %s - today: %s - name: %s" % ( len(cardsRes), stat.col.sched.today, name) print(text) WriteToLog(text) counts = len(cardsRes) + counts return counts
def get_collection(): col = getattr(g, '_collection', None) if col is None: username = session['current_user']['username'] collection_path = data_root + '/' + username + '/collection.anki2' col = g._collection = Collection(collection_path, lock=True) return col
def loadCollection(self): cpath = self.pm.collectionPath() try: self.col = Collection(cpath, log=True) except anki.db.DBError: # warn user showWarning(_("""\ Your collection is corrupt. Please create a new profile, then \ see the manual for how to restore from an automatic backup. Debug info: """)+traceback.format_exc()) self.unloadProfile() except Exception as e: # the custom exception handler won't catch these if we immediately # unload, so we have to manually handle it if "invalidColVersion" == str(e): showWarning("""\ This profile requires a newer version of Anki to open. Did you forget to use the Downgrade button prior to switching Anki versions?""") sys.exit(1) if "invalidTempFolder" in repr(str(e)): showWarning(self.errorHandler.tempFolderMsg()) self.unloadProfile() return self.unloadProfile() raise self.progress.setupDB(self.col.db) self.col.db._db.create_function( "filterTxtSearch", 1, noOpTxtSearch ) self.maybeEnableUndo() self.moveToState("deckBrowser")
def serve() -> None: global col col = Collection(col_path(), server=True) # don't hold an outer transaction open col.db.rollback() host = os.getenv("HOST", "0.0.0.0") port = int(os.getenv("PORT", "8080")) server = create_server( app, host=host, port=port, clear_untrusted_proxy_headers=True, ) effective_port = server.effective_port # type: ignore print(f"Sync server listening on http://{host}:{effective_port}/sync/") if host == "0.0.0.0": ip = socket.gethostbyname(socket.gethostname()) print(f"Replace 0.0.0.0 with your machine's IP address (perhaps {ip})") print( "For more info, see https://github.com/ankitects/anki/blob/master/docs/syncserver.md" ) server.run()
def main(): collection_path = "collection.anki2" collection = Collection(collection_path) # get ordering of languages in the model ord_lang = { field['name']: field['ord'] for field in collection.models.all()[1]['flds'] } for note_id in collection.findNotes('*'): note = collection.getNote(note_id) langs, word_fr = parse_FR(note.fields[ord_lang['FR']]) note.fields[ord_lang['FR']] = word_fr if langs: words = parse_EN(note.fields[ord_lang['EN']], langs) if len(words) == len(langs): for idl, lang in enumerate(langs): note.fields[ord_lang[lang]] = words[idl] logging.info('Updated %s', word_fr) else: logging.error('Error with note %s', str(note.fields)) else: logging.debug('Not updated: %s', note.fields[ord_lang['FR']]) note.flush() collection.close()
def loadCollection(self): cpath = self.pm.collectionPath() try: self.col = Collection(cpath, log=True) except anki.db.Error: # warn user showWarning( _("""\ Your collection is corrupt. Please create a new profile, then \ see the manual for how to restore from an automatic backup. Debug info: """) + traceback.format_exc()) self.unloadProfile() except Exception as e: # the custom exception handler won't catch this if we immediately # unload, so we have to manually handle it if "invalidTempFolder" in repr(str(e)): showWarning(self.errorHandler.tempFolderMsg()) self.unloadProfile() return self.unloadProfile() raise self.progress.setupDB(self.col.db) self.maybeEnableUndo() self.moveToState("deckBrowser")
def get_collection(username): col = getattr(g, '_collection', None) if col is None: collection_path = data_root + '/' + username + '/collection.anki2' # col = g._collection = Collection(collection_path, lock=True) col = g._collection = Collection(collection_path, server=True) return col
def open_or_create_collection(ankiweb_username): from anki import Collection base_path = get_base_path(ankiweb_username) app.logger.info(base_path) if not os.path.exists(base_path): os.makedirs(base_path) collection = Collection(base_path + "/collection.anki2", log=True) return collection
def test_upgrade2(): fd, p = tempfile.mkstemp(suffix=".anki2", prefix="alpha-upgrade") if os.path.exists(p): os.close(fd) os.unlink(p) shutil.copy2(os.path.join(testDir, "support/anki2-alpha.anki2"), p) col = Collection(p) assert col.db.scalar("select ver from col") == SCHEMA_VERSION
def col(): try: from aqt import mw return mw.col except: (fd, name) = tempfile.mkstemp(suffix=".anki2") os.close(fd) os.unlink(name) return Collection(name)
def listitems(args): col = Collection(args._path) idlist = col.findCards(args.query) print len(idlist) for id_ in idlist: card = col.getCard(id_) note = card.note() print note.items() col.close()
def _loadCollection(self): cpath = self.pm.collectionPath() self.col = Collection(cpath, log=True) self.setEnabled(True) self.progress.setupDB(self.col.db) self.maybeEnableUndo() self.moveToState("deckBrowser") return True
def setup(): # create collection 1 with a single note c1 = getEmptyCol() f = c1.newNote() f['Front'] = "startingpoint" nid = f.id c1.addNote(f) cid = f.cards()[0].id c1.beforeUpload() # start both clients and server off in this state s1path = c1.path.replace(".anki2", "-s1.anki2") c2path = c1.path.replace(".anki2", "-c2.anki2") shutil.copy2(c1.path, s1path) shutil.copy2(c1.path, c2path) # open them c1 = Collection(c1.path) c2 = Collection(c2path) s1 = Collection(s1path, server=True) return c1, c2, s1, nid, cid
def duestat(args): col = Collection(args._path) idlist = col.findCards(args.query) lbllist = ("new", "learning", "due") cnt = Counter({0: 0, 1: 0, 2: 0}) for id_ in idlist: card = col.getCard(id_) cnt[card.type] += 1 for index, lbl in enumerate(lbllist): print "{1:5}: {0}".format(lbl, cnt[index])
def run(self): # init this first so an early crash doesn't cause an error # in the main thread self.syncMsg = "" self.uname = "" try: self.col = Collection(self.path, log=True) except: self.fireEvent("corrupt") return self.server = RemoteServer(self.hkey) self.client = Syncer(self.col, self.server) self.sentTotal = 0 self.recvTotal = 0 # throttle updates; qt doesn't handle lots of posted events well self.byteUpdate = time.time() def syncEvent(type): self.fireEvent("sync", type) def syncMsg(msg): self.fireEvent("syncMsg", msg) def canPost(): if (time.time() - self.byteUpdate) > 0.1: self.byteUpdate = time.time() return True def sendEvent(bytes): self.sentTotal += bytes if canPost(): self.fireEvent("send", str(self.sentTotal)) def recvEvent(bytes): self.recvTotal += bytes if canPost(): self.fireEvent("recv", str(self.recvTotal)) addHook("sync", syncEvent) addHook("syncMsg", syncMsg) addHook("httpSend", sendEvent) addHook("httpRecv", recvEvent) # run sync and catch any errors try: self._sync() except: err = traceback.format_exc() self.fireEvent("error", err) finally: # don't bump mod time unless we explicitly save self.col.close(save=False) remHook("sync", syncEvent) remHook("syncMsg", syncMsg) remHook("httpSend", sendEvent) remHook("httpRecv", recvEvent)
def migrate_users_decks(src, dst): # Create the new Anki2 collection os.mkdir(dst) col = Collection(os.path.join(dst, 'collection.anki2')) # loop over all the Anki1 decks, importing them for deck_path in glob.glob(os.path.join(src, '*.anki')): importer = Anki1Importer(col, deck_path.decode('utf-8')) importer.run() col.close()
def run(self): # init this first so an early crash doesn't cause an error # in the main thread self.syncMsg = "" self.uname = "" try: self.col = Collection(self.path, log=True) except: self.fireEvent("corrupt") return self.server = RemoteServer(self.hkey, hostNum=self.hostNum) self.client = Syncer(self.col, self.server) self.sentTotal = 0 self.recvTotal = 0 def syncEvent(type): self.fireEvent("sync", type) def syncMsg(msg): self.fireEvent("syncMsg", msg) def sendEvent(bytes): if not self._abort: self.sentTotal += bytes self.fireEvent("send", str(self.sentTotal)) elif self._abort == 1: self._abort = 2 raise Exception("sync cancelled") def recvEvent(bytes): if not self._abort: self.recvTotal += bytes self.fireEvent("recv", str(self.recvTotal)) elif self._abort == 1: self._abort = 2 raise Exception("sync cancelled") addHook("sync", syncEvent) addHook("syncMsg", syncMsg) addHook("httpSend", sendEvent) addHook("httpRecv", recvEvent) # run sync and catch any errors try: self._sync() except: err = traceback.format_exc() self.fireEvent("error", err) finally: # don't bump mod time unless we explicitly save self.col.close(save=False) remHook("sync", syncEvent) remHook("syncMsg", syncMsg) remHook("httpSend", sendEvent) remHook("httpRecv", recvEvent)
def _addDummyCollection(self, zip): path = namedtmp("dummy.anki2") c = Collection(path) n = c.newNote() n[_('Front')] = "This file requires a newer version of Anki." c.addNote(n) c.save() c.close() zip.write(path, "collection.anki2") os.unlink(path)
def get_collection(): """Not used""" col = Collection(COLLECTION) ids = col.findCards(QUERY) print(ids) for id in ids: card = col.getCard(id) print(card) note = col.getNote(card.nid) print(note['Word']) return col
def __create_master_col(self): """ Creates an empty master anki db that will be copied on each request for a new db. This is more efficient than initializing a new db each time. """ file_path = os.path.join(self.tempdir, "collection.anki2") master_col = Collection(file_path) master_col.db.close() self.master_db_path = file_path
def clean(collection_path=None, profile=None): collection_path = create_check_collection_path(collection_path, profile) # Create collection. col = Collection(collection_path) # Get notes to remove. notes = col.db.list(QUERY) # Remove non-updated notes. print("Removing old cards with IDs:", notes) col.remNotes(notes) # Close collection. col.close()
def gen_test_collection(new_dir) -> Collection: """Generates a test collection for us in tests, by copying a template collection""" # col_dir = Path(tmp_path_factory, "Collection") # col_dir.mkdir() shutil.copytree(TEMPLATE_COLLECTION_PTH.parent, new_dir, dirs_exist_ok=True) test_col_pth = Path(new_dir, TEMPLATE_COLLECTION_PTH.name) assert test_col_pth test_col = Collection(path=str(test_col_pth)) print(f"Test collection created at {test_col_pth}") return test_col
def openCollection(colPath): while(True): try: col = Collection(colPath) return col except DBError: print("Collection could not be read. Is Anki running?") inp = input("Enter a new collection path, or leave blank to try again.") if inp: colPath = inp except AssertionError: colPath = input("Invalid collection file. Please input a new path: ")
def _prepareFiles(self): importingV2 = self.file.endswith(".anki21") self.mustResetLearning = False self.dst = self.col self.src = Collection(self.file) if not importingV2 and self.col.schedVer() != 1: # any scheduling included? if self.src.db.scalar( "select 1 from cards where queue != 0 limit 1"): self.mustResetLearning = True
def import_command(apkgs, collection_path=None, profile=None): collection_path = create_check_collection_path(collection_path, profile) # I need to do this because creating a collection changes the current path. # Anki should fix that I think... apkgs = [os.path.abspath(i) for i in apkgs] # Create collection. col = Collection(collection_path) # Import collection. for a in apkgs: AnkiPackageImporter(col, a).run() # Close collection. col.close()
def loadCollection(self): self.hideSchemaMsg = True try: self.col = Collection(self.pm.collectionPath()) except: # move back to profile manager showWarning("""\ Your collection is corrupt. Please see the manual for \ how to restore from a backup.""") return self.unloadProfile() self.hideSchemaMsg = False self.progress.setupDB(self.col.db) self.moveToState("deckBrowser")
def _prepareFiles(self): importingV2 = self.file.endswith(".anki21") if importingV2 and self.col.schedVer() == 1: raise Exception("V2 scheduler must be enabled to import this file.") self.dst = self.col self.src = Collection(self.file) if not importingV2 and self.col.schedVer() != 1: # if v2 scheduler enabled, can't import v1 decks that include scheduling if self.src.db.scalar("select 1 from cards where queue != 0 limit 1"): self.src.close(save=False) raise Exception("V2 scheduler can not import V1 decks with scheduling included.")
def run(self): # open profile deck self.col = Collection(self.colpath) # loop through paths while True: path = self.paths.pop() self.name = os.path.basename(path) self.upgrade(path) # abort if finished if not self.paths: break self.current += 1 self.col.close() self.finished = True
def col_tuple(tmpdir): """ Get a test Anki collection. Anki irritatingly changes the working directory to the media directory, so also return our old working directory. """ old_cwd = os.getcwd() my_col = Collection(tmpdir / "collection.anki2") col_obj = TestCollection(my_col, old_cwd) try: yield col_obj finally: my_col.close()