Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2962: CAN Summary Card - update funding summary endpoint #3075

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 71 additions & 59 deletions backend/openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -711,7 +711,7 @@ paths:
summary: Get a list of all the CAN funding budgets
parameters:
- $ref: "#/components/parameters/simulatedError"
description: Get CANFundingBudgetss
description: Get CANFundingBudgets
responses:
"200":
description: OK
Expand Down Expand Up @@ -1080,13 +1080,13 @@ paths:
get:
tags:
- CAN Funding Summary
operationId: getFundingTotals
description: Get Funding Totals
operationId: getFundingSummary
description: Get Funding Summary
parameters:
- $ref: "#/components/parameters/simulatedError"
- name: can_ids
in: query
description: List of CAN Ids (required)
description: List of CAN Ids (required), Use an integer of 0 to get funding summary for all CANs.
required: true
schema:
type: array
Expand Down Expand Up @@ -1146,84 +1146,96 @@ paths:
schema:
type: object
properties:
available_funding:
type: string
carry_forward_funding:
type: integer
can:
type: object
properties:
appropriation_term:
type: integer
authorizer_id:
type: integer
arrangement_type_id:
type: integer
number:
can:
type: array
items:
type: object
properties:
appropriation_term:
type: integer
authorizer_id:
type: integer
arrangement_type_id:
type: integer
number:
type: string
purpose:
type: string
managing_portfolio_id:
type: integer
nickname:
type: string
appropriation_date:
type: string
description:
type: string
id:
type: integer
expiration_date:
type: string
managing_project_id:
type: integer
carry_forward_label:
type: string
purpose:
type: string
managing_portfolio_id:
type: integer
nickname:
type: string
appropriation_date:
type: string
description:
type: string
id:
type: integer
example: ""
expiration_date:
type: string
managing_project_id:
type: integer
received_funding:
type: string
planned_funding:
type: integer
obligated_funding:
type: integer
available_funding:
type: string
total_funding:
example: ""
expected_funding:
type: string
expiration_date:
in_draft_funding:
type: string
in_execution_funding:
type: string
carry_forward_funding:
type: integer
carry_forward_label:
new_funding:
type: string
expected_funding:
obligated_funding:
type: integer
planned_funding:
type: integer
received_funding:
type: string
new_funding:
total_funding:
type: string
examples:
"0":
value: |
{
"available_funding": "8000000.00",
"can": {
"appropriation_date": "01/10/2022",
"appropriation_term": 1,
"arrangement_type_id": 4,
"authorizer_id": 26,
"description": "Child Development Research Fellowship Grant Program",
"expiration_date": "01/09/2023",
"id": 4,
"managing_portfolio_id": 1,
"managing_project_id": 1,
"nickname": "ASPE SRCD-IDDA",
"number": "G990136",
"purpose": ""
},
"carry_forward_funding": 0,
"carry_forward_label": "Carry-Forward",
"received_funding": "6000000.00",
"cans": [{
"can":{
"appropriation_date": "01/10/2022",
"appropriation_term": 1,
"arrangement_type_id": 4,
"authorizer_id": 26,
"description": "Child Development Research Fellowship Grant Program",
"expiration_date": "01/09/2023",
"id": 4,
"managing_portfolio_id": 1,
"managing_project_id": 1,
"nickname": "ASPE SRCD-IDDA",
"number": "G990136",
"purpose": ""
},
"carry_forward_label": "Carry-Forward",
"expiration_date": "09/01/2023",
}],
"expected_funding": "4000000.00",
"expiration_date": "09/01/2023",
"in_draft_funding": "500000.00",
"in_execution_funding": "2000000.00",
"new_funding": "2000000.00",
"obligated_funding": 0,
"planned_funding": 0,
"total_funding": "10000000.00",
"new_funding": "2000000.00"
"received_funding": "6000000.00",
"total_funding": "10000000.00"
}
/api/v1/users/:
get:
Expand Down
64 changes: 59 additions & 5 deletions backend/ops_api/ops/resources/can_funding_summary.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,74 @@
from flask import Response, request

from marshmallow import ValidationError
from models.base import BaseModel
from ops_api.ops.auth.auth_types import Permission, PermissionType
from ops_api.ops.auth.decorators import is_authorized
from ops_api.ops.base_views import BaseItemAPI
from ops_api.ops.utils.cans import get_can_funding_summary
from ops_api.ops.schemas.can_funding_summary import GetCANFundingSummaryResponseSchema
from ops_api.ops.utils.cans import aggregate_funding_summaries, get_can_funding_summary, get_filtered_cans
from ops_api.ops.utils.response import make_response_with_headers


class CANFundingSummaryItemAPI(BaseItemAPI):
class CANFundingSummaryListAPI(BaseItemAPI):
def __init__(self, model: BaseModel):
super().__init__(model)

@is_authorized(PermissionType.GET, Permission.CAN)
def get(self, id: int) -> Response:
def get(self) -> Response:
# Get query parameters
can_ids = request.args.getlist("can_ids")
fiscal_year = request.args.get("fiscal_year")
can = self._get_item(id)
active_period = request.args.getlist("active_period", type=int)
transfer = request.args.getlist("transfer")
portfolio = request.args.getlist("portfolio")
fy_budget = request.args.getlist("fy_budget", type=int)

# Ensure required 'can_ids' parameter is provided
if not can_ids:
return make_response_with_headers({"error": "'can_ids' parameter is required"}, 400)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The above request.args handling and can_ids validation would be better in a Marshmallow schema but it would be acceptable to take that as a tech debt item if you don't have time to implement it.


# When 'can_ids' is 0 (all CANS)
if can_ids == ["0"]:
cans = self._get_all_items()
return self._apply_filters_and_return(cans, fiscal_year, active_period, transfer, portfolio, fy_budget)

# Single 'can_id' without additional filters
if len(can_ids) == 1 and not (active_period or transfer or portfolio or fy_budget):
return self._handle_single_can_no_filters(can_ids[0], fiscal_year)

# Multiple 'can_ids' with filters
cans = [self._get_item(can_id) for can_id in can_ids]
return self._apply_filters_and_return(cans, fiscal_year, active_period, transfer, portfolio, fy_budget)

def _handle_single_can_no_filters(self, can_id: str, fiscal_year: str = None) -> Response:
can = self._get_item(can_id)
can_funding_summary = get_can_funding_summary(can, int(fiscal_year) if fiscal_year else None)
return make_response_with_headers(can_funding_summary)
return self._create_can_funding_budget_response(can_funding_summary)

def _apply_filters_and_return(
self,
cans: list,
fiscal_year: str = None,
active_period: list = None,
transfer: list = None,
portfolio: list = None,
fy_budget: list = None,
) -> Response:
cans_with_filters = get_filtered_cans(
cans, int(fiscal_year) if fiscal_year else None, active_period, transfer, portfolio, fy_budget
)
can_funding_summaries = [
get_can_funding_summary(can, int(fiscal_year) if fiscal_year else None) for can in cans_with_filters
]
aggregated_summary = aggregate_funding_summaries(can_funding_summaries)
return self._create_can_funding_budget_response(aggregated_summary)

@staticmethod
def _create_can_funding_budget_response(result) -> Response:
try:
schema = GetCANFundingSummaryResponseSchema(many=False)
result = schema.dump(result)
return make_response_with_headers(result)
except ValidationError as e:
return make_response_with_headers({"Validation Error": str(e)}, 500)
22 changes: 22 additions & 0 deletions backend/ops_api/ops/schemas/can_funding_summary.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from marshmallow import Schema, fields
from ops_api.ops.schemas.cans import BasicCANSchema


class CANSFundingSourceSchema(Schema):
can = fields.Nested(BasicCANSchema())
carry_forward_label = fields.String(allow_none=True)
expiration_date = fields.String(allow_none=True)


class GetCANFundingSummaryResponseSchema(Schema):
available_funding = fields.String(allow_none=True)
cans = fields.List(fields.Nested(CANSFundingSourceSchema), default=[])
carry_forward_funding = fields.String(allow_none=True)
received_funding = fields.String(allow_none=True)
expected_funding = fields.String(allow_none=True)
in_draft_funding = fields.String(allow_none=True)
in_execution_funding = fields.String(allow_none=True)
obligated_funding = fields.String(allow_none=True)
planned_funding = fields.String(allow_none=True)
total_funding = fields.String(allow_none=True)
new_funding = fields.String(allow_none=True)
10 changes: 5 additions & 5 deletions backend/ops_api/ops/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@
AZURE_SAS_TOKEN_VIEW_FUNC,
BUDGET_LINE_ITEMS_ITEM_API_VIEW_FUNC,
BUDGET_LINE_ITEMS_LIST_API_VIEW_FUNC,
CAN_FUNDING_RECEIVED_ITEM_API_VIEW_FUNC,
CAN_FUNDING_RECEIVED_LIST_API_VIEW_FUNC,
CAN_FUNDING_BUDGET_ITEM_API_VIEW_FUNC,
CAN_FUNDING_BUDGET_LIST_API_VIEW_FUNC,
CAN_FUNDING_DETAILS_ITEM_API_VIEW_FUNC,
CAN_FUNDING_DETAILS_LIST_API_VIEW_FUNC,
CAN_FUNDING_SUMMARY_ITEM_API_VIEW_FUNC,
CAN_FUNDING_RECEIVED_ITEM_API_VIEW_FUNC,
CAN_FUNDING_RECEIVED_LIST_API_VIEW_FUNC,
CAN_FUNDING_SUMMARY_LIST_API_VIEW_FUNC,
CAN_ITEM_API_VIEW_FUNC,
CAN_LIST_API_VIEW_FUNC,
CANS_BY_PORTFOLIO_API_VIEW_FUNC,
Expand Down Expand Up @@ -172,8 +172,8 @@ def register_api(api_bp: Blueprint) -> None:
)

api_bp.add_url_rule(
"/can-funding-summary/<int:id>",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The endpoints for *ITEM_API_VIEW* are designed to take an resource id, e.g. <int:id> while the *LIST_API_VIEW does not. It would be more consistent if /can-funding-summary/ used CAN_FUNDING_RECEIVED_LIST_API_VIEW_FUNC.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great catch! I changed the view name to CAN_FUNDING_SUMMARY_LIST_API_VIEW_FUNC.

view_func=CAN_FUNDING_SUMMARY_ITEM_API_VIEW_FUNC,
"/can-funding-summary",
view_func=CAN_FUNDING_SUMMARY_LIST_API_VIEW_FUNC,
)
api_bp.add_url_rule(
"/portfolio-funding-summary/<int:id>",
Expand Down
Loading