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

Add support for editing expenses #379

Merged
merged 3 commits into from
Jun 26, 2024
Merged

Add support for editing expenses #379

merged 3 commits into from
Jun 26, 2024

Conversation

Hrishabh17
Copy link
Contributor

No description provided.

Copy link

coderabbitai bot commented Jun 26, 2024

Walkthrough

The changes incorporate validation enhancements, exception handling, and logging across various modules in the project. Improvements include adding new validation functions, updating function signatures to accommodate additional parameters, rearranging and enhancing test data, and increasing the logging depth to monitor specific actions. These modifications target better error handling, data validation, and performance tracking, improving overall code stability and maintainability.

Changes

File(s) Change Summary
apps/exceptions.py Added ValidationError import and new exception handling block in new_fn function.
apps/fyle/helpers.py Updated imports and added assert_valid_request function for validation.
apps/fyle/queue.py Added logging setup and modified async_import_and_export_expenses to include workspace_id parameter.
apps/fyle/tasks.py Added update_non_exported_expenses function to handle non-exported expenses.
apps/fyle/views.py Modified ExportView.post method to accept workspace_id and call async_import_and_export_expenses with it.
requirements.txt Updated fyle-integrations-platform-connector version to 1.38.1.
tests/test_fyle/fixtures.py Enhanced data dictionary with detailed expense information and added default_raw_expense.
tests/test_fyle/test_queue.py, tests/test_fyle/test_task.py Added Workspace to imports and included test logic for new changes.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant ExportView
    participant QueueModule
    participant Logger
    participant Validator

    Client->>ExportView: POST request with body
    ExportView->>ExportView: Extracts workspace_id
    ExportView->>Validator: Validate workspace_id
    Validator-->>ExportView: Validation result
    ExportView->>QueueModule: Call async_import_and_export_expenses(body, workspace_id)
    QueueModule->>Logger: Log the process
    QueueModule->>QueueModule: Perform import/export operations
    QueueModule-->>ExportView: Return result
    ExportView-->>Client: Respond with result
Loading

Poem

In the fields of code so green,
Where logic dances, serene,
With validations bright and clear,
Errors whisper, disappear,
Logs like constellations shine,
Guiding us through every line.
Thus our code, it hums in tune,
Beneath the programmer's moon. 🌙🐇


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>.
    • 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 generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @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 as 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 resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configration 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

Tests Skipped Failures Errors Time
160 0 💤 1 ❌ 0 🔥 1m 9s ⏱️

Copy link

@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: 2

Outside diff range and nitpick comments (5)
apps/fyle/queue.py (1)

[!TIP]
Codebase Verification

Verification of workspace_id parameter usage in async_import_and_export_expenses function calls:

  • tests/test_fyle/test_queue.py

    • Line: async_import_and_export_expenses(body, worksapce.id) (Note: worksapce.id seems like a typo, should be workspace.id)
  • apps/fyle/views.py

    • Line: async_import_and_export_expenses(request.data, int(kwargs['workspace_id']))

These lines confirm that the new workspace_id parameter is being passed in the function calls.

    • Typo in tests/test_fyle/test_queue.py: worksapce.id should be corrected to workspace.id.
Analysis chain

Line range hint 20-36: Review of updated function with added workspace_id parameter and logging.

The addition of the workspace_id parameter and the corresponding validation and logging enhance the function's capability to handle different workspaces and improve traceability. Ensure that all calls to this function across the application have been updated to pass the new parameter.

Consider abstracting the repeated pattern of fetching org_id and logging into a separate helper function to reduce code duplication and improve maintainability.

Scripts executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify all function calls to `async_import_and_export_expenses` include the `workspace_id` parameter.

# Test: Search for the function usage. Expect: Only occurances of the new signature.
rg --type python $'async_import_and_export_expenses'

Length of output: 82



Script:

#!/bin/bash
# Description: Verify all function calls to `async_import_and_export_expenses` include the `workspace_id` parameter.

# Test: Search for the function usage. Expect: Only occurrences of the new signature.
rg -t py 'async_import_and_export_expenses'

Length of output: 615

tests/test_fyle/test_task.py (1)

Line range hint 95-95: Refactor boolean checks to follow Pythonic practices.

Replace direct comparisons to True and False with more idiomatic Python constructs.

- if some_condition == False:
-     # do something
+ if not some_condition:
+     # do something

- if some_condition == True:
-     # do something
+ if some_condition:
+     # do something

Also applies to: 103-103

apps/fyle/helpers.py (3)

Line range hint 32-32: Refactor string formatting to use f-strings

The use of .format() is outdated. Python's f-strings provide a more readable and performant way to format strings.

- api_headers["Authorization"] = "Bearer {0}".format(access_token)
+ api_headers["Authorization"] = f"Bearer {access_token}"

Repeat this change for all instances where .format() is used.

Also applies to: 104-104, 145-145, 156-156


Line range hint 110-110: Use is not for identity tests

Using is not None is more idiomatic and clear than not ... is None.

- if not params[k] is None:
+ if params[k] is not None:

Line range hint 212-212: Remove redundant conditional expression

The ternary operation is unnecessary when converting a boolean.

- value = True if str(value) == 'true' else False
+ value = str(value) == 'true'
Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 096add4 and 23bec31.

Files selected for processing (9)
  • apps/exceptions.py (2 hunks)
  • apps/fyle/helpers.py (2 hunks)
  • apps/fyle/queue.py (3 hunks)
  • apps/fyle/tasks.py (2 hunks)
  • apps/fyle/views.py (1 hunks)
  • requirements.txt (1 hunks)
  • tests/test_fyle/fixtures.py (1 hunks)
  • tests/test_fyle/test_queue.py (2 hunks)
  • tests/test_fyle/test_task.py (2 hunks)
Files skipped from review due to trivial changes (1)
  • requirements.txt
Additional context used
Ruff
tests/test_fyle/test_task.py

95-95: Avoid equality comparisons to False; use if not ...: for false checks (E712)

Replace comparison


103-103: Avoid equality comparisons to True; use if ...: for truth checks (E712)

Replace comparison

apps/fyle/helpers.py

32-32: Use implicit references for positional format fields (UP030)

Remove explicit positional indices


32-32: Use f-string instead of format call (UP032)

Convert to f-string


104-104: Use implicit references for positional format fields (UP030)

Remove explicit positional indices


104-104: Use f-string instead of format call (UP032)

Convert to f-string


110-110: Test for object identity should be is not (E714)

Convert to is not


145-145: Use implicit references for positional format fields (UP030)

Remove explicit positional indices


145-145: Use f-string instead of format call (UP032)

Convert to f-string


156-156: Use implicit references for positional format fields (UP030)

Remove explicit positional indices


156-156: Use f-string instead of format call (UP032)

Convert to f-string


212-212: Remove unnecessary True if ... else False (SIM210)

Remove unnecessary True if ... else False

apps/fyle/tasks.py

325-328: Use expense_state = expense.accounting_export_summary.get("state", "NOT_EXPORTED") instead of an if block (SIM401)

Replace with expense_state = expense.accounting_export_summary.get("state", "NOT_EXPORTED")

Additional comments not posted (7)
apps/fyle/queue.py (1)

1-6: Ensure appropriate logging level and consistency in usage.

The logger is configured at the INFO level, which is suitable for production environments if the intention is to log high-level operational details. Ensure that this level is consistent with the overall application's logging strategy.

tests/test_fyle/test_queue.py (1)

44-48: Ensure correct handling of workspace creation or update in tests.

The test logic for creating or updating a workspace instance seems correct. However, ensure that the workspace handling does not introduce side effects that could affect other tests or the application environment.

apps/fyle/views.py (1)

120-122: Review of updated post method in ExportView with workspace_id handling.

The addition of workspace_id handling in the post method is a necessary update to support multi-tenancy. Ensure that the rest of the application correctly handles this parameter and that it is consistently used in related API calls.

Verification successful

Consistent Handling of workspace_id Verified

The workspace_id parameter is consistently used across the apps/fyle/views.py file. The updated post method in the ExportView class correctly handles this parameter, and similar methods in the file also use workspace_id appropriately.

  • lookup_field = "workspace_id"
  • lookup_url_kwarg = "workspace_id"
  • async_import_and_export_expenses(request.data, int(kwargs['workspace_id']))
Scripts executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Description: Verify consistent handling of `workspace_id` across similar views.

# Test: Search for similar views handling `workspace_id`. Expect: Consistent usage.
rg --type python 'workspace_id' -- 'apps/fyle/views.py'

Length of output: 84



Script:

#!/bin/bash
# Description: Verify consistent handling of `workspace_id` across similar views.

# Test: Search for similar views handling `workspace_id`. Expect: Consistent usage.
rg 'workspace_id' apps/fyle/views.py

Length of output: 621

apps/exceptions.py (1)

105-110: Proper handling of ValidationError with detailed logging and response.

The handling of ValidationError is appropriately implemented with detailed logging and a specific response that includes the error details. This enhances error traceability and user feedback.

apps/fyle/helpers.py (2)

8-9: Added import for ValidationError

This import is necessary for the new exception handling logic introduced in the assert_valid_request function.


13-13: Updated imports to include Workspace

The addition of Workspace to the imports is necessary for the new validation function assert_valid_request which uses this model.

tests/test_fyle/fixtures.py (1)

2-265: Expanded test data structures in fixtures.py

The expansion of the test data dictionary to include detailed fields such as accounting_export_summary, approvals, custom_fields, etc., enhances the test coverage by providing a more comprehensive dataset for testing.

Comment on lines +192 to +200
def assert_valid_request(workspace_id:int, fyle_org_id:str):
"""
Assert if the request is valid by checking
the url_workspace_id and fyle_org_id workspace
"""
workspace = Workspace.objects.get(fyle_org_id=fyle_org_id)
if workspace.id != workspace_id:
raise ValidationError('Workspace mismatch')

Copy link

Choose a reason for hiding this comment

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

New validation function: assert_valid_request

This function provides essential validation for workspace and organization ID matching, which is crucial for security and data integrity. However, the function could potentially raise an unhandled Workspace.DoesNotExist exception if no workspace matches the provided fyle_org_id.

- workspace = Workspace.objects.get(fyle_org_id=fyle_org_id)
+ workspace = Workspace.objects.filter(fyle_org_id=fyle_org_id).first()
+ if not workspace:
+     raise ValidationError('Workspace not found')
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def assert_valid_request(workspace_id:int, fyle_org_id:str):
"""
Assert if the request is valid by checking
the url_workspace_id and fyle_org_id workspace
"""
workspace = Workspace.objects.get(fyle_org_id=fyle_org_id)
if workspace.id != workspace_id:
raise ValidationError('Workspace mismatch')
def assert_valid_request(workspace_id:int, fyle_org_id:str):
"""
Assert if the request is valid by checking
the url_workspace_id and fyle_org_id workspace
"""
workspace = Workspace.objects.filter(fyle_org_id=fyle_org_id).first()
if not workspace:
raise ValidationError('Workspace not found')
if workspace.id != workspace_id:
raise ValidationError('Workspace mismatch')

Comment on lines +314 to +336
def update_non_exported_expenses(data: Dict) -> None:
"""
To update expenses not in COMPLETE, IN_PROGRESS state
"""
expense_state = None
org_id = data['org_id']
expense_id = data['id']
workspace = Workspace.objects.get(fyle_org_id = org_id)
expense = Expense.objects.filter(workspace_id=workspace.id, expense_id=expense_id).first()

if expense:
if 'state' in expense.accounting_export_summary:
expense_state = expense.accounting_export_summary['state']
else:
expense_state = 'NOT_EXPORTED'

if expense_state and expense_state not in ['COMPLETE', 'IN_PROGRESS']:
expense_obj = []
expense_obj.append(data)
expense_objects = FyleExpenses().construct_expense_object(expense_obj, expense.workspace_id)
Expense.create_expense_objects(
expense_objects, expense.workspace_id
)
Copy link

Choose a reason for hiding this comment

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

New function to update non-exported expenses

This function updates expenses that are not in the COMPLETE or IN_PROGRESS state. It's a valuable addition for handling edge cases in expense management. However, the implementation can be optimized by using .get() for dictionary access with a default value.

- if 'state' in expense.accounting_export_summary:
-     expense_state = expense.accounting_export_summary['state']
- else:
-     expense_state = 'NOT_EXPORTED'
+ expense_state = expense.accounting_export_summary.get('state', 'NOT_EXPORTED')
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def update_non_exported_expenses(data: Dict) -> None:
"""
To update expenses not in COMPLETE, IN_PROGRESS state
"""
expense_state = None
org_id = data['org_id']
expense_id = data['id']
workspace = Workspace.objects.get(fyle_org_id = org_id)
expense = Expense.objects.filter(workspace_id=workspace.id, expense_id=expense_id).first()
if expense:
if 'state' in expense.accounting_export_summary:
expense_state = expense.accounting_export_summary['state']
else:
expense_state = 'NOT_EXPORTED'
if expense_state and expense_state not in ['COMPLETE', 'IN_PROGRESS']:
expense_obj = []
expense_obj.append(data)
expense_objects = FyleExpenses().construct_expense_object(expense_obj, expense.workspace_id)
Expense.create_expense_objects(
expense_objects, expense.workspace_id
)
def update_non_exported_expenses(data: Dict) -> None:
"""
To update expenses not in COMPLETE, IN_PROGRESS state
"""
expense_state = None
org_id = data['org_id']
expense_id = data['id']
workspace = Workspace.objects.get(fyle_org_id = org_id)
expense = Expense.objects.filter(workspace_id=workspace.id, expense_id=expense_id).first()
if expense:
expense_state = expense.accounting_export_summary.get('state', 'NOT_EXPORTED')
if expense_state and expense_state not in ['COMPLETE', 'IN_PROGRESS']:
expense_obj = []
expense_obj.append(data)
expense_objects = FyleExpenses().construct_expense_object(expense_obj, expense.workspace_id)
Expense.create_expense_objects(
expense_objects, expense.workspace_id
)
Tools
Ruff

325-328: Use expense_state = expense.accounting_export_summary.get("state", "NOT_EXPORTED") instead of an if block (SIM401)

Replace with expense_state = expense.accounting_export_summary.get("state", "NOT_EXPORTED")

Copy link

Tests Skipped Failures Errors Time
160 0 💤 0 ❌ 0 🔥 1m 9s ⏱️

Copy link

@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

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 23bec31 and 588c337.

Files selected for processing (1)
  • apps/exceptions.py (2 hunks)
Files skipped from review as they are similar to previous changes (1)
  • apps/exceptions.py

elif body.get('action') == 'UPDATED_AFTER_APPROVAL' and body.get('data') and body.get('resource') == 'EXPENSE':
org_id = body['data']['org_id']
logger.info("| Updating non-exported expenses through webhook | Content: {{WORKSPACE_ID: {} Payload: {}}}".format(workspace_id, body.get('data')))
assert_valid_request(workspace_id=workspace_id, fyle_org_id=org_id)
Copy link
Contributor

Choose a reason for hiding this comment

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

let's move this out of the if block since this is repeated in both blocks and common for both

Copy link

Tests Skipped Failures Errors Time
160 0 💤 0 ❌ 0 🔥 1m 9s ⏱️

@Hrishabh17 Hrishabh17 merged commit 85d9112 into master Jun 26, 2024
1 check passed
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.

2 participants