Provenance archive linkage, post reconciliation close-out, and the cleanup/admin DRY pass #127
@@ -573,6 +573,29 @@ def _repoint_post_links(session: Session, loser_id: int, keeper_id: int) -> None
|
||||
dup_imgs = select(ImageProvenance.image_record_id).where(
|
||||
ImageProvenance.post_id == keeper_id
|
||||
)
|
||||
# Before dropping the colliding loser rows, carry their from_attachment_id
|
||||
# (which archive the file came out of, milestone #87) onto the keeper's
|
||||
# surviving row when the keeper didn't record one. For the gallery-dl→native
|
||||
# case this very milestone targets, the keeper is the native stub (no
|
||||
# archive) and the loser is the gallery-dl row that extracted the member, so
|
||||
# a blind delete would silently lose the containing-archive linkage.
|
||||
for img_id, att_id in session.execute(
|
||||
select(ImageProvenance.image_record_id, ImageProvenance.from_attachment_id)
|
||||
.where(
|
||||
ImageProvenance.post_id == loser_id,
|
||||
ImageProvenance.image_record_id.in_(dup_imgs),
|
||||
ImageProvenance.from_attachment_id.is_not(None),
|
||||
)
|
||||
).all():
|
||||
session.execute(
|
||||
update(ImageProvenance)
|
||||
.where(
|
||||
ImageProvenance.post_id == keeper_id,
|
||||
ImageProvenance.image_record_id == img_id,
|
||||
ImageProvenance.from_attachment_id.is_(None),
|
||||
)
|
||||
.values(from_attachment_id=att_id)
|
||||
)
|
||||
session.execute(
|
||||
delete(ImageProvenance).where(
|
||||
ImageProvenance.post_id == loser_id,
|
||||
|
||||
@@ -853,3 +853,36 @@ def test_reconcile_dedups_provenance_collision(db_sync, tmp_path):
|
||||
select(ImageProvenance.post_id).where(ImageProvenance.image_record_id == img_id)
|
||||
).scalars().all()
|
||||
assert prov == [native_id]
|
||||
|
||||
|
||||
def test_reconcile_preserves_from_attachment_on_provenance_collision(db_sync, tmp_path):
|
||||
# The gallery-dl loser row recorded which archive the image came out of
|
||||
# (#87); the native keeper row didn't. Merging drops the loser provenance row
|
||||
# on the (image, post) collision — but must first carry its from_attachment_id
|
||||
# onto the keeper so the containing-archive linkage survives.
|
||||
a = _make_artist(db_sync, slug="rc4")
|
||||
img = _make_image(db_sync, artist=a, path=str(tmp_path / "z.jpg"), sha256="e" * 64)
|
||||
legacy = Post(artist_id=a.id, external_post_id="9", raw_metadata={"post_id": 77})
|
||||
native = Post(artist_id=a.id, external_post_id="77", raw_metadata={"post_id": 77})
|
||||
db_sync.add_all([legacy, native])
|
||||
db_sync.flush()
|
||||
att = PostAttachment(
|
||||
post_id=legacy.id, artist_id=a.id, sha256="f" * 64,
|
||||
path=str(tmp_path / "bundle.cbz"), original_filename="bundle.cbz",
|
||||
ext=".cbz", size_bytes=9,
|
||||
)
|
||||
db_sync.add(att)
|
||||
db_sync.flush()
|
||||
db_sync.add(ImageProvenance(
|
||||
image_record_id=img.id, post_id=legacy.id, from_attachment_id=att.id,
|
||||
))
|
||||
db_sync.add(ImageProvenance(image_record_id=img.id, post_id=native.id))
|
||||
img_id, native_id, att_id = img.id, native.id, att.id
|
||||
db_sync.commit()
|
||||
|
||||
cleanup_service.reconcile_duplicate_posts(db_sync, dry_run=False)
|
||||
rows = db_sync.execute(
|
||||
select(ImageProvenance.post_id, ImageProvenance.from_attachment_id)
|
||||
.where(ImageProvenance.image_record_id == img_id)
|
||||
).all()
|
||||
assert rows == [(native_id, att_id)]
|
||||
|
||||
Reference in New Issue
Block a user