Source code for czech_plus.logic.compiler
"""Module for the card compiler."""
import typing as t
from anki.collection import Collection as AnkiCollection
from czech_plus._vendor.loguru import logger
from czech_plus.config import Config
from czech_plus.logic import processor
import anki.notes # isort:skip # Circular import before importing anki.collection
[docs]class Compiler:
"""Compile a card (note in Anki) to processed and ready to use data.
So in this way we solve two problems in once:
1. We can use compiled content for TTS addon.
2. We can use it on mobile.
"""
def __init__(self, anki_collection_getter: t.Callable[[], AnkiCollection]) -> None:
self._config = Config()
self._get_anki_collection = anki_collection_getter
self._cached_anki_collection: t.Optional[AnkiCollection] = None
@property
[docs] def _anki_collection(self) -> AnkiCollection:
"""Get the Anki collection object."""
if self._cached_anki_collection is None:
self._cached_anki_collection = self._get_anki_collection()
return self._cached_anki_collection
[docs] def compile_all_notes(self) -> None:
"""Compile all notes.
Just fetches all notes via :meth:`_get_notes_ids` and calls
:meth:`compile_note` for each of them.
"""
logger.debug("Compile notes was called.")
for note_id, note_type in self._get_notes_ids():
try:
self.compile_note(note_id, note_type)
except Exception:
logger.exception(f"Failed to compile note {note_id} ({note_type})")
[docs] def compile_note(self, note_id: int, note_type: str) -> None:
"""Compile a note.
Args:
note_id: ID of the note.
note_type: Name of the note type.
"""
logger.debug(f"Compiling note {note_id} ({note_type})...")
note = anki.notes.Note(self._anki_collection, id=anki.notes.NoteId(note_id))
processed = processor.process_card(dict(note.items()), note_type)
if processed is None:
raise ValueError(f"You specified invalid note type name in config - {note_type!r}")
processed_field_name = {
self._config.cards.nouns.note_type_name: self._config.cards.nouns.fields.processed,
self._config.cards.verbs.note_type_name: self._config.cards.verbs.fields.processed,
self._config.cards.adjectives.note_type_name: self._config.cards.adjectives.fields.processed,
}[note_type]
note[processed_field_name] = processed
note.flush()
[docs] def _get_notes_ids(self) -> list[tuple[int, str]]:
"""Get IDs of all notes with needed note types.
Returns:
List of tuples with note ID and note type name.
"""
note_type_id = self._anki_collection.models.id_for_name(self._config.cards.nouns.note_type_name)
assert note_type_id is not None
noun_notes_ids = self._anki_collection.models.nids(note_type_id)
logger.trace(f"{noun_notes_ids=}")
note_type_id = self._anki_collection.models.id_for_name(self._config.cards.verbs.note_type_name)
assert note_type_id is not None
verb_notes_ids = self._anki_collection.models.nids(note_type_id)
logger.trace(f"{verb_notes_ids=}")
note_type_id = self._anki_collection.models.id_for_name(self._config.cards.adjectives.note_type_name)
assert note_type_id is not None
adjective_notes_ids = self._anki_collection.models.nids(note_type_id)
logger.trace(f"{adjective_notes_ids=}")
notes_ids: list[tuple[int, str]] = [
(note_id, self._config.cards.nouns.note_type_name) for note_id in noun_notes_ids
]
notes_ids.extend((note_id, self._config.cards.verbs.note_type_name) for note_id in verb_notes_ids)
notes_ids.extend((note_id, self._config.cards.adjectives.note_type_name) for note_id in adjective_notes_ids)
return notes_ids