Skip to content

Commit

Permalink
feat: Filter between legacy and not legacy fuel types
Browse files Browse the repository at this point in the history
* Pass compliance period for get other use table options
* Add filter on compliance period < 2024 to enable the is_legacy filter.
* Add new is_legacy to FuelTypes and default to false
  • Loading branch information
dhaselhan committed Jan 6, 2025
1 parent 6049bb1 commit 2d106d5
Show file tree
Hide file tree
Showing 18 changed files with 172 additions and 96 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""Add is_legacy to fuel_type
Revision ID: ca7200152130
Revises: 9329e38396e1
Create Date: 2025-01-03 18:43:43.638740
"""

import sqlalchemy as sa
from alembic import op

# revision identifiers, used by Alembic.
revision = "ca7200152130"
down_revision = "9329e38396e1"
branch_labels = None
depends_on = None


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column(
"fuel_type",
sa.Column(
"is_legacy",
sa.Boolean(),
server_default=sa.text("FALSE"),
nullable=False,
comment="Indicates if the fuel type is legacy and should not be used for new reports",
),
)
# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column("fuel_type", "is_legacy")
# ### end Alembic commands ###
8 changes: 7 additions & 1 deletion backend/lcfs/db/models/fuel/FuelType.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import enum

from sqlalchemy import Column, Integer, Text, Boolean, Enum, Numeric
from sqlalchemy import Column, Integer, Text, Boolean, Enum, Numeric, text
from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship

Expand Down Expand Up @@ -54,6 +54,12 @@ class FuelType(BaseModel, Auditable, DisplayOrder):
nullable=False,
comment="Indicates if the fuel type is unrecognized",
)
is_legacy = Column(
Boolean,
server_default=text("FALSE"),
nullable=False,
comment="Indicates if the fuel type is legacy and should not be used for new reports",
)

# Relationships
fuel_codes = relationship("FuelCode", back_populates="fuel_type")
Expand Down
5 changes: 3 additions & 2 deletions backend/lcfs/tests/other_uses/test_other_uses_repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def other_uses_repo(mock_db_session):
repo.fuel_code_repo.get_expected_use_types = AsyncMock(return_value=[])

# Mock for local get_formatted_fuel_types method
async def mock_get_formatted_fuel_types():
async def mock_get_formatted_fuel_types(include_legacy=False):
mock_result = await mock_db_session.execute(AsyncMock())
return mock_result.unique().scalars().all()

Expand Down Expand Up @@ -116,7 +116,7 @@ def mock_execute_side_effect(*args, **kwargs):
)

# Execute the method under test
result = await other_uses_repo.get_table_options()
result = await other_uses_repo.get_table_options("2024")

# Assertions
assert isinstance(result, dict)
Expand Down Expand Up @@ -222,6 +222,7 @@ async def test_get_latest_other_uses_by_group_uuid(other_uses_repo, mock_db_sess
assert result.user_type == UserTypeEnum.GOVERNMENT
assert result.version == 2


@pytest.mark.anyio
async def test_get_other_use_version_by_user(other_uses_repo, mock_db_session):
group_uuid = "test-group-uuid"
Expand Down
32 changes: 14 additions & 18 deletions backend/lcfs/tests/other_uses/test_other_uses_services.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ async def test_get_table_options(other_uses_service):
}
)

response = await service.get_table_options()
response = await service.get_table_options("2024")

assert isinstance(response, OtherUsesTableOptionsSchema)
mock_repo.get_table_options.assert_awaited_once()
Expand Down Expand Up @@ -66,16 +66,15 @@ async def test_create_other_use(other_uses_service):
mock_fuel_code.fuel_code = "FuelCode123"

# Mock fuel repository methods
mock_fuel_repo.get_fuel_category_by = AsyncMock(
return_value=mock_fuel_category)
mock_fuel_repo.get_fuel_type_by_name = AsyncMock(
return_value=mock_fuel_type)
mock_fuel_repo.get_fuel_category_by = AsyncMock(return_value=mock_fuel_category)
mock_fuel_repo.get_fuel_type_by_name = AsyncMock(return_value=mock_fuel_type)
mock_fuel_repo.get_expected_use_type_by_name = AsyncMock(
return_value=mock_expected_use)
return_value=mock_expected_use
)
mock_fuel_repo.get_provision_of_the_act_by_name = AsyncMock(
return_value=mock_provision_of_the_act)
mock_fuel_repo.get_fuel_code_by_name = AsyncMock(
return_value=mock_fuel_code)
return_value=mock_provision_of_the_act
)
mock_fuel_repo.get_fuel_code_by_name = AsyncMock(return_value=mock_fuel_code)

# Create a mock for the created other use
mock_created_use = create_mock_entity({})
Expand Down Expand Up @@ -120,8 +119,7 @@ async def test_update_other_use(other_uses_service):
mock_existing_use = create_mock_entity({})

# Configure repository methods to return these mocked objects
mock_repo.get_other_use_version_by_user = AsyncMock(
return_value=mock_existing_use)
mock_repo.get_other_use_version_by_user = AsyncMock(return_value=mock_existing_use)

# Mock related entities with proper string attributes
mock_fuel_type = MagicMock()
Expand All @@ -140,17 +138,15 @@ async def test_update_other_use(other_uses_service):
mock_fuel_code.fuel_code = "NewFuelCode"

# Mock fuel repository methods
mock_fuel_repo.get_fuel_type_by_name = AsyncMock(
return_value=mock_fuel_type)
mock_fuel_repo.get_fuel_category_by = AsyncMock(
return_value=mock_fuel_category)
mock_fuel_repo.get_fuel_type_by_name = AsyncMock(return_value=mock_fuel_type)
mock_fuel_repo.get_fuel_category_by = AsyncMock(return_value=mock_fuel_category)
mock_fuel_repo.get_expected_use_type_by_name = AsyncMock(
return_value=mock_expected_use)
return_value=mock_expected_use
)
mock_fuel_repo.get_provision_of_the_act_by_name = AsyncMock(
return_value=mock_provision_of_the_act
)
mock_fuel_repo.get_fuel_code_by_name = AsyncMock(
return_value=mock_fuel_code)
mock_fuel_repo.get_fuel_code_by_name = AsyncMock(return_value=mock_fuel_code)

# Mock the updated use returned after the update
mock_updated_use = MagicMock()
Expand Down
7 changes: 5 additions & 2 deletions backend/lcfs/tests/other_uses/test_other_uses_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
from lcfs.db.base import UserTypeEnum, ActionTypeEnum
from lcfs.db.models.user.Role import RoleEnum
from lcfs.web.api.base import ComplianceReportRequestSchema
from lcfs.web.api.other_uses.schema import PaginatedOtherUsesRequestSchema, OtherUsesSchema
from lcfs.web.api.other_uses.schema import (
PaginatedOtherUsesRequestSchema,
OtherUsesSchema,
)
from lcfs.web.api.other_uses.services import OtherUsesServices
from lcfs.web.api.other_uses.validation import OtherUsesValidation
from lcfs.tests.other_uses.conftest import create_mock_schema, create_mock_entity
Expand Down Expand Up @@ -47,7 +50,7 @@ async def test_get_table_options(
lambda: mock_other_uses_service
)

response = await client.get(url)
response = await client.get(url + "?compliancePeriod=2024")

assert response.status_code == 200
data = response.json()
Expand Down
6 changes: 4 additions & 2 deletions backend/lcfs/web/api/allocation_agreement/repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,12 @@ def __init__(
self.fuel_code_repo = fuel_repo

@repo_handler
async def get_table_options(self) -> dict:
async def get_table_options(self, compliance_period: str) -> dict:
"""Get all table options"""
fuel_categories = await self.fuel_code_repo.get_fuel_categories()
fuel_types = await self.fuel_code_repo.get_formatted_fuel_types()
fuel_types = await self.fuel_code_repo.get_formatted_fuel_types(
include_legacy=compliance_period < "2024"
)
units_of_measure = [unit.value for unit in QuantityUnitsEnum]
allocation_transaction_types = (
(await self.db.execute(select(AllocationTransactionType))).scalars().all()
Expand Down
6 changes: 4 additions & 2 deletions backend/lcfs/web/api/allocation_agreement/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,13 @@ async def convert_to_model(
)

@service_handler
async def get_table_options(self) -> AllocationAgreementTableOptionsSchema:
async def get_table_options(
self, compliance_period: str
) -> AllocationAgreementTableOptionsSchema:
"""
Gets the list of table options related to allocation agreements.
"""
table_options = await self.repo.get_table_options()
table_options = await self.repo.get_table_options(compliance_period)
fuel_types = [
{
**fuel_type,
Expand Down
3 changes: 2 additions & 1 deletion backend/lcfs/web/api/allocation_agreement/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,11 @@
@view_handler(["*"])
async def get_table_options(
request: Request,
compliancePeriod: str,
service: AllocationAgreementServices = Depends(),
):
"""Endpoint to retrieve table options related to allocation agreements"""
return await service.get_table_options()
return await service.get_table_options(compliancePeriod)


@router.post(
Expand Down
38 changes: 23 additions & 15 deletions backend/lcfs/web/api/fuel_code/repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,23 +51,23 @@ def __init__(self, db: AsyncSession = Depends(get_async_db_session)):
self.db = db

@repo_handler
async def get_fuel_types(self) -> List[FuelType]:
"""Get all fuel type options"""
return (
(
await self.db.execute(
select(FuelType).options(
joinedload(FuelType.provision_1),
joinedload(FuelType.provision_2),
)
)
)
.scalars()
.all()
async def get_fuel_types(self, include_legacy=False) -> List[FuelType]:
stmt = select(FuelType).options(
joinedload(FuelType.provision_1),
joinedload(FuelType.provision_2),
)

# Conditionally add the legacy filter
if not include_legacy:
stmt = stmt.where(FuelType.is_legacy == False)

result = await self.db.execute(stmt)
return result.scalars().all()

@repo_handler
async def get_formatted_fuel_types(self) -> List[Dict[str, Any]]:
async def get_formatted_fuel_types(
self, include_legacy=False
) -> List[Dict[str, Any]]:
"""Get all fuel type options with their associated fuel categories and fuel codes"""
# Define the filtering conditions for fuel codes
current_date = date.today()
Expand All @@ -77,13 +77,21 @@ async def get_formatted_fuel_types(self) -> List[Dict[str, Any]]:
FuelCode.expiration_date == None, FuelCode.expiration_date > current_date
)

conditions = [fuel_code_filters]

# If we don't want to include legacy fuel types, filter them out
if not include_legacy:
conditions.append(FuelType.is_legacy == False)

combined_conditions = and_(*conditions)

# Build the query with filtered fuel_codes
query = (
select(FuelType)
.outerjoin(FuelType.fuel_instances)
.outerjoin(FuelInstance.fuel_category)
.outerjoin(FuelType.fuel_codes)
.where(fuel_code_filters)
.where(combined_conditions)
.options(
contains_eager(FuelType.fuel_instances).contains_eager(
FuelInstance.fuel_category
Expand Down
8 changes: 6 additions & 2 deletions backend/lcfs/web/api/fuel_export/repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,13 @@ def __init__(self, db: AsyncSession = Depends(get_async_db_session)):
)

@repo_handler
async def get_fuel_export_table_options(self, compliancePeriod: str):
async def get_fuel_export_table_options(self, compliance_period: str):
"""
Retrieve Fuel Type and other static data to use them while populating fuel supply form.
"""
subquery_compliance_period_id = (
select(CompliancePeriod.compliance_period_id)
.where(CompliancePeriod.description == compliancePeriod)
.where(CompliancePeriod.description == compliance_period)
.scalar_subquery()
)

Expand Down Expand Up @@ -164,6 +164,10 @@ async def get_fuel_export_table_options(self, compliancePeriod: str):
)
)

include_legacy = compliance_period < "2024"
if not include_legacy:
query = query.where(FuelType.is_legacy == False)

results = (await self.db.execute(query)).all()
return results

Expand Down
4 changes: 4 additions & 0 deletions backend/lcfs/web/api/fuel_supply/repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,10 @@ async def get_fuel_supply_table_options(self, compliance_period: str):
)
)

include_legacy = compliance_period < "2024"
if not include_legacy:
query.where(FuelType.is_legacy == False)

fuel_type_results = (await self.db.execute(query)).all()

return {
Expand Down
32 changes: 20 additions & 12 deletions backend/lcfs/web/api/other_uses/repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,12 @@ def __init__(
self.fuel_code_repo = fuel_repo

@repo_handler
async def get_table_options(self) -> dict:
async def get_table_options(self, compliance_period: str) -> dict:
"""Get all table options"""
fuel_categories = await self.fuel_code_repo.get_fuel_categories()
fuel_types = await self.get_formatted_fuel_types()
fuel_types = await self.get_formatted_fuel_types(
include_legacy=compliance_period < "2024"
)
expected_uses = await self.fuel_code_repo.get_expected_use_types()
units_of_measure = [unit.value for unit in QuantityUnitsEnum]
provisions_of_the_act = (
Expand Down Expand Up @@ -305,28 +307,34 @@ async def get_other_use_version_by_user(
return result.scalars().first()

@repo_handler
async def get_formatted_fuel_types(self) -> List[Dict[str, Any]]:
async def get_formatted_fuel_types(
self, include_legacy=False
) -> List[Dict[str, Any]]:
"""Get all fuel type options with their associated fuel categories and fuel codes for other uses"""
# Define the filtering conditions for fuel codes
current_date = date.today()
fuel_code_filters = (
base_conditions = [
or_(
FuelCode.effective_date == None, FuelCode.effective_date <= current_date
)
& or_(
),
or_(
FuelCode.expiration_date == None,
FuelCode.expiration_date > current_date,
)
& (FuelType.other_uses_fossil_derived == True)
)
),
FuelType.other_uses_fossil_derived == True,
]

# Conditionally add the is_legacy filter
if not include_legacy:
base_conditions.append(FuelType.is_legacy == False)

combined_conditions = and_(*base_conditions)

# Build the query with filtered fuel_codes
query = (
select(FuelType)
.outerjoin(FuelType.fuel_instances)
.outerjoin(FuelInstance.fuel_category)
.outerjoin(FuelType.fuel_codes)
.where(fuel_code_filters)
.where(combined_conditions)
.options(
contains_eager(FuelType.fuel_instances).contains_eager(
FuelInstance.fuel_category
Expand Down
Loading

0 comments on commit 2d106d5

Please sign in to comment.