PEP8 collection.py
This commit is contained in:
parent
17533e6a78
commit
1b15069b24
|
@ -48,4 +48,5 @@ disable=
|
|||
good-names =
|
||||
id,
|
||||
tr,
|
||||
db,
|
||||
db,
|
||||
ok,
|
|
@ -1,6 +1,8 @@
|
|||
# Copyright: Ankitects Pty Ltd and contributors
|
||||
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
# pylint: enable=invalid-name
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Generator, List, Literal, Optional, Sequence, Tuple, Union, cast
|
||||
|
@ -8,6 +10,8 @@ from typing import Any, Generator, List, Literal, Optional, Sequence, Tuple, Uni
|
|||
import anki._backend.backend_pb2 as _pb
|
||||
|
||||
# protobuf we publicly export - listed first to avoid circular imports
|
||||
from anki._legacy import DeprecatedNamesMixin, deprecated
|
||||
|
||||
SearchNode = _pb.SearchNode
|
||||
Progress = _pb.Progress
|
||||
EmptyCardsReport = _pb.EmptyCardsReport
|
||||
|
@ -24,8 +28,6 @@ BrowserColumns = _pb.BrowserColumns
|
|||
|
||||
import copy
|
||||
import os
|
||||
import pprint
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
|
@ -52,7 +54,6 @@ from anki.sync import SyncAuth, SyncOutput, SyncStatus
|
|||
from anki.tags import TagManager
|
||||
from anki.types import assert_exhaustive
|
||||
from anki.utils import (
|
||||
devMode,
|
||||
from_json_bytes,
|
||||
ids2str,
|
||||
intTime,
|
||||
|
@ -81,7 +82,7 @@ class LegacyCheckpoint:
|
|||
LegacyUndoResult = Union[None, LegacyCheckpoint, LegacyReviewUndo]
|
||||
|
||||
|
||||
class Collection:
|
||||
class Collection(DeprecatedNamesMixin):
|
||||
sched: Union[V1Scheduler, V2Scheduler, V3Scheduler]
|
||||
|
||||
def __init__(
|
||||
|
@ -104,7 +105,7 @@ class Collection:
|
|||
self.decks = DeckManager(self)
|
||||
self.tags = TagManager(self)
|
||||
self.conf = ConfigManager(self)
|
||||
self._loadScheduler()
|
||||
self._load_scheduler()
|
||||
|
||||
def name(self) -> Any:
|
||||
return os.path.splitext(os.path.basename(self.path))[0]
|
||||
|
@ -142,17 +143,17 @@ class Collection:
|
|||
##########################################################################
|
||||
|
||||
# for backwards compatibility, v3 is represented as 2
|
||||
supportedSchedulerVersions = (1, 2)
|
||||
_supported_scheduler_versions = (1, 2)
|
||||
|
||||
def schedVer(self) -> Literal[1, 2]:
|
||||
def sched_ver(self) -> Literal[1, 2]:
|
||||
ver = self.conf.get("schedVer", 1)
|
||||
if ver in self.supportedSchedulerVersions:
|
||||
if ver in self._supported_scheduler_versions:
|
||||
return ver
|
||||
else:
|
||||
raise Exception("Unsupported scheduler version")
|
||||
|
||||
def _loadScheduler(self) -> None:
|
||||
ver = self.schedVer()
|
||||
def _load_scheduler(self) -> None:
|
||||
ver = self.sched_ver()
|
||||
if ver == 1:
|
||||
self.sched = V1Scheduler(self)
|
||||
elif ver == 2:
|
||||
|
@ -164,17 +165,17 @@ class Collection:
|
|||
def upgrade_to_v2_scheduler(self) -> None:
|
||||
self._backend.upgrade_scheduler()
|
||||
self.clear_python_undo()
|
||||
self._loadScheduler()
|
||||
self._load_scheduler()
|
||||
|
||||
def v3_scheduler(self) -> bool:
|
||||
return self.get_config_bool(Config.Bool.SCHED_2021)
|
||||
|
||||
def set_v3_scheduler(self, enabled: bool) -> None:
|
||||
if self.v3_scheduler() != enabled:
|
||||
if enabled and self.schedVer() != 2:
|
||||
if enabled and self.sched_ver() != 2:
|
||||
raise Exception("must upgrade to v2 scheduler first")
|
||||
self.set_config_bool(Config.Bool.SCHED_2021, enabled)
|
||||
self._loadScheduler()
|
||||
self._load_scheduler()
|
||||
|
||||
# DB-related
|
||||
##########################################################################
|
||||
|
@ -193,14 +194,6 @@ class Collection:
|
|||
def mod(self) -> int:
|
||||
return self.db.scalar("select mod from col")
|
||||
|
||||
# legacy
|
||||
def setMod(self) -> None:
|
||||
# this is now a no-op, as modifications to things like the config
|
||||
# will mark the collection modified automatically
|
||||
pass
|
||||
|
||||
flush = setMod
|
||||
|
||||
def modified_by_backend(self) -> bool:
|
||||
# Until we can move away from long-running transactions, the Python
|
||||
# code needs to know if the transaction should be committed, so we need
|
||||
|
@ -242,7 +235,6 @@ class Collection:
|
|||
self._backend.close_collection(downgrade_to_schema11=downgrade)
|
||||
self.db = None
|
||||
self.media.close()
|
||||
self._closeLog()
|
||||
|
||||
def close_for_full_sync(self) -> None:
|
||||
# save and cleanup, but backend will take care of collection close
|
||||
|
@ -251,7 +243,6 @@ class Collection:
|
|||
self._clear_caches()
|
||||
self.db = None
|
||||
self.media.close()
|
||||
self._closeLog()
|
||||
|
||||
def rollback(self) -> None:
|
||||
self._clear_caches()
|
||||
|
@ -273,7 +264,7 @@ class Collection:
|
|||
log_path = ""
|
||||
should_log = not self.server and self._should_log
|
||||
if should_log:
|
||||
log_path = self.path.replace(".anki2", "2.log")
|
||||
log_path = self.path.replace(".anki2", ".log")
|
||||
|
||||
# connect
|
||||
if not after_full_sync:
|
||||
|
@ -288,17 +279,18 @@ class Collection:
|
|||
self.db = DBProxy(weakref.proxy(self._backend))
|
||||
self.db.begin()
|
||||
|
||||
self._openLog()
|
||||
|
||||
def modSchema(self, check: bool) -> None:
|
||||
"Mark schema modified. Call this first so user can abort if necessary."
|
||||
if not self.schemaChanged():
|
||||
if check and not hooks.schema_will_change(proceed=True):
|
||||
raise AbortSchemaModification()
|
||||
def set_schema_modified(self) -> None:
|
||||
self.db.execute("update col set scm=?", intTime(1000))
|
||||
self.save()
|
||||
|
||||
def schemaChanged(self) -> bool:
|
||||
def mod_schema(self, check: bool) -> None:
|
||||
"Mark schema modified. GUI catches this and will ask user if required."
|
||||
if not self.schema_changed():
|
||||
if check and not hooks.schema_will_change(proceed=True):
|
||||
raise AbortSchemaModification()
|
||||
self.set_schema_modified()
|
||||
|
||||
def schema_changed(self) -> bool:
|
||||
"True if schema changed since last sync."
|
||||
return self.db.scalar("select scm > ls from col")
|
||||
|
||||
|
@ -308,12 +300,6 @@ class Collection:
|
|||
else:
|
||||
return -1
|
||||
|
||||
def beforeUpload(self) -> None:
|
||||
"Called before a full upload."
|
||||
self.save(trx=False)
|
||||
self._backend.before_upload()
|
||||
self.close(save=False, downgrade=True)
|
||||
|
||||
# Object helpers
|
||||
##########################################################################
|
||||
|
||||
|
@ -341,7 +327,9 @@ class Collection:
|
|||
# Utils
|
||||
##########################################################################
|
||||
|
||||
def nextID(self, type: str, inc: bool = True) -> Any:
|
||||
def nextID( # pylint: disable=invalid-name
|
||||
self, type: str, inc: bool = True
|
||||
) -> Any:
|
||||
type = f"next{type.capitalize()}"
|
||||
id = self.conf.get(type, 1)
|
||||
if inc:
|
||||
|
@ -353,15 +341,6 @@ class Collection:
|
|||
self.autosave()
|
||||
self.sched.reset()
|
||||
|
||||
# Deletion logging
|
||||
##########################################################################
|
||||
|
||||
def _logRem(self, ids: List[Union[int, NoteId]], type: int) -> None:
|
||||
self.db.executemany(
|
||||
"insert into graves values (%d, ?, %d)" % (self.usn(), type),
|
||||
([x] for x in ids),
|
||||
)
|
||||
|
||||
# Notes
|
||||
##########################################################################
|
||||
|
||||
|
@ -419,32 +398,16 @@ class Collection:
|
|||
or None
|
||||
)
|
||||
|
||||
# legacy
|
||||
|
||||
def noteCount(self) -> int:
|
||||
def note_count(self) -> int:
|
||||
return self.db.scalar("select count() from notes")
|
||||
|
||||
def newNote(self, forDeck: bool = True) -> Note:
|
||||
"Return a new note with the current model."
|
||||
return Note(self, self.models.current(forDeck))
|
||||
|
||||
def addNote(self, note: Note) -> int:
|
||||
self.add_note(note, note.note_type()["did"])
|
||||
return len(note.cards())
|
||||
|
||||
def remNotes(self, ids: Sequence[NoteId]) -> None:
|
||||
self.remove_notes(ids)
|
||||
|
||||
def _remNotes(self, ids: List[NoteId]) -> None:
|
||||
pass
|
||||
|
||||
# Cards
|
||||
##########################################################################
|
||||
|
||||
def isEmpty(self) -> bool:
|
||||
def is_empty(self) -> bool:
|
||||
return not self.db.scalar("select 1 from cards limit 1")
|
||||
|
||||
def cardCount(self) -> Any:
|
||||
def card_count(self) -> Any:
|
||||
return self.db.scalar("select count() from cards")
|
||||
|
||||
def remove_cards_and_orphaned_notes(self, card_ids: Sequence[CardId]) -> None:
|
||||
|
@ -457,36 +420,17 @@ class Collection:
|
|||
def get_empty_cards(self) -> EmptyCardsReport:
|
||||
return self._backend.get_empty_cards()
|
||||
|
||||
# legacy
|
||||
|
||||
def remCards(self, ids: List[CardId], notes: bool = True) -> None:
|
||||
self.remove_cards_and_orphaned_notes(ids)
|
||||
|
||||
def emptyCids(self) -> List[CardId]:
|
||||
print("emptyCids() will go away")
|
||||
return []
|
||||
|
||||
# Card generation & field checksums/sort fields
|
||||
##########################################################################
|
||||
|
||||
def after_note_updates(
|
||||
self, nids: List[NoteId], mark_modified: bool, generate_cards: bool = True
|
||||
) -> None:
|
||||
"If notes modified directly in database, call this afterwards."
|
||||
self._backend.after_note_updates(
|
||||
nids=nids, generate_cards=generate_cards, mark_notes_modified=mark_modified
|
||||
)
|
||||
|
||||
# legacy
|
||||
|
||||
def updateFieldCache(self, nids: List[NoteId]) -> None:
|
||||
self.after_note_updates(nids, mark_modified=False, generate_cards=False)
|
||||
|
||||
# this also updates field cache
|
||||
def genCards(self, nids: List[NoteId]) -> List[int]:
|
||||
self.after_note_updates(nids, mark_modified=False, generate_cards=True)
|
||||
# previously returned empty cards, no longer does
|
||||
return []
|
||||
|
||||
# Finding cards
|
||||
##########################################################################
|
||||
|
||||
|
@ -591,21 +535,21 @@ class Collection:
|
|||
return self._backend.field_names_for_notes(nids)
|
||||
|
||||
# returns array of ("dupestr", [nids])
|
||||
def findDupes(self, fieldName: str, search: str = "") -> List[Tuple[str, list]]:
|
||||
def find_dupes(self, field_name: str, search: str = "") -> List[Tuple[str, list]]:
|
||||
nids = self.find_notes(
|
||||
self.build_search_string(search, SearchNode(field_name=fieldName))
|
||||
self.build_search_string(search, SearchNode(field_name=field_name))
|
||||
)
|
||||
# go through notes
|
||||
vals: Dict[str, List[int]] = {}
|
||||
dupes = []
|
||||
fields: Dict[int, int] = {}
|
||||
|
||||
def ordForMid(mid: NotetypeId) -> int:
|
||||
def ord_for_mid(mid: NotetypeId) -> int:
|
||||
if mid not in fields:
|
||||
model = self.models.get(mid)
|
||||
for c, f in enumerate(model["flds"]):
|
||||
if f["name"].lower() == fieldName.lower():
|
||||
fields[mid] = c
|
||||
for idx, field in enumerate(model["flds"]):
|
||||
if field["name"].lower() == field_name.lower():
|
||||
fields[mid] = idx
|
||||
break
|
||||
return fields[mid]
|
||||
|
||||
|
@ -613,7 +557,7 @@ class Collection:
|
|||
f"select id, mid, flds from notes where id in {ids2str(nids)}"
|
||||
):
|
||||
flds = splitFields(flds)
|
||||
ord = ordForMid(mid)
|
||||
ord = ord_for_mid(mid)
|
||||
if ord is None:
|
||||
continue
|
||||
val = flds[ord]
|
||||
|
@ -626,10 +570,6 @@ class Collection:
|
|||
dupes.append((val, vals[val]))
|
||||
return dupes
|
||||
|
||||
findCards = find_cards
|
||||
findNotes = find_notes
|
||||
findReplace = find_and_replace
|
||||
|
||||
# Search Strings
|
||||
##########################################################################
|
||||
|
||||
|
@ -880,35 +820,6 @@ table.review-log {{ {revlog_style} }}
|
|||
"Don't use this, it will likely go away in the future."
|
||||
return self._backend.congrats_info().SerializeToString()
|
||||
|
||||
# legacy
|
||||
|
||||
def cardStats(self, card: Card) -> str:
|
||||
return self.card_stats(card.id, include_revlog=False)
|
||||
|
||||
# Timeboxing
|
||||
##########################################################################
|
||||
# fixme: there doesn't seem to be a good reason why this code is in main.py
|
||||
# instead of covered in reviewer, and the reps tracking is covered by both
|
||||
# the scheduler and reviewer.py. in the future, we should probably move
|
||||
# reps tracking to reviewer.py, and remove the startTimebox() calls from
|
||||
# other locations like overview.py. We just need to make sure not to reset
|
||||
# the count on things like edits, which we probably could do by checking
|
||||
# the previous state in moveToState.
|
||||
|
||||
def startTimebox(self) -> None:
|
||||
self._startTime = time.time()
|
||||
self._startReps = self.sched.reps
|
||||
|
||||
def timeboxReached(self) -> Union[Literal[False], Tuple[Any, int]]:
|
||||
"Return (elapsedTime, reps) if timebox reached, or False."
|
||||
if not self.conf["timeLim"]:
|
||||
# timeboxing disabled
|
||||
return False
|
||||
elapsed = time.time() - self._startTime
|
||||
if elapsed > self.conf["timeLim"]:
|
||||
return (self.conf["timeLim"], self.sched.reps - self._startReps)
|
||||
return False
|
||||
|
||||
# Undo
|
||||
##########################################################################
|
||||
|
||||
|
@ -1070,10 +981,10 @@ table.review-log {{ {revlog_style} }}
|
|||
)
|
||||
|
||||
# update daily counts
|
||||
n = card.queue
|
||||
idx = card.queue
|
||||
if card.queue in (QUEUE_TYPE_DAY_LEARN_RELEARN, QUEUE_TYPE_PREVIEW):
|
||||
n = QUEUE_TYPE_LRN
|
||||
type = ("new", "lrn", "rev")[n]
|
||||
idx = QUEUE_TYPE_LRN
|
||||
type = ("new", "lrn", "rev")[idx]
|
||||
self.sched._updateStats(card, type, -1)
|
||||
self.sched.reps -= 1
|
||||
|
||||
|
@ -1082,20 +993,10 @@ table.review-log {{ {revlog_style} }}
|
|||
|
||||
return entry
|
||||
|
||||
# legacy
|
||||
|
||||
clearUndo = clear_python_undo
|
||||
markReview = save_card_review_undo_info
|
||||
|
||||
def undoName(self) -> Optional[str]:
|
||||
"Undo menu item name, or None if undo unavailable."
|
||||
status = self.undo_status()
|
||||
return status.undo or None
|
||||
|
||||
# DB maintenance
|
||||
##########################################################################
|
||||
|
||||
def fixIntegrity(self) -> Tuple[str, bool]:
|
||||
def fix_integrity(self) -> Tuple[str, bool]:
|
||||
"""Fix possible problems and rebuild caches.
|
||||
|
||||
Returns tuple of (error: str, ok: bool). 'ok' will be true if no
|
||||
|
@ -1106,8 +1007,8 @@ table.review-log {{ {revlog_style} }}
|
|||
problems = list(self._backend.check_database())
|
||||
ok = not problems
|
||||
problems.append(self.tr.database_check_rebuilt())
|
||||
except DBError as e:
|
||||
problems = [str(e.args[0])]
|
||||
except DBError as err:
|
||||
problems = [str(err.args[0])]
|
||||
ok = False
|
||||
finally:
|
||||
try:
|
||||
|
@ -1123,46 +1024,6 @@ table.review-log {{ {revlog_style} }}
|
|||
self.db.execute("analyze")
|
||||
self.db.begin()
|
||||
|
||||
# Logging
|
||||
##########################################################################
|
||||
|
||||
def log(self, *args: Any, **kwargs: Any) -> None:
|
||||
if not self._should_log:
|
||||
return
|
||||
|
||||
def customRepr(x: Any) -> str:
|
||||
if isinstance(x, str):
|
||||
return x
|
||||
return pprint.pformat(x)
|
||||
|
||||
path, num, fn, y = traceback.extract_stack(limit=2 + kwargs.get("stack", 0))[0]
|
||||
buf = "[%s] %s:%s(): %s" % (
|
||||
intTime(),
|
||||
os.path.basename(path),
|
||||
fn,
|
||||
", ".join([customRepr(x) for x in args]),
|
||||
)
|
||||
self._logHnd.write(f"{buf}\n")
|
||||
if devMode:
|
||||
print(buf)
|
||||
|
||||
def _openLog(self) -> None:
|
||||
if not self._should_log:
|
||||
return
|
||||
lpath = re.sub(r"\.anki2$", ".log", self.path)
|
||||
if os.path.exists(lpath) and os.path.getsize(lpath) > 10 * 1024 * 1024:
|
||||
lpath2 = f"{lpath}.old"
|
||||
if os.path.exists(lpath2):
|
||||
os.unlink(lpath2)
|
||||
os.rename(lpath, lpath2)
|
||||
self._logHnd = open(lpath, "a", encoding="utf8")
|
||||
|
||||
def _closeLog(self) -> None:
|
||||
if not self._should_log:
|
||||
return
|
||||
self._logHnd.close()
|
||||
self._logHnd = None
|
||||
|
||||
##########################################################################
|
||||
|
||||
def set_user_flag_for_cards(
|
||||
|
@ -1210,6 +1071,104 @@ table.review-log {{ {revlog_style} }}
|
|||
"Not intended for public consumption at this time."
|
||||
return self._backend.render_markdown(markdown=text, sanitize=sanitize)
|
||||
|
||||
# Timeboxing
|
||||
##########################################################################
|
||||
# fixme: there doesn't seem to be a good reason why this code is in main.py
|
||||
# instead of covered in reviewer, and the reps tracking is covered by both
|
||||
# the scheduler and reviewer.py. in the future, we should probably move
|
||||
# reps tracking to reviewer.py, and remove the startTimebox() calls from
|
||||
# other locations like overview.py. We just need to make sure not to reset
|
||||
# the count on things like edits, which we probably could do by checking
|
||||
# the previous state in moveToState.
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
|
||||
def startTimebox(self) -> None:
|
||||
self._startTime = time.time()
|
||||
self._startReps = self.sched.reps
|
||||
|
||||
def timeboxReached(self) -> Union[Literal[False], Tuple[Any, int]]:
|
||||
"Return (elapsedTime, reps) if timebox reached, or False."
|
||||
if not self.conf["timeLim"]:
|
||||
# timeboxing disabled
|
||||
return False
|
||||
elapsed = time.time() - self._startTime
|
||||
if elapsed > self.conf["timeLim"]:
|
||||
return (self.conf["timeLim"], self.sched.reps - self._startReps)
|
||||
return False
|
||||
|
||||
# Legacy
|
||||
##########################################################################
|
||||
|
||||
@deprecated(info="no longer used")
|
||||
def log(self, *args: Any, **kwargs: Any) -> None:
|
||||
print(args, kwargs)
|
||||
|
||||
@deprecated(replaced_by=undo_status)
|
||||
def undo_name(self) -> Optional[str]:
|
||||
"Undo menu item name, or None if undo unavailable."
|
||||
status = self.undo_status()
|
||||
return status.undo or None
|
||||
|
||||
# @deprecated(replaced_by=new_note)
|
||||
def newNote(self, forDeck: bool = True) -> Note:
|
||||
"Return a new note with the current model."
|
||||
return Note(self, self.models.current(forDeck))
|
||||
|
||||
# @deprecated(replaced_by=add_note)
|
||||
def addNote(self, note: Note) -> int:
|
||||
self.add_note(note, note.note_type()["did"])
|
||||
return len(note.cards())
|
||||
|
||||
@deprecated(replaced_by=remove_notes)
|
||||
def remNotes(self, ids: Sequence[NoteId]) -> None:
|
||||
self.remove_notes(ids)
|
||||
|
||||
@deprecated(replaced_by=remove_notes)
|
||||
def _remNotes(self, ids: List[NoteId]) -> None:
|
||||
pass
|
||||
|
||||
@deprecated(replaced_by=card_stats)
|
||||
def cardStats(self, card: Card) -> str:
|
||||
return self.card_stats(card.id, include_revlog=False)
|
||||
|
||||
@deprecated(replaced_by=after_note_updates)
|
||||
def updateFieldCache(self, nids: List[NoteId]) -> None:
|
||||
self.after_note_updates(nids, mark_modified=False, generate_cards=False)
|
||||
|
||||
@deprecated(replaced_by=after_note_updates)
|
||||
def genCards(self, nids: List[NoteId]) -> List[int]:
|
||||
self.after_note_updates(nids, mark_modified=False, generate_cards=True)
|
||||
# previously returned empty cards, no longer does
|
||||
return []
|
||||
|
||||
@deprecated(info="no longer used")
|
||||
def emptyCids(self) -> List[CardId]:
|
||||
return []
|
||||
|
||||
@deprecated(info="handled by backend")
|
||||
def _logRem(self, ids: List[Union[int, NoteId]], type: int) -> None:
|
||||
self.db.executemany(
|
||||
"insert into graves values (%d, ?, %d)" % (self.usn(), type),
|
||||
([x] for x in ids),
|
||||
)
|
||||
|
||||
@deprecated(info="no longer required")
|
||||
def setMod(self) -> None:
|
||||
pass
|
||||
|
||||
@deprecated(info="no longer required")
|
||||
def flush(self) -> None:
|
||||
pass
|
||||
|
||||
|
||||
Collection.register_deprecated_aliases(
|
||||
clearUndo=Collection.clear_python_undo,
|
||||
markReview=Collection.save_card_review_undo_info,
|
||||
findReplace=Collection.find_and_replace,
|
||||
remCards=Collection.remove_cards_and_orphaned_notes,
|
||||
)
|
||||
|
||||
|
||||
# legacy name
|
||||
_Collection = Collection
|
||||
|
|
|
@ -349,7 +349,7 @@ class DeckManager(DeprecatedNamesMixin):
|
|||
|
||||
def remove_config(self, id: DeckConfigId) -> None:
|
||||
"Remove a configuration and update all decks using it."
|
||||
self.col.modSchema(check=True)
|
||||
self.col.mod_schema(check=True)
|
||||
for deck in self.all():
|
||||
# ignore cram decks
|
||||
if "conf" not in deck:
|
||||
|
|
|
@ -197,7 +197,7 @@ class AnkiExporter(Exporter):
|
|||
|
||||
def exportInto(self, path: str) -> None:
|
||||
# sched info+v2 scheduler not compatible w/ older clients
|
||||
self._v2sched = self.col.schedVer() != 1 and self.includeSched
|
||||
self._v2sched = self.col.sched_ver() != 1 and self.includeSched
|
||||
|
||||
# create a new collection at the target
|
||||
try:
|
||||
|
@ -246,7 +246,7 @@ class AnkiExporter(Exporter):
|
|||
# need to reset card state
|
||||
self.dst.sched.resetCards(cids)
|
||||
# models - start with zero
|
||||
self.dst.modSchema(check=False)
|
||||
self.dst.mod_schema(check=False)
|
||||
self.dst.models.remove_all_notetypes()
|
||||
for m in self.src.models.all():
|
||||
if int(m["id"]) in mids:
|
||||
|
@ -298,7 +298,7 @@ class AnkiExporter(Exporter):
|
|||
self.mediaFiles = list(media.keys())
|
||||
self.dst.crt = self.src.crt
|
||||
# todo: tags?
|
||||
self.count = self.dst.cardCount()
|
||||
self.count = self.dst.card_count()
|
||||
self.postExport()
|
||||
self.dst.close(downgrade=True)
|
||||
|
||||
|
@ -426,8 +426,8 @@ class AnkiCollectionPackageExporter(AnkiPackageExporter):
|
|||
def doExport(self, z, path):
|
||||
"Export collection. Caller must re-open afterwards."
|
||||
# close our deck & write it into the zip file
|
||||
self.count = self.col.cardCount()
|
||||
v2 = self.col.schedVer() != 1
|
||||
self.count = self.col.card_count()
|
||||
v2 = self.col.sched_ver() != 1
|
||||
mdir = self.col.media.dir()
|
||||
self.col.close(downgrade=True)
|
||||
if not v2:
|
||||
|
|
|
@ -53,11 +53,11 @@ class Anki2Importer(Importer):
|
|||
self.dst = self.col
|
||||
self.src = Collection(self.file)
|
||||
|
||||
if not self._importing_v2 and self.col.schedVer() != 1:
|
||||
if not self._importing_v2 and self.col.sched_ver() != 1:
|
||||
# any scheduling included?
|
||||
if self.src.db.scalar("select 1 from cards where queue != 0 limit 1"):
|
||||
self.source_needs_upgrade = True
|
||||
elif self._importing_v2 and self.col.schedVer() == 1:
|
||||
elif self._importing_v2 and self.col.sched_ver() == 1:
|
||||
raise Exception("must upgrade to new scheduler to import this file")
|
||||
|
||||
def _import(self) -> None:
|
||||
|
@ -186,7 +186,7 @@ class Anki2Importer(Importer):
|
|||
self.dst.db.executemany(
|
||||
"insert or replace into notes values (?,?,?,?,?,?,?,?,?,?,?)", update
|
||||
)
|
||||
self.dst.updateFieldCache(dirty)
|
||||
self.dst.after_note_updates(dirty, mark_modified=False, generate_cards=False)
|
||||
|
||||
# determine if note is a duplicate, and adjust mid and/or guid as required
|
||||
# returns true if note should be added
|
||||
|
|
|
@ -400,7 +400,7 @@ and notes.mid = ? and cards.ord = ?""",
|
|||
cmap: Optional[Dict[int, Optional[int]]],
|
||||
) -> None:
|
||||
# - maps are ord->ord, and there should not be duplicate targets
|
||||
self.col.modSchema(check=True)
|
||||
self.col.mod_schema(check=True)
|
||||
assert fmap
|
||||
field_map = self._convert_legacy_map(fmap, len(newModel["flds"]))
|
||||
if (
|
||||
|
|
|
@ -46,7 +46,6 @@ class Scheduler(V2):
|
|||
self._haveQueues = False
|
||||
|
||||
def answerCard(self, card: Card, ease: int) -> None:
|
||||
self.col.log()
|
||||
assert 1 <= ease <= 4
|
||||
self.col.save_card_review_undo_info(card)
|
||||
if self._burySiblingsOnAnswer:
|
||||
|
|
|
@ -104,7 +104,6 @@ class Scheduler(SchedulerBaseWithLegacy):
|
|||
self.reset()
|
||||
card = self._getCard()
|
||||
if card:
|
||||
self.col.log(card)
|
||||
if not self._burySiblingsOnAnswer:
|
||||
self._burySiblings(card)
|
||||
card.start_timer()
|
||||
|
@ -452,7 +451,6 @@ limit ?"""
|
|||
##########################################################################
|
||||
|
||||
def answerCard(self, card: Card, ease: int) -> None:
|
||||
self.col.log()
|
||||
assert 1 <= ease <= 4
|
||||
assert 0 <= card.queue <= 4
|
||||
self.col.save_card_review_undo_info(card)
|
||||
|
|
|
@ -665,7 +665,7 @@ select count(), avg(ivl), max(ivl) from cards where did in %s and queue = {QUEUE
|
|||
[13, 3],
|
||||
[14, 4],
|
||||
]
|
||||
if self.col.schedVer() != 1:
|
||||
if self.col.sched_ver() != 1:
|
||||
ticks.insert(3, [4, 4])
|
||||
txt = self._title(
|
||||
"Answer Buttons", "The number of times you have pressed each button."
|
||||
|
@ -725,7 +725,7 @@ select count(), avg(ivl), max(ivl) from cards where did in %s and queue = {QUEUE
|
|||
lim = "where " + " and ".join(lims)
|
||||
else:
|
||||
lim = ""
|
||||
if self.col.schedVer() == 1:
|
||||
if self.col.sched_ver() == 1:
|
||||
ease4repl = "3"
|
||||
else:
|
||||
ease4repl = "ease"
|
||||
|
@ -815,7 +815,7 @@ order by thetype, ease"""
|
|||
lim = self._revlogLimit()
|
||||
if lim:
|
||||
lim = " and " + lim
|
||||
if self.col.schedVer() == 1:
|
||||
if self.col.sched_ver() == 1:
|
||||
sd = datetime.datetime.fromtimestamp(self.col.crt)
|
||||
rolloverHour = sd.hour
|
||||
else:
|
||||
|
|
|
@ -16,8 +16,8 @@ def test_delete():
|
|||
col.reset()
|
||||
col.sched.answerCard(col.sched.getCard(), 2)
|
||||
col.remove_cards_and_orphaned_notes([cid])
|
||||
assert col.cardCount() == 0
|
||||
assert col.noteCount() == 0
|
||||
assert col.card_count() == 0
|
||||
assert col.note_count() == 0
|
||||
assert col.db.scalar("select count() from notes") == 0
|
||||
assert col.db.scalar("select count() from cards") == 0
|
||||
assert col.db.scalar("select count() from graves") == 2
|
||||
|
@ -72,7 +72,7 @@ def test_gendeck():
|
|||
note = col.new_note(cloze)
|
||||
note["Text"] = "{{c1::one}}"
|
||||
col.addNote(note)
|
||||
assert col.cardCount() == 1
|
||||
assert col.card_count() == 1
|
||||
assert note.cards()[0].did == 1
|
||||
# set the model to a new default col
|
||||
newId = col.decks.id("new")
|
||||
|
|
|
@ -62,14 +62,14 @@ def test_noteAddDelete():
|
|||
t["afmt"] = "{{Front}}"
|
||||
mm.add_template(m, t)
|
||||
mm.save(m)
|
||||
assert col.cardCount() == 2
|
||||
assert col.card_count() == 2
|
||||
# creating new notes should use both cards
|
||||
note = col.newNote()
|
||||
note["Front"] = "three"
|
||||
note["Back"] = "four"
|
||||
n = col.addNote(note)
|
||||
assert n == 2
|
||||
assert col.cardCount() == 4
|
||||
assert col.card_count() == 4
|
||||
# check q/a generation
|
||||
c0 = note.cards()[0]
|
||||
assert "three" in c0.question()
|
||||
|
|
|
@ -57,9 +57,9 @@ def test_remove():
|
|||
col.addNote(note)
|
||||
c = note.cards()[0]
|
||||
assert c.did == deck1
|
||||
assert col.cardCount() == 1
|
||||
assert col.card_count() == 1
|
||||
col.decks.remove([deck1])
|
||||
assert col.cardCount() == 0
|
||||
assert col.card_count() == 0
|
||||
# if we try to get it, we get the default
|
||||
assert col.decks.name(c.did) == "[no deck]"
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@ def test_export_anki():
|
|||
assert conf["id"] != 1
|
||||
# connect to new deck
|
||||
col2 = aopen(newname)
|
||||
assert col2.cardCount() == 2
|
||||
assert col2.card_count() == 2
|
||||
# as scheduling was reset, should also revert decks to default conf
|
||||
did = col2.decks.id("test", create=False)
|
||||
assert did
|
||||
|
@ -82,7 +82,7 @@ def test_export_anki():
|
|||
e.did = 1
|
||||
e.exportInto(newname)
|
||||
col2 = aopen(newname)
|
||||
assert col2.cardCount() == 1
|
||||
assert col2.card_count() == 1
|
||||
|
||||
|
||||
def test_export_ankipkg():
|
||||
|
@ -109,7 +109,6 @@ def test_export_anki_due():
|
|||
note["Front"] = "foo"
|
||||
col.addNote(note)
|
||||
col.crt -= 86400 * 10
|
||||
col.flush()
|
||||
col.sched.reset()
|
||||
c = col.sched.getCard()
|
||||
col.sched.answerCard(c, 3)
|
||||
|
|
|
@ -14,7 +14,7 @@ class DummyCollection:
|
|||
return None
|
||||
|
||||
|
||||
def test_findCards():
|
||||
def test_find_cards():
|
||||
col = getEmptyCol()
|
||||
note = col.newNote()
|
||||
note["Front"] = "dog"
|
||||
|
@ -49,84 +49,80 @@ def test_findCards():
|
|||
col.save()
|
||||
latestCardIds = [c.id for c in note.cards()]
|
||||
# tag searches
|
||||
assert len(col.findCards("tag:*")) == 5
|
||||
assert len(col.findCards("tag:\\*")) == 1
|
||||
assert len(col.findCards("tag:%")) == 1
|
||||
assert len(col.findCards("tag:sheep_goat")) == 0
|
||||
assert len(col.findCards('"tag:sheep goat"')) == 0
|
||||
assert len(col.findCards('"tag:* *"')) == 0
|
||||
assert len(col.findCards("tag:animal_1")) == 2
|
||||
assert len(col.findCards("tag:animal\\_1")) == 1
|
||||
assert not col.findCards("tag:donkey")
|
||||
assert len(col.findCards("tag:sheep")) == 1
|
||||
assert len(col.findCards("tag:sheep tag:goat")) == 1
|
||||
assert len(col.findCards("tag:sheep tag:monkey")) == 0
|
||||
assert len(col.findCards("tag:monkey")) == 1
|
||||
assert len(col.findCards("tag:sheep -tag:monkey")) == 1
|
||||
assert len(col.findCards("-tag:sheep")) == 4
|
||||
assert len(col.find_cards("tag:*")) == 5
|
||||
assert len(col.find_cards("tag:\\*")) == 1
|
||||
assert len(col.find_cards("tag:%")) == 1
|
||||
assert len(col.find_cards("tag:sheep_goat")) == 0
|
||||
assert len(col.find_cards('"tag:sheep goat"')) == 0
|
||||
assert len(col.find_cards('"tag:* *"')) == 0
|
||||
assert len(col.find_cards("tag:animal_1")) == 2
|
||||
assert len(col.find_cards("tag:animal\\_1")) == 1
|
||||
assert not col.find_cards("tag:donkey")
|
||||
assert len(col.find_cards("tag:sheep")) == 1
|
||||
assert len(col.find_cards("tag:sheep tag:goat")) == 1
|
||||
assert len(col.find_cards("tag:sheep tag:monkey")) == 0
|
||||
assert len(col.find_cards("tag:monkey")) == 1
|
||||
assert len(col.find_cards("tag:sheep -tag:monkey")) == 1
|
||||
assert len(col.find_cards("-tag:sheep")) == 4
|
||||
col.tags.bulk_add(col.db.list("select id from notes"), "foo bar")
|
||||
assert len(col.findCards("tag:foo")) == len(col.findCards("tag:bar")) == 5
|
||||
assert len(col.find_cards("tag:foo")) == len(col.find_cards("tag:bar")) == 5
|
||||
col.tags.bulkRem(col.db.list("select id from notes"), "foo")
|
||||
assert len(col.findCards("tag:foo")) == 0
|
||||
assert len(col.findCards("tag:bar")) == 5
|
||||
assert len(col.find_cards("tag:foo")) == 0
|
||||
assert len(col.find_cards("tag:bar")) == 5
|
||||
# text searches
|
||||
assert len(col.findCards("cat")) == 2
|
||||
assert len(col.findCards("cat -dog")) == 1
|
||||
assert len(col.findCards("cat -dog")) == 1
|
||||
assert len(col.findCards("are goats")) == 1
|
||||
assert len(col.findCards('"are goats"')) == 0
|
||||
assert len(col.findCards('"goats are"')) == 1
|
||||
assert len(col.find_cards("cat")) == 2
|
||||
assert len(col.find_cards("cat -dog")) == 1
|
||||
assert len(col.find_cards("cat -dog")) == 1
|
||||
assert len(col.find_cards("are goats")) == 1
|
||||
assert len(col.find_cards('"are goats"')) == 0
|
||||
assert len(col.find_cards('"goats are"')) == 1
|
||||
# card states
|
||||
c = note.cards()[0]
|
||||
c.queue = c.type = CARD_TYPE_REV
|
||||
assert col.findCards("is:review") == []
|
||||
assert col.find_cards("is:review") == []
|
||||
c.flush()
|
||||
assert col.findCards("is:review") == [c.id]
|
||||
assert col.findCards("is:due") == []
|
||||
assert col.find_cards("is:review") == [c.id]
|
||||
assert col.find_cards("is:due") == []
|
||||
c.due = 0
|
||||
c.queue = QUEUE_TYPE_REV
|
||||
c.flush()
|
||||
assert col.findCards("is:due") == [c.id]
|
||||
assert len(col.findCards("-is:due")) == 4
|
||||
assert col.find_cards("is:due") == [c.id]
|
||||
assert len(col.find_cards("-is:due")) == 4
|
||||
c.queue = QUEUE_TYPE_SUSPENDED
|
||||
# ensure this card gets a later mod time
|
||||
c.flush()
|
||||
col.db.execute("update cards set mod = mod + 1 where id = ?", c.id)
|
||||
assert col.findCards("is:suspended") == [c.id]
|
||||
assert col.find_cards("is:suspended") == [c.id]
|
||||
# nids
|
||||
assert col.findCards("nid:54321") == []
|
||||
assert len(col.findCards(f"nid:{note.id}")) == 2
|
||||
assert len(col.findCards(f"nid:{n1id},{n2id}")) == 2
|
||||
assert col.find_cards("nid:54321") == []
|
||||
assert len(col.find_cards(f"nid:{note.id}")) == 2
|
||||
assert len(col.find_cards(f"nid:{n1id},{n2id}")) == 2
|
||||
# templates
|
||||
assert len(col.findCards("card:foo")) == 0
|
||||
assert len(col.findCards('"card:card 1"')) == 4
|
||||
assert len(col.findCards("card:reverse")) == 1
|
||||
assert len(col.findCards("card:1")) == 4
|
||||
assert len(col.findCards("card:2")) == 1
|
||||
assert len(col.find_cards("card:foo")) == 0
|
||||
assert len(col.find_cards('"card:card 1"')) == 4
|
||||
assert len(col.find_cards("card:reverse")) == 1
|
||||
assert len(col.find_cards("card:1")) == 4
|
||||
assert len(col.find_cards("card:2")) == 1
|
||||
# fields
|
||||
assert len(col.findCards("front:dog")) == 1
|
||||
assert len(col.findCards("-front:dog")) == 4
|
||||
assert len(col.findCards("front:sheep")) == 0
|
||||
assert len(col.findCards("back:sheep")) == 2
|
||||
assert len(col.findCards("-back:sheep")) == 3
|
||||
assert len(col.findCards("front:do")) == 0
|
||||
assert len(col.findCards("front:*")) == 5
|
||||
assert len(col.find_cards("front:dog")) == 1
|
||||
assert len(col.find_cards("-front:dog")) == 4
|
||||
assert len(col.find_cards("front:sheep")) == 0
|
||||
assert len(col.find_cards("back:sheep")) == 2
|
||||
assert len(col.find_cards("-back:sheep")) == 3
|
||||
assert len(col.find_cards("front:do")) == 0
|
||||
assert len(col.find_cards("front:*")) == 5
|
||||
# ordering
|
||||
col.conf["sortType"] = "noteCrt"
|
||||
col.flush()
|
||||
assert col.findCards("front:*", order=True)[-1] in latestCardIds
|
||||
assert col.findCards("", order=True)[-1] in latestCardIds
|
||||
assert col.find_cards("front:*", order=True)[-1] in latestCardIds
|
||||
assert col.find_cards("", order=True)[-1] in latestCardIds
|
||||
col.conf["sortType"] = "noteFld"
|
||||
col.flush()
|
||||
assert col.findCards("", order=True)[0] == catCard.id
|
||||
assert col.findCards("", order=True)[-1] in latestCardIds
|
||||
assert col.find_cards("", order=True)[0] == catCard.id
|
||||
assert col.find_cards("", order=True)[-1] in latestCardIds
|
||||
col.conf["sortType"] = "cardMod"
|
||||
col.flush()
|
||||
assert col.findCards("", order=True)[-1] in latestCardIds
|
||||
assert col.findCards("", order=True)[0] == firstCardId
|
||||
assert col.find_cards("", order=True)[-1] in latestCardIds
|
||||
assert col.find_cards("", order=True)[0] == firstCardId
|
||||
col.set_config_bool(Config.Bool.BROWSER_SORT_BACKWARDS, True)
|
||||
col.flush()
|
||||
assert col.findCards("", order=True)[0] in latestCardIds
|
||||
assert col.find_cards("", order=True)[0] in latestCardIds
|
||||
assert (
|
||||
col.find_cards("", order=col.get_browser_column("cardDue"), reverse=False)[0]
|
||||
== firstCardId
|
||||
|
@ -136,42 +132,42 @@ def test_findCards():
|
|||
!= firstCardId
|
||||
)
|
||||
# model
|
||||
assert len(col.findCards("note:basic")) == 3
|
||||
assert len(col.findCards("-note:basic")) == 2
|
||||
assert len(col.findCards("-note:foo")) == 5
|
||||
assert len(col.find_cards("note:basic")) == 3
|
||||
assert len(col.find_cards("-note:basic")) == 2
|
||||
assert len(col.find_cards("-note:foo")) == 5
|
||||
# col
|
||||
assert len(col.findCards("deck:default")) == 5
|
||||
assert len(col.findCards("-deck:default")) == 0
|
||||
assert len(col.findCards("-deck:foo")) == 5
|
||||
assert len(col.findCards("deck:def*")) == 5
|
||||
assert len(col.findCards("deck:*EFAULT")) == 5
|
||||
assert len(col.findCards("deck:*cefault")) == 0
|
||||
assert len(col.find_cards("deck:default")) == 5
|
||||
assert len(col.find_cards("-deck:default")) == 0
|
||||
assert len(col.find_cards("-deck:foo")) == 5
|
||||
assert len(col.find_cards("deck:def*")) == 5
|
||||
assert len(col.find_cards("deck:*EFAULT")) == 5
|
||||
assert len(col.find_cards("deck:*cefault")) == 0
|
||||
# full search
|
||||
note = col.newNote()
|
||||
note["Front"] = "hello<b>world</b>"
|
||||
note["Back"] = "abc"
|
||||
col.addNote(note)
|
||||
# as it's the sort field, it matches
|
||||
assert len(col.findCards("helloworld")) == 2
|
||||
# assert len(col.findCards("helloworld", full=True)) == 2
|
||||
assert len(col.find_cards("helloworld")) == 2
|
||||
# assert len(col.find_cards("helloworld", full=True)) == 2
|
||||
# if we put it on the back, it won't
|
||||
(note["Front"], note["Back"]) = (note["Back"], note["Front"])
|
||||
note.flush()
|
||||
assert len(col.findCards("helloworld")) == 0
|
||||
# assert len(col.findCards("helloworld", full=True)) == 2
|
||||
# assert len(col.findCards("back:helloworld", full=True)) == 2
|
||||
assert len(col.find_cards("helloworld")) == 0
|
||||
# assert len(col.find_cards("helloworld", full=True)) == 2
|
||||
# assert len(col.find_cards("back:helloworld", full=True)) == 2
|
||||
# searching for an invalid special tag should not error
|
||||
with pytest.raises(Exception):
|
||||
len(col.findCards("is:invalid"))
|
||||
len(col.find_cards("is:invalid"))
|
||||
# should be able to limit to parent col, no children
|
||||
id = col.db.scalar("select id from cards limit 1")
|
||||
col.db.execute(
|
||||
"update cards set did = ? where id = ?", col.decks.id("Default::Child"), id
|
||||
)
|
||||
col.save()
|
||||
assert len(col.findCards("deck:default")) == 7
|
||||
assert len(col.findCards("deck:default::child")) == 1
|
||||
assert len(col.findCards("deck:default -deck:default::*")) == 6
|
||||
assert len(col.find_cards("deck:default")) == 7
|
||||
assert len(col.find_cards("deck:default::child")) == 1
|
||||
assert len(col.find_cards("deck:default -deck:default::*")) == 6
|
||||
# properties
|
||||
id = col.db.scalar("select id from cards limit 1")
|
||||
col.db.execute(
|
||||
|
@ -179,61 +175,61 @@ def test_findCards():
|
|||
"where id = ?",
|
||||
id,
|
||||
)
|
||||
assert len(col.findCards("prop:ivl>5")) == 1
|
||||
assert len(col.findCards("prop:ivl<5")) > 1
|
||||
assert len(col.findCards("prop:ivl>=5")) == 1
|
||||
assert len(col.findCards("prop:ivl=9")) == 0
|
||||
assert len(col.findCards("prop:ivl=10")) == 1
|
||||
assert len(col.findCards("prop:ivl!=10")) > 1
|
||||
assert len(col.findCards("prop:due>0")) == 1
|
||||
assert len(col.find_cards("prop:ivl>5")) == 1
|
||||
assert len(col.find_cards("prop:ivl<5")) > 1
|
||||
assert len(col.find_cards("prop:ivl>=5")) == 1
|
||||
assert len(col.find_cards("prop:ivl=9")) == 0
|
||||
assert len(col.find_cards("prop:ivl=10")) == 1
|
||||
assert len(col.find_cards("prop:ivl!=10")) > 1
|
||||
assert len(col.find_cards("prop:due>0")) == 1
|
||||
# due dates should work
|
||||
assert len(col.findCards("prop:due=29")) == 0
|
||||
assert len(col.findCards("prop:due=30")) == 1
|
||||
assert len(col.find_cards("prop:due=29")) == 0
|
||||
assert len(col.find_cards("prop:due=30")) == 1
|
||||
# ease factors
|
||||
assert len(col.findCards("prop:ease=2.3")) == 0
|
||||
assert len(col.findCards("prop:ease=2.2")) == 1
|
||||
assert len(col.findCards("prop:ease>2")) == 1
|
||||
assert len(col.findCards("-prop:ease>2")) > 1
|
||||
assert len(col.find_cards("prop:ease=2.3")) == 0
|
||||
assert len(col.find_cards("prop:ease=2.2")) == 1
|
||||
assert len(col.find_cards("prop:ease>2")) == 1
|
||||
assert len(col.find_cards("-prop:ease>2")) > 1
|
||||
# recently failed
|
||||
if not isNearCutoff():
|
||||
# rated
|
||||
assert len(col.findCards("rated:1:1")) == 0
|
||||
assert len(col.findCards("rated:1:2")) == 0
|
||||
assert len(col.find_cards("rated:1:1")) == 0
|
||||
assert len(col.find_cards("rated:1:2")) == 0
|
||||
c = col.sched.getCard()
|
||||
col.sched.answerCard(c, 2)
|
||||
assert len(col.findCards("rated:1:1")) == 0
|
||||
assert len(col.findCards("rated:1:2")) == 1
|
||||
assert len(col.find_cards("rated:1:1")) == 0
|
||||
assert len(col.find_cards("rated:1:2")) == 1
|
||||
c = col.sched.getCard()
|
||||
col.sched.answerCard(c, 1)
|
||||
assert len(col.findCards("rated:1:1")) == 1
|
||||
assert len(col.findCards("rated:1:2")) == 1
|
||||
assert len(col.findCards("rated:1")) == 2
|
||||
assert len(col.findCards("rated:2:2")) == 1
|
||||
assert len(col.findCards("rated:0")) == len(col.findCards("rated:1"))
|
||||
assert len(col.find_cards("rated:1:1")) == 1
|
||||
assert len(col.find_cards("rated:1:2")) == 1
|
||||
assert len(col.find_cards("rated:1")) == 2
|
||||
assert len(col.find_cards("rated:2:2")) == 1
|
||||
assert len(col.find_cards("rated:0")) == len(col.find_cards("rated:1"))
|
||||
|
||||
# added
|
||||
col.db.execute("update cards set id = id - 86400*1000 where id = ?", id)
|
||||
assert len(col.findCards("added:1")) == col.cardCount() - 1
|
||||
assert len(col.findCards("added:2")) == col.cardCount()
|
||||
assert len(col.findCards("added:0")) == len(col.findCards("added:1"))
|
||||
assert len(col.find_cards("added:1")) == col.card_count() - 1
|
||||
assert len(col.find_cards("added:2")) == col.card_count()
|
||||
assert len(col.find_cards("added:0")) == len(col.find_cards("added:1"))
|
||||
else:
|
||||
print("some find tests disabled near cutoff")
|
||||
# empty field
|
||||
assert len(col.findCards("front:")) == 0
|
||||
assert len(col.find_cards("front:")) == 0
|
||||
note = col.newNote()
|
||||
note["Front"] = ""
|
||||
note["Back"] = "abc2"
|
||||
assert col.addNote(note) == 1
|
||||
assert len(col.findCards("front:")) == 1
|
||||
assert len(col.find_cards("front:")) == 1
|
||||
# OR searches and nesting
|
||||
assert len(col.findCards("tag:monkey or tag:sheep")) == 2
|
||||
assert len(col.findCards("(tag:monkey OR tag:sheep)")) == 2
|
||||
assert len(col.findCards("-(tag:monkey OR tag:sheep)")) == 6
|
||||
assert len(col.findCards("tag:monkey or (tag:sheep sheep)")) == 2
|
||||
assert len(col.findCards("tag:monkey or (tag:sheep octopus)")) == 1
|
||||
assert len(col.find_cards("tag:monkey or tag:sheep")) == 2
|
||||
assert len(col.find_cards("(tag:monkey OR tag:sheep)")) == 2
|
||||
assert len(col.find_cards("-(tag:monkey OR tag:sheep)")) == 6
|
||||
assert len(col.find_cards("tag:monkey or (tag:sheep sheep)")) == 2
|
||||
assert len(col.find_cards("tag:monkey or (tag:sheep octopus)")) == 1
|
||||
# flag
|
||||
with pytest.raises(Exception):
|
||||
col.findCards("flag:12")
|
||||
col.find_cards("flag:12")
|
||||
|
||||
|
||||
def test_findReplace():
|
||||
|
@ -304,15 +300,15 @@ def test_findDupes():
|
|||
note4["Front"] = "quuux"
|
||||
note4["Back"] = "nope"
|
||||
col.addNote(note4)
|
||||
r = col.findDupes("Back")
|
||||
r = col.find_dupes("Back")
|
||||
assert r[0][0] == "bar"
|
||||
assert len(r[0][1]) == 3
|
||||
# valid search
|
||||
r = col.findDupes("Back", "bar")
|
||||
r = col.find_dupes("Back", "bar")
|
||||
assert r[0][0] == "bar"
|
||||
assert len(r[0][1]) == 3
|
||||
# excludes everything
|
||||
r = col.findDupes("Back", "invalid")
|
||||
r = col.find_dupes("Back", "invalid")
|
||||
assert not r
|
||||
# front isn't dupe
|
||||
assert col.findDupes("Front") == []
|
||||
assert col.find_dupes("Front") == []
|
||||
|
|
|
@ -17,16 +17,16 @@ def test_flags():
|
|||
c.flush()
|
||||
# no flags to start with
|
||||
assert c.user_flag() == 0
|
||||
assert len(col.findCards("flag:0")) == 1
|
||||
assert len(col.findCards("flag:1")) == 0
|
||||
assert len(col.find_cards("flag:0")) == 1
|
||||
assert len(col.find_cards("flag:1")) == 0
|
||||
# set flag 2
|
||||
col.set_user_flag_for_cards(2, [c.id])
|
||||
c.load()
|
||||
assert c.user_flag() == 2
|
||||
assert c.flags & origBits == origBits
|
||||
assert len(col.findCards("flag:0")) == 0
|
||||
assert len(col.findCards("flag:2")) == 1
|
||||
assert len(col.findCards("flag:3")) == 0
|
||||
assert len(col.find_cards("flag:0")) == 0
|
||||
assert len(col.find_cards("flag:2")) == 1
|
||||
assert len(col.find_cards("flag:3")) == 0
|
||||
# change to 3
|
||||
col.set_user_flag_for_cards(3, [c.id])
|
||||
c.load()
|
||||
|
|
|
@ -115,9 +115,9 @@ def test_anki2_diffmodel_templates():
|
|||
imp.dupeOnSchemaChange = True
|
||||
imp.run()
|
||||
# collection should contain the note we imported
|
||||
assert dst.noteCount() == 1
|
||||
assert dst.note_count() == 1
|
||||
# the front template should contain the text added in the 2nd package
|
||||
tcid = dst.findCards("")[0] # only 1 note in collection
|
||||
tcid = dst.find_cards("")[0] # only 1 note in collection
|
||||
tnote = dst.getCard(tcid).note()
|
||||
assert "Changed Front Template" in tnote.cards()[0].template()["qfmt"]
|
||||
|
||||
|
@ -138,7 +138,7 @@ def test_anki2_updates():
|
|||
assert imp.added == 0
|
||||
assert imp.updated == 0
|
||||
# importing a newer note should update
|
||||
assert dst.noteCount() == 1
|
||||
assert dst.note_count() == 1
|
||||
assert dst.db.scalar("select flds from notes").startswith("hello")
|
||||
col = getUpgradeDeckPath("update2.apkg")
|
||||
imp = AnkiPackageImporter(dst, col)
|
||||
|
@ -146,7 +146,7 @@ def test_anki2_updates():
|
|||
assert imp.dupes == 0
|
||||
assert imp.added == 0
|
||||
assert imp.updated == 1
|
||||
assert dst.noteCount() == 1
|
||||
assert dst.note_count() == 1
|
||||
assert dst.db.scalar("select flds from notes").startswith("goodbye")
|
||||
|
||||
|
||||
|
@ -176,12 +176,12 @@ def test_csv():
|
|||
i.run()
|
||||
assert i.total == 0
|
||||
# and if dupes mode, will reimport everything
|
||||
assert col.cardCount() == 5
|
||||
assert col.card_count() == 5
|
||||
i.importMode = 2
|
||||
i.run()
|
||||
# includes repeated field
|
||||
assert i.total == 6
|
||||
assert col.cardCount() == 11
|
||||
assert col.card_count() == 11
|
||||
col.close()
|
||||
|
||||
|
||||
|
@ -330,7 +330,7 @@ def test_mnemo():
|
|||
file = str(os.path.join(testDir, "support", "mnemo.db"))
|
||||
i = MnemosyneImporter(col, file)
|
||||
i.run()
|
||||
assert col.cardCount() == 7
|
||||
assert col.card_count() == 7
|
||||
assert "a_longer_tag" in col.tags.all()
|
||||
assert col.db.scalar(f"select count() from cards where type = {CARD_TYPE_NEW}") == 1
|
||||
col.close()
|
||||
|
|
|
@ -16,9 +16,9 @@ def test_modelDelete():
|
|||
note["Front"] = "1"
|
||||
note["Back"] = "2"
|
||||
col.addNote(note)
|
||||
assert col.cardCount() == 1
|
||||
assert col.card_count() == 1
|
||||
col.models.remove(col.models.current()["id"])
|
||||
assert col.cardCount() == 0
|
||||
assert col.card_count() == 0
|
||||
|
||||
|
||||
def test_modelCopy():
|
||||
|
@ -95,7 +95,7 @@ def test_templates():
|
|||
note["Front"] = "1"
|
||||
note["Back"] = "2"
|
||||
col.addNote(note)
|
||||
assert col.cardCount() == 2
|
||||
assert col.card_count() == 2
|
||||
(c, c2) = note.cards()
|
||||
# first card should have first ord
|
||||
assert c.ord == 0
|
||||
|
@ -110,7 +110,7 @@ def test_templates():
|
|||
# removing a template should delete its cards
|
||||
col.models.remove_template(m, m["tmpls"][0])
|
||||
col.models.update(m)
|
||||
assert col.cardCount() == 1
|
||||
assert col.card_count() == 1
|
||||
# and should have updated the other cards' ordinals
|
||||
c = note.cards()[0]
|
||||
assert c.ord == 0
|
||||
|
@ -146,7 +146,7 @@ def test_cloze_ordinals():
|
|||
note = col.newNote()
|
||||
note["Text"] = "{{c1::firstQ::firstA}}{{c2::secondQ::secondA}}"
|
||||
col.addNote(note)
|
||||
assert col.cardCount() == 2
|
||||
assert col.card_count() == 2
|
||||
(c, c2) = note.cards()
|
||||
# first card should have first ord
|
||||
assert c.ord == 0
|
||||
|
@ -202,10 +202,10 @@ def test_cloze():
|
|||
note.cards()[0].answer()
|
||||
)
|
||||
# if we add another cloze, a card should be generated
|
||||
cnt = col.cardCount()
|
||||
cnt = col.card_count()
|
||||
note["Text"] = "{{c2::hello}} {{c1::foo}}"
|
||||
note.flush()
|
||||
assert col.cardCount() == cnt + 1
|
||||
assert col.card_count() == cnt + 1
|
||||
# 0 or negative indices are not supported
|
||||
note["Text"] += "{{c0::zero}} {{c-1:foo}}"
|
||||
note.flush()
|
||||
|
|
|
@ -15,7 +15,7 @@ def getEmptyCol() -> Collection:
|
|||
col = getEmptyColOrig()
|
||||
# only safe in test environment
|
||||
col.set_config("schedVer", 1)
|
||||
col._loadScheduler()
|
||||
col._load_scheduler()
|
||||
return col
|
||||
|
||||
|
||||
|
@ -820,7 +820,7 @@ def test_ordcycle():
|
|||
note["Front"] = "1"
|
||||
note["Back"] = "1"
|
||||
col.addNote(note)
|
||||
assert col.cardCount() == 3
|
||||
assert col.card_count() == 3
|
||||
col.reset()
|
||||
# ordinals should arrive in order
|
||||
assert col.sched.getCard().ord == 0
|
||||
|
|
|
@ -894,7 +894,7 @@ def test_ordcycle():
|
|||
note["Front"] = "1"
|
||||
note["Back"] = "1"
|
||||
col.addNote(note)
|
||||
assert col.cardCount() == 3
|
||||
assert col.card_count() == 3
|
||||
|
||||
conf = col.decks.get_config(1)
|
||||
conf["new"]["bury"] = False
|
||||
|
|
|
@ -14,12 +14,12 @@ def test_stats():
|
|||
col.addNote(note)
|
||||
c = note.cards()[0]
|
||||
# card stats
|
||||
assert col.cardStats(c)
|
||||
assert col.card_stats(c.id, include_revlog=True)
|
||||
col.reset()
|
||||
c = col.sched.getCard()
|
||||
col.sched.answerCard(c, 3)
|
||||
col.sched.answerCard(c, 2)
|
||||
assert col.cardStats(c)
|
||||
assert col.card_stats(c.id, include_revlog=True)
|
||||
|
||||
|
||||
def test_graphs_empty():
|
||||
|
|
|
@ -16,33 +16,33 @@ def getEmptyCol():
|
|||
def test_op():
|
||||
col = getEmptyCol()
|
||||
# should have no undo by default
|
||||
assert not col.undoName()
|
||||
assert not col.undo_status().undo
|
||||
# let's adjust a study option
|
||||
col.save("studyopts")
|
||||
col.conf["abc"] = 5
|
||||
# it should be listed as undoable
|
||||
assert col.undoName() == "studyopts"
|
||||
assert col.undo_status().undo == "studyopts"
|
||||
# with about 5 minutes until it's clobbered
|
||||
assert time.time() - col._last_checkpoint_at < 1
|
||||
# undoing should restore the old value
|
||||
col.undo_legacy()
|
||||
assert not col.undoName()
|
||||
assert not col.undo_status().undo
|
||||
assert "abc" not in col.conf
|
||||
# an (auto)save will clear the undo
|
||||
col.save("foo")
|
||||
assert col.undoName() == "foo"
|
||||
assert col.undo_status().undo == "foo"
|
||||
col.save()
|
||||
assert not col.undoName()
|
||||
assert not col.undo_status().undo
|
||||
# and a review will, too
|
||||
col.save("add")
|
||||
note = col.newNote()
|
||||
note["Front"] = "one"
|
||||
col.addNote(note)
|
||||
col.reset()
|
||||
assert "add" in col.undoName().lower()
|
||||
assert "add" in col.undo_status().undo.lower()
|
||||
c = col.sched.getCard()
|
||||
col.sched.answerCard(c, 2)
|
||||
assert col.undoName() == "Review"
|
||||
assert col.undo_status().undo == "Review"
|
||||
|
||||
|
||||
def test_review():
|
||||
|
@ -64,14 +64,14 @@ def test_review():
|
|||
assert col.sched.counts() == (1, 1, 0)
|
||||
assert c.queue == QUEUE_TYPE_LRN
|
||||
# undo
|
||||
assert col.undoName()
|
||||
assert col.undo_status().undo
|
||||
col.undo_legacy()
|
||||
col.reset()
|
||||
assert col.sched.counts() == (2, 0, 0)
|
||||
c.load()
|
||||
assert c.queue == QUEUE_TYPE_NEW
|
||||
assert c.left % 1000 != 1
|
||||
assert not col.undoName()
|
||||
assert not col.undo_status().undo
|
||||
# we should be able to undo multiple answers too
|
||||
c = col.sched.getCard()
|
||||
col.sched.answerCard(c, 3)
|
||||
|
@ -87,8 +87,8 @@ def test_review():
|
|||
# performing a normal op will clear the review queue
|
||||
c = col.sched.getCard()
|
||||
col.sched.answerCard(c, 3)
|
||||
assert col.undoName() == "Review"
|
||||
assert col.undo_status().undo == "Review"
|
||||
col.save("foo")
|
||||
assert col.undoName() == "foo"
|
||||
assert col.undo_status().undo == "foo"
|
||||
col.undo_legacy()
|
||||
assert not col.undoName()
|
||||
assert not col.undo_status().undo
|
||||
|
|
|
@ -521,7 +521,7 @@ class Browser(QMainWindow):
|
|||
|
||||
def createFilteredDeck(self) -> None:
|
||||
search = self.current_search()
|
||||
if self.mw.col.schedVer() != 1 and KeyboardModifiersPressed().alt:
|
||||
if self.mw.col.sched_ver() != 1 and KeyboardModifiersPressed().alt:
|
||||
aqt.dialogs.open("FilteredDeckConfigDialog", self.mw, search_2=search)
|
||||
else:
|
||||
aqt.dialogs.open("FilteredDeckConfigDialog", self.mw, search=search)
|
||||
|
|
|
@ -65,7 +65,7 @@ class FindDuplicatesDialog(QDialog):
|
|||
field = fields[form.fields.currentIndex()]
|
||||
QueryOp(
|
||||
parent=self.browser,
|
||||
op=lambda col: col.findDupes(field, search_text),
|
||||
op=lambda col: col.find_dupes(field, search_text),
|
||||
success=self.show_duplicates_report,
|
||||
).run_in_background()
|
||||
|
||||
|
|
|
@ -53,4 +53,4 @@ def check_db(mw: aqt.AnkiQt) -> None:
|
|||
n += 1
|
||||
continue
|
||||
|
||||
mw.taskman.with_progress(mw.col.fixIntegrity, on_future_done)
|
||||
mw.taskman.with_progress(mw.col.fix_integrity, on_future_done)
|
||||
|
|
|
@ -349,7 +349,7 @@ class DeckBrowser:
|
|||
######################################################################
|
||||
|
||||
def _v1_upgrade_message(self) -> str:
|
||||
if self.mw.col.schedVer() == 2:
|
||||
if self.mw.col.sched_ver() == 2:
|
||||
return ""
|
||||
|
||||
return f"""
|
||||
|
@ -371,7 +371,7 @@ class DeckBrowser:
|
|||
"""
|
||||
|
||||
def _confirm_upgrade(self) -> None:
|
||||
self.mw.col.modSchema(check=True)
|
||||
self.mw.col.mod_schema(check=True)
|
||||
self.mw.col.upgrade_to_v2_scheduler()
|
||||
|
||||
showInfo(tr.scheduling_update_done())
|
||||
|
|
|
@ -144,7 +144,7 @@ class DeckConf(QDialog):
|
|||
showInfo(tr.scheduling_the_default_configuration_cant_be_removed(), self)
|
||||
else:
|
||||
gui_hooks.deck_conf_will_remove_config(self, self.deck, self.conf)
|
||||
self.mw.col.modSchema(check=True)
|
||||
self.mw.col.mod_schema(check=True)
|
||||
self.mw.col.decks.remove_config(self.conf["id"])
|
||||
self.conf = None
|
||||
self.deck["conf"] = 1
|
||||
|
@ -220,7 +220,7 @@ class DeckConf(QDialog):
|
|||
f.revplim.setText(self.parentLimText("rev"))
|
||||
f.buryRev.setChecked(c.get("bury", True))
|
||||
f.hardFactor.setValue(int(c.get("hardFactor", 1.2) * 100))
|
||||
if self.mw.col.schedVer() == 1:
|
||||
if self.mw.col.sched_ver() == 1:
|
||||
f.hardFactor.setVisible(False)
|
||||
f.hardFactorLabel.setVisible(False)
|
||||
# lapse
|
||||
|
|
|
@ -106,7 +106,7 @@ def display_options_for_deck_id(deck_id: DeckId) -> None:
|
|||
|
||||
def display_options_for_deck(deck: DeckDict) -> None:
|
||||
if not deck["dyn"]:
|
||||
if KeyboardModifiersPressed().shift or aqt.mw.col.schedVer() == 1:
|
||||
if KeyboardModifiersPressed().shift or aqt.mw.col.sched_ver() == 1:
|
||||
deck_legacy = aqt.mw.col.decks.get(DeckId(deck["id"]))
|
||||
aqt.deckconf.DeckConf(aqt.mw, deck_legacy)
|
||||
else:
|
||||
|
|
|
@ -95,7 +95,7 @@ class FilteredDeckConfigDialog(QDialog):
|
|||
self.form.buttonBox.helpRequested, lambda: openHelp(HelpPage.FILTERED_DECK)
|
||||
)
|
||||
|
||||
if self.col.schedVer() == 1:
|
||||
if self.col.sched_ver() == 1:
|
||||
self.form.secondFilter.setVisible(False)
|
||||
restoreGeom(self, self.GEOMETRY_KEY)
|
||||
|
||||
|
@ -127,7 +127,7 @@ class FilteredDeckConfigDialog(QDialog):
|
|||
form.order.setCurrentIndex(term1.order)
|
||||
form.limit.setValue(term1.limit)
|
||||
|
||||
if self.col.schedVer() == 1:
|
||||
if self.col.sched_ver() == 1:
|
||||
if config.delays:
|
||||
form.steps.setText(self.listToUser(list(config.delays)))
|
||||
form.stepsOn.setChecked(True)
|
||||
|
@ -227,7 +227,7 @@ class FilteredDeckConfigDialog(QDialog):
|
|||
"""Return a search node that matches learning cards if the old scheduler is enabled.
|
||||
If it's a rebuild, exclude cards from this filtered deck as those will be reset.
|
||||
"""
|
||||
if self.col.schedVer() == 1:
|
||||
if self.col.sched_ver() == 1:
|
||||
if self.deck.id:
|
||||
return (
|
||||
self.col.group_searches(
|
||||
|
@ -252,7 +252,7 @@ class FilteredDeckConfigDialog(QDialog):
|
|||
|
||||
def _onReschedToggled(self, _state: int) -> None:
|
||||
self.form.previewDelayWidget.setVisible(
|
||||
not self.form.resched.isChecked() and self.col.schedVer() > 1
|
||||
not self.form.resched.isChecked() and self.col.sched_ver() > 1
|
||||
)
|
||||
|
||||
def _update_deck(self) -> bool:
|
||||
|
@ -266,7 +266,7 @@ class FilteredDeckConfigDialog(QDialog):
|
|||
config.reschedule = form.resched.isChecked()
|
||||
|
||||
del config.delays[:]
|
||||
if self.col.schedVer() == 1 and form.stepsOn.isChecked():
|
||||
if self.col.sched_ver() == 1 and form.stepsOn.isChecked():
|
||||
if (delays := self.userToList(form.steps)) is None:
|
||||
return False
|
||||
config.delays.extend(delays)
|
||||
|
|
|
@ -499,7 +499,7 @@ def _replaceWithApkg(mw: aqt.AnkiQt, filename: str, backup: bool) -> None:
|
|||
if not mw.loadCollection():
|
||||
return
|
||||
if backup:
|
||||
mw.col.modSchema(check=False)
|
||||
mw.col.mod_schema(check=False)
|
||||
|
||||
tooltip(tr.importing_importing_complete())
|
||||
|
||||
|
|
|
@ -527,7 +527,7 @@ class AnkiQt(QMainWindow):
|
|||
|
||||
def _loadCollection(self) -> None:
|
||||
cpath = self.pm.collectionPath()
|
||||
self.col = Collection(cpath, backend=self.backend, log=True)
|
||||
self.col = Collection(cpath, backend=self.backend)
|
||||
self.setEnabled(True)
|
||||
|
||||
def reopen(self) -> None:
|
||||
|
@ -1358,7 +1358,7 @@ title="%s" %s>%s</button>""" % (
|
|||
def confirm_schema_modification(self) -> bool:
|
||||
"""If schema unmodified, ask user to confirm change.
|
||||
True if confirmed or already modified."""
|
||||
if self.col.schemaChanged():
|
||||
if self.col.schema_changed():
|
||||
return True
|
||||
return askUser(tr.qt_misc_the_requested_change_will_require_a())
|
||||
|
||||
|
|
|
@ -143,7 +143,7 @@ class Overview:
|
|||
|
||||
def on_unbury(self) -> None:
|
||||
mode = UnburyDeck.Mode.ALL
|
||||
if self.mw.col.schedVer() != 1:
|
||||
if self.mw.col.sched_ver() != 1:
|
||||
info = self.mw.col.sched.congratulations_info()
|
||||
if info.have_sched_buried and info.have_user_buried:
|
||||
opts = [
|
||||
|
|
|
@ -203,7 +203,7 @@ for you than the default driver, please let us know on the Anki forums."""
|
|||
self.form.autoSyncMedia.isChecked() and 15 or 0
|
||||
)
|
||||
if self.form.fullSync.isChecked():
|
||||
self.mw.col.modSchema(check=False)
|
||||
self.mw.col.mod_schema(check=False)
|
||||
|
||||
# Profile: backup
|
||||
######################################################################
|
||||
|
|
|
@ -891,7 +891,7 @@ def supportText() -> str:
|
|||
|
||||
def schedVer() -> str:
|
||||
try:
|
||||
return str(mw.col.schedVer())
|
||||
return str(mw.col.sched_ver())
|
||||
except:
|
||||
return "?"
|
||||
|
||||
|
|
Loading…
Reference in New Issue