diff --git a/.coderabbit.yaml b/.coderabbit.yaml index 400e4279d..82b372832 100644 --- a/.coderabbit.yaml +++ b/.coderabbit.yaml @@ -8,138 +8,138 @@ language: en early_access: true chat: - # CodeRabbit will automatically respond to @coderabbitai mentions in PR comments - auto_reply: true + # CodeRabbit will automatically respond to @coderabbitai mentions in PR comments + auto_reply: true issue_enrichment: - labeling: - auto_apply_labels: true - labeling_instructions: - - label: bug - instructions: Issues reporting bugs, errors, crashes, incorrect behavior, or unexpected results. - - label: enhancement - instructions: Feature requests, improvements to existing functionality, performance optimizations, refactoring suggestions, UI/UX enhancements. - - label: documentation - instructions: Documentation updates, additions, corrections, or clarifications needed. - planning: - enabled: true - auto_planning: - enabled: true - labels: - - 'plan-me' - - 'feature' - - '!no-plan' + labeling: + auto_apply_labels: true + labeling_instructions: + - label: bug + instructions: Issues reporting bugs, errors, crashes, incorrect behavior, or unexpected results. + - label: enhancement + instructions: Feature requests, improvements to existing functionality, performance optimizations, refactoring suggestions, UI/UX enhancements. + - label: documentation + instructions: Documentation updates, additions, corrections, or clarifications needed. + planning: + enabled: true + auto_planning: + enabled: true + labels: + - "plan-me" + - "feature" + - "!no-plan" reviews: - profile: chill # Options: chill, assertive - - auto_review: - enabled: true - ignore_title_keywords: - - 'WIP' - drafts: false - base_branches: - - develop - - high_level_summary: true - sequence_diagrams: true - poem: true - review_status: true - collapse_walkthrough: false - changed_files_summary: true - request_changes_workflow: true - - pre_merge_checks: - description: - mode: warning - docstrings: - mode: off - - path_filters: - - '!**/node_modules/**' - - '!**/dist/**' - - '!**/build/**' - - '!**/__pycache__/**' - - '!**/.venv/**' - - tools: - ruff: - enabled: true - markdownlint: - enabled: true - github-checks: - enabled: true - timeout_ms: 90000 - eslint: - enabled: true - biome: - enabled: true - yamllint: - enabled: true - gitleaks: - enabled: true - clippy: - enabled: true - - labeling_instructions: - - label: Python - instructions: Apply when the PR/MR contains changes to python source-code - - label: TypeScript/JavaScript - instructions: Apply when the PR/MR contains changes to javascript or typescript source-code - - label: Rust - instructions: Apply when the PR/MR contains changes to Rust source-code (Tauri backend) - - label: Documentation - instructions: Apply whenever project documentation is updated by the PR/MR - - label: Linter - instructions: Apply when the purpose of the PR/MR is related to fixing the feedback from a linter - - path_instructions: - # Catch-all general instructions that apply to all files - - path: '**/*' - instructions: | - - Verify that documentation and comments are free of spelling mistakes - - Ensure that test code is automated, comprehensive, and follows testing best practices - - Verify that all critical functionality is covered by tests - - Confirm that the code meets the project's requirements and objectives - - Point out redundant obvious comments that do not add clarity to the code - - Look for code duplication - - Suggest code completions when seeing a TODO or FIXME comment - - # TypeScript/JavaScript/React files (Vite Frontend) - - path: '**/*.{ts,tsx,js,jsx}' - instructions: | - TypeScript & React: - - Avoid 'any', use explicit types. - - The code adheres to best practices associated with React and Vite. - - Ensure responsive design principles are followed. - - Check for proper React hook dependencies and prevent unnecessary re-renders. - - # Rust files (Tauri Backend) - - path: '**/src-tauri/**/*.rs' - instructions: | - Rust (Tauri): - - Ensure idiomatic Rust code is written. - - Watch out for unnecessary clone() calls or inefficient memory usage. - - Ensure errors are handled properly using Result rather than unwrap() or expect(). - - Ensure Tauri commands are safely exposed to the frontend. - - # Python files (Backend Microservices) - - path: '**/*.py' - instructions: | - Python: - - Check for PEP 8 violations and Python best practices. - - Check for performance considerations in asynchronous code. - - Ensure proper use of type hints. - - # CSS/SCSS files - - path: '**/*.{css,scss}' - instructions: | - CSS & Styling: - - The code adheres to best practices associated with CSS. - - Look for responsive design issues. - - # Asset files - - path: '**/assets/**/*' - instructions: | - Assets: - - Check image optimization and proper formats (WebP, SVG). + profile: chill # Options: chill, assertive + + auto_review: + enabled: true + ignore_title_keywords: + - "WIP" + drafts: false + base_branches: + - develop + + high_level_summary: true + sequence_diagrams: true + poem: true + review_status: true + collapse_walkthrough: false + changed_files_summary: true + request_changes_workflow: true + + pre_merge_checks: + description: + mode: warning + docstrings: + mode: off + + path_filters: + - "!**/node_modules/**" + - "!**/dist/**" + - "!**/build/**" + - "!**/__pycache__/**" + - "!**/.venv/**" + + tools: + ruff: + enabled: true + markdownlint: + enabled: true + github-checks: + enabled: true + timeout_ms: 90000 + eslint: + enabled: true + biome: + enabled: true + yamllint: + enabled: true + gitleaks: + enabled: true + clippy: + enabled: true + + labeling_instructions: + - label: Python + instructions: Apply when the PR/MR contains changes to python source-code + - label: TypeScript/JavaScript + instructions: Apply when the PR/MR contains changes to javascript or typescript source-code + - label: Rust + instructions: Apply when the PR/MR contains changes to Rust source-code (Tauri backend) + - label: Documentation + instructions: Apply whenever project documentation is updated by the PR/MR + - label: Linter + instructions: Apply when the purpose of the PR/MR is related to fixing the feedback from a linter + + path_instructions: + # Catch-all general instructions that apply to all files + - path: "**/*" + instructions: | + - Verify that documentation and comments are free of spelling mistakes + - Ensure that test code is automated, comprehensive, and follows testing best practices + - Verify that all critical functionality is covered by tests + - Confirm that the code meets the project's requirements and objectives + - Point out redundant obvious comments that do not add clarity to the code + - Look for code duplication + - Suggest code completions when seeing a TODO or FIXME comment + + # TypeScript/JavaScript/React files (Vite Frontend) + - path: "**/*.{ts,tsx,js,jsx}" + instructions: | + TypeScript & React: + - Avoid 'any', use explicit types. + - The code adheres to best practices associated with React and Vite. + - Ensure responsive design principles are followed. + - Check for proper React hook dependencies and prevent unnecessary re-renders. + + # Rust files (Tauri Backend) + - path: "**/src-tauri/**/*.rs" + instructions: | + Rust (Tauri): + - Ensure idiomatic Rust code is written. + - Watch out for unnecessary clone() calls or inefficient memory usage. + - Ensure errors are handled properly using Result rather than unwrap() or expect(). + - Ensure Tauri commands are safely exposed to the frontend. + + # Python files (Backend Microservices) + - path: "**/*.py" + instructions: | + Python: + - Check for PEP 8 violations and Python best practices. + - Check for performance considerations in asynchronous code. + - Ensure proper use of type hints. + + # CSS/SCSS files + - path: "**/*.{css,scss}" + instructions: | + CSS & Styling: + - The code adheres to best practices associated with CSS. + - Look for responsive design issues. + + # Asset files + - path: "**/assets/**/*" + instructions: | + Assets: + - Check image optimization and proper formats (WebP, SVG). diff --git a/.github/.markdownlint-cli2.jsonc b/.github/.markdownlint-cli2.jsonc index 287b3953e..fb76c230b 100644 --- a/.github/.markdownlint-cli2.jsonc +++ b/.github/.markdownlint-cli2.jsonc @@ -2,8 +2,8 @@ "config": { "MD013": false, "MD024": { - "siblings_only": true - } + "siblings_only": true, + }, }, "globs": [ "**/*.md", @@ -11,6 +11,6 @@ "!**/target/**", "!**/venv/**", "!**/.venv/**", - "!LICENSE.md" - ] + "!LICENSE.md", + ], } diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml index 94d4c8b27..71fb36c4e 100644 --- a/.github/ISSUE_TEMPLATE/feature.yml +++ b/.github/ISSUE_TEMPLATE/feature.yml @@ -22,4 +22,3 @@ body: - label: "I agree to follow this project's Code of Conduct" required: true - label: "I want to work on this issue" - diff --git a/.github/workflows/aur-publish.yml b/.github/workflows/aur-publish.yml index 25e95d2a9..df6b9e824 100644 --- a/.github/workflows/aur-publish.yml +++ b/.github/workflows/aur-publish.yml @@ -4,7 +4,8 @@ on: # schedule: # - cron: '0 0 * * *' workflow_dispatch: -permissions: + +permissions: contents: write jobs: @@ -13,7 +14,6 @@ jobs: outputs: need_update: ${{ steps.check_version.outputs.need_update }} latest_version: ${{ steps.check_version.outputs.latest_version }} - steps: - name: Checkout repository uses: actions/checkout@v4 @@ -27,42 +27,40 @@ jobs: id: check_version run: | cd publishing - LATEST_VERSION=$(curl -fsSL https://api.github.com/repos/AOSSIE-Org/PictoPy/releases/latest \ - | grep -oP '(?<="tag_name": "v)[^"]+') + | grep -oP '(?<="tag_name": ")[^"]+') echo "Latest version: $LATEST_VERSION" - CURRENT_VERSION=$(grep -oP '(?<=pkgver=)[0-9.]+' PKGBUILD) echo "Current version: $CURRENT_VERSION" - if [ "$LATEST_VERSION" != "$CURRENT_VERSION" ]; then echo "need_update=true" >> "$GITHUB_OUTPUT" else echo "need_update=false" >> "$GITHUB_OUTPUT" fi - echo "latest_version=$LATEST_VERSION" >> "$GITHUB_OUTPUT" - name: Compute sha256 of new deb if: steps.check_version.outputs.need_update == 'true' id: sha run: | - VERSION=${{ steps.check_version.outputs.latest_version }} - DEB_URL="https://github.com/AOSSIE-Org/PictoPy/releases/download/v${VERSION}/PictoPy_${VERSION}_amd64.deb" + VERSION="${{ steps.check_version.outputs.latest_version }}" + DEB_URL="https://github.com/AOSSIE-Org/PictoPy/releases/download/${VERSION}/PictoPy_${VERSION}_amd64.deb" SHA=$(curl -fsSL "$DEB_URL" | sha256sum | cut -d' ' -f1) + if ! echo "$SHA" | grep -qP '^[a-f0-9]{64}$'; then + echo "ERROR: Invalid SHA256 — deb download may have failed" + exit 1 + fi echo "sha=$SHA" >> "$GITHUB_OUTPUT" - name: Update PKGBUILD if: steps.check_version.outputs.need_update == 'true' run: | - VERSION=${{ steps.check_version.outputs.latest_version }} - SHA=${{ steps.sha.outputs.sha }} - + VERSION="${{ steps.check_version.outputs.latest_version }}" + SHA="${{ steps.sha.outputs.sha }}" cd publishing sed -i "s/^pkgver=.*/pkgver=$VERSION/" PKGBUILD sed -i "s/^pkgrel=.*/pkgrel=1/" PKGBUILD sed -i "s/sha256sums=.*/sha256sums=('$SHA')/" PKGBUILD - git add PKGBUILD git commit -m "upgpkg: pictopy-bin $VERSION" git push @@ -80,22 +78,22 @@ jobs: runs-on: ubuntu-latest needs: update_version if: needs.update_version.outputs.need_update == 'true' - steps: - name: Download artifacts uses: actions/download-artifact@v4 with: name: PKGBUILD + path: publishing/ - name: Publish to AUR uses: KSXGitHub/github-actions-deploy-aur@v4.1.3 with: pkgname: pictopy-bin - pkgbuild: ./PKGBUILD + pkgbuild: ./publishing/PKGBUILD commit_username: ${{ secrets.AUR_USERNAME }} commit_email: ${{ secrets.AUR_EMAIL }} ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }} - commit_message: v${{ needs.update_version.outputs.latest_version }} + commit_message: ${{ needs.update_version.outputs.latest_version }} ssh_keyscan_types: rsa,ecdsa,ed25519 updpkgsums: false allow_empty_commits: false diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index 88da332f2..7bdae5362 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -357,9 +357,9 @@ jobs: - uses: tauri-apps/tauri-action@v0 env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY}} - TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY}} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} with: projectPath: ./frontend tagName: ${{ github.event.release.tag_name || github.event.inputs.tag }} diff --git a/.github/workflows/duplicate_issue_detector.yaml b/.github/workflows/duplicate_issue_detector.yaml index 54bd6b224..0a796a9ce 100644 --- a/.github/workflows/duplicate_issue_detector.yaml +++ b/.github/workflows/duplicate_issue_detector.yaml @@ -15,7 +15,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v5 with: - python-version: '3.11' + python-version: "3.11" - name: Install dependencies run: | diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index f350d0772..1b4164d12 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -17,23 +17,23 @@ diverse, inclusive, and healthy community. Examples of behavior that contributes to a positive environment for our community include: -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience -* Focusing on what is best not just for us as individuals, but for the +- Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: -* The use of sexualized language or imagery, and sexual attention or +- The use of sexualized language or imagery, and sexual attention or advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a +- Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities @@ -106,7 +106,7 @@ Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an +standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within diff --git a/docs/Manual_Setup_Guide.md b/docs/Manual_Setup_Guide.md index 029cbaf0c..e959972b0 100644 --- a/docs/Manual_Setup_Guide.md +++ b/docs/Manual_Setup_Guide.md @@ -31,17 +31,17 @@ git remote add upstream https://github.com/AOSSIE-Org/PictoPy Before setting up the Python backend and sync-microservice, you need to have **Miniconda** installed and set up on your system. 1. **Download and Install Miniconda:** - - Visit the [Miniconda installation guide](https://www.anaconda.com/docs/getting-started/miniconda/install#quickstart-install-instructions). - - Follow the quickstart install instructions for your operating system. - - Make sure `conda` is available in your terminal after installation. + - Visit the [Miniconda installation guide](https://www.anaconda.com/docs/getting-started/miniconda/install#quickstart-install-instructions). + - Follow the quickstart install instructions for your operating system. + - Make sure `conda` is available in your terminal after installation. 2. **Verify Installation:** - ```bash - conda --version - ``` + ```bash + conda --version + ``` - You should see the conda version number if installed correctly. + You should see the conda version number if installed correctly. ## Tauri Frontend Setup @@ -49,21 +49,21 @@ Before setting up the Python backend and sync-microservice, you need to have **M 2. **Navigate to the Frontend Directory:** Open your terminal and use `cd` to change directories: - ```bash - cd frontend - ``` + ```bash + cd frontend + ``` 3. **Install Dependencies:** - ```bash - npm install - ``` + ```bash + npm install + ``` 4. **Start the Tauri desktop app in development mode:** - ```bash - npm run tauri dev - ``` + ```bash + npm run tauri dev + ``` ## Python (FastAPI) Backend Setup Steps @@ -71,39 +71,39 @@ Before setting up the Python backend and sync-microservice, you need to have **M 1. **Navigate to the Backend Directory:** Open your terminal and use `cd` to change directories: - ```bash - cd backend - ``` + ```bash + cd backend + ``` 2. **Create a Conda Environment:** Create a new conda environment with Python 3.12: - ```bash - conda create -p .env python=3.12 - ``` + ```bash + conda create -p .env python=3.12 + ``` 3. **Activate the Conda Environment:** - ```bash - conda activate ./.env - ``` + ```bash + conda activate ./.env + ``` 4. **Install Dependencies:** The `requirements.txt` file lists required packages. Install them using pip: - ```bash - pip install -r requirements.txt - ``` + ```bash + pip install -r requirements.txt + ``` - > Local development keeps the CPU-only `onnxruntime` package. GPU acceleration is enabled per-platform in the release workflow, while the model recommendation step uses direct hardware detection instead of ONNX Runtime providers. + > Local development keeps the CPU-only `onnxruntime` package. GPU acceleration is enabled per-platform in the release workflow, while the model recommendation step uses direct hardware detection instead of ONNX Runtime providers. 5. **Run the backend:** To start the backend in development mode, run this command while you are in the backend folder and the conda environment is activated: - ```bash - fastapi dev --port 52123 - ``` + ```bash + fastapi dev --port 52123 + ``` - The server will start on `http://localhost:52123` by default. In test mode, the server will automatically restart if any errors are detected or if source files are modified. + The server will start on `http://localhost:52123` by default. In test mode, the server will automatically restart if any errors are detected or if source files are modified. - ![Server running screenshot](/docs/assets/screenshots/serverRunning.png) + ![Server running screenshot](/docs/assets/screenshots/serverRunning.png) ## Sync-Microservice Setup Steps @@ -111,53 +111,53 @@ Before setting up the Python backend and sync-microservice, you need to have **M 1. **Navigate to the Sync-Microservice Directory:** Open your terminal and use `cd` to change directories: - ```bash - cd sync-microservice - ``` + ```bash + cd sync-microservice + ``` 2. **Create a Conda Environment:** Create a new conda environment with Python 3.12: - ```bash - conda create -p .sync-env python=3.12 - ``` + ```bash + conda create -p .sync-env python=3.12 + ``` 3. **Activate the Conda Environment:** - ```bash - conda activate ./.sync-env - ``` + ```bash + conda activate ./.sync-env + ``` 4. **Install Dependencies:** The `requirements.txt` file lists required packages. Install them using pip: - ```bash - pip install -r requirements.txt - ``` + ```bash + pip install -r requirements.txt + ``` 5. **Run the sync-microservice:** To start the sync-microservice in development mode, run this command while you are in the sync-microservice folder and the conda environment is activated: - ```bash - fastapi dev --port 52124 - ``` + ```bash + fastapi dev --port 52124 + ``` - The server will start on `http://localhost:52124` by default. In development mode, the server will automatically restart if any errors are detected or if source files are modified. + The server will start on `http://localhost:52124` by default. In development mode, the server will automatically restart if any errors are detected or if source files are modified. ## Troubleshooting Common Issues 1. **Missing System Dependencies:** Some dependencies might need system-level libraries like `libGL.so.1`, which is often needed by OpenCV. Install the appropriate packages based on your distribution: - **Debian/Ubuntu:** + **Debian/Ubuntu:** - ```bash - sudo apt update - sudo apt install -y libglib2.0-dev libgl1-mesa-glx - ``` + ```bash + sudo apt update + sudo apt install -y libglib2.0-dev libgl1-mesa-glx + ``` - **Other Systems:** Consult your distribution's documentation for installation instructions. + **Other Systems:** Consult your distribution's documentation for installation instructions. 2. **`gobject-2.0` Not Found Error:** Resolve this error by installing `libglib2.0-dev` on Debian/Ubuntu: - ```bash - sudo apt install -y libglib2.0-dev pkg-config - ``` + ```bash + sudo apt install -y libglib2.0-dev pkg-config + ``` - For other systems, consult your distribution's documentation. + For other systems, consult your distribution's documentation. diff --git a/docs/backend/backend_python/openapi.json b/docs/backend/backend_python/openapi.json index 4ab3ffa44..473231ac7 100644 --- a/docs/backend/backend_python/openapi.json +++ b/docs/backend/backend_python/openapi.json @@ -18,9 +18,7 @@ "paths": { "/health": { "get": { - "tags": [ - "Health" - ], + "tags": ["Health"], "summary": "Root", "operationId": "root_health_get", "responses": { @@ -37,9 +35,7 @@ }, "/folders/add-folder": { "post": { - "tags": [ - "Folders" - ], + "tags": ["Folders"], "summary": "Add Folder", "operationId": "add_folder_folders_add_folder_post", "requestBody": { @@ -118,9 +114,7 @@ }, "/folders/enable-ai-tagging": { "post": { - "tags": [ - "Folders" - ], + "tags": ["Folders"], "summary": "Enable Ai Tagging", "description": "Enable AI tagging for multiple folders.", "operationId": "enable_ai_tagging_folders_enable_ai_tagging_post", @@ -180,9 +174,7 @@ }, "/folders/disable-ai-tagging": { "post": { - "tags": [ - "Folders" - ], + "tags": ["Folders"], "summary": "Disable Ai Tagging", "description": "Disable AI tagging for multiple folders.", "operationId": "disable_ai_tagging_folders_disable_ai_tagging_post", @@ -242,9 +234,7 @@ }, "/folders/delete-folders": { "delete": { - "tags": [ - "Folders" - ], + "tags": ["Folders"], "summary": "Delete Folders", "description": "Delete multiple folders by their IDs.", "operationId": "delete_folders_folders_delete_folders_delete", @@ -304,9 +294,7 @@ }, "/folders/sync-folder": { "post": { - "tags": [ - "Folders" - ], + "tags": ["Folders"], "summary": "Sync Folder", "description": "Sync a folder by comparing filesystem folders with database entries and removing extra DB entries.", "operationId": "sync_folder_folders_sync_folder_post", @@ -376,9 +364,7 @@ }, "/folders/all-folders": { "get": { - "tags": [ - "Folders" - ], + "tags": ["Folders"], "summary": "Get All Folders", "description": "Get details of all folders in the database.", "operationId": "get_all_folders_folders_all_folders_get", @@ -408,9 +394,7 @@ }, "/albums/": { "get": { - "tags": [ - "Albums" - ], + "tags": ["Albums"], "summary": "Get Albums", "operationId": "get_albums_albums__get", "parameters": [ @@ -449,9 +433,7 @@ } }, "post": { - "tags": [ - "Albums" - ], + "tags": ["Albums"], "summary": "Create Album", "operationId": "create_album_albums__post", "requestBody": { @@ -490,9 +472,7 @@ }, "/albums/{album_id}": { "get": { - "tags": [ - "Albums" - ], + "tags": ["Albums"], "summary": "Get Album", "operationId": "get_album_albums__album_id__get", "parameters": [ @@ -530,9 +510,7 @@ } }, "put": { - "tags": [ - "Albums" - ], + "tags": ["Albums"], "summary": "Update Album", "operationId": "update_album_albums__album_id__put", "parameters": [ @@ -580,9 +558,7 @@ } }, "delete": { - "tags": [ - "Albums" - ], + "tags": ["Albums"], "summary": "Delete Album", "operationId": "delete_album_albums__album_id__delete", "parameters": [ @@ -622,9 +598,7 @@ }, "/albums/{album_id}/images/get": { "post": { - "tags": [ - "Albums" - ], + "tags": ["Albums"], "summary": "Get Album Images", "operationId": "get_album_images_albums__album_id__images_get_post", "parameters": [ @@ -674,9 +648,7 @@ }, "/albums/{album_id}/images": { "post": { - "tags": [ - "Albums" - ], + "tags": ["Albums"], "summary": "Add Images To Album", "operationId": "add_images_to_album_albums__album_id__images_post", "parameters": [ @@ -724,9 +696,7 @@ } }, "delete": { - "tags": [ - "Albums" - ], + "tags": ["Albums"], "summary": "Remove Images From Album", "operationId": "remove_images_from_album_albums__album_id__images_delete", "parameters": [ @@ -776,9 +746,7 @@ }, "/albums/{album_id}/images/{image_id}": { "delete": { - "tags": [ - "Albums" - ], + "tags": ["Albums"], "summary": "Remove Image From Album", "operationId": "remove_image_from_album_albums__album_id__images__image_id__delete", "parameters": [ @@ -827,9 +795,7 @@ }, "/images/": { "get": { - "tags": [ - "Images" - ], + "tags": ["Images"], "summary": "Get All Images", "description": "Get all images from the database.", "operationId": "get_all_images_images__get", @@ -889,9 +855,7 @@ }, "/images/toggle-favourite": { "post": { - "tags": [ - "Images" - ], + "tags": ["Images"], "summary": "Toggle Favourite", "operationId": "toggle_favourite_images_toggle_favourite_post", "requestBody": { @@ -928,9 +892,7 @@ }, "/face-clusters/{cluster_id}": { "put": { - "tags": [ - "Face Clusters" - ], + "tags": ["Face Clusters"], "summary": "Rename Cluster", "description": "Rename a face cluster by its ID.", "operationId": "rename_cluster_face_clusters__cluster_id__put", @@ -1011,9 +973,7 @@ }, "/face-clusters/": { "get": { - "tags": [ - "Face Clusters" - ], + "tags": ["Face Clusters"], "summary": "Get All Clusters", "description": "Get metadata for all face clusters including face counts.", "operationId": "get_all_clusters_face_clusters__get", @@ -1043,9 +1003,7 @@ }, "/face-clusters/{cluster_id}/images": { "get": { - "tags": [ - "Face Clusters" - ], + "tags": ["Face Clusters"], "summary": "Get Cluster Images", "description": "Get all images that contain faces belonging to a specific cluster.", "operationId": "get_cluster_images_face_clusters__cluster_id__images_get", @@ -1106,9 +1064,7 @@ }, "/face-clusters/face-search": { "post": { - "tags": [ - "Face Clusters" - ], + "tags": ["Face Clusters"], "summary": "Face Tagging", "operationId": "face_tagging_face_clusters_face_search_post", "parameters": [ @@ -1183,9 +1139,7 @@ }, "/face-clusters/global-recluster": { "post": { - "tags": [ - "Face Clusters" - ], + "tags": ["Face Clusters"], "summary": "Trigger Global Reclustering", "description": "Manually trigger global face reclustering.\nThis forces full reclustering regardless of the 24-hour rule.", "operationId": "trigger_global_reclustering_face_clusters_global_recluster_post", @@ -1215,9 +1169,7 @@ }, "/user-preferences/": { "get": { - "tags": [ - "User Preferences" - ], + "tags": ["User Preferences"], "summary": "Get User Preferences", "description": "Get user preferences from metadata.", "operationId": "get_user_preferences_user_preferences__get", @@ -1245,9 +1197,7 @@ } }, "put": { - "tags": [ - "User Preferences" - ], + "tags": ["User Preferences"], "summary": "Update User Preferences", "description": "Update user preferences in metadata.", "operationId": "update_user_preferences_user_preferences__put", @@ -1307,9 +1257,7 @@ }, "/api/memories/generate": { "post": { - "tags": [ - "memories" - ], + "tags": ["memories"], "summary": "Generate Memories", "description": "SIMPLIFIED: Generate memories from ALL images.\n- GPS images \u2192 location-based memories\n- Non-GPS images \u2192 monthly date-based memories\n\nReturns simple breakdown: {location_count, date_count, total}", "operationId": "generate_memories_api_memories_generate_post", @@ -1381,9 +1329,7 @@ }, "/api/memories/timeline": { "get": { - "tags": [ - "memories" - ], + "tags": ["memories"], "summary": "Get Timeline", "description": "Get memories from the past N days as a timeline.\n\nThis endpoint:\n1. Calculates date range (today - N days to today)\n2. Fetches images within that date range\n3. Clusters them into memories\n4. Returns timeline of memories\n\nArgs:\n days: Number of days to look back (default: 365 = 1 year)\n location_radius_km: Location clustering radius (default: 5km)\n date_tolerance_days: Date tolerance for temporal clustering (default: 3)\n\nReturns:\n TimelineResponse with memories ordered by date\n\nRaises:\n HTTPException: If database query fails", "operationId": "get_timeline_api_memories_timeline_get", @@ -1455,9 +1401,7 @@ }, "/api/memories/on-this-day": { "get": { - "tags": [ - "memories" - ], + "tags": ["memories"], "summary": "Get On This Day", "description": "Get photos taken on this date in previous years.\n\nThis endpoint:\n1. Gets current month and day\n2. Searches for images from this month-day in all previous years\n3. Groups by year\n4. Returns images sorted by year (most recent first)\n\nReturns:\n OnThisDayResponse with images from this date in previous years\n\nRaises:\n HTTPException: If database query fails", "operationId": "get_on_this_day_api_memories_on_this_day_get", @@ -1475,9 +1419,7 @@ }, "/api/memories/locations": { "get": { - "tags": [ - "memories" - ], + "tags": ["memories"], "summary": "Get Locations", "description": "Get all unique locations where photos were taken.\n\nThis endpoint:\n1. Fetches all images with GPS coordinates\n2. Clusters them by location\n3. Returns location clusters with photo counts\n4. Includes sample images for each location\n\nArgs:\n location_radius_km: Location clustering radius (default: 5km)\n max_sample_images: Maximum sample images per location (default: 5)\n\nReturns:\n LocationsResponse with list of location clusters\n\nRaises:\n HTTPException: If database query fails", "operationId": "get_locations_api_memories_locations_get", @@ -1535,10 +1477,7 @@ }, "/shutdown": { "post": { - "tags": [ - "Shutdown", - "Shutdown" - ], + "tags": ["Shutdown", "Shutdown"], "summary": "Shutdown", "description": "Gracefully shutdown the PictoPy backend.\n\nThis endpoint schedules backend server termination after response is sent.\nThe frontend is responsible for shutting down the sync service separately.\n\nReturns:\n ShutdownResponse with status and message", "operationId": "shutdown_shutdown_post", @@ -1558,9 +1497,7 @@ }, "/models/status": { "get": { - "tags": [ - "Models" - ], + "tags": ["Models"], "summary": "Get Model Status", "description": "Returns the installation status of all models in the registry.", "operationId": "get_model_status_models_status_get", @@ -1578,9 +1515,7 @@ }, "/models/hardware": { "get": { - "tags": [ - "Models" - ], + "tags": ["Models"], "summary": "Get Hardware Recommendation", "description": "Returns hardware specs and the recommended model tier.", "operationId": "get_hardware_recommendation_models_hardware_get", @@ -1598,9 +1533,7 @@ }, "/models/{model_key}": { "delete": { - "tags": [ - "Models" - ], + "tags": ["Models"], "summary": "Delete Model", "description": "Deletes a specific model from disk.", "operationId": "delete_model_models__model_key__delete", @@ -1639,9 +1572,7 @@ }, "/models/setup": { "post": { - "tags": [ - "Models" - ], + "tags": ["Models"], "summary": "Setup Models", "description": "Initializes setup by starting downloads for a specific tier + required models.\nReturns a single task_id to track overall progress.", "operationId": "setup_models_models_setup_post", @@ -1679,9 +1610,7 @@ }, "/models/download/{model_key}": { "post": { - "tags": [ - "Models" - ], + "tags": ["Models"], "summary": "Start Download Model", "description": "Starts download for a specific model by key. Returns a task_id.", "operationId": "start_download_model_models_download__model_key__post", @@ -1720,9 +1649,7 @@ }, "/models/download/{task_id}/progress": { "get": { - "tags": [ - "Models" - ], + "tags": ["Models"], "summary": "Download Progress", "description": "Streams SSE progress for a given download task_id.", "operationId": "download_progress_models_download__task_id__progress_get", @@ -1774,10 +1701,7 @@ } }, "type": "object", - "required": [ - "folder_id", - "folder_path" - ], + "required": ["folder_id", "folder_path"], "title": "AddFolderData" }, "AddFolderRequest": { @@ -1811,9 +1735,7 @@ } }, "type": "object", - "required": [ - "folder_path" - ], + "required": ["folder_path"], "title": "AddFolderRequest" }, "AddFolderResponse": { @@ -1856,9 +1778,7 @@ } }, "type": "object", - "required": [ - "success" - ], + "required": ["success"], "title": "AddFolderResponse" }, "Album": { @@ -1881,12 +1801,7 @@ } }, "type": "object", - "required": [ - "album_id", - "album_name", - "description", - "is_hidden" - ], + "required": ["album_id", "album_name", "description", "is_hidden"], "title": "Album" }, "ClusterMetadata": { @@ -1968,9 +1883,7 @@ } }, "type": "object", - "required": [ - "name" - ], + "required": ["name"], "title": "CreateAlbumRequest" }, "CreateAlbumResponse": { @@ -1985,10 +1898,7 @@ } }, "type": "object", - "required": [ - "success", - "album_id" - ], + "required": ["success", "album_id"], "title": "CreateAlbumResponse" }, "DeleteFoldersData": { @@ -2006,10 +1916,7 @@ } }, "type": "object", - "required": [ - "deleted_count", - "folder_ids" - ], + "required": ["deleted_count", "folder_ids"], "title": "DeleteFoldersData" }, "DeleteFoldersRequest": { @@ -2023,9 +1930,7 @@ } }, "type": "object", - "required": [ - "folder_ids" - ], + "required": ["folder_ids"], "title": "DeleteFoldersRequest" }, "DeleteFoldersResponse": { @@ -2068,9 +1973,7 @@ } }, "type": "object", - "required": [ - "success" - ], + "required": ["success"], "title": "DeleteFoldersResponse" }, "app__schemas__folders__ErrorResponse": { @@ -2156,10 +2059,7 @@ } }, "type": "object", - "required": [ - "message", - "error" - ], + "required": ["message", "error"], "title": "ErrorResponse" }, "app__schemas__user_preferences__ErrorResponse": { @@ -2178,11 +2078,7 @@ } }, "type": "object", - "required": [ - "success", - "error", - "message" - ], + "required": ["success", "error", "message"], "title": "ErrorResponse", "description": "Error response model" }, @@ -2296,10 +2192,7 @@ } }, "type": "object", - "required": [ - "success", - "image_ids" - ], + "required": ["success", "image_ids"], "title": "GetAlbumImagesResponse" }, "GetAlbumResponse": { @@ -2313,10 +2206,7 @@ } }, "type": "object", - "required": [ - "success", - "data" - ], + "required": ["success", "data"], "title": "GetAlbumResponse" }, "GetAlbumsResponse": { @@ -2334,10 +2224,7 @@ } }, "type": "object", - "required": [ - "success", - "albums" - ], + "required": ["success", "albums"], "title": "GetAlbumsResponse" }, "GetAllFoldersData": { @@ -2355,10 +2242,7 @@ } }, "type": "object", - "required": [ - "folders", - "total_count" - ], + "required": ["folders", "total_count"], "title": "GetAllFoldersData" }, "GetAllFoldersResponse": { @@ -2401,9 +2285,7 @@ } }, "type": "object", - "required": [ - "success" - ], + "required": ["success"], "title": "GetAllFoldersResponse" }, "GetAllImagesResponse": { @@ -2425,11 +2307,7 @@ } }, "type": "object", - "required": [ - "success", - "message", - "data" - ], + "required": ["success", "message", "data"], "title": "GetAllImagesResponse" }, "GetClusterImagesData": { @@ -2462,11 +2340,7 @@ } }, "type": "object", - "required": [ - "cluster_id", - "images", - "total_images" - ], + "required": ["cluster_id", "images", "total_images"], "title": "GetClusterImagesData", "description": "Data model for cluster images response." }, @@ -2510,9 +2384,7 @@ } }, "type": "object", - "required": [ - "success" - ], + "required": ["success"], "title": "GetClusterImagesResponse", "description": "Response model for getting images in a cluster." }, @@ -2527,9 +2399,7 @@ } }, "type": "object", - "required": [ - "clusters" - ], + "required": ["clusters"], "title": "GetClustersData" }, "GetClustersResponse": { @@ -2572,9 +2442,7 @@ } }, "type": "object", - "required": [ - "success" - ], + "required": ["success"], "title": "GetClustersResponse" }, "GetUserPreferencesResponse": { @@ -2592,11 +2460,7 @@ } }, "type": "object", - "required": [ - "success", - "message", - "user_preferences" - ], + "required": ["success", "message", "user_preferences"], "title": "GetUserPreferencesResponse", "description": "Response model for getting user preferences" }, @@ -2657,9 +2521,7 @@ } }, "type": "object", - "required": [ - "success" - ], + "required": ["success"], "title": "GlobalReclusterResponse" }, "HTTPValidationError": { @@ -2742,9 +2604,7 @@ } }, "type": "object", - "required": [ - "image_ids" - ], + "required": ["image_ids"], "title": "ImageIdsRequest" }, "ImageInCluster": { @@ -2817,20 +2677,13 @@ } }, "type": "object", - "required": [ - "id", - "path", - "face_id" - ], + "required": ["id", "path", "face_id"], "title": "ImageInCluster", "description": "Represents an image that contains faces from a specific cluster." }, "InputType": { "type": "string", - "enum": [ - "path", - "base64" - ], + "enum": ["path", "base64"], "title": "InputType" }, "MetadataModel": { @@ -2928,10 +2781,7 @@ } }, "type": "object", - "required": [ - "cluster_id", - "cluster_name" - ], + "required": ["cluster_id", "cluster_name"], "title": "RenameClusterData" }, "RenameClusterRequest": { @@ -2942,9 +2792,7 @@ } }, "type": "object", - "required": [ - "cluster_name" - ], + "required": ["cluster_name"], "title": "RenameClusterRequest" }, "RenameClusterResponse": { @@ -2987,9 +2835,7 @@ } }, "type": "object", - "required": [ - "success" - ], + "required": ["success"], "title": "RenameClusterResponse" }, "SetupRequest": { @@ -3000,9 +2846,7 @@ } }, "type": "object", - "required": [ - "tier" - ], + "required": ["tier"], "title": "SetupRequest" }, "ShutdownResponse": { @@ -3017,10 +2861,7 @@ } }, "type": "object", - "required": [ - "status", - "message" - ], + "required": ["status", "message"], "title": "ShutdownResponse", "description": "Response model for shutdown endpoint." }, @@ -3036,10 +2877,7 @@ } }, "type": "object", - "required": [ - "success", - "msg" - ], + "required": ["success", "msg"], "title": "SuccessResponse" }, "SyncFolderData": { @@ -3098,10 +2936,7 @@ } }, "type": "object", - "required": [ - "folder_path", - "folder_id" - ], + "required": ["folder_path", "folder_id"], "title": "SyncFolderRequest" }, "SyncFolderResponse": { @@ -3144,9 +2979,7 @@ } }, "type": "object", - "required": [ - "success" - ], + "required": ["success"], "title": "SyncFolderResponse" }, "ToggleFavouriteRequest": { @@ -3157,9 +2990,7 @@ } }, "type": "object", - "required": [ - "image_id" - ], + "required": ["image_id"], "title": "ToggleFavouriteRequest" }, "UpdateAITaggingData": { @@ -3177,10 +3008,7 @@ } }, "type": "object", - "required": [ - "updated_count", - "folder_ids" - ], + "required": ["updated_count", "folder_ids"], "title": "UpdateAITaggingData" }, "UpdateAITaggingRequest": { @@ -3194,9 +3022,7 @@ } }, "type": "object", - "required": [ - "folder_ids" - ], + "required": ["folder_ids"], "title": "UpdateAITaggingRequest" }, "UpdateAITaggingResponse": { @@ -3239,9 +3065,7 @@ } }, "type": "object", - "required": [ - "success" - ], + "required": ["success"], "title": "UpdateAITaggingResponse" }, "UpdateAlbumRequest": { @@ -3290,10 +3114,7 @@ } }, "type": "object", - "required": [ - "name", - "is_hidden" - ], + "required": ["name", "is_hidden"], "title": "UpdateAlbumRequest" }, "UpdateUserPreferencesRequest": { @@ -3302,11 +3123,7 @@ "anyOf": [ { "type": "string", - "enum": [ - "nano", - "small", - "medium" - ] + "enum": ["nano", "small", "medium"] }, { "type": "null" @@ -3345,11 +3162,7 @@ } }, "type": "object", - "required": [ - "success", - "message", - "user_preferences" - ], + "required": ["success", "message", "user_preferences"], "title": "UpdateUserPreferencesResponse", "description": "Response model for updating user preferences" }, @@ -3357,11 +3170,7 @@ "properties": { "YOLO_model_size": { "type": "string", - "enum": [ - "nano", - "small", - "medium" - ], + "enum": ["nano", "small", "medium"], "title": "Yolo Model Size", "default": "small" }, @@ -3401,13 +3210,9 @@ } }, "type": "object", - "required": [ - "loc", - "msg", - "type" - ], + "required": ["loc", "msg", "type"], "title": "ValidationError" } } } -} \ No newline at end of file +} diff --git a/docs/frontend/memories.md b/docs/frontend/memories.md index 471c24517..199a0bdc5 100644 --- a/docs/frontend/memories.md +++ b/docs/frontend/memories.md @@ -69,11 +69,11 @@ Photos grouped by month for images without GPS: ```typescript const applyFilter = (memories: Memory[]) => { - if (filter === 'location') { - return memories.filter(m => m.center_lat !== 0 || m.center_lon !== 0); + if (filter === "location") { + return memories.filter((m) => m.center_lat !== 0 || m.center_lon !== 0); } - if (filter === 'date') { - return memories.filter(m => m.center_lat === 0 && m.center_lon === 0); + if (filter === "date") { + return memories.filter((m) => m.center_lat === 0 && m.center_lon === 0); } return memories; // 'all' }; @@ -278,11 +278,9 @@ def _reverse_geocode(self, lat: float, lon: float) -> str: **Solution:** Conditional rendering of MemoryViewer backdrop: ```tsx -{!showMediaView && ( -
- {/* Grid content */} -
-)} +{ + !showMediaView &&
{/* Grid content */}
+} ``` ### Image Upload Fix @@ -306,8 +304,8 @@ image_record = { **Solution:** Format as "Trip to [City], [Year]" using reverse geocoding: ```typescript -const year = memory.date_start ? new Date(memory.date_start).getFullYear() : ''; -displayTitle = `Trip to ${displayLocation}${year ? `, ${year}` : ''}`; +const year = memory.date_start ? new Date(memory.date_start).getFullYear() : ""; +displayTitle = `Trip to ${displayLocation}${year ? `, ${year}` : ""}`; ``` ## Testing diff --git a/docs/frontend/screenshots.md b/docs/frontend/screenshots.md index be55f4f88..3a3c311a4 100644 --- a/docs/frontend/screenshots.md +++ b/docs/frontend/screenshots.md @@ -7,18 +7,18 @@ This section showcases the PictoPy application interface with sample photos feat The main gallery displays photos in a grid layout, organized by date with filtering options. ![Main Gallery](../assets/screenshots/home.png) -*Main gallery view showing a collection of photos with people, organized in a responsive grid layout* +_Main gallery view showing a collection of photos with people, organized in a responsive grid layout_ ## AI Tagging Features The application includes AI-powered features for intelligent photo organization, including automatic object detection, face recognition, and smart clustering. ![AI Tagging](../assets/screenshots/ai-tagging.png) -*AI Tagging interface showing face collections, object detection, and intelligent photo organization* +_AI Tagging interface showing face collections, object detection, and intelligent photo organization_ ## Settings Panel The settings panel allows users to configure directories, preferences, and application behavior. ![Settings](../assets/screenshots/settings.png) -*Settings panel showing directory configuration and user preferences* +_Settings panel showing directory configuration and user preferences_ diff --git a/docs/frontend/state-management.md b/docs/frontend/state-management.md index 4995a95d1..d06835e29 100644 --- a/docs/frontend/state-management.md +++ b/docs/frontend/state-management.md @@ -186,7 +186,7 @@ import { showLoader, hideLoader } from "../features/loaderSlice"; const ImageViewer = () => { const dispatch = useDispatch(); const { images, currentViewIndex } = useSelector( - (state: RootState) => state.images + (state: RootState) => state.images, ); const { loading, message } = useSelector((state: RootState) => state.loader); @@ -230,7 +230,7 @@ Example selector usage: import { getFolderById } from "@/features/folderSelectors"; const folder = useSelector((state: RootState) => - getFolderById(state, folderId) + getFolderById(state, folderId), ); ``` diff --git a/docs/index.md b/docs/index.md index 1a1f9ae3f..eae3e936e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,4 +1,5 @@ + # PictoPy PictoPy is a modern desktop app designed to transform the handling of digital photos. It facilitates efficient gallery management with a robust focus on privacy, offering smart tagging capabilities for photos based on objects, faces, or scenes. diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css index d26b9c2f0..56d84158a 100644 --- a/docs/stylesheets/extra.css +++ b/docs/stylesheets/extra.css @@ -1,6 +1,5 @@ .md-typeset .admonition-title { margin-bottom: 10px; - } .md-header__button.md-logo img { @@ -12,12 +11,12 @@ display: flex; align-items: center; - padding-right: 0; - margin-right: 0; + padding-right: 0; + margin-right: 0; } - + .md-header__title { - margin-left: 8px !important; + margin-left: 8px !important; } /* Responsive tweaks: ensure header, images, tables and code blocks behave well on small screens */ @@ -68,7 +67,8 @@ } /* General container/content spacing adjustments for smaller screens */ -.md-main, .md-content { +.md-main, +.md-content { padding-left: 1rem; padding-right: 1rem; } @@ -215,7 +215,8 @@ } /* Ensure code samples are responsive */ -.swagger-ui pre, .swagger-ui code { +.swagger-ui pre, +.swagger-ui code { white-space: pre-wrap; word-break: break-word; } @@ -232,10 +233,12 @@ } /* Redoc-specific fixes in case the component renders Redoc */ -.redoc-wrap, .redoc { +.redoc-wrap, +.redoc { max-width: 100%; } -.redoc .menu-content, .redoc .operations-wrapper { +.redoc .menu-content, +.redoc .operations-wrapper { overflow-x: auto; } @@ -368,9 +371,3 @@ display: inline-block !important; vertical-align: middle !important; } - - - - - -