diff --git a/IDENTIFY_PANEL_FIXES.md b/IDENTIFY_PANEL_FIXES.md new file mode 100644 index 0000000..d59e527 --- /dev/null +++ b/IDENTIFY_PANEL_FIXES.md @@ -0,0 +1,166 @@ +# Identify Panel Fixes + +**Date:** October 16, 2025 +**Status:** ✅ Complete + +## Issues Fixed + +### 1. ✅ Unique Checkbox Default State +**Issue:** User requested that the "Unique faces only" checkbox be unchecked by default. + +**Status:** Already correct! The checkbox was already unchecked by default. + +**Code Location:** `src/gui/identify_panel.py`, line 76 +```python +self.components['unique_var'] = tk.BooleanVar() # Defaults to False (unchecked) +``` + +### 2. ✅ Quality Filter Not Working +**Issue:** The "Min quality" filter slider wasn't actually filtering faces when loading them from the database. + +**Root Cause:** +- The quality filter value was being captured in the GUI (slider with 0-100% range) +- However, the `_get_unidentified_faces()` method wasn't using this filter when querying the database +- Quality filtering was only happening during navigation (Back/Next buttons), not during initial load + +**Solution:** +1. Modified `_get_unidentified_faces()` to accept a `min_quality_score` parameter +2. Added SQL WHERE clause to filter by quality score: `AND f.quality_score >= ?` +3. Updated all 4 calls to `_get_unidentified_faces()` to pass the quality filter value: + - `_start_identification()` - Initial load + - `on_unique_change()` - When toggling unique faces filter + - `_load_more_faces()` - Loading additional batches + - `_apply_date_filters()` - When applying date filters + +**Code Changes:** + +**File:** `src/gui/identify_panel.py` + +**Modified Method Signature (line 519-521):** +```python +def _get_unidentified_faces(self, batch_size: int, date_from: str = None, date_to: str = None, + date_processed_from: str = None, date_processed_to: str = None, + min_quality_score: float = 0.0) -> List[Tuple]: +``` + +**Added SQL Filter (lines 537-540):** +```python +# Add quality filtering if specified +if min_quality_score > 0.0: + query += ' AND f.quality_score >= ?' + params.append(min_quality_score) +``` + +**Updated Call Sites:** + +1. **`_start_identification()` (lines 494-501):** +```python +# Get quality filter +min_quality = self.components['quality_filter_var'].get() +min_quality_score = min_quality / 100.0 + +# Get unidentified faces with quality filter +self.current_faces = self._get_unidentified_faces(batch_size, date_from, date_to, + date_processed_from, date_processed_to, + min_quality_score) +``` + +2. **`on_unique_change()` (lines 267-274):** +```python +# Get quality filter +min_quality = self.components['quality_filter_var'].get() +min_quality_score = min_quality / 100.0 + +# Reload faces with current filters +self.current_faces = self._get_unidentified_faces(batch_size, date_from, date_to, + date_processed_from, date_processed_to, + min_quality_score) +``` + +3. **`_load_more_faces()` (lines 1378-1385):** +```python +# Get quality filter +min_quality = self.components['quality_filter_var'].get() +min_quality_score = min_quality / 100.0 + +# Get more faces +more_faces = self._get_unidentified_faces(DEFAULT_BATCH_SIZE, date_from, date_to, + date_processed_from, date_processed_to, + min_quality_score) +``` + +4. **`_apply_date_filters()` (lines 1575-1581):** +```python +# Quality filter is already extracted above in min_quality +min_quality_score = min_quality / 100.0 + +# Reload faces with new filters +self.current_faces = self._get_unidentified_faces(batch_size, date_from, date_to, + date_processed_from, date_processed_to, + min_quality_score) +``` + +## Testing + +**Syntax Check:** ✅ Passed +```bash +python3 -m py_compile src/gui/identify_panel.py +``` + +**Linter Check:** ✅ No errors found + +## How Quality Filter Now Works + +1. **User adjusts slider:** Sets quality from 0% to 100% (in 5% increments) +2. **User clicks "Start Identification":** + - Gets quality value (e.g., 75%) + - Converts to 0.0-1.0 scale (e.g., 0.75) + - Passes to `_get_unidentified_faces()` + - SQL query filters: `WHERE f.quality_score >= 0.75` + - Only faces with quality ≥ 75% are loaded +3. **Quality filter persists:** + - When loading more batches + - When toggling unique faces + - When applying date filters + - When navigating (Back/Next already had quality filtering) + +## Expected Behavior + +### Quality Filter = 0% (default) +- Shows all faces regardless of quality +- SQL: No quality filter applied + +### Quality Filter = 50% +- Shows only faces with quality ≥ 50% +- SQL: `WHERE f.quality_score >= 0.5` + +### Quality Filter = 75% +- Shows only faces with quality ≥ 75% +- SQL: `WHERE f.quality_score >= 0.75` + +### Quality Filter = 100% +- Shows only perfect quality faces +- SQL: `WHERE f.quality_score >= 1.0` + +## Notes + +- The quality score is stored in the database as a float between 0.0 and 1.0 +- The GUI displays it as a percentage (0-100%) for user-friendliness +- The conversion happens at every call site: `min_quality_score = min_quality / 100.0` +- The Back/Next navigation already had quality filtering logic via `_find_next_qualifying_face()` - this continues to work as before + +## Files Modified + +- `src/gui/identify_panel.py` (1 file, ~15 lines changed) + +## Validation Checklist + +- [x] Quality filter parameter added to method signature +- [x] SQL WHERE clause added for quality filtering +- [x] All 4 call sites updated with quality filter +- [x] Syntax validation passed +- [x] No linter errors +- [x] Unique checkbox already defaults to unchecked +- [x] Code follows PEP 8 style guidelines +- [x] Changes are backward compatible (min_quality_score defaults to 0.0) + diff --git a/src/gui/identify_panel.py b/src/gui/identify_panel.py index 0232441..9c2aca5 100644 --- a/src/gui/identify_panel.py +++ b/src/gui/identify_panel.py @@ -264,9 +264,14 @@ class IdentifyPanel: except Exception: batch_size = DEFAULT_BATCH_SIZE + # Get quality filter + min_quality = self.components['quality_filter_var'].get() + min_quality_score = min_quality / 100.0 + # Reload faces with current filters self.current_faces = self._get_unidentified_faces(batch_size, date_from, date_to, - date_processed_from, date_processed_to) + date_processed_from, date_processed_to, + min_quality_score) print(f"✅ Reloaded: {len(self.current_faces)} faces") @@ -491,9 +496,14 @@ class IdentifyPanel: date_processed_from = self.components['date_processed_from_var'].get().strip() or None date_processed_to = self.components['date_processed_to_var'].get().strip() or None - # Get unidentified faces (without quality filter - we'll filter in display) + # Get quality filter + min_quality = self.components['quality_filter_var'].get() + min_quality_score = min_quality / 100.0 + + # Get unidentified faces with quality filter self.current_faces = self._get_unidentified_faces(batch_size, date_from, date_to, - date_processed_from, date_processed_to) + date_processed_from, date_processed_to, + min_quality_score) if not self.current_faces: messagebox.showinfo("No Faces", "🎉 All faces have been identified!") @@ -517,8 +527,9 @@ class IdentifyPanel: self.is_active = True def _get_unidentified_faces(self, batch_size: int, date_from: str = None, date_to: str = None, - date_processed_from: str = None, date_processed_to: str = None) -> List[Tuple]: - """Get unidentified faces from database with optional date filtering""" + date_processed_from: str = None, date_processed_to: str = None, + min_quality_score: float = 0.0) -> List[Tuple]: + """Get unidentified faces from database with optional date and quality filtering""" with self.db.get_db_connection() as conn: cursor = conn.cursor() @@ -533,6 +544,11 @@ class IdentifyPanel: ''' params = [] + # Add quality filtering if specified + if min_quality_score > 0.0: + query += ' AND f.quality_score >= ?' + params.append(min_quality_score) + # Add date taken filtering if specified if date_from: query += ' AND p.date_taken >= ?' @@ -1359,9 +1375,14 @@ class IdentifyPanel: date_processed_from = self.components['date_processed_from_var'].get().strip() or None date_processed_to = self.components['date_processed_to_var'].get().strip() or None + # Get quality filter + min_quality = self.components['quality_filter_var'].get() + min_quality_score = min_quality / 100.0 + # Get more faces more_faces = self._get_unidentified_faces(DEFAULT_BATCH_SIZE, date_from, date_to, - date_processed_from, date_processed_to) + date_processed_from, date_processed_to, + min_quality_score) if more_faces: # Add to current faces @@ -1551,9 +1572,13 @@ class IdentifyPanel: except Exception: batch_size = DEFAULT_BATCH_SIZE + # Quality filter is already extracted above in min_quality + min_quality_score = min_quality / 100.0 + # Reload faces with new filters self.current_faces = self._get_unidentified_faces(batch_size, date_from, date_to, - date_processed_from, date_processed_to) + date_processed_from, date_processed_to, + min_quality_score) if not self.current_faces: messagebox.showinfo("No Faces Found", "No unidentified faces found with the current filters.")