Commit Graph

1871 Commits

Author SHA1 Message Date
Damien Elmes
9b028e8f3d Bump Chrono now that Rumo's TZ parser has been merged
https://github.com/chronotope/chrono/pull/978

+ Update workspace hack/licenses
2023-06-16 11:43:12 +10:00
Damien Elmes
81ac5a91ad Include backend comments in Python and Rust codegen
This is of limited usefulness at the moment, as it doesn't help consumers
of the public API.

Also removed detached comments from the included comments.
2023-06-16 10:59:00 +10:00
Damien Elmes
02a1c891a6 Don't require DESCRIPTORS_BIN in an external workspace
Consumers may not require it
2023-06-15 22:32:16 +10:00
Damien Elmes
de8f62f831 Don't fail silently when an empty csv is imported 2023-06-15 17:47:46 +10:00
Damien Elmes
f4fca1f5fd Skip writing descriptors if unchanged 2023-06-15 17:17:56 +10:00
Damien Elmes
9701055eb5 Add support for using n2 instead of ninja
Provides better visibility into what the build is currently doing.
Motivated by slow node.js downloads making the build appear stuck.

You can test this out by running ./tools/install-n2 then building
normally. Please report any problems, and 'cargo uninstall n2' to get
back to the old behaviour. It works on Windows, but prints a new line
each second instead of redrawing the same area.

A couple of changes were required for compatibility:

- n2 doesn't resolve $variable names inside other variables, so the
resolution needs to be done by our build generator.
- Our inputs and outputs in build.ninja need to be listed in a deterministic
order to avoid unwanted rebuilds. I've made a few other tweaks so the
build file should now be fully-deterministic.
2023-06-15 17:17:56 +10:00
Damien Elmes
45f5709214
Migrate to protobuf-es (#2547)
* Fix .no-reduce-motion missing from graphs spinner, and not being honored

* Begin migration from protobuf.js -> protobuf-es

Motivation:

- Protobuf-es has a nicer API: messages are represented as classes, and
fields which should exist are not marked as nullable.
- As it uses modules, only the proto messages we actually use get included
in our bundle output. Protobuf.js put everything in a namespace, which
prevented tree-shaking, and made it awkward to access inner messages.
- ./run after touching a proto file drops from about 8s to 6s on my machine. The tradeoff
is slower decoding/encoding (#2043), but that was mainly a concern for the
graphs page, and was unblocked by
37151213cd

Approach/notes:

- We generate the new protobuf-es interface in addition to existing
protobuf.js interface, so we can migrate a module at a time, starting
with the graphs module.
- rslib:proto now generates RPC methods for TS in addition to the Python
interface. The input-arg-unrolling behaviour of the Python generation is
not required here, as we declare the input arg as a PlainMessage<T>, which
marks it as requiring all fields to be provided.
- i64 is represented as bigint in protobuf-es. We were using a patch to
protobuf.js to get it to output Javascript numbers instead of long.js
types, but now that our supported browser versions support bigint, it's
probably worth biting the bullet and migrating to bigint use. Our IDs
fit comfortably within MAX_SAFE_INTEGER, but that may not hold for future
fields we add.
- Oneofs are handled differently in protobuf-es, and are going to need
some refactoring.

Other notable changes:

- Added a --mkdir arg to our build runner, so we can create a dir easily
during the build on Windows.
- Simplified the preference handling code, by wrapping the preferences
in an outer store, instead of a separate store for each individual
preference. This means a change to one preference will trigger a redraw
of all components that depend on the preference store, but the redrawing
is cheap after moving the data processing to Rust, and it makes the code
easier to follow.
- Drop async(Reactive).ts in favour of more explicit handling with await
blocks/updating.
- Renamed add_inputs_to_group() -> add_dependency(), and fixed it not adding
dependencies to parent groups. Renamed add() -> add_action() for clarity.

* Remove a couple of unused proto imports

* Migrate card info

* Migrate congrats, image occlusion, and tag editor

+ Fix imports for multi-word proto files.

* Migrate change-notetype

* Migrate deck options

* Bump target to es2020; simplify ts lib list

Have used caniuse.com to confirm Chromium 77, iOS 14.5 and the Chrome
on Android support the full es2017-es2020 features.

* Migrate import-csv

* Migrate i18n and fix missing output types in .js

* Migrate custom scheduling, and remove protobuf.js

To mostly maintain our old API contract, we make use of protobuf-es's
ability to convert to JSON, which follows the same format as protobuf.js
did. It doesn't cover all case: users who were previously changing the
variant of a type will need to update their code, as assigning to a new
variant no longer automatically removes the old one, which will cause an
error when we try to convert back from JSON. But I suspect the large majority
of users are adjusting the current variant rather than creating a new one,
and this saves us having to write proxy wrappers, so it seems like a
reasonable compromise.

One other change I made at the same time was to rename value->kind for
the oneofs in our custom study protos, as 'value' was easily confused
with the 'case/value' output that protobuf-es has.

With protobuf.js codegen removed, touching a proto file and invoking
./run drops from about 8s to 6s.

This closes #2043.

* Allow tree-shaking on protobuf types

* Display backend error messages in our ts alert()

* Make sourcemap generation opt-in for ts-run

Considerably slows down build, and not used most of the time.
2023-06-14 22:47:37 +10:00
Damien Elmes
f6fdf64c5d Fix build failure in fresh checkout 2023-06-12 17:10:22 +10:00
Damien Elmes
850e564a99 Fix build failure on Windows 2023-06-12 16:06:40 +10:00
Damien Elmes
d380f3034c Split io.rs into separate crate, and use it in proto build
Will be handy to use it in our other scripts in the future too - thanks
Rumo!

Results of benchmarking ./run before and after these crate splits:

- Touching a proto file leads to a slight increase: about +90ms
- Touching an rslib file leads to a bigger decrease, as there's less to
recompile: about -700ms

And ./ninja test is even better: about +200ms and -3800ms.
2023-06-12 15:47:51 +10:00
Damien Elmes
a83c4a7da7 Move generated protobuf into anki_proto
Due to the orphan rule, this meant removing our usages of impl ProtoStruct,
or converting them to a trait when they were used commonly.

rslib now directly references anki_proto and anki_i18n, instead of
'pub use'-ing them, and we can put the generated files back in OUT_DIR.
2023-06-12 15:47:51 +10:00
RumovZ
651ba88393
Automate schema 11 other duplicates clearing (#2542)
* Skip linting target folder

Contains build files not passing the copyright header check.

* Implicitly clear duplicate keys when serializing

Fixes `originalStockKind` not being cleared from `other`, as it had
mistakenly been added to the field list for `NoteFieldSchema11`.
2023-06-12 11:14:14 +10:00
Damien Elmes
bac05039a7 Move protobuf generation into a separate crate; write .py interface in Rust
A couple of motivations for this:

- genbackend.py was somewhat messy, and difficult to change with the
lack of types. The mobile clients used it as a base for their generation,
so improving it will make life easier for them too, once they're ported.
- It will make it easier to write a .ts generator in the future
- We currently implement a bunch of helper methods on protobuf types
which don't allow us to compile the protobuf types until we compile
the Anki crate. If we change this in the future, we will be able to
do more of the compilation up-front.

We no longer need to record the services in the proto file, as we can
extract the service order from the compiled protos. Support for map types
has also been added.
2023-06-12 09:52:00 +10:00
Damien Elmes
7a9c6172ef When cards are intraday learning queue, don't use original due
https://forums.ankiweb.net/t/backlog-with-no-backlo/30832
2023-06-07 16:20:44 +10:00
Damien Elmes
f758b33346
Default to current deck in csv import if settings allow it (#2527)
* Default to current deck in csv import if settings allow it

Reuses defaults_for_adding(). In the future we might also want to update
the last deck/notetype on successful completion, if entries weren't
specified in the file.

https://forums.ankiweb.net/t/importing-new-notes-to-wrong-deck-in-anki-2-1-63/30598

* Address review feedback from Rumo
2023-05-31 13:47:12 +10:00
Damien Elmes
7f6c410ca5
Various changes to I/O handling (#2513)
* Store coordinates as ratios of full size

* Use single definition for cappedCanvasSize()

* Move I/O review code into ts/image-occlusion

A bit simpler when it's all in one place.

* Reduce number precision, and round to whole pixels

>>> n=10000
>>> for i in range(1, int(n)): assert i == round(float("%0.4f" % (i/n))*n)

* Minor typing tweak

So, it turns out that typing is mostly broken in ts/image-occlusion.
We're importing from fabric which is a js file without types, so types
like fabric.Canvas are resolving to any.

I first tried switching to `@types/fabric`, which introduced a slew of
typing errors. Wasted a few hours trying to address them, before deciding
to give up on it, since the types were not complete. Then found fabric
has a 6.0 beta that introduces typing, and spent some time with that, but
ran into some new issues as it still seems to be a work in progress.
I think we're probably best off waiting until it's out and stabilized
before sinking more effort into this.

* Refactor (de)serialization of occlusions

To make the code easier to follow/maintain, cloze deletions are now decoded/
encoded into simple data classes, which can then be converted to Fabric objects
and back. The data objects handle converting from absolute/normal positions, and
producing values suitable for writing to text (eg truncated floats).

Various other changes:

- Polygon points are now stored as 'x,y x2,y2 ...' instead of JSON in cloze
divs, as that makes the handling consistent with reading from cloze deletion
text.
- Fixed the reviewer not showing updated placement when a polygon was moved.
- Disabled rotation controls in the editor, since we don't support rotation during
review.
- Renamed hideInactive to occludeInactive, as it wasn't clear whether the former
meant to hide the occlusions, or keep them (hiding the content). It's stored
as 'oi=1' in the cloze text.

* Increase canvas size limit, and double pixels when required.

* Size canvas based on container size

This results in sharper masks when the intrinsic image size is smaller
than the container, and more legible ones when the container is smaller than
the intrinsic image size.

By using the container instead of the viewport, we account for margins,
and when the pixel ratio is 1x, the canvas size and container size should
match.

* Disable zoom animation on editor load

* Default to rectangle when adding new occlusions

* Allow users to add/update notes directly from mask editing page

* The mask editor needs to work with css pixels, not actual pixels

The canvas and image were being scaled too large, which impacted
performance.
2023-05-31 13:45:12 +10:00
Damien Elmes
921e8c7985 percent-encoding -> percent-encoding-iri
Closes #2526
2023-05-29 14:46:02 +10:00
Damien Elmes
15dcb09036
Detect incorrect usage of triple slash in TypeScript (#2524)
* Migrate check_copyright to Rust

* Add a new lint to check accidental usages of /// in ts/svelte comments

* Fix a bunch of incorrect jdoc comments

* Move contributor check into minilints

Will allow users to detect the issue locally with './ninja check'
before pushing to CI.

* Make Cargo.toml consistent with other crates
2023-05-26 12:49:44 +10:00
Damien Elmes
7686cb8de8
Fix misaligned image occlusions (#2512)
* Cloze styling is not required in I/O notetype

* Use raw string for IO template

* Rename to notetype.css and use more specific ids

* Move internal i/o styling into runtime

Storing it in the notetype makes it difficult to make changes, and
makes it easier for the user to break.

* Fix misaligned occlusions

At larger screen sizes, the canvas was not increasing above its configured
size, so it ended up being placed top center instead of expanding to fit
the entire container area.

To resolve this, both the image and canvas are forced to the container
size, and the container is constrained to the size of the viewport,
with the same aspect ratio as the image.

Closes #2492
2023-05-23 11:59:50 +10:00
Joel Koen
6b1c799cbf
Expose some collection methods already public in rslib backend (#2510)
* Expose some collection methods already public in rslib backend
2023-05-22 13:44:18 +10:00
Damien Elmes
c520b59311 Support prop queries in filtered decks
https://forums.ankiweb.net/t/phantom-overdue-reviews/30300
2023-05-20 10:49:00 +10:00
Damien Elmes
40a68afad3 Add the ability to search for custom data properties
Closes #2491
2023-05-18 15:54:01 +10:00
Damien Elmes
a19cb75768 Fix broken media importing in non-legacy apkg path
- Ensure our unit tests cover the non-legacy path, and check file
contents and not just existence.
- Pass `compressed` into copy_and_ensure_sha1_set(). We can't just
test for self.sha1.is_none(), as that fails in the case where an existing
media file was found in the legacy path.

https://forums.ankiweb.net/t/anki-media-file-corruption-when-exporting-png-files/30315
2023-05-18 11:33:01 +10:00
Damien Elmes
63fd70ad34 Add a .with_desktop_media_paths() helper 2023-05-18 11:02:02 +10:00
Damien Elmes
cff613bfef Display a more informative message when attempting media op without folder 2023-05-18 10:48:25 +10:00
Damien Elmes
3999c99af7 Fix a panic when user sets a huge learning step 2023-05-11 09:10:22 +10:00
XeR
0d58a04596
Fix sync client ignoring directories (#2490)
* Fix sync client ignoring directories

Current implementation of the sync client does not properly send requests to a
self-hosted sync server if the URL has directories.

If the self-hosted sync URL is `https://example.org/foo/bar/`, Anki is expected
to send requests to `https://example.org/foo/bar/sync/hostKey`. Instead, it will
discard the directories and wrongly send requests to
`https://example.org/sync/hostKey`.

Fixes: e5d5d1d ("Fix panic with invalid sync server URL with port")

* Add XeR to contributors
2023-05-08 11:40:57 +10:00
Damien Elmes
894b7862e3
Improve import messaging when notetype has changed (#2483)
* Fix file extension not being appended on export

Regressed in #2427

* Improve import messaging when notetype has changed

- If the local notes are up to date, we don't need to warn about the
changed notetype, as no updates are required.
- Make it clearer that a changed notetype only affects updates.

Will update the docs as well.
2023-04-28 11:39:18 +10:00
Damien Elmes
f6486da233
Various tweaks to I/O code (#2478)
* Allow user to select I/O notetype instead of enforcing a specific name

* Display a clearer error when I/O note is missing an image

Opening the card layout screen from "manage notetypes" was showing an
error about the Anki version being too old.

Replacement error is not currently translatable.

* Preserve existing notetype when adding I/O notetype

* Add a 'from clipboard' string

The intention is to use this in the future to allow an image occlusion
to be created from an image on the clipboard.

* Tweak I/O init

- Use union type instead of multiple nullable values
- Pass the notetype id in to initialization

* Fix image insertion in I/O note

- The regex expected double quotes, and we were using single ones
- Image tags don't need to be closed

* Use more consistent naming in image_occlusion.proto

* Tweaks to default I/O notetype

- Show the header on the front side as well (I presume this is what
users expect; if not am happy to revert)
- Don't show comments on card (again, I presume users expect to use
this field to add notes that aren't displayed during review, as they
can use back extra for that)

* Fix sticky footer missing background

Caused by earlier CSS refactoring
2023-04-19 15:30:18 +10:00
Damien Elmes
dd13e78eca
Add ability to restore a notetype to its original configuration (#2472)
* Store the original stock notetype kind in the notetype

Will allow us to provide a command to restore a notetype to its default
settings/templates.

* Add a new action to restore a notetype to its original state
2023-04-18 14:07:51 +10:00
Damien Elmes
8f460b2ba9 Use original deck name if card is in filtered deck
https://forums.ankiweb.net/t/inconsistent-read-deck-name-and-seed-from-ctx-will-recognize-the-filtered-deck-as-the-deck-name/29327
2023-04-13 14:46:47 +10:00
Damien Elmes
6fe9728b80 Bring chrono minimum back down for AnkiDroid
Accidentally bumped in recent deps update
2023-04-12 08:45:51 +10:00
Damien Elmes
0466e220f3 Move ascii_percent_encoding into a separate repo
This makes it easier to update independently, and means we don't need
to exclude it from formatting/tests.
2023-04-12 08:45:23 +10:00
Damien Elmes
dc8c4438d7 Update iana-time-zone and windows crates, as windows 0.47 yanked 2023-04-12 08:25:53 +10:00
Damien Elmes
f0d430623c Update to Rumo's latest linkchecker code
cf838122a4 (r107719275)
2023-04-12 07:25:39 +10:00
Damien Elmes
d9624819e9 Add a fully-random review order
https://forums.ankiweb.net/t/bug-the-random-option-in-the-review-sort-order-setting-is-not-truly-random/29199
2023-04-11 16:16:45 +10:00
Damien Elmes
f2e61c8982 Fix a line failing cargo fmt with stable Rust
https://github.com/ankidroid/Anki-Android-Backend/pull/280#issuecomment-1493144833
2023-04-03 09:39:30 +10:00
Damien Elmes
26c640805c Update Rust deps
axum-client-ip excluded, as it will need further work.
2023-03-31 14:38:24 +10:00
Damien Elmes
f497bd6a33 Bump Rust version 2023-03-31 14:11:33 +10:00
Mani
2bf134dc72
Feature image occlusion (#2367)
* add note types with occlusions and image fields

* generate image occlusion cloze div data
- generate div element with data-* atrributes for canvas shape generate for reviewer

* getting image data & deck id and adding notes
the implementation added into backend
- added service index in backend.proto for image occlusion request
- created image_occlusion.proto with required message and service
- implementation in backend for getting image and adding notes, also during editing return imagecloze note and update notes
- add notes to selected deck, if no notetype then add image occlusion notetypes
- reuse notetype from stock notetypes when not exist

* script for generating shapes using canvas api in reviewer
- the flash issues fixed by loading image and using image size to draw canvas, also when image get resized, calculate scale using natural width and canvas width to draw shape at right position
- limit size of canvas for safari

* init image occlusion page in ts and build page
with
- fabricjs for editing shapes
- panzoom for drag and zoom
- pickr for color picker
- build page using web.rs

* implement top toolbar for canvas shapes
- undo & redo tools
- zoom in, zoom out and zoom fit
- group & ungroup
- copy & paste
- set transparency of shapes
- align tools

* implement side toolbar for drawing shapes
add top toolbar and the side toolbar contains following tools
- cursor for selecting shapes
- zoom for drag and zoom shapes in mask editor
- rectangle for creating it
- ellipse for creating it
- polygon for creating it using points
- shape fill color
- question mask color (currently only single color can be added for all shapes)

* add maskeditor page for editing mask
- add side toolbar and sidebar include toptoolbar
- load maskeditor in two mode
     - for adding note using path to image
     - for editing note using note id

* implement note editor page for adding notes
- the note editor page have simple button (B/I/U) and option to toggle html view
- option to select deck for adding notes into that deck
- option to generate to hide all, guess one & hide one, guess one notes

* add image occlusion page
add side toolbar, top toolbar, mask editor and note editor
- option to switch between mask editor and note editor

* implement generates notes and save notes
implemention to show toast components for messages

* removed pickr & implemented color picker component
- remove pickr
- implemented using html5 canvas
- range input for changing color
- another range input for opacity changes
- hex and rgba value support

* rename methods name & rust unwrap safety
- change plural names to singular
- create respone message in proto and return response with imagecloze note or error if not found with note id
- remove image_occlusion from post handler list
- rename service name in mediasrv.py
- rename methods name for image occlusion in backend and image_occlusion
- update frontend also for update functions' names
- handle error in frontend mask-editor.ts, when error getting notes then toast message shown to frontend

* extract to function & add comments & remove global
- extract function in mask-editor.ts to reduce duplicate
- remove unused global from css
- add comments to store.ts explaining usage
- changes id to noteId in lib.ts
- add comments for limitSize, becuase of duplicate implementation

* remove image_occlusion notetype
- remove from stock notetype, stdmodels
- add implementation for notetype to image occlusion
- add i18n for errors

* update smooth scroll, always show cursor tools
- change questionmask to qmask
- make selectable for shape true in all tools to simplify edits and draw shapes
- update image occlusion in reviewer ts to load image properly

* add and get notetype else return errors

* fix: not showing occlusion

* Use a oneof for ImageClozeNoteResponse

Makes it clearer that only one of them can be returned

* Don't crash if image filename not provided

The second unwrap should be ok, as the input is utf8

* Refactor get_image_cloze_note

- fixes crash when note doesn't exist - Ok(None) case was not covered
- decouples business logic from native error->proto error conversion
- no need for original copy
- field[x] is more idiomatic than field.get(x).unwrap()
- don't need mutable access to fields

* Fix crash if image file unreadable

+ Use our read_file helper for better error context

* Add metadata() helper

* Fix crash if file metadata can't be read

* remove color picker, qmask and shape color
- remove strings from ftl
- remove color picker component
- remove from cloze generation
- remove icons for two buttons
- use constant color for shapes

* update color in reviewer and ftl strings

* fix shape position in canvas & add border to shape
- rename mask to inactive shape and active shape color
- border witdth and border color
- change decimal point deserializing string and toFixed(2)
- add thin border in mask editor, may be image background was transparent

* fix shape position in canvas after modified
- do not draw fixed ratio shapes by turn of uniformScaling
- fix rectangle width,height
- fix ellipse rx,ry,width,height
- fix polygon postion and points
- draw outside of canvas also

* fix border width and color in reviewer canvas
- rename variable

* refactor cloze div generate and remove angle

* fix origin when drawn outside of canvas from right

* fix shape at boundry & not include rx,ry rectangle
- move shapes at boundry when pointer is outside of canvas
- include rx, ry for ellipse only
- include points for polygon only

* fix lint errors & update image size in editor canvas based on height and width

* remove unsupported layerX & layerX for touchscreen
- fix shapes at edges

* implemented undo redo with canvas state

- implemented undo redo using fabric canvas events
- polygon is special case and implemented only added and modified event
- rectangle and ellipse have object:added, object:modified and object:removed case
- change id to undo and redo

* remove background image from canvas and used css to put image tag below canvas editor

- set image width and height after adding image

* fix for polygon points, add br in cloze strings, & toogle masks button

- fix shapes at edges
- toggle masks button to show/hide masks
- hide clozes string, it contains <br>
- set height for div container (used 'relative' in css)

* refactor top toolbar, add space and border radius
- rename cursor tools
- add left and right border

* fix undo after undo happen, use transparent color in draw mode
2023-03-29 12:33:19 +10:00
Kieran Black
fe591f6be7
fix stats calendar incorrect due to daylight savings time (#2456)
* fix stats calendar daylight saving time offset bug

Previously, when computing counts for the calendar in the stats menu, it was assumed that days had 86,400 seconds. However, this assumption does not hold true on the day when daylight savings occurs.

* add self to CONTRIBUTORS and about.py

* fix stats calendar anki day to calendar day mapping

Since Anki days don't necessarily roll over at midnight, mapping an Anki day into a calendar day needs to have a linear shift applied. By providing the frontend with access to the scheduler's rollover hour, we can account for this offset.
2023-03-28 15:35:06 +10:00
Damien Elmes
39e503d0aa Add time of date to browser date columns 2023-03-26 14:49:49 +10:00
Abdo
946eb46813
Add option to exclude fields from search (#2394)
* Add option to exclude fields from unqualified searches

* Use temp tables instead

This is slightly faster according to my (very rough) tests.

* Make query a bit more readable

* exclude_from_search -> excludeFromSearch

* Remove superfluous notetypes table from query

* Rework to use field search logic

Thanks to Rumo for the suggestion: https://github.com/ankitects/anki/pull/2394#issuecomment-1446702402

* Exclude fields from field searches too

* Fix error on notetypes with no included fields

* Add back the exclude_fields function

This approach seems to perform better on average than the previously
benchmarked ones.

* Use pure-SQL approach to excluding fields

* Change single field search to use new approach

* Fix flawed any_excluded/sortf_excluded logic

* Support field exclusion in the nc operator

Also fix search text being wrapped in % in the any_excluded=true case.

* Support field exclusion in the re and w operators

* Label field exclusion as being slower

* Unqualified search should be wrapped in % in all cases

I was under the impression that it shouldn't be wrapped with the new
field exclusion logic.

* Remove unnecessary .collect()

* Refactor some complex return types into structs

* Do not exclude fields in field searches

* Add a test and docstring for CollectRanges

* Avoid destructuring in closures

* Remove the exclude_fields function

Minor wording tweaks by dae:
* num_fields -> total_fields_in_note
* fields -> field_ranges_to_search
* fields -> fields_to_search
* SingleField -> FieldQualified
* mid -> ntid
2023-03-20 07:46:03 +10:00
RumovZ
fdfa33fea4
Fix invalid ids on import (#2449) 2023-03-20 07:04:59 +10:00
RumovZ
039ebfeed6
Fix invalid ids on db check (#2445)
* Move open_test_collection into Collection test impl

* Fix invalid ids when checking database

* Report fixed invalid ids

* Improve message when trying to export invalid ids

Also move ImportError due to namespace conflicts with snafu macro.

* Take a human name in DeckAdder::new

* Mention timestamps in the db check message (dae)

Will help to correlate the fix with the message shown when importing/
exporting.
2023-03-19 10:58:35 +10:00
RumovZ
662dbbd4ca
Fix new limit being decremented unduly (#2447) 2023-03-18 11:48:50 +10:00
RumovZ
bd88c6d352
Ensure state mutator runs after card is rendered (#2421)
* Ensure state mutator runs after card is rendered

* Ensure ease buttons only show when states are ready

* Pass context into states mutator

* Revert queuing of state mutator hook

Now that context data is exposed users shouldn't rely on the question
having been rendered anymore.

* Use callbacks instead of signals and timeout

... to track whether the states mutator ran or failed.

* Make mutator async

* Remove State enum

* Reduce requests and compute seed on backend
2023-03-16 16:31:00 +10:00
RumovZ
f9126927b1
Check URLs in TS code (#2436)
* Move help page URLs in ts to new file

* Unnest linkchecker test module

* Check TS help pages

* Add a comment (dae)
2023-03-15 15:46:03 +10:00
RumovZ
c4d5eeb7a3
Revert bury restriction (#2432)
* Remove outdated comment.

* Revert removal of independent bury rules

* Revert 'hierarchical bury modes'

It's now again allowed to bury new, but not review cards e.g., but
siblings of previously gathered card queues will not be buried.

* Tweak docs (dae)

* Add missing Learn and PreviewRepeat queues
2023-03-11 17:49:18 +10:00
RumovZ
0d92bd2ca8
Fix backup creation for collections > 1 GiB (#2423)
Co-authored-by: Damien Elmes <dae@users.noreply.github.com>
2023-03-06 19:56:27 +10:00
RumovZ
84167d6e6b
Export static references from sound tags (#2420) 2023-03-06 19:29:19 +10:00
RumovZ
bb297b95bc
Global new ignore review limit (#2417)
* Add CardAdder test helper

* Add option to have new cards ignore the review limit

Also entails a lot of refactoring because the old code was deeply
coupled to the previous behaviour.

* Add global option to ignore review limit

* Refactor decrementation

* Unify testing
2023-03-06 19:06:12 +10:00
Daniel Tang
e5d5d1d4bd
Fix panic with invalid sync server URL with port (#2412) 2023-03-03 20:28:11 +10:00
Damien Elmes
4a053e5f88 Ensure good is at least previous+1 even if low hard factor set
https://forums.ankiweb.net/t/anki-scheduler-broken/27987/8
2023-03-03 15:34:08 +10:00
Damien Elmes
d9d36078f1 Make some methods/fields public 2023-03-01 19:46:06 +10:00
Damien Elmes
96a606d78c Add custom display for SyncError 2023-03-01 19:46:06 +10:00
Damien Elmes
7ebf8dd84a Implement HttpError directly so that sources can be extracted properly
When disabling the default snafu source handling, <dyn Error>.source()
doesn't work.
2023-03-01 19:46:06 +10:00
Damien Elmes
d5772ac43a Refactor MediaIter
Removes the dependency on a local path, and allows the unicode checks
to be skipped if appropriate.
2023-03-01 18:57:48 +10:00
Damien Elmes
ce35ba123b Allow .or_http_err() to work with anyhow 2023-03-01 18:54:01 +10:00
RumovZ
85aebae573
Add option to tag notes with missing media (#2379)
* Keep track of notes with missing media files

* Add option to tag notes with missing media

* Update ftl/core/media-check.ftl (dae)
2023-02-20 18:48:09 +10:00
Damien Elmes
e9385d2999 Trigger rebuild on STRINGS_JSON change 2023-02-17 19:03:33 +10:00
Damien Elmes
9e0a6d5bf7 Fix card ease being reset on schema upgrade
d20a7d291f introduced a serious regression,
causing cards to be reset to the default ease when upgrading to the latest
schema version. This could also be triggered when exporting a colpkg with
legacy support.
2023-02-17 12:27:19 +10:00
RumovZ
cdfb84f19a
Implement TTS using windows crate (#2371)
* Implement TTS using windows crate

* Use API calls instead of SSML

* Properly stop player in case of TTS error

* Add context to WindowsErrors

* Validate available voices

* Remove TTS text from synthesize error

* Limit maximum buffer size

* Make validation optional and list it in tts filter

* We no longer need the winrt module (dae)

* Use a separate request object so the meaning of the bool is clear (dae)

* Slightly shorten runtime error message (dae)

The default message appears to clip slightly.

* Alternate buffer implementation (dae)

* Use array instead of vec

* Drop the max buffer size to 128k (dae)
2023-02-17 12:26:07 +10:00
RumovZ
5a53da23ca
Deck scoped dupe check (#2372)
* Support limiting dupe check to deck

* Expose deck limiting dupe check on frontend

* Make CSV dupe options configurable with headers

* Rename duplicate file headers

* Change dupe check limit to enum
2023-02-16 17:53:36 +10:00
Damien Elmes
0be31b1b17 Fix excess missed markers in non-Latin text 2023-02-10 15:11:36 +10:00
Damien Elmes
f616bea580 Allow the network timeout to be customized
https://forums.ankiweb.net/t/local-sync-server-collection-exceeds-size-limit/27183/7
2023-02-08 14:33:02 +10:00
RumovZ
ed54cf71ec
Improve HttpError mapping (#2370)
* Use reqwest From impl in HttpError From impl

* Map timeouts to NetworkError (dae)
2023-02-08 12:54:42 +10:00
Damien Elmes
567ba06b5c Show custom data in stats screen
https://forums.ankiweb.net/t/feature-request-display-custom-data-in-card-info/27187
2023-02-07 12:27:43 +10:00
Damien Elmes
92cf5cd898 Don't enforce download size on client
https://forums.ankiweb.net/t/local-sync-server-collection-exceeds-size-limit/27183/6
2023-02-07 11:58:07 +10:00
RumovZ
855dc9d75b
Add Rust bin to deprecate unused ftl entries (#2364)
* Add Rust bin to deprecate unused ftl entries

* Align function names with bin names

* Support passing in multiple ftl roots

* Use source instead of jsons for deprecating

* Fix CargoRun not working more than once (dae)

* Add ftl:deprecate (dae)

* Deprecate some strings (dae)

This is not all of the strings that are currently unused

* Check json files before deprecating; add allowlist (dae)

The scheduler messages we'll probably want to reuse for the v2->v3
transition, so I'd prefer to keep them undeprecated for now.

* Deprecate old bury options (dae)

* Support gathering usages from Kotlin files for AnkiDroid (dae)

* Update json scripts (dae)

* Remove old deprecation headers

* Parameterize JSON roots to keep

* Tweak deprecation message (dae)
2023-02-07 11:56:14 +10:00
RumovZ
c824dd0b90
Disable burying of previously gathered cards (#2361)
* Enforce hierarchical bury modes

Interday learning burying is only allowed if review burying is enabled
and review burying is only allowed if new burying is enabled.
Closes #2352.

* Switch front end to new bury modes

* Wording tweaks (dae)

* Hide interday option if using v2 scheduler (dae)
2023-02-06 12:02:27 +10:00
Damien Elmes
6a97efe7af Deck-specific limits should be ignored when the v2 scheduler is enabled
Closes #2365
2023-02-06 11:02:26 +10:00
RumovZ
f3ef242bc5
Support UTF-8-BOM when importing CSV (#2360)
The csv crate already supports it, but the meta line parsing didn't.
2023-02-05 11:53:21 +10:00
Damien Elmes
32ec6e06f6 Fix comparison being collapsed when extra characters in input 2023-02-02 19:13:12 +10:00
Damien Elmes
321402a6d0 Revert to blanking out missed chars in provided text
Quite a few users seem surprised to have missed characters appear in
their input text
2023-02-02 19:13:11 +10:00
Damien Elmes
2c952cf3af Wrap expected text in code block when no answer provided 2023-02-02 18:01:23 +10:00
Kaben Nanlohy
77bba533ea
Allow burying cards in browser (#2351)
* Allow burying cards in browser

This code is based on existing "toggle suspend" command in browser.

- Adds "toggle bury" command to browser cards menu.
- Adds "browsing-toggle-bury" to core translation. Only english-language.
- Adds "buried" coloring to rows for buried cards in browser table.

Not yet done:

- Keyboard shortcut for "toggle bury" action.
- Non-english translations.

* Add contributor as requested in CONTRIBUTORS.

* Fix formatting in browser_table.rs.

* Add keyboard shortcut to "toggle bury" command.

This adds keyboard shortcut "ctrl-shift-j" to "toggle bury" command in
browser cards menu.

* Simplify logic for color of buried-card rows.
2023-01-30 19:21:06 +10:00
Damien Elmes
d20a7d291f Validate and clamp deck config when loading
Fixes #2353
2023-01-28 21:51:47 +10:00
Damien Elmes
17480a2c80 Use HTTP/1.1 for syncing
HTTP2 + a request body size not being declared up front was creating extra
work for AnkiWeb.
2023-01-28 21:51:47 +10:00
Damien Elmes
4142de57e2 Fix clean build failure due to protoc change
da7d4dd2fc changed the name of the env
var in .cargo/config.toml, causing the check in setup_protoc() to think
a custom path had been provided, which skipped the download and extract
step.
2023-01-26 09:33:39 +10:00
Damien Elmes
17b33f8298
Use Rust nightly for formatting (#2348)
* Support specifying a working dir to a build command

* Use nightly for formatting

* Pass valid TERM in from environment

Rustfmt depends on a valid setting, and not just the var to be non-empty.

* Wrap comment
2023-01-25 23:35:53 +10:00
Mani
da7d4dd2fc
Use a ninja variable for Protoc binary (#2345)
* Use a ninja variable for Protoc binary

* fix whitespace
2023-01-23 20:44:47 +10:00
Damien Elmes
dc0185720a Fix outer whitespace not being trimmed in type answer 2023-01-23 17:20:35 +10:00
Damien Elmes
c8275257ce Probable fix for future due graph
Cards due earlier today will have a negative offset like -78000(secs).
The old typescript code was using floating point division to yield -1;
with integer division we get 0 instead.

https://forums.ankiweb.net/t/wrong-info-when-hovering-over-future-due-graph/26522
2023-01-20 00:18:13 +10:00
Damien Elmes
19bbcb4cc0 Use backend for extracting cloze text to type
Closes #2311
2023-01-18 23:05:28 +10:00
Damien Elmes
03814fa1c4 Add some extra info to the 'modified without updating' message
For #2315
2023-01-18 22:38:28 +10:00
Damien Elmes
943dddf28f
Update Rust deps (#2332)
* Temporarily disable hakari

* Upgrade compatible deps except Chrono

* Update semver-incompatible crates

* Re-enable hakari

* Update licenses & cargo-deny

* Fix new clippy lints

* Update to latest Rust
2023-01-18 22:24:29 +10:00
Damien Elmes
ded805b504
Switch Rust import style (#2330)
* Prepare to switch Rust import style

* Run nightly format

Closes #2320

* Clean up a few imports

* Enable comment wrapping

* Wrap comments
2023-01-18 21:39:55 +10:00
Damien Elmes
9d84f357b6 Probable fix for flaky test 2023-01-18 21:32:08 +10:00
Damien Elmes
c923553a53 Replace newlines with linebreaks when importing without HTML
https://forums.ankiweb.net/t/line-breaks-are-removed-when-importing-csv-on-linux/26451
2023-01-18 21:00:33 +10:00
Damien Elmes
cf45cbf429
Rework syncing code, and replace local sync server (#2329)
This PR replaces the existing Python-driven sync server with a new one in Rust.
The new server supports both collection and media syncing, and is compatible
with both the new protocol mentioned below, and older clients. A setting has
been added to the preferences screen to point Anki to a local server, and a
similar setting is likely to come to AnkiMobile soon.

Documentation is available here: <https://docs.ankiweb.net/sync-server.html>

In addition to the new server and refactoring, this PR also makes changes to the
sync protocol. The existing sync protocol places payloads and metadata inside a
multipart POST body, which causes a few headaches:

- Legacy clients build the request in a non-deterministic order, meaning the
entire request needs to be scanned to extract the metadata.
- Reqwest's multipart API directly writes the multipart body, without exposing
the resulting stream to us, making it harder to track the progress of the
transfer. We've been relying on a patched version of reqwest for timeouts,
which is a pain to keep up to date.

To address these issues, the metadata is now sent in a HTTP header, with the
data payload sent directly in the body. Instead of the slower gzip, we now
use zstd. The old timeout handling code has been replaced with a new implementation
that wraps the request and response body streams to track progress, allowing us
to drop the git dependencies for reqwest, hyper-timeout and tokio-io-timeout.

The main other change to the protocol is that one-way syncs no longer need to
downgrade the collection to schema 11 prior to sending.
2023-01-18 12:43:46 +10:00
RumovZ
1be30573e1
Replace dissimilar crate with difflib (#2322)
This also inserts the expected text if it's missing at the very
beginning of the provided text.
2023-01-16 09:49:34 +10:00
RumovZ
6df7be90c5
Fix negated introduced:x search (#2306)
1. Add outer brackets.
2. Coalesce aggregate, because `null and true` is `null` in SQL land,
so cards that were not introduced, but manually rescheduled in the
period of interest, would not show up in a negated search.
2023-01-10 08:49:35 +10:00
Damien Elmes
07fd88ddea Allow timestamps to be a day ahead
https://github.com/ankitects/anki/issues/1895#issuecomment-1374574230
2023-01-09 10:04:48 +10:00
dependabot[bot]
4e92c29815
Bump tokio from 1.23.0 to 1.23.1 (#2303)
Bumps [tokio](https://github.com/tokio-rs/tokio) from 1.23.0 to 1.23.1.
- [Release notes](https://github.com/tokio-rs/tokio/releases)
- [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.23.0...tokio-1.23.1)

---
updated-dependencies:
- dependency-name: tokio
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-07 10:01:56 +10:00
Damien Elmes
8206f39bdc Fix sparse today stats
Not safe to assume revlog is ordered by query
2023-01-05 10:14:37 +10:00
Damien Elmes
0eddb25287
Integrate AnkiDroid's backend patches into the repo (#2290)
* Relax chrono specification for AnkiDroid

https://github.com/ankidroid/Anki-Android-Backend/pull/251

* Add AnkiDroid service and AnkiDroid customizations

Most of the work here was done by David in the Backend repo; integrating
it into this repo for ease of future maintenance.

Based on 5d9f262f4c
with some tweaks:

- Protobuf imports have been fixed to match the recent refactor
- FatalError has been renamed to AnkidroidPanicError
- Tweaks to the desktop code to deal with the extra arg to open_collection,
and exclude AnkiDroid service methods from our Python code.

* Refactor AnkiDroid's DB code to avoid uses of unsafe
2023-01-03 13:11:23 +10:00
Damien Elmes
4c5a2461d0 Handle broken utimes() on Android
Closes https://github.com/ankidroid/Anki-Android/issues/12827
Closes https://github.com/ankidroid/Anki-Android/issues/12974
2022-12-28 16:33:06 +10:00
Damien Elmes
a38121a713 Add Debug impl for Collection 2022-12-24 10:44:41 +10:00
Damien Elmes
a82f8d6872 Remove unused before_upload method 2022-12-24 10:44:41 +10:00
Damien Elmes
0570cfdf48 Migrate from slog to tracing
The Rust community appear to have converged on tracing - it's used by
the Rust compiler, and receives close to 10x the number of downloads
that slog does. Its API is more ergonomic, and it does a much nicer
job with async rust.

To make this change, we no longer pass around explicit loggers, and rely
on a globally-registered one. The log file location has been changed
from one in each profile folder to a single one in the base folder. This
will remain empty for most users, since only errors are logged by default,
but may be useful for debugging future changes.
2022-12-24 10:44:40 +10:00
Damien Elmes
d294fff882 Simplify solution for #2279 2022-12-19 18:13:34 +10:00
Damien Elmes
67acaa17e4 Fix panic when adding non-Latin text to fields
Closes #2279
2022-12-19 18:02:43 +10:00
TRIAEIOU
9901ae428a
Nested clozes and increased cloze meta data (#2141)
* Nested clozes and increased cloze meta data

* Update contributors

* This reverts commit 3423df73f89f04a606b1bff3542a68a49ca52e9f.

* Update CONTRIBUTORS

* Formating

* Formating

* Formating

* Formating

* Formating

* Formating

* Formating

* Formating

* Code refactor

* Formating

* Formating

* Formating

* Formating and dead code

* Correct test case

* Remove Hint and Close storage of token string

* Update

* Formating

* Formating

* Formating

* Use write! instead of .push_str(&format).

* Formating
2022-12-19 12:03:15 +10:00
Damien Elmes
0555f4898c Update crate name in docstrings to fix 'cargo test' 2022-12-16 22:30:54 +10:00
Damien Elmes
37151213cd Move more of the graph processing into the backend
The existing architecture serializes all cards and revlog entries in
the search range into a protobuf message, which the web frontend needs
to decode and then process. The thinking at the time was that this would
make it easier for add-ons to add extra graphs, but in the ~2.5 years
since the new graphs were introduced, no add-ons appear to have taken
advantage of it.

The cards and revlog entries can grow quite large on large collections -
on a collection I tested with approximately 2.5M reviews, the serialized
data is about 110MB, which is a lot to have to deserialize in JavaScript.

This commit shifts the preliminary processing of the data to the Rust end,
which means the data is able to be processed faster, and less needs to
be sent to the frontend. On the test collection above, this reduces the
serialized data from about 110MB to about 160KB, resulting in a more
than 2x performance improvement, and reducing frontend memory usage from
about 400MB to about 40MB.

This also makes #2043 more feasible - while it is still about 50-100%
slower than protobufjs, with the much smaller message size, the difference
is only about 10ms.
2022-12-16 21:42:17 +10:00
Damien Elmes
fa625d7ad8
Minor Rust cleanups (#2272)
* Run cargo +nightly fmt

* Latest prost-build includes clippy workaround

* Tweak Rust protobuf imports

- Avoid use of stringify!(), as JetBrains editors get confused by it
- Stop merging all protobuf symbols into a single namespace

* Remove some unnecessary qualifications

Found via IntelliJ lint

* Migrate some asserts to assert_eq/ne

* Remove mention of node_modules exclusion

This no longer seems to be necessary after migrating away from Bazel,
and excluding it means TS/Svelte files can't be edited properly.
2022-12-16 21:40:27 +10:00
Damien Elmes
22ecef6fb2
Merge pull request #2255 from RumovZ/replace-pct-str-2
Replace pct-str with local percent_encoding crate
2022-12-16 11:21:04 +00:00
Damien Elmes
35cbde65d7 Revert "Always scan for media changes"
This reverts commit 09cb8b3cf6.

Overhead on larger folders/slower devices is more than I originally
anticipated, and can run into multiple seconds. This seems to be
particularly egregious on mobile, which I presume is due to sandboxing
overhead.
2022-12-15 20:40:20 +10:00
Damien Elmes
86b52f7626 Add a small unit test
pct-str encoded the / character as well, but the difference shouldn't
matter in our case.
2022-12-12 14:54:11 +10:00
RumovZ
c888ccc285 Replace pct-str with local ascii_percent_encoding 2022-12-09 11:49:39 +01:00
RumovZ
0b206b8a81 Ignore non-ASCII chars in ascii_percent_encoding 2022-12-09 11:47:59 +01:00
RumovZ
55f27779cf Add local copy of percent_encoding crate 2022-12-09 11:46:00 +01:00
RumovZ
0e7f02bfb7
Update Chrono Crate (#2242)
* Remove deprecated `and_hms()`

* Update chrono

* Update licenses and fix script

* Remove deprecated Date struct

* Remove chrono pin

* Skip format check on .vscode

Was failing for no reason.

* Replace deprecated chrono functions

* Add cargo-deny to update-licenses & pin versions (dae)

* Remove time 0.1 dependency  (dae)

We don't need to wait for chrono 0.5; it was provided behind a legacy
feature flag.
2022-12-07 17:00:14 +10:00
Damien Elmes
7e3ac53869 Allow unit tests to pass even when Qt translations overridden 2022-12-04 14:25:54 +10:00
RumovZ
90a3bbfdb8
Cap calculated hard delay secs at again delay + 1d (#2229) 2022-12-03 18:54:40 +10:00
Damien Elmes
e3167c4e3c Update incompatible crates 2022-11-30 12:38:10 +10:00
Damien Elmes
b4f4df0030 Pin chrono; update compatible Rust crates
Also pin reqwest in our other crates
2022-11-30 12:38:10 +10:00
Damien Elmes
0ac7969e2a Use workspace package info in more crates; mark private for cargo-deny 2022-11-30 12:19:56 +10:00
Damien Elmes
e497a56f54 Re-enable formatting for .toml files 2022-11-28 09:16:28 +10:00
Damien Elmes
7972708029 Remove rslib cargo config
Was added during initial Bazel experimentation back in 2020
2022-11-27 17:50:36 +10:00
Damien Elmes
5e0a761b87
Move away from Bazel (#2202)
(for upgrading users, please see the notes at the bottom)

Bazel brought a lot of nice things to the table, such as rebuilds based on
content changes instead of modification times, caching of build products,
detection of incorrect build rules via a sandbox, and so on. Rewriting the build
in Bazel was also an opportunity to improve on the Makefile-based build we had
prior, which was pretty poor: most dependencies were external or not pinned, and
the build graph was poorly defined and mostly serialized. It was not uncommon
for fresh checkouts to fail due to floating dependencies, or for things to break
when trying to switch to an older commit.

For day-to-day development, I think Bazel served us reasonably well - we could
generally switch between branches while being confident that builds would be
correct and reasonably fast, and not require full rebuilds (except on Windows,
where the lack of a sandbox and the TS rules would cause build breakages when TS
files were renamed/removed).

Bazel achieves that reliability by defining rules for each programming language
that define how source files should be turned into outputs. For the rules to
work with Bazel's sandboxing approach, they often have to reimplement or
partially bypass the standard tools that each programming language provides. The
Rust rules call Rust's compiler directly for example, instead of using Cargo,
and the Python rules extract each PyPi package into a separate folder that gets
added to sys.path.

These separate language rules allow proper declaration of inputs and outputs,
and offer some advantages such as caching of build products and fine-grained
dependency installation. But they also bring some downsides:

- The rules don't always support use-cases/platforms that the standard language
tools do, meaning they need to be patched to be used. I've had to contribute a
number of patches to the Rust, Python and JS rules to unblock various issues.
- The dependencies we use with each language sometimes make assumptions that do
not hold in Bazel, meaning they either need to be pinned or patched, or the
language rules need to be adjusted to accommodate them.

I was hopeful that after the initial setup work, things would be relatively
smooth-sailing. Unfortunately, that has not proved to be the case. Things
frequently broke when dependencies or the language rules were updated, and I
began to get frustrated at the amount of Anki development time I was instead
spending on build system upkeep. It's now about 2 years since switching to
Bazel, and I think it's time to cut losses, and switch to something else that's
a better fit.

The new build system is based on a small build tool called Ninja, and some
custom Rust code in build/. This means that to build Anki, Bazel is no longer
required, but Ninja and Rust need to be installed on your system. Python and
Node toolchains are automatically downloaded like in Bazel.

This new build system should result in faster builds in some cases:

- Because we're using cargo to build now, Rust builds are able to take advantage
of pipelining and incremental debug builds, which we didn't have with Bazel.
It's also easier to override the default linker on Linux/macOS, which can
further improve speeds.
- External Rust crates are now built with opt=1, which improves performance
of debug builds.
- Esbuild is now used to transpile TypeScript, instead of invoking the TypeScript
compiler. This results in faster builds, by deferring typechecking to test/check
time, and by allowing more work to happen in parallel.

As an example of the differences, when testing with the mold linker on Linux,
adding a new message to tags.proto (which triggers a recompile of the bulk of
the Rust and TypeScript code) results in a compile that goes from about 22s on
Bazel to about 7s in the new system. With the standard linker, it's about 9s.

Some other changes of note:

- Our Rust workspace now uses cargo-hakari to ensure all packages agree on
available features, preventing unnecessary rebuilds.
- pylib/anki is now a PEP420 implicit namespace, avoiding the need to merge
source files and generated files into a single folder for running. By telling
VSCode about the extra search path, code completion now works with generated
files without needing to symlink them into the source folder.
- qt/aqt can't use PEP420 as it's difficult to get rid of aqt/__init__.py.
Instead, the generated files are now placed in a separate _aqt package that's
added to the path.
- ts/lib is now exposed as @tslib, so the source code and generated code can be
provided under the same namespace without a merging step.
- MyPy and PyLint are now invoked once for the entire codebase.
- dprint will be used to format TypeScript/json files in the future instead of
the slower prettier (currently turned off to avoid causing conflicts). It can
automatically defer to prettier when formatting Svelte files.
- svelte-check is now used for typechecking our Svelte code, which revealed a
few typing issues that went undetected with the old system.
- The Jest unit tests now work on Windows as well.

If you're upgrading from Bazel, updated usage instructions are in docs/development.md and docs/build.md. A summary of the changes:

- please remove node_modules and .bazel
- install rustup (https://rustup.rs/)
- install rsync if not already installed  (on windows, use pacman - see docs/windows.md)
- install Ninja (unzip from https://github.com/ninja-build/ninja/releases/tag/v1.11.1 and
  place on your path, or from your distro/homebrew if it's 1.10+)
- update .vscode/settings.json from .vscode.dist
2022-11-27 15:24:20 +10:00
Stefan Kangas
5551a37f03
Fix typos (#2210) 2022-11-24 20:18:57 +10:00
Damien Elmes
ade2738458 Fix invalid utf8 not being detected
- Rusqlite was returning a different error
- The sort field may also contain invalid utf8
2022-11-22 13:27:20 +10:00
Damien Elmes
063623af3c Format .toml files with dprint 2022-11-09 20:03:49 +10:00
dobefore
0c9cecd7a3
Fix error while compiling rslib (#2187)
* protobuf generate error

pass --experimental_allow_proto3_optional to protoc
add missing dereive trait to struct Daylimit

* delete invalid derive trait Eq

* remove argument from protoc

--experimental_allow_proto3_optional
2022-11-09 12:36:23 +10:00
Damien Elmes
c9a9f38ea9 Remove untranslated 'see old deck options' notice
It was intended to be a temporary message, and it's been about 15 months.

https://forums.ankiweb.net/t/anki-2-1-55-beta-3/24295/42
2022-11-03 12:05:19 +10:00
Abdo
95d0c78b78
Fix issue when dragged deck is a prefix of target (#2149) 2022-10-29 09:42:34 +10:00
Abdo
abbfdbf420
Fix grandchild tag not being reparentable under the same root (#2148)
Introduced in #2146
2022-10-29 09:39:44 +10:00
Abdo
951c80a4e9
Fix some issues with tag reparenting (#2146)
* Fix reparented_name not correctly handling tags that are prefixes of the
new parent

To reproduce the issue:
1. Add two tags: `a` and `ab`.
2. From the browser's sidebar, drag & drop `a` into `ab`.

Result: panic

* Fix reparent_tags panicking if new parent is a child of source tag

This is the "foo, foo::bar" case that should be a no-op.

* Add more tests for tag reparenting
2022-10-28 21:42:10 +10:00
RumovZ
c521753057
Refactor error handling (#2136)
* Add crate snafu

* Replace all inline structs in AnkiError

* Derive Snafu on AnkiError

* Use snafu for card type errors

* Use snafu whatever error for InvalidInput

* Use snafu for NotFoundError and improve message

* Use snafu for FileIoError to attach context

Remove IoError.
Add some context-attaching helpers to replace code returning bare
io::Errors.

* Add more context-attaching io helpers

* Add message, context and backtrace to new snafus

* Utilize error context and backtrace on frontend

* Rename LocalizedError -> BackendError.
* Remove DocumentedError.
* Have all backend exceptions inherit BackendError.

* Rename localized(_description) -> message

* Remove accidentally committed experimental trait

* invalid_input_context -> ok_or_invalid

* ensure_valid_input! -> require!

* Always return `Err` from `invalid_input!`

Instead of a Result to unwrap, the macro accepts a source error now.

* new_tempfile_in_parent -> new_tempfile_in_parent_of

* ok_or_not_found -> or_not_found

* ok_or_invalid -> or_invalid

* Add crate convert_case

* Use unqualified lowercase type name

* Remove uses of snafu::ensure

* Allow public construction of InvalidInputErrors (dae)

Needed to port the AnkiDroid changes.

* Make into_protobuf() public (dae)

Also required for AnkiDroid. Not sure why it worked previously - possible
bug in older Rust version?
2022-10-21 18:02:12 +10:00
RumovZ
b8c294bf4d
Explicitly evaluate symlink on Windows (#2135) 2022-10-19 20:08:58 +10:00
Damien Elmes
fb9c934ef2 Use protoc from Bazel if missing from path
Closes #2134
2022-10-17 09:58:51 +10:00
Damien Elmes
cfb309e6b3 Update Rust deps 2022-09-24 13:22:46 +10:00
Damien Elmes
a39a3b4d34 Update to latest rules_rust and Rust 1.64 2022-09-24 11:12:58 +10:00
Damien Elmes
b97a8bfa26 Fix daily counts being included in apkg import
Fixes https://github.com/ankidroid/Anki-Android/issues/12477
2022-09-24 09:05:29 +10:00
Damien Elmes
09cb8b3cf6 Always scan for media changes
The check runs in the background, and the dentry cache and SQLite
page cache should make this more efficient after the first run.

Closes #1736
2022-09-20 16:13:10 +10:00
RumovZ
e7af0febb1
More template checks (#2032)
* Show warning if multiple type boxes are used

* Report templates referencing media in Media Check

* Apply suggestions from code review

* Fix media-check.ftl

* Only report media references with fields

Like `<img src={{Front}}>`.
Also report Anki sound tags and latex.

* Loop existing media regexes
2022-09-05 16:52:25 +10:00
RumovZ
e39fb74e82
Enable state-dependent custom scheduling data (#2049)
* Enable state-dependent custom scheduling data

* Next(Card)States -> SchedulingStates

The fact that `current` was included in `next` always bothered me,
and custom data is part of the card state, so that was a bit confusing
too.

* Store custom_data in SchedulingState

* Make custom_data optional when answering

Avoids having to send it 4 extra times to the frontend, and avoids the
legacy answerCard() API clobbering the stored data.

Co-authored-by: Damien Elmes <gpg@ankiweb.net>
2022-09-05 16:48:01 +10:00
Damien Elmes
0bcb3a3564 Add non-legacy backend interface for adding new decks 2022-09-04 14:12:29 +10:00
RumovZ
1f8189fe91
Some import/export features and fixes (#2038)
* Keep filtered decks when importing apkg

If all original decks exist and scheduling is included.

* Create missing decks from csv

* Export original decks if with_scheduling

* Also remap original deck ids on import

* Update imported filtered decks

* Fix meta column being mapped to tags

* Fix ids in csv deck and notetype columns

Note: This implies names which parse to an i64 will be seen as ids,
likely resulting in the intended deck/notetype not being found.

* Check for scheduling with revlog and deck configs

Might help with cases in which scheduling was included, but all cards
are new. In such a case, filtered deck should not be converted.

* Fix duplicate with same GUID being created

* Remove redundant `distinct`s from sql query

* Match notes by _either_ guid _or_ first field

* Refactor to emphasise GUID/first field distinction

* Export default deck and config if with scheduling

* Fix default deck being exported if it's a parent
2022-09-03 11:29:06 +10:00
RumovZ
31b7464c67
Add card meta for persisting custom scheduling state (#2040)
* Add card meta for persisting custom scheduling state

* Rename meta -> custom_data

* Enforce limits on size of custom data

Large values will slow down table scans of the cards table, and it's
easier to be strict now and possibly relax things in the future than
the opposite.

* Pack card states and customData into a single message

+ default customData to empty if it can't be parsed

Co-authored-by: Damien Elmes <gpg@ankiweb.net>
2022-09-02 11:22:49 +10:00
Matthias Metelka
d110c4916c
Introduce setting to collapse field by default (#1990)
* Introduce setting to collapse field by default

* Fix schema order

* Change wording from adjective to imperative

sounds a bit less clunky

* Update rslib/src/notetype/schema11.rs (dae)

* Keep settings in single column

* Add back Toggle Visual Editor string

* Add RichTextBadge component and show it conditionally

* Reverse input order depending on default setting

* Make PlainTextInput border-radius responsive to toggle states

* Prevent first Collapsible transition differently

* Focus inputs after Collapsible transition

The double tick calls are just a temporary solution until I find the exact moment an input is focusable again.

* Use requestAnimationFrame to await focusable state

Note: Svelte tick doesn't seem to work in this scenario.
2022-08-31 23:34:39 +10:00
RumovZ
79fbb6c8d8
Keep content of unmapped fields when importing (#2023)
* Keep content of unmapped fields when importing

* Test new behaviour

* Fix typo in `canonify_tags_without_resgistering`

* Log updated note instead of original one

* Revert merging imported tags

But keep old note tags if no new ones are provided.
2022-08-24 16:04:32 +10:00
Matthias Metelka
d1cbb86178
Default input setting in fields dialog (#1987)
* Introduce field setting to use plain text editor by default

* Remove leftover function from #1476

* Use boolean instead of string

* Simplify clear_other_field_duplicates

* Convert plain text key to camelCase

* Move HTML item below the existing checkbox, instead of to the right (dae)

Showing it on the right is more space efficient, but feels a bit
cluttered IMHO.
2022-08-18 12:30:18 +10:00
Damien Elmes
bd2f866c68 Fix {{CardFlags}} not working with flags 5-7
https://forums.ankiweb.net/t/please-activate-flags-5-7-on-the-cardflag/22143
2022-08-08 10:56:07 +10:00
Damien Elmes
f017525ea8 Handle deeply nested decks 2022-08-03 14:37:56 +10:00
Damien Elmes
340da23104 Wrap type answer output in code tag; remove extra div
This would make more sense as a class on the outer div, but existing
templates reference code#typeans, so avoid breaking things for now.
2022-07-22 20:37:56 +10:00
Damien Elmes
ad8c23fc3d Remove resolved comment 2022-07-22 20:06:45 +10:00
Damien Elmes
09eef8e746 Spelling fix 2022-07-22 19:32:31 +10:00
Damien Elmes
1e0be26b7e Partially migrate type answer to backend
Partially completes #1068, and will allow mobile clients to drop
their separate diff-match-patch imports. Does not yet try to handle
case folding or combining-char stripping, and leaves some of the outer
HTML wrapping up to the frontend for now.

The logic for rendering the provided string has changed: missing chars
are now only inserted if they follow a correct section, and the original
text is shown instead of hyphens. This is an experiment, and can be
changed if it's not well received.
2022-07-22 19:28:34 +10:00
RumovZ
173a5bfed5
Improve temporary table handling (#1976)
* Use all_cards_for_search() helper

* Add CardTableGuard

* Add for_each_card_in_search() helper

* Add all_cards_for_search_in_order() helper

* Add all_cards_for_ids() helper

* Return siblings for bury instead of only searching

* Remove redundant clear_searched_cards_table() calls

* Add with_searched_cards_table()

* Remove false comment

* Add NoteTableGuard

* Add with_ids_in_searched_notes_table() helper

* Make some last routines use table helpers
2022-07-22 17:51:26 +10:00
RumovZ
09841c7c0f
Populate media DB on import (#1977)
* Refactor MediaManager transactions

* Add media entries when importing

* Fix legacy apkg import not setting csum

Also test for this in the roundtrip test.

* Avoid reallocating MediaCopier's buffer

* Make sha1 optional (dae)
2022-07-22 17:50:15 +10:00
RumovZ
b9fd6688d2
Fix field check for cloze conditional (#1975) 2022-07-20 20:26:26 +10:00
RumovZ
5f9fa51026
Fix missing limits field in tests (#1973) 2022-07-19 18:51:32 +10:00
RumovZ
cc929687ae
Deck-specific Limits (#1955)
* Add deck-specific limits to DeckNormal

* Add deck-specific limits to schema11

* Add DeckLimitsDialog

* deck_limits_qt6.py needs to be a symlink

* Clear duplicate deck setting keys on downgrade

* Export deck limits when exporting with scheduling

* Revert "deck_limits_qt6.py needs to be a symlink"

This reverts commit 4ee7be1e10c4e8c49bb20de3bf45ac18b5e2d4f6.

* Revert "Add DeckLimitsDialog"

This reverts commit eb0e2a62d33df0b518d9204a27b09e97966ce82a.

* Add day limits to DeckNormal

* Add deck and day limits mock to deck options

* Revert "Add deck and day limits mock to deck options"

This reverts commit 0775814989e8cb486483d06727b1af266bb4513a.

* Add Tabs component for daily limits

* Add borders to tabs component

* Revert "Add borders to tabs component"

This reverts commit aaaf5538932540f944d92725c63bb04cfe97ea14.

* Implement tabbed limits properly

* Add comment to translations

* Update rslib/src/decks/limits.rs

Co-authored-by: Damien Elmes <dae@users.noreply.github.com>

* Fix camel case in clear_other_duplicates()

* day_limit → current_limit

* Also import day limits

* Remember last used day limits

* Add day limits to schema 11

* Tweak comment (dae)

* Exclude day limit in export (dae)

* Tweak tab wording (dae)

* Update preset limits on preset change

* Explain tabs in tooltip (dae)

* Omit deck and today limits if v2 is enabled

* Preserve deck limit when switching to today limit
2022-07-19 18:27:25 +10:00
Matthias Metelka
d1ba48bc48
Expose cloze text as HTML attribute on question side (#1968)
* Expose cloze text as attribute on front side

* Update test_models.py

* Update template_filters.rs

* Escape HTML for data-attribute

* Use minimal HTML encoding in Rust

to match Python's html.escape and pass tests.

* Rename attribute to data-cloze

to make it more generic.

* Run formatter

* Revert to using Rust encode_attribute and add helper function for tests
2022-07-19 04:22:57 +10:00
RumovZ
f6390e9455
Adjust remaining steps after config update (#1956)
* Adjust remaining steps after config update

* Handle relearning steps separately

Also refactor a lot.

* Also adjust remaining steps after deck change

* Test step adjustment after config update

* Fix `SearchBuilder::(re)learning_cards()`

* Fix step adjustment after deck change

* Test step adjustment after deck change

* Fix test name

* Readjust remaining steps according to last delay

Also atomize tests and add some tooling.
2022-07-14 11:24:34 +10:00
RumovZ
8c515e316e
Template err improvements (#1953)
* Throw error for unknown condition fields as well

So if 'foo' is not a field, refuse to save a template containing
`{{#foo}}bar{{/foo}}`. Previously, only `{{foo}}` would be checked.
As a side effect, templates which *only* contain fields as conditions
may be saved. Meh.

* Display template errors in q/a columns only

So the affected browser row remains active and the user can fix the
template more easily.

* Specify if error occured in a browser template

* Minor wording tweak (dae)

There's an argument for using the exact wording as well, but this just
reads a little more naturally to me.
2022-07-09 13:00:03 +10:00
Sam Penny
9dcceff4af
Remember previous choices in reposition dialog (#1950)
* remember previous choices in reposition dialog

* remember previous choice for randomize option as well

* fix failing test
2022-07-08 11:28:38 +10:00
RumovZ
2bbd1fca2a
Skip card generation if fronts remain unchanged (#1949)
Closes #1945
2022-07-06 19:26:14 +10:00
RumovZ
bebc6fd23a
Disregard manual reschedulings in introduced:x (#1932) 2022-06-27 17:20:36 +10:00
Damien Elmes
5ea78e1c8e Since DupeResolution is in CsvMetadata, we don't need to pass it separately
Follow-up to #1930
2022-06-27 17:15:54 +10:00
Damien Elmes
67e4edcd8b Expose backend_proto publicly for AnkiDroid, and rename to pb
We were aliasing it on import half the time anyway
2022-06-27 15:27:53 +10:00
Damien Elmes
6ff159304d Fix backups failing on Android 2022-06-27 15:27:51 +10:00
RumovZ
b6a7760cb4
Restore and save last dupe resolution setting (#1930)
* Restore dupe resolution setting

* Save dupe resolution setting

* Push config logic into backend (dae)
2022-06-24 15:10:06 +10:00
RumovZ
8478492190
Check ids when gathering data (#1928)
This will throw an error if a card, note or revlog id from the future
is found during apkg import or export.
2022-06-24 13:56:52 +10:00
RumovZ
1b8bab73d1
Csv import tweaks (#1920)
* Don't use special label for tags column

This was setting the label of whichever column the user chose for tags
to "Tags". Showing field content is more helpful.

* Map tags column automatically like fields
2022-06-22 09:44:12 +10:00
Damien Elmes
4d51ee8a64 Fix number of days in a (standard) year
https://forums.ankiweb.net/t/prop-ivl-faulty/20665
2022-06-13 09:09:27 +10:00
Damien Elmes
f15e294981 Fix collection load failure on Android
Loading a larger collection with V16 enabled was failing in openCollection
with error 6410, similar to https://github.com/simolus3/drift/issues/876
2022-06-10 23:21:35 +10:00
RumovZ
6da5e5b042
CSV import/export fixes and features (#1898)
* Fix footer moving upwards

* Fix column detection

Was broken because escaped line breaks were not considered.
Also removes delimiter detection on `#columns:` line. User must use tabs
or set delimiter beforehand.

* Add CSV preview

* Parse `#tags column:`

* Optionally export deck and notetype with CSV

* Avoid clones in CSV export

* Prevent bottom of page appearing under footer (dae)

* Increase padding to 1em (dae)

With 0.5em, when a vertical scrollbar is shown, it sits right next to
the right edge of the content, making it look like there's no right
margin.

* Experimental changes to make table fit+scroll (dae)

- limit individual cells to 15em, and show ellipses when truncated
- limit total table width to body width, so that inner table is shown
with scrollbar
- use class rather than id - ids are bad practice in Svelte components,
as more than one may be displayed on a single page

* Skip importing foreign notes with filtered decks

Were implicitly imported into the default deck before.
Also some refactoring to fetch deck ids and names beforehand.

* Hide spacer below hidden field mapping

* Fix guid being replaced when updating note

* Fix dupe identity check

Canonify tags before checking if dupe is identical, but only add update
tags later if appropriate.

* Fix deck export for notes with missing card 1

* Fix note lines starting with `#`

csv crate doesn't support escaping a leading comment char. :(

* Support import/export of guids

* Strip HTML from preview rows

* Fix initially set deck if current is filtered

* Make isHtml toggle reactive

* Fix `html_to_text_line()` stripping sound names

* Tweak export option labels

* Switch to patched rust-csv fork

Fixes writing lines starting with `#`, so revert 5ece10ad05f331.

* List column options with first column field

* Fix flag for exports with HTML stripped
2022-06-09 10:28:01 +10:00
Damien Elmes
59ee399c5f Update regex crate for CVE 2022-06-07 08:39:07 +10:00
RumovZ
42cbe42f06
Plaintext import/export (#1850)
* Add crate csv

* Add start of csv importing on backend

* Add Menomosyne serializer

* Add csv and json importing on backend

* Add plaintext importing on frontend

* Add csv metadata extraction on backend

* Add csv importing with GUI

* Fix missing dfa file in build

Added compile_data_attr, then re-ran cargo/update.py.

* Don't use doubly buffered reader in csv

* Escape HTML entities if CSV is not HTML

Also use name 'is_html' consistently.

* Use decimal number as foreign ease (like '2.5')

* ForeignCard.ivl → ForeignCard.interval

* Only allow fixed set of CSV delimiters

* Map timestamp of ForeignCard to native due time

* Don't trim CSV records

* Document use of empty strings for defaults

* Avoid creating CardGenContexts for every note

This requires CardGenContext to be generic, so it works both with an
owned and borrowed notetype.

* Show all accepted file types  in import file picker

* Add import_json_file()

* factor → ease_factor

* delimter_from_value → delimiter_from_value

* Map columns to fields, not the other way around

* Fallback to current config for csv metadata

* Add start of new import csv screen

* Temporary fix for compilation issue on Linux/Mac

* Disable jest bazel action for import-csv

Jest fails with an error code if no tests are available, but this would
not be noticable on Windows as Jest is not run there.

* Fix field mapping issue

* Revert "Temporary fix for compilation issue on Linux/Mac"

This reverts commit 21f8a261408cdae49ec031aa21a1b659c4f66d82.

* Add HtmlSwitch and move Switch to components

* Fix spacing and make selectors consistent

* Fix shortcut tooltip

* Place import button at the top with path

* Fix meta column indices

* Remove NotetypeForString

* Fix queue and type of foreign cards

* Support different dupe resolution strategies

* Allow dupe resolution selection when importing CSV

* Test import of unnormalized text

Close  #1863.

* Fix logging of foreign notes

* Implement CSV exports

* Use db_scalar() in notes_table_len()

* Rework CSV metadata

- Notetypes and decks are either defined by a global id or by a column.
- If a notetype id is provided, its field map must also be specified.
- If a notetype column is provided, fields are now mapped by index
instead of name at import time. So the first non-meta column is used for
the first field of every note, regardless of notetype. This makes
importing easier and should improve compatiblity with files without a
notetype column.
- Ensure first field can be mapped to a column.
- Meta columns must be defined as `#[meta name]:[column index]` instead
of in the `#columns` tag.
- Column labels contain the raw names defined by the file and must be
prettified by the frontend.

* Adjust frontend to new backend column mapping

* Add force flags for is_html and delimiter

* Detect if CSV is HTML by field content

* Update dupe resolution labels

* Simplify selectors

* Fix coalescence of oneofs in TS

* Disable meta columns from selection

Plus a lot of refactoring.

* Make import button stick to the bottom

* Write delimiter and html flag into csv

* Refetch field map after notetype change

* Fix log labels for csv import

* Log notes whose deck/notetype was missing

* Fix hiding of empty log queues

* Implement adding tags to all notes of a csv

* Fix dupe resolution not being set in log

* Implement adding tags to updated notes of a csv

* Check first note field is not empty

* Temporary fix for build on Linux/Mac

* Fix inverted html check (dae)

* Remove unused ftl string

* Delimiter → Separator

* Remove commented-out line

* Don't accept .json files

* Tweak tag ftl strings

* Remove redundant blur call

* Strip sound and add spaces in csv export

* Export HTML by default

* Fix unset deck in Mnemosyne import

Also accept both numbers and strings for notetypes and decks in JSON.

* Make DupeResolution::Update the default

* Fix missing dot in extension

* Make column indices 1-based

* Remove StickContainer from TagEditor

Fixes line breaking, border and z index on ImportCsvPage.

* Assign different key combos to tag editors

* Log all updated duplicates

Add a log field for the true number of found notes.

* Show identical notes as skipped

* Split tag-editor into separate ts module (dae)

* Add progress for CSV export

* Add progress for text import

* Tidy-ups after tag-editor split (dae)

- import-csv no longer depends on editor
- remove some commented lines
2022-06-01 20:26:16 +10:00
dobefore
b4cffe2339
rust impl stdError trait (#1890)
* impl stdError trait

* Update CONTRIBUTORS
2022-05-31 17:52:12 +10:00
Damien Elmes
5d835ae813 Always generate missing cards when saving card templates
https://forums.ankiweb.net/t/anki-doesnt-generate-cards-after-un-doing-selective-card-generation/20283
2022-05-26 12:44:57 +10:00
Damien Elmes
82196753ec Rework display of available cards in custom study
In v3, it's more informative to show the count of child decks separately,
since increasing the limit of the current deck does not increase the limits
of child decks. When we rework the decks list in the future, a tooltip
will hopefully provide an easier way for users to see where cards are
available, and where limits are being applied.

Closes #1868
2022-05-20 17:52:02 +10:00
RumovZ
6c807c1b2e
Fix review queue if limit is reached (#1855) 2022-05-10 12:11:35 +10:00
Damien Elmes
d946e5ddd5
Fix for crash with invalid dates on Windows (#1837)
https://forums.ankiweb.net/t/bug-report-crashing-when-opening-deck-browser/19768

Caused by a note mtime that was 1000x larger than it should have been.
Check DB will now fix this case (but there are others it still does not
cover, such as invalid card/note IDs).

https://docs.rs/chrono/0.4.19/x86_64-pc-windows-msvc/src/chrono/sys/windows.rs.html#128
2022-05-07 10:30:23 +10:00
Damien Elmes
c8a4e5ea22 Add more progress + abort points to export code
The old `media_files_did_export` hook has been kept around for use with
the legacy apkg exporter (an add-on uses it), and a new 
`legacy_export_progress` hook has been added so we can get progress
from the new colpkg exporter until we move over fully to the new code.
2022-05-06 15:32:23 +10:00
RumovZ
99c4ec93d2
Fix some apkg import issues (#1836)
* Allow gathering of orphaned decks

* Skip redundant media checking in apkg import
2022-05-06 09:22:15 +10:00
Damien Elmes
ac1cbaf719 Fix implicit transaction not being cleared on transact() failure
"release foo" clears the implicit outer transaction, but "rollback to foo"
does not.
2022-05-05 13:39:41 +10:00
Damien Elmes
1297beff63 Minor progress tweaks to apkg importing
- Set progress as soon as possible; previously the extracting and
gathering steps happened prior to the first progress.
- Add "extracting" and "gathering" progress steps, so that large imports
show more feedback in the early stage, and can be more quickly
interrupted.
2022-05-05 12:23:05 +10:00
RumovZ
5f9451f547
Add apkg import/export on backend (#1743)
* Add apkg export on backend

* Filter out missing media-paths at write time

* Make TagMatcher::new() infallible

* Gather export data instead of copying directly

* Revert changes to rslib/src/tags/

* Reuse filename_is_safe/check_filename_safe()

* Accept func to produce MediaIter in export_apkg()

* Only store file folder once in MediaIter

* Use temporary tables for gathering

export_apkg() now accepts a search instead of a deck id. Decks are
gathered according to the matched notes' cards.

* Use schedule_as_new() to reset cards

* ExportData → ExchangeData

* Ignore ascii case when filtering system tags

* search_notes_cards_into_table →

search_cards_of_notes_into_table

* Start on apkg importing on backend

* Fix due dates in days for apkg export

* Refactor import-export/package

- Move media and meta code into appropriate modules.
- Normalize/check for normalization when deserializing media entries.

* Add SafeMediaEntry for deserialized MediaEntries

* Prepare media based on checksums

- Ensure all existing media files are hashed.
- Hash incoming files during preparation to detect conflicts.
- Uniquify names of conflicting files with hash (not notetype id).
- Mark media files as used while importing notes.
- Finally copy used media.

* Handle encoding in `replace_media_refs()`

* Add trait to keep down cow boilerplate

* Add notetypes immediately instaed of preparing

* Move target_col into Context

* Add notes immediately instaed of preparing

* Note id, not guid of conflicting notes

* Add import_decks()

* decks_configs → deck_configs

* Add import_deck_configs()

* Add import_cards(), import_revlog()

* Use dyn instead of generic for media_fn

Otherwise, would have to pass None with type annotation in the default
case.

* Fix signature of import_apkg()

* Fix search_cards_of_notes_into_table()

* Test new functions in text.rs

* Add roundtrip test for apkg (stub)

* Keep source id of imported cards (or skip)

* Keep source ids of imported revlog (or skip)

* Try to keep source ids of imported notes

* Make adding notetype with id undoable

* Wrap apkg import in transaction

* Keep source ids of imported deck configs (or skip)

* Handle card due dates and original due/did

* Fix importing cards/revlog

Card ids are manually uniquified.

* Factor out card importing

* Refactor card and revlog importing

* Factor out card importing

Also handle missing parents .

* Factor out note importing

* Factor out media importing

* Maybe upgrade scheduler of apkg

* Fix parent deck gathering

* Unconditionally import static media

* Fix deck importing edge cases

Test those edge cases, and add some global test helpers.

* Test note importing

* Let import_apkg() take a progress func

* Expand roundtrip apkg test

* Use fat pointer to avoid propogating generics

* Fix progress_fn type

* Expose apkg export/import on backend

* Return note log when importing apkg

* Fix archived collection name on apkg import

* Add CollectionOpWithBackendProgress

* Fix wrong Interrupted Exception being checked

* Add ClosedCollectionOp

* Add note ids to log and strip HTML

* Update progress when checking incoming media too

* Conditionally enable new importing in GUI

* Fix all_checksums() for media import

Entries of deleted files are nulled, not removed.

* Make apkg exporting on backend abortable

* Return number of notes imported from apkg

* Fix exception printing for QueryOp as well

* Add QueryOpWithBackendProgress

Also support backend exporting progress.

* Expose new apkg and colpkg exporting

* Open transaction in insert_data()

Was slowing down exporting by several orders of magnitude.

* Handle zstd-compressed apkg

* Add legacy arg to ExportAnkiPackage

Currently not exposed on the frontend

* Remove unused import in proto file

* Add symlink for typechecking of import_export_pb2

* Avoid kwargs in pb message creation, so typechecking is not lost

Protobuf's behaviour is rather subtle and I had to dig through the docs
to figure it out: set a field on a submessage to automatically assign 
the submessage to the parent, or call SetInParent() to persist a default
version of the field you specified.

* Avoid re-exporting protobuf msgs we only use internally

* Stop after one test failure

mypy often fails much faster than pylint

* Avoid an extra allocation when extracting media checksums

* Update progress after prepare_media() finishes

Otherwise the bulk of the import ends up being shown as "Checked: 0"
in the progress window.

* Show progress of note imports

Note import is the slowest part, so showing progress here makes the UI
feel more responsive.

* Reset filtered decks at import time

Before this change, filtered decks exported with scheduling remained
filtered on import, and maybe_remove_from_filtered_deck() moved cards
into them as their home deck, leading to errors during review.

We may still want to provide a way to preserve filtered decks on import,
but to do that we'll need to ensure we don't rewrite the home decks of
cards, and we'll need to ensure the home decks are included as part of
the import (or give an error if they're not).

https://github.com/ankitects/anki/pull/1743/files#r839346423

* Fix a corner-case where due dates were shifted by a day

This issue existed in the old Python code as well. We need to include
the user's UTC offset in the exported file, or days_elapsed falls back
on the v1 cutoff calculation, which may be a day earlier or later than
the v2 calculation.

* Log conflicting note in remapped nt case

* take_fields() → into_fields()

* Alias `[u8; 20]` with `Sha1Hash`

* Truncate logged fields

* Rework apkg note import tests

- Use macros for more helpful errors.
- Split monolith into unit tests.
- Fix some unknown error with the previous test along the way.
(Was failing after 969484de4388d225c9f17d94534b3ba0094c3568.)

* Fix sorting of imported decks

Also adjust the test, so it fails without the patch. It was only passing
before, because the parent deck happened to come before the
inconsistently capitalised child alphabetically. But we want all parent
decks to be imported before their child decks, so their children can
adopt their capitalisation.

* target[_id]s → existing_card[_id]s

* export_collection_extracting_media() → ...

export_into_collection_file()

* target_already_exists→card_ordinal_already_exists

* Add search_cards_of_notes_into_table.sql

* Imrove type of apkg export selector/limit

* Remove redundant call to mod_schema()

* Parent tooltips to mw

* Fix a crash when truncating note text

String::truncate() is a bit of a footgun, and I've hit this before
too :-)

* Remove ExportLimit in favour of separate classes

* Remove OpWithBackendProgress and ClosedCollectionOp

Backend progress logic is now in ProgressManager. QueryOp can be used
for running on closed collection.

Also fix aborting of colpkg exports, which slipped through in #1817.

* Tidy up import log

* Avoid QDialog.exec()

* Default to excluding scheuling for deck list deck

* Use IncrementalProgress in whole import_export code

* Compare checksums when importing colpkgs

* Avoid registering changes if hashes are not needed

* ImportProgress::Collection → ImportProgress::File

* Make downgrading apkgs depend on meta version

* Generalise IncrementableProgress

And use it in entire import_export code instead.

* Fix type complexity lint

* Take count_map for IncrementableProgress::get_inner

* Replace import/export env with Shift click

* Accept all args from update() for backend progress

* Pass fields of ProgressUpdate explicitly

* Move update_interval into IncrementableProgress

* Outsource incrementing into Incrementor

* Mutate ProgressUpdate in progress_update callback

* Switch import/export legacy toggle to profile setting

Shift would have been nice, but the existing shortcuts complicate things.
If the user triggers an import with ctrl+shift+i, shift is unlikely to
have been released by the time our code runs, meaning the user accidentally
triggers the new code. We could potentially wait a while before bringing
up the dialog, but then we're forced to guess at how long it will take the
user to release the key.

One alternative would be to use alt instead of shift, but then we need to
trigger our shortcut when that key is pressed as well, and it could
potentially cause a conflict with an add-on that already uses that
combination.

* Show extension in export dialog

* Continue to provide separate options for schema 11+18 colpkg export

* Default to colpkg export when using File>Export

* Improve appearance of combo boxes when switching between apkg/colpkg

+ Deal with long deck names

* Convert newlines to spaces when showing fields from import

Ensures each imported note appears on a separate line

* Don't separate total note count from the other summary lines

This may come down to personal preference, but I feel the other counts
are equally as important, and separating them feels like it makes it
a bit easier to ignore them.

* Fix 'deck not normal' error when importing a filtered deck for the 2nd time

* Fix [Identical] being shown on first import

* Revert "Continue to provide separate options for schema 11+18 colpkg export"

This reverts commit 8f0b2c175f4794d642823b60414d142a12768441.

Will use a different approach

* Move legacy support into a separate exporter option; add to apkg export

* Adjust 'too new' message to also apply to .apkg import case

* Show a better message when attempting to import new apkg into old code

Previously the user could end seeing a message like:

UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb5 in position 1: invalid start byte

Unfortunately we can't retroactively fix this for older clients.

* Hide legacy support option in older exporting screen

* Reflect change from paths to fnames in type & name

* Make imported decks normal at once

Then skip special casing in update_deck(). Also skip updating
description if new one is empty.

Co-authored-by: Damien Elmes <gpg@ankiweb.net>
2022-05-02 21:12:46 +10:00
Damien Elmes
d2ec004ac3 Fix custom study applying previously-selected limit
extend_limits() mutates deck, so we need to re-fetch it.

Closes #1829
2022-05-02 20:57:29 +10:00
Abdo
a3d9a25d18
Extends the SearchNode interface to support field and unqualified searches (#1821)
* Add field search option to SearchNode

* Add unqualified/literal search option to SearchNode

* Rename text to literal_text

* Interpret field name parameter literally
2022-04-28 14:09:51 +10:00
RumovZ
8ccadd96e6
Store last position when scheduling as review (#1800) 2022-04-14 18:53:58 +10:00
Damien Elmes
51a0641bba Sort field change without field change should mark schema modified
Closes #1789
2022-04-10 08:52:55 +10:00
RumovZ
4a6767b1fc
Implicitly group when joining searches (#1759)
* Implicitly group when joining searches

* Allow joining search types directly

* Test search joining

* Add comment for future selves (dae)

* Add one more assert that shows nested grouping (dae)

* Join user searches without grouping again

* Flatten a few clauses in custom study (dae)
2022-04-09 13:22:27 +10:00
Abdo
964f0a5763
Add relative overdueness to review order (#1757)
* Add relative overdueness to review order

* Add test for relative overdue
2022-04-09 13:20:09 +10:00
Damien Elmes
4673fb5fe4 Colpkg import now marks collection as modified
Addresses a corner case where a backup is imported into an
unchanged collection, resulting in the sync indicator showing a full
sync is required, which doesn't go away as syncing thinks no changes
are required.

An alternative way to solve this would be to reverse the order of checks
in the syncing code. It would have the advantage of retaining the
modification time of the backup, but any action like clicking on a deck
would cause it to be updated anyway, so I'm not sure that buys us much.
2022-04-03 16:07:39 +10:00
Damien Elmes
d90608198f Don't rely on frontend to cap time taken in v3 2022-04-02 20:06:23 +10:00
RumovZ
dc36a08f3a
Also restore/keep position of new cards (#1760)
* Also restore/keep position of new cards

* Refactor Card::last_position()
2022-03-31 14:31:13 +10:00
Damien Elmes
9d64afc7bc Handle gaps in media in colpkg imports
Our old Python code was also skipping numbers when it encountered a
directory, leading to a colpkg that couldn't be imported with our new
code.
2022-03-31 13:32:38 +10:00
RumovZ
ab57fa484b
Fix SearchBuilder (#1754)
* Fix missing search grouping

* Fix SearchBuilder::or_join

* Unify search concatenations
2022-03-29 10:53:02 +10:00
RumovZ
f1488b5983
Card type error (#1749)
* TemplateSaveError -> CardTypeError

* Don't show success tooltip if export fails

* Attach help page to error

Show help link if export fails due to card type error.

* Add type (dae)

* Add shared show_exception() (dae)

- Use a shared routine for printing standard backend errors, so that
we can take advantage of the help links in eg. the card layout screen
as well.
- The truthiness check on help in showInfo() would have ignored the
enum 0 value.
- Close the exporting dialog on a documented failure as well

* Fix local variable help_page
2022-03-28 22:17:50 +10:00
RumovZ
dd16890c11
Add Deleted error and disable all bad browser rows (#1742)
* Add Deleted error and disable all bad browser rows

* Avoid error when opening the browse screen to a card with a missing note (dae)

* In cards mode, a missing note is NotFound, not Deleted (dae)

So we distinguish between referential integrity error, and explicit
deletion.

* Remove redundant try block
2022-03-28 19:06:19 +10:00
Damien Elmes
bbcab21f13 Start random card position from 1
Anki's DB schema unfortunately uses odid=0 instead of null to indicate
a lack of an original due date, so having a due position of 0 leads to
the temporary due date not being reset when the card is removed from
a filtered deck.

https://forums.ankiweb.net/t/anki-2-1-50-beta-6-9-stable-release/18181/52
2022-03-23 22:00:25 +10:00
Damien Elmes
6735b23e5b Force a schema change on colpkg import
If we don't force a full sync when restoring, any items that were added
since the backup may have already been sent to AnkiWeb, and they
won't have deletion records. After the user restores from a backup,
they'll end up in a state where their local and AnkiWeb collections
differ, and the changes will not sync. The old backup code forced a schema
change, but we weren't previously doing it via File>Import.
2022-03-22 13:10:32 +10:00
Damien Elmes
4515c41d2c
Backup improvements (#1728)
* Collection needs to be closed prior to backup even when not downgrading

* Backups -> BackupLimits

* Some improvements to backup_task

- backup_inner now returns the error instead of logging it, so that
the frontend can discover the issue when they await a backup (or create
another one)
- start_backup() was acquiring backup_task twice, and if another thread
started a backup between the two locks, the task could have been accidentally
overwritten without awaiting it

* Backups no longer require a collection close

- Instead of closing the collection, we ensure there is no active
transaction, and flush the WAL to disk. This means the undo history
is no longer lost on backup, which will be particularly useful if we
add a periodic backup in the future.
- Because a close is no longer required, backups are now achieved with
a separate command, instead of being included in CloseCollection().
- Full sync no longer requires an extra close+reopen step, and we now
wait for the backup to complete before proceeding.
- Create a backup before 'check db'

* Add File>Create Backup

https://forums.ankiweb.net/t/anki-mac-os-no-backup-on-sync/6157

* Defer checkpoint until we know we need it

When running periodic backups on a timer, we don't want to be fsync()ing
unnecessarily.

* Skip backup if modification time has not changed

We don't want the user leaving Anki open overnight, and coming back
to lots of identical backups.

* Periodic backups

Creates an automatic backup every 30 minutes if the collection has been
modified.

If there's a legacy checkpoint active, tries again 5 minutes later.

* Switch to a user-configurable backup duration

CreateBackup() now uses a simple force argument to determine whether
the user's limits should be respected or not, and only potentially
destructive ops (full download, check DB) override the user's configured
limit.

I considered having a separate limit for collection close and automatic
backups (eg keeping the previous 5 minute limit for collection close),
but that had two downsides:

- When the user closes their collection at the end of the day, they'd
get a recent backup. When they open the collection the next day, it
would get backed up again within 5 minutes, even though not much had
changed.
- Multiple limits are harder to communicate to users in the UI

Some remaining decisions I wasn't 100% sure about:

- If force is true but the collection has not been modified, the backup
will be skipped. If the user manually deleted their backups without
closing Anki, they wouldn't get a new one if the mtime hadn't changed.
- Force takes preference over the configured backup interval - should
we be ignored the user here, or take no backups at all?

Did a sneaky edit of the existing ftl string, as it hasn't been live
long.

* Move maybe_backup() into Collection

* Use a single method for manual and periodic backups

When manually creating a backup via the File menu, we no longer make
the user wait until the backup completes. As we continue waiting for
the backup in the background, if any errors occur, the user will get
notified about it fairly quickly.

* Show message to user if backup was skipped due to no changes

+ Don't incorrectly assert a backup will be created on force

* Add "automatic" to description

* Ensure we backup prior to importing colpkg if collection open

The backup doesn't happen when invoked from 'open backup' in the profile
screen, which matches Anki's previous behaviour. The user could
potentially clobber up to 30 minutes of their work if they exited to
the profile screen and restored a backup, but the alternative is we
create backups every time a backup is restored, which may happen a number
of times if the user is trying various ones. Or we could go back to a
separate throttle amount for this case, at the cost of more complexity.

* Remove the 0 special case on backup interval; minimum of 5 minutes

https://github.com/ankitects/anki/pull/1728#discussion_r830876833
2022-03-21 19:40:42 +10:00
Damien Elmes
e2eff7deae Fix change notetype affecting cards outside selection
Accidentally introduced by the search refactoring in #1600, this lead
to a query that matched items outside of the selected notes, eg

(n.id in (1506029488152) and c.ord = 43 or c.ord = 4)

Closes #1727
2022-03-20 15:36:54 +10:00