Skip to content

Commit

Permalink
feat(fastani): Re-enable FastANI calculator.
Browse files Browse the repository at this point in the history
  • Loading branch information
aaronmussig committed Mar 6, 2024
1 parent 3ab66f8 commit 3e0e6a5
Show file tree
Hide file tree
Showing 22 changed files with 2,271 additions and 294 deletions.
52 changes: 45 additions & 7 deletions assets/api/fastani.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ export class FastAniApi {
})
}

getJob(jobId: string) {
getJob(jobId: number) {
return axios.get<FastAniJobResult>(`${apiBase}/fastani/${jobId}`,
{
timeout: 60000,
params: {cacheKey: apiCacheKey}
})
}

getJobInfo(jobId: string) {
getJobInfo(jobId: number) {
return axios.get<FastAniJobInfo>(`${apiBase}/fastani/${jobId}/info`,
{
timeout: 60000,
Expand All @@ -38,15 +38,15 @@ export class FastAniApi {
})
}

getJobHeatmap(jobId: string, method: string) {
getJobHeatmap(jobId: number, method: string) {
return axios.get<FastAniJobHeatmap>(`${apiBase}/fastani/${jobId}/heatmap/${method}`,
{
timeout: 60000,
params: {cacheKey: apiCacheKey}
})
}

getJobCsvUrl(jobId: string) {
getJobCsvUrl(jobId: number) {
return `${apiBase}/fastani/${jobId}/csv?cacheKey=${apiCacheKey}`
}

Expand All @@ -58,6 +58,23 @@ export class FastAniApi {
})
}

validateGenomes(payload: FastAniGenomeValidationRequest) {
return axios.post<FastAniGenomeValidationResponse[]>(`${apiBase}/fastani/validate/genomes`, payload,
{
timeout: apiTimeout,
params: {cacheKey: apiCacheKey}
})
}


getFastAniJobMetadata(jobId: number) {
return axios.get<FastAniJobMetadata>(`${apiBase}/fastani/${jobId}/metadata`,
{
timeout: apiTimeout,
params: {cacheKey: apiCacheKey}
})
}

}

// --------------------------------------------------------------------------------------------
Expand All @@ -81,7 +98,7 @@ export interface FastAniParameters {
}

export interface FastAniJobResult {
job_id: string,
job_id: number,
group_1: string[],
group_2: string[],
parameters: FastAniParameters,
Expand All @@ -108,7 +125,6 @@ export interface FastAniResult {

export interface FastAniConfig {
maxPairwise: number
maxPairwiseLow: number,
}

export enum FastAniHeatmapDataStatus {
Expand Down Expand Up @@ -144,7 +160,29 @@ export enum FastAniJobStatus {
}

export interface FastAniJobInfo {
jobId: string,
jobId: number,
createdOn: number,
status: FastAniJobStatus,
}

export interface FastAniGenomeValidationRequest {
genomes: string[]
}

export interface FastAniGenomeValidationResponse {
accession: string,
isSpRep: boolean | null,
gtdbDomain: string | null,
gtdbPhylum: string | null,
gtdbClass: string | null,
gtdbOrder: string | null,
gtdbFamily: string | null,
gtdbGenus: string | null,
gtdbSpecies: string | null,
}

export interface FastAniJobMetadata {
query: FastAniGenomeValidationResponse[]
reference: FastAniGenomeValidationResponse[]
parameters: FastAniParameters
}
24 changes: 24 additions & 0 deletions assets/api/taxon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,15 @@ export class TaxonApi {
})
}

getTaxonGenomesDetail(taxon: string, sp_reps_only: boolean) {
return axios.get<TaxonGenomesDetailResponse>(`${apiBase}/taxon/${encodeURIComponent(taxon)}/genomes-detail`,
{
timeout: apiTimeout,
params: {cacheKey: apiCacheKey, sp_reps_only: sp_reps_only}
})
}


}

// --------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -94,3 +103,18 @@ export interface TaxonDescendants {
export interface TaxonSearchResponse {
matches: string[]
}

export interface TaxonGenomesDetailRow {
gid: string,
gtdbIsRep: boolean,
gtdbDomain: string,
gtdbPhylum: string,
gtdbClass: string,
gtdbOrder: string,
gtdbFamily: string,
gtdbGenus: string,
gtdbSpecies: string,
}
export interface TaxonGenomesDetailResponse {
rows: TaxonGenomesDetailRow[]
}
25 changes: 25 additions & 0 deletions assets/models/fastani.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@


// The row used when displaying the genomes selected for query / reference
export interface FastAniQueryRow {
name: string,
isRep: boolean | null,
domain: string | null,
phylum: string | null,
class: string | null,
order: string | null,
family: string | null,
genus: string | null,
species: string | null
}

export enum FastAniInputGroup {
query,
reference,
both
}

export interface FastAniAddGenomesFromUserInput {
genomes: FastAniQueryRow[],
group: FastAniInputGroup
}
227 changes: 227 additions & 0 deletions components/fastani/FastAniAddGenomesFromTaxon.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
<template>
<v-dialog
v-model="modalVisible"
width="700"
>
<template v-slot:activator="{ on, attrs }">
<v-btn
class="white--text"
color="#5a6855"
depressed
small
v-bind="attrs"
@click="onAddGenomesFromTaxonClick"
v-on="on"
>
<v-icon left>
{{ addGenomesSvg }}
</v-icon>
Add genomes from taxon
</v-btn>
</template>

<v-card>
<v-card-title class="text-h5 white--text" style="background-color: #5a6855">
<v-icon dark left>
{{ addGenomesSvg }}
</v-icon>
Add genomes from taxon
</v-card-title>

<v-card-text class="py-1">
<v-row no-gutters>
<TaxonSearchAutocomplete
v-model="selectedTaxon"
:disabled="formDisabled"
class="ml-2 mt-3"
label="Taxon"
/>
</v-row>
<v-row class="mt-2" no-gutters>
<v-select
v-model="selectedGroup"
:items="groupOptions"
class="ml-2 mt-3"
clearable
dense
hide-details
label="Add genomes to"
outlined
>
</v-select>
</v-row>
<v-row no-gutters>
<v-checkbox
v-model="spReps"
:disabled="formDisabled"
class="ml-2"
label="Only include species representatives"
></v-checkbox>
</v-row>
</v-card-text>

<v-divider></v-divider>

<v-card-actions>
<v-chip>
<template v-if="getTaxonSearching">
Loading...
</template>
<template v-else>
{{ nGenomesInTaxon }} genomes to add
</template>

</v-chip>
<v-spacer></v-spacer>
<v-btn
:disabled="formDisabled"
color="primary"
text
@click="emitGenomeIds"
>
Save
</v-btn>
<v-btn
color="primary"
text
@click="modalVisible = false"
>
Close
</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>

<script lang="ts">
import Vue from "vue";
import TaxonSearchAutocomplete from "~/components/shared/TaxonSearchAutocomplete.vue";
import {mdiPlusThick} from "@mdi/js";
import {isDefined, maybeToLocaleString} from "~/assets/ts/common";
import {FastAniAddGenomesFromUserInput, FastAniInputGroup, FastAniQueryRow} from "~/assets/models/fastani";
import {TaxonGenomesDetailRow} from "~/assets/api/taxon";
export default Vue.extend({
components: {TaxonSearchAutocomplete},
data: () => ({
// Icons
addGenomesSvg: mdiPlusThick,
// Modal data
modalVisible: false,
// Form data
groupOptions: ['Query genomes', 'Reference genomes', 'Query & Reference genomes'],
selectedTaxon: '',
selectedGroup: 'Query genomes',
spReps: true,
// API calls
getTaxonSearching: false,
// API return data
apiSearchResult: [] as FastAniQueryRow[],
}),
watch: {
// If the taxon changes, we need to load those genomes
selectedTaxon(after: string, before: string) {
if (after && after.length > 0 && after != before) {
this.getGenomesFromTaxon();
}
},
// If the user toggles the species representatives checkbox, we need to reload the genomes
spReps(after: boolean, before: boolean) {
if (after !== before && this.selectedTaxon.length > 3) {
this.getGenomesFromTaxon();
}
},
},
computed: {
nGenomesInTaxon(): string {
return maybeToLocaleString(this.apiSearchResult.length) as string;
},
formDisabled(): boolean {
return this.getTaxonSearching;
},
selectedGroupAsEnum(): FastAniInputGroup {
if (this.selectedGroup === 'Query genomes') {
return FastAniInputGroup.query;
} else if (this.selectedGroup === 'Reference genomes') {
return FastAniInputGroup.reference;
} else {
return FastAniInputGroup.both;
}
}
},
methods: {
// Emit the chosen genome ids to the parent
emitGenomeIds() {
const output = {
genomes: this.apiSearchResult,
group: this.selectedGroupAsEnum,
} as FastAniAddGenomesFromUserInput;
this.$emit('input', output);
this.modalVisible = false;
},
// Load the genomes associated with a taxon
getGenomesFromTaxon() {
if (isDefined(this.selectedTaxon) && this.selectedTaxon.length > 3) {
// Register the start of the search
this.getTaxonSearching = true;
// Make the API call
this.$api.taxon.getTaxonGenomesDetail(this.selectedTaxon, this.spReps).then(resp => {
const out: FastAniQueryRow[] = [];
for (let i = 0; i < resp.data.rows.length; i++) {
const curItem:TaxonGenomesDetailRow = resp.data.rows[i];
out.push({
name: curItem.gid,
isRep: curItem.gtdbIsRep,
domain: curItem.gtdbDomain,
phylum: curItem.gtdbPhylum,
class: curItem.gtdbClass,
order: curItem.gtdbOrder,
family: curItem.gtdbFamily,
genus: curItem.gtdbGenus,
species: curItem.gtdbSpecies
})
}
this.apiSearchResult = out;
})
.catch((err) => {
this.$accessor.api.defaultCatch(err);
})
.finally(() => {
this.getTaxonSearching = false;
})
}
},
// Toggle the modal open
onAddGenomesFromTaxonClick() {
this.modalVisible = !this.modalVisible;
},
},
})
</script>

<style scoped>
</style>
Loading

0 comments on commit 3e0e6a5

Please sign in to comment.