-
Notifications
You must be signed in to change notification settings - Fork 5
ZOHO - Transaction sync script #600
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
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR Summary
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here: app.greptile.com/review/github.
2 file(s) reviewed, no comment(s)
Edit PR Review Bot Settings | Greptile
832dcd5 to
4ee270f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR Summary
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here: app.greptile.com/review/github.
2 file(s) reviewed, no comment(s)
Edit PR Review Bot Settings | Greptile
|
use the django extensions runscript format for the script https://django-extensions.readthedocs.io/en/latest/runscript.html |
|
I also don't see any use for the classes here, I think it would be better to just use functions. For the logger you can just use |
| ): | ||
| stats = {"processed": 0, "successful": 0, "failed": 0, "errors": []} | ||
| # Build transaction query with optional date filtering | ||
| transaction_query = AppUserTransaction.objects.all().order_by("created_at") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AppUserTransaction.objects.select_related("user", "workspace__created_by").all().order_by(...)
(fewer database queries)
|
bump |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR Summary
This PR adds a new script for syncing transaction data with Zoho CRM, implementing a comprehensive integration system with bulk upload capabilities.
- Added
scripts/zoho_crm.pywith three main classes:ConfigurableFieldMapperfor field mapping,ZohoBulkUploaderfor handling bulk uploads, andZOHOSyncfor orchestrating the synchronization process - Implemented batch processing to handle large datasets efficiently with configurable batch sizes
- Added proper error handling and logging throughout the synchronization process
- Added Zoho CRM configuration settings (
ZOHO_AUTH_CODEandZOHO_ORG_ID) to Django settings - Created a modular design with separate mapping configurations for contacts, accounts, and deals
Gooey AI: Gooey-server PR Review Bot
2 file(s) reviewed, 4 comment(s)
Edit PR Review Bot Settings | Greptile
| import json | ||
| from typing import List, Dict, Optional, Any, Callable | ||
| from datetime import datetime, timedelta | ||
| import requests | ||
| import json | ||
| import csv |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: json is imported twice (lines 5 and 9) and List, Dict, Optional, Any are imported twice (lines 6 and 12)
| import json | |
| from typing import List, Dict, Optional, Any, Callable | |
| from datetime import datetime, timedelta | |
| import requests | |
| import json | |
| import csv | |
| import json | |
| from typing import List, Dict, Optional, Any, Callable, Tuple | |
| from datetime import datetime, timedelta | |
| import requests | |
| import csv |
| def _get_user_confirmation(self, label: str) -> bool: | ||
| f"Are you sure you want to proceed with the upload for batch {label}?" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
syntax: Line 546 is an f-string but it's not assigned to anything - should be a docstring or comment
| def _get_user_confirmation(self, label: str) -> bool: | |
| f"Are you sure you want to proceed with the upload for batch {label}?" | |
| def _get_user_confirmation(self, label: str) -> bool: | |
| """Ask user for confirmation before proceeding with batch upload.""" |
| start_date = datetime.strptime(argv[0], "%Y-%m-%d") if argv else None | ||
| run_optimized_sync() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: start_date is parsed from command line but not passed to run_optimized_sync()
| start_date = datetime.strptime(argv[0], "%Y-%m-%d") if argv else None | |
| run_optimized_sync() | |
| start_date = datetime.strptime(argv[0], "%Y-%m-%d") if argv else None | |
| run_optimized_sync(start_date=start_date) |
| if accounts: | ||
| account_file = self.bulk_uploader.create_bulk_upload_file( | ||
| accounts, "Accounts", batch_label | ||
| ) | ||
|
|
||
| if contacts: | ||
| contact_file = self.bulk_uploader.create_bulk_upload_file( | ||
| contacts, "Contacts", batch_label | ||
| ) | ||
|
|
||
| if deals: | ||
| deal_file = self.bulk_uploader.create_bulk_upload_file( | ||
| deals, "Deals", batch_label | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: Missing initialization of variables before conditional blocks. If any of these conditions are false, the variables will be undefined when used later
| if accounts: | |
| account_file = self.bulk_uploader.create_bulk_upload_file( | |
| accounts, "Accounts", batch_label | |
| ) | |
| if contacts: | |
| contact_file = self.bulk_uploader.create_bulk_upload_file( | |
| contacts, "Contacts", batch_label | |
| ) | |
| if deals: | |
| deal_file = self.bulk_uploader.create_bulk_upload_file( | |
| deals, "Deals", batch_label | |
| ) | |
| account_file = None | |
| contact_file = None | |
| deal_file = None | |
| if accounts: | |
| account_file = self.bulk_uploader.create_bulk_upload_file( | |
| accounts, "Accounts", batch_label | |
| ) | |
| if contacts: | |
| contact_file = self.bulk_uploader.create_bulk_upload_file( | |
| contacts, "Contacts", batch_label | |
| ) | |
| if deals: | |
| deal_file = self.bulk_uploader.create_bulk_upload_file( | |
| deals, "Deals", batch_label | |
| ) |
| "contact_mapping": { | ||
| "Gooey_User_ID": {"db_key": "uid"}, | ||
| "Gooey_Admin_Link": { | ||
| "db_key": "id", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use named tuple instead of dict
| :return: Mapping configuration dictionary | ||
| """ | ||
| default_config = { | ||
| "contact_mapping": { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
contact_mapping, transaction_mapping & workspace_mapping can be 3 top level constants instead of a dynamic string inside a dict generated from a class method which is very un-necessary and adds cruft
|
|
||
| ZOHO_CONTACT_API = "https://www.zohoapis.com/crm/v2/Contacts" | ||
| ZOHO_DEAL_API = "https://www.zohoapis.com/crm/v7/Deals" | ||
| ZOHO_HEADERS = {"Authorization": f"Bearer {settings.ZOHO_AUTH_CODE}"} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
generate this inside a method like we do in azure_image_moderation so that settings can be loaded at runtime if needed
| ZOHO_HEADERS = {"Authorization": f"Bearer {settings.ZOHO_AUTH_CODE}"} | ||
| ZOHO_BULK_FILE_UPLOAD_API = "https://content.zohoapis.com/crm/v7/upload" | ||
| ZOHO_BULK_CREATE_JOB = "https://www.zohoapis.com/crm/bulk/v7/write" | ||
| ZOHO_ORG_ID = settings.ZOHO_ORG_ID |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
un-necessary to re-declare this variable
| else: | ||
| zoho_fields[zoho_field] = field_config.get("default") or "None" | ||
|
|
||
| except Exception as e: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
|
|
||
| return default_config | ||
|
|
||
| def _deep_merge(self, base: Dict, update: Dict): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unused method
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

Q/A checklist
You can visualize this using tuna:
To measure import time for a specific library:
To reduce import times, import libraries that take a long time inside the functions that use them instead of at the top of the file:
Legal Boilerplate
Look, I get it. The entity doing business as “Gooey.AI” and/or “Dara.network” was incorporated in the State of Delaware in 2020 as Dara Network Inc. and is gonna need some rights from me in order to utilize my contributions in this PR. So here's the deal: I retain all rights, title and interest in and to my contributions, and by keeping this boilerplate intact I confirm that Dara Network Inc can use, modify, copy, and redistribute my contributions, under its choice of terms.