# 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: Union[str, NotetypeId] = ""
deck: Union[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: Union[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)