Anki *.apkg reader in a human-readable format; and an editor
Project description
# AnkiTools
[](https://travis-ci.org/patarapolw/AnkiTools)
[](https://pypi.python.org/pypi/AnkiTools/)
[](https://pypi.python.org/pypi/AnkiTools/)
[](https://pypi.python.org/pypi/AnkiTools/)
[](https://pypi.python.org/pypi/AnkiTools/)
An Anki \*.apkg and \*.anki2 reader/editor to work with in Python. Also included a module on [AnkiConnect](https://github.com/FooSoft/anki-connect).
## Installation
pip install AnkiTools
## Parsing \*.apkg and \*.anki2 in a human readable and easily manageable format.
>>> from AnkiTools.tools.read import readApkg
>>> anki = readApkg('test/testfile/Chinese.apkg')
>>> anki.models
{'1514177308904': {'mid': '1514177308904',
'name': 'Chinese Hanzi Freq',
'fields': ['frequency', 'Hanzi', 'Count', ...],
'templates': [{'name': 'Writing',
'qfmt': '<a href="http://hanzi.koohii.com/study/?framenum={{text:Count}}">{{English}}</a>',
'did': None,
'bafmt': '',
'afmt': '{{FrontSide}}\n\n<hr id=answer>\n ...',
'ord': 0,
'bqfmt': ''},
{'name': 'Meaning',
'qfmt': '<span class="hanzi"><a href="http://www.nciku.com/search/zh/{{text:Hanzi}}" style="text-decoration:none; color: black">{{Hanzi}}</a></span>\n<br>\nMeaning:\n{{type:English}}',
'did': None,
'bafmt': '',
'afmt': '{{FrontSide}}\n\n<hr id=answer>\n ...',
'ord': 1,
'bqfmt': ''},
{'name': 'Reading',
'qfmt': '<span class="hanzi"><a href="http://www.nciku.com/search/zh/{{text:Hanzi}}" style="text-decoration:none; color: black">{{Hanzi}}</a></span><br>\nReading:\n{{type:Pinyin}}',
'did': None,
'bafmt': '',
'afmt': '{{FrontSide}}\n\n<hr id=answer>\n ...',
'ord': 2,
'bqfmt': ''}]},
...
}
>>> anki.decks
{'1518095427151': {'did': '1518095427151',
'name': 'Chinese::Vocab::English::21-30 Death::Level 21'},
'1518098032250': {'did': '1518098032250',
'name': 'Chinese::Hanzi::Meaning::61-100 Infinity::Level 77'},
'1518097744745': {'did': '1518097744745',
'name': 'Chinese::Hanzi::Writing::41-50 Paradise::Level 49'},
...
}
>>> anki.notes
{'1419644212689': {'nid': '1419644212689',
'mid': '1377171239634',
'model': {'mid': '1377171239634',
'name': 'SpoonFed',
'fields': ['English', 'Pinyin', 'Hanzi', 'Audio'],
'templates': [{'name': 'CE',
'qfmt': '<font style="font-family:SimSun;">{{Hanzi}}</font><br>\n[sound:silence.mp3]\n{{Audio}}',
'did': None,
'bafmt': '',
'afmt': '<font style="font-family:SimSun;">{{Hanzi}}</font><br>\n{{Pinyin}}<br>\n{{English}}<br>\n{{Audio}}',
'ord': 0,
'bqfmt': ''},
{'name': 'EC',
'qfmt': '{{English}}',
'did': None,
'bafmt': '',
'afmt': '{{English}}<br>\n{{Pinyin}}<br>\n<font style="font-family:SimSun;">{{Hanzi}}</font><br>\n{{Audio}}',
'ord': 1,
'bqfmt': ''}]},
'content': ['Hello!', 'Nǐ hǎo!', '你好!', '[sound:tmp1cctcn.mp3]'],
'tags': ['']},
...
}
>>> anki.cards
{'1419644220831': {'cid': '1419644220831',
'nid': '1419644212689',
'note': {'nid': '1419644212689',
'mid': '1377171239634',
'model': {'mid': '1377171239634',
'name': 'SpoonFed',
'fields': ['English', 'Pinyin', 'Hanzi', 'Audio'],
'templates': [{'name': 'CE',
'qfmt': '<font style="font-family:SimSun;">{{Hanzi}}</font><br>\n[sound:silence.mp3]\n{{Audio}}',
'did': None,
'bafmt': '',
'afmt': '<font style="font-family:SimSun;">{{Hanzi}}</font><br>\n{{Pinyin}}<br>\n{{English}}<br>\n{{Audio}}',
'ord': 0,
'bqfmt': ''},
{'name': 'EC',
'qfmt': '{{English}}',
'did': None,
'bafmt': '',
'afmt': '{{English}}<br>\n{{Pinyin}}<br>\n<font style="font-family:SimSun;">{{Hanzi}}</font><br>\n{{Audio}}',
'ord': 1,
'bqfmt': ''}]},
'content': ['Hello!', 'Nǐ hǎo!', '你好!', '[sound:tmp1cctcn.mp3]'],
'tags': ['']},
'did': '1518099616462',
'deck': {'did': '1518099616462',
'name': 'Chinese::sentence::CE:: 1-10 Pleasant::Level 1'},
'ord': 0},
...
}
>>> anki.close()
Using `readApkg()` as a context manager also works.
```python
from AnkiTools.tools.read import readApkg
with readApkg('Chinese.apkg') as anki:
pass
```
`readAnki2()` also works the same way, but without `readAnki2.close()` function.
```python
from AnkiTools.tools.read import readAnki2
with readAnki2('collection.anki2') as anki:
pass
```
Result formats
```
# Model
{
'mid': model_id,
'name': model_name,
'fields': fieldNames,
'templates': [{
"name": template_name,
"qfmt": question_markup,
"did":null,
"bafmt":"",
"afmt": answer_markup,
"ord": order_in_template,
"bqfmt":""
}, ... ]
}
# Deck
{
'did': deck_id,
'name': deck_name
}
# Note
{
'nid': note_id,
'mid': model_id,
'model': {
'mid': model_id,
'name': model_name,
'fields': fieldNames,
'templates': templateNames
}
'content': list_of_contents,
'tags': list_of_tags
}
# Card
{
'cid': card_id
'nid': note_id,
'note': {
'nid': note_id,
'mid': model_id,
'model': {
'name': model_name,
'fields': fieldNames,
'templates': list_of_templates
}
'content': content,
'tags': tags
}
'did': deck_id,
'deck': {
'did': deck_id,
'name': v['name']
}
'ord': ord
}
```
See also the \*.apkg format documentation from [Anki decks collaboration Wiki](http://decks.wikia.com/wiki/Anki_APKG_format_documentation) and [AnkiDroid](https://github.com/ankidroid/Anki-Android/wiki/Database-Structure)
## Editing a \*.apkg and \*.anki2 file without Anki
It will also generate a new model/deck/note/card, if one doesn't exist. The ID's are Unix timestamp in milliseconds.
Subdecks can be made by putting in `::`; for example, `Chinese::SpoonFedChinese`.
```python
from AnkiTools.tools.edit import editApkg
with edit.editApkg('Chinese.apkg') as anki:
anki.updateModels([{
'mid': model_id, # May be left out
'name': model_name,
'fields': list_of_field_names,
'templates': list_of_template_names
}])
anki.updateDecks([{
'did': deck_id, # May be left out
'name': deck_name
}])
anki.updateNotes([{
'nid': note_id, # May be left out
'mid': model_id, # Must specify either mid or model
'model': { # Will be ignored if mid is specified
'name': model_name,
'fields': list_of_field_names,
'templates': list_of_template_names
}
'content': list_of_field_contents,
'tags': list_of_tags
}])
anki.updateCards([{
'cid': card_id, # May be left out
'nid': note_id, # Must specify either nid or note
'note': {
'model': {
'name': model_name,
'fields': list_of_field_names,
'templates': list_of_template_names
}
'content': list_of_field_contents,
'tags': list_of_tags
}
'did': deck_id, # Must specify either did or deck
'deck': {
'name': deck_name
}
'ord': order_in_list_of_template_names
}])
```
## Exporting \*.anki2 to \*.apkg
```python
from AnkiTools.tools.edit import editAnki2
with edit.editAnki2('Chinese.anki2') as anki:
anki.export()
```
## AnkiConnect module
```python
from AnkiTools.AnkiConnect import POST
POST('deckNames')
```
You can also specify `params=dict()` in POST. Version is set to `5` as per default. For what you can put in, please refer to [AnkiConnect](https://github.com/FooSoft/anki-connect).
[](https://travis-ci.org/patarapolw/AnkiTools)
[](https://pypi.python.org/pypi/AnkiTools/)
[](https://pypi.python.org/pypi/AnkiTools/)
[](https://pypi.python.org/pypi/AnkiTools/)
[](https://pypi.python.org/pypi/AnkiTools/)
An Anki \*.apkg and \*.anki2 reader/editor to work with in Python. Also included a module on [AnkiConnect](https://github.com/FooSoft/anki-connect).
## Installation
pip install AnkiTools
## Parsing \*.apkg and \*.anki2 in a human readable and easily manageable format.
>>> from AnkiTools.tools.read import readApkg
>>> anki = readApkg('test/testfile/Chinese.apkg')
>>> anki.models
{'1514177308904': {'mid': '1514177308904',
'name': 'Chinese Hanzi Freq',
'fields': ['frequency', 'Hanzi', 'Count', ...],
'templates': [{'name': 'Writing',
'qfmt': '<a href="http://hanzi.koohii.com/study/?framenum={{text:Count}}">{{English}}</a>',
'did': None,
'bafmt': '',
'afmt': '{{FrontSide}}\n\n<hr id=answer>\n ...',
'ord': 0,
'bqfmt': ''},
{'name': 'Meaning',
'qfmt': '<span class="hanzi"><a href="http://www.nciku.com/search/zh/{{text:Hanzi}}" style="text-decoration:none; color: black">{{Hanzi}}</a></span>\n<br>\nMeaning:\n{{type:English}}',
'did': None,
'bafmt': '',
'afmt': '{{FrontSide}}\n\n<hr id=answer>\n ...',
'ord': 1,
'bqfmt': ''},
{'name': 'Reading',
'qfmt': '<span class="hanzi"><a href="http://www.nciku.com/search/zh/{{text:Hanzi}}" style="text-decoration:none; color: black">{{Hanzi}}</a></span><br>\nReading:\n{{type:Pinyin}}',
'did': None,
'bafmt': '',
'afmt': '{{FrontSide}}\n\n<hr id=answer>\n ...',
'ord': 2,
'bqfmt': ''}]},
...
}
>>> anki.decks
{'1518095427151': {'did': '1518095427151',
'name': 'Chinese::Vocab::English::21-30 Death::Level 21'},
'1518098032250': {'did': '1518098032250',
'name': 'Chinese::Hanzi::Meaning::61-100 Infinity::Level 77'},
'1518097744745': {'did': '1518097744745',
'name': 'Chinese::Hanzi::Writing::41-50 Paradise::Level 49'},
...
}
>>> anki.notes
{'1419644212689': {'nid': '1419644212689',
'mid': '1377171239634',
'model': {'mid': '1377171239634',
'name': 'SpoonFed',
'fields': ['English', 'Pinyin', 'Hanzi', 'Audio'],
'templates': [{'name': 'CE',
'qfmt': '<font style="font-family:SimSun;">{{Hanzi}}</font><br>\n[sound:silence.mp3]\n{{Audio}}',
'did': None,
'bafmt': '',
'afmt': '<font style="font-family:SimSun;">{{Hanzi}}</font><br>\n{{Pinyin}}<br>\n{{English}}<br>\n{{Audio}}',
'ord': 0,
'bqfmt': ''},
{'name': 'EC',
'qfmt': '{{English}}',
'did': None,
'bafmt': '',
'afmt': '{{English}}<br>\n{{Pinyin}}<br>\n<font style="font-family:SimSun;">{{Hanzi}}</font><br>\n{{Audio}}',
'ord': 1,
'bqfmt': ''}]},
'content': ['Hello!', 'Nǐ hǎo!', '你好!', '[sound:tmp1cctcn.mp3]'],
'tags': ['']},
...
}
>>> anki.cards
{'1419644220831': {'cid': '1419644220831',
'nid': '1419644212689',
'note': {'nid': '1419644212689',
'mid': '1377171239634',
'model': {'mid': '1377171239634',
'name': 'SpoonFed',
'fields': ['English', 'Pinyin', 'Hanzi', 'Audio'],
'templates': [{'name': 'CE',
'qfmt': '<font style="font-family:SimSun;">{{Hanzi}}</font><br>\n[sound:silence.mp3]\n{{Audio}}',
'did': None,
'bafmt': '',
'afmt': '<font style="font-family:SimSun;">{{Hanzi}}</font><br>\n{{Pinyin}}<br>\n{{English}}<br>\n{{Audio}}',
'ord': 0,
'bqfmt': ''},
{'name': 'EC',
'qfmt': '{{English}}',
'did': None,
'bafmt': '',
'afmt': '{{English}}<br>\n{{Pinyin}}<br>\n<font style="font-family:SimSun;">{{Hanzi}}</font><br>\n{{Audio}}',
'ord': 1,
'bqfmt': ''}]},
'content': ['Hello!', 'Nǐ hǎo!', '你好!', '[sound:tmp1cctcn.mp3]'],
'tags': ['']},
'did': '1518099616462',
'deck': {'did': '1518099616462',
'name': 'Chinese::sentence::CE:: 1-10 Pleasant::Level 1'},
'ord': 0},
...
}
>>> anki.close()
Using `readApkg()` as a context manager also works.
```python
from AnkiTools.tools.read import readApkg
with readApkg('Chinese.apkg') as anki:
pass
```
`readAnki2()` also works the same way, but without `readAnki2.close()` function.
```python
from AnkiTools.tools.read import readAnki2
with readAnki2('collection.anki2') as anki:
pass
```
Result formats
```
# Model
{
'mid': model_id,
'name': model_name,
'fields': fieldNames,
'templates': [{
"name": template_name,
"qfmt": question_markup,
"did":null,
"bafmt":"",
"afmt": answer_markup,
"ord": order_in_template,
"bqfmt":""
}, ... ]
}
# Deck
{
'did': deck_id,
'name': deck_name
}
# Note
{
'nid': note_id,
'mid': model_id,
'model': {
'mid': model_id,
'name': model_name,
'fields': fieldNames,
'templates': templateNames
}
'content': list_of_contents,
'tags': list_of_tags
}
# Card
{
'cid': card_id
'nid': note_id,
'note': {
'nid': note_id,
'mid': model_id,
'model': {
'name': model_name,
'fields': fieldNames,
'templates': list_of_templates
}
'content': content,
'tags': tags
}
'did': deck_id,
'deck': {
'did': deck_id,
'name': v['name']
}
'ord': ord
}
```
See also the \*.apkg format documentation from [Anki decks collaboration Wiki](http://decks.wikia.com/wiki/Anki_APKG_format_documentation) and [AnkiDroid](https://github.com/ankidroid/Anki-Android/wiki/Database-Structure)
## Editing a \*.apkg and \*.anki2 file without Anki
It will also generate a new model/deck/note/card, if one doesn't exist. The ID's are Unix timestamp in milliseconds.
Subdecks can be made by putting in `::`; for example, `Chinese::SpoonFedChinese`.
```python
from AnkiTools.tools.edit import editApkg
with edit.editApkg('Chinese.apkg') as anki:
anki.updateModels([{
'mid': model_id, # May be left out
'name': model_name,
'fields': list_of_field_names,
'templates': list_of_template_names
}])
anki.updateDecks([{
'did': deck_id, # May be left out
'name': deck_name
}])
anki.updateNotes([{
'nid': note_id, # May be left out
'mid': model_id, # Must specify either mid or model
'model': { # Will be ignored if mid is specified
'name': model_name,
'fields': list_of_field_names,
'templates': list_of_template_names
}
'content': list_of_field_contents,
'tags': list_of_tags
}])
anki.updateCards([{
'cid': card_id, # May be left out
'nid': note_id, # Must specify either nid or note
'note': {
'model': {
'name': model_name,
'fields': list_of_field_names,
'templates': list_of_template_names
}
'content': list_of_field_contents,
'tags': list_of_tags
}
'did': deck_id, # Must specify either did or deck
'deck': {
'name': deck_name
}
'ord': order_in_list_of_template_names
}])
```
## Exporting \*.anki2 to \*.apkg
```python
from AnkiTools.tools.edit import editAnki2
with edit.editAnki2('Chinese.anki2') as anki:
anki.export()
```
## AnkiConnect module
```python
from AnkiTools.AnkiConnect import POST
POST('deckNames')
```
You can also specify `params=dict()` in POST. Version is set to `5` as per default. For what you can put in, please refer to [AnkiConnect](https://github.com/FooSoft/anki-connect).
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
No source distribution files available for this release.See tutorial on generating distribution archives.
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file AnkiTools-0.1.8-py2.py3-none-any.whl.
File metadata
- Download URL: AnkiTools-0.1.8-py2.py3-none-any.whl
- Upload date:
- Size: 10.5 kB
- Tags: Python 2, Python 3
- Uploaded using Trusted Publishing? No
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8614050d1e30d81f3d3cfe3fe80036ba9b2b610da440e06dd4eb2bf6cc3a954f
|
|
| MD5 |
a5e2ef1ae0e704c5bf3a312e9d8b882c
|
|
| BLAKE2b-256 |
5470ba524b6868d9d3354cb7b2d2451c3cd6550d01769c7ea245cefbaa9e03bb
|