# Copyright: Ankitects Pty Ltd and contributors # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html """Helpers for serializing third-party collections to a common JSON form.""" from __future__ import annotations import json from dataclasses import asdict, dataclass, field from typing import Union from anki.consts import STARTING_FACTOR_FRACTION from anki.decks import DeckId from anki.models import NotetypeId @dataclass class ForeignCardType: name: str qfmt: str afmt: str @staticmethod def front_back() -> ForeignCardType: return ForeignCardType( "Card 1", qfmt="{{Front}}", afmt="{{FrontSide}}\n\n
\n\n{{Back}}", ) @staticmethod def back_front() -> ForeignCardType: return ForeignCardType( "Card 2", qfmt="{{Back}}", afmt="{{FrontSide}}\n\n
\n\n{{Front}}", ) @staticmethod def cloze() -> ForeignCardType: return ForeignCardType( "Cloze", qfmt="{{cloze:Text}}", afmt="{{cloze:Text}}
\n{{Back Extra}}" ) @dataclass class ForeignNotetype: name: str fields: list[str] templates: list[ForeignCardType] is_cloze: bool = False @staticmethod def basic(name: str) -> ForeignNotetype: return ForeignNotetype(name, ["Front", "Back"], [ForeignCardType.front_back()]) @staticmethod def basic_reverse(name: str) -> ForeignNotetype: return ForeignNotetype( name, ["Front", "Back"], [ForeignCardType.front_back(), ForeignCardType.back_front()], ) @staticmethod def cloze(name: str) -> ForeignNotetype: return ForeignNotetype( name, ["Text", "Back Extra"], [ForeignCardType.cloze()], is_cloze=True ) @dataclass class ForeignCard: """Data for creating an Anki card. Usually a review card, as the default card generation routine will take care of missing new cards. due -- UNIX timestamp interval -- days ease_factor -- decimal fraction (2.5 corresponds to default ease) """ # TODO: support new and learning cards? due: int = 0 interval: int = 1 ease_factor: float = STARTING_FACTOR_FRACTION reps: int = 0 lapses: int = 0 @dataclass class ForeignNote: fields: list[str] = field(default_factory=list) tags: list[str] = field(default_factory=list) notetype: str | NotetypeId = "" deck: str | DeckId = "" cards: list[ForeignCard] = field(default_factory=list) @dataclass class ForeignData: notes: list[ForeignNote] = field(default_factory=list) notetypes: list[ForeignNotetype] = field(default_factory=list) default_deck: str | DeckId = "" def serialize(self) -> str: return json.dumps(self, cls=ForeignDataEncoder, separators=(",", ":")) class ForeignDataEncoder(json.JSONEncoder): def default(self, obj: object) -> dict: if isinstance( obj, (ForeignData, ForeignNote, ForeignCard, ForeignNotetype, ForeignCardType), ): return asdict(obj) return json.JSONEncoder.default(self, obj)