Skip to content

Conversation

@Arthur-Milchior
Copy link
Member

I loved David's recent work in using value class. And thought that we could finally kill the overhead cost of DeepClone in JSONObject. So I transformed Deck into a value class, and added the needed properties so that the other part of the code can use Deck without considering it as a JSONObject.

@mikehardy mikehardy added the Needs Author Reply Waiting for a reply from the original author label Jan 6, 2025
@Arthur-Milchior Arthur-Milchior added Needs Review and removed Needs Author Reply Waiting for a reply from the original author Has Conflicts labels Jan 8, 2025
Copy link
Member

@david-allison david-allison left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One question on the 1 and 2 discrepancy

@david-allison david-allison added Needs Author Reply Waiting for a reply from the original author and removed Needs Review labels Jan 16, 2025
@Arthur-Milchior Arthur-Milchior removed Needs Author Reply Waiting for a reply from the original author Has Conflicts labels Jan 16, 2025
@Arthur-Milchior
Copy link
Member Author

Test added for each property

@Arthur-Milchior Arthur-Milchior force-pushed the value_class_deck branch 2 times, most recently from 292c595 to 4f95714 Compare January 16, 2025 23:52
Copy link
Member

@david-allison david-allison left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The typings for a number of properties are incorrect, and I'm concerned.

previewGoodSecs is an Int. if you insert it into JSON as a String, it's quoted


"limit" -> {
ar.getJSONArray(0).put(1, value)
deck.firstFilter.limit = value as String
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a string?

Copy link
Member Author

@Arthur-Milchior Arthur-Milchior Feb 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the reason is that I misunderstood the way this part of the code worked. I thought all values were strings unless converted to other values. I'll update all of that

@Arthur-Milchior Arthur-Milchior force-pushed the value_class_deck branch 3 times, most recently from c9e9db4 to b7ba1ae Compare January 17, 2025 03:12
@Arthur-Milchior

This comment was marked as resolved.

@david-allison david-allison added the Needs Author Reply Waiting for a reply from the original author label Jan 17, 2025
@david-allison david-allison added Needs Review and removed Needs Author Reply Waiting for a reply from the original author labels Jan 18, 2025
@david-allison david-allison self-requested a review January 18, 2025 03:12
david-allison

This comment was marked as resolved.

@david-allison david-allison added the cleanup Non functional change that would improve the code readability label Jun 3, 2025
@david-allison david-allison changed the title Value class deck convert Deck to strongly typed value class Jun 3, 2025
@Arthur-Milchior
Copy link
Member Author

Sorry but I fail to understand what you mean by "break compat".
Anki don't have any file similar to our Deck.kt, so I don't think you are telling me that this code is too different from upstream code. And using constant or literal is a implementation details which is invisible to everything outside this file, so it's not breaking anything else I can see

@david-allison
Copy link
Member

david-allison commented Jun 25, 2025

@ankidroid/core Thoughts? I'm fine with light wrappers. I'm a medium-strength 'no' here, but want to check for consensus


libAnki should track pylib upstream in almost all cases. This makes it very simple for us to ingest upstream changes from pylib.

When we deviate, it should be for a good reason (ideally, the changes should be pushed upstream, then ingested downstream, or added as extension methods/helper classes outside libAnki). As a few examples:

  • When we were doing the Rust backend conversion, it was a source of pain each time the code deviated. It deviated a LOT
  • Splitting out libAnki into a separate module is harder work due to additions which add circular dependencies between AnkiDroid and libAnki
  • Whenever we take the time to bring libAnki up to date with pylib, these deviations from pylib mean that it's not a 1:1 conversion

Making the code nicer to read/better factored is probably slowing us down here in the long run. Maybe the documentation is worthwhile, but this change goes much deeper than adding a wrapper over existing functionality.

@david-allison david-allison added the Needs reviewer reply Waiting for a reply from another reviewer label Jun 25, 2025
@BrayanDSO
Copy link
Member

I prefer the alternative that makes me think less, which in this case is copying libanki as much as possible.

ideally, the changes should be pushed upstream, then ingested downstream, or added as extension methods/helper classes outside libAnki

Completely agreed with that.

@github-actions
Copy link
Contributor

github-actions bot commented Jul 9, 2025

Hello 👋, this PR has had no activity for more than 2 weeks and needs a reply from the author. If you think this is a mistake please comment and ping a maintainer to get this merged ASAP! Thanks for contributing! You have 7 days until this gets closed automatically

@github-actions github-actions bot added the Stale label Jul 9, 2025
@david-allison
Copy link
Member

I'm happy to take on splitting out the first few commits which are non-controversial

Would be useful to wait until #18015 is in. Most of the conflicts are done, but there will be a little more on the test side.

@github-actions github-actions bot removed the Stale label Jul 9, 2025
@github-actions
Copy link
Contributor

Hello 👋, this PR has had no activity for more than 2 weeks and needs a reply from the author. If you think this is a mistake please comment and ping a maintainer to get this merged ASAP! Thanks for contributing! You have 7 days until this gets closed automatically

@github-actions github-actions bot added the Stale label Jul 31, 2025
@github-actions github-actions bot closed this Aug 7, 2025
@david-allison david-allison added the Needs a new dev For PR that were good start but the original dev' left. Any dev can take over and finish it. label Aug 7, 2025
@david-allison david-allison reopened this Aug 7, 2025
@david-allison
Copy link
Member

Happy to close if I haven't split this off & merged within 2 months, overall change is worthwhile

@github-actions github-actions bot removed the Stale label Aug 7, 2025
The first interest of this change is that we are not deep cloning all
decks when creating a Deck object which should save a little bit of
time, especially loading time on collection with a lot of decks.

Instead, we have a better typing at zero runtime cost.

The second interest is that we are certain that the fact that the Deck
is implemented as a JSONObject is hidden from the developer using this
class. Instead, they can only use the provided extension, or add the
one they needs.

While most changes are relatively trivial, moving the implementation
from everywhere in the code to Deck.kt, there are some extra changes
that were needed.

I moved `getLongOrNull` to PythonExtension given that it implements
the behavior of a Python function we want to reuse, this seems more
consistent.

As value class can't be lateinit, I hardcoded
AppCompatPreferenceActivity as being a getter for the backing field
`_deck: Deck?`.

I added in testutils/JsonUtils helper method that accepts a json
holder, and a string representing the expected json, to simplify the
use of those methods.

The code used sometime `optLong("id")` and sometime
`getLong("id")`. There seems to be no reason for the difference
between both use case, and just be random choice from the
developer. Anyway, if a deck lacks an id, any place using
`getLong("id")` would have caused the code to crash, so replacing the
`optLong` by `getLong` would, in the worst case, move the time at
which the user is asked to clean their database.

There seems to be a single key we sometime set to JSONObject.NULL, it
was `delays`. For the sake of typing, the property `delays` in `deck`
is thus of type `JSONArray?`. When the value `null` is assigned to
this property, `NULL` is saved in the object, and reciprocally.

There is no need to check whether `deck` has "empty" key before
removing it. It's not even more efficient.
Deck is now declared as an interface so that it can be
inherited. `Deck` contains a facotry to decide whether to return a
normal deck or a filtered one.
This test ensures that every changes possibly done in the deck options
are correctly applied
@github-actions
Copy link
Contributor

github-actions bot commented Sep 1, 2025

Hello 👋, this PR has had no activity for more than 2 weeks and needs a reply from the author. If you think this is a mistake please comment and ping a maintainer to get this merged ASAP! Thanks for contributing! You have 7 days until this gets closed automatically

@github-actions github-actions bot added the Stale label Sep 1, 2025
@github-actions github-actions bot closed this Sep 8, 2025
@david-allison
Copy link
Member

I'll fixup

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cleanup Non functional change that would improve the code readability Has Conflicts Needs a new dev For PR that were good start but the original dev' left. Any dev can take over and finish it. Needs Author Reply Waiting for a reply from the original author Needs reviewer reply Waiting for a reply from another reviewer Needs Second Approval Has one approval, one more approval to merge Stale

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants