diff --git a/backend/Pipfile b/backend/Pipfile index 248e6dc4c15e1c8e5da1d32337170c2d43d9e115..99a30ee4ce4816f746a0e2aa645834f25efb1622 100644 --- a/backend/Pipfile +++ b/backend/Pipfile @@ -59,6 +59,7 @@ python-slugify = "*" marshmallow = "*" django-pandas = "*" bioapi = {git = "https://github.com/khillion/bioapi.git"} +django-admin-list-filter-dropdown = "*" [requires] python_version = "3.7" diff --git a/backend/Pipfile.lock b/backend/Pipfile.lock index 86dcc9a13d99385894ebfe846e4901d1eeafab72..57d51111b69cb596f26167047a8e0003193c4d85 100644 --- a/backend/Pipfile.lock +++ b/backend/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "c448dca73004a15b2c0d65f4ff1e2b2da1aac4d1439a0be2adf97531783af899" + "sha256": "57d6d564aa9c1a9f750c89666f2191d1c5036bfa413119cb26f92719fbe48a6e" }, "pipfile-spec": 6, "requires": { @@ -65,6 +65,14 @@ "index": "pypi", "version": "==3.0" }, + "django-admin-list-filter-dropdown": { + "hashes": [ + "sha256:07cd37b6a9be1b08f11d4a92957c69b67bc70b1f87a2a7d4ae886c93ea51eb53", + "sha256:bf1b48bab9772dad79db71efef17e78782d4f2421444d5e49bb10e0da71cd6bb" + ], + "index": "pypi", + "version": "==1.0.3" + }, "django-cors-headers": { "hashes": [ "sha256:84933651fbbde8f2bc084bef2f077b79db1ec1389432f21dd661eaae6b3d6a95", @@ -403,7 +411,7 @@ "sha256:5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0", "sha256:8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71" ], - "markers": "sys_platform == 'darwin'", + "markers": "platform_system == 'Darwin'", "version": "==0.1.0" }, "astroid": { diff --git a/backend/metagenedb/api/catalog/urls.py b/backend/metagenedb/api/catalog/urls.py index e4f6b6f387a0ca42a4cc53dc3553c9074b317226..49c88b3f60ed8d01d742f69fa11a20d97a965bde 100644 --- a/backend/metagenedb/api/catalog/urls.py +++ b/backend/metagenedb/api/catalog/urls.py @@ -54,7 +54,7 @@ class CustomRouter(DefaultRouter): api_router = CustomRouter() api_router.register(r'functions', views.FunctionViewSet, basename='functions') api_router.register(r'kegg-orthologies', views.KeggOrthologyViewSet, basename='kegg-orthologies') -api_router.register(r'eggnogs', views.EggNOGv5ViewSet, basename='eggnogs') +api_router.register(r'eggnogs', views.EggNOGViewSet, basename='eggnogs') api_router.register(r'genes', views.GeneViewSet, basename='genes') api_router.register(r'taxonomy', views.TaxonomyViewSet, basename='taxonomy') diff --git a/backend/metagenedb/api/catalog/views/__init__.py b/backend/metagenedb/api/catalog/views/__init__.py index 07491013b0382a900af3e1ac28fa1a640ec929dd..03229a3b12067d04e43c6fd59407a8ccff6e529a 100644 --- a/backend/metagenedb/api/catalog/views/__init__.py +++ b/backend/metagenedb/api/catalog/views/__init__.py @@ -1,3 +1,3 @@ -from .function import EggNOGv5ViewSet, KeggOrthologyViewSet, FunctionViewSet # noqa +from .function import EggNOGViewSet, KeggOrthologyViewSet, FunctionViewSet # noqa from .gene import GeneViewSet # noqa from .taxonomy import TaxonomyViewSet # noqa diff --git a/backend/metagenedb/api/catalog/views/function.py b/backend/metagenedb/api/catalog/views/function.py index 9d1dc4cc0edfcbd32b5465e30407f9115f2e4dbd..3dcb685f5a07bc7b5a6455ff4dafd09679656d60 100644 --- a/backend/metagenedb/api/catalog/views/function.py +++ b/backend/metagenedb/api/catalog/views/function.py @@ -7,7 +7,7 @@ from rest_framework.viewsets import ModelViewSet from metagenedb.api.catalog.filters import FunctionFilter from metagenedb.api.catalog.qparams_validators.function import FunctionQueryParams -from metagenedb.apps.catalog.models import EggNOGv5, Function, KeggOrthology +from metagenedb.apps.catalog.models import EggNOG, Function, KeggOrthology from metagenedb.apps.catalog.serializers import EggNOGSerializer, FunctionSerializer, KeggOrthologySerializer from metagenedb.common.utils.external_api.togows import GetFunctionExternalInfo @@ -57,7 +57,7 @@ class KeggOrthologyViewSet(ModelViewSet): return Response(returned_data) -class EggNOGv5ViewSet(ModelViewSet): - queryset = EggNOGv5.objects.all() +class EggNOGViewSet(ModelViewSet): + queryset = EggNOG.objects.all() serializer_class = EggNOGSerializer lookup_field = 'function_id' diff --git a/backend/metagenedb/api/catalog/views/test_function.py b/backend/metagenedb/api/catalog/views/test_function.py index 5734445f2733c4d83789726d5034e03ce638a241..217e7b54edb52fc8385b41149f4e43a4d949293c 100644 --- a/backend/metagenedb/api/catalog/views/test_function.py +++ b/backend/metagenedb/api/catalog/views/test_function.py @@ -2,7 +2,7 @@ from rest_framework.test import APITestCase import mock -from metagenedb.apps.catalog.factory import EggNOGv5Factory, KeggOrthologyFactory +from metagenedb.apps.catalog.factory import EggNOGFactory, KeggOrthologyFactory from metagenedb.common.utils.mocks.metagenedb import ( MetageneDBCatalogFunctionAPIMock, MetageneDBCatalogKeggOrthologyAPIMock ) @@ -14,7 +14,7 @@ class TestKeggOrthologyViewSet(APITestCase): self.function_api = MetageneDBCatalogFunctionAPIMock(self.client) self.kegg_ortho_api = MetageneDBCatalogKeggOrthologyAPIMock(self.client) self.kegg_function = KeggOrthologyFactory.create() - self.eggnog_function = EggNOGv5Factory.create() + self.eggnog_function = EggNOGFactory.create() def test_retrieve(self): for function in [self.kegg_function, self.eggnog_function]: diff --git a/backend/metagenedb/apps/catalog/admin/function.py b/backend/metagenedb/apps/catalog/admin/function.py index c6a675b86f0dc41253a569199480f9c73a79e7d7..a3df9e83f991656586796ab707c607e92d1bdc22 100644 --- a/backend/metagenedb/apps/catalog/admin/function.py +++ b/backend/metagenedb/apps/catalog/admin/function.py @@ -1,6 +1,6 @@ from django.contrib import admin -from metagenedb.apps.catalog.models import EggNOGv5, EggNOGFunctionalCategory, Function, KeggOrthology +from metagenedb.apps.catalog.models import EggNOG, EggNOGFunctionalCategory, Function, KeggOrthology @admin.register(KeggOrthology) @@ -17,10 +17,11 @@ class FunctionAdmin(admin.ModelAdmin): search_fields = ('function_id',) -@admin.register(EggNOGv5) -class EggNOGv5Admin(admin.ModelAdmin): +@admin.register(EggNOG) +class EggNOGAdmin(admin.ModelAdmin): - list_display = ('function_id', 'name', 'get_functional_categories') + list_display = ('function_id', 'name', 'get_functional_categories', 'version') + list_filter = ('version',) search_fields = ('function_id', 'name') def get_functional_categories(self, obj): diff --git a/backend/metagenedb/apps/catalog/admin/taxonomy.py b/backend/metagenedb/apps/catalog/admin/taxonomy.py index 633f57b0c94b4556ae0e747b57a16c6479356ef0..445228df8d00eb61d4d43397a05ffa7ab323fa09 100644 --- a/backend/metagenedb/apps/catalog/admin/taxonomy.py +++ b/backend/metagenedb/apps/catalog/admin/taxonomy.py @@ -1,4 +1,5 @@ from django.contrib import admin +from django_admin_listfilter_dropdown.filters import DropdownFilter from metagenedb.apps.catalog.models import Taxonomy @@ -10,4 +11,5 @@ class TaxonomyAdmin(admin.ModelAdmin): 'tax_id', 'name', 'rank', 'superkingdom', 'kingdom', 'phylum', 'class_rank', 'order', 'family', 'genus', 'species', ) + list_filter = (('rank', DropdownFilter),) search_fields = ('tax_id', 'name') diff --git a/backend/metagenedb/apps/catalog/factory/__init__.py b/backend/metagenedb/apps/catalog/factory/__init__.py index a02fda04be86dd90dfc5a3b11c5fd37e61c4ac16..d7b5b1431f8ae24bc153ae78122bafacc3a015d5 100644 --- a/backend/metagenedb/apps/catalog/factory/__init__.py +++ b/backend/metagenedb/apps/catalog/factory/__init__.py @@ -1,3 +1,3 @@ -from .function import EggNOGv5Factory, FunctionFactory, KeggOrthologyFactory # noqa +from .function import EggNOGFactory, FunctionFactory, KeggOrthologyFactory # noqa from .gene import GeneFactory # noqa from .taxonomy import TaxonomyFactory # noqa diff --git a/backend/metagenedb/apps/catalog/factory/function.py b/backend/metagenedb/apps/catalog/factory/function.py index fc523e88f98bffc587b0e9c481985503af8ea39d..1ab878bd9d42815cbf8ce6da17db64a18674804d 100644 --- a/backend/metagenedb/apps/catalog/factory/function.py +++ b/backend/metagenedb/apps/catalog/factory/function.py @@ -8,6 +8,7 @@ from .fuzzy_base import FuzzyLowerText faker = Factory.create() SELECTED_SOURCE = [i[0] for i in models.Function.SOURCE_CHOICES] +EGGNOG_VERSIONS = [i[0] for i in models.EggNOG.VERSION_CHOICES] class BaseFunctionFactory(DjangoModelFactory): @@ -22,9 +23,11 @@ class FunctionFactory(BaseFunctionFactory): function_id = FuzzyLowerText(prefix='function-', length=15) -class EggNOGv5Factory(BaseFunctionFactory): +class EggNOGFactory(BaseFunctionFactory): class Meta: - model = models.EggNOGv5 + model = models.EggNOG + + version = fuzzy.FuzzyChoice(EGGNOG_VERSIONS) class KeggOrthologyFactory(BaseFunctionFactory): diff --git a/backend/metagenedb/apps/catalog/management/commands/create_update_eggnog.py b/backend/metagenedb/apps/catalog/management/commands/create_update_eggnog.py index 3c6750f5de090491cb06b6937b31b3a35b8ecc17..91fcbfb1a1b29de1b3644f0d60c5737981cb24d1 100644 --- a/backend/metagenedb/apps/catalog/management/commands/create_update_eggnog.py +++ b/backend/metagenedb/apps/catalog/management/commands/create_update_eggnog.py @@ -3,7 +3,7 @@ import logging from django.core.management.base import BaseCommand from django.core.exceptions import ValidationError -from metagenedb.apps.catalog.models import EggNOGv5, EggNOGFunctionalCategory +from metagenedb.apps.catalog.models import EggNOG, EggNOGFunctionalCategory from metagenedb.common.utils.chunks import file_len from metagenedb.common.utils.parsers import EggNOGAnnotationLineParser @@ -43,14 +43,14 @@ class ImportEggNOG(object): payload = {k: v for k, v in eggnog_dict.items() if v != ""} eggnog = None try: - eggnog = EggNOGv5(**payload) + eggnog = EggNOG(**payload) eggnog.full_clean() eggnog.save() self.created_count += 1 except ValidationError as validation_error: if 'function_id' in validation_error.error_dict.keys(): try: - eggnog = EggNOGv5.objects.get(function_id=payload.get('function_id')) + eggnog = EggNOG.objects.get(function_id=payload.get('function_id')) for k, v in payload.items(): setattr(eggnog, k, v) eggnog.full_clean() diff --git a/backend/metagenedb/apps/catalog/migrations/0016_add_eggnog_version.py b/backend/metagenedb/apps/catalog/migrations/0016_add_eggnog_version.py new file mode 100644 index 0000000000000000000000000000000000000000..b92f5b5b55b286d284f94cdf564eb7cc2fbb0864 --- /dev/null +++ b/backend/metagenedb/apps/catalog/migrations/0016_add_eggnog_version.py @@ -0,0 +1,28 @@ +# Generated by Django 3.0 on 2019-12-12 10:46 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('catalog', '0015_m2m_func_cat_eggnog'), + ] + + operations = [ + migrations.AlterModelOptions( + name='eggnog', + options={}, + ), + migrations.AddField( + model_name='eggnog', + name='version', + field=models.CharField(choices=[('3.0', '3.0'), ('5.0', '5.0')], default=5.0, max_length=5), + preserve_default=False, + ), + migrations.AlterField( + model_name='eggnog', + name='functional_categories', + field=models.ManyToManyField(to='catalog.EggNOGFunctionalCategory'), + ), + ] diff --git a/backend/metagenedb/apps/catalog/migrations/0016_rename_eggnog.py b/backend/metagenedb/apps/catalog/migrations/0017_rename_meta_eggnog.py similarity index 53% rename from backend/metagenedb/apps/catalog/migrations/0016_rename_eggnog.py rename to backend/metagenedb/apps/catalog/migrations/0017_rename_meta_eggnog.py index bc77106a2c864d227a5ae5b663e3d2d605a68948..ad200e61e6557020089e7b835bd0c37ab5afc8ff 100644 --- a/backend/metagenedb/apps/catalog/migrations/0016_rename_eggnog.py +++ b/backend/metagenedb/apps/catalog/migrations/0017_rename_meta_eggnog.py @@ -1,4 +1,4 @@ -# Generated by Django 3.0 on 2019-12-11 22:07 +# Generated by Django 3.0 on 2019-12-12 11:12 from django.db import migrations, models @@ -6,16 +6,16 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('catalog', '0015_m2m_func_cat_eggnog'), + ('catalog', '0016_add_eggnog_version'), ] operations = [ - migrations.RenameModel( - old_name='EggNog', - new_name='EggNOGv5', + migrations.AlterModelOptions( + name='eggnog', + options={'verbose_name': 'EggNOG', 'verbose_name_plural': 'EggNOGs'}, ), migrations.AlterField( - model_name='eggnogv5', + model_name='eggnog', name='functional_categories', field=models.ManyToManyField(to='catalog.EggNOGFunctionalCategory'), ), diff --git a/backend/metagenedb/apps/catalog/models/__init__.py b/backend/metagenedb/apps/catalog/models/__init__.py index 50aa3ddeabf0f49892c9db59f9b5f152e4fb89f4..795bdebd8f5b8a28cccea5a2319dd1a4474b9da8 100644 --- a/backend/metagenedb/apps/catalog/models/__init__.py +++ b/backend/metagenedb/apps/catalog/models/__init__.py @@ -1,3 +1,3 @@ -from .function import EggNOGv5, EggNOGFunctionalCategory, Function, KeggOrthology # noqa +from .function import EggNOG, EggNOGFunctionalCategory, Function, KeggOrthology # noqa from .gene import Gene, GeneFunction # noqa from .taxonomy import Taxonomy # noqa diff --git a/backend/metagenedb/apps/catalog/models/function.py b/backend/metagenedb/apps/catalog/models/function.py index aa10577c604720186651e7a133c333c9fa69ff9e..45b6fe9a294b60360085b968a52017f73f3d4b13 100644 --- a/backend/metagenedb/apps/catalog/models/function.py +++ b/backend/metagenedb/apps/catalog/models/function.py @@ -56,14 +56,19 @@ class EggNOGFunctionalCategory(models.Model): verbose_name_plural = "EggNOG functional categories" -class EggNOGv5(Function): +class EggNOG(Function): SOURCE = 'eggnog' + VERSION_CHOICES = [ + ('3.0', '3.0'), + ('5.0', '5.0') + ] functional_categories = models.ManyToManyField(EggNOGFunctionalCategory) + version = models.CharField(max_length=5, choices=VERSION_CHOICES) def __init__(self, *args, **kwargs): - super(EggNOGv5, self).__init__(source=self.SOURCE, *args, **kwargs) + super(EggNOG, self).__init__(source=self.SOURCE, *args, **kwargs) class Meta: - verbose_name = "EggNOG v5.0" - verbose_name_plural = "EggNOGs v5.0" + verbose_name = "EggNOG" + verbose_name_plural = "EggNOGs" diff --git a/backend/metagenedb/apps/catalog/serializers/function.py b/backend/metagenedb/apps/catalog/serializers/function.py index f937dd327c1c1d39fa288f89b1b8e80b137d227a..15cbe30400d9378c1ff3c46ed383858dd9815349 100644 --- a/backend/metagenedb/apps/catalog/serializers/function.py +++ b/backend/metagenedb/apps/catalog/serializers/function.py @@ -1,5 +1,5 @@ from rest_framework import serializers -from metagenedb.apps.catalog.models import EggNOGv5, Function, KeggOrthology +from metagenedb.apps.catalog.models import EggNOG, Function, KeggOrthology from .bulk_list import BulkListSerializer @@ -21,15 +21,15 @@ class FunctionSerializer(serializers.ModelSerializer): class EggNOGListSerializer(BulkListSerializer): class Meta: - model = EggNOGv5 + model = EggNOG class EggNOGSerializer(serializers.ModelSerializer): class Meta: - model = EggNOGv5 + model = EggNOG list_serializer_class = EggNOGListSerializer - fields = ('function_id', 'name', 'functional_categories') + fields = ('function_id', 'name', 'functional_categories', 'version') class KeggOrthologyListSerializer(BulkListSerializer): diff --git a/backend/metagenedb/common/utils/mocks/metagenedb.py b/backend/metagenedb/common/utils/mocks/metagenedb.py index ec8baee2d0d52780db568d687cee75b806d04843..c8e0185ff457f23f4c968f80b429bf427046f83c 100644 --- a/backend/metagenedb/common/utils/mocks/metagenedb.py +++ b/backend/metagenedb/common/utils/mocks/metagenedb.py @@ -90,5 +90,5 @@ class MetageneDBCatalogKeggOrthologyAPIMock(MetageneDBCatalogFunctionAPIMock): REVERSE_PATH = 'catalog:v1:kegg-orthologies' -class MetageneDBCatalogEggNOGv5APIMock(MetageneDBCatalogFunctionAPIMock): +class MetageneDBCatalogEggNOGAPIMock(MetageneDBCatalogFunctionAPIMock): REVERSE_PATH = 'catalog:v1:eggnogs' diff --git a/backend/metagenedb/settings/django.py b/backend/metagenedb/settings/django.py index 02ddd749b22c50635e6a69de4b1ee9792d2d904a..fd14467b70c5ee664e00feee19801f6d1778afe4 100644 --- a/backend/metagenedb/settings/django.py +++ b/backend/metagenedb/settings/django.py @@ -21,6 +21,7 @@ INSTALLED_APPS = [ 'django_filters', 'corsheaders', 'drf_yasg', + 'django_admin_listfilter_dropdown', ] MIDDLEWARE = [ diff --git a/backend/scripts/populate_db/import_igc_data.py b/backend/scripts/populate_db/import_igc_data.py index a0d9d685d007852814d3268d1b2b7fe9a0c1a97f..c95c40975524597e0ae03b7207abca2a7dfcbc10 100755 --- a/backend/scripts/populate_db/import_igc_data.py +++ b/backend/scripts/populate_db/import_igc_data.py @@ -10,7 +10,7 @@ from bioapi import ( MetageneDBCatalogGeneAPI, MetageneDBCatalogTaxonomyAPI, MetageneDBCatalogKeggOrthologyAPI, - MetageneDBCatalogEggNogAPI + MetageneDBCatalogEggNogAPI, ) from requests.exceptions import HTTPError from slugify import slugify diff --git a/backend/scripts/populate_db/test_import_igc_data.py b/backend/scripts/populate_db/test_import_igc_data.py index 9960d0216530f4a72ad4349ea5a00c708b01d764..ba76a096564c3ef44cd6fcf4f8eda4c20f281346 100644 --- a/backend/scripts/populate_db/test_import_igc_data.py +++ b/backend/scripts/populate_db/test_import_igc_data.py @@ -4,13 +4,13 @@ from rest_framework.test import APITestCase from metagenedb.common.utils.mocks.metagenedb import ( MetageneDBCatalogTaxonomyAPIMock, - MetageneDBCatalogEggNOGv5APIMock, + MetageneDBCatalogEggNOGAPIMock, MetageneDBCatalogKeggOrthologyAPIMock ) from metagenedb.apps.catalog.factory import ( TaxonomyFactory, KeggOrthologyFactory, - EggNOGv5Factory + EggNOGFactory ) from scripts.populate_db.import_igc_data import ImportIGCGenes @@ -302,12 +302,12 @@ class TestBuildBuildFunctionCatalog(APITestCase): @classmethod def setUpTestData(cls): cls.keggs = KeggOrthologyFactory.create_batch(100) - cls.eggnogs = EggNOGv5Factory.create_batch(100) + cls.eggnogs = EggNOGFactory.create_batch(100) def setUp(self): self.import_igc_genes = ImportIGCGenes('test', 'test_url', 'test_token') self.kegg_api_mock = MetageneDBCatalogKeggOrthologyAPIMock(self.client) - self.eggnog_api_mock = MetageneDBCatalogEggNOGv5APIMock(self.client) + self.eggnog_api_mock = MetageneDBCatalogEggNOGAPIMock(self.client) self.import_igc_genes.metagenedb_kegg_api = self.kegg_api_mock self.import_igc_genes.metagenedb_eggnog_api = self.eggnog_api_mock