Remove fixture-based reporting methods
These methods probably can be reused to handle real Azure reporting data
This commit is contained in:
parent
4a78aa07c9
commit
5b60a54dbc
@ -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):
|
||||
"""
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
Loading…
x
Reference in New Issue
Block a user