From 6312e45a23a31b839397d8ea2263413744d9e12c Mon Sep 17 00:00:00 2001 From: Dylan Leard Date: Mon, 16 Dec 2024 09:38:34 -0800 Subject: [PATCH 01/20] test: fix frontend tests & interface --- .../ComplianceSummaryForm.tsx | 30 ++-- .../ComplianceSummaryForm.test.tsx | 146 +++++++++--------- 2 files changed, 91 insertions(+), 85 deletions(-) diff --git a/bciers/apps/reporting/src/app/components/complianceSummary/ComplianceSummaryForm.tsx b/bciers/apps/reporting/src/app/components/complianceSummary/ComplianceSummaryForm.tsx index 7764d0fcce..19bfbb0af3 100644 --- a/bciers/apps/reporting/src/app/components/complianceSummary/ComplianceSummaryForm.tsx +++ b/bciers/apps/reporting/src/app/components/complianceSummary/ComplianceSummaryForm.tsx @@ -16,24 +16,26 @@ interface Props { versionId: number; needsVerification: boolean; summaryFormData: { - attributableForReporting: string; - reportingOnlyEmission: string; - emissionsLimit: string; - excessEmissions: string; - creditedEmissions: string; - regulatoryValues: { - reductionFactor: string; - tighteningRate: string; - initialCompliancePeriod: string; - compliancePeriod: string; + emissions_attributable_for_reporting: string; + reporting_only_emissions: string; + emissions_attributable_for_compliance: string; + emissions_limit: string; + excess_emissions: string; + credited_emissions: string; + regulatory_values: { + reduction_factor: string; + tightening_rate: string; + initial_compliance_period: string; + compliance_period: string; }; products: { name: string; customUnit: string; - annualProduction: string; - emissionIntensity: string; - allocatedIndustrialProcessEmissions: string; - allocatedComplianceEmissions: string; + annual_production: string; + apr_dec_production: string; + emission_intensity: string; + allocated_industrial_process_emissions: string; + allocated_compliance_emissions: string; }[]; }; taskListElements: TaskListElement[]; diff --git a/bciers/apps/reporting/src/tests/components/complianceSummary/ComplianceSummaryForm.test.tsx b/bciers/apps/reporting/src/tests/components/complianceSummary/ComplianceSummaryForm.test.tsx index 867d94b503..6029b24a5e 100644 --- a/bciers/apps/reporting/src/tests/components/complianceSummary/ComplianceSummaryForm.test.tsx +++ b/bciers/apps/reporting/src/tests/components/complianceSummary/ComplianceSummaryForm.test.tsx @@ -15,25 +15,27 @@ vi.mock("next/navigation", () => ({ })); const mockSummaryData = { - attributableForReporting: "1000", - reportingOnlyEmission: "1000", - emissionsLimit: "1000", - excessEmissions: "1000", - creditedEmissions: "1000", - regulatoryValues: { - reductionFactor: "1000", - tighteningRate: "1000", - initialCompliancePeriod: "1000", - compliancePeriod: "1000", + emissions_attributable_for_reporting: "1000", + reporting_only_emissions: "1000", + emissions_attributable_for_compliance: "1000", + emissions_limit: "1000", + excess_emissions: "1000", + credited_emissions: "1000", + regulatory_values: { + reduction_factor: "1000", + tightening_rate: "1000", + initial_compliance_period: "1000", + compliance_period: "1000", }, products: [ { name: "Pucks", customUnit: "Goals", - annualProduction: "1000", - emissionIntensity: "1000", - allocatedIndustrialProcessEmissions: "1000", - allocatedComplianceEmissions: "1000", + annual_production: "1000", + apr_dec_production: "1000", + emission_intensity: "1000", + allocated_industrial_process_emissions: "1000", + allocated_compliance_emissions: "1000", }, ], }; @@ -52,63 +54,65 @@ describe("ComplianceSummaryForm", () => { vi.clearAllMocks(); }); - // it("should render the calculation summary data", async () => { - // render( - // , - // ); - - // expect( - // screen.getByLabelText("Emissions attributable for reporting").value, - // ).toBe("1000"); - // expect(screen.getByLabelText("Reporting-only emissions").value).toBe( - // "1000", - // ); - // expect(screen.getByLabelText("Emissions limit").value).toBe("1000"); - // expect(screen.getByLabelText("Excess emissions").value).toBe("1000"); - // expect(screen.getByLabelText("Credited emissions").value).toBe("1000"); - // }); - - // it("should render the regulatory values summary data", async () => { - // render( - // , - // ); - - // expect(screen.getByLabelText("Reduction factor").value).toBe("1000"); - // expect(screen.getByLabelText("Tightening rate").value).toBe("1000"); - // expect(screen.getByLabelText("Initial compliance period").value).toBe( - // "1000", - // ); - // expect(screen.getByLabelText("Compliance period").value).toBe("1000"); - // }); - - // it("should render the production summary data", async () => { - // render( - // , - // ); - - // expect(screen.getByLabelText("Annual production").value).toBe("1000"); - // expect( - // screen.getByLabelText("Weighted average emission intensity").value, - // ).toBe("1000"); - // expect( - // screen.getByLabelText("Emissions allocated to industrial process").value, - // ).toBe("1000"); - // expect( - // screen.getByLabelText("Emissions allocated to compliance").value, - // ).toBe("1000"); - // }); + it("should render the calculation summary data", async () => { + render( + , + ); + + expect( + screen.getByLabelText("Emissions attributable for reporting").value, + ).toBe("1000"); + expect(screen.getByLabelText("Reporting-only emissions").value).toBe( + "1000", + ); + expect(screen.getByLabelText("Emissions limit").value).toBe("1000"); + expect(screen.getByLabelText("Excess emissions").value).toBe("1000"); + expect(screen.getByLabelText("Credited emissions").value).toBe("1000"); + }); + + it("should render the regulatory values summary data", async () => { + render( + , + ); + + expect(screen.getByLabelText("Reduction factor").value).toBe("1000"); + expect(screen.getByLabelText("Tightening rate").value).toBe("1000"); + expect(screen.getByLabelText("Initial compliance period").value).toBe( + "1000", + ); + expect(screen.getByLabelText("Compliance period").value).toBe("1000"); + }); + + it("should render the production summary data", async () => { + render( + , + ); + + expect(screen.getByLabelText("Annual production").value).toBe("1000"); + expect( + screen.getByLabelText("Production-weighted average emission intensity") + .value, + ).toBe("1000"); + expect( + screen.getByLabelText("Allocated industrial process emissions").value, + ).toBe("1000"); + expect( + screen.getByLabelText("Allocated Emissions attributable to compliance") + .value, + ).toBe("1000"); + }); it("should render a back button that navigates to the additional information page", async () => { render( From 3c2441a5fe63bb1677cfc3b95adee4c63f6c5181 Mon Sep 17 00:00:00 2001 From: Dylan Leard Date: Mon, 16 Dec 2024 10:40:33 -0800 Subject: [PATCH 02/20] chore: use classes instead of schemas for type interfaces in service --- bc_obps/reporting/api/compliance_data.py | 9 +-- .../reporting/service/compliance_service.py | 68 ++++++++++++++++--- 2 files changed, 63 insertions(+), 14 deletions(-) diff --git a/bc_obps/reporting/api/compliance_data.py b/bc_obps/reporting/api/compliance_data.py index c01687efdc..520c5313c6 100644 --- a/bc_obps/reporting/api/compliance_data.py +++ b/bc_obps/reporting/api/compliance_data.py @@ -1,11 +1,10 @@ from typing import Literal, Tuple -from common.permissions import authorize from django.http import HttpRequest from registration.decorators import handle_http_errors from reporting.constants import EMISSIONS_REPORT_TAGS from service.error_service.custom_codes_4xx import custom_codes_4xx from reporting.schema.generic import Message -from reporting.service.compliance_service import ComplianceService +from reporting.service.compliance_service import ComplianceService, ComplianceData from reporting.schema.compliance_data import ComplianceDataSchemaOut from .router import router @@ -16,12 +15,10 @@ tags=EMISSIONS_REPORT_TAGS, description="""Retrieves the data for the compliance summary page from multiple data sources.""", exclude_none=True, - auth=authorize("approved_industry_user"), + # auth=authorize("approved_industry_user"), ) @handle_http_errors() -def get_compliance_summary_data( - request: HttpRequest, report_version_id: int -) -> Tuple[Literal[200], ComplianceDataSchemaOut]: +def get_compliance_summary_data(request: HttpRequest, report_version_id: int) -> Tuple[Literal[200], ComplianceData]: compliance_data = ComplianceService.get_calculated_compliance_data(report_version_id) return 200, compliance_data diff --git a/bc_obps/reporting/service/compliance_service.py b/bc_obps/reporting/service/compliance_service.py index d2bf6194d5..c4a6206102 100644 --- a/bc_obps/reporting/service/compliance_service.py +++ b/bc_obps/reporting/service/compliance_service.py @@ -2,8 +2,6 @@ from reporting.models.report_product_emission_allocation import ReportProductEmissionAllocation from reporting.models.report_product import ReportProduct from reporting.models import NaicsRegulatoryValue, ReportVersion -from reporting.schema.compliance_data import RegulatoryValueSchema -from reporting.schema.compliance_data import ReportProductComplianceSchema, ComplianceDataSchemaOut from reporting.models.product_emission_intensity import ProductEmissionIntensity from reporting.models.emission_category import EmissionCategory from decimal import Decimal @@ -11,13 +9,67 @@ from typing import Dict, List +class RegulatoryValues: + def __init__( + self, + reduction_factor: Decimal, + tightening_rate: Decimal, + initial_compliance_period: int, + compliance_period: int, + ): + self.reduction_factor = reduction_factor + self.tightening_rate = tightening_rate + self.initial_compliance_period = initial_compliance_period + self.compliance_period = compliance_period + + +class ReportProductComplianceData: + def __init__( + self, + name: str, + annual_production: Decimal | int, + apr_dec_production: Decimal | int, + emission_intensity: Decimal, + allocated_industrial_process_emissions: Decimal | int, + allocated_compliance_emissions: Decimal | int, + ): + self.name = name + self.annual_production = annual_production + self.apr_dec_production = apr_dec_production + self.emission_intensity = emission_intensity + self.allocated_industrial_process_emissions = allocated_industrial_process_emissions + self.allocated_compliance_emissions = allocated_compliance_emissions + + +class ComplianceData: + def __init__( + self, + emissions_attributable_for_reporting: Decimal | int, + reporting_only_emissions: Decimal | int, + emissions_attributable_for_compliance: Decimal | int, + emissions_limit: Decimal | int, + excess_emissions: Decimal | int, + credited_emissions: Decimal | int, + regulatory_values: RegulatoryValues, + products: List[ReportProductComplianceData], + ): + self.emissions_attributable_for_reporting = emissions_attributable_for_reporting + self.reporting_only_emissions = reporting_only_emissions + self.emissions_attributable_for_compliance = emissions_attributable_for_compliance + self.emissions_limit = emissions_limit + self.excess_emissions = excess_emissions + self.credited_emissions = credited_emissions + self.regulatory_values = regulatory_values + self.products = products + + class ComplianceService: """ Service that fetches the data & performs the calculations necessary for the compliance summary """ @staticmethod - def get_regulatory_values_by_naics_code(report_version_id: int) -> RegulatoryValueSchema: + def get_regulatory_values_by_naics_code(report_version_id: int) -> RegulatoryValues: data = ReportVersion.objects.select_related('report__operation').get(pk=report_version_id) naics_code_id = data.report.operation.naics_code_id compliance_year = data.report.reporting_year.reporting_year @@ -27,7 +79,7 @@ def get_regulatory_values_by_naics_code(report_version_id: int) -> RegulatoryVal valid_to__gte=data.report.reporting_year.reporting_window_end, ) - return RegulatoryValueSchema( + return RegulatoryValues( reduction_factor=regulatory_values.reduction_factor, tightening_rate=regulatory_values.tightening_rate, initial_compliance_period=2024, @@ -120,11 +172,11 @@ def calculate_product_emission_limit( return product_emission_limit @classmethod - def get_calculated_compliance_data(cls, report_version_id: int) -> ComplianceDataSchemaOut: + def get_calculated_compliance_data(cls, report_version_id: int) -> ComplianceData: naics_data = ComplianceService.get_regulatory_values_by_naics_code(report_version_id) registration_purpose = ReportVersion.objects.get(pk=report_version_id).report.operation.registration_purpose ##### Don't use schemas, use classes or dicts - compliance_product_list: List[ReportProductComplianceSchema] = [] + compliance_product_list: List[ReportProductComplianceData] = [] total_allocated_reporting_only = Decimal(0) total_allocated_for_compliance = Decimal(0) total_allocated_for_compliance_2024 = Decimal(0) @@ -173,7 +225,7 @@ def get_calculated_compliance_data(cls, report_version_id: int) -> ComplianceDat # Add product to list of products compliance_product_list.append( - ReportProductComplianceSchema( + ReportProductComplianceData( name=rp.product.name, annual_production=production_totals["annual_amount"], apr_dec_production=production_totals["apr_dec"], @@ -198,7 +250,7 @@ def get_calculated_compliance_data(cls, report_version_id: int) -> ComplianceDat excess_emissions = Decimal(0) credited_emissions = Decimal(0) # Craft return object with all data - return_object = ComplianceDataSchemaOut( + return_object = ComplianceData( emissions_attributable_for_reporting=attributable_for_reporting_total, reporting_only_emissions=round(Decimal(total_allocated_reporting_only), 4), emissions_attributable_for_compliance=round(total_allocated_for_compliance_2024, 4), From 1b8192a359709d87436c613f496235d781bec18c Mon Sep 17 00:00:00 2001 From: Dylan Leard Date: Tue, 7 Jan 2025 13:30:54 -0800 Subject: [PATCH 03/20] chore: fix authorize in service --- bc_obps/reporting/api/compliance_data.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bc_obps/reporting/api/compliance_data.py b/bc_obps/reporting/api/compliance_data.py index 520c5313c6..f5cf32261a 100644 --- a/bc_obps/reporting/api/compliance_data.py +++ b/bc_obps/reporting/api/compliance_data.py @@ -7,6 +7,7 @@ from reporting.service.compliance_service import ComplianceService, ComplianceData from reporting.schema.compliance_data import ComplianceDataSchemaOut from .router import router +from common.permissions import authorize @router.get( @@ -15,7 +16,7 @@ tags=EMISSIONS_REPORT_TAGS, description="""Retrieves the data for the compliance summary page from multiple data sources.""", exclude_none=True, - # auth=authorize("approved_industry_user"), + auth=authorize("approved_industry_user"), ) @handle_http_errors() def get_compliance_summary_data(request: HttpRequest, report_version_id: int) -> Tuple[Literal[200], ComplianceData]: From 17f32654c91a5e3d93a5654121359e74fb44122e Mon Sep 17 00:00:00 2001 From: Dylan Leard Date: Tue, 7 Jan 2025 14:53:23 -0800 Subject: [PATCH 04/20] chore: fix Decimal parsing --- bc_obps/reporting/service/compliance_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bc_obps/reporting/service/compliance_service.py b/bc_obps/reporting/service/compliance_service.py index c4a6206102..2081a62aad 100644 --- a/bc_obps/reporting/service/compliance_service.py +++ b/bc_obps/reporting/service/compliance_service.py @@ -164,7 +164,7 @@ def calculate_product_emission_limit( product_emission_limit = (apr_dec_production * pwaei) * ( reduction_factor - ( - (Decimal(1) - (allocated_industrial_process / allocated_for_compliance)) + (Decimal('1') - (allocated_industrial_process / allocated_for_compliance)) * tightening_rate * (compliance_period - initial_compliance_period) ) From 02f5021dd154ccb7cf29cc697078dc38f37588da Mon Sep 17 00:00:00 2001 From: Dylan Leard Date: Tue, 7 Jan 2025 14:55:55 -0800 Subject: [PATCH 05/20] test: add test for compliance_service internal functions --- .../tests/service/test_compliance_service.py | 267 ++++++++++++++++++ .../reporting/tests/utils/baker_recipes.py | 10 +- 2 files changed, 276 insertions(+), 1 deletion(-) create mode 100644 bc_obps/reporting/tests/service/test_compliance_service.py diff --git a/bc_obps/reporting/tests/service/test_compliance_service.py b/bc_obps/reporting/tests/service/test_compliance_service.py new file mode 100644 index 0000000000..c5b1de9715 --- /dev/null +++ b/bc_obps/reporting/tests/service/test_compliance_service.py @@ -0,0 +1,267 @@ +from django.test import TestCase +from decimal import Decimal +from reporting.models.report_product import ReportProduct +from reporting.models.emission_category import EmissionCategory +from reporting.models.report import Report +from registration.models.operation import Operation +from reporting.service.compliance_service import ComplianceService +from model_bakery.baker import make_recipe + + +class TestComplianceSummaryService(TestCase): + def test_get_regulatory_values_by_naics_code(self): + ## SETUP ## + # Create report version records with recipes + report_version_1 = make_recipe("reporting.tests.utils.report_version") + report_version_2 = make_recipe("reporting.tests.utils.report_version") + # Override parent values auto-generated by recipe + operation = Operation.objects.get(pk=report_version_1.report.operation.id) + report = Report.objects.get(pk=report_version_1.report_id) + operation.naics_code_id = 1 + operation.save() + report.reporting_year_id = 2024 + report.save() + operation = Operation.objects.get(pk=report_version_2.report.operation.id) + report = Report.objects.get(pk=report_version_2.report_id) + operation.naics_code_id = 22 + operation.save() + report.reporting_year_id = 2024 + report.save() + + ## TESTS ## + # Test service function returns correct values + regulatory_values_1 = ComplianceService.get_regulatory_values_by_naics_code(report_version_1.id) + regulatory_values_2 = ComplianceService.get_regulatory_values_by_naics_code(report_version_2.id) + + assert regulatory_values_1.reduction_factor == Decimal('0.6500') + assert regulatory_values_1.tightening_rate == Decimal('0.0100') + assert regulatory_values_2.reduction_factor == Decimal('0.9000') + assert regulatory_values_2.tightening_rate == Decimal('0.0100') + + def test_get_emissions_attributable_for_reporting(self): + ## SETUP ## + # Create report_emission records with recipes + report_emission_1 = make_recipe( + "reporting.tests.utils.report_emission", + json_data={"equivalentEmission": 100.0001}, + ) + report_emission_2 = make_recipe( + "reporting.tests.utils.report_emission", + report_version=report_emission_1.report_version, + json_data={"equivalentEmission": 200.9988}, + ) + report_emission_3 = make_recipe( + "reporting.tests.utils.report_emission", + report_version=report_emission_1.report_version, + json_data={"equivalentEmission": 300.05}, + ) + + # Set emission categories for report_emission records + # emission_category id 1 = Flaring, 3 = Industrial Process, 4 = On-site transportation, 12 = excluded non-biomass + report_emission_1.emission_categories.set([1]) + report_emission_2.emission_categories.set([3]) + report_emission_3.emission_categories.set([4, 12]) + + ## TESTS ## + # Only basic should be counted, report_emission_3 should not be counted twice + sum_for_testing = ComplianceService.get_emissions_attributable_for_reporting( + report_emission_1.report_version_id + ) + emission_sum = ( + Decimal(str(report_emission_1.json_data['equivalentEmission'])) + + Decimal(str(report_emission_2.json_data['equivalentEmission'])) + + Decimal(str(report_emission_3.json_data['equivalentEmission'])) + ) + assert sum_for_testing == emission_sum + + def test_get_production_totals(self): + ## SETUP ## + report_product_1 = make_recipe( + "reporting.tests.utils.report_product", + annual_production=Decimal('10000'), + production_data_apr_dec=Decimal('5000'), + ) + report_product_2 = make_recipe( + "reporting.tests.utils.report_product", + report_version=report_product_1.report_version, + annual_production=Decimal('100000'), + production_data_apr_dec=Decimal('25000'), + ) + report_product_3 = make_recipe( + "reporting.tests.utils.report_product", + report_version=report_product_1.report_version, + annual_production=Decimal('200000'), + production_data_apr_dec=Decimal('150000'), + ) + + ## TESTS ## + sums_for_testing = ComplianceService.get_production_totals(report_product_1.report_version_id) + annual_sum = ( + report_product_1.annual_production + report_product_2.annual_production + report_product_3.annual_production + ) + apr_dec_sum = ( + report_product_1.production_data_apr_dec + + report_product_2.production_data_apr_dec + + report_product_3.production_data_apr_dec + ) + + assert sums_for_testing['annual_amount'] == annual_sum + assert sums_for_testing['apr_dec'] == apr_dec_sum + + def test_get_allocated_emissions_by_report_product_emission_category(self): + ## SETUP ## + allocation_1 = make_recipe( + "reporting.tests.utils.report_product_emission_allocation", + emission_category=EmissionCategory.objects.get(pk=1), + allocated_quantity=Decimal('1000.0001'), + ) + allocation_2 = make_recipe( + "reporting.tests.utils.report_product_emission_allocation", + report_version=allocation_1.report_version, + emission_category=EmissionCategory.objects.get(pk=1), + report_product=allocation_1.report_product, + allocated_quantity=Decimal('2000.0002'), + ) + allocation_3 = make_recipe( + "reporting.tests.utils.report_product_emission_allocation", + report_version=allocation_1.report_version, + emission_category=EmissionCategory.objects.get(pk=3), + allocated_quantity=Decimal('6000.0006'), + ) + allocation_4 = make_recipe( + "reporting.tests.utils.report_product_emission_allocation", + report_version=allocation_1.report_version, + emission_category=EmissionCategory.objects.get(pk=12), + report_product=allocation_3.report_product, + allocated_quantity=Decimal('500.0005'), + ) + + ## TESTS ## + allocated_to_flaring_for_test = ComplianceService.get_allocated_emissions_by_report_product_emission_category( + allocation_1.report_version_id, allocation_1.report_product.product_id, [1] + ) + allocated_to_industrial_for_test = ( + ComplianceService.get_allocated_emissions_by_report_product_emission_category( + allocation_3.report_version_id, allocation_3.report_product.product_id, [3] + ) + ) + allocated_to_excluded_non_biomass_for_test = ( + ComplianceService.get_allocated_emissions_by_report_product_emission_category( + allocation_4.report_version_id, allocation_4.report_product.product_id, [12] + ) + ) + assert allocated_to_flaring_for_test == allocation_1.allocated_quantity + allocation_2.allocated_quantity + assert allocated_to_industrial_for_test == allocation_3.allocated_quantity + assert allocated_to_excluded_non_biomass_for_test == allocation_4.allocated_quantity + + def test_get_report_product_aggregated_totals(self): + ## SETUP ## + report_product_1 = make_recipe( + "reporting.tests.utils.report_product", + annual_production=Decimal('10000.0001'), + production_data_apr_dec=Decimal('5000.05'), + ) + report_product_2 = make_recipe( + "reporting.tests.utils.report_product", + report_version=report_product_1.report_version, + product=report_product_1.product, + annual_production=Decimal('100000.003'), + production_data_apr_dec=Decimal('25000.0002'), + ) + report_product_3 = make_recipe( + "reporting.tests.utils.report_product", + report_version=report_product_1.report_version, + annual_production=Decimal('200000.0091'), + production_data_apr_dec=Decimal('150000.1234'), + ) + report_product_4 = make_recipe( + "reporting.tests.utils.report_product", + report_version=report_product_1.report_version, + product=report_product_3.product, + annual_production=Decimal('400000.002'), + production_data_apr_dec=Decimal('50000.321'), + ) + + ## TESTS ## + product_1_2_aggregate_for_test = ComplianceService.get_report_product_aggregated_totals( + report_product_1.report_version_id, report_product_1.product_id + ) + product_3_4_aggregate_for_test = ComplianceService.get_report_product_aggregated_totals( + report_product_3.report_version_id, report_product_3.product_id + ) + + assert ( + product_1_2_aggregate_for_test['annual_amount'] + == report_product_1.annual_production + report_product_2.annual_production + ) + assert ( + product_1_2_aggregate_for_test['apr_dec'] + == report_product_1.production_data_apr_dec + report_product_2.production_data_apr_dec + ) + + assert ( + product_3_4_aggregate_for_test['annual_amount'] + == report_product_3.annual_production + report_product_4.annual_production + ) + assert ( + product_3_4_aggregate_for_test['apr_dec'] + == report_product_3.production_data_apr_dec + report_product_4.production_data_apr_dec + ) + + def test_get_reporting_only_allocated(self): + ## SETUP ## + allocation_1 = make_recipe( + "reporting.tests.utils.report_product_emission_allocation", + emission_category=EmissionCategory.objects.get(pk=1), + allocated_quantity=Decimal('1000.0001'), + ) + allocation_2 = make_recipe( + "reporting.tests.utils.report_product_emission_allocation", + report_version=allocation_1.report_version, + emission_category=EmissionCategory.objects.get(pk=2), + report_product=allocation_1.report_product, + allocated_quantity=Decimal('2000.0002'), + ) + make_recipe( + "reporting.tests.utils.report_product_emission_allocation", + report_version=allocation_1.report_version, + emission_category=EmissionCategory.objects.get(pk=3), + report_product=allocation_1.report_product, + allocated_quantity=Decimal('6000.0006'), + ) + allocation_4 = make_recipe( + "reporting.tests.utils.report_product_emission_allocation", + report_version=allocation_1.report_version, + emission_category=EmissionCategory.objects.get(pk=12), + report_product=allocation_1.report_product, + allocated_quantity=Decimal('500.0005'), + ) + + ## TESTS ## + # Correctly aggregates reporting-only emissions + reporting_only_for_test = ComplianceService.get_reporting_only_allocated( + allocation_1.report_version, allocation_1.report_product.product_id + ) + assert reporting_only_for_test == allocation_2.allocated_quantity + allocation_4.allocated_quantity + + # Add a fog product + fog_product_allocation = make_recipe( + "reporting.tests.utils.report_product_emission_allocation", + report_version=allocation_1.report_version, + emission_category=EmissionCategory.objects.get(pk=1), + allocated_quantity=Decimal('12.0'), + ) + rp = ReportProduct.objects.get(pk=fog_product_allocation.report_product_id) + rp.product_id = 39 + rp.save() + + # Correctly aggregates reporting-only emissions when there is a fog product + reporting_only_with_fog_for_test = ComplianceService.get_reporting_only_allocated( + allocation_1.report_version, allocation_1.report_product.product_id + ) + assert ( + reporting_only_with_fog_for_test + == allocation_2.allocated_quantity + + allocation_4.allocated_quantity + + fog_product_allocation.allocated_quantity + ) diff --git a/bc_obps/reporting/tests/utils/baker_recipes.py b/bc_obps/reporting/tests/utils/baker_recipes.py index d25b338fc1..8522e9eb27 100644 --- a/bc_obps/reporting/tests/utils/baker_recipes.py +++ b/bc_obps/reporting/tests/utils/baker_recipes.py @@ -1,6 +1,6 @@ from datetime import date, timedelta, datetime from typing import Any - +from reporting.models.report_product_emission_allocation import ReportProductEmissionAllocation from registration.models import NaicsCode from registration.models.activity import Activity from reporting.models import ( @@ -210,3 +210,11 @@ def json_seq(json_key="generated_json", json_value="test json value", seq_value: representative_name="Test Report", selected_for_report=True, ) + +report_product_emission_allocation = Recipe( + ReportProductEmissionAllocation, + report_version=foreign_key(report_version), + facility_report=foreign_key(facility_report), + report_product=foreign_key(report_product), + emission_category=foreign_key(emission_category), +) From 57cc18071459e22d810ca39b7d2c6ff3b14ed73e Mon Sep 17 00:00:00 2001 From: Dylan Leard Date: Wed, 8 Jan 2025 09:13:51 -0800 Subject: [PATCH 06/20] test: split test into separate files for static & class functions --- .../test_compliance_service/test_compliance_service_class.py | 5 +++++ .../test_compliance_service_static.py} | 0 2 files changed, 5 insertions(+) create mode 100644 bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_class.py rename bc_obps/reporting/tests/service/{test_compliance_service.py => test_compliance_service/test_compliance_service_static.py} (100%) diff --git a/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_class.py b/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_class.py new file mode 100644 index 0000000000..ca03187803 --- /dev/null +++ b/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_class.py @@ -0,0 +1,5 @@ +from django.test import TestCase + + +class TestComplianceSummaryServiceClass(TestCase): + print('class') diff --git a/bc_obps/reporting/tests/service/test_compliance_service.py b/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_static.py similarity index 100% rename from bc_obps/reporting/tests/service/test_compliance_service.py rename to bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_static.py From 14ed7a665e97fcbb06f793b1ef49ea20502ddcd3 Mon Sep 17 00:00:00 2001 From: Dylan Leard Date: Thu, 9 Jan 2025 14:59:59 -0800 Subject: [PATCH 07/20] test: add test infrastructure helpers --- .../test_compliance_service/infrastructure.py | 124 +++++++++++++++++ .../test_compliance_service_class.py | 130 +++++++++++++++++- 2 files changed, 253 insertions(+), 1 deletion(-) create mode 100644 bc_obps/reporting/tests/service/test_compliance_service/infrastructure.py diff --git a/bc_obps/reporting/tests/service/test_compliance_service/infrastructure.py b/bc_obps/reporting/tests/service/test_compliance_service/infrastructure.py new file mode 100644 index 0000000000..3fe3e28060 --- /dev/null +++ b/bc_obps/reporting/tests/service/test_compliance_service/infrastructure.py @@ -0,0 +1,124 @@ +from decimal import Decimal +from reporting.models.report_product import ReportProduct +from reporting.models.report import Report +from reporting.models.report_product_emission_allocation import ReportProductEmissionAllocation +from reporting.models.report_emission import ReportEmission +from registration.models.operation import Operation +from reporting.models.report_version import ReportVersion +from registration.models.naics_code import NaicsCode +from reporting.models.reporting_year import ReportingYear +from reporting.models.emission_category import EmissionCategory +from model_bakery.baker import make_recipe + + +class ComplianceTestInfrastructure: + operation_1: Operation + report_1: Report + report_version_1: ReportVersion + report_emission_1: ReportEmission + report_emission_2: ReportEmission + report_emission_3: ReportEmission + report_product_1: ReportProduct + report_product_2: ReportProduct + report_product_3: ReportProduct + allocation_1: ReportProductEmissionAllocation + allocation_2: ReportProductEmissionAllocation + allocation_3: ReportProductEmissionAllocation + allocation_4: ReportProductEmissionAllocation + allocation_5: ReportProductEmissionAllocation + + @classmethod + def build(cls): + t = ComplianceTestInfrastructure() + t.operation_1 = make_recipe( + 'registration.tests.utils.operation', + name='test sfo', + naics_code=NaicsCode.objects.get(pk=1), + type='Single Facility Operation', + ) + t.report_1 = make_recipe( + "reporting.tests.utils.report", operation=t.operation_1, reporting_year=ReportingYear.objects.get(pk=2024) + ) + t.report_version_1 = make_recipe("reporting.tests.utils.report_version", report=t.report_1) + t.report_emission_1 = make_recipe( + "reporting.tests.utils.report_emission", + report_version=t.report_version_1, + gas_type_id=1, + json_data={"equivalentEmission": 10000.0001}, + ) + t.report_emission_2 = make_recipe( + "reporting.tests.utils.report_emission", + report_version=t.report_version_1, + gas_type_id=2, + json_data={"equivalentEmission": 20000.9988}, + ) + t.report_emission_3 = make_recipe( + "reporting.tests.utils.report_emission", + report_version=t.report_version_1, + gas_type_id=3, + json_data={"equivalentEmission": 3000.05}, + ) + + t.report_emission_1.emission_categories.set([1]) + t.report_emission_2.emission_categories.set([3]) + t.report_emission_3.emission_categories.set([4, 12]) + + t.report_product_1 = make_recipe( + "reporting.tests.utils.report_product", + report_version=t.report_version_1, + product_id=1, + annual_production=Decimal('100000'), + production_data_apr_dec=Decimal('50000'), + ) + t.report_product_2 = make_recipe( + "reporting.tests.utils.report_product", + report_version=t.report_version_1, + product_id=2, + annual_production=Decimal('1000000'), + production_data_apr_dec=Decimal('250000'), + ) + t.report_product_3 = make_recipe( + "reporting.tests.utils.report_product", + report_version=t.report_version_1, + product_id=3, + annual_production=Decimal('2000000'), + production_data_apr_dec=Decimal('1500000'), + ) + + t.allocation_1 = make_recipe( + "reporting.tests.utils.report_product_emission_allocation", + report_version=t.report_version_1, + report_product=t.report_product_1, + emission_category=EmissionCategory.objects.get(pk=1), + allocated_quantity=Decimal('10000.0001'), + ) + t.allocation_2 = make_recipe( + "reporting.tests.utils.report_product_emission_allocation", + report_version=t.report_version_1, + emission_category=EmissionCategory.objects.get(pk=3), + report_product=t.report_product_2, + allocated_quantity=Decimal('10000.9900'), + ) + t.allocation_3 = make_recipe( + "reporting.tests.utils.report_product_emission_allocation", + report_version=t.report_version_1, + emission_category=EmissionCategory.objects.get(pk=3), + report_product=t.report_product_3, + allocated_quantity=Decimal('10000.0088'), + ) + t.allocation_4 = make_recipe( + "reporting.tests.utils.report_product_emission_allocation", + report_version=t.report_version_1, + emission_category=EmissionCategory.objects.get(pk=4), + report_product=t.report_product_1, + allocated_quantity=Decimal('3000.0005'), + ) + t.allocation_5 = make_recipe( + "reporting.tests.utils.report_product_emission_allocation", + report_version=t.report_version_1, + emission_category=EmissionCategory.objects.get(pk=12), + report_product=t.report_product_1, + allocated_quantity=Decimal('3000.0005'), + ) + + return t diff --git a/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_class.py b/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_class.py index ca03187803..eae3dd22a2 100644 --- a/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_class.py +++ b/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_class.py @@ -1,5 +1,133 @@ from django.test import TestCase +from reporting.models.report_emission import ReportEmission +from reporting.models.report_product import ReportProduct +from reporting.models.report_product_emission_allocation import ReportProductEmissionAllocation +from reporting.service.compliance_service import ComplianceService +from infrastructure import ComplianceTestInfrastructure class TestComplianceSummaryServiceClass(TestCase): - print('class') + def test_compliance_summary_only_flaring_single_product(self): + build_data = ComplianceTestInfrastructure.build() + # Pare down build data to data needed for this test + ReportProductEmissionAllocation.objects.exclude(emission_category_id=1).delete() + ReportProduct.objects.exclude(product_id=1).delete() + ReportEmission.objects.exclude(gas_type_id=1).delete() + + result = ComplianceService.get_calculated_compliance_data(build_data.report_version_1.id) + emissions = ReportEmission.objects.all() + print(emissions) + print('### RESULT ###') + print(result.__dict__) + # print('### REG VALUES ###') + # print (result.regulatory_values.__dict__) + # print('### PRODUCTS ###') + # for p in result.products: + # print (p.__dict__) + + # def test_compliance_summary_with_all_data(self): + # build_data = ComplianceTestInfrastructure.build() + # ## SETUP ## + # # operation_1 = make_recipe( + # # 'registration.tests.utils.operation', + # # name='test sfo', + # # naics_code=NaicsCode.objects.get(pk=1), + # # type='Single Facility Operation' + # # ) + # # report_1 = make_recipe( + # # "reporting.tests.utils.report", + # # operation=operation_1, + # # reporting_year=ReportingYear.objects.get(pk=2024) + # # ) + # # report_version_1 = make_recipe( + # # "reporting.tests.utils.report_version", + # # report=report_1 + # # ) + # # report_emission_1 = make_recipe( + # # "reporting.tests.utils.report_emission", + # # report_version=report_version_1, + # # json_data={"equivalentEmission": 10000.0001}, + # # ) + # # report_emission_2 = make_recipe( + # # "reporting.tests.utils.report_emission", + # # report_version=report_version_1, + # # json_data={"equivalentEmission": 20000.9988}, + # # ) + # # report_emission_3 = make_recipe( + # # "reporting.tests.utils.report_emission", + # # report_version=report_version_1, + # # json_data={"equivalentEmission": 3000.05}, + # # ) + + # # report_emission_1.emission_categories.set([1]) + # # report_emission_2.emission_categories.set([3]) + # # report_emission_3.emission_categories.set([4, 12]) + + # # report_product_1 = make_recipe( + # # "reporting.tests.utils.report_product", + # # report_version=report_version_1, + # # product_id=1, + # # annual_production=Decimal('100000'), + # # production_data_apr_dec=Decimal('50000'), + # # ) + # # report_product_2 = make_recipe( + # # "reporting.tests.utils.report_product", + # # report_version=report_version_1, + # # product_id=2, + # # annual_production=Decimal('1000000'), + # # production_data_apr_dec=Decimal('250000'), + # # ) + # # report_product_3 = make_recipe( + # # "reporting.tests.utils.report_product", + # # report_version=report_version_1, + # # product_id=3, + # # annual_production=Decimal('2000000'), + # # production_data_apr_dec=Decimal('1500000'), + # # ) + + # # make_recipe( + # # "reporting.tests.utils.report_product_emission_allocation", + # # report_version=report_version_1, + # # report_product=report_product_1, + # # emission_category=EmissionCategory.objects.get(pk=1), + # # allocated_quantity=Decimal('10000.0001'), + # # ) + # # make_recipe( + # # "reporting.tests.utils.report_product_emission_allocation", + # # report_version=report_version_1, + # # emission_category=EmissionCategory.objects.get(pk=3), + # # report_product=report_product_2, + # # allocated_quantity=Decimal('10000.9900'), + # # ) + # # make_recipe( + # # "reporting.tests.utils.report_product_emission_allocation", + # # report_version=report_version_1, + # # emission_category=EmissionCategory.objects.get(pk=3), + # # report_product=report_product_3, + # # allocated_quantity=Decimal('10000.0088'), + # # ) + # # make_recipe( + # # "reporting.tests.utils.report_product_emission_allocation", + # # report_version=report_version_1, + # # emission_category=EmissionCategory.objects.get(pk=4), + # # report_product=report_product_1, + # # allocated_quantity=Decimal('3000.0005'), + # # ) + # # make_recipe( + # # "reporting.tests.utils.report_product_emission_allocation", + # # report_version=report_version_1, + # # emission_category=EmissionCategory.objects.get(pk=12), + # # report_product=report_product_1, + # # allocated_quantity=Decimal('3000.0005'), + # # ) + + # ## TESTS ## + + # result = ComplianceService.get_calculated_compliance_data(build_data.report_version_1.id) + # print('### RESULT ###') + # print (result.__dict__) + # print('### REG VALUES ###') + # print (result.regulatory_values.__dict__) + # print('### PRODUCTS ###') + # for p in result.products: + # print (p.__dict__) From a85eda42575c2940c0cfaa083da5a4e932a4c792 Mon Sep 17 00:00:00 2001 From: Dylan Leard Date: Mon, 13 Jan 2025 15:33:23 -0800 Subject: [PATCH 08/20] test: add infrastructure for test cases --- .../test_compliance_service/infrastructure.py | 27 ++++ .../test_compliance_service_class.py | 137 +++--------------- 2 files changed, 47 insertions(+), 117 deletions(-) diff --git a/bc_obps/reporting/tests/service/test_compliance_service/infrastructure.py b/bc_obps/reporting/tests/service/test_compliance_service/infrastructure.py index 3fe3e28060..29965b7a5d 100644 --- a/bc_obps/reporting/tests/service/test_compliance_service/infrastructure.py +++ b/bc_obps/reporting/tests/service/test_compliance_service/infrastructure.py @@ -122,3 +122,30 @@ def build(cls): ) return t + + @classmethod + def pare_data_single_product_flaring(cls): + # Pare down build data to data needed for this test + ReportProductEmissionAllocation.objects.exclude(emission_category_id=1).delete() + ReportProduct.objects.exclude(product_id=1).delete() + ReportEmission.objects.exclude(gas_type_id=1).delete() + + @classmethod + def pare_data_remove_reporting_only(cls): + # Pare down build data to data needed for this test + ReportProductEmissionAllocation.objects.filter(allocated_quantity='3000.0005').delete() + ReportProduct.objects.exclude(product_id=1).delete() + ReportEmission.objects.filter(gas_type_id=3).delete() + + @classmethod + def reporting_year_2025(cls): + reporting_year = make_recipe( + "reporting.tests.utils.reporting_year", + reporting_year=2025, + reporting_window_start='2025-12-31 16:00:00-08', + reporting_window_end='2026-12-31 16:00:00-08', + report_due_data='2025-05-31 16:59:59.999-07', + ) + report = Report.objects.get(reporting_year_id=2024) + report.reporting_year = reporting_year + report.save() diff --git a/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_class.py b/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_class.py index eae3dd22a2..2af0a80411 100644 --- a/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_class.py +++ b/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_class.py @@ -1,7 +1,5 @@ from django.test import TestCase from reporting.models.report_emission import ReportEmission -from reporting.models.report_product import ReportProduct -from reporting.models.report_product_emission_allocation import ReportProductEmissionAllocation from reporting.service.compliance_service import ComplianceService from infrastructure import ComplianceTestInfrastructure @@ -9,125 +7,30 @@ class TestComplianceSummaryServiceClass(TestCase): def test_compliance_summary_only_flaring_single_product(self): build_data = ComplianceTestInfrastructure.build() - # Pare down build data to data needed for this test - ReportProductEmissionAllocation.objects.exclude(emission_category_id=1).delete() - ReportProduct.objects.exclude(product_id=1).delete() - ReportEmission.objects.exclude(gas_type_id=1).delete() - + ComplianceTestInfrastructure.pare_data_single_product_flaring() result = ComplianceService.get_calculated_compliance_data(build_data.report_version_1.id) emissions = ReportEmission.objects.all() print(emissions) print('### RESULT ###') print(result.__dict__) - # print('### REG VALUES ###') - # print (result.regulatory_values.__dict__) - # print('### PRODUCTS ###') - # for p in result.products: - # print (p.__dict__) - - # def test_compliance_summary_with_all_data(self): - # build_data = ComplianceTestInfrastructure.build() - # ## SETUP ## - # # operation_1 = make_recipe( - # # 'registration.tests.utils.operation', - # # name='test sfo', - # # naics_code=NaicsCode.objects.get(pk=1), - # # type='Single Facility Operation' - # # ) - # # report_1 = make_recipe( - # # "reporting.tests.utils.report", - # # operation=operation_1, - # # reporting_year=ReportingYear.objects.get(pk=2024) - # # ) - # # report_version_1 = make_recipe( - # # "reporting.tests.utils.report_version", - # # report=report_1 - # # ) - # # report_emission_1 = make_recipe( - # # "reporting.tests.utils.report_emission", - # # report_version=report_version_1, - # # json_data={"equivalentEmission": 10000.0001}, - # # ) - # # report_emission_2 = make_recipe( - # # "reporting.tests.utils.report_emission", - # # report_version=report_version_1, - # # json_data={"equivalentEmission": 20000.9988}, - # # ) - # # report_emission_3 = make_recipe( - # # "reporting.tests.utils.report_emission", - # # report_version=report_version_1, - # # json_data={"equivalentEmission": 3000.05}, - # # ) - - # # report_emission_1.emission_categories.set([1]) - # # report_emission_2.emission_categories.set([3]) - # # report_emission_3.emission_categories.set([4, 12]) - - # # report_product_1 = make_recipe( - # # "reporting.tests.utils.report_product", - # # report_version=report_version_1, - # # product_id=1, - # # annual_production=Decimal('100000'), - # # production_data_apr_dec=Decimal('50000'), - # # ) - # # report_product_2 = make_recipe( - # # "reporting.tests.utils.report_product", - # # report_version=report_version_1, - # # product_id=2, - # # annual_production=Decimal('1000000'), - # # production_data_apr_dec=Decimal('250000'), - # # ) - # # report_product_3 = make_recipe( - # # "reporting.tests.utils.report_product", - # # report_version=report_version_1, - # # product_id=3, - # # annual_production=Decimal('2000000'), - # # production_data_apr_dec=Decimal('1500000'), - # # ) - # # make_recipe( - # # "reporting.tests.utils.report_product_emission_allocation", - # # report_version=report_version_1, - # # report_product=report_product_1, - # # emission_category=EmissionCategory.objects.get(pk=1), - # # allocated_quantity=Decimal('10000.0001'), - # # ) - # # make_recipe( - # # "reporting.tests.utils.report_product_emission_allocation", - # # report_version=report_version_1, - # # emission_category=EmissionCategory.objects.get(pk=3), - # # report_product=report_product_2, - # # allocated_quantity=Decimal('10000.9900'), - # # ) - # # make_recipe( - # # "reporting.tests.utils.report_product_emission_allocation", - # # report_version=report_version_1, - # # emission_category=EmissionCategory.objects.get(pk=3), - # # report_product=report_product_3, - # # allocated_quantity=Decimal('10000.0088'), - # # ) - # # make_recipe( - # # "reporting.tests.utils.report_product_emission_allocation", - # # report_version=report_version_1, - # # emission_category=EmissionCategory.objects.get(pk=4), - # # report_product=report_product_1, - # # allocated_quantity=Decimal('3000.0005'), - # # ) - # # make_recipe( - # # "reporting.tests.utils.report_product_emission_allocation", - # # report_version=report_version_1, - # # emission_category=EmissionCategory.objects.get(pk=12), - # # report_product=report_product_1, - # # allocated_quantity=Decimal('3000.0005'), - # # ) - - # ## TESTS ## + def test_with_industrial_process_emissions(self): + build_data = ComplianceTestInfrastructure.build() + ComplianceTestInfrastructure.pare_data_remove_reporting_only() + result = ComplianceService.get_calculated_compliance_data(build_data.report_version_1.id) + emissions = ReportEmission.objects.all() + print(emissions) + print('### RESULT ###') + print(result.__dict__) - # result = ComplianceService.get_calculated_compliance_data(build_data.report_version_1.id) - # print('### RESULT ###') - # print (result.__dict__) - # print('### REG VALUES ###') - # print (result.regulatory_values.__dict__) - # print('### PRODUCTS ###') - # for p in result.products: - # print (p.__dict__) + def test_compliance_summary_with_all_data(self): + build_data = ComplianceTestInfrastructure.build() + ## TESTS ## + result = ComplianceService.get_calculated_compliance_data(build_data.report_version_1.id) + print('### RESULT ###') + print(result.__dict__) + print('### REG VALUES ###') + print(result.regulatory_values.__dict__) + print('### PRODUCTS ###') + for p in result.products: + print(p.__dict__) From 058484b8a6fba8c606224f62fd774a325a5fb44f Mon Sep 17 00:00:00 2001 From: Dylan Leard Date: Mon, 13 Jan 2025 16:06:21 -0800 Subject: [PATCH 09/20] test: add to infrastructure --- .../tests/service/test_compliance_service/infrastructure.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bc_obps/reporting/tests/service/test_compliance_service/infrastructure.py b/bc_obps/reporting/tests/service/test_compliance_service/infrastructure.py index 29965b7a5d..c789a86e0f 100644 --- a/bc_obps/reporting/tests/service/test_compliance_service/infrastructure.py +++ b/bc_obps/reporting/tests/service/test_compliance_service/infrastructure.py @@ -149,3 +149,9 @@ def reporting_year_2025(cls): report = Report.objects.get(reporting_year_id=2024) report.reporting_year = reporting_year report.save() + + @classmethod + def new_entrant(cls): + operation = Operation.objects.get(name='test sfo') + operation.registration_purpose = 'New Entrant Operation' + operation.save() From 2cfc8f1641494cb7b0c15a952ad937c7d9db75eb Mon Sep 17 00:00:00 2001 From: Dylan Leard Date: Tue, 14 Jan 2025 14:35:31 -0800 Subject: [PATCH 10/20] chore: use named parameters in emission limit function call --- bc_obps/reporting/service/compliance_service.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/bc_obps/reporting/service/compliance_service.py b/bc_obps/reporting/service/compliance_service.py index 2081a62aad..4fc2ed2556 100644 --- a/bc_obps/reporting/service/compliance_service.py +++ b/bc_obps/reporting/service/compliance_service.py @@ -208,13 +208,13 @@ def get_calculated_compliance_data(cls, report_version_id: int) -> ComplianceDat allocated_for_compliance / Decimal(production_totals["annual_amount"]) ) * Decimal(production_totals["apr_dec"]) product_emission_limit = ComplianceService.calculate_product_emission_limit( - ei, - Decimal(production_totals["apr_dec"]), - Decimal(industrial_process), - Decimal(allocated_for_compliance), - naics_data.reduction_factor, - naics_data.tightening_rate, - naics_data.compliance_period, + pwaei=ei, + apr_dec_production=Decimal(production_totals["apr_dec"]), + allocated_industrial_process=Decimal(industrial_process), + allocated_for_compliance=Decimal(allocated_for_compliance), + tightening_rate=naics_data.tightening_rate, + reduction_factor=naics_data.reduction_factor, + compliance_period=naics_data.compliance_period, ) # Add individual product amounts to totals From 50dd23fcf8b4309cd690a1e94381ccd051945219 Mon Sep 17 00:00:00 2001 From: Dylan Leard Date: Wed, 15 Jan 2025 16:56:13 -0800 Subject: [PATCH 11/20] test: update test --- .../test_compliance_service_class.py | 45 +++++++++++-------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_class.py b/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_class.py index 2af0a80411..0ae87d0194 100644 --- a/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_class.py +++ b/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_class.py @@ -1,7 +1,7 @@ from django.test import TestCase -from reporting.models.report_emission import ReportEmission from reporting.service.compliance_service import ComplianceService from infrastructure import ComplianceTestInfrastructure +from decimal import Decimal class TestComplianceSummaryServiceClass(TestCase): @@ -9,24 +9,7 @@ def test_compliance_summary_only_flaring_single_product(self): build_data = ComplianceTestInfrastructure.build() ComplianceTestInfrastructure.pare_data_single_product_flaring() result = ComplianceService.get_calculated_compliance_data(build_data.report_version_1.id) - emissions = ReportEmission.objects.all() - print(emissions) - print('### RESULT ###') - print(result.__dict__) - def test_with_industrial_process_emissions(self): - build_data = ComplianceTestInfrastructure.build() - ComplianceTestInfrastructure.pare_data_remove_reporting_only() - result = ComplianceService.get_calculated_compliance_data(build_data.report_version_1.id) - emissions = ReportEmission.objects.all() - print(emissions) - print('### RESULT ###') - print(result.__dict__) - - def test_compliance_summary_with_all_data(self): - build_data = ComplianceTestInfrastructure.build() - ## TESTS ## - result = ComplianceService.get_calculated_compliance_data(build_data.report_version_1.id) print('### RESULT ###') print(result.__dict__) print('### REG VALUES ###') @@ -34,3 +17,29 @@ def test_compliance_summary_with_all_data(self): print('### PRODUCTS ###') for p in result.products: print(p.__dict__) + assert result.emissions_attributable_for_reporting == Decimal('10000.0001') + # assert result.emissions_attributable_for_compliance == Decimal('5000.0001') + assert result.emissions_limit == Decimal('159.25') + assert result.excess_emissions == Decimal('4840.75') + assert result.credited_emissions == 0 + + # def test_with_industrial_process_emissions(self): + # build_data = ComplianceTestInfrastructure.build() + # ComplianceTestInfrastructure.pare_data_remove_reporting_only() + # result = ComplianceService.get_calculated_compliance_data(build_data.report_version_1.id) + # emissions = ReportEmission.objects.all() + # print(emissions) + # print('### RESULT ###') + # print(result.__dict__) + + # def test_compliance_summary_with_all_data(self): + # build_data = ComplianceTestInfrastructure.build() + # ## TESTS ## + # result = ComplianceService.get_calculated_compliance_data(build_data.report_version_1.id) + # print('### RESULT ###') + # print(result.__dict__) + # print('### REG VALUES ###') + # print(result.regulatory_values.__dict__) + # print('### PRODUCTS ###') + # for p in result.products: + # print(p.__dict__) From 18b643c760feb12d1427965e3189c61d59533549 Mon Sep 17 00:00:00 2001 From: Dylan Leard Date: Thu, 16 Jan 2025 17:01:04 -0800 Subject: [PATCH 12/20] test: add compliance service tests --- .../test_compliance_service/infrastructure.py | 51 +++++++----- .../test_compliance_service_class.py | 77 ++++++++++++++----- 2 files changed, 89 insertions(+), 39 deletions(-) diff --git a/bc_obps/reporting/tests/service/test_compliance_service/infrastructure.py b/bc_obps/reporting/tests/service/test_compliance_service/infrastructure.py index c789a86e0f..fe79f132ff 100644 --- a/bc_obps/reporting/tests/service/test_compliance_service/infrastructure.py +++ b/bc_obps/reporting/tests/service/test_compliance_service/infrastructure.py @@ -18,6 +18,7 @@ class ComplianceTestInfrastructure: report_emission_1: ReportEmission report_emission_2: ReportEmission report_emission_3: ReportEmission + report_emission_4: ReportEmission report_product_1: ReportProduct report_product_2: ReportProduct report_product_3: ReportProduct @@ -26,6 +27,7 @@ class ComplianceTestInfrastructure: allocation_3: ReportProductEmissionAllocation allocation_4: ReportProductEmissionAllocation allocation_5: ReportProductEmissionAllocation + allocation_6: ReportProductEmissionAllocation @classmethod def build(cls): @@ -50,9 +52,15 @@ def build(cls): "reporting.tests.utils.report_emission", report_version=t.report_version_1, gas_type_id=2, - json_data={"equivalentEmission": 20000.9988}, + json_data={"equivalentEmission": 10000.9988}, ) t.report_emission_3 = make_recipe( + "reporting.tests.utils.report_emission", + report_version=t.report_version_1, + gas_type_id=2, + json_data={"equivalentEmission": 100000.0088}, + ) + t.report_emission_4 = make_recipe( "reporting.tests.utils.report_emission", report_version=t.report_version_1, gas_type_id=3, @@ -61,7 +69,8 @@ def build(cls): t.report_emission_1.emission_categories.set([1]) t.report_emission_2.emission_categories.set([3]) - t.report_emission_3.emission_categories.set([4, 12]) + t.report_emission_3.emission_categories.set([4]) + t.report_emission_4.emission_categories.set([5, 12]) t.report_product_1 = make_recipe( "reporting.tests.utils.report_product", @@ -74,51 +83,58 @@ def build(cls): "reporting.tests.utils.report_product", report_version=t.report_version_1, product_id=2, - annual_production=Decimal('1000000'), - production_data_apr_dec=Decimal('250000'), + annual_production=Decimal('100000'), + production_data_apr_dec=Decimal('25000'), ) t.report_product_3 = make_recipe( "reporting.tests.utils.report_product", report_version=t.report_version_1, product_id=3, - annual_production=Decimal('2000000'), - production_data_apr_dec=Decimal('1500000'), + annual_production=Decimal('20000'), + production_data_apr_dec=Decimal('15000'), ) t.allocation_1 = make_recipe( "reporting.tests.utils.report_product_emission_allocation", report_version=t.report_version_1, report_product=t.report_product_1, - emission_category=EmissionCategory.objects.get(pk=1), + emission_category=EmissionCategory.objects.get(pk=1), # Flaring Product 1 allocated_quantity=Decimal('10000.0001'), ) t.allocation_2 = make_recipe( "reporting.tests.utils.report_product_emission_allocation", report_version=t.report_version_1, - emission_category=EmissionCategory.objects.get(pk=3), + emission_category=EmissionCategory.objects.get(pk=3), # Industrial Process Product 2 report_product=t.report_product_2, - allocated_quantity=Decimal('10000.9900'), + allocated_quantity=Decimal('10000.9988'), ) t.allocation_3 = make_recipe( "reporting.tests.utils.report_product_emission_allocation", report_version=t.report_version_1, - emission_category=EmissionCategory.objects.get(pk=3), + emission_category=EmissionCategory.objects.get(pk=4), # Mobile Product 3 report_product=t.report_product_3, - allocated_quantity=Decimal('10000.0088'), + allocated_quantity=Decimal('75000.0088'), ) t.allocation_4 = make_recipe( "reporting.tests.utils.report_product_emission_allocation", report_version=t.report_version_1, - emission_category=EmissionCategory.objects.get(pk=4), + emission_category=EmissionCategory.objects.get(pk=5), # GSC Product 1 report_product=t.report_product_1, - allocated_quantity=Decimal('3000.0005'), + allocated_quantity=Decimal('3000.05'), ) t.allocation_5 = make_recipe( "reporting.tests.utils.report_product_emission_allocation", report_version=t.report_version_1, - emission_category=EmissionCategory.objects.get(pk=12), + emission_category=EmissionCategory.objects.get(pk=12), # Excluded nonbio Product 1 report_product=t.report_product_1, - allocated_quantity=Decimal('3000.0005'), + allocated_quantity=Decimal('3000.05'), + ) + t.allocation_6 = make_recipe( + "reporting.tests.utils.report_product_emission_allocation", + report_version=t.report_version_1, + report_product=t.report_product_2, + emission_category=EmissionCategory.objects.get(pk=4), # Mobile Product 2 + allocated_quantity=Decimal('25000.0000'), ) return t @@ -133,8 +149,7 @@ def pare_data_single_product_flaring(cls): @classmethod def pare_data_remove_reporting_only(cls): # Pare down build data to data needed for this test - ReportProductEmissionAllocation.objects.filter(allocated_quantity='3000.0005').delete() - ReportProduct.objects.exclude(product_id=1).delete() + ReportProductEmissionAllocation.objects.filter(emission_category__in=[5, 12]).delete() ReportEmission.objects.filter(gas_type_id=3).delete() @classmethod @@ -144,7 +159,7 @@ def reporting_year_2025(cls): reporting_year=2025, reporting_window_start='2025-12-31 16:00:00-08', reporting_window_end='2026-12-31 16:00:00-08', - report_due_data='2025-05-31 16:59:59.999-07', + report_due_date='2025-05-31 16:59:59.999-07', ) report = Report.objects.get(reporting_year_id=2024) report.reporting_year = reporting_year diff --git a/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_class.py b/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_class.py index 0ae87d0194..8bedaa8920 100644 --- a/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_class.py +++ b/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_class.py @@ -18,28 +18,63 @@ def test_compliance_summary_only_flaring_single_product(self): for p in result.products: print(p.__dict__) assert result.emissions_attributable_for_reporting == Decimal('10000.0001') - # assert result.emissions_attributable_for_compliance == Decimal('5000.0001') assert result.emissions_limit == Decimal('159.25') assert result.excess_emissions == Decimal('4840.75') assert result.credited_emissions == 0 - # def test_with_industrial_process_emissions(self): - # build_data = ComplianceTestInfrastructure.build() - # ComplianceTestInfrastructure.pare_data_remove_reporting_only() - # result = ComplianceService.get_calculated_compliance_data(build_data.report_version_1.id) - # emissions = ReportEmission.objects.all() - # print(emissions) - # print('### RESULT ###') - # print(result.__dict__) - - # def test_compliance_summary_with_all_data(self): - # build_data = ComplianceTestInfrastructure.build() - # ## TESTS ## - # result = ComplianceService.get_calculated_compliance_data(build_data.report_version_1.id) - # print('### RESULT ###') - # print(result.__dict__) - # print('### REG VALUES ###') - # print(result.regulatory_values.__dict__) - # print('### PRODUCTS ###') - # for p in result.products: - # print(p.__dict__) + def test_with_industrial_process_emissions(self): + build_data = ComplianceTestInfrastructure.build() + ComplianceTestInfrastructure.pare_data_remove_reporting_only() + result = ComplianceService.get_calculated_compliance_data(build_data.report_version_1.id) + print('### RESULT ###') + print(result.__dict__) + print(result.regulatory_values.__dict__) + print('### PRODUCTS ###') + for p in result.products: + print(p.__dict__) + + assert result.emissions_attributable_for_reporting == Decimal('120001.0077') + assert result.reporting_only_emissions == 0 + assert result.emissions_attributable_for_compliance == Decimal('70000.2564') + assert result.emissions_limit == Decimal('20767.5000') + assert result.excess_emissions == Decimal('49232.7564') + assert result.credited_emissions == 0 + + def test_compliance_summary_with_all_data(self): + build_data = ComplianceTestInfrastructure.build() + ## TESTS ## + result = ComplianceService.get_calculated_compliance_data(build_data.report_version_1.id) + print('### RESULT ###') + print(result.__dict__) + print('### REG VALUES ###') + print(result.regulatory_values.__dict__) + print('### PRODUCTS ###') + for p in result.products: + print(p.__dict__) + + assert result.emissions_attributable_for_reporting == Decimal('123001.0577') + assert result.reporting_only_emissions == Decimal('3000.05') + assert result.emissions_attributable_for_compliance == Decimal('70000.2564') + assert result.emissions_limit == Decimal('20767.5000') + assert result.excess_emissions == Decimal('49232.7564') + assert result.credited_emissions == 0 + + def test_compliance_summary_2025_period(self): + build_data = ComplianceTestInfrastructure.build() + ComplianceTestInfrastructure.reporting_year_2025() + ## TESTS ## + result = ComplianceService.get_calculated_compliance_data(build_data.report_version_1.id) + print('### RESULT ###') + print(result.__dict__) + print('### REG VALUES ###') + print(result.regulatory_values.__dict__) + print('### PRODUCTS ###') + for p in result.products: + print(p.__dict__) + + assert result.emissions_attributable_for_reporting == Decimal('123001.0577') + assert result.reporting_only_emissions == Decimal('3000.05') + assert result.emissions_attributable_for_compliance == Decimal('70000.2564') + assert result.emissions_limit == Decimal('20492.7318') + assert result.excess_emissions == Decimal('49507.5246') + assert result.credited_emissions == 0 From b385c351cd096e3f21a49f44ade53b06556206aa Mon Sep 17 00:00:00 2001 From: Dylan Leard Date: Fri, 17 Jan 2025 10:12:19 -0800 Subject: [PATCH 13/20] test: new entrant test --- .../test_compliance_service_class.py | 43 ++++++------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_class.py b/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_class.py index 8bedaa8920..14b15498ad 100644 --- a/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_class.py +++ b/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_class.py @@ -8,15 +8,9 @@ class TestComplianceSummaryServiceClass(TestCase): def test_compliance_summary_only_flaring_single_product(self): build_data = ComplianceTestInfrastructure.build() ComplianceTestInfrastructure.pare_data_single_product_flaring() + result = ComplianceService.get_calculated_compliance_data(build_data.report_version_1.id) - print('### RESULT ###') - print(result.__dict__) - print('### REG VALUES ###') - print(result.regulatory_values.__dict__) - print('### PRODUCTS ###') - for p in result.products: - print(p.__dict__) assert result.emissions_attributable_for_reporting == Decimal('10000.0001') assert result.emissions_limit == Decimal('159.25') assert result.excess_emissions == Decimal('4840.75') @@ -25,13 +19,8 @@ def test_compliance_summary_only_flaring_single_product(self): def test_with_industrial_process_emissions(self): build_data = ComplianceTestInfrastructure.build() ComplianceTestInfrastructure.pare_data_remove_reporting_only() + result = ComplianceService.get_calculated_compliance_data(build_data.report_version_1.id) - print('### RESULT ###') - print(result.__dict__) - print(result.regulatory_values.__dict__) - print('### PRODUCTS ###') - for p in result.products: - print(p.__dict__) assert result.emissions_attributable_for_reporting == Decimal('120001.0077') assert result.reporting_only_emissions == 0 @@ -42,15 +31,8 @@ def test_with_industrial_process_emissions(self): def test_compliance_summary_with_all_data(self): build_data = ComplianceTestInfrastructure.build() - ## TESTS ## + result = ComplianceService.get_calculated_compliance_data(build_data.report_version_1.id) - print('### RESULT ###') - print(result.__dict__) - print('### REG VALUES ###') - print(result.regulatory_values.__dict__) - print('### PRODUCTS ###') - for p in result.products: - print(p.__dict__) assert result.emissions_attributable_for_reporting == Decimal('123001.0577') assert result.reporting_only_emissions == Decimal('3000.05') @@ -62,15 +44,8 @@ def test_compliance_summary_with_all_data(self): def test_compliance_summary_2025_period(self): build_data = ComplianceTestInfrastructure.build() ComplianceTestInfrastructure.reporting_year_2025() - ## TESTS ## + result = ComplianceService.get_calculated_compliance_data(build_data.report_version_1.id) - print('### RESULT ###') - print(result.__dict__) - print('### REG VALUES ###') - print(result.regulatory_values.__dict__) - print('### PRODUCTS ###') - for p in result.products: - print(p.__dict__) assert result.emissions_attributable_for_reporting == Decimal('123001.0577') assert result.reporting_only_emissions == Decimal('3000.05') @@ -78,3 +53,13 @@ def test_compliance_summary_2025_period(self): assert result.emissions_limit == Decimal('20492.7318') assert result.excess_emissions == Decimal('49507.5246') assert result.credited_emissions == 0 + + def test_new_entrant(self): + build_data = ComplianceTestInfrastructure.build() + ComplianceTestInfrastructure.new_entrant() + ## TESTS ## + result = ComplianceService.get_calculated_compliance_data(build_data.report_version_1.id) + + assert result.emissions_limit == 0 + assert result.excess_emissions == 0 + assert result.credited_emissions == 0 From e1abded0779645e4d6686155ebb06e6b67eb1216 Mon Sep 17 00:00:00 2001 From: Dylan Leard Date: Fri, 17 Jan 2025 10:28:41 -0800 Subject: [PATCH 14/20] test: add emission limit static test --- .../test_compliance_service_static.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_static.py b/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_static.py index c5b1de9715..08f37e7cf5 100644 --- a/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_static.py +++ b/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_static.py @@ -265,3 +265,31 @@ def test_get_reporting_only_allocated(self): + allocation_4.allocated_quantity + fog_product_allocation.allocated_quantity ) + + def test_get_emission_limit(self): + emission_limit_for_test = ComplianceService.calculate_product_emission_limit( + pwaei=Decimal('0.5'), + apr_dec_production=Decimal('20000'), + allocated_industrial_process=Decimal('5000'), + allocated_for_compliance=Decimal('20000'), + tightening_rate=Decimal('0.1'), + reduction_factor=Decimal('0.65'), + compliance_period=2025, + initial_compliance_period=2024, + ) + + # CALCULATION DEFINITION + # product_emission_limit = (apr_dec_production * pwaei) * ( + # reduction_factor + # - ( + # (Decimal('1') - (allocated_industrial_process / allocated_for_compliance)) + # * tightening_rate + # * (compliance_period - initial_compliance_period) + # ) + # ) + # = (20000 * 0.5) * (0.65 - ((1 - (5000 / 20000)) * 0.1 * (2025-2024))) + # = 10000 * (0.65 - ((1 - 0.25)) * 0.1) + # = 10000 * (0.65 - 0.075) + # = 10000 * 0.575 + # = 5750 + assert emission_limit_for_test == Decimal('5750') From b1a73ef35c0dcf0f7bbda626331f7cc5398bd6f9 Mon Sep 17 00:00:00 2001 From: Dylan Leard Date: Fri, 17 Jan 2025 10:39:08 -0800 Subject: [PATCH 15/20] test: add contextual excel sheet used to calculate test assertion values --- .../compliance_class_manual_calcs.xlsx | Bin 0 -> 11489 bytes .../test_compliance_service_class.py | 4 ++++ 2 files changed, 4 insertions(+) create mode 100644 bc_obps/reporting/tests/service/test_compliance_service/compliance_class_manual_calcs.xlsx diff --git a/bc_obps/reporting/tests/service/test_compliance_service/compliance_class_manual_calcs.xlsx b/bc_obps/reporting/tests/service/test_compliance_service/compliance_class_manual_calcs.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..dcdc62808c30ac74400373d7e2df31e51660150f GIT binary patch literal 11489 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 CS(YaN literal 0 HcmV?d00001 diff --git a/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_class.py b/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_class.py index 14b15498ad..b22a51a025 100644 --- a/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_class.py +++ b/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_class.py @@ -6,6 +6,7 @@ class TestComplianceSummaryServiceClass(TestCase): def test_compliance_summary_only_flaring_single_product(self): + # Assertion values from compliance_class_manual_calcs.xlsx sheet 1 build_data = ComplianceTestInfrastructure.build() ComplianceTestInfrastructure.pare_data_single_product_flaring() @@ -17,6 +18,7 @@ def test_compliance_summary_only_flaring_single_product(self): assert result.credited_emissions == 0 def test_with_industrial_process_emissions(self): + # Assertion values from compliance_class_manual_calcs.xlsx sheet 2 build_data = ComplianceTestInfrastructure.build() ComplianceTestInfrastructure.pare_data_remove_reporting_only() @@ -30,6 +32,7 @@ def test_with_industrial_process_emissions(self): assert result.credited_emissions == 0 def test_compliance_summary_with_all_data(self): + # Assertion values from compliance_class_manual_calcs.xlsx sheet 3 build_data = ComplianceTestInfrastructure.build() result = ComplianceService.get_calculated_compliance_data(build_data.report_version_1.id) @@ -42,6 +45,7 @@ def test_compliance_summary_with_all_data(self): assert result.credited_emissions == 0 def test_compliance_summary_2025_period(self): + # Assertion values from compliance_class_manual_calcs.xlsx sheet 4 build_data = ComplianceTestInfrastructure.build() ComplianceTestInfrastructure.reporting_year_2025() From e55b0031cc6ec9c5760923e7bcc0b4af98266144 Mon Sep 17 00:00:00 2001 From: Dylan Leard Date: Fri, 17 Jan 2025 10:58:52 -0800 Subject: [PATCH 16/20] test: fix FE tests after rebase --- .../complianceSummary/ComplianceSummaryForm.test.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/bciers/apps/reporting/src/tests/components/complianceSummary/ComplianceSummaryForm.test.tsx b/bciers/apps/reporting/src/tests/components/complianceSummary/ComplianceSummaryForm.test.tsx index 6029b24a5e..892147b1cd 100644 --- a/bciers/apps/reporting/src/tests/components/complianceSummary/ComplianceSummaryForm.test.tsx +++ b/bciers/apps/reporting/src/tests/components/complianceSummary/ComplianceSummaryForm.test.tsx @@ -56,7 +56,8 @@ describe("ComplianceSummaryForm", () => { it("should render the calculation summary data", async () => { render( - { it("should render the regulatory values summary data", async () => { render( - { it("should render the production summary data", async () => { render( - Date: Fri, 17 Jan 2025 13:36:50 -0800 Subject: [PATCH 17/20] chore: handle divide by zero in emission limit calc --- bc_obps/reporting/service/compliance_service.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/bc_obps/reporting/service/compliance_service.py b/bc_obps/reporting/service/compliance_service.py index 4fc2ed2556..7ffe40ef05 100644 --- a/bc_obps/reporting/service/compliance_service.py +++ b/bc_obps/reporting/service/compliance_service.py @@ -161,10 +161,15 @@ def calculate_product_emission_limit( compliance_period: int, initial_compliance_period: int = 2024, ) -> Decimal: + # Handle divide by 0 error if allocated_for_compliance is 0 + industrial_process_compliance_allocated_division = Decimal('0') + if allocated_for_compliance > 0: + industrial_process_compliance_allocated_division = allocated_industrial_process / allocated_for_compliance + product_emission_limit = (apr_dec_production * pwaei) * ( reduction_factor - ( - (Decimal('1') - (allocated_industrial_process / allocated_for_compliance)) + (Decimal('1') - (industrial_process_compliance_allocated_division)) * tightening_rate * (compliance_period - initial_compliance_period) ) From 2037afa292beb8c4e049d49d7955f0868f91442b Mon Sep 17 00:00:00 2001 From: Dylan Leard Date: Fri, 17 Jan 2025 13:37:39 -0800 Subject: [PATCH 18/20] test: add test for divide by zero solution in emission limit calc --- .../test_compliance_service_static.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_static.py b/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_static.py index 08f37e7cf5..1303a991ff 100644 --- a/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_static.py +++ b/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_static.py @@ -293,3 +293,31 @@ def test_get_emission_limit(self): # = 10000 * 0.575 # = 5750 assert emission_limit_for_test == Decimal('5750') + + def test_get_emission_limit_handles_divide_by_zero(self): + emission_limit_for_test = ComplianceService.calculate_product_emission_limit( + pwaei=Decimal('0.5'), + apr_dec_production=Decimal('20000'), + allocated_industrial_process=Decimal('5000'), + allocated_for_compliance=Decimal('0'), + tightening_rate=Decimal('0.1'), + reduction_factor=Decimal('0.65'), + compliance_period=2025, + initial_compliance_period=2024, + ) + + # CALCULATION DEFINITION + # product_emission_limit = (apr_dec_production * pwaei) * ( + # reduction_factor + # - ( + # (Decimal('1') - (allocated_industrial_process / allocated_for_compliance)) + # * tightening_rate + # * (compliance_period - initial_compliance_period) + # ) + # ) + # = (20000 * 0.5) * (0.65 - ((1 - (5000 / 0)) * 0.1 * (2025-2024))) + # = 10000 * (0.65 - ((1)) * 0.1) + # = 10000 * (0.65 - 0.1) + # = 10000 * 0.55 + # = 5500 + assert emission_limit_for_test == Decimal('5500') From 683990ae082457439e66843b31cb773f125f9c3d Mon Sep 17 00:00:00 2001 From: Dylan Leard Date: Fri, 17 Jan 2025 15:56:44 -0800 Subject: [PATCH 19/20] chore: remove unnecessary int types from class --- .../reporting/service/compliance_service.py | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/bc_obps/reporting/service/compliance_service.py b/bc_obps/reporting/service/compliance_service.py index 7ffe40ef05..a02c74d8d9 100644 --- a/bc_obps/reporting/service/compliance_service.py +++ b/bc_obps/reporting/service/compliance_service.py @@ -27,11 +27,11 @@ class ReportProductComplianceData: def __init__( self, name: str, - annual_production: Decimal | int, - apr_dec_production: Decimal | int, + annual_production: Decimal, + apr_dec_production: Decimal, emission_intensity: Decimal, - allocated_industrial_process_emissions: Decimal | int, - allocated_compliance_emissions: Decimal | int, + allocated_industrial_process_emissions: Decimal, + allocated_compliance_emissions: Decimal, ): self.name = name self.annual_production = annual_production @@ -44,12 +44,12 @@ def __init__( class ComplianceData: def __init__( self, - emissions_attributable_for_reporting: Decimal | int, - reporting_only_emissions: Decimal | int, - emissions_attributable_for_compliance: Decimal | int, - emissions_limit: Decimal | int, - excess_emissions: Decimal | int, - credited_emissions: Decimal | int, + emissions_attributable_for_reporting: Decimal, + reporting_only_emissions: Decimal, + emissions_attributable_for_compliance: Decimal, + emissions_limit: Decimal, + excess_emissions: Decimal, + credited_emissions: Decimal, regulatory_values: RegulatoryValues, products: List[ReportProductComplianceData], ): @@ -87,17 +87,17 @@ def get_regulatory_values_by_naics_code(report_version_id: int) -> RegulatoryVal ) @staticmethod - def get_emissions_attributable_for_reporting(report_version_id: int) -> Decimal | int: + def get_emissions_attributable_for_reporting(report_version_id: int) -> Decimal: records = ReportEmission.objects_with_decimal_emissions.filter( report_version_id=report_version_id, emission_categories__category_type='basic', ) attributable_sum = records.aggregate(emission_sum=Sum('emission')) - return attributable_sum['emission_sum'] or 0 + return attributable_sum['emission_sum'] or Decimal('0') @staticmethod - def get_production_totals(report_version_id: int) -> Dict[str, Decimal | int]: + def get_production_totals(report_version_id: int) -> Dict[str, Decimal]: records = ReportProduct.objects.filter( report_version_id=report_version_id, ) @@ -105,14 +105,14 @@ def get_production_totals(report_version_id: int) -> Dict[str, Decimal | int]: apr_dec_sum = records.aggregate(production_sum=Sum('production_data_apr_dec')) return { - "annual_amount": annual_production_sum['production_sum'] or 0, - "apr_dec": apr_dec_sum['production_sum'] or 0, + "annual_amount": annual_production_sum['production_sum'] or Decimal('0'), + "apr_dec": apr_dec_sum['production_sum'] or Decimal('0'), } @staticmethod def get_allocated_emissions_by_report_product_emission_category( report_version_id: int, product_id: int, emission_category_ids: List[int] - ) -> Decimal | int: + ) -> Decimal: records = ReportProductEmissionAllocation.objects.filter( report_version_id=report_version_id, report_product__product_id=product_id, @@ -120,10 +120,10 @@ def get_allocated_emissions_by_report_product_emission_category( ) allocated_to_category_sum = records.aggregate(production_sum=Sum('allocated_quantity')) - return allocated_to_category_sum['production_sum'] or 0 + return allocated_to_category_sum['production_sum'] or Decimal('0') @staticmethod - def get_report_product_aggregated_totals(report_version_id: int, product_id: int) -> Dict[str, Decimal | int]: + def get_report_product_aggregated_totals(report_version_id: int, product_id: int) -> Dict[str, Decimal]: records = ReportProduct.objects.filter( report_version_id=report_version_id, product_id=product_id, @@ -132,12 +132,12 @@ def get_report_product_aggregated_totals(report_version_id: int, product_id: int apr_dec_sum = records.aggregate(production_sum=Sum('production_data_apr_dec')) return { - "annual_amount": product_annual_amount_sum['production_sum'] or 0, - "apr_dec": apr_dec_sum['production_sum'] or 0, + "annual_amount": product_annual_amount_sum['production_sum'] or Decimal('0'), + "apr_dec": apr_dec_sum['production_sum'] or Decimal('0'), } @staticmethod - def get_reporting_only_allocated(report_version_id: int, product_id: int) -> Decimal | int: + def get_reporting_only_allocated(report_version_id: int, product_id: int) -> Decimal: # CO2 emissions from excluded woody biomass, Other emissions from excluded biomass, Emissions from excluded non-biomass, Fugitive emissions, Venting emissions — non-useful reporting_only_category_ids = [10, 11, 12, 2, 7] reporting_only_allocated = ComplianceService.get_allocated_emissions_by_report_product_emission_category( @@ -148,7 +148,7 @@ def get_reporting_only_allocated(report_version_id: int, product_id: int) -> Dec report_product__product_id=39, # Special Fat, Oil & Grease product ) fog_allocated_amount = fog_records.aggregate(allocated_sum=Sum('allocated_quantity')) - return reporting_only_allocated + (fog_allocated_amount['allocated_sum'] or 0) + return reporting_only_allocated + (fog_allocated_amount['allocated_sum'] or Decimal('0')) @staticmethod def calculate_product_emission_limit( From fdab5dd4b0a3d336dc2376da1106f18786f607c4 Mon Sep 17 00:00:00 2001 From: Dylan Leard Date: Fri, 17 Jan 2025 16:50:44 -0800 Subject: [PATCH 20/20] test: remove potential side effects of test infrastructure --- .../test_compliance_service/infrastructure.py | 34 +++++++++++++------ .../test_compliance_service_class.py | 12 +++---- .../test_compliance_service_static.py | 20 +++-------- 3 files changed, 32 insertions(+), 34 deletions(-) diff --git a/bc_obps/reporting/tests/service/test_compliance_service/infrastructure.py b/bc_obps/reporting/tests/service/test_compliance_service/infrastructure.py index fe79f132ff..ade1f7e79b 100644 --- a/bc_obps/reporting/tests/service/test_compliance_service/infrastructure.py +++ b/bc_obps/reporting/tests/service/test_compliance_service/infrastructure.py @@ -142,18 +142,31 @@ def build(cls): @classmethod def pare_data_single_product_flaring(cls): # Pare down build data to data needed for this test - ReportProductEmissionAllocation.objects.exclude(emission_category_id=1).delete() - ReportProduct.objects.exclude(product_id=1).delete() - ReportEmission.objects.exclude(gas_type_id=1).delete() + t = cls.build() + t.allocation_2.delete() + t.allocation_3.delete() + t.allocation_4.delete() + t.allocation_5.delete() + t.allocation_6.delete() + t.report_product_2.delete() + t.report_product_3.delete() + t.report_emission_2.delete() + t.report_emission_3.delete() + t.report_emission_4.delete() + return t @classmethod def pare_data_remove_reporting_only(cls): # Pare down build data to data needed for this test - ReportProductEmissionAllocation.objects.filter(emission_category__in=[5, 12]).delete() - ReportEmission.objects.filter(gas_type_id=3).delete() + t = cls.build() + t.allocation_4.delete() + t.allocation_5.delete() + t.report_emission_4.delete() + return t @classmethod def reporting_year_2025(cls): + t = cls.build() reporting_year = make_recipe( "reporting.tests.utils.reporting_year", reporting_year=2025, @@ -161,12 +174,11 @@ def reporting_year_2025(cls): reporting_window_end='2026-12-31 16:00:00-08', report_due_date='2025-05-31 16:59:59.999-07', ) - report = Report.objects.get(reporting_year_id=2024) - report.reporting_year = reporting_year - report.save() + Report.objects.filter(pk=t.report_1.id).update(reporting_year=reporting_year) + return t @classmethod def new_entrant(cls): - operation = Operation.objects.get(name='test sfo') - operation.registration_purpose = 'New Entrant Operation' - operation.save() + t = cls.build() + Operation.objects.filter(pk=t.operation_1.id).update(registration_purpose='New Entrant Operation') + return t diff --git a/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_class.py b/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_class.py index b22a51a025..b8684e254f 100644 --- a/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_class.py +++ b/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_class.py @@ -7,8 +7,7 @@ class TestComplianceSummaryServiceClass(TestCase): def test_compliance_summary_only_flaring_single_product(self): # Assertion values from compliance_class_manual_calcs.xlsx sheet 1 - build_data = ComplianceTestInfrastructure.build() - ComplianceTestInfrastructure.pare_data_single_product_flaring() + build_data = ComplianceTestInfrastructure.pare_data_single_product_flaring() result = ComplianceService.get_calculated_compliance_data(build_data.report_version_1.id) @@ -19,8 +18,7 @@ def test_compliance_summary_only_flaring_single_product(self): def test_with_industrial_process_emissions(self): # Assertion values from compliance_class_manual_calcs.xlsx sheet 2 - build_data = ComplianceTestInfrastructure.build() - ComplianceTestInfrastructure.pare_data_remove_reporting_only() + build_data = ComplianceTestInfrastructure.pare_data_remove_reporting_only() result = ComplianceService.get_calculated_compliance_data(build_data.report_version_1.id) @@ -46,8 +44,7 @@ def test_compliance_summary_with_all_data(self): def test_compliance_summary_2025_period(self): # Assertion values from compliance_class_manual_calcs.xlsx sheet 4 - build_data = ComplianceTestInfrastructure.build() - ComplianceTestInfrastructure.reporting_year_2025() + build_data = ComplianceTestInfrastructure.reporting_year_2025() result = ComplianceService.get_calculated_compliance_data(build_data.report_version_1.id) @@ -59,8 +56,7 @@ def test_compliance_summary_2025_period(self): assert result.credited_emissions == 0 def test_new_entrant(self): - build_data = ComplianceTestInfrastructure.build() - ComplianceTestInfrastructure.new_entrant() + build_data = ComplianceTestInfrastructure.new_entrant() ## TESTS ## result = ComplianceService.get_calculated_compliance_data(build_data.report_version_1.id) diff --git a/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_static.py b/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_static.py index 1303a991ff..a3902751c6 100644 --- a/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_static.py +++ b/bc_obps/reporting/tests/service/test_compliance_service/test_compliance_service_static.py @@ -15,18 +15,10 @@ def test_get_regulatory_values_by_naics_code(self): report_version_1 = make_recipe("reporting.tests.utils.report_version") report_version_2 = make_recipe("reporting.tests.utils.report_version") # Override parent values auto-generated by recipe - operation = Operation.objects.get(pk=report_version_1.report.operation.id) - report = Report.objects.get(pk=report_version_1.report_id) - operation.naics_code_id = 1 - operation.save() - report.reporting_year_id = 2024 - report.save() - operation = Operation.objects.get(pk=report_version_2.report.operation.id) - report = Report.objects.get(pk=report_version_2.report_id) - operation.naics_code_id = 22 - operation.save() - report.reporting_year_id = 2024 - report.save() + Operation.objects.filter(pk=report_version_1.report.operation.id).update(naics_code_id=1) + Report.objects.filter(pk=report_version_1.report_id).update(reporting_year_id=2024) + Operation.objects.filter(pk=report_version_2.report.operation.id).update(naics_code_id=22) + Report.objects.filter(pk=report_version_2.report_id).update(reporting_year_id=2024) ## TESTS ## # Test service function returns correct values @@ -251,9 +243,7 @@ def test_get_reporting_only_allocated(self): emission_category=EmissionCategory.objects.get(pk=1), allocated_quantity=Decimal('12.0'), ) - rp = ReportProduct.objects.get(pk=fog_product_allocation.report_product_id) - rp.product_id = 39 - rp.save() + ReportProduct.objects.filter(pk=fog_product_allocation.report_product_id).update(product_id=39) # Correctly aggregates reporting-only emissions when there is a fog product reporting_only_with_fog_for_test = ComplianceService.get_reporting_only_allocated(