Creating and editing *.apkg and *.anki2 safely
Project description
AnkiSync 2
*.apkg and *.anki2 file structure is very simple, but with some quirks of incompleteness.
*.apkg file structure is a zip of at least two files.
.
├── example
│ ├── example.anki2
│ ├── media
│ ├── 1 # Media files with the names masked as integers
│ ├── 2
│ ├── 3
| └── ...
└── example.apkg
*.anki2 is a SQLite file with foreign key disabled, and the usage of some JSON schemas instead of some tables
Also, *.anki2 is used internally at os.path.join(appdirs.user_data_dir('Anki2'), 'User 1', 'collection.anki2')
, so editing the SQLite there will also edit the database.
The media
file is a text file of at least a string of {}
, which is actually a dictionary of keys -- stringified int; and values -- filenames.
Usage
Some extra tables are created if not exists.
from ankisync2.anki import Anki2, Apkg
apkg = Apkg("example.apkg") # Or Apkg("example/") also works, otherwise the folder named 'example' will be created.
apkg.db.execute_sql(SQL, PARAMS)
apkg.zip(output="example1.apkg")
I also support adding media.
apkg.add_media("path/to/media.jpg")
To find the wanted cards and media, iterate though the Apkg
and Apkg.iter_media
object.
iter_apkg = iter(apkg)
for i in range(5):
print(next(iter_apkg))
Creating a new *.apkg
You can create a new *.apkg via Apkg
with any custom filename (and *.anki2 via Anki2()
). A folder required to create *.apkg needs to be created first.
from ankisync2.anki import Apkg
apkg = Apkg("example") # Create example folder
After that, the Apkg will require at least 1 card, which is connected to at least 1 note, 1 model, 1 template, and 1 deck; which should be created in this order.
- Model, Deck
- Template, Note
- Card
from ankisync2 import db
m = db.Models.create(name="foo", flds=["field1", "field2"])
d = db.Decks.create(name="bar::baz")
t = [
db.Templates.create(name="fwd", mid=m.id, qfmt="{{field1}}", afmt="{{field2}}"),
db.Templates.create(name="bwd", mid=m.id, qfmt="{{field2}}", afmt="{{field1}}")
]
n = db.Notes.create(mid=m.id, flds=["data1", "<img src='media.jpg'>"], tags=["tag1", "tag2"])
c = [
db.Cards.create(nid=n.id, did=d.id, ord=i)
for i, _ in enumerate(t)
]
You can also add media, which is not related to the SQLite database.
apkg.add_media("path/to/media.jpg")
Finally, finalize with
apkg.zip(output="example1.apkg")
apkg.close()
Updating an *.apkg
This is also possible, by modifying db.Notes.data
as sqlite_ext.JSONField
, with peewee.signals
.
It is now as simple as,
from ankisync2.anki import Apkg
from ankisync2 import db
apkg = Apkg("example1.apkg")
for n in db.Notes.filter(db.Notes.data["field1"] == "data1"):
n.data["field3"] = "data2"
n.save()
apkg.close()
JSON schema of Col.models
, Col.decks
, Col.conf
and Col.dconf
I have created dataclasses
for this at /ankisync2/builder.py. To serialize it, use dataclasses.asdict
or
from ankisync2.util import DataclassJSONEncoder
import json
json.dumps(dataclassObject, cls=DataclassJSONEncoder)
For an example of how this works, please see /ankisync2/anki.py#L56
Using peewee
framework
You can also use peewee
ORM framework; and ArrayField, X1fField and JSONField will be automated. You can use Dataclasses and Lists directly, without converting them to string first.
Examples
Please see /example.ipynb.
Installation
pip install ankisync2
Related projects
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 Distribution
Built Distribution
Hashes for ankisync2-0.2.4.1-py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | da15326f2815660c473e3ec1b9b31e3297fd24560eb0393d4696ff1c9a92268b |
|
MD5 | 6b595ecdb668d02b6e3e96e73e59d5ca |
|
BLAKE2b-256 | 069b01d7c37fc6e6bc761c85b710b9b0cdcfe4b643f9d67adf865892c7c5ae28 |