-
Notifications
You must be signed in to change notification settings - Fork 918
Dev #557
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
WalkthroughThis pull request updates the project documentation and refines the database schema across multiple Django apps. It replaces the use of the deprecated “Tags” model with the new “Tag” model in models, serializers, and views. Several migrations have been updated, removed, or reordered to alter field references and dependencies, primarily shifting foreign key targets from hard-coded models to settings‑based user models. Additionally, a new FastAPI application with dedicated routers for authentication, leads, organizations, and users has been introduced along with middleware and improved environment variable management in settings and requirements. Changes
Sequence Diagram(s)sequenceDiagram
participant C as Client
participant M as log_request Middleware
participant A as FastAPI Auth Router
participant G as GoogleAPI
participant D as Database
C->>M: Send POST /api/auth/google with Google token
M->>A: Forward request after header setup
A->>G: Validate token against Google API
G-->>A: Return user info JSON
A->>D: Lookup/Create user by email
D-->>A: Return user record
A->>A: Generate JWT tokens using SimpleJWT
A-->>C: Return username, access & refresh tokens, user ID
sequenceDiagram
participant C as Client
participant M as FastAPI Middleware
participant L as Leads Router
participant V as verify_token Utility
participant DB as Database
C->>M: Send request to create/retrieve/update lead
M->>L: Pass request along with header info
L->>V: Verify JWT token
V-->>L: Return decoded token data
L->>DB: Perform lead operation (e.g., create, update, fetch)
DB-->>L: Return operation result
L-->>C: Return response with lead data
Poem
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
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.
Some food for thought. View full project report here.
f.write(file_content) | ||
|
||
# Create leads from file (process asynchronously) | ||
create_lead_from_file.delay(file_path, user.id, profile.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.
create_lead_from_file.delay(file_path, user.id, profile.org.id) | |
create_lead_from_file.delay(file_path, user.id, profile.org_id) |
profile.org.id
performs a database read when id
is evaluated. You could take advantage of Django's caching of related fields by using profile.org_id
, which does not do a database read. More info.
if profile.role == "ADMIN" or user.is_superuser or profile.id == comment_obj.commented_by.id: | ||
comment_obj.comment = comment_data.comment | ||
comment_obj.save() | ||
return {"error": False, "message": "Comment updated successfully"} |
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.
if profile.role == "ADMIN" or user.is_superuser or profile.id == comment_obj.commented_by.id: | |
comment_obj.comment = comment_data.comment | |
comment_obj.save() | |
return {"error": False, "message": "Comment updated successfully"} | |
if ( | |
profile.role == "ADMIN" | |
or user.is_superuser | |
or profile.id == comment_obj.commented_by_id | |
): | |
comment_obj.comment = comment_data.comment | |
comment_obj.save() | |
return {"error": False, "message": "Comment updated successfully"} |
Same as above: consider comment_obj.commented_by_id
if profile.role == "ADMIN" or user.is_superuser or profile.id == comment_obj.commented_by.id: | ||
comment_obj.delete() | ||
return {"error": False, "message": "Comment deleted successfully"} |
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.
if profile.role == "ADMIN" or user.is_superuser or profile.id == comment_obj.commented_by.id: | |
comment_obj.delete() | |
return {"error": False, "message": "Comment deleted successfully"} | |
if ( | |
profile.role == "ADMIN" | |
or user.is_superuser | |
or profile.id == comment_obj.commented_by_id | |
): | |
comment_obj.delete() | |
return {"error": False, "message": "Comment deleted successfully"} |
As above, consider comment_obj.commented_by_id
""" | ||
Comprehensive audit trail system to track all user actions | ||
""" | ||
user = models.ForeignKey(User, related_name="audit_logs", on_delete=models.SET_NULL, null=True) |
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.
user = models.ForeignKey(User, related_name="audit_logs", on_delete=models.SET_NULL, null=True) | |
user = models.ForeignKey( | |
User, related_name="audit_logs", on_delete=models.SET_NULL, null=True, blank=True | |
) |
Expect unwanted behavior if null
and blank
are different values: null
controls if the database allows no value for user
and blank
controls if the application allows no value for user
. Consider setting null
and blank
to the same value for user
. Explained here.
Comprehensive audit trail system to track all user actions | ||
""" | ||
user = models.ForeignKey(User, related_name="audit_logs", on_delete=models.SET_NULL, null=True) | ||
content_type = models.ForeignKey('contenttypes.ContentType', on_delete=models.CASCADE) |
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.
Django automatically creates a related_name
if it's not set. If it were set then a more readable and explicit relationship is set up. More info.
Associates tags with any model using generic relations | ||
""" | ||
tag = models.ForeignKey(Tag, on_delete=models.CASCADE, related_name="tagged_items") | ||
content_type = models.ForeignKey('contenttypes.ContentType', on_delete=models.CASCADE) |
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.
As above, with an explicit related_name
would be better.
""" | ||
Stores the values for custom fields | ||
""" | ||
custom_field = models.ForeignKey(CustomField, on_delete=models.CASCADE) |
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.
Likewise, with an explicit related_name
would be better.
Stores the values for custom fields | ||
""" | ||
custom_field = models.ForeignKey(CustomField, on_delete=models.CASCADE) | ||
content_type = models.ForeignKey('contenttypes.ContentType', on_delete=models.CASCADE) |
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.
Again, with an explicit related_name
would be better.
recipient = models.ForeignKey(User, related_name="notifications", on_delete=models.CASCADE) | ||
title = models.CharField(max_length=200) | ||
message = models.TextField() | ||
link = models.CharField(max_length=255, null=True, blank=True) |
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.
link = models.CharField(max_length=255, null=True, blank=True) | |
link = models.CharField(max_length=255, default="", blank=True) |
Again, consider replacing null=True
with default=""
(and blank=True
to pass validation checks).
("follow_up", "Follow Up"), | ||
("comment", "Comment"), | ||
)) | ||
content_type = models.ForeignKey('contenttypes.ContentType', on_delete=models.CASCADE, null=True, blank=True) |
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.
As above, with an explicit related_name
would be better.
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.
Actionable comments posted: 14
🔭 Outside diff range comments (7)
leads/views.py (3)
140-140
:⚠️ Potential issueUndefined Tags model due to missing import
Tags
is used here but not imported. This should be updated to use theTag
model instead.-context["tags"] = TagsSerializer(Tags.objects.all(), many=True).data +context["tags"] = TagsSerializer(Tag.objects.all(), many=True).data🧰 Tools
🪛 Ruff (0.8.2)
140-140: Undefined name
Tags
(F821)
169-174
:⚠️ Potential issueReplace Tags with Tag model
This code block uses the
Tags
model which is no longer imported. Update to use theTag
model.-tag = Tags.objects.filter(slug=t.lower()) +tag = Tag.objects.filter(slug=t.lower()) if tag.exists(): tag = tag[0] else: - tag = Tags.objects.create(name=t) + tag = Tag.objects.create(name=t)🧰 Tools
🪛 Ruff (0.8.2)
169-169: Undefined name
Tags
(F821)
170-173: Use ternary operator
tag = tag[0] if tag.exists() else Tags.objects.create(name=t)
instead ofif
-else
-blockReplace
if
-else
-block withtag = tag[0] if tag.exists() else Tags.objects.create(name=t)
(SIM108)
173-173: Undefined name
Tags
(F821)
445-450
:⚠️ Potential issueReplace Tags with Tag model
This code block also uses the
Tags
model which is no longer imported. Update to use theTag
model.-tag = Tags.objects.filter(slug=t.lower()) +tag = Tag.objects.filter(slug=t.lower()) if tag.exists(): tag = tag[0] else: - tag = Tags.objects.create(name=t) + tag = Tag.objects.create(name=t)🧰 Tools
🪛 Ruff (0.8.2)
445-445: Undefined name
Tags
(F821)
446-449: Use ternary operator
tag = tag[0] if tag.exists() else Tags.objects.create(name=t)
instead ofif
-else
-blockReplace
if
-else
-block withtag = tag[0] if tag.exists() else Tags.objects.create(name=t)
(SIM108)
449-449: Undefined name
Tags
(F821)
common/views.py (2)
799-803
:⚠️ Potential issueUndefined Tags model due to missing import
Tags
is used here but not imported. This should be updated to use theTag
model instead.-tag_obj = Tags.objects.filter(name=tag).first() +tag_obj = Tag.objects.filter(name=tag).first() if not tag_obj: - tag_obj = Tags.objects.create(name=tag) + tag_obj = Tag.objects.create(name=tag)🧰 Tools
🪛 Ruff (0.8.2)
800-800:
Tags
may be undefined, or defined from star imports(F405)
802-802:
Tags
may be undefined, or defined from star imports(F405)
847-851
:⚠️ Potential issueReplace Tags with Tag model
This code block also uses the
Tags
model which is no longer imported. Update to use theTag
model.-tag_obj = Tags.objects.filter(name=tag).first() +tag_obj = Tag.objects.filter(name=tag).first() if not tag_obj: - tag_obj = Tags.objects.create(name=tag) + tag_obj = Tag.objects.create(name=tag)🧰 Tools
🪛 Ruff (0.8.2)
848-848:
Tags
may be undefined, or defined from star imports(F405)
850-850:
Tags
may be undefined, or defined from star imports(F405)
opportunity/views.py (2)
129-134
:⚠️ Potential issueInconsistent tag model usage.
While you've updated line 92 to use the new Tag model, this block still uses the old Tags model which is now deprecated. This will cause runtime errors since Tags is no longer available.
if params.get("tags"): tags = params.get("tags") for tag in tags: - obj_tag = Tags.objects.filter(slug=tag.lower()) + obj_tag = Tag.objects.filter(name=tag.lower()) if obj_tag.exists(): obj_tag = obj_tag[0] else: - obj_tag = Tags.objects.create(name=tag) + obj_tag = Tag.objects.create(name=tag, org=request.profile.org) opportunity_obj.tags.add(obj_tag)🧰 Tools
🪛 Ruff (0.8.2)
129-129:
Tags
may be undefined, or defined from star imports(F405)
133-133:
Tags
may be undefined, or defined from star imports(F405)
237-242
:⚠️ Potential issueAnother instance of inconsistent tag model usage.
This is another location where the old Tags model is still being used. This needs to be updated to use the new Tag model.
if params.get("tags"): tags = params.get("tags") for tag in tags: - obj_tag = Tags.objects.filter(slug=tag.lower()) + obj_tag = Tag.objects.filter(name=tag.lower()) if obj_tag.exists(): obj_tag = obj_tag[0] else: - obj_tag = Tags.objects.create(name=tag) + obj_tag = Tag.objects.create(name=tag, org=request.profile.org) opportunity_object.tags.add(obj_tag)🧰 Tools
🪛 Ruff (0.8.2)
237-237:
Tags
may be undefined, or defined from star imports(F405)
241-241:
Tags
may be undefined, or defined from star imports(F405)
♻️ Duplicate comments (1)
apiv2/routers/leads.py (1)
130-130
: Use meaningful function names or unify logic.
Redefinition ofget_leads
from line 117 to 130 can be confusing. Merge them if they share the same purpose or rename them to reflect distinct endpoints, as recommended above.🧰 Tools
🪛 Ruff (0.8.2)
130-130: Redefinition of unused
get_leads
from line 117(F811)
🧹 Nitpick comments (31)
apiv2/schemas/user.py (1)
1-10
: New Pydantic model looks good, but has a minor syntax issue.The UserOut model for FastAPI is well structured with clear type annotations.
There's a missing space in line 9:
- class Config: - from_attributes =True + class Config: + from_attributes = Trueplanner/migrations/0001_initial.py (1)
1-1
: Reordered dependencies in migrationThe timestamp in the comment has been updated and dependencies have been reordered. While this doesn't affect functionality directly, it's important to ensure that migrations run in the correct order.
Consider adding a comment explaining why the migration timestamp was updated and dependencies reordered to help other developers understand the reasoning behind these changes.
Also applies to: 16-17
opportunity/serializer.py (1)
11-14
: Consider renaming TagsSerializer to match singular model nameWhile the model reference has been updated to the singular
Tag
, the serializer name remains plural asTagsSerializer
. For consistency with the new model name, consider renaming this toTagSerializer
.-class TagsSerializer(serializers.ModelSerializer): +class TagSerializer(serializers.ModelSerializer): class Meta: model = Tag fields = ("id", "name", "slug")apiv2/utils.py (2)
2-5
: Remove unused importsThere are several imports that aren't being used in this file:
os
(line 2)django
(line 3)APIRouter
(line 5)-# fastapi_app/main.py -import os -import django -# fastapi_app/routers/products.py -from fastapi import APIRouter, Depends, HTTPException, status +# fastapi_app/utils.py +from fastapi import Depends, HTTPException, status🧰 Tools
🪛 Ruff (0.8.2)
2-2:
os
imported but unusedRemove unused import:
os
(F401)
3-3:
django
imported but unusedRemove unused import:
django
(F401)
5-5:
fastapi.APIRouter
imported but unusedRemove unused import:
fastapi.APIRouter
(F401)
16-27
: Improve exception handling in verify_token functionWhen catching exceptions and raising new ones, it's best practice to use
raise ... from err
syntax to preserve the exception chain for better debugging.def verify_token(token: str = Depends(oauth2_scheme)): token_backend = TokenBackend(algorithm=ALGORITHM, signing_key=SECRET_KEY) try: # Decode the token and verify its signature and claims. validated_token = token_backend.decode(token, verify=True) return validated_token - except (TokenError, InvalidToken, TokenBackendError): + except (TokenError, InvalidToken, TokenBackendError) as err: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token", headers={"WWW-Authenticate": "Bearer"} - ) + ) from err🧰 Tools
🪛 Ruff (0.8.2)
23-27: Within an
except
clause, raise exceptions withraise ... from err
orraise ... from None
to distinguish them from errors in exception handling(B904)
crm/settings.py (1)
17-19
: Add default values or error handling for environment variablesThe current implementation will raise an exception if any of the Google environment variables are missing. Consider providing default values or more descriptive error messages.
-GOOGLE_CLIENT_ID = env("GOOGLE_CLIENT_ID") -GOOGLE_CLIENT_SECRET = env("GOOGLE_CLIENT_SECRET") -GOOGLE_LOGIN_DOMAIN = env("GOOGLE_LOGIN_DOMAIN") +GOOGLE_CLIENT_ID = env("GOOGLE_CLIENT_ID", default="") +GOOGLE_CLIENT_SECRET = env("GOOGLE_CLIENT_SECRET", default="") +GOOGLE_LOGIN_DOMAIN = env("GOOGLE_LOGIN_DOMAIN", default="")Or for required variables with better error messages:
-GOOGLE_CLIENT_ID = env("GOOGLE_CLIENT_ID") +GOOGLE_CLIENT_ID = env("GOOGLE_CLIENT_ID", default=None) +if GOOGLE_CLIENT_ID is None: + raise Exception("GOOGLE_CLIENT_ID environment variable is required for Google authentication")accounts/models.py (1)
16-33
: Proper removal of deprecated Tags model.Commenting out the Tags class is a good approach if you're in a transitional phase. However, if the transition to the new Tag model is complete, consider removing this code entirely rather than leaving it commented out.
common/models.py (2)
14-15
: Unused import forContentType
.
Since all references toContentType
are by string ("contenttypes.ContentType"
), this import is not needed.-from django.contrib.contenttypes.models import ContentType
🧰 Tools
🪛 Ruff (0.8.2)
14-14:
django.contrib.contenttypes.models.ContentType
imported but unusedRemove unused import:
django.contrib.contenttypes.models.ContentType
(F401)
70-90
: NewUserPreference
model.
The overall design looks good; however, be aware that the fieldtimezone
at line 82 overshadows the importedtimezone
fromdjango.utils
. Consider renaming the model field to avoid confusion:- timezone = models.CharField(max_length=50, default="UTC") + time_zone = models.CharField(max_length=50, default="UTC")🧰 Tools
🪛 Ruff (0.8.2)
82-82: Redefinition of unused
timezone
from line 11(F811)
common/migrations/0001_initial.py (1)
189-191
: RevisitingOrg
name andapi_key
api_key
is a TextField. If you know the max length is short, consider switching to CharField.common/utils.py (2)
149-394
: Loss of translation support for countries.Removing the
_()
translation wrappers means internationalization is no longer supported for these country names. Confirm that this is intended and doesn't affect existing localization requirements.
398-555
: Loss of translation support for currency codes.Similarly, removing the
_()
translation wrappers from currency codes discontinues their localization. Ensure this aligns with your requirements, or consider retaining translation support if needed.apiv2/routers/users.py (2)
1-10
: Incorrect file path comment.The comment on line 1 references "fastapi_app/routers/users.py" which appears to be an incorrect path. It should match your actual project structure.
-# fastapi_app/routers/users.py +# apiv2/routers/users.py
7-9
: Add error handling to the endpoint.The current implementation lacks error handling. If the database query fails, the API will return a 500 error without a helpful message.
@router.get("/") def list_users(): - return [{"id": user.id, "email": user.email} for user in User.objects.all()] + try: + return [{"id": user.id, "email": user.email} for user in User.objects.all()] + except Exception as e: + raise HTTPException(status_code=500, detail=f"Failed to retrieve users: {str(e)}")README.md (4)
95-98
: Code block needs a language specifier.The code block should have a language specifier for proper syntax highlighting.
-``` +```bash cp ENV.md .env sed -i "s|^SECRET_KEY=.*|SECRET_KEY=\"$(python3 -c 'import secrets; print(secrets.token_urlsafe(50))')\"|" .env<details> <summary>🧰 Tools</summary> <details> <summary>🪛 markdownlint-cli2 (0.17.2)</summary> 95-95: Fenced code blocks should have a language specified null (MD040, fenced-code-language) </details> </details> --- `112-116`: **Missing word in API documentation URL.** There's a missing "the" before "API" in the documentation instructions. ```diff python manage.py migrate uvicorn apiv2.main:app --reload python manage.py runserver -``` -- Then open http://localhost:8000/docs/ in your browser to explore API. +``` +- Then open http://localhost:8000/docs/ in your browser to explore the API.
🧰 Tools
🪛 LanguageTool
[uncategorized] ~116-~116: Possible missing article found.
Context: ...t:8000/docs/ in your browser to explore API. - After running API, Go to Frontend U...(AI_HYDRA_LEO_MISSING_THE)
🪛 markdownlint-cli2 (0.17.2)
116-116: Bare URL used
null(MD034, no-bare-urls)
113-113
: Include port configuration in uvicorn command.The uvicorn command doesn't specify a port, which might conflict with Django's runserver on port 8000.
-uvicorn apiv2.main:app --reload +uvicorn apiv2.main:app --reload --port 8001This ensures both the Django app and FastAPI app can run simultaneously on different ports.
116-116
: Avoid using bare URLs in Markdown.According to Markdown best practices, URLs should be properly formatted with link syntax.
-- Then open http://localhost:8000/docs/ in your browser to explore API. +- Then open [http://localhost:8000/docs/](http://localhost:8000/docs/) in your browser to explore the API.🧰 Tools
🪛 LanguageTool
[uncategorized] ~116-~116: Possible missing article found.
Context: ...t:8000/docs/ in your browser to explore API. - After running API, Go to Frontend U...(AI_HYDRA_LEO_MISSING_THE)
🪛 markdownlint-cli2 (0.17.2)
116-116: Bare URL used
null(MD034, no-bare-urls)
apiv2/routers/auth.py (4)
1-1
: Incorrect file path comment.The comment at the top of the file incorrectly references "fastapi_app/routers/users.py" instead of auth.py.
-# fastapi_app/routers/users.py +# apiv2/routers/auth.py
14-49
: Add proper docstring and improve error handling.The function lacks a docstring explaining its purpose and parameters. Additionally, the error handling could be improved to handle network issues when contacting Google's API.
@router.post("/google", summary="Login through Google") def google_login(payload: SocialLoginSerializer, request: Request): + """ + Authenticate a user using Google OAuth. + + Args: + payload: Contains the Google access token + request: FastAPI request object + + Returns: + Dict containing user info and JWT tokens + + Raises: + HTTPException: If the token is invalid or the Google API request fails + """ access_token = payload.token - # Validate token with Google - print("Google response:", access_token) - response = requests.get( - 'https://www.googleapis.com/oauth2/v2/userinfo', - params={'access_token': access_token} - ) - data = response.json() - - print("Google response data:", data) - if 'error' in data: - raise HTTPException(status_code=400, detail="Invalid or expired Google token") + try: + response = requests.get( + 'https://www.googleapis.com/oauth2/v2/userinfo', + params={'access_token': access_token}, + timeout=10 + ) + response.raise_for_status() + data = response.json() + if 'error' in data: + raise HTTPException(status_code=400, detail="Invalid or expired Google token") + if 'email' not in data: + raise HTTPException(status_code=400, detail="Email not found in Google account data") + except requests.RequestException as e: + raise HTTPException(status_code=503, detail=f"Failed to communicate with Google API: {str(e)}")
34-41
: Consider adding email verification check.The current implementation creates a user account for any Google email without verifying if the email belongs to your organization or is verified by Google.
try: user = User.objects.get(email=data['email']) except User.DoesNotExist: + # Check if email is verified by Google + if data.get('verified_email', False) is not True: + raise HTTPException(status_code=400, detail="Email not verified with Google") + + # Optional: Check if email belongs to your organization + # if not data['email'].endswith('@yourcompany.com'): + # raise HTTPException(status_code=403, detail="Only company emails are allowed") + user = User( email=data['email'], profile_pic=data.get('picture', ''), password=make_password(BaseUserManager().make_random_password()) ) user.save()
50-52
: Remove unnecessary empty lines.There are several consecutive empty lines at the end of the file that should be removed.
"refresh_token": str(token), "user_id": user.id } - - -requirements.txt (2)
19-20
: Remove commented line.There's a commented line "# remove it" which should be properly addressed. Either remove both the comment and the package, or keep the package and remove the comment.
Redis==4.6.0 -# remove it django-phonenumber-field==7.1.0
Or if the package should be removed:
Redis==4.6.0 -# remove it -django-phonenumber-field==7.1.0
23-27
: New dependencies for FastAPI integration.The added packages support the new FastAPI application. These are appropriate for integrating FastAPI with Django.
Consider adding version pinning for sub-dependencies as well using a requirements.lock file to ensure consistent builds.
apiv2/main.py (2)
15-29
: Use proper logging instead of print statements in production.
Line 18 uses aprint("Request body:", body)
statement. Printing to stdout in production may clutter logs and omit essential contextual data. Consider switching to Python’s built-inlogging
module or another robust logging solution.- print("Request body:", body) # or use proper logging + import logging + logger = logging.getLogger("uvicorn") + logger.info("Request body: %s", body)
31-36
: Remove or clarify commented-out router lines.
Commented lines 35–36 referencingleads.router
andtasks.router
might confuse future maintainers. If these routers are no longer needed, remove them. If they are planned for future use, add a brief comment explaining their purpose.# app.include_router(leads.router, prefix="/api/leads", tags=["Leads"]) -# app.include_router(tasks.router, prefix="/api/tasks", tags=["Tasks"]) +# app.include_router(tasks.router, prefix="/api/tasks", tags=["Tasks"]) # Uncomment once tasks router is readyapiv2/routers/org.py (2)
16-32
: Consider raising exceptions withraise ... from err
and removing debug prints.
- Static analysis (B904) suggests using
raise ... from ve
in theexcept ValidationError as ve:
block to better preserve tracebacks.- The debug print call on line 28 (
print(org_serialized)
) could pollute logs in production. Consider removing or replacing with structured logging.--- a/apiv2/routers/org.py +++ b/apiv2/routers/org.py @@ -28,7 +28,7 @@ def list_products(token_payload: dict = Depends(verify_token)): org_serialized = list(org.values('id', 'name')) - print(org_serialized) + # logger.debug("Serialized orgs: %s", org_serialized) return { "orgs": org_serialized, }🧰 Tools
🪛 Ruff (0.8.2)
16-16: Do not perform function call
Depends
in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable(B008)
49-52
: Raise from the caught exception for clarity.
Raising the original exception with context helps distinguish errors in exception handling from the actual error.- raise HTTPException( + raise HTTPException from ve( status_code=status.HTTP_400_BAD_REQUEST, detail=ve.message_dict )🧰 Tools
🪛 Ruff (0.8.2)
49-52: Within an
except
clause, raise exceptions withraise ... from err
orraise ... from None
to distinguish them from errors in exception handling(B904)
apiv2/routers/leads.py (3)
1-3
: Remove unused imports.
Form
is imported but never used. Consider removing it to keep imports lean and the code clean.-from typing import List, Optional, Any, Dict -from fastapi import APIRouter, Depends, HTTPException, Query, File, UploadFile, Form, status, Path, Request +from typing import List, Optional, Any, Dict +from fastapi import APIRouter, Depends, HTTPException, Query, File, UploadFile, status, Path, Request🧰 Tools
🪛 Ruff (0.8.2)
2-2:
fastapi.Form
imported but unusedRemove unused import:
fastapi.Form
(F401)
185-190
: Remove unused local variables (closed_leads_count
,closed_leads
,companies
).
They’re not utilized later in the function, causing linter warnings (F841). Consider removing them to maintain a clean codebase.closed_leads_count = queryset_closed.count() closed_leads = list(queryset_closed[offset:offset+limit].values()) - contacts = Contact.objects.filter(org=profile.org).values('id', 'first_name') - companies = Company.objects.filter(org=profile.org)🧰 Tools
🪛 Ruff (0.8.2)
185-185: Local variable
closed_leads_count
is assigned to but never usedRemove assignment to unused variable
closed_leads_count
(F841)
186-186: Local variable
closed_leads
is assigned to but never usedRemove assignment to unused variable
closed_leads
(F841)
190-190: Local variable
companies
is assigned to but never usedRemove assignment to unused variable
companies
(F841)
362-362
: Raise exceptions withraise ... from err
orraise ... from None
.
Static analysis (B904) flags these except clauses. Useraise X from e
to maintain proper traceback references.Also applies to: 734-734, 848-848, 882-882, 915-915, 1058-1058, 1093-1093, 1125-1125
🧰 Tools
🪛 Ruff (0.8.2)
362-362: Within an
except
clause, raise exceptions withraise ... from err
orraise ... from None
to distinguish them from errors in exception handling(B904)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (56)
README.md
(2 hunks)accounts/migrations/0001_initial.py
(1 hunks)accounts/migrations/0002_initial.py
(4 hunks)accounts/migrations/0003_alter_account_created_by.py
(0 hunks)accounts/models.py
(2 hunks)accounts/serializer.py
(2 hunks)accounts/views.py
(3 hunks)apiv2/main.py
(1 hunks)apiv2/routers/auth.py
(1 hunks)apiv2/routers/leads.py
(1 hunks)apiv2/routers/org.py
(1 hunks)apiv2/routers/users.py
(1 hunks)apiv2/schemas/user.py
(1 hunks)apiv2/utils.py
(1 hunks)cases/migrations/0001_initial.py
(1 hunks)cases/migrations/0002_initial.py
(3 hunks)cases/migrations/0003_alter_case_created_by.py
(0 hunks)cms/migrations/0001_initial.py
(1 hunks)common/migrations/0001_initial.py
(7 hunks)common/migrations/0002_initial.py
(6 hunks)common/migrations/0003_alter_user_profile_pic.py
(0 hunks)common/migrations/0004_alter_profile_alternate_phone.py
(0 hunks)common/migrations/0005_org_api_key.py
(0 hunks)common/migrations/0006_alter_org_api_key.py
(0 hunks)common/migrations/0007_org_is_active.py
(0 hunks)common/migrations/0008_alter_user_managers.py
(0 hunks)common/migrations/0009_user_is_staff.py
(0 hunks)common/models.py
(4 hunks)common/utils.py
(3 hunks)common/views.py
(1 hunks)contacts/migrations/0001_initial.py
(3 hunks)contacts/migrations/0002_alter_contact_created_by.py
(0 hunks)contacts/migrations/0003_alter_contact_secondary_number_and_more.py
(0 hunks)contacts/migrations/0004_alter_contact_address.py
(0 hunks)contacts/migrations/0005_alter_contact_address.py
(0 hunks)crm/settings.py
(1 hunks)emails/migrations/0001_initial.py
(1 hunks)events/migrations/0001_initial.py
(2 hunks)invoices/migrations/0001_initial.py
(2 hunks)leads/migrations/0001_initial.py
(3 hunks)leads/migrations/0002_alter_lead_created_by.py
(0 hunks)leads/models.py
(2 hunks)leads/serializer.py
(2 hunks)leads/views.py
(1 hunks)opportunity/migrations/0001_initial.py
(3 hunks)opportunity/migrations/0002_alter_opportunity_created_by.py
(0 hunks)opportunity/models.py
(2 hunks)opportunity/serializer.py
(2 hunks)opportunity/views.py
(2 hunks)planner/migrations/0001_initial.py
(2 hunks)requirements.txt
(2 hunks)tasks/migrations/0001_initial.py
(3 hunks)tasks/migrations/0002_alter_task_created_by.py
(0 hunks)teams/migrations/0001_initial.py
(2 hunks)teams/migrations/0002_alter_teams_table.py
(0 hunks)teams/migrations/0003_alter_teams_created_by.py
(0 hunks)
💤 Files with no reviewable changes (18)
- common/migrations/0003_alter_user_profile_pic.py
- common/migrations/0007_org_is_active.py
- common/migrations/0009_user_is_staff.py
- common/migrations/0005_org_api_key.py
- common/migrations/0004_alter_profile_alternate_phone.py
- common/migrations/0008_alter_user_managers.py
- contacts/migrations/0003_alter_contact_secondary_number_and_more.py
- tasks/migrations/0002_alter_task_created_by.py
- cases/migrations/0003_alter_case_created_by.py
- contacts/migrations/0002_alter_contact_created_by.py
- leads/migrations/0002_alter_lead_created_by.py
- contacts/migrations/0005_alter_contact_address.py
- opportunity/migrations/0002_alter_opportunity_created_by.py
- teams/migrations/0002_alter_teams_table.py
- teams/migrations/0003_alter_teams_created_by.py
- accounts/migrations/0003_alter_account_created_by.py
- common/migrations/0006_alter_org_api_key.py
- contacts/migrations/0004_alter_contact_address.py
🧰 Additional context used
🧬 Code Graph Analysis (14)
accounts/serializer.py (2)
accounts/models.py (3)
Account
(36-140)AccountEmail
(143-163)AccountEmailLog
(165-183)common/models.py (1)
Tag
(592-609)
leads/views.py (1)
accounts/models.py (1)
Account
(36-140)
opportunity/models.py (2)
accounts/models.py (1)
Account
(36-140)common/models.py (3)
Org
(149-169)Profile
(172-214)Tag
(592-609)
leads/models.py (1)
common/models.py (2)
Org
(149-169)Tag
(592-609)
leads/serializer.py (2)
accounts/models.py (1)
Account
(36-140)common/models.py (1)
Tag
(592-609)
accounts/models.py (1)
common/models.py (2)
Org
(149-169)Tag
(592-609)
apiv2/routers/auth.py (2)
common/models.py (2)
User
(36-67)save
(557-560)emails/views.py (1)
apiv2/routers/org.py (2)
apiv2/utils.py (1)
verify_token
(16-27)common/models.py (4)
Profile
(172-214)User
(36-67)Org
(149-169)AuditLog
(563-589)
common/models.py (3)
accounts/models.py (3)
Meta
(91-95)Meta
(156-160)Meta
(176-180)accounts/serializer.py (2)
Meta
(17-19)Meta
(32-61)emails/views.py (1)
apiv2/main.py (4)
accounts/views.py (2)
get
(150-152)get
(342-436)common/views.py (10)
get
(73-83)get
(144-202)get
(213-249)get
(335-368)get
(418-430)get
(437-441)get
(539-541)get
(588-625)get
(772-784)get
(824-829)opportunity/views.py (2)
get
(103-105)get
(318-389)leads/views.py (2)
get
(151-153)get
(358-361)
common/views.py (2)
accounts/models.py (1)
Account
(36-140)contacts/models.py (1)
Contact
(12-83)
common/migrations/0001_initial.py (1)
common/models.py (1)
generate_unique_key
(145-146)
opportunity/serializer.py (1)
common/models.py (1)
Tag
(592-609)
apiv2/routers/leads.py (3)
apiv2/utils.py (1)
verify_token
(16-27)common/models.py (6)
Attachments
(327-433)Comment
(217-301)Org
(149-169)Profile
(172-214)Tag
(592-609)User
(36-67)leads/models.py (2)
Lead
(33-134)Company
(20-31)
🪛 Ruff (0.8.2)
apiv2/utils.py
2-2: os
imported but unused
Remove unused import: os
(F401)
3-3: django
imported but unused
Remove unused import: django
(F401)
5-5: fastapi.APIRouter
imported but unused
Remove unused import: fastapi.APIRouter
(F401)
23-27: Within an except
clause, raise exceptions with raise ... from err
or raise ... from None
to distinguish them from errors in exception handling
(B904)
opportunity/views.py
92-92: Tag
may be undefined, or defined from star imports
(F405)
apiv2/routers/org.py
16-16: Do not perform function call Depends
in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
35-35: Do not perform function call Depends
in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
49-52: Within an except
clause, raise exceptions with raise ... from err
or raise ... from None
to distinguish them from errors in exception handling
(B904)
common/models.py
14-14: django.contrib.contenttypes.models.ContentType
imported but unused
Remove unused import: django.contrib.contenttypes.models.ContentType
(F401)
82-82: Redefinition of unused timezone
from line 11
(F811)
apiv2/routers/leads.py
2-2: fastapi.Form
imported but unused
Remove unused import: fastapi.Form
(F401)
119-119: Do not perform function call Depends
in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
130-130: Redefinition of unused get_leads
from line 117
(F811)
135-135: Do not perform function call Query
in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
137-137: Do not perform function call Query
in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
142-142: Do not perform function call Depends
in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
185-185: Local variable closed_leads_count
is assigned to but never used
Remove assignment to unused variable closed_leads_count
(F841)
186-186: Local variable closed_leads
is assigned to but never used
Remove assignment to unused variable closed_leads
(F841)
190-190: Local variable companies
is assigned to but never used
Remove assignment to unused variable companies
(F841)
246-246: Do not perform function call Depends
in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
344-344: Do not perform function call Depends
in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
362-362: Within an except
clause, raise exceptions with raise ... from err
or raise ... from None
to distinguish them from errors in exception handling
(B904)
372-373: Use a single if
statement instead of nested if
statements
(SIM102)
465-465: Do not perform function call Depends
in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
466-466: Do not perform function call File
in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
484-484: Within an except
clause, raise exceptions with raise ... from err
or raise ... from None
to distinguish them from errors in exception handling
(B904)
493-495: Use a single if
statement instead of nested if
statements
(SIM102)
563-563: Do not perform function call Depends
in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
564-564: Do not perform function call File
in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
582-582: Within an except
clause, raise exceptions with raise ... from err
or raise ... from None
to distinguish them from errors in exception handling
(B904)
716-716: Do not perform function call Depends
in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
734-734: Within an except
clause, raise exceptions with raise ... from err
or raise ... from None
to distinguish them from errors in exception handling
(B904)
776-776: Do not perform function call File
in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
777-777: Do not perform function call Depends
in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
830-830: Do not perform function call Depends
in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
848-848: Within an except
clause, raise exceptions with raise ... from err
or raise ... from None
to distinguish them from errors in exception handling
(B904)
864-864: Do not perform function call Depends
in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
882-882: Within an except
clause, raise exceptions with raise ... from err
or raise ... from None
to distinguish them from errors in exception handling
(B904)
897-897: Do not perform function call Depends
in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
915-915: Within an except
clause, raise exceptions with raise ... from err
or raise ... from None
to distinguish them from errors in exception handling
(B904)
945-948: Within an except
clause, raise exceptions with raise ... from err
or raise ... from None
to distinguish them from errors in exception handling
(B904)
978-978: Do not perform function call Depends
in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
999-999: Do not perform function call Depends
in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
1040-1040: Do not perform function call Depends
in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
1053-1053: Local variable profile
is assigned to but never used
Remove assignment to unused variable profile
(F841)
1058-1058: Within an except
clause, raise exceptions with raise ... from err
or raise ... from None
to distinguish them from errors in exception handling
(B904)
1075-1075: Do not perform function call Depends
in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
1088-1088: Local variable profile
is assigned to but never used
Remove assignment to unused variable profile
(F841)
1093-1093: Within an except
clause, raise exceptions with raise ... from err
or raise ... from None
to distinguish them from errors in exception handling
(B904)
1107-1107: Do not perform function call Depends
in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
1120-1120: Local variable profile
is assigned to but never used
Remove assignment to unused variable profile
(F841)
1125-1125: Within an except
clause, raise exceptions with raise ... from err
or raise ... from None
to distinguish them from errors in exception handling
(B904)
🪛 markdownlint-cli2 (0.17.2)
README.md
95-95: Fenced code blocks should have a language specified
null
(MD040, fenced-code-language)
116-116: Bare URL used
null
(MD034, no-bare-urls)
🪛 LanguageTool
README.md
[uncategorized] ~116-~116: Possible missing article found.
Context: ...t:8000/docs/ in your browser to explore API. - After running API, Go to Frontend U...
(AI_HYDRA_LEO_MISSING_THE)
🔇 Additional comments (69)
cms/migrations/0001_initial.py (1)
1-1
: The migration file timestamp has been updated.This change updates the generation timestamp of the migration file, which is a metadata change and doesn't affect functionality.
emails/migrations/0001_initial.py (1)
1-1
: The migration file timestamp has been updated.This change updates the generation timestamp of the migration file, which is a metadata change and doesn't affect functionality.
accounts/migrations/0001_initial.py (1)
1-1
:❓ Verification inconclusive
The migration file timestamp has been updated and Tags model removed.
The timestamp has been updated, and according to the AI summary, the Tags model has been removed from this migration. The removal is part of the transition from the deprecated Tags model to the new Tag model across the codebase.
🏁 Script executed:
#!/bin/bash # Check related models and references to Tags or Tag rg -A 5 "Tag model|Tags model" --glob "*.py"Length of output: 46
Action Required: Confirm Tags Model Removal and Migration Integrity
The migration file’s timestamp update appears correct. However, to be sure that the deprecated Tags model has been fully removed in favor of the new Tag model across the codebase, please run the verification script above. Once you confirm there are no lingering definitions or references to the "Tags" model, you can proceed with approving the changes.
🏁 Script executed:
#!/bin/bash echo "Searching for definitions of the deprecated Tags model (class Tags(...)):" rg "class Tags(" --glob "*.py" echo "" echo "Searching for definitions of the new Tag model (class Tag(...)):" rg "class Tag(" --glob "*.py"Length of output: 637
Action Required: Re-Verify Model Removal
The migration file timestamp update is correct and reflects the removal of the deprecated Tags model in favor of the new Tag model. However, the previous search commands produced regex errors. Please re-run the updated script above— which correctly escapes the parentheses—to confirm that there are no lingering references or definitions of the deprecated Tags model in the codebase.
Once you’ve verified that no unwanted references exist, the migration update can be finalized.
🏁 Script executed:
#!/bin/bash echo "Searching for definitions of the deprecated Tags model (class Tags(...)):" rg "class Tags\\(" --glob "*.py" echo "" echo "Searching for definitions of the new Tag model (class Tag\\(" --glob "*.py"Length of output: 522
Migration Update Verification: Timestamp & Model Removal
- The migration file’s timestamp is correctly updated.
- The deprecated
Tags
model has been removed from active code. A commented-out reference (# class Tags(BaseModel):
) remains inaccounts/models.py
; please verify if this artifact should be cleaned up.- No active references to the deprecated model were found, which aligns with the intended transition to the new
Tag
model.Please review the commented legacy code and remove it if it is no longer needed.
accounts/serializer.py (1)
3-4
: Properly replaced Tags model with Tag modelGood job migrating from the deprecated
Tags
model to the newTag
model in the serializer. This change aligns with the model updates in the rest of the codebase.Make sure to update all references to
Tags
in other files as well to maintain consistency.Also applies to: 18-18
opportunity/serializer.py (1)
4-4
: Great model update from Tags to TagYou've correctly updated the import and model reference in TagsSerializer to use the new Tag model from common.models instead of the deprecated Tags model.
Also applies to: 13-13
crm/settings.py (1)
8-19
: Good enhancement to environment variable handlingThe addition of environ.Env() provides a more robust way to manage environment variables compared to using os.environ directly. This will help with maintainability as the project grows.
leads/models.py (2)
7-7
: Import update for Tag model is correctThe import has been updated to use
Tag
from common.models, which aligns with the project's move from the deprecated Tags model to the new Tag model.
64-64
: Field updated to use the new Tag modelThe tags field has been properly updated to reference the new
Tag
model from common.models instead of the deprecatedTags
model. This change maintains the same functionality while using the improved tag implementation.invoices/migrations/0001_initial.py (2)
1-1
: Updated migration timestampThe timestamp has been updated to reflect the regeneration of this migration file.
17-18
: Reordered migration dependenciesThe dependencies have been reordered to move the swappable AUTH_USER_MODEL dependency before the accounts app dependency. This reordering doesn't change the migration's functionality but might improve dependency resolution.
leads/serializer.py (2)
3-4
: Updated imports for Tag modelImports have been correctly updated to use the new Tag model from common.models instead of the deprecated Tags model from accounts.models.
19-19
: Updated TagsSerializer to use the new Tag modelThe serializer's Meta class has been updated to reference the new Tag model, maintaining compatibility while using the improved tag implementation.
opportunity/models.py (2)
6-7
: Updated imports for Tag modelImports have been properly updated to use the Tag model from common.models instead of the deprecated Tags model from accounts.models.
51-51
: Field updated to use the new Tag modelThe tags field has been correctly updated to reference the new Tag model, which provides enhanced functionality including organization scoping, color support, and descriptions.
teams/migrations/0001_initial.py (2)
28-28
: Good improvement on user reference approach.Updating the
created_by
field to usesettings.AUTH_USER_MODEL
instead of hardcoded 'common.profile' makes your code more flexible and maintainable. The dynamic related_name pattern'%(class)s_created_by'
also helps prevent name collisions across models.
36-36
: Fixed database table name.Correcting the db_table value from 'eams' to 'teams' aligns the table name with the model name, which is good practice for clarity and maintainability.
accounts/migrations/0002_initial.py (3)
13-15
: Proper dependency ordering.Reordering the dependencies to include 'common', 'contacts', and the AUTH_USER_MODEL before they're referenced in the migration is a good practice that prevents migration errors.
75-75
: Consistent user reference model.Changing the
created_by
field to referencesettings.AUTH_USER_MODEL
with a dynamic related_name maintains consistency with changes in other models and allows for more flexibility in your user model implementation.
90-90
: Improved tag model reference.Updating the
tags
field to reference 'common.tag' instead of 'accounts.tags' correctly implements the transition to the centralized Tag model from common.accounts/models.py (2)
9-9
: Good addition of Tag import.Correctly importing Tag from common.models is necessary for the updated field reference.
68-68
: Updated tag model reference in Account model.The change to reference the new Tag model in the ManyToManyField is consistent with the project-wide transition from model-specific Tags to a centralized Tag model.
opportunity/migrations/0001_initial.py (3)
16-18
: Dependencies reordering looks correct.
No issues found. Pulling insettings.AUTH_USER_MODEL
as a swappable dependency is a standard Django practice.
41-41
: Foreign key refactor tosettings.AUTH_USER_MODEL
.
Switching fromcommon.profile
tosettings.AUTH_USER_MODEL
is a recommended approach that improves flexibility and compatibility with Django’s user model.
43-43
: Updatedtags
field to point tocommon.tag
.
This aligns with the new tagging system.leads/migrations/0001_initial.py (3)
16-16
: Dependency changes approved.
Removing the dependency onaccounts
while reordering the rest is consistent with the new relationships.
74-74
:created_by
now referencessettings.AUTH_USER_MODEL
.
This harmonizes with the shift away from referencingcommon.profile
directly.
76-76
:tags
field referencingcommon.tag
.
No issues found; it complements the updated tagging system.accounts/views.py (2)
17-17
: Refined import statements.
ImportingAccount
fromaccounts.models
remains consistent with usage across this view file.
32-32
: AddedTag
to imports fromcommon.models
.
This ensures alignment with the unified tagging system.common/models.py (7)
150-153
: Customized error messages forOrg.name
.
Great addition to improve form validation user-friendliness.
562-563
: No functional changes in these blank lines.
592-610
: NewTag
model.
Introducing color support and organization scoping aligns well with advanced tagging features.
612-629
: NewTaggedItem
model.
Enabling generic relations for tags across different models is a solid approach to flexible tagging.
631-672
: NewCustomField
model.
Allows administrators to define dynamic fields. Implementation looks strong.
674-692
: NewCustomFieldValue
model.
Stores user-defined values, completing the custom fields feature. Nicely handled.
694-723
: NewNotification
system.
Offers a flexible way to alert and inform users. Implementation details look good.tasks/migrations/0001_initial.py (3)
1-1
: Auto-generated migration timestamp
No issues with this line, as it is a standard Django comment.
16-18
: Re-ordered migration dependencies
Be sure there's no circular dependency added by this reordering.Would you like a script to check references in related apps for potential migration loops?
35-35
: Confirm removal ofblank=True
Removingblank=True
means forms will now treat this field as required. Verify if that is desired behavior.Please ensure no admin pages or forms break due to the stricter requirement.
contacts/migrations/0001_initial.py (5)
1-1
: Auto-generated migration timestamp
No concerns found.
17-17
: Swappable user model reference
Usingsettings.AUTH_USER_MODEL
for user references aligns with best practices.
36-36
: Optional secondary phone number
Making the secondary phone field optional is typical and appears correct.
43-43
: Allow blank Twitter username
This is appropriate for optional social media fields.
48-48
: Created_by references user model
Switching from a direct profile reference tosettings.AUTH_USER_MODEL
is consistent and flexible.common/migrations/0001_initial.py (11)
1-1
: Auto-generated migration timestamp
No issues with this line.
16-16
: Added contenttypes dependency
This is standard when dealing with generic relationships or tagging.
28-28
: Switchedprofile_pic
to CharField
Double-check if storing file references in a CharField is sufficient, especially for long paths or actual file uploads.Would you like me to search for any file-upload logic that still expects a FileField?
32-32
: Newis_staff
field
Implementation looks correct and will integrate with Django’s built-in admin permissions.
94-112
: IntroducedAuditLog
model
JSONField usage and optional IP/User-Agent fields are useful for auditing.
145-167
: AddedCustomField
model
This provides flexible metadata. Implementation appears solid.
168-182
: AddedCustomFieldValue
model
Stores the values for corresponding custom fields. Looks good.
202-221
: NewTag
model
Implementation and fields for color/description seem fine.
223-243
: NewUserPreference
model
Allows per-user custom settings. No concerns.
245-261
: NewTaggedItem
model
Associates tags using content types. Standard approach.
289-312
: NewNotification
model
Captures notifications with type and optional content type reference.common/utils.py (2)
30-30
: Use consistent conjunction for business naming.You've replaced "&" with "and" in multiple industry options. This minor textual update improves consistency, and it should not cause any functional issues.
Also applies to: 34-34, 40-40, 42-42
73-73
: Check lead status naming consistency.The newly added
("new", "New")
entry is lowercase for the first value in the tuple. Verify that referencing"new"
doesn't conflict with or cause confusion among other statuses that may use different naming conventions (e.g.,("in process", "In Process")
).common/migrations/0002_initial.py (8)
1-1
: Migration timestamp updated.This updated timestamp reflects the new schema state. No issues here.
15-16
: Check the new migration dependencies order.Multiple new dependencies have been introduced. Confirm that the order won't trigger circular imports or other dependency conflicts.
Also applies to: 19-24
38-72
: Verify new fields for CustomField and CustomFieldValue.These fields define important relationships (e.g.,
content_type
,created_by
,custom_field
,updated_by
). Ensure allon_delete
behaviors align with your design to avoid unintended data loss.
148-167
: Check new references in AuditLog.The added fields (
content_type
,created_by
,updated_by
,user
) may change how logs are stored. Confirm these references match your auditing strategy.
258-261
: Unique together constraint for TaggedItem.
(tag, content_type, object_id)
must now be unique. Confirm existing data is compatible, or handle duplicates before applying this migration.
262-265
: Unique together constraint for Tag.Uniqueness on
(org, name)
could cause collisions if tags share the same name in the same org. Validate data to avoid migration conflicts.
270-273
: Unique together constraint for CustomFieldValue.
(custom_field, content_type, object_id)
ensures a single value per field-object combination. This can break if multiple entries previously existed. Promptly verify existing data.
274-277
: Unique together constraint for CustomField.
(org, name, model_name)
is now unique. Ensure that older records using the same field name for different model contexts won't collide.cases/migrations/0001_initial.py (1)
1-1
: Migration timestamp updated.This is a routine notation change to reflect the new generation time. No concerns.
requirements.txt (1)
1-1
: Django version updated for security.Upgrading Django from 4.2.1 to 4.2.20 is a good practice as it includes important security fixes.
cases/migrations/0002_initial.py (2)
15-17
: Good practice to usesettings.AUTH_USER_MODEL
for references.
This ensures better compatibility with custom user models and future scalability. The dependency reordering here also follows recommended Django practices.
34-34
: Enhanced flexibility with dynamic related names.
Using'%(class)s_created_by'
and referencingsettings.AUTH_USER_MODEL
is a robust approach, aligning well with Django's best practices.
@@ -7,7 +7,7 @@ | |||
from rest_framework.response import Response | |||
from rest_framework.views import APIView | |||
|
|||
from accounts.models import Account, Tags | |||
from accounts.models import Account |
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.
Missing import for Tags model
The Tags
model has been removed from the import but is still used in multiple places throughout the file (lines 140, 169-173, 445-449). This will cause runtime errors when these lines are executed.
Fix the import by adding the Tag model from common.models:
-from accounts.models import Account
+from accounts.models import Account
+from common.models import Tag
Then replace all occurrences of Tags
with Tag
in the file.
📝 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.
from accounts.models import Account | |
from accounts.models import Account | |
from common.models import Tag |
@@ -32,7 +32,7 @@ | |||
from rest_framework.response import Response | |||
from rest_framework.views import APIView | |||
|
|||
from accounts.models import Account, Contact, Tags | |||
from accounts.models import Account, Contact |
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.
Missing import for Tags model
The Tags
model has been removed from the import but is still used in multiple places in the file (lines 799-802, 847-851). This will cause runtime errors when these lines are executed.
Fix the import by adding the Tag model from common.models:
from accounts.models import Account, Contact
+from common.models import Tag
Then replace all occurrences of Tags
with Tag
in the file.
📝 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.
from accounts.models import Account, Contact | |
from accounts.models import Account, Contact | |
from common.models import Tag |
class TagsSerailizer(serializers.ModelSerializer): | ||
class Meta: | ||
model = Tags | ||
model = Tag | ||
fields = ("id", "name", "slug") |
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.
💡 Verification agent
🧩 Analysis chain
Check for potential serializer field mapping issues
While the model has been updated, it's important to verify that the fields specified in the serializer (id
, name
, slug
) are all available in the new Tag
model. Based on the provided common/models.py
snippet, the Tag
model has additional fields like color
and description
that might be useful to include.
You may want to run the following command to verify field mapping:
🏁 Script executed:
#!/bin/bash
# Check the Tag model fields
echo "Checking Tag model fields..."
grep -A 15 "class Tag" common/models.py
Length of output: 1313
Action: Update the serializer field mapping for the Tag model.
It appears that the current serializer in accounts/serializer.py
(lines 16–19) still references ("id", "name", "slug")
. However, based on the updated Tag
model in common/models.py
, the model now includes the fields org
, name
, color
, and description
(along with the default id
field), and it does not define a slug
field. Please review whether:
- The
slug
field is intended to be a computed property (in which case, add the necessary logic in the serializer) or it has been removed in favor of the new fields. - You need to include the new fields such as
color
anddescription
(and possiblyorg
if relevant) in your serializer.
Adjusting the serializer to correctly map to the current model is necessary to avoid runtime errors or unintended behavior.
@@ -1,4 +1,4 @@ | |||
# Generated by Django 4.2.1 on 2023-06-19 13:08 | |||
# Generated by Django 4.2.1 on 2025-04-06 07:18 |
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.
Warning: Modifying existing migration file could cause issues
Changing the timestamp and dependencies in an existing migration file (0001_initial.py) can cause problems if this migration has already been applied in any environment. Migration files should generally be treated as immutable once they've been committed, as changes can disrupt Django's migration system.
If you need to add a dependency on the common app, consider creating a new migration instead of modifying an existing one.
Also applies to: 15-15
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | ||
|
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.
🛠️ Refactor suggestion
Remove duplicate BASE_DIR definition
BASE_DIR is defined twice in the file - once at line 11 and again at line 39. This is redundant and could lead to confusion. Keep only one definition.
# Set the project base directory
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Take environment variables from .env file
environ.Env.read_env(os.path.join(BASE_DIR, '.env'))
GOOGLE_CLIENT_ID = env("GOOGLE_CLIENT_ID")
GOOGLE_CLIENT_SECRET = env("GOOGLE_CLIENT_SECRET")
GOOGLE_LOGIN_DOMAIN = env("GOOGLE_LOGIN_DOMAIN")
# JWT_AUTH = {
...
}
-# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
-BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
Also applies to: 39-39
tags = Tag.objects.all() | ||
tags = TagsSerailizer(tags, many=True).data |
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.
Incorrect serializer name for tags.
There seems to be a typo in the serializer class name (TagsSerailizer
instead of TagsSerializer
), which can break the code if the mismatch persists.
Below is a suggested fix:
- from accounts.serializer import (
- AccountCreateSerializer,
- AccountSerializer,
- EmailSerializer,
- TagsSerailizer,
- ...
- )
+ from accounts.serializer import (
+ AccountCreateSerializer,
+ AccountSerializer,
+ EmailSerializer,
+ TagsSerializer,
+ ...
+ )
...
- tags = TagsSerailizer(tags, many=True).data
+ tags = TagsSerializer(tags, many=True).data
📝 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.
tags = Tag.objects.all() | |
tags = TagsSerailizer(tags, many=True).data | |
- from accounts.serializer import ( | |
- AccountCreateSerializer, | |
- AccountSerializer, | |
- EmailSerializer, | |
- TagsSerailizer, | |
- ... | |
- ) | |
+ from accounts.serializer import ( | |
+ AccountCreateSerializer, | |
+ AccountSerializer, | |
+ EmailSerializer, | |
+ TagsSerializer, | |
+ ... | |
+ ) | |
... | |
tags = Tag.objects.all() | |
- tags = TagsSerailizer(tags, many=True).data | |
+ tags = TagsSerializer(tags, many=True).data |
@@ -127,7 +183,7 @@ class Migration(migrations.Migration): | |||
migrations.AddField( | |||
model_name='attachments', | |||
name='created_by', |
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.
🛠️ Refactor suggestion
Validate updated attachments reference.
Switching created_by
from common.profile
to settings.AUTH_USER_MODEL
can be breaking if existing references assume a Profile
object. Make sure data migrations handle this transition smoothly.
@router.get("/") | ||
def list_users(): | ||
return [{"id": user.id, "email": user.email} for user in User.objects.all()] |
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.
Missing authentication for user data endpoint.
This endpoint exposes user data without any authentication mechanism, which poses a security risk. User information should be protected.
Consider adding authentication middleware or a dependency to protect this route:
-@router.get("/")
+@router.get("/", dependencies=[Depends(verify_token)])
def list_users():
return [{"id": user.id, "email": user.email} for user in User.objects.all()]
You'll need to import the authentication dependency:
from apiv2.utils import verify_token
from fastapi import Depends
🛠️ Refactor suggestion
Implement pagination for scalability.
Fetching all users at once can lead to performance issues as your user base grows. Consider implementing pagination to limit the number of results returned.
-@router.get("/")
+@router.get("/")
def list_users():
- return [{"id": user.id, "email": user.email} for user in User.objects.all()]
+ return [{"id": user.id, "email": user.email} for user in User.objects.all()[:100]] # Temporary limit
A better solution would be to implement proper pagination:
@router.get("/")
def list_users(skip: int = 0, limit: int = 100):
users = User.objects.all()[skip:skip+limit]
return [{"id": user.id, "email": user.email} for user in users]
|
||
|
||
# Validate token with Google | ||
print("Google response:", access_token) |
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.
🛠️ Refactor suggestion
Remove debug print statements.
Debug print statements should not be included in production code as they could expose sensitive information and clutter logs.
- print("Google response:", access_token)
response = requests.get(
'https://www.googleapis.com/oauth2/v2/userinfo',
params={'access_token': access_token}
)
data = response.json()
- print("Google response data:", data)
if 'error' in data:
raise HTTPException(status_code=400, detail="Invalid or expired Google token")
Also applies to: 27-27
def get_leads( | ||
request: Request, | ||
token_payload: dict = Depends(verify_token) | ||
): | ||
|
||
return { | ||
"status": force_strings(LEAD_STATUS), | ||
"source": force_strings(LEAD_SOURCE), | ||
"countries": force_strings(COUNTRIES), | ||
"industries": force_strings(INDCHOICES), | ||
} | ||
|
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.
🛠️ Refactor suggestion
Avoid overshadowing functions named get_leads
.
There is a @router.get("/meta/")
and another @router.get("/")
both using get_leads
, which triggers redefinition warnings (F811). Rename one of them to avoid confusion and maintain clarity.
@router.get("/meta/", response_model=Dict[str, Any])
-def get_leads(
+def get_lead_metadata(
request: Request,
token_payload: dict = Depends(verify_token)
):
...
📝 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.
def get_leads( | |
request: Request, | |
token_payload: dict = Depends(verify_token) | |
): | |
return { | |
"status": force_strings(LEAD_STATUS), | |
"source": force_strings(LEAD_SOURCE), | |
"countries": force_strings(COUNTRIES), | |
"industries": force_strings(INDCHOICES), | |
} | |
@router.get("/meta/", response_model=Dict[str, Any]) | |
def get_lead_metadata( | |
request: Request, | |
token_payload: dict = Depends(verify_token) | |
): | |
return { | |
"status": force_strings(LEAD_STATUS), | |
"source": force_strings(LEAD_SOURCE), | |
"countries": force_strings(COUNTRIES), | |
"industries": force_strings(INDCHOICES), | |
} |
🧰 Tools
🪛 Ruff (0.8.2)
119-119: Do not perform function call Depends
in argument defaults; instead, perform the call within the function, or read the default from a module-level singleton variable
(B008)
Summary by CodeRabbit
New Features
Documentation
Chores