mirror of
https://github.com/ankitects/anki.git
synced 2025-09-19 14:32:22 -04:00

* chore: add myself to CONTRIBUTORS file * refactor: use newer type hints for Union/Optional * refactor: fix deprecated type annotations use collections.abc rather than typing * refactor: use lower letter type annotations * style: reformat with black * refactor: remove unused imports * refactor: add missing imports for type hints * fixup! refactor: use newer type hints for Union/Optional * fix: add missing imports for type annotations * fixup! refactor: use newer type hints for Union/Optional * fixup! style: reformat with black * refactor: fix remaining imports re: type hints
119 lines
3.1 KiB
Python
119 lines
3.1 KiB
Python
# 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<hr id=answer>\n\n{{Back}}",
|
|
)
|
|
|
|
@staticmethod
|
|
def back_front() -> ForeignCardType:
|
|
return ForeignCardType(
|
|
"Card 2",
|
|
qfmt="{{Back}}",
|
|
afmt="{{FrontSide}}\n\n<hr id=answer>\n\n{{Front}}",
|
|
)
|
|
|
|
@staticmethod
|
|
def cloze() -> ForeignCardType:
|
|
return ForeignCardType(
|
|
"Cloze", qfmt="{{cloze:Text}}", afmt="{{cloze:Text}}<br>\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)
|