Skip to content

Commit

Permalink
add(user service) (#118)
Browse files Browse the repository at this point in the history
# Description

add user service
  • Loading branch information
ChrOertlin authored Apr 3, 2024
1 parent 0a24d1d commit 34d6b55
Show file tree
Hide file tree
Showing 11 changed files with 285 additions and 112 deletions.
33 changes: 27 additions & 6 deletions genotype_api/api/endpoints/analyses.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"""Routes for analysis"""

from fastapi import APIRouter, Depends, File, Query, UploadFile, status
from http import HTTPStatus

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

Expand All @@ -9,6 +11,7 @@
from genotype_api.dto.analysis import AnalysisResponse

from genotype_api.database.session_handler import get_session
from genotype_api.exceptions import AnalysisNotFoundError
from genotype_api.security import get_active_user
from genotype_api.services.analysis_service.analysis_service import AnalysisService

Expand All @@ -26,7 +29,13 @@ def read_analysis(
current_user: User = Depends(get_active_user),
):
"""Return analysis."""
return analysis_service.get_analysis_with_genotype(analysis_id)
try:
return analysis_service.get_analysis(analysis_id)
except AnalysisNotFoundError:
raise HTTPException(
detail=f"Could not find analysis with id: {analysis_id}",
status_code=HTTPStatus.BAD_REQUEST,
)


@router.get("/", response_model=list[AnalysisResponse], response_model_exclude={"genotypes"})
Expand All @@ -37,7 +46,13 @@ def read_analyses(
current_user: User = Depends(get_active_user),
):
"""Return all analyses."""
return analysis_service.get_analyses_to_display(skip=skip, limit=limit)
try:
return analysis_service.get_analyses(skip=skip, limit=limit)
except AnalysisNotFoundError:
raise HTTPException(
detail="Could not fetch analyses from backend.",
status_code=HTTPStatus.BAD_REQUEST,
)


@router.delete("/{analysis_id}")
Expand All @@ -46,9 +61,15 @@ def delete_analysis(
analysis_service: AnalysisService = Depends(get_analysis_service),
current_user: User = Depends(get_active_user),
):
"""Delete analysis based on analysis_id"""
analysis_service.delete_analysis(analysis_id)
return JSONResponse(f"Deleted analysis: {analysis_id}", status_code=status.HTTP_200_OK)
"""Delete analysis based on analysis id."""
try:
analysis_service.delete_analysis(analysis_id)
except AnalysisNotFoundError:
raise HTTPException(
detail=f"Could not find analysis with id: {analysis_id}",
status_code=HTTPStatus.BAD_REQUEST,
)
return JSONResponse(content=f"Deleted analysis: {analysis_id}", status_code=status.HTTP_200_OK)


@router.post(
Expand Down
34 changes: 25 additions & 9 deletions genotype_api/api/endpoints/plates.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
"""Routes for plates"""

from http import HTTPStatus
from typing import Literal
from fastapi import APIRouter, Depends, File, Query, UploadFile, status
from fastapi import APIRouter, Depends, File, Query, UploadFile, status, HTTPException
from fastapi.responses import JSONResponse
from sqlmodel import Session
from genotype_api.database.filter_models.plate_models import PlateOrderParams
from genotype_api.database.models import User
from genotype_api.database.session_handler import get_session
from genotype_api.dto.plate import PlateResponse
from genotype_api.exceptions import PlateNotFoundError
from genotype_api.security import get_active_user
from genotype_api.services.plate_service.plate_service import PlateService

Expand Down Expand Up @@ -85,8 +87,12 @@ def read_plate(
current_user: User = Depends(get_active_user),
):
"""Display information about a plate."""

return plate_service.read_plate(plate_id=plate_id)
try:
return 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
)


@router.get(
Expand All @@ -107,7 +113,12 @@ async def read_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)
try:
return 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}")
Expand All @@ -117,8 +128,13 @@ def delete_plate(
current_user: User = Depends(get_active_user),
):
"""Delete plate."""
analysis_ids = plate_service.delete_plate(plate_id)
return JSONResponse(
f"Deleted plate: {plate_id} and analyses: {analysis_ids}",
status_code=status.HTTP_200_OK,
)
try:
analysis_ids = plate_service.delete_plate(plate_id)
return JSONResponse(
f"Deleted plate: {plate_id} and analyses: {analysis_ids}",
status_code=status.HTTP_200_OK,
)
except PlateNotFoundError:
raise HTTPException(
detail=f"Could not find plate with id: {plate_id}", status_code=HTTPStatus.BAD_REQUEST
)
85 changes: 41 additions & 44 deletions genotype_api/api/endpoints/users.py
Original file line number Diff line number Diff line change
@@ -1,90 +1,87 @@
"""Routes for users"""

from fastapi import APIRouter, Depends, HTTPException, Query
from fastapi import APIRouter, Depends, Query, HTTPException
from pydantic import EmailStr
from sqlmodel import Session
from sqlmodel.sql.expression import Select, SelectOfScalar
from starlette import status

from starlette.responses import JSONResponse

from genotype_api.database.crud.read import (
get_user_by_id,
get_user_by_email,
get_users_with_skip_and_limit,
)
from genotype_api.database.crud.update import update_user_email
from genotype_api.database.crud import delete, create
from genotype_api.database.models import User
from genotype_api.dto.dto import UserRead, UserCreate, UserReadWithPlates
from genotype_api.database.session_handler import get_session
from genotype_api.dto.user import UserRequest, UserResponse
from genotype_api.exceptions import UserNotFoundError, UserArchiveError, UserExistsError
from genotype_api.security import get_active_user
from genotype_api.services.user_service.user_service import UserService

SelectOfScalar.inherit_cache = True
Select.inherit_cache = True
router = APIRouter()


router = APIRouter()
def get_user_service(session: Session = Depends(get_session)) -> UserService:
return UserService(session)


@router.get("/{user_id}", response_model=UserReadWithPlates)
@router.get("/{user_id}", response_model=UserResponse)
def read_user(
user_id: int,
session: Session = Depends(get_session),
user_service: UserService = Depends(get_user_service),
current_user: User = Depends(get_active_user),
) -> User:
return get_user_by_id(session=session, user_id=user_id)
) -> UserResponse:
try:
return user_service.get_user(user_id)
except UserNotFoundError:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found")


@router.delete("/{user_id}")
def delete_user(
user_id: int,
session: Session = Depends(get_session),
user_service: UserService = Depends(get_user_service),
current_user: User = Depends(get_active_user),
) -> JSONResponse:
user: User = get_user_by_id(session=session, user_id=user_id)
if not user:
try:
user_service.delete_user(user_id)
except UserNotFoundError:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found")
if user.plates:
raise HTTPException(
except UserArchiveError:
HTTPException(
status_code=status.HTTP_406_NOT_ACCEPTABLE,
detail="User previously signed plates, please archive instead",
)
delete.delete_user(session=session, user=user)
return JSONResponse(content="User deleted successfully", status_code=status.HTTP_200_OK)
return JSONResponse(content=f"Deleted user with id: {user_id}.", status_code=status.HTTP_200_OK)


@router.put("/{user_id}/email", response_model=User)
@router.put("/{user_id}/email", response_model=UserResponse, response_model_exclude={"plates"})
def change_user_email(
user_id: int,
email: EmailStr,
session: Session = Depends(get_session),
user_service: UserService = Depends(get_user_service),
current_user: User = Depends(get_active_user),
) -> User:
user: User = get_user_by_id(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)
return user
) -> UserResponse:
try:
return user_service.update_user_email(user_id=user_id, email=email)
except UserNotFoundError:
HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="User not found")


@router.get("/", response_model=list[UserRead])
@router.get("/", response_model=list[UserResponse], response_model_exclude={"plates"})
def read_users(
skip: int = 0,
limit: int = Query(default=100, lte=100),
session: Session = Depends(get_session),
user_service: UserService = Depends(get_user_service),
current_user: User = Depends(get_active_user),
) -> list[User]:
return get_users_with_skip_and_limit(session=session, skip=skip, limit=limit)
) -> list[UserResponse]:

return user_service.get_users(skip=skip, limit=limit)


@router.post("/", response_model=User)
@router.post("/", response_model=UserResponse, response_model_exclude={"plates"})
def create_user(
user: UserCreate,
session: Session = Depends(get_session),
user: UserRequest,
user_service: UserService = Depends(get_user_service),
current_user: User = Depends(get_active_user),
):
existing_user: User = get_user_by_email(session=session, email=user.email)
if existing_user:
raise HTTPException(status_code=409, detail="Email already registered")
new_user: User = create.create_user(session=session, user=user)
return new_user
try:
return user_service.create_user(user)
except UserExistsError:
HTTPException(status_code=409, detail="Email already registered.")
3 changes: 2 additions & 1 deletion genotype_api/database/crud/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from genotype_api.database.models import Analysis, Plate, Sample, User, SNP
from genotype_api.dto.dto import UserCreate, PlateCreate
from genotype_api.dto.user import UserRequest

SelectOfScalar.inherit_cache = True
Select.inherit_cache = True
Expand Down Expand Up @@ -51,7 +52,7 @@ def create_analyses_samples(session: Session, analyses: list[Analysis]) -> list[
]


def create_user(session: Session, user: UserCreate):
def create_user(session: Session, user: UserRequest):
db_user = User.from_orm(user)
session.add(db_user)
session.commit()
Expand Down
12 changes: 0 additions & 12 deletions genotype_api/dto/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

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


class AnalysisResponse(BaseModel):
Expand All @@ -18,14 +17,3 @@ class AnalysisResponse(BaseModel):
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
29 changes: 24 additions & 5 deletions genotype_api/dto/plate.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
from collections import Counter
from datetime import datetime

from pydantic import BaseModel, validator, Field
from genotype_api.dto.analysis import AnalysisSampleResponse
from genotype_api.dto.user import UserResponse
from pydantic import BaseModel, validator, Field, EmailStr

from genotype_api.constants import Types, Sexes

from genotype_api.dto.sample import SampleStatusResponse


class PlateStatusCounts(BaseModel):
Expand All @@ -20,6 +22,23 @@ class Config:
allow_population_by_field_name = True


class UserOnPlate(BaseModel):
email: EmailStr | None = None
name: str | None = None
id: int | None = None


class AnalysisOnPlate(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


class PlateResponse(BaseModel):
created_at: datetime | None = None
plate_id: str | None = None
Expand All @@ -28,8 +47,8 @@ class PlateResponse(BaseModel):
method_document: str | None = None
method_version: str | None = None
id: str | None = None
user: UserResponse | None = None
analyses: list[AnalysisSampleResponse] | None = None
user: UserOnPlate | None = None
analyses: list[AnalysisOnPlate] | None = None
plate_status_counts: PlateStatusCounts | None = None

@validator("plate_status_counts")
Expand Down
16 changes: 16 additions & 0 deletions genotype_api/dto/user.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,25 @@
"""Module for the plate DTOs."""

from datetime import datetime

from pydantic import BaseModel, EmailStr


class PlateOnUser(BaseModel):
created_at: datetime | None = None
plate_id: str | None = None
signed_by: int | None = None
signed_at: datetime | None = None
id: str | None = None


class UserResponse(BaseModel):
email: EmailStr | None = None
name: str | None = None
id: int | None = None
plates: list[PlateOnUser] | None = None


class UserRequest(BaseModel):
email: EmailStr
name: str
Loading

0 comments on commit 34d6b55

Please sign in to comment.