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
|
import json
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
import pendulum
|
import pendulum
|
||||||
@ -12,132 +11,6 @@ def load_fixture_data():
|
|||||||
class MockReportingProvider:
|
class MockReportingProvider:
|
||||||
FIXTURE_SPEND_DATA = load_fixture_data()
|
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):
|
def prepare_azure_reporting_data(rows: list):
|
||||||
"""
|
"""
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
from flask import current_app
|
from flask import current_app
|
||||||
from itertools import groupby
|
|
||||||
from atst.domain.csp.cloud.models import (
|
from atst.domain.csp.cloud.models import (
|
||||||
ReportingCSPPayload,
|
ReportingCSPPayload,
|
||||||
CostManagementQueryCSPResult,
|
CostManagementQueryCSPResult,
|
||||||
@ -9,46 +8,12 @@ import pendulum
|
|||||||
|
|
||||||
|
|
||||||
class Reports:
|
class Reports:
|
||||||
@classmethod
|
|
||||||
def monthly_spending(cls, portfolio):
|
|
||||||
return current_app.csp.reports.get_portfolio_monthly_spending(portfolio)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def expired_task_orders(cls, portfolio):
|
def expired_task_orders(cls, portfolio):
|
||||||
return [
|
return [
|
||||||
task_order for task_order in portfolio.task_orders if task_order.is_expired
|
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
|
@classmethod
|
||||||
def get_portfolio_spending(cls, portfolio):
|
def get_portfolio_spending(cls, portfolio):
|
||||||
# TODO: Extend this function to make from_date and to_date configurable
|
# 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 tests.factories import PortfolioFactory
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
import pendulum
|
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:
|
class TestPrepareAzureData:
|
||||||
start_of_month = pendulum.today(tz="utc").start_of("month").replace(tzinfo=None)
|
start_of_month = pendulum.today(tz="utc").start_of("month").replace(tzinfo=None)
|
||||||
next_month = start_of_month.add(months=1).to_atom_string()
|
next_month = start_of_month.add(months=1).to_atom_string()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user