Skip to content

implement functionality of creation, validation profile, add image su…#274

Open
sashasyrota wants to merge 6 commits intomate-academy:mainfrom
sashasyrota:develop
Open

implement functionality of creation, validation profile, add image su…#274
sashasyrota wants to merge 6 commits intomate-academy:mainfrom
sashasyrota:develop

Conversation

@sashasyrota
Copy link

…pport, email notification

Copy link

@mateacademy-ai-mentor mateacademy-ai-mentor left a comment

Choose a reason for hiding this comment

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

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:

  1. Data Loss in Profile Schema: In src/schemas/profiles.py, all @field_validator methods are missing a return value statement. Pydantic validators must always return the value, otherwise the field will be set to None.
  2. Broken Email Links: The URLs generated in src/routes/accounts.py for account activation and password reset are incorrect. The password reset link is missing the necessary token, and the activation link is a GET request while the endpoint expects a POST with a body.
  3. 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 add avatar.file.seek(0) before Image.open() is called.
  4. 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

  1. Rate AI review example

  2. Re-request AI review example

  3. Add AI review comments example

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:

Choose a reason for hiding this comment

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

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.

except TokenExpiredError:
raise HTTPException(status_code=401, detail="Token has expired.")
except InvalidTokenError:
raise HTTPException(status_code=401, detail="Token has invalid")

Choose a reason for hiding this comment

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

This error message is grammatically incorrect. Consider changing the detail to "Invalid token" for better clarity.

back_populates="user",
cascade="all, delete-orphan"
cascade="all, delete-orphan",
lazy="joined"

Choose a reason for hiding this comment

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

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.

Comment on lines +133 to +137
background_tasks.add_task(
notificator.send_activation_email,
str(user_data.email),
f"{api_url}activate/?token={activation_token.token}"
)

Choose a reason for hiding this comment

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

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.

Comment on lines 318 to +325
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/"
)

Choose a reason for hiding this comment

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

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.

Comment on lines +29 to +52
@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)

Choose a reason for hiding this comment

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

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.

try:
image = Image.open(BytesIO(contents))
# image = Image.open(BytesIO(contents))
image = Image.open(avatar.file)

Choose a reason for hiding this comment

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

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).

Copy link

@mateacademy-ai-mentor mateacademy-ai-mentor left a comment

Choose a reason for hiding this comment

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

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 a GET endpoint. The /activate/ endpoint is a POST endpoint 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

  1. Rate AI review example

  2. Re-request AI review example

  3. Add AI review comments example


try:
avatar_bytes = avatar.file.read()
file_name = f"avatars/{user_id}_avatar.jpg"

Choose a reason for hiding this comment

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

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}"

Choose a reason for hiding this comment

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

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.

background_tasks.add_task(
notificator.send_password_reset_email,
str(user.email),
f"{api_url}reset-password/complete/"

Choose a reason for hiding this comment

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

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.

Copy link

@mateacademy-ai-mentor mateacademy-ai-mentor left a comment

Choose a reason for hiding this comment

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

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

  1. Rate AI review example

background_tasks.add_task(
notificator.send_activation_email,
str(user_data.email),
f"{api_url}activate/?token={activation_token.token}"

Choose a reason for hiding this comment

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

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.

info=info,
avatar=avatar
)
except Exception as exc:

Choose a reason for hiding this comment

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

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:

Choose a reason for hiding this comment

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

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)

Choose a reason for hiding this comment

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

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)
Copy link

Choose a reason for hiding this comment

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

This commented-out print statement should be removed before finalizing the code. It's good practice to keep the codebase clean of debugging artifacts.

Comment on lines +51 to +52
)
except Exception as exc:
Copy link

Choose a reason for hiding this comment

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

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.

@sashasyrota sashasyrota requested a review from vsmutok March 15, 2026 15:48
Copy link

@vsmutok vsmutok left a comment

Choose a reason for hiding this comment

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

Also, please do not ignore the comments from the AI mentor—implement the recommendations it provides.


@router.post(
"/users/{user_id}/profile/",
# response_model=TokenRefreshResponseSchema,
Copy link

Choose a reason for hiding this comment

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

remove this comment

@sashasyrota
Copy link
Author

Also, please do not ignore the comments from the AI mentor—implement the recommendations it provides.

І fix image format, delete unnesessary comments, but if i change post router to get router, test not passed

@sashasyrota sashasyrota requested a review from vsmutok March 16, 2026 08:14
Copy link

@vsmutok vsmutok left a comment

Choose a reason for hiding this comment

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

Great!

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants