Metadata-Version: 2.4
Name: fastanki
Version: 0.0.2
Summary: Python tools for Anki
Home-page: https://github.com/AnswerDotAI/fastanki
Author: Jeremy Howard
Author-email: github@jhoward.fastmail.fm
License: Apache Software License 2.0
Keywords: nbdev jupyter notebook python
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Natural Language :: English
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: License :: OSI Approved :: Apache Software License
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: fastcore
Requires-Dist: anki
Provides-Extra: dev
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: keywords
Dynamic: license
Dynamic: license-file
Dynamic: provides-extra
Dynamic: requires-dist
Dynamic: requires-python
Dynamic: summary

# fastanki


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

## Usage

### Installation

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

``` sh
$ pip install fastanki
```

### Documentation

``` python
from fastanki import *
import os
```

``` python
user = os.environ['ANKI_USER']
passw = os.environ['ANKI_PASS']
```

## 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(Front='你好', Back='hello')
```

[`find_cards`](https://AnswerDotAI.github.io/fastanki/core.html#find_cards)
searches your collection and returns a list of `Card` objects. Pass any
Anki search query as the first argument. Common query patterns: -
`deck:Spanish` — cards in a specific deck - `tag:vocab` — cards with a
tag - `front:hello` — match field content - `is:due` — cards due for
review - `added:7` — added in the last 7 days

Combine with spaces (AND) or `OR`: `deck:Spanish tag:verb` finds Spanish
cards tagged “verb”.

``` python
cards = find_cards("deck:Default")
cards
```

    [Card(1764738198390, nid=1764738198390, due=49, ivl=0, queue=0)]

``` python
cards[0]
```

Card 1764738198390 (nid: 1764738198390, due: 49, ivl: 0d, queue: 0)

``` python
find_card_ids("deck:Default")
```

    [1764738198390]

[`find_notes`](https://AnswerDotAI.github.io/fastanki/core.html#find_notes)
searches your collection and returns a list of `Note` objects (rather
than `Card` objects). The query language is the same as
[`find_cards`](https://AnswerDotAI.github.io/fastanki/core.html#find_cards)
— all the same search patterns work. The difference is that
[`find_notes`](https://AnswerDotAI.github.io/fastanki/core.html#find_notes)
returns one result per note, while
[`find_cards`](https://AnswerDotAI.github.io/fastanki/core.html#find_cards)
may return multiple cards if a note generates more than one card (e.g.,
with Cloze or Basic-and-Reversed note types).

``` python
notes = find_notes("hello")
notes
```

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

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

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

``` python
find_note_ids("hello")
```

    [1764738198390]

[`update_note`](https://AnswerDotAI.github.io/fastanki/core.html#update_note)
modifies an existing note’s fields and/or tags. Pass either a `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')
```

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

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

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

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

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

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

✓ 1 change(s)

[`sync`](https://AnswerDotAI.github.io/fastanki/core.html#sync) handles
the entire sync lifecycle for you — opening the collection,
authenticating with AnkiWeb, syncing, and closing up afterwards. The
first time you sync, pass your AnkiWeb credentials; they’ll be saved for
future use.

``` python
sync(user=user, passw=passw) # First time
# sync()  # after that
```

    host_number: 5

## Tool use

``` python
anki_tools()
```

    &`[add_fb_card, find_notes, find_note_ids, find_cards, find_card_ids, get_note, del_card, update_fb_note, sync]`

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

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

##### 🤖Reply🤖<!-- SOLVEIT_SEPARATOR_7f3a9b2c -->

🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠

<details class="tool-usage-details">

``` json
{
  "id": "toolu_016SJDA7Ls3sNefL5JdPpe7e",
  "call": {
    "function": "find_notes",
    "arguments": {
      "query": "*"
    }
  },
  "result": "[Note(1764724348705, Front='What is the capital of France?', Back='Paris', tags=[]),\n Note(176472434<TRUNCATED>"
}
```

</details>

Here’s what I found in your collection:

| ID            | Front                          | Back  | Tags   |
|---------------|--------------------------------|-------|--------|
| 1764724348705 | What is the capital of France? | Paris | (none) |
| 1764724348737 | What is the capital of France? | Paris | (none) |

You have 2 notes, and interestingly they appear to be duplicates — both
have the same question and answer about the capital of France. Would you
like to remove one of them, or is there something else you’d like to do
with your collection?

Delete them.

##### 🤖Reply🤖<!-- SOLVEIT_SEPARATOR_7f3a9b2c -->

🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠

<details class="tool-usage-details">

``` json
{
  "id": "toolu_011dKzVuBUvYyWEydkYtG1sF",
  "call": {
    "function": "del_card",
    "arguments": {
      "notes": "1764724348705"
    }
  },
  "result": "OpChangesWithCount(1)"
}
```

</details>

<details class="tool-usage-details">

``` json
{
  "id": "toolu_016yZBTCXdNVpTydhTg8uTQZ",
  "call": {
    "function": "del_card",
    "arguments": {
      "notes": "1764724348737"
    }
  },
  "result": "OpChangesWithCount(1)"
}
```

</details>

Done! Both notes have been deleted (2 changes total). Your collection
should now be empty. Would you like to add some new cards or do
something else?

Try finding all notes again.

##### 🤖Reply🤖<!-- SOLVEIT_SEPARATOR_7f3a9b2c -->

🧠🧠🧠🧠

<details class="tool-usage-details">

``` json
{
  "id": "toolu_01JVQAUn4vRZZYxwJikGDAbn",
  "call": {
    "function": "find_notes",
    "arguments": {
      "query": "*"
    }
  },
  "result": "[]"
}
```

</details>

The collection is now empty — no notes found. Ready for a fresh start!
What would you like to add?

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.

##### 🤖Reply🤖<!-- SOLVEIT_SEPARATOR_7f3a9b2c -->

🧠🧠🧠🧠🧠🧠🧠🧠

<details class="tool-usage-details">

``` json
{
  "id": "toolu_01EbRahwqio96vUq7AtMr2uU",
  "call": {
    "function": "add_fb_card",
    "arguments": {
      "front": "What is the speed of light in a vacuum?",
      "back": "Approximately 299,792,458 meters per second (about 3 \u00d7 10\u2078 m/s)"
    }
  },
  "result": "1764727897520"
}
```

</details>

Done! I added a physics flashcard about the speed of light. The note ID
is **1764727897520**.

Would you like to add more cards, or do something else with this one?

Try finding all notes again.

##### 🤖Reply🤖<!-- SOLVEIT_SEPARATOR_7f3a9b2c -->

🧠🧠🧠🧠🧠

<details class="tool-usage-details">

``` json
{
  "id": "toolu_01RpBYttNcGRr15YRP7HBhuy",
  "call": {
    "function": "find_notes",
    "arguments": {
      "query": "*"
    }
  },
  "result": "[Note(1764727897520, Front='What is the speed of light in a vacuum?', Back='Approximately 299,792,45<TRUNCATED>"
}
```

</details>

There’s now 1 note in your collection:

| ID | Front | Back | Tags |
|----|----|----|----|
| 1764727897520 | What is the speed of light in a vacuum? | Approximately 299,792,458 meters per second (about 3 × 10⁸ m/s) | (none) |

What would you like to do next?

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

##### 🤖Reply🤖<!-- SOLVEIT_SEPARATOR_7f3a9b2c -->

🧠🧠🧠🧠🧠🧠

<details class="tool-usage-details">

``` json
{
  "id": "toolu_01Qy2LvuJSdxr421DrcdLq8y",
  "call": {
    "function": "get_note",
    "arguments": {
      "note_id": "1764727897520"
    }
  },
  "result": "Note(1764727897520, Front='What is the speed of light in a vacuum?', Back='Approximately 299,792,458<TRUNCATED>"
}
```

</details>

It works! The note comes back as a `Note` object with:

- **ID**: 1764727897520
- **Front**: What is the speed of light in a vacuum?
- **Back**: Approximately 299,792,458 meters per second (about 3 × 10⁸
  m/s)
- **Tags**: \[\] (empty)

The serialization looks clean — the `__repr__` method you defined
earlier is being used. Anything else you’d like to test?

Delete it now.

##### 🤖Reply🤖<!-- SOLVEIT_SEPARATOR_7f3a9b2c -->

🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠

<details class="tool-usage-details">

``` json
{
  "id": "toolu_01HE56VzDm5pAydPYQS3pPRg",
  "call": {
    "function": "del_card",
    "arguments": {
      "notes": "1764727897520"
    }
  },
  "result": "OpChangesWithCount(1)"
}
```

</details>

Done! The note has been deleted. Your collection is empty again.

OK create, update, and verify a note now.

##### 🤖Reply🤖<!-- SOLVEIT_SEPARATOR_7f3a9b2c -->

🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠

<details class="tool-usage-details">

``` json
{
  "id": "toolu_018fiP5JTuM8nCVfW46os4Qu",
  "call": {
    "function": "add_fb_card",
    "arguments": {
      "front": "What is H\u2082O?",
      "back": "Water"
    }
  },
  "result": "1764731543071"
}
```

</details>

<details class="tool-usage-details">

``` json
{
  "id": "toolu_0153ANMUNw3HQ5AdZzkxu6Mj",
  "call": {
    "function": "update_fb_note",
    "arguments": {
      "note_id": "1764731543071",
      "back": "Water (the chemical formula for water)",
      "add_tags": "chemistry"
    }
  },
  "result": "Note(1764731543071, Front='What is H\u2082O?', Back='Water (the chemical formula for water)', tags=['chem<TRUNCATED>"
}
```

</details>

<details class="tool-usage-details">

``` json
{
  "id": "toolu_017TbPKpAPK6AJmSswhDDRhA",
  "call": {
    "function": "get_note",
    "arguments": {
      "note_id": "1764731543071"
    }
  },
  "result": "Note(1764731543071, Front='What is H\u2082O?', Back='Water (the chemical formula for water)', tags=['chem<TRUNCATED>"
}
```

</details>

All working! Here’s what happened:

1.  **Added** a card with Front=“What is H₂O?” and Back=“Water”
2.  **Updated** the back to “Water (the chemical formula for water)” and
    added the tag “chemistry”
3.  **Verified** with
    [`get_note`](https://AnswerDotAI.github.io/fastanki/core.html#get_note)
    — the changes persisted

The note ID is **1764731543071**. What would you like to test next?

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

##### 🤖Reply🤖<!-- SOLVEIT_SEPARATOR_7f3a9b2c -->

🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠🧠

<details class="tool-usage-details">

``` json
{
  "id": "toolu_016JBXpr9shwyawqdBqf7diZ",
  "call": {
    "function": "find_note_ids",
    "arguments": {
      "query": "*"
    }
  },
  "result": "[1764731543071]"
}
```

</details>

<details class="tool-usage-details">

``` json
{
  "id": "toolu_019mDgnKk6ifAinw6ej84LmG",
  "call": {
    "function": "find_cards",
    "arguments": {
      "query": "*"
    }
  },
  "result": "[Card(1764731543071, nid=1764731543071, due=47, ivl=0, queue=0)]"
}
```

</details>

<details class="tool-usage-details">

``` json
{
  "id": "toolu_01BBTUkQNTZfCgU2ehbFxDAU",
  "call": {
    "function": "find_card_ids",
    "arguments": {
      "query": "*"
    }
  },
  "result": "[1764731543071]"
}
```

</details>

All three work! Here’s the comparison:

| Function | Returns |
|----|----|
| `find_note_ids("*")` | `[1764731543071]` — just the note ID |
| [`find_note_ids`](https://AnswerDotAI.github.io/fastanki/core.html#find_note_ids) gives just IDs (lightweight) |  |
| `find_cards("*")` | `Card(1764731543071, nid=1764731543071, due=47, ivl=0, queue=0)` — full Card object with scheduling info |
| `find_card_ids("*")` | `[1764731543071]` — just the card ID |

Interesting that in this case the note ID and card ID happen to be the
same (1764731543071). That’s because with a Basic note type, there’s
exactly one card per note.

That just leaves
**[`sync`](https://AnswerDotAI.github.io/fastanki/core.html#sync)** —
want me to try that too?

Sure. Delete that note, then sync.

##### 🤖Reply🤖<!-- SOLVEIT_SEPARATOR_7f3a9b2c -->

🧠🧠🧠🧠🧠🧠

<details class="tool-usage-details">

``` json
{
  "id": "toolu_013cYoVSkSeF2iUxFoRsFqWb",
  "call": {
    "function": "del_card",
    "arguments": {
      "notes": "1764731543071"
    }
  },
  "result": "OpChangesWithCount(1)"
}
```

</details>

<details class="tool-usage-details">

``` json
{
  "id": "toolu_01Gx7MqumvhrTBiq92jdHytr",
  "call": {
    "function": "sync",
    "arguments": {}
  },
  "result": "host_number: 5"
}
```

</details>

Done! Note deleted and synced to AnkiWeb. All tools are working. 🎉

## OO API

``` python
data_path()
```

    Path('/app/data/.local/share/Anki2')

``` python
profiles()
```

    ['User 1']

``` python
col = Collection.open()
col.profile_path
```

    Path('/app/data/.local/share/Anki2/User 1')

``` python
# First sync requires credentials
col.sync(user=user, passw=passw)

# Subsequent syncs use saved auth
col.sync()
```

``` python
col.models
```

- Basic
- Basic (and reversed card)
- Basic (optional reversed card)
- Basic (type in the answer)
- Cloze
- Image Occlusion

``` python
col.sched.deck_due_tree().children[0]
```

**Default**: 2 new, 0 learn, 0 review

``` python
col.decks
```

- Default

``` python
mdl = col.models['Basic']
mdl['id']
```

    1764648511131

``` python
note = col.new_note(mdl)
note['Front'], note['Back'] = 'hola', 'hello'
note
```

**Front**: hola \| **Back**: hello

``` python
note2 = col.add(Front='hola', Back='hello', tags=['spanish'])
```

``` python
col.add_deck('Spanish::Vocab')
```

    changes {
      deck: true
      browser_table: true
      browser_sidebar: true
      study_queues: true
      mtime: true
    }
    id: 1764727764420

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

**Vocab**: 1 new, 0 learn, 0 review

``` python
col.remove_notes([note2.id])
```

✓ 1 change(s)

``` python
col.decks.remove([col.decks.id_for_name('Spanish')])
```

✓ 1 change(s)

``` python
col.sync()
```

    host_number: 5

``` python
col.close()
```
