Skip to content

Commit

Permalink
Merge pull request #69 from ag-computational-bio/feature/add-statisti…
Browse files Browse the repository at this point in the history
…cs-to-landing-page

Feature/add statistics to landing page
  • Loading branch information
lukasjelonek authored Nov 9, 2023
2 parents 2f44090 + bd57169 commit 55db200
Show file tree
Hide file tree
Showing 12 changed files with 263 additions and 50 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@ coverage

# Custom

.notes.md
.notes.md
shell.nix
33 changes: 25 additions & 8 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,13 @@
"dependencies": {
"bootstrap": "^5.2.3",
"bootstrap-icons": "^1.10.3",
"chart.js": "^4.3.0",
"datatables.net-bs5": "^1.13.6",
"datatables.net-vue3": "^2.1.3",
"file-saver": "^2.0.5",
"igv": "^2.15.10",
"json5": "^2.2.3",
"jsonpointer": "^5.0.1",
"pako": "^2.1.0",
"plotly.js-dist-min": "^2.27.0",
"vue": "^3.2.45",
"vue-chartjs": "^5.2.0",
"vue-router": "^4.1.6",
Expand All @@ -44,6 +43,7 @@
"@types/jsdom": "^21.1.2",
"@types/node": "^18.11.12",
"@types/pako": "^2.0.0",
"@types/plotly.js-dist-min": "^2.3.3",
"@vitejs/plugin-vue": "^4.0.0",
"@vue/eslint-config-prettier": "^8.0.0",
"@vue/eslint-config-typescript": "^11.0.0",
Expand Down
32 changes: 28 additions & 4 deletions src/BakrepApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ import { DatasetSchema, type Dataset } from "./model/Dataset";
import { GtdbtkResultSchema, type GtdbtkResult } from "./model/GtdbtkResult";
import { MlstResultSchema, type MlstResult } from "./model/MlstResults";
import type { SearchInfo, SearchRequest } from "./model/Search";
import {
RepositoryStatisticsSchema,
type RepositoryStatistics,
} from "./model/statistics/RepositoryStatistics";
import {
KeywordCountsSchema,
type KeywordCounts,
} from "./model/statistics/KeywordCounts";

import {
tsvSearchResultFromText,
type TsvSearchResult,
Expand All @@ -22,12 +31,14 @@ function initApi(url: string) {

interface BakrepApi {
getDataset(id: string): Promise<Dataset>;
fetchUrlContentAsJson(url: string): Promise<any>;
fetchBaktaResult(dataset: Dataset): Promise<BaktaResult | undefined>;
fetchGtdbtkResult(dataset: Dataset): Promise<GtdbtkResult | undefined>;
fetchCheckmResult(dataset: Dataset): Promise<CheckmResult | undefined>;
fetchMlstResult(dataset: Dataset): Promise<MlstResult | undefined>;
fetchMetadata(dataset: Dataset): Promise<Metadata | undefined>;
fetchGenusStatistics(): Promise<KeywordCounts>;
fetchSpeciesStatistics(): Promise<KeywordCounts>;
fetchSummary(): Promise<RepositoryStatistics>;
search(request: SearchRequest): Promise<BakrepSearchResult>;
searchTsv(
request: SearchRequest,
Expand Down Expand Up @@ -56,9 +67,7 @@ class BakrepApiImpl implements BakrepApi {
.then(this.toJson)
.then((j) => DatasetSchema.parse(j));
}
fetchUrlContentAsJson(url: string): Promise<any> {
return fetch(url).then(this.toJson);
}

fetchBaktaResult(dataset: Dataset): Promise<BaktaResult | undefined> {
const bakta = dataset.results.filter(
(x) => x.attributes.tool === "bakta" && x.attributes.filetype === "json",
Expand Down Expand Up @@ -127,6 +136,21 @@ class BakrepApiImpl implements BakrepApi {
}
return fetch(metadata[0].url).then(this.toJson).then(MetadataSchema.parse);
}
fetchSummary(): Promise<RepositoryStatistics> {
return fetch(baseurl + "/stats/summary")
.then(this.toJson)
.then(RepositoryStatisticsSchema.parse);
}
fetchGenusStatistics(): Promise<KeywordCounts> {
return fetch(baseurl + "/stats/genus")
.then(this.toJson)
.then(KeywordCountsSchema.parse);
}
fetchSpeciesStatistics(): Promise<KeywordCounts> {
return fetch(baseurl + "/stats/species")
.then(this.toJson)
.then(KeywordCountsSchema.parse);
}
searchinfo(): Promise<SearchInfo> {
return fetch(baseurl + "/search/_info")
.then(this.toJson)
Expand Down
1 change: 1 addition & 0 deletions src/components/Navbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const items = ref([
{ href: { name: "browse" }, text: "Browse" },
{ href: { name: "search" }, text: "Search" },
{ href: { name: "composition" }, text: "Species composition" },
{ href: { name: "statistics" }, text: "Repository statistics" },
{ href: { name: "about" }, text: "About" },
]);
</script>
Expand Down
13 changes: 13 additions & 0 deletions src/model/statistics/KeywordCounts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { z } from "zod";

const KeywordCountSchema = z.object({
count: z.number(),
key: z.string(),
});

const KeywordCountsSchema = z.array(KeywordCountSchema);

export type KeywordCountEntry = z.infer<typeof KeywordCountSchema>;
export type KeywordCounts = z.infer<typeof KeywordCountsSchema>;

export { KeywordCountSchema, KeywordCountsSchema };
11 changes: 11 additions & 0 deletions src/model/statistics/RepositoryStatistics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { z } from "zod";

const RepositoryStatisticsSchema = z.object({
genera: z.number(),
datasets: z.number(),
species: z.number(),
});

export type RepositoryStatistics = z.infer<typeof RepositoryStatisticsSchema>;

export { RepositoryStatisticsSchema };
5 changes: 5 additions & 0 deletions src/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ const router = createRouter({
name: "composition",
component: () => import("../views/DatasetCompositionView.vue"),
},
{
path: "/statistics",
name: "statistics",
component: () => import("../views/statistics/StatisticsView.vue"),
},
],
});

Expand Down
69 changes: 34 additions & 35 deletions src/views/HomeView.vue
Original file line number Diff line number Diff line change
@@ -1,35 +1,34 @@
<script setup lang="ts"></script>

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

<div class="row">
<div class="col-md-12 col-lg-6">
<h2>BakRep</h2>
<p>
The large amount of bacterial genomic data in public genome databases
is an important resource for research in various fields. However, most
of this data has been processed differently, making accurate
comparisons challenging. <em>Blackwell et al</em>., used a uniform
approach to assemble and characterise 661,405 bacterial genomes
retrieved from the European Nucleotide Archive (ENA) in November of
2018. We build up on this resource and further analyse the assembled
genomes in a standardised manner. We conducted a robust taxonomic
classification using the Genome Taxonomy Database (GTDB) and
furthermore subtyped all eligible genomes via multilocus-sequence
typing. In addition we annotated all genomes assigning functional
categories, e.g. COG and E.C., and database cross references to public
databases. The overarching goal is to make this standardised resource
available to non-bioinformaticians via an interactive website. This
website will provide researchers with a flexible search engine to
query the repository
</p>
</div>
<div class="col-md-12 col-lg-6">
<img src="/krona_main.svg" class="w-100" />
</div>
</div>
</main>
</template>

<style></style>
<script setup lang="ts"></script>

<template>
<main class="container pt-5">
<div class="row">
<div class="col-md-12 col-lg-6">
<h2>BakRep</h2>
<p>
The large amount of bacterial genomic data in public genome databases
is an important resource for research in various fields. However, most
of this data has been processed differently, making accurate
comparisons challenging. <em>Blackwell et al</em>., used a uniform
approach to assemble and characterise 661,405 bacterial genomes
retrieved from the European Nucleotide Archive (ENA) in November of
2018. We build up on this resource and further analyse the assembled
genomes in a standardised manner. We conducted a robust taxonomic
classification using the Genome Taxonomy Database (GTDB) and
furthermore subtyped all eligible genomes via multilocus-sequence
typing. In addition we annotated all genomes assigning functional
categories, e.g. COG and E.C., and database cross references to public
databases. The overarching goal is to make this standardised resource
available to non-bioinformaticians via an interactive website. This
website will provide researchers with a flexible search engine to
query the repository
</p>
</div>
<div class="col-md-12 col-lg-6">
<img src="/krona_main.svg" class="w-100" />
</div>
</div>
</main>
</template>

<style></style>
56 changes: 56 additions & 0 deletions src/views/statistics/PhylogenyCountsBarchart.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<script setup lang="ts">
import { type KeywordCounts } from "@/model/statistics/KeywordCounts";
import Plotly from "plotly.js-dist-min";
import type { Data, Layout } from "plotly.js-dist-min";
import {
computed,
type ComputedRef,
onMounted,
ref,
onBeforeUnmount,
} from "vue";
import type { PropType } from "vue";
const props = defineProps({
inputData: { type: Array as PropType<KeywordCounts>, required: true },
});
let data: Data[] = [];
const reversedData: ComputedRef<KeywordCounts> = computed(() =>
[...props.inputData].reverse(),
);
data.push({
y: reversedData.value.map((item) => item.key),
x: reversedData.value.map((item) => item.count),
type: "bar",
orientation: "h",
name: "count",
});
const layout: Partial<Layout> = {
height: 800,
yaxis: {
automargin: true,
},
xaxis: {
type: "log",
// autorange: true,
},
};
const plot = ref();
onMounted(() => {
Plotly.newPlot(plot.value, data, layout);
});
onBeforeUnmount(() => {
Plotly.purge(plot.value);
});
</script>

<template>
<div ref="plot"></div>
</template>
28 changes: 28 additions & 0 deletions src/views/statistics/RepositoryStatistics.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<template>
<table class="w-50">
<tr v-for="(key, index) in Object.keys(entries)" :key="index">
<th scope="row">
{{ key.charAt(0).toLocaleUpperCase() + key.slice(1) }}:
</th>
<td>{{ entries[key] }}</td>
</tr>
</table>
</template>

<script setup lang="ts">
import type { PropType } from "vue";
defineProps({
entries: { type: Object as PropType<Record<string, number>>, required: true },
});
</script>

<style scoped>
th {
text-align: start;
}
td {
text-align: start;
}
</style>
Loading

0 comments on commit 55db200

Please sign in to comment.