Skip to content

fix(pubs): carry open-access metadata through the discovery review queue - #26

Merged
JamesKane merged 1 commit into
mainfrom
fix/openalex-open-access-metadata
Aug 1, 2026
Merged

fix(pubs): carry open-access metadata through the discovery review queue#26
JamesKane merged 1 commit into
mainfrom
fix/openalex-open-access-metadata

Conversation

@JamesKane

Copy link
Copy Markdown
Owner

A paper promoted from the OpenAlex discovery queue lands on /references with no Open access badge and no citation count, until the nightly by-DOI enrichment job happens to re-fetch it. In dev the un-badged rows are exactly the recently-promoted ones.

Root cause

The search response already carries open_access.oa_status and cited_by_count — the wire Work type even parsed them — but Candidate dropped both, pubs.publication_candidate had nowhere to put them, and promote_candidate's INSERT omitted them. So a promoted publication starts with open_access_status = NULL, which the list template maps to "no badge".

Now carried end to end, from both the discovery job and the public "suggest a paper" form. When promotion reuses an existing publication it gap-fills only — a row the curators already own is never overwritten.

Two related defects on the same path

Publications with an OpenAlex id but no DOI were unreachable by enrichment. The work-list is WHERE doi IS NOT NULL, but a candidate is promoted on its OpenAlex id alone, so those rows keep NULL citations/OA forever. Adds work_by_id and a second enrichment pass. Confirmed against live OpenAlex on the dev row that hit this (W7111817982: no DOI, oa_status: green).

DOIs from discovery were stored as resolver URLs. OpenAlex returns https://doi.org/10.x and it was stored verbatim, so the reference list rendered href="https://doi.org/https://doi.org/10.x" (broken link), and exists_by_doi couldn't match the bare DOI the public form normalizes — meaning a paper already in the catalog could be queued a second time. Ingest now normalizes; normalize_doi moves to du-external and is shared with the submit form.

Migration 0073

Adds cited_by_count / open_access_status to pubs.publication_candidate and backfills URL-form DOIs to bare form in both tables. publication.doi is UNIQUE, so the publication update skips any row whose bare form is already taken by another publication — a pre-existing duplicate pair survives un-normalized instead of aborting the migration. Dev and cutover both had zero such collisions; worth re-checking on prod after deploy.

Applied to dev: 30 publications and 5,934 candidates normalized, 0 URL-form rows left.

Notes

  • upsert_candidate now takes a NewCandidate struct — the positional list was at seven arguments and this would have made nine.
  • Existing prod/dev rows that are already un-enriched still need one publication-update run to backfill (requires OPENALEX_MAILTO); new promotions carry the badge at accept time.

Test

cargo test --workspace against a fresh empty database: 54 binaries, 239 tests, 0 failures. cargo clippy --workspace -- -D warnings clean. New coverage: candidate keeps OA/citations through promotion (live-DB test), DOI normalization, and the search-payload parse.

🤖 Generated with Claude Code

A paper promoted from the OpenAlex discovery queue landed on /references with
no "Open access" badge and no citation count, until the nightly by-DOI
enrichment job happened to re-fetch it.

The search response already carries `open_access.oa_status` and
`cited_by_count` — the wire type even parsed them — but `Candidate` dropped
both, `pubs.publication_candidate` had nowhere to put them, and
`promote_candidate`'s INSERT omitted them. Carry them end to end (discovery
job and the public "suggest a paper" form), and gap-fill rather than overwrite
when promotion reuses an existing publication.

Two related defects on the same path:

- Publications with an OpenAlex id but no DOI were unreachable by enrichment —
  the work-list is `WHERE doi IS NOT NULL`, and a candidate is promoted on its
  OpenAlex id alone. Adds `work_by_id` and a second pass over those rows.
- OpenAlex returns `doi` as a resolver URL, stored verbatim. That rendered the
  reference list's link as https://doi.org/https://doi.org/... and made
  `exists_by_doi` miss, so a paper already in the catalog could be queued a
  second time from the public form. Ingest now normalizes to bare form and
  mig 0073 backfills what is stored (30 publications, 5,934 candidates in dev).

`upsert_candidate` takes a `NewCandidate` struct — the positional list was at
seven arguments and this would have made nine.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@JamesKane
JamesKane merged commit 684be70 into main Aug 1, 2026
1 check passed
@JamesKane
JamesKane deleted the fix/openalex-open-access-metadata branch August 1, 2026 11:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant