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

Draft: Feature/filter #26

Merged
merged 13 commits into from
Sep 7, 2023
42 changes: 42 additions & 0 deletions src/components/QueryFilter.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<template>
<div class="input-group mb-3">
<span class="input-label input-group-text">{{ label }}:</span>
<input class="form-control" type="text" v-model.number="from" />
<span class="input-group-text">Min</span>
<input class="form-control" type="text" v-model.number="to" />
<span class="input-group-text">Max</span>
</div>
</template>

<script setup lang="ts">
import type { FilterTuple } from "@/views/BrowseView.vue";
import { computed, type PropType } from "vue";

const props = defineProps({
label: { type: String, required: true },
modelValue: { type: Object as PropType<FilterTuple>, required: true },
});

const emits = defineEmits<{
(e: "update:modelValue", value: FilterTuple): void;
}>();

const from = computed({
get: () => props.modelValue.from,
set: (x) => {
emits("update:modelValue", { ...props.modelValue, from: x });
},
});
const to = computed({
get: () => props.modelValue.to,
set: (x) => {
emits("update:modelValue", { ...props.modelValue, to: x });
},
});
</script>

<style scoped>
.input-label {
width: 8.5rem;
}
</style>
64 changes: 0 additions & 64 deletions src/components/ResultTable.vue

This file was deleted.

171 changes: 128 additions & 43 deletions src/views/BrowseView.vue
Original file line number Diff line number Diff line change
@@ -1,54 +1,139 @@
<script setup lang="ts">
import { ref } from "vue";
import ResultTable from "@/components/ResultTable.vue";

const entries = ref([{
"id": "SAMD00000344",
"GC": 0.75,
"Contigs": 175,
"Length": 2359987,
"Name": "Decumaria L."
}, {
"id": "SAMD00000344",
"GC": 0.23,
"Contigs": 171,
"Length": 2359987,
"Name": "Avicennia germinans (L.) L."
}, {
"id": "SAMD00000344",
"GC": 0.40,
"Contigs": 19,
"Length": 2359987,
"Name": "Cetraria ericetorum Opiz ssp. reticulata (Rasanen) Karnefelt"
}, {
"id": "SAMD00000344",
"GC": 0.61,
"Contigs": 66,
"Length": 2359987,
"Name": "Rudbeckia laciniata L. var. laciniata"
}, {
"id": "SAMD00000344",
"GC": 0.49,
"Contigs": 170,
"Length": 2359987,
"Name": "Tetraclinis articulata (Vahl) Masters"
}
])
import { useApi } from "@/BakrepApi";
import usePageState, { State } from "@/PageState";
import Loading from "@/components/Loading.vue";
import QueryFilter from "@/components/QueryFilter.vue";
import {
empty,
toPosition,
type PaginationData,
type PositionInResult,
} from "@/components/pagination/Pagination";
import Pagination from "@/components/pagination/Pagination.vue";
import type { BakrepSearchResultEntry } from "@/model/BakrepSearchResult";
import ResultTable from "@/views/search/ResultTable.vue";
import { computed, onMounted, ref, type Ref } from "vue";
const searchState = usePageState();
const entries: Ref<BakrepSearchResultEntry[]> = ref([]);
const pageState = usePageState();

const api = useApi();
const pagination: Ref<PaginationData> = ref(empty());

export type FilterTuple = {
from: number;
to: number;
};

const sizeTuple = ref<FilterTuple>({ from: 0, to: 9999999 });
const gcTuple = ref<FilterTuple>({ from: 0, to: 75 });
const contigTuple = ref<FilterTuple>({ from: 0, to: 1000 });
const qualityTuple = ref<FilterTuple>({ from: 0, to: 100 });
const contaminationTuple = ref<FilterTuple>({ from: 0, to: 100 });

function filter(offset = 0) {
let query;
query = {
op: "and",
value: [
// Size Filter

{
field: "bakta.stats.size",
op: "[]",
value: sizeTuple.value,
},

{
field: "bakta.stats.gc",
op: "[]",
value: { from: gcTuple.value.from / 100, to: gcTuple.value.to / 100 },
},
// Contig Count Filter

{
field: "bakta.stats.no_sequences",
op: "[]",
value: contigTuple.value,
},

// Quality Filter

{
field: "checkm2.quality.completeness",
op: "[]",
value: qualityTuple.value,
},

// Contamination Filter

{
field: "checkm2.quality.contamination",
op: "[]",
value: contaminationTuple.value,
},
],
};

searchState.value.setState(State.Loading);

api
.search({
query: query,
sort: [{ field: "bakta.stats.no_sequences", ord: "asc" }],
offset: offset,
limit: 10,
})
.then((r) => {
entries.value = r.results;
searchState.value.setState(State.Main);
pagination.value.offset = r.offset;
pagination.value.total = r.total;
})
.catch((err) => pageState.value.setError(err));
}
const positionInResults: Ref<PositionInResult> = computed(() =>
toPosition(pagination.value),
);

onMounted(filter);
</script>

<template>
<main class="container pt-5">

<div class="row">
<h2>Browse BakRep Genomes</h2>
</div>

<div class="row">
<ResultTable :entries=entries />
</div>
<loading :state="pageState">
<div class="row">
<div class="col">
<div class="rounded bg-body-secondary p-4 mb-4">
<QueryFilter label="Size" v-model="sizeTuple" />
<QueryFilter label="GC Ratio" v-model="gcTuple" />
<QueryFilter label="Contig Count" v-model="contigTuple" />
<QueryFilter label="Quality" v-model="qualityTuple" />
<QueryFilter label="Contamination" v-model="contaminationTuple" />
<button class="btn btn-light w-100" @click="filter()">
Apply Filter
</button>
</div>
</div>
</div>
<Loading :state="searchState">
<div class="row py-3 my-5">
Showing results {{ positionInResults.firstElement }}-{{
positionInResults.lastElement
}}
of {{ pagination.total }} results
<ResultTable :entries="entries" />
<Pagination
class="mt-3"
:value="pagination"
@update:offset="filter"
/>
</div>
</Loading>
</loading>
</main>
</template>

<style>
</style>
<style></style>
Loading