implement functionality of creation, validation profile, add image su…#274
implement functionality of creation, validation profile, add image su…#274sashasyrota wants to merge 6 commits intomate-academy:mainfrom
Conversation
…pport, email notification
mateacademy-ai-mentor
left a comment
There was a problem hiding this comment.
Review Summary
Great work on implementing the core logic for email notifications and building a comprehensive test suite for the new profile endpoint. However, this requires changes because there are a few critical bugs that prevent the new features from functioning as required.
Here are the main issues to address:
- Data Loss in Profile Schema: In
src/schemas/profiles.py, all@field_validatormethods are missing areturn valuestatement. Pydantic validators must always return the value, otherwise the field will be set toNone. - Broken Email Links: The URLs generated in
src/routes/accounts.pyfor account activation and password reset are incorrect. The password reset link is missing the necessary token, and the activation link is aGETrequest while the endpoint expects aPOSTwith a body. - Image Validation Fails: In
src/validation/profile.py, the image validation function reads the file to check its size but doesn't reset the file pointer. You need to addavatar.file.seek(0)beforeImage.open()is called. - Incorrect Error Handling: In
src/routes/profiles.py, the code checks if a user has a profile before checking if the user exists. This will cause a 500 error for a non-existent user instead of the required 401.
Once these issues are resolved, the implementation should be fully functional. You're very close!
✨ Please rate this review in the Platform's notification center1. I will be grateful for your feedback! ✨
Important
Fix the issues, commit the changes, and then re-request my review using the Re-request review button2 in the Reviewers tab.
Tip
If you have any comments about this review, please leave a message in the Add a comment section below and mention me with the @mateacademy-ai-mentor tag3.
I will read your message during my next review of your pull request. Please do not reply directly to my comments, as that feature is not supported yet.
Caution
Do not close the pull request until the review is complete. Closing it early can lead to unexpected behavior and disrupt the review process.
Footnotes
| current_user = await get_user_by_id(user_jwt["user_id"], db) | ||
| query_user = await get_user_by_id(user_id, db) | ||
|
|
||
| if query_user.profile: |
There was a problem hiding this comment.
There's a potential AttributeError here if query_user is None because the user ID doesn't exist. The check for user existence on line 66 should be performed before attempting to access any of the user's attributes, such as .profile.
src/routes/profiles.py
Outdated
| except TokenExpiredError: | ||
| raise HTTPException(status_code=401, detail="Token has expired.") | ||
| except InvalidTokenError: | ||
| raise HTTPException(status_code=401, detail="Token has invalid") |
There was a problem hiding this comment.
This error message is grammatically incorrect. Consider changing the detail to "Invalid token" for better clarity.
src/database/models/accounts.py
Outdated
| back_populates="user", | ||
| cascade="all, delete-orphan" | ||
| cascade="all, delete-orphan", | ||
| lazy="joined" |
There was a problem hiding this comment.
Using lazy="joined" is a valid performance optimization that avoids the N+1 query problem by fetching the user's profile in the same query as the user. However, this means a JOIN will be performed every time a UserModel is loaded, even in scenarios where the profile data isn't needed (like during login). For frequently accessed one-to-one relationships this is fine, but it's good to be aware of the trade-off. The default lazy="select" would load the profile only when accessed.
| background_tasks.add_task( | ||
| notificator.send_activation_email, | ||
| str(user_data.email), | ||
| f"{api_url}activate/?token={activation_token.token}" | ||
| ) |
There was a problem hiding this comment.
The activation URL generated here is a GET link containing only a token. However, the activate_account endpoint is a POST endpoint that expects both an email and a token in the request body. This mismatch will prevent the activation flow from working correctly. Please ensure the generated URL is compatible with the target endpoint's requirements.
| reset_token = PasswordResetTokenModel(user_id=cast(int, user.id)) | ||
| db.add(reset_token) | ||
| await db.commit() | ||
| background_tasks.add_task( | ||
| notificator.send_password_reset_email, | ||
| str(user.email), | ||
| f"{api_url}reset-password/complete/" | ||
| ) |
There was a problem hiding this comment.
The password reset link sent to the user is missing the required token. The reset_password endpoint needs this token to proceed. Additionally, the database operations are not ordered correctly to retrieve the token's value before sending the email. Consider using db.flush() to get the generated token, include it in the URL, and then call db.commit() at the end of the block, similar to the implementation in the register_user function.
| @field_validator("first_name", "last_name") | ||
| @classmethod | ||
| def validate_first_last_name(cls, value): | ||
| validate_name(value) | ||
|
|
||
| @field_validator("gender") | ||
| @classmethod | ||
| def validate_gender(cls, value): | ||
| validate_gender(value) | ||
|
|
||
| @field_validator("date_of_birth") | ||
| @classmethod | ||
| def validate_date_of_birth(cls, value): | ||
| validate_birth_date(value) | ||
|
|
||
| @field_validator("info") | ||
| @classmethod | ||
| def validate_info(cls, value): | ||
| validate_info(value) | ||
|
|
||
| @field_validator("avatar") | ||
| @classmethod | ||
| def validate_avatar(cls, value): | ||
| validate_image(value) |
There was a problem hiding this comment.
All the @field_validator methods in this schema (validate_first_last_name, validate_gender, validate_date_of_birth, validate_info, and validate_avatar) are missing a return value statement. A Pydantic validator must always return a value; otherwise, the field it's validating will be assigned None, causing data loss.
src/validation/profile.py
Outdated
| try: | ||
| image = Image.open(BytesIO(contents)) | ||
| # image = Image.open(BytesIO(contents)) | ||
| image = Image.open(avatar.file) |
There was a problem hiding this comment.
The file stream's cursor is at the end after being read on line 19 to check the file size. Calling Image.open(avatar.file) here will fail because there's nothing left to read. You should reset the stream's position to the beginning before this line by calling avatar.file.seek(0).
…g, change joined to selectin
mateacademy-ai-mentor
left a comment
There was a problem hiding this comment.
Review Summary
Great work on this task! You've successfully addressed most of the feedback from the previous review, especially with the validation logic.
However, I'm requesting a few changes because the email notification links are currently non-functional, which is a critical part of the requirements.
Here are the details:
- Account Activation Link: In
src/routes/accounts.py, the URL generated for account activation points to aGETendpoint. The/activate/endpoint is aPOSTendpoint that requires the email and token in the request body, so users clicking the link cannot activate their accounts. - Password Reset Link: The URL for password reset is missing the
reset_token.token. The/reset-password/complete/endpoint requires this token to function.
Fixing these will make the account management features fully operational. Once these are addressed, I'll be happy to approve your work!
✨ Please rate this review in the Platform's notification center1. I will be grateful for your feedback! ✨
Important
Fix the issues, commit the changes, and then re-request my review using the Re-request review button2 in the Reviewers tab.
Tip
If you have any comments about this review, please leave a message in the Add a comment section below and mention me with the @mateacademy-ai-mentor tag3.
I will read your message during my next review of your pull request. Please do not reply directly to my comments, as that feature is not supported yet.
Caution
Do not close the pull request until the review is complete. Closing it early can lead to unexpected behavior and disrupt the review process.
Footnotes
src/routes/profiles.py
Outdated
|
|
||
| try: | ||
| avatar_bytes = avatar.file.read() | ||
| file_name = f"avatars/{user_id}_avatar.jpg" |
There was a problem hiding this comment.
The filename for the avatar is hardcoded with a .jpg extension. However, the validation allows for other formats like PNG. This can cause a mismatch between the file's content and its extension. Consider dynamically setting the extension based on the uploaded file's type, for example by using the extension from avatar.filename.
| background_tasks.add_task( | ||
| notificator.send_activation_email, | ||
| str(user_data.email), | ||
| f"{api_url}activate/?token={activation_token.token}" |
There was a problem hiding this comment.
The URL generated for the activation email is for a GET request and only includes the token. However, the /activate/ endpoint is a POST endpoint and requires both the email and token in the request body. A user clicking this link won't be able to activate their account, which makes this feature non-functional.
src/routes/accounts.py
Outdated
| background_tasks.add_task( | ||
| notificator.send_password_reset_email, | ||
| str(user.email), | ||
| f"{api_url}reset-password/complete/" |
There was a problem hiding this comment.
The URL for the password reset email is missing the reset token. According to the requirements, the /reset-password/complete/ endpoint needs this token to work. This was a critical issue from the previous review that remains unaddressed. You need to include the generated reset_token.token in this URL.
mateacademy-ai-mentor
left a comment
There was a problem hiding this comment.
Review Summary
I am approving your submission as an educational override to help you progress, but it's important to address the critical issue we discussed previously.
You've done an excellent job implementing the new user profile creation feature—it's well-tested and meets all requirements. However, the core problem in src/routes/accounts.py with the non-functional email links still needs to be fixed. The activation and password reset links point to POST endpoints, but a user clicking them from an email sends a GET request, causing the action to fail. You'll need to create corresponding GET endpoints to handle these links correctly.
I've also left a few minor suggestions in the comments for improving code quality, such as using more specific exceptions and removing commented-out code. Keep up the great work, and focus on resolving the email link functionality!
✨ Please rate this review in the Platform's notification center1. I will be grateful for your feedback! ✨
Footnotes
| background_tasks.add_task( | ||
| notificator.send_activation_email, | ||
| str(user_data.email), | ||
| f"{api_url}activate/?token={activation_token.token}" |
There was a problem hiding this comment.
The URL generated for account activation is not functional. The /activate/ endpoint is a POST method that expects a request body with an email and token. However, a user clicking this link in an email will send a GET request with only the token in the URL, which will fail.
This same problem exists for the password reset link generated on line 295. This was a critical issue from the previous review that needs to be resolved for the feature to be usable.
src/routes/profiles.py
Outdated
| info=info, | ||
| avatar=avatar | ||
| ) | ||
| except Exception as exc: |
There was a problem hiding this comment.
Catching a generic Exception here is a bit risky because it could hide unexpected errors that are not related to Pydantic validation. It would be safer to catch the specific pydantic.ValidationError to ensure you're only handling validation failures as intended.
| if query_user.profile: | ||
| raise HTTPException(status_code=400, detail="User already has a profile.") | ||
|
|
||
| if current_user.id != user_id and current_user.group_id == 1: |
There was a problem hiding this comment.
Using a magic number like 1 for the user group ID makes the code less readable and harder to maintain. It's better to use the UserGroupEnum for comparison. To do this without causing extra database queries, you might consider adjusting the get_user_by_id function to eager-load the group relationship.
| } | ||
|
|
||
| response = await client.post(profile_url, headers=headers, files=files) | ||
| # print(user.profile) |
There was a problem hiding this comment.
This commented-out print statement should be removed before finalizing the code. It's good practice to keep the codebase clean of debugging artifacts.
| } | ||
|
|
||
| response = await client.post(profile_url, headers=headers, files=files) | ||
| # print(user.profile) |
There was a problem hiding this comment.
This commented-out print statement should be removed before finalizing the code. It's good practice to keep the codebase clean of debugging artifacts.
src/routes/profiles.py
Outdated
| ) | ||
| except Exception as exc: |
There was a problem hiding this comment.
Catching a generic Exception here is a bit risky because it could hide unexpected errors that are not related to Pydantic validation. It would be safer to catch the specific pydantic.ValidationError to ensure you're only handling validation failures as intended.
vsmutok
left a comment
There was a problem hiding this comment.
Also, please do not ignore the comments from the AI mentor—implement the recommendations it provides.
src/routes/profiles.py
Outdated
|
|
||
| @router.post( | ||
| "/users/{user_id}/profile/", | ||
| # response_model=TokenRefreshResponseSchema, |
І fix image format, delete unnesessary comments, but if i change post router to get router, test not passed |
…pport, email notification