Anki via Draft - Roadblocks

Hey, I’m trying to create a working drafts to Anki action, it might already exist but I can’t find it anywhere, but here’s what I’ve done so far:
anki://x-callback-url/addnote?profile={{User 1}}&type=Basic&deck={{hmph}}&fldFront=[[title]]&fldBack=[[body]]

Just a simple callback URL

The problem is that for every card I would have to make a new draft, and that’s annoying and hopefully needless. What I want is to create Anki cards during the lecture in one draft and then with one action, it’s all imported into an Anki deck. If it’s possible it would be awesome to select which one. Any help would be appreciated. Thanks!!!

Have you read through all the posts on the forum about Anki? There are quite a few.

https://forums.getdrafts.com/search?q=anki

Also, there’s a post from yesterday about autmatically splitting a draft into multiple drafts that might be useful if you need to process a batch of data within a single draft. It’s also based on flash card content.

I’ve just looked at them, they all seem reliant on titles for the front of the card. Is there anyway to set up something with variables, where if you start a line with “Q” then that’s the front and “A” would be the back? It just seems overly complex to make a new card every time.

That example above from yesterday splits on lines starting “Q:” so that sounds very much like the sort of splitting you are looking for…

I know! That’s why I brought it up, I just have 0 experience with coding and don’t know how to take that and convert it into x-callback, sorry

Ah okay. I got the impression you were just looking for how to proceed.

I don’t have time to look at it today, but if no one else steps in i’ll try and take a look in the next few days. I don’t use Anki so it will take me a bit of time to get setup.

Anki is super cool, but I’m sorry for shoving it down your throat, I’ve figured out URL schemes for other apps before but this seems out of my depth. Thanks so much for offering your time, I will wait.

the script from @sylumer is a great starting point.
If you stick to the formatting in the referenced post you need to make a few adaptions to the script:

Starting with this:

const DELIM = "Q:";
const TAG = "qacard";
let astrCards = draft.content.split("\n" + DELIM);
astrCards.map(strCard =>
{
	if (strCard.length > 0)
	{
	    let dNew = Draft.create();
	    if (strCard.startsWith(DELIM)) dNew.content = strCard;
	    else dNew.content = DELIM + strCard;
	    dNew.content = dNew.content.replaceAll("\nA:", "\n\nA:");
	    dNew.addTag(TAG);
	    dNew.update();
	}
});

What the script does is it splits the text in the current draft by "\n" + DELIM where DELIM is defined as Q: at the beginning.
The variable astrCards contains an array (lets name it list) of all the questions with their answers.
The goals with the script was that a new draft shall be created for each card.
This is done by looping through the array with astrCards.map(...) inside the brackets each element (=Q+A) in the list is then represented by the variable strCard

after checking the length with if (strCard.length > 0) to ensure that no empty elements are used a new draft is created (the if-else lines just ensure that a “Q:” is at the beginning of each new draft.

Hope that made it understandable what the script is doing :smiley:

Now if we face your “problem” what you want to do from my understanding is have each question as the front of the card and the answer as the back of the card.
I now assume that you write your questions in the same format like in the mentioned request (just copy pasting it for completeness)

Q: It's a slowly progressive, persistent airflow obstruction associated with chronic inflammation of the airway [...]
A: COPD

Q: An oximetry reading of = [...]% is generally acceptable in an asymptomatic client with COPD
A: 88

Q: In order to prevent aspiration the food should be thickened liquids and also make sure they are not [...] prior to having their meal.
A: sedated

Everytime the script runs through the loop the strCard variable will contain a string like this:

Q: It's a slowly progressive, persistent airflow obstruction associated with chronic inflammation of the airway [...]
A: COPD

To just get the Q: you can use regex or simply split the text in the variable again by new lines (this also means that a Q must not contain a new line).

Splitting up the variable is easy to do:

const lines = strCard.split('\n')

The variable lines now contains two elements, the first is the question and the second is the answer.
we can access them by the index in the array:

const question = lines[0]
const answer = lines[1]

The last we need to complete is to use the callback url scheme of anki to create the card. To learn how to use them you can have a look at the documentation of the Callback URL API documentation in the Drafts docs.

first we need the base url (this is the static part of the callback url- i just use the one you shared:

const baseURL = "anki://x-callback-url/addnote?";

Then we create and configure the Callback URL object with the needed parameters in the callback url:

let cb = new CallbackURL()
cb.baseURL = baseURL;
// profile parameter
cb.addParameter("profile", "{{User 1}}");
// type parameter
cb.addParameter("type", "Basic");
// deck parameter
cb.addParameter("deck", "{{hmph}}");
// now the front of the card by using the question variable
cb.addParameter("fldFront", question);
// now the back of the card by using the answer variable
cb.addParameter("fldBack", answer);

Now the Callback URL is ready to be opened (you can add some basic error handling, too

let success = cb.open();
if (success) {
    // maybe log the created card 
    console.log("Card with question \"" + question + "\" created");
    }
    else { 
    // something went wrong or was cancelled
    console.log(cb.status);
    if (cb.status == "cancelled") {
        context.cancel();
    }
    else {
    context.fail();
    }
}

Putting it all together the script could look like this:

const DELIM = "Q:";

// optional parameters for the callback url scheme of anki, leave empty to use defaults
const profileName = ""
const deckName = ""

let astrCards = draft.content.split("\n" + DELIM);

astrCards.map(strCard =>
{
	if (strCard.length > 0)
	{
	     
	    if (!strCard.startsWith(DELIM)) strCard = DELIM + strCard;

            const lines = strCard.split('\n')
            const question = lines[0].replace("Q: ", "")
            const answer = lines[1].replace("A: ", "")

				const baseURL = "anki://x-callback-url/addnote?"
            let cb = new CallbackURL()
            cb.baseURL = baseURL;
            // profile parameter
            if(profileName.length > 0) cb.addParameter("profile", profileName);
            // type parameter
            cb.addParameter("type", "Basic");
            // deck parameter
            if(deckName.length >0 ) cb.addParameter("deck", deckName);
            // now the front of the card by using the question variable
            cb.addParameter("fldFront", question);
            // now the back of the card by using the answer variable
            cb.addParameter("fldBack", answer);
            let success = cb.open();
            if (success) {
                // maybe log the created card 
                console.log("Card with question \"" + question + "\" created");
                }
                else { 
                // something went wrong or was cancelled
                console.log(cb.status);
                if (cb.status == "cancelled") {
                    context.cancel();
                }
                else {
                context.fail();
                }
            }   
	}
});

I creaded an unlisted action since I can’t test it by myself - I don’t use AnkiMobile by myself (downloaded the wrong App first).

Let me know if it works

1 Like

It works! And it works well, but it struggles when it comes to sub-decks, which in the URL Scheme are respected by double colons (::slight_smile: but if I just add those in the script in the Deckname, it doesn’t work. Been trying to troubleshoot by myself but can’t figure it out, could you help?