Commit 6a851eb1 authored by Kenzo-Hugo Hillion's avatar Kenzo-Hugo Hillion
Browse files

add gene length computation for different gene source and update front

parent 8ac32cc0
......@@ -93,6 +93,11 @@ DATABASE_PORT=5432
port is redirected to `5433` on the `localhost`. This is done in order to not interfere with your local
postgres if you have one. This means you need to change `DATABASE_HOST` to `localhost` and `DATABASE_POS
### Pre-computed statistics
Some statistics about genes are pre-computed and can be accessed through the `/api/catalog/v1/statistics` endpoint.
The ID is constructed with the following format: `<statisctics-type>-<gene_source>-<method>-<options>`.
----
## Run the application
......
......@@ -39,15 +39,19 @@ class ComputeCounts(ComputeStatistics):
FUNCTION_SOURCES = ['kegg', 'eggnog']
GENE_SOURCES = ['all', 'igc', 'virgo']
def compute_count(self, method, filters=None, **kwargs):
def compute_count(self, method, gene_source, filters=None, **kwargs):
if filters is None:
filters = {}
gene_stats = GeneStatistics(filters=filters)
print_kwargs = '_'.join([str(k) + '-' + str(v) for k, v in kwargs.items() if v])
print_filters = '_'.join([str(k) + '-' + str(v) for k, v in filters.items() if v])
logger.info("Call GeneStatistics(%s).%s(%s)", print_filters, method, print_kwargs)
print_kwargs = '-'.join([str(k) + '-' + str(v) for k, v in kwargs.items() if v])
print_filters = '-'.join([str(k) + '-' + str(v) for k, v in filters.items() if v])
stats_id = slugify(f"GeneStatistics({gene_source}).{method}({print_kwargs})")
logger.info(
"Call GeneStatistics(%s).%s(%s) and saving under id <%s>",
print_filters, method, print_kwargs, stats_id
)
payload = {
'stats_id': slugify(f"GeneStatistics({print_filters}).{method}({print_kwargs})"),
'stats_id': stats_id,
'body': {
'count': getattr(gene_stats, method)(**kwargs)
}
......@@ -61,9 +65,9 @@ class ComputeCounts(ComputeStatistics):
else:
filters = {'source': gene_source}
for method in self.METHODS:
self.compute_count(method, filters=filters)
self.compute_count(method, gene_source, filters=filters)
for source in self.FUNCTION_SOURCES:
self.compute_count('count_has_function', filters=filters, source=source)
self.compute_count('count_has_function', gene_source, filters=filters, source=source)
class ComputeGeneLength(ComputeStatistics):
......@@ -82,20 +86,34 @@ class ComputeGeneLength(ComputeStatistics):
'functions__isnull': False
},
}
GENE_SOURCES = ['all', 'igc', 'virgo']
def _compute_gene_length(self, filters, category, gene_source):
gene_stats = GeneLengthDistribution(
window_size=self.MIN_WINDOW_SIZE, stop_at=self.MAX_STOP_AT, filters=filters
)
for window_size in self.WINDOW_SIZES:
for stop_at in self.STOP_ATS:
stats_id = slugify(f"GeneStatistics({gene_source}).gene-length-{window_size}-{stop_at}-{category}")
logger.info(
"Call GeneStatistics.gene_length(%s, %s) for %s and saving under id <%s>",
window_size, stop_at, category, stats_id)
payload = {
'stats_id': stats_id,
'body': gene_stats.get_distribution(window_size=window_size, stop_at=stop_at)
}
self._save_to_db(payload)
def all(self):
for category, filters in self.CATEGORIES.items():
gene_stats = GeneLengthDistribution(
window_size=self.MIN_WINDOW_SIZE, stop_at=self.MAX_STOP_AT, filters=filters
)
for window_size in self.WINDOW_SIZES:
for stop_at in self.STOP_ATS:
logger.info("Call GeneStatistics.gene_length(%s, %s) for %s", window_size, stop_at, category)
payload = {
'stats_id': slugify(f"GeneStatistics.gene_length-{window_size}-{stop_at}-{category}"),
'body': gene_stats.get_distribution(window_size=window_size, stop_at=stop_at)
}
self._save_to_db(payload)
for gene_source in self.GENE_SOURCES:
if gene_source == 'all':
filters = {}
else:
filters = {'source': gene_source}
for category, cat_filters in self.CATEGORIES.items():
if cat_filters is not None:
filters.update(**cat_filters)
self._compute_gene_length(filters, category, gene_source)
class ComputeTaxonomyRepartition(ComputeStatistics):
......
......@@ -4,7 +4,7 @@ import Router from 'vue-router';
import Home from '@/views/Home.vue';
import GeneDetail from '@/views/GeneDetail.vue';
import Genes from '@/views/genes/genes.vue';
import Catalogstats from '@/views/Stats.vue';
import Catalogstats from '@/views/stats/stats.vue';
Vue.use(Router);
......
<template>
<div class="stats">
<v-flex>
<v-toolbar class="secondary darken-1 white--text" dense>
<v-icon class="white--text">far fa-chart-bar</v-icon>
<v-toolbar-title>Catalog Statistics</v-toolbar-title>
</v-toolbar>
<v-card>
<v-container
fluid
grid-list-md
>
<v-layout row wrap>
<countcard :count="geneCountAll"></countcard>
<countcard :count="geneCountFunctions"></countcard>
<countcard :count="geneCountEggnogs"></countcard>
<countcard :count="geneCountKeggs"></countcard>
<countcard :count="geneCountTaxo"></countcard>
<countcard :count="geneCountFull"></countcard>
</v-layout>
<v-layout row wrap class="mt-2">
<!-- Histogram -->
<v-flex md12 xl6>
<v-toolbar
class="primary lighten-1"
dark
dense
>
<v-icon class="white--text">far fa-chart-bar</v-icon>
<v-toolbar-title>Gene length distribution</v-toolbar-title>
<v-spacer></v-spacer>
<v-flex xs4 md2 xl3 class="mt-3">
<v-slider
v-model="geneLengthWindowSize"
@change="getAllGeneLength"
thumb-label
color="secondary lighten-2"
thumb-color="secondary lighten-1"
step="100"
min="100"
max=200
always-dirty
label="Window size (bp):"
></v-slider>
</v-flex>
<v-flex xs4 md2 xl3 class="mt-3 ml-4">
<v-slider
v-model="stopAt"
@change="getAllGeneLength"
thumb-label
color="secondary lighten-2"
thumb-color="secondary lighten-1"
step="1000"
min="1000"
max=5000
ticks
always-dirty
label="Stop at (bp):"
></v-slider>
</v-flex>
</v-toolbar>
<histogram :histoData="geneLengthData" :requestStatus="geneLengthRequestDone" chartId="histo_gene_length"></histogram>
</v-flex>
<!-- Doughnut -->
<v-flex md12 xl6>
<v-toolbar
class="taxonomy"
dark
dense
>
<v-icon class="white--text">fa-sitemap</v-icon>
<v-toolbar-title>Taxonomical annotation</v-toolbar-title>
<v-spacer></v-spacer>
<v-flex xs3 md2>
<v-select
:items="selectLevel"
v-model="taxLevel"
color="taxonomy ligthen-1"
@change="getTaxoCounts"
></v-select>
</v-flex>
</v-toolbar>
<doughnut :doughnutData="taxoCounts" chartId="taxo_annotation"></doughnut>
</v-flex>
</v-layout>
</v-container>
</v-card>
</v-flex>
</div>
</template>
<script>
import axios from 'axios';
import Histogram from '@/components/Histogram.vue';
import CountCard from '@/components/CountCard.vue';
import Doughnut from '@/components/Doughnut.vue';
export default {
name: 'home',
data() {
return {
// Gene length data
geneLengthRequestDone: false,
geneLengthLabels: [],
geneLengthAll: {},
geneLengthFunctions: {},
geneLengthKegg: {},
geneLengthEggnog: {},
geneLengthTaxonomy: {},
geneLengthFull: {},
geneLengthWindowSize: 200,
stopAt: 4000,
// Gene counts data
geneCountAll: {},
geneCountFunctions: {},
geneCountKeggs: {},
geneCountEggnogs: {},
geneCountTaxo: {},
geneCountFull: {},
// Taxonomy repartition
taxoCounts: {},
taxLevel: 'phylum',
};
},
computed: {
selectLevel() {
return ['kingdom', 'superkingdom', 'phylum', 'class', 'order', 'family', 'genus'];
},
geneLengthData() {
return {
labels: this.geneLengthLabels,
datasets: [
this.geneLengthAll,
this.geneLengthFunctions,
this.geneLengthEggnog,
this.geneLengthKegg,
this.geneLengthTaxonomy,
this.geneLengthFull,
],
};
},
},
mounted() {
this.getAllGeneLength();
this.getGeneCountsAll();
this.getGeneCountsFunctions();
this.getGeneCountsKeggs();
this.getGeneCountsEggnogs();
this.getGeneCountsTaxo();
this.getGeneCountsFull();
this.getTaxoCounts();
},
methods: {
getGeneLength() {
axios.get(`/api/catalog/v1/statistics/genestatistics-gene-length-${this.geneLengthWindowSize}-${this.stopAt}-all`, {
headers: {
Accept: 'application/json',
},
})
.then((response) => {
this.geneLengthLabels = response.data.body.labels;
this.geneLengthAll = {
data: response.data.body.counts,
label: 'All',
backgroundColor: this.$vuetify.theme.primary,
};
this.geneLengthRequestDone = true;
})
.catch((error) => {
this.geneLengthLabels = [];
this.geneLengthAll = {};
console.error(error);
this.geneLengthRequestDone = true;
});
},
getGeneLengthWithFunctions() {
axios.get(`/api/catalog/v1/statistics/genestatistics-gene-length-${this.geneLengthWindowSize}-${this.stopAt}-with-functions`, {
headers: {
Accept: 'application/json',
},
})
.then((response) => {
this.geneLengthLabels = response.data.body.labels;
this.geneLengthFunctions = {
data: response.data.body.counts,
label: 'Functions',
backgroundColor: this.$vuetify.theme.function,
};
})
.catch((error) => {
console.error(error);
});
},
getGeneLengthWithKEGG() {
axios.get(`/api/catalog/v1/statistics/genestatistics-gene-length-${this.geneLengthWindowSize}-${this.stopAt}-with-kegg`, {
headers: {
Accept: 'application/json',
},
})
.then((response) => {
this.geneLengthLabels = response.data.body.labels;
this.geneLengthKegg = {
data: response.data.body.counts,
label: 'KEGG',
backgroundColor: this.$vuetify.theme.kegg,
};
})
.catch((error) => {
console.error(error);
});
},
getGeneLengthWithEggNOG() {
axios.get(`/api/catalog/v1/statistics/genestatistics-gene-length-${this.geneLengthWindowSize}-${this.stopAt}-with-eggnog`, {
headers: {
Accept: 'application/json',
},
})
.then((response) => {
this.geneLengthLabels = response.data.body.labels;
this.geneLengthEggnog = {
data: response.data.body.counts,
label: 'EggNOG',
backgroundColor: this.$vuetify.theme.eggnog,
};
})
.catch((error) => {
console.error(error);
});
},
getGeneLengthWithTaxonomy() {
axios.get(`/api/catalog/v1/statistics/genestatistics-gene-length-${this.geneLengthWindowSize}-${this.stopAt}-with-taxonomy`, {
headers: {
Accept: 'application/json',
},
})
.then((response) => {
this.geneLengthLabels = response.data.body.labels;
this.geneLengthTaxonomy = {
data: response.data.body.counts,
label: 'Taxonomy',
backgroundColor: this.$vuetify.theme.taxonomy,
};
})
.catch((error) => {
console.error(error);
});
},
getGeneLengthWithFunctionsAndTaxonomy() {
axios.get(`/api/catalog/v1/statistics/genestatistics-gene-length-${this.geneLengthWindowSize}-${this.stopAt}-with-function-tax`, {
headers: {
Accept: 'application/json',
},
})
.then((response) => {
this.geneLengthLabels = response.data.body.labels;
this.geneLengthFull = {
data: response.data.body.counts,
label: 'Full',
backgroundColor: this.$vuetify.theme.tertiary,
};
})
.catch((error) => {
console.error(error);
});
},
getAllGeneLength() {
this.geneLengthDatasets = {};
this.getGeneLength();
this.getGeneLengthWithFunctions();
this.getGeneLengthWithKEGG();
this.getGeneLengthWithEggNOG();
this.getGeneLengthWithTaxonomy();
this.getGeneLengthWithFunctionsAndTaxonomy();
},
getGeneCountsAll() {
axios.get('/api/catalog/v1/statistics/genestatistics-count-all', {
headers: {
Accept: 'application/json',
},
})
.then((response) => {
this.geneCountAll = {
class: 'primary',
icon: 'bar_chart',
text: response.data.body.count,
title: 'Genes',
};
})
.catch((error) => {
console.error(error);
});
},
getGeneCountsFunctions() {
axios.get('/api/catalog/v1/statistics/genestatistics-count-has-function', {
headers: {
Accept: 'application/json',
},
})
.then((response) => {
this.geneCountFunctions = {
class: 'function darken-1',
icon: 'bar_chart',
text: response.data.body.count,
title: 'Functions',
};
})
.catch((error) => {
console.error(error);
});
},
getGeneCountsKeggs() {
axios.get('/api/catalog/v1/statistics/genestatistics-count-has-function-source-kegg', {
headers: {
Accept: 'application/json',
},
})
.then((response) => {
this.geneCountKeggs = {
class: 'kegg',
icon: 'bar_chart',
text: response.data.body.count,
title: 'KEGG',
};
})
.catch((error) => {
console.error(error);
});
},
getGeneCountsEggnogs() {
axios.get('/api/catalog/v1/statistics/genestatistics-count-has-function-source-eggnog', {
headers: {
Accept: 'application/json',
},
})
.then((response) => {
this.geneCountEggnogs = {
class: 'eggnog darken-1',
icon: 'bar_chart',
text: response.data.body.count,
title: 'EggNOG',
};
})
.catch((error) => {
console.error(error);
});
},
getGeneCountsTaxo() {
axios.get('/api/catalog/v1/statistics/genestatistics-count-has-taxonomy', {
headers: {
Accept: 'application/json',
},
})
.then((response) => {
this.geneCountTaxo = {
class: 'taxonomy darken-1',
icon: 'bar_chart',
text: response.data.body.count,
title: 'Taxonomy',
};
})
.catch((error) => {
console.error(error);
});
},
getGeneCountsFull() {
axios.get('/api/catalog/v1/statistics/genestatistics-count-has-function-has-taxonomy', {
headers: {
Accept: 'application/json',
},
})
.then((response) => {
this.geneCountFull = {
class: 'tertiary',
icon: 'bar_chart',
text: response.data.body.count,
title: 'Full',
};
})
.catch((error) => {
console.error(error);
});
},
getTaxoCounts() {
axios.get(`/api/catalog/v1/statistics/genestatistics-taxonomy-repartition-${this.taxLevel}`, {
headers: {
Accept: 'application/json',
},
})
.then((response) => {
const chartId = `taxo_distri_${this.taxLevel}`;
this.taxoCounts = {
data: response.data.body.counts,
labels: response.data.body.labels,
colors: response.data.body.colors,
level: this.taxLevel,
chartId,
};
})
.catch((error) => {
console.error(error);
});
},
},
components: {
histogram: Histogram,
countcard: CountCard,
doughnut: Doughnut,
},
};
</script>
<div class="stats">
<v-flex>
<v-toolbar class="secondary darken-1 white--text" dense>
<v-icon class="white--text">far fa-chart-bar</v-icon>
<v-toolbar-title>Catalog Statistics</v-toolbar-title>
<v-spacer/>
<v-flex xs12 sm6 md4 lg2 d-flex>
<v-select
dark
:items="geneSources"
v-model="geneSource"
@change="getAllStats"
></v-select>
</v-flex>
</v-toolbar>
<v-card>
<v-container
fluid
grid-list-md
>
<v-layout row wrap>
<countcard :count="geneCountAll"></countcard>
<countcard :count="geneCountFunctions"></countcard>
<countcard :count="geneCountEggnogs"></countcard>
<countcard :count="geneCountKeggs"></countcard>
<countcard :count="geneCountTaxo"></countcard>
<countcard :count="geneCountFull"></countcard>
</v-layout>
<v-layout row wrap class="mt-2">
<!-- Histogram -->
<v-flex md12 xl6>
<v-toolbar
class="primary lighten-1"
dark
dense
>
<v-icon class="white--text">far fa-chart-bar</v-icon>
<v-toolbar-title>Gene length distribution</v-toolbar-title>
<v-spacer></v-spacer>
<v-flex xs4 md2 xl3 class="mt-3">
<v-slider
v-model="geneLengthWindowSize"
@change="getAllGeneLength"
thumb-label
color="secondary lighten-2"
thumb-color="secondary lighten-1"
step="100"
min="100"
max=200
always-dirty
label="Window size (bp):"
></v-slider>
</v-flex>
<v-flex xs4 md2 xl3 class="mt-3 ml-4">
<v-slider
v-model="stopAt"
@change="getAllGeneLength"
thumb-label
color="secondary lighten-2"
thumb-color="secondary lighten-1"
step="1000"
min="1000"
max=5000
ticks
always-dirty
label="Stop at (bp):"
></v-slider>
</v-flex>
</v-toolbar>
<histogram :histoData="geneLengthData" :requestStatus="geneLengthRequestDone" chartId="histo_gene_length"></histogram>
</v-flex>
<!-- Doughnut -->
<v-flex md12 xl6>
<v-toolbar
class="taxonomy"
dark
dense
>
<v-icon class="white--text">fa-sitemap</v-icon>
<v-toolbar-title>Taxonomical annotation</v-toolbar-title>
<v-spacer></v-spacer>
<v-flex xs3 md2>
<v-select
:items="selectLevel"