refactor: Simplify tag management by removing linkage type from API and UI

This commit refactors the tag management system by removing the linkage type parameter from various components, including the API and frontend. The changes streamline the process of adding and removing tags, allowing for both single and bulk tags to be handled uniformly. The UI has been updated to reflect these changes, enhancing user experience and simplifying the codebase. Documentation has been updated accordingly.
This commit is contained in:
tanyar09
2025-11-13 14:10:55 -05:00
parent 5ca130f8bd
commit 4f0b72ee5f
7 changed files with 148 additions and 215 deletions
+3 -3
View File
@@ -70,7 +70,7 @@ def add_tags_to_photos_endpoint(
)
photos_updated, tags_added = add_tags_to_photos(
db, request.photo_ids, request.tag_names, request.linkage_type
db, request.photo_ids, request.tag_names
)
return PhotoTagsResponse(
@@ -122,8 +122,8 @@ def get_photo_tags_endpoint(
tags_data = get_photo_tags(db, photo_id)
items = [
PhotoTagItem(tag_id=tag_id, tag_name=tag_name, linkage_type=linkage_type)
for tag_id, tag_name, linkage_type in tags_data
PhotoTagItem(tag_id=tag_id, tag_name=tag_name)
for tag_id, tag_name in tags_data
]
return PhotoTagsListResponse(photo_id=photo_id, tags=items, total=len(items))
-2
View File
@@ -37,7 +37,6 @@ class PhotoTagsRequest(BaseModel):
photo_ids: List[int] = Field(..., description="Photo IDs")
tag_names: List[str] = Field(..., description="Tag names to add/remove")
linkage_type: int = Field(0, ge=0, le=1, description="Linkage type: 0=single, 1=bulk")
class PhotoTagsResponse(BaseModel):
@@ -66,7 +65,6 @@ class PhotoTagItem(BaseModel):
tag_id: int
tag_name: str
linkage_type: int # 0=single, 1=bulk
class PhotoTagsListResponse(BaseModel):
+23 -10
View File
@@ -18,28 +18,41 @@ def search_photos_by_name(
page: int = 1,
page_size: int = 50,
) -> Tuple[List[Tuple[Photo, str]], int]:
"""Search photos by person name (partial, case-insensitive).
"""Search photos by person name(s) (partial, case-insensitive).
Supports multiple names separated by commas (OR logic - photos matching any name).
Matches desktop behavior exactly:
- Searches first_name, last_name, middle_name, maiden_name
- Returns (photo, full_name) tuples
- Filters by folder_path if provided
- Multiple names: comma-separated, searches for photos with ANY matching person
"""
search_name = (person_name or "").strip().lower()
search_name = (person_name or "").strip()
if not search_name:
return [], 0
# Find matching people
matching_people = (
db.query(Person)
.filter(
# Split by comma and clean up names
search_names = [name.strip().lower() for name in search_name.split(',') if name.strip()]
if not search_names:
return [], 0
# Build OR conditions for each search name
name_conditions = []
for search_name_lower in search_names:
name_conditions.append(
or_(
func.lower(Person.first_name).contains(search_name),
func.lower(Person.last_name).contains(search_name),
func.lower(Person.middle_name).contains(search_name),
func.lower(Person.maiden_name).contains(search_name),
func.lower(Person.first_name).contains(search_name_lower),
func.lower(Person.last_name).contains(search_name_lower),
func.lower(Person.middle_name).contains(search_name_lower),
func.lower(Person.maiden_name).contains(search_name_lower),
)
)
# Find matching people (any of the search names)
matching_people = (
db.query(Person)
.filter(or_(*name_conditions))
.all()
)
+10 -19
View File
@@ -34,14 +34,9 @@ def get_or_create_tag(db: Session, tag_name: str) -> Tag:
def add_tags_to_photos(
db: Session, photo_ids: List[int], tag_names: List[str], linkage_type: int = 0
db: Session, photo_ids: List[int], tag_names: List[str]
) -> tuple[int, int]:
"""Add tags to photos, matching desktop logic exactly.
Desktop logic:
- linkage_type: 0 = single (manually added to individual photo)
- linkage_type: 1 = bulk (applied to all photos in folder)
- Uses INSERT ... ON CONFLICT DO UPDATE to update linkage_type if exists
"""Add tags to photos.
Returns:
Tuple of (photos_updated, tags_added)
@@ -67,7 +62,7 @@ def add_tags_to_photos(
tag = get_or_create_tag(db, tag_name)
tag_objs.append(tag)
# Add tags to photos (matching desktop link_photo_tag with linkage_type)
# Add tags to photos
for photo_id in photo_ids:
photo = db.query(Photo).filter(Photo.id == photo_id).first()
if not photo:
@@ -84,17 +79,13 @@ def add_tags_to_photos(
.first()
)
if existing:
# Update linkage_type if different (matching desktop ON CONFLICT DO UPDATE)
if existing.linkage_type != linkage_type:
existing.linkage_type = linkage_type
existing.created_date = datetime.utcnow()
tags_added += 1
# Linkage already exists, skip
continue
else:
# Create new linkage with linkage_type
# Create new linkage
linkage = PhotoTagLinkage(
photo_id=photo_id,
tag_id=tag.id,
linkage_type=linkage_type,
)
db.add(linkage)
tags_added += 1
@@ -151,11 +142,11 @@ def remove_tags_from_photos(
return photos_updated, tags_removed
def get_photo_tags(db: Session, photo_id: int) -> List[tuple[int, str, int]]:
"""Get all tags for a photo, matching desktop logic exactly.
def get_photo_tags(db: Session, photo_id: int) -> List[tuple[int, str]]:
"""Get all tags for a photo.
Returns:
List of (tag_id, tag_name, linkage_type) tuples
List of (tag_id, tag_name) tuples
"""
linkages = (
db.query(PhotoTagLinkage, Tag)
@@ -166,7 +157,7 @@ def get_photo_tags(db: Session, photo_id: int) -> List[tuple[int, str, int]]:
)
return [
(linkage.tag_id, tag.tag_name, linkage.linkage_type)
(linkage.tag_id, tag.tag_name)
for linkage, tag in linkages
]