feat(trip-cluster): idempotent write-back + CLI apply with confirmation
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
from photoflow.immich import pipeline
|
||||
|
||||
APPLYABLE = ("approved", "non_trip", "skipped")
|
||||
|
||||
|
||||
def _apply_tag(client, store, asset_ids, action, tag, tag_id):
|
||||
todo = [a for a in asset_ids if not store.already_applied(a, action, tag)]
|
||||
if not todo:
|
||||
return [], []
|
||||
try:
|
||||
client.tag_assets(tag_id, todo)
|
||||
except Exception as e: # noqa: BLE001 — recorded, surfaced
|
||||
for a in todo:
|
||||
store.log_writeback(a, action, tag, f"error:{e}")
|
||||
return [], [(a, str(e)) for a in todo]
|
||||
for a in todo:
|
||||
store.log_writeback(a, action, tag, "ok")
|
||||
return todo, []
|
||||
|
||||
|
||||
def apply_cluster(client, store, cluster_id) -> dict:
|
||||
c = store.get_cluster(cluster_id)
|
||||
if c is None or c.status not in APPLYABLE:
|
||||
return {"cluster_id": cluster_id, "status": c.status if c else None,
|
||||
"succeeded": [], "failed": []}
|
||||
|
||||
included = [a.immich_id for a, m in store.cluster_members(cluster_id) if m.included]
|
||||
succeeded, failed = [], []
|
||||
|
||||
if c.status == "approved":
|
||||
tag = c.decided_name or c.suggested_name
|
||||
ok, fail = _apply_tag(client, store, included, "trip", tag, client.upsert_tag(tag))
|
||||
succeeded += ok
|
||||
failed += fail
|
||||
elif c.status == "non_trip":
|
||||
ok, fail = _apply_tag(client, store, included, "non-trip", pipeline.NON_TRIP,
|
||||
client.upsert_tag(pipeline.NON_TRIP))
|
||||
succeeded += ok
|
||||
failed += fail
|
||||
# 'skipped': no content/non-trip tag, only processed below.
|
||||
|
||||
failed_ids = {i for i, _ in failed}
|
||||
proc_targets = [a for a in included if a not in failed_ids]
|
||||
if proc_targets:
|
||||
_apply_tag(client, store, proc_targets, "processed", pipeline.PROCESSED,
|
||||
client.upsert_tag(pipeline.PROCESSED))
|
||||
for a in proc_targets:
|
||||
if store.already_applied(a, "processed", pipeline.PROCESSED):
|
||||
store.mark_processed(a)
|
||||
|
||||
return {"cluster_id": cluster_id, "status": c.status,
|
||||
"succeeded": succeeded, "failed": failed}
|
||||
|
||||
|
||||
def apply_all(client, store) -> list:
|
||||
return [apply_cluster(client, store, c.id)
|
||||
for c in store.all_clusters() if c.status in APPLYABLE]
|
||||
Reference in New Issue
Block a user