How is a deck created (programmatically speaking)?

I’m writing an application in haskell that handles creating new cards for anki (that seems to be an easy part), also it’s should be capable of creating new decks (here things starts to get a lit’ bit of confusion), I’m trying to use sqlite to be able to create new decks (if the deck doesn’t exists), but what I’m seeing is that I can not do something as simple as:

INSERT INTO decks VALUES (2, 'name of a deck', 1615344032, -1);

As I don’t know exactly what I supposed to set for the last 2 fields common and kind and special collation unicase.

I’ve been reading some other projects to see if something hit me up, but without success.

Here deck.py from genanki project, which seems to be using an json object to represent decks (which I quite didn’t understand how exactly it works, because I tried to mimic the that representation using that json structure as base to ‘create’ a deck and it didn’t appear on my anki).

Also using Database-Structure as a base to understand the database structure.

So, my question is if someone could explain to me (or provide documentation that explains it) how am I supposed to handle deck creation, what am I supposed to supply to db common and kind to it accept deck creating, etc.

EDIT:

If possible someone could explain what is happening here? Rust is not my strongest point:

fn row_to_deck(row: &Row) -> Result<Deck> {
    let common = DeckCommon::decode(row.get_raw(4).as_blob()?)?;
    let kind = DeckKindProto::decode(row.get_raw(5).as_blob()?)?;
    let id = row.get(0)?;
    Ok(Deck {
        id,
        name: row.get(1)?,
        mtime_secs: row.get(2)?,
        usn: row.get(3)?,
        common,
        kind: kind.kind.ok_or_else(|| AnkiError::DBError {
            kind: DBErrorKind::MissingEntity,
            info: format!("invalid deck kind: {}", id),
        })?,
    })
}
...
    pub(crate) fn add_or_update_deck_with_existing_id(&self, deck: &Deck) -> Result<()> {
        if deck.id.0 == 0 {
            return Err(AnkiError::invalid_input("deck with id 0"));
        }
        let mut stmt = self
            .db
            .prepare_cached(include_str!("add_or_update_deck.sql"))?;
        let mut common = vec![];
        deck.common.encode(&mut common)?;
        let kind_enum = DeckKindProto {
            kind: Some(deck.kind.clone()),
        };
        let mut kind = vec![];
        kind_enum.encode(&mut kind)?;
        stmt.execute(params![
            deck.id,
            deck.name,
            deck.mtime_secs,
            deck.usn,
            common,
            kind
        ])?;

        Ok(())
    }
...

.apkg files were not really intended to be written by external tools. In the future Anki will probably introduce a separate file format that is easier to write by external programs; in the mean time, I’d recommend you either:

  • write to a TSV file if that’s practical
  • use the AnkiConnect add-on to push content directly into Anki
  • or if you’re not bound to Haskell, just write the script in Python instead and use Anki’s API.
1 Like

@dae
Will future updates break import .apkg format file? Because I have created Anki addon in browser which generate .apkg file. And how it will impact?

The addon uses genanki in browser using pyodide.
GitHub - infinyte7/image-occlusion-in-browser: Create image occlusion in browser
GitHub - infinyte7/ocloze: cloze overlapper in browser for Anki

Thank you for you response.

I guess you were right, also ‘reverse engineering’ rust isn’t quite a viable task at the moment (encode, decode, unicase, etc), about the suggestions, I already wrote in python (using the anki’s api) something similar what I’m trying to do now in haskell.
AnkiConnect doesn’t fit well in this case.
I guess I’ll go with the TSV solution, for now, and see in the future if I manage something with the changes that you mentioned.

Again, thank you! :grin:

1 Like

.apkg file support won’t be going anywhere any time soon