about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--gn3/api/menu.py14
-rw-r--r--gn3/app.py2
-rw-r--r--gn3/db/menu.py229
-rw-r--r--tests/unit/db/test_gen_menu.py442
4 files changed, 687 insertions, 0 deletions
diff --git a/gn3/api/menu.py b/gn3/api/menu.py
new file mode 100644
index 0000000..cc77ab8
--- /dev/null
+++ b/gn3/api/menu.py
@@ -0,0 +1,14 @@
+"""API for data used to generate menus"""
+
+from flask import jsonify, Blueprint
+
+from gn3.db.menu import gen_dropdown_json
+from gn3.db_utils import database_connector
+
+menu = Blueprint("menu", __name__)
+
+@menu.route("/generate/json")
+def generate_json():
+    """Get the menu in the JSON format"""
+    with database_connector() as conn:
+        return jsonify(gen_dropdown_json(conn))
diff --git a/gn3/app.py b/gn3/app.py
index 790e87c..8a44983 100644
--- a/gn3/app.py
+++ b/gn3/app.py
@@ -16,6 +16,7 @@ from gn3.api.data_entry import data_entry
 from gn3.api.wgcna import wgcna
 from gn3.api.ctl import ctl
 from gn3.api.async_commands import async_commands
+from gn3.api.menu import menu
 
 def create_app(config: Union[Dict, str, None] = None) -> Flask:
     """Create a new flask object"""
@@ -49,4 +50,5 @@ def create_app(config: Union[Dict, str, None] = None) -> Flask:
     app.register_blueprint(wgcna, url_prefix="/api/wgcna")
     app.register_blueprint(ctl, url_prefix="/api/ctl")
     app.register_blueprint(async_commands, url_prefix="/api/async_commands")
+    app.register_blueprint(menu, url_prefix="/api/menu")
     return app
diff --git a/gn3/db/menu.py b/gn3/db/menu.py
new file mode 100644
index 0000000..8dccabf
--- /dev/null
+++ b/gn3/db/menu.py
@@ -0,0 +1,229 @@
+"""Menu generation code for the data in the dropdowns in the index page."""
+
+from typing import Tuple
+from functools import reduce
+
+from gn3.db.species import get_all_species
+
+def gen_dropdown_json(conn):
+    """
+    Generates and outputs (as json file) the data for the main dropdown menus on
+    the home page.
+    """
+    species = get_all_species(conn)
+    groups = get_groups(conn, tuple(row[0] for row in species))
+    types = get_types(conn, groups)
+    datasets = get_datasets(conn, types)
+    return dict(species=species,
+                groups=groups,
+                types=types,
+                datasets=datasets)
+
+def get_groups(conn, species_names: Tuple[str, ...]):
+    """Build groups list"""
+    with conn.cursor() as cursor:
+        query = (
+            "SELECT InbredSet.Name, InbredSet.FullName, "
+            "IFNULL(InbredSet.Family, 'None'), "
+            "Species.Name AS species_name "
+            "FROM Species "
+            "INNER JOIN InbredSet ON InbredSet.SpeciesId = Species.Id "
+            "WHERE Species.Name IN "
+            f"({', '.join(['%s']*len(species_names))}) "
+            "GROUP BY InbredSet.Name "
+            "ORDER BY IFNULL(InbredSet.FamilyOrder, InbredSet.FullName) ASC, "
+            "IFNULL(InbredSet.Family, InbredSet.FullName) ASC, "
+            "InbredSet.FullName ASC, "
+            "InbredSet.MenuOrderId ASC")
+        cursor.execute(query, tuple(species_names))
+        results = cursor.fetchall()
+
+    def __organise_by_species(acc, row):
+        family_name = f"Family:{str(row[2])}"
+        species_name = row[3]
+        key_exists = bool(acc.get(species_name, False))
+        if not key_exists:
+            return {
+                **acc,
+                species_name: [[str(row[0]), str(row[1]), family_name],]
+            }
+
+        return {
+            **acc,
+            species_name: acc[species_name] + [
+                [str(row[0]), str(row[1]), family_name],]
+        }
+
+    return reduce(__organise_by_species, results, {})
+
+def get_types(conn, groups):
+    """Build types list"""
+    types = {}
+
+    for species, group_dict in list(groups.items()):
+        types[species] = {}
+        for group_name, _group_full_name, _family_name in group_dict:
+            if phenotypes_exist(conn, group_name):
+                types[species][group_name] = [
+                    ("Phenotypes", "Traits and Cofactors", "Phenotypes")]
+            if genotypes_exist(conn, group_name):
+                if group_name in types[species]:
+                    types[species][group_name] += [
+                        ("Genotypes", "DNA Markers and SNPs", "Genotypes")]
+                else:
+                    types[species][group_name] = [
+                        ("Genotypes", "DNA Markers and SNPs", "Genotypes")]
+            if group_name in types[species]:
+                types_list = build_types(conn, species, group_name)
+                if len(types_list) > 0:
+                    types[species][group_name] += types_list
+            else:
+                types_list = build_types(conn, species, group_name)
+                if len(types_list) > 0:
+                    types[species][group_name] = types_list
+                else:
+                    types[species].pop(group_name, None)
+                    groups[species] = list(
+                        group for group in groups[species]
+                        if group[0] != group_name)
+    return types
+
+def phenotypes_exist(conn, group_name):
+    "Check whether phenotypes exist for the given group"
+    with conn.cursor() as cursor:
+        cursor.execute(
+            ("SELECT Name FROM PublishFreeze "
+             "WHERE PublishFreeze.Name = %s"),
+            (group_name + "Publish",))
+        results = cursor.fetchone()
+        return bool(results)
+
+def genotypes_exist(conn, group_name):
+    "Check whether genotypes exist for the given group"
+    with conn.cursor() as cursor:
+        cursor.execute(
+            ("SELECT Name FROM GenoFreeze " +
+             "WHERE GenoFreeze.Name = %s"),
+            (group_name + "Geno",))
+        results = cursor.fetchone()
+        return bool(results)
+
+def build_types(conn, species, group):
+    """Fetches tissues
+
+    Gets the tissues with data for this species/group
+    (all types except phenotype/genotype are tissues)
+    """
+    query = (
+        "SELECT DISTINCT Tissue.Name "
+        "FROM ProbeFreeze, ProbeSetFreeze, InbredSet, "
+        "Tissue, Species WHERE Species.Name = %s "
+        "AND Species.Id = InbredSet.SpeciesId AND "
+        "InbredSet.Name = %s AND ProbeFreeze.TissueId = "
+        "Tissue.Id AND ProbeFreeze.InbredSetId = InbredSet.Id "
+        "AND ProbeSetFreeze.ProbeFreezeId = ProbeFreeze.Id "
+        "ORDER BY Tissue.Name")
+    results = []
+    with conn.cursor() as cursor:
+        cursor.execute(query, (species, group))
+        for result in cursor.fetchall():
+            if bool(result):
+                these_datasets = build_datasets(conn, species, group, result[0])
+                if len(these_datasets) > 0:
+                    results.append([
+                        str(result[0]), str(result[0]), "Molecular Traits"])
+
+    return results
+
+def get_datasets(conn, types):
+    """Build datasets list"""
+    datasets = {}
+    for species, group_dict in list(types.items()):
+        datasets[species] = {}
+        for group, type_list in list(group_dict.items()):
+            datasets[species][group] = {}
+            for type_name in type_list:
+                these_datasets = build_datasets(
+                    conn, species, group, type_name[0])
+                if bool(these_datasets):
+                    datasets[species][group][type_name[0]] = these_datasets
+
+    return datasets
+
+def build_datasets(conn, species, group, type_name):
+    """Gets dataset names from database"""
+    dataset_text = dataset_value = None
+    datasets = []
+    with conn.cursor() as cursor:
+        if type_name == "Phenotypes":
+            cursor.execute(
+                ("SELECT InfoFiles.GN_AccesionId, PublishFreeze.Name, "
+                 "PublishFreeze.FullName FROM InfoFiles, PublishFreeze, "
+                 "InbredSet WHERE InbredSet.Name = %s AND "
+                 "PublishFreeze.InbredSetId = InbredSet.Id AND "
+                 "InfoFiles.InfoPageName = PublishFreeze.Name "
+                 "ORDER BY PublishFreeze.CreateTime ASC"), (group,))
+            results = cursor.fetchall()
+            if bool(results):
+                for result in results:
+                    dataset_id = str(result[0])
+                    dataset_value = str(result[1])
+                    dataset_text = str(result[2])
+                    if group == 'MDP':
+                        dataset_text = "Mouse Phenome Database"
+
+                    datasets.append([dataset_id, dataset_value, dataset_text])
+            else:
+                cursor.execute(
+                    ("SELECT PublishFreeze.Name, PublishFreeze.FullName "
+                     "FROM PublishFreeze, InbredSet "
+                     "WHERE InbredSet.Name = %s AND "
+                     "PublishFreeze.InbredSetId = InbredSet.Id "
+                     "ORDER BY PublishFreeze.CreateTime ASC"), (group,))
+                result = cursor.fetchone()
+                dataset_id = "None"
+                dataset_value = str(result[0])
+                dataset_text = str(result[1])
+                datasets.append([dataset_id, dataset_value, dataset_text])
+
+        elif type_name == "Genotypes":
+            cursor.execute(
+                ("SELECT InfoFiles.GN_AccesionId "
+                 "FROM InfoFiles, GenoFreeze, InbredSet "
+                 "WHERE InbredSet.Name = %s AND "
+                 "GenoFreeze.InbredSetId = InbredSet.Id AND "
+                 "InfoFiles.InfoPageName = GenoFreeze.ShortName "
+                 "ORDER BY GenoFreeze.CreateTime "
+                 "DESC"), (group,))
+            results = cursor.fetchone()
+            dataset_id = "None"
+            if bool(results):
+                dataset_id = str(results[0])
+
+            dataset_value = f"{group}Geno"
+            dataset_text = f"{group} Genotypes"
+            datasets.append([dataset_id, dataset_value, dataset_text])
+
+        else:  # for mRNA expression/ProbeSet
+            cursor.execute(
+                ("SELECT ProbeSetFreeze.Id, ProbeSetFreeze.Name, "
+                 "ProbeSetFreeze.FullName FROM ProbeSetFreeze, "
+                 "ProbeFreeze, InbredSet, Tissue, Species WHERE "
+                 "Species.Name = %s AND Species.Id = "
+                 "InbredSet.SpeciesId AND InbredSet.Name = %s "
+                 "AND ProbeSetFreeze.ProbeFreezeId = ProbeFreeze.Id "
+                 "AND Tissue.Name = %s AND ProbeFreeze.TissueId = "
+                 "Tissue.Id AND ProbeFreeze.InbredSetId = InbredSet.Id "
+                 "AND ProbeSetFreeze.public > 0 "
+                 "ORDER BY -ProbeSetFreeze.OrderList DESC, "
+                 "ProbeSetFreeze.CreateTime "
+                 "DESC"), (species, group, type_name))
+            results = cursor.fetchall()
+            datasets = []
+            for dataset_info in results:
+                this_dataset_info = []
+                for info in dataset_info:
+                    this_dataset_info.append(str(info))
+                datasets.append(this_dataset_info)
+
+    return datasets
diff --git a/tests/unit/db/test_gen_menu.py b/tests/unit/db/test_gen_menu.py
new file mode 100644
index 0000000..e6b5711
--- /dev/null
+++ b/tests/unit/db/test_gen_menu.py
@@ -0,0 +1,442 @@
+"""Test cases for gn3.db.menu"""
+import unittest
+from unittest import mock
+
+import pytest
+
+from gn3.db.menu import gen_dropdown_json
+from gn3.db.menu import get_groups
+from gn3.db.menu import get_types
+from gn3.db.menu import get_datasets
+from gn3.db.menu import phenotypes_exist
+from gn3.db.menu import genotypes_exist
+from gn3.db.menu import build_datasets
+from gn3.db.menu import build_types
+
+
+class TestGenMenu(unittest.TestCase):
+    """Tests for the gen_menu module"""
+
+    def setUp(self):
+        self.test_group = {
+            'mouse': [
+                ['H_T1',
+                 'H_T',
+                 'Family:DescriptionA'
+                 ],
+                ['H_T2', "H_T'", 'Family:None']
+            ],
+            'human': [
+                ['BXD', 'BXD', 'Family:None'],
+                ['HLC', 'Liver: Normal Gene Expression with Genotypes (Merck)',
+                 'Family:Test']
+            ]
+        }
+
+        self.test_type = {
+            'mouse': {
+                'H_T2': [('Phenotypes',
+                          'Traits and Cofactors',
+                          'Phenotypes'),
+                         ('Genotypes',
+                          'DNA Markers and SNPs',
+                          'Genotypes'),
+                         ['M', 'M', 'Molecular Trait Datasets']],
+                'H_T1': [('Phenotypes',
+                          'Traits and Cofactors',
+                          'Phenotypes'),
+                         ('Genotypes',
+                          'DNA Markers and SNPs',
+                          'Genotypes'),
+                         ['M', 'M', 'Molecular Trait Datasets']]
+            },
+            'human': {
+                'HLC': [('Phenotypes',
+                         'Traits and Cofactors',
+                         'Phenotypes'),
+                        ('Genotypes',
+                         'DNA Markers and SNPs',
+                         'Genotypes'),
+                        ['M', 'M', 'Molecular Trait Datasets']],
+                'BXD': [('Phenotypes',
+                         'Traits and Cofactors',
+                         'Phenotypes'),
+                        ('Genotypes',
+                         'DNA Markers and SNPs',
+                         'Genotypes'),
+                        ['M', 'M', 'Molecular Trait Datasets']]
+            }
+        }
+
+    @pytest.mark.unit_test
+    def test_get_groups(self):
+        """Test that species groups are grouped correctly"""
+        db_mock = mock.MagicMock()
+        with db_mock.cursor() as cursor:
+            cursor.fetchall.side_effect = [(
+                # Human
+                ('BXD', 'BXD', None, "human"),
+                (
+                    'HLC',
+                    ('Liver: Normal Gene Expression with Genotypes (Merck)'),
+                    'Test',
+                    "human"),
+                # Mouse
+                ('H_T1', "H_T", "DescriptionA", "mouse"),
+                ('H_T2', "H_T'", None, "mouse")
+            )]
+
+            self.assertEqual(
+                get_groups(db_mock, ["human", "mouse"]), self.test_group)
+
+            cursor.execute.assert_called_once_with(
+                ("SELECT InbredSet.Name, InbredSet.FullName, "
+                 "IFNULL(InbredSet.Family, 'None'), Species.Name AS "
+                 "species_name FROM Species INNER JOIN InbredSet "
+                 "ON InbredSet.SpeciesId = Species.Id "
+                 "WHERE "
+                 "Species.Name IN (%s, %s) "
+                 "GROUP BY "
+                 "InbredSet.Name ORDER BY IFNULL(InbredSet.FamilyOrder, "
+                 "InbredSet.FullName) ASC, IFNULL(InbredSet.Family, "
+                 "InbredSet.FullName) ASC, InbredSet.FullName ASC, "
+                 "InbredSet.MenuOrderId ASC"),
+                ("human", "mouse"))
+
+    @pytest.mark.unit_test
+    def test_phenotypes_exist_with_falsy_values(self):
+        """Test that phenotype check returns correctly when given
+        a None value"""
+        db_mock = mock.MagicMock()
+        with db_mock.cursor() as cursor:
+            for item in [None, False, (), [], ""]:
+                cursor.fetchone.return_value = item
+            self.assertFalse(phenotypes_exist(db_mock, "test"))
+
+    @pytest.mark.unit_test
+    def test_phenotypes_exist_with_truthy_value(self):
+        """Test that phenotype check returns correctly when given Truthy"""
+        db_mock = mock.MagicMock()
+        with db_mock.cursor() as conn:
+            with conn.cursor() as cursor:
+                for item in ["x", ("result"), ["result"], [1]]:
+                    cursor.fetchone.return_value = (item)
+                self.assertTrue(phenotypes_exist(db_mock, "test"))
+
+    @pytest.mark.unit_test
+    def test_genotypes_exist_with_falsy_values(self):
+        """Test that genotype check returns correctly when given a None value
+
+        """
+        db_mock = mock.MagicMock()
+        with db_mock.cursor() as cursor:
+            for item in [None, False, (), [], ""]:
+                cursor.fetchone.return_value = item
+                self.assertFalse(genotypes_exist(db_mock, "test"))
+
+    @pytest.mark.unit_test
+    def test_genotypes_exist_with_truthy_value(self):
+        """Test that genotype check returns correctly when given Truthy """
+        db_mock = mock.MagicMock()
+        with db_mock.cursor() as cursor:
+            for item in ["x", ("result"), ["result"], [1]]:
+                cursor.fetchone.return_value = (item)
+                self.assertTrue(phenotypes_exist(db_mock, "test"))
+
+    @pytest.mark.unit_test
+    def test_build_datasets_with_type_phenotypes(self):
+        """Test that correct dataset is returned for a phenotype type"""
+        db_mock = mock.MagicMock()
+        with db_mock.cursor() as cursor:
+            cursor.fetchall.return_value = (
+                (602, "BXDPublish", "BXD Published Phenotypes"),
+            )
+            self.assertEqual(build_datasets(db_mock, "Mouse", "BXD",
+                                            "Phenotypes"),
+                             [['602', "BXDPublish",
+                               "BXD Published Phenotypes"]])
+            cursor.execute.assert_called_with((
+                "SELECT InfoFiles.GN_AccesionId, PublishFreeze.Name, "
+                "PublishFreeze.FullName FROM InfoFiles, PublishFreeze, "
+                "InbredSet WHERE InbredSet.Name = %s AND "
+                "PublishFreeze.InbredSetId = InbredSet.Id AND "
+                "InfoFiles.InfoPageName = PublishFreeze.Name "
+                "ORDER BY PublishFreeze.CreateTime ASC"
+            ), ("BXD",))
+            self.assertEqual(
+                build_datasets(db_mock, "Mouse", "MDP","Phenotypes"),
+                [['602', "BXDPublish", "Mouse Phenome Database"]])
+
+            cursor.fetchall.return_value = ()
+            cursor.fetchone.return_value = (
+                "BXDPublish", "Mouse Phenome Database"
+            )
+            self.assertEqual(
+                build_datasets(db_mock, "Mouse", "MDP", "Phenotypes"),
+                [["None", "BXDPublish", "Mouse Phenome Database"]])
+
+    @pytest.mark.unit_test
+    def test_build_datasets_with_type_phenotypes_and_no_results(self):
+        """Test that correct dataset is returned for a phenotype type with no
+        results
+
+        """
+        db_mock = mock.MagicMock()
+        with db_mock.cursor() as cursor:
+            cursor.fetchall.return_value = None
+            cursor.fetchone.return_value = (121,
+                                            "text value")
+            self.assertEqual(
+                build_datasets(db_mock, "Mouse", "BXD", "Phenotypes"),
+                [["None", "121", "text value"]])
+            cursor.execute.assert_called_with((
+                "SELECT PublishFreeze.Name, PublishFreeze.FullName "
+                "FROM PublishFreeze, InbredSet "
+                "WHERE InbredSet.Name = %s AND "
+                "PublishFreeze.InbredSetId = InbredSet.Id "
+                "ORDER BY PublishFreeze.CreateTime ASC"
+            ), ("BXD",))
+
+    @pytest.mark.unit_test
+    def test_build_datasets_with_type_genotypes(self):
+        """Test that correct dataset is returned for a phenotype type"""
+        db_mock = mock.MagicMock()
+        with db_mock.cursor() as cursor:
+            cursor.fetchone.return_value = (
+                635, "HLCPublish", "HLC Published Genotypes"
+            )
+            self.assertEqual(
+                build_datasets(db_mock, "Mouse", "HLC", "Genotypes"),
+                [["635", "HLCGeno", "HLC Genotypes"]])
+            cursor.execute.assert_called_with((
+                "SELECT InfoFiles.GN_AccesionId FROM InfoFiles, "
+                "GenoFreeze, InbredSet WHERE InbredSet.Name = %s AND "
+                "GenoFreeze.InbredSetId = InbredSet.Id AND "
+                "InfoFiles.InfoPageName = GenoFreeze.ShortName "
+                "ORDER BY GenoFreeze.CreateTime DESC"
+            ), ("HLC",))
+            cursor.fetchone.return_value = ()
+            self.assertEqual(
+                build_datasets(db_mock, "Mouse", "HLC", "Genotypes"),
+                [["None", "HLCGeno", "HLC Genotypes"]])
+
+    @pytest.mark.unit_test
+    def test_build_datasets_with_type_mrna(self):
+        """Test that correct dataset is returned for a mRNA
+        expression/ Probeset"""
+        db_mock = mock.MagicMock()
+        with db_mock.cursor() as cursor:
+            cursor.fetchall.return_value = (
+                (112, "HC_M2_0606_P",
+                 "Hippocampus Consortium M430v2 (Jun06) PDNN"), )
+            self.assertEqual(
+                build_datasets(db_mock, "Mouse", "HLC", "mRNA"),
+                [["112", 'HC_M2_0606_P',
+                  "Hippocampus Consortium M430v2 (Jun06) PDNN"]])
+            cursor.execute.assert_called_once_with((
+                "SELECT ProbeSetFreeze.Id, ProbeSetFreeze.Name, "
+                "ProbeSetFreeze.FullName FROM ProbeSetFreeze, "
+                "ProbeFreeze, InbredSet, Tissue, Species WHERE "
+                "Species.Name = %s AND Species.Id = "
+                "InbredSet.SpeciesId AND InbredSet.Name = %s AND "
+                "ProbeSetFreeze.ProbeFreezeId = ProbeFreeze.Id AND "
+                "Tissue.Name = %s AND ProbeFreeze.TissueId = "
+                "Tissue.Id AND ProbeFreeze.InbredSetId = InbredSet.Id AND "
+                "ProbeSetFreeze.public > 0 "
+                "ORDER BY -ProbeSetFreeze.OrderList DESC, "
+                "ProbeSetFreeze.CreateTime DESC"), ("Mouse", "HLC", "mRNA"))
+
+    @pytest.mark.unit_test
+    @mock.patch('gn3.db.menu.build_datasets')
+    def test_build_types(self, datasets_mock):
+        """Test that correct tissue metadata is returned"""
+        db_mock = mock.MagicMock()
+        datasets_mock.return_value = [
+            ["112", 'HC_M2_0606_P',
+                "Hippocampus Consortium M430v2 (Jun06) PDNN"]
+        ]
+        with db_mock.cursor() as cursor:
+            cursor.fetchall.return_value = (
+                ('Mouse Tissue'), ('Human Tissue'), ('Rat Tissue')
+            )
+            self.assertEqual(
+                build_types(db_mock, 'mouse', 'random group'),
+                [['M', 'M', 'Molecular Traits'],
+                 ['H', 'H', 'Molecular Traits'],
+                 ['R', 'R', 'Molecular Traits']])
+            cursor.execute.assert_called_once_with((
+                "SELECT DISTINCT Tissue.Name "
+                "FROM ProbeFreeze, ProbeSetFreeze, InbredSet, "
+                "Tissue, Species WHERE Species.Name = %s "
+                "AND Species.Id = InbredSet.SpeciesId AND "
+                "InbredSet.Name = %s AND "
+                "ProbeFreeze.TissueId = Tissue.Id AND "
+                "ProbeFreeze.InbredSetId = InbredSet.Id AND "
+                "ProbeSetFreeze.ProbeFreezeId = ProbeFreeze.Id "
+                "ORDER BY Tissue.Name"
+            ), ("mouse", "random group"))
+
+    @pytest.mark.unit_test
+    @mock.patch('gn3.db.menu.build_types')
+    @mock.patch('gn3.db.menu.genotypes_exist')
+    @mock.patch('gn3.db.menu.phenotypes_exist')
+    def test_get_types_with_existing_genotype_and_phenotypes(
+            self,
+            phenotypes_exist_mock,
+            genotypes_exist_mock,
+            build_types_mock):
+        """Test that build types are constructed correctly if phenotypes and genotypes
+        exist
+
+        """
+        phenotypes_exist_mock.return_value = True
+        genotypes_exist_mock.return_value = True
+
+        expected_result = self.test_type
+
+        build_types_mock.return_value = [
+            ['M', 'M', 'Molecular Trait Datasets']
+        ]
+        self.assertEqual(
+            get_types(mock.MagicMock(), self.test_group,), expected_result)
+
+    @pytest.mark.unit_test
+    @mock.patch('gn3.db.menu.build_types')
+    @mock.patch('gn3.db.menu.genotypes_exist')
+    @mock.patch('gn3.db.menu.phenotypes_exist')
+    def test_get_types_with_buildtype_and_non_existent_genotype_and_phenotypes(
+            self,
+            phenotypes_exist_mock,
+            genotypes_exist_mock,
+            build_types_mock):
+        """Test that build types are constructed correctly if phenotypes_exist and
+        genotypes_exist are false but build_type is falsy
+
+        """
+        phenotypes_exist_mock.return_value = False
+        genotypes_exist_mock.return_value = False
+
+        build_types_mock.return_value = []
+        self.assertEqual(get_types(mock.MagicMock(), self.test_group),
+                         {'mouse': {}, 'human': {}})
+
+    @pytest.mark.unit_test
+    @mock.patch('gn3.db.menu.build_types')
+    @mock.patch('gn3.db.menu.genotypes_exist')
+    @mock.patch('gn3.db.menu.phenotypes_exist')
+    def test_get_types_with_non_existent_genotype_phenotypes_and_buildtype(
+            self,
+            phenotypes_exist_mock,
+            genotypes_exist_mock,
+            build_types_mock):
+        """Test that build types are constructed correctly if phenotypes_exist,
+        genotypes_exist and build_types are truthy
+
+        """
+        phenotypes_exist_mock.return_value = False
+        genotypes_exist_mock.return_value = False
+
+        build_types_mock.return_value = [
+            ['M', 'M', 'Molecular Trait Datasets']
+        ]
+        expected_result = {
+            'mouse': {
+                'H_T2': [['M', 'M', 'Molecular Trait Datasets']],
+                'H_T1': [['M', 'M', 'Molecular Trait Datasets']]},
+            'human': {
+                'HLC': [['M', 'M', 'Molecular Trait Datasets']],
+                'BXD': [['M', 'M', 'Molecular Trait Datasets']]}}
+        self.assertEqual(get_types(mock.MagicMock(), self.test_group),
+                         expected_result)
+
+    @pytest.mark.unit_test
+    @mock.patch('gn3.db.menu.build_datasets')
+    def test_get_datasets_with_existent_datasets(
+            self, build_datasets_mock):
+        """Test correct dataset is returned with existent build_datasets"""
+        build_datasets_mock.return_value = "Test"
+        expected_result = {
+            'mouse': {
+                'H_T2': {'Genotypes': 'Test',
+                         'M': 'Test',
+                         'Phenotypes': 'Test'},
+                'H_T1': {'Genotypes': 'Test',
+                         'M': 'Test',
+                         'Phenotypes': 'Test'}},
+            'human': {'HLC': {'Genotypes': 'Test',
+                              'M': 'Test',
+                              'Phenotypes': 'Test'},
+                      'BXD': {'Genotypes': 'Test',
+                              'M': 'Test',
+                              'Phenotypes': 'Test'}}}
+        self.assertEqual(
+            get_datasets(mock.MagicMock(), self.test_type), expected_result)
+
+    @pytest.mark.unit_test
+    @mock.patch('gn3.db.menu.build_datasets')
+    def test_get_datasets_with_non_existent_datasets(self,
+                                                     build_datasets_mock):
+        """Test correct dataset is returned with non-existent build_datasets"""
+        build_datasets_mock.return_value = None
+        expected_result = {
+            'mouse': {
+                'H_T2': {},
+                'H_T1': {}},
+            'human': {'HLC': {},
+                      'BXD': {}}}
+        self.assertEqual(
+            get_datasets(mock.MagicMock(), self.test_type), expected_result)
+
+    @pytest.mark.unit_test
+    @mock.patch('gn3.db.menu.get_datasets')
+    @mock.patch('gn3.db.menu.get_types')
+    @mock.patch('gn3.db.menu.get_groups')
+    @mock.patch('gn3.db.menu.get_all_species')
+    def test_gen_dropdown_json(self,
+                               species_mock,
+                               groups_mock,
+                               types_mock,
+                               datasets_mock):
+        "Test that the correct dictionary is constructed properly"
+        species_mock.return_value = ("speciesA speciesB speciesC speciesD"
+                                     .split(" "))
+        datasets_mock.return_value = ("datasetA datasetB datasetC datasetD"
+                                      .split(" "))
+        groups_mock.return_value = ("groupA groupB groupC groupD"
+                                    .split(" "))
+        types_mock.return_value = ("typeA typeB typeC typeD"
+                                   .split(" "))
+        datasets_mock.return_value = ("datasetA datasetB datasetC datasetD"
+                                      .split(" "))
+
+        expected_result = {
+            'datasets': ['datasetA', 'datasetB', 'datasetC', 'datasetD'],
+            'types': ['typeA', 'typeB', 'typeC', 'typeD'],
+            'groups': ['groupA', 'groupB', 'groupC', 'groupD'],
+            'species': ['speciesA', 'speciesB', 'speciesC', 'speciesD']}
+
+        self.assertEqual(gen_dropdown_json(mock.MagicMock()), expected_result)
+
+@pytest.mark.unit_test
+def test_phenotypes_exist_called_with_correct_query():
+    """Test that phenotypes_exist is called with the correct query"""
+    db_mock = mock.MagicMock()
+    with db_mock.cursor() as cursor:
+        cursor.fetchone.return_value = None
+        phenotypes_exist(db_mock, "test")
+        cursor.execute.assert_called_with((
+            "SELECT Name FROM PublishFreeze "
+            "WHERE PublishFreeze.Name = %s"
+        ), ("testPublish",))
+
+@pytest.mark.unit_test
+def test_genotypes_exist_called_with_correct_query():
+    """Test that genotypes_exist is called with the correct query"""
+    db_mock = mock.MagicMock()
+    with db_mock.cursor() as cursor:
+        cursor.fetchone.return_value = None
+        genotypes_exist(db_mock, "test")
+        cursor.execute.assert_called_with((
+            "SELECT Name FROM GenoFreeze WHERE "
+            "GenoFreeze.Name = %s"
+        ), ("testGeno",))