Commit graph

179 commits

Author SHA1 Message Date
Damien Elmes
410660990e add LIFO sorting options for new cards 2021-06-08 14:01:46 +10:00
Damien Elmes
1479957538 fix note changes triggering a queue rebuild 2021-06-08 12:09:35 +10:00
Damien Elmes
50961a9196 push review randomizing into SQL
This makes the review backlog case more expensive, since we end up
shuffling items outside the daily limit, but for the common case it's
about the same speed, and it means we don't need two separate sorting
steps. New cards remain handled the same way, since a backlog
is common there.

Also ensures that interday learning cards honor the deck sorting, and
that the non-default sort orders shuffle at the end.
2021-06-01 14:50:35 +10:00
Damien Elmes
562787bce1 add options to sort reviews by deck
https://forums.ankiweb.net/t/is-studying-subdeck-by-subdeck-broken-in-2-1-44-mac/10458/2
2021-06-01 13:22:39 +10:00
Damien Elmes
57ec4cc7b5 change get_queued_cards() to no longer return congrats info 2021-05-26 12:59:45 +10:00
Damien Elmes
ea20c31b53 suppress some tests around the daily rollover 2021-05-24 14:18:07 +10:00
Damien Elmes
29f9717c84 fix new ease not being applied to card on lapse
+ zero remaining steps when graduating (they shouldn't have been doing
any harm, but this is neater)
+ add some more tests that cover these cases
2021-05-24 10:04:56 +10:00
Damien Elmes
3d4cf26758 expose undoable config changes to frontend; refresh sidebar
The browser header handling still needs updating
2021-05-21 17:50:41 +10:00
Damien Elmes
b412bf97fb add a separate DeckId search for decks with children
- The "unbury deck" option was broken, as it was ignoring child
decks. It would be nice if we could use active_decks instead, but
plugging that into the old scheduler without breaking undo seems a bit
tricky.
- Remove the implicit From impl for decks, so we need to be forced to
think about whether we want child decks or not.
2021-05-20 11:44:37 +10:00
RumovZ
c1066167fa Adjust search syntax for filtered deck presets 2021-05-17 12:14:02 +02:00
Damien Elmes
1f16ce2096 shift learning fuzz into answering stage in test scheduler
When shown on the answer buttons, it's too distracting
2021-05-17 13:05:42 +10:00
Damien Elmes
13519a929c rework various aspects of the test scheduler
- Daily limits are no longer inherited - each deck limits its own
cards, and the selected deck enforces a maximum limit.
- Fetching of review cards now uses a single query, and sorts in advance.
In collections with a large number of overdue cards and decks, this is
faster than iterating over each deck in turn.
- Include interday learning count in review count & review limit, and
allow them to be buried.
- Warn when parent review limit is lower than child deck in deck options.
- Cap the new card limit to the review limit.
- Add option to control whether new card fetching short-circuits.
2021-05-16 20:23:07 +10:00
Damien Elmes
35063316d3 remove some duplicate code & add deck.or() helper 2021-05-14 22:35:52 +10:00
Damien Elmes
390a8421aa fix test scheduler undo + implement look-ahead
Instead of using a separate undo queue, the code now defers checking for
newly-due learning cards until the answering stage, and logs the updated
cutoff time as an undoable change, so that any newly-due learning cards
won't appear instead of a new/review card that was just undone.

Queue redo now uses a similar approach to undo, instead of rebuilding the
queues.
2021-05-14 22:16:53 +10:00
Damien Elmes
9990a10161 drop binary heap in test scheduler
The original rationale was avoiding a possible O(n) insertion if
the learning card was due outside the cutoff, but the increased code
complexity doesn't seem worth it, given that learning cards will
rarely grow above 1000.

Also added a currently-disabled test that demonstrates the current undo
handling behaviour is yielding incorrect counts; that will be reworked
in the next commit, and this change will make that easier.
2021-05-14 16:19:46 +10:00
Damien Elmes
a1bd6b481d pass sort options into test scheduler
- split new card fetch order and subsequent sort order; use latter
when building queues
- default to spacing siblings when burying is off, with options to
show each sibling in turn, and shuffle the fetched cards
2021-05-13 15:21:20 +10:00
Damien Elmes
b64f7a9456 fix burying in test scheduler
The bury new/review flags are now pulled from each card's home deck,
instead of using a global setting that had not been hooked up. This
unfortunately means we need to fetch the map of all decks up front, as
we need to be able to look up a deck configuration for cards that are
in filtered decks.

Fixes a "card was modified" error caused by cards being buried during
review, when they weren't removed up-front.
2021-05-12 12:00:15 +10:00
Damien Elmes
e737eb3088 is_stale() doesn't need to be passed deck
Deck changes will trigger a queue rebuild via requires_study_queue_rebuild()
2021-05-12 09:44:10 +10:00
Damien Elmes
49a1580566 use new API for test scheduler
Avoids duplicate work, and is a step towards allowing the next
states to be modified by third-party code.

Also:

- fixed incorrect underlined count, due to reviews being labeled as
learning cards
- fixed reviewer not refreshing when undoing a test review, by splitting
up backend queue rebuilding from frontend reviewer refresh
- moved answering into a CollectionOp
2021-05-11 13:06:03 +10:00
Damien Elmes
6775002709 better leech tag handling for test scheduler 2021-05-10 14:58:04 +10:00
Damien Elmes
be994f4102 add support for custom undo steps, and merging multiple actions
Allows add-on authors to define their own label for a group of undoable
operations. For example:

def mark_and_bury(
    *,
    parent: QWidget,
    card_id: CardId,
) -> CollectionOp[OpChanges]:
    def op(col: Collection) -> OpChanges:
        target = col.add_custom_undo_entry("Mark and Bury")
        col.sched.bury_cards([card_id])
        card = col.get_card(card_id)
        col.tags.bulk_add(note_ids=[card.nid], tags="marked")
        return col.merge_undo_entries(target)

    return CollectionOp(parent, op)

The .add_custom_undo_entry() is for adding your own custom actions.
When extending a standard Anki action, instead store `target = 
col.undo_status().last_step` after executing the standard operation.

This started out as a bigger refactor that required a separate
.commit_undoable() call to be run after each operation, instead of
having each operation return changes directly. But that proved to be
somewhat cumbersome in unit tests, and ran the risk of unexpected
behaviour if the caller invoked an operation without remembering to
finalize it.
2021-05-06 16:39:06 +10:00
Damien Elmes
abab4826bb support undo for (renamed) unbury_deck() action 2021-04-30 20:03:20 +10:00
Damien Elmes
9d604f1ad0 make it more ergonomic to search directly via nodes in Rust 2021-04-30 11:37:55 +10:00
Damien Elmes
a95cbb8515 DeckConfId -> DeckConfigId 2021-04-28 21:09:26 +10:00
Damien Elmes
1df86b28e8 sort deck on config update; fix id not being updated after deletion 2021-04-28 21:08:09 +10:00
Damien Elmes
fd81700679 deckconf -> deckconfig 2021-04-20 21:54:24 +10:00
Damien Elmes
64ebc32b3d tidy up Rust imports
rustfmt can do this automatically, but only when run with a nightly
toolchain, so it needs to be manually done for now - see rslib/rusfmt.toml
2021-04-18 18:38:54 +10:00
Damien Elmes
262b50445c start on making deck config and schema/mod changes undoable
+ move timestamps into a struct in a separate file for convenience
2021-04-18 17:33:12 +10:00
Damien Elmes
e71f7714ad as_str() -> as_native_str() 2021-04-18 09:33:39 +10:00
Damien Elmes
1acc679e8f hide NativeName inner value, and require explicit accessors 2021-04-18 09:29:35 +10:00
RumovZ
20bd207f00 Give deck.name the newtype NativeDeckName
The deck name must be constructed by calling associated functions of
NativeDeckName, unless the name is guaranteed to be valid machine
name (like "Default").
NativeDeckName exposes methods to mutate the deck name and return
the human name.
The storage routines take &strs, but those should be slices of
NativeDeckNames to ensure machine form and normalization.
2021-04-17 22:47:04 +02:00
Damien Elmes
84fe309583 update scheduling ops
- migrate to CollectionOp()
- return actual change count when suspending/burying
- add helper to convert vec to vec of newtype
2021-04-06 16:38:42 +10:00
Damien Elmes
097121424b cache scheduling info
Saves us having to recalculate it for each browser row
2021-04-05 17:09:53 +10:00
Damien Elmes
ebf7cc61d4 switch next_day_at to a newtype 2021-04-05 16:17:26 +10:00
Damien Elmes
a18bb2af12 add booleans for various screens to OpChanges
The backend knows exactly which op has executed, and it saves us having
to re-implement this logic on each client.

Fixes the browser table refreshing when toggling decks.
2021-04-05 14:28:56 +10:00
Damien Elmes
afc8680f2a make sure we don't invoke second search in v1 scheduler 2021-04-02 21:05:22 +10:00
Damien Elmes
ac1b9fadde merge the filtered deck errors into an enum
Fixes the wrong message being shown when trying to move cards to a
filtered deck
2021-04-01 22:30:00 +10:00
Damien Elmes
094e4ad461 crate::err -> crate::error 2021-04-01 16:07:13 +10:00
Damien Elmes
9f4a06abee ID -> Id in protobuf and Python
follow-up to dc81a7fed0
2021-03-27 21:38:20 +10:00
Damien Elmes
561d160590 fix some clippy lints in tests 2021-03-27 20:44:31 +10:00
Damien Elmes
1055acb9f2 fix more issues uncovered by the latest clippy 2021-03-27 20:25:34 +10:00
Damien Elmes
dc81a7fed0 use mixed case for abbreviations in Rust code
So, this is fun. Apparently "DeckId" is considered preferable to the
"DeckID" were were using until now, and the latest clippy will start
warning about it. We could of course disable the warning, but probably
better to bite the bullet and switch to the naming that's generally
considered best.
2021-03-27 19:53:33 +10:00
Damien Elmes
3433c02242 i18n->tr in rslib/ to match Python/TS code 2021-03-27 12:09:51 +10:00
Damien Elmes
f485efce16 update remaining TR references in rslib 2021-03-27 11:18:34 +10:00
Damien Elmes
dc5fdd30d4 update no-arg TR references in rslib/ 2021-03-26 23:16:08 +10:00
Damien Elmes
9aece2a7b8 rework translation handling
Instead of generating a fluent.proto file with a giant enum, create
a .json file representing the translations that downstream consumers
can use for code generation.

This enables the generation of a separate method for each translation,
with a docstring that shows the actual text, and any required arguments
listed in the function signature.

The codebase is still using the old enum for now; updating it will need
to come in future commits, and the old enum will need to be kept
around, as add-ons are referencing it.

Other changes:

- move translation code into a separate crate
- store the translations on a per-file/module basis, which will allow
us to avoid sending 1000+ strings on each JS page load in the future
- drop the undocumented support for external .ftl files, that we weren't
using
- duplicate strings in translation files are now checked for at build
time
- fix i18n test failing when run outside Bazel
- drop slog dependency in i18n module
2021-03-26 09:41:32 +10:00
Damien Elmes
d382b33585 rework filtered deck screen & search errors
- Filtered deck creation now happens as an atomic operation, and is
undoable.
- The logic for initial search text, normalizing searches and so on
has been pushed into the backend.
- Use protobuf to pass the filtered deck to the updated dialog, so
we don't need to deal with untyped JSON.
- Change the "revise your search?" prompt to be a simple info box -
user has access to cancel and build buttons, and doesn't need a separate
prompt. Tweak the wording so the 'show excluded' button should be more
obvious.
- Filtered decks have a time appended to them instead of a number,
primarily because it's easier to implement. No objections going back to
the old behaviour if someone wants to contribute a clean patch.
The standard de-duplication will happen if two decks are created in the
same minute with the same name.
- Tweak the default sort order, and start with two searches. The UI
will still hide the second search by default, but by starting with two,
the frontend doesn't need logic for creating the starting text.
- Search errors now have their own error type, instead of using
InvalidInput, as that was intended mainly for bad API calls. The markdown
conversion is done when the error is converted from the backend, allowing
errors to printed as a string without any special handling by the calling
code.

TODO: when building a new filtered deck, update_active() is clobbering
the undo log when the overview is refreshed
2021-03-24 22:04:35 +10:00
Damien Elmes
12597e1094 support undo of filtered deck build/empty 2021-03-24 12:56:06 +10:00
Damien Elmes
2a168adb66 move filter code into scheduler/ 2021-03-23 23:55:28 +10:00
Damien Elmes
0123a382ec deck rename with perform_op() 2021-03-22 20:38:51 +10:00
Damien Elmes
0331d8b588 make reposition undoable 2021-03-19 19:45:21 +10:00
Damien Elmes
6b0fe4b381 undoable ops now return changes directly; add new *_ops.py files
- Introduced a new transact() method that wraps the return value
in a separate struct that describes the changes that were made.
- Changes are now gathered from the undo log, so we don't need to
guess at what was changed - eg if update_note() is called with identical
note contents, no changes are returned. Card changes will only be set
if cards were actually generated by the update_note() call, and tag
will only be set if a new tag was added.
- mw.perform_op() has been updated to expect the op to return the changes,
or a structure with the changes in it, and it will use them to fire the
change hook, instead of fetching the changes from undo_status(), so there
is no risk of race conditions.
- the various calls to mw.perform_op() have been split into separate
files like card_ops.py. Aside from making the code cleaner, this works
around a rather annoying issue with mypy. Because we run it with
no_strict_optional, mypy is happy to accept an operation that returns None,
despite the type signature saying it requires changes to be returned.
Turning no_strict_optional on for the whole codebase is not practical
at the moment, but we can enable it for individual files.

Still todo:
- The cursor keeps moving back to the start of a field when typing -
we need to ignore the refresh hook when we are the initiator.
- The busy cursor icon should probably be delayed a few hundreds ms.
- Still need to think about a nicer way of handling saveNow()
- op_made_changes(), op_affects_study_queue() might be better embedded
as properties in the object instead
2021-03-19 19:45:21 +10:00
Damien Elmes
90526c61cd move ops.rs out of undo/ 2021-03-19 19:45:21 +10:00
Damien Elmes
7b7d7e8330 consume original card when updating 2021-03-12 16:20:58 +10:00
Damien Elmes
24762261d9 make 'forget card' undoable; remove checkpoint() in set_due_date 2021-03-12 16:13:50 +10:00
Damien Elmes
3b067c7a66 limit initial sort selection to new cards
https://github.com/ankidroid/Anki-Android/issues/8172
2021-03-12 14:58:19 +10:00
Damien Elmes
c1316bb65f 'set due date' now undoable 2021-03-12 14:50:31 +10:00
Damien Elmes
96940f0527 undo support for config entries 2021-03-10 11:53:27 +10:00
Damien Elmes
3d0ddc8539 make flag changes in the reviewer undoable
This splits update_card() into separate undoable/non-undoable ops
like the change to notes in b4396b94abdeba3347d30025c5c0240d991006c9

It means that actions get a blanket 'Update Card' description - in the
future we'll probably want to either add specific actions to the backend,
or allow an enum or string to be passed in to describe the op.

Other changes:
- card.flush() can no longer be used to add new cards. Card creation
is only supposed to be done in response to changes in a note's fields,
and this functionality was only exposed because the card generation
hadn't been migrated to the backend at that point. As far as I'm aware,
only Arthur's "copy notes" add-on used this functionality, and that should
be an easy fix - when the new note is added, the associated cards will
be generated, and they can then be retrieved with note.cards()
- tidy ups/PEP8
2021-03-10 11:53:27 +10:00
Damien Elmes
ecea2161e3 dispatch undo operations via enum instead of trait
To coalesce successive note edits into a single undo op we'll need to
be able to get the original Undoable type, which is awkward to do with
a trait object.
2021-03-10 11:53:27 +10:00
Damien Elmes
d70e35e0a2 move remaining undo ops into separate files 2021-03-10 11:53:27 +10:00
Damien Elmes
7900cc6868 fix some clippy lints 2021-03-10 11:53:27 +10:00
Damien Elmes
49a1970399 note deletion undo; refactoring
- transact() now automatically clears card queues unless an op
opts-out (and currently only AnswerCard does). This means there's no
risk of forgetting to clear the queues in an operation, or when undoing/
redoing
- CollectionOp->UndoableOp
- clear queues when redoing "answer card", instead of clearing redo
when clearing queues
2021-03-10 11:47:53 +10:00
Damien Elmes
105ce94dd4 undo unbury/unsuspend 2021-03-10 11:47:53 +10:00
Damien Elmes
41779c1aad implement bury/suspend undo 2021-03-10 11:47:53 +10:00
Damien Elmes
b466f0ce90 rework undo
- use dataclasses for the review/checkpoint undo cases, instead of the
nasty ad-hoc list structure
- expose backend review undo to Python, and hook it into GUI
- redo is not currently exposed on the GUI, and the backend can only
cope with reviews done by the new scheduler at the moment
- the initial undo prototype code was bumping mtime/usn on undo, but
that was not ideal, as it was breaking the queue handling which expected
the mtime to match. The original rationale for bumping mtime/usn was
to avoid problems with syncing, but various operations like removing
a revlog can't be synced anyway - so we just need to ensure we clear the
undo queue prior to syncing
2021-03-10 11:47:53 +10:00
Damien Elmes
67c490a8dc support undoing deck mutations
This required refactoring the deck code a bit to split up the 'update'
and 'add' cases better.
2021-03-10 11:47:53 +10:00
Damien Elmes
c9eeb91e0a initial work on undoing reviews+burying siblings
- fetch sfld and csum when fetching notes, to make it cheaper
to write them back out unmodified
- make `fields` private, and access it via accessors, so we can
still catch when fields have been mutated without calling
prepare_for_update()
- fix python importing code passing a string in as the checksum
2021-03-10 11:47:53 +10:00
Damien Elmes
359d0bc331 use native struct for QueuedCard 2021-03-10 11:47:53 +10:00
Damien Elmes
de7baa80bd switch to 4 buttons when previewing in test scheduler
- Currently we just use 1.5x and 2x the normal preview delay; we could
change this in the future.
- Don't try to capture the current state; just use a flag to denote
exit status.
- Show (end) when exiting
2021-03-01 23:47:00 +10:00
Damien Elmes
c74a71a6d7 interval sorting 2021-03-01 21:48:14 +10:00
Damien Elmes
2c6b6734b5 experimental queue building
Still a work in progress, and hidden behind a feature flag.
2021-03-01 12:18:21 +10:00
Damien Elmes
3bddf99ba1 remove local tz test
This was breaking some of the unit tests when they happened to
complete in a particular order
2021-03-01 10:59:01 +10:00
Damien Elmes
c0b9285923 move cards out of the new queue on filtered deck upgrade 2021-02-26 19:16:18 +10:00
Damien Elmes
37429f1580 add test to the previewing code 2021-02-23 17:35:20 +10:00
Damien Elmes
02e23e1063 split rescheduling_filter, and more tidyups 2021-02-23 17:35:20 +10:00
Damien Elmes
b887057448 split review/learn/new/relearn 2021-02-23 17:35:20 +10:00
Damien Elmes
e74210717a split state fetching, revlog and preview code out 2021-02-23 17:35:20 +10:00
Damien Elmes
6a44269280 sched->scheduler 2021-02-23 17:35:20 +10:00