kerrickstaley / genanki

A Python 3 library for generating Anki decks
MIT License
2.06k stars 161 forks source link

Enable adding cards within a note to specific decks #86

Open z1lc opened 3 years ago

z1lc commented 3 years ago

Currently, genanki allows you to specify which deck a note (and all of its corresponding cards) should be added to.

my_deck.add_note(my_note)

In my personal project, I generate notes that have templates/cards which I would rather not have show up in the same deck (at least initially). I'm looking to see how feasible it would be to allow for this kind of split in code. Since the library is currently built around just adding notes to decks, I'm also looking to see if there are any thoughts on what a compelling way to structure this ability might be.

One option that comes to mind is to change from the my_deck.add_note(my_note) approach and instead move this up to the package level. So, you'd create all your decks up-front, pass them to the Package, and then use add_note directly there, along with a configuration of card => deck. For example:

deck_1 = genanki.Deck(2059400110, 'Deck A')
deck_2 = genanki.Deck(2059400111, 'Deck B')

package = genanki.Package([deck_1, deck_2])

my_model = genanki.Model(
  1607392319,
  'Simple Model',
  fields=[
    {'name': 'Question'},
    {'name': 'Answer'},
  ],
  templates=[
    {
      'name': 'Card 1',
      'qfmt': '{{Question}}',
      'afmt': '{{FrontSide}}<hr id="answer">{{Answer}}',
    },
    {
      'name': 'Card 2',
      'qfmt': '{{Answer}}',
      'afmt': '{{FrontSide}}<hr id="answer">{{Question}}',
    },
  ])
my_note = genanki.Note(
  model=my_model,
  fields=['Capital of Argentina', 'Buenos Aires'])

package.add_note(my_note, {deck_1: ['Card 1'], deck_2: ['Card 2'])

This would be an alternative to the deck_1.add_note call, and could throw in case not all cards for the corresponding model were included in one of the mapping keys.

Any thoughts here? I think the implementation of this shouldn't be too crazy, as we just need to pass a deck ID mapping from the Package in write_to_db, instead of how currently we only pass a single deck_id value. Of course, we'd need to deal with template name collisions across note types so internally this would likely be tracked as Dict[Tuple[genanki.Model, str], genanki.Deck]. In the above example, this would mean the dictionary used by the Package class internally is

{
  (my_model, 'Card 1'): deck_1,
  (my_model, 'Card 2'): deck_2,
}

Happy to talk through this or take a stab at implementing this approach if it sounds good.

kerrickstaley commented 2 years ago

Interesting, I didn't realize that Anki allowed cards from the same note to belong in different decks.

I think that this could be achieved by adding a Deck.add_card method that works similarly to Deck.add_note. You would have to also add a .note attribute to Card and set it in Card.__init__, because currently Cards are unaware of which Note they belong to. You would need to do some additional bookkeeping in Deck to keep track of which Cards have been added so that write_to_file can work correctly.