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

wip: Add dev dashboard to show processed sequences by pipeline version #3153

Closed
wants to merge 3 commits into from
Closed
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 @@ -5,8 +5,10 @@ import io.swagger.v3.oas.annotations.security.SecurityRequirement
import org.loculus.backend.config.BackendSpringProperty
import org.loculus.backend.config.DEBUG_MODE_ON_VALUE
import org.loculus.backend.service.debug.DeleteSequenceDataService
import org.loculus.backend.service.submission.SubmissionDatabaseService
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.ResponseStatus
Expand All @@ -16,12 +18,21 @@ import org.springframework.web.bind.annotation.RestController
@RequestMapping("/debug")
@SecurityRequirement(name = "bearerAuth")
@ConditionalOnProperty(BackendSpringProperty.DEBUG_MODE, havingValue = DEBUG_MODE_ON_VALUE)
class DebugController(private val deleteSequenceDataService: DeleteSequenceDataService) {
class DebugController(
private val deleteSequenceDataService: DeleteSequenceDataService,
private val submissionDatabaseService: SubmissionDatabaseService
) {

@Operation(description = "An internal endpoint to delete all the sequence data for testing and debugging purposes.")
@ResponseStatus(HttpStatus.NO_CONTENT)
@PostMapping("/delete-all-sequence-data")
fun deleteAllSequenceData() {
deleteSequenceDataService.deleteAllSequenceData()
}

@Operation(description = "Fetch the count of processed sequences by pipeline version.")
@GetMapping("/processed-sequences-count")
fun getProcessedSequencesCount(): Map<Long, Int> {
return submissionDatabaseService.getProcessedSequencesCountByPipelineVersion()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1077,6 +1077,18 @@ open class SubmissionDatabaseService(
newVersion
}
}

fun getProcessedSequencesCountByPipelineVersion(): Map<Long, Int> {
val table = SequenceEntriesPreprocessedDataTable
val countExpression = table.accessionColumn.count()

return table
.slice(table.pipelineVersionColumn, countExpression)
.selectAll()
.groupBy(table.pipelineVersionColumn)
.associate {
it[table.pipelineVersionColumn] to it[countExpression].toInt()
}
}

private fun Transaction.findNewPreprocessingPipelineVersion(): Long? {
Expand Down
100 changes: 100 additions & 0 deletions unknown-target
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { Zodios } from '@zodios/core';
import { ZodiosHooks, type ZodiosHooksInstance } from '@zodios/react';
import { isAxiosError } from 'axios';

import { backendApi } from './backendApi.ts';
import { lapisApi } from './lapisApi.ts';
import { seqSetCitationApi } from './seqSetCitationApi.ts';
import { problemDetail } from '../types/backend.ts';
import type { SequenceRequest } from '../types/lapis.ts';
import type { ClientConfig } from '../types/runtimeConfig.ts';
import { fastaEntries } from '../utils/parseFasta.ts';
import { isAlignedSequence, isUnalignedSequence, type SequenceType } from '../utils/sequenceTypeHelpers.ts';

export function backendClientHooks(clientConfig: ClientConfig) {
const zodiosHooks = new ZodiosHooks('loculus', new Zodios(clientConfig.backendUrl, backendApi));
return {
zodiosHooks,
utilityHooks: {
useGetProcessedSequencesCount(token: string) {
return zodiosHooks.useGetProcessedSequencesCount({
headers: { Authorization: `Bearer ${token}` },
});
},
},
};
}

export function lapisClientHooks(lapisUrl: string) {
const zodiosHooks = new ZodiosHooks('lapis', new Zodios(lapisUrl, lapisApi, { transform: false }));
return {
zodiosHooks,
utilityHooks: {
useGetSequence(accessionVersion: string, sequenceType: SequenceType, isMultiSegmented: boolean) {
const { data, error, isLoading } = getSequenceHook(
zodiosHooks,
{
accessionVersion,
dataFormat: 'FASTA',
},
sequenceType,
isMultiSegmented,
);

if (data === undefined) {
if (isAxiosError(error)) {
const maybeProblemDetail = error.response?.data.error ?? error.response?.data;

const problemDetailParseResult = problemDetail.safeParse(maybeProblemDetail);

if (problemDetailParseResult.success) {
return { data: null, error: problemDetailParseResult.data, isLoading };
}
}

return { data, error, isLoading };
}

const parseResult = fastaEntries.safeParse(data);

if (parseResult.success) {
return {
data: parseResult.data.length > 0 ? parseResult.data[0] : null,
error,
isLoading,
};
}
return {
data: undefined,
error: parseResult.error,
isLoading,
};
},
},
};
}

function getSequenceHook(
hooks: ZodiosHooksInstance<typeof lapisApi>,
request: SequenceRequest,
sequenceType: SequenceType,
isMultiSegmented: boolean,
) {
if (isUnalignedSequence(sequenceType)) {
return isMultiSegmented
? hooks.useUnalignedNucleotideSequencesMultiSegment(request, { params: { segment: sequenceType.name } })
: hooks.useUnalignedNucleotideSequences(request);
}

if (isAlignedSequence(sequenceType)) {
return isMultiSegmented
? hooks.useAlignedNucleotideSequencesMultiSegment(request, { params: { segment: sequenceType.name } })
: hooks.useAlignedNucleotideSequences(request);
}

return hooks.useAlignedAminoAcidSequences(request, { params: { gene: sequenceType.name } });
}

export function seqSetCitationClientHooks(clientConfig: ClientConfig) {
return new ZodiosHooks('loculus', new Zodios(clientConfig.backendUrl, seqSetCitationApi));
}
10 changes: 10 additions & 0 deletions website/src/services/backendApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,15 @@ const infoEndpoint = makeEndpoint({
response: info,
});

const getProcessedSequencesCountEndpoint = makeEndpoint({
method: 'get',
path: '/debug/processed-sequences-count',
alias: 'getProcessedSequencesCount',
parameters: [authorizationHeader],
response: z.record(z.string(), z.number()),
errors: [notAuthorizedError],
});

export const backendApi = makeApi([
submitEndpoint,
reviseEndpoint,
Expand All @@ -264,4 +273,5 @@ export const backendApi = makeApi([
getDataUseTermsHistoryEndpoint,
setDataUseTerms,
infoEndpoint,
getProcessedSequencesCountEndpoint,
]);
6 changes: 6 additions & 0 deletions website/src/services/backendClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,10 @@ export class BackendClient extends ZodiosWrapperClient<typeof backendApi> {
() => false,
);
}

public getProcessedSequencesCount(token: string) {
return this.call('getProcessedSequencesCount', {
headers: createAuthorizationHeader(token),
});
}
}
12 changes: 11 additions & 1 deletion website/src/services/serviceHooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,17 @@ import { fastaEntries } from '../utils/parseFasta.ts';
import { isAlignedSequence, isUnalignedSequence, type SequenceType } from '../utils/sequenceTypeHelpers.ts';

export function backendClientHooks(clientConfig: ClientConfig) {
return new ZodiosHooks('loculus', new Zodios(clientConfig.backendUrl, backendApi));
const zodiosHooks = new ZodiosHooks('loculus', new Zodios(clientConfig.backendUrl, backendApi));
return {
zodiosHooks,
utilityHooks: {
useGetProcessedSequencesCount(token: string) {
return zodiosHooks.useGetProcessedSequencesCount({
headers: { Authorization: `Bearer ${token}` },
});
},
},
};
}

export function lapisClientHooks(lapisUrl: string) {
Expand Down
4 changes: 4 additions & 0 deletions website/src/services/zodiosWrapperClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,8 @@ export class ZodiosWrapperClient<Api extends ZodiosEndpointDefinitions> {
instance: method,
};
}

public async getProcessedSequencesCount(): Promise<Result<Record<string, number>, ProblemDetail>> {
return this.call('getProcessedSequencesCount');
}
}
Loading