Repository: iossifovlab/gpf

Issue:
[BUG] Exception in gene scores with empty HG38 instance
**What happened**:
I am working with an empty instance prepared as described in https://www.iossifovlab.com/gpfuserdocs/administration/getting_started.html#create-an-empty-gpf-instance

I imported a study as described here: https://www.iossifovlab.com/gpfuserdocs/administration/getting_started.html#example-import-of-de-novo-variants-helloworld

When I go to `Genotype Browser` and select `Gene Scores`

<img width="1345" alt="Screenshot 2022-11-16 at 9 06 18" src="https://user-images.githubusercontent.com/827119/202109840-ea93b158-ba22-4882-8c20-1cc1c21af97c.png">

 I get the following error:

```
ERROR 2022-11-16 00:59:20,801 log 2378396 139838594078464 Internal Server Error: /api/v3/gene_scores
Traceback (most recent call last):
  File "/home/lubo/miniconda3/envs/gpf/lib/python3.9/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
  File "/home/lubo/miniconda3/envs/gpf/lib/python3.9/site-packages/django/core/handlers/base.py", line 197, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/home/lubo/miniconda3/envs/gpf/lib/python3.9/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "/home/lubo/miniconda3/envs/gpf/lib/python3.9/site-packages/django/views/generic/base.py", line 103, in view
    return self.dispatch(request, *args, **kwargs)
  File "/home/lubo/miniconda3/envs/gpf/lib/python3.9/site-packages/rest_framework/views.py", line 509, in dispatch
    response = self.handle_exception(exc)
  File "/home/lubo/miniconda3/envs/gpf/lib/python3.9/site-packages/rest_framework/views.py", line 469, in handle_exception
    self.raise_uncaught_exception(exc)
  File "/home/lubo/miniconda3/envs/gpf/lib/python3.9/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
    raise exc
  File "/home/lubo/miniconda3/envs/gpf/lib/python3.9/site-packages/rest_framework/views.py", line 506, in dispatch
    response = handler(request, *args, **kwargs)
  File "/home/lubo/Work/seq-pipeline/gpf/wdae/wdae/gene_scores/views.py", line 22, in get
    gene_scores = self.gpf_instance.get_all_gene_scores()
  File "/home/lubo/Work/seq-pipeline/gpf/dae/dae/gpf_instance/gpf_instance.py", line 364, in get_all_gene_scores
    return self.gene_scores_db.get_gene_scores()
AttributeError: 'list' object has no attribute 'get_gene_scores'
ERROR 2022-11-16 00:59:20,802 basehttp 2378396 139838594078464 "GET /api/v3/gene_scores HTTP/1.1" 500 121573
```

**What you expected to happen**:

I expect to have an empty list of gene scores or a label similar to the Genomic scores when no genomic scores found.

<img width="872" alt="Screenshot 2022-11-16 at 8 48 36" src="https://user-images.githubusercontent.com/827119/202110387-1e943ed2-33e2-40ac-9c9e-fe709607d9e3.png">


**Minimal Complete Verifiable Example**:

**Anything else we need to know?**:

**Environment**:

- GPF version: development version 3.7dev5 from the master branch
- Python version: 3.9
- Operating System: linux
- Install method (conda, pip, source): source


Discussion:

Patch:
diff --git a/dae/dae/gene/gene_scores.py b/dae/dae/gene/gene_scores.py
index f94aa6bed..14dbe6d61 100644
--- a/dae/dae/gene/gene_scores.py
+++ b/dae/dae/gene/gene_scores.py
@@ -2,7 +2,6 @@ from __future__ import annotations
 
 import itertools
 import logging
-from collections import OrderedDict
 from typing import Optional, List
 from functools import cache
 
@@ -206,7 +205,7 @@ class GeneScoresDb:
 
     def __init__(self, gene_scores):
         super().__init__()
-        self.scores = OrderedDict()
+        self.scores = {}
         for score in gene_scores:
             self.scores[score.score_id] = score
 
@@ -222,8 +221,10 @@ class GeneScoresDb:
 
     def get_gene_score(self, score_id):
         """Return a given gene score."""
-        assert self[score_id].df is not None
-        return self[score_id]
+        if score_id not in self.scores:
+            return None
+        assert self.scores[score_id].df is not None
+        return self.scores[score_id]
 
     def __getitem__(self, score_id):
         if score_id not in self.scores:
diff --git a/dae/dae/gpf_instance/gpf_instance.py b/dae/dae/gpf_instance/gpf_instance.py
index d0dc782a9..1c8e9d0ec 100644
--- a/dae/dae/gpf_instance/gpf_instance.py
+++ b/dae/dae/gpf_instance/gpf_instance.py
@@ -160,7 +160,7 @@ class GPFInstance:
     def gene_scores_db(self):
         """Load and return gene scores db."""
         if self.dae_config.gene_scores_db is None:
-            return []
+            return GeneScoresDb([])
 
         gene_scores = self.dae_config.gene_scores_db.gene_scores
         result = []
@@ -358,7 +358,7 @@ class GPFInstance:
         return gene_score_id in self.gene_scores_db
 
     def get_gene_score(self, gene_score_id):
-        return self.gene_scores_db[gene_score_id]
+        return self.gene_scores_db.get_gene_score(gene_score_id)
 
     def get_all_gene_scores(self):
         return self.gene_scores_db.get_gene_scores()
diff --git a/wdae/wdae/wdae/settings.py b/wdae/wdae/wdae/settings.py
index cd0f75dbc..a9b2d4836 100644
--- a/wdae/wdae/wdae/settings.py
+++ b/wdae/wdae/wdae/settings.py
@@ -3,6 +3,9 @@
 
 # pylint: disable=wildcard-import,unused-wildcard-import
 import os
+
+from gpf_instance.gpf_instance import get_wgpf_instance_path
+
 from .default_settings import *
 
 ALLOWED_HOSTS += ["localhost"]
@@ -26,7 +29,7 @@ OPEN_REGISTRATION = True
 ########################################################
 
 DEFAULT_WDAE_DIR = os.path.join(
-    os.environ.get("DAE_DB_DIR", ""), "wdae")
+    get_wgpf_instance_path(), "wdae")
 os.makedirs(DEFAULT_WDAE_DIR, exist_ok=True)
 
 DATABASES = {
diff --git a/wdae/wdae/wdae/wgpf.py b/wdae/wdae/wdae/wgpf.py
index 07be3fe1d..b16aa3fd0 100644
--- a/wdae/wdae/wdae/wgpf.py
+++ b/wdae/wdae/wdae/wgpf.py
@@ -85,14 +85,12 @@ def _run_init_command(wgpf_instance, **kwargs):
             "please use '--force' flag.", wgpf_instance.dae_dir)
         sys.exit(0)
 
-    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "wdae.settings")
-
     try:
         try:
             execute_from_command_line([
                 "wgpf", "migrate",
                 "--skip-checks",
-                "--settings", os.environ["DJANGO_SETTINGS_MODULE"]])
+            ])
         except SystemExit:
             if not force:
                 raise
@@ -105,7 +103,7 @@ def _run_init_command(wgpf_instance, **kwargs):
                 "--name", "GPF development server",
                 "--redirect-uris", "http://localhost:8000/datasets",
                 "--skip-checks",
-                "--settings", os.environ["DJANGO_SETTINGS_MODULE"]])
+            ])
         except SystemExit:
             if not force:
                 raise
@@ -125,17 +123,11 @@ def _run_run_command(wgpf_instance, **kwargs):
     host = kwargs.get("host")
     port = kwargs.get("port")
 
-    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "wdae.settings")
-
     try:
-        django.setup()
-        settings.DISABLE_PERMISSIONS = True
-        settings.STUDIES_EAGER_LOADING = True
-
         execute_from_command_line([
             "wgpf", "runserver", f"{host}:{port}",
             "--skip-checks",
-            "--settings", os.environ["DJANGO_SETTINGS_MODULE"]])
+        ])
 
     finally:
         pass
@@ -174,6 +166,21 @@ def cli(argv=None):
     # pylint: disable=import-outside-toplevel
     from gpf_instance import gpf_instance
     wgpf_instance = gpf_instance.build_wgpf_instance(args.gpf_instance)
+    logger.info("using GPF instance at %s", wgpf_instance.dae_dir)
+
+    if command not in {"init", "run"}:
+        logger.error("unknown subcommand %s used in `wgpf`", command)
+        sys.exit(1)
+
+    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "wdae.settings")
+
+    django.setup()
+    settings.DISABLE_PERMISSIONS = True
+    settings.STUDIES_EAGER_LOADING = True
+    settings.DEFAULT_WDAE_DIR = os.path.join(
+        wgpf_instance.dae_dir, "wdae")
+    os.makedirs(settings.DEFAULT_WDAE_DIR, exist_ok=True)
+    logger.info("using wdae directory: %s", settings.DEFAULT_WDAE_DIR)
 
     if command == "init":
         _run_init_command(wgpf_instance, **vars(args))


Test patch:
diff --git a/dae/dae/gpf_instance/tests/test_empty_gpf_instance.py b/dae/dae/gpf_instance/tests/test_empty_gpf_instance.py
index 31a4986a1..e2c0e6fc6 100644
--- a/dae/dae/gpf_instance/tests/test_empty_gpf_instance.py
+++ b/dae/dae/gpf_instance/tests/test_empty_gpf_instance.py
@@ -30,6 +30,18 @@ def test_empty_gene_scores(alla_instance):
     assert len(alla_instance.gene_scores_db) == 0
 
 
+def test_has_gene_score(alla_instance):
+    assert not alla_instance.has_gene_score("ala-bala")
+
+
+def test_get_gene_score(alla_instance):
+    assert alla_instance.get_gene_score("ala-bala") is None
+
+
+def test_get_all_gene_scores(alla_instance):
+    assert len(alla_instance.get_all_gene_scores()) == 0
+
+
 def test_empty_genomic_scores(alla_instance):
     assert len(alla_instance.genomic_scores_db) == 0
 
diff --git a/wdae/tests/integration/test_wgpf_command/test_wgpf_run.py b/wdae/tests/integration/test_wgpf_command/test_wgpf_run.py
index 654b86870..06e9833b3 100644
--- a/wdae/tests/integration/test_wgpf_command/test_wgpf_run.py
+++ b/wdae/tests/integration/test_wgpf_command/test_wgpf_run.py
@@ -21,7 +21,7 @@ def test_wgpf_run_simple(wgpf_fixture, wdae_django_setup, mocker):
         # pylint: disable=no-member
         wgpf.execute_from_command_line.assert_called_once_with([
             "wgpf", "runserver", "0.0.0.0:8000", "--skip-checks",
-            "--settings", "tests.integration.test_wgpf_command.wgpf_settings"])
+        ])
 
 
 def test_wgpf_run_without_init(wgpf_fixture, wdae_django_setup, mocker):
@@ -41,22 +41,21 @@ def test_wgpf_run_without_init(wgpf_fixture, wdae_django_setup, mocker):
         # pylint: disable=no-member
         wgpf.execute_from_command_line.assert_called_with([
             "wgpf", "runserver", "0.0.0.0:8000", "--skip-checks",
-            "--settings", "tests.integration.test_wgpf_command.wgpf_settings"])
+        ])
 
         wgpf.execute_from_command_line.assert_has_calls([
             mocker.call([
-                "wgpf", "migrate", "--skip-checks", "--settings",
-                "tests.integration.test_wgpf_command.wgpf_settings"]),
+                "wgpf", "migrate", "--skip-checks",
+            ]),
             mocker.call([
                 "wgpf", "createapplication", "public", "authorization-code",
                 "--client-id", "gpfjs", "--name", "GPF development server",
                 "--redirect-uris", "http://localhost:8000/datasets",
-                "--skip-checks", "--settings",
-                "tests.integration.test_wgpf_command.wgpf_settings"]),
+                "--skip-checks",
+            ]),
             mocker.call([
                 "wgpf", "runserver", "0.0.0.0:8000", "--skip-checks",
-                "--settings",
-                "tests.integration.test_wgpf_command.wgpf_settings"])
+            ])
         ])
 
 
@@ -78,7 +77,7 @@ def test_wgpf_run_with_port(wgpf_fixture, wdae_django_setup, mocker):
         # pylint: disable=no-member
         wgpf.execute_from_command_line.assert_called_once_with([
             "wgpf", "runserver", "0.0.0.0:9000", "--skip-checks",
-            "--settings", "tests.integration.test_wgpf_command.wgpf_settings"])
+        ])
 
 
 def test_wgpf_run_with_host(wgpf_fixture, wdae_django_setup, mocker):
@@ -99,4 +98,23 @@ def test_wgpf_run_with_host(wgpf_fixture, wdae_django_setup, mocker):
         # pylint: disable=no-member
         wgpf.execute_from_command_line.assert_called_once_with([
             "wgpf", "runserver", "localhost:8000", "--skip-checks",
-            "--settings", "tests.integration.test_wgpf_command.wgpf_settings"])
+        ])
+
+
+def test_wgpf_run_check_wdae_directory(
+        wgpf_fixture, wdae_django_setup, mocker):
+    # Given
+    with wdae_django_setup(
+            wgpf_fixture,
+            "tests.integration.test_wgpf_command.wgpf_settings"):
+
+        (wgpf_fixture.instance_dir / ".wgpf_init.flag").touch()
+        mocker.patch(
+            "wdae.wgpf.execute_from_command_line",
+            return_value=None)
+
+        # When
+        wgpf.cli(["wgpf", "run"])
+
+        # Then
+        assert (wgpf_fixture.instance_dir / "wdae").exists()