diff --git a/atst/domain/csp/reports.py b/atst/domain/csp/reports.py index 0e29c5ff..700947f7 100644 --- a/atst/domain/csp/reports.py +++ b/atst/domain/csp/reports.py @@ -1,4 +1,3 @@ -from collections import defaultdict import json from decimal import Decimal import pendulum @@ -12,132 +11,6 @@ def load_fixture_data(): class MockReportingProvider: FIXTURE_SPEND_DATA = load_fixture_data() - @classmethod - def get_portfolio_monthly_spending(cls, portfolio): - """ - returns an array of application and environment spending for the - portfolio. Applications and their nested environments are sorted in - alphabetical order by name. - [ - { - name - this_month - last_month - total - environments [ - { - name - this_month - last_month - total - } - ] - } - ] - """ - - fixture_apps = cls.FIXTURE_SPEND_DATA.get(portfolio.name, {}).get( - "applications", [] - ) - - for application in portfolio.applications: - if application.name not in [app["name"] for app in fixture_apps]: - fixture_apps.append({"name": application.name, "environments": []}) - - return sorted( - [ - cls._get_application_monthly_totals(portfolio, fixture_app) - for fixture_app in fixture_apps - if fixture_app["name"] - in [application.name for application in portfolio.applications] - ], - key=lambda app: app["name"], - ) - - @classmethod - def _get_environment_monthly_totals(cls, environment): - """ - returns a dictionary that represents spending totals for an environment e.g. - { - name - this_month - last_month - total - } - """ - return { - "name": environment["name"], - "this_month": sum(environment["spending"]["this_month"].values()), - "last_month": sum(environment["spending"]["last_month"].values()), - "total": sum(environment["spending"]["total"].values()), - } - - @classmethod - def _get_application_monthly_totals(cls, portfolio, fixture_app): - """ - returns a dictionary that represents spending totals for an application - and its environments e.g. - { - name - this_month - last_month - total - environments: [ - { - name - this_month - last_month - total - } - ] - } - """ - application_envs = [ - env - for env in portfolio.all_environments - if env.application.name == fixture_app["name"] - ] - - environments = [ - cls._get_environment_monthly_totals(env) - for env in fixture_app["environments"] - if env["name"] in [e.name for e in application_envs] - ] - - for env in application_envs: - if env.name not in [env["name"] for env in environments]: - environments.append({"name": env.name}) - - return { - "name": fixture_app["name"], - "this_month": sum(env.get("this_month", 0) for env in environments), - "last_month": sum(env.get("last_month", 0) for env in environments), - "total": sum(env.get("total", 0) for env in environments), - "environments": sorted(environments, key=lambda env: env["name"]), - } - - @classmethod - def get_spending_by_JEDI_clin(cls, portfolio): - """ - returns an dictionary of spending per JEDI CLIN for a portfolio - { - jedi_clin: { - invoiced - estimated - }, - } - """ - if portfolio.name in cls.FIXTURE_SPEND_DATA: - CLIN_spend_dict = defaultdict(lambda: defaultdict(Decimal)) - for application in cls.FIXTURE_SPEND_DATA[portfolio.name]["applications"]: - for environment in application["environments"]: - for clin, spend in environment["spending"]["this_month"].items(): - CLIN_spend_dict[clin]["estimated"] += Decimal(spend) - for clin, spend in environment["spending"]["total"].items(): - CLIN_spend_dict[clin]["invoiced"] += Decimal(spend) - return CLIN_spend_dict - return {} - def prepare_azure_reporting_data(rows: list): """ diff --git a/atst/domain/reports.py b/atst/domain/reports.py index 30996290..fc619649 100644 --- a/atst/domain/reports.py +++ b/atst/domain/reports.py @@ -1,5 +1,4 @@ from flask import current_app -from itertools import groupby from atst.domain.csp.cloud.models import ( ReportingCSPPayload, CostManagementQueryCSPResult, @@ -9,46 +8,12 @@ import pendulum class Reports: - @classmethod - def monthly_spending(cls, portfolio): - return current_app.csp.reports.get_portfolio_monthly_spending(portfolio) - @classmethod def expired_task_orders(cls, portfolio): return [ task_order for task_order in portfolio.task_orders if task_order.is_expired ] - @classmethod - def obligated_funds_by_JEDI_clin(cls, portfolio): - clin_spending = current_app.csp.reports.get_spending_by_JEDI_clin(portfolio) - active_clins = portfolio.active_clins - for jedi_clin, clins in groupby( - active_clins, key=lambda clin: clin.jedi_clin_type - ): - if not clin_spending.get(jedi_clin.name): - clin_spending[jedi_clin.name] = {} - clin_spending[jedi_clin.name]["obligated"] = sum( - clin.obligated_amount for clin in clins - ) - - output = [] - for clin in clin_spending.keys(): - invoiced = clin_spending[clin].get("invoiced", 0) - estimated = clin_spending[clin].get("estimated", 0) - obligated = clin_spending[clin].get("obligated", 0) - remaining = obligated - (invoiced + estimated) - output.append( - { - "name": clin, - "invoiced": invoiced, - "estimated": estimated, - "obligated": obligated, - "remaining": remaining, - } - ) - return output - @classmethod def get_portfolio_spending(cls, portfolio): # TODO: Extend this function to make from_date and to_date configurable diff --git a/tests/domain/cloud/reports/test_reports.py b/tests/domain/cloud/reports/test_reports.py index 4c85a9bc..91079d44 100644 --- a/tests/domain/cloud/reports/test_reports.py +++ b/tests/domain/cloud/reports/test_reports.py @@ -1,65 +1,9 @@ -from atst.domain.csp.reports import MockReportingProvider, prepare_azure_reporting_data +from atst.domain.csp.reports import prepare_azure_reporting_data from tests.factories import PortfolioFactory from decimal import Decimal import pendulum -def test_get_environment_monthly_totals(): - environment = { - "name": "Test Environment", - "spending": { - "this_month": {"JEDI_CLIN_1": 100, "JEDI_CLIN_2": 100}, - "last_month": {"JEDI_CLIN_1": 200, "JEDI_CLIN_2": 200}, - "total": {"JEDI_CLIN_1": 1000, "JEDI_CLIN_2": 1000}, - }, - } - totals = MockReportingProvider._get_environment_monthly_totals(environment) - assert totals == { - "name": "Test Environment", - "this_month": 200, - "last_month": 400, - "total": 2000, - } - - -def test_get_application_monthly_totals(): - portfolio = PortfolioFactory.create( - applications=[ - {"name": "Test Application", "environments": [{"name": "Z"}, {"name": "A"}]} - ], - ) - application = { - "name": "Test Application", - "environments": [ - { - "name": "Z", - "spending": { - "this_month": {"JEDI_CLIN_1": 50, "JEDI_CLIN_2": 50}, - "last_month": {"JEDI_CLIN_1": 150, "JEDI_CLIN_2": 150}, - "total": {"JEDI_CLIN_1": 250, "JEDI_CLIN_2": 250}, - }, - }, - { - "name": "A", - "spending": { - "this_month": {"JEDI_CLIN_1": 100, "JEDI_CLIN_2": 100}, - "last_month": {"JEDI_CLIN_1": 200, "JEDI_CLIN_2": 200}, - "total": {"JEDI_CLIN_1": 1000, "JEDI_CLIN_2": 1000}, - }, - }, - ], - } - - totals = MockReportingProvider._get_application_monthly_totals( - portfolio, application - ) - assert totals["name"] == "Test Application" - assert totals["this_month"] == 300 - assert totals["last_month"] == 700 - assert totals["total"] == 2500 - assert [env["name"] for env in totals["environments"]] == ["A", "Z"] - - class TestPrepareAzureData: start_of_month = pendulum.today(tz="utc").start_of("month").replace(tzinfo=None) next_month = start_of_month.add(months=1).to_atom_string()