kerrickstaley / genanki Goto Github PK
View Code? Open in Web Editor NEWA Python 3 library for generating Anki decks
License: MIT License
A Python 3 library for generating Anki decks
License: MIT License
Hi there, thank you for this great package.
When I was looking into the code for Model.to_json in model.py I noticed that the LaTex preamble for the Note type is constant.
return {
"css": self.css,
"did": deck_id,
"flds": self.fields,
"id": str(self.model_id),
"latexPost": "\\end{document}",
"latexPre": "\\documentclass[12pt]{article}\n\\special{papersize=3in,5in}\n\\usepackage{amssymb,amsmath}\n"
"\\pagestyle{empty}\n\\setlength{\\parindent}{0in}\n\\begin{document}\n",
"mod": now_ts,
"name": self.name,
"req": self._req,
"sortf": 0,
"tags": [],
"tmpls": self.templates,
"type": self.model_type,
"usn": -1,
"vers": []
}
Is there some way to change the LaTex preamble for a Model object to use more LaTex packages & define environments?
hello! Thank you so much for this library! It has been really helpful!
I just got a question cause once you create an object of type Model
you're going to pass in fields
and template
right... So in my program I'll be needing to somehow UPDATE those fields and templates depending on the Question and Answer data that I have so my question is... Is there a function
or setter method
that would update the fields
and templates
attribute in the class?
I know I can make a new instance of a model over and over again but I think that's not programmatically efficient so I am asking the devs about this if they have included it... I know I can also call dir()
to see the methods and review the code but I would like to ask it here and probably learn more about it when I talk to one of the developers.
Example here:
So when I create that my_model
is there a way to update the attributes? Can I just simply do a .fields = #something
same with templates
? I was wondering if there could be a function for it.,
my_model = genanki.Model(1607392319,'Simple Model',
fields=[
{'name': 'Question'},
{'name': 'Answer'}
],
templates=[
{
'name': 'Card 1',
'qfmt': '{{Question}}',
'afmt': '{{FrontSide}}<hr id="answer">{{Answer}}',
},
])
Again THANK YOU FOR THIS! SUCH A HELPFUL LIBRARY!
but VerseText is clearly present
Here's the template, as this is likely the problem:
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<script>
//Found here: https://gist.github.com/hinekidori/4085a1222af572057f1058d43cfce773
//Script for modifying 'Show Answer' behavior for Input types.
var htmlTextNodes = [];
var innerHTMLText = [];
var htmlNodeLength =document.getElementById('typeans').childNodes.length;
var typedAnswer;
var correctAnswer;
var firstBr = null;
var secondBr;
//capture each node to array
for (i = 0; i < htmlNodeLength; i++) {
htmlTextNodes[i] = document.getElementById('typeans').childNodes[i];
innerHTMLText[i] = document.getElementById('typeans').childNodes[i].innerHTML;
//locate <br> tags for output change markers
if (document.getElementById('typeans').childNodes[i].nodeName == "BR") {
console.log("Runs if BR");
if (firstBr != null) {
secondBr = i;
} else {
firstBr = i;
};
};
};
//If answer is correct, firstBr will still be null, so must set to length of typeans.childNode
if (firstBr == null) {
firstBr = htmlNodeLength;
};
//assemble typed and correct answer strings
typedAnswer = innerHTMLText.slice(0,firstBr).join("");
var corr = document.getElementById('correctAnswer');
correctAnswer = corr.innerHTML;
//Modify answer output
if (typedAnswer === correctAnswer) {
var c = "<div id='correct'>"+typedAnswer+"</div>";
var d = document.getElementById('typeans');
d.innerHTML = c;
} else {
var e = "<div id='incorrect'>"+typedAnswer+"</div>";
var f = document.getElementById('typeans');
f.innerHTML = e;
};
</script><div class="front rote even container">
<div class="row">
<div class="field hint verse id col-12">
{{BookName}} {{ChapterNumber}}:{{VerseNumber}}
</div>
</div>
<div class="row">
<div class="field hint even-words col-12">
{{EvenWords}}
</div>
</div>
<div class="row">
<div class="type answer col-12">
{{type: VerseText}}
</div>
</div>
</div>
Cloze notes are great, let's do that.
I have grown dissatisfied with a shared deck I am using, and want to make some changes to it programatically.
I want to do this while maintaining the reviews/data/progress etc
I assume I could make a new deck and overwrite the cards if I keep the GUIDs the same, but there are over 2000 cards in this deck so it would be impractical to read these GUIDs manually.
If it's too much work to add read functionality to this library, I would appreciate some advice on how to extract an ordered list of GUIDs from within Anki, so I can use genanki without reading the old set of cards.
Thanks
Instead of having a .media_files
attribute on Package
, it may be more ergonomic to have a .media_files
attribute on each genanki.Note
instance. The .media_files
for the overall package will then be computed by combining the lists for each individual Note
(removing duplicates).
Need to think a little more about whether this design will handle all use-cases and whether it will be genuinely easier to use. One consideration is "static" media files; those still need to be specified at the package level.
Dear KerrickStaley,
Thank you for sharing your library.
I think I might have found an issue.
Example code:
data = {
'deck1':
[{
'a': 'content deck 1 a1',
'b': 'content deck 1 b1'
},
{
'a': 'content deck 1 a2',
'b': 'content deck 1 b2'
}],
'deck2':
[{
'a': 'content deck 2 a1',
'b': 'content deck 2 b1'
},
{
'a': 'content deck 2 a2',
'b': 'content deck 2 b2'
}],
'deck3':
[{
'a': 'content deck 3 a1',
'b': 'content deck 3 b1'
},
{
'a': 'content deck 3 a2',
'b': 'content deck 3 b2'
}]
}
# Create the model
a_to_b = genanki.Model(
2043134337, # unique identifier
'a-to-b', # human readable name
fields = [
{'name': 'a'},
{'name': 'b'},
],
templates=[
{
'name': 'Card 1',
'qfmt': '{{a}}',
'afmt': '{{FrontSide}}<hr id="answer">{{b}}',
},
]
)
decks = []
for deck_name in data:
deck = genanki.Deck(
2043134340,
deck_name
)
# Add the notes
for note in data[deck_name]:
note = genanki.Note(
model=a_to_b,
fields=[note.get('a',''),
note.get('b',''),
])
deck.add_note(note)
decks.append(deck)
# Create the file
genanki.Package(decks).write_to_file('output.apkg')
When I then load this apkg file into Anki, Anki adds all 6 cards that should be in deck1, deck2 and deck3 into only deck1.
Travis CI is dead, long live GitHub Actions!
I am using genanki to convert data from an SRS app to Anki. I want to translate the SRS mastery data so the Anki items are due roughly when they would have been in the original SRS app.
How can I accomplish this using genanki?
Near as I can tell the fields in Card.write_to_db
control this, but I am not sure what the SRS-related fields mean. I guess I will have to look at the Anki source to figure it out. AnkiDroid has some convenient documentation.
I would prefer to set SRS data at the Note
level.
I plan to implement this feature, but thought I would ask for some guidance first.
Getting this YAMLLoadWarning
on newer versions of the yaml
lib:
/home/kerrick/.local/lib/python3.7/site-packages/genanki-0.6.5-py3.7.egg/genanki/model.py:18: YAMLLoadWarning: calling yaml.load() without Loader=... is deprecated, as the default Loader is unsafe. Please read https://msg.pyyaml.org/load for full details.
self.fields = yaml.load(fields)
/home/kerrick/.local/lib/python3.7/site-packages/genanki-0.6.5-py3.7.egg/genanki/model.py:24: YAMLLoadWarning: calling yaml.load() without Loader=... is deprecated, as the default Loader is unsafe. Please read https://msg.pyyaml.org/load for full details.
self.templates = yaml.load(templates)
Hello first of all thank you with this python library. It is so cool! really helps me a lot in creating anki decks!
I have a question thoooo can you please help me and I think my question is just very very simple so here it is
my_note = genanki.Note(
model=my_model,
fields=['Capital of Argentina', 'Buenos Aires'])
On this code is you create a card right with the question "Capital of Argentina" and as the answer "Buenos Aires"... and it looks like this
I was thinking of can you provide an example on how can I make a field like this?
Thank you so much!
Hello! Thank you for this library :) really been helpful!
May I ask on how can I apply cloze. I am trying to read this data from google doc but when I generate a deck, it doesn't work
Instead I get this
Here is the code where I generate the deck. Thank you
model = genanki.Model(1607392319,'USC1 Sample Model',
fields=[
{'name': 'Question'},
{'name': 'Answer'}
],
templates=[
{
'name': 'Card 1',
'qfmt': '{{Question}}',
'afmt': '{{FrontSide}}<hr id="answer">{{Answer}}',
},
])
deck = genanki.Deck(2059400110,'USC1 Beta Deck')
for key,value in output_dict.items():
if isinstance(value,Mapping) == True:
for subkey,subvalue in value.items():
answer = subkey
if subvalue:
answer = f"{subkey} <ul>"+ ''.join([f"<li>{sa}</li>" for sa in subvalue]) + "<ul>"
note = genanki.Note(
model=model,
fields=[key,answer])
deck.add_note(note)
Hi,
You're package si great but this doesn't work :
note = genanki.Note(
model=model,
fields=[citation, title, author, "", ""]
)
When a Package containing such card is uploaded, I can't get the deck to be generated correctly by athenki.
Multiline cloze deletions aren't detected. When exporting deck to Anki, cards with multiline clozes (probably only the ones with multiline clozes, but I didn't test that) don't show. Only when database check is preformed they are detected and put into deck named "Default".
Edit3: I tested my solution and seems that there is also some other problem
Edit4: it seems to be working (I forgot to change model to cloze in my test)
Change in line 101 of file genanki/note.py:
card_ords.update(int(m)-1 for m in re.findall(r"{{c(\d+)::.+?}}", field_value) if int(m) > 0)
to:
card_ords.update(int(m)-1 for m in re.findall(r"{{c(\d+)::.+?}}", field_value, re.DOTALL) if int(m) > 0)
or:
card_ords.update(int(m)-1 for m in re.findall(r"{{c(\d+)::[\s\S]+?}}", field_value) if int(m) > 0)
.
matches anything except a newline character (relevant python3 doc).
test_str_part = \
"""
{{c1:: test
}}
"""
clozes = re.findall(r"{{c(\d+)::.+?}}", test_str_part)
for e in clozes:
print(e)
#cloze isn't detected
I'm new to git and currently I don't have much time, but if I find any I will try to send commit with fixed code (if that's possible).
With regards
mat383
Edit1: expanded issue description
Edit2: grammar
Python 2 is never going to die. Support Python 2 for those poor unenlightened souls who can't or won't use Python 3.
I use a Python module genanki to generate cards.But the image is always broken in the card.
My code are as follow
.
‘’’
import genanki
my_model = genanki.Model(
1091735104,
‘Simple Model with Media’,
fields=[
{‘name’: ‘Question’},
{‘name’: ‘Answer’},
{‘name’: ‘MyMedia’},
{‘name’: ‘MyAnswerMedia’} # ADD THIS
],
templates=[
{
‘name’: ‘Card 1’,
‘qfmt’: ‘{{Question}}
{{MyAnswerMedia}}’, # AND THIS
‘afmt’: ‘{{FrontSide}}
{{MyMedia}}
{{Answer}}’,
},
])
deckname = “newDeck”
decknumber = 987655
my_deck = genanki.Deck(
decknumber,
deckname)
my_note=genanki.Note(
model=my_model,
fields=[“test”,“test”, ‘’,“test”])
my_deck.add_note(my_note)
my_package = genanki.Package(my_deck)
my_package.media_files = [ “img_2991.jpg”]
genanki.Package(my_deck).write_to_file(deckname + r".apkg")
‘’’
By the way when I check the media file within apkg file and open it with notebook,it simply contain a {}.Can someone help me?
This creates a dependency cycle and it pretty unneeded. Additionally, you can't use media files to the deck this way.
I think these methods should be removed... thoughts?
I'm trying to add mathjax support to one of my model templates using this guide
I have to insert it to the front and the back of the card, so far I tried to load it in
mathjax="<script type=\"text/x-mathjax-config\">\n"
mathjax+="MathJax.Hub.processSectionDelay = 0;\n"
mathjax+="MathJax.Hub.Config({\n"
mathjax+=" messageStyle: 'none',\n"
mathjax+=" showProcessingMessages: false,\n"
mathjax+=" tex2jax: {\n"
mathjax+=" inlineMath: [['$', '$']],\n"
mathjax+=" displayMath: [['$$', '$$']],\n"
mathjax+=" processEscapes: true\n"
mathjax+=" }\n"
mathjax+="});\n"
mathjax+="</script>\n"
mathjax+="<script type=\"text/javascript\">\n"
mathjax+="(function() {\n"
mathjax+=" if (window.MathJax != null) {\n"
mathjax+=" var card = document.querySelector('.card');\n"
mathjax+=" MathJax.Hub.Queue(['Typeset', MathJax.Hub, card]);\n"
mathjax+=" return;\n"
mathjax+=" }\n"
mathjax+=" var script = document.createElement('script');\n"
mathjax+=" script.type = 'text/javascript';\n"
mathjax+=" script.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-MML-AM_CHTML';\n"
mathjax+=" document.body.appendChild(script);\n"
mathjax+="})();\n"
mathjax+="</script>\n"
reversible_mathjax = genanki.Model(
1179517143,
'Reversible Model with mathjax',
fields=[
{'name': 'Question'},
{'name': 'Answer'},
],
templates=[
{
'name': 'Card 1',
'qfmt': ('{{Question}}\n{}').format(mathjax),
'afmt': ('{{FrontSide}}<hr id="answer">\n{}').format(mathjax),
},
{
'name': 'Card 2',
'qfmt': ('{{Answer}}\n{}').format(mathjax),
'afmt': ('{{FrontSide}}<hr id="answer">{{Question}}\n{}').format(mathjax),
}
])
Turns out this worked, but I still want to leave this here for other people to use.
In Anki when you use the ::
symbol when naming a deck and you get a little dropdown to help organise a large set of flashcards into multiple subdecks so for example Root name::Subdeck name
would have Root name
as the main deck name and Subdeck name
as the name of the subdeck within the main deck.
Is it possible to do this with genanki?
We would love to be able to make subdecks as this would help us to autogenerate flashcards for various projects such as https://github.com/darigovresearch/Universal-Foreign-Language-Flashcards
Would be happy to do a pull request if given guidance on where to insert what in the relevant sections of the codebase.
I get an error whenever I try to write a deck to file. It happens also with the example in the documentation, so it shouldn't be caused by the deck I am trying to create.
This is the output I get:
>>> Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/carles/Desktop/kindletoanki/test.py", line 30, in <module>
genanki.Package(my_deck).write_to_file('output.apkg')
File "/home/carles/.local/lib/python2.7/site-packages/genanki-0.2.1-py2.7.egg/genanki/__init__.py", line 289, in write_to_file
self.write_to_db(cursor, now_ts)
File "/home/carles/.local/lib/python2.7/site-packages/genanki-0.2.1-py2.7.egg/genanki/__init__.py", line 302, in write_to_db
deck.write_to_db(cursor, now_ts)
File "/home/carles/.local/lib/python2.7/site-packages/genanki-0.2.1-py2.7.egg/genanki/__init__.py", line 271, in write_to_db
note.write_to_db(cursor, now_ts, self.deck_id)
File "/home/carles/.local/lib/python2.7/site-packages/genanki-0.2.1-py2.7.egg/genanki/__init__.py", line 232, in write_to_db
'', # data
sqlite3.IntegrityError: NOT NULL constraint failed: notes.guid
I created two decks with genanki:
Importing deck A works, but importing deck B fails after I import A: "Notes skipped, as they’re already in your collection"
So Anki thinks the notes are identical because they have the same GUID. The mid
of course is different.
I raised this topic on the Anki forum and it seems they don't consider this worth fixing. Anki author Damien Elmes said:
I think the original intention was to combine notetype ID + guid to get a standard UUID size, but I don't think the current code is
doing that. I'd be surprised if conflicts were common in practice however, outside of usage of third-party tools that are not
generating them randomly.
So I'm wondering if genanki should also use the note ID or deck ID to generate the GUID. This would avoid the problem I encountered.
One question is how to handle such a change without breaking existing deks (GUIDs).
I had like to use genanki to create cards having a sound as front side.
My understanding is that it is not yet possible because genanki does not support media. Do you plan to add media support?
Creating Models is hard, let's add some default models based on Anki's built-in ones.
When creating the deck, upon import, Anki says the notes have been added yet I cannot find them anywhere. Here's the code:
from openpyxl import load_workbook
import genanki
import random
"""
1. be able to generate a random number that's really big without repitions
2. setup note type
3. setup the model
4. make a deck to fill
5. figure out note types
"""
beg = 1000000000
end = 9999999999
id_list = []
for i in range(0, 5000):
randomNum = random.randint(beg,end)
while randomNum in id_list:
randomNum = random.randint(beg, end)
id_list.append(randomNum)
model = genanki.Model(
id_list[-1],
'Simple Model',
fields=[
{'name': 'Meaning'},
{'name': 'Arabic Spelling'},
{'name': 'Phonetic Spelling'},
{'name': 'Audio'},
],
templates=[
{
'name': 'Only Card',
'qfmt': 'How do you say:<div style="font-family: Arial; font-size: 40px; padding: 20px;">{{Arabic Spelling}}</div>',
'afmt': '{{FrontSide}}<hr id=answer><div style="font-family: Arial; font-size: 20px; padding: 20px;">{{Meaning}}</div><div style="font-family: Arial; font-size: 20px; padding: 20px;"><em>{{Phonetic Spelling}}</em></div><div style="font-family: Arial; font-size: 20px; padding: 20px;">{{Audio}}</div>',
},
])
del id_list[-1]
my_deck = genanki.Deck(
id_list[-1],
'test')
del id_list[-1]
wb = load_workbook("all_words.xlsx")
sheet = wb.get_sheet_by_name('ALL WORDS')
englishWordList = []
for col_cells in sheet.iter_cols(min_row=2, min_col=1, max_col=1):
for cell in col_cells:
englishWordList.append(cell.value)
i = 0
for word in englishWordList:
note = genanki.Note(
sort_field=id_list[i],
model=model,
fields=[word, "", "", ""]
)
my_deck.add_note(note)
i = i + 1
i = 0
genanki.Package(my_deck).write_to_file("testOutput.apkg")
Anki adds all the notes (which have no cards made) yet I can't find them inside Anki. I thought maybe it's because of my add-ons, yet from the readme file it's unclear how to use the .write_to_collection_from_addon() method. I don't know how to use the method.
When I close and reopen Anki, it says there is a problem with the database and tells me to "Check Database". Once I do this, it deletes all the notes that were added from my script. What's the best way to use the method, and/or what is the solution to this issue? I've tried looking everywhere.
Please could you tell me how to stop gen anki putting <style> tags in? I would like it just to be plain text.
Very best,
S
Line 124 in d3a5ac5
I ran into this error recently when installing genanki on a new user account (and incidentally updating it), and it was very hard to debug.
I generate thousands of cards each time across two note types, and I still don't know which note type or card generated the problem – instead, I ended up downgrading to 0.8.0.
I suggest adding model name or similar to the valueError, but am not sufficiently competent to add a new commit :-)
Hi Kerrick,
I have found this repository and very much like it. Thank your for investing your time into that code.
As I have not too much experience with python and some with Linux, I struggle to get it to run.
I have managed to download and install these 3 packages:
but "pyyaml" I am not sure if it was installed, as I downloaded that manually and not with the Software Manager of Linux.
When I do a "make install" in the "genanki-master"-directory, I receive this output:
python3 setup.py install --user
Traceback (most recent call last):
File "setup.py", line 1, in <module>
from setuptools import setup
ModuleNotFoundError: No module named 'setuptools'
Makefile:3: recipe for target 'install' failed
make: *** [install] Error 1
It seems like it point to this line here:
Line 1 in 6d173fc
Thank you very much for your help :)
Best regards
Joel
Tags should not contain spaces. If you pass a tag that contains spaces, it will be split into two tags. Instead we should raise an exception.
Following the discussion on issue #74 thought it would be good to open a specific issue on generating documentation in case the community had any thoughts on what the best docs format would be and to see if anyone would like to contribute in making one.
I'm confused as to the connection between this branch and the one here:
https://github.com/dvklopfenstein/genanki
Though the dvklopfenstein fork has the same version number it appears to have received more recent contributions from kerrickstaley than this branch has. For that reason, I'm using the dvklopfenstein fork.
Should I be? And if I have questions to ask or issues to report, where should I post them? I guess here because the other fork's "Issues" button is missing, but please confirm.
Anki allows HTML in field data, but for the sort field, it looks like it renders it down to plain ASCII. Currently genanki just copies the field data directly into the sort field, without stripping the HMTL tags. We should mimic Anki's behavior. (Needs more investigation / more detailed examples, just putting this here so I don't lose track of it.)
I've reviewed the prior issues and I don't think the problem is related to #46. Neither my sound files nor images appear when the .apkg file is loaded. I'm using genaki version 0.80.
ItalianModel = genanki.Model(
1300703785,
'Italian Model',
fields=[
{'name': 'Question'},
{'name': 'Answer'},
{'name': 'Image'},
{'name': 'Audio'},
],
templates=[
{
'name': 'Card 1',
'qfmt': '{{Question}}<br><br>{{Image}}',
'afmt': '{{FrontSide}}<hr id="answer">{{Answer}}<br>{{ Audio}}',
},
],
)
ItalianDeck = genanki.Deck(
1281791336,
'My Italian Deck')
def build_anki_deck(*source):
package = genanki.Package(ItalianDeck)
for question, answer, image, mp3file in source:
package.media_files.append(image)
package.media_files.append(mp3file)
note = genanki.Note(
model=ItalianModel,
fields=[
question,
answer,
'<img src=\"{}\">'.format(image),
"[sound:{}]".format(mp3file),
])
ItalianDeck.add_note(note)
genanki.Package(ItalianDeck).write_to_file('output.apkg')
The script is run from the same path where all the media files are located. I'm running Python 3.7.5 on Linux and the libraries are in a virtual environment.
hi , thank you for the amazing library you maid.
I remark that when i generate a deck with genanki and just after import it , the creation date displayed by anki is incorrect . check the picture below :
My main problem is that when i create decks by adding notes one by one , this order is not the same in anki .
Is it possible to set the description that appears on the study screen?
Hi, I think class Deck have an attribute to save card added to Deck,
Thank you very much.
Hi guys!
You're doing great work, and this software has allowed me to do a bunch of things I couldn't do without it. Is there a place I can donate to, or do you have a preferred charity? :-)
Martin
My version Version 2.1.36 (c505894b)
Python 3.8.6 Qt 5.14.2 PyQt 5.14.2
Problem I am creating notes with genanki but I realised that for some notes which have the characters <
/>
in them they are partially parsed. So, I've checked the collections.anki2 file within the genanki-generate .apkg. (the following refers to the notes table of the Anki sqlite database) .E.g. for the flds value
2-Programming-Techniq...�0�code out the simplest c++ template for CP?��#include <bits/stdc++.h>
using namsepace std;
int main() {
}
where each field is correctly separated by the 0x1f character, I incorrectly get the output shown on the image. **The field is correctly set for other string values, so I was wondering if this is a bug relating to how it escapes <
and >
character? @kerrickstaley **
I've disabled all add-ons to make sure it wasn't an add-on that incorrectly modified the card.
[decks_info.json] [ { "name": "Guide to Competitive Programming", "cards": [ { "name": "code out the simplest c++ template for CP?", "back": "", "tags": [ "Guide-to-Competitive-Programming::2-Programming-Techniq..." ], "media": [], "model": "code", "number": 0, "answer": "#include <bits/stdc++.h>\n\nusing namsepace std;\n\nint main() {\n}" },
[note model] based on fields=[ {"name": "CategoryTag"}, {"name": "OrderNumber"}, {"name": "Front"}, {"name": "Back"}, {"name": "Answer"}, {"name": "MyMedia"}, ],
This library is really a convenient way to create decks! I am trying to create decks programmatically to learn the names of my students (https://github.com/jlumbroso/tiger-anki). As such, I am trying to associate names to images and vice-versa.
This seems to be a perfect use of the media_files
attribute. However, every attempt I have made, results in a deck in which Anki displays all images as "broken images."
I have attached a minimal reproducible example, test.py
, on my system with Python 3.6, Anki 2.1.4 and genanki 0.6.0 (installed with pip). It generates an output.apkg
deck, with a single card that produces one of these broken links.
When I unzip the deck, it seems like the media
file is encoded appropriately following the format specs:
http://decks.wikia.com/wiki/Anki_APKG_format_documentation#Media_Formatting
I am not sure what is the wrong, or whether it is an issue with genanki or anki.
I created a minimal example in the following gist:
https://gist.github.com/jlumbroso/60971ec65324603c0ab2efd02fd8098b
which will generate the deck python3 genanki-media-test.py
.
The file I have included as an example image format.jpg
is:
But any JPG image would do.
On Arch Linux, Python 3.7.2
If I create a apkg file with this script:
import genanki
my_deck = genanki.Deck(1391554143, "my_deck")
my_model = genanki.Model(
1836072805,
"my_model",
fields=[{"name": "Question"}, {"name": "Answer"}],
templates=[{"name": "Card 1", "qfmt": "{{Question}}", "afmt": "{{Answer}}"}],
)
rxn0 = r"""[latex]
\schemestart
\chemfig{*6(--(<OH)-(<:Br)---)}
\arrow{->[?]}
\chemfig{*6(--(<[:30]{O}?)(<:H)-?[,{>},](<:H)---)}
\schemestop[/latex]"""
reagents0 = r"""\(\ce{NaOH, H_2O_2}\)"""
rxn1 = r"""[latex]
\schemestart
\chemfig{*6(--=---)}
\arrow{->[?]}
\chemfig{*6(--(<[:30]{O}?)(<:H)-?[,{>},](<:H)---)}
\schemestop[/latex]
"""
reagents1 = r"""\(\ce{mCPBA, CH_2Cl_2}\)"""
rxn2 = r"""[latex]
\schemestart
\chemfig{C(-[:0]
C(-[:90]X)(-[:0])(-[:270])
)(-[:90])(-[:180])(-[:270]H)}
\arrow{->[?]}
\chemfig{C(-[:120])(-[:-120])=C(-[:60])(-[:-60])} \+ \ce{HX}
\schemestop[/latex]
"""
reagents2 = r"""\(\ce{Na^{+}{}^{-}OCH_2CH_3, EtOH, 70°C}\)"""
my_deck.add_note(genanki.Note(model=my_model, fields=[rxn0, reagents0]))
my_deck.add_note(genanki.Note(model=my_model, fields=[rxn1, reagents1]))
my_deck.add_note(genanki.Note(model=my_model, fields=[rxn2, reagents2]))
genanki.Package(my_deck).write_to_file("output.apkg")
results in an anki popup window that says:
Notes found in file: 3
Notes added from file: 3
[Added] [latex]
\schemestart
\chemfig{*6(--(<OH)-(<:Br)---)}
\arrow{->[?]}
\chemfig{*6(--(<[:30]{O}?)(<:H)-?[,{>},](<:H)---)}
\schemestop[/latex], \(\ce{NaOH, H_2O_2}\)
[Added] [latex]
\schemestart
\chemfig{*6(--=---)}
\arrow{->[?]}
\chemfig{*6(--(<[:30]{O}?)(<:H)-?[,{>},](<:H)---)}
\schemestop[/latex]
, \(\ce{mCPBA, CH_2Cl_2}\)
[Added] [latex]
\schemestart
\chemfig{C(-[:0]
C(-[:90]X)(-[:0])(-[:270])
)(-[:90])(-[:180])(-[:270]H)}
\arrow{->[?]}
\chemfig{C(-[:120])(-[:-120])=C(-[:60])(-[:-60])} \+ \ce{HX}
\schemestop[/latex]
, \(\ce{Na^{+}{}^{-}OCH_2CH_3, EtOH, 70°C}\)
which looks fine.
And I set the appropriate latex header and enable dvisvgm:
\documentclass[varwidth=100cm]{standalone}
\usepackage[version=4]{mhchem}
\usepackage{chemfig}
\being{document}
However, when studying or browsing, for some reason the latex output of rxn0 and rxn1 gets messed up:
[latex] \schemestart \chemfig{*6(--([?]} \chemfig{*6(--(<[:30]{O}?)(<:H)-?[,{>},](<:H)---)} \schemestop[/latex]
/tmp/anki_temp/tmp.tex
:
\documentclass[varwidth=100cm]{standalone}
\usepackage[version=4]{mhchem}
\usepackage{chemfig}
\being{document}
\schemestart
\chemfig{*6(--([?]}
\chemfig{*6(--(<[:30]{O}?)(<:H)-?[,{>},](<:H)---)}
\schemestop
\end{document}
when it should be:
[latex]
{\Large Halohydrine $\to$ Epoxide}\\[12pt]
\schemestart
\chemfig{*6(--(<OH)-(<:Br)---)}
\arrow{->[?]}
\chemfig{*6(--(<[:30]{O}?)(<:H)-?[,{>},](<:H)---)}
\schemestop[/latex]
/tmp/anki_temp/tmp.tex
:
\documentclass[varwidth=100cm]{standalone}
\usepackage[version=4]{mhchem}
\usepackage{chemfig}
\being{document}
\schemestart
\chemfig{*6(--(<OH)-(<:Br)---)}
\arrow{->[?]}
\chemfig{*6(--(<[:30]{O}?)(<:H)-?[,{>},](<:H)---)}
\schemestop
\end{document}
[latex] \schemestart \chemfig{*6(--=---)} \arrow{->[?]} \chemfig{*6(--(<[:30]{O}?)(<:H)-?[,{>},](<:H)---)} \schemestop[/latex]
/tmp/anki_temp/tmp.tex
:
\documentclass[varwidth=100cm]{standalone}
\usepackage[version=4]{mhchem}
\usepackage{chemfig}
\begin{document}
\schemestart
\chemfig{*6(--=---)}
\arrow{->[?]}
\chemfig{*6(--(},](<:H)---)}
\schemestop
\end{document}
instead of
[latex]
\schemestart
\chemfig{*6(--=---)}
\arrow{->[?]}
\chemfig{*6(--(<[:30]{O}?)(<:H)-?[,{>},](<:H)---)}
\schemestop[/latex]
/tmp/anki_temp/tmp.tex
:
\documentclass[varwidth=100cm]{standalone}
\usepackage[version=4]{mhchem}
\usepackage{chemfig}
\begin{document}
\schemestart
\chemfig{*6(--=---)}
\arrow{->[?]}
\chemfig{*6(--(<[:30]{O}?)(<:H)-?[,{>},](<:H)---)}
\schemestop
\end{document}
So basically,
\chemfig{*6(--(<OH)-(<:Br)---)}
\arrow{->[?]}
becomes
\chemfig{*6(--([?]}
and
\chemfig{*6(--(<[:30]{O}?)(<:H)-?[,{>},](<:H)---)}
becomes
\chemfig{*6(--(},](<:H)---)}
What's stranger is that I tested the python script several times and one time rxn1 did not have the issue. rxn2 has never been affected by this quirk. For some reason, chemfigs of epoxides get messed up by anki when importing a apkg generated by genanki.
Currently when generating decks with genanki all the cards have the due date/order of 0. This can be seen when viewing the deck in the browser.
Would be great to auto-generate the due order within the code if possible or to be able to set the start so that it would start counting from the value given instead of zero.
This is particularly helpful when auto generating flashcards for a course where learning order makes it easier to learn.
Again, would be happy to do a pull request if given guidance on where to insert what in the relevant sections of the codebase.
Firstly would like to thank you for making this @kerrickstaley ! My application is making Anki cards for children learning spellings, now that the schools are closing for COVID-19 in UK. I have audio files named after each word to be learned in the folder where my .py
file is (using Pycharm).
When I package up and then import to Anki, I can't get the audio files to play unless I manually find the collection.media
folder in the Anki2 appdata folder (on Win10). When I unzip the Anki package in 7Zip and open the media
file, the only contents are {}
, so I currently think this is a packaging issue.
My code is below. The whole thing works really well when I manually add the media files but from the readme.md section on adding media, it seems they should be packaged for import too. Thanks for any insight you can offer - this will help so many people if I can get it to work (making all the spelling lists automatically into Anki cards using Text to Speech and genanki)!
#read the file into a list of words
lines_list = open('file.txt').read().splitlines()
# declare the Deck and give it a name and unique ID number
my_deck = genanki.Deck(
2759406110,
'Test Deck')
# Declare the Model to be used (sets the format for the cards)
my_model = genanki.Model(
1607792720,
'Basic (type in the answer)',
fields=[ # These fields need to match up with the field names used inside the `Template` section below
{'name': 'Front'},
{'name': 'Back'},
],
templates=[ # {{type:Back}} is what prompts Anki to put the box in the card for text input for your answer. Anki compares and shows a "diff" against the real answer
{
'name': 'Card 1',
'qfmt': '{{Front}} {{type:Back}}', # Question Format?
'afmt': '{{Front}}<hr id="answer">{{type:Back}}', # Answer Format? `Frontside` can be used instead of 'Front' if you want to display the whole front as well as the back, but this can lead to repetition
},
])
mediaList = [] # declare a list to hold the names of all our media files
for l in lines_list:
mediaName = l + '.mp3' # generate the file name for the audio file from the word in the list
mediaList.append(l + '.mp3') # append the name of each media file to the list
soundName = '[sound:' + mediaName + ']'
print(soundName)
my_note = genanki.Note(
model=my_model,
fields=[soundName , l])
my_deck.add_note(my_note)
print(mediaList) # show the list for debug purposes
my_package = genanki.Package(my_deck)
my_package.media_files = mediaList # Contains the list of all our media files. May need to experiment with and without square braces here
#my_package.media_files = ['actual.mp3','bicycle.mp3','business.mp3']
genanki.Package(my_deck).write_to_file('test_output.apkg')
When creating a new deck with a set of new cards, running the line my_package.media_files = media_list
, and importing the .apkg file to Anki the media names do appear in the cards fields, but neither the images are shown or the sound is being played, which seems to indicate that my_package.media_files
is not doing its job (here is a sample code that supposedly should work that I also tried: https://gist.github.com/jlumbroso/e17abff02d89be04240072191af09ab2)
It took me a while to realize why my new Anki decks could not show the media files that I wanted to, until I ended up changing my_package.media_files = media_list
with the following function, where media_path is the folder where Anki stores all the media (C:\Users\user\AppData\Roaming\Anki2\User 1\collection.media)
def move_media(media_list, media_path):
for file in media_list:
...destination = media_path + '\\' + os.path.basename(file)
...shutil.move(file, destination)
those existing Note types already in anki.
Do I need to find the model id? How do I do that?
Currently I'm manually changing note types after importing the deck.
I am making a toy example where I make the template for "afmt" be this string:
'afmt': {{FrontSide}}<hr id='answer'>{{Answer}}
However, using single quotes around answer notably gives me a syntax error, meaning this code does not use a prepared statement and hence is vulnerable to a SQL injection. Although I'm pretty sure the stakes here are low, it's an issue worth fixing, since any question or answer template that contains a single quote is rejected.
Thank you very much for making this repo! It's been really useful to me :)
Since this project doesn't have the GitHub Discussions feature enabled, I thought I'd ask my question here.
The official Anki application is open-source and has classes for importing (https://github.com/ankitects/anki/tree/main/pylib/anki/importing) and exporting (https://github.com/ankitects/anki/blob/main/pylib/anki/exporting.py).
How does this compare? Are there any pros/cons for using one over the other?
From what I can see, genanki seems to have a more permissive licence and the API is documented. On the other hand, I would be concerned that since it's not an official library that the apkg files it generates could have compatibility issues with the official Anki applications.
Thanks!
import genanki
my_model = genanki.Model(
1091735104,
'Simple Model with Media',
fields=[
{'name': 'Question'},
{'name': 'Answer'},
],
templates=[
{
'name': 'Card 1',
'qfmt': '{{Question}}<br>{{MyMedia}}',
'afmt': '{{FrontSide}}<hr id="answer">{{Answer}}',
},
])
my_note = genanki.Note(
model=my_model,
fields=['Capital of Argentina', 'Buenos Aires'])
my_deck = genanki.Deck(
2059400110,
'Country Capitals')
my_deck.add_note(my_note)
genanki.Package(my_deck).write_to_file('output.apkg')
Produces a file, where the question on the card can not be displayed, because it needs {{MyMedia}}, but I didn't define {{MyMedia}} as a field.
Should there maybe be a check if all fields are present, or should all template items which have no corresponding field be replaced with an empty string?
As I'm currently working on a Rust port of genanki I am quite familiar with the codebase.
I would like to solve this issue, if you can tell me, whether this is intended or not, and if yes, where to insert a check in the codebase.
First of all, thank you for this awesome tool.
I have a few questions about the model id and deck id behavior. According the README, the model id must be randomly generated and then hardcoded into the source code:
You need to pass a
model_id
so that Anki can keep track of your model. It's important that you use a uniquemodel_id
for each Model you define. Userandom.randrange(1 << 30, 1 << 31)
to generate a suitablemodel_id
, and hardcode it into your Model definition.
Same for the deck id:
Once again, you need a unique
deck_id
that you should generate once and then hardcode into your.py
file.
After reading this, I have the following questions:
How do the model and deck ids work in Anki? Can the model id be unique per deck? What if my deck id collides with another deck already imported in my Anki profile?
Also, why does the id need to be hardcoded? Can the change the deck id change on future versions? What would happen in that case?
Hi,
I wanted to use sort_field_index
but it appears the feature has not been released yet. The latest release is from December 2020 but sort_field_index
was implemented only 3 months ago. Can you release a new version or is there another (clean) way to make use of that feature?
Also, to me it is not clear what sort_field
is expecting. Is it
sort_field=10
appears before a card with sort_field=11
Level
, then sort_field="Level"
will sort all cards according to the value of the Field Level
Thanks!
Best
René
Thanks for publishing this! In case you'd like to make your repo more findable, you could add that topic, or another common topic that those other projects use ;-) Thank you!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.