Skip to content

Commit

Permalink
Merge branch 'main' into update-python
Browse files Browse the repository at this point in the history
# Conflicts:
#	poetry.lock
#	pyproject.toml
  • Loading branch information
henrikstranneheim committed Oct 24, 2024
2 parents 64d370d + 73005a6 commit f111f31
Show file tree
Hide file tree
Showing 43 changed files with 1,660 additions and 1,265 deletions.
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 3.0.18
current_version = 3.0.19
commit = True
tag = True
tag_name = v{new_version}
Expand Down
2 changes: 1 addition & 1 deletion genotype_api/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "3.0.18"
__version__ = "3.0.19"
49 changes: 30 additions & 19 deletions genotype_api/api/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,46 @@
"""

from fastapi import FastAPI, status, Request
from fastapi.responses import JSONResponse
import logging
from contextlib import asynccontextmanager

from fastapi import FastAPI, Request, status
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from sqlalchemy.exc import NoResultFound, OperationalError

from genotype_api.api.middleware import DBSessionMiddleware
from genotype_api.config import security_settings, settings
from genotype_api.database.database import create_all_tables, initialise_database
from genotype_api.api.endpoints import samples, snps, users, plates, analyses
from sqlalchemy.exc import NoResultFound
from genotype_api.api.endpoints import analyses, plates, samples, snps, users
from genotype_api.config import security_settings

app = FastAPI(
root_path=security_settings.api_root_path,
root_path_in_servers=True,
openapi_prefix=security_settings.api_root_path,
)
LOG = logging.getLogger(__name__)


@asynccontextmanager
async def lifespan(app: FastAPI):
# Startup actions, like connecting to the database
LOG.debug("Starting up...")
yield # This is important, it must yield control
# Shutdown actions, like closing the database connection
LOG.debug("Shutting down...")


app = FastAPI(lifespan=lifespan, root_path=security_settings.api_root_path)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
app.add_middleware(DBSessionMiddleware)


@app.exception_handler(OperationalError)
async def db_connection_exception_handler(request: Request, exc: OperationalError):
LOG.error(f"Database connection error: {exc}")
return JSONResponse(
content={"detail": "Database connection error. Please try again later."},
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
)


@app.exception_handler(NoResultFound)
Expand Down Expand Up @@ -72,9 +89,3 @@ def welcome():
tags=["analyses"],
responses={status.HTTP_404_NOT_FOUND: {"description": "Not found"}},
)


@app.on_event("startup")
def on_startup():
initialise_database(settings.db_uri)
create_all_tables()
24 changes: 10 additions & 14 deletions genotype_api/api/endpoints/analyses.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,15 @@

from http import HTTPStatus

from fastapi import APIRouter, Depends, File, Query, UploadFile, status, HTTPException
from fastapi import APIRouter, Depends, File, HTTPException, Query, UploadFile, status
from fastapi.responses import JSONResponse

from genotype_api.database.store import Store, get_store
from genotype_api.dto.analysis import AnalysisResponse
from genotype_api.dto.user import CurrentUser

from genotype_api.exceptions import AnalysisNotFoundError
from genotype_api.security import get_active_user
from genotype_api.services.endpoint_services.analysis_service import (
AnalysisService,
)

from genotype_api.services.endpoint_services.analysis_service import AnalysisService

router = APIRouter()

Expand All @@ -24,14 +20,14 @@ def get_analysis_service(store: Store = Depends(get_store)) -> AnalysisService:


@router.get("/{analysis_id}", response_model=AnalysisResponse)
def read_analysis(
async def read_analysis(
analysis_id: int,
analysis_service: AnalysisService = Depends(get_analysis_service),
current_user: CurrentUser = Depends(get_active_user),
):
"""Return analysis."""
try:
return analysis_service.get_analysis(analysis_id)
return await analysis_service.get_analysis(analysis_id)
except AnalysisNotFoundError:
raise HTTPException(
detail=f"Could not find analysis with id: {analysis_id}",
Expand All @@ -40,15 +36,15 @@ def read_analysis(


@router.get("/", response_model=list[AnalysisResponse], response_model_exclude={"genotypes"})
def read_analyses(
async def read_analyses(
skip: int = 0,
limit: int = Query(default=100, lte=100),
analysis_service: AnalysisService = Depends(get_analysis_service),
current_user: CurrentUser = Depends(get_active_user),
):
"""Return all analyses."""
try:
return analysis_service.get_analyses(skip=skip, limit=limit)
return await analysis_service.get_analyses(skip=skip, limit=limit)
except AnalysisNotFoundError:
raise HTTPException(
detail="Could not fetch analyses from backend.",
Expand All @@ -57,14 +53,14 @@ def read_analyses(


@router.delete("/{analysis_id}")
def delete_analysis(
async def delete_analysis(
analysis_id: int,
analysis_service: AnalysisService = Depends(get_analysis_service),
current_user: CurrentUser = Depends(get_active_user),
):
"""Delete analysis based on analysis id."""
try:
analysis_service.delete_analysis(analysis_id)
await analysis_service.delete_analysis(analysis_id)
except AnalysisNotFoundError:
raise HTTPException(
detail=f"Could not find analysis with id: {analysis_id}",
Expand All @@ -76,12 +72,12 @@ def delete_analysis(
@router.post(
"/sequence", response_model=list[AnalysisResponse], response_model_exclude={"genotypes"}
)
def upload_sequence_analysis(
async def upload_sequence_analysis(
file: UploadFile = File(...),
analysis_service: AnalysisService = Depends(get_analysis_service),
current_user: CurrentUser = Depends(get_active_user),
):
"""Reading VCF file, creating and uploading sequence analyses and sample objects to the database."""

analyses: list[AnalysisResponse] = analysis_service.get_upload_sequence_analyses(file)
analyses: list[AnalysisResponse] = await analysis_service.get_upload_sequence_analyses(file)
return analyses
27 changes: 13 additions & 14 deletions genotype_api/api/endpoints/plates.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,18 @@

from http import HTTPStatus
from typing import Literal
from fastapi import APIRouter, Depends, File, Query, UploadFile, status, HTTPException
from fastapi.responses import JSONResponse
from genotype_api.database.filter_models.plate_models import PlateOrderParams

from fastapi import APIRouter, Depends, File, HTTPException, Query, UploadFile, status
from fastapi.responses import JSONResponse

from genotype_api.database.filter_models.plate_models import PlateOrderParams
from genotype_api.database.store import Store, get_store
from genotype_api.dto.plate import PlateResponse
from genotype_api.dto.user import CurrentUser
from genotype_api.exceptions import PlateNotFoundError, PlateExistsError
from genotype_api.exceptions import PlateExistsError, PlateNotFoundError
from genotype_api.security import get_active_user
from genotype_api.services.endpoint_services.plate_service import PlateService


router = APIRouter()


Expand All @@ -25,14 +24,14 @@ def get_plate_service(store: Store = Depends(get_store)) -> PlateService:
@router.post(
"/plate",
)
def upload_plate(
async def upload_plate(
file: UploadFile = File(...),
plate_service: PlateService = Depends(get_plate_service),
current_user: CurrentUser = Depends(get_active_user),
):

try:
plate_service.upload_plate(file)
await plate_service.upload_plate(file)
except PlateExistsError:
raise HTTPException(
detail="Plate already exists in the database.", status_code=HTTPStatus.BAD_REQUEST
Expand All @@ -45,7 +44,7 @@ def upload_plate(
response_model=PlateResponse,
response_model_exclude={"analyses", "user", "plate_status_counts"},
)
def sign_off_plate(
async def sign_off_plate(
plate_id: int,
method_document: str = Query(...),
method_version: str = Query(...),
Expand All @@ -57,7 +56,7 @@ def sign_off_plate(
Add Depends with current user
"""

return plate_service.update_plate_sign_off(
return await plate_service.update_plate_sign_off(
plate_id=plate_id,
user_email=current_user.email,
method_version=method_version,
Expand Down Expand Up @@ -87,14 +86,14 @@ def sign_off_plate(
}
},
)
def read_plate(
async def read_plate(
plate_id: int,
plate_service: PlateService = Depends(get_plate_service),
current_user: CurrentUser = Depends(get_active_user),
):
"""Display information about a plate."""
try:
return plate_service.get_plate(plate_id=plate_id)
return await plate_service.get_plate(plate_id=plate_id)
except PlateNotFoundError:
raise HTTPException(
detail=f"Could not find plate with id: {plate_id}", status_code=HTTPStatus.BAD_REQUEST
Expand All @@ -120,22 +119,22 @@ async def read_plates(
order_by=order_by, skip=skip, limit=limit, sort_order=sort_order
)
try:
return plate_service.get_plates(order_params=order_params)
return await plate_service.get_plates(order_params=order_params)
except PlateNotFoundError:
raise HTTPException(
detail="Could not fetch plates from backend.", status_code=HTTPStatus.BAD_REQUEST
)


@router.delete("/{plate_id}")
def delete_plate(
async def delete_plate(
plate_id: int,
plate_service: PlateService = Depends(get_plate_service),
current_user: CurrentUser = Depends(get_active_user),
):
"""Delete plate."""
try:
analysis_ids = plate_service.delete_plate(plate_id)
analysis_ids = await plate_service.delete_plate(plate_id)
return JSONResponse(
f"Deleted plate: {plate_id} and analyses: {analysis_ids}",
status_code=status.HTTP_200_OK,
Expand Down
Loading

0 comments on commit f111f31

Please sign in to comment.