Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions backend/docs/db/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,7 @@ CREATE TABLE public.seqset_citation_source (
title text NOT NULL,
year integer NOT NULL,
contributors jsonb NOT NULL,
journal text,
CONSTRAINT seqset_citation_source_origin_check CHECK ((origin = ANY (ARRAY['CROSSREF'::text, 'CURATED'::text])))
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ data class CitationSource(
description = "List of contributors to the citation source.",
)
val contributors: List<CitationContributor>,
@Schema(
description = "The journal in which the citation source was published, when applicable.",
example = "Journal of Examples",
)
val journal: String? = null,
)

data class SeqSetCitationSource(val source: CitationSource, val seqSetDOIs: Set<String> = emptySet())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,13 +166,15 @@ class CrossRefService(
CitationContributor(givenName, surname)
}
}
val journal = citationElement.selectFirst("journal_title")?.text()?.takeIf { it.isNotBlank() }

SeqSetCitationSource(
source = CitationSource(
sourceDOI = sourceDOI,
title = title,
year = year,
contributors = contributors,
journal = journal,
),
seqSetDOIs = setOf(seqSetDOI),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ class SeqSetCitationsDatabaseService(
this[SeqSetCitationSourceTable.title] = it.source.title
this[SeqSetCitationSourceTable.year] = it.source.year
this[SeqSetCitationSourceTable.contributors] = it.source.contributors
this[SeqSetCitationSourceTable.journal] = it.source.journal
}
.flatMap { result ->
val citationSourceId = result[SeqSetCitationSourceTable.citationSourceId]
Expand Down Expand Up @@ -456,6 +457,7 @@ class SeqSetCitationsDatabaseService(
it[SeqSetCitationSourceTable.title],
it[SeqSetCitationSourceTable.year],
it[SeqSetCitationSourceTable.contributors],
it[SeqSetCitationSourceTable.journal],
),
)
}
Expand Down Expand Up @@ -494,6 +496,7 @@ class SeqSetCitationsDatabaseService(
title = first[SeqSetCitationSourceTable.title],
year = first[SeqSetCitationSourceTable.year],
contributors = first[SeqSetCitationSourceTable.contributors],
journal = first[SeqSetCitationSourceTable.journal],
),
seqSets = rows.map {
SeqSetCitingSequence(
Expand Down Expand Up @@ -529,6 +532,7 @@ class SeqSetCitationsDatabaseService(
title = first[SeqSetCitationSourceTable.title],
year = first[SeqSetCitationSourceTable.year],
contributors = first[SeqSetCitationSourceTable.contributors],
journal = first[SeqSetCitationSourceTable.journal],
),
seqSets = rows.map { it.toSeqSet() },
origin = first[SeqSetCitationSourceTable.origin],
Expand Down Expand Up @@ -584,6 +588,7 @@ class SeqSetCitationsDatabaseService(
this[SeqSetCitationSourceTable.title] = it.title
this[SeqSetCitationSourceTable.year] = it.year
this[SeqSetCitationSourceTable.contributors] = it.contributors
this[SeqSetCitationSourceTable.journal] = it.journal
}
.single()[SeqSetCitationSourceTable.citationSourceId]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ object SeqSetCitationSourceTable : Table("seqset_citation_source") {
val title = text("title")
val year = integer("year")
val contributors = jacksonSerializableJsonb<List<CitationContributor>>("contributors")
val journal = text("journal").nullable()
override val primaryKey = PrimaryKey(citationSourceId)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
alter table seqset_citation_source add column journal text;
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ class CrossRefServiceTest(
CitationContributor(givenName = "Jane", surname = "Doe"),
CitationContributor(givenName = "John", surname = "Smith"),
),
journal = "Journal of Citations",
),
seqSetDOIs = setOf("10.1234/seqset-1"),
),
Expand Down
20 changes: 20 additions & 0 deletions website/src/components/AdminDashboard/AddSeqSetCitationForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const parseContributor = (line: string) => {
/* eslint-disable @typescript-eslint/naming-convention */
type CrossRefWork = {
'title'?: string[];
'container-title'?: string[];
'author'?: { given?: string; family?: string }[];
'issued'?: { 'date-parts'?: number[][] };
'published'?: { 'date-parts'?: number[][] };
Expand All @@ -60,6 +61,7 @@ export const AddSeqSetCitationForm: FC<Props> = ({ clientConfig, accessToken, on
const isClient = useClientFlag();
const [sourceDOI, setSourceDOI] = useState('');
const [title, setTitle] = useState('');
const [journal, setJournal] = useState('');
const [year, setYear] = useState('');
const [contributorsInput, setContributorsInput] = useState('');
const [seqSetAccessionsInput, setSeqSetAccessionsInput] = useState('');
Expand All @@ -72,6 +74,7 @@ export const AddSeqSetCitationForm: FC<Props> = ({ clientConfig, accessToken, on
const resetForm = () => {
setSourceDOI('');
setTitle('');
setJournal('');
setYear('');
setContributorsInput('');
setSeqSetAccessionsInput('');
Expand All @@ -96,6 +99,9 @@ export const AddSeqSetCitationForm: FC<Props> = ({ clientConfig, accessToken, on
if (work.title?.[0] !== undefined) {
setTitle(work.title[0]);
}
if (work['container-title']?.[0] !== undefined) {
setJournal(work['container-title'][0]);
}
const year = extractYear(work);
if (year !== undefined) {
setYear(String(year));
Expand Down Expand Up @@ -146,6 +152,7 @@ export const AddSeqSetCitationForm: FC<Props> = ({ clientConfig, accessToken, on
title: title.trim(),
year: parsedYear,
contributors: splitLines(contributorsInput).map(parseContributor),
journal: journal.trim() || undefined,
},
seqSetAccessionVersions,
});
Expand Down Expand Up @@ -226,6 +233,19 @@ export const AddSeqSetCitationForm: FC<Props> = ({ clientConfig, accessToken, on
disabled={!isClient}
/>
</div>
<div className='mb-4'>
<label htmlFor='citation-journal' className='block mb-1 text-sm font-medium text-gray-900'>
Journal
</label>
<input
id='citation-journal'
type='text'
className={inputStyles}
value={journal}
onChange={(e) => setJournal(e.target.value)}
disabled={!isClient}
/>
</div>
<div className='mb-4'>
<label htmlFor='citation-contributors' className='block mb-1 text-sm font-medium text-gray-900'>
Contributors (one &lsquo;Given Surname&rsquo; per line)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,14 @@ import type { FC } from 'react';
import { routes } from '../../routes/routes';
import type { AdminSeqSetCitation } from '../../types/seqSetCitation';
import { getAccessionVersionString } from '../../utils/extractAccessionVersion';
import { formatCitationContributors } from '../SeqSetCitations/formatCitationContributors';
import { Button } from '../common/Button';

interface Props {
citations: AdminSeqSetCitation[];
onDelete?: (sourceDOI: string) => void;
}

const formatContributors = (contributors: AdminSeqSetCitation['source']['contributors']) =>
contributors
.map((contributor) => [contributor.givenName, contributor.surname].filter((name) => name).join(' '))
.join(', ');

export const AdminSeqSetCitationsTable: FC<Props> = ({ citations, onDelete }) => {
if (citations.length === 0) {
return <p className='mt-4'>No publications currently cite any SeqSet.</p>;
Expand Down Expand Up @@ -52,8 +48,11 @@ export const AdminSeqSetCitationsTable: FC<Props> = ({ citations, onDelete }) =>
{citation.source.title}
</a>
<div className='text-sm text-gray-700'>
{formatContributors(citation.source.contributors)}
{formatCitationContributors(citation.source.contributors)}
</div>
{citation.source.journal !== undefined && citation.source.journal !== null && (
<div className='text-sm italic text-gray-700'>{citation.source.journal}</div>
)}
<div className='text-xs text-gray-500'>{citation.source.sourceDOI}</div>
</td>
<td className='border px-2 py-1 align-top text-right'>{citation.source.year}</td>
Expand Down
12 changes: 6 additions & 6 deletions website/src/components/SeqSetCitations/CitationTable.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { type FC } from 'react';

import { formatCitationContributors } from './formatCitationContributors';
import { routes } from '../../routes/routes';
import { type SeqSetCitation, type SequenceCitation } from '../../types/seqSetCitation';

Expand All @@ -19,7 +20,7 @@ export const CitationDetails: FC<{
displayYear?: boolean;
}> = ({ citation, className = '', displayYear = false }) => {
return (
<div className={`flex flex-col gap-2 ${className}`}>
<div className={`flex flex-col gap-1 ${className}`}>
<a
className='text-primary-700'
href={`https://doi.org/${citation.source.sourceDOI}`}
Expand All @@ -29,11 +30,10 @@ export const CitationDetails: FC<{
{citation.source.title}
{displayYear && ` (${citation.source.year})`}
</a>
<div className='text-sm text-gray-700'>
{citation.source.contributors
.map((contributor) => [contributor.givenName, contributor.surname].filter((name) => name).join(' '))
.join(', ')}
</div>
<div className='text-sm text-gray-700'>{formatCitationContributors(citation.source.contributors)}</div>
{citation.source.journal !== undefined && citation.source.journal !== null && (
<div className='text-sm italic text-gray-700'>{citation.source.journal}</div>
)}
{'seqSets' in citation && citation.seqSets.length > 0 && (
<span className='text-sm text-gray-500'>
From SeqSet{citation.seqSets.length > 1 ? 's' : ''}:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { describe, expect, test } from 'vitest';

import { formatCitationContributors } from './formatCitationContributors';

describe('formatCitationContributors', () => {
test('shows all contributors when there are ten or fewer', () => {
expect(
formatCitationContributors([
{ givenName: 'Jane', surname: 'Doe' },
{ givenName: 'John', surname: 'Smith' },
{ givenName: 'Alex', surname: 'Jones' },
]),
).toBe('Jane Doe, John Smith, Alex Jones');
});

test('truncates contributor lists longer than ten with an ellipsis', () => {
expect(
formatCitationContributors([
{ givenName: 'Jane', surname: 'Doe' },
{ givenName: 'John', surname: 'Smith' },
{ givenName: 'Alex', surname: 'Jones' },
{ givenName: 'Sam', surname: 'Taylor' },
{ givenName: 'Morgan', surname: 'Lee' },
{ givenName: 'Jordan', surname: 'Patel' },
{ givenName: 'Casey', surname: 'Brown' },
{ givenName: 'Taylor', surname: 'Wilson' },
{ givenName: 'Jamie', surname: 'Davis' },
{ givenName: 'Robin', surname: 'Garcia' },
{ givenName: 'Avery', surname: 'Martinez' },
]),
).toBe(
'Jane Doe, John Smith, Alex Jones, Sam Taylor, Morgan Lee, Jordan Patel, Casey Brown, Taylor Wilson, Jamie Davis, Robin Garcia, ...',
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import type { CitationContributor } from '../../types/seqSetCitation';

const MAX_DISPLAYED_CONTRIBUTORS = 10;

const formatContributor = ({ givenName, surname }: CitationContributor) =>
[givenName, surname].filter((name) => name).join(' ');

export const formatCitationContributors = (contributors: CitationContributor[]) => {
const formattedContributors = contributors.map(formatContributor).filter((name) => name);

if (formattedContributors.length <= MAX_DISPLAYED_CONTRIBUTORS) {
return formattedContributors.join(', ');
}

return `${formattedContributors.slice(0, MAX_DISPLAYED_CONTRIBUTORS).join(', ')}, ...`;
Comment thread
theosanderson marked this conversation as resolved.
};
4 changes: 3 additions & 1 deletion website/src/types/seqSetCitation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,18 @@ export const authorProfile = z.object({
});
export type AuthorProfile = z.infer<typeof authorProfile>;

const citationContributor = z.object({
export const citationContributor = z.object({
givenName: z.string(),
surname: z.string(),
});
export type CitationContributor = z.infer<typeof citationContributor>;

export const citationSource = z.object({
sourceDOI: z.string(),
title: z.string(),
year: z.number(),
contributors: z.array(citationContributor),
journal: z.string().nullish(),
});

export const citationOrigin = z.enum(['CROSSREF', 'CURATED']);
Expand Down
Loading