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

enhance: add ability to control category learning per payee and globally #4081

Open
wants to merge 14 commits into
base: master
Choose a base branch
from

Conversation

NullScope
Copy link
Contributor

@NullScope NullScope commented Jan 3, 2025

This PR introduces the following enhancements to the Category Learning feature:

  1. Per-Payee Control: Users can now enable or disable Category Learning for individual payees.
  2. Global Toggle: Category Learning can also be enabled or disabled globally.

Some payees, such as Amazon, frequently involve transactions spanning multiple categories. In such cases, pre-filled categories can lead to inaccuracies, requiring extra effort to correct, update transaction, or delete unhelpful rules. This can result in wasted time or complications, especially during syncing as it can make it harder to spot an incorrect transaction. Allowing greater control over category learning improves flexibility and reduces friction for users.

Copy link

netlify bot commented Jan 3, 2025

Deploy Preview for actualbudget ready!

Name Link
🔨 Latest commit 23961cd
🔍 Latest deploy log https://app.netlify.com/sites/actualbudget/deploys/677d9d5bd5f89400087d2955
😎 Deploy Preview https://deploy-preview-4081.demo.actualbudget.org
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site configuration.

Copy link
Contributor

github-actions bot commented Jan 3, 2025

Bundle Stats — desktop-client

Hey there, this message comes from a GitHub action that helps you and reviewers to understand how these changes affect the size of this project's bundle.

As this PR is updated, I'll keep you updated on how the bundle size is impacted.

Total

Files count Total bundle size % Changed
10 5.66 MB → 5.66 MB (+3.76 kB) +0.06%
Changeset
File Δ Size
src/components/settings/LearnCategories.tsx 🆕 +1.13 kB 0 B → 1.13 kB
src/icons/v1/LightBulb.tsx 🆕 +441 B 0 B → 441 B
src/components/payees/PayeeTableRow.tsx 📈 +942 B (+16.09%) 5.72 kB → 6.64 kB
src/components/payees/PayeeMenu.tsx 📈 +225 B (+14.12%) 1.56 kB → 1.78 kB
src/components/payees/ManagePayees.tsx 📈 +692 B (+8.79%) 7.69 kB → 8.37 kB
src/components/transactions/TransactionList.jsx 📈 +318 B (+5.95%) 5.22 kB → 5.53 kB
src/components/settings/index.tsx 📈 +76 B (+1.03%) 7.21 kB → 7.28 kB
View detailed bundle breakdown

Added

No assets were added

Removed

No assets were removed

Bigger

Asset File Size % Changed
static/js/index.js 3.72 MB → 3.73 MB (+3.45 kB) +0.09%
static/js/AppliedFilters.js 10.24 kB → 10.55 kB (+318 B) +3.03%

Smaller

No assets were smaller

Unchanged

Asset File Size % Changed
static/js/useAccountPreviewTransactions.js 1.63 kB 0%
static/js/indexeddb-main-thread-worker-e59fee74.js 13.5 kB 0%
static/js/workbox-window.prod.es5.js 5.69 kB 0%
static/js/resize-observer.js 18.37 kB 0%
static/js/BackgroundImage.js 122.29 kB 0%
static/js/narrow.js 84.22 kB 0%
static/js/wide.js 104.59 kB 0%
static/js/ReportRouter.js 1.58 MB 0%

Copy link
Contributor

github-actions bot commented Jan 3, 2025

Bundle Stats — loot-core

Hey there, this message comes from a GitHub action that helps you and reviewers to understand how these changes affect the size of this project's bundle.

As this PR is updated, I'll keep you updated on how the bundle size is impacted.

Total

Files count Total bundle size % Changed
1 1.33 MB → 1.33 MB (+69 B) +0.00%
Changeset
File Δ Size
packages/loot-core/src/server/accounts/transaction-rules.ts 📈 +69 B (+0.21%) 31.84 kB → 31.91 kB
View detailed bundle breakdown

Added

No assets were added

Removed

No assets were removed

Bigger

Asset File Size % Changed
kcab.worker.js 1.33 MB → 1.33 MB (+69 B) +0.00%

Smaller

No assets were smaller

Unchanged

No assets were unchanged

@NullScope
Copy link
Contributor Author

While I feel this PR is ready on my eyes, I've placed it as draft because I would like an opinion on the following things:

  • Should the per payee control be stored in the database or somewhere else?
  • Is there any more unit testing I should add for this feature?
  • On the frontend, does it make sense to have the lightbulb red in the payees section to indicate that its disabled for that payee?
  • Are the frontend changes good in general?

@NullScope
Copy link
Contributor Author

/update-vrt

@UnderKoen
Copy link
Member

UnderKoen commented Jan 3, 2025

  • Should the per payee control be stored in the database or somewhere else?

I think DB is good for this.

  • Is there any more unit testing I should add for this feature?

I think the case added is good. But I personally don't know how I would test this. Could you explain a flow for me @NullScope

  • On the frontend, does it make sense to have the lightbulb red in the payees section to indicate that its disabled for that payee?

It seems fine, but im not much of a designer.

  • Are the frontend changes good in general?

They look good, but could you add a menu option to the context menu (right click)? And a label for on hover of the icon.

Addionally if the option is disable, I don't think we should show it in the menu and/or the icon.

@NullScope
Copy link
Contributor Author

NullScope commented Jan 3, 2025

@UnderKoen thanks for the feedback!

I think the case added is good. But I personally don't know how I would test this. Could you explain a flow for me

There are two flows here:

  • on the backend, whenever a transaction is added, it checks through a database query which ignores any transaction for a payee in that state. (This is the unit test that I added)
  • on the frontend, whenever a transaction is added, it sends to the backend the global setting if it should try to learn a category or not. If its disabled, it skips the first flow entirely. (The missing unit test?)

They look good, but could you add a menu option to the context menu (right click)? And a label for on hover of the icon.

Will do 👍

Addionally if the option is disable, I don't think we should show it in the menu and/or the icon.

I'm not sure I understood this part, could you please explain again?

@NullScope NullScope marked this pull request as ready for review January 4, 2025 01:12
Copy link
Contributor

coderabbitai bot commented Jan 4, 2025

Walkthrough

This pull request introduces a new feature for category learning in the application. The changes span multiple files across the desktop client and core server packages. A new learn_categories property is added to the PayeeEntity, allowing users to control whether specific payees can have their transaction categories automatically learned.

The implementation includes modifications to various components such as ManagePayees, PayeeMenu, and PayeeTableRow to support the new functionality. A new settings component, LearnCategoriesSettings, is added to allow users to enable or disable category learning globally. The server-side logic in transaction-rules.ts is updated to respect the learn_categories flag when updating category rules, ensuring that only payees with learning enabled are considered for automatic categorization. Additionally, a SQL migration script is included to add the new column to the database, and corresponding tests are created to verify the new behavior.

Possibly related PRs


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (11)
packages/loot-core/src/types/models/payee.d.ts (1)

8-8: Use a boolean type instead of numeric flags for clarity.

Defining learn_categories as 1 | 0 can lead to confusion, since calling code may incorrectly treat those values as numbers. Consider using a boolean type for improved readability and reduced risk of accidental misuse.

-  learn_categories?: 1 | 0;
+  learn_categories?: boolean;
packages/desktop-client/src/components/settings/LearnCategories.tsx (1)

21-26: Provide explicit context in the button labels.

It might be more user-friendly to label the actions as something like "Disable Automatic Category Learning" and "Enable Automatic Category Learning" for clarity, particularly if the user is not familiar with the term "learning".

- {isLearnCategoriesEnabled
-   ? t('Disable Category Learning')
-   : t('Enable Category Learning')}
+ {isLearnCategoriesEnabled
+   ? t('Disable Automatic Category Learning')
+   : t('Enable Automatic Category Learning')}
packages/desktop-client/src/components/payees/PayeeMenu.tsx (3)

6-6: Avoid importing unused icons.

If some icons from the same file (like SvgBookmark) are not used or no longer needed, consider removing them to reduce bundle size and potential confusion.


27-27: Consistent destructuring.

The destructuring is consistent. Make sure all newly introduced functions (e.g., onLearn) are documented for maintainers, especially if it has side effects in other parts of the code.


93-99: Add a tooltip or context label.

The new menu item is great. For enhanced usability, consider showing a tooltip or short text label when hovering or focusing on this icon, clarifying the purpose of “Category Learning” for new users.

packages/desktop-client/src/components/payees/PayeeTableRow.tsx (2)

180-186: Toggling learn_categories in onMenuSelect.
The approach to store 0 or 1 is fine, but consider using booleans if supported system-wide to reduce confusion.


217-231: Red icon for "learning disabled".
While clear, it may be worth checking color accessibility or theming. A more semantic color or an additional style might improve clarity for users with visual impairments.

packages/desktop-client/src/components/transactions/TransactionList.jsx (1)

106-107: Potential type mismatch.
String(learnCategories) !== 'false' works, but consider a more robust check (e.g., Boolean() or !!learnCategories) if the stored pref changes type in the future.

packages/desktop-client/src/components/payees/ManagePayees.tsx (1)

243-243: Menu property wiring.
Supplying onLearn to PayeeMenu is appropriate. Confirm that there's a clear tooltip or label for clarity.

packages/loot-core/src/server/accounts/transaction-rules.ts (1)

804-805: Consider indexing or optimizing the p.learn_categories lookup.
You might want to ensure there's an index on the payees.learn_categories column to prevent potential performance bottlenecks when filtering numerous rows, especially if the payee table grows large.

packages/loot-core/src/server/accounts/transaction-rules.test.ts (1)

872-893: Remove the expect(true).toBe(true) or convert it into a meaningful assertion.
Line 881’s expect(true).toBe(true) is redundant and does not validate any behavior. Consider removing it, or replace with a test that confirms the user-defined payee initializes properly.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 832fd1e and c0a9578.

⛔ Files ignored due to path filters (4)
  • packages/desktop-client/e2e/settings.test.js-snapshots/Settings-checks-the-page-visuals-1-chromium-linux.png is excluded by !**/*.png
  • packages/desktop-client/e2e/settings.test.js-snapshots/Settings-checks-the-page-visuals-2-chromium-linux.png is excluded by !**/*.png
  • packages/desktop-client/e2e/settings.test.js-snapshots/Settings-checks-the-page-visuals-3-chromium-linux.png is excluded by !**/*.png
  • upcoming-release-notes/4081.md is excluded by !**/*.md
📒 Files selected for processing (11)
  • packages/desktop-client/src/components/payees/ManagePayees.tsx (3 hunks)
  • packages/desktop-client/src/components/payees/PayeeMenu.tsx (5 hunks)
  • packages/desktop-client/src/components/payees/PayeeTableRow.tsx (5 hunks)
  • packages/desktop-client/src/components/settings/LearnCategories.tsx (1 hunks)
  • packages/desktop-client/src/components/settings/index.tsx (2 hunks)
  • packages/desktop-client/src/components/transactions/TransactionList.jsx (4 hunks)
  • packages/loot-core/migrations/1735868660625_add_learn_categories_to_payees.sql (1 hunks)
  • packages/loot-core/src/server/accounts/transaction-rules.test.ts (1 hunks)
  • packages/loot-core/src/server/accounts/transaction-rules.ts (1 hunks)
  • packages/loot-core/src/types/models/payee.d.ts (1 hunks)
  • packages/loot-core/src/types/prefs.d.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/loot-core/migrations/1735868660625_add_learn_categories_to_payees.sql
🔇 Additional comments (18)
packages/desktop-client/src/components/settings/LearnCategories.tsx (2)

15-16: Verify handling for the pref’s string value.

String(learnCategories) !== 'false' could cause confusion if learnCategories isn’t strictly bool | string. Consider an explicit type check or schema validation to ensure that learnCategories holds a consistent type (e.g., boolean) rather than converting to string.


29-41: Ensure i18n coverage for all text blocks.

While most text is already localized via t(), confirm that the appended ' '} spaces and markup are correctly captured in translation. Potentially move them into the translation text for clarity and consistency.

packages/loot-core/src/types/prefs.d.ts (1)

34-35: Confirm consistent naming for flags.

Adding | 'learn-categories' extends the possible keys. Make sure that the naming is aligned across the entire codebase so that referencing code does not introduce confusion (some places might expect 'learnCategories').

packages/desktop-client/src/components/payees/PayeeMenu.tsx (2)

17-17: Props type looks good.

Adding onLearn follows the established pattern for menu actions, and the callback signature is concise. No issues found.


51-53: Ensure onLearn can handle multiple payees.

If multiple payees can be selected, ensure your onLearn handler can properly update each selected payee’s learn_categories state. Consider a short comment clarifying that only single-payee operations are allowed or how multi-payee scenarios are handled.

packages/desktop-client/src/components/settings/index.tsx (2)

35-35: Good modular addition.
Importing LearnCategoriesSettings keeps settings organized in their own component. Looks good!


187-187: Concise integration in settings.
Adding <LearnCategoriesSettings /> here ensures seamless navigation and user access to the new feature. No issues found.

packages/desktop-client/src/components/payees/PayeeTableRow.tsx (4)

9-9: Additional icon import is valid.
The SvgLightBulb icon import is straightforward and used consistently. No issues seen.


14-14: Tooltip import recognized.
Importing Tooltip aligns with showing context when learning is disabled. This is good UX.


67-70: Extended editable fields.
Allowing 'learn_categories' in EditablePayeeFields is necessary for toggling. Implementation is coherent.


165-170: Menu options for enabling/disabling learning.
Suggest verifying that payees with transfer_acct never display the learning toggle if unneeded. Otherwise, logic is clear.

packages/desktop-client/src/components/transactions/TransactionList.jsx (5)

16-16: New custom hook usage looks fine.
The useSyncedPref('learn-categories') integration is consistent with how preferences are handled elsewhere.


41-46: New parameter in saveDiff.
Adding learnCategories to the request payload is logical. No apparent issues. Ensure the server side properly handles this parameter.


53-54: Extended signature for saveDiffAndApply.
Accepting learnCategories preserves consistency. Confirm that all calls pass the correct value.


130-130: Maintaining top sort order.
Using Date.now() is a quick fix. Confirm that a repeated call won't cause collisions if transactions are updated very rapidly.


134-139: Consistent usage of learnCategories.
Passing isLearnCategoriesEnabled to saveDiffAndApply aligns with the same approach in saveDiff. Looks good.

packages/desktop-client/src/components/payees/ManagePayees.tsx (2)

119-119: Enhancement to onUpdate.
Including 'learn_categories' in the generic type is a natural extension for toggling. This is consistent with other updated fields.


171-190: Bulk toggling learn_categories.
This logic is straightforward. Double-check that partially mixed states (some enabled, some disabled) are handled by toggling them all to one state, which appears to be the intended behavior.

@NullScope NullScope changed the title enhancement: add ability to control category learning per payee and globally enhance: add ability to control category learning per payee and globally Jan 6, 2025
@NullScope NullScope force-pushed the control-auto-categories branch from 46148fb to d0c7c9f Compare January 6, 2025 23:58
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/desktop-client/src/components/transactions/TransactionList.jsx (1)

130-139: Consider more robust preference handling.

While the implementation works, consider making the preference handling more explicit and robust:

  1. Add a default value to handle cases where the preference might not be set
  2. Use a more explicit boolean conversion
- const [learnCategories] = useSyncedPref('learn-categories');
- const isLearnCategoriesEnabled = String(learnCategories) !== 'false';
+ const [learnCategories = true] = useSyncedPref('learn-categories');
+ const isLearnCategoriesEnabled = Boolean(learnCategories === true || learnCategories === 'true');
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 46148fb and d0c7c9f.

⛔ Files ignored due to path filters (4)
  • packages/desktop-client/e2e/settings.test.js-snapshots/Settings-checks-the-page-visuals-1-chromium-linux.png is excluded by !**/*.png
  • packages/desktop-client/e2e/settings.test.js-snapshots/Settings-checks-the-page-visuals-2-chromium-linux.png is excluded by !**/*.png
  • packages/desktop-client/e2e/settings.test.js-snapshots/Settings-checks-the-page-visuals-3-chromium-linux.png is excluded by !**/*.png
  • upcoming-release-notes/4081.md is excluded by !**/*.md
📒 Files selected for processing (11)
  • packages/desktop-client/src/components/payees/ManagePayees.tsx (3 hunks)
  • packages/desktop-client/src/components/payees/PayeeMenu.tsx (5 hunks)
  • packages/desktop-client/src/components/payees/PayeeTableRow.tsx (5 hunks)
  • packages/desktop-client/src/components/settings/LearnCategories.tsx (1 hunks)
  • packages/desktop-client/src/components/settings/index.tsx (2 hunks)
  • packages/desktop-client/src/components/transactions/TransactionList.jsx (4 hunks)
  • packages/loot-core/migrations/1735868660625_add_learn_categories_to_payees.sql (1 hunks)
  • packages/loot-core/src/server/accounts/transaction-rules.test.ts (1 hunks)
  • packages/loot-core/src/server/accounts/transaction-rules.ts (1 hunks)
  • packages/loot-core/src/types/models/payee.d.ts (1 hunks)
  • packages/loot-core/src/types/prefs.d.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (10)
  • packages/desktop-client/src/components/settings/index.tsx
  • packages/loot-core/src/server/accounts/transaction-rules.test.ts
  • packages/loot-core/migrations/1735868660625_add_learn_categories_to_payees.sql
  • packages/loot-core/src/types/models/payee.d.ts
  • packages/loot-core/src/types/prefs.d.ts
  • packages/loot-core/src/server/accounts/transaction-rules.ts
  • packages/desktop-client/src/components/settings/LearnCategories.tsx
  • packages/desktop-client/src/components/payees/PayeeTableRow.tsx
  • packages/desktop-client/src/components/payees/ManagePayees.tsx
  • packages/desktop-client/src/components/payees/PayeeMenu.tsx
🔇 Additional comments (3)
packages/desktop-client/src/components/transactions/TransactionList.jsx (3)

16-16: LGTM! Import of useSyncedPref is appropriate.

The addition of useSyncedPref hook aligns with the PR's objective of managing synchronized category learning preferences.


41-51: Function signature updates look good and maintain proper separation of concerns.

The addition of the learnCategories parameter to both functions is well-implemented:

  • Properly propagates the learning preference to the server
  • Maintains existing error handling patterns
  • Preserves function focus and single responsibility

Also applies to: 53-59


106-107: State management implementation looks good.

The use of useSyncedPref for managing the learning preference is well-implemented and the boolean conversion is clear.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🔭 Outside diff range comments (1)
packages/desktop-client/src/components/transactions/TransactionList.jsx (1)

Line range hint 119-142: Add missing dependencies to useCallback hooks

The isLearnCategoriesEnabled value is used in callbacks but not included in their dependency arrays.

   const onSave = useCallback(async transaction => {
     // ... implementation
-  }, []);
+  }, [isLearnCategoriesEnabled]);

   const onAddSplit = useCallback(id => {
     // ... implementation
-  }, []);
+  }, [isLearnCategoriesEnabled]);

   const onSplit = useCallback(id => {
     // ... implementation
-  }, []);
+  }, [isLearnCategoriesEnabled]);

Also applies to: 144-147, 150-153

🧹 Nitpick comments (3)
packages/desktop-client/src/components/transactions/TransactionList.jsx (3)

106-107: Consider using Boolean parsing for more robust type conversion

The string comparison could be replaced with Boolean parsing for more robust type handling.

-  const [learnCategories = 'false'] = useSyncedPref('learn-categories');
-  const isLearnCategoriesEnabled = String(learnCategories) === 'true';
+  const [learnCategories = false] = useSyncedPref('learn-categories');
+  const isLearnCategoriesEnabled = Boolean(learnCategories);

41-46: Add JSDoc for the new parameter

The function changes look good, but would benefit from parameter documentation.

+/**
+ * @param {Object} diff - The transaction diff to save
+ * @param {boolean} learnCategories - Whether to enable category learning
+ * @returns {Promise<{updates?: Array}>} The remote updates if any
+ */
 async function saveDiff(diff, learnCategories) {

Line range hint 53-58: Improve parameter formatting consistency

The function implementation looks good, but the parameter formatting could be more consistent.

-async function saveDiffAndApply(diff, changes, onChange, learnCategories) {
-  const remoteDiff = await saveDiff(diff, learnCategories);
-  onChange(
-    applyTransactionDiff(changes.newTransaction, remoteDiff),
-    applyChanges(remoteDiff, changes.data),
-  );
+async function saveDiffAndApply(
+  diff,
+  changes,
+  onChange,
+  learnCategories
+) {
+  const remoteDiff = await saveDiff(diff, learnCategories);
+  onChange(
+    applyTransactionDiff(changes.newTransaction, remoteDiff),
+    applyChanges(remoteDiff, changes.data)
+  );
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d0c7c9f and 409f0c0.

📒 Files selected for processing (2)
  • packages/desktop-client/src/components/settings/LearnCategories.tsx (1 hunks)
  • packages/desktop-client/src/components/transactions/TransactionList.jsx (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/desktop-client/src/components/settings/LearnCategories.tsx
⏰ Context from checks skipped due to timeout of 90000ms (5)
  • GitHub Check: build (macos-latest)
  • GitHub Check: Analyze
  • GitHub Check: build (windows-latest)
  • GitHub Check: Wait for Netlify build to finish
  • GitHub Check: build (ubuntu-latest)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/desktop-client/src/components/transactions/TransactionList.jsx (1)

Line range hint 41-58: Consider adding JSDoc comments for the modified functions

While the implementation is solid, adding JSDoc comments for saveDiff and saveDiffAndApply would improve code maintainability by documenting the new learnCategories parameter.

Example documentation:

+/**
+ * Saves transaction changes to the backend
+ * @param {Object} diff - The transaction changes to save
+ * @param {boolean} learnCategories - Whether to enable category learning for these transactions
+ * @returns {Promise<Object>} The remote updates if any
+ */
async function saveDiff(diff, learnCategories) {

+/**
+ * Saves transaction changes and applies them to the local state
+ * @param {Object} diff - The transaction changes to save
+ * @param {Object} changes - The local changes to apply
+ * @param {Function} onChange - Callback to update local state
+ * @param {boolean} learnCategories - Whether to enable category learning
+ * @returns {Promise<void>}
+ */
async function saveDiffAndApply(diff, changes, onChange, learnCategories) {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 409f0c0 and b755e79.

📒 Files selected for processing (1)
  • packages/desktop-client/src/components/transactions/TransactionList.jsx (5 hunks)
🔇 Additional comments (4)
packages/desktop-client/src/components/transactions/TransactionList.jsx (4)

16-16: LGTM! Clean integration of category learning preferences

The hook usage and state management follow React best practices with proper default value handling and explicit boolean conversion.

Also applies to: 106-107


Line range hint 53-58: LGTM! Consistent parameter forwarding

The learnCategories parameter is correctly propagated to the underlying saveDiff function while preserving existing functionality.


116-116: LGTM! Comprehensive implementation of category learning across all transaction operations

The learnCategories parameter is now consistently passed to all relevant transaction operations, addressing the previous review comments.

Also applies to: 130-130, 134-139, 147-147, 154-154


41-46: Verify backend handling of learnCategories parameter

The parameter is correctly passed to the backend endpoint. However, we should verify that the backend properly handles this new parameter.

✅ Verification successful

Backend properly handles learnCategories parameter

The backend implementation in packages/loot-core/src/server/accounts/transactions.ts correctly handles the learnCategories parameter. The parameter is properly typed, has appropriate default values, and is used to learn from user's categorization activity.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for backend handler of transactions-batch-update
ast-grep --pattern 'transactions-batch-update' 

Length of output: 46


Script:

#!/bin/bash
# Search for transaction batch update related patterns
rg -i "transactions?.?batch.?update" -A 5

# Search for IPC handlers related to transactions
rg -i "ipcMain\.handle.*transactions?" -A 5

# Search in potential backend directories
fd -t f . "packages/node-lib" "packages/backend" "packages/server" --exec rg -l "transactions?.?batch"

Length of output: 13998


Script:

#!/bin/bash
# Look for batchUpdateTransactions implementation
ast-grep --pattern 'function batchUpdateTransactions'

# Search for the implementation using ripgrep as fallback
rg -l "batchUpdateTransactions" packages/loot-core/src/

Length of output: 451


Script:

#!/bin/bash
# Check the implementation in transactions.ts
rg -A 10 "export async function batchUpdateTransactions" packages/loot-core/src/server/accounts/transactions.ts

# Check how learnCategories is used
rg -A 5 "learnCategories" packages/loot-core/src/server/accounts/transactions.ts

Length of output: 1031

@NullScope NullScope force-pushed the control-auto-categories branch from 5a12995 to 73c3b13 Compare January 7, 2025 16:49
@NullScope
Copy link
Contributor Author

/update-vrt

@UnderKoen
Copy link
Member

I think the case added is good. But I personally don't know how I would test this. Could you explain a flow for me

There are two flows here:

  • on the backend, whenever a transaction is added, it checks through a database query which ignores any transaction for a payee in that state. (This is the unit test that I added)
  • on the frontend, whenever a transaction is added, it sends to the backend the global setting if it should try to learn a category or not. If its disabled, it skips the first flow entirely. (The missing unit test?)

Sorry I meant in the app manually

They look good, but could you add a menu option to the context menu (right click)? And a label for on hover of the icon.

Will do 👍

Addionally if the option is disable, I don't think we should show it in the menu and/or the icon.

I'm not sure I understood this part, could you please explain again?

If you disable category learning globally the menu item and icon should not show

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
packages/loot-core/src/server/accounts/transaction-rules.ts (1)

804-805: Consider handling NULL values in the learn_categories condition.

The LEFT JOIN with payees table combined with the condition p.learn_categories = 1 might exclude transactions with NULL payees. Consider using COALESCE to handle NULL values:

-     LEFT JOIN payees p ON p.id = t.payee
-     WHERE date >= ? AND date <= ? AND is_parent = 0 AND a.closed = 0 AND p.learn_categories = 1
+     LEFT JOIN payees p ON p.id = t.payee
+     WHERE date >= ? AND date <= ? AND is_parent = 0 AND a.closed = 0 
+           AND COALESCE(p.learn_categories, 1) = 1
packages/desktop-client/src/components/payees/PayeeMenu.tsx (2)

99-105: Consider adding hover tooltip and clarifying disabled state visibility.

Based on the PR discussion:

  1. Add a tooltip to explain the Category Learning feature
  2. Clarify whether the menu item should be hidden (not just disabled) when the feature is unavailable

Consider adding a tooltip:

 {
   icon: SvgLightBulb,
   iconSize: 9,
   name: 'learn',
   text: t('Category Learning'),
+  tooltip: t('Enable or disable automatic category learning for selected payees'),
   disabled: isDisabled,
 },

99-105: Consider accessibility improvements for the Category Learning feature.

To improve accessibility:

  1. Add an ARIA label to make the purpose clear to screen readers
  2. Ensure the disabled state is properly announced
  3. Consider keyboard navigation for the feature
 {
   icon: SvgLightBulb,
   iconSize: 9,
   name: 'learn',
   text: t('Category Learning'),
+  'aria-label': t('Toggle automatic category learning for selected payees'),
   disabled: isDisabled,
 },
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b755e79 and 23961cd.

⛔ Files ignored due to path filters (4)
  • packages/desktop-client/e2e/settings.test.js-snapshots/Settings-checks-the-page-visuals-1-chromium-linux.png is excluded by !**/*.png
  • packages/desktop-client/e2e/settings.test.js-snapshots/Settings-checks-the-page-visuals-2-chromium-linux.png is excluded by !**/*.png
  • packages/desktop-client/e2e/settings.test.js-snapshots/Settings-checks-the-page-visuals-3-chromium-linux.png is excluded by !**/*.png
  • upcoming-release-notes/4081.md is excluded by !**/*.md
📒 Files selected for processing (11)
  • packages/desktop-client/src/components/payees/ManagePayees.tsx (3 hunks)
  • packages/desktop-client/src/components/payees/PayeeMenu.tsx (5 hunks)
  • packages/desktop-client/src/components/payees/PayeeTableRow.tsx (5 hunks)
  • packages/desktop-client/src/components/settings/LearnCategories.tsx (1 hunks)
  • packages/desktop-client/src/components/settings/index.tsx (2 hunks)
  • packages/desktop-client/src/components/transactions/TransactionList.jsx (5 hunks)
  • packages/loot-core/migrations/1735868660625_add_learn_categories_to_payees.sql (1 hunks)
  • packages/loot-core/src/server/accounts/transaction-rules.test.ts (1 hunks)
  • packages/loot-core/src/server/accounts/transaction-rules.ts (1 hunks)
  • packages/loot-core/src/types/models/payee.d.ts (1 hunks)
  • packages/loot-core/src/types/prefs.d.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (9)
  • packages/loot-core/src/types/prefs.d.ts
  • packages/desktop-client/src/components/settings/index.tsx
  • packages/loot-core/src/server/accounts/transaction-rules.test.ts
  • packages/desktop-client/src/components/settings/LearnCategories.tsx
  • packages/loot-core/src/types/models/payee.d.ts
  • packages/loot-core/migrations/1735868660625_add_learn_categories_to_payees.sql
  • packages/desktop-client/src/components/payees/ManagePayees.tsx
  • packages/desktop-client/src/components/payees/PayeeTableRow.tsx
  • packages/desktop-client/src/components/transactions/TransactionList.jsx
🔇 Additional comments (5)
packages/loot-core/src/server/accounts/transaction-rules.ts (1)

804-805: Implementation aligns with PR objectives.

The changes effectively implement the per-payee category learning control by:

  1. Joining with the payees table to access the learn_categories flag
  2. Filtering transactions based on this flag during category rule updates

This ensures that category learning only occurs for payees where it's enabled, meeting the PR's goal of providing granular control over the feature.

packages/desktop-client/src/components/payees/PayeeMenu.tsx (4)

6-6: LGTM! Icon choice is appropriate.

The lightbulb icon is a good metaphor for the learning feature, and using the v1 icons package maintains consistency.


17-17: LGTM! Type definition follows established patterns.

The onLearn callback type is consistent with other menu action callbacks and follows the component's typing conventions.


27-27: LGTM! Prop destructuring is correct.

The onLearn prop is properly added to the component's parameter destructuring.


56-58: LGTM! Switch case implementation is consistent.

The 'learn' case follows the established pattern of other menu actions.

@NullScope
Copy link
Contributor Author

NullScope commented Jan 7, 2025

Sorry I meant in the app manually

Ah, what I've done to test is use the demo file and do the following:

  • Change all the categories from the transactions of the Kroger payee to the same category, which should create a rule automatically, and any new transaction will automatically change to that category.
  • Delete the rule and changing the category of an existing transaction should create the rule again.
  • Disable the learning globally or on the Kroger payee.
  • Repeat step 2 but this time it should not create any rules automatically.

If you disable category learning globally the menu item and icon should not show

Good idea, will do 👍

Copy link
Contributor

@matt-fidd matt-fidd left a comment

Choose a reason for hiding this comment

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

Couple of tweaks with the translations because I've just pushed some changes in the other settings files. Other than that, looking great!

Tested and all works as expected so happy to approve when the changes have been made

@@ -0,0 +1,44 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
import { useTranslation } from 'react-i18next';
import { useTranslation, Trans } from 'react-i18next';

Comment on lines +30 to +33
<strong>{t('Category Learning')}</strong>
{t(
' will automatically determine the best category for a transaction and create a rule that sets the category for the payee.',
)}{' '}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
<strong>{t('Category Learning')}</strong>
{t(
' will automatically determine the best category for a transaction and create a rule that sets the category for the payee.',
)}{' '}
<Trans>
<strong>Category Learning</strong> will automatically determine the best category for a transaction and create a rule that sets the category for the payee.
</Trans>{' '}

to="https://actualbudget.org/docs/budgeting/rules/#automatic-rules"
linkColor="purple"
>
{t('Learn more…')}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
{t('Learn more…')}
<Trans>Learn more</Trans>

@youngcw
Copy link
Member

youngcw commented Jan 10, 2025

I think the global learning disable option should be in the payee page, not the settings page. Sorry if that has been brought up before, but I didn't see it.

@youngcw
Copy link
Member

youngcw commented Jan 10, 2025

This is minor, but it may be nice to try and make the spacing between icons/text more consistent. Maybe the icons can start centered and spread out if both are present?

image

@matt-fidd
Copy link
Contributor

I think the global learning disable option should be in the payee page, not the settings page. Sorry if that has been brought up before, but I didn't see it.

Good idea, probably the same place proposed for the schedule length adjustment setting

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

Successfully merging this pull request may close these issues.

4 participants