diff --git a/app/api/models/resolver.py b/app/api/models/resolver.py
index fb5d8e4..82e389c 100644
--- a/app/api/models/resolver.py
+++ b/app/api/models/resolver.py
@@ -1,4 +1,5 @@
-from typing import Optional, Literal, List, Dict
+from enum import Enum
+from typing import Optional, Literal, List, Dict, Annotated
from pydantic import BaseModel, Field
@@ -38,5 +39,24 @@ class MetadataResult(BaseModel):
class ResolvedPayload(MetadataResult):
resolved_url: str
-class ResolvedURLResponse(BaseModel):
+
+class RapidResolverHtmlResponseType(str, Enum):
+ HOME = "HOME"
+ ERROR = "ERROR"
+ BLAST = "BLAST"
+ HELP = "HELP"
+ INFO = "INFO"
+
+ # Exclude all fields except resolved_url in JSON response.
+class RapidResolverResponse(BaseModel):
resolved_url: str
+ response_type: Annotated[Optional[RapidResolverHtmlResponseType], Field(exclude=True)] = None
+ code: Annotated[Optional[int], Field(exclude=True)] = None
+ species_name: Annotated[Optional[str], Field(exclude=True)] = None
+ gene_id: Annotated[Optional[str], Field(exclude=True)] = None
+ location: Annotated[Optional[str], Field(exclude=True)] = None
+ message: Annotated[Optional[str], Field(exclude=True)] = None
+ rapid_archive_url: Annotated[Optional[str], Field(exclude=True)] = None
+
+ class Config:
+ use_enum_values = True
diff --git a/app/api/resources/rapid_view.py b/app/api/resources/rapid_view.py
index 028168c..af3c846 100644
--- a/app/api/resources/rapid_view.py
+++ b/app/api/resources/rapid_view.py
@@ -1,12 +1,18 @@
+import os
from urllib.parse import parse_qs
-from fastapi import APIRouter, Request, HTTPException, Query
-from fastapi.responses import RedirectResponse
+
+from dotenv import load_dotenv
+from fastapi import APIRouter, Request, Query, HTTPException
+
+from jinja2 import Environment, FileSystemLoader
+from starlette.responses import HTMLResponse
+
import logging
-from api.models.resolver import ResolvedURLResponse
+from api.models.resolver import RapidResolverResponse, RapidResolverHtmlResponseType
from core.logging import InterceptHandler
from core.config import ENSEMBL_URL
from api.utils.metadata import get_genome_id_from_assembly_accession_id
-from api.utils.rapid import construct_url, format_assembly_accession
+from api.utils.rapid import construct_url, format_assembly_accession, construct_rapid_archive_url
logging.getLogger().handlers = [InterceptHandler()]
@@ -15,14 +21,22 @@
@router.get("/info/{subpath:path}", name="Resolve rapid help page")
async def resolve_rapid_help(request: Request, subpath: str = ""):
- help_page_url = f"{ENSEMBL_URL}/help"
- return resolved_response(help_page_url, request)
+ response = RapidResolverResponse(
+ response_type=RapidResolverHtmlResponseType.HELP,
+ code=308,
+ resolved_url=f"{ENSEMBL_URL}/help",
+ )
+ return resolved_response(response, request)
-@router.get("/Blast", name="Resolve rapid blast page")
+@router.get("/Multi/Tools/Blast", name="Resolve rapid blast page")
async def resolve_rapid_blast(request: Request):
- blast_page_url = f"{ENSEMBL_URL}/blast"
- return resolved_response(blast_page_url, request)
+ response = RapidResolverResponse(
+ response_type=RapidResolverHtmlResponseType.BLAST,
+ code=308,
+ resolved_url=f"{ENSEMBL_URL}/blast",
+ )
+ return resolved_response(response, request)
# Resolve rapid urls
@@ -31,12 +45,28 @@ async def resolve_rapid_blast(request: Request):
async def resolve_species(
request: Request, species_url_name: str, subpath: str = "", r: str = Query(None)
):
+ # Check if its blast redirect
+ if "tools/blast" in subpath.lower():
+ response = RapidResolverResponse(
+ response_type=RapidResolverHtmlResponseType.BLAST,
+ code=308,
+ resolved_url=f"{ENSEMBL_URL}/blast",
+ species_name=species_url_name,
+ )
+ return resolved_response(response, request)
+
assembly_accession_id = format_assembly_accession(species_url_name)
if assembly_accession_id is None:
- raise HTTPException(
- status_code=422, detail="Unable to process input accession ID"
+ input_error_response = RapidResolverResponse(
+ response_type=RapidResolverHtmlResponseType.ERROR,
+ code=422,
+ resolved_url=f"{ENSEMBL_URL}/species-selector",
+ message="Invalid input accession ID",
+ species_name=species_url_name,
)
+ return resolved_response(input_error_response, request)
+
try:
genome_object = get_genome_id_from_assembly_accession_id(assembly_accession_id)
@@ -48,27 +78,72 @@ async def resolve_species(
query_params = parse_qs(query_string, separator=";")
url = construct_url(genome_id, subpath, query_params)
- return resolved_response(url, request)
+ rapid_archive_url = construct_rapid_archive_url(species_url_name, subpath, query_params)
+ response = RapidResolverResponse(
+ response_type=RapidResolverHtmlResponseType.INFO,
+ code=308,
+ resolved_url=url,
+ species_name=species_url_name,
+ gene_id=query_params.get("g", [None])[0],
+ location=query_params.get("r", [None])[0],
+ rapid_archive_url=rapid_archive_url,
+ )
+ return resolved_response(response, request)
else:
raise HTTPException(status_code=404, detail="Genome not found")
-
except HTTPException as e:
logging.debug(e)
- raise HTTPException(
- status_code=e.status_code, detail="Unexpected error occured"
+ response = RapidResolverResponse(
+ response_type=RapidResolverHtmlResponseType.ERROR,
+ code=e.status_code,
+ resolved_url=f"{ENSEMBL_URL}/species-selector",
+ message=e.detail,
+ species_name=species_url_name,
)
-
+ return resolved_response(response, request)
except Exception as e:
logging.debug(f"Unexpected error occurred: {e}")
- raise HTTPException(status_code=500, detail="Unexpected error occurred")
+ response = RapidResolverResponse(
+ species_name=species_url_name,
+ response_type=RapidResolverHtmlResponseType.ERROR,
+ code=500,
+ resolved_url=f"{ENSEMBL_URL}/species-selector",
+ message=str(e),
+ )
+ return resolved_response(response, request)
@router.get("/", name="Rapid Home")
async def resolve_home(request: Request):
- return resolved_response(ENSEMBL_URL, request)
+ response = RapidResolverResponse(
+ response_type=RapidResolverHtmlResponseType.HOME,
+ code=308,
+ resolved_url=ENSEMBL_URL,
+ )
+ return resolved_response(response, request)
-def resolved_response(url: str, request: Request):
+def resolved_response(response: RapidResolverResponse, request: Request):
+ # Return JSON response if requested
if "application/json" in request.headers.get("accept"):
- return ResolvedURLResponse(resolved_url=url)
- return RedirectResponse(url=url, status_code=301)
+ # Handle error responses for JSON requests
+ if response.response_type == RapidResolverHtmlResponseType.ERROR:
+ raise HTTPException(
+ status_code=response.code,
+ detail=response.message or "An error occurred",
+ )
+ # Doesn't raise redirect for JSON requests, just return the URL. Because swagger UI doesn't handle redirects well.
+ # So code is always 200 for successful JSON response.
+ return response
+
+ # Default to HTML response
+ return HTMLResponse(generate_html_content(response))
+
+
+def generate_html_content(response):
+ load_dotenv()
+ CURR_DIR = os.path.dirname(os.path.abspath(__file__))
+ env = Environment(loader=FileSystemLoader(os.path.join(CURR_DIR, "templates/rapid")))
+ rapid_redirect_page_template = env.get_template("main.html")
+ rapid_redirect_page_html = rapid_redirect_page_template.render(response=response)
+ return rapid_redirect_page_html
diff --git a/app/api/resources/templates/rapid/_home_icon.html b/app/api/resources/templates/rapid/_home_icon.html
new file mode 100644
index 0000000..2ba4690
--- /dev/null
+++ b/app/api/resources/templates/rapid/_home_icon.html
@@ -0,0 +1,16 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/api/resources/templates/rapid/_logo.html b/app/api/resources/templates/rapid/_logo.html
new file mode 100644
index 0000000..9b8dfae
--- /dev/null
+++ b/app/api/resources/templates/rapid/_logo.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/api/resources/templates/rapid/appbar.html b/app/api/resources/templates/rapid/appbar.html
new file mode 100644
index 0000000..50f8336
--- /dev/null
+++ b/app/api/resources/templates/rapid/appbar.html
@@ -0,0 +1,25 @@
+
+
+
+
+ {% include "_logo.html" %}
+
+
+
+
+ Genome data & annotation
+
+
\ No newline at end of file
diff --git a/app/api/resources/templates/rapid/content.html b/app/api/resources/templates/rapid/content.html
new file mode 100644
index 0000000..40ffdc0
--- /dev/null
+++ b/app/api/resources/templates/rapid/content.html
@@ -0,0 +1,44 @@
+{% import "url.html" as url_helper %}
+
+
+
+ {% if response.response_type == "BLAST" or response.response_type == "HELP" %}
+ {% if response.species_name %}
+
+ Species {{ response.species_name }}
+
+ {% endif %}
+ {{ url_helper.render_url(response.resolved_url) }}
+ {% elif response.response_type == "INFO" %}
+
+ Species {{ response.species_name }}
+
+ {% if response.gene_id %}
+
+ Gene {{ response.gene_id }}
+
+ {% endif %}
+ {% if response.location %}
+
+ Location {{ response.location }}
+
+ {% endif %}
+ {{ url_helper.render_url(response.resolved_url) }}
+ {% elif response.response_type == "ERROR" %}
+
+ Species {{ response.species_name }}
+
+
+ {{ response.message }}
+
+ {{ url_helper.render_url(response.resolved_url) }}
+ {% else %}
+ {{ url_helper.render_url("https://beta.ensembl.org/") }}
+ {% endif %}
+
+
+
+ You will be redirected to the new Ensembl website, where you will find the latest genomic information
+
+
+
\ No newline at end of file
diff --git a/app/api/resources/templates/rapid/footer.html b/app/api/resources/templates/rapid/footer.html
new file mode 100644
index 0000000..3b14a93
--- /dev/null
+++ b/app/api/resources/templates/rapid/footer.html
@@ -0,0 +1,10 @@
+
+
\ No newline at end of file
diff --git a/app/api/resources/templates/rapid/main.html b/app/api/resources/templates/rapid/main.html
new file mode 100644
index 0000000..27128d7
--- /dev/null
+++ b/app/api/resources/templates/rapid/main.html
@@ -0,0 +1,26 @@
+
+
+
+ Ensembl Rapid Resolver
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% include "appbar.html" %}
+
+ {% include "venn.html" %}
+ {% include "content.html" %}
+ {% include "footer.html" %}
+
+
+
diff --git a/app/api/resources/templates/rapid/url.html b/app/api/resources/templates/rapid/url.html
new file mode 100644
index 0000000..f91f4fb
--- /dev/null
+++ b/app/api/resources/templates/rapid/url.html
@@ -0,0 +1,8 @@
+{% macro render_url(url) %}
+
+{% endmacro %}
diff --git a/app/api/resources/templates/rapid/venn.html b/app/api/resources/templates/rapid/venn.html
new file mode 100644
index 0000000..0f46790
--- /dev/null
+++ b/app/api/resources/templates/rapid/venn.html
@@ -0,0 +1,14 @@
+
+
+
+ Your URL has changed or gone...
+
+
+ ...we suggest this instead
+
+
+ {{ response.code }}
+
+
\ No newline at end of file
diff --git a/app/api/utils/rapid.py b/app/api/utils/rapid.py
index 950652b..56f0507 100644
--- a/app/api/utils/rapid.py
+++ b/app/api/utils/rapid.py
@@ -1,6 +1,6 @@
from loguru import logger
import requests
-from core.config import ENSEMBL_URL, NCBI_DATASETS_URL
+from core.config import ENSEMBL_URL, NCBI_DATASETS_URL, RAPID_ARCHIVE_URL
import re
@@ -58,20 +58,54 @@ def construct_url(genome_id, subpath, query_params):
location = query_params.get("r", [None])[0]
gene_id = query_params.get("g", [None])[0]
- if subpath == "" or re.search("Info/Index", subpath, re.IGNORECASE):
+ subpath = subpath or ""
+ subpath_lower = subpath.lower()
+
+ if genome_id is None:
+ return ENSEMBL_URL
+
+ if not subpath or "info/index" in subpath_lower:
return f"{ENSEMBL_URL}/species/{genome_id}"
- elif re.search("Location", subpath):
+
+ if "location" in subpath_lower:
+ if "genome" in subpath_lower:
+ return f"{ENSEMBL_URL}/species/{genome_id}"
+ if location is None:
+ raise ValueError("Location paramater 'r' is missing for Location view")
return f"{ENSEMBL_URL}/genome-browser/{genome_id}?focus=location:{location}"
- elif re.search("Gene", subpath):
- if re.search("Gene/Compara_Homolog", subpath):
- return (
- f"{ENSEMBL_URL}/entity-viewer/{genome_id}/gene:{gene_id}?view=homology"
- )
+
+ if "gene" in subpath_lower:
+ if not gene_id:
+ raise ValueError("Gene parameter 'g' is missing for Gene view")
+ if "compara_homolog" in subpath_lower:
+ return f"{ENSEMBL_URL}/entity-viewer/{genome_id}/gene:{gene_id}?view=homology"
return f"{ENSEMBL_URL}/entity-viewer/{genome_id}/gene:{gene_id}"
- elif re.search("Transcript", subpath):
- if re.search("Domains|ProteinSummary", subpath):
- return (
- f"{ENSEMBL_URL}/entity-viewer/{genome_id}/gene:{gene_id}?view=protein"
- )
+
+ if "transcript" in subpath_lower:
+ if not gene_id:
+ raise ValueError("Gene parameter 'g' is missing for Transcript view")
+ if "domains" in subpath_lower or "proteinsummary" in subpath_lower:
+ return f"{ENSEMBL_URL}/entity-viewer/{genome_id}/gene:{gene_id}?view=protein"
return f"{ENSEMBL_URL}/entity-viewer/{genome_id}/gene:{gene_id}"
- return ENSEMBL_URL
+
+ raise ValueError("Invalid path")
+
+
+def construct_rapid_archive_url(species_url_name, subpath, query_params):
+ url = f"{RAPID_ARCHIVE_URL}/{species_url_name}"
+ query_params = query_params or {}
+
+ params = []
+ for key, values in query_params.items():
+ value = values[0] if values else ""
+ if value:
+ params.append(f"{key}={value}")
+
+ if subpath:
+ url += f"/{subpath}"
+
+ if params:
+ url += "?" + ";".join(params) # ; separator for rapid
+
+ return url
+
diff --git a/app/core/config.py b/app/core/config.py
index 16ec69b..1899f2c 100644
--- a/app/core/config.py
+++ b/app/core/config.py
@@ -36,6 +36,7 @@
)
DEFAULT_APP = config("DEFAULT_APP", cast=str, default="entity-viewer")
ENSEMBL_URL = config("ENSEMBL_URL", cast=str, default="https://beta.ensembl.org")
+RAPID_ARCHIVE_URL = config("RAPID_ARCHIVE_URL", cast=str, default="https://rapid-archive.ensembl.org")
NCBI_DATASETS_URL = config(
"NCBI_DATASETS_URL", cast=str, default="https://api.ncbi.nlm.nih.gov/datasets/v2"
)
diff --git a/app/static/APISpecification.yaml b/app/static/APISpecification.yaml
index 6aaa319..ebe8c1d 100644
--- a/app/static/APISpecification.yaml
+++ b/app/static/APISpecification.yaml
@@ -112,8 +112,6 @@ paths:
tags:
- Rapid Resolver
responses:
- '301':
- $ref: '#/components/responses/301'
'200':
$ref: '#/components/responses/200'
'400':
@@ -122,15 +120,13 @@ paths:
$ref: '#/components/responses/404'
'500':
$ref: '#/components/responses/500'
- /rapid/Blast:
+ /rapid/Multi/Tools/Blast:
get:
summary: Resolve rapid site blast url
description: Resolves to Ensembl site blast url.
tags:
- Rapid Resolver
responses:
- '301':
- $ref: '#/components/responses/301'
'200':
$ref: '#/components/responses/200'
'400':
@@ -154,8 +150,6 @@ paths:
type: string
example: Camarhynchus_parvulus_GCA_902806625.1
responses:
- '301':
- $ref: '#/components/responses/301'
'200':
$ref: '#/components/responses/200'
'422':
@@ -195,8 +189,6 @@ paths:
type: string
example: 2:361680-384534
responses:
- '301':
- $ref: '#/components/responses/301'
'200':
$ref: '#/components/responses/200'
'422':
@@ -207,6 +199,29 @@ paths:
$ref: '#/components/responses/404'
'500':
$ref: '#/components/responses/500'
+ /rapid/{species_url_name}/Tools/Blast:
+ get:
+ summary: Resolve rapid site blast url
+ description: Resolve rapid site blast url
+ tags:
+ - Rapid Resolver
+ parameters:
+ - name: species_url_name
+ in: path
+ description: Species name with assembly accession
+ required: true
+ schema:
+ type: string
+ example: Camarhynchus_parvulus_GCA_902806625.1
+ responses:
+ '200':
+ $ref: '#/components/responses/200'
+ '400':
+ $ref: '#/components/responses/400'
+ '404':
+ $ref: '#/components/responses/404'
+ '500':
+ $ref: '#/components/responses/500'
/rapid/info/{subpath}:
get:
summary: Resolve rapid site help and docs url
@@ -221,8 +236,6 @@ paths:
type: string
example: index.html
responses:
- '301':
- $ref: '#/components/responses/301'
'200':
$ref: '#/components/responses/200'
'400':
@@ -251,12 +264,6 @@ components:
resolved_url:
type: string
description: Resolved URL to the Ensembl site
- 301:
- description: Redirect to resolved URL
- headers:
- Location:
- $ref: '#/components/headers/Location'
- content: { }
400:
description: Bad Request. Missing stable id in the url
content:
diff --git a/app/static/css/rapid_page.css b/app/static/css/rapid_page.css
new file mode 100644
index 0000000..452d1e9
--- /dev/null
+++ b/app/static/css/rapid_page.css
@@ -0,0 +1,184 @@
+:root {
+ --standard-gutter: 30px;
+ --global-padding-left: calc(var(--standard-gutter) * 4);
+ --color-light-blue: #33adff;
+ --font-weight-light: 300;
+ --color-medium-dark-grey: #9aa7b1;
+ --color-red: #d90000;
+ --color-medium-light-grey: #d4d9de;
+}
+
+html,
+body {
+ margin: 0;
+ padding: 0;
+ height: 100%;
+ font-family: Lato, 'Helvetica Neue', Helvetica, Arial, sans-serif;
+}
+
+.topbar {
+ display: flex;
+ flex-wrap: nowrap;
+ justify-content: space-between;
+ align-items: center;
+ color: #fff;
+ height: 32px;
+ padding: 0 30px 0 22px;
+ background-color: #1b2c39;
+}
+
+.topbar-left {
+ display: flex;
+ align-items: center;
+ line-height: 1;
+}
+
+.logotype-wrapper {
+ margin-top: 3px;
+ margin-right: 15px;
+}
+
+.topbar-left-text {
+ display: flex;
+ align-items: baseline;
+ margin-bottom: -3px;
+ column-gap: 30px;
+}
+
+.topbar-right {
+ font-size: 13px;
+}
+
+.release {
+ font-size: 11px;
+}
+
+.copyright {
+ font-size: 12px;
+}
+
+.copyright a {
+ color: var(--color-light-blue);
+ text-decoration: none;
+}
+
+.light {
+ font-weight: var(--font-weight-light);
+}
+
+.home-link {
+ font-size: 0;
+ margin-right: 16px;
+ cursor: pointer;
+}
+
+.container {
+ display: flex;
+ flex-direction: column;
+ padding: 0 var(--global-padding-left);
+ align-items: center;
+ margin-top: 50px;
+}
+
+.redirect-content {
+ margin-top: 30px;
+ text-align: center;
+ width: 350px;
+}
+
+.redirect-text {
+ color: var(--color-medium-dark-grey);
+ font-size: 12px;
+ margin-top: 50px;
+}
+
+.footer {
+ font-size: 12px;
+ color: var(--color-medium-dark-grey);
+ margin-top: 50px;
+ width: 300px;
+ text-align: center;
+}
+
+.footer-link a {
+ font-size: 12px;
+ color: var(--color-light-blue);
+ text-decoration: none;
+}
+
+.link {
+ color: var(--color-light-blue);
+ text-decoration: none;
+ font-size: 13px;
+}
+
+.error-message {
+ color: var(--color-red);
+ font-size: 13px;
+ margin-bottom: 20px;
+}
+
+.search-param {
+ font-size: 13px;
+ margin-bottom: 5px;
+}
+
+.venn-header {
+ font-size: 14px;
+ margin-right: 50px;
+ margin-bottom: 20px;
+ margin-left: 50px;
+}
+
+.venn {
+ position: relative;
+ width: 400px;
+ height: 200px;
+ margin-left: 50px;
+}
+
+.circle {
+ position: absolute;
+ width: 200px;
+ height: 200px;
+ border-radius: 50%;
+ opacity: 0.6;
+ display: flex;
+ align-items: center;
+}
+
+.circle.a {
+ left: 0;
+ background: var(--color-medium-light-grey);
+ justify-content: flex-start;
+ font-size: 12px;
+}
+
+.circle.a span {
+ margin-left: 20px;
+ width: 60%;
+}
+
+.circle.b span {
+ width: 60%;
+}
+
+.circle.b {
+ left: 150px;
+ background: var(--color-medium-light-grey);
+ justify-content: flex-end;
+ font-size: 12px;
+}
+
+.intersection {
+ position: absolute;
+ left: 44%;
+ top: 50%;
+ transform: translate(-50%, -50%);
+ font-size: 20px;
+}
+
+.search-param-label {
+ font-size: 13px;
+ color: var(--color-medium-dark-grey);
+}
\ No newline at end of file
diff --git a/app/static/icons/favicon-16x16.png b/app/static/icons/favicon-16x16.png
new file mode 100644
index 0000000..77ee00c
Binary files /dev/null and b/app/static/icons/favicon-16x16.png differ
diff --git a/app/static/icons/favicon-32x32.png b/app/static/icons/favicon-32x32.png
new file mode 100644
index 0000000..17be8f9
Binary files /dev/null and b/app/static/icons/favicon-32x32.png differ
diff --git a/app/tests/test_rapid.py b/app/tests/test_rapid.py
index 762e2fa..cf083ca 100644
--- a/app/tests/test_rapid.py
+++ b/app/tests/test_rapid.py
@@ -1,7 +1,7 @@
import unittest
from unittest.mock import patch
from fastapi.testclient import TestClient
-from api.models.resolver import ResolvedURLResponse
+from api.models.resolver import RapidResolverResponse
from core.config import ENSEMBL_URL
from main import app
@@ -35,11 +35,6 @@ def setUp(self):
# Test rapid home page
def test_rapid_home_success(self):
- response = self.client.get(f"{self.mock_rapid_api_url}/", follow_redirects=False)
- self.assertEqual(response.status_code, 301)
- self.assertIn("location", response.headers)
- self.assertEqual(response.headers["location"], ENSEMBL_URL)
-
# test with accept header for JSON response
response = self.client.get(
f"{self.mock_rapid_api_url}/",
@@ -49,21 +44,12 @@ def test_rapid_home_success(self):
self.assertEqual(response.status_code, 200) # OK
self.assertEqual(
response.json(),
- ResolvedURLResponse(resolved_url=ENSEMBL_URL).model_dump(mode='json')
+ RapidResolverResponse(resolved_url=ENSEMBL_URL).model_dump(mode='json')
)
# Test rapid help page
def test_rapid_help_success(self):
- response = self.client.get(
- f"{self.mock_rapid_api_url}/info/index.html", follow_redirects=False
- )
- self.assertEqual(response.status_code, 301)
- self.assertIn("location", response.headers)
- self.assertEqual(
- response.headers["location"], f"{ENSEMBL_URL}/help"
- )
-
# test with accept header for JSON response
response = self.client.get(
f"{self.mock_rapid_api_url}/info/index.html",
@@ -73,31 +59,33 @@ def test_rapid_help_success(self):
self.assertEqual(response.status_code, 200) # OK
self.assertEqual(
response.json(),
- ResolvedURLResponse(resolved_url=f"{ENSEMBL_URL}/help").model_dump(mode='json')
+ RapidResolverResponse(resolved_url=f"{ENSEMBL_URL}/help").model_dump(mode='json')
)
# Test rapid blast page
def test_rapid_blast_success(self):
- response = self.client.get(
- f"{self.mock_rapid_api_url}/Blast", follow_redirects=False
+ # test with accept header for JSON response
+ response1 = self.client.get(
+ f"{self.mock_rapid_api_url}/Multi/Tools/Blast",
+ headers={"accept": "application/json"},
+ follow_redirects=False,
)
- self.assertEqual(response.status_code, 301)
- self.assertIn("location", response.headers)
+ self.assertEqual(response1.status_code, 200)
self.assertEqual(
- response.headers["location"], f"{ENSEMBL_URL}/blast"
+ response1.json(),
+ RapidResolverResponse(resolved_url=f"{ENSEMBL_URL}/blast").model_dump(mode='json')
)
- # test with accept header for JSON response
- response = self.client.get(
- f"{self.mock_rapid_api_url}/Blast",
+ response2 = self.client.get(
+ f"{self.mock_rapid_api_url}/{self.species_url_name}/Tools/Blast",
headers={"accept": "application/json"},
follow_redirects=False,
)
- self.assertEqual(response.status_code, 200)
+ self.assertEqual(response2.status_code, 200)
self.assertEqual(
- response.json(),
- ResolvedURLResponse(resolved_url=f"{ENSEMBL_URL}/blast").model_dump(mode='json')
+ response2.json(),
+ RapidResolverResponse(resolved_url=f"{ENSEMBL_URL}/blast").model_dump(mode='json')
)
@@ -106,39 +94,6 @@ def test_rapid_blast_success(self):
def test_rapid_species_home_success(
self, mock_get_genome_id_from_assembly_accession_id
):
-
- # test web-metadata-api response without genome_tag
- mock_get_genome_id_from_assembly_accession_id.return_value = (
- self.mock_genome_id_response1
- )
-
- response = self.client.get(
- f"{self.mock_rapid_api_url}/{self.species_url_name}/",
- follow_redirects=False,
- )
-
- self.assertEqual(response.status_code, 301) # Redirect
- self.assertIn("location", response.headers)
- self.assertEqual(
- response.headers["location"], self.mock_resolved_url["genome1"]
- )
-
- # test web-metadata-api response with genome_tag
- mock_get_genome_id_from_assembly_accession_id.return_value = (
- self.mock_genome_id_response2
- )
-
- response = self.client.get(
- f"{self.mock_rapid_api_url}/{self.species_url_name}/",
- follow_redirects=False,
- )
-
- self.assertEqual(response.status_code, 301) # Redirect
- self.assertIn("location", response.headers)
- self.assertEqual(
- response.headers["location"], self.mock_resolved_url["genome2"]
- )
-
# test with accept header for JSON response
mock_get_genome_id_from_assembly_accession_id.return_value = (
self.mock_genome_id_response1
@@ -153,7 +108,7 @@ def test_rapid_species_home_success(
self.assertEqual(response.status_code, 200) # OK
self.assertEqual(
response.json(),
- ResolvedURLResponse(resolved_url = self.mock_resolved_url["genome1"]).model_dump(mode='json')
+ RapidResolverResponse(resolved_url = self.mock_resolved_url["genome1"]).model_dump(mode='json')
)
@@ -162,24 +117,6 @@ def test_rapid_species_home_success(
def test_rapid_species_location_success(
self, mock_get_genome_id_from_assembly_accession_id
):
-
- # test web-metadata-api response without genome_tag
- mock_get_genome_id_from_assembly_accession_id.return_value = (
- self.mock_genome_id_response1
- )
-
- response = self.client.get(
- f"{self.mock_rapid_api_url}/{self.species_url_name}/Location/View",
- params={"r": "1:1000-2000"},
- follow_redirects=False,
- )
-
- self.assertEqual(response.status_code, 301) # Redirect
- self.assertIn(
- f"{ENSEMBL_URL}/genome-browser/genome_uuid1?focus=location:1:1000-2000",
- response.headers["location"],
- )
-
# test with accept header for JSON response
mock_get_genome_id_from_assembly_accession_id.return_value = (
self.mock_genome_id_response1
@@ -195,7 +132,7 @@ def test_rapid_species_location_success(
self.assertEqual(response.status_code, 200) # OK
self.assertEqual(
response.json(),
- ResolvedURLResponse(resolved_url = f"{ENSEMBL_URL}/genome-browser/genome_uuid1?focus=location:1:1000-2000").model_dump(mode='json')
+ RapidResolverResponse(resolved_url = f"{ENSEMBL_URL}/genome-browser/genome_uuid1?focus=location:1:1000-2000").model_dump(mode='json')
)
# Test Gene pages
@@ -203,22 +140,6 @@ def test_rapid_species_location_success(
def test_rapid_species_gene_compara_homolog(
self, mock_get_genome_id_from_assembly_accession_id
):
- mock_get_genome_id_from_assembly_accession_id.return_value = (
- self.mock_genome_id_response1
- )
-
- response = self.client.get(
- f"{self.mock_rapid_api_url}/{self.species_url_name}/Gene/Compara_Homolog",
- params={"g": "GENE123"},
- follow_redirects=False,
- )
-
- self.assertEqual(response.status_code, 301) # Redirect
- self.assertIn(
- f"{ENSEMBL_URL}/entity-viewer/genome_uuid1/gene:GENE123?view=homology",
- response.headers["location"],
- )
-
# test with accept header for JSON response
mock_get_genome_id_from_assembly_accession_id.return_value = (
self.mock_genome_id_response1
@@ -234,7 +155,7 @@ def test_rapid_species_gene_compara_homolog(
self.assertEqual(response.status_code, 200) # OK
self.assertEqual(
response.json(),
- ResolvedURLResponse(
+ RapidResolverResponse(
resolved_url=f"{ENSEMBL_URL}/entity-viewer/genome_uuid1/gene:GENE123?view=homology"
).model_dump(mode='json'),
)
@@ -247,19 +168,21 @@ def test_rapid_species_404_not_found(
mock_get_genome_id_from_assembly_accession_id.return_value = {}
response = self.client.get(
- f"{self.mock_rapid_api_url}/{self.species_url_name}/Invalid_GCA"
+ f"{self.mock_rapid_api_url}/{self.species_url_name}/Invalid_GCA",
+ headers={"accept": "application/json"},
)
self.assertEqual(response.status_code, 404)
# Test invalid url entity
def test_rapid_species_422_unprocessable_entity(self):
- response = self.client.get(f"{self.mock_rapid_api_url}/Invalid_Name/")
+ response = self.client.get(f"{self.mock_rapid_api_url}/Invalid_Name/", headers={"accept": "application/json"},)
self.assertEqual(response.status_code, 422)
# Test POST
def test_rapid_species_post_method_not_allowed(self):
response = self.client.post(
- f"{self.mock_rapid_api_url}/{self.species_url_name}/"
+ f"{self.mock_rapid_api_url}/{self.species_url_name}/",
+ headers={"accept": "application/json"},
)
self.assertEqual(response.status_code, 405)
@@ -277,6 +200,7 @@ def test_rapid_species_500_internal_server_error(
)
response = self.client.get(
- f"{self.mock_rapid_api_url}/{self.species_url_name}/"
+ f"{self.mock_rapid_api_url}/{self.species_url_name}/",
+ headers={"accept": "application/json"},
)
self.assertEqual(response.status_code, 500)