Commit 9650a34f authored by Kenzo-Hugo Hillion's avatar Kenzo-Hugo Hillion
Browse files

Merge branch '67-statistics-on-genes' into 'dev'

Add cards for global counts of annotation on the database

See merge request !22
parents a494b00c 87cb9d53
Pipeline #19072 passed with stages
in 2 minutes and 29 seconds
from .function import FunctionFilter # noqa
from .gene import GeneFilter # noqa
from .taxonomy import TaxonomyFilter # noqa
from django_filters import rest_framework as filters
from metagenedb.apps.catalog.models import Gene
class GeneFilter(filters.FilterSet):
no_taxonomy = filters.BooleanFilter(field_name='taxonomy', lookup_expr="isnull")
no_functions = filters.BooleanFilter(field_name='functions', lookup_expr="isnull")
class Meta:
model = Gene
fields = ['taxonomy', 'functions']
from marshmallow import Schema, fields
from metagenedb.common.django_default.qparams_validators import PaginatedQueryParams
class GeneLengthQueryParams(Schema):
window_size = fields.Integer()
stop_at = fields.Integer()
class GeneQueryParams(PaginatedQueryParams):
no_taxonomy = fields.Boolean()
no_functions = fields.Boolean()
......@@ -2,13 +2,13 @@ from django_pandas.io import read_frame
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
from marshmallow.exceptions import ValidationError
from rest_framework import filters
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.status import HTTP_204_NO_CONTENT, HTTP_422_UNPROCESSABLE_ENTITY
from metagenedb.apps.catalog.models import Gene
from metagenedb.api.catalog.qparams_validators.gene import GeneLengthQueryParams
from metagenedb.api.catalog.filters import GeneFilter
from metagenedb.api.catalog.qparams_validators.gene import GeneLengthQueryParams, GeneQueryParams
from metagenedb.apps.catalog.serializers import GeneSerializer
from metagenedb.common.utils.df_operations import get_mask
......@@ -46,11 +46,12 @@ class DocGeneLength(object):
class GeneViewSet(BulkViewSet):
search_fields = ['gene_name']
filter_backends = (filters.SearchFilter,)
queryset = Gene.objects.select_related('taxonomy').prefetch_related('functions').all()
serializer_class = GeneSerializer
filterset_class = GeneFilter
query_params_parser = GeneQueryParams
lookup_field = 'gene_id'
GENE_LENGTH_COL = 'length'
DEFAULT_WINDOW_SIZE = 1000
DEFAULT_STOP_AT = 10000
......
......@@ -4589,6 +4589,11 @@
"integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=",
"dev": true
},
"d3-format": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.2.tgz",
"integrity": "sha512-gco1Ih54PgMsyIXgttLxEhNy/mXxq8+rLnCb5shQk+P5TsiySrwWU5gpB4zen626J4LIwBxHvDChyA8qDm57ww=="
},
"dashdash": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
......
......@@ -13,6 +13,7 @@
"axios": "^0.19.0",
"chart.js": "^2.8.0",
"core-js": "^2.6.5",
"d3-format": "^1.4.2",
"element-ui": "^2.10.0",
"register-service-worker": "^1.6.2",
"vue": "^2.6.10",
......
<template>
<v-flex xs6 sm4 md2 xl1>
<v-card :color="count.class" dark class="elevation-2">
<v-card-title primary-title>
<div v-if="count.text">
<div class="blue-grey--text text--lighten-5 subheading font-weight-thin mb-1">{{ count.title }}</div>
<div class="display-1 font-weight-medium">{{ count.text }}</div>
</div>
<div v-else class="text-xs-center">
<v-progress-circular
indeterminate
color="secondary"
></v-progress-circular>
</div>
</v-card-title>
</v-card>
</v-flex>
</template>
<script>
export default {
props: {
count: {
type: Object,
required: true,
},
},
};
</script>
<template>
<div v-if="geneLengthData.counts">
<canvas id="histogram"></canvas>
</div>
<div class="text-xs-center" v-else>
<v-progress-circular
indeterminate
color="secondary"
></v-progress-circular>
</div>
<v-flex xs12 md6 xl4>
<v-toolbar
:class="histoData.class"
dark
v-if="histoData.data"
>
<v-icon class="white--text">{{ histoData.icon }}</v-icon>
<v-toolbar-title>{{ histoData.title }}</v-toolbar-title>
</v-toolbar>
<v-card class="pa-2">
<div class="card-body">
<div v-if="histoData.data">
<canvas :id="histoData.chart_id"></canvas>
</div>
<div class="text-xs-center" v-else>
<v-progress-circular
indeterminate
color="secondary"
></v-progress-circular>
</div>
</div>
</v-card>
</v-flex>
</template>
<script>
......@@ -15,25 +29,25 @@ import Chart from 'chart.js';
export default {
props: {
geneLengthData: {
histoData: {
type: Object,
required: true,
},
},
updated() {
this.createChart('histogram');
this.createChart(this.histoData.chart_id);
},
methods: {
createChart(chartId) {
const ctx = document.getElementById(chartId);
const histoData = {
labels: this.geneLengthData.labels,
labels: this.histoData.labels,
datasets: [
{
label: 'Number of genes',
data: this.geneLengthData.counts,
borderColor: '#edb183',
backgroundColor: '#f15152',
label: this.histoData.label,
data: this.histoData.data,
borderColor: this.histoData.bordelColor,
backgroundColor: this.histoData.backgroundColor,
},
],
};
......
<template>
<div class="stats">
<h1 class="subheading primary--text">Statistics</h1>
<v-container fluid>
<v-layout row wrap>
<v-flex xs12 md6>
<v-toolbar
class="secondary"
dark
>
<v-toolbar-title>Gene length distribution</v-toolbar-title>
</v-toolbar>
<v-card class="pa-2">
<div class="card-body">
<histogram v-bind:geneLengthData="geneLengthData"></histogram>
<!-- <label for="geneLengthWindowSize">Gene size window</label>
<input type="range" class="custom-range" min="200" max="10000" step="200"
v-model="geneLengthWindowSize" v-on:input="getGeneLength(geneLengthWindowSize)"
debounce="500">
{{geneLengthWindowSize}} -->
</div>
</v-card>
</v-flex>
</v-layout>
</v-container>
<v-flex>
<v-toolbar class="secondary white--text" dense>
<v-btn dark icon>
<v-icon>list_alt</v-icon>
</v-btn>
<v-toolbar-title>Catalog Statistics</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn dark icon>
<v-icon>search</v-icon>
</v-btn>
</v-toolbar>
<v-card>
<v-container
fluid
grid-list-md
>
<v-layout row wrap>
<countcard :count="geneCountAll"></countcard>
<countcard :count="geneCountFunctions"></countcard>
<countcard :count="geneCountTaxo"></countcard>
<countcard :count="geneCountFull"></countcard>
</v-layout>
<v-layout row wrap>
<histogram :histoData="geneLengthData"></histogram>
</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';
export default {
name: 'home',
data() {
return {
geneLengthData: {},
geneLengthWindowSize: 1000,
geneCountAll: {},
geneCountFunctions: {},
geneCountTaxo: {},
geneCountFull: {},
geneLengthWindowSize: 400,
stopAt: 5000,
};
},
mounted() {
this.getGeneLength(this.geneLengthWindowSize);
this.getGeneLength(this.geneLengthWindowSize, this.stopAt);
this.getGeneCountsAll();
this.getGeneCountsFunctions();
this.getGeneCountsTaxo();
this.getGeneCountsFull();
},
methods: {
getGeneLength(geneLengthWindowSize) {
getGeneLength(geneLengthWindowSize, stopAt) {
axios.get('/api/catalog/v1/genes/gene_length', {
params: {
'window_size': geneLengthWindowSize,
'stop_at': stopAt,
},
headers: {
Accept: 'application/json',
},
})
.then((response) => {
this.geneLengthData = response.data.results;
this.geneLengthData = {
chart_id: "histo1",
data: response.data.results.counts,
labels: response.data.results.labels,
title: "Gene length distribution",
label: "Number of genes",
borderColor: '#edb183',
backgroundColor: '#f15152',
class: "secondary",
icon: "bar_chart",
};
})
.catch((error) => {
console.log(error);
});
},
getGeneCountsAll() {
axios.get('/api/catalog/v1/genes', {
headers: {
Accept: 'application/json',
},
})
.then((response) => {
this.geneCountAll = {
class: "secondary",
icon: "bar_chart",
text: response.data.count,
title: "Genes",
};
})
.catch((error) => {
console.log(error);
});
},
getGeneCountsFunctions() {
axios.get('/api/catalog/v1/genes', {
params: {
no_functions: false,
},
headers: {
Accept: 'application/json',
},
})
.then((response) => {
this.geneCountFunctions = {
class: "secondary",
icon: "bar_chart",
text: response.data.count,
title: "Functions annotation",
};
})
.catch((error) => {
console.log(error);
});
},
getGeneCountsTaxo() {
axios.get('/api/catalog/v1/genes', {
params: {
no_taxonomy: false,
},
headers: {
Accept: 'application/json',
},
})
.then((response) => {
this.geneCountTaxo = {
class: "secondary",
icon: "bar_chart",
text: response.data.count,
title: "Taxonomy Annotation",
};
})
.catch((error) => {
console.log(error);
});
},
getGeneCountsFull() {
axios.get('/api/catalog/v1/genes', {
params: {
no_functions: false,
no_taxonomy: false,
},
headers: {
Accept: 'application/json',
},
})
.then((response) => {
this.geneCountFull = {
class: "secondary",
icon: "bar_chart",
text: response.data.count,
title: "Full annotation",
};
})
.catch((error) => {
console.log(error);
});
},
},
components: {
histogram: Histogram,
countcard: CountCard,
},
components: { histogram: Histogram },
};
</script>
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment