From 0d51b93aa7ce491d75ededbdbbfbc77fb45b1fc5 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Sun, 14 Jun 2026 23:39:30 -0400 Subject: [PATCH] refactor(importer): single _apply_post_fields predicate for both ingest paths (#842/#753) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The per-media path (_apply_sidecar) and the post-record path (upsert_post_record) each carried a VERBATIM copy of the post-field write (url/title/date/description/ attachment_count/raw_metadata + external-link sync). Two copies of one concept = the divergence risk #753 targets. Consolidate into one _apply_post_fields(post, sd) helper both call — a single predicate for how a post body/links get stored, so the two sources can't drift. Behavior identical (fill-with-non-empty); both paths already covered by existing importer tests. Groundwork for the planned post-first ingest model (single authoritative post record; media attaches to it) as more platforms move onto the native ingester. Co-Authored-By: Claude Opus 4.8 --- backend/app/services/importer.py | 49 ++++++++++++++++---------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/backend/app/services/importer.py b/backend/app/services/importer.py index 8cc3bd3..4b82466 100644 --- a/backend/app/services/importer.py +++ b/backend/app/services/importer.py @@ -739,6 +739,29 @@ class Importer: self.session.commit() return ImportResult(status="refreshed", image_id=existing.id) + def _apply_post_fields(self, post: Post, sd) -> None: + """Write a parsed sidecar's post-level fields onto a Post — the SINGLE + predicate shared by BOTH ingest paths: the per-media path (_apply_sidecar) + and the post-record path (upsert_post_record). Fill-with-non-empty: + parse_sidecar yields None for empty values, so a None field is left + untouched (an empty feed body never wipes a populated one). raw_metadata + + external-link sync always run (latest snapshot). Keeping ONE copy stops + the two paths from diverging on how a post body/links get stored — they + were verbatim duplicates (#842 DRY pass; see [[feedback_preview_apply_parity]]). + """ + if sd.post_url is not None: + post.post_url = sd.post_url + if sd.post_title is not None: + post.post_title = sd.post_title + if sd.post_date is not None: + post.post_date = sd.post_date + if sd.description is not None: + post.description = sd.description + if sd.attachment_count is not None: + post.attachment_count = sd.attachment_count + post.raw_metadata = sd.raw + self._sync_external_links(post) + def upsert_post_record( self, sidecar: Path, *, artist: Artist | None = None, source: Source | None = None, @@ -785,18 +808,7 @@ class Importer: ) if post.artist_id is None: post.artist_id = artist.id - if sd.post_url is not None: - post.post_url = sd.post_url - if sd.post_title is not None: - post.post_title = sd.post_title - if sd.post_date is not None: - post.post_date = sd.post_date - if sd.description is not None: - post.description = sd.description - if sd.attachment_count is not None: - post.attachment_count = sd.attachment_count - post.raw_metadata = sd.raw - self._sync_external_links(post) + self._apply_post_fields(post, sd) self.session.commit() return True @@ -1098,18 +1110,7 @@ class Importer: external_post_id=epid, artist_id=artist.id, ) - if sd.post_url is not None: - post.post_url = sd.post_url - if sd.post_title is not None: - post.post_title = sd.post_title - if sd.post_date is not None: - post.post_date = sd.post_date - if sd.description is not None: - post.description = sd.description - if sd.attachment_count is not None: - post.attachment_count = sd.attachment_count - post.raw_metadata = sd.raw - self._sync_external_links(post) + self._apply_post_fields(post, sd) # Race-safe (image_record_id, post_id) upsert — mirrors the # _find_or_create_source/post savepoint pattern. The plain