diff --git a/.github/workflows/bar-api.yml b/.github/workflows/bar-api.yml index ca61222b..abd298e8 100644 --- a/.github/workflows/bar-api.yml +++ b/.github/workflows/bar-api.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-22.04 strategy: matrix: - python-version: [3.8, 3.9, 3.10.11, 3.11] + python-version: [3.8, 3.9, 3.10.13, 3.11, 3.12] services: redis: diff --git a/Dockerfile b/Dockerfile index 5b3b310c..5aea5065 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.11.3-bullseye +FROM python:3.12-bookworm WORKDIR /usr/src/app diff --git a/api/__init__.py b/api/__init__.py index 2d039103..ac8ed22f 100644 --- a/api/__init__.py +++ b/api/__init__.py @@ -61,6 +61,7 @@ def create_app(): from api.resources.interactions import itrns from api.resources.gene_localizations import loc from api.resources.efp_image import efp_image + from api.resources.fastpheno import fastpheno bar_api.add_namespace(gene_information) bar_api.add_namespace(rnaseq_gene_expression) @@ -72,6 +73,7 @@ def create_app(): bar_api.add_namespace(itrns) bar_api.add_namespace(loc) bar_api.add_namespace(efp_image) + bar_api.add_namespace(fastpheno) bar_api.init_app(bar_app) return bar_app diff --git a/api/models/fastpheno.py b/api/models/fastpheno.py new file mode 100644 index 00000000..abaa1a3a --- /dev/null +++ b/api/models/fastpheno.py @@ -0,0 +1,65 @@ +from api import db +import enum +from sqlalchemy.dialects.mysql import DECIMAL, ENUM +from sqlalchemy.orm import relationship +from sqlalchemy import ForeignKey +from typing import List + + +class Sites(db.Model): + __bind_key__ = "fastpheno" + __tablename__ = "sites" + + sites_pk: db.Mapped[int] = db.mapped_column(db.Integer, nullable=False, primary_key=True) + site_name: db.Mapped[str] = db.mapped_column(db.String(45), nullable=False) + site_desc: db.Mapped[str] = db.mapped_column(db.String(99), nullable=True) + children: db.Mapped[List["Trees"]] = relationship() + + +class Trees(db.Model): + __bind_key__ = "fastpheno" + __tablename__ = "trees" + + trees_pk: db.Mapped[int] = db.mapped_column(db.Integer, nullable=False, primary_key=True) + sites_pk: db.Mapped[int] = db.mapped_column(ForeignKey("sites.sites_pk")) + longitude: db.Mapped[float] = db.mapped_column(db.Float, nullable=False) + latitude: db.Mapped[float] = db.mapped_column(db.Float, nullable=False) + genotype_id: db.Mapped[str] = db.mapped_column(db.String(5), nullable=True) + external_link: db.Mapped[str] = db.mapped_column(db.String(200), nullable=True) + tree_given_id: db.Mapped[str] = db.mapped_column(db.String(25), nullable=True) + children: db.Mapped[List["Band"]] = relationship() + + +class MonthChoices(enum.Enum): + jan = "1" + feb = "2" + mar = "3" + apr = "4" + may = "5" + jun = "6" + jul = "7" + aug = "8" + sep = "9" + oct = "10" + nov = "11" + dec = "12" + + +class Band(db.Model): + __bind_key__ = "fastpheno" + __tablename__ = "band" + + trees_pk: db.Mapped[int] = db.mapped_column(ForeignKey("trees.trees_pk"), primary_key=True) + month: db.Mapped[str] = db.mapped_column(ENUM(MonthChoices), nullable=False, primary_key=True) + band: db.Mapped[float] = db.mapped_column(db.String(100), nullable=False, primary_key=True) + value: db.Mapped[float] = db.mapped_column(DECIMAL(20, 15), nullable=False) + + +class Height(db.Model): + __bind_key__ = "fastpheno" + __tablename__ = "height" + + trees_pk: db.Mapped[int] = db.mapped_column(ForeignKey("trees.trees_pk"), primary_key=True) + month: db.Mapped[str] = db.mapped_column(ENUM(MonthChoices), nullable=False, primary_key=True) + tree_height_proxy: db.Mapped[float] = db.mapped_column(DECIMAL(20, 15), nullable=False) + ground_height_proxy: db.Mapped[float] = db.mapped_column(DECIMAL(20, 15), nullable=False) diff --git a/api/resources/fastpheno.py b/api/resources/fastpheno.py new file mode 100644 index 00000000..81701f50 --- /dev/null +++ b/api/resources/fastpheno.py @@ -0,0 +1,93 @@ +""" +Date: Aug 2023 +Author: Vince L +Fastpheno endpoint for retrieving tree data +""" +from flask_restx import Namespace, Resource +from api import db +from api.models.fastpheno import Sites, Trees, Band, Height +from api.utils.bar_utils import BARUtils +from markupsafe import escape + + +fastpheno = Namespace("FastPheno", description="FastPheno API service", path="/fastpheno") + + +@fastpheno.route("/get_bands///") +class FastPheno(Resource): + @fastpheno.param("site", _in="path", default="pintendre") + @fastpheno.param("month", _in="path", default="jan") + @fastpheno.param("band", _in="path", default="band_1") + def get(self, site, month, band): + """This end point returns band values for a specific site AND month-date""" + # Escape input data + site = escape(site).capitalize() + month = escape(month) + band = escape(band) + + rows = db.session.execute( + db.select(Sites, Trees, Height, Band) + .select_from(Sites) + .join(Trees, Trees.sites_pk == Sites.sites_pk) # don't need to use 2nd arg, for clarity... I set ORM rel + .join(Height, Height.trees_pk == Trees.trees_pk) + .join(Band, Band.trees_pk == Trees.trees_pk) + .where(Sites.site_name == site, Band.month == month, Height.month == month, Band.band == band) + ).all() + res = [ + { + "site_name": s.site_name, + "tree_id": t.trees_pk, + "longitude": t.longitude, + "latitutde": t.latitude, + "genotype_id": t.genotype_id, + "tree_given_id": t.tree_given_id, + "external_link": t.external_link, + "band_value": float(b.value), + "tree_height_proxy": float(h.tree_height_proxy), + "ground_height_proxy": float(h.ground_height_proxy), + "band_month": b.month.name, + } + for s, t, h, b in rows + ] + if len(rows) == 0: + return ( + BARUtils.error_exit("There are no data found for the given parameters"), + 400, + ) + + return BARUtils.success_exit(res) + + +@fastpheno.route("/get_trees/") +class FastPhenoTrees(Resource): + @fastpheno.param("genotype_id", _in="path", default="C") + def get(self, genotype_id): + """This end point returns trees for a given genotype_id across sites""" + # Escape input data + genotype_id = escape(genotype_id).capitalize() + + rows = db.session.execute( + db.select(Sites, Trees) + .select_from(Sites) + .join(Trees, Trees.sites_pk == Sites.sites_pk) + .where(Trees.genotype_id == genotype_id) + ).all() + res = [ + { + "site_name": s.site_name, + "tree_id": t.trees_pk, + "longitude": t.longitude, + "latitutde": t.latitude, + "genotype_id": t.genotype_id, + "tree_given_id": t.tree_given_id, + "external_link": t.external_link, + } + for s, t in rows + ] + if len(rows) == 0: + return ( + BARUtils.error_exit("There are no data found for the given parameters"), + 400, + ) + + return BARUtils.success_exit(res) diff --git a/config/BAR_API.cfg b/config/BAR_API.cfg index be3be4aa..2ad4b35f 100644 --- a/config/BAR_API.cfg +++ b/config/BAR_API.cfg @@ -21,6 +21,7 @@ SQLALCHEMY_BINDS = { 'eplant_rice' : 'mysql://root:root@localhost/eplant_rice', 'eplant_soybean' : 'mysql://root:root@localhost/eplant_soybean', 'eplant_tomato' : 'mysql://root:root@localhost/eplant_tomato', + 'fastpheno' : 'mysql://root:root@localhost/fastpheno', 'germination': 'mysql://root:root@localhost/germination', 'kalanchoe': 'mysql://root:root@localhost/kalanchoe', 'klepikova': 'mysql://root:root@localhost/klepikova', diff --git a/config/databases/fastpheno.sql b/config/databases/fastpheno.sql new file mode 100644 index 00000000..7f19979e --- /dev/null +++ b/config/databases/fastpheno.sql @@ -0,0 +1,153 @@ +-- MySQL dump 10.13 Distrib 8.0.33, for Linux (x86_64) +-- +-- Host: localhost Database: cannabis +-- ------------------------------------------------------ +-- Server version 8.0.33 + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!50503 SET NAMES utf8mb4 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Current Database: `fastpheno` +-- + +DROP DATABASE IF EXISTS `fastpheno` ; + +CREATE DATABASE /*!32312 IF NOT EXISTS*/ `fastpheno` /*!40100 DEFAULT CHARACTER SET latin1 */ /*!80016 DEFAULT ENCRYPTION='N' */; + +USE `fastpheno`; + +-- +-- Table structure for table `sites` +-- + +DROP TABLE IF EXISTS `sites` ; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE IF NOT EXISTS `sites` ( + `sites_pk` INT NOT NULL AUTO_INCREMENT, + `site_name` VARCHAR(45) UNIQUE NOT NULL, + `site_desc` VARCHAR(999) NULL, + PRIMARY KEY (`sites_pk`)) ENGINE=InnoDB DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `sites` +-- + +LOCK TABLES `sites` WRITE; +/*!40000 ALTER TABLE `sites` DISABLE KEYS */; +INSERT INTO `sites` VALUES (1,'Pintendre', 'Lorem ipsum dolor sit amet, consectetur adipiscing elit'),(2,'Pickering','Lorem ipsum dolor sit amet,'); +/*!40000 ALTER TABLE `sites` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `trees` +-- + +DROP TABLE IF EXISTS `trees` ; + +CREATE TABLE IF NOT EXISTS `trees` ( + `trees_pk` INT NOT NULL AUTO_INCREMENT, + `sites_pk` INT NOT NULL, + `longitude` DECIMAL(10) NOT NULL, + `latitude` DECIMAL(10) NOT NULL, + `genotype_id` VARCHAR(5) NULL, + `external_link` VARCHAR(200) NULL, + `tree_given_id` VARCHAR(25) NULL, + PRIMARY KEY (`trees_pk`), + INDEX `sites_fk_idx` (`sites_pk` ASC), + CONSTRAINT `sites_fk` + FOREIGN KEY (`sites_pk`) + REFERENCES `sites` (`sites_pk`) + ON DELETE RESTRICT + ON UPDATE RESTRICT) ENGINE=InnoDB DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `trees` +-- + +LOCK TABLES `trees` WRITE; +/*!40000 ALTER TABLE `trees` DISABLE KEYS */; +INSERT INTO `trees` VALUES (1,1,336839,5178557,'C','example','11'),(2,1,336872,5178486,'C','example2','11'),(3,1,346872,5278486,'C','example3','B'),(4,2,330502,5262486,'XZ','example4','K123'); +/*!40000 ALTER TABLE `trees` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `band` +-- + +DROP TABLE IF EXISTS `band` ; + +CREATE TABLE IF NOT EXISTS `band` ( + `trees_pk` INT NOT NULL, + `month` ENUM('jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec') NOT NULL, + `band` VARCHAR(100) NOT NULL, + `value` DECIMAL(20,15) NOT NULL, + INDEX `trees_fk_idx` (`trees_pk` ASC), + PRIMARY KEY (`trees_pk`, `month`, `band`), + CONSTRAINT `trees_fk` + FOREIGN KEY (`trees_pk`) + REFERENCES `trees` (`trees_pk`) + ON DELETE RESTRICT + ON UPDATE RESTRICT) ENGINE=InnoDB DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `band` +-- + +LOCK TABLES `band` WRITE; +/*!40000 ALTER TABLE `band` DISABLE KEYS */; +INSERT INTO `band` VALUES (1,'jan','band_1',0.025796278000000),(1,'jan','band_2',0.025796278000000),(1,'feb','band_1',0.025796278000000),(1,'mar','band_1',0.0234423232241),(1,'apr','band_1',0.089900613000000),(2,'feb','band_1',0.183586478000000),(4,'feb','band_1',0.223586478000000); +/*!40000 ALTER TABLE `band` ENABLE KEYS */; +UNLOCK TABLES; + + +-- ----------------------------------------------------- +-- Table `height` +-- ----------------------------------------------------- +DROP TABLE IF EXISTS `height` ; + +CREATE TABLE IF NOT EXISTS `height` ( + `trees_pk` INT NOT NULL, + `month` ENUM('jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec') NOT NULL, + `tree_height_proxy` DECIMAL(20,15) NOT NULL, + `ground_height_proxy` DECIMAL(20,15) NOT NULL, + INDEX `tree_fk_idx` (`trees_pk` ASC), + PRIMARY KEY (`trees_pk`, `month`), + CONSTRAINT `tree_fk` + FOREIGN KEY (`trees_pk`) + REFERENCES `trees` (`trees_pk`) + ON DELETE RESTRICT + ON UPDATE RESTRICT) ENGINE=InnoDB DEFAULT CHARSET=latin1; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `height` +-- + +LOCK TABLES `height` WRITE; +/*!40000 ALTER TABLE `height` DISABLE KEYS */; +INSERT INTO `height` VALUES (1,'jan',2.23428942871000000,45.106719970000000),(1,'feb',3.478942871000000,49.106719970000000),(1,'mar',2.383630037000000,48.887859340000000),(1,'apr',1.376412749000000,49.052417760000000),(2,'feb',2.383630037000000,48.12341242131163),(4,'feb',2.623630037000000,45.22341242131163); +/*!40000 ALTER TABLE `height` ENABLE KEYS */; +UNLOCK TABLES; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2023-06-07 14:37:50 diff --git a/config/init.sh b/config/init.sh index a31c5771..4386705a 100755 --- a/config/init.sh +++ b/config/init.sh @@ -20,6 +20,7 @@ mysql -u $DB_USER -p$DB_PASS < ./config/databases/eplant_poplar.sql mysql -u $DB_USER -p$DB_PASS < ./config/databases/eplant_rice.sql mysql -u $DB_USER -p$DB_PASS < ./config/databases/eplant_soybean.sql mysql -u $DB_USER -p$DB_PASS < ./config/databases/eplant_tomato.sql +mysql -u $DB_USER -p$DB_PASS < ./config/databases/fastpheno.sql mysql -u $DB_USER -p$DB_PASS < ./config/databases/germination.sql mysql -u $DB_USER -p$DB_PASS < ./config/databases/kalanchoe.sql mysql -u $DB_USER -p$DB_PASS < ./config/databases/klepikova.sql diff --git a/docker-compose.yml b/docker-compose.yml index 13b894c5..a2fea400 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,7 +3,7 @@ version: "3.7" services: mysqldb: - image: mysql:8.0.33 + image: mysql:8.1.0 container_name: BAR_mysqldb # Must use this for mariadb client to connect command: --default-authentication-plugin=mysql_native_password @@ -12,7 +12,7 @@ services: - MYSQL_ROOT_PASSWORD=root redis: - image: redis:7.0.11 + image: redis:7.2.1 container_name: BAR_redis restart: always ports: diff --git a/docs/requirements.txt b/docs/requirements.txt index dcf41b47..91f72925 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,27 +1,27 @@ alabaster==0.7.13 -Babel==2.11.0 -beautifulsoup4==4.11.1 -certifi==2022.12.7 -charset-normalizer==3.0.1 -docutils==0.19 -furo==2022.12.7 +Babel==2.12.1 +beautifulsoup4==4.12.2 +certifi==2023.7.22 +charset-normalizer==3.2.0 +docutils==0.20.1 +furo==2023.8.19 idna==3.4 imagesize==1.4.1 Jinja2==3.1.2 -MarkupSafe==2.1.2 -packaging==23.0 -Pygments==2.14.0 -pytz==2022.7.1 -requests==2.28.2 +MarkupSafe==2.1.3 +packaging==23.1 +Pygments==2.16.1 +pytz==2023.3 +requests==2.31.0 snowballstemmer==2.2.0 -soupsieve==2.3.2.post1 -Sphinx==5.3.0 +soupsieve==2.4.1 +Sphinx==7.2.4 sphinx-basic-ng==1.0.0b1 -sphinx-copybutton==0.5.1 -sphinxcontrib-applehelp==1.0.2 -sphinxcontrib-devhelp==1.0.2 -sphinxcontrib-htmlhelp==2.0.0 +sphinx-copybutton==0.5.2 +sphinxcontrib-applehelp==1.0.7 +sphinxcontrib-devhelp==1.0.5 +sphinxcontrib-htmlhelp==2.0.4 sphinxcontrib-jsmath==1.0.1 -sphinxcontrib-qthelp==1.0.3 -sphinxcontrib-serializinghtml==1.1.5 -urllib3==1.26.14 +sphinxcontrib-qthelp==1.0.6 +sphinxcontrib-serializinghtml==1.1.9 +urllib3==2.0.4 diff --git a/requirements.txt b/requirements.txt index 4a1b36a5..4b593432 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,58 +1,58 @@ aniso8601==9.0.1 -async-timeout==4.0.2 +async-timeout==4.0.3 attrs==23.1.0 -black==23.7.0 -blinker==1.6.2 +black==23.10.0 +blinker==1.6.3 cachelib==0.9.0 certifi==2023.7.22 -charset-normalizer==3.2.0 -click==8.1.6 -coverage==7.2.7 +charset-normalizer==3.3.0 +click==8.1.7 +coverage==7.3.2 Deprecated==1.2.14 flake8==6.1.0 -Flask==2.3.2 -Flask-Caching==2.0.2 +Flask==2.3.3 +Flask-Caching==2.1.0 Flask-Cors==4.0.0 -Flask-Limiter==3.3.1 +Flask-Limiter==3.5.0 flask-marshmallow==0.15.0 flask-restx==1.1.0 -Flask-SQLAlchemy==3.0.5 -greenlet==2.0.2 +Flask-SQLAlchemy==3.1.1 +greenlet==3.0.0 idna==3.4 -importlib-resources==6.0.0 +importlib-resources==6.1.0 iniconfig==2.0.0 itsdangerous==2.1.2 Jinja2==3.1.2 -jsonschema==4.18.4 +jsonschema==4.19.1 jsonschema-specifications==2023.7.1 -limits==3.5.0 +limits==3.6.0 markdown-it-py==3.0.0 MarkupSafe==2.1.3 marshmallow==3.20.1 mccabe==0.7.0 mdurl==0.1.2 mypy-extensions==1.0.0 -mysqlclient==2.1.1 +mysqlclient==2.2.0 ordered-set==4.1.0 -packaging==23.1 +packaging==23.2 pathspec==0.11.2 -platformdirs==3.10.0 -pluggy==1.2.0 -pycodestyle==2.11.0 +platformdirs==3.11.0 +pluggy==1.3.0 +pycodestyle==2.11.1 pyflakes==3.1.0 -Pygments==2.15.1 +Pygments==2.16.1 pyrsistent==0.19.3 -pytest==7.4.0 +pytest==7.4.2 python-dateutil==2.8.2 -pytz==2023.3 -redis==4.6.0 -referencing==0.30.0 +pytz==2023.3.post1 +redis==5.0.1 +referencing==0.30.2 requests==2.31.0 -rich==13.5.1 -rpds-py==0.9.2 +rich==13.6.0 +rpds-py==0.10.6 six==1.16.0 -SQLAlchemy==2.0.19 -typing_extensions==4.7.1 -urllib3==2.0.4 -Werkzeug==2.3.6 +SQLAlchemy==2.0.22 +typing_extensions==4.8.0 +urllib3==2.0.7 +Werkzeug==2.3.7 wrapt==1.15.0 diff --git a/tests/resources/test_fastpheno.py b/tests/resources/test_fastpheno.py new file mode 100644 index 00000000..85a23c1e --- /dev/null +++ b/tests/resources/test_fastpheno.py @@ -0,0 +1,100 @@ +from api import app +from unittest import TestCase + + +class TestIntegrations(TestCase): + def setUp(self): + self.app_client = app.test_client() + + def test_bands(self): + """This function checks GET request for fastpheno bands + :return: + """ + response = self.app_client.get("/fastpheno/get_bands/pintendre/feb/band_1") + expected = { + "wasSuccessful": True, + "data": [ + { + "site_name": "Pintendre", + "tree_id": 1, + "longitude": 336839, + "latitutde": 5178557, + "genotype_id": "C", + "tree_given_id": "11", + "external_link": "example", + "band_value": 0.025796278, + "tree_height_proxy": 3.478942871, + "ground_height_proxy": 49.10671997, + "band_month": "feb", + }, + { + "site_name": "Pintendre", + "tree_id": 2, + "longitude": 336872, + "latitutde": 5178486, + "genotype_id": "C", + "tree_given_id": "11", + "external_link": "example2", + "band_value": 0.183586478, + "tree_height_proxy": 2.383630037, + "ground_height_proxy": 48.12341242131163, + "band_month": "feb", + }, + ], + } + self.assertEqual(response.json, expected) + + # Not working version + response = self.app_client.get("/fastpheno/get_bands/NOTASITE/feb/band_1") + expected = { + "wasSuccessful": False, + "error": "There are no data found for the given parameters", + } + self.assertEqual(response.json, expected) + + def test_site_genotype_ids(self): + """This function checks GET request for fastpheno sites for genotype_ids + :return: + """ + response = self.app_client.get("/fastpheno/get_trees/C") + expected = { + "wasSuccessful": True, + "data": [ + { + "site_name": "Pintendre", + "tree_id": 1, + "longitude": 336839, + "latitutde": 5178557, + "genotype_id": "C", + "tree_given_id": "11", + "external_link": "example", + }, + { + "site_name": "Pintendre", + "tree_id": 2, + "longitude": 336872, + "latitutde": 5178486, + "genotype_id": "C", + "tree_given_id": "11", + "external_link": "example2", + }, + { + "site_name": "Pintendre", + "tree_id": 3, + "longitude": 346872, + "latitutde": 5278486, + "genotype_id": "C", + "tree_given_id": "B", + "external_link": "example3", + }, + ], + } + self.assertEqual(response.json, expected) + + # Not working version + response = self.app_client.get("/fastpheno/get_trees/NOTAGENOTYPE") + expected = { + "wasSuccessful": False, + "error": "There are no data found for the given parameters", + } + self.assertEqual(response.json, expected)