def import_apkg_model(path, delete=False): #If delete==True, our sole purpose is to import the note types and flashcard templates, not the data. imp = AnkiPackageImporter(mw.col, path) imp.run() mw.col.models.flush() mw.reset( True) # refresh the screen so that the newly imported deck is visible if delete: # Delete ALL cards/notes in the LIFT deck. Ignore errors try: # deck = mw.col.decks.byName(TARGET_DECK) #hmm, this returns a Python dict(); how to get the deck object? # assert deck.cardCount() > 0 # assert deck.noteCount() > 0 ids = mw.col.findCards("deck:{}".format(TARGET_DECK)) mw.col.remCards(ids, True) # assert deck.cardCount() == 0 # assert deck.noteCount() == 0 except: return "Failed to delete cards and notes after importing the APKG file." mw.col.models.flush() mw.reset(True) return ""
def reset(self, path): self.log.info('removing previous Kanji Damage decks') util.remove_model_and_deck(self.col, KD_MODEL, KD_DECK_NAME, self.log) util.remove_model_and_deck(self.col, KD_MODEL, KDR_DECK_NAME, self.log) self.log.info('importing %s', path) importer = AnkiPackageImporter(self.col, path) importer.run() self.col.save()
def test_anki2_updates(): # create a new empty deck dst = getEmptyCol() tmp = getUpgradeDeckPath("update1.apkg") imp = AnkiPackageImporter(dst, tmp) imp.run() assert imp.dupes == 0 assert imp.added == 1 assert imp.updated == 0 # importing again should be idempotent imp = AnkiPackageImporter(dst, tmp) imp.run() assert imp.dupes == 1 assert imp.added == 0 assert imp.updated == 0 # importing a newer note should update assert dst.noteCount() == 1 assert dst.db.scalar("select flds from notes").startswith("hello") tmp = getUpgradeDeckPath("update2.apkg") imp = AnkiPackageImporter(dst, tmp) imp.run() assert imp.dupes == 1 assert imp.added == 0 assert imp.updated == 1 assert dst.noteCount() == 1 assert dst.db.scalar("select flds from notes").startswith("goodbye")
def importPackage(self, path): collection = self.collection() if collection is not None: try: self.startEditing() importer = AnkiPackageImporter(collection, path) importer.run() except: self.stopEditing() raise else: self.stopEditing() return True return False
def test_anki2_diffmodel_templates(): # different from the above as this one tests only the template text being # changed, not the number of cards/fields dst = getEmptyCol() # import the first version of the model tmp = getUpgradeDeckPath("diffmodeltemplates-1.apkg") imp = AnkiPackageImporter(dst, tmp) imp.dupeOnSchemaChange = True imp.run() # then the version with updated template tmp = getUpgradeDeckPath("diffmodeltemplates-2.apkg") imp = AnkiPackageImporter(dst, tmp) imp.dupeOnSchemaChange = True imp.run() # collection should contain the note we imported assert dst.noteCount() == 1 # the front template should contain the text added in the 2nd package tcid = dst.findCards("")[0] # only 1 note in collection tnote = dst.getCard(tcid).note() assert "Changed Front Template" in tnote.cards()[0].template()["qfmt"]
def test_apkg(): tmp = getEmptyDeck() apkg = unicode(os.path.join(testDir, "support/media.apkg")) imp = AnkiPackageImporter(tmp, apkg) assert os.listdir(tmp.media.dir()) == [] imp.run() assert os.listdir(tmp.media.dir()) == ['foo.wav'] # importing again should be idempotent in terms of media tmp.remCards(tmp.db.list("select id from cards")) imp = AnkiPackageImporter(tmp, apkg) imp.run() assert os.listdir(tmp.media.dir()) == ['foo.wav'] # but if the local file has different data, it will rename tmp.remCards(tmp.db.list("select id from cards")) open(os.path.join(tmp.media.dir(), "foo.wav"), "w").write("xyz") imp = AnkiPackageImporter(tmp, apkg) imp.run() assert len(os.listdir(tmp.media.dir())) == 2
def import_apkg_model(path, delete=False): #If delete==True, our sole purpose is to import the note types and flashcard templates, not the data. imp = AnkiPackageImporter(mw.col, path) imp.run() mw.col.models.flush() mw.reset(True) # refresh the screen so that the newly imported deck is visible if delete: # Delete ALL cards/notes in the LIFT deck. Ignore errors try: # deck = mw.col.decks.byName(TARGET_DECK) #hmm, this returns a Python dict(); how to get the deck object? # assert deck.cardCount() > 0 # assert deck.noteCount() > 0 ids = mw.col.findCards("deck:{}".format(TARGET_DECK)) mw.col.remCards(ids, True) # assert deck.cardCount() == 0 # assert deck.noteCount() == 0 except: return "Failed to delete cards and notes after importing the APKG file." mw.col.models.flush() mw.reset(True) return ""
def test_anki2_diffmodel_templates(): # different from the above as this one tests only the template text being # changed, not the number of cards/fields dst = getEmptyCol() # import the first version of the model tmp = getUpgradeDeckPath("diffmodeltemplates-1.apkg") imp = AnkiPackageImporter(dst, tmp) imp.dupeOnSchemaChange = True imp.run() # then the version with updated template tmp = getUpgradeDeckPath("diffmodeltemplates-2.apkg") imp = AnkiPackageImporter(dst, tmp) imp.dupeOnSchemaChange = True imp.run() # collection should contain the note we imported assert(dst.noteCount() == 1) # the front template should contain the text added in the 2nd package tcid = dst.findCards("")[0] # only 1 note in collection tnote = dst.getCard(tcid).note() assert("Changed Front Template" in dst.findTemplates(tnote)[0]['qfmt'])
APKG_PATH = DOCS_PATH + 'accelerated_spanish_empty.apkg' CSV_PATH = DOCS_PATH + 'spanish5.csv' TMP_COL_PATH = TMP_PATH DECK_NAME = 'accelerated spanish' DECK_ID = None #1518389138178 MODEL_NAME = 'Basic' QUESTION_FIELD = 'Front' ANSWER_FIELD = 'Back' MEDIA_PATH = '/home/cowley/.local/share/Anki2/User 1/collection.media/' with open(CSV_PATH) as f: reader = csv.reader(f, delimiter='\t') collection = anki.Collection(TMP_COL_PATH + str(random.randint(10**9, 10**10)) + '.anki2') api = AnkiPackageImporter(collection, APKG_PATH) api.run() collection = api.col # get deck try: deck_id = collection.decks.id(DECK_NAME) except: print('invalid deck name') raise collection.decks.select(deck_id) # get model to use try: model = collection.models.byName(MODEL_NAME)
def test_anki2_diffmodels(): # create a new empty deck dst = getEmptyDeck() # import the 1 card version of the model tmp = getUpgradeDeckPath("diffmodels2-1.apkg") imp = AnkiPackageImporter(dst, tmp) imp.run() before = dst.noteCount() # repeating the process should do nothing imp = AnkiPackageImporter(dst, tmp) imp.run() assert before == dst.noteCount() # then the 2 card version tmp = getUpgradeDeckPath("diffmodels2-2.apkg") imp = AnkiPackageImporter(dst, tmp) imp.run() after = dst.noteCount() # as the model schemas differ, should have been imported as new model assert after == before + 1 # and the new model should have both cards assert dst.cardCount() == 3 # repeating the process should do nothing imp = AnkiPackageImporter(dst, tmp) imp.run() after = dst.noteCount() assert after == before + 1 assert dst.cardCount() == 3
def test_apkg(): tmp = getEmptyCol() apkg = str(os.path.join(testDir, "support/media.apkg")) imp = AnkiPackageImporter(tmp, apkg) assert os.listdir(tmp.media.dir()) == [] imp.run() assert os.listdir(tmp.media.dir()) == ["foo.wav"] # importing again should be idempotent in terms of media tmp.remove_cards_and_orphaned_notes(tmp.db.list("select id from cards")) imp = AnkiPackageImporter(tmp, apkg) imp.run() assert os.listdir(tmp.media.dir()) == ["foo.wav"] # but if the local file has different data, it will rename tmp.remove_cards_and_orphaned_notes(tmp.db.list("select id from cards")) with open(os.path.join(tmp.media.dir(), "foo.wav"), "w") as f: f.write("xyz") imp = AnkiPackageImporter(tmp, apkg) imp.run() assert len(os.listdir(tmp.media.dir())) == 2
def test_anki2_updates(): # create a new empty deck dst = getEmptyCol() tmp = getUpgradeDeckPath("update1.apkg") imp = AnkiPackageImporter(dst, tmp) imp.run() assert imp.dupes == 0 assert imp.added == 1 assert imp.updated == 0 # importing again should be idempotent imp = AnkiPackageImporter(dst, tmp) imp.run() assert imp.dupes == 1 assert imp.added == 0 assert imp.updated == 0 # importing a newer note should update assert dst.noteCount() == 1 assert dst.db.scalar("select flds from notes").startswith("hello") tmp = getUpgradeDeckPath("update2.apkg") imp = AnkiPackageImporter(dst, tmp) imp.run() assert imp.dupes == 0 assert imp.added == 0 assert imp.updated == 1 assert dst.noteCount() == 1 assert dst.db.scalar("select flds from notes").startswith("goodbye")
# Define the path to the Anki SQLite collection COLLECTION_PATH = '/Users/admin/Library/Application Support/Anki2/User 1/collection.anki2' # Load the Collection col = Collection(COLLECTION_PATH, log=False) # Entry point to the API # Clear collection for did in col.decks.allIds(): col.decks.rem(did, True) col.save() # Import game decks for filename in os.listdir(IMPORT_PATH): apkg = IMPORT_PATH + filename AnkiPackageImporter(col, apkg).run() col.save() # Create a base dynamic deck base_deck_id = col.decks.newDyn(BASE_DECK) # Fetch db collections client = MongoClient(MONGO_DB_URI) db = client[DB] deck_titles_collection = db.deckTitles old_cards = db.oldCards # Fetch deck titles deck_titles = [doc['fullTitle'] for doc in deck_titles_collection.find()] # Make new subdeck for every deck title
def convert(self, in_file_str: str, out_file_str: str = None) -> None: """Convert a single file to ANKI deck.""" log.info( f"Converting file '{in_file_str}' to ANKI deck @ '{out_file_str}'") # Create paths in_file = pathlib.Path(in_file_str) if out_file_str is not None: assert out_file_str.endswith(AnkiConvertor.ANKI_EXT) out_file = pathlib.Path(out_file_str).resolve() else: out_file = in_file.with_suffix(AnkiConvertor.ANKI_EXT).resolve() tmp_out_file = out_file.with_suffix(".tmp.apkg").resolve() # Convert the org nodes into list of Notes cards: typing.List[genanki.Note] = [] # Preprocess and parse the file preprocessed_source = self.preprocessor.preprocess_file(str(in_file)) org_file = orgparse.loads(preprocessed_source) # If user did not supplied the convert mode - try to get the convert mode # from the org file header fall back to NORMAL mode if not self.user_supplied_convert_mode: try: self.convert_mode = AnkiConvertMode[org_file._special_comments[ self.COMMENT_ANKI_CONVERT_MODE][0].upper()] except KeyError: self.convert_mode = AnkiConvertMode.NORMAL else: self.convert_mode = self._convert_mode self._get_cards(org_file, cards) # Try to set the deck name to a org file title comment try: deck_name = org_file._special_comments["TITLE"][0] except (KeyError, IndexError): deck_name = out_file.stem # TODO: Hash should be calculated from the cards deck = genanki.Deck(random.randrange(1 << 30, 1 << 31), deck_name) for c in cards: deck.add_note(c) package = genanki.Package(deck) package.media_files = self.node_convertor.media_files package.write_to_file(str(tmp_out_file)) # Import and export the collection using Anki # This is neccessary to make mobile version work (include rendered equations) with utils.create_empty_anki_collection() as col: log.debug("Importing to tmp ANKI collection") imp = AnkiPackageImporter(col, str(tmp_out_file)) imp.run() log.debug("Exporting from tmp ANKI collection") exp = AnkiPackageExporter(col) exp.exportInto(str(out_file)) tmp_out_file.unlink()
def open_apkg(apkg_path): with utils.create_empty_anki_collection() as col: imp = AnkiPackageImporter(col, str(apkg_path)) imp.run() yield col