Rust crate to create Anki decks. Based on the python library genanki

Overview

genanki-rs: A Rust Crate for Generating Anki Decks

With genanki-rs you can easily generate decks for the popular open source flashcard platform Anki.

The code of this library is based on the code of genanki, a python library to generate Anki decks.

This library and its author(s) are not affiliated/associated with the main Anki project in any way.

example workflow

How to use (Use the documentation for further information)

Add

[dependencies]
genanki-rs = "0.1.0"

to your Cargo.toml or find another version on crates.io

Notes

The basic unit in Anki is the Note, which contains a fact to memorize. Notes correspond to one or more Cards.

Here's how you create a Note:

use genanki_rs::{Note, Error};

fn main() -> Result<(), Error> {
    // let my_model = ...
    let my_note = Note::new(my_model, vec!["Capital of Argentina", "Buenos Aires"])?;
    Ok(())
}

You pass in a Model, discussed below, and a set of fields (encoded as HTML).

Models

A Model defines the fields and cards for a type of Note. For example:

use genanki_rs::{Field, Model, Template, Error};

fn main() -> Result<(), Error> {
    let my_model = Model::new(
        1607392319,
        "Simple Model",
        vec![Field::new("Question"), Field::new("Answer")],
        vec![Template::new("Card 1")
            .qfmt("{{Question}}")
            .afmt(r#"{{FrontSide}}<hr id="answer">{{Answer}}"#)],
    );
    // let my_note = ...
    Ok(())
}

This note-type has two fields and one card. The card displays the Question field on the front and the Question and Answer fields on the back, separated by a <hr>. You can also pass custom css by calling Model::new_with_options() to supply custom CSS.

let custom_css = ".card {\n font-family: arial;\n font-size: 20px;\n text-align: center;\n color: black;\n}\n";
let my_model_with_css = Model::new_with_options(
    1607392319,
    "Simple Model",
    vec![Field::new("Question"), Field::new("Answer")],
    vec![Template::new("Card 1")
        .qfmt("{{Question}}")
        .afmt(r#"{{FrontSide}}<hr id="answer">{{Answer}}"#)],
    Some(custom_css),
    None,
    None,
    None,
    None,
);

You need to pass a model id and a model name so that Anki can keep track of your model. It's important that you use a unique model id for each Model you define.

Generating a Deck/Package

To import your notes into Anki, you need to add them to a Deck:

use genanki_rs::{Deck, Error};

fn main() -> Result<(), Error> {
    // let my_note = ...
    let mut my_deck = Deck::new(
        2059400110,
        "Country Capitals",
        "Deck for studying country capitals",
    );
    my_deck.add_note(my_note);
    Ok(())
}

Once again, you need a unique deck id, a deck name and a deck description.

Then, create a Package for your Deck and write it to a file:

my_deck.write_to_file("output.apkg")?;

You can then load output.apkg into Anki using File -> Import...

Media Files

To add sounds or images, create a Package and pass the decks and media_files you want to include:

use genanki_rs::{Deck, Error, Package};

fn main() -> Result<(), Error> {
    // ...
    // my_deck.add(my_note)
    let mut my_package = Package::new(vec![my_deck], vec!["sound.mp3", "images/image.jpg"])?;
    my_package.write_to_file("output.apkg")?;
    Ok(())
}

media_files should have the path (relative or absolute) to each file. To use them in notes, first add a field to your model, and reference that field in your template:

let my_model = Model::new(
    1607392319,
    "Simple Model",
    vec![
        Field::new("Question"),
        Field::new("Answer"),
        Field::new("MyMedia"),                           // ADD THIS
    ],
    vec![Template::new("Card 1")
        .qfmt("{{Question}}{{Question}}<br>{{MyMedia}}") // AND THIS
        .afmt(r#"{{FrontSide}}<hr id="answer">{{Answer}}"#)],
);

Then, set the MyMedia field on your Note to [sound:sound.mp3] for audio and <img src="image.jpg"> for images (e.g):

let my_note = Note::new(my_model, vec!["Capital of Argentina", "Buenos Aires", "[sound:sound.mp3]"])?;
// or
let my_note = Note::new(my_model, vec!["Capital of Argentina", "Buenos Aires", r#"<img src="image.jpg">"#])?;

You cannot put <img src="{MyMedia}"> in the template and image.jpg in the field. See these sections in the Anki manual for more information: Importing Media and Media & LaTeX.

You should only put the filename (aka basename) and not the full path in the field; <img src="images/image.jpg"> will not work. Media files should have unique filenames.

sort_field

Anki has a value for each Note called the sort_field. Anki uses this value to sort the cards in the Browse interface. Anki also is happier if you avoid having two notes with the same sort_field, although this isn't strictly necessary. By default, the sort_field is the first field, but you can change it by calling Note::new_with_options().

You can also call Model::new_with_options(), passing the sort_field_index to change the sort field. 0 means the first field in the Note, 1 means the second, etc.

FAQ

My field data is getting garbled

If fields in your notes contain literal <, >, or & characters, you need to HTML-encode them: field data is HTML, not plain text.

For example, you should write

let fields = vec!["AT&amp;T was originally called", "Bell Telephone Company"]

This applies even if the content is LaTeX; for example, you should write

let fields = vec!["Piketty calls this the \"central contradiction of capitalism\".", "[latex]r &gt; g[/latex]"]
You might also like...
Python+Rust implementation of the Probabilistic Principal Component Analysis model

Probabilistic Principal Component Analysis (PPCA) model This project implements a PPCA model implemented in Rust for Python using pyO3 and maturin. In

Low effort scraping Python's pickle format in Rust. It is to complete pickle parsing as BeautifulSoup was to complete HTML parsing.

repugnant-pickle Because it is, isn't it? This is a Rust crate for dealing with the Python pickle format. It also has support for opening PyTorch file

Sample Python extension using Rust/PyO3/tch to interact with PyTorch

Python extensions using tch to interact with PyTorch This sample crate shows how to use tch to write a Python extension that manipulates PyTorch tenso

convolutions-rs is a crate that provides a fast, well-tested convolutions library for machine learning

convolutions-rs convolutions-rs is a crate that provides a fast, well-tested convolutions library for machine learning written entirely in Rust with m

Rye is Armin's personal one-stop-shop for all his Python needs.
Rye is Armin's personal one-stop-shop for all his Python needs.

Rye Rye is Armin's personal one-stop-shop for all his Python needs. It installs and manages Python installations, manages pyproject.toml files, instal

Machine learning crate for Rust

rustlearn A machine learning package for Rust. For full usage details, see the API documentation. Introduction This crate contains reasonably effectiv

High-level non-blocking Deno bindings to the rust-bert machine learning crate.

bertml High-level non-blocking Deno bindings to the rust-bert machine learning crate. Guide Introduction The ModelManager class manages the FFI bindin

Machine learning crate in Rust
Machine learning crate in Rust

DeepRust - Machine learning in Rust Vision To create a deeplearning crate in rust aiming to create a great experience for ML researchers & developers

Talk with your machine in this minimalistic Rust crate!

Speak Speak is a simple, easy to use Natural Language Processor (NLP) written in Rust. Why use Speak? Speak uses a custom engine, and to setup you jus

Comments
  • Use thiserror instead of anyhow

    Use thiserror instead of anyhow

    First of all, thanks for making this crate! I needed something like this for a small project and this worked great!

    That said, I wanted to use eyre for easy error handling in my project and I discovered that anyhow errors do not actually implement std::error::Error, which meant I had to use unwrap instead of ? on any results returned by genanki-rs. These days the recommendation seems to be to use thiserror in library code and anyhow in application code, so I decided to migrate genanki-rs to use a custom error type.

    While this change is a semver breaking change itself, I've tried to do this PR in a way that avoids future semver hazards around error handling. For example, instead of bubbling up errors from dependencies, I cast them to a dyn Error so that it would still be possible to swap out the underlying library in the future.

    I'm happy to take suggestions for how errors should be named or categorized though! This is basically a straightforward attempt at translating the different error sites to an enum that indicates which error happened.

    I also wasn't sure whether some of these should be errors or panics, but I left the errors as errors for now.

    opened by eholk 3
  • Ability to generate custom IDs

    Ability to generate custom IDs

    Thanks for making this port! The Python version of genanki has the ability to override the ID computation, and I think that would be a useful feature for this library as well. The hash of the fields is not sufficient as a note ID if one ever wants to correct a mistake on a card.

    opened by langston-barrett 0
  • Use i64 for Model and Deck ids

    Use i64 for Model and Deck ids

    Hi, Thanks for your work on genanki-rs.

    I got an error when using a randomly generated Model id because it was too large (out of range integral type conversion attempted). The error occurred here:

    https://github.com/yannickfunk/genanki-rs/blob/6ddffe94be05e34c15053eb130c837c304b43b33/src/note.rs#L168-L185

    I was also able to reproduce a similar error by trying to use a large Deck id. It seems like the cause is that the integer used in SQLite is an i64. Anki doesn't seem to mind negative model or deck ids, so this PR changes the ids to be i64 to avoid this kind of error. Note ids seem to be unaffected so I didn't change them.

    opened by Heliozoa 0
Releases(0.3.0)
  • 0.3.0(Sep 4, 2022)

    What's Changed

    • Add a builder-style methods for creating Models and setting Note options by @eholk in https://github.com/yannickfunk/genanki-rs/pull/10

    Full Changelog: https://github.com/yannickfunk/genanki-rs/compare/0.2.0...0.3.0

    Source code(tar.gz)
    Source code(zip)
  • 0.2.0(Aug 23, 2022)

    What's Changed

    • Use thiserror instead of anyhow by @eholk in https://github.com/yannickfunk/genanki-rs/pull/9

    New Contributors

    • @eholk made their first contribution in https://github.com/yannickfunk/genanki-rs/pull/9

    Full Changelog: https://github.com/yannickfunk/genanki-rs/compare/0.1.3...0.2.0

    Source code(tar.gz)
    Source code(zip)
  • 0.1.3(May 29, 2022)

    • Fix using new protobuf version by adding PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION env variable
    • Change deprecated python anki calls to recommended ones
    Source code(tar.gz)
    Source code(zip)
  • 0.1.2(Aug 1, 2021)

  • 0.1.1(May 14, 2021)

  • 0.1.0(May 9, 2021)

  • 0.0.2(May 9, 2021)

Owner
Yannick Funk
22 years old CS student from Karlsruhe
Yannick Funk
A Python CLI tool that finds all third-party packages imported into your Python project

python-third-party-imports This is a Python CLI tool built with Rust that finds all third-party packages imported into your Python project. Install Yo

Maksudul Haque 24 Feb 1, 2023
Rust numeric library with R, MATLAB & Python syntax

Peroxide Rust numeric library contains linear algebra, numerical analysis, statistics and machine learning tools with R, MATLAB, Python like macros. W

Tae Geun Kim 351 Dec 29, 2022
Robust and Fast tokenizations alignment library for Rust and Python

Robust and Fast tokenizations alignment library for Rust and Python

Yohei Tamura 14 Dec 10, 2022
A high performance python technical analysis library written in Rust and the Numpy C API.

Panther A efficient, high-performance python technical analysis library written in Rust using PyO3 and rust-numpy. Indicators ATR CMF SMA EMA RSI MACD

Greg 210 Dec 22, 2022
A high level, easy to use gpgpu crate based on wgpu

A high level, easy to use gpgpu crate based on wgpu. It is made for very large computations on powerful gpus

null 18 Nov 26, 2022
Locality Sensitive Hashing in Rust with Python bindings

lsh-rs (Locality Sensitive Hashing) Locality sensitive hashing can help retrieving Approximate Nearest Neighbors in sub-linear time. For more informat

Ritchie Vink 65 Jan 2, 2023
Python package to compute levensthein distance in rust

Contents Introduction Installation Usage License Introduction Rust implementation of levensthein distance (https://en.wikipedia.org/wiki/Levenshtein_d

Thibault Blanc 2 Feb 21, 2022
Pyxirr - Rust-powered collection of financial functions for Python.

PyXIRR Rust-powered collection of financial functions. PyXIRR stands for "Python XIRR" (for historical reasons), but contains many other financial fun

Alexander Volkovsky 82 Jan 2, 2023
Rust-port of spotify/annoy as a wrapper for Approximate Nearest Neighbors in C++/Python optimized for memory usage.

Rust-port of spotify/annoy as a wrapper for Approximate Nearest Neighbors in C++/Python optimized for memory usage.

Arthur·Thomas 13 Mar 10, 2022
Rust-port of spotify/annoy as a wrapper for Approximate Nearest Neighbors in C++/Python optimized for memory usage.

Fareast This library is a rust port of spotify/annoy , currently only index serving is supported. It also provides FFI bindings for jvm, dotnet and da

Arthur·Thomas 13 Mar 10, 2022