From b6ffd3282c702e089f4a21d880315430de9b72bc Mon Sep 17 00:00:00 2001 From: Quan Pham Date: Wed, 26 Feb 2025 10:19:38 -0500 Subject: [PATCH 1/2] Implemented processor for BM usage --- process_report/process_report.py | 2 + .../processors/bm_usage_processor.py | 20 ++++++++++ .../processors/bu_subsidy_processor.py | 7 +++- .../processors/new_pi_credit_processor.py | 4 ++ .../processors/test_bm_usage_processor.py | 37 +++++++++++++++++++ process_report/tests/util.py | 15 ++++++++ 6 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 process_report/processors/bm_usage_processor.py create mode 100644 process_report/tests/unit/processors/test_bm_usage_processor.py diff --git a/process_report/process_report.py b/process_report/process_report.py index 6271f522..d7cee7a0 100644 --- a/process_report/process_report.py +++ b/process_report/process_report.py @@ -30,6 +30,7 @@ bu_subsidy_processor, prepayment_processor, validate_cluster_name_processor, + bm_usage_processor, ) @@ -71,6 +72,7 @@ def main(): validate_cluster_name_processor.ValidateClusterNameProcessor, coldfront_fetch_processor.ColdfrontFetchProcessor, validate_pi_alias_processor.ValidatePIAliasProcessor, + bm_usage_processor.BMUsageProcessor, add_institution_processor.AddInstitutionProcessor, lenovo_processor.LenovoProcessor, validate_billable_pi_processor.ValidateBillablePIsProcessor, diff --git a/process_report/processors/bm_usage_processor.py b/process_report/processors/bm_usage_processor.py new file mode 100644 index 00000000..ce972051 --- /dev/null +++ b/process_report/processors/bm_usage_processor.py @@ -0,0 +1,20 @@ +from dataclasses import dataclass + +import pandas + +from process_report.invoices import invoice +from process_report.processors import processor + + +@dataclass +class BMUsageProcessor(processor.Processor): + def _get_bm_project_mask(self): + return pandas.Series(True, index=self.data.index) # TODO: Remove dummy mask + + def _process(self): + bm_projects_mask = self._get_bm_project_mask() + self.data.loc[bm_projects_mask, invoice.PROJECT_FIELD] = self.data.loc[ + bm_projects_mask, invoice.PROJECT_FIELD + ].apply(lambda v: v + " BM Usage") + self.data.loc[bm_projects_mask, invoice.PROJECT_ID_FIELD] = "ESI Bare Metal" + self.data.loc[bm_projects_mask, invoice.INVOICE_EMAIL_FIELD] = "nclinton@bu.edu" diff --git a/process_report/processors/bu_subsidy_processor.py b/process_report/processors/bu_subsidy_processor.py index f2e6c787..15ebebc5 100644 --- a/process_report/processors/bu_subsidy_processor.py +++ b/process_report/processors/bu_subsidy_processor.py @@ -33,7 +33,12 @@ def _get_subsidy_eligible_projects(data): ] filtered_data = filtered_data[ filtered_data[invoice.INSTITUTION_FIELD] == "Boston University" - ].copy() + ] + filtered_data = ( + filtered_data[ # TODO Does it make sense to test this filter in test cases? + ~(filtered_data[invoice.PROJECT_ID_FIELD] == "ESI Bare Metal") + ] + ) return filtered_data diff --git a/process_report/processors/new_pi_credit_processor.py b/process_report/processors/new_pi_credit_processor.py index f0bc3a32..cecb1e93 100644 --- a/process_report/processors/new_pi_credit_processor.py +++ b/process_report/processors/new_pi_credit_processor.py @@ -101,10 +101,14 @@ def _filter_nonbillables(self, data): def _filter_missing_pis(self, data): return data[~data["Missing PI"]] + def _filter_bm_projects(self, data): + return data[~(data[invoice.PROJECT_ID_FIELD] == "ESI Bare Metal")] + def _get_credit_eligible_projects(self, data: pandas.DataFrame): filtered_data = self._filter_nonbillables(data) filtered_data = self._filter_missing_pis(filtered_data) filtered_data = self._filter_excluded_su_types(filtered_data) + filtered_data = self._filter_bm_projects(filtered_data) if self.limit_new_pi_credit_to_partners: filtered_data = self._filter_partners(filtered_data) diff --git a/process_report/tests/unit/processors/test_bm_usage_processor.py b/process_report/tests/unit/processors/test_bm_usage_processor.py new file mode 100644 index 00000000..54979ef9 --- /dev/null +++ b/process_report/tests/unit/processors/test_bm_usage_processor.py @@ -0,0 +1,37 @@ +from unittest import TestCase + +import pandas + +from process_report.tests import util as test_utils + + +class TestBUSubsidyProcessor(TestCase): + def test_get_bm_project_mask(self): + test_invoice = pandas.DataFrame({}) + + answer_invoice = test_invoice.iloc[[0, 2]] + + bm_usage_proc = test_utils.new_bm_usage_processor(data=test_invoice) + bm_project_mask = bm_usage_proc._get_bm_project_mask() + self.assertTrue(test_invoice[bm_project_mask].equals(answer_invoice)) + + def test_process_bm_usage(self): + test_invoice = pandas.DataFrame( + { + "Project - Allocation": ["test", "test bm-bm"], + "Project - Allocation ID": [None] * 2, + "Invoice Email": [None] * 2, + } + ) + + answer_invoice = pandas.DataFrame( + { + "Project - Allocation": ["test BM Usage", "test bm-bm BM Usage"], + "Project - Allocation ID": ["ESI Bare Metal"] * 2, + "Invoice Email": ["nclinton@bu.edu"] * 2, + } + ) + + bm_usage_proc = test_utils.new_bm_usage_processor(data=test_invoice) + bm_usage_proc.process() + self.assertTrue(bm_usage_proc.data.equals(answer_invoice)) diff --git a/process_report/tests/util.py b/process_report/tests/util.py index 747f4a6a..a7e6edbd 100644 --- a/process_report/tests/util.py +++ b/process_report/tests/util.py @@ -16,6 +16,7 @@ bu_subsidy_processor, prepayment_processor, validate_cluster_name_processor, + bm_usage_processor, ) @@ -201,3 +202,17 @@ def new_validate_cluster_name_processor( return validate_cluster_name_processor.ValidateClusterNameProcessor( invoice_month, data, name ) + + +def new_bm_usage_processor( + name="", + invoice_month="0000-00", + data=None, +): + if data is None: + data = pandas.DataFrame() + return bm_usage_processor.BMUsageProcessor( + name, + invoice_month, + data, + ) From aeb25faa31b9e031739e05497c013a20f8950027 Mon Sep 17 00:00:00 2001 From: Quan Pham Date: Tue, 25 Feb 2025 11:42:51 -0500 Subject: [PATCH 2/2] Implemented processor for baremetal --- process_report/invoices/bm_invoice.py | 29 +++++ process_report/process_report.py | 2 + .../processors/bm_usage_processor.py | 4 +- .../processors/bu_subsidy_processor.py | 8 +- .../processors/new_pi_credit_processor.py | 2 +- process_report/tests/e2e/test_e2e_pipeline.py | 1 + .../processors/test_bm_usage_processor.py | 27 ++--- .../processors/test_bu_subsidy_processor.py | 23 ++++ .../test_new_pi_credit_processor.py | 105 ++++++++++++++---- process_report/tests/util.py | 2 +- 10 files changed, 155 insertions(+), 48 deletions(-) create mode 100644 process_report/invoices/bm_invoice.py diff --git a/process_report/invoices/bm_invoice.py b/process_report/invoices/bm_invoice.py new file mode 100644 index 00000000..c1afb3dc --- /dev/null +++ b/process_report/invoices/bm_invoice.py @@ -0,0 +1,29 @@ +from dataclasses import dataclass + + +from process_report.invoices import invoice + + +@dataclass +class BMInvoice(invoice.Invoice): + name: str = "bm_projects" + export_columns_list = [ + invoice.INVOICE_DATE_FIELD, + invoice.PROJECT_FIELD, + invoice.PROJECT_ID_FIELD, + invoice.PI_FIELD, + invoice.INVOICE_EMAIL_FIELD, + invoice.INVOICE_ADDRESS_FIELD, + invoice.INSTITUTION_FIELD, + invoice.INSTITUTION_ID_FIELD, + invoice.SU_HOURS_FIELD, + invoice.SU_TYPE_FIELD, + invoice.RATE_FIELD, + invoice.COST_FIELD, + invoice.CREDIT_FIELD, + invoice.CREDIT_CODE_FIELD, + invoice.BALANCE_FIELD, + ] + + def _prepare_export(self): + self.export_data = self.data[self.data[invoice.CLUSTER_NAME_FIELD] == "bm"] diff --git a/process_report/process_report.py b/process_report/process_report.py index d7cee7a0..17b16b01 100644 --- a/process_report/process_report.py +++ b/process_report/process_report.py @@ -10,6 +10,7 @@ from process_report import util from process_report.invoices import ( invoice, + bm_invoice, lenovo_invoice, nonbillable_invoice, billable_invoice, @@ -96,6 +97,7 @@ def main(): MOCA_prepaid_invoice.MOCAPrepaidInvoice, prepay_credits_snapshot.PrepayCreditsSnapshot, ocp_test_invoice.OcpTestInvoice, + bm_invoice.BMInvoice, ], invoice_settings.upload_to_s3, ) diff --git a/process_report/processors/bm_usage_processor.py b/process_report/processors/bm_usage_processor.py index ce972051..21dbadff 100644 --- a/process_report/processors/bm_usage_processor.py +++ b/process_report/processors/bm_usage_processor.py @@ -1,7 +1,5 @@ from dataclasses import dataclass -import pandas - from process_report.invoices import invoice from process_report.processors import processor @@ -9,7 +7,7 @@ @dataclass class BMUsageProcessor(processor.Processor): def _get_bm_project_mask(self): - return pandas.Series(True, index=self.data.index) # TODO: Remove dummy mask + return self.data[invoice.CLUSTER_NAME_FIELD] == "bm" def _process(self): bm_projects_mask = self._get_bm_project_mask() diff --git a/process_report/processors/bu_subsidy_processor.py b/process_report/processors/bu_subsidy_processor.py index 15ebebc5..0d8a6c9a 100644 --- a/process_report/processors/bu_subsidy_processor.py +++ b/process_report/processors/bu_subsidy_processor.py @@ -34,11 +34,9 @@ def _get_subsidy_eligible_projects(data): filtered_data = filtered_data[ filtered_data[invoice.INSTITUTION_FIELD] == "Boston University" ] - filtered_data = ( - filtered_data[ # TODO Does it make sense to test this filter in test cases? - ~(filtered_data[invoice.PROJECT_ID_FIELD] == "ESI Bare Metal") - ] - ) + filtered_data = filtered_data[ + ~(filtered_data[invoice.CLUSTER_NAME_FIELD] == "bm") + ] return filtered_data diff --git a/process_report/processors/new_pi_credit_processor.py b/process_report/processors/new_pi_credit_processor.py index cecb1e93..bb1f1816 100644 --- a/process_report/processors/new_pi_credit_processor.py +++ b/process_report/processors/new_pi_credit_processor.py @@ -102,7 +102,7 @@ def _filter_missing_pis(self, data): return data[~data["Missing PI"]] def _filter_bm_projects(self, data): - return data[~(data[invoice.PROJECT_ID_FIELD] == "ESI Bare Metal")] + return data[~(data[invoice.CLUSTER_NAME_FIELD] == "bm")] def _get_credit_eligible_projects(self, data: pandas.DataFrame): filtered_data = self._filter_nonbillables(data) diff --git a/process_report/tests/e2e/test_e2e_pipeline.py b/process_report/tests/e2e/test_e2e_pipeline.py index a9b86841..8d796e81 100644 --- a/process_report/tests/e2e/test_e2e_pipeline.py +++ b/process_report/tests/e2e/test_e2e_pipeline.py @@ -21,6 +21,7 @@ "MOCA-A_Prepaid_Groups-2025-06-Invoice.csv", "NERC_Prepaid_Group-Credits-2025-06.csv", "OCP_TEST 2025-06.csv", + "bm_projects 2025-06.csv", ] EXPECTED_DIRECTORIES = ["pi_invoices"] diff --git a/process_report/tests/unit/processors/test_bm_usage_processor.py b/process_report/tests/unit/processors/test_bm_usage_processor.py index 54979ef9..24d0e60d 100644 --- a/process_report/tests/unit/processors/test_bm_usage_processor.py +++ b/process_report/tests/unit/processors/test_bm_usage_processor.py @@ -6,29 +6,26 @@ class TestBUSubsidyProcessor(TestCase): - def test_get_bm_project_mask(self): - test_invoice = pandas.DataFrame({}) - - answer_invoice = test_invoice.iloc[[0, 2]] - - bm_usage_proc = test_utils.new_bm_usage_processor(data=test_invoice) - bm_project_mask = bm_usage_proc._get_bm_project_mask() - self.assertTrue(test_invoice[bm_project_mask].equals(answer_invoice)) - def test_process_bm_usage(self): test_invoice = pandas.DataFrame( { - "Project - Allocation": ["test", "test bm-bm"], - "Project - Allocation ID": [None] * 2, - "Invoice Email": [None] * 2, + "Project - Allocation": ["test", "test bm-bm", "not-bm"], + "Project - Allocation ID": [None] * 3, + "Invoice Email": [None] * 3, + "Cluster Name": ["bm", "bm", "ocp"], } ) answer_invoice = pandas.DataFrame( { - "Project - Allocation": ["test BM Usage", "test bm-bm BM Usage"], - "Project - Allocation ID": ["ESI Bare Metal"] * 2, - "Invoice Email": ["nclinton@bu.edu"] * 2, + "Project - Allocation": [ + "test BM Usage", + "test bm-bm BM Usage", + "not-bm", + ], + "Project - Allocation ID": ["ESI Bare Metal"] * 2 + [None], + "Invoice Email": ["nclinton@bu.edu"] * 2 + [None], + "Cluster Name": ["bm", "bm", "ocp"], } ) diff --git a/process_report/tests/unit/processors/test_bu_subsidy_processor.py b/process_report/tests/unit/processors/test_bu_subsidy_processor.py index 6761e209..d99e6d74 100644 --- a/process_report/tests/unit/processors/test_bu_subsidy_processor.py +++ b/process_report/tests/unit/processors/test_bu_subsidy_processor.py @@ -32,6 +32,7 @@ def _get_test_invoice( institution=None, is_billable=None, missing_pi=None, + clusters=None, ): if not balances: balances = pi_balances @@ -48,6 +49,9 @@ def _get_test_invoice( if not missing_pi: missing_pi = [False for _ in range(len(pi))] + if not clusters: + clusters = ["" for _ in range(len(pi))] + return pandas.DataFrame( { "Manager (PI)": pi, @@ -57,6 +61,7 @@ def _get_test_invoice( "Institution": institution, "Is Billable": is_billable, "Missing PI": missing_pi, + "Cluster Name": clusters, } ) @@ -175,3 +180,21 @@ def test_two_pi(self): answer_invoice["PI Balance"] = [0, 60, 0, 0] self._assert_result_invoice(subsidy_amount, test_invoice, answer_invoice) + + def test_exclude_bm_cluster(self): + """Projects in the 'bm' cluster should be excluded from BU subsidy calculation.""" + subsidy_amount = 100 + test_invoice = self._get_test_invoice( + ["PI"] * 2, # single PI (will be broadcast to two rows by lengths below) + pi_balances=[60, 60], + project_names=["P1", "P2"], + clusters=["bm", "ocp"], + ) + + answer_invoice = test_invoice.copy() + answer_invoice["Project"] = answer_invoice["Project - Allocation"] + # bm allocation gets no subsidy, non-bm allocation gets up to its PI balance (60) + answer_invoice["Subsidy"] = [0, 60] + answer_invoice["PI Balance"] = [60, 0] + + self._assert_result_invoice(subsidy_amount, test_invoice, answer_invoice) diff --git a/process_report/tests/unit/processors/test_new_pi_credit_processor.py b/process_report/tests/unit/processors/test_new_pi_credit_processor.py index 6584bb13..90c7a5bd 100644 --- a/process_report/tests/unit/processors/test_new_pi_credit_processor.py +++ b/process_report/tests/unit/processors/test_new_pi_credit_processor.py @@ -85,7 +85,7 @@ def _assert_result_invoice_and_old_pi_file( assert output_old_pi_df.equals(answer_old_pi_df) def _get_test_invoice( - self, pi, cost, su_type=None, is_billable=None, missing_pi=None + self, pi, cost, su_type=None, is_billable=None, missing_pi=None, clusters=None ): if not su_type: su_type = ["CPU" for _ in range(len(pi))] @@ -96,6 +96,9 @@ def _get_test_invoice( if not missing_pi: missing_pi = [False for _ in range(len(pi))] + if not clusters: + clusters = ["" for _ in range(len(pi))] + return pandas.DataFrame( { "Manager (PI)": pi, @@ -103,15 +106,18 @@ def _get_test_invoice( "SU Type": su_type, "Is Billable": is_billable, "Missing PI": missing_pi, + "Cluster Name": clusters, } ) + def setUp(self): + super().setUp() + self.test_old_pi_file = self.tempdir / "old_pi_file.csv" + def test_no_new_pi(self): test_invoice = self._get_test_invoice( ["PI" for _ in range(3)], [100 for _ in range(3)] ) - test_old_pi_file = self.tempdir / "old_pi.csv" - # Other fields of old PI file not accessed if PI is no longer # eligible for new-PI credit test_old_pi_df = pandas.DataFrame( @@ -121,7 +127,7 @@ def test_no_new_pi(self): "Initial Credits": [1000], } ) - test_old_pi_df.to_csv(test_old_pi_file, index=False) + test_old_pi_df.to_csv(self.test_old_pi_file, index=False) answer_invoice = pandas.concat( [ @@ -143,7 +149,7 @@ def test_no_new_pi(self): self._assert_result_invoice_and_old_pi_file( "2024-06", test_invoice, - str(test_old_pi_file), + str(self.test_old_pi_file), answer_invoice, answer_old_pi_df, ) @@ -154,7 +160,6 @@ def test_one_new_pi(self): # One allocation invoice_month = "2024-06" test_invoice = self._get_test_invoice(["PI"], [100]) - test_old_pi_file = self.tempdir / "old_pi.csv" test_old_pi_df = pandas.DataFrame( columns=[ "PI", @@ -164,7 +169,7 @@ def test_one_new_pi(self): "2nd Month Used", ] ) - test_old_pi_df.to_csv(test_old_pi_file, index=False) + test_old_pi_df.to_csv(self.test_old_pi_file, index=False) answer_invoice = pandas.concat( [ @@ -194,7 +199,7 @@ def test_one_new_pi(self): self._assert_result_invoice_and_old_pi_file( invoice_month, test_invoice, - str(test_old_pi_file), + str(self.test_old_pi_file), answer_invoice, answer_old_pi_df, ) @@ -230,7 +235,7 @@ def test_one_new_pi(self): self._assert_result_invoice_and_old_pi_file( invoice_month, test_invoice, - str(test_old_pi_file), + str(self.test_old_pi_file), answer_invoice, answer_old_pi_df, ) @@ -266,7 +271,7 @@ def test_one_new_pi(self): self._assert_result_invoice_and_old_pi_file( invoice_month, test_invoice, - str(test_old_pi_file), + str(self.test_old_pi_file), answer_invoice, answer_old_pi_df, ) @@ -277,7 +282,6 @@ def test_one_month_pi(self): # Remaining credits completely covers costs invoice_month = "2024-07" test_invoice = self._get_test_invoice(["PI"], [200]) - test_old_pi_file = self.tempdir / "old_pi.csv" test_old_pi_df = pandas.DataFrame( { "PI": ["PI"], @@ -287,7 +291,7 @@ def test_one_month_pi(self): "2nd Month Used": [0], } ) - test_old_pi_df.to_csv(test_old_pi_file, index=False) + test_old_pi_df.to_csv(self.test_old_pi_file, index=False) answer_invoice = pandas.concat( [ @@ -317,7 +321,7 @@ def test_one_month_pi(self): self._assert_result_invoice_and_old_pi_file( invoice_month, test_invoice, - str(test_old_pi_file), + str(self.test_old_pi_file), answer_invoice, answer_old_pi_df, ) @@ -353,7 +357,7 @@ def test_one_month_pi(self): self._assert_result_invoice_and_old_pi_file( invoice_month, test_invoice, - str(test_old_pi_file), + str(self.test_old_pi_file), answer_invoice, answer_old_pi_df, ) @@ -364,7 +368,6 @@ def test_two_new_pi(self): # Costs partially and completely covered invoice_month = "2024-07" test_invoice = self._get_test_invoice(["PI1", "PI1", "PI2"], [800, 500, 500]) - test_old_pi_file = self.tempdir / "old_pi.csv" test_old_pi_df = pandas.DataFrame( { "PI": ["PI1"], @@ -374,7 +377,7 @@ def test_two_new_pi(self): "2nd Month Used": [0], } ) - test_old_pi_df.to_csv(test_old_pi_file, index=False) + test_old_pi_df.to_csv(self.test_old_pi_file, index=False) answer_invoice = pandas.concat( [ @@ -404,7 +407,7 @@ def test_two_new_pi(self): self._assert_result_invoice_and_old_pi_file( invoice_month, test_invoice, - str(test_old_pi_file), + str(self.test_old_pi_file), answer_invoice, answer_old_pi_df, ) @@ -415,7 +418,6 @@ def test_old_pi_file_overwritten(self): invoice_month = "2024-06" test_invoice = self._get_test_invoice(["PI", "PI"], [500, 500]) - test_old_pi_file = self.tempdir / "old_pi.csv" test_old_pi_df = pandas.DataFrame( { "PI": ["PI"], @@ -425,7 +427,7 @@ def test_old_pi_file_overwritten(self): "2nd Month Used": [0], } ) - test_old_pi_df.to_csv(test_old_pi_file, index=False) + test_old_pi_df.to_csv(self.test_old_pi_file, index=False) answer_invoice = pandas.concat( [ @@ -455,7 +457,7 @@ def test_old_pi_file_overwritten(self): self._assert_result_invoice_and_old_pi_file( invoice_month, test_invoice, - str(test_old_pi_file), + str(self.test_old_pi_file), answer_invoice, answer_old_pi_df, ) @@ -474,7 +476,6 @@ def test_excluded_su_types(self): "OpenStack GPUH100", ], ) - test_old_pi_file = self.tempdir / "old_pi.csv" test_old_pi_df = pandas.DataFrame( columns=[ "PI", @@ -484,7 +485,7 @@ def test_excluded_su_types(self): "2nd Month Used", ] ) - test_old_pi_df.to_csv(test_old_pi_file, index=False) + test_old_pi_df.to_csv(self.test_old_pi_file, index=False) answer_invoice = pandas.concat( [ @@ -514,7 +515,7 @@ def test_excluded_su_types(self): self._assert_result_invoice_and_old_pi_file( invoice_month, test_invoice, - str(test_old_pi_file), + str(self.test_old_pi_file), answer_invoice, answer_old_pi_df, ) @@ -528,3 +529,61 @@ def test_apply_credit_error(self): test_invoice = test_utils.new_new_pi_credit_processor() with pytest.raises(SystemExit): test_invoice._get_pi_age(old_pi_df, "PI1", invoice_month) + + def test_excluded_bm_cluster(self): + """Projects in the 'bm' cluster should not receive New-PI credits.""" + invoice_month = "2024-06" + # Single PI with two allocations: one in 'bm' (should be excluded), one eligible + test_invoice = self._get_test_invoice( + ["PI", "PI"], + [600, 600], + clusters=["bm", "compute"], + ) + + # Start with empty old PI file to simulate a new PI + test_old_pi_df = pandas.DataFrame( + columns=[ + "PI", + "First Invoice Month", + "Initial Credits", + "1st Month Used", + "2nd Month Used", + ] + ) + test_old_pi_df.to_csv(self.test_old_pi_file, index=False) + + answer_invoice = pandas.concat( + [ + test_invoice, + pandas.DataFrame( + { + # 'bm' row excluded -> no credit; eligible row gets credit equal to its cost + "Credit": [None, 600], + "Credit Code": [None, "0002"], + # follow existing tests' convention where excluded row keeps its cost as PI Balance + "PI Balance": [600, 0], + "Balance": [600, 0], + } + ), + ], + axis=1, + ) + + # Old PI file should now record the new PI and used amount for the first month + answer_old_pi_df = pandas.DataFrame( + { + "PI": ["PI"], + "First Invoice Month": ["2024-06"], + "Initial Credits": [1000], + "1st Month Used": [600], + "2nd Month Used": [0], + } + ) + + self._assert_result_invoice_and_old_pi_file( + invoice_month, + test_invoice, + str(self.test_old_pi_file), + answer_invoice, + answer_old_pi_df, + ) diff --git a/process_report/tests/util.py b/process_report/tests/util.py index a7e6edbd..914a2049 100644 --- a/process_report/tests/util.py +++ b/process_report/tests/util.py @@ -212,7 +212,7 @@ def new_bm_usage_processor( if data is None: data = pandas.DataFrame() return bm_usage_processor.BMUsageProcessor( - name, invoice_month, data, + name, )