feat: Enhance photo identification and tagging features with new filters and counts
This commit introduces several enhancements to the photo identification and tagging functionalities. The Identify component now supports filtering by photo IDs, allowing users to view faces from specific photos. Additionally, the Tags component has been updated to include an unidentified face count for each photo, improving user awareness of untagged faces. The API has been modified to accommodate these new parameters, ensuring seamless integration with the frontend. Documentation has been updated to reflect these changes.
This commit is contained in:
@@ -113,6 +113,7 @@ def get_unidentified_faces(
|
||||
sort_dir: str = Query("desc"),
|
||||
tag_names: str | None = Query(None, description="Comma-separated tag names for filtering"),
|
||||
match_all: bool = Query(False, description="Match all tags (for tag filtering)"),
|
||||
photo_ids: str | None = Query(None, description="Comma-separated photo IDs for filtering"),
|
||||
db: Session = Depends(get_db),
|
||||
) -> UnidentifiedFacesResponse:
|
||||
"""Get unidentified faces with filters and pagination."""
|
||||
@@ -138,6 +139,14 @@ def get_unidentified_faces(
|
||||
if tag_names:
|
||||
tag_names_list = [t.strip() for t in tag_names.split(',') if t.strip()]
|
||||
|
||||
# Parse photo IDs
|
||||
photo_ids_list = None
|
||||
if photo_ids:
|
||||
try:
|
||||
photo_ids_list = [int(pid.strip()) for pid in photo_ids.split(',') if pid.strip()]
|
||||
except ValueError:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid photo_ids format")
|
||||
|
||||
# Convert single date_processed to date_processed_from and date_processed_to (exact date match)
|
||||
date_processed_from = dp
|
||||
date_processed_to = dp
|
||||
@@ -155,6 +164,7 @@ def get_unidentified_faces(
|
||||
sort_dir=sort_dir,
|
||||
tag_names=tag_names_list,
|
||||
match_all=match_all,
|
||||
photo_ids=photo_ids_list,
|
||||
)
|
||||
|
||||
items = [
|
||||
|
||||
@@ -180,6 +180,7 @@ def get_photos_with_tags_endpoint(db: Session = Depends(get_db)) -> PhotosWithTa
|
||||
date_taken=p['date_taken'],
|
||||
date_added=p['date_added'],
|
||||
face_count=p['face_count'],
|
||||
unidentified_face_count=p['unidentified_face_count'],
|
||||
tags=p['tags'],
|
||||
)
|
||||
for p in photos_data
|
||||
|
||||
@@ -87,6 +87,7 @@ class PhotoWithTagsItem(BaseModel):
|
||||
date_taken: Optional[str] = None
|
||||
date_added: Optional[str] = None
|
||||
face_count: int
|
||||
unidentified_face_count: int # Count of faces with person_id IS NULL
|
||||
tags: str # Comma-separated tags string (matching desktop)
|
||||
|
||||
|
||||
|
||||
@@ -1206,6 +1206,7 @@ def list_unidentified_faces(
|
||||
sort_dir: str = "desc",
|
||||
tag_names: Optional[List[str]] = None,
|
||||
match_all: bool = False,
|
||||
photo_ids: Optional[List[int]] = None,
|
||||
) -> Tuple[List[Face], int]:
|
||||
"""Return paginated unidentified faces with filters.
|
||||
|
||||
@@ -1214,6 +1215,7 @@ def list_unidentified_faces(
|
||||
- Date taken (date_taken_from, date_taken_to)
|
||||
- Date processed (date_processed_from, date_processed_to) - uses photo.date_added
|
||||
- Tags (tag_names, match_all)
|
||||
- Photo IDs (photo_ids)
|
||||
|
||||
Legacy parameters (date_from, date_to) are kept for backward compatibility
|
||||
and filter by date_taken when available, else date_added as fallback.
|
||||
@@ -1252,6 +1254,10 @@ def list_unidentified_faces(
|
||||
# No matching tags found - return empty result
|
||||
return [], 0
|
||||
|
||||
# Photo ID filtering
|
||||
if photo_ids:
|
||||
query = query.filter(Face.photo_id.in_(photo_ids))
|
||||
|
||||
# Min quality (stored 0.0-1.0)
|
||||
if min_quality is not None:
|
||||
query = query.filter(Face.quality_score >= min_quality)
|
||||
|
||||
@@ -252,13 +252,21 @@ def get_photos_with_tags(db: Session) -> List[dict]:
|
||||
|
||||
result = []
|
||||
for photo in photos:
|
||||
# Get face count
|
||||
# Get face count (all faces)
|
||||
face_count = (
|
||||
db.query(func.count(Face.id))
|
||||
.filter(Face.photo_id == photo.id)
|
||||
.scalar() or 0
|
||||
)
|
||||
|
||||
# Get unidentified face count (only faces with person_id IS NULL)
|
||||
unidentified_face_count = (
|
||||
db.query(func.count(Face.id))
|
||||
.filter(Face.photo_id == photo.id)
|
||||
.filter(Face.person_id.is_(None))
|
||||
.scalar() or 0
|
||||
)
|
||||
|
||||
# Get tags as comma-separated string (matching desktop GROUP_CONCAT)
|
||||
tags_query = (
|
||||
db.query(Tag.tag_name)
|
||||
@@ -277,6 +285,7 @@ def get_photos_with_tags(db: Session) -> List[dict]:
|
||||
'date_taken': photo.date_taken.isoformat() if photo.date_taken else None,
|
||||
'date_added': photo.date_added.isoformat() if photo.date_added else None,
|
||||
'face_count': face_count,
|
||||
'unidentified_face_count': unidentified_face_count,
|
||||
'tags': tags,
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user