PEP8 cards.py

This commit is contained in:
Damien Elmes 2021-06-27 12:12:23 +10:00
parent 1cabe9507c
commit 2a93355824
26 changed files with 209 additions and 197 deletions

View file

@ -3,7 +3,11 @@
from __future__ import annotations from __future__ import annotations
from typing import Any, Callable, Dict, Optional, Tuple, Union import functools
import os
import pathlib
import traceback
from typing import Any, Callable, Dict, Optional, Tuple, Union, no_type_check
import stringcase import stringcase
@ -18,20 +22,37 @@ def _target_to_string(target: DeprecatedAliasTarget) -> str:
return target[1] # type: ignore return target[1] # type: ignore
def partial_path(full_path: str, components: int) -> str:
path = pathlib.Path(full_path)
return os.path.join(*path.parts[-components:])
def _print_deprecation_warning(old: str, doc: str) -> None:
path, linenum, fn, y = traceback.extract_stack(limit=5)[2]
path = partial_path(path, components=3)
print(f"{path}:{linenum}:{old} is deprecated: {doc}")
class DeprecatedNamesMixin: class DeprecatedNamesMixin:
"Expose instance methods/vars as camelCase for legacy callers." "Expose instance methods/vars as camelCase for legacy callers."
# the @no_type_check lines are required to prevent mypy allowing arbitrary
# attributes on the consuming class
_deprecated_aliases: Dict[str, str] = {} _deprecated_aliases: Dict[str, str] = {}
@no_type_check
def __getattr__(self, name: str) -> Any: def __getattr__(self, name: str) -> Any:
remapped = self._deprecated_aliases.get(name) or stringcase.snakecase(name) remapped = self._deprecated_aliases.get(name) or stringcase.snakecase(name)
if remapped == name: if remapped == name:
raise AttributeError raise AttributeError
out = getattr(self, remapped) out = getattr(self, remapped)
print(f"please use {remapped} instead of {name} on {self}") _print_deprecation_warning(f"'{name}'", f"please use '{remapped}'")
return out return out
@no_type_check
@classmethod @classmethod
def register_deprecated_aliases(cls, **kwargs: DeprecatedAliasTarget) -> None: def register_deprecated_aliases(cls, **kwargs: DeprecatedAliasTarget) -> None:
"""Manually add aliases that are not a simple transform. """Manually add aliases that are not a simple transform.
@ -47,15 +68,15 @@ def deprecated(replaced_by: Optional[Callable] = None, info: str = "") -> Callab
"""Print a deprecation warning, telling users to use `replaced_by`, or show `doc`.""" """Print a deprecation warning, telling users to use `replaced_by`, or show `doc`."""
def decorator(func: Callable) -> Callable: def decorator(func: Callable) -> Callable:
@functools.wraps(func)
def decorated_func(*args: Any, **kwargs: Any) -> Any: def decorated_func(*args: Any, **kwargs: Any) -> Any:
if replaced_by: if replaced_by:
doc = f"please use {replaced_by.__name__} instead." doc = f"please use {replaced_by.__name__} instead."
else: else:
doc = info doc = info
print(
f"'{func.__name__}' is deprecated, and will be removed in the future: ", _print_deprecation_warning(f"{func.__name__}()", doc)
doc,
)
return func(*args, **kwargs) return func(*args, **kwargs)
return decorated_func return decorated_func

View file

@ -1,6 +1,8 @@
# Copyright: Ankitects Pty Ltd and contributors # Copyright: Ankitects Pty Ltd and contributors
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
# pylint: enable=invalid-name
from __future__ import annotations from __future__ import annotations
import pprint import pprint
@ -10,6 +12,7 @@ from typing import List, NewType, Optional
import anki # pylint: disable=unused-import import anki # pylint: disable=unused-import
import anki._backend.backend_pb2 as _pb import anki._backend.backend_pb2 as _pb
from anki import hooks from anki import hooks
from anki._legacy import DeprecatedNamesMixin, deprecated
from anki.consts import * from anki.consts import *
from anki.models import NotetypeDict, TemplateDict from anki.models import NotetypeDict, TemplateDict
from anki.notes import Note from anki.notes import Note
@ -22,7 +25,7 @@ from anki.sound import AVTag
# Queue: same as above, and: # Queue: same as above, and:
# -1=suspended, -2=user buried, -3=sched buried # -1=suspended, -2=user buried, -3=sched buried
# Due is used differently for different queues. # Due is used differently for different queues.
# - new queue: note id or random int # - new queue: position
# - rev queue: integer day # - rev queue: integer day
# - lrn queue: integer timestamp # - lrn queue: integer timestamp
@ -31,9 +34,8 @@ CardId = NewType("CardId", int)
BackendCard = _pb.Card BackendCard = _pb.Card
class Card: class Card(DeprecatedNamesMixin):
_note: Optional[Note] _note: Optional[Note]
timerStarted: Optional[float]
lastIvl: int lastIvl: int
ord: int ord: int
nid: anki.notes.NoteId nid: anki.notes.NoteId
@ -50,7 +52,7 @@ class Card:
backend_card: Optional[BackendCard] = None, backend_card: Optional[BackendCard] = None,
) -> None: ) -> None:
self.col = col.weakref() self.col = col.weakref()
self.timerStarted = None self.timer_started: Optional[float] = None
self._render_output: Optional[anki.template.TemplateRenderOutput] = None self._render_output: Optional[anki.template.TemplateRenderOutput] = None
if id: if id:
# existing card # existing card
@ -63,31 +65,31 @@ class Card:
self._load_from_backend_card(_pb.Card()) self._load_from_backend_card(_pb.Card())
def load(self) -> None: def load(self) -> None:
c = self.col._backend.get_card(self.id) card = self.col._backend.get_card(self.id)
assert c assert card
self._load_from_backend_card(c) self._load_from_backend_card(card)
def _load_from_backend_card(self, c: _pb.Card) -> None: def _load_from_backend_card(self, card: _pb.Card) -> None:
self._render_output = None self._render_output = None
self._note = None self._note = None
self.id = CardId(c.id) self.id = CardId(card.id)
self.nid = anki.notes.NoteId(c.note_id) self.nid = anki.notes.NoteId(card.note_id)
self.did = anki.decks.DeckId(c.deck_id) self.did = anki.decks.DeckId(card.deck_id)
self.ord = c.template_idx self.ord = card.template_idx
self.mod = c.mtime_secs self.mod = card.mtime_secs
self.usn = c.usn self.usn = card.usn
self.type = CardType(c.ctype) self.type = CardType(card.ctype)
self.queue = CardQueue(c.queue) self.queue = CardQueue(card.queue)
self.due = c.due self.due = card.due
self.ivl = c.interval self.ivl = card.interval
self.factor = c.ease_factor self.factor = card.ease_factor
self.reps = c.reps self.reps = card.reps
self.lapses = c.lapses self.lapses = card.lapses
self.left = c.remaining_steps self.left = card.remaining_steps
self.odue = c.original_due self.odue = card.original_due
self.odid = anki.decks.DeckId(c.original_deck_id) self.odid = anki.decks.DeckId(card.original_deck_id)
self.flags = c.flags self.flags = card.flags
self.data = c.data self.data = card.data
def _to_backend_card(self) -> _pb.Card: def _to_backend_card(self) -> _pb.Card:
# mtime & usn are set by backend # mtime & usn are set by backend
@ -131,10 +133,6 @@ class Card:
def answer_av_tags(self) -> List[AVTag]: def answer_av_tags(self) -> List[AVTag]:
return self.render_output().answer_av_tags return self.render_output().answer_av_tags
# legacy
def css(self) -> str:
return f"<style>{self.render_output().css}</style>"
def render_output( def render_output(
self, reload: bool = False, browser: bool = False self, reload: bool = False, browser: bool = False
) -> anki.template.TemplateRenderOutput: ) -> anki.template.TemplateRenderOutput:
@ -157,31 +155,25 @@ class Card:
def note_type(self) -> NotetypeDict: def note_type(self) -> NotetypeDict:
return self.col.models.get(self.note().mid) return self.col.models.get(self.note().mid)
# legacy aliases
flushSched = flush
q = question
a = answer
model = note_type
def template(self) -> TemplateDict: def template(self) -> TemplateDict:
m = self.model() notetype = self.note_type()
if m["type"] == MODEL_STD: if notetype["type"] == MODEL_STD:
return self.model()["tmpls"][self.ord] return self.note_type()["tmpls"][self.ord]
else: else:
return self.model()["tmpls"][0] return self.note_type()["tmpls"][0]
def startTimer(self) -> None: def start_timer(self) -> None:
self.timerStarted = time.time() self.timer_started = time.time()
def current_deck_id(self) -> anki.decks.DeckId: def current_deck_id(self) -> anki.decks.DeckId:
return anki.decks.DeckId(self.odid or self.did) return anki.decks.DeckId(self.odid or self.did)
def timeLimit(self) -> int: def time_limit(self) -> int:
"Time limit for answering in milliseconds." "Time limit for answering in milliseconds."
conf = self.col.decks.confForDid(self.current_deck_id()) conf = self.col.decks.confForDid(self.current_deck_id())
return conf["maxTaken"] * 1000 return conf["maxTaken"] * 1000
def shouldShowTimer(self) -> bool: def should_show_timer(self) -> bool:
conf = self.col.decks.confForDid(self.current_deck_id()) conf = self.col.decks.confForDid(self.current_deck_id())
return conf["timer"] return conf["timer"]
@ -192,23 +184,19 @@ class Card:
def autoplay(self) -> bool: def autoplay(self) -> bool:
return self.col.decks.confForDid(self.current_deck_id())["autoplay"] return self.col.decks.confForDid(self.current_deck_id())["autoplay"]
def timeTaken(self) -> int: def time_taken(self) -> int:
"Time taken to answer card, in integer MS." "Time taken to answer card, in integer MS."
total = int((time.time() - self.timerStarted) * 1000) total = int((time.time() - self.timer_started) * 1000)
return min(total, self.timeLimit()) return min(total, self.time_limit())
# legacy def description(self) -> str:
def isEmpty(self) -> bool: dict_copy = dict(self.__dict__)
return False
def __repr__(self) -> str:
d = dict(self.__dict__)
# remove non-useful elements # remove non-useful elements
del d["_note"] del dict_copy["_note"]
del d["_render_output"] del dict_copy["_render_output"]
del d["col"] del dict_copy["col"]
del d["timerStarted"] del dict_copy["timerStarted"]
return f"{super().__repr__()} {pprint.pformat(d, width=300)}" return f"{super().__repr__()} {pprint.pformat(dict_copy, width=300)}"
def user_flag(self) -> int: def user_flag(self) -> int:
return self.flags & 0b111 return self.flags & 0b111
@ -218,7 +206,18 @@ class Card:
assert 0 <= flag <= 7 assert 0 <= flag <= 7
self.flags = (self.flags & ~0b111) | flag self.flags = (self.flags & ~0b111) | flag
# legacy @deprecated(info="use card.render_output() directly")
def css(self) -> str:
return f"<style>{self.render_output().css}</style>"
userFlag = user_flag @deprecated(info="handled by template rendering")
setUserFlag = set_user_flag def is_empty(self) -> bool:
return False
Card.register_deprecated_aliases(
flushSched=Card.flush,
q=Card.question,
a=Card.answer,
model=Card.note_type,
)

View file

@ -429,7 +429,7 @@ class Collection:
return Note(self, self.models.current(forDeck)) return Note(self, self.models.current(forDeck))
def addNote(self, note: Note) -> int: def addNote(self, note: Note) -> int:
self.add_note(note, note.model()["did"]) self.add_note(note, note.note_type()["did"])
return len(note.cards()) return len(note.cards())
def remNotes(self, ids: Sequence[NoteId]) -> None: def remNotes(self, ids: Sequence[NoteId]) -> None:

View file

@ -118,8 +118,8 @@ class TextCardExporter(Exporter):
out = "" out = ""
for cid in ids: for cid in ids:
c = self.col.getCard(cid) c = self.col.getCard(cid)
out += esc(c.q()) out += esc(c.question())
out += "\t" + esc(c.a()) + "\n" out += "\t" + esc(c.answer()) + "\n"
file.write(out.encode("utf-8")) file.write(out.encode("utf-8"))

View file

@ -61,7 +61,7 @@ class Note(DeprecatedNamesMixin):
self.usn = note.usn self.usn = note.usn
self.tags = list(note.tags) self.tags = list(note.tags)
self.fields = list(note.fields) self.fields = list(note.fields)
self._fmap = self.col.models.fieldMap(self.model()) self._fmap = self.col.models.fieldMap(self.note_type())
def _to_backend_note(self) -> _pb.Note: def _to_backend_note(self) -> _pb.Note:
hooks.note_will_flush(self) hooks.note_will_flush(self)
@ -98,7 +98,7 @@ class Note(DeprecatedNamesMixin):
card.ord = ord card.ord = ord
card.did = anki.decks.DEFAULT_DECK_ID card.did = anki.decks.DEFAULT_DECK_ID
model = custom_note_type or self.model() model = custom_note_type or self.note_type()
template = copy.copy( template = copy.copy(
custom_template custom_template
or ( or (
@ -125,10 +125,10 @@ class Note(DeprecatedNamesMixin):
def card_ids(self) -> Sequence[anki.cards.CardId]: def card_ids(self) -> Sequence[anki.cards.CardId]:
return self.col.card_ids_of_note(self.id) return self.col.card_ids_of_note(self.id)
def model(self) -> Optional[NotetypeDict]: def note_type(self) -> Optional[NotetypeDict]:
return self.col.models.get(self.mid) return self.col.models.get(self.mid)
_model = property(model) _note_type = property(note_type)
def cloze_numbers_in_fields(self) -> Sequence[int]: def cloze_numbers_in_fields(self) -> Sequence[int]:
return self.col._backend.cloze_numbers_in_note(self._to_backend_note()) return self.col._backend.cloze_numbers_in_note(self._to_backend_note())
@ -193,4 +193,6 @@ class Note(DeprecatedNamesMixin):
dupeOrEmpty = duplicate_or_empty = fields_check dupeOrEmpty = duplicate_or_empty = fields_check
Note.register_deprecated_aliases(delTag=Note.remove_tag, _fieldOrd=Note._field_index) Note.register_deprecated_aliases(
delTag=Note.remove_tag, _fieldOrd=Note._field_index, model=Note.note_type
)

View file

@ -85,7 +85,7 @@ class Scheduler(V2):
card.did, card.did,
new_delta=new_delta, new_delta=new_delta,
review_delta=review_delta, review_delta=review_delta,
milliseconds_delta=+card.timeTaken(), milliseconds_delta=+card.time_taken(),
) )
card.mod = intTime() card.mod = intTime()
@ -363,7 +363,7 @@ limit %d"""
ivl, ivl,
lastIvl, lastIvl,
card.factor, card.factor,
card.timeTaken(), card.time_taken(),
type, type,
) )

View file

@ -105,7 +105,7 @@ class Scheduler(SchedulerBaseWithLegacy):
self.col.log(card) self.col.log(card)
if not self._burySiblingsOnAnswer: if not self._burySiblingsOnAnswer:
self._burySiblings(card) self._burySiblings(card)
card.startTimer() card.start_timer()
return card return card
return None return None
@ -494,7 +494,7 @@ limit ?"""
card.did, card.did,
new_delta=new_delta, new_delta=new_delta,
review_delta=review_delta, review_delta=review_delta,
milliseconds_delta=+card.timeTaken(), milliseconds_delta=+card.time_taken(),
) )
# once a card has been answered once, the original due date # once a card has been answered once, the original due date
@ -760,7 +760,7 @@ limit ?"""
ivl, ivl,
lastIvl, lastIvl,
card.factor, card.factor,
card.timeTaken(), card.time_taken(),
type, type,
) )
@ -891,7 +891,7 @@ limit ?"""
-delay or card.ivl, -delay or card.ivl,
card.lastIvl, card.lastIvl,
card.factor, card.factor,
card.timeTaken(), card.time_taken(),
type, type,
) )

View file

@ -78,7 +78,7 @@ class Scheduler(SchedulerBaseWithLegacy):
new_state=new_state, new_state=new_state,
rating=rating, rating=rating,
answered_at_millis=intTime(1000), answered_at_millis=intTime(1000),
milliseconds_taken=card.timeTaken(), milliseconds_taken=card.time_taken(),
) )
def answer_card(self, input: CardAnswer) -> OpChanges: def answer_card(self, input: CardAnswer) -> OpChanges:
@ -106,7 +106,7 @@ class Scheduler(SchedulerBaseWithLegacy):
card = Card(self.col) card = Card(self.col)
card._load_from_backend_card(queued_card.card) card._load_from_backend_card(queued_card.card)
card.startTimer() card.start_timer()
return card return card
def _is_finished(self) -> bool: def _is_finished(self) -> bool:

View file

@ -155,7 +155,7 @@ class TemplateRenderContext:
self._fields: Optional[Dict] = None self._fields: Optional[Dict] = None
self._latex_svg = False self._latex_svg = False
if not notetype: if not notetype:
self._note_type = note.model() self._note_type = note.note_type()
else: else:
self._note_type = notetype self._note_type = notetype
@ -190,7 +190,7 @@ class TemplateRenderContext:
def card(self) -> Card: def card(self) -> Card:
"""Returns the card being rendered. """Returns the card being rendered.
Be careful not to call .q() or .a() on the card, or you'll create an Be careful not to call .question() or .answer() on the card, or you'll create an
infinite loop.""" infinite loop."""
return self._card return self._card

View file

@ -72,7 +72,7 @@ def test_noteAddDelete():
assert col.cardCount() == 4 assert col.cardCount() == 4
# check q/a generation # check q/a generation
c0 = note.cards()[0] c0 = note.cards()[0]
assert "three" in c0.q() assert "three" in c0.question()
# it should not be a duplicate # it should not be a duplicate
assert not note.fields_check() assert not note.fields_check()
# now let's make a duplicate # now let's make a duplicate
@ -138,15 +138,15 @@ def test_furigana():
n["Front"] = "foo[abc]" n["Front"] = "foo[abc]"
col.addNote(n) col.addNote(n)
c = n.cards()[0] c = n.cards()[0]
assert c.q().endswith("abc") assert c.question().endswith("abc")
# and should avoid sound # and should avoid sound
n["Front"] = "foo[sound:abc.mp3]" n["Front"] = "foo[sound:abc.mp3]"
n.flush() n.flush()
assert "anki:play" in c.q(reload=True) assert "anki:play" in c.question(reload=True)
# it shouldn't throw an error while people are editing # it shouldn't throw an error while people are editing
m["tmpls"][0]["qfmt"] = "{{kana:}}" m["tmpls"][0]["qfmt"] = "{{kana:}}"
mm.save(m) mm.save(m)
c.q(reload=True) c.question(reload=True)
def test_translate(): def test_translate():

View file

@ -53,12 +53,12 @@ def test_remove():
deck1 = col.decks.id("deck1") deck1 = col.decks.id("deck1")
note = col.newNote() note = col.newNote()
note["Front"] = "1" note["Front"] = "1"
note.model()["did"] = deck1 note.note_type()["did"] = deck1
col.addNote(note) col.addNote(note)
c = note.cards()[0] c = note.cards()[0]
assert c.did == deck1 assert c.did == deck1
assert col.cardCount() == 1 assert col.cardCount() == 1
col.decks.rem(deck1) col.decks.remove([deck1])
assert col.cardCount() == 0 assert col.cardCount() == 0
# if we try to get it, we get the default # if we try to get it, we get the default
assert col.decks.name(c.did) == "[no deck]" assert col.decks.name(c.did) == "[no deck]"
@ -142,7 +142,7 @@ def test_renameForDragAndDrop():
new_hsk_did = col.decks.id("hsk") new_hsk_did = col.decks.id("hsk")
col.decks.renameForDragAndDrop(new_hsk_did, chinese_did) col.decks.renameForDragAndDrop(new_hsk_did, chinese_did)
assert deckNames() == ["Chinese", "Chinese::HSK", "Chinese::hsk+", "Languages"] assert deckNames() == ["Chinese", "Chinese::HSK", "Chinese::hsk+", "Languages"]
col.decks.rem(new_hsk_did) col.decks.remove([new_hsk_did])
# '' is a convenient alias for the top level DID # '' is a convenient alias for the top level DID
col.decks.renameForDragAndDrop(hsk_did, "") col.decks.renameForDragAndDrop(hsk_did, "")

View file

@ -36,7 +36,7 @@ def setup1():
note = col.newNote() note = col.newNote()
note["Front"] = "baz" note["Front"] = "baz"
note["Back"] = "qux" note["Back"] = "qux"
note.model()["did"] = col.decks.id("new col") note.note_type()["did"] = col.decks.id("new col")
col.addNote(note) col.addNote(note)

View file

@ -35,11 +35,3 @@ def test_flags():
col.set_user_flag_for_cards(0, [c.id]) col.set_user_flag_for_cards(0, [c.id])
c.load() c.load()
assert c.user_flag() == 0 assert c.user_flag() == 0
# should work with Cards method as well
c.set_user_flag(2)
assert c.user_flag() == 2
c.set_user_flag(3)
assert c.user_flag() == 3
c.set_user_flag(0)
assert c.user_flag() == 0

View file

@ -38,7 +38,7 @@ def test_anki2_mediadupes():
# add a note that references a sound # add a note that references a sound
n = col.newNote() n = col.newNote()
n["Front"] = "[sound:foo.mp3]" n["Front"] = "[sound:foo.mp3]"
mid = n.model()["id"] mid = n.note_type()["id"]
col.addNote(n) col.addNote(n)
# add that sound to media folder # add that sound to media folder
with open(os.path.join(col.media.dir(), "foo.mp3"), "w") as note: with open(os.path.join(col.media.dir(), "foo.mp3"), "w") as note:

View file

@ -23,7 +23,7 @@ def test_latex():
# but since latex couldn't run, there's nothing there # but since latex couldn't run, there's nothing there
assert len(os.listdir(col.media.dir())) == 0 assert len(os.listdir(col.media.dir())) == 0
# check the error message # check the error message
msg = note.cards()[0].q() msg = note.cards()[0].question()
assert "executing nolatex" in without_unicode_isolation(msg) assert "executing nolatex" in without_unicode_isolation(msg)
assert "installed" in msg assert "installed" in msg
# check if we have latex installed, and abort test if we don't # check if we have latex installed, and abort test if we don't
@ -35,12 +35,12 @@ def test_latex():
# check media db should cause latex to be generated # check media db should cause latex to be generated
col.media.render_all_latex() col.media.render_all_latex()
assert len(os.listdir(col.media.dir())) == 1 assert len(os.listdir(col.media.dir())) == 1
assert ".png" in note.cards()[0].q() assert ".png" in note.cards()[0].question()
# adding new notes should cause generation on question display # adding new notes should cause generation on question display
note = col.newNote() note = col.newNote()
note["Front"] = "[latex]world[/latex]" note["Front"] = "[latex]world[/latex]"
col.addNote(note) col.addNote(note)
note.cards()[0].q() note.cards()[0].question()
assert len(os.listdir(col.media.dir())) == 2 assert len(os.listdir(col.media.dir())) == 2
# another note with the same media should reuse # another note with the same media should reuse
note = col.newNote() note = col.newNote()
@ -48,7 +48,7 @@ def test_latex():
col.addNote(note) col.addNote(note)
assert len(os.listdir(col.media.dir())) == 2 assert len(os.listdir(col.media.dir())) == 2
oldcard = note.cards()[0] oldcard = note.cards()[0]
assert ".png" in oldcard.q() assert ".png" in oldcard.question()
# if we turn off building, then previous cards should work, but cards with # if we turn off building, then previous cards should work, but cards with
# missing media will show a broken image # missing media will show a broken image
anki.latex.build = False anki.latex.build = False
@ -56,7 +56,7 @@ def test_latex():
note["Front"] = "[latex]foo[/latex]" note["Front"] = "[latex]foo[/latex]"
col.addNote(note) col.addNote(note)
assert len(os.listdir(col.media.dir())) == 2 assert len(os.listdir(col.media.dir())) == 2
assert ".png" in oldcard.q() assert ".png" in oldcard.question()
# turn it on again so other test don't suffer # turn it on again so other test don't suffer
anki.latex.build = True anki.latex.build = True
@ -95,5 +95,5 @@ def _test_includes_bad_command(bad):
note = col.newNote() note = col.newNote()
note["Front"] = "[latex]%s[/latex]" % bad note["Front"] = "[latex]%s[/latex]" % bad
col.addNote(note) col.addNote(note)
q = without_unicode_isolation(note.cards()[0].q()) q = without_unicode_isolation(note.cards()[0].question())
return ("'%s' is not allowed on cards" % bad in q, "Card content: %s" % q) return ("'%s' is not allowed on cards" % bad in q, "Card content: %s" % q)

View file

@ -112,7 +112,7 @@ def test_templates():
# and should have updated the other cards' ordinals # and should have updated the other cards' ordinals
c = note.cards()[0] c = note.cards()[0]
assert c.ord == 0 assert c.ord == 0
assert stripHTML(c.q()) == "1" assert stripHTML(c.question()) == "1"
# it shouldn't be possible to orphan notes by removing templates # it shouldn't be possible to orphan notes by removing templates
t = mm.newTemplate("template name") t = mm.newTemplate("template name")
t["qfmt"] = "{{Front}}2" t["qfmt"] = "{{Front}}2"
@ -158,14 +158,14 @@ def test_text():
note = col.newNote() note = col.newNote()
note["Front"] = "hello<b>world" note["Front"] = "hello<b>world"
col.addNote(note) col.addNote(note)
assert "helloworld" in note.cards()[0].q() assert "helloworld" in note.cards()[0].question()
def test_cloze(): def test_cloze():
col = getEmptyCol() col = getEmptyCol()
col.models.setCurrent(col.models.byName("Cloze")) col.models.setCurrent(col.models.byName("Cloze"))
note = col.newNote() note = col.newNote()
assert note.model()["name"] == "Cloze" assert note.note_type()["name"] == "Cloze"
# a cloze model with no clozes is not empty # a cloze model with no clozes is not empty
note["Text"] = "nothing" note["Text"] = "nothing"
assert col.addNote(note) assert col.addNote(note)
@ -173,30 +173,30 @@ def test_cloze():
note = col.newNote() note = col.newNote()
note["Text"] = "hello {{c1::world}}" note["Text"] = "hello {{c1::world}}"
assert col.addNote(note) == 1 assert col.addNote(note) == 1
assert "hello <span class=cloze>[...]</span>" in note.cards()[0].q() assert "hello <span class=cloze>[...]</span>" in note.cards()[0].question()
assert "hello <span class=cloze>world</span>" in note.cards()[0].a() assert "hello <span class=cloze>world</span>" in note.cards()[0].answer()
# and with a comment # and with a comment
note = col.newNote() note = col.newNote()
note["Text"] = "hello {{c1::world::typical}}" note["Text"] = "hello {{c1::world::typical}}"
assert col.addNote(note) == 1 assert col.addNote(note) == 1
assert "<span class=cloze>[typical]</span>" in note.cards()[0].q() assert "<span class=cloze>[typical]</span>" in note.cards()[0].question()
assert "<span class=cloze>world</span>" in note.cards()[0].a() assert "<span class=cloze>world</span>" in note.cards()[0].answer()
# and with 2 clozes # and with 2 clozes
note = col.newNote() note = col.newNote()
note["Text"] = "hello {{c1::world}} {{c2::bar}}" note["Text"] = "hello {{c1::world}} {{c2::bar}}"
assert col.addNote(note) == 2 assert col.addNote(note) == 2
(c1, c2) = note.cards() (c1, c2) = note.cards()
assert "<span class=cloze>[...]</span> bar" in c1.q() assert "<span class=cloze>[...]</span> bar" in c1.question()
assert "<span class=cloze>world</span> bar" in c1.a() assert "<span class=cloze>world</span> bar" in c1.answer()
assert "world <span class=cloze>[...]</span>" in c2.q() assert "world <span class=cloze>[...]</span>" in c2.question()
assert "world <span class=cloze>bar</span>" in c2.a() assert "world <span class=cloze>bar</span>" in c2.answer()
# if there are multiple answers for a single cloze, they are given in a # if there are multiple answers for a single cloze, they are given in a
# list # list
note = col.newNote() note = col.newNote()
note["Text"] = "a {{c1::b}} {{c1::c}}" note["Text"] = "a {{c1::b}} {{c1::c}}"
assert col.addNote(note) == 1 assert col.addNote(note) == 1
assert "<span class=cloze>b</span> <span class=cloze>c</span>" in ( assert "<span class=cloze>b</span> <span class=cloze>c</span>" in (
note.cards()[0].a() note.cards()[0].answer()
) )
# if we add another cloze, a card should be generated # if we add another cloze, a card should be generated
cnt = col.cardCount() cnt = col.cardCount()
@ -218,11 +218,11 @@ def test_cloze_mathjax():
] = r"{{c1::ok}} \(2^2\) {{c2::not ok}} \(2^{{c3::2}}\) \(x^3\) {{c4::blah}} {{c5::text with \(x^2\) jax}}" ] = r"{{c1::ok}} \(2^2\) {{c2::not ok}} \(2^{{c3::2}}\) \(x^3\) {{c4::blah}} {{c5::text with \(x^2\) jax}}"
assert col.addNote(note) assert col.addNote(note)
assert len(note.cards()) == 5 assert len(note.cards()) == 5
assert "class=cloze" in note.cards()[0].q() assert "class=cloze" in note.cards()[0].question()
assert "class=cloze" in note.cards()[1].q() assert "class=cloze" in note.cards()[1].question()
assert "class=cloze" not in note.cards()[2].q() assert "class=cloze" not in note.cards()[2].question()
assert "class=cloze" in note.cards()[3].q() assert "class=cloze" in note.cards()[3].question()
assert "class=cloze" in note.cards()[4].q() assert "class=cloze" in note.cards()[4].question()
note = col.newNote() note = col.newNote()
note["Text"] = r"\(a\) {{c1::b}} \[ {{c1::c}} \]" note["Text"] = r"\(a\) {{c1::b}} \[ {{c1::c}} \]"
@ -230,7 +230,7 @@ def test_cloze_mathjax():
assert len(note.cards()) == 1 assert len(note.cards()) == 1
assert ( assert (
note.cards()[0] note.cards()[0]
.q() .question()
.endswith(r"\(a\) <span class=cloze>[...]</span> \[ [...] \]") .endswith(r"\(a\) <span class=cloze>[...]</span> \[ [...] \]")
) )
@ -244,7 +244,7 @@ def test_typecloze():
note = col.newNote() note = col.newNote()
note["Text"] = "hello {{c1::world}}" note["Text"] = "hello {{c1::world}}"
col.addNote(note) col.addNote(note)
assert "[[type:cloze:Text]]" in note.cards()[0].q() assert "[[type:cloze:Text]]" in note.cards()[0].question()
def test_chained_mods(): def test_chained_mods():
@ -275,11 +275,11 @@ def test_chained_mods():
assert col.addNote(note) == 1 assert col.addNote(note) == 1
assert ( assert (
"This <span class=cloze>[sentence]</span> demonstrates <span class=cloze>[chained]</span> clozes." "This <span class=cloze>[sentence]</span> demonstrates <span class=cloze>[chained]</span> clozes."
in note.cards()[0].q() in note.cards()[0].question()
) )
assert ( assert (
"This <span class=cloze>phrase</span> demonstrates <span class=cloze>en chaine</span> clozes." "This <span class=cloze>phrase</span> demonstrates <span class=cloze>en chaine</span> clozes."
in note.cards()[0].a() in note.cards()[0].answer()
) )
@ -309,16 +309,16 @@ def test_modelChange():
# switch cards # switch cards
c0 = note.cards()[0] c0 = note.cards()[0]
c1 = note.cards()[1] c1 = note.cards()[1]
assert "b123" in c0.q() assert "b123" in c0.question()
assert "note" in c1.q() assert "note" in c1.question()
assert c0.ord == 0 assert c0.ord == 0
assert c1.ord == 1 assert c1.ord == 1
col.models.change(basic, [note.id], basic, noop, map) col.models.change(basic, [note.id], basic, noop, map)
note.load() note.load()
c0.load() c0.load()
c1.load() c1.load()
assert "note" in c0.q() assert "note" in c0.question()
assert "b123" in c1.q() assert "b123" in c1.question()
assert c0.ord == 1 assert c0.ord == 1
assert c1.ord == 0 assert c1.ord == 0
# .cards() returns cards in order # .cards() returns cards in order

View file

@ -78,7 +78,7 @@ def test_new():
# qs = ("2", "3", "2", "3") # qs = ("2", "3", "2", "3")
# for n in range(4): # for n in range(4):
# c = col.sched.getCard() # c = col.sched.getCard()
# assert qs[n] in c.q() # assert qs[n] in c.question()
# col.sched.answerCard(c, 2) # col.sched.answerCard(c, 2)
@ -90,7 +90,7 @@ def test_newLimits():
note = col.newNote() note = col.newNote()
note["Front"] = str(i) note["Front"] = str(i)
if i > 4: if i > 4:
note.model()["did"] = deck2 note.note_type()["did"] = deck2
col.addNote(note) col.addNote(note)
# give the child deck a different configuration # give the child deck a different configuration
c2 = col.decks.add_config_returning_id("new conf") c2 = col.decks.add_config_returning_id("new conf")
@ -224,17 +224,17 @@ def test_learn_collapsed():
col.reset() col.reset()
# should get '1' first # should get '1' first
c = col.sched.getCard() c = col.sched.getCard()
assert c.q().endswith("1") assert c.question().endswith("1")
# pass it so it's due in 10 minutes # pass it so it's due in 10 minutes
col.sched.answerCard(c, 2) col.sched.answerCard(c, 2)
# get the other card # get the other card
c = col.sched.getCard() c = col.sched.getCard()
assert c.q().endswith("2") assert c.question().endswith("2")
# fail it so it's due in 1 minute # fail it so it's due in 1 minute
col.sched.answerCard(c, 1) col.sched.answerCard(c, 1)
# we shouldn't get the same card again # we shouldn't get the same card again
c = col.sched.getCard() c = col.sched.getCard()
assert not c.q().endswith("2") assert not c.question().endswith("2")
def test_learn_day(): def test_learn_day():
@ -316,7 +316,7 @@ def test_reviews():
c.reps = 3 c.reps = 3
c.lapses = 1 c.lapses = 1
c.ivl = 100 c.ivl = 100
c.startTimer() c.start_timer()
c.flush() c.flush()
# save it for later use as well # save it for later use as well
cardcopy = copy.copy(c) cardcopy = copy.copy(c)
@ -393,7 +393,7 @@ def test_button_spacing():
c.due = col.sched.today c.due = col.sched.today
c.reps = 1 c.reps = 1
c.ivl = 1 c.ivl = 1
c.startTimer() c.start_timer()
c.flush() c.flush()
col.reset() col.reset()
ni = col.sched.nextIvlStr ni = col.sched.nextIvlStr
@ -575,7 +575,7 @@ def test_cram():
c.due = col.sched.today + 25 c.due = col.sched.today + 25
c.mod = 1 c.mod = 1
c.factor = STARTING_FACTOR c.factor = STARTING_FACTOR
c.startTimer() c.start_timer()
c.flush() c.flush()
col.reset() col.reset()
assert col.sched.counts() == (0, 0, 0) assert col.sched.counts() == (0, 0, 0)
@ -638,7 +638,7 @@ def test_cram():
assert col.sched.nextIvl(c, 2) == 600 assert col.sched.nextIvl(c, 2) == 600
assert col.sched.nextIvl(c, 3) == 86400 assert col.sched.nextIvl(c, 3) == 86400
# delete the deck, returning the card mid-study # delete the deck, returning the card mid-study
col.decks.rem(col.decks.selected()) col.decks.remove([col.decks.selected()])
assert len(col.sched.deck_due_tree().children) == 1 assert len(col.sched.deck_due_tree().children) == 1
c.load() c.load()
assert c.ivl == 1 assert c.ivl == 1
@ -964,7 +964,7 @@ def test_deckDue():
# and one that's a child # and one that's a child
note = col.newNote() note = col.newNote()
note["Front"] = "two" note["Front"] = "two"
default1 = note.model()["did"] = col.decks.id("Default::1") default1 = note.note_type()["did"] = col.decks.id("Default::1")
col.addNote(note) col.addNote(note)
# make it a review card # make it a review card
c = note.cards()[0] c = note.cards()[0]
@ -974,12 +974,12 @@ def test_deckDue():
# add one more with a new deck # add one more with a new deck
note = col.newNote() note = col.newNote()
note["Front"] = "two" note["Front"] = "two"
note.model()["did"] = col.decks.id("foo::bar") note.note_type()["did"] = col.decks.id("foo::bar")
col.addNote(note) col.addNote(note)
# and one that's a sibling # and one that's a sibling
note = col.newNote() note = col.newNote()
note["Front"] = "three" note["Front"] = "three"
note.model()["did"] = col.decks.id("foo::baz") note.note_type()["did"] = col.decks.id("foo::baz")
col.addNote(note) col.addNote(note)
col.reset() col.reset()
assert len(col.decks.all_names_and_ids()) == 5 assert len(col.decks.all_names_and_ids()) == 5
@ -1010,12 +1010,12 @@ def test_deckFlow():
# and one that's a child # and one that's a child
note = col.newNote() note = col.newNote()
note["Front"] = "two" note["Front"] = "two"
note.model()["did"] = col.decks.id("Default::2") note.note_type()["did"] = col.decks.id("Default::2")
col.addNote(note) col.addNote(note)
# and another that's higher up # and another that's higher up
note = col.newNote() note = col.newNote()
note["Front"] = "three" note["Front"] = "three"
default1 = note.model()["did"] = col.decks.id("Default::1") default1 = note.note_type()["did"] = col.decks.id("Default::1")
col.addNote(note) col.addNote(note)
# should get top level one first, then ::1, then ::2 # should get top level one first, then ::1, then ::2
col.reset() col.reset()
@ -1040,7 +1040,7 @@ def test_norelearn():
c.reps = 3 c.reps = 3
c.lapses = 1 c.lapses = 1
c.ivl = 100 c.ivl = 100
c.startTimer() c.start_timer()
c.flush() c.flush()
col.reset() col.reset()
col.sched.answerCard(c, 1) col.sched.answerCard(c, 1)
@ -1062,7 +1062,7 @@ def test_failmult():
c.factor = STARTING_FACTOR c.factor = STARTING_FACTOR
c.reps = 3 c.reps = 3
c.lapses = 1 c.lapses = 1
c.startTimer() c.start_timer()
c.flush() c.flush()
conf = col.sched._cardConf(c) conf = col.sched._cardConf(c)
conf["lapse"]["mult"] = 0.5 conf["lapse"]["mult"] = 0.5

View file

@ -90,7 +90,7 @@ def test_new():
# qs = ("2", "3", "2", "3") # qs = ("2", "3", "2", "3")
# for n in range(4): # for n in range(4):
# c = col.sched.getCard() # c = col.sched.getCard()
# assert qs[n] in c.q() # assert qs[n] in c.question()
# col.sched.answerCard(c, 2) # col.sched.answerCard(c, 2)
@ -102,7 +102,7 @@ def test_newLimits():
note = col.newNote() note = col.newNote()
note["Front"] = str(i) note["Front"] = str(i)
if i > 4: if i > 4:
note.model()["did"] = deck2 note.note_type()["did"] = deck2
col.addNote(note) col.addNote(note)
# give the child deck a different configuration # give the child deck a different configuration
c2 = col.decks.add_config_returning_id("new conf") c2 = col.decks.add_config_returning_id("new conf")
@ -270,17 +270,17 @@ def test_learn_collapsed():
col.reset() col.reset()
# should get '1' first # should get '1' first
c = col.sched.getCard() c = col.sched.getCard()
assert c.q().endswith("1") assert c.question().endswith("1")
# pass it so it's due in 10 minutes # pass it so it's due in 10 minutes
col.sched.answerCard(c, 3) col.sched.answerCard(c, 3)
# get the other card # get the other card
c = col.sched.getCard() c = col.sched.getCard()
assert c.q().endswith("2") assert c.question().endswith("2")
# fail it so it's due in 1 minute # fail it so it's due in 1 minute
col.sched.answerCard(c, 1) col.sched.answerCard(c, 1)
# we shouldn't get the same card again # we shouldn't get the same card again
c = col.sched.getCard() c = col.sched.getCard()
assert not c.q().endswith("2") assert not c.question().endswith("2")
def test_learn_day(): def test_learn_day():
@ -374,7 +374,7 @@ def test_reviews():
c.reps = 3 c.reps = 3
c.lapses = 1 c.lapses = 1
c.ivl = 100 c.ivl = 100
c.startTimer() c.start_timer()
c.flush() c.flush()
# save it for later use as well # save it for later use as well
cardcopy = copy.copy(c) cardcopy = copy.copy(c)
@ -509,7 +509,7 @@ def test_button_spacing():
c.due = col.sched.today c.due = col.sched.today
c.reps = 1 c.reps = 1
c.ivl = 1 c.ivl = 1
c.startTimer() c.start_timer()
c.flush() c.flush()
col.reset() col.reset()
ni = col.sched.nextIvlStr ni = col.sched.nextIvlStr
@ -731,7 +731,7 @@ def test_filt_reviewing_early_normal():
c.due = col.sched.today + 25 c.due = col.sched.today + 25
c.mod = 1 c.mod = 1
c.factor = STARTING_FACTOR c.factor = STARTING_FACTOR
c.startTimer() c.start_timer()
c.flush() c.flush()
col.reset() col.reset()
assert col.sched.counts() == (0, 0, 0) assert col.sched.counts() == (0, 0, 0)
@ -1082,7 +1082,7 @@ def test_deckDue():
# and one that's a child # and one that's a child
note = col.newNote() note = col.newNote()
note["Front"] = "two" note["Front"] = "two"
default1 = note.model()["did"] = col.decks.id("Default::1") default1 = note.note_type()["did"] = col.decks.id("Default::1")
col.addNote(note) col.addNote(note)
# make it a review card # make it a review card
c = note.cards()[0] c = note.cards()[0]
@ -1092,12 +1092,12 @@ def test_deckDue():
# add one more with a new deck # add one more with a new deck
note = col.newNote() note = col.newNote()
note["Front"] = "two" note["Front"] = "two"
note.model()["did"] = col.decks.id("foo::bar") note.note_type()["did"] = col.decks.id("foo::bar")
col.addNote(note) col.addNote(note)
# and one that's a sibling # and one that's a sibling
note = col.newNote() note = col.newNote()
note["Front"] = "three" note["Front"] = "three"
note.model()["did"] = col.decks.id("foo::baz") note.note_type()["did"] = col.decks.id("foo::baz")
col.addNote(note) col.addNote(note)
col.reset() col.reset()
assert len(col.decks.all_names_and_ids()) == 5 assert len(col.decks.all_names_and_ids()) == 5
@ -1138,12 +1138,12 @@ def test_deckFlow():
# and one that's a child # and one that's a child
note = col.newNote() note = col.newNote()
note["Front"] = "two" note["Front"] = "two"
note.model()["did"] = col.decks.id("Default::2") note.note_type()["did"] = col.decks.id("Default::2")
col.addNote(note) col.addNote(note)
# and another that's higher up # and another that's higher up
note = col.newNote() note = col.newNote()
note["Front"] = "three" note["Front"] = "three"
default1 = note.model()["did"] = col.decks.id("Default::1") default1 = note.note_type()["did"] = col.decks.id("Default::1")
col.addNote(note) col.addNote(note)
col.reset() col.reset()
assert col.sched.counts() == (3, 0, 0) assert col.sched.counts() == (3, 0, 0)
@ -1255,7 +1255,7 @@ def test_norelearn():
c.reps = 3 c.reps = 3
c.lapses = 1 c.lapses = 1
c.ivl = 100 c.ivl = 100
c.startTimer() c.start_timer()
c.flush() c.flush()
col.reset() col.reset()
col.sched.answerCard(c, 1) col.sched.answerCard(c, 1)
@ -1277,7 +1277,7 @@ def test_failmult():
c.factor = STARTING_FACTOR c.factor = STARTING_FACTOR
c.reps = 3 c.reps = 3
c.lapses = 1 c.lapses = 1
c.startTimer() c.start_timer()
c.flush() c.flush()
conf = col.sched._cardConf(c) conf = col.sched._cardConf(c)
conf["lapse"]["mult"] = 0.5 conf["lapse"]["mult"] = 0.5

View file

@ -15,4 +15,4 @@ def test_deferred_frontside():
note["Back"] = "" note["Back"] = ""
col.addNote(note) col.addNote(note)
assert "xxtest" in note.cards()[0].a() assert "xxtest" in note.cards()[0].answer()

View file

@ -128,14 +128,14 @@ class AddCards(QDialog):
if old: if old:
old_fields = list(old.keys()) old_fields = list(old.keys())
new_fields = list(new.keys()) new_fields = list(new.keys())
for n, f in enumerate(new.model()["flds"]): for n, f in enumerate(new.note_type()["flds"]):
field_name = f["name"] field_name = f["name"]
# copy identical fields # copy identical fields
if field_name in old_fields: if field_name in old_fields:
new[field_name] = old[field_name] new[field_name] = old[field_name]
elif n < len(old.model()["flds"]): elif n < len(old.note_type()["flds"]):
# set non-identical fields by field index # set non-identical fields by field index
old_field_name = old.model()["flds"][n]["name"] old_field_name = old.note_type()["flds"][n]["name"]
if old_field_name not in new_fields: if old_field_name not in new_fields:
new.fields[n] = old.fields[n] new.fields[n] = old.fields[n]
new.tags = old.tags new.tags = old.tags
@ -147,7 +147,7 @@ class AddCards(QDialog):
def _load_new_note(self, sticky_fields_from: Optional[Note] = None) -> None: def _load_new_note(self, sticky_fields_from: Optional[Note] = None) -> None:
note = self._new_note() note = self._new_note()
if old_note := sticky_fields_from: if old_note := sticky_fields_from:
flds = note.model()["flds"] flds = note.note_type()["flds"]
# copy fields from old note # copy fields from old note
if old_note: if old_note:
for n in range(min(len(note.fields), len(old_note.fields))): for n in range(min(len(note.fields), len(old_note.fields))):

View file

@ -187,11 +187,11 @@ class Previewer(QDialog):
return return
# need to force reload even if answer # need to force reload even if answer
txt = c.q(reload=True) txt = c.question(reload=True)
if self._state == "answer": if self._state == "answer":
func = "_showAnswer" func = "_showAnswer"
txt = c.a() txt = c.answer()
txt = re.sub(r"\[\[type:[^]]+\]\]", "", txt) txt = re.sub(r"\[\[type:[^]]+\]\]", "", txt)
bodyclass = theme_manager.body_classes_for_card_ord(c.ord) bodyclass = theme_manager.body_classes_for_card_ord(c.ord)

View file

@ -52,7 +52,7 @@ class CardLayout(QDialog):
self.ord = ord self.ord = ord
self.col = self.mw.col.weakref() self.col = self.mw.col.weakref()
self.mm = self.mw.col.models self.mm = self.mw.col.models
self.model = note.model() self.model = note.note_type()
self.templates = self.model["tmpls"] self.templates = self.model["tmpls"]
self.fill_empty_action_toggled = fill_empty self.fill_empty_action_toggled = fill_empty
self.night_mode_is_enabled = self.mw.pm.night_mode() self.night_mode_is_enabled = self.mw.pm.night_mode()
@ -493,11 +493,11 @@ class CardLayout(QDialog):
) )
if self.pform.preview_front.isChecked(): if self.pform.preview_front.isChecked():
q = ti(self.mw.prepare_card_text_for_display(c.q())) q = ti(self.mw.prepare_card_text_for_display(c.question()))
q = gui_hooks.card_will_show(q, c, "clayoutQuestion") q = gui_hooks.card_will_show(q, c, "clayoutQuestion")
text = q text = q
else: else:
a = ti(self.mw.prepare_card_text_for_display(c.a()), type="a") a = ti(self.mw.prepare_card_text_for_display(c.answer()), type="a")
a = gui_hooks.card_will_show(a, c, "clayoutAnswer") a = gui_hooks.card_will_show(a, c, "clayoutAnswer")
text = a text = a

View file

@ -325,7 +325,7 @@ $editorToolbar.then(({{ toolbar }}) => toolbar.appendGroup({{
def _onFields(self) -> None: def _onFields(self) -> None:
from aqt.fields import FieldDialog from aqt.fields import FieldDialog
FieldDialog(self.mw, self.note.model(), parent=self.parentWindow) FieldDialog(self.mw, self.note.note_type(), parent=self.parentWindow)
def onCardLayout(self) -> None: def onCardLayout(self) -> None:
self.call_after_note_saved(self._onCardLayout) self.call_after_note_saved(self._onCardLayout)
@ -394,7 +394,7 @@ $editorToolbar.then(({{ toolbar }}) => toolbar.appendGroup({{
(type, num) = cmd.split(":", 1) (type, num) = cmd.split(":", 1)
ord = int(num) ord = int(num)
model = self.note.model() model = self.note.note_type()
fld = model["flds"][ord] fld = model["flds"][ord]
new_state = not fld["sticky"] new_state = not fld["sticky"]
fld["sticky"] = new_state fld["sticky"] = new_state
@ -463,7 +463,7 @@ $editorToolbar.then(({{ toolbar }}) => toolbar.appendGroup({{
) )
if self.addMode: if self.addMode:
sticky = [field["sticky"] for field in self.note.model()["flds"]] sticky = [field["sticky"] for field in self.note.note_type()["flds"]]
js += " setSticky(%s);" % json.dumps(sticky) js += " setSticky(%s);" % json.dumps(sticky)
js = gui_hooks.editor_will_load_note(js, self.note, self) js = gui_hooks.editor_will_load_note(js, self.note, self)
@ -478,7 +478,7 @@ $editorToolbar.then(({{ toolbar }}) => toolbar.appendGroup({{
def fonts(self) -> List[Tuple[str, int, bool]]: def fonts(self) -> List[Tuple[str, int, bool]]:
return [ return [
(gui_hooks.editor_will_use_font_for_field(f["font"]), f["size"], f["rtl"]) (gui_hooks.editor_will_use_font_for_field(f["font"]), f["size"], f["rtl"])
for f in self.note.model()["flds"] for f in self.note.note_type()["flds"]
] ]
def call_after_note_saved( def call_after_note_saved(
@ -532,7 +532,7 @@ $editorToolbar.then(({{ toolbar }}) => toolbar.appendGroup({{
search=( search=(
SearchNode( SearchNode(
dupe=SearchNode.Dupe( dupe=SearchNode.Dupe(
notetype_id=self.note.model()["id"], notetype_id=self.note.note_type()["id"],
first_field=self.note.fields[0], first_field=self.note.fields[0],
) )
), ),
@ -542,7 +542,7 @@ $editorToolbar.then(({{ toolbar }}) => toolbar.appendGroup({{
def fieldsAreBlank(self, previousNote: Optional[Note] = None) -> bool: def fieldsAreBlank(self, previousNote: Optional[Note] = None) -> bool:
if not self.note: if not self.note:
return True return True
m = self.note.model() m = self.note.note_type()
for c, f in enumerate(self.note.fields): for c, f in enumerate(self.note.fields):
f = f.replace("<br>", "").strip() f = f.replace("<br>", "").strip()
notChangedvalues = {"", "<br>"} notChangedvalues = {"", "<br>"}
@ -681,7 +681,7 @@ $editorToolbar.then(({{ toolbar }}) => toolbar.appendGroup({{
def _onCloze(self) -> None: def _onCloze(self) -> None:
# check that the model is set up for cloze deletion # check that the model is set up for cloze deletion
if self.note.model()["type"] != MODEL_CLOZE: if self.note.note_type()["type"] != MODEL_CLOZE:
if self.addMode: if self.addMode:
tooltip(tr.editing_warning_cloze_deletions_will_not_work()) tooltip(tr.editing_warning_cloze_deletions_will_not_work())
else: else:
@ -1280,7 +1280,7 @@ gui_hooks.editor_will_munge_html.append(reverse_url_quoting)
def set_cloze_button(editor: Editor) -> None: def set_cloze_button(editor: Editor) -> None:
if editor.note.model()["type"] == MODEL_CLOZE: if editor.note.note_type()["type"] == MODEL_CLOZE:
editor.web.eval( editor.web.eval(
'$editorToolbar.then(({ templateButtons }) => templateButtons.showButton("cloze")); ' '$editorToolbar.then(({ templateButtons }) => templateButtons.showButton("cloze")); '
) )

View file

@ -118,7 +118,7 @@ class ImportDialog(QDialog):
self.deck = aqt.deckchooser.DeckChooser(self.mw, self.frm.deckArea, label=False) self.deck = aqt.deckchooser.DeckChooser(self.mw, self.frm.deckArea, label=False)
def modelChanged(self, unused: Any = None) -> None: def modelChanged(self, unused: Any = None) -> None:
self.importer.model = self.mw.col.models.current() self.importer.note_type = self.mw.col.models.current()
self.importer.initMapping() self.importer.initMapping()
self.showMapping() self.showMapping()
@ -191,9 +191,9 @@ class ImportDialog(QDialog):
self.importer.tagModified = self.frm.tagModified.text() self.importer.tagModified = self.frm.tagModified.text()
self.mw.pm.profile["tagModified"] = self.importer.tagModified self.mw.pm.profile["tagModified"] = self.importer.tagModified
self.mw.col.set_aux_notetype_config( self.mw.col.set_aux_notetype_config(
self.importer.model["id"], "lastDeck", self.deck.selected_deck_id self.importer.note_type["id"], "lastDeck", self.deck.selected_deck_id
) )
self.mw.col.models.save(self.importer.model, updateReqs=False) self.mw.col.models.save(self.importer.note_type, updateReqs=False)
self.mw.progress.start() self.mw.progress.start()
self.mw.checkpoint(tr.actions_import()) self.mw.checkpoint(tr.actions_import())
@ -273,7 +273,7 @@ class ImportDialog(QDialog):
qconnect(button.clicked, lambda _, s=self, n=num: s.changeMappingNum(n)) qconnect(button.clicked, lambda _, s=self, n=num: s.changeMappingNum(n))
def changeMappingNum(self, n: int) -> None: def changeMappingNum(self, n: int) -> None:
f = ChangeMap(self.mw, self.importer.model, self.mapping[n]).getField() f = ChangeMap(self.mw, self.importer.note_type, self.mapping[n]).getField()
try: try:
# make sure we don't have it twice # make sure we don't have it twice
index = self.mapping.index(f) index = self.mapping.index(f)

View file

@ -218,7 +218,7 @@ class Reviewer:
if self.cardQueue: if self.cardQueue:
# undone/edited cards to show # undone/edited cards to show
card = self.cardQueue.pop() card = self.cardQueue.pop()
card.startTimer() card.start_timer()
self.hadCardQueue = True self.hadCardQueue = True
else: else:
if self.hadCardQueue: if self.hadCardQueue:
@ -236,7 +236,7 @@ class Reviewer:
return return
self._v3 = V3CardInfo.from_queue(output) self._v3 = V3CardInfo.from_queue(output)
self.card = Card(self.mw.col, backend_card=self._v3.top_card().card) self.card = Card(self.mw.col, backend_card=self._v3.top_card().card)
self.card.startTimer() self.card.start_timer()
def get_next_states(self) -> Optional[NextStates]: def get_next_states(self) -> Optional[NextStates]:
if v3 := self._v3: if v3 := self._v3:
@ -320,7 +320,7 @@ class Reviewer:
self.typedAnswer: str = None self.typedAnswer: str = None
c = self.card c = self.card
# grab the question and play audio # grab the question and play audio
q = c.q() q = c.question()
# play audio? # play audio?
if c.autoplay(): if c.autoplay():
AnkiWebView.setPlaybackRequiresGesture(False) AnkiWebView.setPlaybackRequiresGesture(False)
@ -370,7 +370,7 @@ class Reviewer:
return return
self.state = "answer" self.state = "answer"
c = self.card c = self.card
a = c.a() a = c.answer()
# play audio? # play audio?
if c.autoplay(): if c.autoplay():
sounds = c.answer_av_tags() sounds = c.answer_av_tags()
@ -535,7 +535,7 @@ class Reviewer:
clozeIdx = self.card.ord + 1 clozeIdx = self.card.ord + 1
fld = fld.split(":")[1] fld = fld.split(":")[1]
# loop through fields for a match # loop through fields for a match
for f in self.card.model()["flds"]: for f in self.card.note_type()["flds"]:
if f["name"] == fld: if f["name"] == fld:
self.typeCorrect = self.card.note()[f["name"]] self.typeCorrect = self.card.note()[f["name"]]
if clozeIdx: if clozeIdx:
@ -733,7 +733,7 @@ time = %(time)d;
editkey=tr.actions_shortcut_key(val="E"), editkey=tr.actions_shortcut_key(val="E"),
more=tr.studying_more(), more=tr.studying_more(),
downArrow=downArrow(), downArrow=downArrow(),
time=self.card.timeTaken() // 1000, time=self.card.time_taken() // 1000,
) )
def _showAnswerButton(self) -> None: def _showAnswerButton(self) -> None:
@ -749,8 +749,8 @@ time = %(time)d;
"<table cellpadding=0><tr><td class=stat2 align=center>%s</td></tr></table>" "<table cellpadding=0><tr><td class=stat2 align=center>%s</td></tr></table>"
% middle % middle
) )
if self.card.shouldShowTimer(): if self.card.should_show_timer():
maxTime = self.card.timeLimit() / 1000 maxTime = self.card.time_limit() / 1000
else: else:
maxTime = 0 maxTime = 0
self.bottom.web.eval("showQuestion(%s,%d);" % (json.dumps(middle), maxTime)) self.bottom.web.eval("showQuestion(%s,%d);" % (json.dumps(middle), maxTime))

View file

@ -7,7 +7,7 @@ mod learning;
mod main; mod main;
pub(crate) mod undo; pub(crate) mod undo;
use std::{collections::VecDeque, time::Instant}; use std::collections::VecDeque;
pub(crate) use builder::{DueCard, NewCard}; pub(crate) use builder::{DueCard, NewCard};
pub(crate) use entry::{QueueEntry, QueueEntryKind}; pub(crate) use entry::{QueueEntry, QueueEntryKind};
@ -205,9 +205,7 @@ impl Collection {
self.discard_undo_and_study_queues(); self.discard_undo_and_study_queues();
} }
if self.state.card_queues.is_none() { if self.state.card_queues.is_none() {
let now = Instant::now();
self.state.card_queues = Some(self.build_queues(deck)?); self.state.card_queues = Some(self.build_queues(deck)?);
println!("queue build in {:?}", now.elapsed());
} }
Ok(self.state.card_queues.as_mut().unwrap()) Ok(self.state.card_queues.as_mut().unwrap())