-
Notifications
You must be signed in to change notification settings - Fork 0
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
Back to python #33
Back to python #33
Conversation
# Conflicts: # .circleci/config.yml # Dockerfile # poetry.lock # pyproject.toml # requirements.txt # src/bss_web_file_server/config.py
WalkthroughThe project saw significant enhancements, shifting its focus towards video upload management. Major updates include Docker and GitHub CLI integration for improved CI/CD workflows, code quality improvements through linting and testing, and Dockerfile restructuring for efficiency. The backend now supports health checks and has enhanced media file handling, organizing them per member using UUIDs and improving file processing capabilities. Changes
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 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 as PR comments)
Additionally, you can add CodeRabbit Configration File (
|
Quality Gate passedIssues Measures |
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.
Review Status
Actionable comments generated: 13
Configuration used: CodeRabbit UI
Files ignored due to path filters (3)
docker-compose.yml
is excluded by:!**/*.yml
poetry.lock
is excluded by:!**/*.lock
pyproject.toml
is excluded by:!**/*.toml
Files selected for processing (16)
- .circleci/config.yml (1 hunks)
- .dockerignore (1 hunks)
- Dockerfile (1 hunks)
- README.md (1 hunks)
- requirements.txt (1 hunks)
- src/bss_web_file_server/main.py (1 hunks)
- src/bss_web_file_server/models/member.py (1 hunks)
- src/bss_web_file_server/models/video.py (1 hunks)
- src/bss_web_file_server/routers/health.py (1 hunks)
- src/bss_web_file_server/routers/member.py (1 hunks)
- src/bss_web_file_server/routers/video.py (1 hunks)
- src/bss_web_file_server/services/image.py (1 hunks)
- src/bss_web_file_server/services/member.py (1 hunks)
- src/bss_web_file_server/services/video.py (1 hunks)
- src/bss_web_file_server/settings.py (1 hunks)
- tests/bss_web_file_server/models/test_video.py (1 hunks)
Files not summarized due to errors (1)
- requirements.txt: Error: Message exceeds token limit
Additional comments: 20
src/bss_web_file_server/models/member.py (3)
- 1-1: The module-level docstring provides a clear and concise explanation of the module's purpose.
- 9-9: The class-level docstring effectively communicates the purpose of the
Member
class.- 10-10: The use of
UUID
for theid
attribute is appropriate for ensuring unique identification of members.src/bss_web_file_server/models/video.py (3)
- 1-1: The module-level docstring provides a clear and concise explanation of the module's purpose.
- 9-9: The class-level docstring effectively communicates the purpose of the
Video
class.- 12-12: Changing the
Video
class to store a list of URLs (urls: list[str]
) instead of a single URL is a significant improvement for supporting multiple video sources or versions.src/bss_web_file_server/settings.py (3)
- 1-1: The module-level docstring clearly explains the purpose of the settings module.
- 6-9: The
Settings
class is well-defined, with a clear class-level docstring explaining its purpose. It effectively reads settings from environment variables, showcasing a good practice for configuration management.- 12-12: Creating the settings instance at the module level is a common and effective pattern for making application settings accessible throughout the application.
src/bss_web_file_server/routers/health.py (3)
- 1-1: The module-level docstring clearly explains the purpose of the health check endpoints.
- 5-5: Creating the APIRouter instance with a specific tag (
"Health"
) is a good practice for organizing and documenting the API.- 8-15: The
/health
and/ping
endpoints are correctly defined and serve their purpose as basic health checks for the application.src/bss_web_file_server/services/image.py (1)
- 24-29: To improve efficiency, consider checking if the original image size is smaller than the target size before applying
thumbnail
. Resizing to a larger size can degrade image quality and is unnecessary if the original is already smaller..circleci/config.yml (3)
- 3-9: The definition of orbs and the
python
executor are correctly configured and make good use of CircleCI's features to simplify the CI/CD process.- 35-42: Consider adding comments explaining the purpose of the
verify-requirements
job, especially for those unfamiliar with the workflow. This job checks for changes in therequirements.txt
file, which is important for ensuring dependency consistency.- 54-79: The
Build
andRelease
workflows are well-structured and correctly use filters to ensure that release tasks are only run on tagged commits. This separation of build and release tasks is a best practice for CI/CD pipelines.src/bss_web_file_server/services/member.py (1)
- 1-91: > 📝 NOTE
This review was outside the diff hunks and was mapped to the diff hunk with the greatest overlap. Original lines [67-111]
The utility functions for handling base paths and conversions between member IDs and paths are well-implemented. Consider enhancing the documentation for these functions to provide more context and examples, improving clarity for future maintainers.
requirements.txt (3)
- 7-9: The version of
certifi
specified is2024.2.2
. While specifying future-dated versions can be a forward-looking approach to ensure the latest certificates are used, it's important to verify that this version is not a typo and actually intended, considering it significantly deviates from the current year.- 13-15: The conditional Python version and platform-specific dependency for
colorama
is correctly specified for Windows platforms. This is a good practice for ensuring cross-platform compatibility of the project.- 528-529: The dependency
uvloop
is specified with a platform constraint excluding Windows and Cygwin, which is appropriate givenuvloop
's Unix-only compatibility. This ensures that the project's dependencies are correctly tailored to the operating system capabilities.
def create_images(img_file: bytes, image_path: Path, image_sizes: list[ImgFormat]): | ||
""" | ||
This method will create the images in three formats: avif, webp and jpeg | ||
The files will be created in the given image_path with the given image_sizes | ||
:param img_file: the image file | ||
:param image_path: the path where the images will be saved | ||
:param image_sizes: the sizes of the images | ||
:return: None | ||
""" | ||
with Image.open(BytesIO(img_file)) as image: | ||
for size in image_sizes: | ||
i = image.copy() | ||
i.thumbnail(size=(size.width, size.height)) | ||
for img_format in img_formats: | ||
i.save(Path(image_path, size.name + "." + img_format)) |
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.
Consider adding error handling for the image processing steps to gracefully handle cases where the image cannot be opened or saved in the specified formats. This could include try-except blocks around the Image.open
and i.save
calls.
from PIL import Image | ||
|
||
ImgFormat = namedtuple("ImgFormat", ["width", "height", "name"]) | ||
img_formats = ["avif", "webp", "jpeg"] |
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.
The use of a global variable img_formats
for specifying image formats is not ideal for modularity and testability. Consider passing this as a parameter to the create_images
function or defining it within the function if it's not intended to be modified externally.
@router.post("", response_model=Video) | ||
def create_video_folder(video: Video): | ||
long_path = video_path(video.id) | ||
if long_path.exists(): | ||
return Response(status_code=status.HTTP_204_NO_CONTENT) | ||
create_folder_structure(video, with_symlinks=True) | ||
""" | ||
Create a folder structure for a video and return the video object. | ||
:param video: Video object | ||
:return: 200 and the original video object | ||
""" | ||
create_folder_structure(video) |
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.
Consider enhancing the response model to better reflect the operation's outcome, including potential errors. This could involve using a more detailed response model or HTTPException for error handling.
:param video: Video object | ||
:return: 200 and the original video object | ||
""" | ||
if not to_id_path(video.id).exists(): |
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.
Consider using HTTPException
instead of a generic Response
object for the 404 error to provide more informative error messages and maintain consistency with FastAPI's exception handling practices.
if file.content_type is not None and not re.match("image/.+", file.content_type): | ||
return Response( | ||
content="Mime is not an image format", | ||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, | ||
) |
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.
Consider using a 400 (Bad Request) status code instead of 500 for invalid content type errors, as this more accurately reflects the nature of the error. Additionally, the content type validation logic appears to be duplicated; consider abstracting this into a utility function or middleware for reuse.
def create_folder_structure(member: Member): | ||
""" | ||
This method will create the folder structure for a member. | ||
/m/{member.id}/profile | ||
And a symlink to the id folder from the url folder | ||
/member/{member.url} -> /m/{member.id} | ||
:param member: the member object | ||
:return: None | ||
""" | ||
id_path = to_id_path(member.id) | ||
id_path.mkdir(parents=True, exist_ok=True) | ||
# create a folder for the profile pictures | ||
Path(id_path, "profile").mkdir(exist_ok=True) | ||
update_symlink(member) |
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.
Consider adding error handling for directory and symlink creation operations to gracefully handle potential filesystem errors. Additionally, verify that symlink creation is robust, especially in scenarios where the target or link might already exist or in case of filesystem permissions issues.
def create_profile_picture(img_file: bytes, member_id: UUID): | ||
""" | ||
This method will create the profile picture in different formats | ||
/m/{member.id}/profile/{size}.{format} | ||
:param img_file: the image file | ||
:param member_id: the id of the member | ||
:return: None | ||
""" | ||
profile_picture_path = Path(to_id_path(member_id), "profile") | ||
profile_picture_sizes = [ | ||
ImgFormat(1920, 1080, "xl"), | ||
ImgFormat(1280, 720, "l"), | ||
ImgFormat(854, 480, "m"), | ||
] | ||
create_images(img_file, profile_picture_path, profile_picture_sizes) |
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.
Consider adding error handling around the image processing steps in create_profile_picture
to gracefully handle cases where the image cannot be processed or saved in the specified formats.
def update_symlink(member: Member): | ||
""" | ||
This method will update the symlink to the id folder from the url folder | ||
First it will remove all references to the id folder | ||
Then it will create a new symlink to the id path | ||
:param member: the member object | ||
:return: None | ||
""" | ||
id_path = to_id_path(member.id) | ||
for url_path in url_paths_base.glob("*/"): | ||
if url_path.is_symlink() and url_path.readlink().samefile(id_path): | ||
url_path.unlink(missing_ok=True) | ||
to_url_path(member.url).symlink_to( | ||
# use the absolute path to the id folder | ||
target=id_path.resolve(), | ||
target_is_directory=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.
Consider optimizing the symlink update process by directly creating or updating the symlink if possible, rather than removing all references first. This could improve efficiency and reduce the risk of leaving the member without a valid symlink in case of errors.
def create_folder_structure(video: Video): | ||
""" | ||
This method will create the folder structure for a video. | ||
/v/{video.id}/thumbnail | ||
And a symlink to the id folder from the url folders | ||
/video/{video.url[0]} -> /v/{video.id} | ||
/video/{video.url[1]} -> /v/{video.id} | ||
... | ||
:param video: the video object | ||
:return: None | ||
""" | ||
to_id_path(video.id).mkdir(parents=True, exist_ok=True) | ||
# create a folder for the thumbnails | ||
Path(to_id_path(video.id), "thumbnail").mkdir(exist_ok=True) | ||
update_symlinks(video) |
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.
Consider adding error handling for directory and symlink creation operations to gracefully handle potential filesystem errors. Additionally, verify that symlink creation is robust, especially in scenarios where the target or link might already exist or in case of filesystem permissions issues.
def create_thumbnails(img_file: bytes, video_id: UUID): | ||
""" | ||
This method will create the thumbnails in different formats | ||
/v/{video.id}/thumbnail/{size}.{format} | ||
:param img_file: the image file | ||
:param video_id: the id of the video | ||
:return: None | ||
""" | ||
thumbnail_path = Path(to_id_path(video_id), "thumbnail") | ||
poster_sizes = [ | ||
ImgFormat(1920, 1080, "fhd"), | ||
ImgFormat(1280, 720, "hd"), | ||
ImgFormat(854, 480, "sd"), | ||
] | ||
create_images(img_file, thumbnail_path, poster_sizes) |
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.
Consider adding error handling around the image processing steps in create_thumbnails
to gracefully handle cases where the image cannot be processed or saved in the specified formats.
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.
Review Status
Actionable comments generated: 0
Configuration used: CodeRabbit UI
Files selected for processing (1)
- Dockerfile (1 hunks)
Files skipped from review as they are similar to previous changes (1)
- Dockerfile
Summary by CodeRabbit