Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: compliance report fuel supply end use logic #1587

Merged
merged 6 commits into from
Jan 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,8 @@ def upgrade() -> None:
(20, 'Compression-ignition engine- Marine, with methane slip reduction kit- Operated within 76 to 100% of load range', TRUE),
(21, 'Compression-ignition engine- Marine, unknown whether kit is installed or average operating load range', TRUE),
(22, 'Unknown engine type', TRUE),
(23, 'Other (i.e. road transportation)', TRUE)
(23, 'Other (i.e. road transportation)', TRUE),
(24, 'Any', TRUE)
ON CONFLICT (end_use_type_id) DO NOTHING;
""")
Expand Down Expand Up @@ -224,13 +225,13 @@ def upgrade() -> None:
eer_id, fuel_category_id, fuel_type_id, end_use_type_id, ratio, effective_status
)
VALUES
(1, 1, 2, NULL, 0.9, TRUE),
(1, 1, 2, 24, 0.9, TRUE),
(2, 1, 3, 1, 3.5, TRUE),
(3, 1, 3, 2, 1.0, TRUE),
(4, 1, 6, 3, 2.4, TRUE),
(5, 1, 6, 2, 0.9, TRUE),
(6, 1, 13, NULL, 0.9, TRUE),
(7, 2, 2, NULL, 0.9, TRUE),
(6, 1, 13, 24, 0.9, TRUE),
(7, 2, 2, 24, 0.9, TRUE),
(8, 2, 3, 4, 3.8, TRUE),
(9, 2, 3, 5, 3.2, TRUE),
(10, 2, 3, 6, 2.5, TRUE),
Expand All @@ -242,9 +243,9 @@ def upgrade() -> None:
(16, 2, 3, 2, 1.0, TRUE),
(17, 2, 6, 3, 1.8, TRUE),
(18, 2, 6, 2, 0.9, TRUE),
(19, 2, 13, NULL, 0.9, TRUE),
(20, 3, 3, NULL, 2.5, TRUE),
(21, 3, 11, NULL, 1.0, TRUE),
(19, 2, 13, 24, 0.9, TRUE),
(20, 3, 3, 24, 2.5, TRUE),
(21, 3, 11, 24, 1.0, TRUE),
(22, 2, 7, 15, 1.0, TRUE),
(23, 2, 7, 16, 1.0, TRUE),
(24, 2, 7, 17, 1.0, TRUE),
Expand All @@ -253,7 +254,20 @@ def upgrade() -> None:
(27, 2, 7, 20, 1.0, TRUE),
(28, 2, 7, 21, 1.0, TRUE),
(29, 2, 7, 22, 0.9, TRUE),
(30, 2, 7, 23, 0.9, TRUE)
(30, 2, 7, 23, 0.9, TRUE),
(31, 2, 1, 24, 1.0, TRUE),
(32, 2, 5, 24, 1.0, TRUE),
(33, 3, 6, 24, 1.0, TRUE),
(34, 1, 14, 24, 1.0, TRUE),
(35, 1, 15, 24, 1.0, TRUE),
(36, 2, 16, 24, 1.0, TRUE),
(37, 1, 17, 24, 1.0, TRUE),
(38, 3, 18, 24, 1.0, TRUE),
(39, 1, 19, 24, 1.0, TRUE),
(40, 2, 19, 24, 1.0, TRUE),
(41, 3, 7, 24, 1.0, TRUE),
(42, 2, 20, 24, 1.0, TRUE),
(43, 1, 4, 24, 1.0, TRUE)
ON CONFLICT (eer_id) DO NOTHING;
""")

Expand Down
4 changes: 3 additions & 1 deletion backend/lcfs/tests/fuel_supply/test_fuel_supplies_repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ async def test_check_duplicate(fuel_supply_repo, mock_db_session):
compliance_report_id=1,
fuel_type_id=1,
fuel_category_id=1,
end_use_id=24,
provision_of_the_act_id=1,
quantity=1000,
units="L",
Expand All @@ -82,7 +83,8 @@ async def test_check_duplicate(fuel_supply_repo, mock_db_session):
# Set up the mock chain using regular MagicMock since the chained methods are sync
mock_result_chain = MagicMock()
mock_result_chain.scalars = MagicMock(return_value=mock_result_chain)
mock_result_chain.first = MagicMock(return_value=MagicMock(spec=FuelSupply))
mock_result_chain.first = MagicMock(
return_value=MagicMock(spec=FuelSupply))

# Define an async execute function that returns our mock chain
async def mock_execute(*args, **kwargs):
Expand Down
23 changes: 16 additions & 7 deletions backend/lcfs/tests/fuel_supply/test_fuel_supplies_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,15 @@ def fuel_supply_service():
@pytest.mark.anyio
async def test_get_fuel_supply_options(fuel_supply_service):
service, mock_repo, mock_fuel_code_repo = fuel_supply_service
mock_repo.get_fuel_supply_table_options = AsyncMock(return_value={"fuel_types": []})
mock_repo.get_fuel_supply_table_options = AsyncMock(
return_value={"fuel_types": []})
compliance_period = "2023"

response = await service.get_fuel_supply_options(compliance_period)

assert isinstance(response, FuelTypeOptionsResponse)
mock_repo.get_fuel_supply_table_options.assert_awaited_once_with(compliance_period)
mock_repo.get_fuel_supply_table_options.assert_awaited_once_with(
compliance_period)


# Asynchronous test for get_fuel_supply_list
Expand All @@ -90,7 +92,8 @@ async def test_get_fuel_supply_list(fuel_supply_service):
response = await service.get_fuel_supply_list(compliance_report_id)

assert isinstance(response, FuelSuppliesSchema)
mock_repo.get_fuel_supply_list.assert_awaited_once_with(compliance_report_id)
mock_repo.get_fuel_supply_list.assert_awaited_once_with(
compliance_report_id)


@pytest.mark.anyio
Expand All @@ -102,6 +105,7 @@ async def test_update_fuel_supply_not_found(fuel_supply_action_service):
compliance_report_id=1,
fuel_type_id=1,
fuel_category_id=1,
end_use_id=24,
provision_of_the_act_id=1,
quantity=2000,
units="L",
Expand Down Expand Up @@ -274,8 +278,10 @@ async def test_create_fuel_supply(fuel_supply_action_service):
"fuelCode": "FUEL123",
"carbonIntensity": 15.0,
},
provisionOfTheAct={"provisionOfTheActId": 1, "name": "Act Provision"},
endUseType={"endUseTypeId": 1, "type": "Transport", "subType": "Personal"},
provisionOfTheAct={"provisionOfTheActId": 1,
"name": "Act Provision"},
endUseType={"endUseTypeId": 1,
"type": "Transport", "subType": "Personal"},
units="L",
compliancePeriod="2024",
)
Expand All @@ -290,7 +296,8 @@ async def test_create_fuel_supply(fuel_supply_action_service):
)
mock_density = MagicMock(spec=EnergyDensity)
mock_density.density = 30.0
mock_fuel_code_repo.get_energy_density = AsyncMock(return_value=mock_density)
mock_fuel_code_repo.get_energy_density = AsyncMock(
return_value=mock_density)

user_type = UserTypeEnum.SUPPLIER

Expand All @@ -315,6 +322,7 @@ async def test_delete_fuel_supply(fuel_supply_action_service):
group_uuid="some-uuid",
fuel_type_id=1,
fuel_category_id=1,
end_use_id=24,
provision_of_the_act_id=1,
quantity=1000,
units="L",
Expand All @@ -338,5 +346,6 @@ async def test_delete_fuel_supply(fuel_supply_action_service):

assert response.success is True
assert response.message == "Marked as deleted."
mock_repo.get_latest_fuel_supply_by_group_uuid.assert_awaited_once_with("some-uuid")
mock_repo.get_latest_fuel_supply_by_group_uuid.assert_awaited_once_with(
"some-uuid")
mock_repo.create_fuel_supply.assert_awaited_once()
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ async def test_check_duplicate(fuel_supply_validation):
compliance_report_id=1,
fuel_type_id=1,
fuel_category_id=1,
end_use_id=24,
provision_of_the_act_id=1,
quantity=2000,
units="L",
Expand All @@ -54,6 +55,7 @@ async def test_validate_other_recognized_type(fuel_supply_validation):
compliance_report_id=1,
fuel_type_id=1, # Some recognized type ID
fuel_category_id=1,
end_use_id=24,
provision_of_the_act_id=1,
quantity=2000,
units="L",
Expand All @@ -76,6 +78,7 @@ async def test_validate_other_unrecognized_type_with_other(fuel_supply_validatio
compliance_report_id=1,
fuel_type_id=99, # Assume 99 is unrecognized "Other" type
fuel_category_id=1,
end_use_id=24,
provision_of_the_act_id=1,
quantity=2000,
units="L",
Expand All @@ -99,6 +102,7 @@ async def test_validate_other_unrecognized_type_missing_other(fuel_supply_valida
compliance_report_id=1,
fuel_type_id=99, # Assume 99 is unrecognized "Other" type
fuel_category_id=1,
end_use_id=24,
provision_of_the_act_id=1,
quantity=2000,
units="L",
Expand Down
6 changes: 5 additions & 1 deletion backend/lcfs/tests/fuel_supply/test_fuel_supplies_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ async def test_save_fuel_supply_row_create(
"compliance_report_id": 1,
"fuel_type_id": 1,
"fuel_category_id": 1,
"end_use_id": 24,
"provision_of_the_act_id": 1,
"quantity": 1000,
"units": "L",
Expand Down Expand Up @@ -104,6 +105,7 @@ async def test_save_fuel_supply_row_update(
"fuel_supply_id": 123,
"fuel_type_id": 1,
"fuel_category_id": 1,
"end_use_id": 24,
"provision_of_the_act_id": 1,
"quantity": 1000,
"units": "L",
Expand Down Expand Up @@ -160,6 +162,7 @@ async def test_save_fuel_supply_row_delete(
"fuel_supply_id": 123,
"fuel_type_id": 1,
"fuel_category_id": 1,
"end_use_id": 24,
"provision_of_the_act_id": 1,
"quantity": 1000,
"units": "L",
Expand Down Expand Up @@ -191,7 +194,8 @@ async def test_save_fuel_supply_row_delete(

assert response.status_code == status.HTTP_201_CREATED
data = response.json()
assert data == {"success": True, "message": "Fuel supply row deleted successfully"}
assert data == {"success": True,
"message": "Fuel supply row deleted successfully"}


@pytest.mark.anyio
Expand Down
2 changes: 1 addition & 1 deletion backend/lcfs/web/api/fuel_supply/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class FuelSupplyCreateUpdateSchema(BaseSchema):
version: Optional[int] = None
fuel_type_id: int
fuel_category_id: int
end_use_id: Optional[int] = None
end_use_id: int
provision_of_the_act_id: int
quantity: int
units: str
Expand Down
15 changes: 9 additions & 6 deletions backend/lcfs/web/api/fuel_supply/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,15 @@ def fuel_type_row_mapper(self, compliance_period, fuel_types, row):
)
eer = EnergyEffectivenessRatioSchema(
eer_id=row_data["eer_id"],
energy_effectiveness_ratio=round(row_data["energy_effectiveness_ratio"], 2),
energy_effectiveness_ratio=round(
row_data["energy_effectiveness_ratio"], 2),
fuel_category=fuel_category,
end_use_type=end_use_type,
)
tci = TargetCarbonIntensitySchema(
target_carbon_intensity_id=row_data["target_carbon_intensity_id"],
target_carbon_intensity=round(row_data["target_carbon_intensity"], 2),
target_carbon_intensity=round(
row_data["target_carbon_intensity"], 2),
reduction_target_percentage=round(
row_data["reduction_target_percentage"], 2
),
Expand All @@ -94,7 +96,8 @@ def fuel_type_row_mapper(self, compliance_period, fuel_types, row):
)
# Find the existing fuel type if it exists
existing_fuel_type = next(
(ft for ft in fuel_types if ft.fuel_type == row_data["fuel_type"]), None
(ft for ft in fuel_types if ft.fuel_type ==
row_data["fuel_type"]), None
)

if existing_fuel_type:
Expand Down Expand Up @@ -135,8 +138,7 @@ def fuel_type_row_mapper(self, compliance_period, fuel_types, row):
(
e
for e in existing_fuel_type.eer_ratios
if e.end_use_type == row_data["end_use_type"]
and e.fuel_category == fuel_category
if e.eer_id == eer.eer_id
),
None,
)
Expand Down Expand Up @@ -258,7 +260,8 @@ async def get_fuel_supplies_paginated(
size=pagination.size,
total=total_count,
total_pages=(
math.ceil(total_count / pagination.size) if total_count > 0 else 0
math.ceil(total_count /
pagination.size) if total_count > 0 else 0
),
),
fuel_supplies=[
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/assets/locales/en/fuelSupply.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"fuelType": "Fuel type",
"fuelTypeOther": "Fuel type other",
"fuelCategoryId": "Fuel category",
"endUse": "End use",
"endUseId": "End use",
"provisionOfTheActId": "Determining carbon intensity",
"fuelCode": "Fuel code",
"quantity": "Quantity supplied",
Expand All @@ -33,4 +33,4 @@
"validateMsg": {
"isRequired": "{{field}} is required"
}
}
}
32 changes: 30 additions & 2 deletions frontend/src/views/FuelSupplies/AddEditFuelSupplies.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export const AddEditFuelSupplies = () => {
item.fuelType?.fuelType === 'Other' ? item.fuelTypeOther : null,
provisionOfTheAct: item.provisionOfTheAct?.name,
fuelCode: item.fuelCode?.fuelCode,
endUse: item.endUse?.type || 'Any',
endUse: item.endUse?.type,
id: uuid()
}))
setRowData([...updatedRowData, { id: uuid() }])
Expand Down Expand Up @@ -140,7 +140,7 @@ export const AddEditFuelSupplies = () => {
item.fuelType?.fuelType === 'Other' ? item.fuelTypeOther : null,
provisionOfTheAct: item.provisionOfTheAct?.name,
fuelCode: item.fuelCode?.fuelCode,
endUse: item.endUse?.type || 'Any',
endUse: item.endUse?.type,
id: uuid()
}))
setRowData(updatedRowData)
Expand All @@ -161,11 +161,39 @@ export const AddEditFuelSupplies = () => {
(item) => item.fuelCategory
)

const endUseTypes = selectedFuelType.eerRatios.map(
(item) => item.endUseType
)

// Set to null if multiple options, otherwise use first item
const categoryValue =
fuelCategoryOptions.length === 1 ? fuelCategoryOptions[0] : null
const endUseValue =
endUseTypes.length === 1 ? endUseTypes[0].type : null

params.node.setDataValue('fuelCategory', categoryValue)
params.node.setDataValue('endUseType', endUseValue)
}
}

if (params.column.colId === 'fuelCategory') {
Copy link
Collaborator

@dhaselhan dhaselhan Jan 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think its cleaner for column specific logic to live in a value setter or value getter for that column rather than a big if else in handle cell changed? I believe we kinda have a mix of both.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea I was debating this as well. The thing I don't like about ag grid is that they don't provide a clean validation schema that's separated from row/column/cell logic. I'm going to look to rework some of this logic in a refactor ticket

const selectedFuelType = optionsData?.fuelTypes?.find(
(obj) => params.node.data.fuelType === obj.fuelType
)

if (selectedFuelType) {
const endUseTypes = selectedFuelType.eerRatios
.filter(
(item) =>
item.fuelCategory.fuelCategory === params.data.fuelCategory
)
.map((item) => item.endUseType)

// Set to null if multiple options, otherwise use first item
const endUseValue =
endUseTypes.length === 1 ? endUseTypes[0].type : null

params.node.setDataValue('endUseType', endUseValue)
}
}
},
Expand Down
12 changes: 4 additions & 8 deletions frontend/src/views/FuelSupplies/_schema.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ export const fuelSupplyColDefs = (optionsData, errors, warnings) => [
{
field: 'endUseType',
headerComponent: RequiredHeader,
headerName: i18n.t('fuelSupply:fuelSupplyColLabels.endUse'),
headerName: i18n.t('fuelSupply:fuelSupplyColLabels.endUseId'),
cellEditorParams: (params) => ({
options: [
...new Set(
Expand All @@ -206,7 +206,7 @@ export const fuelSupplyColDefs = (optionsData, errors, warnings) => [
?.map((item) => item.endUseType?.type)
.sort()
)
].filter((item) => item != null) || ['Any'],
].filter((item) => item != null),
multiple: false,
disableCloseOnSelect: false,
freeSolo: false,
Expand All @@ -219,14 +219,10 @@ export const fuelSupplyColDefs = (optionsData, errors, warnings) => [
cellStyle: (params) =>
StandardCellWarningAndErrors(params, errors, warnings),
suppressKeyboardEvent,
valueGetter: (params) => {
return params.colDef?.cellEditorParams(params).options.length < 1
? 'Any'
: params.data?.endUseType?.type
},
valueGetter: (params) => params.data.endUseType?.type,
editable: (params) => {
const cellParams = params.colDef?.cellEditorParams(params)
return cellParams.options.length > 0
return cellParams.options.length > 1
},
valueSetter: (params) => {
if (params.newValue) {
Expand Down
Loading