Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check for Duplicates In Mining Mode #109

Merged
merged 11 commits into from
Dec 12, 2024
Merged

Check for Duplicates In Mining Mode #109

merged 11 commits into from
Dec 12, 2024

Conversation

bpwhelan
Copy link
Contributor

@bpwhelan bpwhelan commented Nov 25, 2024

closes #107.

Added a config (default false) for checking duplicates when entering mining mode.

This will check all the results in mining mode for an already existing expression in the configured anki deck, and display a warning next to the result if it finds one.

Creating this as a draft as I want to:

  • Take another look at the method for checking duplicates. I assume it can be simplified quite a bit... I did, however, already try creating a method that would send everything to anki in one call, since "canAddNote" can receive multiple notes, but performance was markedly worse.
  • Do some extensive testing on the overall performance impact of this with large dictionary(s) as requested. With just the default dictionaries it seems to be completely unnoticeable.

@bpwhelan bpwhelan closed this Nov 25, 2024
@bpwhelan bpwhelan reopened this Nov 25, 2024
@rampaa
Copy link
Owner

rampaa commented Nov 25, 2024

I did, however, already try creating a method that would send everything to anki in one call, since "canAddNote" can receive multiple notes, but performance was markedly worse

Interesting. Is AnkiConnect responding to, say, 100 separate canAddNote requests faster than a single request for those 100 notes? Or is it slower due to how we create and process those requests in JL?


if (!configManager.MineToFileInsteadOfAnki)
{
if (MiningUtils.CheckDuplicate(result).Result)
Copy link
Owner

Choose a reason for hiding this comment

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

Accessing the Result property of a Task is a blocking operation, thus it should be avoided. Instead we should do something like

bool duplicateCard = await MiningUtils.CheckDuplicate(result).ConfigureAwait(true);
if (duplicateCard)

Copy link
Contributor Author

@bpwhelan bpwhelan Nov 25, 2024

Choose a reason for hiding this comment

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

This is the biggest thing that I'm having trouble with since this method is synchronous.

Toying with something to do the check for duplicates after the window is loaded and toggling the visibility so there is 0 impact on loading the window.

}

Dictionary<string, JLField> userFields = ankiConfig.Fields;
Dictionary<JLField, string> miningParams = new()
Copy link
Owner

@rampaa rampaa Nov 25, 2024

Choose a reason for hiding this comment

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

We probably should use GetMiningParameters here. The implementation provided here just partially creates mining parameters but for all we know the user might have SourceText as their note type's first field (which is all Anki cares for when checking if a card is a duplicate AFAIK) and its value is missing here.

Or better yet, we can check what's the first Field in the config and just create a note with that field to check whether it's a duplicate note.

// Audio/Picture/Video shouldn't be set here
// Otherwise AnkiConnect will place them under the "collection.media" folder even when it's a duplicate note
Note note = new(ankiConfig.DeckName, ankiConfig.ModelName, fields, ankiConfig.Tags, null, null, null, null);
if (!coreConfigManager.AllowDuplicateCards)
Copy link
Owner

Choose a reason for hiding this comment

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

Why do we have this check here? As is, this method will always return false if the user enabled the Allow duplicate cards option, is this really the intended behavior?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Already removed in local.

@bpwhelan
Copy link
Contributor Author

IN 0d51e21

  • Changed this to add the icon (collapsed), and then do the duplicate check work afterwords.
  • Moved around boolean logic to where we don't even enter the duplicate check method if we don't need to.
  • Changed to only send one call to ankiconnect, when i got it to go successfully async, ankiconnect could not handle the requests and most of them were getting thrown out due to 503 or something.

2 things..

  • I think the checkduplicates method is a bit overcomplicated... But I couldn't really find another way to do it cleanly.
  • I'm not sure if there is any danger with with this line. I can't get it to break, but I'm not entirely confident what would be the best way to make this safer.

@bpwhelan bpwhelan marked this pull request as ready for review November 27, 2024 01:33
@bpwhelan bpwhelan requested a review from rampaa November 27, 2024 01:33
@rampaa
Copy link
Owner

rampaa commented Nov 27, 2024

CheckResultForDuplicates method should return a Task because:

  1. async void should always be avoided, the only exceptiona to this rule is event handlers
  2. Before calling the UpdateLayout method in DisplayResults method, we should ensure we made all the changes to the GUI, because otherwise UpdateLayout will not be able to render the GUI in its final state and methods like UpdatePosition should be called after the popup is rendered in its final state. So we can't use a fire-and-forget approach here. So we do need to await CheckResultForDuplicates's result and waiting CheckResultForDuplicates before showing the popup effectively means that the lookup speed is still affected by CheckResultForDuplicates and I am not sure if we can avoid it.

That being the case, I suggest discarding CheckResultForDuplicates altogether and doing the following in DisplayResults method:

    public Task DisplayResults(bool generateAllResults)
    {
        ...

        bool[]? isDuplicateCardArray = null;
        int resultCount;
        if (generateAllResults)
        {
            resultCount = LastLookupResults.Count;

            CoreConfigManager coreConfigManager = CoreConfigManager.Instance;
            if (coreConfigManager.CheckForDuplicateCards
                && !ConfigManager.Instance.MineToFileInsteadOfAnki
                && coreConfigManager.AnkiIntegration)
            {
                isDuplicateCardArray = await MiningUtils.CheckDuplicates(LastLookupResults, _currentText, _currentCharPosition).ConfigureAwait(true);
            }
        }
        else
        {
            resultCount = Math.Min(LastLookupResults.Count, ConfigManager.Instance.MaxNumResultsNotInMiningMode);
        }

        StackPanel[] popupItemSource = new StackPanel[resultCount];

        for (int i = 0; i < resultCount; i++)
        {
            LookupResult lookupResult = LastLookupResults[i];

            if (!_dictsWithResults.Contains(lookupResult.Dict))
            {
                _dictsWithResults.Add(lookupResult.Dict);
            }

            popupItemSource[i] = PrepareResultStackPanel(lookupResult, i, resultCount, pitchDict, pitchDictIsActive, showPOrthographyInfo, showROrthographyInfo, showAOrthographyInfo, pOrthographyInfoFontSize, isDuplicateCardArray?[i] ?? false);
        }

        PopupListView.ItemsSource = popupItemSource;
        GenerateDictTypeButtons();
        UpdateLayout();
    }

Then we can simply do the following in PrepareResultStackPanel

if (duplicateCard)
{
    Button duplicate = new()
    {
        ...
    };

    _ = top.Children.Add(audioButton);
}

I think this is the proper way of doing it. Though I'd like to have some tests regarding the performance impact of doing it like this.

@bpwhelan
Copy link
Contributor Author

bpwhelan commented Nov 27, 2024

Yeah, This is how I had it written at some point... But it was the most noticeable in terms of performance for the time I had it in.

I'm not really sure I get the technical reason for everything to be final before UpdateLayout but my understanding is not really that important. Guess my thought was that the buttons already exist in the render, their visibility is just being toggled.

@rampaa
Copy link
Owner

rampaa commented Nov 27, 2024

Guess my thought was that the buttons already exist in the render, their visibility is just being toggled.

If you used Visibility.Hidden instead of Visibility.Collapsed that would mostly be correct. Visibility.Collapsed does not reserve space for the control in the layout, thus when you make it visible it can change the popup height/width when dynamic height/width is enabled for the popup, which can, in turn, affect where it should be positioned exactly. If we were to use Visibility.Hidden instead of Visibility.Collapsed, then we wouldn't have this problem but then we would have blank space for non-duplicate entries where the duplicate icon is placed.

JL.Core/Mining/Anki/AnkiUtils.cs Outdated Show resolved Hide resolved
JL.Windows/GUI/PopupWindow.xaml.cs Outdated Show resolved Hide resolved
JL.Windows/GUI/PopupWindow.xaml.cs Outdated Show resolved Hide resolved
JL.Core/Mining/MiningUtils.cs Outdated Show resolved Hide resolved
JL.Core/Mining/MiningUtils.cs Outdated Show resolved Hide resolved
JL.Windows/GUI/PopupWindow.xaml.cs Outdated Show resolved Hide resolved
JL.Windows/GUI/PopupWindow.xaml.cs Outdated Show resolved Hide resolved
JL.Windows/GUI/PopupWindow.xaml.cs Outdated Show resolved Hide resolved
JL.Windows/GUI/PopupWindow.xaml.cs Outdated Show resolved Hide resolved
JL.Core/Mining/MiningUtils.cs Outdated Show resolved Hide resolved
@bpwhelan
Copy link
Contributor Author

bpwhelan commented Dec 2, 2024

Everything done including suggested changes, sitting on it for a bit to test and refactor.

Moved Icon to the end of the row so we can actually toggle visibility of the icon instead of uncollapse it
PR review changes like return types, array instead of List, etc.
Changed to Textblock instead of button
Made method to get first mining param instead of getting everything and then only getting the first.
@bpwhelan bpwhelan requested a review from rampaa December 5, 2024 22:36
@bpwhelan
Copy link
Contributor Author

bpwhelan commented Dec 5, 2024

Sorry for the delay on this. in a687c8d.

  • Moved Icon to the end of the row so we can actually toggle visibility of the icon instead of uncollapse it. Let me know if this is still not desired. I would prefer not to add any time to lookups even if it's relatively small, and this seems like the only realistic way of doing it.
  • PR review changes like return types, array instead of List, etc.
  • Changed to Textblock instead of button
  • Made method to get first mining param instead of getting everything and then only grabbing the first.

Edit: Realized I forgot to include an image of what it looks like on the end.

image

JL.Core/Mining/MiningUtils.cs Outdated Show resolved Hide resolved
JL.Core/Mining/MiningUtils.cs Outdated Show resolved Hide resolved
JL.Core/Mining/MiningUtils.cs Outdated Show resolved Hide resolved
JL.Core/Mining/MiningUtils.cs Outdated Show resolved Hide resolved
JL.Core/Mining/MiningUtils.cs Outdated Show resolved Hide resolved
JL.Windows/GUI/PopupWindow.xaml.cs Outdated Show resolved Hide resolved
JL.Windows/GUI/PopupWindow.xaml.cs Outdated Show resolved Hide resolved
JL.Windows/GUI/PopupWindow.xaml.cs Outdated Show resolved Hide resolved
JL.Windows/GUI/PopupWindow.xaml.cs Outdated Show resolved Hide resolved
JL.Windows/GUI/PopupWindow.xaml.cs Outdated Show resolved Hide resolved
Fill out GetMiningParameter Method
Move duplicateIcons List out of Fields
More small Object/List Related Changes
@bpwhelan
Copy link
Contributor Author

bpwhelan commented Dec 11, 2024

Changes in dbcc60a all per PR. Bulk of this commit is in GetMiningParameter to fill out all the ones possible, like you said just a lot of duplicate code ripped straight out of the full GetMiningParameters method.

  • Fill out GetMiningParameter Method
  • Move duplicateIcons List out of Fields
  • More small Object/List Related Changes

@bpwhelan bpwhelan requested a review from rampaa December 11, 2024 01:28
JL.Core/Mining/MiningUtils.cs Outdated Show resolved Hide resolved
JL.Core/Mining/MiningUtils.cs Outdated Show resolved Hide resolved
JL.Core/Mining/MiningUtils.cs Outdated Show resolved Hide resolved
JL.Core/Mining/MiningUtils.cs Show resolved Hide resolved
bpwhelan and others added 2 commits December 12, 2024 12:28
Add braces to case
refactor frequencies to inline conditional
get rid of unused var in Preferred Frequency
move unused and default cases to top
moved ankiconfigcheck
downmerged from upstream
changed to array instead of list per upstream change
other small pr related changes
@bpwhelan
Copy link
Contributor Author

bpwhelan commented Dec 12, 2024

In a5aee43, more changes per PR review

Add braces to case
refactor frequencies to inline conditional
get rid of unused var in Preferred Frequency
move unused and default cases to top
moved ankiconfigcheck
downmerged from upstream
changed to array instead of list per upstream change
other small pr related changes

@rampaa rampaa merged commit b80e5f0 into rampaa:master Dec 12, 2024
1 check passed
@rampaa
Copy link
Owner

rampaa commented Dec 12, 2024

Thanks for contributing to JL!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Mark Duplicates in Lookups/Mining Dialogue
2 participants