Skip to content

Commit

Permalink
Revert "add(plate service) (#111) (patch)"
Browse files Browse the repository at this point in the history
This reverts commit 4fbdea7.
  • Loading branch information
ChrOertlin authored Mar 25, 2024
1 parent f2e2319 commit 5089d47
Show file tree
Hide file tree
Showing 16 changed files with 165 additions and 292 deletions.
4 changes: 2 additions & 2 deletions genotype_api/api/endpoints/analyses.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@
get_analysis_by_id,
)
from genotype_api.database.models import Analysis, User
from genotype_api.dto.analysis import AnalysisGenotypeResponse, AnalysisResponse
from genotype_api.dto.analysis import AnalysisWithGenotypeResponse, AnalysisResponse
from genotype_api.database.session_handler import get_session
from genotype_api.security import get_active_user
from genotype_api.services.analysis_service.analysis_service import AnalysisService

router = APIRouter()


@router.get("/{analysis_id}", response_model=AnalysisGenotypeResponse)
@router.get("/{analysis_id}", response_model=AnalysisWithGenotypeResponse)
def read_analysis(
analysis_id: int,
session: Session = Depends(get_session),
Expand Down
139 changes: 99 additions & 40 deletions genotype_api/api/endpoints/plates.py
Original file line number Diff line number Diff line change
@@ -1,65 +1,117 @@
"""Routes for plates"""

from typing import Literal
from fastapi import APIRouter, Depends, File, Query, UploadFile, status
from datetime import datetime
from io import BytesIO
from pathlib import Path
from typing import Literal, Sequence

from fastapi import APIRouter, Depends, File, HTTPException, Query, UploadFile, status
from fastapi.responses import JSONResponse
from sqlalchemy import asc, desc
from sqlmodel import Session
from genotype_api.database.filter_models.plate_models import PlateOrderParams
from genotype_api.database.models import User
from sqlmodel.sql.expression import Select, SelectOfScalar

from genotype_api.database.crud.create import create_analyses_samples, create_plate
from genotype_api.database.crud.delete import delete_analysis
from genotype_api.database.crud.read import (
check_analyses_objects,
get_analyses_from_plate,
get_plate,
get_plate_read_analysis_single,
get_ordered_plates,
get_user_by_email,
)
from genotype_api.database.crud.update import (
refresh_sample_status,
refresh_plate,
update_plate_sign_off,
)
from genotype_api.database.filter_models.plate_models import PlateSignOff, PlateOrderParams
from genotype_api.database.models import (
Analysis,
Plate,
User,
)
from genotype_api.dto.dto import (
PlateCreate,
PlateReadWithAnalyses,
PlateReadWithAnalysisDetail,
PlateReadWithAnalysisDetailSingle,
)
from genotype_api.database.session_handler import get_session
from genotype_api.dto.plate import PlateResponse
from genotype_api.file_parsing.excel import GenotypeAnalysis
from genotype_api.file_parsing.files import check_file
from genotype_api.security import get_active_user
from genotype_api.services.plate_service.plate_service import PlateService

SelectOfScalar.inherit_cache = True
Select.inherit_cache = True

router = APIRouter()


def get_plate_service(session: Session = Depends(get_session)) -> PlateService:
return PlateService(session)
def get_plate_id_from_file(file_name: Path) -> str:
# Get the plate id from the standardized name of the plate
return file_name.name.split("_", 1)[0]


@router.post(
"/plate",
response_model=PlateResponse,
response_model_exclude={"detail"},
)
@router.post("/plate", response_model=PlateReadWithAnalyses)
def upload_plate(
file: UploadFile = File(...),
plate_service: PlateService = Depends(get_plate_service),
session: Session = Depends(get_session),
current_user: User = Depends(get_active_user),
):
return plate_service.upload_plate(file)


@router.patch(
"/{plate_id}/sign-off",
response_model=PlateResponse,
response_model_exclude={"analyses", "user", "detail"},
)
file_name: Path = check_file(file_path=file.filename, extension=".xlsx")
plate_id: str = get_plate_id_from_file(file_name)
db_plate = session.get(Plate, plate_id)
if db_plate:
raise HTTPException(
status_code=status.HTTP_409_CONFLICT,
detail=f"Plate with id {db_plate.id} already exists",
)

excel_parser = GenotypeAnalysis(
excel_file=BytesIO(file.file.read()),
file_name=str(file_name),
include_key="-CG-",
)
analyses: list[Analysis] = list(excel_parser.generate_analyses())
check_analyses_objects(session=session, analyses=analyses, analysis_type="genotype")
create_analyses_samples(session=session, analyses=analyses)
plate_obj = PlateCreate(plate_id=plate_id)
plate_obj.analyses = analyses
plate: Plate = create_plate(session=session, plate=plate_obj)
for analysis in plate.analyses:
refresh_sample_status(sample=analysis.sample, session=session)
refresh_plate(session=session, plate=plate)
return plate


@router.patch("/{plate_id}/sign-off", response_model=Plate)
def sign_off_plate(
plate_id: int,
method_document: str = Query(...),
method_version: str = Query(...),
plate_service: PlateService = Depends(get_plate_service),
session: Session = Depends(get_session),
current_user: User = Depends(get_active_user),
):
"""Sign off a plate.
This means that current User sign off that the plate is checked
Add Depends with current user
"""

return plate_service.update_plate_sign_off(
plate_id=plate_id,
user_email=current_user.email,
method_version=method_version,
user: User = get_user_by_email(session=session, email=current_user.email)
plate: Plate = get_plate(session=session, plate_id=plate_id)
plate_sign_off = PlateSignOff(
user_id=user.id,
signed_at=datetime.now(),
method_document=method_document,
method_version=method_version,
)
return update_plate_sign_off(session=session, plate=plate, plate_sign_off=plate_sign_off)


@router.get(
"/{plate_id}",
response_model=PlateResponse,
response_model=PlateReadWithAnalysisDetailSingle,
response_model_by_alias=False,
response_model_exclude={
"analyses": {
Expand All @@ -81,17 +133,16 @@ def sign_off_plate(
)
def read_plate(
plate_id: int,
plate_service: PlateService = Depends(get_plate_service),
session: Session = Depends(get_session),
current_user: User = Depends(get_active_user),
):
"""Display information about a plate."""

return plate_service.read_plate(plate_id=plate_id)
return get_plate_read_analysis_single(session=session, plate_id=plate_id)


@router.get(
"/",
response_model=list[PlateResponse],
response_model=list[PlateReadWithAnalysisDetail],
response_model_exclude={"analyses"},
response_model_by_alias=False,
)
Expand All @@ -100,25 +151,33 @@ async def read_plates(
sort_order: Literal["ascend", "descend"] | None = "descend",
skip: int | None = 0,
limit: int | None = 10,
plate_service: PlateService = Depends(get_plate_service),
session: Session = Depends(get_session),
current_user: User = Depends(get_active_user),
):
) -> Sequence[Plate]:
"""Display all plates"""

order_params = PlateOrderParams(
order_by=order_by, skip=skip, limit=limit, sort_order=sort_order
)
return plate_service.read_plates(order_params=order_params)
plates: Sequence[Plate] = get_ordered_plates(session=session, order_params=order_params)
return plates


@router.delete("/{plate_id}")
@router.delete("/{plate_id}", response_model=Plate)
def delete_plate(
plate_id: int,
plate_service: PlateService = Depends(get_plate_service),
session: Session = Depends(get_session),
current_user: User = Depends(get_active_user),
):
"""Delete plate."""
analysis_ids = plate_service.delete_plate(plate_id)
plate = session.get(Plate, plate_id)
analyses: list[Analysis] = get_analyses_from_plate(session=session, plate_id=plate_id)
analyse_ids = [analyse.id for analyse in analyses]
for analysis in analyses:
delete_analysis(session=session, analysis=analysis)
delete_plate(session=session, plate=plate)

return JSONResponse(
f"Deleted plate: {plate_id} and analyses: {analysis_ids}",
f"Deleted plate: {plate_id} and analyses: {analyse_ids}",
status_code=status.HTTP_200_OK,
)
8 changes: 4 additions & 4 deletions genotype_api/api/endpoints/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from starlette.responses import JSONResponse

from genotype_api.database.crud.read import (
get_user_by_id,
get_user,
get_user_by_email,
get_users_with_skip_and_limit,
)
Expand All @@ -32,7 +32,7 @@ def read_user(
session: Session = Depends(get_session),
current_user: User = Depends(get_active_user),
) -> User:
return get_user_by_id(session=session, user_id=user_id)
return get_user(session=session, user_id=user_id)


@router.delete("/{user_id}")
Expand All @@ -41,7 +41,7 @@ def delete_user(
session: Session = Depends(get_session),
current_user: User = Depends(get_active_user),
) -> JSONResponse:
user: User = get_user_by_id(session=session, user_id=user_id)
user: User = get_user(session=session, user_id=user_id)
if not user:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found")
if user.plates:
Expand All @@ -60,7 +60,7 @@ def change_user_email(
session: Session = Depends(get_session),
current_user: User = Depends(get_active_user),
) -> User:
user: User = get_user_by_id(session=session, user_id=user_id)
user: User = get_user(session=session, user_id=user_id)
if not user:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found")
user: User = update_user_email(session=session, user=user, email=email)
Expand Down
2 changes: 1 addition & 1 deletion genotype_api/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class Sexes(str, Enum):
UNKNOWN = "unknown"


class Status(str, Enum):
class STATUS(str, Enum):
PASS = "pass"
FAIL = "fail"
CANCEL = "cancel"
Expand Down
16 changes: 13 additions & 3 deletions genotype_api/database/crud/read.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,23 +76,33 @@ def get_analysis_by_type_and_sample_id(
).one()


def get_plate_by_id(session: Session, plate_id: int) -> Plate:
def get_plate(session: Session, plate_id: int) -> Plate:
"""Get plate"""
statement = select(Plate).where(Plate.id == plate_id)
return session.exec(statement).one()


def get_plate_read_analysis_single(
session: Session, plate_id: int
) -> PlateReadWithAnalysisDetailSingle:
plate: Plate = get_plate(session=session, plate_id=plate_id)
return PlateReadWithAnalysisDetailSingle.from_orm(plate)


def get_ordered_plates(session: Session, order_params: PlateOrderParams) -> list[Plate]:
sort_func = desc if order_params.sort_order == "descend" else asc
return session.exec(
plates: list[Plate] = session.exec(
select(Plate)
.order_by(sort_func(order_params.order_by))
.offset(order_params.skip)
.limit(order_params.limit)
).all()
return plates


def get_incomplete_samples(statement: SelectOfScalar) -> SelectOfScalar:
"""Returning sample query statement for samples with less than two analyses."""

return (
statement.group_by(Analysis.sample_id)
.order_by(Analysis.created_at)
Expand Down Expand Up @@ -152,7 +162,7 @@ def get_samples(statement: SelectOfScalar, sample_id: str) -> SelectOfScalar:
return statement.where(Sample.id.contains(sample_id))


def get_user_by_id(session: Session, user_id: int):
def get_user(session: Session, user_id: int):
statement = select(User).where(User.id == user_id)
return session.exec(statement).one()

Expand Down
4 changes: 2 additions & 2 deletions genotype_api/database/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from sqlalchemy import Index
from sqlmodel import Field, Relationship, SQLModel

from genotype_api.constants import Sexes, Status, Types
from genotype_api.constants import Sexes, STATUS, Types


class GenotypeBase(SQLModel):
Expand Down Expand Up @@ -59,7 +59,7 @@ def check_no_calls(self) -> dict[str, int]:


class SampleSlim(SQLModel):
status: Status | None
status: STATUS | None
comment: str | None


Expand Down
18 changes: 3 additions & 15 deletions genotype_api/dto/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
from pydantic import BaseModel

from genotype_api.constants import Sexes, Types
from genotype_api.dto.genotype import GenotypeResponse
from genotype_api.dto.sample import SampleStatusResponse
from genotype_api.dto.genotype import GenotypeBase


class AnalysisResponse(BaseModel):
Expand All @@ -19,23 +18,12 @@ class AnalysisResponse(BaseModel):
id: int | None


class AnalysisGenotypeResponse(BaseModel):
class AnalysisWithGenotypeResponse(BaseModel):
type: Types | None
source: str | None
sex: Sexes | None
created_at: datetime | None
sample_id: str | None
plate_id: str | None
id: int | None
genotypes: list[GenotypeResponse] | None = None


class AnalysisSampleResponse(BaseModel):
type: Types | None
source: str | None
sex: Sexes | None
created_at: datetime | None
sample_id: str | None
plate_id: str | None
id: int | None
sample: SampleStatusResponse | None = None
genotypes: list[GenotypeBase] | None
3 changes: 1 addition & 2 deletions genotype_api/dto/dto.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@

import genotype_api.database.models
from genotype_api.database import models
from genotype_api.models import SampleDetail
from genotype_api.dto.plate import PlateStatusCounts
from genotype_api.models import SampleDetail, PlateStatusCounts
from genotype_api.services.match_genotype_service.utils import check_snps, check_sex


Expand Down
2 changes: 1 addition & 1 deletion genotype_api/dto/genotype.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from pydantic import BaseModel, Field


class GenotypeResponse(BaseModel):
class GenotypeBase(BaseModel):
rsnumber: str = Field(max_length=10)
analysis_id: int
allele_1: str = Field(max_length=1)
Expand Down
Loading

0 comments on commit 5089d47

Please sign in to comment.