Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: some low hanging drive-by improvements #993

Merged
merged 1 commit into from
Feb 12, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import kotlinx.datetime.toLocalDateTime
import mu.KotlinLogging
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.SqlExpressionBuilder.plus
import org.jetbrains.exposed.sql.and
import org.jetbrains.exposed.sql.andWhere
import org.jetbrains.exposed.sql.deleteWhere
Expand Down Expand Up @@ -180,10 +179,12 @@ class DatasetCitationsDatabaseService(

var selectedVersion = version

val datasetUuid = UUID.fromString(datasetId)

if (selectedVersion == null) {
selectedVersion = DatasetsTable
.slice(DatasetsTable.datasetVersion.max())
.select { DatasetsTable.datasetId eq UUID.fromString(datasetId) }
.select { DatasetsTable.datasetId eq datasetUuid }
.singleOrNull()?.get(DatasetsTable.datasetVersion)
}
if (selectedVersion == null) {
Expand All @@ -192,7 +193,7 @@ class DatasetCitationsDatabaseService(

if (DatasetToRecordsTable
.select {
(DatasetToRecordsTable.datasetId eq UUID.fromString(datasetId)) and
(DatasetToRecordsTable.datasetId eq datasetUuid) and
(DatasetToRecordsTable.datasetVersion eq selectedVersion)
}
.empty()
Expand All @@ -203,7 +204,7 @@ class DatasetCitationsDatabaseService(
val selectedDatasetRecords = DatasetToRecordsTable
.innerJoin(DatasetRecordsTable)
.select {
(DatasetToRecordsTable.datasetId eq UUID.fromString(datasetId)) and
(DatasetToRecordsTable.datasetId eq datasetUuid) and
(DatasetToRecordsTable.datasetVersion eq selectedVersion)
}
.map {
Expand Down Expand Up @@ -262,11 +263,13 @@ class DatasetCitationsDatabaseService(
throw NotFoundException("Dataset $datasetId, version $version does not exist")
}

DatasetsTable.update({
(DatasetsTable.datasetId eq UUID.fromString(datasetId)) and
(DatasetsTable.datasetVersion eq version) and
(DatasetsTable.createdBy eq username)
}) {
DatasetsTable.update(
{
(DatasetsTable.datasetId eq UUID.fromString(datasetId)) and
(DatasetsTable.datasetVersion eq version) and
(DatasetsTable.createdBy eq username)
},
) {
it[DatasetsTable.datasetDOI] = datasetDOI
}

Expand All @@ -284,6 +287,7 @@ class DatasetCitationsDatabaseService(
val datasetVersion: Long,
val createdAt: Timestamp,
)

val selectedDatasetRecords = DatasetRecordsTable
.innerJoin(DatasetToRecordsTable)
.innerJoin(DatasetsTable)
Expand Down Expand Up @@ -326,8 +330,8 @@ class DatasetCitationsDatabaseService(
.toSet()

val citedBy = CitedBy(
mutableListOf<Long>(),
mutableListOf<Long>(),
mutableListOf(),
mutableListOf(),
)
for (dataset in uniqueLatestDatasets) {
val year = dataset.createdAt.toLocalDateTime().year.toLong()
Expand Down Expand Up @@ -365,10 +369,7 @@ class DatasetCitationsDatabaseService(
where = { AuthorsTable.username eq username },
)
.firstOrNull()

if (selectedAuthor == null) {
throw NotFoundException("Author $username does not exist")
}
?: throw NotFoundException("Author $username does not exist")

return Author(
selectedAuthor[AuthorsTable.authorId],
Expand Down
68 changes: 26 additions & 42 deletions website/src/pages/datasets/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { ErrorFeedback } from '../../components/ErrorFeedback';
import { getRuntimeConfig } from '../../config';
import BaseLayout from '../../layouts/BaseLayout.astro';
import { DatasetCitationClient } from '../../services/datasetCitationClient.ts';
import { createAuthorizationHeader } from '../../utils/createAuthorizationHeader.ts';
import { getAccessToken } from '../../utils/getAccessToken';

const clientConfig = getRuntimeConfig().public;
Expand All @@ -17,30 +16,9 @@ const session = Astro.locals.session;

const datasetClient = DatasetCitationClient.create();

const datasetsResponse = (await datasetClient.call('getDatasetsOfUser', {
headers: createAuthorizationHeader(accessToken),
})) as any;

const userCitedByResponse = (await datasetClient.call('getUserCitedBy', {
params: { username },
headers: createAuthorizationHeader(accessToken),
})) as any;

const authorResponse = (await datasetClient.call('getAuthor', {
headers: createAuthorizationHeader(accessToken),
})) as any;

const handlePrivateAuthorProfile = (authorResponse: any, userSession: any) => {
const authorData = authorResponse ?? {};
return {
name: userSession?.name,
email: userSession?.email,
emailVerified: userSession?.email_verified,
...authorData,
};
};

const authorData = handlePrivateAuthorProfile(authorResponse.value, session.user);
const datasetsResponse = await datasetClient.getDatasetsOfUser(accessToken);
const userCitedByResponse = await datasetClient.getUserCitedBy(username, accessToken);
const authorResponse = await datasetClient.getAuthor(accessToken);
fengelniederhammer marked this conversation as resolved.
Show resolved Hide resolved
---

<BaseLayout title='Datasets' data-testid='datasets-list-container'>
Expand All @@ -53,7 +31,13 @@ const authorData = handlePrivateAuthorProfile(authorResponse.value, session.user
accessToken={accessToken}
fontSize={120}
client:load
{...authorData}
name={session.user?.name}
email={session.user?.email}
emailVerified={session.user?.emailVerified}
{...authorResponse.match<{ authorId: string | undefined; affiliation?: string | undefined }>(
(ok) => ok,
() => ({ authorId: undefined, affiliation: undefined }),
)}
/>
<hr />
<div class='flex justify-start'>
Expand All @@ -64,15 +48,14 @@ const authorData = handlePrivateAuthorProfile(authorResponse.value, session.user
</div>
<div>
{
datasetsResponse.value !== undefined ? (
<DatasetList datasets={datasetsResponse.value} username={username} client:load />
) : (
<ErrorFeedback
message={
'Error while fetching datasets: ' + JSON.stringify(datasetsResponse.error)
}
client:load
/>
datasetsResponse.match(
(datasets) => <DatasetList datasets={datasets} username={username} client:load />,
(error) => (
<ErrorFeedback
message={'Error while fetching datasets: ' + JSON.stringify(error)}
client:load
/>
),
)
}
</div>
Expand All @@ -82,13 +65,14 @@ const authorData = handlePrivateAuthorProfile(authorResponse.value, session.user
<div class='w-1/4 flex flex-col justify-start items-start pl-4'>
<span class='text-xl'>Cited By</span>
{
userCitedByResponse.value !== undefined ? (
<CitationPlot citedByData={userCitedByResponse.value} client:load />
) : (
<ErrorFeedback
message={'Error while fetching datasets: ' + JSON.stringify(userCitedByResponse.error)}
client:load
/>
userCitedByResponse.match(
(citedByData) => <CitationPlot citedByData={citedByData} client:load />,
(error) => (
<ErrorFeedback
message={'Error while fetching datasets: ' + JSON.stringify(error)}
client:load
/>
),
)
}
</div>
Expand Down
5 changes: 1 addition & 4 deletions website/src/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,7 @@ export const routes = {
const userPagePath = `/user` as const;
return organism === undefined ? userPagePath : withOrganism(organism, userPagePath);
},
groupOverviewPage: (groupName: string) => {
const groupPagePath = `/group/${groupName}` as const;
return groupPagePath;
},
groupOverviewPage: (groupName: string) => `/group/${groupName}`,
userSequencesPage: (organism: string) => withOrganism(organism, `/user/seq`),
userSequenceReviewPage: (organism: string) => withOrganism(organism, `/submit/review`),
versionPage: (organism: string, accession: string) => withOrganism(organism, `/seq/${accession}/versions`),
Expand Down
20 changes: 20 additions & 0 deletions website/src/services/datasetCitationClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { datasetCitationApi } from './datasetCitationApi.ts';
import { ZodiosWrapperClient } from './zodiosWrapperClient.ts';
import { getRuntimeConfig } from '../config.ts';
import { getInstanceLogger } from '../logger.ts';
import { createAuthorizationHeader } from '../utils/createAuthorizationHeader.ts';

export class DatasetCitationClient extends ZodiosWrapperClient<typeof datasetCitationApi> {
public static create(
Expand All @@ -16,4 +17,23 @@ export class DatasetCitationClient extends ZodiosWrapperClient<typeof datasetCit
'backend',
);
}

public getDatasetsOfUser(accessToken: string) {
return this.call('getDatasetsOfUser', {
headers: createAuthorizationHeader(accessToken),
});
}

public getUserCitedBy(username: string, accessToken: string) {
return this.call('getUserCitedBy', {
params: { username },
headers: createAuthorizationHeader(accessToken),
});
}

public getAuthor(accessToken: string) {
return this.call('getAuthor', {
headers: createAuthorizationHeader(accessToken),
});
}
}
44 changes: 16 additions & 28 deletions website/src/utils/parseAccessionInput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,53 +21,41 @@ export const serializeRecordsToAccessionsInput = (records?: DatasetRecord[], del
};
};

const nucleotidePatterns = [/^[A-Z]{1}\d{5}$/, /^[A-Z]{2}\d{6}$/, /^[A-Z]{2}\d{8}$/];
const proteinPatterns = [/^[A-Z]{3}\d{5}$/, /^[A-Z]{3}\d{7}$/];
const wgsPatterns = [/^[A-Z]{4}\d{2}\d{6,}$/, /^[A-Z]{6}\d{2}\d{7,}$/];
const mgaPatterns = [/^[A-Z]{5}\d{7}$/];

const validateGenbankAccession = (accession: string): boolean => {
fengelniederhammer marked this conversation as resolved.
Show resolved Hide resolved
// https://www.ncbi.nlm.nih.gov/genbank/acc_prefix/

// Nucleotide patterns
const nucleotidePatterns = [/^[A-Z]{1}\d{5}$/, /^[A-Z]{2}\d{6}$/, /^[A-Z]{2}\d{8}$/];

// Protein patterns
const proteinPatterns = [/^[A-Z]{3}\d{5}$/, /^[A-Z]{3}\d{7}$/];

// WGS patterns
const wgsPatterns = [/^[A-Z]{4}\d{2}\d{6,}$/, /^[A-Z]{6}\d{2}\d{7,}$/];

// MGA pattern
const mgaPatterns = [/^[A-Z]{5}\d{7}$/];

return [...nucleotidePatterns, ...proteinPatterns, ...wgsPatterns, ...mgaPatterns].some((pattern) =>
pattern.test(accession),
);
};

const validateSRAAccession = (accession: string): boolean => {
// Study patterns
const studyPatterns = [/^SRP\d+$/, /^ERP\d+$/, /^DRP\d+$/];

// Sample patterns
const samplePatterns = [/^SRS\d+$/, /^ERS\d+$/, /^DRS\d+$/];

// Experiment patterns
const experimentPatterns = [/^SRX\d+$/, /^ERX\d+$/, /^DRX\d+$/];

// Run patterns
const runPatterns = [/^SRR\d+$/, /^ERR\d+$/, /^DRR\d+$/];
const studyPatterns = [/^SRP\d+$/, /^ERP\d+$/, /^DRP\d+$/];
const samplePatterns = [/^SRS\d+$/, /^ERS\d+$/, /^DRS\d+$/];
const experimentPatterns = [/^SRX\d+$/, /^ERX\d+$/, /^DRX\d+$/];
const runPatterns = [/^SRR\d+$/, /^ERR\d+$/, /^DRR\d+$/];

const validateSRAAccession = (accession: string): boolean => {
return [...studyPatterns, ...samplePatterns, ...experimentPatterns, ...runPatterns].some((pattern) =>
pattern.test(accession),
);
};

const gisaidPatterns = [/^EPI_ISL_\d+$/];

const validateGISAIDAccession = (accession: string): boolean => {
const gisaidPatterns = [/^EPI_ISL_\d+$/];
return gisaidPatterns.some((pattern) => pattern.test(accession));
};

// TODO: update after finalizing accession format:
// https://github.com/loculus-project/loculus/issues/444
const loculusPatterns = [/^[A-Z]+_\d+(\.\d+)?(\d+)?$/];

const validateLoculusAccession = (accession: string): boolean => {
// TODO: update after finalizing accession format:
// https://github.com/loculus-project/loculus/issues/444
const loculusPatterns = [/^[A-Z]+_\d+(\.\d+)?(\d+)?$/];
return loculusPatterns.some((pattern) => pattern.test(accession));
};

Expand Down
Loading