feat(website): add SeqSet citations section to admin dashboard#6769
feat(website): add SeqSet citations section to admin dashboard#6769theosanderson-agent wants to merge 8 commits into
Conversation
Adds an admin-only view listing all publications that cite any SeqSet in the instance, alongside the existing pipeline statistics. Backend: - New `GET /admin/get-all-seqset-citations` endpoint on the SeqSetCitationsController. It lives under `/admin/*`, so it is gated to super users by the existing security config, and remains behind the `ENABLE_SEQSETS` conditional like the rest of the controller. - `SeqSetCitationsDatabaseService.getAllSeqSetCitations()` returns each citation source together with the SeqSets it references (accession version, name and DOI), grouped per citation source and ordered by year. - New `AdminSeqSetCitation` / `CitedSeqSet` API types. Website: - `BackendClient.getAllSeqSetCitations()` calls the new endpoint. - New `SeqSetCitationsTable` component renders citations with links to the source DOI and to each cited SeqSet page. - The admin dashboard fetches and renders the section when SeqSets are enabled. Tests: - Backend endpoint tests cover auth (unauthorized / forbidden for non-super users), the empty case, and the populated case asserting the cited SeqSet metadata. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Super users can now register that a publication or other source cites a SeqSet without waiting for CrossRef to discover it, via new POST /admin/add-seqset-citation and DELETE /admin/delete-seqset-citation endpoints. These set/require origin=CURATED on seqset_citation_source, distinct from CrossRef-discovered (origin=CROSSREF) citations, which cannot be deleted through this path. The admin dashboard's SeqSet citations section gained a form to add citations and a remove button on manually curated rows. Renamed the existing read-only table/section components to AdminSeqSetCitations* for clarity. This also unblocks e2e coverage of the citations feature, which previously had no way to seed data without an external CrossRef call. Added a Playwright spec that logs in as the super user, creates a SeqSet, adds and removes a citation via the admin UI. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…tions spec
CI caught a race: createSeqSet() returns as soon as the Save click fires,
before the client redirects to /seqsets/{id}.{version}. The test then read
page.url() too early and got the list page, so accession parsing failed.
Wait for the URL to match the detail route first, like expectDetailLayout
already does in seqset.page.ts.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Add a "Fetch from DOI" button next to the source DOI field on the
manual-citation admin form. It calls CrossRef's public REST API
(api.crossref.org/works/{doi}) directly from the browser -- that
endpoint sends Access-Control-Allow-Origin: *, so no backend proxy is
needed -- and fills in title, year, and contributors from the response.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Claude finished @theosanderson's task in 6m 13s —— View job Code reviewTodo list
SummaryThis is a clean, well-scoped addition. Reusing the existing One actionable bug found, left as an inline comment:
Minor, non-blocking observations (not worth separate inline comments):
|
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 89cbdbe646
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Reads better with existing citations listed first and the add form last, and means a newly added citation appears right above the form that added it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Reject manually adding/updating a citation source whose DOI was
already discovered via CrossRef, instead of silently flipping its
origin to CURATED (which would have made it deletable through the
admin-only manual-citation path). Per review discussion, reject
rather than just preserving the existing origin.
- addCuratedCitation now returns the full set of SeqSets linked to the
citation source after the upsert, not just the ones in the current
request, so re-adding a known DOI with one new SeqSet no longer
drops previously linked SeqSets from the response.
- EndpointTestExtension's per-test DB cleanup never truncated
seqset_citation_source / seqset_to_citation_source, so citation rows
leaked across test methods within a run. This was latent before
(upsert silently overwrote origin) but surfaced real failures once
CROSSREF-origin protection was added, since some tests reused the
same literal source DOI. Fixed the truncate statement.
- AddSeqSetCitationForm: disable inputs/textareas until client
hydration completes, per AGENTS.md's flaky-Playwright-test guidance.
- Fix Year validation: an empty Year field coerced to 0 via Number('')
and incorrectly passed Number.isInteger(0), letting the form submit
with year: 0 instead of failing validation.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
table-auto let the Year and Cited SeqSets columns get squeezed by long citation titles. Switch to table-fixed with a colgroup so Citation, Year, Cited SeqSets, and the Remove column each get a stable share of the width, and wrap long content instead of crushing neighbors. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ations # Conflicts: # backend/src/main/kotlin/org/loculus/backend/service/seqsetcitations/SeqSetCitationsDatabaseService.kt # backend/src/test/kotlin/org/loculus/backend/controller/seqsetcitations/CitationEndpointsTest.kt
Summary
Adds a SeqSet citations section to the admin dashboard that lists every publication citing a SeqSet in the instance. This gives super users a single place to see all external citations across all SeqSets, complementing the existing per-SeqSet (
get-seqset-citations) and per-sequence (get-sequence-citations) views, which previously had no aggregate/admin-level counterpart.It also lets super users manually register a citation ("curated", as opposed to "crossref"-discovered) — useful when a publication hasn't been indexed by CrossRef yet, and as a way to seed citation data for e2e testing without depending on an external CrossRef call.
Backend
GET /admin/get-all-seqset-citationsonSeqSetCitationsController./admin/*means it is automatically gated to super users by the existingSecurityConfigmatcher (auth.requestMatchers("/admin/*").hasAuthority(SUPER_USER)), so no new security wiring is needed.SeqSetCitationsController, which is@ConditionalOnProperty(ENABLE_SEQSETS), so the endpoint only exists when SeqSets are enabled.SeqSetCitationsDatabaseService.getAllSeqSetCitations()joins the citation source, join, and SeqSets tables and returns each citation source together with the list of SeqSets it references (accession version, name and DOI). Results are grouped per citation source and ordered by year (descending), mirroring the existinggetSequenceCitationsquery shape.AdminSeqSetCitationandCitedSeqSet.POST /admin/add-seqset-citationandDELETE /admin/delete-seqset-citationendpoints (also under/admin/*, so also super-user gated):origin = CURATEDonseqset_citation_source(the schema already had aCROSSREF/CURATEDenum, but nothing ever wroteCURATEDbefore this). Re-submitting the samesourceDOIupdates its metadata and links any newly listed SeqSets without dropping existing links.CURATEDcitations —CROSSREF-discovered citations can't be removed through this admin path.AdminSeqSetCitationnow also returnsorigin, so the frontend knows which rows are deletable.Website
BackendClient.getAllSeqSetCitations(token)calls the new endpoint (parsed with the newadminSeqSetCitationszod schema).AdminSeqSetCitationsTablecomponent (renamed fromSeqSetCitationsTablefor clarity, since it's specifically the admin aggregate view) renders, per citation: the title (linking to the source DOI), contributors, year, and the cited SeqSets (each linking to its SeqSet page, with accession version and DOI), plus a "Remove" button onCURATEDrows.AddSeqSetCitationFormrenders a form (source DOI, title, year, contributors, cited SeqSet accession-versions) that posts to the new add endpoint.api.crossref.org/works/{doi}) directly from the browser to autopopulate title, year, and contributors. That endpoint sendsAccess-Control-Allow-Origin: *, so no backend proxy is needed for this lookup (unlike the institutional CrossRef deposit/cited-by API the backend already talks to, which needs credentials).AdminSeqSetCitationsSectionis a small client island that owns the citation list state so add/remove feel immediate, without a full page reload.admin/dashboard.astrofetches and renders the new section below the pipeline statistics. The fetch is only issued whenseqSetsAreEnabled(), and the section degrades gracefully on error.Tests
CitationEndpointsTest:origin = CURATEDand shows up in both the admin and per-SeqSet citation views,sourceDOIwith an additional SeqSet updates metadata and links both SeqSets,CROSSREF-origin citation is rejected; deleting an unknown DOI returns 404.admin-citations.dependent.spec.ts: logs in as the super user, creates a SeqSet, adds a citation through the admin UI, asserts it renders, deletes it via the UI, asserts it disappears.Verification
./gradlew ktlintFormatand the full backend test suite (./gradlew test, 72 test classes) pass, real Postgres via Testcontainers.npm run check-types,npm run format, andCI=1 npm run testpass (683 unit tests).0. Screenshots below.Admin dashboard: citations table above the add-citation form
Autofilling from a DOI via CrossRef
Typing a DOI and clicking "Fetch from DOI" populates title, year, and contributors from CrossRef's public API:
Citation appears in the table (above the form), with real CrossRef metadata and a Remove button
Citation also appears on the SeqSet's own citations dialog, and on the cited sequence's page
🤖 Generated with Claude Code
🚀 Preview: https://feat-admin-seqset-citatio.loculus.org