feat: Add tag filtering and developer mode options in Identify and AutoMatch components
This commit introduces new features for tag filtering in the Identify and AutoMatch components. Users can now filter unidentified faces by tags, with options to match all or any specified tags. The UI has been updated to include tag selection and management, enhancing user experience. Additionally, developer mode options have been added to display tolerance and auto-accept threshold settings conditionally. The API has been updated to support these new parameters, ensuring seamless integration. Documentation has been updated to reflect these changes.
This commit is contained in:
@@ -106,6 +106,8 @@ def get_unidentified_faces(
|
||||
date_to: str | None = Query(None),
|
||||
sort_by: str = Query("quality"),
|
||||
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)"),
|
||||
db: Session = Depends(get_db),
|
||||
) -> UnidentifiedFacesResponse:
|
||||
"""Get unidentified faces with filters and pagination."""
|
||||
@@ -114,6 +116,11 @@ def get_unidentified_faces(
|
||||
df = _date.fromisoformat(date_from) if date_from else None
|
||||
dt = _date.fromisoformat(date_to) if date_to else None
|
||||
|
||||
# Parse tag names
|
||||
tag_names_list = None
|
||||
if tag_names:
|
||||
tag_names_list = [t.strip() for t in tag_names.split(',') if t.strip()]
|
||||
|
||||
faces, total = list_unidentified_faces(
|
||||
db,
|
||||
page=page,
|
||||
@@ -123,6 +130,8 @@ def get_unidentified_faces(
|
||||
date_to=dt,
|
||||
sort_by=sort_by,
|
||||
sort_dir=sort_dir,
|
||||
tag_names=tag_names_list,
|
||||
match_all=match_all,
|
||||
)
|
||||
|
||||
items = [
|
||||
|
||||
@@ -29,7 +29,7 @@ from src.core.config import (
|
||||
)
|
||||
from src.utils.exif_utils import EXIFOrientationHandler
|
||||
from src.utils.pose_detection import PoseDetector, RETINAFACE_AVAILABLE
|
||||
from src.web.db.models import Face, Photo, Person
|
||||
from src.web.db.models import Face, Photo, Person, Tag, PhotoTagLinkage
|
||||
|
||||
|
||||
def _pre_warm_deepface(
|
||||
@@ -1204,6 +1204,8 @@ def list_unidentified_faces(
|
||||
date_processed_to: Optional[date] = None,
|
||||
sort_by: str = "quality",
|
||||
sort_dir: str = "desc",
|
||||
tag_names: Optional[List[str]] = None,
|
||||
match_all: bool = False,
|
||||
) -> Tuple[List[Face], int]:
|
||||
"""Return paginated unidentified faces with filters.
|
||||
|
||||
@@ -1211,12 +1213,44 @@ def list_unidentified_faces(
|
||||
- Min quality
|
||||
- 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)
|
||||
|
||||
Legacy parameters (date_from, date_to) are kept for backward compatibility
|
||||
and filter by date_taken when available, else date_added as fallback.
|
||||
"""
|
||||
# Base query: faces with no person
|
||||
query = db.query(Face).join(Photo, Face.photo_id == Photo.id).filter(Face.person_id.is_(None))
|
||||
|
||||
# Tag filtering
|
||||
if tag_names:
|
||||
# Find tag IDs (case-insensitive)
|
||||
tag_ids = (
|
||||
db.query(Tag.id)
|
||||
.filter(func.lower(Tag.tag_name).in_([t.lower().strip() for t in tag_names]))
|
||||
.all()
|
||||
)
|
||||
tag_ids = [tid[0] for tid in tag_ids]
|
||||
|
||||
if tag_ids:
|
||||
if match_all:
|
||||
# Photos that have ALL specified tags
|
||||
# Join with PhotoTagLinkage and filter
|
||||
query = (
|
||||
query.join(PhotoTagLinkage, Photo.id == PhotoTagLinkage.photo_id)
|
||||
.filter(PhotoTagLinkage.tag_id.in_(tag_ids))
|
||||
.group_by(Face.id, Photo.id)
|
||||
.having(func.count(func.distinct(PhotoTagLinkage.tag_id)) == len(tag_ids))
|
||||
)
|
||||
else:
|
||||
# Photos that have ANY of the specified tags
|
||||
query = (
|
||||
query.join(PhotoTagLinkage, Photo.id == PhotoTagLinkage.photo_id)
|
||||
.filter(PhotoTagLinkage.tag_id.in_(tag_ids))
|
||||
.distinct()
|
||||
)
|
||||
else:
|
||||
# No matching tags found - return empty result
|
||||
return [], 0
|
||||
|
||||
# Min quality (stored 0.0-1.0)
|
||||
if min_quality is not None:
|
||||
|
||||
Reference in New Issue
Block a user