Skip to main content

Anki *.apkg reader in a human-readable format; and an editor

Project description

# AnkiTools

[![Build Status](https://travis-ci.org/patarapolw/AnkiTools.svg?branch=master)](https://travis-ci.org/patarapolw/AnkiTools)
[![PyPI version shields.io](https://img.shields.io/pypi/v/AnkiTools.svg)](https://pypi.python.org/pypi/AnkiTools/)
[![PyPI license](https://img.shields.io/pypi/l/AnkiTools.svg)](https://pypi.python.org/pypi/AnkiTools/)
[![PyPI pyversions](https://img.shields.io/pypi/pyversions/AnkiTools.svg)](https://pypi.python.org/pypi/AnkiTools/)
[![PyPI status](https://img.shields.io/pypi/status/AnkiTools.svg)](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


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

If you're not sure about the file name format, learn more about wheel file names.

AnkiTools-0.1.8-py2.py3-none-any.whl (10.5 kB view details)

Uploaded Python 2Python 3

File details

Details for the file AnkiTools-0.1.8-py2.py3-none-any.whl.

File metadata

File hashes

Hashes for AnkiTools-0.1.8-py2.py3-none-any.whl
Algorithm Hash digest
SHA256 8614050d1e30d81f3d3cfe3fe80036ba9b2b610da440e06dd4eb2bf6cc3a954f
MD5 a5e2ef1ae0e704c5bf3a312e9d8b882c
BLAKE2b-256 5470ba524b6868d9d3354cb7b2d2451c3cd6550d01769c7ea245cefbaa9e03bb

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page