-
Notifications
You must be signed in to change notification settings - Fork 572
Docs/UI improvements #765
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
Docs/UI improvements #765
Conversation
- Improved spacing and alignment for better visual hierarchy - Enhanced readability of text and headings with better typography - Added styling refinements for consistency across sections - Improved contrast and clarity for content blocks (tables, code blocks, blockquotes, admonitions) - Better spacing for lists, images, and navigation sections - Added subtle shadows and hover effects for better visual feedback
- Enhanced index.md with better structure, spacing, and visual hierarchy - Improved CSS with additional styling for better readability and contrast - Enhanced heading structure in features.md for better organization - Added dark mode support improvements - Improved spacing, alignment, and visual consistency across all pages - Enhanced link styling, image hover effects, and content block styling - Better responsive design and mobile-friendly improvements
…SIE-Org#733) Sidebar Navigation Improvements: - Enhanced navigation link styling with hover effects and smooth transitions - Improved active state styling with accent color and border - Better section header typography and spacing - Enhanced nested navigation with visual hierarchy - Improved table of contents styling - Better mobile navigation support Documentation Section Cards Improvements: - Added gradient backgrounds and enhanced shadows - Added animated top border on hover (accent color) - Improved card hover effects with lift animation - Enhanced link styling with arrow indicators - Better spacing and padding throughout - Improved dark mode support - Added backdrop filter for modern glass effect - Enhanced typography with better font weights and colors
- Removed excessive gradients, animations, and effects - Simplified card styling with clean borders and subtle shadows - Reduced sidebar navigation effects to minimal hover states - Changed arrow indicators to simple bullet points - Removed backdrop filters and complex transitions - Maintained clean, readable design with better spacing
…IE-Org#733) - Increased card padding and spacing for better visual breathing room - Enhanced card headings with accent color and better typography - Improved arrow indicators with hover animations - Better link styling with smooth transitions - Enhanced hover effects with subtle lift animation - Improved spacing between list items - Better card height consistency with flexbox - Enhanced dark mode support
WalkthroughAdds Pydantic models and a FastAPI "PictoPy Albums API" with in-memory photo/album stores and CRUD endpoints, a lightweight Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant API as FastAPI App
participant Store as In-Memory Store
participant AI as ai_stub
Client->>API: POST /albums/{id}/refresh
API->>Store: Get album by id
Store-->>API: album
API->>Store: Get photos dict
Store-->>API: photos
API->>AI: refresh_album_contents(album, photos)
alt album.type == "object"
AI->>AI: case-insensitive substring match of album.name vs photo.tags
AI-->>API: filtered photo IDs
else album.type == "face"
AI->>AI: select photos where faces > 0
AI-->>API: filtered photo IDs
else album.type == "manual" or unknown
AI-->>API: return album.photos unchanged
end
API->>Store: Update album.photos
Store-->>API: updated album
API-->>Client: 200 OK (updated SmartAlbum)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (3 warnings)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
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: 6
🧹 Nitpick comments (4)
models.py (1)
13-18: Consider validating the type field with Literal.The
typefield comment indicates only three valid values ('object', 'face', 'manual'), but there's no validation enforcing this constraint. UsingLiteralwould provide type safety and runtime validation.from pydantic import BaseModel, Field -from typing import List +from typing import List, Literal from uuid import uuid4 class SmartAlbum(BaseModel): id: str = Field(default_factory=lambda: str(uuid4())) name: str - type: str # 'object', 'face', or 'manual' + type: Literal['object', 'face', 'manual'] photos: List[str] = [] # list of photo ids auto_update: bool = Falsemain.py (1)
79-87: Consider immutable update pattern for clarity.Lines 85-86 mutate the album's photos list directly and then re-assign the same object back to the store. While this works (Pydantic models are mutable by default), it's redundant since
albums[album_id]already references the mutated object.For clarity and to align with the immutable pattern used in
update_album, consider:@app.post("/albums/{album_id}/refresh", response_model=SmartAlbum) def refresh_album(album_id: str): if album_id not in albums: raise HTTPException(status_code=404, detail="Album not found") album = albums[album_id] new_photo_ids = refresh_album_contents(album, photos) - album.photos = new_photo_ids - albums[album_id] = album + updated_album = album.copy(update={"photos": new_photo_ids}) + albums[album_id] = updated_album - return album + return updated_albumai_stub.py (1)
10-32: Remove unnecessarygetattrwith default.Line 30 uses
getattr(p, "faces", 0)but per models.py, thePhotomodel always includes afacesfield with a default value of0. The defensivegetattris unnecessary.if album.type == "face": - return [pid for pid, p in photos.items() if getattr(p, "faces", 0) > 0] + return [pid for pid, p in photos.items() if p.faces > 0]Otherwise, the refresh logic is well-structured with clear type-based branching and appropriate fallback behavior for manual/unknown album types.
test_albums.py (1)
1-7: Remove unused import.Line 3 imports
asynciobut it's never used in this file.import pytest import httpx -import asyncio from fastapi.testclient import TestClient
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (11)
ai_stub.py(1 hunks)backend/app/logging/setup_logging.py(1 hunks)docs/index.md(1 hunks)docs/overview/features.md(1 hunks)docs/setup.md(1 hunks)docs/stylesheets/extra.css(2 hunks)main.py(1 hunks)models.py(1 hunks)requirements.txt(1 hunks)test_albums.py(1 hunks)test_logging_fix.py(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
ai_stub.py (1)
models.py (2)
SmartAlbum(13-18)Photo(6-10)
main.py (2)
models.py (2)
SmartAlbum(13-18)Photo(6-10)ai_stub.py (1)
refresh_album_contents(10-32)
test_albums.py (1)
models.py (1)
SmartAlbum(13-18)
🪛 OSV Scanner (2.3.0)
requirements.txt
[HIGH] 1-1: fastapi 0.100.0: undefined
(PYSEC-2024-38)
[CRITICAL] 1-1: h11 0.14.0: h11 accepts some malformed Chunked-Encoding bodies
[HIGH] 1-1: starlette 0.27.0: Starlette has possible denial-of-service vector when parsing large files in multipart forms
[HIGH] 1-1: starlette 0.27.0: Starlette Denial of service (DoS) via multipart/form-data
🔇 Additional comments (13)
docs/setup.md (1)
3-3: LGTM! Cleaner formatting.The removal of bold emphasis improves readability while maintaining the same content and link functionality.
docs/overview/features.md (1)
3-32: LGTM! Improved heading hierarchy.The promotion of feature section headings from
###to##creates better visual hierarchy and clearer document structure, aligning with the PR's UI improvement objectives.test_logging_fix.py (1)
17-112: LGTM! Well-structured validation tests.Both test functions effectively validate that the ColorFormatter clears exception information before formatting. The tests use appropriate assertions and provide clear output for debugging.
docs/index.md (1)
5-46: LGTM! Enhanced documentation layout.The restructured flex-based layout creates a cleaner, more organized presentation of documentation sections with improved responsive behavior. The inline styles work in conjunction with the CSS enhancements to achieve the UI improvements outlined in the PR objectives.
docs/stylesheets/extra.css (1)
1-857: LGTM! Comprehensive UI enhancements.This CSS file delivers extensive improvements across typography, spacing, visual hierarchy, dark mode support, and responsive design. The changes are well-organized with clear section comments and directly address the UI improvement goals outlined in issue #733. Key enhancements include:
- Refined heading hierarchy and readability
- Enhanced content blocks (code, tables, blockquotes, admonitions) with proper contrast and spacing
- Improved navigation styling with active states and hover effects
- Comprehensive responsive behavior across device sizes
- Consistent dark mode theming
The styling-only changes successfully improve documentation presentation without affecting functionality.
backend/app/logging/setup_logging.py (1)
65-68: Removing exception traces may hinder debugging.Unconditionally clearing
exc_infoandstack_inforemoves all exception tracebacks from log output. While this creates cleaner logs, it eliminates critical debugging information that developers rely on when troubleshooting issues in production or development.Consider making this behavior configurable (e.g., via an environment variable or configuration flag) so that exception details can be preserved when needed for debugging while still allowing cleaner output when desired.
Example approach:
+ def __init__( + self, + fmt: str, + component_config: Dict[str, Any], + level_colors: Dict[str, str], + use_colors: bool = True, + suppress_exceptions: bool = False, + ): """ Initialize the formatter with the given format string and color settings. Args: fmt: The format string to use component_config: Configuration for the component (prefix and color) level_colors: Dictionary mapping log levels to colors use_colors: Whether to use colors in log output + suppress_exceptions: Whether to suppress exception traces """ super().__init__(fmt) self.component_config = component_config self.level_colors = level_colors self.use_colors = use_colors + self.suppress_exceptions = suppress_exceptions def format(self, record: logging.LogRecord) -> str: """Format the log record with colors and component prefix.""" - # Clear exc_info and stack_info before formatting - record.exc_info = None - record.stack_info = None + # Clear exc_info and stack_info before formatting if configured + if self.suppress_exceptions: + record.exc_info = None + record.stack_info = None⛔ Skipped due to learnings
Learnt from: Hemil36 Repo: AOSSIE-Org/PictoPy PR: 548 File: backend/app/logging/setup_logging.py:224-243 Timestamp: 2025-10-13T16:40:22.622Z Learning: In the PictoPy project's centralized logging system (backend/app/logging/setup_logging.py and sync-microservice/app/logging/setup_logging.py), stack traces should not be shown in logs. When rerouting logs through InterceptHandler, exc_info and stack_info should not be preserved.Learnt from: Hemil36 Repo: AOSSIE-Org/PictoPy PR: 548 File: backend/app/database/images.py:115-115 Timestamp: 2025-10-15T07:13:34.502Z Learning: In the PictoPy backend and sync-microservice, exception logging using `logger.error(f"...")` without stack traces (i.e., without `exc_info=True` or `logger.exception()`) is acceptable for console logging, as this is a deliberate design decision made with the maintainer for console-only logs.main.py (4)
24-31: LGTM!The in-memory stores with sample data are appropriate for this stub implementation, and the comment clearly indicates this should be replaced with a database in production.
48-57: LGTM with minor redundancy note.The endpoints are well-structured with appropriate status codes. Line 55's
body.photos or []fallback is technically redundant sinceAlbumCreate.photosalready defaults to[], but it doesn't hurt and adds explicit clarity.
60-76: LGTM!The update and delete endpoints correctly implement:
- PATCH semantics with partial updates via
exclude_unset=True- Proper 404 error handling for non-existent albums
- Status code 204 with
Nonereturn for successful deletion
90-99: LGTM!The photo endpoints are straightforward and correctly implement list and retrieve operations with proper 404 handling for non-existent photos.
test_albums.py (3)
9-19: LGTM!The fixtures provide proper test isolation with automatic cleanup. The
autouse=Trueonclear_albumsensures each test starts with a clean state.
22-102: LGTM!The CRUD test suite provides comprehensive coverage including:
- Empty state listing
- Album creation with proper status code (201) and payload validation
- List after create to verify persistence
- Update with partial fields and verification
- Delete with verification and proper status code (204)
- Error cases for non-existent resources (404)
The test coverage aligns well with the API surface defined in main.py.
125-186: LGTM!The refresh and photos test suites provide excellent coverage:
- Refresh tests validate behavior for all three album types (object, face, manual) with correct photo filtering
- Photo tests verify list and retrieve operations with proper 404 handling
- Assertions check both presence and absence of expected photo IDs
The tests effectively validate the integration between main.py endpoints and ai_stub.py logic.
- Add warning comment in ai_stub.py about false positive matches - Fix hardcoded localhost URL in root endpoint using Request object - Add validation for album type (Literal) and photo IDs in endpoints - Update dependencies to secure versions (fastapi>=0.109.1) - Fix confusing comment in test_albums.py - Improve path construction in test_logging_fix.py Fixes all CodeRabbit review issues
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: 2
🧹 Nitpick comments (6)
test_logging_fix.py (2)
10-14: Path setup assumes specific repository structure.The path manipulation assumes the test file is at the project root and that
backend/app/logging/setup_logging.pyexists at a specific location. While cleaner than before, this will still break if:
- The test is moved to a subdirectory (e.g.,
tests/)- The backend structure is reorganized
Consider either:
- Moving this test to a proper test directory with a conftest.py that handles imports
- Using relative imports if backend is a proper package
- Adding a comment documenting the expected structure
# Add the project root to the path +# NOTE: This test must be run from the project root directory project_root = Path(__file__).parent sys.path.insert(0, str(project_root))
62-68: Refactor to use a proper testing framework.The tests rely on manual print statements and string checking instead of assertions. This makes them:
- Not automatable in CI/CD pipelines
- Harder to integrate with test reporting tools
- Likely to be skipped by developers running
pytestor similarRefactor to use pytest or unittest:
+import unittest + -def test_exc_info_cleared(): - """Test that exc_info is cleared before formatting.""" - print("=" * 60) - print("Test 1: Verify exc_info is cleared before formatting") - print("=" * 60) +class TestColorFormatter(unittest.TestCase): + def test_exc_info_cleared(self): + """Test that exc_info is cleared before formatting.""" - # Create a test formatter with simple config - component_config = {"prefix": "TEST", "color": "blue"} - level_colors = {"ERROR": "red"} - formatter = ColorFormatter( - "[%(component)s] | %(levelname)s | %(message)s", - component_config, - level_colors, - use_colors=False # Disable colors for easier output inspection - ) + # Create a test formatter with simple config + component_config = {"prefix": "TEST", "color": "blue"} + level_colors = {"ERROR": "red"} + formatter = ColorFormatter( + "[%(component)s] | %(levelname)s | %(message)s", + component_config, + level_colors, + use_colors=False + ) - # Create a log record with exc_info and stack_info - try: - 1 / 0 - except ZeroDivisionError: - record = logging.LogRecord( - name="test", - level=logging.ERROR, - pathname="test.py", - lineno=1, - msg="Division failed!", - args=(), - exc_info=sys.exc_info(), - stack_info=True - ) - - print("\nBefore formatting:") - print(f" exc_info is None: {record.exc_info is None}") - print(f" stack_info is None: {record.stack_info is None}") - - # Format the record - formatted = formatter.format(record) - - print("\nAfter formatting:") - print(f" exc_info is None: {record.exc_info is None}") - print(f" stack_info is None: {record.stack_info is None}") - - print(f"\nFormatted output:\n{formatted}") - - # Check that the output only contains the message, not the traceback - if "Traceback" not in formatted and "ZeroDivisionError" not in formatted: - print("\n✓ SUCCESS: Traceback is NOT in the formatted output") - else: - print("\n✗ FAILURE: Traceback found in the formatted output") - return False - - return True + # Create a log record with exc_info and stack_info + try: + 1 / 0 + except ZeroDivisionError: + record = logging.LogRecord( + name="test", + level=logging.ERROR, + pathname="test.py", + lineno=1, + msg="Division failed!", + args=(), + exc_info=sys.exc_info(), + stack_info=True + ) + + # Format the record + formatted = formatter.format(record) + + # Check that the output only contains the message, not the traceback + self.assertNotIn("Traceback", formatted) + self.assertNotIn("ZeroDivisionError", formatted) + self.assertIsNone(record.exc_info) + self.assertIsNone(record.stack_info) -if __name__ == "__main__": - print("\nTesting the ColorFormatter fix...\n") - - test1_passed = test_exc_info_cleared() - test2_passed = test_with_logger() - - print("\n" + "=" * 60) - if test1_passed and test2_passed: - print("✓ All tests passed!") - else: - print("✗ Some tests failed") - print("=" * 60 + "\n") +if __name__ == "__main__": + unittest.main()Also applies to: 115-126
main.py (4)
34-46: Literal types added, but consider Pydantic validators for photo IDs.Good addition of
Literaltypes for thetypefield, which provides compile-time type safety and automatic validation. However, photo ID validation is currently done in endpoint handlers (lines 56-58, 72-74) rather than in Pydantic validators.While the current approach works, consider moving photo ID validation to Pydantic validators for better reusability and separation of concerns:
+from pydantic import validator + class AlbumCreate(BaseModel): name: str type: Literal["object", "face", "manual"] photos: Optional[List[str]] = [] auto_update: Optional[bool] = False + + @validator('photos') + def validate_photo_ids(cls, v): + invalid = [pid for pid in (v or []) if pid not in photos] + if invalid: + raise ValueError(f"Invalid photo IDs: {invalid}") + return vNote: This would require the
photosdict to be accessible during validation, which may require restructuring.
53-62: Add validation for album name.The endpoint validates photo IDs but doesn't validate the album name field. Consider adding:
- Non-empty name validation
- Optional duplicate name checking (if business logic requires unique names)
Apply this diff to add basic name validation:
@app.post("/albums", response_model=SmartAlbum, status_code=201) def create_album(body: AlbumCreate): + # Validate name is not empty + if not body.name or not body.name.strip(): + raise HTTPException(status_code=400, detail="Album name cannot be empty") + # Validate photo IDs exist invalid_photos = [pid for pid in (body.photos or []) if pid not in photos] if invalid_photos: raise HTTPException(status_code=400, detail=f"Invalid photo IDs: {invalid_photos}")Or add it to the Pydantic model:
+from pydantic import Field + class AlbumCreate(BaseModel): - name: str + name: str = Field(..., min_length=1) type: Literal["object", "face", "manual"] photos: Optional[List[str]] = [] auto_update: Optional[bool] = False
83-88: Remove redundantreturn None.Line 88's explicit
return Noneis unnecessary for a 204 No Content response. FastAPI will automatically handle the empty response body.Apply this diff:
@app.delete("/albums/{album_id}", status_code=204) def delete_album(album_id: str): if album_id not in albums: raise HTTPException(status_code=404, detail="Album not found") del albums[album_id] - return None
24-31: Global mutable state will cause test isolation issues.The in-memory stores are global mutable dictionaries that will be shared across all requests and tests. This creates several problems:
- Test isolation: Tests will interfere with each other, requiring manual cleanup
- Concurrency: Multiple requests can cause race conditions
- State persistence: Server restart is required to reset state
For improved testing and development:
Short term: Add a reset endpoint for testing:
@app.post("/reset", tags=["Testing"]) def reset_stores(): albums.clear() photos.clear() photos.update({ "p1": Photo(id="p1", url="http://example.com/cat.jpg", tags=["cat", "pet"], faces=0), # ... rest of sample data }) return {"message": "Stores reset"}Medium term: Use dependency injection with
Depends()to make stores testable:from fastapi import Depends def get_albums_store() -> Dict[str, SmartAlbum]: return albums @app.get("/albums") def list_albums(albums_store: Dict = Depends(get_albums_store)): return list(albums_store.values())Long term: Replace with proper database (as the comment indicates).
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
ai_stub.py(1 hunks)main.py(1 hunks)requirements.txt(1 hunks)test_albums.py(1 hunks)test_logging_fix.py(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- requirements.txt
- test_albums.py
- ai_stub.py
🧰 Additional context used
🧬 Code graph analysis (1)
main.py (2)
models.py (2)
SmartAlbum(13-18)Photo(6-10)ai_stub.py (1)
refresh_album_contents(15-37)
🔇 Additional comments (2)
main.py (2)
10-21: Good fix for dynamic docs URL.The root endpoint now builds the docs URL dynamically using the request object, addressing the previous portability concern. This will work correctly across different deployment environments.
1-7: Clean imports and app initialization.The imports are well-organized and the FastAPI app initialization is straightforward. Good use of
Literalfrom typing for type constraints.
- Use consistent .copy(update={...}) pattern in refresh_album endpoint
- Fix stack_info parameter to use actual stack trace string instead of boolean
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: 4
🧹 Nitpick comments (2)
test_logging_fix.py (1)
116-127: Remove the__main__runner from a test module.
Keeping a manual runner encourages “print-based testing” and duplicates what the test runner already does.main.py (1)
34-46: Tighten request model defaults (Optional[List[str]] = []/ nullable booleans).
photos: Optional[List[str]] = []andauto_update: Optional[bool] = Falsecreate an odd API contract (nullable inputs but non-nullable domain model) and complicate endpoint logic. Prefer non-optional fields withdefault_factory/ plain defaults.-from pydantic import BaseModel +from pydantic import BaseModel, Field ... class AlbumCreate(BaseModel): name: str type: Literal["object", "face", "manual"] - photos: Optional[List[str]] = [] - auto_update: Optional[bool] = False + photos: List[str] = Field(default_factory=list) + auto_update: bool = False
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
main.py(1 hunks)test_logging_fix.py(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
main.py (2)
models.py (2)
SmartAlbum(13-18)Photo(6-10)ai_stub.py (1)
refresh_album_contents(15-37)
🔇 Additional comments (2)
main.py (2)
91-100: Refresh endpoint is consistent and side-effect-contained.
Good: checks existence, delegates selection logic, and persists via a new model instance (copy(update=...)) rather than mutating in place.
53-63: No fix needed—Pydantic v1.x automatically coercesnulltoFalsefor bool fields. The codebase usespydantic>=1.10.12,<2.0.0, which silently convertsNonetoFalsewhen passed to theauto_update: boolfield inSmartAlbum. No server error occurs, and the current code works correctly.Likely an incorrect or invalid review comment.
This PR improves the UI and readability of the PictoPy documentation website, focusing on a cleaner layout, better spacing, improved typography, and more consistent styling across pages, while keeping all content unchanged.
Updates include refined homepage and documentation cards, improved sidebar navigation, better responsive behavior, and enhanced dark mode support. Minor formatting and heading hierarchy improvements were also made for consistency.
Old vs New UI comparison:
Old version screenshot:
New version screenshot:
🧪 Testing
Closes #733.
Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Tests
Chores
✏️ Tip: You can customize this high-level summary in your review settings.