more progress

This commit is contained in:
Jedrzej Kosinski 2026-01-15 18:16:00 -08:00
parent f14129947c
commit e527b72b09
2 changed files with 42 additions and 1 deletions

View File

@ -1,9 +1,11 @@
import os
import sqlalchemy as sa import sqlalchemy as sa
from collections import defaultdict from collections import defaultdict
from datetime import datetime
from sqlalchemy import select, exists, func from sqlalchemy import select, exists, func
from sqlalchemy.orm import Session, contains_eager, noload from sqlalchemy.orm import Session, contains_eager, noload
from app.assets.database.models import Asset, AssetInfo, AssetCacheState, AssetInfoMeta, AssetInfoTag, Tag from app.assets.database.models import Asset, AssetInfo, AssetCacheState, AssetInfoMeta, AssetInfoTag, Tag
from app.assets.helpers import escape_like_prefix, normalize_tags from app.assets.helpers import escape_like_prefix, normalize_tags, utcnow
from typing import Sequence from typing import Sequence
@ -42,6 +44,7 @@ def apply_tag_filters(
) )
return stmt return stmt
def apply_metadata_filter( def apply_metadata_filter(
stmt: sa.sql.Select, stmt: sa.sql.Select,
metadata_filter: dict | None = None, metadata_filter: dict | None = None,
@ -105,9 +108,11 @@ def asset_exists_by_hash(session: Session, asset_hash: str) -> bool:
).first() ).first()
return row is not None return row is not None
def get_asset_info_by_id(session: Session, asset_info_id: str) -> AssetInfo | None: def get_asset_info_by_id(session: Session, asset_info_id: str) -> AssetInfo | None:
return session.get(AssetInfo, asset_info_id) return session.get(AssetInfo, asset_info_id)
def list_asset_infos_page( def list_asset_infos_page(
session: Session, session: Session,
owner_id: str = "", owner_id: str = "",
@ -177,6 +182,7 @@ def list_asset_infos_page(
return infos, tag_map, total return infos, tag_map, total
def fetch_asset_info_asset_and_tags( def fetch_asset_info_asset_and_tags(
session: Session, session: Session,
asset_info_id: str, asset_info_id: str,
@ -208,6 +214,7 @@ def fetch_asset_info_asset_and_tags(
tags.append(tag_name) tags.append(tag_name)
return first_info, first_asset, tags return first_info, first_asset, tags
def fetch_asset_info_and_asset( def fetch_asset_info_and_asset(
session: Session, session: Session,
*, *,
@ -241,6 +248,23 @@ def list_cache_states_by_asset_id(
) )
).scalars().all() ).scalars().all()
def touch_asset_info_by_id(
session: Session,
*,
asset_info_id: str,
ts: datetime | None = None,
only_if_newer: bool = True,
) -> None:
ts = ts or utcnow()
stmt = sa.update(AssetInfo).where(AssetInfo.id == asset_info_id)
if only_if_newer:
stmt = stmt.where(
sa.or_(AssetInfo.last_access_time.is_(None), AssetInfo.last_access_time < ts)
)
session.execute(stmt.values(last_access_time=ts))
def list_tags_with_usage( def list_tags_with_usage(
session: Session, session: Session,
prefix: str | None = None, prefix: str | None = None,
@ -298,3 +322,19 @@ def list_tags_with_usage(
rows_norm = [(name, ttype, int(count or 0)) for (name, ttype, count) in rows] rows_norm = [(name, ttype, int(count or 0)) for (name, ttype, count) in rows]
return rows_norm, int(total or 0) return rows_norm, int(total or 0)
def pick_best_live_path(states: Sequence[AssetCacheState]) -> str:
"""
Return the best on-disk path among cache states:
1) Prefer a path that exists with needs_verify == False (already verified).
2) Otherwise, pick the first path that exists.
3) Otherwise return empty string.
"""
alive = [s for s in states if getattr(s, "file_path", None) and os.path.isfile(s.file_path)]
if not alive:
return ""
for s in alive:
if not getattr(s, "needs_verify", False):
return s.file_path
return alive[0].file_path

View File

@ -11,6 +11,7 @@ from app.assets.database.queries import (
list_cache_states_by_asset_id, list_cache_states_by_asset_id,
list_asset_infos_page, list_asset_infos_page,
list_tags_with_usage, list_tags_with_usage,
pick_best_live_path,
) )