Metadata-Version: 2.4
Name: fastanki
Version: 0.0.6
Summary: Python tools for Anki
Author-email: Jeremy Howard <github@jhoward.fastmail.fm>
License: Apache-2.0
Project-URL: Repository, https://github.com/AnswerDotAI/fastanki
Project-URL: Documentation, https://AnswerDotAI.github.io/fastanki
Keywords: nbdev,jupyter,notebook,python
Classifier: Natural Language :: English
Classifier: Intended Audience :: Developers
Classifier: Development Status :: 3 - Alpha
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: fastcore
Requires-Dist: httpx
Requires-Dist: zstandard
Requires-Dist: protobuf>=6.31
Requires-Dist: apsw
Provides-Extra: dev
Requires-Dist: anki; extra == "dev"
Requires-Dist: pytest; extra == "dev"
Requires-Dist: pytest-timeout; extra == "dev"
Requires-Dist: fastcore>=2.1.18; extra == "dev"
Dynamic: license-file

# fastanki


<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

fastanki reads and writes Anki’s collection format and speaks the AnkiWeb sync protocol directly, in Python. There’s no dependency on the Anki application or its Rust library: your cards live in a small sqlite file of fastanki’s own, and reach your desktop and phone the same way any Anki client’s changes do, by syncing through AnkiWeb. Media files sync too: [`add_media`](https://AnswerDotAI.github.io/fastanki/core.html#add_media) a picture or sound, cite it in a field, and it reaches your other devices alongside the cards.

## Usage

### Installation

Install latest from [pypi](https://pypi.org/project/fastanki/)

``` sh
$ pip install fastanki
```

### Documentation

``` python
from fastanki import *
import os, tempfile
```

## Functional API

[`add_card`](https://AnswerDotAI.github.io/fastanki/core.html#add_card) lets you create a new card with a single function call. Just pass your field values as keyword arguments. By default it uses the Basic note type and Default deck, but you can specify any model, deck, or tags you like.

``` python
notezh = add_card(fields={'Front':'你好','Back':'hello'})
```

[`find_cards`](https://AnswerDotAI.github.io/fastanki/core.html#find_cards) searches your collection and returns a list of [`Card`](https://AnswerDotAI.github.io/fastanki/collection.html#card) objects. Criteria are keyword arguments, all optional and combined with AND:

- `deck='Spanish'` matches that deck and its subdecks
- `tag='vocab'` matches a tag
- `added_days=7` matches cards added in the last week
- `is_due=True` matches cards due for review
- Any other keyword is a field name, matched as a case-insensitive substring: `Front='hello'`
- `where="n.id=?", args=(nid,)` drops through to SQL over `notes n` joined with `cards c`

``` python
cards = find_cards(deck='Default')
cards
```

    [Card(1784759309631, nid=1784759309631, due=1, ivl=0, queue=0)]

``` python
cards[0]
```

<div class="prose" markdown="1">

Card 1784759309631 (nid: 1784759309631, due: 1, ivl: 0d, queue: 0)

</div>

``` python
find_card_ids(deck='Default')
```

    [1784759309631]

[`find_notes`](https://AnswerDotAI.github.io/fastanki/core.html#find_notes) takes the same criteria and returns one [`Note`](https://AnswerDotAI.github.io/fastanki/collection.html#note) per matching note, where [`find_cards`](https://AnswerDotAI.github.io/fastanki/core.html#find_cards) may return several cards for a note (a Cloze note generates one card per cloze number, for instance).

``` python
notes = find_notes(fields={'Back':'hello'})
notes
```

    [Note(1784759309631, Front='你好', Back='hello', tags=[])]

``` python
note = notes[0]
note
```

<div class="prose" markdown="1">

**Front**: 你好 \| **Back**: hello \| 🏷 -

</div>

``` python
find_note_ids(fields={'Back':'hello'})
```

    [1784759309631]

[`update_note`](https://AnswerDotAI.github.io/fastanki/core.html#update_note) modifies an existing note’s fields and/or tags. Pass either a [`Note`](https://AnswerDotAI.github.io/fastanki/collection.html#note) object or a note ID, along with any fields you want to change as keyword arguments. For tags:
- `tags=['a','b']` — replaces all tags
- `add_tags='newtag'` — adds without removing existing tags

``` python
update_note(note, Back="updated answer", tags='testtag')
```

<div class="prose" markdown="1">

**Front**: 你好 \| **Back**: updated answer \| 🏷 testtag

</div>

``` python
update_note(note, add_tags='moretagz')
```

<div class="prose" markdown="1">

**Front**: 你好 \| **Back**: updated answer \| 🏷 testtag moretagz

</div>

``` python
get_note(note.id)
```

<div class="prose" markdown="1">

**Front**: 你好 \| **Back**: updated answer \| 🏷 testtag moretagz

</div>

``` python
del_note([notezh, note])
```

    2

[`sync`](https://AnswerDotAI.github.io/fastanki/core.html#sync) connects to AnkiWeb: pass your credentials the first time, and they’re saved (as a host key, not your password) for later calls. The first sync of a fresh collection is a full download of your existing AnkiWeb collection; after that, syncs exchange deltas in both directions. fastanki will never replace a non-empty server collection without an explicit `upload=True`. Each [`sync`](https://AnswerDotAI.github.io/fastanki/core.html#sync) also syncs media: to put an image or sound on a card, [`add_media`](https://AnswerDotAI.github.io/fastanki/core.html#add_media) the file and cite the returned name in a field with `<img src="name">` or `[sound:name]`.

``` python
sync(user=os.environ['ANKI_USER'], passw=os.environ['ANKI_PASS'])  # first time
sync()  # after that
```

    host_number: 5

## Tool use

``` python
anki_tools()
```

    &`[add_card, add_fb_card, add_cloze_card, add_media, find_notes, find_note_ids, find_cards, find_card_ids, get_note, del_note, update_fb_note, sync]`

Here are the available tools:
&`[add_card, add_fb_card, add_cloze_card, find_notes, find_note_ids, find_cards, find_card_ids, get_note, del_note, update_fb_note, sync]`.

Try to find all my notes. List the IDs and contents you see.

Delete them.

Try finding all notes again.

Try adding a note of your choice using [`add_fb_card`](https://AnswerDotAI.github.io/fastanki/core.html#add_fb_card) and tell me the id.

Try finding all notes again.

OK try [`get_note`](https://AnswerDotAI.github.io/fastanki/core.html#get_note) with it.

Delete it now.

OK create, update, and verify a note now.

Try the various find ones that we haven’t done yet.

Sure. Delete that note, then sync.

## OO API

The functional API opens and closes the collection on every call. For a batch of work, [`Collection`](https://AnswerDotAI.github.io/fastanki/collection.html#collection) keeps it open, and everything the functions above do is a method here.

``` python
col = Collection.open()
col.path.name
```

    'collection.anki2'

``` python
col.notetypes(), col.decks()
```

    (['Basic', 'Cloze'], ['Default'])

``` python
n = col.add(Front='adiós', Back='goodbye', deck='Spanish::Vocab', tags=['spanish'])
n
```

<div class="prose" markdown="1">

**Front**: adiós \| **Back**: goodbye \| 🏷 spanish

</div>

``` python
col.due_counts('Spanish')
```

    (1, 0, 0)

``` python
col.find_notes(deck='Spanish')
```

    [Note(1784759309645, Front='adiós', Back='goodbye', tags=['spanish'])]

``` python
col.remove_deck('Spanish')
col.close()
```

[`Collection`](https://AnswerDotAI.github.io/fastanki/collection.html#collection) is also a context manager, so a one-shot batch reads naturally:

``` python
with Collection.open() as c: c.add(Front='hola', Back='hello')
```
