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

Merge branch '27-backend-cleaning' into 'master'

Resolve "Reorganize backend architecture"

Closes #27 and #33

See merge request !7
parents 802c9c13 9ea066fd
Pipeline #13532 passed with stages
in 1 minute and 59 seconds
......@@ -12,7 +12,7 @@ class TestAccounts(APITestCase):
def test_obtain_jwt(self):
# create an inactive user
url = reverse('api-jwt-auth')
url = reverse('api:auth:api-jwt-auth')
u = User.objects.create_user(username='user', email='user@foo.com', password='pass')
u.is_active = False
u.save()
......@@ -39,7 +39,7 @@ class TestAccounts(APITestCase):
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
verify_url = reverse('api-jwt-verify')
verify_url = reverse('api:auth:api-jwt-verify')
credentials = {
'token': token
}
......
......@@ -8,17 +8,17 @@ from rest_framework_jwt.views import (
urlpatterns = [
re_path(
r'^auth/obtain_token/',
r'^obtain_token/',
obtain_jwt_token,
name='api-jwt-auth'
),
re_path(
r'^auth/refresh_token/',
r'^refresh_token/',
refresh_jwt_token,
name='api-jwt-refresh'
),
re_path(
r'^auth/verify_token/',
r'^verify_token/',
verify_jwt_token,
name='api-jwt-verify'
),
......
from django.conf.urls import url, include
from rest_framework.routers import DefaultRouter
from metagenedb.api.catalog.views import GeneViewSet
api_router = DefaultRouter()
api_router.register(r'genes', GeneViewSet, base_name='genes')
urlpatterns = [
url(r'v1/', include((api_router.urls, 'v1')))
]
from .gene import GeneViewSet # noqa
__all__ = ['GeneViewSet']
import pandas as pd
from rest_framework.viewsets import GenericViewSet
from rest_framework import mixins
from rest_framework.decorators import action
from rest_framework.response import Response
from metagenedb.common.utils.df_operations import get_mask
from metagenedb.apps.catalog.models import Gene
from metagenedb.apps.catalog.serializers import GeneSerializer
class GeneViewSet(mixins.ListModelMixin,
mixins.RetrieveModelMixin,
GenericViewSet):
queryset = Gene.objects.all()
serializer_class = GeneSerializer
GENE_LENGTH_COL = 'gene_length'
def _count_windows(self, df, window_size=10000, window_col=GENE_LENGTH_COL):
"""
Count how many line of the df belong to each windows defined by the window_size for the window_col
:param df:
:param window_col: column concerned by the window
:param window_size: size of the window
:return: {'data': COUNTS_BY_WINDOW, 'labels': START-END}
"""
all_ranges = [(i, i + window_size) for i in range(0, df[window_col].max(), window_size)]
data = []
labels = []
for rg in all_ranges:
labels.append(f"{rg[0]}-{rg[1]-1}")
data.append(df[get_mask(df, rg, window_col)].count()['gene_length'])
return {
'counts': data,
'labels': labels
}
@action(methods=['get'], detail=False)
def gene_length(self, request, window_size=10000):
if 'window_size' in request.query_params:
window_size = int(request.query_params.get('window_size'))
df = pd.DataFrame(list(self.queryset.values(self.GENE_LENGTH_COL)))
return Response(
{'results': self._count_windows(df, window_size)}
)
......@@ -2,18 +2,21 @@ from django.contrib.auth.models import User
from django.test import TestCase
from django.urls import reverse
import pandas as pd
from rest_framework import status
from rest_framework_jwt.settings import api_settings
from metagenedb.api.catalog.views.gene import GeneViewSet
class TestGenes(TestCase):
"""Post Tests"""
def test_get_genes_no_auth(self):
"""
Unauthenticated users should be able to access genes
@TODO make unaccessible
"""
url = reverse('genes')
url = reverse('api:catalog:v1:genes-list')
resp = self.client.get(url)
self.assertEqual(resp.status_code, status.HTTP_200_OK)
......@@ -31,6 +34,25 @@ class TestGenes(TestCase):
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
url = reverse('genes')
url = reverse('api:catalog:v1:genes-list')
resp = self.client.get(url, format='json', HTTP_AUTHORIZATION=f"JWT {token}")
self.assertEqual(resp.status_code, status.HTTP_200_OK)
class TestCountWindows(TestCase):
def setUp(self):
self.window_col = "gene_length"
self.df = pd.DataFrame(
[22, 29, 35],
columns=[self.window_col]
)
def test_simple_count_window10(self):
expected_dict = {
'labels': ['0-9', '10-19', '20-29', '30-39'],
'counts': [0, 0, 2, 1]
}
geneviewset = GeneViewSet()
test_dict = geneviewset._count_windows(self.df, 10, window_col=self.window_col)
self.assertDictEqual(test_dict, expected_dict)
from django.urls import include, path
urlpatterns = [
path('auth/', include(('metagenedb.api.accounts.urls', 'auth'))),
path('catalog/', include(('metagenedb.api.catalog.urls', 'catalog')))
]
from django.apps import AppConfig
class AccountsConfig(AppConfig):
name = 'accounts'
from rest_framework import serializers
from .models import Gene, Function
class FunctionSerializer(serializers.ModelSerializer):
class Meta:
model = Function
fields = ('function_id', 'source', 'name')
class GeneSerializer(serializers.ModelSerializer):
functions = FunctionSerializer(many=True, read_only=True)
class Meta:
model = Gene
fields = ('gene_id', 'gene_length', 'functions')
from django.urls import re_path
from . import views
urlpatterns = [
re_path(r'^api/genes/$', views.gene_list, name='genes'),
re_path(r'^api/genes/(?P<gene_id>.*)$', views.gene_detail),
re_path(r'^api/gene_length$', views.gene_length, name='gene_length'),
]
from .gene import gene_detail, gene_list # noqa
from .statistics import gene_length # noqa
\ No newline at end of file
import logging
from rest_framework import status
from rest_framework.decorators import (
api_view,
authentication_classes,
permission_classes
)
from rest_framework.response import Response
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from metagenedb.apps.catalog.models import Gene
from metagenedb.apps.catalog.serializers import GeneSerializer
logging.basicConfig(level=logging.INFO)
_LOGGER = logging.getLogger(__name__)
@api_view(['GET'])
@authentication_classes(())
@permission_classes(())
def gene_list(request):
"""
List genes
"""
next_page = 1
previous_page = 1
genes = Gene.objects.all()
page = request.GET.get('page', 1)
paginator = Paginator(genes, 10)
try:
data = paginator.page(page)
except PageNotAnInteger:
data = paginator.page(1)
except EmptyPage:
data = paginator.page(paginator.num_pages)
serializer = GeneSerializer(data, context={'request': request}, many=True)
if data.has_next():
next_page = data.next_page_number()
if data.has_previous():
previous_page = data.previous_page_number()
return Response(
{'data': serializer.data, 'count': paginator.count, 'numpages': paginator.num_pages,
'nextlink': '/api/genes/?page=' + str(next_page),
'prevlink': '/api/genes/?page=' + str(previous_page)}
)
@api_view(['GET'])
def gene_detail(request, gene_id):
"""
Retrieve, update or delete a gene instance.
"""
try:
gene = Gene.objects.get(gene_id=gene_id)
except Gene.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
serializer = GeneSerializer(gene, context={'request': request})
return Response(serializer.data)
import pandas as pd
from rest_framework.decorators import api_view
from rest_framework.response import Response
from metagenedb.apps.catalog.models import Gene
GENE_LENGTH_COL = 'gene_length'
def _get_mask(df, rg, col_name):
"""
rg is a range, e.g. (10-20)
"""
return (df[col_name] >= rg[0]) & (df[col_name] < rg[1])
def _count_windows(df, window_col, window_size=10000):
"""
Count how many line of the df belong to each windows defined by the window_size for the window_col
:param df:
:param window_col: column concerned by the window
:param window_size: size of the window
:return: {'data': COUNTS_BY_WINDOW, 'labels': START-END}
"""
all_ranges = [(i, i + window_size) for i in range(0, df[window_col].max(), window_size)]
data = []
labels = []
for rg in all_ranges:
labels.append(f"{rg[0]}-{rg[1]-1}")
data.append(df[_get_mask(df, rg, window_col)].count()['gene_length'])
return {
'counts': data,
'labels': labels
}
@api_view(['GET'])
def gene_length(request, window_size=10000):
if 'window_size' in request.query_params:
window_size = int(request.query_params.get('window_size'))
df = pd.DataFrame(list(Gene.objects.all().values(GENE_LENGTH_COL)))
return Response({
'data': _count_windows(df, GENE_LENGTH_COL, window_size)
})
import pandas as pd
from unittest import TestCase
from .statistics import _count_windows
class TestCountWindows(TestCase):
def setUp(self):
self.window_col = "gene_length"
self.df = pd.DataFrame(
[22, 29, 35],
columns=[self.window_col]
)
def test_simple_count_window10(self):
expected_dict = {
'labels': ['0-9', '10-19', '20-29', '30-39'],
'counts': [0, 0, 2, 1]
}
test_dict = _count_windows(self.df, self.window_col, 10)
self.assertDictEqual(test_dict, expected_dict)
def get_mask(df, range, col_name):
"""
Get mask for a dataframe for a given range on a column defined by col_name
Lower is included but not upper:
e.g (4,8): 4, 5, 6 and 7 are selected
"""
return (df[col_name] >= range[0]) & (df[col_name] < range[1])
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