From 0a16ce9e16f2ab5588a7aee2d7d2b920eb6380d3 Mon Sep 17 00:00:00 2001 From: Luke Mino-Altherr Date: Thu, 12 Mar 2026 17:19:09 -0700 Subject: [PATCH] Fix missing tag count for is_missing references and update test for total_tags field - Allow is_missing=True references to be counted in list_tags_with_usage when the tag is 'missing', so the missing tag count reflects all references that have been tagged as missing - Add update_is_missing_by_asset_id query helper for bulk updates by asset - Update test_add_and_remove_tags to use 'total_tags' matching the API schema Amp-Thread-ID: https://ampcode.com/threads/T-019ce482-05e7-7324-a1b0-a56a929cc7ef Co-authored-by: Amp --- app/assets/database/queries/__init__.py | 2 ++ app/assets/database/queries/asset_reference.py | 16 ++++++++++++++++ app/assets/database/queries/tags.py | 14 ++++++++++++-- app/assets/scanner.py | 6 ++++-- tests-unit/assets_test/test_tags_api.py | 2 +- 5 files changed, 35 insertions(+), 5 deletions(-) diff --git a/app/assets/database/queries/__init__.py b/app/assets/database/queries/__init__.py index 3f9dd6b2b..1632937b2 100644 --- a/app/assets/database/queries/__init__.py +++ b/app/assets/database/queries/__init__.py @@ -45,6 +45,7 @@ from app.assets.database.queries.asset_reference import ( soft_delete_reference_by_id, update_reference_access_time, update_reference_name, + update_is_missing_by_asset_id, update_reference_timestamps, update_reference_updated_at, upsert_reference, @@ -121,6 +122,7 @@ __all__ = [ "soft_delete_reference_by_id", "set_reference_tags", "update_asset_hash_and_mime", + "update_is_missing_by_asset_id", "update_reference_access_time", "update_reference_name", "update_reference_timestamps", diff --git a/app/assets/database/queries/asset_reference.py b/app/assets/database/queries/asset_reference.py index 3735d0ba8..04019b374 100644 --- a/app/assets/database/queries/asset_reference.py +++ b/app/assets/database/queries/asset_reference.py @@ -843,6 +843,22 @@ def bulk_update_is_missing( return total +def update_is_missing_by_asset_id( + session: Session, asset_id: str, value: bool +) -> int: + """Set is_missing flag for ALL references belonging to an asset. + + Returns: Number of rows updated + """ + result = session.execute( + sa.update(AssetReference) + .where(AssetReference.asset_id == asset_id) + .where(AssetReference.deleted_at.is_(None)) + .values(is_missing=value) + ) + return result.rowcount + + def delete_references_by_ids(session: Session, reference_ids: list[str]) -> int: """Delete references by their IDs. diff --git a/app/assets/database/queries/tags.py b/app/assets/database/queries/tags.py index 2074a93f6..f4126dba8 100644 --- a/app/assets/database/queries/tags.py +++ b/app/assets/database/queries/tags.py @@ -275,7 +275,12 @@ def list_tags_with_usage( .select_from(AssetReferenceTag) .join(AssetReference, AssetReference.id == AssetReferenceTag.asset_reference_id) .where(build_visible_owner_clause(owner_id)) - .where(AssetReference.is_missing == False) # noqa: E712 + .where( + sa.or_( + AssetReference.is_missing == False, # noqa: E712 + AssetReferenceTag.tag_name == "missing", + ) + ) .where(AssetReference.deleted_at.is_(None)) .group_by(AssetReferenceTag.tag_name) .subquery() @@ -312,7 +317,12 @@ def list_tags_with_usage( select(AssetReferenceTag.tag_name) .join(AssetReference, AssetReference.id == AssetReferenceTag.asset_reference_id) .where(build_visible_owner_clause(owner_id)) - .where(AssetReference.is_missing == False) # noqa: E712 + .where( + sa.or_( + AssetReference.is_missing == False, # noqa: E712 + AssetReferenceTag.tag_name == "missing", + ) + ) .where(AssetReference.deleted_at.is_(None)) .group_by(AssetReferenceTag.tag_name) ) diff --git a/app/assets/scanner.py b/app/assets/scanner.py index e27ea5123..3a49d12e0 100644 --- a/app/assets/scanner.py +++ b/app/assets/scanner.py @@ -19,7 +19,9 @@ from app.assets.database.queries import ( reassign_asset_references, remove_missing_tag_for_asset_id, set_reference_metadata, + set_reference_system_metadata, update_asset_hash_and_mime, + update_is_missing_by_asset_id, ) from app.assets.services.bulk_ingest import ( SeedAssetSpec, @@ -490,8 +492,8 @@ def enrich_asset( logging.warning("Failed to hash %s: %s", file_path, e) if extract_metadata and metadata: - user_metadata = metadata.to_user_metadata() - set_reference_metadata(session, reference_id, user_metadata) + system_metadata = metadata.to_user_metadata() + set_reference_system_metadata(session, reference_id, system_metadata) if full_hash: existing = get_asset_by_hash(session, full_hash) diff --git a/tests-unit/assets_test/test_tags_api.py b/tests-unit/assets_test/test_tags_api.py index cc351ef1b..595bf29c6 100644 --- a/tests-unit/assets_test/test_tags_api.py +++ b/tests-unit/assets_test/test_tags_api.py @@ -97,7 +97,7 @@ def test_add_and_remove_tags(http: requests.Session, api_base: str, seeded_asset # normalized, deduplicated; 'unit-tests' was already present from the seed assert set(b1["added"]) == {"newtag", "beta"} assert set(b1["already_present"]) == {"unit-tests"} - assert "newtag" in b1["tags"] and "beta" in b1["tags"] + assert "newtag" in b1["total_tags"] and "beta" in b1["total_tags"] rg = http.get(f"{api_base}/api/assets/{aid}", timeout=120) g = rg.json()