Skip to content

Commit

Permalink
feat: added pagination, separated get applications endpoints for FI a…
Browse files Browse the repository at this point in the history
…nd OCP users, changed responses to ApplicationResponse in borrowers and awards endpoints
  • Loading branch information
Lombardoh committed Jun 14, 2023
1 parent d687c8e commit 0fe32e2
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 46 deletions.
62 changes: 54 additions & 8 deletions app/routers/applications.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from datetime import datetime
from typing import List

from fastapi import APIRouter, Depends, Query
from sqlalchemy.orm import Session
Expand All @@ -16,7 +15,7 @@


@router.put(
"/applications/{application_id}/award",
"/applications/{application_id}/award/",
tags=["applications"],
response_model=core.Application,
)
Expand All @@ -37,13 +36,45 @@ async def update_application_award(
core.ApplicationActionType.AWARD_UPDATE,
payload,
)

return application


@router.patch(
"/applications/{application_id}/borrower/",
tags=["applications"],
response_model=ApiSchema.ApplicationResponse,
)
async def update_application_borrower(
application_id: int,
payload: ApiSchema.BorrowerUpdate,
user: core.User = Depends(get_user),
session: Session = Depends(get_db),
):
with transaction_session(session):
print(payload)
application = utils.update_application_borrower(
session, application_id, payload, user
)

utils.create_application_action(
session,
user.id,
application_id,
core.ApplicationActionType.BORROWER_UPDATE,
payload,
)
return ApiSchema.ApplicationResponse(
application=application,
borrower=application.borrower,
award=application.award,
)


@router.get(
"/applications/{id}",
tags=["applications"],
response_model=core.Application,
response_model=ApiSchema.ApplicationResponse,
)
@OCP_only()
async def get_application(
Expand All @@ -56,23 +87,38 @@ async def get_application(
session.query(core.Application).filter(core.Application.id == id).first()
)

return application
return ApiSchema.ApplicationResponse(
application=application, borrower=application.borrower, award=application.award
)


@router.get(
"/applications-list/",
tags=["applications"],
response_model=ApiSchema.Pagination,
)
@OCP_only()
async def get_applications_list(
page: int = Query(1, gt=0),
page_size: int = Query(10, gt=0),
current_user: core.User = Depends(get_current_user),
session: Session = Depends(get_db),
user: core.User = None,
):
return utils.get_all_active_applications(page, page_size, session)


@router.get(
"/applications/",
tags=["applications"],
response_model=List[core.Application],
response_model=ApiSchema.Pagination,
)
async def get_applications(
page: int = Query(1, gt=0),
page_size: int = Query(10, gt=0),
user: core.User = Depends(get_user),
session: Session = Depends(get_db),
):
if user.is_OCP():
return utils.get_all_active_applications(page, page_size, session)

return utils.get_all_FI_user_applications(page, page_size, session, user.id)


Expand Down
58 changes: 40 additions & 18 deletions app/schema/api.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,47 @@
from decimal import Decimal
from typing import Optional
from typing import List, Optional

from pydantic import BaseModel

from app.schema.core import Application, Award, Borrower
from app.schema import core


class Pagination(BaseModel):
items: List[core.Application]
total: int
page: int
page_size: int
total_pages: int
next_page: Optional[int]
previous_page: Optional[int]


class AwardUpdate(BaseModel):
source_contract_id: str
title: str
description: str
award_currency: str
payment_method: dict
buyer_name: str
source_url: str
entity_code: str
contract_status: str
previous: bool
procurement_method: str
contracting_process_id: str
procurement_category: str
source_contract_id: Optional[str]
title: Optional[str]
description: Optional[str]
award_currency: Optional[str]
payment_method: Optional[dict]
buyer_name: Optional[str]
source_url: Optional[str]
entity_code: Optional[str]
contract_status: Optional[str]
previous: Optional[bool]
procurement_method: Optional[str]
contracting_process_id: Optional[str]
procurement_category: Optional[str]


class BorrowerUpdate(BaseModel):
borrower_identifier: Optional[str]
legal_name: Optional[str]
email: Optional[str]
address: Optional[str]
legal_identifier: Optional[str]
type: Optional[str]
sector: Optional[str]
size: Optional[core.BorrowerSize]
status: Optional[core.BorrowerStatus]


class ApplicationUpdate(BaseModel):
Expand All @@ -33,9 +55,9 @@ class ApplicationUpdate(BaseModel):


class ApplicationResponse(BaseModel):
application: Application
borrower: Borrower
award: Award
application: core.Application
borrower: core.Borrower
award: core.Award


class ApplicationBase(BaseModel):
Expand Down
88 changes: 70 additions & 18 deletions app/utils/applications.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from datetime import datetime
from typing import List
from math import ceil

from fastapi import HTTPException, status
from fastapi.encoders import jsonable_encoder
from sqlalchemy.orm import Session, defaultload

from app.schema.api import Pagination

from ..schema import core

excluded_applications = [
Expand All @@ -29,7 +31,7 @@ def create_application_action(
type: core.ApplicationAction,
payload: dict,
) -> core.ApplicationAction:
update_dict = jsonable_encoder(payload)
update_dict = jsonable_encoder(payload, exclude_unset=True)

new_action = core.ApplicationAction(
type=type,
Expand All @@ -38,12 +40,41 @@ def create_application_action(
user_id=user_id,
)
session.add(new_action)
session.commit()
session.refresh(new_action)

return new_action


def update_application_borrower(
session: Session, application_id: int, payload: dict, user: core.User
) -> core.Application:
application = (
session.query(core.Application)
.filter(core.Application.id == application_id)
.options(defaultload(core.Application.borrower))
.first()
)
if not application or not application.borrower:
raise HTTPException(status_code=404, detail="Application or borrower not found")

if application.status == core.ApplicationStatus.APPROVED:
raise HTTPException(
status_code=409, detail="Approved applications cannot be updated"
)

if user.is_OCP() and application.status not in OCP_can_modify:
raise HTTPException(
status_code=409, detail="This application cannot be updated by OCP Admins"
)

update_dict = jsonable_encoder(payload, exclude_unset=True)
for field, value in update_dict.items():
setattr(application.borrower, field, value)

session.add(application)

return application


def update_application_award(
session: Session, application_id: int, payload: dict, user: core.User
) -> core.Application:
Expand Down Expand Up @@ -78,30 +109,51 @@ def update_application_award(

def get_all_active_applications(
page: int, page_size: int, session: Session
) -> List[core.Application]:
) -> Pagination:
applications_query = session.query(core.Application).filter(
core.Application.status.notin_(excluded_applications)
)

total_count = applications_query.count()
total_pages = ceil(total_count / page_size)

applications = (
session.query(core.Application)
.filter(core.Application.status.notin_(excluded_applications))
.offset((page - 1) * page_size)
.limit(page_size)
.all()
applications_query.offset((page - 1) * page_size).limit(page_size).all()
)

return applications
return Pagination(
items=applications,
total=total_count,
page=page,
page_size=page_size,
total_pages=total_pages,
next_page=page + 1 if page < total_pages else None,
previous_page=page - 1 if page > 1 else None,
)


def get_all_FI_user_applications(
page: int, page_size: int, session: Session, user_id
) -> List[core.Application]:
) -> Pagination:
applications_query = session.query(core.Application).filter(
core.Application.lender_id == user_id
)
total_count = applications_query.count()
total_pages = ceil(total_count / page_size)

applications = (
session.query(core.Application)
.filter(core.Application.lender_id == user_id)
.offset((page - 1) * page_size)
.limit(page_size)
.all()
applications_query.offset((page - 1) * page_size).limit(page_size).all()
)

return applications
return Pagination(
items=applications,
total=total_count,
page=page,
page_size=page_size,
total_pages=total_pages,
next_page=page + 1 if page < total_pages else None,
previous_page=page - 1 if page > 1 else None,
)


def get_application_by_uuid(uuid: str, session: Session):
Expand Down
7 changes: 6 additions & 1 deletion app/utils/permissions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from functools import wraps

from fastapi import HTTPException, status

from ..schema.core import User


Expand All @@ -18,7 +20,10 @@ async def wrapper(*args, **kwargs):
kwargs["user"] = user # Pass the user as a keyword argument
return await func(*args, **kwargs)
else:
return {"message": "Insufficient permissions"}
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Insufficient permissions",
)

return wrapper

Expand Down
2 changes: 1 addition & 1 deletion background_processes/application_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,6 @@ def create_application(
try:
insert_application(application)
except Exception as e:
print(e)
raise e

return new_uuid

0 comments on commit 0fe32e2

Please sign in to comment.